diff options
Diffstat (limited to 'host')
71 files changed, 1621 insertions, 1135 deletions
diff --git a/host/CMakeLists.txt b/host/CMakeLists.txt index 45d0cd940..1973f7b0a 100644 --- a/host/CMakeLists.txt +++ b/host/CMakeLists.txt @@ -200,7 +200,7 @@ ADD_CUSTOM_TARGET(uninstall # Install Package Docs ######################################################################## UHD_INSTALL(FILES - ${CMAKE_CURRENT_SOURCE_DIR}/../README.md + ${CMAKE_CURRENT_SOURCE_DIR}/README.md ${CMAKE_CURRENT_SOURCE_DIR}/LICENSE DESTINATION ${PKG_DOC_DIR} COMPONENT readme @@ -210,8 +210,8 @@ UHD_INSTALL(FILES # Images download directory for utils/uhd_images_downloader.py ######################################################################## -SET(UHD_IMAGES_MD5SUM "00784ebb5243b0abb15db305f557e230") -SET(UHD_IMAGES_DOWNLOAD_SRC "http://files.ettus.com/binaries/maint_images/archive/uhd-images_003.007.000-48-ge1c32905.zip") +SET(UHD_IMAGES_MD5SUM "fb5b36972c453e5b7954879873bbcf74") +SET(UHD_IMAGES_DOWNLOAD_SRC "http://files.ettus.com/binaries/maint_images/archive/uhd-images_003.007.001-20-g468e7f35.zip") ######################################################################## # Register top level components diff --git a/host/LICENSE b/host/LICENSE index 9aa03b39b..b91233b22 100644 --- a/host/LICENSE +++ b/host/LICENSE @@ -1,3 +1,7 @@ +This LICENSE file applies only to this directory and all subdirectories. Other +top-level directories in the UHD(tm) Software distribution are not necessarily +covered by this license. + 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 diff --git a/host/README.md b/host/README.md new file mode 100644 index 000000000..e80b10b49 --- /dev/null +++ b/host/README.md @@ -0,0 +1,38 @@ +Host UHD™ Software Source Code +============================================ + +This directory tree contains the source code that builds the UHD software +library on your host computer. This library contains the drivers for all Ettus +Research products, and the framework and API that make them usable to +application-level programs. The UHD library runs entirely in user-space. + +## Documentation + +For documentation, please refer to the following resources: + +For technical documentation related UHD check out the +[UHD Manual](http://files.ettus.com/uhd_docs/manual/html/). + +If you are looking for API documentation, check out the following resources: + +* [Doxygen](http://files.ettus.com/uhd_docs/doxygen/html/index.html) +* [Coding to the API](http://files.ettus.com/uhd_docs/manual/html/coding.html) +* [Device Streaming](http://files.ettus.com/uhd_docs/manual/html/stream.html) + +Additionally, be sure to check out the Ettus Research +[FAQ](http://www.ettus.com/kb/detail/frequently-asked-questions), and the +[Knowledge Base](http://www.ettus.com/kb) for useful application notes and +tutorials. + +## Support + +If you have purchased an Ettus Research USRP™ product and need technical support +using the device or using this UHD™ software, please e-mail the `USRP-Users` +mailing list, which is where Ettus Research, and our community, provide support +to users. + +Note that you must be *subscribed* to the list in order to post a message to the +list. This is to prevent spammers from just sending garbage messages out over +the listserve. + +* [Ettus Research USRP-Users Mailing List](http://lists.ettus.com/mailman/listinfo/usrp-users_lists.ettus.com) diff --git a/host/cmake/Modules/UHDPackage.cmake b/host/cmake/Modules/UHDPackage.cmake index a98a52fc7..d1c0fc099 100644 --- a/host/cmake/Modules/UHDPackage.cmake +++ b/host/cmake/Modules/UHDPackage.cmake @@ -87,7 +87,21 @@ IF(${CPACK_GENERATOR} STREQUAL NSIS) include(CheckTypeSize) check_type_size("void*[8]" BIT_WIDTH BUILTIN_TYPES_ONLY) - SET(CPACK_PACKAGE_FILE_NAME "uhd_${UHD_VERSION}_Win${BIT_WIDTH}") + # If CMake option given, specify MSVC version in installer filename + IF(SPECIFY_MSVC_VERSION) + IF(MSVC90) # Visual Studio 2008 (9.0) + SET(MSVC_VERSION "VS2008") + ELSEIF(MSVC10) # Visual Studio 2010 (10.0) + SET(MSVC_VERSION "VS2010") + ELSEIF(MSVC11) # Visual Studio 2012 (11.0) + SET(MSVC_VERSION "VS2012") + ELSEIF(MSVC12) # Visual Studio 2013 (12.0) + SET(MSVC_VERSION "VS2013") + ENDIF() + SET(CPACK_PACKAGE_FILE_NAME "uhd_${UHD_VERSION}_Win${BIT_WIDTH}_${MSVC_VERSION}") + ELSE() + SET(CPACK_PACKAGE_FILE_NAME "uhd_${UHD_VERSION}_Win${BIT_WIDTH}") + ENDIF(SPECIFY_MSVC_VERSION) SET(CPACK_PACKAGE_INSTALL_DIRECTORY "${CMAKE_PROJECT_NAME}") ENDIF() @@ -99,7 +113,7 @@ 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.md) +SET(CPACK_RESOURCE_FILE_WELCOME ${CMAKE_SOURCE_DIR}/README.md) SET(CPACK_RESOURCE_FILE_LICENSE ${CMAKE_SOURCE_DIR}/LICENSE) ######################################################################## diff --git a/host/cmake/Modules/UHDVersion.cmake b/host/cmake/Modules/UHDVersion.cmake index 41c3ac20b..00b3029ea 100644 --- a/host/cmake/Modules/UHDVersion.cmake +++ b/host/cmake/Modules/UHDVersion.cmake @@ -27,7 +27,7 @@ FIND_PACKAGE(Git QUIET) ######################################################################## SET(UHD_VERSION_MAJOR 003) SET(UHD_VERSION_MINOR 007) -SET(UHD_VERSION_PATCH 000) +SET(UHD_VERSION_PATCH 001) ######################################################################## # Set up trimmed version numbers for DLL resource files and packages @@ -63,12 +63,12 @@ EXECUTE_PROCESS( IF(_git_describe_result EQUAL 0) EXECUTE_PROCESS( WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} - COMMAND ${PYTHON_EXECUTABLE} -c "print '${_git_describe}'.split('-')[1]" + COMMAND ${PYTHON_EXECUTABLE} -c "print '${_git_describe}'.split('-')[-2]" OUTPUT_VARIABLE UHD_GIT_COUNT OUTPUT_STRIP_TRAILING_WHITESPACE ) EXECUTE_PROCESS( WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} - COMMAND ${PYTHON_EXECUTABLE} -c "print '${_git_describe}'.split('-')[2]" + COMMAND ${PYTHON_EXECUTABLE} -c "print '${_git_describe}'.split('-')[-1]" OUTPUT_VARIABLE UHD_GIT_HASH OUTPUT_STRIP_TRAILING_WHITESPACE ) ENDIF() diff --git a/host/docs/usrp_x3x0.rst b/host/docs/usrp_x3x0.rst index 3b7e9914e..7dd322dbf 100644 --- a/host/docs/usrp_x3x0.rst +++ b/host/docs/usrp_x3x0.rst @@ -65,15 +65,18 @@ in order not to damage sensitive electronics through static discharge! Network Connectivity ^^^^^^^^^^^^^^^^^^^^ -The next step is to make sure your computer can talk to the USRP. An otherwise unconfigured -USRP device will have the IP address 192.168.10.2 when using 1GigE. -It is recommended to directly connect your USRP to the computer at first, -and to set the IP address on your machine to 192.168.10.1. -See Section `Setup the host interface`_ on details how to change your machine's IP address. +The next step is to make sure your computer can talk to the USRP. An otherwise +unconfigured USRP device will have the IP address 192.168.10.2 when using +1GigE. It is recommended to directly connect your USRP to the computer at +first, and to set the IP address on your machine to 192.168.10.1. -**Note**: If you are running an automatic IP configuration service such as Network Manager, make -sure it is either deactivated or configured to not change the network device! This can, in extreme cases, -lead to you bricking the USRP! +See the `system configuration manual <./usrp_x3x0_config.html>`_ on details how +to change your machine's IP address. + +**Note**: If you are running an automatic IP configuration service such as +Network Manager, make sure it is either deactivated or configured to not manage +the network interface! This can, in extreme cases, lead to you bricking the +USRP! If your network configuration is correct, running ``uhd_find_devices`` will find your USRP and print some information about it. You will also be able to ping the USRP by running:: @@ -86,31 +89,32 @@ on the command line. At this point, you should also run:: to make sure all of your components (daughterboards, GPSDO) are correctly detected and usable. -^^^^^^^^^^^^^^^^^^^^^ -Updating the firmware -^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^^^^^^^^^ +Updating the FPGA Image +^^^^^^^^^^^^^^^^^^^^^^^^^ -If the output from ``uhd_find_devices`` and ``uhd_usrp_probe`` didn't show any warnings, you -can skip this step. However, if there were warnings regarding version incompatibility, you will -have to upate the FPGA image before you can start using your USRP. +If the output from ``uhd_find_devices`` and ``uhd_usrp_probe`` didn't show any +warnings, you can skip this step. However, if there were errors regarding the +FPGA version compatibility number (compat number), you will have to upate the +FPGA image before you can start using your USRP. 1. Download the current UHD images. You can use the ``uhd_images_downloader`` script provided with UHD (see also `FPGA Image Flavors`_). 2. Use the ``usrp_x3xx_fpga_burner`` utility to update the FPGA image. On the command line, run:: - usrp_x3xx_fpga_burner --addr=192.168.10.2 --type=HGS # Since we are using 1GigE, type is HGS + usrp_x3xx_fpga_burner --addr=192.168.10.2 --type=HGS If you have installed the images to a non-standard location, you might need to run (change the filename according to your device):: usrp_x3xx_fpga_burner --addr=192.168.10.2 --fpga-path <path_to_images>/usrp_x310_fpga_HGS.bit - The process of updating the firmware will take several minutes. Make sure the process of flashing the image does not get interrupted. + The process of updating the FPGA image will take several minutes. Make sure the process of flashing the image does not get interrupted. See `Load the Images onto the On-board Flash`_ for more details. -When your firmware is up to date, power-cycle the device and re-run ``uhd_usrp_probe``. There should -be no more warnings at this point, and all components should be correctly detected. Your USRP is now -ready for development! +When your FPGA image is up to date, power-cycle the device and re-run +``uhd_usrp_probe``. There should be no errors at this point, and all components +should be correctly detected. Your USRP is now ready for development! -------------- Hardware Setup @@ -386,63 +390,21 @@ The default IP address for the USRP X300/X310 device depends on the Ethernet Por You must configure the host Ethernet interface with a static IP address on the same subnet as the connected device to enable communication, as shown in the following table: -+---------------+-------------------------+----------------+----------------+---------------+ -| Ethernet | USRP | Default USRP | Host Static | Host Static | -| Interface | Ethernet Port | IP Address | IP Address | Subnet Mask | -+===============+=========================+================+================+===============+ -| Gigabit | Port 0 (HGS Image) | 192.168.10.2 | 192.168.10.1 | 255.255.255.0 | -+---------------+-------------------------+----------------+----------------+---------------+ -| Ten Gigabit | Port 1 (HGS/XGS Image) | 192.168.40.2 | 192.168.40.1 | 255.255.255.0 | -+---------------+-------------------------+----------------+----------------+---------------+ -| Ten Gigabit | Port 0 (XGS Image) | 192.168.30.2 | 192.168.30.1 | 255.255.255.0 | -+---------------+-------------------------+----------------+----------------+---------------+ - - -On a Linux system, you can add a static IP address very easily by using the -'ip' command - -:: - - sudo ip addr add 192.168.10.1/24 dev <interface> - -Note that **<interface>** is usually something like **eth0**. You can discover the -names of the network interfaces in your computer by running: - -:: - - ip addr show - -**Note:** -When using UHD software, if an IP address for the USRP-X Series device is not specified, -the software will use UDP broadcast packets to locate the USRP-X Series device. -On some systems, the firewall will block UDP broadcast packets. -It is recommended that you change or disable your firewall settings. - -On many Linux distributions, NetworkManager or similar tools may control the network interface. -It is important to deactivate these tools for your device before continuing! - -^^^^^^^^^^^^^^^ -Setting the MTU -^^^^^^^^^^^^^^^ -As UHD by default uses receive and transmit frames larger than the standard MTU of 1500 Bytes, -the NIC needs to be configured to use a larger MTU when used with the USRP X series devices. - -:: - - sudo ip link set mtu 8192 dev <interface> - -Upon initialization UHD will probe for the maximum possible path MTU along the path between the USRP X series device -and the host, both in receive and transmit direction. - -If the network hardware does not support MTUs as large as 8000 Bytes, passing the **send_frame_size** and **receive_frame_size** -arguments will make UHD use smaller MTUs: - -:: - - uhd_usrp_probe --args='send_frame_size=<max send MTU>, recv_frame_size=<max receive MTU>' ++---------------+-------------------------+----------------+----------------+---------------+---------------+ +| Ethernet | USRP | Default USRP | Host Static | Host Static | Address | +| Interface | Ethernet Port | IP Address | IP Address | Subnet Mask | EEPROM key | ++===============+=========================+================+================+===============+===============+ +| Gigabit | Port 0 (HGS Image) | 192.168.10.2 | 192.168.10.1 | 255.255.255.0 | ``ip-addr0`` | ++---------------+-------------------------+----------------+----------------+---------------+---------------+ +| Ten Gigabit | Port 0 (XGS Image) | 192.168.30.2 | 192.168.30.1 | 255.255.255.0 | ``ip-addr2`` | ++---------------+-------------------------+----------------+----------------+---------------+---------------+ +| Ten Gigabit | Port 1 (HGS/XGS Image) | 192.168.40.2 | 192.168.40.1 | 255.255.255.0 | ``ip-addr3`` | ++---------------+-------------------------+----------------+----------------+---------------+---------------+ -**Note:** This will most likely have a severe performance penalty. +As you can see, the X300/X310 actually stores different IP addresses, which all address the device differently: Each combination of Ethernet port and interface type (i.e., Gigabit or Ten Gigabit) has its own IP address. As an example, when addressing the device through 1 Gigabit Ethernet on its first port (Port 0), the relevant IP address is the one stored in the EEPROM with key ``ip-addr0``, or 192.168.10.2 by default. +See the `system configuration manual <./usrp_x3x0_config.html>`_ on details +how to change your machine's IP address and MTU size to work well with the X300. ^^^^^^^^^^^^^^^^^^^^^^^^^ Multiple devices per host @@ -477,6 +439,7 @@ You may need to change the USRP's IP address for several reasons: To change the USRP's IP address, you must know the current address of the USRP, and the network must be setup properly as described above. +You must also know which IP address of the X300 you want to change, as identified by their address EEPROM key (e.g. ``ip-addr0``, see the table above). Run the following commands: **UNIX:** @@ -484,14 +447,14 @@ Run the following commands: :: cd <install-path>/lib/uhd/utils - ./usrp_burn_mb_eeprom --args=<optional device args> --key=ip-addr --val=192.168.10.3 + ./usrp_burn_mb_eeprom --args=<optional device args> --key=ip-addr0 --val=192.168.10.3 **Windows:** :: cd <install-path>\lib\uhd\utils - usrp_burn_mb_eeprom.exe --args=<optional device args> --key=ip-addr --val=192.168.10.3 + usrp_burn_mb_eeprom.exe --args=<optional device args> --key=ip-addr0 --val=192.168.10.3 --------------------- Addressing the Device diff --git a/host/docs/usrp_x3x0_config.rst b/host/docs/usrp_x3x0_config.rst index 22ef8c595..4be247b04 100644 --- a/host/docs/usrp_x3x0_config.rst +++ b/host/docs/usrp_x3x0_config.rst @@ -46,6 +46,23 @@ You should open your NetworkManager configuration and tell it to ignore the network interface you are using. **This is not the same as simply setting a static IP address.** You *must* tell NetworkManager to ignore the interface. +Changing the host's IP address +------------------------------------- + +On a Linux system, you can add a static IP address very easily by using the +'ip' command: + +:: + + sudo ip addr add 192.168.10.1/24 dev <interface> + +Note that **<interface>** is usually something like **eth0**. You can discover the +names of the network interfaces in your computer by running: + +:: + + ip addr show + Configuring the Socket Buffers ------------------------------------- It is necessary to increase the maximum size of the socket buffers to avoid @@ -92,6 +109,11 @@ Firewall will often interfere with your ability to communicate with your USRP. You should configure your firewall to "trust" the interface you are using. Setting this properly depends on your OS and firewall configuration method. +When using UHD software, if an IP address for the USRP-X Series device is not specified, +the software will use UDP broadcast packets to locate the USRP-X Series device. +On some systems, the firewall will block UDP broadcast packets. +It is therefore recommended that you change or disable your firewall settings. + Interface Configuration File (Fedora) ------------------------------------- On Fedora systems, you can configure the network interface mostly from one diff --git a/host/examples/benchmark_rate.cpp b/host/examples/benchmark_rate.cpp index ea49d48d9..9e9aa67e9 100644 --- a/host/examples/benchmark_rate.cpp +++ b/host/examples/benchmark_rate.cpp @@ -98,7 +98,7 @@ void benchmark_rx_rate(uhd::usrp::multi_usrp::sptr usrp, const std::string &rx_c break; default: - std::cerr << "Error code: " << md.error_code << std::endl; + std::cerr << "Receiver error: " << md.strerror() << std::endl; std::cerr << "Unexpected error on recv, continuing..." << std::endl; break; } diff --git a/host/examples/rx_multi_samples.cpp b/host/examples/rx_multi_samples.cpp index 9e5970978..a50b5f0e0 100644 --- a/host/examples/rx_multi_samples.cpp +++ b/host/examples/rx_multi_samples.cpp @@ -172,8 +172,8 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ if (md.error_code == uhd::rx_metadata_t::ERROR_CODE_TIMEOUT) break; if (md.error_code != uhd::rx_metadata_t::ERROR_CODE_NONE){ throw std::runtime_error(str(boost::format( - "Unexpected error code 0x%x" - ) % md.error_code)); + "Receiver error %s" + ) % md.strerror())); } if(verbose) std::cout << boost::format( diff --git a/host/examples/rx_samples_to_file.cpp b/host/examples/rx_samples_to_file.cpp index 0d42404d3..de3640794 100644 --- a/host/examples/rx_samples_to_file.cpp +++ b/host/examples/rx_samples_to_file.cpp @@ -101,18 +101,15 @@ template<typename samp_type> void recv_to_file( continue; } if (md.error_code != uhd::rx_metadata_t::ERROR_CODE_NONE){ - std::string error = str(boost::format( - "Unexpected error code 0x%x" - ) % md.error_code); - - if (continue_on_bad_packet){ - std::cerr << error << std::endl; - continue; - } - else - throw std::runtime_error(error); + std::string error = str(boost::format("Receiver error: %s") % md.strerror()); + if (continue_on_bad_packet){ + std::cerr << error << std::endl; + continue; + } + else + throw std::runtime_error(error); } - + if (enable_size_map){ SizeMap::iterator it = mapSizes.find(num_rx_samps); if (it == mapSizes.end()) diff --git a/host/examples/rx_timed_samples.cpp b/host/examples/rx_timed_samples.cpp index cc9216cb7..30535907f 100644 --- a/host/examples/rx_timed_samples.cpp +++ b/host/examples/rx_timed_samples.cpp @@ -130,8 +130,8 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ if (md.error_code == uhd::rx_metadata_t::ERROR_CODE_TIMEOUT) break; if (md.error_code != uhd::rx_metadata_t::ERROR_CODE_NONE){ throw std::runtime_error(str(boost::format( - "Unexpected error code 0x%x" - ) % md.error_code)); + "Receiver error %s" + ) % md.strerror())); } if(verbose) std::cout << boost::format( diff --git a/host/examples/test_dboard_coercion.cpp b/host/examples/test_dboard_coercion.cpp index 86c59d9d7..e23390506 100644 --- a/host/examples/test_dboard_coercion.cpp +++ b/host/examples/test_dboard_coercion.cpp @@ -1,5 +1,5 @@ // -// Copyright 2012 Ettus Research LLC +// Copyright 2012,2014 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,81 +24,95 @@ #include <boost/math/special_functions/round.hpp> #include <iostream> #include <complex> +#include <utility> #include <vector> +#define SAMP_RATE 1e6 + namespace po = boost::program_options; +typedef std::pair<double, double> double_pair; //BOOST_FOREACH doesn't like commas +typedef std::vector<std::pair<double, double> > pair_vector; + /************************************************************************ * Misc functions ************************************************************************/ -std::string return_MHz_string(double freq){ +std::string MHz_str(double freq){ std::string nice_string = std::string(str(boost::format("%5.2f MHz") % (freq / 1e6))); return nice_string; } -std::string return_USRP_config_string(uhd::usrp::multi_usrp::sptr usrp, bool test_tx, bool test_rx){ - uhd::dict<std::string, std::string> tx_info = usrp->get_usrp_tx_info(); - uhd::dict<std::string, std::string> rx_info = usrp->get_usrp_rx_info(); +std::string return_usrp_config_string(uhd::usrp::multi_usrp::sptr usrp, int chan, bool test_tx, bool test_rx, bool is_b2xx){ + uhd::dict<std::string, std::string> tx_info = usrp->get_usrp_tx_info(chan); + uhd::dict<std::string, std::string> rx_info = usrp->get_usrp_rx_info(chan); std::string info_string; std::string mboard_id, mboard_serial; std::string tx_serial, tx_subdev_name, tx_subdev_spec; std::string rx_serial, rx_subdev_name, rx_subdev_spec; mboard_id = tx_info.get("mboard_id"); - if(tx_info.get("mboard_serial") != "") mboard_serial = tx_info.get("mboard_serial"); - else mboard_serial = "no serial"; + if(tx_info.get("mboard_serial") == "") mboard_serial = "no serial"; + else mboard_serial = tx_info.get("mboard_serial"); - info_string = std::string(str(boost::format("Motherboard: %s (%s)\n") % mboard_id % mboard_serial)); + info_string = str(boost::format("Motherboard: %s (%s)\n") % mboard_id % mboard_serial); if(test_tx){ - if(tx_info.get("tx_serial") != "") tx_serial = tx_info.get("tx_serial"); - else tx_serial = "no serial"; + if(tx_info.get("tx_serial") == "") tx_serial = "no serial"; + else tx_serial = tx_info.get("tx_serial"); tx_subdev_name = tx_info.get("tx_subdev_name"); tx_subdev_spec = tx_info.get("tx_subdev_spec"); - info_string += std::string(str(boost::format("TX: %s (%s, %s)") % tx_subdev_name % tx_serial % tx_subdev_spec)); + info_string += is_b2xx ? str(boost::format("TX: %s (%s)") + % tx_subdev_name % tx_subdev_spec) + : str(boost::format("TX: %s (%s, %s)") + % tx_subdev_name % tx_serial % tx_subdev_spec); } if(test_tx and test_rx) info_string += "\n"; if(test_rx){ - if(rx_info.get("rx_serial") != "") rx_serial = rx_info.get("rx_serial"); - else rx_serial = "no serial"; + if(rx_info.get("rx_serial") == "") rx_serial = "no serial"; + else rx_serial = rx_info.get("rx_serial"); rx_subdev_name = rx_info.get("rx_subdev_name"); rx_subdev_spec = rx_info.get("rx_subdev_spec"); - info_string += std::string(str(boost::format("RX: %s (%s, %s)") % rx_subdev_name % rx_serial % rx_subdev_spec)); + info_string += is_b2xx ? str(boost::format("RX: %s (%s)") + % rx_subdev_name % rx_subdev_spec) + : str(boost::format("RX: %s (%s, %s)") + % rx_subdev_name % rx_serial % rx_subdev_spec); } return info_string; } -/************************************************************************ - * TX Frequency/Gain Coercion -************************************************************************/ +std::string coercion_test(uhd::usrp::multi_usrp::sptr usrp, std::string type, int chan, + bool test_gain, double freq_step, double gain_step, bool verbose){ -std::string tx_test(uhd::usrp::multi_usrp::sptr usrp, bool test_gain, bool verbose){ + //Getting USRP info + uhd::dict<std::string, std::string> usrp_info = (type == "TX") ? usrp->get_usrp_tx_info(chan) + : usrp->get_usrp_rx_info(chan); + std::string subdev_name = (type == "TX") ? usrp_info.get("tx_subdev_name") + : usrp_info.get("rx_subdev_name"); + std::string subdev_spec = (type == "TX") ? usrp_info.get("tx_subdev_spec") + : usrp_info.get("rx_subdev_spec"); //Establish frequency range - std::vector<double> freqs; - std::vector<double> xcvr_freqs; + std::vector<double> xcvr_freqs; //XCVR2450 has two ranges + uhd::freq_range_t freq_ranges = (type == "TX") ? usrp->get_fe_tx_freq_range(chan) + : usrp->get_fe_rx_freq_range(chan); + + std::cout << boost::format("\nTesting %s coercion...") % type << std::endl; - BOOST_FOREACH(const uhd::range_t &range, usrp->get_fe_tx_freq_range()){ + BOOST_FOREACH(const uhd::range_t &range, freq_ranges){ double freq_begin = range.start(); double freq_end = range.stop(); - double freq_step; - if(usrp->get_usrp_tx_info().get("tx_subdev_name") == "XCVR2450 TX"){ + if(subdev_name.find("XCVR2450") == 0){ xcvr_freqs.push_back(freq_begin); xcvr_freqs.push_back(freq_end); } - if(freq_end - freq_begin > 1000e6) freq_step = 100e6; - else if(freq_end - freq_begin < 300e6) freq_step = 10e6; - else freq_step = 50e6; - double current_freq = freq_begin; - while(current_freq < freq_end){ freqs.push_back(current_freq); current_freq += freq_step; @@ -109,55 +123,66 @@ std::string tx_test(uhd::usrp::multi_usrp::sptr usrp, bool test_gain, bool verbo std::vector<double> gains; if(test_gain){ - //Establish gain range + uhd::gain_range_t gain_range = (type == "TX") ? usrp->get_tx_gain_range(chan) + : usrp->get_rx_gain_range(chan); - double gain_begin = usrp->get_tx_gain_range().start(); + double gain_begin = gain_range.start(); + //Start gain at 0 if range begins negative if(gain_begin < 0.0) gain_begin = 0.0; - double gain_end = usrp->get_tx_gain_range().stop(); + + double gain_end = gain_range.stop(); double current_gain = gain_begin; while(current_gain < gain_end){ gains.push_back(current_gain); - current_gain++; + current_gain += gain_step; } gains.push_back(gain_end); - } //Establish error-storing variables - std::vector<double> bad_tune_freqs; std::vector<double> no_lock_freqs; - std::vector< std::vector< double > > bad_gain_vals; - std::vector<std::string> dboard_sensor_names = usrp->get_tx_sensor_names(); + pair_vector bad_gain_vals; + + //Sensor names + std::vector<std::string> dboard_sensor_names = (type == "TX") ? usrp->get_tx_sensor_names(chan) + : usrp->get_rx_sensor_names(chan); std::vector<std::string> mboard_sensor_names = usrp->get_mboard_sensor_names(); + bool has_sensor = (std::find(dboard_sensor_names.begin(), dboard_sensor_names.end(), "lo_locked")) != dboard_sensor_names.end(); - for(std::vector<double>::iterator f = freqs.begin(); f != freqs.end(); ++f){ + BOOST_FOREACH(double freq, freqs){ //Testing for successful frequency tune + if(type == "TX") usrp->set_tx_freq(freq,chan); + else usrp->set_rx_freq(freq,chan); - usrp->set_tx_freq(*f); boost::this_thread::sleep(boost::posix_time::microseconds(long(1000))); + double actual_freq = (type == "TX") ? usrp->get_tx_freq(chan) + : usrp->get_rx_freq(chan); - double actual_freq = usrp->get_tx_freq(); - - if(*f == 0.0){ + if(freq == 0.0){ if(floor(actual_freq + 0.5) == 0.0){ - if(verbose) std::cout << boost::format("\nTX frequency successfully tuned to %s.") % return_MHz_string(*f) << std::endl; + if(verbose) std::cout << boost::format("\n%s frequency successfully tuned to %s.") + % type % MHz_str(freq) << std::endl; } else{ - if(verbose) std::cout << boost::format("\nTX frequency tuned to %s instead of %s.") % return_MHz_string(actual_freq) % return_MHz_string(*f) << std::endl; + if(verbose) std::cout << boost::format("\n%s frequency tuned to %s instead of %s.") + % type % MHz_str(actual_freq) % MHz_str(freq) << std::endl; + bad_tune_freqs.push_back(freq); } } else{ - if((*f / actual_freq > 0.9999) and (*f / actual_freq < 1.0001)){ - if(verbose) std::cout << boost::format("\nTX frequency successfully tuned to %s.") % return_MHz_string(*f) << std::endl; + if((freq / actual_freq > 0.9999) and (freq / actual_freq < 1.0001)){ + if(verbose) std::cout << boost::format("\n%s frequency successfully tuned to %s.") + % type % MHz_str(freq) << std::endl; } else{ - if(verbose) std::cout << boost::format("\nTX frequency tuned to %s instead of %s.") % return_MHz_string(actual_freq) % return_MHz_string(*f) << std::endl; - bad_tune_freqs.push_back(*f); + if(verbose) std::cout << boost::format("\n%s frequency tuned to %s instead of %s.") + % type % MHz_str(actual_freq) % MHz_str(freq) << std::endl; + bad_tune_freqs.push_back(freq); } } @@ -173,11 +198,13 @@ std::string tx_test(uhd::usrp::multi_usrp::sptr usrp, bool test_gain, bool verbo } } if(is_locked){ - if(verbose) std::cout << boost::format("LO successfully locked at TX frequency %s.") % return_MHz_string(*f) << std::endl; + if(verbose) std::cout << boost::format("LO successfully locked at %s frequency %s.") + % type % MHz_str(freq) << std::endl; } else{ - if(verbose) std::cout << boost::format("LO did not successfully lock at TX frequency %s.") % return_MHz_string(*f) << std::endl; - no_lock_freqs.push_back(*f); + if(verbose) std::cout << boost::format("LO did not successfully lock at %s frequency %s.") + % type % MHz_str(freq) << std::endl; + no_lock_freqs.push_back(freq); } } @@ -185,275 +212,101 @@ std::string tx_test(uhd::usrp::multi_usrp::sptr usrp, bool test_gain, bool verbo //Testing for successful gain tune - for(std::vector<double>::iterator g = gains.begin(); g != gains.end(); ++g){ - usrp->set_tx_gain(*g); + BOOST_FOREACH(double gain, gains){ + if(type == "TX") usrp->set_tx_gain(gain,chan); + else usrp->set_rx_gain(gain,chan); + boost::this_thread::sleep(boost::posix_time::microseconds(1000)); - double actual_gain = usrp->get_tx_gain(); + double actual_gain = (type == "TX") ? usrp->get_tx_gain(chan) + : usrp->get_rx_gain(chan); - if(*g == 0.0){ + if(gain == 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; + if(verbose) std::cout << boost::format("Gain successfully set to %5.2f at %s frequency %s.") + % gain % type % MHz_str(freq) << 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; - bad_gain_freq.push_back(*f); - bad_gain_freq.push_back(*g); - bad_gain_vals.push_back(bad_gain_freq); + if(verbose) std::cout << boost::format("Gain set to %5.2f instead of %5.2f at %s frequency %s.") + % actual_gain % gain % type % MHz_str(freq) << std::endl; + bad_gain_vals.push_back(std::make_pair(freq, gain)); } } else{ - if((*g / actual_gain) > 0.9 and (*g / actual_gain) < 1.1){ - if(verbose) std::cout << boost::format("TX gain successfully set to %5.2f at TX frequency %s.") % *g % return_MHz_string(*f) << std::endl; + if((gain / actual_gain) > 0.9999 and (gain / actual_gain) < 1.0001){ + if(verbose) std::cout << boost::format("Gain successfully set to %5.2f at %s frequency %s.") + % gain % type % MHz_str(freq) << 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; - bad_gain_freq.push_back(*f); - bad_gain_freq.push_back(*g); - bad_gain_vals.push_back(bad_gain_freq); + if(verbose) std::cout << boost::format("Gain set to %5.2f instead of %5.2f at %s frequency %s.") + % actual_gain % gain % type % MHz_str(freq) << std::endl; + bad_gain_vals.push_back(std::make_pair(freq, gain)); } } } } } - std::string tx_results = "TX Summary:\n"; - if(usrp->get_usrp_tx_info().get("tx_subdev_name") == "XCVR2450 TX"){ - tx_results += std::string(str(boost::format("Frequency Range: %s - %s, %s - %s\n") % return_MHz_string(xcvr_freqs.at(0)) % return_MHz_string(xcvr_freqs.at(1)) % - return_MHz_string(xcvr_freqs.at(2)) % return_MHz_string(xcvr_freqs.at(3)))); + std::string results = str(boost::format("%s Summary:\n") % type); + if(subdev_name.find("XCVR2450") == 0){ + results += str(boost::format("Frequency Range: %s - %s, %s - %s\n") + % MHz_str(xcvr_freqs[0]) % MHz_str(xcvr_freqs[1]) + % MHz_str(xcvr_freqs[2]) % MHz_str(xcvr_freqs[3])); + } + else results += str(boost::format("Frequency Range: %s - %s (Step: %s)\n") + % MHz_str(freqs.front()) % MHz_str(freqs.back()) % MHz_str(freq_step)); + if(test_gain) results += str(boost::format("Gain Range:%5.2f - %5.2f (Step:%5.2f)\n") + % gains.front() % gains.back() % gain_step); + + if(bad_tune_freqs.empty()) results += "USRP successfully tuned to all frequencies."; + else if(bad_tune_freqs.size() > 10 and not verbose){ + //If tuning fails at many values, don't print them all + results += str(boost::format("USRP did not successfully tune at %d frequencies.") + % bad_tune_freqs.size()); } - else tx_results += std::string(str(boost::format("Frequency Range: %s - %s\n") % return_MHz_string(freqs.front()) % return_MHz_string(freqs.back()))); - if(test_gain) tx_results += std::string(str(boost::format("Gain Range: %5.2f - %5.2f\n") % gains.front() % gains.back())); - - if(bad_tune_freqs.empty()) tx_results += "USRP successfully tuned to all frequencies."; else{ - tx_results += "USRP did not successfully tune to the following frequencies: "; - for(std::vector<double>::iterator i = bad_tune_freqs.begin(); i != bad_tune_freqs.end(); ++i){ - if(i != bad_tune_freqs.begin()) tx_results += ", "; - tx_results += return_MHz_string(*i); + results += "USRP did not successfully tune to the following frequencies: "; + BOOST_FOREACH(double bad_freq, bad_tune_freqs){ + if(bad_freq != *bad_tune_freqs.begin()) results += ", "; + results += MHz_str(bad_freq); } } if(has_sensor){ - tx_results += "\n"; - if(no_lock_freqs.empty()) tx_results += "LO successfully locked at all frequencies."; - else{ - tx_results += "LO did not lock at the following frequencies: "; - for(std::vector<double>::iterator i = no_lock_freqs.begin(); i != no_lock_freqs.end(); ++i){ - if(i != no_lock_freqs.begin()) tx_results += ", "; - tx_results += return_MHz_string(*i); - } + results += "\n"; + if(no_lock_freqs.empty()) results += "LO successfully locked at all frequencies."; + else if(no_lock_freqs.size() > 10 and not verbose){ + //If locking fails at many values, don't print them all + results += str(boost::format("USRP did not successfully lock at %d frequencies.") + % no_lock_freqs.size()); } - } - if(test_gain){ - tx_results += "\n"; - if(bad_gain_vals.empty()) tx_results += "USRP successfully set all specified gain values at all frequencies."; else{ - tx_results += "USRP did not successfully set gain under the following circumstances:"; - for(std::vector< std::vector<double> >::iterator i = bad_gain_vals.begin(); i != bad_gain_vals.end(); ++i){ - std::vector<double> bad_pair = *i; - double bad_freq = bad_pair.front(); - double bad_gain = bad_pair.back(); - tx_results += std::string(str(boost::format("\nFrequency: %s, Gain: %5.2f") % return_MHz_string(bad_freq) % bad_gain)); + results += "LO did not lock at the following frequencies: "; + BOOST_FOREACH(double bad_freq, no_lock_freqs){ + if(bad_freq != *no_lock_freqs.begin()) results += ", "; + results += MHz_str(bad_freq); } } } - - return tx_results; -} - -/************************************************************************ - * RX Frequency/Gain Coercion -************************************************************************/ - -std::string rx_test(uhd::usrp::multi_usrp::sptr usrp, bool test_gain, bool verbose){ - - //Establish frequency range - - std::vector<double> freqs; - std::vector<double> xcvr_freqs; - - BOOST_FOREACH(const uhd::range_t &range, usrp->get_fe_rx_freq_range()){ - double freq_begin = range.start(); - double freq_end = range.stop(); - - if(usrp->get_usrp_rx_info().get("rx_subdev_name") == "XCVR2450 RX"){ - xcvr_freqs.push_back(freq_begin); - xcvr_freqs.push_back(freq_end); - } - - double freq_step; - - if(freq_end - freq_begin > 1000e6) freq_step = 100e6; - else if(freq_end - freq_begin < 300e6) freq_step = 10e6; - else freq_step = 50e6; - - double current_freq = freq_begin; - - while(current_freq < freq_end){ - freqs.push_back(current_freq); - current_freq += freq_step; - } - } - - std::vector<double> gains; - if(test_gain){ - - //Establish gain range - - double gain_begin = usrp->get_rx_gain_range().start(); - if(gain_begin < 0.0) gain_begin = 0.0; - double gain_end = usrp->get_rx_gain_range().stop(); - - double current_gain = gain_begin; - while(current_gain < gain_end){ - gains.push_back(current_gain); - current_gain++; - } - gains.push_back(gain_end); - - } - - //Establish error-storing variables - - std::vector<double> bad_tune_freqs; - std::vector<double> no_lock_freqs; - std::vector< std::vector< double > > bad_gain_vals; - std::vector<std::string> dboard_sensor_names = usrp->get_rx_sensor_names(); - std::vector<std::string> mboard_sensor_names = usrp->get_mboard_sensor_names(); - bool has_sensor = (std::find(dboard_sensor_names.begin(), dboard_sensor_names.end(), "lo_locked")) != dboard_sensor_names.end(); - - for(std::vector<double>::iterator f = freqs.begin(); f != freqs.end(); ++f){ - - //Testing for successful frequency tune - - usrp->set_rx_freq(*f); - boost::this_thread::sleep(boost::posix_time::microseconds(long(1000))); - - double actual_freq = usrp->get_rx_freq(); - - if(*f == 0.0){ - if(floor(actual_freq + 0.5) == 0.0){ - if(verbose) std::cout << boost::format("\nRX frequency successfully tuned to %s.") % return_MHz_string(*f) << std::endl; - } - else{ - if(verbose) std::cout << boost::format("\nRX frequency tuned to %s instead of %s.") % return_MHz_string(actual_freq) % return_MHz_string(*f) << std::endl; - } + results += "\n"; + if(bad_gain_vals.empty()) results += "USRP successfully set all specified gain values at all frequencies."; + else if(bad_gain_vals.size() > 10 and not verbose){ + //If gain fails at many values, don't print them all + results += str(boost::format("USRP did not successfully set gain at %d values.") + % bad_gain_vals.size()); } else{ - if((*f / actual_freq > 0.9999) and (*f / actual_freq < 1.0001)){ - if(verbose) std::cout << boost::format("\nRX frequency successfully tuned to %s.") % return_MHz_string(*f) << std::endl; - } - else{ - if(verbose) std::cout << boost::format("\nRX frequency tuned to %s instead of %s.") % return_MHz_string(actual_freq) % return_MHz_string(*f) << std::endl; - bad_tune_freqs.push_back(*f); - } - } - - //Testing for successful lock - - if(has_sensor){ - bool is_locked = false; - for(int i = 0; i < 1000; i++){ - boost::this_thread::sleep(boost::posix_time::microseconds(1000)); - if(usrp->get_rx_sensor("lo_locked",0).to_bool()){ - is_locked = true; - break; - } - } - if(is_locked){ - if(verbose) std::cout << boost::format("LO successfully locked at RX frequency %s.") % return_MHz_string(*f) << std::endl; - } - else{ - if(verbose) std::cout << boost::format("LO did not successfully lock at RX frequency %s.") % return_MHz_string(*f) << std::endl; - no_lock_freqs.push_back(*f); - } - } - - if(test_gain){ - - //Testing for successful gain tune - - for(std::vector<double>::iterator g = gains.begin(); g != gains.end(); ++g){ - usrp->set_rx_gain(*g); - boost::this_thread::sleep(boost::posix_time::microseconds(1000)); - - double actual_gain = usrp->get_rx_gain(); - - if(*g == 0.0){ - if(actual_gain == 0.0){ - if(verbose) std::cout << boost::format("RX gain successfully set to %5.2f at RX frequency %s.") % *g % return_MHz_string(*f) << std::endl; - } - else{ - if(verbose) std::cout << boost::format("RX gain set to %5.2f instead of %5.2f at RX frequency %s.") % actual_gain % *g % return_MHz_string(*f) << std::endl; - std::vector<double> bad_gain_freq; - bad_gain_freq.push_back(*f); - bad_gain_freq.push_back(*g); - bad_gain_vals.push_back(bad_gain_freq); - } - } - else{ - if((*g / actual_gain) > 0.9 and (*g / actual_gain) < 1.1){ - if(verbose) std::cout << boost::format("RX gain successfully set to %5.2f at RX frequency %s.") % *g % return_MHz_string(*f) << std::endl; - } - else{ - if(verbose) std::cout << boost::format("RX gain set to %5.2f instead of %5.2f at RX frequency %s.") % actual_gain % *g % return_MHz_string(*f) << std::endl; - std::vector<double> bad_gain_freq; - bad_gain_freq.push_back(*f); - bad_gain_freq.push_back(*g); - bad_gain_vals.push_back(bad_gain_freq); - } - } - } - } - } - - std::string rx_results = "RX Summary:\n"; - if(usrp->get_usrp_rx_info().get("rx_subdev_name") == "XCVR2450 RX"){ - rx_results += std::string(str(boost::format("Frequency Range: %s - %s, %s - %s\n") % return_MHz_string(xcvr_freqs.at(0)) % return_MHz_string(xcvr_freqs.at(1)) % - return_MHz_string(xcvr_freqs.at(2)) % return_MHz_string(xcvr_freqs.at(3)))); - } - else rx_results += std::string(str(boost::format("Frequency Range: %s - %s\n") % return_MHz_string(freqs.front()) % return_MHz_string(freqs.back()))); - if(test_gain) rx_results += std::string(str(boost::format("Gain Range: %5.2f - %5.2f\n") % gains.front() % gains.back())); - - if(bad_tune_freqs.empty()) rx_results += "USRP successfully tuned to all frequencies."; - else{ - rx_results += "USRP did not successfully tune to the following frequencies: "; - for(std::vector<double>::iterator i = bad_tune_freqs.begin(); i != bad_tune_freqs.end(); ++i){ - if(i != bad_tune_freqs.begin()) rx_results += ", "; - rx_results += return_MHz_string(*i); - } - } - if(has_sensor){ - - rx_results += "\n"; - if(no_lock_freqs.empty()) rx_results += "LO successfully locked at all frequencies."; - else{ - rx_results += "LO did not successfully lock at the following frequencies: "; - for(std::vector<double>::iterator i = no_lock_freqs.begin(); i != no_lock_freqs.end(); ++i){ - if( i != no_lock_freqs.begin()) rx_results += ", "; - rx_results += return_MHz_string(*i); - } - } - } - if(test_gain){ - rx_results += "\n"; - if(bad_gain_vals.empty()) rx_results += "USRP successfully set all specified gain values at all frequencies."; - else{ - rx_results += "USRP did not successfully set gain under the following circumstances:"; - for(std::vector< std::vector<double> >::iterator i = bad_gain_vals.begin(); i != bad_gain_vals.end(); ++i){ - std::vector<double> bad_pair = *i; - double bad_freq = bad_pair.front(); - double bad_gain = bad_pair.back(); - rx_results += std::string(str(boost::format("\nFrequency: %s, Gain: %5.2f") % return_MHz_string(bad_freq) % bad_gain)); + results += "USRP did not successfully set gain under the following circumstances:"; + BOOST_FOREACH(double_pair bad_pair, bad_gain_vals){ + double bad_freq = bad_pair.first; + double bad_gain = bad_pair.second; + results += str(boost::format("\nFrequency: %s, Gain: %5.2f") % MHz_str(bad_freq) % bad_gain); } } } - return rx_results; + return results; } /************************************************************************ @@ -463,8 +316,9 @@ std::string rx_test(uhd::usrp::multi_usrp::sptr usrp, bool test_gain, bool verbo int UHD_SAFE_MAIN(int argc, char *argv[]){ //Variables + int chan; std::string args; - double gain_step; + double freq_step, gain_step; std::string ref; std::string tx_results; std::string rx_results; @@ -475,34 +329,20 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ desc.add_options() ("help", "help message") ("args", po::value<std::string>(&args)->default_value(""), "Specify the UHD device") - ("gain_step", po::value<double>(&gain_step)->default_value(1.0), "Specify the delta between gain scans") + ("chan", po::value<int>(&chan)->default_value(0), "Specify multi_usrp channel") + ("freq-step", po::value<double>(&freq_step)->default_value(100e6), "Specify the delta between frequency scans") + ("gain-step", po::value<double>(&gain_step)->default_value(1.0), "Specify the delta between gain scans") ("tx", "Specify to test TX frequency and gain coercion") ("rx", "Specify to test RX frequency and gain coercion") ("ref", po::value<std::string>(&ref)->default_value("internal"), "Waveform type: internal, external, or mimo") - ("no_tx_gain", "Do not test TX gain") - ("no_rx_gain", "Do not test RX gain") + ("no-tx-gain", "Do not test TX gain") + ("no-rx-gain", "Do not test RX gain") ("verbose", "Output every frequency and gain check instead of just final summary") ; po::variables_map vm; po::store(po::parse_command_line(argc, argv, desc), vm); po::notify(vm); - //Create a USRP device - std::cout << std::endl; - uhd::device_addrs_t device_addrs = uhd::device::find(args); - std::cout << boost::format("Creating the USRP device with: %s...") % args << std::endl; - uhd::usrp::multi_usrp::sptr usrp = uhd::usrp::multi_usrp::make(args); - std::cout << std::endl << boost::format("Using Device: %s") % usrp->get_pp_string() << std::endl; - usrp->set_tx_rate(1e6); - usrp->set_rx_rate(1e6); - - //Boolean variables based on command line input - bool test_tx = vm.count("tx") > 0; - bool test_rx = vm.count("rx") > 0; - bool test_tx_gain = !(vm.count("no_tx_gain") > 0) and (usrp->get_tx_gain_range().stop() > 0); - bool test_rx_gain = !(vm.count("no_rx_gain") > 0) and (usrp->get_rx_gain_range().stop() > 0); - bool verbose = vm.count("verbose") > 0; - //Help messages, errors if(vm.count("help") > 0){ std::cout << "UHD Daughterboard Coercion Test\n" @@ -510,42 +350,72 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ "make sure that they can successfully tune to all\n" "frequencies and gains in their advertised ranges.\n\n"; std::cout << desc << std::endl; - return ~0; - } - - if(ref != "internal" and ref != "external" and ref != "mimo"){ - std::cout << desc << std::endl; - std::cout << "REF must equal internal, external, or mimo." << std::endl; - return ~0; + return EXIT_SUCCESS; } if(vm.count("tx") + vm.count("rx") == 0){ std::cout << desc << std::endl; std::cout << "Specify --tx to test for TX frequency coercion\n" "Specify --rx to test for RX frequency coercion\n"; - return ~0; + return EXIT_FAILURE; } - if(test_rx and usrp->get_usrp_rx_info().get("rx_id") == "Basic RX (0x0001)"){ - std::cout << desc << std::endl; - std::cout << "This test does not work with the Basic RX daughterboard." << std::endl; - return ~0; - } - else if(test_rx and usrp->get_usrp_rx_info().get("rx_id") == "Unknown (0xffff)"){ + //Create a USRP device + std::cout << std::endl; + uhd::device_addrs_t device_addrs = uhd::device::find(args); + std::cout << boost::format("Creating the USRP device with: %s...") % args << std::endl; + uhd::usrp::multi_usrp::sptr usrp = uhd::usrp::multi_usrp::make(args); + std::cout << std::endl << boost::format("Using Device: %s") % usrp->get_pp_string() << std::endl; + usrp->set_tx_rate(SAMP_RATE); + usrp->set_rx_rate(SAMP_RATE); + + //Boolean variables based on command line input + bool test_tx = vm.count("tx") > 0; + bool test_rx = vm.count("rx") > 0; + bool test_tx_gain = !(vm.count("no-tx-gain") > 0) and (usrp->get_tx_gain_range().stop() > 0); + bool test_rx_gain = !(vm.count("no-rx-gain") > 0) and (usrp->get_rx_gain_range().stop() > 0); + bool verbose = vm.count("verbose") > 0; + + if(ref != "internal" and ref != "external" and ref != "mimo"){ std::cout << desc << std::endl; - std::cout << "This daughterboard is unrecognized, or there is no RX daughterboard." << std::endl; - return ~0; + std::cout << "REF must equal internal, external, or mimo." << std::endl; + return EXIT_FAILURE; } - if(test_tx and usrp->get_usrp_tx_info().get("tx_id") == "Basic TX (0x0000)"){ - std::cout << desc << std::endl; - std::cout << "This test does not work with the Basic TX daughterboard." << std::endl; - return ~0; + //Use TX mboard ID to determine if this is a B2xx, will still return value if there is no TX + std::string tx_mboard_id = usrp->get_usrp_tx_info(chan).get("mboard_id"); + bool is_b2xx = (tx_mboard_id == "B200" or tx_mboard_id == "B210"); + + //Don't perform daughterboard validity checks for B200/B210 + if((not is_b2xx) and test_tx){ + std::string tx_dboard_name = usrp->get_usrp_tx_info(chan).get("tx_id"); + if(tx_dboard_name == "Basic TX (0x0000)" or tx_dboard_name == "LF TX (0x000e)"){ + std::cout << desc << std::endl; + std::cout << boost::format("This test does not work with the %s daughterboard.") + % tx_dboard_name << std::endl; + return EXIT_FAILURE; + } + else if(tx_dboard_name == "Unknown (0xffff)"){ + std::cout << desc << std::endl; + std::cout << "This daughterboard is unrecognized, or there is no TX daughterboard." << std::endl; + return EXIT_FAILURE; + } } - else if(test_tx and usrp->get_usrp_tx_info().get("tx_id") == "Unknown (0xffff)"){ - std::cout << desc << std::endl; - std::cout << "This daughterboard is unrecognized, or there is no TX daughterboard." << std::endl; - return ~0; + + //Don't perform daughterboard validity checks for B200/B210 + if((not is_b2xx) and test_rx){ + std::string rx_dboard_name = usrp->get_usrp_rx_info(chan).get("rx_id"); + if(rx_dboard_name == "Basic RX (0x0001)" or rx_dboard_name == "LF RX (0x000f)"){ + std::cout << desc << std::endl; + std::cout << boost::format("This test does not work with the %s daughterboard.") + % rx_dboard_name << std::endl; + return EXIT_FAILURE; + } + else if(rx_dboard_name == "Unknown (0xffff)"){ + std::cout << desc << std::endl; + std::cout << "This daughterboard is unrecognized, or there is no RX daughterboard." << std::endl; + return EXIT_FAILURE; + } } //Setting clock source @@ -563,12 +433,11 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ 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); + usrp_config = return_usrp_config_string(usrp, chan, test_tx, test_rx, is_b2xx); + if(test_tx) tx_results = coercion_test(usrp, "TX", chan, test_tx_gain, freq_step, gain_step, verbose); + if(test_rx) rx_results = coercion_test(usrp, "RX", chan, test_rx_gain, freq_step, gain_step, verbose); - if(verbose) std::cout << std::endl; - std::cout << usrp_config << std::endl << std::endl; + std::cout << std::endl << usrp_config << std::endl << std::endl; if(test_tx) std::cout << tx_results << std::endl; if(test_tx and test_rx) std::cout << std::endl; if(test_rx) std::cout << rx_results << std::endl; diff --git a/host/examples/test_timed_commands.cpp b/host/examples/test_timed_commands.cpp index 8c6011c68..3da4bc707 100644 --- a/host/examples/test_timed_commands.cpp +++ b/host/examples/test_timed_commands.cpp @@ -139,8 +139,8 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ const size_t num_rx_samps = rx_stream->recv(&buff.front(), buff.size(), md, 1.0); if (md.error_code != uhd::rx_metadata_t::ERROR_CODE_NONE){ throw std::runtime_error(str(boost::format( - "Unexpected error code 0x%x" - ) % md.error_code)); + "Receiver error %s" + ) % md.strerror())); } std::cout << boost::format( " Received packet: %u samples, %u full secs, %f frac secs" diff --git a/host/examples/transport_hammer.cpp b/host/examples/transport_hammer.cpp index 4b949e5bd..3f233b2a5 100644 --- a/host/examples/transport_hammer.cpp +++ b/host/examples/transport_hammer.cpp @@ -88,7 +88,7 @@ void rx_hammer(uhd::usrp::multi_usrp::sptr usrp, const std::string &rx_cpu, uhd: break; default: - std::cerr << "Error code: " << md.error_code << std::endl; + std::cerr << "Receiver error: " << md.strerror() << std::endl; std::cerr << "Unexpected error on recv, continuing..." << std::endl; break; } diff --git a/host/examples/txrx_loopback_to_file.cpp b/host/examples/txrx_loopback_to_file.cpp index 3d3cf1dfc..a62ccd7b2 100644 --- a/host/examples/txrx_loopback_to_file.cpp +++ b/host/examples/txrx_loopback_to_file.cpp @@ -181,8 +181,8 @@ template<typename samp_type> void recv_to_file( } if (md.error_code != uhd::rx_metadata_t::ERROR_CODE_NONE){ throw std::runtime_error(str(boost::format( - "Unexpected error code 0x%x" - ) % md.error_code)); + "Receiver error %s" + ) % md.strerror())); } num_total_samps += num_rx_samps; diff --git a/host/include/uhd/device.hpp b/host/include/uhd/device.hpp index 1090c243c..b54ffc5f7 100644 --- a/host/include/uhd/device.hpp +++ b/host/include/uhd/device.hpp @@ -77,10 +77,18 @@ public: */ static sptr make(const device_addr_t &hint, size_t which = 0); - //! Make a new receive streamer from the streamer arguments + /*! \brief Make a new receive streamer from the streamer arguments + * + * Note: There can always only be one streamer. When calling get_rx_stream() + * a second time, the first streamer must be destroyed beforehand. + */ virtual rx_streamer::sptr get_rx_stream(const stream_args_t &args) = 0; - //! Make a new transmit streamer from the streamer arguments + /*! \brief Make a new transmit streamer from the streamer arguments + * + * Note: There can always only be one streamer. When calling get_tx_stream() + * a second time, the first streamer must be destroyed beforehand. + */ virtual tx_streamer::sptr get_tx_stream(const stream_args_t &args) = 0; //! Get access to the underlying property structure diff --git a/host/include/uhd/transport/nirio/nirio_driver_iface.h b/host/include/uhd/transport/nirio/nirio_driver_iface.h index 46a1146de..5b430b43d 100644 --- a/host/include/uhd/transport/nirio/nirio_driver_iface.h +++ b/host/include/uhd/transport/nirio/nirio_driver_iface.h @@ -1,5 +1,5 @@ // -// Copyright 2013 Ettus Research LLC +// Copyright 2013-2014 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -28,7 +28,7 @@ #pragma warning(disable:4201) // nonstandard extension used : nameless struct/union #include <WinIoCtl.h> #pragma warning(default:4201) -#elif defined(UHD_PLATFORM_MACOS) || defined(UHD_PLATFORM_BSD) +#elif !defined(UHD_PLATFORM_LINUX) #include <IOKit/IOKitLib.h> #endif @@ -445,10 +445,8 @@ static inline void init_syncop_out_params(nirio_syncop_out_params_t& param, void typedef int rio_dev_handle_t; #elif defined(UHD_PLATFORM_WIN32) typedef HANDLE rio_dev_handle_t; -#elif defined(UHD_PLATFORM_MACOS) || defined(UHD_PLATFORM_BSD) - typedef io_connect_t rio_dev_handle_t; #else - #error OS not supported by nirio_driver_iface. + typedef io_connect_t rio_dev_handle_t; #endif static const rio_dev_handle_t INVALID_RIO_HANDLE = ((rio_dev_handle_t)-1); @@ -492,15 +490,13 @@ static const rio_dev_handle_t INVALID_RIO_HANDLE = ((rio_dev_handle_t)-1); bool is_null() { return addr == NULL; } }; -#elif defined(UHD_PLATFORM_MACOS) || defined(UHD_PLATFORM_BSD) +#else struct rio_mmap_t { rio_mmap_t() : addr(NULL) {} void *addr; bool is_null() { return addr == NULL; } }; -#else - #error OS not supported by nirio_driver_iface. #endif nirio_status rio_open( diff --git a/host/include/uhd/transport/nirio/nirio_fifo.h b/host/include/uhd/transport/nirio/nirio_fifo.h index f7abb396f..fc1de245d 100644 --- a/host/include/uhd/transport/nirio/nirio_fifo.h +++ b/host/include/uhd/transport/nirio/nirio_fifo.h @@ -104,16 +104,20 @@ public: uint32_t& num_remaining); private: //Methods - bool _is_initialized(); datatype_info_t _get_datatype_info(); nirio_status _get_transfer_count(uint64_t& transfer_count); nirio_status _ensure_transfer_completed(uint32_t timeout_ms); private: //Members + enum fifo_state_t { + UNMAPPED, MAPPED, STARTED + }; + std::string _name; fifo_direction_t _fifo_direction; uint32_t _fifo_channel; datatype_info_t _datatype_info; + fifo_state_t _state; size_t _acquired_pending; nirio_driver_iface::rio_mmap_t _mem_map; boost::recursive_mutex _mutex; diff --git a/host/include/uhd/transport/nirio/nirio_fifo.ipp b/host/include/uhd/transport/nirio/nirio_fifo.ipp index 80a0c2a89..437e3a1fc 100644 --- a/host/include/uhd/transport/nirio/nirio_fifo.ipp +++ b/host/include/uhd/transport/nirio/nirio_fifo.ipp @@ -31,6 +31,7 @@ nirio_fifo<data_t>::nirio_fifo( _fifo_direction(direction), _fifo_channel(fifo_instance), _datatype_info(_get_datatype_info()), + _state(UNMAPPED), _acquired_pending(0), _mem_map(), _riok_proxy_ptr(&riok_proxy), @@ -61,28 +62,37 @@ nirio_status nirio_fifo<data_t>::initialize( if (!_riok_proxy_ptr) return NiRio_Status_ResourceNotInitialized; boost::unique_lock<boost::recursive_mutex> lock(_mutex); - nirio_driver_iface::nirio_syncop_in_params_t in = {}; - nirio_driver_iface::nirio_syncop_out_params_t out = {}; + if (_state == UNMAPPED) { + nirio_driver_iface::nirio_syncop_in_params_t in = {}; + nirio_driver_iface::nirio_syncop_out_params_t out = {}; - //Forcefully stop the fifo if it is running - in.function = nirio_driver_iface::NIRIO_FUNC::FIFO; - in.subfunction = nirio_driver_iface::NIRIO_FIFO::STOP; - status = _riok_proxy_ptr->sync_operation(&in, sizeof(in), &out, sizeof(out)); + //Forcefully stop the fifo if it is running + in.function = nirio_driver_iface::NIRIO_FUNC::FIFO; + in.subfunction = nirio_driver_iface::NIRIO_FIFO::STOP; + in.params.fifo.channel = _fifo_channel; + _riok_proxy_ptr->sync_operation(&in, sizeof(in), &out, sizeof(out)); //Cleanup operation. Ignore status. - in.function = nirio_driver_iface::NIRIO_FUNC::FIFO; - in.subfunction = nirio_driver_iface::NIRIO_FIFO::CONFIGURE; + //Configure the FIFO now that we know it is stopped + in.function = nirio_driver_iface::NIRIO_FUNC::FIFO; + in.subfunction = nirio_driver_iface::NIRIO_FIFO::CONFIGURE; + in.params.fifo.channel = _fifo_channel; + in.params.fifo.op.config.requestedDepth = static_cast<uint32_t>(requested_depth); + in.params.fifo.op.config.requiresActuals = 1; + status = _riok_proxy_ptr->sync_operation(&in, sizeof(in), &out, sizeof(out)); - in.params.fifo.channel = _fifo_channel; - in.params.fifo.op.config.requestedDepth = static_cast<uint32_t>(requested_depth); - in.params.fifo.op.config.requiresActuals = 1; + if (nirio_status_fatal(status)) return status; - status = _riok_proxy_ptr->sync_operation(&in, sizeof(in), &out, sizeof(out)); - if (nirio_status_fatal(status)) return status; + actual_depth = out.params.fifo.op.config.actualDepth; + actual_size = out.params.fifo.op.config.actualSize; - actual_depth = out.params.fifo.op.config.actualDepth; - actual_size = out.params.fifo.op.config.actualSize; + status = _riok_proxy_ptr->map_fifo_memory(_fifo_channel, actual_size, _mem_map); - status = _riok_proxy_ptr->map_fifo_memory(_fifo_channel, actual_size, _mem_map); + if (nirio_status_not_fatal(status)) { + _state = MAPPED; + } + } else { + status = NiRio_Status_SoftwareFault; + } return status; } @@ -90,9 +100,13 @@ template <typename data_t> void nirio_fifo<data_t>::finalize() { boost::unique_lock<boost::recursive_mutex> lock(_mutex); - if (!_mem_map.is_null()) { - stop(); + + //If the FIFO is started, the stop will change the state to MAPPED. + stop(); + + if (_state == MAPPED) { _riok_proxy_ptr->unmap_fifo_memory(_mem_map); + _state = UNMAPPED; //Assume teardown succeeded } } @@ -104,16 +118,25 @@ nirio_status nirio_fifo<data_t>::start() boost::unique_lock<boost::recursive_mutex> lock(_mutex); - nirio_driver_iface::nirio_syncop_in_params_t in = {}; - nirio_driver_iface::nirio_syncop_out_params_t out = {}; + if (_state == STARTED) { + //Do nothing. Already started. + } else if (_state == MAPPED) { + nirio_driver_iface::nirio_syncop_in_params_t in = {}; + nirio_driver_iface::nirio_syncop_out_params_t out = {}; - in.function = nirio_driver_iface::NIRIO_FUNC::FIFO; - in.subfunction = nirio_driver_iface::NIRIO_FIFO::START; + in.function = nirio_driver_iface::NIRIO_FUNC::FIFO; + in.subfunction = nirio_driver_iface::NIRIO_FIFO::START; - status = _riok_proxy_ptr->sync_operation(&in, sizeof(in), &out, sizeof(out)); - if (nirio_status_not_fatal(status)) { - _acquired_pending = 0; - _expected_xfer_count = 0; + in.params.fifo.channel = _fifo_channel; + + status = _riok_proxy_ptr->sync_operation(&in, sizeof(in), &out, sizeof(out)); + if (nirio_status_not_fatal(status)) { + _state = STARTED; + _acquired_pending = 0; + _expected_xfer_count = 0; + } + } else { + status = NiRio_Status_ResourceNotInitialized; } return status; } @@ -125,15 +148,22 @@ nirio_status nirio_fifo<data_t>::stop() if (!_riok_proxy_ptr) return NiRio_Status_ResourceNotInitialized; boost::unique_lock<boost::recursive_mutex> lock(_mutex); - if (_acquired_pending > 0) release(_acquired_pending); - nirio_driver_iface::nirio_syncop_in_params_t in = {}; - nirio_driver_iface::nirio_syncop_out_params_t out = {}; + if (_state == STARTED) { + if (_acquired_pending > 0) release(_acquired_pending); + + nirio_driver_iface::nirio_syncop_in_params_t in = {}; + nirio_driver_iface::nirio_syncop_out_params_t out = {}; - in.function = nirio_driver_iface::NIRIO_FUNC::FIFO; - in.subfunction = nirio_driver_iface::NIRIO_FIFO::STOP; + in.function = nirio_driver_iface::NIRIO_FUNC::FIFO; + in.subfunction = nirio_driver_iface::NIRIO_FIFO::STOP; - status = _riok_proxy_ptr->sync_operation(&in, sizeof(in), &out, sizeof(out)); + in.params.fifo.channel = _fifo_channel; + + status = _riok_proxy_ptr->sync_operation(&in, sizeof(in), &out, sizeof(out)); + + _state = MAPPED; //Assume teardown succeeded + } return status; } @@ -151,36 +181,40 @@ nirio_status nirio_fifo<data_t>::acquire( boost::unique_lock<boost::recursive_mutex> lock(_mutex); - nirio_driver_iface::nirio_syncop_in_params_t in = {}; - uint32_t stuffed[2]; - nirio_driver_iface::nirio_syncop_out_params_t out = {}; - init_syncop_out_params(out, stuffed, sizeof(stuffed)); - - in.function = nirio_driver_iface::NIRIO_FUNC::FIFO; - in.subfunction = nirio_driver_iface::NIRIO_FIFO::WAIT; - - in.params.fifo.channel = _fifo_channel; - in.params.fifo.op.wait.elementsRequested = static_cast<uint32_t>(elements_requested); - in.params.fifo.op.wait.scalarType = static_cast<uint32_t>(_datatype_info.scalar_type); - in.params.fifo.op.wait.bitWidth = _datatype_info.width * 8; - in.params.fifo.op.wait.output = _fifo_direction == OUTPUT_FIFO; - in.params.fifo.op.wait.timeout = timeout; - - status = _riok_proxy_ptr->sync_operation(&in, sizeof(in), &out, sizeof(out)); - - if (nirio_status_not_fatal(status)) { - elements = static_cast<data_t*>(out.params.fifo.op.wait.elements.pointer); - elements_acquired = stuffed[0]; - elements_remaining = stuffed[1]; - _acquired_pending = elements_acquired; - - if (UHD_NIRIO_RX_FIFO_XFER_CHECK_EN && - _riok_proxy_ptr->get_rio_quirks().rx_fifo_xfer_check_en() && - get_direction() == INPUT_FIFO - ) { - _expected_xfer_count += static_cast<uint64_t>(elements_requested * sizeof(data_t)); - status = _ensure_transfer_completed(timeout); + if (_state == STARTED) { + nirio_driver_iface::nirio_syncop_in_params_t in = {}; + uint32_t stuffed[2]; + nirio_driver_iface::nirio_syncop_out_params_t out = {}; + init_syncop_out_params(out, stuffed, sizeof(stuffed)); + + in.function = nirio_driver_iface::NIRIO_FUNC::FIFO; + in.subfunction = nirio_driver_iface::NIRIO_FIFO::WAIT; + + in.params.fifo.channel = _fifo_channel; + in.params.fifo.op.wait.elementsRequested = static_cast<uint32_t>(elements_requested); + in.params.fifo.op.wait.scalarType = static_cast<uint32_t>(_datatype_info.scalar_type); + in.params.fifo.op.wait.bitWidth = _datatype_info.width * 8; + in.params.fifo.op.wait.output = _fifo_direction == OUTPUT_FIFO; + in.params.fifo.op.wait.timeout = timeout; + + status = _riok_proxy_ptr->sync_operation(&in, sizeof(in), &out, sizeof(out)); + + if (nirio_status_not_fatal(status)) { + elements = static_cast<data_t*>(out.params.fifo.op.wait.elements.pointer); + elements_acquired = stuffed[0]; + elements_remaining = stuffed[1]; + _acquired_pending = elements_acquired; + + if (UHD_NIRIO_RX_FIFO_XFER_CHECK_EN && + _riok_proxy_ptr->get_rio_quirks().rx_fifo_xfer_check_en() && + get_direction() == INPUT_FIFO + ) { + _expected_xfer_count += static_cast<uint64_t>(elements_requested * sizeof(data_t)); + status = _ensure_transfer_completed(timeout); + } } + } else { + status = NiRio_Status_ResourceNotInitialized; } return status; @@ -194,17 +228,21 @@ nirio_status nirio_fifo<data_t>::release(const size_t elements) boost::unique_lock<boost::recursive_mutex> lock(_mutex); - nirio_driver_iface::nirio_syncop_in_params_t in = {}; - nirio_driver_iface::nirio_syncop_out_params_t out = {}; + if (_state == STARTED) { + nirio_driver_iface::nirio_syncop_in_params_t in = {}; + nirio_driver_iface::nirio_syncop_out_params_t out = {}; - in.function = nirio_driver_iface::NIRIO_FUNC::FIFO; - in.subfunction = nirio_driver_iface::NIRIO_FIFO::GRANT; + in.function = nirio_driver_iface::NIRIO_FUNC::FIFO; + in.subfunction = nirio_driver_iface::NIRIO_FIFO::GRANT; - in.params.fifo.channel = _fifo_channel; - in.params.fifo.op.grant.elements = static_cast<uint32_t>(elements); + in.params.fifo.channel = _fifo_channel; + in.params.fifo.op.grant.elements = static_cast<uint32_t>(elements); - status = _riok_proxy_ptr->sync_operation(&in, sizeof(in), &out, sizeof(out)); - _acquired_pending = 0; + status = _riok_proxy_ptr->sync_operation(&in, sizeof(in), &out, sizeof(out)); + _acquired_pending = 0; + } else { + status = NiRio_Status_ResourceNotInitialized; + } return status; } @@ -222,23 +260,27 @@ nirio_status nirio_fifo<data_t>::read( boost::unique_lock<boost::recursive_mutex> lock(_mutex); - nirio_driver_iface::nirio_syncop_in_params_t in = {}; - nirio_driver_iface::nirio_syncop_out_params_t out = {}; - init_syncop_out_params(out, buf, num_elements * _datatype_info.width); + if (_state == STARTED) { + nirio_driver_iface::nirio_syncop_in_params_t in = {}; + nirio_driver_iface::nirio_syncop_out_params_t out = {}; + init_syncop_out_params(out, buf, num_elements * _datatype_info.width); - in.function = nirio_driver_iface::NIRIO_FUNC::FIFO; - in.subfunction = nirio_driver_iface::NIRIO_FIFO::READ; + in.function = nirio_driver_iface::NIRIO_FUNC::FIFO; + in.subfunction = nirio_driver_iface::NIRIO_FIFO::READ; - in.params.fifo.channel = _fifo_channel; - in.params.fifo.op.readWithDataType.timeout = timeout; - in.params.fifo.op.readWithDataType.scalarType = static_cast<uint32_t>(_datatype_info.scalar_type); - in.params.fifo.op.readWithDataType.bitWidth = _datatype_info.width * 8; + in.params.fifo.channel = _fifo_channel; + in.params.fifo.op.readWithDataType.timeout = timeout; + in.params.fifo.op.readWithDataType.scalarType = static_cast<uint32_t>(_datatype_info.scalar_type); + in.params.fifo.op.readWithDataType.bitWidth = _datatype_info.width * 8; - status = _riok_proxy_ptr->sync_operation(&in, sizeof(in), &out, sizeof(out)); + status = _riok_proxy_ptr->sync_operation(&in, sizeof(in), &out, sizeof(out)); - if (nirio_status_not_fatal(status) || status == NiRio_Status_FifoTimeout) { - num_read = out.params.fifo.op.read.numberRead; - num_remaining = out.params.fifo.op.read.numberRemaining; + if (nirio_status_not_fatal(status) || status == NiRio_Status_FifoTimeout) { + num_read = out.params.fifo.op.read.numberRead; + num_remaining = out.params.fifo.op.read.numberRemaining; + } + } else { + status = NiRio_Status_ResourceNotInitialized; } return status; @@ -256,22 +298,26 @@ nirio_status nirio_fifo<data_t>::write( boost::unique_lock<boost::recursive_mutex> lock(_mutex); - nirio_driver_iface::nirio_syncop_in_params_t in = {}; - init_syncop_in_params(in, buf, num_elements * _datatype_info.width); - nirio_driver_iface::nirio_syncop_out_params_t out = {}; + if (_state == STARTED) { + nirio_driver_iface::nirio_syncop_in_params_t in = {}; + init_syncop_in_params(in, buf, num_elements * _datatype_info.width); + nirio_driver_iface::nirio_syncop_out_params_t out = {}; - in.function = nirio_driver_iface::NIRIO_FUNC::FIFO; - in.subfunction = nirio_driver_iface::NIRIO_FIFO::WRITE; + in.function = nirio_driver_iface::NIRIO_FUNC::FIFO; + in.subfunction = nirio_driver_iface::NIRIO_FIFO::WRITE; - in.params.fifo.channel = _fifo_channel; - in.params.fifo.op.writeWithDataType.timeout = timeout; - in.params.fifo.op.readWithDataType.scalarType = static_cast<uint32_t>(_datatype_info.scalar_type); - in.params.fifo.op.readWithDataType.bitWidth = _datatype_info.width * 8; + in.params.fifo.channel = _fifo_channel; + in.params.fifo.op.writeWithDataType.timeout = timeout; + in.params.fifo.op.readWithDataType.scalarType = static_cast<uint32_t>(_datatype_info.scalar_type); + in.params.fifo.op.readWithDataType.bitWidth = _datatype_info.width * 8; - status = _riok_proxy_ptr->sync_operation(&in, sizeof(in), &out, sizeof(out)); + status = _riok_proxy_ptr->sync_operation(&in, sizeof(in), &out, sizeof(out)); - if (nirio_status_not_fatal(status) || status == NiRio_Status_FifoTimeout) { - num_remaining = out.params.fifo.op.write.numberRemaining; + if (nirio_status_not_fatal(status) || status == NiRio_Status_FifoTimeout) { + num_remaining = out.params.fifo.op.write.numberRemaining; + } + } else { + status = NiRio_Status_ResourceNotInitialized; } return status; diff --git a/host/include/uhd/transport/nirio/nirio_quirks.h b/host/include/uhd/transport/nirio/nirio_quirks.h index 326eeeb8c..ed4f72e7f 100644 --- a/host/include/uhd/transport/nirio/nirio_quirks.h +++ b/host/include/uhd/transport/nirio/nirio_quirks.h @@ -24,8 +24,8 @@ //Quirk#1: We need to verify RX zero-copy data transfers from the RIO // driver if we are in full duplex mode. -// This option allows disabling this quirk by compiling it out. -#define UHD_NIRIO_RX_FIFO_XFER_CHECK_EN 1 +// This option allows enabling this quirk. +#define UHD_NIRIO_RX_FIFO_XFER_CHECK_EN 0 namespace uhd { namespace niusrprio { diff --git a/host/include/uhd/transport/nirio/niriok_proxy.h b/host/include/uhd/transport/nirio/niriok_proxy.h index a6b6183d1..ca6a4ba02 100644 --- a/host/include/uhd/transport/nirio/niriok_proxy.h +++ b/host/include/uhd/transport/nirio/niriok_proxy.h @@ -126,6 +126,8 @@ namespace uhd { namespace niusrprio nirio_status unmap_fifo_memory( nirio_driver_iface::rio_mmap_t& map); + nirio_status stop_all_fifos(); + nirio_quirks& get_rio_quirks() { return _rio_quirks; } diff --git a/host/include/uhd/transport/nirio/niusrprio_session.h b/host/include/uhd/transport/nirio/niusrprio_session.h index c9a61ae76..c84bc75d0 100644 --- a/host/include/uhd/transport/nirio/niusrprio_session.h +++ b/host/include/uhd/transport/nirio/niusrprio_session.h @@ -37,31 +37,31 @@ public: typedef uhd::usrprio_rpc::usrprio_device_info device_info; typedef uhd::usrprio_rpc::usrprio_device_info_vtr device_info_vtr; - static nirio_status enumerate( + static nirio_status enumerate( const std::string& rpc_port_name, device_info_vtr& device_info_vtr); - niusrprio_session( + niusrprio_session( const std::string& resource_name, const std::string& port_name); - virtual ~niusrprio_session(); + virtual ~niusrprio_session(); - nirio_status open( + nirio_status open( nifpga_lvbitx::sptr lvbitx, bool force_download = false); - void close(bool skip_reset = false); + void close(bool skip_reset = false); - nirio_status reset(); + nirio_status reset(); - template<typename data_t> - nirio_status create_tx_fifo( - const char* fifo_name, - boost::shared_ptr< nirio_fifo<data_t> >& fifo) - { + template<typename data_t> + nirio_status create_tx_fifo( + const char* fifo_name, + boost::shared_ptr< nirio_fifo<data_t> >& fifo) + { if (!_session_open) return NiRio_Status_ResourceNotInitialized; return _resource_manager.create_tx_fifo(fifo_name, fifo); - } + } template<typename data_t> nirio_status create_tx_fifo( @@ -73,13 +73,13 @@ public: } template<typename data_t> - nirio_status create_rx_fifo( - const char* fifo_name, - boost::shared_ptr< nirio_fifo<data_t> >& fifo) - { + nirio_status create_rx_fifo( + const char* fifo_name, + boost::shared_ptr< nirio_fifo<data_t> >& fifo) + { if (!_session_open) return NiRio_Status_ResourceNotInitialized; return _resource_manager.create_rx_fifo(fifo_name, fifo); - } + } template<typename data_t> nirio_status create_rx_fifo( @@ -90,9 +90,9 @@ public: return create_rx_fifo(_lvbitx->get_input_fifo_names()[fifo_instance], fifo); } - niriok_proxy& get_kernel_proxy() { - return _riok_proxy; - } + niriok_proxy& get_kernel_proxy() { + return _riok_proxy; + } nirio_status download_bitstream_to_flash(const std::string& bitstream_path); @@ -102,21 +102,22 @@ public: const std::string& rpc_port_name); private: - nirio_status _verify_signature(); - std::string _read_bitstream_checksum(); - nirio_status _write_bitstream_checksum(const std::string& checksum); - nirio_status _wait_for_device_available(); - - std::string _resource_name; - nifpga_lvbitx::sptr _lvbitx; - std::string _interface_path; - bool _session_open; - niriok_proxy _riok_proxy; - nirio_resource_manager _resource_manager; - usrprio_rpc::usrprio_rpc_client _rpc_client; - boost::recursive_mutex _session_mutex; - - static const uint32_t SESSION_LOCK_TIMEOUT_IN_MS = 3000; + nirio_status _verify_signature(); + std::string _read_bitstream_checksum(); + nirio_status _write_bitstream_checksum(const std::string& checksum); + nirio_status _ensure_fpga_ready(); + + std::string _resource_name; + nifpga_lvbitx::sptr _lvbitx; + std::string _interface_path; + bool _session_open; + niriok_proxy _riok_proxy; + nirio_resource_manager _resource_manager; + usrprio_rpc::usrprio_rpc_client _rpc_client; + boost::recursive_mutex _session_mutex; + + static const uint32_t FPGA_READY_TIMEOUT_IN_MS = 1000; + static const uint32_t SESSION_LOCK_TIMEOUT_IN_MS = 3000; static const uint32_t SESSION_LOCK_RETRY_INT_IN_MS = 500; }; diff --git a/host/include/uhd/types/metadata.hpp b/host/include/uhd/types/metadata.hpp index 6a79720d0..51a2b7c43 100644 --- a/host/include/uhd/types/metadata.hpp +++ b/host/include/uhd/types/metadata.hpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2012 Ettus Research LLC +// Copyright 2010-2012,2014 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -117,6 +117,20 @@ namespace uhd{ //! Out of sequence. The transport has either dropped a packet or received data out of order. bool out_of_sequence; + + /*! + * Convert a rx_metadata_t into a pretty print string. + * + * \param compact Set to false for a more verbose output. + * \return a printable string representing the metadata. + */ + std::string to_pp_string(bool compact=true) const; + + /*! + * Similar to C's strerror() function, creates a std::string describing the error code. + * \return a printable string representing the error. + */ + std::string strerror(void) const; }; /*! diff --git a/host/include/uhd/usrp/multi_usrp.hpp b/host/include/uhd/usrp/multi_usrp.hpp index 5b4991202..883e4da3d 100644 --- a/host/include/uhd/usrp/multi_usrp.hpp +++ b/host/include/uhd/usrp/multi_usrp.hpp @@ -118,15 +118,11 @@ public: */ virtual device::sptr get_device(void) = 0; - //! Convenience method to get a RX streamer - rx_streamer::sptr get_rx_stream(const stream_args_t &args){ - return this->get_device()->get_rx_stream(args); - } + //! Convenience method to get a RX streamer. See also uhd::device::get_rx_stream(). + virtual rx_streamer::sptr get_rx_stream(const stream_args_t &args) = 0; - //! Convenience method to get a TX streamer - tx_streamer::sptr get_tx_stream(const stream_args_t &args){ - return this->get_device()->get_tx_stream(args); - } + //! Convenience method to get a TX streamer. See also uhd::device::get_rx_stream(). + virtual tx_streamer::sptr get_tx_stream(const stream_args_t &args) = 0; /*! * Returns identifying information about this USRP's configuration. diff --git a/host/include/uhd/utils/CMakeLists.txt b/host/include/uhd/utils/CMakeLists.txt index c0991b3ce..e9633286f 100644 --- a/host/include/uhd/utils/CMakeLists.txt +++ b/host/include/uhd/utils/CMakeLists.txt @@ -22,6 +22,7 @@ UHD_INSTALL(FILES atomic.hpp byteswap.hpp byteswap.ipp + cast.hpp csv.hpp gain_group.hpp images.hpp diff --git a/host/include/uhd/utils/cast.hpp b/host/include/uhd/utils/cast.hpp new file mode 100644 index 000000000..9db92c526 --- /dev/null +++ b/host/include/uhd/utils/cast.hpp @@ -0,0 +1,43 @@ +// +// Copyright 2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_UHD_UTILS_CAST_HPP +#define INCLUDED_UHD_UTILS_CAST_HPP + +#include <uhd/config.hpp> +#include <string> +#include <sstream> + +namespace uhd{ namespace cast{ + //! Convert a hexadecimal string into a value. + // + // Example: + // boost::uint16_t x = hexstr_cast<boost::uint16_t>("0xDEADBEEF"); + // Uses stringstream. + template<typename T> inline T hexstr_cast(const std::string &in) + { + T x; + std::stringstream ss; + ss << std::hex << in; + ss >> x; + return x; + } + +}} //namespace uhd::cast + +#endif /* INCLUDED_UHD_UTILS_CAST_HPP */ + diff --git a/host/include/uhd/utils/msg_task.hpp b/host/include/uhd/utils/msg_task.hpp index 40ee65cb1..21c47a240 100644 --- a/host/include/uhd/utils/msg_task.hpp +++ b/host/include/uhd/utils/msg_task.hpp @@ -1,5 +1,5 @@ // -// Copyright 2011-2013 Ettus Research LLC +// Copyright 2011-2014 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -48,7 +48,7 @@ namespace uhd{ memcpy(&v.front(), p, n); return v; } - return std::vector<uint8_t>(); + return std::vector<boost::uint8_t>(); } /*! diff --git a/host/include/uhd/version.hpp b/host/include/uhd/version.hpp index 1bb89dd84..81940f078 100644 --- a/host/include/uhd/version.hpp +++ b/host/include/uhd/version.hpp @@ -27,7 +27,7 @@ * The format is oldest API compatible release - ABI compat number. * The compatibility number allows pre-release ABI to be versioned. */ -#define UHD_VERSION_ABI_STRING "3.7.0-0" +#define UHD_VERSION_ABI_STRING "3.7.1-0" namespace uhd{ diff --git a/host/lib/convert/convert_impl.cpp b/host/lib/convert/convert_impl.cpp index dc7f8f9dc..c7907ed83 100644 --- a/host/lib/convert/convert_impl.cpp +++ b/host/lib/convert/convert_impl.cpp @@ -134,6 +134,7 @@ UHD_STATIC_BLOCK(convert_register_item_sizes){ convert::register_bytes_per_item("sc64", sizeof(std::complex<boost::int64_t>)); convert::register_bytes_per_item("sc32", sizeof(std::complex<boost::int32_t>)); convert::register_bytes_per_item("sc16", sizeof(std::complex<boost::int16_t>)); + convert::register_bytes_per_item("sc12", 3 * sizeof(std::complex<boost::int8_t>)); convert::register_bytes_per_item("sc8", sizeof(std::complex<boost::int8_t>)); //register standard real types diff --git a/host/lib/transport/nirio/CMakeLists.txt b/host/lib/transport/nirio/CMakeLists.txt index 6a33da6c5..5f12e91df 100644 --- a/host/lib/transport/nirio/CMakeLists.txt +++ b/host/lib/transport/nirio/CMakeLists.txt @@ -39,10 +39,8 @@ LIBUHD_APPEND_SOURCES( IF(WIN32) LIBUHD_APPEND_SOURCES(${CMAKE_CURRENT_SOURCE_DIR}/nirio_driver_iface_win.cpp) -ELSE(WIN32) - IF(APPLE) - LIBUHD_APPEND_SOURCES(${CMAKE_CURRENT_SOURCE_DIR}/nirio_driver_iface_macos.cpp) - ELSE(APPLE) - LIBUHD_APPEND_SOURCES(${CMAKE_CURRENT_SOURCE_DIR}/nirio_driver_iface_linux.cpp) - ENDIF(APPLE) +ELSEIF(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") #Built-in variable encompasses all UNIX-like systems + LIBUHD_APPEND_SOURCES(${CMAKE_CURRENT_SOURCE_DIR}/nirio_driver_iface_linux.cpp) +ELSE() + LIBUHD_APPEND_SOURCES(${CMAKE_CURRENT_SOURCE_DIR}/nirio_driver_iface_unsupported.cpp) ENDIF(WIN32) diff --git a/host/lib/transport/nirio/nifpga_lvbitx.cpp b/host/lib/transport/nirio/nifpga_lvbitx.cpp index 289a44d4a..b87d87a8d 100644 --- a/host/lib/transport/nirio/nifpga_lvbitx.cpp +++ b/host/lib/transport/nirio/nifpga_lvbitx.cpp @@ -16,6 +16,7 @@ // #include <uhd/transport/nirio/nifpga_lvbitx.h> +#include <cstdlib> #include <string> #include <iostream> #include <fstream> @@ -109,6 +110,21 @@ std::string nifpga_lvbitx::_get_fpga_images_dir(const std::string search_paths) std::vector<std::string> search_path_vtr; boost::split(search_path_vtr, search_paths, boost::is_any_of(",")); + // + // Add the value of the UHD_IMAGES_DIR environment variable to the list of + // directories searched for a LVBITX image. + // + char* uhd_images_dir; +#ifdef UHD_PLATFORM_WIN32 + size_t len; + errno_t err = _dupenv_s(&uhd_images_dir, &len, "UHD_IMAGES_DIR"); + if(not err and uhd_images_dir != NULL) search_path_vtr.push_back(std::string(uhd_images_dir)); + free(uhd_images_dir); +#else + uhd_images_dir = getenv("UHD_IMAGES_DIR"); + if(uhd_images_dir != NULL) search_path_vtr.push_back(std::string(uhd_images_dir)); +#endif + std::string lvbitx_dir; //Traverse through the list of search paths. Priority: lexical BOOST_FOREACH(std::string& search_path, search_path_vtr) { diff --git a/host/lib/transport/nirio/nirio_driver_iface_macos.cpp b/host/lib/transport/nirio/nirio_driver_iface_unsupported.cpp index 1a1142525..1a1142525 100644 --- a/host/lib/transport/nirio/nirio_driver_iface_macos.cpp +++ b/host/lib/transport/nirio/nirio_driver_iface_unsupported.cpp diff --git a/host/lib/transport/nirio/niriok_proxy.cpp b/host/lib/transport/nirio/niriok_proxy.cpp index 031623c9a..ac8faf0a4 100644 --- a/host/lib/transport/nirio/niriok_proxy.cpp +++ b/host/lib/transport/nirio/niriok_proxy.cpp @@ -293,6 +293,16 @@ namespace uhd { namespace niusrprio { return nirio_driver_iface::rio_munmap(map); } + + nirio_status niriok_proxy::stop_all_fifos() + { + nirio_driver_iface::nirio_syncop_in_params_t in = {}; + nirio_driver_iface::nirio_syncop_out_params_t out = {}; + + in.function = nirio_driver_iface::NIRIO_FUNC::FIFO_STOP_ALL; + + return sync_operation(&in, sizeof(in), &out, sizeof(out)); + } }} #ifdef __GNUC__ diff --git a/host/lib/transport/nirio/niusrprio_session.cpp b/host/lib/transport/nirio/niusrprio_session.cpp index a07bc4fdf..97d764736 100644 --- a/host/lib/transport/nirio/niusrprio_session.cpp +++ b/host/lib/transport/nirio/niusrprio_session.cpp @@ -77,6 +77,8 @@ nirio_status niusrprio_session::open( std::string lvbitx_checksum(_lvbitx->get_bitstream_checksum()); boost::uint16_t download_fpga = (force_download || (_read_bitstream_checksum() != lvbitx_checksum)) ? 1 : 0; + nirio_status_chain(_ensure_fpga_ready(), status); + nirio_status_chain(_rpc_client.niusrprio_open_session( _resource_name, bitfile_path, signature, download_fpga), status); _session_open = nirio_status_not_fatal(status); @@ -196,4 +198,59 @@ nirio_status niusrprio_session::_write_bitstream_checksum(const std::string& che return status; } +nirio_status niusrprio_session::_ensure_fpga_ready() +{ + nirio_status status = NiRio_Status_Success; + niriok_scoped_addr_space(_riok_proxy, BUS_INTERFACE, status); + + //Verify that the Ettus FPGA loaded in the device. This may not be true if the + //user is switching to UHD after using LabVIEW FPGA. In that case skip this check. + boost::uint32_t pcie_fpga_signature = 0; + nirio_status_chain(_riok_proxy.peek(FPGA_PCIE_SIG_REG, pcie_fpga_signature), status); + //@TODO: Remove X300 specific constants for future products + if (pcie_fpga_signature != FPGA_X3xx_SIG_VALUE) { + return status; + } + + boost::uint32_t reg_data = 0xffffffff; + nirio_status_chain(_riok_proxy.peek(FPGA_STATUS_REG, reg_data), status); + if (nirio_status_not_fatal(status) && (reg_data & FPGA_STATUS_DMA_ACTIVE_MASK)) + { + //In case this session was re-initialized *immediately* after the previous + //there is a small chance that the server is still finishing up cleaning up + //the DMA FIFOs. We currently don't have any feedback from the driver regarding + //this state so just wait. + boost::this_thread::sleep(boost::posix_time::milliseconds(FPGA_READY_TIMEOUT_IN_MS)); + + //Disable all FIFOs in the FPGA + for (size_t i = 0; i < _lvbitx->get_input_fifo_count(); i++) { + _riok_proxy.poke(PCIE_RX_DMA_REG(DMA_CTRL_STATUS_REG, i), DMA_CTRL_DISABLED); + } + for (size_t i = 0; i < _lvbitx->get_output_fifo_count(); i++) { + _riok_proxy.poke(PCIE_TX_DMA_REG(DMA_CTRL_STATUS_REG, i), DMA_CTRL_DISABLED); + } + + //Disable all FIFOs in the kernel driver + _riok_proxy.stop_all_fifos(); + + boost::posix_time::ptime start_time = boost::posix_time::microsec_clock::local_time(); + boost::posix_time::time_duration elapsed; + do { + boost::this_thread::sleep(boost::posix_time::milliseconds(10)); //Avoid flooding the bus + elapsed = boost::posix_time::microsec_clock::local_time() - start_time; + nirio_status_chain(_riok_proxy.peek(FPGA_STATUS_REG, reg_data), status); + } while ( + nirio_status_not_fatal(status) && + (reg_data & FPGA_STATUS_DMA_ACTIVE_MASK) && + elapsed.total_milliseconds() < FPGA_READY_TIMEOUT_IN_MS); + + nirio_status_chain(_riok_proxy.peek(FPGA_STATUS_REG, reg_data), status); + if (nirio_status_not_fatal(status) && (reg_data & FPGA_STATUS_DMA_ACTIVE_MASK)) { + return NiRio_Status_FifoReserved; + } + } + + return status; +} + }} diff --git a/host/lib/transport/nirio/rpc/rpc_client.cpp b/host/lib/transport/nirio/rpc/rpc_client.cpp index f8dc26b50..0c4b8fe3c 100644 --- a/host/lib/transport/nirio/rpc/rpc_client.cpp +++ b/host/lib/transport/nirio/rpc/rpc_client.cpp @@ -104,6 +104,11 @@ rpc_client::rpc_client ( } catch (boost::exception&) { UHD_LOG << "rpc_client connection request cancelled/aborted." << std::endl; _exec_err.assign(boost::asio::error::connection_aborted, boost::asio::error::get_system_category()); +#if BOOST_VERSION < 104700 + } catch (std::exception& e) { + UHD_LOG << "rpc_client connection error: " << e.what() << std::endl; + _exec_err.assign(boost::asio::error::connection_aborted, boost::asio::error::get_system_category()); +#endif } } diff --git a/host/lib/transport/nirio_zero_copy.cpp b/host/lib/transport/nirio_zero_copy.cpp index 7b1e32fe0..3bb822720 100644 --- a/host/lib/transport/nirio_zero_copy.cpp +++ b/host/lib/transport/nirio_zero_copy.cpp @@ -18,13 +18,13 @@ #include <uhd/transport/nirio_zero_copy.hpp> #include <stdio.h> #include <uhd/transport/nirio/nirio_fifo.h> -#include <uhd/transport/nirio/nirio_fifo.h> #include <uhd/transport/buffer_pool.hpp> #include <uhd/utils/msg.hpp> #include <uhd/utils/log.hpp> #include <uhd/utils/atomic.hpp> #include <boost/format.hpp> #include <boost/make_shared.hpp> +#include <boost/date_time/posix_time/posix_time.hpp> #include <boost/thread/thread.hpp> //sleep #include <vector> #include <algorithm> // std::max @@ -144,6 +144,12 @@ public: nirio_status status = 0; size_t actual_depth = 0, actual_size = 0; + //Disable DMA streams in case last shutdown was unclean (cleanup, so don't status chain) + _proxy().poke(PCIE_TX_DMA_REG(DMA_CTRL_STATUS_REG, _fifo_instance), DMA_CTRL_DISABLED); + _proxy().poke(PCIE_RX_DMA_REG(DMA_CTRL_STATUS_REG, _fifo_instance), DMA_CTRL_DISABLED); + + _wait_until_stream_ready(); + //Configure frame width nirio_status_chain( _proxy().poke(PCIE_TX_DMA_REG(DMA_FRAME_SIZE_REG, _fifo_instance), @@ -153,14 +159,14 @@ public: _proxy().poke(PCIE_RX_DMA_REG(DMA_FRAME_SIZE_REG, _fifo_instance), static_cast<uint32_t>(_xport_params.recv_frame_size/sizeof(fifo_data_t))), status); - //Config 32-bit word flipping and Reset DMA streams + //Config 32-bit word flipping and enable DMA streams nirio_status_chain( _proxy().poke(PCIE_TX_DMA_REG(DMA_CTRL_STATUS_REG, _fifo_instance), - DMA_CTRL_SW_BUF_U32 | DMA_CTRL_RESET), + DMA_CTRL_SW_BUF_U32 | DMA_CTRL_ENABLED), status); nirio_status_chain( _proxy().poke(PCIE_RX_DMA_REG(DMA_CTRL_STATUS_REG, _fifo_instance), - DMA_CTRL_SW_BUF_U32 | DMA_CTRL_RESET), + DMA_CTRL_SW_BUF_U32 | DMA_CTRL_ENABLED), status); //Create FIFOs @@ -190,10 +196,6 @@ public: nirio_status_chain(_send_fifo->start(), status); if (nirio_status_not_fatal(status)) { - //Flush RX kernel buffers in case some cruft was - //left behind from the last run - _flush_rx_buff(); - //allocate re-usable managed receive buffers for (size_t i = 0; i < get_num_recv_frames(); i++){ _mrb_pool.push_back(boost::shared_ptr<nirio_zero_copy_mrb>(new nirio_zero_copy_mrb( @@ -217,9 +219,9 @@ public: { _proxy().get_rio_quirks().remove_tx_fifo(_fifo_instance); - //Reset DMA streams (Teardown, so don't status chain) - _proxy().poke(PCIE_TX_DMA_REG(DMA_CTRL_STATUS_REG, _fifo_instance), DMA_CTRL_RESET); - _proxy().poke(PCIE_RX_DMA_REG(DMA_CTRL_STATUS_REG, _fifo_instance), DMA_CTRL_RESET); + //Disable DMA streams (cleanup, so don't status chain) + _proxy().poke(PCIE_TX_DMA_REG(DMA_CTRL_STATUS_REG, _fifo_instance), DMA_CTRL_DISABLED); + _proxy().poke(PCIE_RX_DMA_REG(DMA_CTRL_STATUS_REG, _fifo_instance), DMA_CTRL_DISABLED); _flush_rx_buff(); @@ -261,19 +263,68 @@ private: UHD_INLINE void _flush_rx_buff() { - nirio_status flush_status = 0; - while (nirio_status_not_fatal(flush_status)) { - static const size_t NUM_ELEMS_TO_FLUSH = 1; - static const uint32_t FLUSH_TIMEOUT_IN_MS = 0; - - fifo_data_t* flush_data_ptr = NULL; - size_t flush_elems_acquired = 0, flush_elems_remaining = 0; - flush_status = _recv_fifo->acquire( - flush_data_ptr, NUM_ELEMS_TO_FLUSH, FLUSH_TIMEOUT_IN_MS, - flush_elems_acquired, flush_elems_remaining); - if (nirio_status_not_fatal(flush_status)) { - _recv_fifo->release(flush_elems_acquired); + // acquire is called with 0 elements requested first to + // get the number of elements in the buffer and then + // repeatedly with the number of remaining elements + // until the buffer is empty + fifo_data_t* elems_buffer; + for (size_t num_elems_requested = 0, + num_elems_acquired = 0, + num_elems_remaining = 1; + num_elems_remaining; + num_elems_requested = num_elems_remaining) + { + nirio_status status = _recv_fifo->acquire( + elems_buffer, + num_elems_requested, + 0, // timeout + num_elems_acquired, + num_elems_remaining); + // throw excetption if status is fatal + nirio_status_to_exception(status, + "NI-RIO PCIe data transfer failed during flush."); + _recv_fifo->release(num_elems_acquired); + } + } + + UHD_INLINE void _wait_until_stream_ready() + { + static const uint32_t TIMEOUT_IN_MS = 100; + + uint32_t reg_data = 0xffffffff; + bool tx_busy = true, rx_busy = true; + boost::posix_time::ptime start_time; + boost::posix_time::time_duration elapsed; + nirio_status status = NiRio_Status_Success; + + nirio_status_chain(_proxy().peek( + PCIE_TX_DMA_REG(DMA_CTRL_STATUS_REG, _fifo_instance), reg_data), status); + tx_busy = (reg_data & DMA_STATUS_BUSY); + nirio_status_chain(_proxy().peek( + PCIE_RX_DMA_REG(DMA_CTRL_STATUS_REG, _fifo_instance), reg_data), status); + rx_busy = (reg_data & DMA_STATUS_BUSY); + + if (nirio_status_not_fatal(status) && (tx_busy || rx_busy)) { + start_time = boost::posix_time::microsec_clock::local_time(); + do { + boost::this_thread::sleep(boost::posix_time::microsec(50)); //Avoid flooding the bus + elapsed = boost::posix_time::microsec_clock::local_time() - start_time; + nirio_status_chain(_proxy().peek( + PCIE_TX_DMA_REG(DMA_CTRL_STATUS_REG, _fifo_instance), reg_data), status); + tx_busy = (reg_data & DMA_STATUS_BUSY); + nirio_status_chain(_proxy().peek( + PCIE_RX_DMA_REG(DMA_CTRL_STATUS_REG, _fifo_instance), reg_data), status); + rx_busy = (reg_data & DMA_STATUS_BUSY); + } while ( + nirio_status_not_fatal(status) && + (tx_busy || rx_busy) && + elapsed.total_milliseconds() < TIMEOUT_IN_MS); + + if (tx_busy || rx_busy) { + nirio_status_chain(NiRio_Status_FpgaBusy, status); } + + nirio_status_to_exception(status, "Could not create nirio_zero_copy transport."); } } diff --git a/host/lib/types/CMakeLists.txt b/host/lib/types/CMakeLists.txt index b69c8e487..7fc6bdd94 100644 --- a/host/lib/types/CMakeLists.txt +++ b/host/lib/types/CMakeLists.txt @@ -82,6 +82,7 @@ SET_SOURCE_FILES_PROPERTIES( LIBUHD_APPEND_SOURCES( ${CMAKE_CURRENT_SOURCE_DIR}/device_addr.cpp ${CMAKE_CURRENT_SOURCE_DIR}/mac_addr.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/metadata.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ranges.cpp ${CMAKE_CURRENT_SOURCE_DIR}/sensors.cpp ${CMAKE_CURRENT_SOURCE_DIR}/serial.cpp diff --git a/host/lib/types/metadata.cpp b/host/lib/types/metadata.cpp new file mode 100644 index 000000000..fec2ac564 --- /dev/null +++ b/host/lib/types/metadata.cpp @@ -0,0 +1,92 @@ +// +// Copyright 2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <string> +#include <sstream> +#include <boost/format.hpp> +#include <uhd/exception.hpp> +#include <uhd/types/metadata.hpp> +#include <uhd/types/time_spec.hpp> + +using namespace uhd; + +std::string rx_metadata_t::to_pp_string(bool compact) const +{ + std::stringstream ss; + + if (compact) { + if (has_time_spec) { + ss << "Time: " << time_spec.get_real_secs() << " s\n"; + } + if (more_fragments) { + ss << "Fragmentation offset: " << fragment_offset << "\n"; + } + if (start_of_burst) { + ss << "Start of burst.\n" << fragment_offset; + } + if (end_of_burst) { + ss << "End of burst.\n" << fragment_offset; + } + if (error_code != ERROR_CODE_NONE) { + ss << strerror() << "\n"; + } + } else { + ss << "Has timespec: " << (has_time_spec ? "Yes" : "No") + << "\tTime of first sample: " << time_spec.get_real_secs() + << "\nFragmented: " << (more_fragments ? "Yes" : "No") + << " Fragmentation offset: " << fragment_offset + << "\nStart of burst: " << (start_of_burst ? "Yes" : "No") + << "\tEnd of burst: " << (end_of_burst ? "Yes" : "No") + << "\nError Code: " << strerror() + << "\tOut of sequence: " << (out_of_sequence ? "Yes" : "No"); + } + + return ss.str(); +} + +std::string rx_metadata_t::strerror() const +{ + std::string errstr = ""; + switch(this->error_code) { + case ERROR_CODE_NONE: + errstr = "ERROR_CODE_NONE"; + break; + case ERROR_CODE_TIMEOUT: + errstr = "ERROR_CODE_TIMEOUT"; + break; + case ERROR_CODE_LATE_COMMAND: + errstr = "ERROR_CODE_LATE_COMMAND"; + break; + case ERROR_CODE_BROKEN_CHAIN: + errstr = "ERROR_CODE_BROKEN_CHAIN (Expected another stream command)"; + break; + case ERROR_CODE_OVERFLOW: + errstr = "ERROR_CODE_OVERFLOW "; + errstr += (this->out_of_sequence ? "(Out of sequence error)" : "(Overflow)"); + break; + case ERROR_CODE_ALIGNMENT: + errstr = "ERROR_CODE_ALIGNMENT (Multi-channel alignment failed)"; + break; + case ERROR_CODE_BAD_PACKET: + errstr = "ERROR_CODE_BAD_PACKET"; + break; + default: + errstr = std::string(str(boost::format("Unknown error code: 0x%x") % error_code)); + } + + return errstr; +} diff --git a/host/lib/usrp/b100/b100_impl.cpp b/host/lib/usrp/b100/b100_impl.cpp index a47856b07..baf2b6ae3 100644 --- a/host/lib/usrp/b100/b100_impl.cpp +++ b/host/lib/usrp/b100/b100_impl.cpp @@ -20,6 +20,7 @@ #include "b100_regs.hpp" #include <uhd/transport/usb_control.hpp> #include <uhd/utils/msg.hpp> +#include <uhd/utils/cast.hpp> #include <uhd/exception.hpp> #include <uhd/utils/static.hpp> #include <uhd/utils/images.hpp> @@ -56,11 +57,11 @@ static device_addrs_t b100_find(const device_addr_t &hint) //since an address and resource is intended for a different, non-USB, device. if (hint.has_key("addr") || hint.has_key("resource")) return b100_addrs; - unsigned int vid, pid; + boost::uint16_t vid, pid; if(hint.has_key("vid") && hint.has_key("pid") && hint.has_key("type") && hint["type"] == "b100") { - sscanf(hint.get("vid").c_str(), "%x", &vid); - sscanf(hint.get("pid").c_str(), "%x", &pid); + vid = uhd::cast::hexstr_cast<boost::uint16_t>(hint.get("vid")); + pid = uhd::cast::hexstr_cast<boost::uint16_t>(hint.get("pid")); } else { vid = B100_VENDOR_ID; pid = B100_PRODUCT_ID; @@ -515,6 +516,7 @@ b100_impl::b100_impl(const device_addr_t &device_addr){ _tree->access<subdev_spec_t>(mb_path / "tx_subdev_spec").set(subdev_spec_t("A:" + _tree->list(mb_path / "dboards/A/tx_frontends").at(0))); _tree->access<std::string>(mb_path / "clock_source/value").set("internal"); _tree->access<std::string>(mb_path / "time_source/value").set("none"); + _tree->create<double>(mb_path / "link_max_rate").set(B100_MAX_RATE_USB2); } b100_impl::~b100_impl(void){ diff --git a/host/lib/usrp/b100/b100_impl.hpp b/host/lib/usrp/b100/b100_impl.hpp index 7d71d5ec3..b6752681e 100644 --- a/host/lib/usrp/b100/b100_impl.hpp +++ b/host/lib/usrp/b100/b100_impl.hpp @@ -54,6 +54,7 @@ static const boost::uint32_t B100_CTRL_MSG_SID = 20; static const double B100_DEFAULT_TICK_RATE = 64e6; static const size_t B100_MAX_PKT_BYTE_LIMIT = 2048; static const std::string B100_EEPROM_MAP_KEY = "B100"; +static const size_t B100_MAX_RATE_USB2 = 32000000; // bytes/s #define I2C_ADDR_TX_A (I2C_DEV_EEPROM | 0x4) #define I2C_ADDR_RX_A (I2C_DEV_EEPROM | 0x5) diff --git a/host/lib/usrp/b200/b200_iface.cpp b/host/lib/usrp/b200/b200_iface.cpp index 5d799bf01..efb9b3a35 100644 --- a/host/lib/usrp/b200/b200_iface.cpp +++ b/host/lib/usrp/b200/b200_iface.cpp @@ -19,6 +19,7 @@ #include <uhd/config.hpp> #include <uhd/utils/msg.hpp> +#include <uhd/utils/log.hpp> #include <uhd/exception.hpp> #include <boost/functional/hash.hpp> #include <boost/thread/thread.hpp> @@ -32,6 +33,12 @@ #include <iomanip> #include <libusb.h> +//! libusb_error_name is only in newer API +#ifndef HAVE_LIBUSB_ERROR_NAME + #define libusb_error_name(code) \ + str(boost::format("LIBUSB_ERROR_CODE %d") % code) +#endif + using namespace uhd; using namespace uhd::transport; @@ -298,10 +305,10 @@ public: } } - void ad9361_transact(const unsigned char in_buff[64], unsigned char out_buff[64]) { - const int bytes_to_write = 64; - const int bytes_to_read = 64; - const size_t read_retries = 30; + void ad9361_transact(const unsigned char in_buff[AD9361_DISPATCH_PACKET_SIZE], unsigned char out_buff[AD9361_DISPATCH_PACKET_SIZE]) { + const int bytes_to_write = AD9361_DISPATCH_PACKET_SIZE; + const int bytes_to_read = AD9361_DISPATCH_PACKET_SIZE; + const size_t read_retries = 5; int ret = fx3_control_write(B200_VREQ_AD9361_CTRL_WRITE, 0x00, 0x00, (unsigned char *)in_buff, bytes_to_write); if (ret < 0) @@ -311,9 +318,26 @@ public: for (size_t i = 0; i < read_retries; i++) { - ret = fx3_control_read(B200_VREQ_AD9361_CTRL_READ, 0x00, 0x00, out_buff, bytes_to_read, 1000); + ret = fx3_control_read(B200_VREQ_AD9361_CTRL_READ, 0x00, 0x00, out_buff, bytes_to_read, 3000); if (ret < 0) - throw uhd::io_error((boost::format("Failed to read AD9361 (%d: %s)") % ret % libusb_error_name(ret)).str()); + { + if (ret == LIBUSB_ERROR_TIMEOUT) + { + UHD_LOG << (boost::format("Failed to read AD9361 (%d: %s). Retrying (%d of %d)...") + % ret + % libusb_error_name(ret) + % (i+1) + % read_retries + ) << std::endl; + } + else + { + throw uhd::io_error((boost::format("Failed to read AD9361 (%d: %s)") + % ret + % libusb_error_name(ret) + ).str()); + } + } if (ret == bytes_to_read) return; @@ -682,7 +706,7 @@ public: 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) + if (percent_before != percent_after) { UHD_MSG(status) << "\b\b\b\b" << std::setw(3) << percent_after << "%" << std::flush; } diff --git a/host/lib/usrp/b200/b200_impl.cpp b/host/lib/usrp/b200/b200_impl.cpp index a7f9b11bd..98141dbaa 100644 --- a/host/lib/usrp/b200/b200_impl.cpp +++ b/host/lib/usrp/b200/b200_impl.cpp @@ -19,6 +19,7 @@ #include "b200_regs.hpp" #include <uhd/transport/usb_control.hpp> #include <uhd/utils/msg.hpp> +#include <uhd/utils/cast.hpp> #include <uhd/exception.hpp> #include <uhd/utils/static.hpp> #include <uhd/utils/images.hpp> @@ -58,11 +59,11 @@ static device_addrs_t b200_find(const device_addr_t &hint) //since an address and resource is intended for a different, non-USB, device. if (hint.has_key("addr") || hint.has_key("resource")) return b200_addrs; - unsigned int vid, pid; + boost::uint16_t vid, pid; if(hint.has_key("vid") && hint.has_key("pid") && hint.has_key("type") && hint["type"] == "b200") { - sscanf(hint.get("vid").c_str(), "%x", &vid); - sscanf(hint.get("pid").c_str(), "%x", &pid); + vid = uhd::cast::hexstr_cast<boost::uint16_t>(hint.get("vid")); + pid = uhd::cast::hexstr_cast<boost::uint16_t>(hint.get("pid")); } else { vid = B200_VENDOR_ID; pid = B200_PRODUCT_ID; @@ -157,12 +158,12 @@ 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; + boost::uint16_t vid = B200_VENDOR_ID; + boost::uint16_t pid = B200_PRODUCT_ID; if (device_addr.has_key("vid")) - sscanf(device_addr.get("vid").c_str(), "%x", &vid); + vid = uhd::cast::hexstr_cast<boost::uint16_t>(device_addr.get("vid")); if (device_addr.has_key("pid")) - sscanf(device_addr.get("pid").c_str(), "%x", &pid); + pid = uhd::cast::hexstr_cast<boost::uint16_t>(device_addr.get("pid")); std::vector<usb_device_handle::sptr> device_list = usb_device_handle::get_device_list(vid, pid); @@ -250,6 +251,7 @@ b200_impl::b200_impl(const device_addr_t &device_addr) ctrl_xport_args ); while (_ctrl_transport->get_recv_buff(0.0)){} //flush ctrl xport + _tree->create<double>(mb_path / "link_max_rate").set((usb_speed == 3) ? B200_MAX_RATE_USB3 : B200_MAX_RATE_USB2); //////////////////////////////////////////////////////////////////// // Async task structure @@ -663,9 +665,40 @@ void b200_impl::codec_loopback_self_test(wb_iface::sptr iface) /*********************************************************************** * Sample and tick rate comprehension below **********************************************************************/ +void b200_impl::enforce_tick_rate_limits(size_t chan_count, double tick_rate, const char* direction /*= NULL*/) +{ + const size_t max_chans = 2; + if (chan_count > max_chans) + { + throw uhd::value_error(boost::str( + boost::format("cannot not setup %d %s channels (maximum is %d)") + % chan_count + % (direction ? direction : "data") + % max_chans + )); + } + else + { + const double max_tick_rate = ((chan_count <= 1) ? AD9361_1_CHAN_CLOCK_RATE_MAX : AD9361_2_CHAN_CLOCK_RATE_MAX); + if (tick_rate > max_tick_rate) + { + throw uhd::value_error(boost::str( + boost::format("current master clock rate (%.2f MHz) exceeds maximum possible master clock rate (%.2f MHz) when using %d %s channels") + % (tick_rate/1e6) + % (max_tick_rate/1e6) + % chan_count + % (direction ? direction : "data") + )); + } + } +} + double b200_impl::set_tick_rate(const double rate) { UHD_MSG(status) << "Asking for clock rate " << rate/1e6 << " MHz\n"; + + check_tick_rate_with_current_streamers(rate); // Defined in b200_io_impl.cpp + _tick_rate = _codec_ctrl->set_clock_rate(rate); UHD_MSG(status) << "Actually got clock rate " << _tick_rate/1e6 << " MHz\n"; diff --git a/host/lib/usrp/b200/b200_impl.hpp b/host/lib/usrp/b200/b200_impl.hpp index a370e54f9..c3508c550 100644 --- a/host/lib/usrp/b200/b200_impl.hpp +++ b/host/lib/usrp/b200/b200_impl.hpp @@ -47,11 +47,13 @@ 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 = 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; static const boost::uint32_t B200_GPSDO_ST_NONE = 0x83; +static const size_t B200_MAX_RATE_USB2 = 32000000; // bytes/s +static const size_t B200_MAX_RATE_USB3 = 500000000; // bytes/s + #define FLIP_SID(sid) (((sid)<<16)|((sid)>>16)) static const boost::uint32_t B200_CTRL0_MSG_SID = 0x00000010; @@ -91,6 +93,7 @@ public: uhd::rx_streamer::sptr get_rx_stream(const uhd::stream_args_t &args); uhd::tx_streamer::sptr get_tx_stream(const uhd::stream_args_t &args); bool recv_async_msg(uhd::async_metadata_t &, double); + void check_streamer_args(const uhd::stream_args_t &args, double tick_rate, const char* direction = NULL); private: //controllers @@ -177,13 +180,15 @@ private: void update_enables(void); void update_atrs(void); - void update_tick_rate(const double); - void update_rx_samp_rate(const size_t, const double); - void update_tx_samp_rate(const size_t, const double); - double _tick_rate; double get_tick_rate(void){return _tick_rate;} double set_tick_rate(const double rate); + void update_tick_rate(const double); + void enforce_tick_rate_limits(size_t chan_count, double tick_rate, const char* direction = NULL); + void check_tick_rate_with_current_streamers(double rate); + + void update_rx_samp_rate(const size_t, const double); + void update_tx_samp_rate(const size_t, const double); }; #endif /* INCLUDED_B200_IMPL_HPP */ diff --git a/host/lib/usrp/b200/b200_io_impl.cpp b/host/lib/usrp/b200/b200_io_impl.cpp index 4f072c4d4..9f6d593fe 100644 --- a/host/lib/usrp/b200/b200_io_impl.cpp +++ b/host/lib/usrp/b200/b200_io_impl.cpp @@ -23,6 +23,7 @@ #include "async_packet_handler.hpp" #include <boost/bind.hpp> #include <boost/make_shared.hpp> +#include <set> using namespace uhd; using namespace uhd::usrp; @@ -31,8 +32,47 @@ using namespace uhd::transport; /*********************************************************************** * update streamer rates **********************************************************************/ +void b200_impl::check_tick_rate_with_current_streamers(double rate) +{ + size_t max_tx_chan_count = 0, max_rx_chan_count = 0; + BOOST_FOREACH(radio_perifs_t &perif, _radio_perifs) + { + { + boost::shared_ptr<sph::recv_packet_streamer> rx_streamer = + boost::dynamic_pointer_cast<sph::recv_packet_streamer>(perif.rx_streamer.lock()); + if (rx_streamer) + max_rx_chan_count = std::max(max_rx_chan_count, rx_streamer->get_num_channels()); + } + + { + boost::shared_ptr<sph::send_packet_streamer> tx_streamer = + boost::dynamic_pointer_cast<sph::send_packet_streamer>(perif.tx_streamer.lock()); + if (tx_streamer) + max_tx_chan_count = std::max(max_tx_chan_count, tx_streamer->get_num_channels()); + } + } + + // Defined in b200_impl.cpp + enforce_tick_rate_limits(max_rx_chan_count, rate, "RX"); + enforce_tick_rate_limits(max_tx_chan_count, rate, "TX"); +} + +void b200_impl::check_streamer_args(const uhd::stream_args_t &args, double tick_rate, const char* direction /*= NULL*/) +{ + std::set<size_t> chans_set; + for (size_t stream_i = 0; stream_i < args.channels.size(); stream_i++) + { + const size_t chan = args.channels[stream_i]; + chans_set.insert(chan); + } + + enforce_tick_rate_limits(chans_set.size(), tick_rate, direction); // Defined in b200_impl.cpp +} + void b200_impl::update_tick_rate(const double rate) { + check_tick_rate_with_current_streamers(rate); + BOOST_FOREACH(radio_perifs_t &perif, _radio_perifs) { boost::shared_ptr<sph::recv_packet_streamer> my_streamer = @@ -222,6 +262,8 @@ rx_streamer::sptr b200_impl::get_rx_stream(const uhd::stream_args_t &args_) if (args.otw_format.empty()) args.otw_format = "sc16"; args.channels = args.channels.empty()? std::vector<size_t>(1, 0) : args.channels; + check_streamer_args(args, this->get_tick_rate(), "RX"); + boost::shared_ptr<sph::recv_packet_streamer> my_streamer; for (size_t stream_i = 0; stream_i < args.channels.size(); stream_i++) { @@ -325,6 +367,8 @@ tx_streamer::sptr b200_impl::get_tx_stream(const uhd::stream_args_t &args_) if (args.otw_format.empty()) args.otw_format = "sc16"; args.channels = args.channels.empty()? std::vector<size_t>(1, 0) : args.channels; + check_streamer_args(args, this->get_tick_rate(), "TX"); + boost::shared_ptr<sph::send_packet_streamer> my_streamer; for (size_t stream_i = 0; stream_i < args.channels.size(); stream_i++) { diff --git a/host/lib/usrp/common/ad9361_ctrl.cpp b/host/lib/usrp/common/ad9361_ctrl.cpp index 1afa2fbb7..10496f2a9 100644 --- a/host/lib/usrp/common/ad9361_ctrl.cpp +++ b/host/lib/usrp/common/ad9361_ctrl.cpp @@ -151,7 +151,7 @@ struct ad9361_ctrl_impl : public ad9361_ctrl //handle errors const size_t len = my_strnlen(out->error_msg, AD9361_TRANSACTION_MAX_ERROR_MSG); const std::string error_msg(out->error_msg, len); - if (not error_msg.empty()) throw uhd::runtime_error("ad9361 do transaction: " + error_msg); + if (not error_msg.empty()) throw uhd::runtime_error("[ad9361_ctrl::do_transaction] firmware reported: \"" + error_msg + "\""); //return result done! return *out; diff --git a/host/lib/usrp/common/ad9361_ctrl.hpp b/host/lib/usrp/common/ad9361_ctrl.hpp index fd8012764..098b5dae8 100644 --- a/host/lib/usrp/common/ad9361_ctrl.hpp +++ b/host/lib/usrp/common/ad9361_ctrl.hpp @@ -27,10 +27,17 @@ #include <vector> #include <string> +#include "ad9361_transaction.h" + + +static const double AD9361_CLOCK_RATE_MAX = 61.44e6; +static const double AD9361_1_CHAN_CLOCK_RATE_MAX = AD9361_CLOCK_RATE_MAX; +static const double AD9361_2_CHAN_CLOCK_RATE_MAX = (AD9361_1_CHAN_CLOCK_RATE_MAX / 2); + struct ad9361_ctrl_iface_type { - virtual void ad9361_transact(const unsigned char in_buff[64], unsigned char out_buff[64]) = 0; + virtual void ad9361_transact(const unsigned char in_buff[AD9361_DISPATCH_PACKET_SIZE], unsigned char out_buff[AD9361_DISPATCH_PACKET_SIZE]) = 0; }; typedef boost::shared_ptr<ad9361_ctrl_iface_type> ad9361_ctrl_iface_sptr; @@ -42,18 +49,18 @@ struct ad9361_ctrl_over_zc : ad9361_ctrl_iface_type _xport = xport; } - void ad9361_transact(const unsigned char in_buff[64], unsigned char out_buff[64]) + void ad9361_transact(const unsigned char in_buff[AD9361_DISPATCH_PACKET_SIZE], unsigned char out_buff[AD9361_DISPATCH_PACKET_SIZE]) { { uhd::transport::managed_send_buffer::sptr buff = _xport->get_send_buff(10.0); - if (not buff or buff->size() < 64) throw std::runtime_error("ad9361_ctrl_over_zc send timeout"); - std::memcpy(buff->cast<void *>(), in_buff, 64); - buff->commit(64); + if (not buff or buff->size() < AD9361_DISPATCH_PACKET_SIZE) throw std::runtime_error("ad9361_ctrl_over_zc send timeout"); + std::memcpy(buff->cast<void *>(), in_buff, AD9361_DISPATCH_PACKET_SIZE); + buff->commit(AD9361_DISPATCH_PACKET_SIZE); } { uhd::transport::managed_recv_buffer::sptr buff = _xport->get_recv_buff(10.0); - if (not buff or buff->size() < 64) throw std::runtime_error("ad9361_ctrl_over_zc recv timeout"); - std::memcpy(out_buff, buff->cast<const void *>(), 64); + if (not buff or buff->size() < AD9361_DISPATCH_PACKET_SIZE) throw std::runtime_error("ad9361_ctrl_over_zc recv timeout"); + std::memcpy(out_buff, buff->cast<const void *>(), AD9361_DISPATCH_PACKET_SIZE); } } @@ -100,7 +107,7 @@ public: static uhd::meta_range_t get_clock_rate_range(void) { //return uhd::meta_range_t(220e3, 61.44e6); - return uhd::meta_range_t(5e6, 61.44e6); //5 MHz DCM low end + return uhd::meta_range_t(5e6, AD9361_CLOCK_RATE_MAX); //5 MHz DCM low end } //! set the filter bandwidth for the frontend diff --git a/host/lib/usrp/common/ad9361_transaction.h b/host/lib/usrp/common/ad9361_transaction.h index 7cbad5908..693f32e41 100644 --- a/host/lib/usrp/common/ad9361_transaction.h +++ b/host/lib/usrp/common/ad9361_transaction.h @@ -25,8 +25,8 @@ extern "C" { #endif //various constants -#define AD9361_TRANSACTION_VERSION 0x4 -#define AD9361_TRANSACTION_MAX_ERROR_MSG 40 +#define AD9361_TRANSACTION_VERSION 0x4 +#define AD9361_DISPATCH_PACKET_SIZE 64 //action types #define AD9361_ACTION_ECHO 0 @@ -100,6 +100,7 @@ typedef struct } ad9361_transaction_t; +#define AD9361_TRANSACTION_MAX_ERROR_MSG (AD9361_DISPATCH_PACKET_SIZE - (sizeof(ad9361_transaction_t)-4)-1) // -4 for 'error_msg' alignment padding, -1 for terminating \0 #ifdef __cplusplus } diff --git a/host/lib/usrp/common/adf435x_common.cpp b/host/lib/usrp/common/adf435x_common.cpp index f0df6a334..972a69388 100644 --- a/host/lib/usrp/common/adf435x_common.cpp +++ b/host/lib/usrp/common/adf435x_common.cpp @@ -16,8 +16,11 @@ // #include "adf435x_common.hpp" + #include <uhd/types/tune_request.hpp> #include <uhd/utils/log.hpp> +#include <cmath> + using namespace uhd; diff --git a/host/lib/usrp/cores/rx_dsp_core_3000.cpp b/host/lib/usrp/cores/rx_dsp_core_3000.cpp index 86846667f..02c24b4bb 100644 --- a/host/lib/usrp/cores/rx_dsp_core_3000.cpp +++ b/host/lib/usrp/cores/rx_dsp_core_3000.cpp @@ -91,8 +91,10 @@ public: uhd::meta_range_t get_host_rates(void){ meta_range_t range; - for (int rate = 1024; rate > 512; rate -= 8){ - range.push_back(range_t(_tick_rate/rate)); + if (!_is_b200) { + for (int rate = 1024; rate > 512; rate -= 8){ + range.push_back(range_t(_tick_rate/rate)); + } } for (int rate = 512; rate > 256; rate -= 4){ range.push_back(range_t(_tick_rate/rate)); diff --git a/host/lib/usrp/dboard/db_tvrx2.cpp b/host/lib/usrp/dboard/db_tvrx2.cpp index c593c5437..c74c64471 100644 --- a/host/lib/usrp/dboard/db_tvrx2.cpp +++ b/host/lib/usrp/dboard/db_tvrx2.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010,2012-2013 Ettus Research LLC +// Copyright 2010,2012-2014 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -1005,8 +1005,8 @@ tvrx2::tvrx2(ctor_args_t args) : rx_dboard_base(args){ _freq_scalar = (4*16.0e6)/(this->get_iface()->get_clock_rate(dboard_iface::UNIT_RX)); } else if (ref_clock == 100e6) { - - this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, REFCLOCK_DIV8); + + this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, REFCLOCK_DIV6); UHD_LOGV(often) << boost::format( "TVRX2 (%s): Dividing Refclock by 6" diff --git a/host/lib/usrp/gps_ctrl.cpp b/host/lib/usrp/gps_ctrl.cpp index 6f5c75dec..d327a84f9 100644 --- a/host/lib/usrp/gps_ctrl.cpp +++ b/host/lib/usrp/gps_ctrl.cpp @@ -74,8 +74,15 @@ private: // Get all GPSDO messages available // Creating a map here because we only want the latest of each message type - for (std::string msg = _recv(); msg.length() > 6; msg = _recv()) + for (std::string msg = _recv(); msg.length(); msg = _recv()) { + if (msg.length() < 6) + continue; + + // Strip any end of line characters + erase_all(msg, "\r"); + erase_all(msg, "\n"); + // Look for SERVO message if (boost::regex_search(msg, status_regex, boost::regex_constants::match_continuous)) msgs["SERVO"] = msg; diff --git a/host/lib/usrp/multi_usrp.cpp b/host/lib/usrp/multi_usrp.cpp index f08709669..71b1f8995 100644 --- a/host/lib/usrp/multi_usrp.cpp +++ b/host/lib/usrp/multi_usrp.cpp @@ -24,6 +24,7 @@ #include <uhd/usrp/dboard_id.hpp> #include <uhd/usrp/mboard_eeprom.hpp> #include <uhd/usrp/dboard_eeprom.hpp> +#include <uhd/convert.hpp> #include <boost/assign/list_of.hpp> #include <boost/thread.hpp> #include <boost/foreach.hpp> @@ -103,6 +104,8 @@ static meta_range_t make_overall_tune_range( return range; } + + /*********************************************************************** * Gain helper functions **********************************************************************/ @@ -589,6 +592,11 @@ public: /******************************************************************* * RX methods ******************************************************************/ + rx_streamer::sptr get_rx_stream(const stream_args_t &args) { + _check_link_rate(args, false); + return this->get_device()->get_rx_stream(args); + } + void set_rx_subdev_spec(const subdev_spec_t &spec, size_t mboard){ if (mboard != ALL_MBOARDS){ _tree->access<subdev_spec_t>(mb_root(mboard) / "rx_subdev_spec").set(spec); @@ -739,7 +747,11 @@ public: void set_rx_dc_offset(const bool enb, size_t chan){ if (chan != ALL_CHANS){ - _tree->access<bool>(rx_fe_root(chan) / "dc_offset" / "enable").set(enb); + if (_tree->exists(rx_fe_root(chan) / "dc_offset" / "enable")) { + _tree->access<bool>(rx_fe_root(chan) / "dc_offset" / "enable").set(enb); + } else { + UHD_MSG(warning) << "Setting DC offset compensation is not possible on this device." << std::endl; + } return; } for (size_t c = 0; c < get_rx_num_channels(); c++){ @@ -749,7 +761,11 @@ public: void set_rx_dc_offset(const std::complex<double> &offset, size_t chan){ if (chan != ALL_CHANS){ - _tree->access<std::complex<double> >(rx_fe_root(chan) / "dc_offset" / "value").set(offset); + if (_tree->exists(rx_fe_root(chan) / "dc_offset" / "value")) { + _tree->access<std::complex<double> >(rx_fe_root(chan) / "dc_offset" / "value").set(offset); + } else { + UHD_MSG(warning) << "Setting DC offset is not possible on this device." << std::endl; + } return; } for (size_t c = 0; c < get_rx_num_channels(); c++){ @@ -759,7 +775,11 @@ public: void set_rx_iq_balance(const std::complex<double> &offset, size_t chan){ if (chan != ALL_CHANS){ - _tree->access<std::complex<double> >(rx_fe_root(chan) / "iq_balance" / "value").set(offset); + if (_tree->exists(rx_fe_root(chan) / "iq_balance" / "value")) { + _tree->access<std::complex<double> >(rx_fe_root(chan) / "iq_balance" / "value").set(offset); + } else { + UHD_MSG(warning) << "Setting IQ balance is not possible on this device." << std::endl; + } return; } for (size_t c = 0; c < get_rx_num_channels(); c++){ @@ -770,6 +790,11 @@ public: /******************************************************************* * TX methods ******************************************************************/ + tx_streamer::sptr get_tx_stream(const stream_args_t &args) { + _check_link_rate(args, true); + return this->get_device()->get_tx_stream(args); + } + void set_tx_subdev_spec(const subdev_spec_t &spec, size_t mboard){ if (mboard != ALL_MBOARDS){ _tree->access<subdev_spec_t>(mb_root(mboard) / "tx_subdev_spec").set(spec); @@ -920,7 +945,11 @@ public: void set_tx_dc_offset(const std::complex<double> &offset, size_t chan){ if (chan != ALL_CHANS){ - _tree->access<std::complex<double> >(tx_fe_root(chan) / "dc_offset" / "value").set(offset); + if (_tree->exists(tx_fe_root(chan) / "dc_offset" / "value")) { + _tree->access<std::complex<double> >(tx_fe_root(chan) / "dc_offset" / "value").set(offset); + } else { + UHD_MSG(warning) << "Setting DC offset is not possible on this device." << std::endl; + } return; } for (size_t c = 0; c < get_tx_num_channels(); c++){ @@ -930,7 +959,11 @@ public: void set_tx_iq_balance(const std::complex<double> &offset, size_t chan){ if (chan != ALL_CHANS){ - _tree->access<std::complex<double> >(tx_fe_root(chan) / "iq_balance" / "value").set(offset); + if (_tree->exists(tx_fe_root(chan) / "iq_balance" / "value")) { + _tree->access<std::complex<double> >(tx_fe_root(chan) / "iq_balance" / "value").set(offset); + } else { + UHD_MSG(warning) << "Setting IQ balance is not possible on this device." << std::endl; + } return; } for (size_t c = 0; c < get_tx_num_channels(); c++){ @@ -1178,6 +1211,34 @@ private: } return gg; } + + //! \param is_tx True for tx + // Assumption is that all mboards use the same link + bool _check_link_rate(const stream_args_t &args, bool is_tx) { + bool link_rate_is_ok = true; + size_t bytes_per_sample = convert::get_bytes_per_item(args.otw_format.empty() ? "sc16" : args.otw_format); + double max_link_rate = 0; + double sum_rate = 0; + BOOST_FOREACH(const size_t chan, args.channels) { + mboard_chan_pair mcp = is_tx ? tx_chan_to_mcp(chan) : rx_chan_to_mcp(chan); + if (_tree->exists(mb_root(mcp.mboard) / "link_max_rate")) { + max_link_rate = std::max( + max_link_rate, + _tree->access<double>(mb_root(mcp.mboard) / "link_max_rate").get() + ); + } + sum_rate += is_tx ? get_tx_rate(chan) : get_rx_rate(chan); + } + if (max_link_rate > 0 and (max_link_rate / bytes_per_sample) < sum_rate) { + UHD_MSG(warning) << boost::format( + "The total sum of rates (%f MSps on %u channels) exceeds the maximum capacity of the connection.\n" + "This can cause %s." + ) % (sum_rate/1e6) % args.channels.size() % (is_tx ? "underruns (U)" : "overflows (O)") << std::endl; + link_rate_is_ok = false; + } + + return link_rate_is_ok; + } }; /*********************************************************************** diff --git a/host/lib/usrp/usrp1/usrp1_impl.cpp b/host/lib/usrp/usrp1/usrp1_impl.cpp index 3b902b343..0ba2e1e4a 100644 --- a/host/lib/usrp/usrp1/usrp1_impl.cpp +++ b/host/lib/usrp/usrp1/usrp1_impl.cpp @@ -20,6 +20,7 @@ #include <uhd/utils/safe_call.hpp> #include <uhd/transport/usb_control.hpp> #include <uhd/utils/msg.hpp> +#include <uhd/utils/cast.hpp> #include <uhd/exception.hpp> #include <uhd/utils/static.hpp> #include <uhd/utils/images.hpp> @@ -59,11 +60,11 @@ static device_addrs_t usrp1_find(const device_addr_t &hint) //since an address and resource is intended for a different, non-USB, device. if (hint.has_key("addr") || hint.has_key("resource")) return usrp1_addrs; - unsigned int vid, pid; + boost::uint16_t vid, pid; if(hint.has_key("vid") && hint.has_key("pid") && hint.has_key("type") && hint["type"] == "usrp1") { - sscanf(hint.get("vid").c_str(), "%x", &vid); - sscanf(hint.get("pid").c_str(), "%x", &pid); + vid = uhd::cast::hexstr_cast<boost::uint16_t>(hint.get("vid")); + pid = uhd::cast::hexstr_cast<boost::uint16_t>(hint.get("pid")); } else { vid = USRP1_VENDOR_ID; pid = USRP1_PRODUCT_ID; @@ -409,7 +410,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); - + _tree->create<double>(mb_path / "link_max_rate").set(USRP1_MAX_RATE_USB2); } usrp1_impl::~usrp1_impl(void){ diff --git a/host/lib/usrp/usrp1/usrp1_impl.hpp b/host/lib/usrp/usrp1/usrp1_impl.hpp index da9fe8b16..012bc0794 100644 --- a/host/lib/usrp/usrp1/usrp1_impl.hpp +++ b/host/lib/usrp/usrp1/usrp1_impl.hpp @@ -39,6 +39,7 @@ #define INCLUDED_USRP1_IMPL_HPP static const std::string USRP1_EEPROM_MAP_KEY = "B000"; +static const size_t USRP1_MAX_RATE_USB2 = 32000000; // bytes/s #define FR_RB_CAPS 3 #define FR_MODE 13 diff --git a/host/lib/usrp/usrp2/CMakeLists.txt b/host/lib/usrp/usrp2/CMakeLists.txt index da39d9df1..c6257c7fe 100644 --- a/host/lib/usrp/usrp2/CMakeLists.txt +++ b/host/lib/usrp/usrp2/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2011-2012 Ettus Research LLC +# Copyright 2011-2012,2014 Ettus Research LLC # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -25,6 +25,18 @@ LIBUHD_REGISTER_COMPONENT("USRP2" ENABLE_USRP2 ON "ENABLE_LIBUHD" OFF) IF(ENABLE_USRP2) + ######################################################################## + # Define UHD_PKG_DATA_PATH for usrp2_iface.cpp + ######################################################################## + FILE(TO_NATIVE_PATH ${CMAKE_INSTALL_PREFIX} UHD_PKG_PATH) + STRING(REPLACE "\\" "\\\\" UHD_PKG_PATH ${UHD_PKG_PATH}) + + SET_SOURCE_FILES_PROPERTIES( + ${CMAKE_CURRENT_SOURCE_DIR}/usrp2_iface.cpp + PROPERTIES COMPILE_DEFINITIONS + "UHD_LIB_DIR=\"lib${LIB_SUFFIX}\"" + ) + LIBUHD_APPEND_SOURCES( ${CMAKE_CURRENT_SOURCE_DIR}/clock_ctrl.cpp ${CMAKE_CURRENT_SOURCE_DIR}/codec_ctrl.cpp diff --git a/host/lib/usrp/usrp2/usrp2_iface.cpp b/host/lib/usrp/usrp2/usrp2_iface.cpp index 7297a30d1..b2085807f 100644 --- a/host/lib/usrp/usrp2/usrp2_iface.cpp +++ b/host/lib/usrp/usrp2/usrp2_iface.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2012 Ettus Research LLC +// Copyright 2010-2012,2014 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -21,6 +21,7 @@ #include "usrp2_iface.hpp" #include <uhd/exception.hpp> #include <uhd/utils/msg.hpp> +#include <uhd/utils/paths.hpp> #include <uhd/utils/tasks.hpp> #include <uhd/utils/images.hpp> #include <uhd/utils/safe_call.hpp> @@ -386,13 +387,13 @@ public: //create the burner commands if (this->get_rev() == USRP2_REV3 or this->get_rev() == USRP2_REV4){ - const std::string card_burner = (fs::path(fw_image_path).branch_path().branch_path() / "utils" / "usrp2_card_burner.py").string(); + const std::string card_burner = (fs::path(uhd::get_pkg_path()) / UHD_LIB_DIR / "uhd" / "utils" / "usrp2_card_burner.py").string(); const std::string card_burner_cmd = str(boost::format("\"%s%s\" %s--fpga=\"%s\" %s--fw=\"%s\"") % sudo % card_burner % ml % fpga_image_path % ml % fw_image_path); return str(boost::format("%s\n%s") % print_images_error() % card_burner_cmd); } else{ const std::string addr = _ctrl_transport->get_recv_addr(); - const std::string net_burner_path = (fs::path(fw_image_path).branch_path().branch_path() / "utils" / "usrp_n2xx_simple_net_burner").string(); + const std::string net_burner_path = (fs::path(uhd::get_pkg_path()) / UHD_LIB_DIR / "uhd" / "utils" / "usrp_n2xx_simple_net_burner").string(); const std::string net_burner_cmd = str(boost::format("\"%s\" %s--addr=\"%s\"") % net_burner_path % ml % addr); return str(boost::format("%s\n%s") % print_images_error() % net_burner_cmd); } diff --git a/host/lib/usrp/usrp2/usrp2_impl.cpp b/host/lib/usrp/usrp2/usrp2_impl.cpp index 16d9b9a54..918f3e892 100644 --- a/host/lib/usrp/usrp2/usrp2_impl.cpp +++ b/host/lib/usrp/usrp2/usrp2_impl.cpp @@ -442,6 +442,7 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr){ _mbc[mb].spiface = _mbc[mb].iface; break; } + _tree->create<double>(mb_path / "link_max_rate").set(USRP2_LINK_RATE_BPS); //////////////////////////////////////////////////////////////// // setup the mboard eeprom @@ -655,12 +656,14 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr){ .subscribe(boost::bind(&time64_core_200::set_time_next_pps, _mbc[mb].time64, _1)); //setup time source props _tree->create<std::string>(mb_path / "time_source/value") - .subscribe(boost::bind(&time64_core_200::set_time_source, _mbc[mb].time64, _1)); + .subscribe(boost::bind(&time64_core_200::set_time_source, _mbc[mb].time64, _1)) + .set("none"); _tree->create<std::vector<std::string> >(mb_path / "time_source/options") .publish(boost::bind(&time64_core_200::get_time_sources, _mbc[mb].time64)); //setup reference source props _tree->create<std::string>(mb_path / "clock_source/value") - .subscribe(boost::bind(&usrp2_impl::update_clock_source, this, mb, _1)); + .subscribe(boost::bind(&usrp2_impl::update_clock_source, this, mb, _1)) + .set("internal"); std::vector<std::string> clock_sources = boost::assign::list_of("internal")("external")("mimo"); if (_mbc[mb].gps and _mbc[mb].gps->gps_detected()) clock_sources.push_back("gpsdo"); _tree->create<std::vector<std::string> >(mb_path / "clock_source/options").set(clock_sources); diff --git a/host/lib/usrp/x300/x300_fw_common.h b/host/lib/usrp/x300/x300_fw_common.h index 632391644..0bbaee319 100644 --- a/host/lib/usrp/x300/x300_fw_common.h +++ b/host/lib/usrp/x300/x300_fw_common.h @@ -31,7 +31,7 @@ extern "C" { #define X300_FW_COMPAT_MAJOR 3 #define X300_FW_COMPAT_MINOR 0 -#define X300_FPGA_COMPAT_MAJOR 4 +#define X300_FPGA_COMPAT_MAJOR 6 //shared memory sections - in between the stack and the program space #define X300_FW_SHMEM_BASE 0x6000 diff --git a/host/lib/usrp/x300/x300_impl.cpp b/host/lib/usrp/x300/x300_impl.cpp index e492b2238..e931b7983 100644 --- a/host/lib/usrp/x300/x300_impl.cpp +++ b/host/lib/usrp/x300/x300_impl.cpp @@ -135,6 +135,12 @@ static device_addrs_t x300_find_with_addr(const device_addr_t &hint) return addrs; } +//We need a zpu xport registry to ensure synchronization between the static finder method +//and the instances of the x300_impl class. +typedef uhd::dict< std::string, boost::weak_ptr<wb_iface> > pcie_zpu_iface_registry_t; +UHD_SINGLETON_FCN(pcie_zpu_iface_registry_t, get_pcie_zpu_iface_registry) +static boost::mutex pcie_zpu_iface_registry_mutex; + static device_addrs_t x300_find_pcie(const device_addr_t &hint, bool explicit_query) { std::string rpc_port_name(NIUSRPRIO_DEFAULT_RPC_PORT); @@ -167,17 +173,30 @@ static device_addrs_t x300_find_pcie(const device_addr_t &hint, bool explicit_qu } niriok_proxy kernel_proxy; - kernel_proxy.open(dev_info.interface_path); //Attempt to read the name from the EEPROM and perform filtering. //This operation can throw due to compatibility mismatch. try { - //This call could throw an exception if the user is switching to using UHD + //This block could throw an exception if the user is switching to using UHD //after LabVIEW FPGA. In that case, skip reading the name and serial and pick //a default FPGA flavor. During make, a new image will be loaded and everything //will be OK - wb_iface::sptr zpu_ctrl = x300_make_ctrl_iface_pcie(kernel_proxy); + + wb_iface::sptr zpu_ctrl; + + //Hold on to the registry mutex as long as zpu_ctrl is alive + //to prevent any use by different threads while enumerating + boost::mutex::scoped_lock(pcie_zpu_iface_registry_mutex); + + if (get_pcie_zpu_iface_registry().has_key(resource_d)) { + zpu_ctrl = get_pcie_zpu_iface_registry()[resource_d].lock(); + } else { + kernel_proxy.open(dev_info.interface_path); + zpu_ctrl = x300_make_ctrl_iface_pcie(kernel_proxy); + //We don't put this zpu_ctrl in the registry because we need + //a persistent niriok_proxy associated with the object + } if (x300_impl::is_claimed(zpu_ctrl)) continue; //claimed by another process //Attempt to autodetect the FPGA type @@ -392,6 +411,8 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr) //Tell the quirks object which FIFOs carry TX stream data const uint32_t tx_data_fifos[2] = {X300_RADIO_DEST_PREFIX_TX, X300_RADIO_DEST_PREFIX_TX + 3}; mb.rio_fpga_interface->get_kernel_proxy().get_rio_quirks().register_tx_streams(tx_data_fifos); + + _tree->create<double>(mb_path / "link_max_rate").set(X300_MAX_RATE_PCIE); } BOOST_FOREACH(const std::string &key, dev_addr.keys()) @@ -456,12 +477,20 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr) << "UHD will use the auto-detected max frame size for this connection." << std::endl; } + + _tree->create<double>(mb_path / "link_max_rate").set(X300_MAX_RATE_10GIGE); } //create basic communication UHD_MSG(status) << "Setup basic communication..." << std::endl; if (mb.xport_path == "nirio") { - mb.zpu_ctrl = x300_make_ctrl_iface_pcie(mb.rio_fpga_interface->get_kernel_proxy()); + boost::mutex::scoped_lock(pcie_zpu_iface_registry_mutex); + if (get_pcie_zpu_iface_registry().has_key(mb.addr)) { + throw uhd::assertion_error("Someone else has a ZPU transport to the device open. Internal error!"); + } else { + mb.zpu_ctrl = x300_make_ctrl_iface_pcie(mb.rio_fpga_interface->get_kernel_proxy()); + get_pcie_zpu_iface_registry()[mb.addr] = boost::weak_ptr<wb_iface>(mb.zpu_ctrl); + } } else { mb.zpu_ctrl = x300_make_ctrl_iface_enet(udp_simple::make_connected(mb.addr, BOOST_STRINGIZE(X300_FW_COMMS_UDP_PORT))); @@ -830,8 +859,14 @@ x300_impl::~x300_impl(void) //kill the claimer task and unclaim the device mb.claimer_task.reset(); - mb.zpu_ctrl->poke32(SR_ADDR(X300_FW_SHMEM_BASE, X300_FW_SHMEM_CLAIM_TIME), 0); - mb.zpu_ctrl->poke32(SR_ADDR(X300_FW_SHMEM_BASE, X300_FW_SHMEM_CLAIM_SRC), 0); + { //Critical section + boost::mutex::scoped_lock(pcie_zpu_iface_registry_mutex); + mb.zpu_ctrl->poke32(SR_ADDR(X300_FW_SHMEM_BASE, X300_FW_SHMEM_CLAIM_TIME), 0); + mb.zpu_ctrl->poke32(SR_ADDR(X300_FW_SHMEM_BASE, X300_FW_SHMEM_CLAIM_SRC), 0); + //If the process is killed, the entire registry will disappear so we + //don't need to worry about unclean shutdowns here. + get_pcie_zpu_iface_registry().pop(mb.addr); + } } } catch(...) @@ -1133,11 +1168,14 @@ x300_impl::both_xports_t x300_impl::make_transport( if (mb.loaded_fpga_image == "HGS") { if (mb.router_dst_here == X300_XB_DST_E0) { eth_data_rec_frame_size = X300_1GE_DATA_FRAME_MAX_SIZE; + _tree->access<double>("/mboards/"+boost::lexical_cast<std::string>(mb_index) / "link_max_rate").set(X300_MAX_RATE_1GIGE); } else if (mb.router_dst_here == X300_XB_DST_E1) { eth_data_rec_frame_size = X300_10GE_DATA_FRAME_MAX_SIZE; + _tree->access<double>("/mboards/"+boost::lexical_cast<std::string>(mb_index) / "link_max_rate").set(X300_MAX_RATE_10GIGE); } } else if (mb.loaded_fpga_image == "XGS") { - eth_data_rec_frame_size = X300_10GE_DATA_FRAME_MAX_SIZE; + eth_data_rec_frame_size = X300_10GE_DATA_FRAME_MAX_SIZE; + _tree->access<double>("/mboards/"+boost::lexical_cast<std::string>(mb_index) / "link_max_rate").set(X300_MAX_RATE_10GIGE); } if (eth_data_rec_frame_size == 0) { @@ -1380,7 +1418,8 @@ void x300_impl::update_time_source(mboard_members_t &mb, const std::string &sour //check for valid pps if (!is_pps_present(mb.zpu_ctrl)) { - throw uhd::runtime_error((boost::format("The %d PPS was not detected. Please check the PPS source and try again.") % source).str()); + // TODO - Implement intelligent PPS detection + /* throw uhd::runtime_error((boost::format("The %d PPS was not detected. Please check the PPS source and try again.") % source).str()); */ } } @@ -1453,13 +1492,18 @@ void x300_impl::set_fp_gpio(gpio_core_200::sptr gpio, const std::string &attr, c void x300_impl::claimer_loop(wb_iface::sptr iface) { - iface->poke32(SR_ADDR(X300_FW_SHMEM_BASE, X300_FW_SHMEM_CLAIM_TIME), time(NULL)); - iface->poke32(SR_ADDR(X300_FW_SHMEM_BASE, X300_FW_SHMEM_CLAIM_SRC), get_process_hash()); - boost::this_thread::sleep(boost::posix_time::milliseconds(1500)); //1.5 seconds + { //Critical section + boost::mutex::scoped_lock(claimer_mutex); + iface->poke32(SR_ADDR(X300_FW_SHMEM_BASE, X300_FW_SHMEM_CLAIM_TIME), time(NULL)); + iface->poke32(SR_ADDR(X300_FW_SHMEM_BASE, X300_FW_SHMEM_CLAIM_SRC), get_process_hash()); + } + boost::this_thread::sleep(boost::posix_time::milliseconds(1000)); //1 second } bool x300_impl::is_claimed(wb_iface::sptr iface) { + boost::mutex::scoped_lock(claimer_mutex); + //If timed out then device is definitely unclaimed if (iface->peek32(SR_ADDR(X300_FW_SHMEM_BASE, X300_FW_SHMEM_CLAIM_STATUS)) == 0) return false; diff --git a/host/lib/usrp/x300/x300_impl.hpp b/host/lib/usrp/x300/x300_impl.hpp index 259ea253d..90aed2fdb 100644 --- a/host/lib/usrp/x300/x300_impl.hpp +++ b/host/lib/usrp/x300/x300_impl.hpp @@ -53,7 +53,7 @@ static const std::string X300_FW_FILE_NAME = "usrp_x300_fw.bin"; static const double X300_DEFAULT_TICK_RATE = 200e6; //Hz -static const double X300_BUS_CLOCK_RATE = 175e6; //Hz +static const double X300_BUS_CLOCK_RATE = 166.666667e6; //Hz static const size_t X300_TX_HW_BUFF_SIZE = 0x90000; //576KiB static const size_t X300_TX_FC_RESPONSE_FREQ = 8; //per flow-control window @@ -76,6 +76,19 @@ static const size_t X300_ETH_MSG_NUM_FRAMES = 32; static const size_t X300_ETH_DATA_NUM_FRAMES = 32; static const double X300_DEFAULT_SYSREF_RATE = 10e6; +static const size_t X300_TX_MAX_HDR_LEN = // bytes + sizeof(boost::uint32_t) // Header + + sizeof(uhd::transport::vrt::if_packet_info_t().sid) // SID + + sizeof(uhd::transport::vrt::if_packet_info_t().tsf); // Timestamp +static const size_t X300_RX_MAX_HDR_LEN = // bytes + sizeof(boost::uint32_t) // Header + + sizeof(uhd::transport::vrt::if_packet_info_t().sid) // SID + + sizeof(uhd::transport::vrt::if_packet_info_t().tsf); // Timestamp + +static const size_t X300_MAX_RATE_PCIE = 800000000; // bytes/s +static const size_t X300_MAX_RATE_10GIGE = 800000000; // bytes/s +static const size_t X300_MAX_RATE_1GIGE = 100000000; // bytes/s + #define X300_RADIO_DEST_PREFIX_TX 0 #define X300_RADIO_DEST_PREFIX_CTRL 1 #define X300_RADIO_DEST_PREFIX_RX 2 @@ -139,6 +152,7 @@ public: bool recv_async_msg(uhd::async_metadata_t &, double); // used by x300_find_with_addr to find X300 devices. + static boost::mutex claimer_mutex; //All claims and checks in this process are serialized static bool is_claimed(uhd::wb_iface::sptr); enum x300_mboard_t { diff --git a/host/lib/usrp/x300/x300_io_impl.cpp b/host/lib/usrp/x300/x300_io_impl.cpp index 85de34a54..9263c9b44 100644 --- a/host/lib/usrp/x300/x300_io_impl.cpp +++ b/host/lib/usrp/x300/x300_io_impl.cpp @@ -242,6 +242,8 @@ struct x300_tx_fc_guts_t boost::shared_ptr<x300_impl::async_md_type> old_async_queue; }; +#define X300_ASYNC_EVENT_CODE_FLOW_CTRL 0 + static size_t get_tx_flow_control_window(size_t frame_size, const device_addr_t& tx_args) { double hw_buff_size = tx_args.cast<double>("send_buff_size", X300_TX_HW_BUFF_SIZE); @@ -283,23 +285,28 @@ static void handle_tx_async_msgs(boost::shared_ptr<x300_tx_fc_guts_t> guts, zero return; } - //catch the flow control packets and react - if (endian_conv(packet_buff[if_packet_info.num_header_words32+0]) == 0) - { - const size_t seq = endian_conv(packet_buff[if_packet_info.num_header_words32+1]); - guts->seq_queue.push_with_haste(seq); - return; - } - //fill in the async metadata async_metadata_t metadata; load_metadata_from_buff( endian_conv, metadata, if_packet_info, packet_buff, clock->get_master_clock_rate(), guts->stream_channel); - guts->async_queue->push_with_pop_on_full(metadata); - metadata.channel = guts->device_channel; - guts->old_async_queue->push_with_pop_on_full(metadata); - standard_async_msg_prints(metadata); + + //The FC response and the burst ack are two indicators that the radio + //consumed packets. Use them to update the FC metadata + if (metadata.event_code == X300_ASYNC_EVENT_CODE_FLOW_CTRL or + metadata.event_code == async_metadata_t::EVENT_CODE_BURST_ACK + ) { + const size_t seq = metadata.user_payload[0]; + guts->seq_queue.push_with_pop_on_full(seq); + } + + //FC responses don't propagate up to the user so filter them here + if (metadata.event_code != X300_ASYNC_EVENT_CODE_FLOW_CTRL) { + guts->async_queue->push_with_pop_on_full(metadata); + metadata.channel = guts->device_channel; + guts->old_async_queue->push_with_pop_on_full(metadata); + standard_async_msg_prints(metadata); + } } static managed_send_buffer::sptr get_tx_buff_with_flowctrl( @@ -319,7 +326,9 @@ static managed_send_buffer::sptr get_tx_buff_with_flowctrl( } managed_send_buffer::sptr buff = xport->get_send_buff(timeout); - if (buff) guts->last_seq_out++; //update seq, this will actually be a send + if (buff) { + guts->last_seq_out++; //update seq, this will actually be a send + } return buff; } @@ -399,15 +408,9 @@ rx_streamer::sptr x300_impl::get_rx_stream(const uhd::stream_args_t &args_) both_xports_t xport = this->make_transport(mb_index, dest, X300_RADIO_DEST_PREFIX_RX, device_addr, data_sid); UHD_LOG << boost::format("data_sid = 0x%08x, actual recv_buff_size = %d\n") % data_sid % xport.recv_buff_size << std::endl; - //calculate packet size - static const size_t hdr_size = 0 - + vrt::num_vrl_words32*sizeof(boost::uint32_t) - + 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().cid) //no class id ever used - - sizeof(vrt::if_packet_info_t().tsi) //no int time ever used - ; - const size_t bpp = xport.recv->get_recv_frame_size() - hdr_size; // bytes per packet + // To calculate the max number of samples per packet, we assume the maximum header length + // to avoid fragmentation should the entire header be used. + const size_t bpp = xport.recv->get_recv_frame_size() - X300_RX_MAX_HDR_LEN; // bytes per packet const size_t bpi = convert::get_bytes_per_item(args.otw_format); // bytes per item const size_t spp = unsigned(args.args.cast<double>("spp", bpp/bpi)); // samples per packet @@ -568,15 +571,9 @@ tx_streamer::sptr x300_impl::get_tx_stream(const uhd::stream_args_t &args_) both_xports_t xport = this->make_transport(mb_index, dest, X300_RADIO_DEST_PREFIX_TX, device_addr, data_sid); UHD_LOG << boost::format("data_sid = 0x%08x\n") % data_sid << std::endl; - //calculate packet size - static const size_t hdr_size = 0 - + vrt::num_vrl_words32*sizeof(boost::uint32_t) - + 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().cid) //no class id ever used - - sizeof(vrt::if_packet_info_t().tsi) //no int time ever used - ; - const size_t bpp = xport.send->get_send_frame_size() - hdr_size; + // To calculate the max number of samples per packet, we assume the maximum header length + // to avoid fragmentation should the entire header be used. + const size_t bpp = xport.send->get_send_frame_size() - X300_TX_MAX_HDR_LEN; const size_t bpi = convert::get_bytes_per_item(args.otw_format); const size_t spp = unsigned(args.args.cast<double>("spp", bpp/bpi)); diff --git a/host/lib/usrp/x300/x300_regs.hpp b/host/lib/usrp/x300/x300_regs.hpp index fb1786deb..cf1e33695 100644 --- a/host/lib/usrp/x300/x300_regs.hpp +++ b/host/lib/usrp/x300/x300_regs.hpp @@ -124,9 +124,12 @@ static const uint32_t FPGA_PCIE_SIG_REG = PCIE_FPGA_REG(0x0000); static const uint32_t FPGA_CNTR_LO_REG = PCIE_FPGA_REG(0x0004); static const uint32_t FPGA_CNTR_HI_REG = PCIE_FPGA_REG(0x0008); static const uint32_t FPGA_CNTR_FREQ_REG = PCIE_FPGA_REG(0x000C); +static const uint32_t FPGA_STATUS_REG = PCIE_FPGA_REG(0x0020); static const uint32_t FPGA_USR_SIG_REG_BASE = PCIE_FPGA_REG(0x0030); static const uint32_t FPGA_USR_SIG_REG_SIZE = 16; +static const uint32_t FPGA_STATUS_DMA_ACTIVE_MASK = 0x3F3F0000; + static const uint32_t PCIE_TX_DMA_REG_BASE = PCIE_FPGA_REG(0x0200); static const uint32_t PCIE_RX_DMA_REG_BASE = PCIE_FPGA_REG(0x0400); @@ -139,12 +142,15 @@ static const uint32_t DMA_PKT_COUNT_REG = 0xC; #define PCIE_TX_DMA_REG(REG, CHAN) (PCIE_TX_DMA_REG_BASE + (CHAN*DMA_REG_GRP_SIZE) + REG) #define PCIE_RX_DMA_REG(REG, CHAN) (PCIE_RX_DMA_REG_BASE + (CHAN*DMA_REG_GRP_SIZE) + REG) -static const uint32_t DMA_CTRL_RESET = 1; +static const uint32_t DMA_CTRL_DISABLED = 0x00000000; +static const uint32_t DMA_CTRL_ENABLED = 0x00000002; +static const uint32_t DMA_CTRL_CLEAR_STB = 0x00000001; static const uint32_t DMA_CTRL_SW_BUF_U64 = (3 << 4); static const uint32_t DMA_CTRL_SW_BUF_U32 = (2 << 4); static const uint32_t DMA_CTRL_SW_BUF_U16 = (1 << 4); static const uint32_t DMA_CTRL_SW_BUF_U8 = (0 << 4); -static const uint32_t DMA_STATUS_ERROR = 1; +static const uint32_t DMA_STATUS_ERROR = 0x00000001; +static const uint32_t DMA_STATUS_BUSY = 0x00000002; static const uint32_t PCIE_ROUTER_REG_BASE = PCIE_FPGA_REG(0x0500); #define PCIE_ROUTER_REG(X) (PCIE_ROUTER_REG_BASE + X) diff --git a/host/lib/utils/log.cpp b/host/lib/utils/log.cpp index d6d1786c7..31ee0c991 100644 --- a/host/lib/utils/log.cpp +++ b/host/lib/utils/log.cpp @@ -1,5 +1,5 @@ // -// Copyright 2012 Ettus Research LLC +// Copyright 2012,2014 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -117,11 +117,11 @@ UHD_SINGLETON_FCN(log_resource_type, log_rs); **********************************************************************/ //! get the relative file path from the host directory static std::string get_rel_file_path(const fs::path &file){ - fs::path abs_path = file.branch_path(); + fs::path abs_path = file.parent_path(); fs::path rel_path = file.leaf(); while (not abs_path.empty() and abs_path.leaf() != "host"){ rel_path = abs_path.leaf() / rel_path; - abs_path = abs_path.branch_path(); + abs_path = abs_path.parent_path(); } return rel_path.string(); } diff --git a/host/tests/CMakeLists.txt b/host/tests/CMakeLists.txt index 2a40d0050..7c4815004 100644 --- a/host/tests/CMakeLists.txt +++ b/host/tests/CMakeLists.txt @@ -28,6 +28,7 @@ SET(test_sources buffer_test.cpp byteswap_test.cpp convert_test.cpp + cast_test.cpp dict_test.cpp error_test.cpp gain_group_test.cpp diff --git a/host/tests/cast_test.cpp b/host/tests/cast_test.cpp new file mode 100644 index 000000000..6b8a4c527 --- /dev/null +++ b/host/tests/cast_test.cpp @@ -0,0 +1,33 @@ +// +// Copyright 2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <iostream> +#include <boost/test/unit_test.hpp> +#include <boost/cstdint.hpp> +#include <uhd/utils/cast.hpp> + +BOOST_AUTO_TEST_CASE(test_mac_addr){ + std::string in = "0x0100"; + boost::uint16_t correct_result = 256; + boost::uint16_t x = uhd::cast::hexstr_cast<boost::uint16_t>(in); + //boost::uint16_t x = uhd::cast::hexstr_cast(in); + std::cout + << "Testing hex -> uint16_t conversion. " + << in << " == " << std::hex << x << "?" << std::endl; + BOOST_CHECK_EQUAL(x, correct_result); +} + diff --git a/host/utils/uhd_cal_rx_iq_balance.cpp b/host/utils/uhd_cal_rx_iq_balance.cpp index 551da7544..3188e02a0 100644 --- a/host/utils/uhd_cal_rx_iq_balance.cpp +++ b/host/utils/uhd_cal_rx_iq_balance.cpp @@ -121,37 +121,14 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ if (vm.count("help")){ std::cout << boost::format("USRP Generate RX IQ Balance Calibration Table %s") % desc << std::endl; std::cout << - "This application measures leakage between RX and TX on an XCVR daughterboard to self-calibrate.\n" + "This application measures leakage between RX and TX on a transceiver daughterboard to self-calibrate.\n" + "Note: Not all daughterboards support this feature. Refer to the UHD manual for details.\n" << std::endl; return EXIT_FAILURE; } - //create a usrp device - std::cout << std::endl; - std::cout << boost::format("Creating the usrp device with: %s...") % args << std::endl; - uhd::usrp::multi_usrp::sptr usrp = uhd::usrp::multi_usrp::make(args); - - // Configure subdev - if (vm.count("subdev")) { - usrp->set_tx_subdev_spec(subdev); - usrp->set_rx_subdev_spec(subdev); - } - UHD_MSG(status) << "Running calibration for " << usrp->get_tx_subdev_name(0) << std::endl; - serial = get_serial(usrp, "tx"); - UHD_MSG(status) << "Daughterboard serial: " << serial << std::endl; - - //set the antennas to cal - if (not uhd::has(usrp->get_rx_antennas(), "CAL") or not uhd::has(usrp->get_tx_antennas(), "CAL")){ - throw std::runtime_error("This board does not have the CAL antenna option, cannot self-calibrate."); - } - usrp->set_rx_antenna("CAL"); - usrp->set_tx_antenna("CAL"); - - //fail if daughterboard has no serial - check_for_empty_serial(usrp, "RX", "rx", args); - - //set optimum defaults - set_optimum_defaults(usrp); + // Create a USRP device + uhd::usrp::multi_usrp::sptr usrp = setup_usrp_for_cal(args, subdev, serial); //create a receive streamer uhd::stream_args_t stream_args("fc32"); //complex floats diff --git a/host/utils/uhd_cal_tx_dc_offset.cpp b/host/utils/uhd_cal_tx_dc_offset.cpp index eb82db826..b5c5293f0 100644 --- a/host/utils/uhd_cal_tx_dc_offset.cpp +++ b/host/utils/uhd_cal_tx_dc_offset.cpp @@ -123,37 +123,14 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ if (vm.count("help")){ std::cout << boost::format("USRP Generate TX DC Offset Calibration Table %s") % desc << std::endl; std::cout << - "This application measures leakage between RX and TX on an XCVR daughterboard to self-calibrate.\n" + "This application measures leakage between RX and TX on a transceiver daughterboard to self-calibrate.\n" + "Note: Not all daughterboards support this feature. Refer to the UHD manual for details.\n" << std::endl; return EXIT_FAILURE; } - //create a usrp device - std::cout << std::endl; - std::cout << boost::format("Creating the usrp device with: %s...") % args << std::endl; - uhd::usrp::multi_usrp::sptr usrp = uhd::usrp::multi_usrp::make(args); - - // Configure subdev - if (vm.count("subdev")) { - usrp->set_tx_subdev_spec(subdev); - usrp->set_rx_subdev_spec(subdev); - } - UHD_MSG(status) << "Running calibration for " << usrp->get_tx_subdev_name(0) << std::endl; - serial = get_serial(usrp, "tx"); - UHD_MSG(status) << "Daughterboard serial: " << serial << std::endl; - - //set the antennas to cal - if (not uhd::has(usrp->get_rx_antennas(), "CAL") or not uhd::has(usrp->get_tx_antennas(), "CAL")){ - throw std::runtime_error("This board does not have the CAL antenna option, cannot self-calibrate."); - } - usrp->set_rx_antenna("CAL"); - usrp->set_tx_antenna("CAL"); - - //fail if daughterboard has no serial - check_for_empty_serial(usrp, "TX", "tx", args); - - //set optimum defaults - set_optimum_defaults(usrp); + // Create a USRP device + uhd::usrp::multi_usrp::sptr usrp = setup_usrp_for_cal(args, subdev, serial); //create a receive streamer uhd::stream_args_t stream_args("fc32"); //complex floats diff --git a/host/utils/uhd_cal_tx_iq_balance.cpp b/host/utils/uhd_cal_tx_iq_balance.cpp index 786aac061..6461b3d71 100644 --- a/host/utils/uhd_cal_tx_iq_balance.cpp +++ b/host/utils/uhd_cal_tx_iq_balance.cpp @@ -18,12 +18,7 @@ #include "usrp_cal_utils.hpp" #include <uhd/utils/thread_priority.hpp> #include <uhd/utils/safe_main.hpp> -#include <uhd/utils/paths.hpp> -#include <uhd/utils/algorithm.hpp> -#include <uhd/utils/msg.hpp> -#include <uhd/usrp/multi_usrp.hpp> #include <boost/program_options.hpp> -#include <boost/format.hpp> #include <boost/thread/thread.hpp> #include <boost/math/special_functions/round.hpp> #include <iostream> @@ -124,37 +119,14 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ if (vm.count("help")){ std::cout << boost::format("USRP Generate TX IQ Balance Calibration Table %s") % desc << std::endl; std::cout << - "This application measures leakage between RX and TX on a daughterboard to self-calibrate.\n" + "This application measures leakage between RX and TX on a transceiver daughterboard to self-calibrate.\n" + "Note: Not all daughterboards support this feature. Refer to the UHD manual for details.\n" << std::endl; return EXIT_FAILURE; } - //create a usrp device - std::cout << std::endl; - std::cout << boost::format("Creating the usrp device with: %s...") % args << std::endl; - uhd::usrp::multi_usrp::sptr usrp = uhd::usrp::multi_usrp::make(args); - - // Configure subdev - if (vm.count("subdev")) { - usrp->set_tx_subdev_spec(subdev); - usrp->set_rx_subdev_spec(subdev); - } - UHD_MSG(status) << "Running calibration for " << usrp->get_tx_subdev_name(0) << std::endl; - serial = get_serial(usrp, "tx"); - UHD_MSG(status) << "Daughterboard serial: " << serial << std::endl; - - //set the antennas to cal - if (not uhd::has(usrp->get_rx_antennas(), "CAL") or not uhd::has(usrp->get_tx_antennas(), "CAL")){ - throw std::runtime_error("This board does not have the CAL antenna option, cannot self-calibrate."); - } - usrp->set_rx_antenna("CAL"); - usrp->set_tx_antenna("CAL"); - - //fail if daughterboard has no serial - check_for_empty_serial(usrp, "TX", "tx", args); - - //set optimum defaults - set_optimum_defaults(usrp); + // Create a USRP device + uhd::usrp::multi_usrp::sptr usrp = setup_usrp_for_cal(args, subdev, serial); //create a receive streamer uhd::stream_args_t stream_args("fc32"); //complex floats diff --git a/host/utils/usrp_cal_utils.hpp b/host/utils/usrp_cal_utils.hpp index 5aff5e22f..9e7f4c469 100644 --- a/host/utils/usrp_cal_utils.hpp +++ b/host/utils/usrp_cal_utils.hpp @@ -20,6 +20,8 @@ #include <uhd/usrp/multi_usrp.hpp> #include <uhd/usrp/dboard_eeprom.hpp> #include <uhd/utils/paths.hpp> +#include <uhd/utils/algorithm.hpp> +#include <uhd/utils/msg.hpp> #include <boost/filesystem.hpp> #include <boost/format.hpp> #include <iostream> @@ -50,6 +52,8 @@ static const size_t default_num_samps = 10000; **********************************************************************/ static inline void set_optimum_defaults(uhd::usrp::multi_usrp::sptr usrp){ uhd::property_tree::sptr tree = usrp->get_device()->get_tree(); + // Will work on 1st subdev, top-level must make sure it's the right one + uhd::usrp::subdev_spec_t subdev_spec = usrp->get_rx_subdev_spec(); const uhd::fs_path mb_path = "/mboards/0"; const std::string mb_name = tree->access<std::string>(mb_path / "name").get(); @@ -69,7 +73,7 @@ static inline void set_optimum_defaults(uhd::usrp::multi_usrp::sptr usrp){ throw std::runtime_error("self-calibration is not supported for this hardware"); } - const uhd::fs_path tx_fe_path = "/mboards/0/dboards/A/tx_frontends/0"; + const uhd::fs_path tx_fe_path = "/mboards/0/dboards/" + subdev_spec[0].db_name + "/tx_frontends/0"; const std::string tx_name = tree->access<std::string>(tx_fe_path / "name").get(); if (tx_name.find("WBX") != std::string::npos){ usrp->set_tx_gain(0); @@ -87,7 +91,7 @@ static inline void set_optimum_defaults(uhd::usrp::multi_usrp::sptr usrp){ throw std::runtime_error("self-calibration is not supported for this hardware"); } - const uhd::fs_path rx_fe_path = "/mboards/0/dboards/A/rx_frontends/0"; + const uhd::fs_path rx_fe_path = "/mboards/0/dboards/" + subdev_spec[0].db_name + "/rx_frontends/0"; const std::string rx_name = tree->access<std::string>(rx_fe_path / "name").get(); if (rx_name.find("WBX") != std::string::npos){ usrp->set_rx_gain(25); @@ -110,24 +114,19 @@ static inline void set_optimum_defaults(uhd::usrp::multi_usrp::sptr usrp){ /*********************************************************************** * Check for empty serial **********************************************************************/ - void check_for_empty_serial( - uhd::usrp::multi_usrp::sptr usrp, - std::string XX, - std::string xx, - std::string uhd_args + uhd::usrp::multi_usrp::sptr usrp ){ + // Will work on 1st subdev, top-level must make sure it's the right one + uhd::usrp::subdev_spec_t subdev_spec = usrp->get_rx_subdev_spec(); //extract eeprom uhd::property_tree::sptr tree = usrp->get_device()->get_tree(); - const uhd::fs_path db_path = "/mboards/0/dboards/A/" + xx + "_eeprom"; + // This only works with transceiver boards, so we can always check rx side + const uhd::fs_path db_path = "/mboards/0/dboards/" + subdev_spec[0].db_name + "/rx_eeprom"; const uhd::usrp::dboard_eeprom_t db_eeprom = tree->access<uhd::usrp::dboard_eeprom_t>(db_path).get(); - std::string args_str = ""; - if(uhd_args != "") args_str = str(boost::format(" --args=%s") % uhd_args); - - std::string error_string = str(boost::format("This %s dboard has no serial!\n\nPlease see the Calibration documentation for details on how to fix this.") % XX); - + std::string error_string = "This dboard has no serial!\n\nPlease see the Calibration documentation for details on how to fix this."; if (db_eeprom.serial.empty()) throw std::runtime_error(error_string); } @@ -188,6 +187,7 @@ static std::string get_serial( const std::string &tx_rx ){ uhd::property_tree::sptr tree = usrp->get_device()->get_tree(); + // Will work on 1st subdev, top-level must make sure it's the right one uhd::usrp::subdev_spec_t subdev_spec = usrp->get_rx_subdev_spec(); const uhd::fs_path db_path = "/mboards/0/dboards/" + subdev_spec[0].db_name + "/" + tx_rx + "_eeprom"; const uhd::usrp::dboard_eeprom_t db_eeprom = tree->access<uhd::usrp::dboard_eeprom_t>(db_path).get(); @@ -257,8 +257,8 @@ static void capture_samples( //validate the received data if (md.error_code != uhd::rx_metadata_t::ERROR_CODE_NONE){ throw std::runtime_error(str(boost::format( - "Unexpected error code 0x%x" - ) % md.error_code)); + "Receiver error: %s" + ) % md.strerror())); } //we can live if all the data didnt come in if (num_rx_samps > buff.size()/2){ @@ -270,3 +270,37 @@ static void capture_samples( } } +/*********************************************************************** + * Setup function + **********************************************************************/ +static uhd::usrp::multi_usrp::sptr setup_usrp_for_cal(std::string &args, std::string &subdev, std::string &serial) +{ + std::cout << std::endl; + std::cout << boost::format("Creating the usrp device with: %s...") % args << std::endl; + uhd::usrp::multi_usrp::sptr usrp = uhd::usrp::multi_usrp::make(args); + + // Configure subdev + if (!subdev.empty()) { + usrp->set_tx_subdev_spec(subdev); + usrp->set_rx_subdev_spec(subdev); + } + UHD_MSG(status) << "Running calibration for " << usrp->get_tx_subdev_name(0) << std::endl; + serial = get_serial(usrp, "tx"); + UHD_MSG(status) << "Daughterboard serial: " << serial << std::endl; + + //set the antennas to cal + if (not uhd::has(usrp->get_rx_antennas(), "CAL") or not uhd::has(usrp->get_tx_antennas(), "CAL")){ + throw std::runtime_error("This board does not have the CAL antenna option, cannot self-calibrate."); + } + usrp->set_rx_antenna("CAL"); + usrp->set_tx_antenna("CAL"); + + //fail if daughterboard has no serial + check_for_empty_serial(usrp); + + //set optimum defaults + set_optimum_defaults(usrp); + + return usrp; +} + diff --git a/host/utils/usrp_n2xx_simple_net_burner.cpp b/host/utils/usrp_n2xx_simple_net_burner.cpp index 277e807d9..cecac5588 100644 --- a/host/utils/usrp_n2xx_simple_net_burner.cpp +++ b/host/utils/usrp_n2xx_simple_net_burner.cpp @@ -17,13 +17,13 @@ #include <csignal> #include <iostream> -#include <map> #include <fstream> #include <time.h> #include <vector> #include <boost/foreach.hpp> #include <boost/asio.hpp> +#include <boost/filesystem.hpp> #include <boost/program_options.hpp> #include <boost/assign.hpp> #include <boost/assign/list_of.hpp> @@ -32,21 +32,97 @@ #include <boost/filesystem.hpp> #include <boost/thread/thread.hpp> -#include "usrp_simple_burner_utils.hpp" #include <uhd/exception.hpp> #include <uhd/property_tree.hpp> #include <uhd/transport/if_addrs.hpp> #include <uhd/transport/udp_simple.hpp> +#include <uhd/types/dict.hpp> #include <uhd/utils/byteswap.hpp> #include <uhd/utils/images.hpp> #include <uhd/utils/safe_main.hpp> #include <uhd/utils/safe_call.hpp> +namespace fs = boost::filesystem; namespace po = boost::program_options; using namespace boost::algorithm; using namespace uhd; using namespace uhd::transport; +#define UDP_FW_UPDATE_PORT 49154 +#define UDP_MAX_XFER_BYTES 1024 +#define UDP_TIMEOUT 3 +#define UDP_POLL_INTERVAL 0.10 //in seconds +#define USRP2_FW_PROTO_VERSION 7 //should be unused after r6 +#define USRP2_UDP_UPDATE_PORT 49154 +#define FLASH_DATA_PACKET_SIZE 256 +#define FPGA_IMAGE_SIZE_BYTES 1572864 +#define FW_IMAGE_SIZE_BYTES 31744 +#define PROD_FPGA_IMAGE_LOCATION_ADDR 0x00180000 +#define PROD_FW_IMAGE_LOCATION_ADDR 0x00300000 +#define SAFE_FPGA_IMAGE_LOCATION_ADDR 0x00000000 +#define SAFE_FW_IMAGE_LOCATION_ADDR 0x003F0000 + +typedef enum { + UNKNOWN = ' ', + + USRP2_QUERY = 'a', + USRP2_ACK = 'A', + + GET_FLASH_INFO_CMD = 'f', + GET_FLASH_INFO_ACK = 'F', + + ERASE_FLASH_CMD = 'e', + ERASE_FLASH_ACK = 'E', + + CHECK_ERASING_DONE_CMD = 'd', + DONE_ERASING_ACK = 'D', + NOT_DONE_ERASING_ACK = 'B', + + WRITE_FLASH_CMD = 'w', + WRITE_FLASH_ACK = 'W', + + READ_FLASH_CMD = 'r', + READ_FLASH_ACK = 'R', + + RESET_USRP_CMD = 's', + RESET_USRP_ACK = 'S', + + GET_HW_REV_CMD = 'v', + GET_HW_REV_ACK = 'V', + +} usrp2_fw_update_id_t; + +typedef struct { + uint32_t proto_ver; + uint32_t id; + uint32_t seq; + union { + uint32_t ip_addr; + uint32_t hw_rev; + struct { + uint32_t flash_addr; + uint32_t length; + uint8_t data[256]; + } flash_args; + struct { + uint32_t sector_size_bytes; + uint32_t memory_size_bytes; + } flash_info_args; + } data; +} usrp2_fw_update_data_t; + +//Mapping revision numbers to filenames +uhd::dict<boost::uint32_t, std::string> filename_map = boost::assign::map_list_of + (0xa, "n200_r3") + (0x100a, "n200_r4") + (0x10a, "n210_r3") + (0x110a, "n210_r4") +; + +boost::uint8_t usrp2_update_data_in_mem[udp_simple::mtu]; +boost::uint8_t fpga_image[FPGA_IMAGE_SIZE_BYTES]; +boost::uint8_t fw_image[FW_IMAGE_SIZE_BYTES]; + /*********************************************************************** * Signal handlers **********************************************************************/ @@ -66,59 +142,94 @@ void sig_int_handler(int){ } } -//Mapping revision numbers to filenames -std::map<boost::uint32_t, std::string> filename_map = boost::assign::map_list_of - (0xa, "n200_r3") - (0x100a, "n200_r4") - (0x10a, "n210_r3") - (0x110a, "n210_r4") -; +/*********************************************************************** + * List all connected USRP N2XX devices + **********************************************************************/ +void list_usrps(){ + udp_simple::sptr udp_bc_transport; + const usrp2_fw_update_data_t *update_data_in = reinterpret_cast<const usrp2_fw_update_data_t *>(usrp2_update_data_in_mem); + boost::uint32_t hw_rev; -//Images and image sizes, to be populated as necessary -boost::uint8_t fpga_image[FPGA_IMAGE_SIZE_BYTES]; -boost::uint8_t fw_image[FW_IMAGE_SIZE_BYTES]; -int fpga_image_size = 0; -int fw_image_size = 0; + usrp2_fw_update_data_t usrp2_ack_pkt = usrp2_fw_update_data_t(); + usrp2_ack_pkt.proto_ver = htonx<boost::uint32_t>(USRP2_FW_PROTO_VERSION); + usrp2_ack_pkt.id = htonx<boost::uint32_t>(USRP2_QUERY); + + std::cout << "Available USRP N2XX devices:" << std::endl; -//For non-standard images not covered by uhd::find_image_path() -bool does_image_exist(std::string image_filepath){ + //Send UDP packets to all broadcast addresses + BOOST_FOREACH(const if_addrs_t &if_addrs, get_if_addrs()){ + //Avoid the loopback device + if(if_addrs.inet == boost::asio::ip::address_v4::loopback().to_string()) continue; + udp_bc_transport = udp_simple::make_broadcast(if_addrs.bcast, BOOST_STRINGIZE(USRP2_UDP_UPDATE_PORT)); + udp_bc_transport->send(boost::asio::buffer(&usrp2_ack_pkt, sizeof(usrp2_ack_pkt))); - std::ifstream ifile((char*)image_filepath.c_str()); - return ifile; + size_t len = udp_bc_transport->recv(boost::asio::buffer(usrp2_update_data_in_mem), UDP_TIMEOUT); + if(len > offsetof(usrp2_fw_update_data_t, data) and ntohl(update_data_in->id) == USRP2_ACK){ + usrp2_ack_pkt.id = htonx<boost::uint32_t>(GET_HW_REV_CMD); + udp_bc_transport->send(boost::asio::buffer(&usrp2_ack_pkt, sizeof(usrp2_ack_pkt))); + + size_t len = udp_bc_transport->recv(boost::asio::buffer(usrp2_update_data_in_mem), UDP_TIMEOUT); + if(len > offsetof(usrp2_fw_update_data_t, data) and ntohl(update_data_in->id) == GET_HW_REV_ACK){ + hw_rev = ntohl(update_data_in->data.hw_rev); + } + + std::cout << boost::format(" * %s (%s)\n") % udp_bc_transport->get_recv_addr() % filename_map[hw_rev]; + } + } +} + +/*********************************************************************** + * Find USRP N2XX with specified IP address and return type + **********************************************************************/ +boost::uint32_t find_usrp(udp_simple::sptr udp_transport){ + boost::uint32_t hw_rev; + bool found_it = false; + + const usrp2_fw_update_data_t *update_data_in = reinterpret_cast<const usrp2_fw_update_data_t *>(usrp2_update_data_in_mem); + usrp2_fw_update_data_t hw_info_pkt = usrp2_fw_update_data_t(); + hw_info_pkt.proto_ver = htonx<boost::uint32_t>(USRP2_FW_PROTO_VERSION); + hw_info_pkt.id = htonx<boost::uint32_t>(GET_HW_REV_CMD); + udp_transport->send(boost::asio::buffer(&hw_info_pkt, sizeof(hw_info_pkt))); + + //Loop and receive until the timeout + size_t len = udp_transport->recv(boost::asio::buffer(usrp2_update_data_in_mem), UDP_TIMEOUT); + if(len > offsetof(usrp2_fw_update_data_t, data) and ntohl(update_data_in->id) == GET_HW_REV_ACK){ + hw_rev = ntohl(update_data_in->data.hw_rev); + if(filename_map.has_key(hw_rev)){ + std::cout << boost::format("Found %s.\n\n") % filename_map[hw_rev]; + found_it = true; + } + else throw std::runtime_error("Invalid revision found."); + } + if(not found_it) throw std::runtime_error("No USRP N2XX found."); + + return hw_rev; } /*********************************************************************** * Custom filename validation functions **********************************************************************/ -void validate_custom_fpga_file(std::string rev_str, std::string fpga_path){ +void validate_custom_fpga_file(std::string rev_str, std::string& fpga_path){ //Check for existence of file - if(!does_image_exist(fpga_path)) throw std::runtime_error(str(boost::format("No file at specified FPGA path: %s") % fpga_path)); + if(not fs::exists(fpga_path)) throw std::runtime_error(str(boost::format("No file at specified FPGA path: %s") % fpga_path)); //Check to find rev_str in filename uhd::fs_path custom_fpga_path(fpga_path); - if(custom_fpga_path.leaf().find("fw") != std::string::npos){ - throw std::runtime_error(str(boost::format("Invalid FPGA image filename at path: %s\nFilename indicates that this is a firmware image.") - % fpga_path)); - } if(custom_fpga_path.leaf().find(rev_str) == std::string::npos){ throw std::runtime_error(str(boost::format("Invalid FPGA image filename at path: %s\nFilename must contain '%s' to be considered valid for this model.") % fpga_path % rev_str)); } } -void validate_custom_fw_file(std::string rev_str, std::string fw_path){ +void validate_custom_fw_file(std::string rev_str, std::string& fw_path){ //Check for existence of file - if(!does_image_exist(fw_path)) throw std::runtime_error(str(boost::format("No file at specified firmware path: %s") % fw_path)); + if(not fs::exists(fw_path)) throw std::runtime_error(str(boost::format("No file at specified firmware path: %s") % fw_path)); //Check to find truncated rev_str in filename uhd::fs_path custom_fw_path(fw_path); - if(custom_fw_path.leaf().find("fpga") != std::string::npos){ - throw std::runtime_error(str(boost::format("Invalid firmware image filename at path: %s\nFilename indicates that this is an FPGA image.") - % fw_path)); - } if(custom_fw_path.leaf().find(erase_tail_copy(rev_str,3)) == std::string::npos){ throw std::runtime_error(str(boost::format("Invalid firmware image filename at path: %s\nFilename must contain '%s' to be considered valid for this model.") % fw_path % erase_tail_copy(rev_str,3))); @@ -126,89 +237,91 @@ void validate_custom_fw_file(std::string rev_str, std::string fw_path){ } /*********************************************************************** - * Grabbing and validating image binaries + * Reading and validating image binaries **********************************************************************/ -int grab_fpga_image(std::string fpga_path){ +int read_fpga_image(std::string& fpga_path){ - //Reading FPGA image from file - std::ifstream to_read_fpga((char*)fpga_path.c_str(), std::ios::binary); - to_read_fpga.seekg(0, std::ios::end); - fpga_image_size = to_read_fpga.tellg(); - to_read_fpga.seekg(0, std::ios::beg); - char fpga_read[FPGA_IMAGE_SIZE_BYTES]; - to_read_fpga.read(fpga_read,fpga_image_size); - to_read_fpga.close(); - for(int i = 0; i < fpga_image_size; i++) fpga_image[i] = (boost::uint8_t)fpga_read[i]; - - //Checking validity of image + //Check size of given image + std::ifstream fpga_file(fpga_path.c_str(), std::ios::binary); + fpga_file.seekg(0, std::ios::end); + int fpga_image_size = fpga_file.tellg(); if(fpga_image_size > FPGA_IMAGE_SIZE_BYTES){ - throw std::runtime_error(str(boost::format("FPGA image is too large. %d > %d") % fpga_image_size % FPGA_IMAGE_SIZE_BYTES)); + throw std::runtime_error(str(boost::format("FPGA image is too large. %d > %d") + % fpga_image_size % FPGA_IMAGE_SIZE_BYTES)); } - //Check sequence of bytes in image + //Check sequence of bytes in image before reading + boost::uint8_t fpga_test_bytes[63]; + fpga_file.seekg(0, std::ios::beg); + fpga_file.read((char*)fpga_test_bytes,63); bool is_good = false; for(int i = 0; i < 63; i++){ - if((boost::uint8_t)fpga_image[i] == 255) continue; - else if((boost::uint8_t)fpga_image[i] == 170 and - (boost::uint8_t)fpga_image[i+1] == 153){ + if(fpga_test_bytes[i] == 255) continue; + else if(fpga_test_bytes[i] == 170 and + fpga_test_bytes[i+1] == 153){ is_good = true; break; } } + if(not is_good) throw std::runtime_error("Not a valid FPGA image."); - if(!is_good) throw std::runtime_error("Not a valid FPGA image."); + //With image validated, read into utility + fpga_file.seekg(0, std::ios::beg); + fpga_file.read((char*)fpga_image,fpga_image_size); + fpga_file.close(); //Return image size return fpga_image_size; } -int grab_fw_image(std::string fw_path){ - - //Reading firmware image from file - std::ifstream to_read_fw((char*)fw_path.c_str(), std::ios::binary); - to_read_fw.seekg(0, std::ios::end); - fw_image_size = to_read_fw.tellg(); - to_read_fw.seekg(0, std::ios::beg); - char fw_read[FW_IMAGE_SIZE_BYTES]; - to_read_fw.read(fw_read,fw_image_size); - to_read_fw.close(); - for(int i = 0; i < fw_image_size; i++) fw_image[i] = (boost::uint8_t)fw_read[i]; +int read_fw_image(std::string& fw_path){ - //Checking validity of image + //Check size of given image + std::ifstream fw_file(fw_path.c_str(), std::ios::binary); + fw_file.seekg(0, std::ios::end); + int fw_image_size = fw_file.tellg(); if(fw_image_size > FW_IMAGE_SIZE_BYTES){ - throw std::runtime_error(str(boost::format("Firmware image is too large. %d > %d") % fw_image_size % FW_IMAGE_SIZE_BYTES)); + throw std::runtime_error(str(boost::format("Firmware image is too large. %d > %d") + % fw_image_size % FW_IMAGE_SIZE_BYTES)); } - //Check first four bytes of image - for(int i = 0; i < 4; i++) if((boost::uint8_t)fw_image[i] != 11) throw std::runtime_error("Not a valid firmware image."); + //Check sequence of bytes in image before reading + boost::uint8_t fw_test_bytes[4]; + fw_file.seekg(0, std::ios::beg); + fw_file.read((char*)fw_test_bytes,4); + for(int i = 0; i < 4; i++) if(fw_test_bytes[i] != 11) throw std::runtime_error("Not a valid firmware image."); + + //With image validated, read into utility + fw_file.seekg(0, std::ios::beg); + fw_file.read((char*)fw_image,fw_image_size); + fw_file.close(); - //Return image size return fw_image_size; } -boost::uint32_t* get_flash_info(std::string ip_addr){ +boost::uint32_t* get_flash_info(std::string& ip_addr){ boost::uint32_t *flash_info = new boost::uint32_t[2]; - boost::uint8_t usrp2_update_data_in_mem[udp_simple::mtu]; const usrp2_fw_update_data_t *update_data_in = reinterpret_cast<const usrp2_fw_update_data_t *>(usrp2_update_data_in_mem); udp_simple::sptr udp_transport = udp_simple::make_connected(ip_addr, BOOST_STRINGIZE(USRP2_UDP_UPDATE_PORT)); usrp2_fw_update_data_t get_flash_info_pkt = usrp2_fw_update_data_t(); get_flash_info_pkt.proto_ver = htonx<boost::uint32_t>(USRP2_FW_PROTO_VERSION); - get_flash_info_pkt.id = htonx<boost::uint32_t>(USRP2_FW_UPDATE_ID_WATS_TEH_FLASH_INFO_LOL); + get_flash_info_pkt.id = htonx<boost::uint32_t>(GET_FLASH_INFO_CMD); udp_transport->send(boost::asio::buffer(&get_flash_info_pkt, sizeof(get_flash_info_pkt))); //Loop and receive until the timeout size_t len = udp_transport->recv(boost::asio::buffer(usrp2_update_data_in_mem), UDP_TIMEOUT); - if(len > offsetof(usrp2_fw_update_data_t, data) and ntohl(update_data_in->id) == USRP2_FW_UPDATE_ID_HERES_TEH_FLASH_INFO_OMG){ + if(len > offsetof(usrp2_fw_update_data_t, data) and ntohl(update_data_in->id) == GET_FLASH_INFO_ACK){ flash_info[0] = ntohl(update_data_in->data.flash_info_args.sector_size_bytes); flash_info[1] = ntohl(update_data_in->data.flash_info_args.memory_size_bytes); } - else if(ntohl(update_data_in->id) != USRP2_FW_UPDATE_ID_HERES_TEH_FLASH_INFO_OMG){ - throw std::runtime_error(str(boost::format("Received invalid reply %d from device.\n") % ntohl(update_data_in->id))); + else if(ntohl(update_data_in->id) != GET_FLASH_INFO_ACK){ + throw std::runtime_error(str(boost::format("Received invalid reply %d from device.\n") + % ntohl(update_data_in->id))); } - + return flash_info; } @@ -218,102 +331,100 @@ boost::uint32_t* get_flash_info(std::string ip_addr){ void erase_image(udp_simple::sptr udp_transport, bool is_fw, boost::uint32_t memory_size){ + boost::uint32_t image_location_addr = is_fw ? PROD_FW_IMAGE_LOCATION_ADDR + : PROD_FPGA_IMAGE_LOCATION_ADDR; + boost::uint32_t image_size = is_fw ? FW_IMAGE_SIZE_BYTES + : FPGA_IMAGE_SIZE_BYTES; + //Making sure this won't attempt to erase past end of device - if(is_fw){ - if(PROD_FW_IMAGE_LOCATION_ADDR+FW_IMAGE_SIZE_BYTES > memory_size) throw std::runtime_error("Cannot erase past end of device."); - } - else{ - if(PROD_FPGA_IMAGE_LOCATION_ADDR+FPGA_IMAGE_SIZE_BYTES > memory_size) throw std::runtime_error("Cannot erase past end of device."); - } + if((image_location_addr+image_size) > memory_size) throw std::runtime_error("Cannot erase past end of device."); - //Setting up UDP transport - boost::uint8_t usrp2_update_data_in_mem[udp_simple::mtu]; + //UDP receive buffer const usrp2_fw_update_data_t *update_data_in = reinterpret_cast<const usrp2_fw_update_data_t *>(usrp2_update_data_in_mem); //Setting up UDP packet usrp2_fw_update_data_t erase_pkt = usrp2_fw_update_data_t(); - erase_pkt.id = htonx<boost::uint32_t>(USRP2_FW_UPDATE_ID_ERASE_TEH_FLASHES_LOL); + erase_pkt.id = htonx<boost::uint32_t>(ERASE_FLASH_CMD); erase_pkt.proto_ver = htonx<boost::uint32_t>(USRP2_FW_PROTO_VERSION); - if(is_fw){ - erase_pkt.data.flash_args.flash_addr = htonx<boost::uint32_t>(PROD_FW_IMAGE_LOCATION_ADDR); - erase_pkt.data.flash_args.length = htonx<boost::uint32_t>(FW_IMAGE_SIZE_BYTES); - } - else{ - erase_pkt.data.flash_args.flash_addr = htonx<boost::uint32_t>(PROD_FPGA_IMAGE_LOCATION_ADDR); - erase_pkt.data.flash_args.length = htonx<boost::uint32_t>(FPGA_IMAGE_SIZE_BYTES); - } + erase_pkt.data.flash_args.flash_addr = htonx<boost::uint32_t>(image_location_addr); + erase_pkt.data.flash_args.length = htonx<boost::uint32_t>(image_size); //Begin erasing udp_transport->send(boost::asio::buffer(&erase_pkt, sizeof(erase_pkt))); size_t len = udp_transport->recv(boost::asio::buffer(usrp2_update_data_in_mem), UDP_TIMEOUT); - if(len > offsetof(usrp2_fw_update_data_t, data) and ntohl(update_data_in->id) == USRP2_FW_UPDATE_ID_ERASING_TEH_FLASHES_OMG){ + if(len > offsetof(usrp2_fw_update_data_t, data) and ntohl(update_data_in->id) == ERASE_FLASH_ACK){ if(is_fw) std::cout << "Erasing firmware image." << std::endl; else std::cout << "Erasing FPGA image." << std::endl; } - else if(ntohl(update_data_in->id) != USRP2_FW_UPDATE_ID_ERASING_TEH_FLASHES_OMG){ - throw std::runtime_error(str(boost::format("Received invalid reply %d from device.\n") % ntohl(update_data_in->id))); + else if(ntohl(update_data_in->id) != ERASE_FLASH_ACK){ + throw std::runtime_error(str(boost::format("Received invalid reply %d from device.\n") + % ntohl(update_data_in->id))); } //Check for erase completion - erase_pkt.id = htonx<boost::uint32_t>(USRP2_FW_UPDATE_ID_R_U_DONE_ERASING_LOL); + erase_pkt.id = htonx<boost::uint32_t>(CHECK_ERASING_DONE_CMD); while(true){ udp_transport->send(boost::asio::buffer(&erase_pkt, sizeof(erase_pkt))); size_t len = udp_transport->recv(boost::asio::buffer(usrp2_update_data_in_mem), UDP_TIMEOUT); - if(len > offsetof(usrp2_fw_update_data_t, data) and ntohl(update_data_in->id) == USRP2_FW_UPDATE_ID_IM_DONE_ERASING_OMG){ - if(is_fw) std::cout << boost::format(" * Successfully erased %d bytes at %d.\n") % FW_IMAGE_SIZE_BYTES % PROD_FW_IMAGE_LOCATION_ADDR; - else std::cout << boost::format(" * Successfully erased %d bytes at %d.\n") % FPGA_IMAGE_SIZE_BYTES % PROD_FPGA_IMAGE_LOCATION_ADDR; + if(len > offsetof(usrp2_fw_update_data_t, data) and ntohl(update_data_in->id) == DONE_ERASING_ACK){ + std::cout << boost::format(" * Successfully erased %d bytes at %d.\n") + % image_size % image_location_addr; break; } - else if(ntohl(update_data_in->id) != USRP2_FW_UPDATE_ID_NOPE_NOT_DONE_ERASING_OMG){ - throw std::runtime_error(str(boost::format("Received invalid reply %d from device.\n") % ntohl(update_data_in->id))); + else if(ntohl(update_data_in->id) != NOT_DONE_ERASING_ACK){ + throw std::runtime_error(str(boost::format("Received invalid reply %d from device.\n") + % ntohl(update_data_in->id))); } } } void write_image(udp_simple::sptr udp_transport, bool is_fw, boost::uint8_t* image, boost::uint32_t memory_size, int image_size){ - boost::uint32_t current_addr; - if(is_fw) current_addr = PROD_FW_IMAGE_LOCATION_ADDR; - else current_addr = PROD_FPGA_IMAGE_LOCATION_ADDR; + boost::uint32_t begin_addr = is_fw ? PROD_FW_IMAGE_LOCATION_ADDR + : PROD_FPGA_IMAGE_LOCATION_ADDR; + boost::uint32_t current_addr = begin_addr; + std::string type = is_fw ? "firmware" : "FPGA"; //Making sure this won't attempt to write past end of device if(current_addr+image_size > memory_size) throw std::runtime_error("Cannot write past end of device."); - //Setting up UDP transport - boost::uint8_t usrp2_update_data_in_mem[udp_simple::mtu]; + //UDP receive buffer const usrp2_fw_update_data_t *update_data_in = reinterpret_cast<const usrp2_fw_update_data_t *>(usrp2_update_data_in_mem); //Setting up UDP packet usrp2_fw_update_data_t write_pkt = usrp2_fw_update_data_t(); - write_pkt.id = htonx<boost::uint32_t>(USRP2_FW_UPDATE_ID_WRITE_TEH_FLASHES_LOL); + write_pkt.id = htonx<boost::uint32_t>(WRITE_FLASH_CMD); write_pkt.proto_ver = htonx<boost::uint32_t>(USRP2_FW_PROTO_VERSION); write_pkt.data.flash_args.length = htonx<boost::uint32_t>(FLASH_DATA_PACKET_SIZE); - //Write image - if(is_fw) std::cout << "Writing firmware image." << std::endl; - else std::cout << "Writing FPGA image." << std::endl; - for(int i = 0; i < ((image_size/FLASH_DATA_PACKET_SIZE)+1); i++){ + //Print progress + std::cout << "\rWriting " << type << " image (" + << int((double(current_addr-begin_addr)/double(image_size))*100) << "%)." << std::flush; + write_pkt.data.flash_args.flash_addr = htonx<boost::uint32_t>(current_addr); std::copy(image+(i*FLASH_DATA_PACKET_SIZE), image+((i+1)*FLASH_DATA_PACKET_SIZE), write_pkt.data.flash_args.data); udp_transport->send(boost::asio::buffer(&write_pkt, sizeof(write_pkt))); size_t len = udp_transport->recv(boost::asio::buffer(usrp2_update_data_in_mem), UDP_TIMEOUT); - if(len > offsetof(usrp2_fw_update_data_t, data) and ntohl(update_data_in->id) != USRP2_FW_UPDATE_ID_WROTE_TEH_FLASHES_OMG){ - throw std::runtime_error(str(boost::format("Invalid reply %d from device.") % ntohl(update_data_in->id))); + if(len > offsetof(usrp2_fw_update_data_t, data) and ntohl(update_data_in->id) != WRITE_FLASH_ACK){ + throw std::runtime_error(str(boost::format("Invalid reply %d from device.") + % ntohl(update_data_in->id))); } current_addr += FLASH_DATA_PACKET_SIZE; } + std::cout << std::flush << "\rWriting " << type << " image (100%)." << std::endl; std::cout << boost::format(" * Successfully wrote %d bytes.\n") % image_size; } void verify_image(udp_simple::sptr udp_transport, bool is_fw, boost::uint8_t* image, boost::uint32_t memory_size, int image_size){ int current_index = 0; - boost::uint32_t current_addr; - if(is_fw) current_addr = PROD_FW_IMAGE_LOCATION_ADDR; - else current_addr = PROD_FPGA_IMAGE_LOCATION_ADDR; + boost::uint32_t begin_addr = is_fw ? PROD_FW_IMAGE_LOCATION_ADDR + : PROD_FPGA_IMAGE_LOCATION_ADDR; + boost::uint32_t current_addr = begin_addr; + std::string type = is_fw ? "firmware" : "FPGA"; //Array size needs to be known at runtime, this constant is guaranteed to be larger than any firmware or FPGA image boost::uint8_t from_usrp[FPGA_IMAGE_SIZE_BYTES]; @@ -321,27 +432,27 @@ void verify_image(udp_simple::sptr udp_transport, bool is_fw, boost::uint8_t* im //Making sure this won't attempt to read past end of device if(current_addr+image_size > memory_size) throw std::runtime_error("Cannot read past end of device."); - //Setting up UDP transport - boost::uint8_t usrp2_update_data_in_mem[udp_simple::mtu]; + //UDP receive buffer const usrp2_fw_update_data_t *update_data_in = reinterpret_cast<const usrp2_fw_update_data_t *>(usrp2_update_data_in_mem); //Setting up UDP packet usrp2_fw_update_data_t verify_pkt = usrp2_fw_update_data_t(); - verify_pkt.id = htonx<boost::uint32_t>(USRP2_FW_UPDATE_ID_READ_TEH_FLASHES_LOL); + verify_pkt.id = htonx<boost::uint32_t>(READ_FLASH_CMD); verify_pkt.proto_ver = htonx<boost::uint32_t>(USRP2_FW_PROTO_VERSION); verify_pkt.data.flash_args.length = htonx<boost::uint32_t>(FLASH_DATA_PACKET_SIZE); - //Verify image - if(is_fw) std::cout << "Verifying firmware image." << std::endl; - else std::cout << "Verifying FPGA image." << std::endl; - for(int i = 0; i < ((image_size/FLASH_DATA_PACKET_SIZE)+1); i++){ + //Print progress + std::cout << "\rVerifying " << type << " image (" + << int((double(current_addr-begin_addr)/double(image_size))*100) << "%)." << std::flush; + verify_pkt.data.flash_args.flash_addr = htonx<boost::uint32_t>(current_addr); udp_transport->send(boost::asio::buffer(&verify_pkt, sizeof(verify_pkt))); size_t len = udp_transport->recv(boost::asio::buffer(usrp2_update_data_in_mem), UDP_TIMEOUT); - if(len > offsetof(usrp2_fw_update_data_t, data) and ntohl(update_data_in->id) != USRP2_FW_UPDATE_ID_KK_READ_TEH_FLASHES_OMG){ - throw std::runtime_error(str(boost::format("Invalid reply %d from device.") % ntohl(update_data_in->id))); + if(len > offsetof(usrp2_fw_update_data_t, data) and ntohl(update_data_in->id) != READ_FLASH_ACK){ + throw std::runtime_error(str(boost::format("Invalid reply %d from device.") + % ntohl(update_data_in->id))); } for(int j = 0; j < FLASH_DATA_PACKET_SIZE; j++) from_usrp[current_index+j] = update_data_in->data.flash_args.data[j]; @@ -350,27 +461,27 @@ void verify_image(udp_simple::sptr udp_transport, bool is_fw, boost::uint8_t* im } for(int i = 0; i < image_size; i++) if(from_usrp[i] != image[i]) throw std::runtime_error("Image write failed."); + std::cout << std::flush << "\rVerifying " << type << " image (100%)." << std::endl; std::cout << " * Successful." << std::endl; } void reset_usrp(udp_simple::sptr udp_transport){ //Set up UDP transport - boost::uint8_t usrp2_update_data_in_mem[udp_simple::mtu]; const usrp2_fw_update_data_t *update_data_in = reinterpret_cast<const usrp2_fw_update_data_t *>(usrp2_update_data_in_mem); //Set up UDP packet usrp2_fw_update_data_t reset_pkt = usrp2_fw_update_data_t(); - reset_pkt.id = htonx<boost::uint32_t>(USRP2_FW_UPDATE_ID_RESET_MAH_COMPUTORZ_LOL); + reset_pkt.id = htonx<boost::uint32_t>(RESET_USRP_CMD); reset_pkt.proto_ver = htonx<boost::uint32_t>(USRP2_FW_PROTO_VERSION); //Reset USRP udp_transport->send(boost::asio::buffer(&reset_pkt, sizeof(reset_pkt))); size_t len = udp_transport->recv(boost::asio::buffer(usrp2_update_data_in_mem), UDP_TIMEOUT); - if(len > offsetof(usrp2_fw_update_data_t, data) and ntohl(update_data_in->id) == USRP2_FW_UPDATE_ID_RESETTIN_TEH_COMPUTORZ_OMG){ + if(len > offsetof(usrp2_fw_update_data_t, data) and ntohl(update_data_in->id) == RESET_USRP_ACK){ throw std::runtime_error("USRP reset failed."); //There should be no response to this UDP packet } - else std::cout << "Resetting USRP." << std::endl; + else std::cout << std::endl << "Resetting USRP." << std::endl; } int UHD_SAFE_MAIN(int argc, char *argv[]){ @@ -386,125 +497,88 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ ("addr", po::value<std::string>(&ip_addr)->default_value("192.168.10.2"), "Specify an IP address.") ("fw", po::value<std::string>(&fw_path), "Specify a filepath for a custom firmware image.") ("fpga", po::value<std::string>(&fpga_path), "Specify a filepath for a custom FPGA image.") - ("no_fw", "Do not burn a firmware image.") - ("no_fpga", "Do not burn an FPGA image.") - ("auto_reboot", "Automatically reboot N2XX without prompting.") + ("no-fw", "Do not burn a firmware image.") + ("no_fw", "Do not burn a firmware image (DEPRECATED).") + ("no-fpga", "Do not burn an FPGA image.") + ("no_fpga", "Do not burn an FPGA image (DEPRECATED).") + ("auto-reboot", "Automatically reboot N2XX without prompting.") + ("auto_reboot", "Automatically reboot N2XX without prompting (DEPRECATED).") ("list", "List available N2XX USRP devices.") ; po::variables_map vm; po::store(po::parse_command_line(argc, argv, desc), vm); po::notify(vm); - //Apply options + //Print help message if(vm.count("help") > 0){ std::cout << boost::format("N2XX Simple Net Burner\n"); std::cout << boost::format("Automatically detects and burns standard firmware and FPGA images onto USRP N2XX devices.\n"); std::cout << boost::format("Can optionally take user input for custom images.\n\n"); std::cout << desc << std::endl; - return EXIT_FAILURE; + return EXIT_SUCCESS; } - bool burn_fpga = (vm.count("no_fpga") == 0); - bool burn_fw = (vm.count("no_fw") == 0); + //List option + if(vm.count("list")){ + list_usrps(); + return EXIT_SUCCESS; + } + + //Process user options + bool burn_fpga = (vm.count("no-fpga") == 0) and (vm.count("no_fpga") == 0); + bool burn_fw = (vm.count("no-fw") == 0) and (vm.count("no_fw") == 0); bool use_custom_fpga = (vm.count("fpga") > 0); bool use_custom_fw = (vm.count("fw") > 0); - bool list_usrps = (vm.count("list") > 0); - bool auto_reboot = (vm.count("auto_reboot") > 0); + bool auto_reboot = (vm.count("auto-reboot") > 0) or (vm.count("auto_reboot") > 0); + int fpga_image_size = 0; + int fw_image_size = 0; - if(!burn_fpga && !burn_fw){ + if(not burn_fpga && not burn_fw){ std::cout << "No images will be burned." << std::endl; return EXIT_FAILURE; } - if(!burn_fw && use_custom_fw) std::cout << boost::format("Conflicting firmware options presented. Will not burn a firmware image.\n\n"); - if(!burn_fpga && use_custom_fpga) std::cout << boost::format("Conflicting FPGA options presented. Will not burn an FPGA image.\n\n"); - - //Variables not from options - boost::uint32_t hw_rev; - bool found_it = false; - boost::uint8_t usrp2_update_data_in_mem[udp_simple::mtu]; - const usrp2_fw_update_data_t *update_data_in = reinterpret_cast<const usrp2_fw_update_data_t *>(usrp2_update_data_in_mem); - - //List option - if(list_usrps){ - udp_simple::sptr udp_bc_transport; - usrp2_fw_update_data_t usrp2_ack_pkt = usrp2_fw_update_data_t(); - usrp2_ack_pkt.proto_ver = htonx<boost::uint32_t>(USRP2_FW_PROTO_VERSION); - usrp2_ack_pkt.id = htonx<boost::uint32_t>(USRP2_FW_UPDATE_ID_OHAI_LOL); - - std::cout << "Available USRP N2XX devices:" << std::endl; - - //Send UDP packets to all broadcast addresses - BOOST_FOREACH(const if_addrs_t &if_addrs, get_if_addrs()){ - //Avoid the loopback device - if(if_addrs.inet == boost::asio::ip::address_v4::loopback().to_string()) continue; - udp_bc_transport = udp_simple::make_broadcast(if_addrs.bcast, BOOST_STRINGIZE(USRP2_UDP_UPDATE_PORT)); - udp_bc_transport->send(boost::asio::buffer(&usrp2_ack_pkt, sizeof(usrp2_ack_pkt))); - - size_t len = udp_bc_transport->recv(boost::asio::buffer(usrp2_update_data_in_mem), UDP_TIMEOUT); - if(len > offsetof(usrp2_fw_update_data_t, data) and ntohl(update_data_in->id) == USRP2_FW_UPDATE_ID_OHAI_OMG){ - usrp2_ack_pkt.id = htonx<boost::uint32_t>(USRP2_FW_UPDATE_ID_I_CAN_HAS_HW_REV_LOL); - udp_bc_transport->send(boost::asio::buffer(&usrp2_ack_pkt, sizeof(usrp2_ack_pkt))); - - size_t len = udp_bc_transport->recv(boost::asio::buffer(usrp2_update_data_in_mem), UDP_TIMEOUT); - if(len > offsetof(usrp2_fw_update_data_t, data) and ntohl(update_data_in->id) == USRP2_FW_UPDATE_ID_HERES_TEH_HW_REV_OMG){ - hw_rev = ntohl(update_data_in->data.hw_rev); - } + //Print deprecation messages if necessary + if(vm.count("no_fpga") > 0) std::cout << "WARNING: --no_fpga option is deprecated! Use --no-fpga instead." << std::endl << std::endl; + if(vm.count("no_fw") > 0) std::cout << "WARNING: --no_fw option is deprecated! Use --no-fw instead." << std::endl << std::endl; + if(vm.count("auto_reboot") > 0) std::cout << "WARNING: --auto_reboot option is deprecated! Use --auto-reboot instead." << std::endl << std::endl; - std::cout << boost::format(" * %s (%s)\n") % udp_bc_transport->get_recv_addr() % filename_map[hw_rev]; - } - - } - return EXIT_FAILURE; - } + //Find USRP and establish connection std::cout << boost::format("Searching for USRP N2XX with IP address %s.\n") % ip_addr; - - //Address specified udp_simple::sptr udp_transport = udp_simple::make_connected(ip_addr, BOOST_STRINGIZE(USRP2_UDP_UPDATE_PORT)); - usrp2_fw_update_data_t hw_info_pkt = usrp2_fw_update_data_t(); - hw_info_pkt.proto_ver = htonx<boost::uint32_t>(USRP2_FW_PROTO_VERSION); - hw_info_pkt.id = htonx<boost::uint32_t>(USRP2_FW_UPDATE_ID_I_CAN_HAS_HW_REV_LOL); - udp_transport->send(boost::asio::buffer(&hw_info_pkt, sizeof(hw_info_pkt))); - - //Loop and receive until the timeout - size_t len = udp_transport->recv(boost::asio::buffer(usrp2_update_data_in_mem), UDP_TIMEOUT); - if(len > offsetof(usrp2_fw_update_data_t, data) and ntohl(update_data_in->id) == USRP2_FW_UPDATE_ID_HERES_TEH_HW_REV_OMG){ - hw_rev = ntohl(update_data_in->data.hw_rev); - if(filename_map.find(hw_rev) != filename_map.end()){ - std::cout << boost::format("Found %s.\n\n") % filename_map[hw_rev]; - found_it = true; - } - else throw std::runtime_error("Invalid revision found."); - } - if(!found_it) throw std::runtime_error("No USRP N2XX found."); - - //Determining default image filenames for validation - std::string default_fw_filename = str(boost::format("usrp_%s_fw.bin") % erase_tail_copy(filename_map[hw_rev],3)); - std::string default_fpga_filename = str(boost::format("usrp_%s_fpga.bin") % filename_map[hw_rev]); - std::string default_fw_filepath = ""; - std::string default_fpga_filepath = ""; + boost::uint32_t hw_rev = find_usrp(udp_transport); //Check validity of file locations and binaries before attempting burn 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{ - //Replace ~ with home directory - if(fpga_path.find("~/") == 0) fpga_path.replace(0,1,getenv("HOME")); + if(use_custom_fpga){ + //Expand tilde usage if applicable + #ifndef UHD_PLATFORM_WIN32 + if(fpga_path.find("~/") == 0) fpga_path.replace(0,1,getenv("HOME")); + #endif validate_custom_fpga_file(filename_map[hw_rev], fpga_path); } + else{ + std::string default_fpga_filename = str(boost::format("usrp_%s_fpga.bin") % filename_map[hw_rev]); + fpga_path = find_image_path(default_fpga_filename); + } - grab_fpga_image(fpga_path); + fpga_image_size = read_fpga_image(fpga_path); } if(burn_fw){ - if(!use_custom_fw) fw_path = find_image_path(default_fw_filename); - else{ - //Replace ~ with home directory - if(fw_path.find("~/") == 0) fw_path.replace(0,1,getenv("HOME")); + if(use_custom_fw){ + //Expand tilde usage if applicable + #ifndef UHD_PLATFORM_WIN32 + if(fw_path.find("~/") == 0) fw_path.replace(0,1,getenv("HOME")); + #endif validate_custom_fw_file(filename_map[hw_rev], fw_path); } + else{ + std::string default_fw_filename = str(boost::format("usrp_%s_fw.bin") % erase_tail_copy(filename_map[hw_rev],3)); + fw_path = find_image_path(default_fw_filename); + } - grab_fw_image(fw_path); + fw_image_size = read_fw_image(fw_path); } std::cout << "Will burn the following images:" << std::endl; @@ -547,7 +621,6 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ std::cout << std::endl; //Formatting } if(reset) reset_usrp(udp_transport); - else return EXIT_SUCCESS; return EXIT_SUCCESS; } diff --git a/host/utils/usrp_simple_burner_utils.hpp b/host/utils/usrp_simple_burner_utils.hpp deleted file mode 100644 index f386c3620..000000000 --- a/host/utils/usrp_simple_burner_utils.hpp +++ /dev/null @@ -1,99 +0,0 @@ -// -// Copyright 2012 Ettus Research LLC -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see <http://www.gnu.org/licenses/>. -// - -#include <iostream> -#include <math.h> -#include <stdint.h> - -#include <boost/foreach.hpp> -#include <boost/asio.hpp> -#include <boost/filesystem.hpp> - -#include <uhd/exception.hpp> -#include <uhd/transport/if_addrs.hpp> -#include <uhd/transport/udp_simple.hpp> -#include <uhd/types/device_addr.hpp> -#include <uhd/utils/msg.hpp> - -#define UDP_FW_UPDATE_PORT 49154 -#define UDP_MAX_XFER_BYTES 1024 -#define UDP_TIMEOUT 3 -#define UDP_POLL_INTERVAL 0.10 //in seconds -#define USRP2_FW_PROTO_VERSION 7 //should be unused after r6 -#define USRP2_UDP_UPDATE_PORT 49154 -#define FLASH_DATA_PACKET_SIZE 256 -#define FPGA_IMAGE_SIZE_BYTES 1572864 -#define FW_IMAGE_SIZE_BYTES 31744 -#define PROD_FPGA_IMAGE_LOCATION_ADDR 0x00180000 -#define PROD_FW_IMAGE_LOCATION_ADDR 0x00300000 -#define SAFE_FPGA_IMAGE_LOCATION_ADDR 0x00000000 -#define SAFE_FW_IMAGE_LOCATION_ADDR 0x003F0000 - -using namespace uhd; -using namespace uhd::transport; -namespace asio = boost::asio; - -typedef enum { - USRP2_FW_UPDATE_ID_WAT = ' ', - - USRP2_FW_UPDATE_ID_OHAI_LOL = 'a', - USRP2_FW_UPDATE_ID_OHAI_OMG = 'A', - - USRP2_FW_UPDATE_ID_WATS_TEH_FLASH_INFO_LOL = 'f', - USRP2_FW_UPDATE_ID_HERES_TEH_FLASH_INFO_OMG = 'F', - - USRP2_FW_UPDATE_ID_ERASE_TEH_FLASHES_LOL = 'e', - USRP2_FW_UPDATE_ID_ERASING_TEH_FLASHES_OMG = 'E', - - USRP2_FW_UPDATE_ID_R_U_DONE_ERASING_LOL = 'd', - USRP2_FW_UPDATE_ID_IM_DONE_ERASING_OMG = 'D', - USRP2_FW_UPDATE_ID_NOPE_NOT_DONE_ERASING_OMG = 'B', - - USRP2_FW_UPDATE_ID_WRITE_TEH_FLASHES_LOL = 'w', - USRP2_FW_UPDATE_ID_WROTE_TEH_FLASHES_OMG = 'W', - - USRP2_FW_UPDATE_ID_READ_TEH_FLASHES_LOL = 'r', - USRP2_FW_UPDATE_ID_KK_READ_TEH_FLASHES_OMG = 'R', - - USRP2_FW_UPDATE_ID_RESET_MAH_COMPUTORZ_LOL = 's', - USRP2_FW_UPDATE_ID_RESETTIN_TEH_COMPUTORZ_OMG = 'S', - - USRP2_FW_UPDATE_ID_I_CAN_HAS_HW_REV_LOL = 'v', - USRP2_FW_UPDATE_ID_HERES_TEH_HW_REV_OMG = 'V', - - USRP2_FW_UPDATE_ID_KTHXBAI = '~' - -} usrp2_fw_update_id_t; - -typedef struct { - uint32_t proto_ver; - uint32_t id; - uint32_t seq; - union { - uint32_t ip_addr; - uint32_t hw_rev; - struct { - uint32_t flash_addr; - uint32_t length; - uint8_t data[256]; - } flash_args; - struct { - uint32_t sector_size_bytes; - uint32_t memory_size_bytes; - } flash_info_args; - } data; -} usrp2_fw_update_data_t; |