From ff1546f8137f7f92bb250f685561b0c34cc0e053 Mon Sep 17 00:00:00 2001 From: Ben Hilburn Date: Fri, 14 Feb 2014 12:05:07 -0800 Subject: Pushing the bulk of UHD-3.7.0 code. --- host/docs/CMakeLists.txt | 1 + host/docs/build.rst | 20 +- host/docs/general.rst | 73 +++---- host/docs/index.rst | 3 +- host/docs/usrp_x3x0.rst | 108 +++++++++- host/docs/usrp_x3x0_config.rst | 296 +++++++++++++++++++++++++++ host/examples/rx_ascii_art_dft.cpp | 2 +- host/examples/rx_samples_to_file.cpp | 2 +- host/examples/rx_samples_to_udp.cpp | 2 +- host/examples/tx_bursts.cpp | 2 +- host/examples/tx_samples_from_file.cpp | 2 +- host/examples/tx_waveforms.cpp | 2 +- host/examples/txrx_loopback_to_file.cpp | 4 +- host/include/uhd/transport/CMakeLists.txt | 1 + host/include/uhd/transport/udp_constants.hpp | 26 +++ host/lib/usrp/common/adf435x_common.cpp | 35 ++-- host/lib/usrp/common/adf435x_common.hpp | 6 +- host/lib/usrp/dboard/db_cbx.cpp | 6 +- host/lib/usrp/dboard/db_sbx_version3.cpp | 8 +- host/lib/usrp/dboard/db_sbx_version4.cpp | 8 +- host/lib/usrp/dboard/db_wbx_version2.cpp | 33 ++- host/lib/usrp/dboard/db_wbx_version3.cpp | 33 ++- host/lib/usrp/dboard/db_wbx_version4.cpp | 34 ++- host/lib/usrp/gps_ctrl.cpp | 47 +++-- host/lib/usrp/x300/x300_clock_ctrl.cpp | 6 +- host/lib/usrp/x300/x300_impl.cpp | 275 +++++++++++++++++-------- host/lib/usrp/x300/x300_impl.hpp | 51 +++-- host/utils/usrp_n2xx_simple_net_burner.cpp | 24 ++- host/utils/usrp_x3xx_fpga_burner.cpp | 47 +++-- 29 files changed, 899 insertions(+), 258 deletions(-) create mode 100644 host/docs/usrp_x3x0_config.rst create mode 100644 host/include/uhd/transport/udp_constants.hpp (limited to 'host') diff --git a/host/docs/CMakeLists.txt b/host/docs/CMakeLists.txt index 0163a5c20..1ee0f1ade 100644 --- a/host/docs/CMakeLists.txt +++ b/host/docs/CMakeLists.txt @@ -40,6 +40,7 @@ SET(manual_sources usrp_b200.rst usrp_e1x0.rst usrp_x3x0.rst + usrp_x3x0_config.rst ) ######################################################################## diff --git a/host/docs/build.rst b/host/docs/build.rst index 5512e71ae..f53a56d9b 100644 --- a/host/docs/build.rst +++ b/host/docs/build.rst @@ -14,9 +14,11 @@ the dependencies should be available in the package repositories for your package manager. **Mac OS X Notes:** -Install the "Xcode Developer Tools" to get the build tools (GCC and Make). +Install the Xcode app to get the build tools (GCC and Make). Use MacPorts to get the Boost and Cheetah dependencies. -Other dependencies can be downloaded as DMG installers from the web. +Other dependencies can be downloaded as DMG installers from the web +or installed via MacPorts. +See the UHD OS X page for more information: http://code.ettus.com/redmine/ettus/projects/uhd/wiki/UHD_OS_X **Windows Notes:** The dependencies can be acquired through installable EXE files. @@ -148,11 +150,6 @@ or add it to **/etc/ld.so.conf** and make sure to run: sudo ldconfig -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Setup the library path (Mac OS X) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Make sure that **libuhd.dylib** is in your **DYLD_LIBRARY_PATH**. - ------------------------------------------------------------------------ Build Instructions (Windows) ------------------------------------------------------------------------ @@ -220,3 +217,12 @@ Post-Install Tasks For USB-based devices, see the `USB Transport Application Notes <./transport.html#usb-transport-libusb>`_ for platform-specific post-installation tasks. + +------------------------------------------------------------------------ +Post-Install Tasks (Mac OS X) +------------------------------------------------------------------------ +Make sure that the value of **CMAKE_INSTALL_PREFIX** is at or near the +front of the shell **PATH** environment variable. Do **NOT** set +DYLD_LIBRARY_PATH or any related DYLD environment variable +permanently; these work differently than under Linux and should be +used for testing / temporary purposes only. diff --git a/host/docs/general.rst b/host/docs/general.rst index 01d0f270c..930c18188 100644 --- a/host/docs/general.rst +++ b/host/docs/general.rst @@ -1,16 +1,16 @@ -======================================================================== +=============================== UHD - General Application Notes -======================================================================== +=============================== .. contents:: Table of Contents ------------------------------------------------------------------------- +------------ Tuning Notes ------------------------------------------------------------------------- +------------ -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^^^^^^^^ Two-stage tuning process -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^^^^^^^^ A USRP device has two stages of tuning: * RF front-end: translates bewteen RF and IF @@ -40,7 +40,7 @@ performance. The daughterboards that support this functionality are: * CBX-120 Tuning the receive chain: -:: +::::::::::::::::::::::::: //tuning to a desired center frequency usrp->set_rx_freq(target_frequency_in_hz); @@ -49,15 +49,15 @@ Tuning the receive chain: //advanced tuning with tune_request_t uhd::tune_request_t tune_req(target_frequency_in_hz, desired_lo_offset); - tune_req.args = uhd::device_addr_t("mode_n=int-n"); //to use Int-N tuning + tune_req.args = uhd::device_addr_t("mode_n=integer"); //to use Int-N tuning //fill in any additional/optional tune request fields... usrp->set_rx_freq(tune_req); More information can be found in `tune_request.hpp <./../../doxygen/html/structuhd_1_1tune__request__t.html>`_. -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^^^^^^^^^^ RF front-end settling time -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^^^^^^^^^^ After tuning, the RF front-end will need time to settle into a usable state. Typically, this means that the local oscillators must be given time to lock before streaming begins. Lock time is not consistent; it varies depending upon @@ -66,6 +66,8 @@ should wait for the **lo_locked** sensor to become true or sleep for a conservative amount of time (perhaps a second). Pseudo-code for dealing with settling time after tuning on receive: +::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + :: usrp->set_rx_freq(...); @@ -80,9 +82,10 @@ Pseudo-code for dealing with settling time after tuning on receive: } usrp->issue_stream_command(...); ------------------------------------------------------------------------- + +------------------------------- Specifying the Subdevice to Use ------------------------------------------------------------------------- +------------------------------- A subdevice specification string for USRP family devices is composed of: :: @@ -109,16 +112,16 @@ Ex: The subdev spec markup string to select a BasicRX on slot B. B:B -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ USRP Family Motherboard Slot Names -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ All USRP family motherboards have a first slot named **A:**. The USRP1 has two daughterboard subdevice slots, known as **A:** and **B:**. -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Daughterboard Frontend Names -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Daughterboard frontend names can be used to specify which signal path is used from a daughterboard. Most daughterboards have only one frontend **:0**. A few @@ -126,15 +129,15 @@ daughterboards (Basic, LF and TVRX2) have multiple frontend names available. The frontend names are documented in the `Daughterboard Application Notes <./dboards.html>`_ ------------------------------------------------------------------------- +------------------------ Overflow/Underflow Notes ------------------------------------------------------------------------- +------------------------ **Note:** The following overflow/underflow notes do not apply to USRP1, which does not support the advanced features available in newer products. -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^ Overflow notes -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^ When receiving, the device produces samples at a constant rate. Overflows occurs when the host does not consume data fast enough. When UHD software detects the overflow, it prints an "O" or "D" to stdout, @@ -157,21 +160,21 @@ If the device was in continuous streaming mode, the UHD software will automatically restart streaming when the buffer has space again. -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^ Underflow notes -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^ When transmitting, the device consumes samples at a constant rate. Underflow occurs when the host does not produce data fast enough. When UHD software detects the underflow, it prints a "U" to stdout, and pushes a message packet into the async message stream. ------------------------------------------------------------------------- +--------------- Threading Notes ------------------------------------------------------------------------- +--------------- -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^^^ Thread safety notes -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^^^ For the most part, UHD software is thread-safe. Please observe the following limitations: @@ -188,9 +191,9 @@ This is because changing one setting could have an impact on how a call affects Example: setting the channel mapping affects how the antennas are set. It is recommended to use at most one thread context for manipulating device settings. -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^^^^^^^^^^ Thread priority scheduling -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^^^^^^^^^^ When UHD software spawns a new thread it may try to boost the thread's scheduling priority. When setting the priority fails, the UHD software prints out an error. @@ -200,29 +203,29 @@ This error is harmless; it simply means that the thread will have a normal sched Non-privileged users need special permission to change the scheduling priority. Add the following line to **/etc/security/limits.conf**: -:: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::: @ - rtprio 99 Replace **** with a group to which your user belongs. Settings will not take effect until the user is in a different login session. ------------------------------------------------------------------------- +------------------- Miscellaneous Notes ------------------------------------------------------------------------- +------------------- -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Support for dynamically loadable modules -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ For a module to be loaded at runtime, it must be: * found in the **UHD_MODULE_PATH** environment variable, * installed into the **/share/uhd/modules** directory, * or installed into **/usr/share/uhd/modules** directory (UNIX only). -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Disabling or redirecting prints to stdout -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The user can disable the UHD library from printing directly to stdout by registering a custom message handler. The handler will intercept all messages, which can be dropped or redirected. Only one handler can be registered at a time. diff --git a/host/docs/index.rst b/host/docs/index.rst index cbab651c1..ffad1488d 100644 --- a/host/docs/index.rst +++ b/host/docs/index.rst @@ -32,7 +32,7 @@ General UHD Manuals USRP N-Series Devices ^^^^^^^^^^^^^^^^^^^^^ * `USRP-N2x0 Series Device Manual <./usrp2.html>`_ -* `USRP-N2x0 Internal GPSDO Device Manual <./gpsdo_x3x0.html>`_ +* `USRP-N2x0 Internal GPSDO Device Manual <./gpsdo.html>`_ ^^^^^^^^^^^^^^^^^^^^^ USRP B-Series Devices @@ -53,6 +53,7 @@ USRP X-Series Devices * `USRP-X3x0 Series Device Manual <./usrp_x3x0.html>`_ * `USRP-X3x0 Internal GPSDO Device Manual <./gpsdo_x3x0.html>`_ * `USRP-X3x0 Front Panel GPIO API <./gpio_api.html>`_ +* `USRP-X3x0 System Configuration <./usrp_x3x0_config.html>`_ ^^^^^^^^^^^^^^^^^^^^^ USRP Legacy Series diff --git a/host/docs/usrp_x3x0.rst b/host/docs/usrp_x3x0.rst index 058230b5a..3b7e9914e 100644 --- a/host/docs/usrp_x3x0.rst +++ b/host/docs/usrp_x3x0.rst @@ -9,7 +9,7 @@ Comparative features list ------------------------- **Hardware Capabilities:** - * 2 transceiver card slots + * 2 transceiver card slots (can do 2x2 MIMO out of the box) * Dual SFP+ Transceivers (can be used with 1 GigE, 10 GigE) * PCI Express over cable (MXI) gen1 x4 * External PPS input & output @@ -29,6 +29,89 @@ Comparative features list * 16-bit and 8-bit sample modes (sc8 and sc16) * Up to 120 MHz of RF bandwidth with 16-bit samples +--------------- +Getting started +--------------- + +This will run you through the first steps relevant to get your USRP X300/X310 +up and running. Here, we assume you will connect your USRP using Gigabit Ethernet (1GigE), +as this interface is readily available in most computers. For 10 Gigabit Ethernet (10GigE) or +PCI Express (PCIe), see the corresponding sections in this manual page. + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Assembling the X300/X310 kit +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Before you can start using your USRP, you might have to assemble the hardware, +if this has not yet happened. Make sure you are grounded (e.g. by touching a radiator) +in order not to damage sensitive electronics through static discharge! + +1. Unscrew the top of your X300/X310 (there are 2 screws which can be easily loosened + using a small Phillips screwdriver). +2. Insert the daughterboards by inserting them into the slots and optionally screwing + them onto the motherboard. +3. Connect the RF connectors on the daughterboards to the front panel. In order to avoid + confusion, make sure the internal connections match the labels on the front panel (i.e. + TX/RX is connected to TX/RX). +4. If you have purchased an internal GPSDO, follow the instructions on + `the internal GPSDO manual page <./gpsdo_x3x0.html>`_ to insert the GPSDO. Note that you + will need an external GPS antenna connected to the rear GPS ANT connector in order to + make use of GPS, although your USRP will still be usable without. +5. Connect the 1 GigE SFP+ transceiver into the Ethernet port 0 and connect the X300/X310 with + your computer. +6. Connect the power supply and switch on the USRP. + +^^^^^^^^^^^^^^^^^^^^ +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. + +**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! + +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:: + + ping 192.168.10.2 + +on the command line. At this point, you should also run:: + + uhd_usrp_probe --args addr=192.168.10.2 + +to make sure all of your components (daughterboards, GPSDO) are correctly detected and usable. + +^^^^^^^^^^^^^^^^^^^^^ +Updating the firmware +^^^^^^^^^^^^^^^^^^^^^ + +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. + +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 + + 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 /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. + +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! + -------------- Hardware Setup -------------- @@ -40,7 +123,7 @@ Gigabit Ethernet (1 GigE) Installing the USRP X300/X310 ::::::::::::::::::::::::::::: * Prior to installing the module, the host PC can remain powered on. -* Plug a 1 Gigabit SFP+ Transceiver into Ethernet Port 0 on the USRP X300/x310 device. +* Plug a 1 Gigabit SFP Transceiver into Ethernet Port 0 on the USRP X300/X310 device. * Use the Ethernet cable to connect the SFP+ transciever on the device to the host computer. For maximum throughput, Ettus Research recommends that you connect each device to its own dedicated Gigabit Ethernet interface on the host computer. * Connect the AC/DC power supply to the device and plug the supply into a wall outlet. * The OS will automatically recognize the device (e.g. when running uhd_find_devices). @@ -195,7 +278,7 @@ To get the latest images, simply use the uhd_images_downloader script: :: - uhd_images_downloader + sudo uhd_images_downloader **Windows:** @@ -223,7 +306,13 @@ Use JTAG to load FPGA images ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The USRP-X Series device features an on-board USB-JTAG programmer that can be accessed on the front-panel of the device. The iMPACT tool in the `Xilinx Programming Tools `_ package can be used to load an image over -the JTAG interface. +the JTAG interface. This can be useful for unbricking devices. + +If you have iMPACT installed, you can use the impact_jtag_programmer.sh tool to install images. Make sure your X3x0 is powered on and connected to your computer using the front panel USB JTAG connector (USB 2.0 is fine for this). Then run the tool: + +:: + + /impact_jtag_programmer.sh --fpga-path= --------------------------------------- Load the Images onto the On-board Flash @@ -276,8 +365,8 @@ Use the burner tool over PCI Express ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Device recovery and bricking ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -It is possible to put the device into an unusable state by loading bad images. -Fortunately, the USRP-X Series device can be loaded with a good image temporarily using the USB-JTAG interface. +It is possible to put the device into an unusable state by loading bad images ("bricking"). +Fortunately, the USRP-X Series device can be loaded with a good image temporarily using the USB-JTAG interface. Once booted into the safe image, the user can once again load images onto the device over Ethernet or PCI Express. ---------------- @@ -541,6 +630,13 @@ Monitor the host network traffic ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use Wireshark to monitor packets sent to and received from the device. +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Observe Ethernet port LEDs +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +When there is network traffic arriving at the Ethernet port, LEDs will light up. +You can use this to make sure the network connection is correctly set up, e.g. +by pinging the USRP and making sure the LEDs start to blink. + -------------- Hardware Notes -------------- diff --git a/host/docs/usrp_x3x0_config.rst b/host/docs/usrp_x3x0_config.rst new file mode 100644 index 000000000..9cb000275 --- /dev/null +++ b/host/docs/usrp_x3x0_config.rst @@ -0,0 +1,296 @@ +======================================================================== +UHD - System Configuration for USRP X3x0 Series +======================================================================== + +.. contents:: Table of Contents + +------------------------------------------------------------------------ +Configuring your Host PC +------------------------------------------------------------------------ + +The USRP X3x0 is capable of delivering very fast sample rates to the host PC, +and even high-powered desktops can have trouble keeping up at the higher rates. +You can improve the performance of your host by configuring a number of +settings that affect the performance of your computer. + +These are: + + * Kernel Version + * Network Configuration + * Power Management Configuration + * Real-Time & Priority Scheduling + * Building with ORC & Volk + +These items are covered in more detail, below. + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Kernel Version +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Performance issues may be encountered with Linux kernels earlier than 3.11. +Ettus Research strongly recommends using kernel version 3.11 or higher for high +sample rates. + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Network Configuration +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +When using Ethernet interfaces to communicate with the device, it is necessary +to configure a number of facets regarding your network connection. + +Configuring NetworkManager +------------------------------------- +Fedora and Ubuntu both use NetworkManager to manage network connections. +Unfortunately, NetworkManager often tries to take control of a connection and +will disconnect the interface. + +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. + +Configuring the Socket Buffers +------------------------------------- +It is necessary to increase the maximum size of the socket buffers to avoid +potential overflows and underruns at high sample rates. Add the following +entries into /etc/sysctl.conf (root privileges required): + +:: + + net.core.rmem_max=33554432 + net.core.wmem_max=33554432 + +Either restart the system or issue the following commands: + +:: + + sudo sysctl -w net.core.rmem_max=33554432 + sudo sysctl -w net.core.wmem_max=33554432 + + +Configuring the MTU +------------------------------------- +In order to achieve maximum performance, we recommend setting the MTU size to +9000 for 10 GigE and 1500 for 1 GigE. It is possible to use smaller MTUs, but this +can affect performance. With some NICs, setting the MTU too high can also cause issues, +though. To set the MTU to 9000, you can use the following command: + +:: + + sudo ifconfig mtu 9000 # For 10 GigE + +Using these MTUs will set the frame sizes for UHD communication to 8000 and 1472, +respectively. + +In some cases, specifying the frame size manually by adding the argument +"_frame_size=1472" can solve issues. Note that a frame size of 1472 will limit +the available sampling rate, although this is likely not a problem issue on 1 GigE. + + +Configuring the Firewall +------------------------------------- +Many Linux distributions come installed with a Firewall, by default. The +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. + +Interface Configuration File (Fedora) +------------------------------------- +On Fedora systems, you can configure the network interface mostly from one +place (with the exception of the socket buffers). Each interface on your system +should have a file in: + +:: + + /etc/sysconfig/network-scripts/ + +As an example, if your 1GigE interface is "em1", your "ifcfg-em1" configuration +file should look something like this, when configured for use with a USRP X3xx: + +:: + + TYPE="Ethernet" + BOOTPROTO="none" + IPADDR0="192.168.10.1" + DEFROUTE="yes" + IPV4_FAILURE_FATAL="no" + IPV6INIT="no" + IPV6_FAILURE_FATAL="no" + NAME="em1" + UUID="" + ONBOOT="no" + HWADDR"" + PEERDNS="yes" + PEERROUTES="yes" + ZONE="trusted" + MTU="9000" + NM_MANAGED="no" + +The above file was generated and modified on a "Fedora 20" system. + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Power Management +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Power management on the host system attempts to save power by reducing clock +frequencies or even powering off devices while not in use. This can lead to +significant performance issues when trying to operate at high sample rates. +Ettus Research strongly recommends disabling all power management. + + +Setting the CPU Governors +------------------------------------- +In Linux, the CPU governors dictate the frequency at which the CPU operates and +attempt to reduce the CPU frequencies at certain times to save power. When +running at high sample rates, reduction of CPU frequencies can cause +significant performance issues. To prevent those issues, set the governor to +"performance". + +**Ubuntu:** +1. Install cpufrequtils: + +:: + + sudo apt-get install cpufrequtils + +2. Edit /etc/init.d/cpufrequtils and set GOVERNOR="performance" on the appropriate line (run as root): + +:: + + sed s/^GOVERNOR=.*$/GOVERNOR=\"performance\"/g /etc/init.d/cpufrequtils > /etc/init.d/cpufrequtils + +3. Restart cpufrequtils: + +:: + + sudo /etc/init.d/cpufrequtils restart + +**Fedora:** + +:: + + sudo cpupower frequency-set -g performance + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Real-Time & Priority Scheduling +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Enabling real-time and priority scheduling can improve the total processing +throughput of your application. Priority scheduling should be enabled for UHD, +and real-time scheduling can be enabled by your application. + +Thread Priority Scheduling with UHD +------------------------------------- +For information regarding how to enable priority scheduling for UHD on your +system, please see the `General UHD Notes <./general.html#threading-notes>`_. + +Real-Time Scheduling in your Application +---------------------------------------- +Please note that turning on real-time scheduling in your application **may lock +up your computer** if the processor cannot keep up with the application. You +should generally avoid using real-time scheduling unless you need to. + +Real-time scheduling is enabled via different methods depending on your +application and operating system. In GNU Radio Companion, it can be turned on in +each individual flowgraph. + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Building with ORC & Volk +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Especially when running high-performance applications, processing performance +can be dramatically improved by SIMD instructions. UHD uses ORC to provide SIMD +capability, and GNU Radio includes a SIMD library called "Volk". These should +both be used to guarantee optimum performance. + +Compiling UHD with ORC +------------------------------------- +ORC, the `Oil Runtime Compiler `_, is +a third-party compiler that UHD uses to create efficient SIMD code for your +particular computer. ORC is generally easily installed from your OS's package +manager. + +On Fedora: + +:: + + $ sudo yum update; sudo yum install orc-compiler orc-devel + +On Ubuntu: + +:: + + $ sudo apt-get update; sudo apt-get install liborc- liborc--dev + +After installing ORC, when building UHD from source, you should see "ORC" as +one of the configured UHD components. + +:: + + -- ###################################################### + -- # UHD enabled components + -- ###################################################### + -- * LibUHD + + -- * ORC + +Compiling GNURadio with Volk +------------------------------------- +If you are using GNURadio to build applications, you should compile GNURadio +with Volk. For instructions on how to do this, `refer to the GNURadio wiki +`_. + + +------------------------------------------------------------------------ +Host PC Hardware Selection +------------------------------------------------------------------------ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Motherboard +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Testing has shown that some motherboards do not provide enough PCIe bus +bandwidth to support higher sample rates. Motherboards with PCIe 3.0 are +required and the PCIe architecture of the motherboard should be carefully +considered. Slots with dedicated PCIe lanes should be used for PCIe or 10GbE +cards that will be connected to the X3x0 device. + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +10GbE NIC +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Intel or Myricom 10GbE NICs are recommended. Mellanox, SolarFlare, and Chelsio +10GbE NICs are not currently recommended. The Ethernet card should be plugged +into the slot that has the most direct connection with the CPU (PCIe lanes are +not shared with another slot). Refer to the motherboard manual for more +information on PCIe architecture. + +------------------------------------------------------------------------ +Troubleshooting Performance Issues +------------------------------------------------------------------------ +The output on the host console provides indicators of performance issues in the +form of single upper-case letters. The following table lists the letters, +their meanings, and possible causes: + +========= ====================== ==================================================================== +Indicator Meaning Possible Causes +========= ====================== ==================================================================== +O Overflow on RX - Data is not being consumed by user's application fast enough. + - CPU governor or other power management not configured correctly. +D Dropped packet on RX - Network hardware failure. (Check host NIC, cable, switch, etc...) + - PCIe bus on host cannot sustain throughput. (Check ethtool -S ). + - CPU governor or other power management not configured correctly. + - Frame size might not work with the current NIC's MTU. +U Underflow on TX - Samples are not being produced by user's application fast enough. + - CPU governor or other power management not configured correctly. +L Late packet - Samples are not being produced by user's application fast enough. + (usually on MIMO TX) - CPU governor or other power management not configured correctly. + - Incorrect/invalid time_spec provided. +S Sequence error on TX - Network hardware failure. (Check host NIC, cable, switch, etc...) + - Frame size might not work with the current NIC's MTU. +========= ====================== ==================================================================== + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Troubleshooting Ethernet Issues +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +1. First, check 'ifconfig ' to see if there are any errors reported + on the interface. If there are errors, it is most likely a network hardware + problem. +2. Next, check the output of 'ethtool -S '. The output is + driver-specific, but may give important clues as to what may be happening. + For example, a high value on rx_missed_errors for an Intel NIC indicates + that the bus (i.e. PCIe) is not keeping up. +3. Finally, Wireshark can be used to validate the traffic between the host and + device and make sure there is no unwanted traffic on the interface. + diff --git a/host/examples/rx_ascii_art_dft.cpp b/host/examples/rx_ascii_art_dft.cpp index ab835a07d..85cf8b50c 100644 --- a/host/examples/rx_ascii_art_dft.cpp +++ b/host/examples/rx_ascii_art_dft.cpp @@ -97,7 +97,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ } std::cout << boost::format("Setting RX Freq: %f MHz...") % (freq/1e6) << std::endl; uhd::tune_request_t tune_request(freq); - if(vm.count("int-n")) tune_request.args = uhd::device_addr_t("mode_n=int-n"); + if(vm.count("int-n")) tune_request.args = uhd::device_addr_t("mode_n=integer"); usrp->set_rx_freq(tune_request); std::cout << boost::format("Actual RX Freq: %f MHz...") % (usrp->get_rx_freq()/1e6) << std::endl << std::endl; diff --git a/host/examples/rx_samples_to_file.cpp b/host/examples/rx_samples_to_file.cpp index 75afddbd9..0d42404d3 100644 --- a/host/examples/rx_samples_to_file.cpp +++ b/host/examples/rx_samples_to_file.cpp @@ -290,7 +290,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ if (vm.count("freq")){ //with default of 0.0 this will always be true std::cout << boost::format("Setting RX Freq: %f MHz...") % (freq/1e6) << std::endl; uhd::tune_request_t tune_request(freq); - if(vm.count("int-n")) tune_request.args = uhd::device_addr_t("mode_n=int-n"); + if(vm.count("int-n")) tune_request.args = uhd::device_addr_t("mode_n=integer"); usrp->set_rx_freq(tune_request); std::cout << boost::format("Actual RX Freq: %f MHz...") % (usrp->get_rx_freq()/1e6) << std::endl << std::endl; } diff --git a/host/examples/rx_samples_to_udp.cpp b/host/examples/rx_samples_to_udp.cpp index 72fb54bd3..2f62652e0 100644 --- a/host/examples/rx_samples_to_udp.cpp +++ b/host/examples/rx_samples_to_udp.cpp @@ -82,7 +82,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ //set the rx center frequency std::cout << boost::format("Setting RX Freq: %f Mhz...") % (freq/1e6) << std::endl; uhd::tune_request_t tune_request(freq); - if(vm.count("int-n")) tune_request.args = uhd::device_addr_t("mode_n=int-n"); + if(vm.count("int-n")) tune_request.args = uhd::device_addr_t("mode_n=integer"); usrp->set_rx_freq(tune_request); std::cout << boost::format("Actual RX Freq: %f Mhz...") % (usrp->get_rx_freq()/1e6) << std::endl << std::endl; diff --git a/host/examples/tx_bursts.cpp b/host/examples/tx_bursts.cpp index 333e15939..fec89a0e4 100644 --- a/host/examples/tx_bursts.cpp +++ b/host/examples/tx_bursts.cpp @@ -101,7 +101,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ std::cout << boost::format("Setting TX Freq: %f MHz...") % (freq/1e6) << std::endl; for(size_t i=0; i < channel_nums.size(); i++){ uhd::tune_request_t tune_request(freq); - if(vm.count("int-n")) tune_request.args = uhd::device_addr_t("mode_n=int-n"); + if(vm.count("int-n")) tune_request.args = uhd::device_addr_t("mode_n=integer"); usrp->set_tx_freq(tune_request, channel_nums[i]); } std::cout << boost::format("Actual TX Freq: %f MHz...") % (usrp->get_tx_freq()/1e6) << std::endl << std::endl; diff --git a/host/examples/tx_samples_from_file.cpp b/host/examples/tx_samples_from_file.cpp index 000f5086b..f911c446e 100644 --- a/host/examples/tx_samples_from_file.cpp +++ b/host/examples/tx_samples_from_file.cpp @@ -137,7 +137,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ uhd::tune_request_t tune_request; if(vm.count("lo_off")) tune_request = uhd::tune_request_t(freq, lo_off); else tune_request = uhd::tune_request_t(freq); - if(vm.count("int-n")) tune_request.args = uhd::device_addr_t("mode_n=int-n"); + if(vm.count("int-n")) tune_request.args = uhd::device_addr_t("mode_n=integer"); usrp->set_tx_freq(tune_request); std::cout << boost::format("Actual TX Freq: %f MHz...") % (usrp->get_tx_freq()/1e6) << std::endl << std::endl; diff --git a/host/examples/tx_waveforms.cpp b/host/examples/tx_waveforms.cpp index 11eec20a3..ba89a8ad5 100644 --- a/host/examples/tx_waveforms.cpp +++ b/host/examples/tx_waveforms.cpp @@ -172,7 +172,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ for(size_t ch = 0; ch < channel_nums.size(); ch++) { std::cout << boost::format("Setting TX Freq: %f MHz...") % (freq/1e6) << std::endl; uhd::tune_request_t tune_request(freq); - if(vm.count("int-n")) tune_request.args = uhd::device_addr_t("mode_n=int-n"); + if(vm.count("int-n")) tune_request.args = uhd::device_addr_t("mode_n=integer"); usrp->set_tx_freq(tune_request, channel_nums[ch]); std::cout << boost::format("Actual TX Freq: %f MHz...") % (usrp->get_tx_freq(channel_nums[ch])/1e6) << std::endl << std::endl; diff --git a/host/examples/txrx_loopback_to_file.cpp b/host/examples/txrx_loopback_to_file.cpp index 18c564097..3d3cf1dfc 100644 --- a/host/examples/txrx_loopback_to_file.cpp +++ b/host/examples/txrx_loopback_to_file.cpp @@ -322,7 +322,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ for(size_t ch = 0; ch < tx_channel_nums.size(); ch++) { std::cout << boost::format("Setting TX Freq: %f MHz...") % (tx_freq/1e6) << std::endl; uhd::tune_request_t tx_tune_request(tx_freq); - if(vm.count("tx-int-n")) tx_tune_request.args = uhd::device_addr_t("mode_n=int-n"); + if(vm.count("tx-int-n")) tx_tune_request.args = uhd::device_addr_t("mode_n=integer"); tx_usrp->set_tx_freq(tx_tune_request, tx_channel_nums[ch]); std::cout << boost::format("Actual TX Freq: %f MHz...") % (tx_usrp->get_tx_freq(tx_channel_nums[ch])/1e6) << std::endl << std::endl; @@ -351,7 +351,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ } std::cout << boost::format("Setting RX Freq: %f MHz...") % (rx_freq/1e6) << std::endl; uhd::tune_request_t rx_tune_request(rx_freq); - if(vm.count("rx-int-n")) rx_tune_request.args = uhd::device_addr_t("mode_n=int-n"); + if(vm.count("rx-int-n")) rx_tune_request.args = uhd::device_addr_t("mode_n=integer"); rx_usrp->set_rx_freq(rx_tune_request); std::cout << boost::format("Actual RX Freq: %f MHz...") % (rx_usrp->get_rx_freq()/1e6) << std::endl << std::endl; diff --git a/host/include/uhd/transport/CMakeLists.txt b/host/include/uhd/transport/CMakeLists.txt index c22554e97..2118674c6 100644 --- a/host/include/uhd/transport/CMakeLists.txt +++ b/host/include/uhd/transport/CMakeLists.txt @@ -21,6 +21,7 @@ UHD_INSTALL(FILES bounded_buffer.ipp buffer_pool.hpp if_addrs.hpp + udp_constants.hpp udp_simple.hpp udp_zero_copy.hpp tcp_zero_copy.hpp diff --git a/host/include/uhd/transport/udp_constants.hpp b/host/include/uhd/transport/udp_constants.hpp new file mode 100644 index 000000000..74ce3a2f8 --- /dev/null +++ b/host/include/uhd/transport/udp_constants.hpp @@ -0,0 +1,26 @@ +// +// 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 . +// + +#ifndef INCLUDED_UHD_TRANSPORT_UDP_CONSTANTS_HPP +#define INCLUDED_UHD_TRANSPORT_UDP_CONSTANTS_HPP + +// Constants related to UDP (over Ethernet) + +static const size_t IP_PROTOCOL_MIN_MTU_SIZE = 576; //bytes +static const size_t IP_PROTOCOL_UDP_PLUS_IP_HEADER = 28; //bytes. Note that this is the minimum value! + +#endif /* INCLUDED_UHD_TRANSPORT_UDP_CONSTANTS_HPP */ diff --git a/host/lib/usrp/common/adf435x_common.cpp b/host/lib/usrp/common/adf435x_common.cpp index e9d018fec..f0df6a334 100644 --- a/host/lib/usrp/common/adf435x_common.cpp +++ b/host/lib/usrp/common/adf435x_common.cpp @@ -25,8 +25,8 @@ using namespace uhd; * ADF 4350/4351 Tuning Utility **********************************************************************/ adf435x_tuning_settings tune_adf435x_synth( - double target_freq, - double ref_freq, + const double target_freq, + const double ref_freq, const adf435x_tuning_constraints& constraints, double& actual_freq) { @@ -66,9 +66,10 @@ adf435x_tuning_settings tune_adf435x_synth( * f_pfd = f_ref*(1+D)/(R*(1+T)) * f_vco = (N + (FRAC/MOD))*f_pfd * N = f_vco/f_pfd - FRAC/MOD = f_vco*((R*(T+1))/(f_ref*(1+D))) - FRAC/MOD - * f_rf = f_vco/RFdiv) - * f_actual = f_rf/2 + * f_actual = f_vco/RFdiv) */ + double feedback_freq = constraints.feedback_after_divider ? target_freq : vco_freq; + for(R = 1; R <= 1023; R+=1){ //PFD input frequency = f_ref/R ... ignoring Reference doubler/divide-by-2 (D & T) pfd_freq = ref_freq*(D?2:1)/(R*(T?2:1)); @@ -76,10 +77,8 @@ adf435x_tuning_settings tune_adf435x_synth( //keep the PFD frequency at or below 25MHz (Loop Filter Bandwidth) if (pfd_freq > constraints.pfd_freq_max) continue; - //ignore fractional part of tuning - //N is computed from target_freq and not vco_freq because the feedback - //mode is set to FEEDBACK_SELECT_DIVIDED - N = boost::uint16_t(std::floor(target_freq/pfd_freq)); + //First, ignore fractional part of tuning + N = boost::uint16_t(std::floor(feedback_freq/pfd_freq)); //keep N > minimum int divider requirement if (N < static_cast(constraints.int_range.start())) continue; @@ -94,9 +93,7 @@ adf435x_tuning_settings tune_adf435x_synth( //Fractional-N calculation MOD = 4095; //max fractional accuracy - //N is computed from target_freq and not vco_freq because the feedback - //mode is set to FEEDBACK_SELECT_DIVIDED - FRAC = static_cast((target_freq/pfd_freq - N)*MOD); + FRAC = static_cast((feedback_freq/pfd_freq - N)*MOD); if (constraints.force_frac0) { if (FRAC > (MOD / 2)) { //Round integer such that actual freq is closest to target N++; @@ -114,8 +111,14 @@ adf435x_tuning_settings tune_adf435x_synth( //Typical phase resync time documented in data sheet pg.24 static const double PHASE_RESYNC_TIME = 400e-6; - //actual frequency calculation - actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(D?2:1)/(R*(T?2:1))); + //If feedback after divider, then compensation for the divider is pulled into the INT value + int rf_div_compensation = constraints.feedback_after_divider ? 1 : RFdiv; + + //Compute the actual frequency in terms of ref_freq, N, FRAC, MOD, D, R and T. + actual_freq = ( + double((N + (double(FRAC)/double(MOD))) * + (ref_freq*(D?2:1)/(R*(T?2:1)))) + ) / rf_div_compensation; //load the settings adf435x_tuning_settings settings; @@ -128,15 +131,13 @@ adf435x_tuning_settings tune_adf435x_synth( settings.r_doubler_en = D; settings.band_select_clock_div = BS; settings.rf_divider = RFdiv; - settings.feedback_after_divider = true; std::string tuning_str = (constraints.force_frac0) ? "Integer-N" : "Fractional"; - UHD_LOGV(often) << boost::format("ADF 435X Frequencies (MHz): REQUESTED=%0.9f, ACTUAL=%0.9f" ) % (target_freq/1e6) % (actual_freq/1e6) << std::endl - << boost::format("ADF 435X Intermediates (MHz): VCO=%0.2f, PFD=%0.2f, BAND=%0.2f, REF=%0.2f" - ) % (vco_freq/1e6) % (pfd_freq/1e6) % (pfd_freq/BS/1e6) % (ref_freq/1e6) << std::endl + << boost::format("ADF 435X Intermediates (MHz): Feedback=%0.2f, VCO=%0.2f, PFD=%0.2f, BAND=%0.2f, REF=%0.2f" + ) % (feedback_freq/1e6) % (vco_freq/1e6) % (pfd_freq/1e6) % (pfd_freq/BS/1e6) % (ref_freq/1e6) << std::endl << boost::format("ADF 435X Tuning: %s") % tuning_str.c_str() << std::endl << boost::format("ADF 435X Settings: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d" ) % R % BS % N % FRAC % MOD % T % D % RFdiv << std::endl; diff --git a/host/lib/usrp/common/adf435x_common.hpp b/host/lib/usrp/common/adf435x_common.hpp index 715b1fd53..617b9d97f 100644 --- a/host/lib/usrp/common/adf435x_common.hpp +++ b/host/lib/usrp/common/adf435x_common.hpp @@ -33,6 +33,7 @@ struct adf435x_tuning_constraints { bool force_frac0; + bool feedback_after_divider; double ref_doubler_threshold; double pfd_freq_max; double band_sel_freq_max; @@ -50,12 +51,11 @@ struct adf435x_tuning_settings { boost::uint16_t clock_divider_12_bit; boost::uint8_t band_select_clock_div; boost::uint16_t rf_divider; - bool feedback_after_divider; }; adf435x_tuning_settings tune_adf435x_synth( - double target_freq, - double ref_freq, + const double target_freq, + const double ref_freq, const adf435x_tuning_constraints& constraints, double& actual_freq ); diff --git a/host/lib/usrp/dboard/db_cbx.cpp b/host/lib/usrp/dboard/db_cbx.cpp index ae41a7971..78ecd9794 100644 --- a/host/lib/usrp/dboard/db_cbx.cpp +++ b/host/lib/usrp/dboard/db_cbx.cpp @@ -18,7 +18,7 @@ #include "max2870_regs.hpp" #include "db_sbx_common.hpp" - +#include using namespace uhd; using namespace uhd::usrp; @@ -47,14 +47,14 @@ double sbx_xcvr::cbx::set_lo_freq(dboard_iface::unit_t unit, double target_freq) ) % (target_freq/1e6) << std::endl; /* - * If the user sets 'mode_n=int-n' in the tuning args, the user wishes to + * If the user sets 'mode_n=integer' in the tuning args, the user wishes to * tune in Integer-N mode, which can result in better spur * performance on some mixers. The default is fractional tuning. */ property_tree::sptr subtree = (unit == dboard_iface::UNIT_RX) ? self_base->get_rx_subtree() : self_base->get_tx_subtree(); device_addr_t tune_args = subtree->access("tune_args").get(); - bool is_int_n = (tune_args.get("mode_n","") == "int-n"); + bool is_int_n = boost::iequals(tune_args.get("mode_n",""), "integer"); //clip the input target_freq = cbx_freq_range.clip(target_freq); diff --git a/host/lib/usrp/dboard/db_sbx_version3.cpp b/host/lib/usrp/dboard/db_sbx_version3.cpp index ef0126557..463de5e15 100644 --- a/host/lib/usrp/dboard/db_sbx_version3.cpp +++ b/host/lib/usrp/dboard/db_sbx_version3.cpp @@ -20,6 +20,7 @@ #include "db_sbx_common.hpp" #include "../common/adf435x_common.hpp" #include +#include using namespace uhd; using namespace uhd::usrp; @@ -47,14 +48,14 @@ double sbx_xcvr::sbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar ) % (target_freq/1e6) << std::endl; /* - * If the user sets 'mode_n=int-n' in the tuning args, the user wishes to + * If the user sets 'mode_n=integer' in the tuning args, the user wishes to * tune in Integer-N mode, which can result in better spur * performance on some mixers. The default is fractional tuning. */ property_tree::sptr subtree = (unit == dboard_iface::UNIT_RX) ? self_base->get_rx_subtree() : self_base->get_tx_subtree(); device_addr_t tune_args = subtree->access("tune_args").get(); - bool is_int_n = (tune_args.get("mode_n","") == "int-n"); + bool is_int_n = boost::iequals(tune_args.get("mode_n",""), "integer"); //clip the input target_freq = sbx_freq_range.clip(target_freq); @@ -84,6 +85,7 @@ double sbx_xcvr::sbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar tuning_constraints.int_range = uhd::range_t(prescaler_to_min_int_div[prescaler], 4095); //INT is a 12-bit field tuning_constraints.pfd_freq_max = 25e6; tuning_constraints.rf_divider_range = uhd::range_t(1, 16); + tuning_constraints.feedback_after_divider = true; double actual_freq; adf435x_tuning_settings tuning_settings = tune_adf435x_synth( @@ -102,7 +104,7 @@ double sbx_xcvr::sbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar regs.int_16_bit = tuning_settings.int_16_bit; regs.mod_12_bit = tuning_settings.mod_12_bit; regs.clock_divider_12_bit = tuning_settings.clock_divider_12_bit; - regs.feedback_select = tuning_settings.feedback_after_divider ? + regs.feedback_select = tuning_constraints.feedback_after_divider ? adf4350_regs_t::FEEDBACK_SELECT_DIVIDED : adf4350_regs_t::FEEDBACK_SELECT_FUNDAMENTAL; regs.clock_div_mode = adf4350_regs_t::CLOCK_DIV_MODE_RESYNC_ENABLE; diff --git a/host/lib/usrp/dboard/db_sbx_version4.cpp b/host/lib/usrp/dboard/db_sbx_version4.cpp index 6c0cebb4b..ff4e19163 100644 --- a/host/lib/usrp/dboard/db_sbx_version4.cpp +++ b/host/lib/usrp/dboard/db_sbx_version4.cpp @@ -20,6 +20,7 @@ #include "db_sbx_common.hpp" #include "../common/adf435x_common.hpp" #include +#include using namespace uhd; using namespace uhd::usrp; @@ -48,14 +49,14 @@ double sbx_xcvr::sbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar ) % (target_freq/1e6) << std::endl; /* - * If the user sets 'mode_n=int-n' in the tuning args, the user wishes to + * If the user sets 'mode_n=integer' in the tuning args, the user wishes to * tune in Integer-N mode, which can result in better spur * performance on some mixers. The default is fractional tuning. */ property_tree::sptr subtree = (unit == dboard_iface::UNIT_RX) ? self_base->get_rx_subtree() : self_base->get_tx_subtree(); device_addr_t tune_args = subtree->access("tune_args").get(); - bool is_int_n = (tune_args.get("mode_n","") == "int-n"); + bool is_int_n = boost::iequals(tune_args.get("mode_n",""), "integer"); //clip the input target_freq = sbx_freq_range.clip(target_freq); @@ -87,6 +88,7 @@ double sbx_xcvr::sbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar tuning_constraints.int_range = uhd::range_t(prescaler_to_min_int_div[prescaler], 4095); //INT is a 12-bit field tuning_constraints.pfd_freq_max = 25e6; tuning_constraints.rf_divider_range = uhd::range_t(1, 64); + tuning_constraints.feedback_after_divider = true; double actual_freq; adf435x_tuning_settings tuning_settings = tune_adf435x_synth( @@ -105,7 +107,7 @@ double sbx_xcvr::sbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar regs.int_16_bit = tuning_settings.int_16_bit; regs.mod_12_bit = tuning_settings.mod_12_bit; regs.clock_divider_12_bit = tuning_settings.clock_divider_12_bit; - regs.feedback_select = tuning_settings.feedback_after_divider ? + regs.feedback_select = tuning_constraints.feedback_after_divider ? adf4351_regs_t::FEEDBACK_SELECT_DIVIDED : adf4351_regs_t::FEEDBACK_SELECT_FUNDAMENTAL; regs.clock_div_mode = adf4351_regs_t::CLOCK_DIV_MODE_RESYNC_ENABLE; diff --git a/host/lib/usrp/dboard/db_wbx_version2.cpp b/host/lib/usrp/dboard/db_wbx_version2.cpp index 2afdce4cd..c5945483d 100644 --- a/host/lib/usrp/dboard/db_wbx_version2.cpp +++ b/host/lib/usrp/dboard/db_wbx_version2.cpp @@ -30,6 +30,7 @@ #include #include #include +#include using namespace uhd; using namespace uhd::usrp; @@ -169,14 +170,14 @@ double wbx_base::wbx_version2::set_lo_freq(dboard_iface::unit_t unit, double tar ) % (target_freq/1e6) << std::endl; /* - * If the user sets 'mode_n=int-n' in the tuning args, the user wishes to + * If the user sets 'mode_n=integer' in the tuning args, the user wishes to * tune in Integer-N mode, which can result in better spur * performance on some mixers. The default is fractional tuning. */ property_tree::sptr subtree = (unit == dboard_iface::UNIT_RX) ? self_base->get_rx_subtree() : self_base->get_tx_subtree(); device_addr_t tune_args = subtree->access("tune_args").get(); - bool is_int_n = (tune_args.get("mode_n","") == "int-n"); + bool is_int_n = boost::iequals(tune_args.get("mode_n",""), "integer"); //map prescaler setting to mininmum integer divider (N) values (pg.18 prescaler) static const uhd::dict prescaler_to_min_int_div = map_list_of @@ -193,7 +194,15 @@ double wbx_base::wbx_version2::set_lo_freq(dboard_iface::unit_t unit, double tar (16, adf4350_regs_t::RF_DIVIDER_SELECT_DIV16) ; - adf4350_regs_t::prescaler_t prescaler = target_freq > 3e9 ? adf4350_regs_t::PRESCALER_8_9 : adf4350_regs_t::PRESCALER_4_5; + double reference_freq = self_base->get_iface()->get_clock_rate(unit); + //The mixer has a divide-by-2 stage on the LO port so the synthesizer + //frequency must 2x the target frequency + double synth_target_freq = target_freq * 2; + //TODO: Document why the following has to be true + bool div_resync_enabled = (target_freq > reference_freq); + + adf4350_regs_t::prescaler_t prescaler = + synth_target_freq > 3e9 ? adf4350_regs_t::PRESCALER_8_9 : adf4350_regs_t::PRESCALER_4_5; adf435x_tuning_constraints tuning_constraints; tuning_constraints.force_frac0 = is_int_n; @@ -202,11 +211,17 @@ double wbx_base::wbx_version2::set_lo_freq(dboard_iface::unit_t unit, double tar tuning_constraints.int_range = uhd::range_t(prescaler_to_min_int_div[prescaler], 4095); tuning_constraints.pfd_freq_max = 25e6; tuning_constraints.rf_divider_range = uhd::range_t(1, 16); + //When divider resync is enabled, a 180 deg phase error is introduced when syncing + //multiple WBX boards. Switching to fundamental mode works arounds this issue. + tuning_constraints.feedback_after_divider = div_resync_enabled; - double actual_freq; + double synth_actual_freq = 0; adf435x_tuning_settings tuning_settings = tune_adf435x_synth( - target_freq, self_base->get_iface()->get_clock_rate(unit), - tuning_constraints, actual_freq); + synth_target_freq, reference_freq, tuning_constraints, synth_actual_freq); + + //The mixer has a divide-by-2 stage on the LO port so the synthesizer + //actual_freq must /2 the synth_actual_freq + double actual_freq = synth_actual_freq / 2; //load the register values adf4350_regs_t regs; @@ -222,10 +237,12 @@ double wbx_base::wbx_version2::set_lo_freq(dboard_iface::unit_t unit, double tar regs.int_16_bit = tuning_settings.int_16_bit; regs.mod_12_bit = tuning_settings.mod_12_bit; regs.clock_divider_12_bit = tuning_settings.clock_divider_12_bit; - regs.feedback_select = tuning_settings.feedback_after_divider ? + regs.feedback_select = tuning_constraints.feedback_after_divider ? adf4350_regs_t::FEEDBACK_SELECT_DIVIDED : adf4350_regs_t::FEEDBACK_SELECT_FUNDAMENTAL; - regs.clock_div_mode = adf4350_regs_t::CLOCK_DIV_MODE_RESYNC_ENABLE; + regs.clock_div_mode = div_resync_enabled ? + adf4350_regs_t::CLOCK_DIV_MODE_RESYNC_ENABLE : + adf4350_regs_t::CLOCK_DIV_MODE_FAST_LOCK; regs.prescaler = prescaler; regs.r_counter_10_bit = tuning_settings.r_counter_10_bit; regs.reference_divide_by_2 = tuning_settings.r_divide_by_2_en ? diff --git a/host/lib/usrp/dboard/db_wbx_version3.cpp b/host/lib/usrp/dboard/db_wbx_version3.cpp index e30d6c665..80ecb426b 100644 --- a/host/lib/usrp/dboard/db_wbx_version3.cpp +++ b/host/lib/usrp/dboard/db_wbx_version3.cpp @@ -29,6 +29,7 @@ #include #include #include +#include using namespace uhd; using namespace uhd::usrp; @@ -200,14 +201,14 @@ double wbx_base::wbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar ) % (target_freq/1e6) << std::endl; /* - * If the user sets 'mode_n=int-n' in the tuning args, the user wishes to + * If the user sets 'mode_n=integer' in the tuning args, the user wishes to * tune in Integer-N mode, which can result in better spur * performance on some mixers. The default is fractional tuning. */ property_tree::sptr subtree = (unit == dboard_iface::UNIT_RX) ? self_base->get_rx_subtree() : self_base->get_tx_subtree(); device_addr_t tune_args = subtree->access("tune_args").get(); - bool is_int_n = (tune_args.get("mode_n","") == "int-n"); + bool is_int_n = boost::iequals(tune_args.get("mode_n",""), "integer"); //map prescaler setting to mininmum integer divider (N) values (pg.18 prescaler) static const uhd::dict prescaler_to_min_int_div = map_list_of @@ -224,7 +225,15 @@ double wbx_base::wbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar (16, adf4350_regs_t::RF_DIVIDER_SELECT_DIV16) ; - adf4350_regs_t::prescaler_t prescaler = target_freq > 3e9 ? adf4350_regs_t::PRESCALER_8_9 : adf4350_regs_t::PRESCALER_4_5; + double reference_freq = self_base->get_iface()->get_clock_rate(unit); + //The mixer has a divide-by-2 stage on the LO port so the synthesizer + //frequency must 2x the target frequency + double synth_target_freq = target_freq * 2; + //TODO: Document why the following has to be true + bool div_resync_enabled = (target_freq > reference_freq); + + adf4350_regs_t::prescaler_t prescaler = + synth_target_freq > 3e9 ? adf4350_regs_t::PRESCALER_8_9 : adf4350_regs_t::PRESCALER_4_5; adf435x_tuning_constraints tuning_constraints; tuning_constraints.force_frac0 = is_int_n; @@ -233,11 +242,17 @@ double wbx_base::wbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar tuning_constraints.int_range = uhd::range_t(prescaler_to_min_int_div[prescaler], 4095); tuning_constraints.pfd_freq_max = 25e6; tuning_constraints.rf_divider_range = uhd::range_t(1, 16); + //When divider resync is enabled, a 180 deg phase error is introduced when syncing + //multiple WBX boards. Switching to fundamental mode works arounds this issue. + tuning_constraints.feedback_after_divider = div_resync_enabled; - double actual_freq; + double synth_actual_freq = 0; adf435x_tuning_settings tuning_settings = tune_adf435x_synth( - target_freq, self_base->get_iface()->get_clock_rate(unit), - tuning_constraints, actual_freq); + synth_target_freq, reference_freq, tuning_constraints, synth_actual_freq); + + //The mixer has a divide-by-2 stage on the LO port so the synthesizer + //actual_freq must /2 the synth_actual_freq + double actual_freq = synth_actual_freq / 2; //load the register values adf4350_regs_t regs; @@ -253,10 +268,12 @@ double wbx_base::wbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar regs.int_16_bit = tuning_settings.int_16_bit; regs.mod_12_bit = tuning_settings.mod_12_bit; regs.clock_divider_12_bit = tuning_settings.clock_divider_12_bit; - regs.feedback_select = tuning_settings.feedback_after_divider ? + regs.feedback_select = tuning_constraints.feedback_after_divider ? adf4350_regs_t::FEEDBACK_SELECT_DIVIDED : adf4350_regs_t::FEEDBACK_SELECT_FUNDAMENTAL; - regs.clock_div_mode = adf4350_regs_t::CLOCK_DIV_MODE_RESYNC_ENABLE; + regs.clock_div_mode = div_resync_enabled ? + adf4350_regs_t::CLOCK_DIV_MODE_RESYNC_ENABLE : + adf4350_regs_t::CLOCK_DIV_MODE_FAST_LOCK; regs.prescaler = prescaler; regs.r_counter_10_bit = tuning_settings.r_counter_10_bit; regs.reference_divide_by_2 = tuning_settings.r_divide_by_2_en ? diff --git a/host/lib/usrp/dboard/db_wbx_version4.cpp b/host/lib/usrp/dboard/db_wbx_version4.cpp index dc1ae4df8..80ff3f998 100644 --- a/host/lib/usrp/dboard/db_wbx_version4.cpp +++ b/host/lib/usrp/dboard/db_wbx_version4.cpp @@ -29,6 +29,7 @@ #include #include #include +#include using namespace uhd; using namespace uhd::usrp; @@ -208,14 +209,14 @@ double wbx_base::wbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar ) % (target_freq/1e6) << std::endl; /* - * If the user sets 'mode_n=int-n' in the tuning args, the user wishes to + * If the user sets 'mode_n=integer' in the tuning args, the user wishes to * tune in Integer-N mode, which can result in better spur * performance on some mixers. The default is fractional tuning. */ property_tree::sptr subtree = (unit == dboard_iface::UNIT_RX) ? self_base->get_rx_subtree() : self_base->get_tx_subtree(); device_addr_t tune_args = subtree->access("tune_args").get(); - bool is_int_n = (tune_args.get("mode_n","") == "int-n"); + bool is_int_n = boost::iequals(tune_args.get("mode_n",""), "integer"); //map prescaler setting to mininmum integer divider (N) values (pg.18 prescaler) static const uhd::dict prescaler_to_min_int_div = map_list_of @@ -234,7 +235,15 @@ double wbx_base::wbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar (64, adf4351_regs_t::RF_DIVIDER_SELECT_DIV64) ; - adf4351_regs_t::prescaler_t prescaler = target_freq > 3e9 ? adf4351_regs_t::PRESCALER_8_9 : adf4351_regs_t::PRESCALER_4_5; + double reference_freq = self_base->get_iface()->get_clock_rate(unit); + //The mixer has a divide-by-2 stage on the LO port so the synthesizer + //frequency must 2x the target frequency + double synth_target_freq = target_freq * 2; + //TODO: Document why the following has to be true + bool div_resync_enabled = (target_freq > reference_freq); + + adf4351_regs_t::prescaler_t prescaler = + synth_target_freq > 3e9 ? adf4351_regs_t::PRESCALER_8_9 : adf4351_regs_t::PRESCALER_4_5; adf435x_tuning_constraints tuning_constraints; tuning_constraints.force_frac0 = is_int_n; @@ -243,11 +252,17 @@ double wbx_base::wbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar tuning_constraints.int_range = uhd::range_t(prescaler_to_min_int_div[prescaler], 4095); tuning_constraints.pfd_freq_max = 25e6; tuning_constraints.rf_divider_range = uhd::range_t(1, 64); + //When divider resync is enabled, a 180 deg phase error is introduced when syncing + //multiple WBX boards. Switching to fundamental mode works arounds this issue. + tuning_constraints.feedback_after_divider = div_resync_enabled; - double actual_freq; + double synth_actual_freq = 0; adf435x_tuning_settings tuning_settings = tune_adf435x_synth( - target_freq, self_base->get_iface()->get_clock_rate(unit), - tuning_constraints, actual_freq); + synth_target_freq, reference_freq, tuning_constraints, synth_actual_freq); + + //The mixer has a divide-by-2 stage on the LO port so the synthesizer + //actual_freq must /2 the synth_actual_freq + double actual_freq = synth_actual_freq / 2; //load the register values adf4351_regs_t regs; @@ -263,10 +278,12 @@ double wbx_base::wbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar regs.int_16_bit = tuning_settings.int_16_bit; regs.mod_12_bit = tuning_settings.mod_12_bit; regs.clock_divider_12_bit = tuning_settings.clock_divider_12_bit; - regs.feedback_select = tuning_settings.feedback_after_divider ? + regs.feedback_select = tuning_constraints.feedback_after_divider ? adf4351_regs_t::FEEDBACK_SELECT_DIVIDED : adf4351_regs_t::FEEDBACK_SELECT_FUNDAMENTAL; - regs.clock_div_mode = adf4351_regs_t::CLOCK_DIV_MODE_RESYNC_ENABLE; + regs.clock_div_mode = div_resync_enabled ? + adf4351_regs_t::CLOCK_DIV_MODE_RESYNC_ENABLE : + adf4351_regs_t::CLOCK_DIV_MODE_FAST_LOCK; regs.prescaler = prescaler; regs.r_counter_10_bit = tuning_settings.r_counter_10_bit; regs.reference_divide_by_2 = tuning_settings.r_divide_by_2_en ? @@ -307,5 +324,6 @@ double wbx_base::wbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar UHD_LOGV(often) << boost::format( "%s tune: actual frequency %f Mhz" ) % board_name.c_str() % (actual_freq/1e6) << std::endl; + return actual_freq; } diff --git a/host/lib/usrp/gps_ctrl.cpp b/host/lib/usrp/gps_ctrl.cpp index 4bf8eb3a3..c0d44abd5 100644 --- a/host/lib/usrp/gps_ctrl.cpp +++ b/host/lib/usrp/gps_ctrl.cpp @@ -30,6 +30,7 @@ #include "boost/tuple/tuple.hpp" #include "boost/foreach.hpp" +#include using namespace uhd; using namespace boost::gregorian; @@ -68,34 +69,36 @@ private: return std::string(); } - std::string msg = _recv(); + const std::list list = boost::assign::list_of("GPGGA")("GPRMC")("SERVO"); static const boost::regex status_regex("\\d\\d-\\d\\d-\\d\\d"); + std::map msgs; + + // Get all GPSDO messages available + // Creating a map here because we only want the latest of each message type + for (std::string msg = _recv(); msg.length() > 6; msg = _recv()) + { + // Look for SERVO message + if (boost::regex_search(msg, status_regex, boost::regex_constants::match_continuous)) + msgs["SERVO"] = msg; + else + msgs[msg.substr(1,5)] = msg; + } + boost::system_time time = boost::get_system_time(); - if(msg.size() < 6) - return std::string(); - std::string nmea = msg.substr(1,5); - const std::list list = boost::assign::list_of("GPGGA")("GPRMC"); + // Update sensors with newly read data BOOST_FOREACH(std::string key, list) { - // beginning matches one of the NMEA keys - if(!nmea.compare(key)) { - sensors[key] = boost::make_tuple(msg, time, !sensor.compare(key)); - // if this was what we're looking for return it - return (!sensor.compare(key))? msg : std::string(); - } + if (msgs[key].length()) + sensors[key] = boost::make_tuple(msgs[key], time, !sensor.compare(key)); } - //We're still here so it's not one of the NMEA strings from above - if(boost::regex_search(msg, status_regex, boost::regex_constants::match_continuous)) { - trim(msg); - sensors["SERVO"] = boost::make_tuple(msg, time, false); - if(!sensor.compare("SERVO")) - return msg; - else - return std::string(); - } + // Return requested sensor if it was updated + if (msgs[sensor].length()) + return msgs[sensor]; + return std::string(); } + public: gps_ctrl_impl(uart_iface::sptr uart){ _uart = uart; @@ -332,8 +335,8 @@ private: } } - std::string _recv(void){ - return _uart->read_uart(GPS_TIMEOUT_DELAY_MS/1000.); + std::string _recv(double timeout = GPS_TIMEOUT_DELAY_MS/1000.){ + return _uart->read_uart(timeout); } void _send(const std::string &buf){ diff --git a/host/lib/usrp/x300/x300_clock_ctrl.cpp b/host/lib/usrp/x300/x300_clock_ctrl.cpp index 1a4cd4668..a986928a7 100644 --- a/host/lib/usrp/x300/x300_clock_ctrl.cpp +++ b/host/lib/usrp/x300/x300_clock_ctrl.cpp @@ -24,6 +24,8 @@ #include #include +static const double X300_REF_CLK_OUT_RATE = 10e6; + using namespace uhd; class x300_clock_ctrl_impl : public x300_clock_ctrl { @@ -66,7 +68,7 @@ double get_sysref_clock_rate(void) { double get_refout_clock_rate(void) { //We support only one reference output rate - return 10e6; + return X300_REF_CLK_OUT_RATE; } void set_dboard_rate(const x300_clock_which_t, double rate) { @@ -292,7 +294,7 @@ void set_master_clock_rate(double clock_rate) { _lmk04816_regs.CLKout8_9_DIV = vco_div; // Register 5 _lmk04816_regs.CLKout10_11_PD = lmk04816_regs_t::CLKOUT10_11_PD_NORMAL; - _lmk04816_regs.CLKout10_11_DIV = vco_div; + _lmk04816_regs.CLKout10_11_DIV = vco_div * static_cast(clock_rate/X300_REF_CLK_OUT_RATE); // Register 6 _lmk04816_regs.CLKout0_TYPE = lmk04816_regs_t::CLKOUT0_TYPE_LVDS; //FPGA diff --git a/host/lib/usrp/x300/x300_impl.cpp b/host/lib/usrp/x300/x300_impl.cpp index f62967018..b20897fc6 100644 --- a/host/lib/usrp/x300/x300_impl.cpp +++ b/host/lib/usrp/x300/x300_impl.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -399,34 +400,25 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr) if (key.find("send") != std::string::npos) mb.send_args[key] = dev_addr[key]; } - const std::vector DB_NAMES = boost::assign::list_of("A")("B"); - - //create basic communication - UHD_MSG(status) << "Setup basic communication..." << std::endl; - if (mb.xport_path == "nirio") { - mb.zpu_ctrl = x300_make_ctrl_iface_pcie(mb.rio_fpga_interface->get_kernel_proxy()); - } else { - mb.zpu_ctrl = x300_make_ctrl_iface_enet(udp_simple::make_connected(mb.addr, - BOOST_STRINGIZE(X300_FW_COMMS_UDP_PORT))); - } - - if (mb.xport_path == "eth") - { - mtu_result_t user_set; - user_set.recv_mtu = dev_addr.has_key("recv_frame_size") \ - ? boost::lexical_cast(dev_addr["recv_frame_size"]) \ - : X300_ETH_DATA_FRAME_SIZE; - user_set.send_mtu = dev_addr.has_key("send_frame_size") \ - ? boost::lexical_cast(dev_addr["send_frame_size"]) \ - : X300_ETH_DATA_FRAME_SIZE; - - // Detect the MTU on the path to the USRP - mtu_result_t result; - try { - result = determine_mtu(mb.addr, user_set); - } catch(std::exception &e) { - UHD_MSG(error) << e.what() << std::endl; - } + if (mb.xport_path == "eth" ) { + /* This is an ETH connection. Figure out what the maximum supported frame + * size is for the transport in the up and down directions. The frame size + * depends on the host PIC's NIC's MTU settings. To determine the frame size, + * we test for support up to an expected "ceiling". If the user + * specified a frame size, we use that frame size as the ceiling. If no + * frame size was specified, we use the maximum UHD frame size. + * + * To optimize performance, the frame size should be greater than or equal + * to the frame size that UHD uses so that frames don't get split across + * multiple transmission units - this is why the limits passed into the + * 'determine_max_frame_size' function are actually frame sizes. */ + frame_size_t req_max_frame_size; + req_max_frame_size.recv_frame_size = (mb.recv_args.has_key("recv_frame_size")) \ + ? boost::lexical_cast(mb.recv_args["recv_frame_size"]) \ + : X300_10GE_DATA_FRAME_MAX_SIZE; + req_max_frame_size.send_frame_size = (mb.send_args.has_key("send_frame_size")) \ + ? boost::lexical_cast(mb.send_args["send_frame_size"]) \ + : X300_10GE_DATA_FRAME_MAX_SIZE; #if defined UHD_PLATFORM_LINUX const std::string mtu_tool("ip link"); @@ -436,23 +428,47 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr) const std::string mtu_tool("ifconfig"); #endif - if(result.recv_mtu < user_set.recv_mtu) { + // Detect the frame size on the path to the USRP + try { + max_frame_sizes = determine_max_frame_size(mb.addr, req_max_frame_size); + } catch(std::exception &e) { + UHD_MSG(error) << e.what() << std::endl; + } + + if ((mb.recv_args.has_key("recv_frame_size")) + && (req_max_frame_size.recv_frame_size < max_frame_sizes.recv_frame_size)) { UHD_MSG(warning) - << boost::format("The receive path contains entities that do not support MTUs >= one recv frame's size (%lu).") - % user_set.recv_mtu << std::endl - << boost::format("Please verify your NIC's MTU setting using '%s' or set the recv_frame_size argument.") - % mtu_tool << std::endl; + << boost::format("You requested a receive frame size of (%lu) but your NIC's max frame size is (%lu).") + % req_max_frame_size.recv_frame_size << max_frame_sizes.recv_frame_size << std::endl + << boost::format("Please verify your NIC's MTU setting using '%s' or set the recv_frame_size argument appropriately.") + % mtu_tool << std::endl + << "UHD will use the auto-detected max frame size for this connection." + << std::endl; } - if(result.send_mtu < user_set.send_mtu) { + if ((mb.recv_args.has_key("send_frame_size")) + && (req_max_frame_size.send_frame_size < max_frame_sizes.send_frame_size)) { UHD_MSG(warning) - << boost::format("The send path contains entities that do not support MTUs >= one send frame's size (%lu).") - % user_set.send_mtu << std::endl - << boost::format("Please verify your NIC's MTU setting using '%s' or set the send_frame_size argument.") - % mtu_tool << std::endl; + << boost::format("You requested a send frame size of (%lu) but your NIC's max frame size is (%lu).") + % req_max_frame_size.send_frame_size << max_frame_sizes.send_frame_size << std::endl + << boost::format("Please verify your NIC's MTU setting using '%s' or set the send_frame_size argument appropriately.") + % mtu_tool << std::endl + << "UHD will use the auto-detected max frame size for this connection." + << std::endl; } } + const std::vector DB_NAMES = boost::assign::list_of("A")("B"); + + //create basic communication + UHD_MSG(status) << "Setup basic communication..." << std::endl; + if (mb.xport_path == "nirio") { + mb.zpu_ctrl = x300_make_ctrl_iface_pcie(mb.rio_fpga_interface->get_kernel_proxy()); + } else { + mb.zpu_ctrl = x300_make_ctrl_iface_enet(udp_simple::make_connected(mb.addr, + BOOST_STRINGIZE(X300_FW_COMMS_UDP_PORT))); + } + mb.claimer_task = uhd::task::make(boost::bind(&x300_impl::claimer_loop, this, mb.zpu_ctrl)); //extract the FW path for the X300 @@ -469,6 +485,9 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr) this->check_fw_compat(mb_path, mb.zpu_ctrl); this->check_fpga_compat(mb_path, mb.zpu_ctrl); + //store which FPGA image is loaded + mb.loaded_fpga_image = get_fpga_option(mb.zpu_ctrl); + //low speed perif access mb.zpu_spi = spi_core_3000::make(mb.zpu_ctrl, SR_ADDR(SET0_BASE, ZPU_SR_SPI), SR_ADDR(SET0_BASE, ZPU_RB_SPI)); @@ -1030,8 +1049,6 @@ x300_impl::both_xports_t x300_impl::make_transport( ) { mboard_members_t &mb = _mb[mb_index]; - const std::string& addr = mb.addr; - const std::string& xport_path = mb.xport_path; both_xports_t xports; sid_config_t config; @@ -1047,47 +1064,122 @@ x300_impl::both_xports_t x300_impl::make_transport( (prefix != X300_RADIO_DEST_PREFIX_CTRL) ? args : DEFAULT_XPORT_ARGS; zero_copy_xport_params default_buff_args; - if (xport_path == "nirio") { + + if (mb.xport_path == "nirio") { default_buff_args.send_frame_size = - (prefix == X300_RADIO_DEST_PREFIX_TX) ? X300_PCIE_DATA_FRAME_SIZE : X300_PCIE_MSG_FRAME_SIZE; + (prefix == X300_RADIO_DEST_PREFIX_TX) + ? X300_PCIE_DATA_FRAME_SIZE + : X300_PCIE_MSG_FRAME_SIZE; + default_buff_args.recv_frame_size = - (prefix == X300_RADIO_DEST_PREFIX_RX) ? X300_PCIE_DATA_FRAME_SIZE : X300_PCIE_MSG_FRAME_SIZE; + (prefix == X300_RADIO_DEST_PREFIX_RX) + ? X300_PCIE_DATA_FRAME_SIZE + : X300_PCIE_MSG_FRAME_SIZE; + default_buff_args.num_send_frames = - (prefix == X300_RADIO_DEST_PREFIX_TX) ? X300_PCIE_DATA_NUM_FRAMES : X300_PCIE_MSG_NUM_FRAMES; + (prefix == X300_RADIO_DEST_PREFIX_TX) + ? X300_PCIE_DATA_NUM_FRAMES + : X300_PCIE_MSG_NUM_FRAMES; + default_buff_args.num_recv_frames = - (prefix == X300_RADIO_DEST_PREFIX_RX) ? X300_PCIE_DATA_NUM_FRAMES : X300_PCIE_MSG_NUM_FRAMES; + (prefix == X300_RADIO_DEST_PREFIX_RX) + ? X300_PCIE_DATA_NUM_FRAMES + : X300_PCIE_MSG_NUM_FRAMES; xports.recv = nirio_zero_copy::make( - mb.rio_fpga_interface, get_pcie_dma_channel(destination, prefix), default_buff_args, xport_args); + mb.rio_fpga_interface, + get_pcie_dma_channel(destination, prefix), + default_buff_args, + xport_args); + xports.send = xports.recv; //For the nirio transport, buffer size is depends on the frame size and num frames xports.recv_buff_size = xports.recv->get_num_recv_frames() * xports.recv->get_recv_frame_size(); xports.send_buff_size = xports.send->get_num_send_frames() * xports.send->get_send_frame_size(); - } else { + } else if (mb.xport_path == "eth") { + + /* Determine what the recommended frame size is for this + * connection type.*/ + size_t eth_data_rec_frame_size = 0; + + 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; + } else if (mb.router_dst_here == X300_XB_DST_E1) { + eth_data_rec_frame_size = X300_10GE_DATA_FRAME_MAX_SIZE; + } + } else if (mb.loaded_fpga_image == "XGS") { + eth_data_rec_frame_size = X300_10GE_DATA_FRAME_MAX_SIZE; + } + + if (eth_data_rec_frame_size == 0) { + throw uhd::runtime_error("Unable to determine ETH link type."); + } + + /* Print a warning if the system's max available frame size is less than the most optimal + * frame size for this type of connection. */ + if (max_frame_sizes.send_frame_size < eth_data_rec_frame_size) { + UHD_MSG(warning) + << boost::format("For this connection, UHD recommends a send frame size of at least %lu for best\nperformance, but your system's MTU will only allow %lu.") + % eth_data_rec_frame_size + % max_frame_sizes.send_frame_size + << std::endl + << "This will negatively impact your maximum achievable sample rate." + << std::endl; + } + + if (max_frame_sizes.recv_frame_size < eth_data_rec_frame_size) { + UHD_MSG(warning) + << boost::format("For this connection, UHD recommends a receive frame size of at least %lu for best\nperformance, but your system's MTU will only allow %lu.") + % eth_data_rec_frame_size + % max_frame_sizes.recv_frame_size + << std::endl + << "This will negatively impact your maximum achievable sample rate." + << std::endl; + } + + // Account for headers + size_t system_max_send_frame_size = (size_t) max_frame_sizes.send_frame_size - 64; + size_t system_max_recv_frame_size = (size_t) max_frame_sizes.recv_frame_size - 64; + + // Make sure frame sizes do not exceed the max available value supported by UHD default_buff_args.send_frame_size = - (prefix == X300_RADIO_DEST_PREFIX_TX) ? X300_ETH_DATA_FRAME_SIZE : X300_ETH_MSG_FRAME_SIZE; + (prefix == X300_RADIO_DEST_PREFIX_TX) + ? std::min(system_max_send_frame_size, X300_10GE_DATA_FRAME_MAX_SIZE) + : std::min(system_max_send_frame_size, X300_ETH_MSG_FRAME_SIZE); + default_buff_args.recv_frame_size = - (prefix == X300_RADIO_DEST_PREFIX_RX) ? X300_ETH_DATA_FRAME_SIZE : X300_ETH_MSG_FRAME_SIZE; + (prefix == X300_RADIO_DEST_PREFIX_RX) + ? std::min(system_max_recv_frame_size, X300_10GE_DATA_FRAME_MAX_SIZE) + : std::min(system_max_recv_frame_size, X300_ETH_MSG_FRAME_SIZE); + default_buff_args.num_send_frames = - (prefix == X300_RADIO_DEST_PREFIX_TX) ? X300_ETH_DATA_NUM_FRAMES : X300_ETH_MSG_NUM_FRAMES; + (prefix == X300_RADIO_DEST_PREFIX_TX) + ? X300_ETH_DATA_NUM_FRAMES + : X300_ETH_MSG_NUM_FRAMES; + default_buff_args.num_recv_frames = - (prefix == X300_RADIO_DEST_PREFIX_RX) ? X300_ETH_DATA_NUM_FRAMES : X300_ETH_MSG_NUM_FRAMES; + (prefix == X300_RADIO_DEST_PREFIX_RX) + ? X300_ETH_DATA_NUM_FRAMES + : X300_ETH_MSG_NUM_FRAMES; //make a new transport - fpga has no idea how to talk to use on this yet udp_zero_copy::buff_params buff_params; - xports.recv = udp_zero_copy::make(addr, BOOST_STRINGIZE(X300_VITA_UDP_PORT), default_buff_args, buff_params, xport_args); + xports.recv = udp_zero_copy::make(mb.addr, + BOOST_STRINGIZE(X300_VITA_UDP_PORT), + default_buff_args, + buff_params, + xport_args); + xports.send = xports.recv; - //For the UDP transport the buffer size if the size of the socket buffer in the kernel + //For the UDP transport the buffer size if the size of the socket buffer + //in the kernel xports.recv_buff_size = buff_params.recv_buff_size; xports.send_buff_size = buff_params.send_buff_size; - } - //always program the framer if this is a socket, even its caching was used - if (xport_path != "nirio") - { //clear the ethernet dispatcher's udp port //NOT clearing this, the dispatcher is now intelligent //_zpu_ctrl->poke32(SR_ADDR(SET0_BASE, (ZPU_SR_ETHINT0+8+3)), 0); @@ -1095,7 +1187,7 @@ x300_impl::both_xports_t x300_impl::make_transport( //send a mini packet with SID into the ZPU //ZPU will reprogram the ethernet framer UHD_LOG << "programming packet for new xport on " - << addr << std::hex << "sid 0x" << sid << std::dec << std::endl; + << mb.addr << std::hex << "sid 0x" << sid << std::dec << std::endl; //YES, get a __send__ buffer from the __recv__ socket //-- this is the only way to program the framer for recv: managed_send_buffer::sptr buff = xports.recv->get_send_buff(); @@ -1113,6 +1205,7 @@ x300_impl::both_xports_t x300_impl::make_transport( //ethernet framer has been programmed before we return. mb.zpu_ctrl->peek32(0); } + return xports; } @@ -1299,13 +1392,15 @@ bool x300_impl::is_claimed(wb_iface::sptr iface) } /*********************************************************************** - * MTU detection + * Frame size detection **********************************************************************/ -x300_impl::mtu_result_t x300_impl::determine_mtu(const std::string &addr, const mtu_result_t &user_mtu) +x300_impl::frame_size_t x300_impl::determine_max_frame_size(const std::string &addr, + const frame_size_t &user_frame_size) { - udp_simple::sptr udp = udp_simple::make_connected(addr, BOOST_STRINGIZE(X300_MTU_DETECT_UDP_PORT)); + udp_simple::sptr udp = udp_simple::make_connected(addr, + BOOST_STRINGIZE(X300_MTU_DETECT_UDP_PORT)); - std::vector buffer(std::max(user_mtu.recv_mtu, user_mtu.send_mtu)); + std::vector buffer(std::max(user_frame_size.recv_frame_size, user_frame_size.send_frame_size)); x300_mtu_t *request = reinterpret_cast(&buffer.front()); static const double echo_timeout = 0.020; //20 ms @@ -1317,54 +1412,62 @@ x300_impl::mtu_result_t x300_impl::determine_mtu(const std::string &addr, const if (!(uhd::ntohx(request->flags) & X300_MTU_DETECT_ECHO_REPLY)) throw uhd::not_implemented_error("Holler protocol not implemented"); - size_t min_recv_mtu = sizeof(x300_mtu_t); - size_t max_recv_mtu = user_mtu.recv_mtu; - size_t min_send_mtu = sizeof(x300_mtu_t); - size_t max_send_mtu = user_mtu.send_mtu; + size_t min_recv_frame_size = sizeof(x300_mtu_t); + size_t max_recv_frame_size = user_frame_size.recv_frame_size; + size_t min_send_frame_size = sizeof(x300_mtu_t); + size_t max_send_frame_size = user_frame_size.send_frame_size; - UHD_MSG(status) << "Determining receive MTU ... "; - while (min_recv_mtu < max_recv_mtu) + UHD_MSG(status) << "Determining maximum frame size... "; + while (min_recv_frame_size < max_recv_frame_size) { - size_t test_mtu = (max_recv_mtu/2 + min_recv_mtu/2 + 3) & ~3; + size_t test_frame_size = (max_recv_frame_size/2 + min_recv_frame_size/2 + 3) & ~3; request->flags = uhd::htonx(X300_MTU_DETECT_ECHO_REQUEST); - request->size = uhd::htonx(test_mtu); + request->size = uhd::htonx(test_frame_size); udp->send(boost::asio::buffer(buffer, sizeof(x300_mtu_t))); size_t len = udp->recv(boost::asio::buffer(buffer), echo_timeout); - if (len >= test_mtu) - min_recv_mtu = test_mtu; + if (len >= test_frame_size) + min_recv_frame_size = test_frame_size; else - max_recv_mtu = test_mtu - 4; + max_recv_frame_size = test_frame_size - 4; + } + if(min_recv_frame_size < IP_PROTOCOL_MIN_MTU_SIZE-IP_PROTOCOL_UDP_PLUS_IP_HEADER) { + throw uhd::runtime_error("System receive MTU size is less than the minimum required by the IP protocol."); } - UHD_MSG(status) << min_recv_mtu << std::endl; - UHD_MSG(status) << "Determining send MTU ... "; - while (min_send_mtu < max_send_mtu) + while (min_send_frame_size < max_send_frame_size) { - size_t test_mtu = (max_send_mtu/2 + min_send_mtu/2 + 3) & ~3; + size_t test_frame_size = (max_send_frame_size/2 + min_send_frame_size/2 + 3) & ~3; request->flags = uhd::htonx(X300_MTU_DETECT_ECHO_REQUEST); request->size = uhd::htonx(sizeof(x300_mtu_t)); - udp->send(boost::asio::buffer(buffer, test_mtu)); + udp->send(boost::asio::buffer(buffer, test_frame_size)); size_t len = udp->recv(boost::asio::buffer(buffer), echo_timeout); if (len >= sizeof(x300_mtu_t)) len = uhd::ntohx(request->size); - if (len >= test_mtu) - min_send_mtu = test_mtu; + if (len >= test_frame_size) + min_send_frame_size = test_frame_size; else - max_send_mtu = test_mtu - 4; + max_send_frame_size = test_frame_size - 4; + } + + if(min_send_frame_size < IP_PROTOCOL_MIN_MTU_SIZE-IP_PROTOCOL_UDP_PLUS_IP_HEADER) { + throw uhd::runtime_error("System send MTU size is less than the minimum required by the IP protocol."); } - UHD_MSG(status) << min_send_mtu << std::endl; - mtu_result_t mtu; - mtu.recv_mtu = min_recv_mtu; - mtu.send_mtu = min_send_mtu; - return mtu; + frame_size_t frame_size; + // There are cases when NICs accept oversized packets, in which case we'd falsely + // detect a larger-than-possible frame size. A safe and sensible value is the minimum + // of the recv and send frame sizes. + frame_size.recv_frame_size = std::min(min_recv_frame_size, min_send_frame_size); + frame_size.send_frame_size = std::min(min_recv_frame_size, min_send_frame_size); + UHD_MSG(status) << frame_size.send_frame_size << " bytes." << std::endl; + return frame_size; } /*********************************************************************** diff --git a/host/lib/usrp/x300/x300_impl.hpp b/host/lib/usrp/x300/x300_impl.hpp index 37f8cc468..1fb3676a0 100644 --- a/host/lib/usrp/x300/x300_impl.hpp +++ b/host/lib/usrp/x300/x300_impl.hpp @@ -58,21 +58,23 @@ static const double X300_BUS_CLOCK_RATE = 175e6; //Hz static const size_t X300_TX_HW_BUFF_SIZE = 0x90000; //576KiB static const size_t X300_TX_FC_RESPONSE_FREQ = 8; //per flow-control window -static const size_t X300_RX_SW_BUFF_SIZE_ETH = 0x2000000; //32MiB For an ~8k MTU any size >32MiB is just wasted buffer space -static const size_t X300_RX_SW_BUFF_SIZE_ETH_MACOS = 0x100000; //1Mib -static const double X300_RX_SW_BUFF_FULL_FACTOR = 0.90; //Buffer should ideally be 90% full. -static const size_t X300_RX_FC_REQUEST_FREQ = 32; //per flow-control window - -static const size_t X300_PCIE_DATA_FRAME_SIZE = 8192; //bytes -static const size_t X300_PCIE_DATA_NUM_FRAMES = 2048; -static const size_t X300_PCIE_MSG_FRAME_SIZE = 256; //bytes -static const size_t X300_PCIE_MSG_NUM_FRAMES = 32; - -static const size_t X300_ETH_DATA_FRAME_SIZE = 8000; //bytes -static const size_t X300_ETH_DATA_NUM_FRAMES = 32; -static const size_t X300_ETH_MSG_FRAME_SIZE = uhd::transport::udp_simple::mtu; //bytes -static const size_t X300_ETH_MSG_NUM_FRAMES = 32; -static const double X300_DEFAULT_SYSREF_RATE = 10e6; +static const size_t X300_RX_SW_BUFF_SIZE_ETH = 0x2000000;//32MiB For an ~8k frame size any size >32MiB is just wasted buffer space +static const size_t X300_RX_SW_BUFF_SIZE_ETH_MACOS = 0x100000; //1Mib +static const double X300_RX_SW_BUFF_FULL_FACTOR = 0.90; //Buffer should ideally be 90% full. +static const size_t X300_RX_FC_REQUEST_FREQ = 32; //per flow-control window + +static const size_t X300_PCIE_DATA_FRAME_SIZE = 8192; //bytes +static const size_t X300_PCIE_DATA_NUM_FRAMES = 2048; +static const size_t X300_PCIE_MSG_FRAME_SIZE = 256; //bytes +static const size_t X300_PCIE_MSG_NUM_FRAMES = 32; + +static const size_t X300_10GE_DATA_FRAME_MAX_SIZE = 8000; //bytes +static const size_t X300_1GE_DATA_FRAME_MAX_SIZE = 1472; //bytes +static const size_t X300_ETH_MSG_FRAME_SIZE = uhd::transport::udp_simple::mtu; //bytes + +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; #define X300_RADIO_DEST_PREFIX_TX 0 #define X300_RADIO_DEST_PREFIX_CTRL 1 @@ -207,6 +209,9 @@ private: int clock_control_regs__pps_out_enb; int clock_control_regs__tcxo_enb; int clock_control_regs__gpsdo_pwr; + + //which FPGA image is loaded + std::string loaded_fpga_image; }; std::vector _mb; @@ -243,13 +248,19 @@ private: const uhd::device_addr_t& args, boost::uint32_t& sid); - struct mtu_result_t + struct frame_size_t { - size_t recv_mtu; - size_t send_mtu; + size_t recv_frame_size; + size_t send_frame_size; }; - - mtu_result_t determine_mtu(const std::string &addr, const mtu_result_t &user_mtu); + frame_size_t max_frame_sizes; + + /*! + * Automatically determine the maximum frame size available by sending a UDP packet + * to the device and see which packet sizes actually work. This way, we can take + * switches etc. into account which might live between the device and the host. + */ + frame_size_t determine_max_frame_size(const std::string &addr, const frame_size_t &user_mtu); //////////////////////////////////////////////////////////////////// // diff --git a/host/utils/usrp_n2xx_simple_net_burner.cpp b/host/utils/usrp_n2xx_simple_net_burner.cpp index 290612f8b..277e807d9 100644 --- a/host/utils/usrp_n2xx_simple_net_burner.cpp +++ b/host/utils/usrp_n2xx_simple_net_burner.cpp @@ -1,5 +1,5 @@ // -// Copyright 2012-2013 Ettus Research LLC +// Copyright 2012-2014 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -15,6 +15,7 @@ // along with this program. If not, see . // +#include #include #include #include @@ -46,6 +47,25 @@ using namespace boost::algorithm; using namespace uhd; using namespace uhd::transport; +/*********************************************************************** + * Signal handlers + **********************************************************************/ +static int num_ctrl_c = 0; +void sig_int_handler(int){ + num_ctrl_c++; + if(num_ctrl_c == 1){ + std::cout << std::endl << "Are you sure you want to abort the image burning? If you do, your " + "USRP-N Series unit will be bricked!" << std::endl + << "Press Ctrl+C again to abort the image burning procedure." << std::endl << std::endl; + } + else{ + std::cout << std::endl << "Aborting. Your USRP-N Series unit will be bricked." << std::endl + << "Refer to http://files.ettus.com/uhd_docs/manual/html/usrp2.html#device-recovery-and-bricking" << std::endl + << "for details on restoring your device." << std::endl; + exit(EXIT_FAILURE); + } +} + //Mapping revision numbers to filenames std::map filename_map = boost::assign::map_list_of (0xa, "n200_r3") @@ -498,7 +518,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ std::cout << boost::format(" * Sector size: %3.2f\n\n") % flash_info[0]; //Burning images - + std::signal(SIGINT, &sig_int_handler); if(burn_fpga){ erase_image(udp_transport, false, flash_info[1]); write_image(udp_transport, false, fpga_image, flash_info[1], fpga_image_size); diff --git a/host/utils/usrp_x3xx_fpga_burner.cpp b/host/utils/usrp_x3xx_fpga_burner.cpp index 07bc63559..2f0202e84 100644 --- a/host/utils/usrp_x3xx_fpga_burner.cpp +++ b/host/utils/usrp_x3xx_fpga_burner.cpp @@ -15,6 +15,7 @@ // along with this program. If not, see . // +#include #include #include #include @@ -81,6 +82,22 @@ namespace po = boost::program_options; using namespace uhd; using namespace uhd::transport; +static int num_ctrl_c = 0; +void sig_int_handler(int){ + num_ctrl_c++; + if(num_ctrl_c == 1){ + std::cout << std::endl << "Are you sure you want to abort the image burning? If you do, your " + "USRP-X series device will be bricked!" << std::endl + << "Press Ctrl+C again to abort the image burning procedure." << std::endl << std::endl; + } + else{ + std::cout << std::endl << "Aborting. Your USRP X-Series device will be bricked." << std::endl + << "Refer to http://files.ettus.com/uhd_docs/manual/html/usrp_x3x0.html#use-jtag-to-load-fpga-images" << std::endl + << "for details on restoring your device." << std::endl; + exit(EXIT_FAILURE); + } +} + typedef struct { boost::uint32_t flags; boost::uint32_t sector; @@ -232,21 +249,16 @@ void ethernet_burn(udp_simple::sptr udp_transport, std::string fpga_path, bool v throw std::runtime_error("Failed to start image burning! Did you specify the correct IP address? If so, power-cycle the device and try again."); } - std::cout << "Progress: " << std::flush; - - int percentage = -1; - int last_percentage = -1; size_t current_pos = 0; + size_t sectors = fpga_image_size / X300_FLASH_SECTOR_SIZE; //Each sector for(size_t i = 0; i < fpga_image_size; i += X300_FLASH_SECTOR_SIZE){ - //Print percentage at beginning of first sector after each 10% - percentage = int(double(i)/double(fpga_image_size)*100); - if((percentage != last_percentage) and (percentage % 10 == 0)){ //Don't print same percentage twice - std::cout << percentage << "%..." << std::flush; - } - last_percentage = percentage; + //Print progress percentage at beginning of each sector + std::cout << "\rProgress: " << int(double(i)/double(fpga_image_size)*100) + << "% (" << (i / X300_FLASH_SECTOR_SIZE) << "/" + << sectors << " sectors)" << std::flush; //Each packet for(size_t j = i; (j < fpga_image_size and j < (i+X300_FLASH_SECTOR_SIZE)); j += X300_PACKET_SIZE_BYTES){ @@ -256,7 +268,7 @@ void ethernet_burn(udp_simple::sptr udp_transport, std::string fpga_path, bool v if(verify) send_packet.flags |= X300_FPGA_PROG_FLAGS_VERIFY; if(j == i) send_packet.flags |= X300_FPGA_PROG_FLAGS_ERASE; //Erase the sector before writing send_packet.flags = htonx(send_packet.flags); - + send_packet.sector = htonx(X300_FPGA_SECTOR_START + (i/X300_FLASH_SECTOR_SIZE)); send_packet.index = htonx((j % X300_FLASH_SECTOR_SIZE) / 2); send_packet.size = htonx(X300_PACKET_SIZE_BYTES / 2); @@ -297,10 +309,12 @@ void ethernet_burn(udp_simple::sptr udp_transport, std::string fpga_path, bool v for(size_t k = 0; k < (X300_PACKET_SIZE_BYTES/2); k++){ send_packet.data[k] = htonx(send_packet.data[k]); } - + udp_transport->send(boost::asio::buffer(&send_packet, sizeof(send_packet))); - udp_transport->recv(boost::asio::buffer(x300_data_in_mem), UDP_TIMEOUT); + if (udp_transport->recv(boost::asio::buffer(x300_data_in_mem), UDP_TIMEOUT) == 0) + throw std::runtime_error("Timed out waiting for ACK!"); + const x300_fpga_update_data_t *update_data_in = reinterpret_cast(x300_data_in_mem); if((ntohl(update_data_in->flags) & X300_FPGA_PROG_FLAGS_ERROR) == X300_FPGA_PROG_FLAGS_ERROR){ @@ -319,14 +333,15 @@ void ethernet_burn(udp_simple::sptr udp_transport, std::string fpga_path, bool v memset(cleanup_packet.data, 0, sizeof(cleanup_packet.data)); udp_transport->send(boost::asio::buffer(&cleanup_packet, sizeof(cleanup_packet))); - udp_transport->recv(boost::asio::buffer(x300_data_in_mem), UDP_TIMEOUT); + if (udp_transport->recv(boost::asio::buffer(x300_data_in_mem), UDP_TIMEOUT) == 0) + throw std::runtime_error("Timed out waiting for ACK!"); const x300_fpga_update_data_t *cleanup_data_in = reinterpret_cast(x300_data_in_mem); if((ntohl(cleanup_data_in->flags) & X300_FPGA_PROG_FLAGS_ERROR) == X300_FPGA_PROG_FLAGS_ERROR){ throw std::runtime_error("Transfer or data verification failed!"); } - std::cout << "100%" << std::endl; + std::cout << "\rProgress: " << "100% (" << sectors << "/" << sectors << " sectors)" << std::endl; } void pcie_burn(std::string resource, std::string rpc_port, std::string fpga_path) @@ -460,7 +475,6 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ } } - /* * Check validity of image through extension */ @@ -469,6 +483,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ throw std::runtime_error("The image filename must end in .bin, .bit, or .lvbitx."); } + std::signal(SIGINT, &sig_int_handler); if(vm.count("addr")){ udp_simple::sptr udp_transport = udp_simple::make_connected(ip_addr, BOOST_STRINGIZE(X300_FPGA_PROG_UDP_PORT)); -- cgit v1.2.3