diff options
Diffstat (limited to 'host')
46 files changed, 1135 insertions, 1014 deletions
diff --git a/host/CMakeLists.txt b/host/CMakeLists.txt index 45d0cd940..c45058562 100644 --- a/host/CMakeLists.txt +++ b/host/CMakeLists.txt @@ -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 "02291061d617c7b398a0dbdcc11ca6b6") +SET(UHD_IMAGES_DOWNLOAD_SRC "http://files.ettus.com/binaries/maint_images/archive/uhd-images_003.007.000-70-gfcc85c95.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/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_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/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/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/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_zero_copy.cpp b/host/lib/transport/nirio_zero_copy.cpp index 7b1e32fe0..c3c8a9368 100644 --- a/host/lib/transport/nirio_zero_copy.cpp +++ b/host/lib/transport/nirio_zero_copy.cpp @@ -18,7 +18,6 @@ #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> @@ -261,19 +260,27 @@ 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); } } 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_impl.cpp b/host/lib/usrp/b200/b200_impl.cpp index 9dd3a424d..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; @@ -160,9 +161,9 @@ b200_impl::b200_impl(const device_addr_t &device_addr) 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(), "%hx", &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(), "%hx", &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 diff --git a/host/lib/usrp/b200/b200_impl.hpp b/host/lib/usrp/b200/b200_impl.hpp index 7d98a8f8d..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; 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/multi_usrp.cpp b/host/lib/usrp/multi_usrp.cpp index f08709669..4883b2410 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); @@ -770,6 +778,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); @@ -1178,6 +1191,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); + 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/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_impl.cpp b/host/lib/usrp/x300/x300_impl.cpp index e492b2238..f5e53678c 100644 --- a/host/lib/usrp/x300/x300_impl.cpp +++ b/host/lib/usrp/x300/x300_impl.cpp @@ -392,6 +392,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,6 +458,8 @@ 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 @@ -1133,11 +1137,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) { diff --git a/host/lib/usrp/x300/x300_impl.hpp b/host/lib/usrp/x300/x300_impl.hpp index 259ea253d..4b3efc845 100644 --- a/host/lib/usrp/x300/x300_impl.hpp +++ b/host/lib/usrp/x300/x300_impl.hpp @@ -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 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/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 12ae7ff46..3188e02a0 100644 --- a/host/utils/uhd_cal_rx_iq_balance.cpp +++ b/host/utils/uhd_cal_rx_iq_balance.cpp @@ -127,32 +127,8 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ 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 c9eab9177..b5c5293f0 100644 --- a/host/utils/uhd_cal_tx_dc_offset.cpp +++ b/host/utils/uhd_cal_tx_dc_offset.cpp @@ -129,32 +129,8 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ 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 27e6356bb..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> @@ -130,32 +125,8 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ 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; |