diff options
Diffstat (limited to 'host')
148 files changed, 6819 insertions, 3065 deletions
diff --git a/host/CMakeLists.txt b/host/CMakeLists.txt index ba532eed2..1b9d96518 100644 --- a/host/CMakeLists.txt +++ b/host/CMakeLists.txt @@ -195,6 +195,12 @@ INSTALL(FILES ) ######################################################################## +# Images download directory for utils/uhd_images_downloader.py +######################################################################## + +SET(UHD_IMAGES_DOWNLOAD_SRC "http://files.ettus.com/binaries/maint_images/archive/uhd-images_003.005.000-release.zip") + +######################################################################## # Register top level components ######################################################################## LIBUHD_REGISTER_COMPONENT("LibUHD" ENABLE_LIBUHD ON "Boost_FOUND;HAVE_PYTHON_PLAT_MIN_VERSION;HAVE_PYTHON_MODULE_CHEETAH" OFF) @@ -225,8 +231,6 @@ IF(ENABLE_UTILS) ADD_SUBDIRECTORY(utils) ENDIF(ENABLE_UTILS) -ADD_SUBDIRECTORY(usrp_e_utils) - ######################################################################## # Create Pkg Config File ######################################################################## @@ -278,3 +282,4 @@ ENDIF(DEFINED UHD_IMAGES_DIR AND EXISTS "${UHD_IMAGES_DIR}") UHD_PRINT_COMPONENT_SUMMARY() MESSAGE(STATUS "Building version: ${UHD_VERSION}") MESSAGE(STATUS "Using install prefix: ${CMAKE_INSTALL_PREFIX}") +MESSAGE(STATUS "Compatible images can be downloaded from: ${UHD_IMAGES_DOWNLOAD_SRC}") diff --git a/host/cmake/Modules/UHDVersion.cmake b/host/cmake/Modules/UHDVersion.cmake index 414990995..6b1d84941 100644 --- a/host/cmake/Modules/UHDVersion.cmake +++ b/host/cmake/Modules/UHDVersion.cmake @@ -26,8 +26,8 @@ FIND_PACKAGE(Git QUIET) # - increment patch on for bug fixes and docs ######################################################################## SET(UHD_VERSION_MAJOR 003) -SET(UHD_VERSION_MINOR 004) -SET(UHD_VERSION_PATCH 004) +SET(UHD_VERSION_MINOR 005) +SET(UHD_VERSION_PATCH 000) ######################################################################## # Version information discovery through git log diff --git a/host/docs/CMakeLists.txt b/host/docs/CMakeLists.txt index e393a79f0..f56358ca9 100644 --- a/host/docs/CMakeLists.txt +++ b/host/docs/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2010-2011 Ettus Research LLC +# Copyright 2010-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 @@ -33,8 +33,8 @@ SET(manual_sources transport.rst usrp1.rst usrp2.rst - usrp_b1xx.rst - usrp_e1xx.rst + usrp_b100.rst + usrp_e1x0.rst ) ######################################################################## diff --git a/host/docs/build.rst b/host/docs/build.rst index ee522780a..0a18e9e9a 100644 --- a/host/docs/build.rst +++ b/host/docs/build.rst @@ -14,25 +14,25 @@ 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 Developer Tools" 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. **Windows Notes:** -The dependencies can be acquired through installable exe files. -Usually, the windows installer can be found on the project's website. -Some projects do not host windows installers, and if this is the case, -follow the auxiliary download url for the windows installer (below). +The dependencies can be acquired through installable EXE files. +Usually, the Windows installer can be found on the project's website. +Some projects do not host Windows installers, and if this is the case, +follow the auxiliary download URL for the Windows installer (below). ^^^^^^^^^^^^^^^^ Git ^^^^^^^^^^^^^^^^ Required to check out the repository. -On windows, install cygwin with git support to checkout the repository, -or install msysgit from http://code.google.com/p/msysgit/downloads/list +On Windows, install Cygwin with Git support to checkout the repository +or install msysGit from http://code.google.com/p/msysgit/downloads/list. ^^^^^^^^^^^^^^^^ -C++ compiler +C++ Compiler ^^^^^^^^^^^^^^^^ The following compilers are known to work: @@ -44,7 +44,7 @@ The following compilers are known to work: CMake ^^^^^^^^^^^^^^^^ * **Purpose:** generates project build files -* **Version:** at least 2.6 +* **Minimum Version:** 2.6 * **Usage:** build time (required) * **Download URL:** http://www.cmake.org/cmake/resources/software.html @@ -52,57 +52,57 @@ CMake Boost ^^^^^^^^^^^^^^^^ * **Purpose:** C++ library -* **Version:** at least 1.36 unix, at least 1.40 windows -* **Usage:** build time + run time (required) +* **Minimum Version:** 1.36 (Linux), 1.40 (Windows) +* **Usage:** build time + runtime (required) * **Download URL:** http://www.boost.org/users/download/ -* **Download URL (windows installer):** http://www.boostpro.com/download +* **Download URL (Windows installer):** http://www.boostpro.com/download ^^^^^^^^^^^^^^^^ LibUSB ^^^^^^^^^^^^^^^^ * **Purpose:** USB-based hardware support -* **Version:** at least 1.0 -* **Usage:** build time + run time (optional) +* **Minimum Version:** 1.0 +* **Usage:** build time + runtime (optional) * **Download URL:** http://sourceforge.net/projects/libusb/files/libusb-1.0/ -* **Download URL (windows binaries):** http://www.libusb.org/wiki/windows_backend#LatestBinarySnapshots +* **Download URL (Windows binaries):** http://www.libusb.org/wiki/windows_backend#LatestBinarySnapshots ^^^^^^^^^^^^^^^^ Python ^^^^^^^^^^^^^^^^ * **Purpose:** used by Cheetah and utility scripts -* **Version:** at least 2.6 -* **Usage:** build time + run time utility scripts (required) +* **Minimum Version:** 2.6 +* **Usage:** build time + runtime utility scripts (required) * **Download URL:** http://www.python.org/download/ ^^^^^^^^^^^^^^^^ Cheetah ^^^^^^^^^^^^^^^^ * **Purpose:** source code generation -* **Version:** at least 2.0 +* **Minimum Version:** 2.0 * **Usage:** build time (required) * **Download URL:** http://www.cheetahtemplate.org/download.html -* **Download URL (windows installer):** http://feisley.com/python/cheetah/ +* **Download URL (Windows installer):** http://feisley.com/python/cheetah/ **Alternative method:** -Install setuptools, and use the easy_install command to install Cheetah. +Install **setuptools**, and use the **easy_install** command to install Cheetah. http://pypi.python.org/pypi/setuptools ^^^^^^^^^^^^^^^^ Doxygen ^^^^^^^^^^^^^^^^ -* **Purpose:** generates html api documentation +* **Purpose:** generates HTML API documentation * **Usage:** build time (optional) * **Download URL:** http://www.stack.nl/~dimitri/doxygen/download.html#latestsrc ^^^^^^^^^^^^^^^^ Docutils ^^^^^^^^^^^^^^^^ -* **Purpose:** generates html user manual +* **Purpose:** generates HTML user manual * **Usage:** build time (optional) * **Download URL:** http://docutils.sourceforge.net/ **Alternate method:** -Install setuptools, and use the easy_install command to install Docutils. +Install **setuptools**, and use the **easy_install** command to install Docutils. http://pypi.python.org/pypi/setuptools ------------------------------------------------------------------------ @@ -110,7 +110,7 @@ Build Instructions (Unix) ------------------------------------------------------------------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Generate Makefiles with cmake +Generate Makefiles with CMake ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ :: @@ -119,11 +119,11 @@ Generate Makefiles with cmake cd build cmake ../ -Additionally, configuration variables can be passed into cmake via the command line. +Additionally, configuration variables can be passed into CMake via the command line. The following common-use configuration variables are listed below: -* For a custom install prefix: -DCMAKE_INSTALL_PREFIX=<install-path> -* To install libs into lib64: cmake -DLIB_SUFFIX=64 +* For a custom install prefix: **-DCMAKE_INSTALL_PREFIX=<install-path>** +* To install libs into lib64: **cmake -DLIB_SUFFIX=64** Example usage: :: @@ -142,8 +142,8 @@ Build and install ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Setup the library path (Linux) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Make sure that libuhd.so is in your LD_LIBRARY_PATH -or add it to /etc/ld.so.conf and make sure to run: +Make sure that **libuhd.so** is in your **LD_LIBRARY_PATH**, +or add it to **/etc/ld.so.conf** and make sure to run: :: sudo ldconfig @@ -151,46 +151,46 @@ or add it to /etc/ld.so.conf and make sure to run: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Setup the library path (Mac OS X) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Make sure that libuhd.dylib is in your DYLD_LIBRARY_PATH +Make sure that **libuhd.dylib** is in your **DYLD_LIBRARY_PATH**. ------------------------------------------------------------------------ Build Instructions (Windows) ------------------------------------------------------------------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Generate the project with cmake +Generate the project with CMake ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -* Open the cmake gui program. -* Set the path to the source code: <uhd-repo-path>/host -* Set the path to the build directory: <uhd-repo-path>/host/build +* Open the CMake GUI. +* Set the path to the source code: **<uhd-repo-path>/host**. +* Set the path to the build directory: **<uhd-repo-path>/host/build**. * Make sure that the paths do not contain spaces. -* Click configure and select the MSVC compiler. -* Set the build variables and click configure again. -* Click generate and a project file will be created in the build directory. +* Click "Configure" and select "Microsoft Visual Studio 10". +* Set the build variables and click "Configure" again. +* Click "Generate", and a project file will be created in the build directory. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -LibUSB cmake notes +LibUSB CMake notes ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -On Windows, cmake does not have the advantage of pkg-config, -so we must manually tell cmake how to locate the LibUSB header and lib. +On Windows, CMake does not have the advantage of **pkg-config**, +so we must manually tell CMake how to locate the LibUSB header and lib. -* From the cmake gui, select "Advanded View" -* Set LIBUSB_INCLUDE_DIRS to the directory with "libusb.h". -* Set LIBUSB_LIBRARIES to the full path for "libusb-1.0.lib". +* From the CMake GUI, select "Advanced View". +* Set **LIBUSB_INCLUDE_DIRS** to the directory with **libusb.h**. +* Set **LIBUSB_LIBRARIES** to the full path for **libusb-1.0.lib**. - * Recommend the static libusb-1.0.lib to simplify runtime dependencies. + * Recommend the static **libusb-1.0.lib** to simplify runtime dependencies. -* Check the box to enable USB support, click configure and generate. +* Check the box to enable USB support, click "Configure" and "Generate". ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Build the project in MSVC ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ * Open the generated project file in MSVC. * Change the build type from "Debug" to "Release". -* Select the build all target, right click, and choose build. -* Select the install target, right click, and choose build. +* Select the "Build All" target, right-click, and choose "Build". +* Select the install target, right-click, and choose "Build". -**Note:** you may not have permission to build the install target. +**Note:** You may not have permission to build the install target. You need to be an administrator or to run MSVC as administrator. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -206,11 +206,11 @@ Open the Visual Studio Command Prompt Shorcut: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Setup the PATH environment variable ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -* Add the uhd bin path to %PATH% (usually c:\\program files\\uhd\\bin) +* Add the UHD bin path to **%PATH%** (usually **C:\\Program Files\\UHD\\bin**) **Note:** -The interface for editing environment variable paths in Windows is very poor. -I recommend using "Rapid Environment Editor" (http://www.rapidee.com) over the default editor. +The default interface for editing environment variable paths in Windows is very poor. +We recommend using "Rapid Environment Editor" (http://www.rapidee.com) over the default editor. ------------------------------------------------------------------------ Post-Install Tasks diff --git a/host/docs/calibration.rst b/host/docs/calibration.rst index 14e66a312..c97eebfd5 100644 --- a/host/docs/calibration.rst +++ b/host/docs/calibration.rst @@ -7,10 +7,10 @@ UHD - Calibration Application Notes ------------------------------------------------------------------------ Self-calibration ------------------------------------------------------------------------ -The UHD comes with several self-calibration utilities for minimizing IQ imbalance and DC offset. +UHD comes with several self-calibration utilities for minimizing IQ imbalance and DC offset. These utilities perform calibration sweeps using transmit leakage into the receive path (special equipment is not required). -The results from a calibration are written to a csv file in the user's home directory. +The results from a calibration are written to a CSV file in the user's home directory. UHD will automatically apply corrections at runtime when the user re-tunes the daughterboard LO. Calibration results are specific to an individual RF board. @@ -21,21 +21,20 @@ the user should re-apply the desired setting every time the LO is re-tuned. UHD comes with the following calibration utilities: - * **uhd_cal_rx_iq_balance:** - mimimizes RX IQ imbalance vs LO frequency - * **uhd_cal_tx_dc_offset:** - mimimizes TX DC offset vs LO frequency - * **uhd_cal_tx_iq_balance:** - mimimizes TX IQ imbalance vs LO frequency - + * **uhd_cal_rx_iq_balance:** - mimimizes RX IQ imbalance vs. LO frequency + * **uhd_cal_tx_dc_offset:** - mimimizes TX DC offset vs. LO frequency + * **uhd_cal_tx_iq_balance:** - mimimizes TX IQ imbalance vs. LO frequency The following RF frontends are supported by the self-calibration utilities: * WBX transceiver board * SBX transceiver board - * more to come... + * RFX transceiver board ******************************************** -Calibration utilities +Calibration Utilities ******************************************** -UHD installs the calibration utilities into <install-path>/bin. +UHD installs the calibration utilities into **<install-path>/bin**. **Disconnect** any extrernal hardware from the RF antenna ports, and run the following from the command line. Each utility will take several minutes to complete. @@ -49,13 +48,13 @@ See the output given by --help for more advanced options, such as: manually choosing the frequency range and step size for the sweeps. ******************************************** -Calibration data +Calibration Data ******************************************** Calibration files are stored in the user's home/application directory. They can easily be moved from machine to another by copying the "cal" directory. Re-running a calibration utility will replace the existing calibration file. The old calibration file will be renamed so it may be recovered by the user. - * **Unix:** ${HOME}/.uhd/cal/ + * **Linux:** ${HOME}/.uhd/cal/ * **Windows:** %APPDATA%\\.uhd\\cal\\ diff --git a/host/docs/coding.rst b/host/docs/coding.rst index ed858ceb4..ef8cb5fe2 100644 --- a/host/docs/coding.rst +++ b/host/docs/coding.rst @@ -23,7 +23,7 @@ considered to be a "device". The device API provides ways to: See the documentation in *device.hpp* for reference. ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -High-Level: The multi usrp +High-Level: The Multi-USRP ^^^^^^^^^^^^^^^^^^^^^^^^^^^ The Multi-USRP class provides a fat interface to a single USRP with one or more channels, or multiple USRPs in a homogeneous setup. diff --git a/host/docs/dboards.rst b/host/docs/dboards.rst index d41bf824c..29812592f 100644 --- a/host/docs/dboards.rst +++ b/host/docs/dboards.rst @@ -13,7 +13,7 @@ Eventually, this page will be expanded to list out the full properties of each board as well. ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Basic RX and and LFRX +Basic RX and LFRX ^^^^^^^^^^^^^^^^^^^^^^^^^^^ The Basic RX and LFRX boards have 4 frontends: @@ -23,21 +23,21 @@ The Basic RX and LFRX boards have 4 frontends: * **Frontend BA:** quadrature frontend using both antennas (QI) The boards have no tunable elements or programmable gains. -Though the magic of aliasing, you can down-convert signals +Through the magic of aliasing, you can down-convert signals greater than the Nyquist rate of the ADC. BasicRX Bandwidth (Hz): -* For Real-Mode (A or B frontend): 250M -* For Complex (AB or BA frontend): 500M +* **For Real-Mode (A or B frontend)**: 250M +* **For Complex (AB or BA frontend)**: 500M LFRX Bandwidth (Hz): -* For Real-Mode (A or B frontend): 33M -* For Complex (AB or BA frontend): 66M +* **For Real-Mode (A or B frontend)**: 33M +* **For Complex (AB or BA frontend)**: 66M ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Basic TX and and LFTX +Basic TX and LFTX ^^^^^^^^^^^^^^^^^^^^^^^^^^^ The Basic TX and LFTX boards have 4 frontends: @@ -47,30 +47,30 @@ The Basic TX and LFTX boards have 4 frontends: * **Frontend BA:** quadrature frontend using both antennas (QI) The boards have no tunable elements or programmable gains. -Though the magic of aliasing, you can up-convert signals +Through the magic of aliasing, you can up-convert signals greater than the Nyquist rate of the DAC. BasicTX Bandwidth (Hz): 250M -* For Real-Mode (A or B frontend): 250M -* For Complex (AB or BA frontend): 500M +* **For Real-Mode (A or B frontend**): 250M +* **For Complex (AB or BA frontend)**: 500M LFTX Bandwidth (Hz): 33M -* For Real-Mode (A or B frontend): 33M -* For Complex (AB or BA frontend): 66M +* **For Real-Mode (A or B frontend)**: 33M +* **For Complex (AB or BA frontend)**: 66M ^^^^^^^^^^^^^^^^^^^^^^^^^^^ DBSRX ^^^^^^^^^^^^^^^^^^^^^^^^^^^ The DBSRX board has 1 quadrature frontend. -It defaults to direct conversion, but can use a low IF through lo_offset in uhd::tune_request_t +It defaults to direct conversion but can use a low IF through lo_offset in **uhd::tune_request_t**. Receive Antennas: **J3** * **Frontend 0:** Complex baseband signal from antenna J3 -The board has no user selectable antenna setting +The board has no user selectable antenna setting. Receive Gains: @@ -87,13 +87,13 @@ Sensors: DBSRX2 ^^^^^^^^^^^^^^^^^^^^^^^^^^^ The DBSRX2 board has 1 quadrature frontend. -It defaults to direct conversion, but can use a low IF through lo_offset in uhd::tune_request_t +It defaults to direct conversion, but can use a low IF through lo_offset in **uhd::tune_request_t**. Receive Antennas: **J3** * **Frontend 0:** Complex baseband signal from antenna J3 -The board has no user selectable antenna setting +The board has no user-selectable antenna setting. Receive Gains: @@ -109,9 +109,9 @@ Sensors: ^^^^^^^^^^^^^^^^^^^^^^^^^^^ RFX Series ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The RFX Series boards have 2 quadrature frontends, one transmit, one receive. -Transmit defaults to low IF and Receive defaults to direct conversion. -The IF can be adjusted through lo_offset in uhd::tune_request_t +The RFX Series boards have 2 quadrature frontends: Transmit and Receive. +Transmit defaults to low IF, and Receive defaults to direct conversion. +The IF can be adjusted through lo_offset in **uhd::tune_request_t**. The RFX Series boards have independent receive and transmit LO's and synthesizers allowing full-duplex operation on different transmit and receive frequencies. @@ -185,7 +185,7 @@ WBX Series ^^^^^^^^^^^^^^^^^^^^^^^^^^^ The WBX Series boards have 2 quadrature frontends, one transmit, one receive. Transmit and Receive default to direct conversion but -can be used in low IF mode through lo_offset in uhd::tune_request_t +can be used in low IF mode through lo_offset in **uhd::tune_request_t**. The WBX Series boards have independent receive and transmit LO's and synthesizers allowing full-duplex operation on different transmit and receive frequencies. @@ -218,7 +218,7 @@ SBX Series ^^^^^^^^^^^^^^^^^^^^^^^^^^^ The SBX Series boards have 2 quadrature frontends, one transmit, one receive. Transmit and Receive default to direct conversion but -can be used in low IF mode through lo_offset in uhd::tune_request_t +can be used in low IF mode through lo_offset in **uhd::tune_request_t**. The SBX Series boards have independent receive and transmit LO's and synthesizers allowing full-duplex operation on different transmit and receive frequencies. @@ -282,7 +282,7 @@ Receive Frontends: * **Frontend RX1:** real-mode baseband from antenna J100 * **Frontend RX2:** real-mode baseband from antenna J140 -Note: The TVRX2 has always-on AGC, the software controllable gain is the +Note: The TVRX2 has always-on AGC; the software controllable gain is the final gain stage which controls the AGC set-point for output to ADC. Receive Gains: @@ -303,8 +303,8 @@ Daughterboard Modifications Sometimes, daughterboards will require modification to work on certain frequencies or to work with certain hardware. -Modification usually involves moving/removing a SMT component -and burning a new daughterboard id into the eeprom. +Modification usually involves moving/removing an SMT component +and burning a new daughterboard ID into the EEPROM. ^^^^^^^^^^^^^^^^^^^^^^^^^^^ DBSRX - Mod @@ -319,13 +319,13 @@ over the standard daughterboard clock lines. **Step 1: Move the clock configuration resistor** -Remove R193 (which is 10 ohms, 0603 size) and put it on R194, which is empty. +Remove **R193** (which is 10 ohms, 0603 size), and put it on **R194**, which is empty. This is made somewhat more complicated by the fact that the silkscreen is not clear in that area. -R193 is on the back, immediately below the large beige connector, J2. -R194 is just below, and to the left of R193. -The silkscreen for R193 is ok, but for R194, +**R193** is on the back, immediately below the large beige connector, **J2**. +**R194** is just below, and to the left of **R193**. +The silkscreen for **R193** is ok, but for **R194**, it is upside down, and partially cut off. -If you lose R193, you can use anything from 0 to 10 ohms there. +If you lose **R193**, you can use anything from 0 to 10 ohms there. **Step 2: Burn a new daughterboard id into the EEPROM** @@ -335,8 +335,8 @@ With the daughterboard plugged-in, run the following commands: cd <install-path>/share/uhd/utils ./usrp_burn_db_eeprom --id=0x000d --unit=RX --args=<args> --slot=<slot> -* <args> are device address arguments (optional if only one USRP is on your machine) -* <slot> is the name of the daughterboard slot (optional if the USRP has only one slot) +* **<args>** are device address arguments (optional if only one USRP is on your machine) +* **<slot>** is the name of the daughterboard slot (optional if the USRP has only one slot) ^^^^^^^^^^^^^^^^^^^^^^^^^^^ RFX - Mod @@ -347,14 +347,14 @@ Please follow the modification procedures below: **Step 1: Disable the daughterboard clocks** -Move R64 to R84, Move R142 to R153 +Move **R64** to **R84**. Move **R142** to **R153**. **Step 2: Connect the motherboard blocks** -Move R35 to R36, Move R117 to R115 -These are all 0-ohm, so if you lose one, just short across the appropriate pads +Move **R35** to **R36**. Move **R117** to **R115**. +These are all 0-ohm, so if you lose one, just short across the appropriate pads. -**Step 3: Burn the appropriate daughterboard id into the EEPROM** +**Step 3: Burn the appropriate daughterboard ID into the EEPROM** With the daughterboard plugged-in, run the following commands: :: @@ -363,19 +363,19 @@ With the daughterboard plugged-in, run the following commands: ./usrp_burn_db_eeprom --id=<rx_id> --unit=RX --args=<args> --slot=<slot> ./usrp_burn_db_eeprom --id=<tx_id> --unit=TX --args=<args> --slot=<slot> -* <rx_id> choose the appropriate RX ID for your daughterboard +* **<rx_id>** choose the appropriate RX ID for your daughterboard * **RFX400:** 0x0024 * **RFX900:** 0x0025 * **RFX1800:** 0x0034 * **RFX1200:** 0x0026 * **RFX2400:** 0x0027 -* <tx_id> choose the appropriate TX ID for your daughterboard +* **<tx_id>** choose the appropriate TX ID for your daughterboard * **RFX400:** 0x0028 * **RFX900:** 0x0029 * **RFX1800:** 0x0035 * **RFX1200:** 0x002a * **RFX2400:** 0x002b -* <args> are device address arguments (optional if only one USRP is on your machine) -* <slot> is the name of the daughterboard slot (optional if the USRP has only one slot) +* **<args>** are device address arguments (optional if only one USRP is on your machine) +* **<slot>** is the name of the daughterboard slot (optional if the USRP has only one slot) diff --git a/host/docs/general.rst b/host/docs/general.rst index 5df89fc19..fc7caff3c 100644 --- a/host/docs/general.rst +++ b/host/docs/general.rst @@ -5,7 +5,7 @@ UHD - General Application Notes .. contents:: Table of Contents ------------------------------------------------------------------------ -Tuning notes +Tuning Notes ------------------------------------------------------------------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -20,12 +20,12 @@ In a typical use-case, the user specifies an overall center frequency for the signal chain. The RF front-end will be tuned as close as possible to the center frequency, and the DSP will account for the error in tuning between target frequency and actual frequency. The user may also explicitly control both -stages of tuning through through the tune_request_t object, which allows for +stages of tuning through through the **tune_request_t** object, which allows for more advanced tuning. In general, Using UHD's advanced tuning is highly recommended as it makes it easy to move the DC component out of your band-of-interest. This can be done by -passing your desired LO offset to the tune_request_t object, and letting UHD +passing your desired LO offset to the **tune_request_t** object, and letting UHD handle the rest. Tuning the receive chain: @@ -50,7 +50,7 @@ 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 the device and requested settings. After tuning and before streaming, the user -should wait for the "lo_locked" sensor to become true, or sleep for +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: @@ -69,7 +69,7 @@ Pseudo-code for dealing with settling time after tuning on receive: usrp->issue_stream_command(...); ------------------------------------------------------------------------ -Specifying the subdevice to use +Specifying the Subdevice to Use ------------------------------------------------------------------------ A subdevice specification string for USRP family devices is composed of: @@ -115,7 +115,7 @@ The frontend names are documented in the `Daughterboard Application Notes <./dboards.html>`_ ------------------------------------------------------------------------ -Overflow/Underflow notes +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. @@ -132,26 +132,26 @@ and pushes an inline message packet into the receive stream. The host does not back-pressure the receive stream. When the kernel's socket buffer becomes full, it will drop subsequent packets. UHD detects the overflow as a discontinuity in the packet's sequence numbers, -and muxes an inline message packet into the receive stream. +and pushes an inline message packet into the receive stream. **Other devices**: The host back-pressures the receive stream. Therefore, overflows always occur in the device itself. -When the device's internal buffers become full, streaming is shutoff, +When the device's internal buffers become full, streaming is shut off, and an inline message packet is sent to the host. If the device was in continuous streaming mode, -the UHD will automatically restart streaming. +UHD will automatically restart streaming. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Underflow notes ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ When transmitting, the device consumes samples at a constant rate. Underflow occurs when the host does not produce data fast enough. -When the UHD detects underflow, it prints an "U" to stdout, +When UHD detects underflow, it prints a "U" to stdout, and pushes a message packet into the async message stream. ------------------------------------------------------------------------ -Threading notes +Threading Notes ------------------------------------------------------------------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -161,7 +161,7 @@ For the most part, UHD is thread-safe. Please observe the following limitations: **Fast-path thread requirements:** -There are three fast-path methods for a device: send(), recv(), and recv_async_msg(). +There are three fast-path methods for a device: **send()**, **recv()**, and **recv_async_msg()**. All three methods are thread-safe and can be called from different thread contexts. For performance, the user should call each method from a separate thread context. These methods can also be used in a non-blocking fashion by using a timeout of zero. @@ -169,7 +169,7 @@ These methods can also be used in a non-blocking fashion by using a timeout of z **Slow-path thread requirements:** It is safe to change multiple settings simultaneously. However, this could leave the settings for a device in an uncertain state. -The is because changing one setting could have an impact on how a call affects other settings. +This is because changing one setting could have an impact on how a call affects other settings. 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. @@ -177,23 +177,23 @@ It is recommended to use at most one thread context for manipulating device sett Thread priority scheduling ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -When the UHD spawns a new thread it may try to boost the thread's scheduling priority. -When setting the priority fails, the UHD prints out an error. -This error is harmless, it simply means that the thread will have a normal scheduling priority. +When UHD spawns a new thread it may try to boost the thread's scheduling priority. +When setting the priority fails, UHD prints out an error. +This error is harmless; it simply means that the thread will have a normal scheduling priority. **Linux Notes:** Non-privileged users need special permission to change the scheduling priority. -Add the following line to */etc/security/limits.conf*: +Add the following line to **/etc/security/limits.conf**: :: @<my_group> - rtprio 99 -Replace <my_group> with a group to which your user belongs. -Settings will not take effect until the user has logged in and out. +Replace **<my_group>** with a group to which your user belongs. +Settings will not take effect until the user is in a different login session. ------------------------------------------------------------------------ -Misc notes +Miscellaneous Notes ------------------------------------------------------------------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -201,9 +201,9 @@ 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 <install-path>/share/uhd/modules directory, -* or installed into /usr/share/uhd/modules directory (unix only). +* found in the **UHD_MODULE_PATH** environment variable, +* installed into the **<install-path>/share/uhd/modules** directory, +* or installed into **/usr/share/uhd/modules** directory (UNIX only). ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Disabling or redirecting prints to stdout @@ -211,7 +211,7 @@ 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. -Make "register_handler" your first call into UHD: +Make **register_handler** your first call into UHD: :: diff --git a/host/docs/gpsdo.rst b/host/docs/gpsdo.rst index f732aae63..1e9019a0f 100644 --- a/host/docs/gpsdo.rst +++ b/host/docs/gpsdo.rst @@ -11,11 +11,11 @@ to the Jackson Labs Firefly-1A device unless noted otherwise. ------------------------------------------------------------------------ Specifications ------------------------------------------------------------------------ -* Receiver type: 50 channel with WAAS, EGNOS, MSAS -* 10MHz ADEV: 1e-11 over >24h -* 1PPS RMS jitter: <50ns 1-sigma -* Holdover: <11us over 3h -* Phase noise: +* **Receiver type**: 50 channel with WAAS, EGNOS, MSAS +* **10MHz ADEV**: 1e-11 over >24h +* **1PPS RMS jitter**: <50ns 1-sigma +* **Holdover**: <11us over 3h +* **Phase noise**: * **1Hz:** -80dBc/Hz * **10Hz:** -110dBc/Hz @@ -25,25 +25,28 @@ Specifications **Antenna Types:** -The GPSDO is capable of supplying a 3V for active GPS antennas or supporting passive antennas +The GPSDO is capable of supplying a 3V for active GPS antennas or supporting passive antennas. ------------------------------------------------------------------------ -Installation instructions +Installation Instructions ------------------------------------------------------------------------ Installation instructions can be found here: -`www.ettus.com/downloads/gpsdo-kit.pdf <http://www.ettus.com/downloads/gpsdo-kit.pdf>`_ +`http://www.ettus.com/content/files/gpsdo-kit_2.pdf <http://www.ettus.com/content/files/gpsdo-kit_2.pdf>`_ ******************************************** -Post installation task (N-Series only) +Post-installation Task (N-Series only) ******************************************** -This is necessary if you require absolute GPS time in your application, + +**Note:** The following instructions are only necessary for UHD 3.4.* and below. + +This is necessary if you require absolute GPS time in your application or need to communicate with the GPSDO to obtain location, satellite info, etc. -If you only require 10MHz and PPS signals for reference or MIMO use, +If you only require 10MHz and PPS signals for reference or MIMO use (see the `Synchronization Application Notes <./sync.html>`_), it is not necessary to perform this step. To configure the USRP to communicate with the GPSDO, use the -usrp_burn_mb_eeprom utility: +**usrp_burn_mb_eeprom** utility: :: @@ -54,7 +57,7 @@ usrp_burn_mb_eeprom utility: ./usrp_burn_mb_eeprom --args=<optional device args> --key=gpsdo --val=none ------------------------------------------------------------------------ -Using the GPSDO in your application +Using the GPSDO in Your Application ------------------------------------------------------------------------ By default, if a GPSDO is detected at startup, the USRP will be configured to use it as a frequency and time reference. The internal VITA timestamp @@ -62,8 +65,8 @@ will be initialized to the GPS time, and the internal oscillator will be phase-locked to the 10MHz GPSDO reference. If the GPSDO is not locked to satellites, the VITA time will not be initialized. -GPS data is obtained through the mboard_sensors interface. To retrieve -the current GPS time, use the "gps_time" sensor: +GPS data is obtained through the **mboard_sensors** interface. To retrieve +the current GPS time, use the **gps_time** sensor: :: @@ -71,10 +74,10 @@ the current GPS time, use the "gps_time" sensor: The returned value will be the current epoch time, in seconds since January 1, 1970. This value is readily converted into human-readable -format using the time.h library in C, boost::posix_time in C++, etc. +format using the **time.h** library in C, **boost::posix_time** in C++, etc. Other information can be fetched as well. You can query the lock status -with the "gps_locked" sensor, as well as obtain raw NMEA sentences using -the "gps_gprmc", and "gps_gpgga" sensors. Location -information can be parsed out of the "gps_gpgga" sensor by using gpsd or +with the **gps_locked** sensor, as well as obtain raw NMEA sentences using +the **gps_gprmc**, and **gps_gpgga** sensors. Location +information can be parsed out of the **gps_gpgga** sensor by using **gpsd** or another NMEA parser. diff --git a/host/docs/identification.rst b/host/docs/identification.rst index deda61531..a5e60e7f9 100644 --- a/host/docs/identification.rst +++ b/host/docs/identification.rst @@ -9,9 +9,9 @@ Identifying USRPs ------------------------------------------------------------------------ Devices are addressed through key/value string pairs. These string pairs can be used to narrow down the search for a specific device or group of devices. -Most UHD utility applications and examples have a --args parameter that takes a device address; -where the device address is expressed as a delimited string. -See the documentation in types/device_addr.hpp for reference. +Most UHD utility applications and examples have an **--args** parameter that takes a device address, which is expressed as a delimited string. + +See the documentation in **types/device_addr.hpp** for reference. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Common device identifiers @@ -33,8 +33,8 @@ Every device has several ways of identifying it on the host system: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Device discovery via command line ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Devices attached to your system can be discovered using the "uhd_find_devices" program. -The find devices program scans your system for supported devices and prints +Devices attached to your system can be discovered using the **uhd_find_devices** program. +This program scans your system for supported devices and prints out an enumerated list of discovered devices and their addresses. The list of discovered devices can be narrowed down by specifying device address args. @@ -55,14 +55,14 @@ Device address arguments can be supplied to narrow the scope of the search. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Device discovery through the API ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The device::find() API call searches for devices and returns a list of discovered devices. +The **device::find()** API call searches for devices and returns a list of discovered devices. :: uhd::device_addr_t hint; //an empty hint discovers all devices uhd::device_addrs_t dev_addrs = uhd::device::find(hint); -The hint argument can be populated to narrow the scope of the search. +The **hint** argument can be populated to narrow the scope of the search. :: @@ -79,9 +79,9 @@ The hint argument can be populated to narrow the scope of the search. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Device properties ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Properties of devices attached to your system can be probed with the "uhd_usrp_probe" program. -The usrp probe program constructs an instance of the device and prints out its properties; -properties such as detected daughter-boards, frequency range, gain ranges, etc... +Properties of devices attached to your system can be probed with the **uhd_usrp_probe** program. +This program constructs an instance of the device and prints out its properties, +such as detected daughterboards, frequency range, gain ranges, etc... **Usage:** :: @@ -97,7 +97,7 @@ The USRP can then be identified via name, rather than a difficult to remember se A name has the following properties: * is composed of ASCII characters -* is between 0 and 20 characters +* is 0-20 characters * is not required to be unique ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -114,7 +114,7 @@ Run the following commands: Discovery via name ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The keyword "name" can be used to narrow the scope of the search. +The keyword **name** can be used to narrow the scope of the search. Example with the find devices utility: :: diff --git a/host/docs/images.rst b/host/docs/images.rst index a25268990..dc9770460 100644 --- a/host/docs/images.rst +++ b/host/docs/images.rst @@ -8,7 +8,7 @@ UHD - Firmware and FPGA Image Application Notes Images Overview ------------------------------------------------------------------------ Every USRP device must be loaded with special firmware and FPGA images. -The methods of loading images into the device varies among devices: +The methods of loading images into the device vary among devices: * **USRP1:** The host code will automatically load the firmware and FPGA at runtime. * **USRP2:** The user must manually write the images onto the USRP2 SD card. @@ -17,10 +17,14 @@ The methods of loading images into the device varies among devices: * **USRP-B Series:** The host code will automatically load the FPGA at runtime. ------------------------------------------------------------------------ -Pre-built images +Pre-built Images ------------------------------------------------------------------------ Pre-built images are available for download. + +* `Master Branch images <http://files.ettus.com/binaries/master_images/>`_ +* `Maint Branch images <http://files.ettus.com/binaries/maint_images/>`_ + See the UHD wiki for the download link. The pre-built images come in two forms: @@ -29,10 +33,23 @@ The pre-built images come in two forms: * stand-alone platform-independent archive files ^^^^^^^^^^^^^^^^^^^^^^ +UHD Images Downloader +^^^^^^^^^^^^^^^^^^^^^^ + +The UHD Images Downloader is a new feature in UHD 003.005.000. This script downloads UHD images that +are guaranteed to be compatible with the host code and places them in the default images +directory. + +By default, it can be found at: **<install-path>/share/uhd/utils/uhd_images_downloader.py** + +By default, it installs images to: **<install-path>/share/uhd/images** + +^^^^^^^^^^^^^^^^^^^^^^ Platform installers ^^^^^^^^^^^^^^^^^^^^^^ -The UNIX-based installers will install the images into /usr/share/uhd/images. -On windows, the images will be installed to <install-path>/share/uhd/images. +The UNIX-based installers will install the images into **/usr/share/uhd/images**. + +The Windows installers will install the images into **C:/Program Files/UHD/share/uhd/images**. ^^^^^^^^^^^^^^^^^^^^^^ Archive install @@ -42,52 +59,53 @@ When installing images from an archive, there are two options: **Option 1:** Unpack the archive into the UHD installation prefix. -The UHD will always search <install-path>/share/uhd/images for image files. -Where <install-path> was set by the CMAKE_INSTALL_PREFIX at configure-time. +UHD will always search **<install-path>/share/uhd/images** for image files. +Where **<install-path>** was set by the **CMAKE_INSTALL_PREFIX** at configure-time. **Option 2:** -Unpack the archive anywhere and set the UHD_IMAGE_PATH environment variable. -The UHD_IMAGE_PATH may contain a list of directories to search for image files. +Unpack the archive anywhere and set the **UHD_IMAGES_PATH** environment variable. +**UHD_IMAGES_PATH** may contain a list of directories to search for image files. ------------------------------------------------------------------------ -Building images +Building Images ------------------------------------------------------------------------ The UHD source repository comes with the source code necessary to build both firmware and FPGA images for all supported devices. -The build commands for a particular image can be found in <uhd-repo-path>/images/Makefile. + +The build commands for a particular image can be found in **<uhd-repo-path>/images/Makefile**. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Xilinx FPGA builds ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Xilinx ISE 12.x and up is required to build the Xilinx FPGA images. -The build requires that you have a unix-like environment with make. -Make sure that xtclsh from the Xilinx ISE bin directory is in your $PATH. +The build requires that you have a UNIX-like environment with **Make**. +Make sure that **xtclsh** from the Xilinx ISE bin directory is in your **$PATH**. -See <uhd-repo-path>/fpga/usrp2/top/* +See **<uhd-repo-path>/fpga/usrp2/top/**. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ZPU firmware builds ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The ZPU GCC compiler is required to build the ZPU firmware images. -The build requires that you have a unix-like environment with cmake and make. -Make sure that zpu-elf-gcc is in your $PATH. +The build requires that you have a UNIX-like environment with **CMake** and **Make**. +Make sure that **zpu-elf-gcc** is in your **$PATH**. -See <uhd-repo-path>/firmware/zpu +See **<uhd-repo-path>/firmware/zpu**. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Altera FPGA builds ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Quartus is required to build the Altera FPGA images. -Pre-built images can also be found in <uhd-repo-path>/fpga/usrp1/rbf +Pre-built images can also be found in **<uhd-repo-path>/fpga/usrp1/rbf**. -See <uhd-repo-path>/fpga/usrp1/toplevel/* +See **<uhd-repo-path>/fpga/usrp1/toplevel/***. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ FX2 firmware builds ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The sdcc compiler is required to build the FX2 firmware images. -The build requires that you have a unix-like environment with cmake and make. +The SDCC compiler is required to build the FX2 firmware images. +The build requires that you have a UNIX-like environment with **CMake** and **Make**. -See <uhd-repo-path>/firmware/fx2 +See **<uhd-repo-path>/firmware/fx2**. diff --git a/host/docs/index.rst b/host/docs/index.rst index f881e8585..00b1c9618 100644 --- a/host/docs/index.rst +++ b/host/docs/index.rst @@ -2,18 +2,20 @@ UHD - USRP Hardware Driver ======================================================================== -The UHD is the "Universal Software Radio Peripheral" hardware driver. -The goal of the UHD is to provide a host driver and API for current and future Ettus Research products. -Users will be able to use the UHD driver standalone or with 3rd party applications. +UHD is the "Universal Software Radio Peripheral" hardware driver. +The goal of UHD is to provide a host driver and API for current and future Ettus Research products. +Users will be able to use the UHD driver standalone or with third-party applications. ------------------------------------------------------------------------ Contents ------------------------------------------------------------------------ -^^^^^^^^^^^^^^^^^^^^^ -Building the UHD -^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Building and Installing UHD +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ * `Build Guide <./build.html>`_ +* `Installation Guide (Linux) <http://code.ettus.com/redmine/ettus/projects/uhd/wiki/UHD_Linux>`_ +* `Installation Guide (Windows) <http://code.ettus.com/redmine/ettus/projects/uhd/wiki/UHD_Windows>`_ ^^^^^^^^^^^^^^^^^^^^^ Application Notes @@ -23,9 +25,9 @@ Application Notes * `Firmware and FPGA Image Notes <./images.html>`_ * `USRP1 Application Notes <./usrp1.html>`_ * `USRP2 Application Notes <./usrp2.html>`_ -* `USRP-N2XX Series Application Notes <./usrp2.html>`_ -* `USRP-B1XX Series Application Notes <./usrp_b1xx.html>`_ -* `USRP-E1XX Series Application Notes <./usrp_e1xx.html>`_ +* `USRP-N2X0 Series Application Notes <./usrp2.html>`_ +* `USRP-B100 Series Application Notes <./usrp_b100.html>`_ +* `USRP-E1X0 Series Application Notes <./usrp_e1x0.html>`_ * `Daughterboard Application Notes <./dboards.html>`_ * `Transport Application Notes <./transport.html>`_ * `Synchronization Application Notes <./sync.html>`_ @@ -37,5 +39,4 @@ API Documentation ^^^^^^^^^^^^^^^^^^^^^ * `Doxygen <./../../doxygen/html/index.html>`_ * `Using the API <./coding.html>`_ -* `Device streaming <./stream.html>`_ - +* `Device Streaming <./stream.html>`_ diff --git a/host/docs/stream.rst b/host/docs/stream.rst index 9ffec22e5..a00ef88ee 100644 --- a/host/docs/stream.rst +++ b/host/docs/stream.rst @@ -5,15 +5,15 @@ UHD - Device streaming .. contents:: Table of Contents ------------------------------------------------------------------------ -Introduction to streaming +Introduction to Streaming ------------------------------------------------------------------------ The concept of streaming refers to the transportation of samples between host and device. A stream is an object that facilitates streaming between host application and device. -A RX stream allows the user to receive samples from the device. +An RX stream allows the user to receive samples from the device. A TX stream allows the user to transmit samples to the device. ------------------------------------------------------------------------ -Link layer encapsulation +Link Layer Encapsulation ------------------------------------------------------------------------ The VITA49 standard provides encapsulation for sample data across a link layer. On all generation2 hardware, samples are encapsulated into VRT IF data packets. @@ -22,12 +22,12 @@ Sample decoration is exposed to the user in the form of RX and TX metadata struc The length of an IF data packet can be limited by several factors: -* MTU of the link layer - network card, network switch -* Buffering on the host - frame size in a ring buffer -* Buffering on the device - size of BRAM FIFOs +* **MTU of the link layer:** network card, network switch +* **Buffering on the host:** frame size in a ring buffer +* **Buffering on the device:** size of BRAM FIFOs ------------------------------------------------------------------------ -Data types +Data Types ------------------------------------------------------------------------ There are two important data types to consider when streaming: @@ -38,15 +38,15 @@ There are two important data types to consider when streaming: The host/CPU data type ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The host data type refers to the format of samples used in the host for baseband processing. -Typically, the data type is complex baseband such as normalized complex-float32 or complex-int16. +Typically, the data type is complex baseband such as normalized **complex-float32** or **complex-int16**. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The link-layer data type ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The link-layer or "over-the-wire" data type refers to the format of the samples sent through the link. -Typically, this data type is complex-int16. -However, To increase throughput over the link-layer, -at the expense of precision, complex-int8 may be used. +Typically, this data type is **complex-int16**. +However, to increase throughput over the link-layer, +at the expense of precision, **complex-int8** may be used. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Conversion @@ -54,6 +54,6 @@ Conversion The user may request arbitrary combinations of host and link data types; however, not all combinations are supported. The user may register custom data type formats and conversion routines. -See uhd/convert.hpp for futher documentation. +See **uhd/convert.hpp** for futher documentation. -TODO provide example of convert API +TODO: provide example of convert API diff --git a/host/docs/sync.rst b/host/docs/sync.rst index 55c9f81f0..8622dc642 100644 --- a/host/docs/sync.rst +++ b/host/docs/sync.rst @@ -12,13 +12,13 @@ or other applications requiring multiple USRPs operating synchronously. which does not support the advanced features available in newer products. ------------------------------------------------------------------------ -Common reference signals +Common Reference Signals ------------------------------------------------------------------------ USRPs take two reference signals in order to synchronize clocks and time: * A 10MHz reference to provide a single frequency reference for both devices. -* A pulse-per-second (1PPS) to synchronize the sample time across devices. -* Or, the MIMO cable transmits an encoded time message from one device to another. +* A pulse-per-second (PPS) to synchronize the sample time across devices. +* A MIMO cable transmits an encoded time message from one device to another. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PPS and 10 MHz reference signals @@ -36,7 +36,7 @@ However, some USRP models can provide these signals from an optional internal GP Sometimes the delay on the PPS signal will cause it to arrive inside the timing margin the FPGA sampling clock, causing PPS edges to be separated by less or more than 100 million cycles of the FPGA clock. If this is the case, -you can change the edge reference of the PPS signal with this special parameter: +you can change the edge reference of the PPS signal with this parameter: :: @@ -44,7 +44,7 @@ you can change the edge reference of the PPS signal with this special parameter: **Note2:** For users generating their own signals for the external SMA connectors, -the pulse-per-second should be clocked from the 10MHz reference. +the PPS should be clocked from the 10MHz reference. See the application notes for your device for specific signal requirements. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -52,7 +52,7 @@ MIMO cable reference signals ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use the MIMO expansion cable to share reference sources (USRP2 and N-Series). The MIMO cable can be used synchronize one device to another device. -Users of the MIMO cable may use method 1 to synchronize multiple pairs of devices. +Users of the MIMO cable may use Method 1 (explained below) to synchronize multiple pairs of devices. :: @@ -60,13 +60,13 @@ Users of the MIMO cable may use method 1 to synchronize multiple pairs of device usrp->set_time_source("mimo"); ------------------------------------------------------------------------ -Synchronizing the device time +Synchronizing the Device Time ------------------------------------------------------------------------ The purpose of the PPS signal is to synchronously latch a time into the device. -You can use the set_time_next_pps(...) function to either initialize the sample time to 0, -or to an absolute time such as GPS time or UTC time. +You can use the **set_time_next_pps(...)** function to either initialize the sample time to 0 +or an absolute time, such as GPS time or UTC time. For the purposes of synchronizing devices, -it doesn't matter what time you initialize to when using set_time_next_pps(...). +it doesn't matter what time you initialize to when using **set_time_next_pps(...)**. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Method 1 - poll the USRP time registers @@ -85,7 +85,7 @@ When the last PPS time increments, the user can determine that a PPS has occurre ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Method 2 - query the GPSDO for seconds ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Most GPSDO can be configured to output a NMEA string over the serial port once every PPS. +Most GPSDOs can be configured to output a NMEA string over the serial port once every PPS. The user can wait for this string to determine the PPS edge, and the user can also parse this string to determine GPS time: @@ -117,7 +117,7 @@ The slave device will automatically synchronize to the time on the master device See the `MIMO Cable Application Notes <./usrp2.html#using-the-mimo-cable>`_ for more detail. ------------------------------------------------------------------------ -Synchronizing channel phase +Synchronizing Channel Phase ------------------------------------------------------------------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -152,8 +152,41 @@ For transmit, a burst is started when the user calls send(). The metadata should //send a single packet size_t num_tx_samps = tx_streamer->send(buffs, samps_to_send, md); +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Align LOs in the front-end (SBX/WBX + N-Series) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Using timed commands, multiple frontends can be tuned at a specific time. +This timed-tuning ensures that the phase offsets between VCO/PLL chains +will remain constant after each re-tune. See notes below: + +* There is a random phase offset between any two frontends +* This phase offset is different for different LO frequencies +* This phase offset remains constant after retuning + + * Due to divider, WBX phase offset will be randomly +/- 180 deg after re-tune + +* This phase offset will drift over time due to thermal and other characteristics +* Periodic calibration will be necessary for phase-coherent applications + +Code snippet example, tuning with timed commands: +:: + + //we will tune the frontends in 100ms from now + uhd::time_spec_t cmd_time = usrp->get_time_now() + uhd::time_spec_t(0.1); + + //sets command time on all devices + //the next commands are all timed + usrp->set_command_time(cmd_time); + + //tune channel 0 and channel 1 + usrp->set_rx_freq(1.03e9, 0/*ch0*/); + usrp->set_rx_freq(1.03e9, 1/*ch1*/); + + //end timed commands + usrp->clear_command_time(); + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Align LOs in the front-end +Align LOs in the front-end (others) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ After tuning the RF front-ends, each local oscillator may have a random phase offset due to the dividers diff --git a/host/docs/transport.rst b/host/docs/transport.rst index 7dc465b4c..2e39e75d1 100644 --- a/host/docs/transport.rst +++ b/host/docs/transport.rst @@ -17,7 +17,7 @@ that are known to perform well on a variety of systems. The transport parameters are defined below for the various transports in the UHD: ------------------------------------------------------------------------ -UDP transport (sockets) +UDP Transport (Sockets) ------------------------------------------------------------------------ The UDP transport is implemented with user-space sockets. This means standard Berkeley sockets API using send()/recv(). @@ -33,15 +33,15 @@ The following parameters can be used to alter the transport's default behavior: * **num_send_frames:** The number of send buffers to allocate **Note1:** -num_recv_frames does not affect performance. +**num_recv_frames** does not affect performance. **Note2:** -num_send_frames does not affect performance. +**num_send_frames** does not affect performance. **Note3:** -recv_frame_size and send_frame_size can be used to +**recv_frame_size** and **send_frame_size** can be used to increase or decrease the maximum number of samples per packet. -The frame sizes default to an MTU of 1472 bytes per IP/UDP packet, +The frame sizes default to an MTU of 1472 bytes per IP/UDP packet and may be increased if permitted by your network hardware. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -58,8 +58,8 @@ The following mechanisms affect the transmission of periodic update packets: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Resize socket buffers ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -It may be useful increase the size of the socket buffers to -move the burden of buffering samples into the kernel, or to +It may be useful to increase the size of the socket buffers to +move the burden of buffering samples into the kernel or to buffer incoming samples faster than they can be processed. However, if your application cannot process samples fast enough, no amount of buffering can save you. @@ -74,7 +74,7 @@ The following parameters can be used to alter socket's buffer sizes: Latency Optimization ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Latency is a measurement of the time it takes a sample to travel between the host and device. -Most computer hardware and software is bandwidth optimized which may negatively affect latency. +Most computer hardware and software is bandwidth optimized, which may negatively affect latency. If your application has strict latency requirements, please consider the following notes: **Note1:** @@ -92,7 +92,7 @@ Also, consult: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Linux specific notes ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -On linux, the maximum buffer sizes are capped by the sysctl values +On Linux, the maximum buffer sizes are capped by the sysctl values **net.core.rmem_max** and **net.core.wmem_max**. To change the maximum values, run the following commands: :: @@ -100,22 +100,33 @@ To change the maximum values, run the following commands: sudo sysctl -w net.core.rmem_max=<new value> sudo sysctl -w net.core.wmem_max=<new value> -Set the values permanently by editing */etc/sysctl.conf* +Set the values permanently by editing **/etc/sysctl.conf**. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Windows specific notes ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -On Windows, it is important to change the default UDP behavior such that +**UDP send fast-path:** +It is important to change the default UDP behavior such that 1500 byte packets still travel through the fast path of the sockets stack. -FastSendDatagramThreshold registry key to change documented here: +This can be adjusted with the FastSendDatagramThreshold registry key: -* http://www.microsoft.com/windows/windowsmedia/howto/articles/optimize_web.aspx#appendix_e +* FastSendDatagramThreshold registry key documented here: + + * http://www.microsoft.com/windows/windowsmedia/howto/articles/optimize_web.aspx#appendix_e + +* Double click and run <install-path>/share/uhd/FastSendDatagramThreshold.reg +* A system reboot is recommended after the registry key change. + +**Power profile:** +The Windows power profile can seriously impact instantaneous bandwidth. +Application can take time to ramp-up to full performance capability. +It is recommended that users set the power profile to "high performance". ------------------------------------------------------------------------ -USB transport (libusb) +USB Transport (LibUSB) ------------------------------------------------------------------------ -The USB transport is implemented with libusb. -Libusb provides an asynchronous API for USB bulk transfers. +The USB transport is implemented with LibUSB. +LibUSB provides an asynchronous API for USB bulk transfers. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Transport parameters @@ -145,9 +156,9 @@ Install USB driver (Windows) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ A driver package must be installed to use a USB-based product with UHD: -* Download the driver from the UHD wiki page. -* Unzip the file into a known location. We will refer to this as the <directory>. -* Open the device manager and plug-in the USRP. You will see an unrecognized USB device in the device manager. +* Download the driver from the UHD wiki page `here <http://files.ettus.com/binaries/misc/erllc_uhd_winusb_driver.zip>`_. +* Unzip the file into a known location. We will refer to this as the **<directory>**. +* Open the device manager and plug in the USRP. You will see an unrecognized USB device in the device manager. * Right click on the unrecognized USB device and select update/install driver software (may vary for your OS). -* In the driver installation wizard, select "browse for driver", browse to the <directory>, and select the .inf file. +* In the driver installation wizard, select "browse for driver", browse to the **<directory>**, and select the **.inf** file. * Continue through the installation wizard until the driver is installed. diff --git a/host/docs/usrp1.rst b/host/docs/usrp1.rst index 597b5b17f..c1fdec146 100644 --- a/host/docs/usrp1.rst +++ b/host/docs/usrp1.rst @@ -5,7 +5,18 @@ UHD - USRP1 Application Notes .. contents:: Table of Contents ------------------------------------------------------------------------ -Specify a non-standard image +Comparative features list +------------------------------------------------------------------------ + +* 2 transceiver card slots +* 2 RX DDC chains in FPGA +* 2 TX DUC chains in FPGA (no TX CORDIC -> uses DAC) +* 64 MHz fixed clock rate +* sc16 sample modes +* sc8 sample mode - RX only + +------------------------------------------------------------------------ +Specify a Non-standard Image ------------------------------------------------------------------------ The standard USRP1 images installer comes with two FPGA images: * **usrp1_fpga.rbf:** 2 DDCs + 2 DUCs @@ -31,7 +42,7 @@ Example device address string representations to specify non-standard firmware a fpga=usrp1_fpga_4rx.rbf, fw=usrp1_fw_custom.ihx ------------------------------------------------------------------------ -Missing and emulated features +Missing and Emulated Features ------------------------------------------------------------------------ The USRP1 FPGA does not have the necessary space to support the advanced streaming capabilities that are possible with the newer USRP devices. @@ -53,7 +64,7 @@ List of emulated features * Notification on broken chain error **Note:** -These emulated features rely on the host system's clock for timed operations, +These emulated features rely on the host system's clock for timed operations and therefore may not have sufficient precision for the application. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -62,17 +73,17 @@ List of missing features * Start of burst flags for transmit/receive ------------------------------------------------------------------------ -Hardware setup notes +Hardware Setup Notes ------------------------------------------------------------------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ External clock modification ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The USRP can be modified to accept an external clock reference instead of the 64MHz onboard reference. - * Solder SMA (LTI-SASF54GT) connector to J2001 - * Move 0 ohm 0603 resistor R2029 to R2030 - * Move 0.01uF 0603 capacitor C925 to C926 - * Remove 0.01uF 0603 capacitor C924 + * Solder SMA (**LTI-SASF54GT**) connector to **J2001**. + * Move 0 ohm 0603 resistor **R2029** to **R2030**. + * Move 0.01uF 0603 capacitor **C925** to **C926**. + * Remove 0.01uF 0603 capacitor **C924**. The new external clock needs to be a square wave between +7dBm and +15dBm diff --git a/host/docs/usrp2.rst b/host/docs/usrp2.rst index d81440b07..075a9684e 100644 --- a/host/docs/usrp2.rst +++ b/host/docs/usrp2.rst @@ -1,15 +1,31 @@ ======================================================================== -UHD - USRP2 and N Series Application Notes +UHD - USRP2 and N2X0 Series Application Notes ======================================================================== .. contents:: Table of Contents ------------------------------------------------------------------------ -Load the images onto the SD card (USRP2 only) +Comparative features list +------------------------------------------------------------------------ + +* 1 transceiver card slot +* 2 RX DDC chains in FPGA +* 1 TX DUC chain in FPGA +* Timed commands in FPGA (N2x0 only) +* Timed sampling in FPGA +* External PPS reference +* External 10MHz reference +* MIMO cable shared reference +* Fixed 100 MHz clock rate +* Internal GPSDO option (N2x0 only) +* sc8 and sc16 sample modes + +------------------------------------------------------------------------ +Load the Images onto the SD card (USRP2 only) ------------------------------------------------------------------------ **Warning!** -Use the usrp2_card_burner.py with caution. If you specify the wrong device node, -you could overwrite your hard drive. Make sure that --dev= specifies the SD card. +Use **usrp2_card_burner.py** with caution. If you specify the wrong device node, +you could overwrite your hard drive. Make sure that **--dev=** specifies the SD card. **Warning!** It is possible to use 3rd party SD cards with the USRP2. @@ -21,7 +37,7 @@ However, certain types of SD cards will not interface with the CPLD: For these reasons, we recommend that you use the SD card that was supplied with the USRP2. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Use the card burner tool (unix) +Use the card burner tool (UNIX) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ :: @@ -33,19 +49,19 @@ Use the card burner tool (unix) sudo ./usrp2_card_burner.py --dev=/dev/sd<XXX> --fpga=<path_to_fpga_image> sudo ./usrp2_card_burner.py --dev=/dev/sd<XXX> --fw=<path_to_firmware_image> -Use the *--list* option to get a list of possible raw devices. +Use the **--list** option to get a list of possible raw devices. The list result will filter out disk partitions and devices too large to be the sd card. The list option has been implemented on Linux, Mac OS X, and Windows. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Use the card burner tool (windows) +Use the card burner tool (Windows) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ :: <path_to_python.exe> <install-path>/share/uhd/utils/usrp2_card_burner_gui.py ------------------------------------------------------------------------ -Load the images onto the on-board flash (USRP-N Series only) +Load the Images onto the On-board Flash (USRP-N Series only) ------------------------------------------------------------------------ The USRP-N Series can be reprogrammed over the network to update or change the firmware and FPGA images. @@ -58,7 +74,7 @@ Determine the revision number from the sticker on the rear of the chassis. Use this number to select the correct FPGA image for your device. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Use the net burner tool (unix) +Use the net burner tool (UNIX) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ :: @@ -78,6 +94,17 @@ Use the net burner tool (Windows) <path_to_python.exe> <install-path>/share/uhd/utils/usrp_n2xx_net_burner_gui.py ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Burning images without Python +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +For users who do not wish to install Python, a new script is available in UHD 003.005.000: +the USRP N2XX Simple Net Burner. It provides the same functionality as its Python +counterpart, but by default, it automatically installs the default images without the user needing +to specify their location on the command line. + +The utility can be found at: **<install-path>/share/uhd/utils/usrp_n2xx_simple_net_burner** + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Device recovery and bricking ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Its possible to put the device into an unusable state by loading bad images. @@ -88,22 +115,22 @@ The safe-mode button is a pushbutton switch (S2) located inside the enclosure. To boot into the safe image, hold-down the safe-mode button while power-cycling the device. Continue to hold-down the button until the front-panel LEDs blink and remain solid. -When in safe-mode, the USRP-N device will always have the IP address 192.168.10.2 +When in safe-mode, the USRP-N device will always have the IP address **192.168.10.2**. ------------------------------------------------------------------------ -Setup networking +Setup Networking ------------------------------------------------------------------------ -The USRP2 only supports gigabit ethernet, +The USRP2 only supports Gigabit Ethernet and will not work with a 10/100 Mbps interface. However, a 10/100 Mbps interface can be connected indirectly -to a USRP2 through a gigabit ethernet switch. +to a USRP2 through a Gigabit Ethernet switch. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Setup the host interface ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The USRP2 communicates at the IP/UDP layer over the gigabit ethernet. -The default IP address of the USRP2 is **192.168.10.2** -You will need to configure the host's ethernet interface with a static IP +The default IP address of the USRP2 is **192.168.10.2**. +You will need to configure the host's Ethernet interface with a static IP address to enable communication. An address of **192.168.10.1** and a subnet mask of **255.255.255.0** is recommended. @@ -113,15 +140,15 @@ On a Linux system, you can set a static IP address very easily by using the sudo ifconfig <interface> 192.168.10.1 -Note that <interface> is usually something like 'eth0'. You can discover the -names of the network interfaces in your computer by running 'ifconfig' without +Note that **<interface>** is usually something like **eth0**. You can discover the +names of the network interfaces in your computer by running **ifconfig** without any parameters: :: ifconfig -a **Note:** -When using the UHD, if an IP address for the USRP2 is not specified, +When using UHD, if an IP address for the USRP2 is not specified, the software will use UDP broadcast packets to locate the USRP2. On some systems, the firewall will block UDP broadcast packets. It is recommended that you change or disable your firewall settings. @@ -129,23 +156,23 @@ It is recommended that you change or disable your firewall settings. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Multiple devices per host ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -For maximum throughput, one ethernet interface per USRP2 is recommended, -although multiple devices may be connected via a gigabit ethernet switch. -In any case, each ethernet interface should have its own subnet, +For maximum throughput, one Ethernet interface per USRP2 is recommended, +although multiple devices may be connected via a Gigabit Ethernet switch. +In any case, each Ethernet interface should have its own subnet, and the corresponding USRP2 device should be assigned an address in that subnet. Example: **Configuration for USRP2 device 0:** -* Ethernet interface IPv4 address: 192.168.10.1 -* Ethernet interface subnet mask: 255.255.255.0 -* USRP2 device IPv4 address: 192.168.10.2 +* Ethernet interface IPv4 address: **192.168.10.1** +* Ethernet interface subnet mask: **255.255.255.0** +* USRP2 device IPv4 address: **192.168.10.2** **Configuration for USRP2 device 1:** -* Ethernet interface IPv4 address: 192.168.20.1 -* Ethernet interface subnet mask: 255.255.255.0 -* USRP2 device IPv4 address: 192.168.20.2 +* Ethernet interface IPv4 address: **192.168.20.1** +* Ethernet interface subnet mask: **255.255.255.0** +* USRP2 device IPv4 address: **192.168.20.2** ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Change the USRP2's IP address @@ -157,7 +184,7 @@ You may need to change the USRP2's IP address for several reasons: * to set a known IP address into USRP2 (in case you forgot) **Method 1:** -To change the USRP2's IP address +To change the USRP2's IP address, you must know the current address of the USRP2, and the network must be setup properly as described above. Run the following commands: @@ -168,7 +195,7 @@ Run the following commands: **Method 2 (Linux Only):** This method assumes that you do not know the IP address of your USRP2. -It uses raw ethernet packets to bypass the IP/UDP layer to communicate with the USRP2. +It uses raw Ethernet packets to bypass the IP/UDP layer to communicate with the USRP2. Run the following commands: :: @@ -176,23 +203,23 @@ Run the following commands: sudo ./usrp2_recovery.py --ifc=eth0 --new-ip=192.168.10.3 ------------------------------------------------------------------------ -Communication problems +Communication Problems ------------------------------------------------------------------------ When setting up a development machine for the first time, you may have various difficulties communicating with the USRP device. -The following tips are designed to help narrow-down and diagnose the problem. +The following tips are designed to help narrow down and diagnose the problem. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ RuntimeError: no control response ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ This is a common error that occurs when you have set the subnet of your network interface to a different subnet than the network interface of the USRP. For -example, if your network interface is set to 192.168.20.1, and the USRP is -192.168.10.2 (note the difference in the third numbers of the IP addresses), you +example, if your network interface is set to **192.168.20.1**, and the USRP is +**192.168.10.2** (note the difference in the third numbers of the IP addresses), you will likely see a 'no control response' error message. Fixing this is simple - just set the your host PC's IP address to the same -subnet as your USRP. Instructions for setting your IP address are in the +subnet as that of your USRP. Instructions for setting your IP address are in the previous section of this documentation. @@ -200,19 +227,19 @@ previous section of this documentation. Firewall issues ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ When the IP address is not specified, -the device discovery sends broadcast UDP packets from each ethernet interface. +the device discovery broadcasts UDP packets from each ethernet interface. Many firewalls will block the replies to these broadcast packets. -If disabling your system's firewall, -or specifying the IP address yeilds a discovered device, +If disabling your system's firewall +or specifying the IP address yields a discovered device, then your firewall may be blocking replies to UDP broadcast packets. -If this is the case, we recommend that you disable the firewall, -or create a rule to allow all incoming packets with UDP source port 49152. +If this is the case, we recommend that you disable the firewall +or create a rule to allow all incoming packets with UDP source port **49152**. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Ping the device ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The USRP will reply to icmp echo requests. -A successful ping response means that the device has booted properly, +The USRP will reply to ICMP echo requests. +A successful ping response means that the device has booted properly and that it is using the expected IP address. :: @@ -222,12 +249,12 @@ and that it is using the expected IP address. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Monitor the serial output ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Read the serial port to get debug verbose from the embedded microcontroller. +Read the serial port to get debug verbose output from the embedded microcontroller. The microcontroller prints useful information about IP addresses, MAC addresses, control packets, fast-path settings, and bootloading. Use a standard USB to 3.3v-level serial converter at 230400 baud. -Connect GND to the converter ground, and connect TXD to the converter receive. -The RXD pin can be left unconnected as this is only a one-way communication. +Connect **GND** to the converter ground, and connect **TXD** to the converter receive. +The **RXD** pin can be left unconnected as this is only a one-way communication. * **USRP2:** Serial port located on the rear edge * **N210:** Serial port located on the left side @@ -235,10 +262,10 @@ The RXD pin can be left unconnected as this is only a one-way communication. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Monitor the host network traffic ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Use wireshark to monitor packets sent to and received from the device. +Use Wireshark to monitor packets sent to and received from the device. ------------------------------------------------------------------------ -Addressing the device +Addressing the Device ------------------------------------------------------------------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -248,9 +275,9 @@ In a single-device configuration, the USRP device must have a unique IPv4 address on the host computer. The USRP can be identified through its IPv4 address, resolvable hostname, or by other means. See the application notes on `device identification <./identification.html>`_. -Use this addressing scheme with the *single_usrp* interface. +Use this addressing scheme with the **single_usrp** interface. -Example device address string representation for a USRP2 with IPv4 address 192.168.10.2 +Example device address string representation for a USRP2 with IPv4 address **192.168.10.2**: :: @@ -263,12 +290,12 @@ In a multi-device configuration, each USRP device must have a unique IPv4 address on the host computer. The device address parameter keys must be suffixed with the device index. Each parameter key should be of the format <key><index>. -Use this addressing scheme with the *multi_usrp* interface. +Use this addressing scheme with the **multi_usrp** interface. * The order in which devices are indexed corresponds to the indexing of the transmit and receive channels. * The key indexing provides the same granularity of device identification as in the single device case. -Example device address string representation for 2 USRP2s with IPv4 addresses 192.168.10.2 and 192.168.20.2 +Example device address string representation for 2 USRP2s with IPv4 addresses **192.168.10.2** and **192.168.20.2**: :: addr0=192.168.10.2, addr1=192.168.20.2 @@ -277,31 +304,31 @@ Example device address string representation for 2 USRP2s with IPv4 addresses 19 Using the MIMO Cable ------------------------------------------------------------------------ The MIMO cable allows two USRP devices to share reference clocks, -time synchronization, and the ethernet interface. -One of the devices will sink its clock and time references to the MIMO cable. +time synchronization, and the Ethernet interface. +One of the devices will sync its clock and time references to the MIMO cable. This device will be referred to as the slave, and the other device, the master. * The slave device acquires the clock and time references from the master device. * The master and slave may be used individually or in a multi-device configuration. -* External clocking is optional, and should only be supplied to the master device. +* External clocking is optional and should only be supplied to the master device. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Shared ethernet mode ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -In shared ethernet mode, -only one device in the configuration can be attached to the ethernet. +In shared Ethernet mode, +only one device in the configuration can be attached to the Tthernet. * Clock reference, time reference, and data are communicated over the MIMO cable. -* Both master and slave must have different IPv4 addresses in the same subnet. +* Master and slave must have different IPv4 addresses in the same subnet. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Dual ethernet mode ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -In dual ethernet mode, -both devices in the configuration must be attached to the ethernet. +In dual Ethernet mode, +both devices in the configuration must be attached to the Ethernet. * Only clock reference and time reference are communicated over the MIMO cable. -* Both master and slave must have different IPv4 addresses in different subnets. +* The master and slave must have different IPv4 addresses in different subnets. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Configuring the slave @@ -315,8 +342,57 @@ the following clock configuration must be set on the slave device: clock_config.pps_source = uhd::clock_config_t::PPS_MIMO; usrp->set_clock_config(clock_config, slave_index); + +------------------------------------------------------------------------ +Alternative stream destination +------------------------------------------------------------------------ +It is possible to program the USRP to send RX packets to an alternative IP/UDP destination. + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Set the subnet and gateway +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +To use an alternative streaming destination, +the device needs to be able to determine if the destination address +is within its subnet, and ARP appropriately. +Therefore, the user should ensure that subnet and gateway addresses +have been programmed into the device's EEPROM. + +Run the following commands: +:: + + cd <install-path>/share/uhd/utils + ./usrp_burn_mb_eeprom --args=<optional device args> --key=subnet --val=255.255.255.0 + ./usrp_burn_mb_eeprom --args=<optional device args> --key=gateway --val=192.168.10.1 + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Create a receive streamer +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Set the stream args "addr" and "port" values to the alternative destination. +Packets will be sent to this destination when the user issues a stream command. + +:: + + //create a receive streamer, host type does not matter + uhd::stream_args_t stream_args("fc32"); + + //resolvable address and port for a remote udp socket + stream_args.args["addr"] = "192.168.10.42"; + stream_args.args["port"] = "12345"; + + //create the streamer + uhd::rx_streamer::sptr rx_stream = usrp->get_rx_stream(stream_args); + + //issue stream command + uhd::stream_cmd_t stream_cmd(uhd::stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_DONE); + stream_cmd.num_samps = total_num_samps; + stream_cmd.stream_now = true; + usrp->issue_stream_cmd(stream_cmd); + +**Note:** +Calling recv() on this streamer object should yield a timeout. + ------------------------------------------------------------------------ -Hardware setup notes +Hardware Setup Notes ------------------------------------------------------------------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -336,8 +412,8 @@ The LEDs reveal the following about the state of the device: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Ref Clock - 10MHz ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Using an external 10MHz reference clock, square wave will offer the best phase -noise performance, but sinusoid is acceptable. The reference clock requires the following power level: +Using an external 10MHz reference clock, a square wave will offer the best phase +noise performance, but a sinusoid is acceptable. The reference clock requires the following power level: * **USRP2** 5 to 15dBm * **N2XX** 0 to 15dBm @@ -353,7 +429,7 @@ Using a PPS signal for timestamp synchronization requires a square wave signal w Test the PPS input with the following app: -* <args> are device address arguments (optional if only one USRP is on your machine) +* **<args>** are device address arguments (optional if only one USRP is on your machine) :: @@ -376,8 +452,8 @@ Available Sensors The following sensors are available for the USRP2/N-Series motherboards; they can be queried through the API. -* mimo_locked - clock reference locked over the MIMO cable -* ref_locked - clock reference locked (internal/external) +* **mimo_locked** - clock reference locked over the MIMO cable +* **ref_locked** - clock reference locked (internal/external) * other sensors are added when the GPSDO is enabled ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -386,13 +462,13 @@ Multiple RX channels There are two complete DDC chains in the FPGA. In the single channel case, only one chain is ever used. To receive from both channels, -the user must set the RX subdevice specification. +the user must set the **RX** subdevice specification. This hardware has only one daughterboard slot, -which has been aptly named slot "A". +which has been aptly named slot **A**. In the following example, a TVRX2 is installed. -Channel 0 is sourced from subdevice RX1, -channel 1 is sourced from subdevice RX2: +Channel 0 is sourced from subdevice **RX1**, +and channel 1 is sourced from subdevice **RX2**: :: usrp->set_rx_subdev_spec("A:RX1 A:RX2"); diff --git a/host/docs/usrp_b1xx.rst b/host/docs/usrp_b100.rst index 538d60fce..cdb853b61 100644 --- a/host/docs/usrp_b1xx.rst +++ b/host/docs/usrp_b100.rst @@ -1,14 +1,28 @@ ======================================================================== -UHD - USRP-B1XX Series Application Notes +UHD - USRP-B100 Series Application Notes ======================================================================== .. contents:: Table of Contents ------------------------------------------------------------------------ -Specify a non-standard image +Comparative features list ------------------------------------------------------------------------ -The UHD will automatically select the USRP B-Series images from the installed images package. -The image selection can be overridden with the "fpga" and "fw" device address parameters. + +* 1 transceiver card slot +* 1 RX DDC chain in FPGA +* 1 TX DUC chain in FPGA +* Timed commands in FPGA +* Timed sampling in FPGA +* External PPS reference +* External 10MHz reference +* Configurable clock rate (defaults 64 MHz) +* sc8 and sc16 sample modes + +------------------------------------------------------------------------ +Specify a Non-standard Image +------------------------------------------------------------------------ +UHD will automatically select the USRP B-Series images from the installed images package. +The image selection can be overridden with the **--fpga=** and **--fw=** device address parameters. Example device address string representations to specify non-standard images: @@ -21,15 +35,15 @@ Example device address string representations to specify non-standard images: fw=usrp_b100_fw_firmware.ihx ------------------------------------------------------------------------ -Changing the master clock rate +Changing the Master Clock Rate ------------------------------------------------------------------------ The master clock rate of the USRP embedded feeds both the FPGA DSP and the codec chip. Hundreds of rates between 32MHz and 64MHz are available. A few notable rates are: -* 64MHz - maximum rate of the codec chip -* 61.44MHz - good for UMTS/WCDMA applications -* 52Mhz - good for GSM applications +* **64MHz:** maximum rate of the codec chip +* **61.44MHz:** good for UMTS/WCDMA applications +* **52Mhz:** good for GSM applications ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Set 61.44MHz - uses external VCXO @@ -40,7 +54,7 @@ and X4 must be populated with a 61.44 MHz oscillator. * **J15** is a three pin header, move the jumper to (pin1, pin2) * **357LB3I061M4400** is the recommended oscillator for X4 -**Note:** See instructions below to communicate the desired clock rate into the UHD. +**Note:** See instructions below to communicate the desired clock rate into UHD. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Set other rates - uses internal VCO @@ -49,9 +63,9 @@ To use other clock rates, the jumper will need to be in the default position. * **J15** is a three pin header, move the jumper to (pin2, pin3) -To communicate the desired clock rate into the UHD, +To communicate the desired clock rate into UHD, specify the a special device address argument, -where the key is "master_clock_rate" and the value is a rate in Hz. +where the key is **master_clock_rate** and the value is a rate in Hz. Example: :: @@ -84,4 +98,4 @@ Available Sensors The following sensors are available; they can be queried through the API. -* ref_locked - clock reference locked (internal/external) +* **ref_locked:** clock reference locked (internal/external) diff --git a/host/docs/usrp_e1xx.rst b/host/docs/usrp_e1x0.rst index ef1e22b3a..189cbb86b 100644 --- a/host/docs/usrp_e1xx.rst +++ b/host/docs/usrp_e1x0.rst @@ -1,15 +1,30 @@ ======================================================================== -UHD - USRP-E1XX Series Application Notes +UHD - USRP-E1X0 Series Application Notes ======================================================================== .. contents:: Table of Contents ------------------------------------------------------------------------ -Specify a non-standard image +Comparative features list +------------------------------------------------------------------------ + +* 1 transceiver card slot +* 2 RX DDC chains in FPGA +* 1 TX DUC chain in FPGA +* Timed commands in FPGA +* Timed sampling in FPGA +* Internal PPS reference +* Internal 10MHz reference +* Configurable clock rate (defaults 64 MHz) +* Internal GPSDO option +* sc8 and sc16 sample modes + +------------------------------------------------------------------------ +Specify a Non-standard Image ------------------------------------------------------------------------ UHD will automatically select the USRP-Embedded FPGA image from the installed images package. The FPGA image selection can be overridden with the -"fpga" device address parameter. +**--fpga=** device address parameter. Example device address string representations to specify non-standard FPGA image: @@ -19,15 +34,15 @@ image: fpga=usrp_e100_custom.bin ------------------------------------------------------------------------ -Changing the master clock rate +Changing the Master Clock Rate ------------------------------------------------------------------------ The master clock rate of the USRP-Embedded feeds both the FPGA DSP and the codec chip. Hundreds of rates between 32MHz and 64MHz are available. A few notable rates are: -* 64MHz - maximum rate of the codec chip -* 61.44MHz - good for UMTS/WCDMA applications -* 52Mhz - good for GSM applications +* **64MHz:** maximum rate of the codec chip +* **61.44MHz:** good for UMTS/WCDMA applications +* **52Mhz:** good for GSM applications ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Set 61.44MHz - uses external VCXO @@ -35,23 +50,22 @@ Set 61.44MHz - uses external VCXO To use the 61.44MHz clock rate with the USRP-Embedded, two jumpers must be moved on the device. -* J16 is a two pin header, remove the jumper (or leave it on pin1 only) -* J15 is a three pin header, move the jumper to (pin1, pin2) +* **J16** is a two pin header; remove the jumper (or leave it on pin1 only). +* **J15** is a three pin header; move the jumper to (pin1, pin2). -**Note:** See instructions below to communicate the desired clock rate into the -UHD. +**Note:** See instructions below to communicate the desired clock rate UHD. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Set other rates - uses internal VCO ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ To use other clock rates, the jumpers will need to be in the default position. -* J16 is a two pin header, move the jumper to (pin1, pin2) -* J15 is a three pin header, move the jumper to (pin2, pin3) +* **J16** is a two pin header; move the jumper to (pin1, pin2). +* **J15** is a three pin header; move the jumper to (pin2, pin3). -To communicate the desired clock rate into the UHD, +To communicate the desired clock rate into UHD, specify the a special device address argument, -where the key is "master_clock_rate" and the value is a rate in Hz. +where the key is **master_clock_rate** and the value is a rate in Hz. Example: :: @@ -66,15 +80,15 @@ Clock Synchronization Ref Clock - 10MHz ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The E1xx has a 10MHz TCXO which can be used to discipline the flexible clocking -by selecting REF_INT for the clock_config_t. +by selecting **REF_INT** for the **clock_config_t**. Alternately, an external 10MHz reference clock can be supplied by soldering a connector. -* Connector J10 (REF_IN) needs MCX connector WM5541-ND or similar -* Square wave will offer the best phase noise performance, but sinusoid is acceptable -* Power level: 0 to 15dBm -* Select REF_SMA in clock_config_t +* Connector **J10** (REF_IN) needs MCX connector **WM5541-ND** or similar. +* Square wave will offer the best phase noise performance, but sinusoid is acceptable. +* **Power level:** 0 to 15dBm +* Select **REF_SMA** in **clock_config_t**. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -83,13 +97,13 @@ PPS - Pulse Per Second An exteral PPS signal for timestamp synchronization can be supplied by soldering a connector. -* Connector J13 (PPS) needs MCX connector WM5541-ND or similar -* Requires a square wave signal -* Amplitude: 3.3 to 5Vpp +* Connector **J13** (PPS) needs MCX connector **WM5541-ND** or similar. +* Requires a square wave signal. +* **Amplitude:** 3.3 to 5Vpp Test the PPS input with the following app: -* <args> are device address arguments (optional if only one USRP is on your machine) +* **<args** are device address arguments (optional if only one USRP is on your machine). :: @@ -106,7 +120,7 @@ UHD will always try to detect an installed GPSDO at runtime. There is not a special EEPROM value to burn for GPSDO detection. ------------------------------------------------------------------------ -Hardware setup notes +Hardware Setup Notes ------------------------------------------------------------------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -116,7 +130,7 @@ The LEDs on the front panel can be useful in debugging hardware and software issues. The LEDs reveal the following about the state of the device: * **LED A:** transmitting -* **LED B:** fpga loaded +* **LED B:** PPS signal * **LED C:** receiving * **LED D:** fpga loaded * **LED E:** reference lock @@ -132,5 +146,5 @@ Available Sensors The following sensors are available; they can be queried through the API. -* ref_locked - clock reference locked (internal/external) +* **ref_locked:** clock reference locked (internal/external) * other sensors are added when the GPSDO is enabled diff --git a/host/examples/CMakeLists.txt b/host/examples/CMakeLists.txt index 3c9a3880a..3ba483134 100644 --- a/host/examples/CMakeLists.txt +++ b/host/examples/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2010-2011 Ettus Research LLC +# Copyright 2010-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 @@ -25,12 +25,16 @@ SET(example_sources rx_samples_to_file.cpp rx_samples_to_udp.cpp rx_timed_samples.cpp + test_dboard_coercion.cpp test_messages.cpp test_pps_input.cpp + test_timed_commands.cpp + transport_hammer.cpp tx_bursts.cpp tx_samples_from_file.cpp tx_timed_samples.cpp tx_waveforms.cpp + txrx_loopback_to_file.cpp latency_test.cpp ) diff --git a/host/examples/benchmark_rate.cpp b/host/examples/benchmark_rate.cpp index 8f00e25de..8a000f6c3 100644 --- a/host/examples/benchmark_rate.cpp +++ b/host/examples/benchmark_rate.cpp @@ -1,5 +1,5 @@ // -// Copyright 2011 Ettus Research LLC +// Copyright 2011-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 @@ -16,6 +16,7 @@ // #include <uhd/utils/thread_priority.hpp> +#include <uhd/convert.hpp> #include <uhd/utils/safe_main.hpp> #include <uhd/usrp/multi_usrp.hpp> #include <boost/program_options.hpp> @@ -40,11 +41,13 @@ unsigned long long num_seq_errors = 0; /*********************************************************************** * Benchmark RX Rate **********************************************************************/ -void benchmark_rx_rate(uhd::usrp::multi_usrp::sptr usrp, const std::string &rx_otw){ +void benchmark_rx_rate(uhd::usrp::multi_usrp::sptr usrp, const std::string &rx_cpu, const std::string &rx_otw){ uhd::set_thread_priority_safe(); //create a receive streamer - uhd::stream_args_t stream_args("fc32", rx_otw); //complex floats + uhd::stream_args_t stream_args(rx_cpu, rx_otw); + for (size_t ch = 0; ch < usrp->get_num_mboards(); ch++) //linear channel mapping + stream_args.channels.push_back(ch); uhd::rx_streamer::sptr rx_stream = usrp->get_rx_stream(stream_args); //print pre-test summary @@ -55,16 +58,20 @@ void benchmark_rx_rate(uhd::usrp::multi_usrp::sptr usrp, const std::string &rx_o //setup variables and allocate buffer uhd::rx_metadata_t md; const size_t max_samps_per_packet = rx_stream->get_max_num_samps(); - std::vector<std::complex<float> > buff(max_samps_per_packet); + std::vector<char> buff(max_samps_per_packet*uhd::convert::get_bytes_per_item(rx_cpu)); + std::vector<void *> buffs; + for (size_t ch = 0; ch < stream_args.channels.size(); ch++) + buffs.push_back(&buff.front()); //same buffer for each channel bool had_an_overflow = false; uhd::time_spec_t last_time; const double rate = usrp->get_rx_rate(); - usrp->issue_stream_cmd(uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS); + uhd::stream_cmd_t cmd(uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS); + cmd.time_spec = usrp->get_time_now() + uhd::time_spec_t(0.05); + cmd.stream_now = (buffs.size() == 1); + usrp->issue_stream_cmd(cmd); while (not boost::this_thread::interruption_requested()){ - num_rx_samps += rx_stream->recv( - &buff.front(), buff.size(), md - ); + num_rx_samps += rx_stream->recv(buffs, max_samps_per_packet, md); //handle the error codes switch(md.error_code){ @@ -94,11 +101,13 @@ void benchmark_rx_rate(uhd::usrp::multi_usrp::sptr usrp, const std::string &rx_o /*********************************************************************** * Benchmark TX Rate **********************************************************************/ -void benchmark_tx_rate(uhd::usrp::multi_usrp::sptr usrp, const std::string &tx_otw){ +void benchmark_tx_rate(uhd::usrp::multi_usrp::sptr usrp, const std::string &tx_cpu, const std::string &tx_otw){ uhd::set_thread_priority_safe(); //create a transmit streamer - uhd::stream_args_t stream_args("fc32", tx_otw); //complex floats + uhd::stream_args_t stream_args(tx_cpu, tx_otw); + for (size_t ch = 0; ch < usrp->get_num_mboards(); ch++) //linear channel mapping + stream_args.channels.push_back(ch); uhd::tx_streamer::sptr tx_stream = usrp->get_tx_stream(stream_args); //print pre-test summary @@ -108,17 +117,22 @@ void benchmark_tx_rate(uhd::usrp::multi_usrp::sptr usrp, const std::string &tx_o //setup variables and allocate buffer uhd::tx_metadata_t md; - md.has_time_spec = false; + md.time_spec = usrp->get_time_now() + uhd::time_spec_t(0.05); const size_t max_samps_per_packet = tx_stream->get_max_num_samps(); - std::vector<std::complex<float> > buff(max_samps_per_packet); + std::vector<char> buff(max_samps_per_packet*uhd::convert::get_bytes_per_item(tx_cpu)); + std::vector<const void *> buffs; + for (size_t ch = 0; ch < stream_args.channels.size(); ch++) + buffs.push_back(&buff.front()); //same buffer for each channel + md.has_time_spec = (buffs.size() != 1); while (not boost::this_thread::interruption_requested()){ - num_tx_samps += tx_stream->send(&buff.front(), buff.size(), md); + num_tx_samps += tx_stream->send(buffs, max_samps_per_packet, md); + md.has_time_spec = false; } //send a mini EOB packet md.end_of_burst = true; - tx_stream->send("", 0, md); + tx_stream->send(buffs, 0, md); } void benchmark_tx_rate_async_helper(uhd::usrp::multi_usrp::sptr usrp){ @@ -163,6 +177,8 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ double duration; double rx_rate, tx_rate; std::string rx_otw, tx_otw; + std::string rx_cpu, tx_cpu; + std::string mode; //setup the program options po::options_description desc("Allowed options"); @@ -174,6 +190,9 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ ("tx_rate", po::value<double>(&tx_rate), "specify to perform a TX rate test (sps)") ("rx_otw", po::value<std::string>(&rx_otw)->default_value("sc16"), "specify the over-the-wire sample mode for RX") ("tx_otw", po::value<std::string>(&tx_otw)->default_value("sc16"), "specify the over-the-wire sample mode for TX") + ("rx_cpu", po::value<std::string>(&rx_cpu)->default_value("fc32"), "specify the host/cpu sample mode for RX") + ("tx_cpu", po::value<std::string>(&tx_cpu)->default_value("fc32"), "specify the host/cpu sample mode for TX") + ("mode", po::value<std::string>(&mode)->default_value("none"), "multi-channel sync mode option: none, mimo") ; po::variables_map vm; po::store(po::parse_command_line(argc, argv, desc), vm); @@ -201,18 +220,24 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ uhd::usrp::multi_usrp::sptr usrp = uhd::usrp::multi_usrp::make(args); std::cout << boost::format("Using Device: %s") % usrp->get_pp_string() << std::endl; + if (mode == "mimo"){ + usrp->set_clock_source("mimo", 0); + usrp->set_time_source("mimo", 0); + boost::this_thread::sleep(boost::posix_time::seconds(1)); + } + boost::thread_group thread_group; //spawn the receive test thread if (vm.count("rx_rate")){ usrp->set_rx_rate(rx_rate); - thread_group.create_thread(boost::bind(&benchmark_rx_rate, usrp, rx_otw)); + thread_group.create_thread(boost::bind(&benchmark_rx_rate, usrp, rx_cpu, rx_otw)); } //spawn the transmit test thread if (vm.count("tx_rate")){ usrp->set_tx_rate(tx_rate); - thread_group.create_thread(boost::bind(&benchmark_tx_rate, usrp, tx_otw)); + thread_group.create_thread(boost::bind(&benchmark_tx_rate, usrp, tx_cpu, tx_otw)); thread_group.create_thread(boost::bind(&benchmark_tx_rate_async_helper, usrp)); } diff --git a/host/examples/test_dboard_coercion.cpp b/host/examples/test_dboard_coercion.cpp new file mode 100644 index 000000000..5b1e9f0e2 --- /dev/null +++ b/host/examples/test_dboard_coercion.cpp @@ -0,0 +1,577 @@ +// +// 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 <uhd/utils/thread_priority.hpp> +#include <uhd/utils/safe_main.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> +#include <complex> +#include <vector> + +namespace po = boost::program_options; + +/************************************************************************ + * Misc functions +************************************************************************/ + +std::string return_MHz_string(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 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"; + + info_string = std::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"; + 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)); + } + 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"; + 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)); + } + + return info_string; +} + +/************************************************************************ + * TX Frequency/Gain Coercion +************************************************************************/ + +std::string tx_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_tx_freq_range()){ + 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"){ + 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; + } + if(freq_end != *freqs.end()) freqs.push_back(freq_end); + } + + std::vector<double> gains; + + if(test_gain){ + + //Establish gain range + + double gain_begin = usrp->get_tx_gain_range().start(); + if(gain_begin < 0.0) gain_begin = 0.0; + double gain_end = usrp->get_tx_gain_range().stop(); + + double current_gain = gain_begin; + while(current_gain < gain_end){ + gains.push_back(current_gain); + current_gain++; + } + if(gain_end != *gains.end()) 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(); + 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_tx_freq(*f); + boost::this_thread::sleep(boost::posix_time::microseconds(long(1000))); + + double actual_freq = usrp->get_tx_freq(); + + if(*f == 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; + } + 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; + } + } + 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; + } + 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); + } + } + + //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_tx_sensor("lo_locked",0).to_bool()){ + is_locked = true; + break; + } + } + if(is_locked){ + if(verbose) std::cout << boost::format("LO successfully locked at TX frequency %s.") % return_MHz_string(*f) << 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(test_gain){ + + //Testing for successful gain tune + + for(std::vector<double>::iterator g = gains.begin(); g != gains.end(); ++g){ + usrp->set_tx_gain(*g); + boost::this_thread::sleep(boost::posix_time::microseconds(1000)); + + double actual_gain = usrp->get_tx_gain(); + + if(*g == 0.0){ + if(actual_gain == 0.0){ + if(verbose) std::cout << boost::format("TX gain successfully set to %5.2f at TX frequency %s.") % *g % return_MHz_string(*f) << std::endl; + } + else{ + if(verbose) std::cout << boost::format("TX gain set to %5.2f instead of %5.2f at TX frequency %s.") % actual_gain % *g % return_MHz_string(*f) << std::endl; + std::vector<double> bad_gain_freq; + 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("TX gain successfully set to %5.2f at TX frequency %s.") % *g % return_MHz_string(*f) << std::endl; + } + else{ + if(verbose) std::cout << boost::format("TX gain set to %5.2f instead of %5.2f at TX frequency %s.") % actual_gain % *g % return_MHz_string(*f) << std::endl; + std::vector<double> bad_gain_freq; + bad_gain_freq.push_back(*f); + bad_gain_freq.push_back(*g); + bad_gain_vals.push_back(bad_gain_freq); + } + } + } + } + } + + 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)))); + } + 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); + } + } + 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); + } + } + } + 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)); + } + } + } + + 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++; + } + if(gain_end != *gains.end()) 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; + } + } + 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)); + } + } + } + + return rx_results; +} + +/************************************************************************ + * Initial Setup +************************************************************************/ + +int UHD_SAFE_MAIN(int argc, char *argv[]){ + + //Variables + std::string args; + double gain_step; + std::string ref; + std::string tx_results; + std::string rx_results; + std::string usrp_config; + + //Set up the program options + po::options_description desc("Allowed Options"); + 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") + ("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") + ("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" + "This program tests your USRP daughterboard(s) to\n" + "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; + } + + 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; + } + + 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)"){ + std::cout << desc << std::endl; + std::cout << "This daughterboard is unrecognized, or there is no RX daughterboard." << std::endl; + return ~0; + } + + 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; + } + 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; + } + + //Setting clock source + usrp->set_clock_source(ref); + boost::this_thread::sleep(boost::posix_time::seconds(1)); + + std::vector<std::string> sensor_names = usrp->get_mboard_sensor_names(0); + if ((ref == "mimo") and (std::find(sensor_names.begin(), sensor_names.end(), "mimo_locked") != sensor_names.end())) { + uhd::sensor_value_t mimo_locked = usrp->get_mboard_sensor("mimo_locked",0); + std::cout << boost::format("Checking MIMO lock: %s ...") % mimo_locked.to_pp_string() << std::endl; + UHD_ASSERT_THROW(mimo_locked.to_bool()); + } + if ((ref == "external") and (std::find(sensor_names.begin(), sensor_names.end(), "ref_locked") != sensor_names.end())) { + uhd::sensor_value_t ref_locked = usrp->get_mboard_sensor("ref_locked",0); + std::cout << boost::format("Checking REF lock: %s ...") % ref_locked.to_pp_string() << std::endl; + UHD_ASSERT_THROW(ref_locked.to_bool()); + } + usrp_config = return_USRP_config_string(usrp, test_tx, test_rx); + if(test_tx) tx_results = tx_test(usrp, test_tx_gain, verbose); + if(test_rx) rx_results = rx_test(usrp, test_rx_gain, verbose); + + if(verbose) std::cout << std::endl; + std::cout << 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; + + return 0; +} diff --git a/host/examples/test_timed_commands.cpp b/host/examples/test_timed_commands.cpp new file mode 100644 index 000000000..34c83dfd6 --- /dev/null +++ b/host/examples/test_timed_commands.cpp @@ -0,0 +1,129 @@ +// +// 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 <uhd/utils/thread_priority.hpp> +#include <uhd/utils/safe_main.hpp> +#include <uhd/usrp/multi_usrp.hpp> +#include <boost/program_options.hpp> +#include <boost/format.hpp> +#include <iostream> +#include <complex> + +namespace po = boost::program_options; + +int UHD_SAFE_MAIN(int argc, char *argv[]){ + uhd::set_thread_priority_safe(); + + //variables to be set by po + std::string args; + + //setup the program options + po::options_description desc("Allowed options"); + desc.add_options() + ("help", "help message") + ("args", po::value<std::string>(&args)->default_value(""), "single uhd device address args") + ; + po::variables_map vm; + po::store(po::parse_command_line(argc, argv, desc), vm); + po::notify(vm); + + //print the help message + if (vm.count("help")){ + std::cout << boost::format("UHD Test Timed Commands %s") % desc << std::endl; + return ~0; + } + + //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); + std::cout << boost::format("Using Device: %s") % usrp->get_pp_string() << std::endl; + + //check if timed commands are supported + std::cout << std::endl; + std::cout << "Testing support for timed commands on this hardware... " << std::flush; + try{ + usrp->set_command_time(uhd::time_spec_t(0.0)); + usrp->clear_command_time(); + } + catch (const std::exception &e){ + std::cout << "fail" << std::endl; + std::cerr << "Got exception: " << e.what() << std::endl; + std::cerr << "Timed commands are not supported on this hardware." << std::endl; + return ~0; + } + std::cout << "pass" << std::endl; + + //readback time really fast, time diff is small + std::cout << std::endl; + std::cout << "Perform fast readback of registers:" << std::endl; + uhd::time_spec_t total_time; + for (size_t i = 0; i < 100; i++){ + const uhd::time_spec_t t0 = usrp->get_time_now(); + const uhd::time_spec_t t1 = usrp->get_time_now(); + total_time += (t1-t0); + } + std::cout << boost::format( + "Difference between paired reads: %f us" + ) % (total_time.get_real_secs()/100*1e6) << std::endl; + + //use a timed command to start a stream at a specific time + //this is not the right way start streaming at time x, + //but it should approximate it within control RTT/2 + //setup streaming + std::cout << std::endl; + std::cout << "About to start streaming using timed command:" << std::endl; + + //create a receive streamer + uhd::stream_args_t stream_args("fc32"); //complex floats + uhd::rx_streamer::sptr rx_stream = usrp->get_rx_stream(stream_args); + + uhd::stream_cmd_t stream_cmd(uhd::stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_DONE); + stream_cmd.num_samps = 100; + stream_cmd.stream_now = true; + const uhd::time_spec_t stream_time = usrp->get_time_now() + uhd::time_spec_t(0.1); + usrp->set_command_time(stream_time); + usrp->issue_stream_cmd(stream_cmd); + usrp->clear_command_time(); + + //meta-data will be filled in by recv() + uhd::rx_metadata_t md; + + //allocate buffer to receive with samples + std::vector<std::complex<float> > buff(stream_cmd.num_samps); + + const size_t num_rx_samps = rx_stream->recv(&buff.front(), buff.size(), md); + 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)); + } + std::cout << boost::format( + "Received packet: %u samples, %u full secs, %f frac secs" + ) % num_rx_samps % md.time_spec.get_full_secs() % md.time_spec.get_frac_secs() << std::endl; + std::cout << boost::format( + "Stream time was: %u full secs, %f frac secs" + ) % stream_time.get_full_secs() % stream_time.get_frac_secs() << std::endl; + std::cout << boost::format( + "Difference between stream time and first packet: %f us" + ) % ((md.time_spec-stream_time).get_real_secs()/100*1e6) << std::endl; + + //finished + std::cout << std::endl << "Done!" << std::endl << std::endl; + + return 0; +} diff --git a/host/examples/transport_hammer.cpp b/host/examples/transport_hammer.cpp new file mode 100644 index 000000000..e32912eaa --- /dev/null +++ b/host/examples/transport_hammer.cpp @@ -0,0 +1,276 @@ +// +// 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 <uhd/utils/thread_priority.hpp> +#include <uhd/convert.hpp> +#include <uhd/utils/safe_main.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> +#include <complex> + +namespace po = boost::program_options; + +/*********************************************************************** + * Test result variables + **********************************************************************/ +unsigned long long num_overflows = 0; +unsigned long long num_underflows = 0; +unsigned long long num_rx_samps = 0; +unsigned long long num_tx_samps = 0; +unsigned long long num_dropped_samps = 0; +unsigned long long num_seq_errors = 0; + +/*********************************************************************** + * RX Hammer + **********************************************************************/ +void rx_hammer(uhd::usrp::multi_usrp::sptr usrp, const std::string &rx_cpu, const std::string &rx_otw){ + uhd::set_thread_priority_safe(); + + //create a receive streamer + uhd::stream_args_t stream_args(rx_cpu, rx_otw); + for (size_t ch = 0; ch < usrp->get_num_mboards(); ch++) //linear channel mapping + stream_args.channels.push_back(ch); + uhd::rx_streamer::sptr rx_stream = usrp->get_rx_stream(stream_args); + + //print pre-test summary + std::cout << boost::format( + "Testing receive rate %f Msps" + ) % (usrp->get_rx_rate()/1e6) << std::endl; + + //setup variables and allocate buffer + uhd::rx_metadata_t md; + const size_t max_samps_per_packet = rx_stream->get_max_num_samps(); + std::vector<char> buff(max_samps_per_packet*uhd::convert::get_bytes_per_item(rx_cpu)); + std::vector<void *> buffs; + for (size_t ch = 0; ch < stream_args.channels.size(); ch++) + buffs.push_back(&buff.front()); //same buffer for each channel + bool had_an_overflow = false; + uhd::time_spec_t last_time; + const double rate = usrp->get_rx_rate(); + double timeout = 1; + + uhd::stream_cmd_t cmd(uhd::stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_DONE); + cmd.time_spec = usrp->get_time_now() + uhd::time_spec_t(0.05); + cmd.stream_now = (buffs.size() == 1); + srand( time(NULL) ); + + while (not boost::this_thread::interruption_requested()){ + cmd.num_samps = rand() % 100000; + usrp->issue_stream_cmd(cmd); + num_rx_samps += rx_stream->recv(buffs, max_samps_per_packet, md, timeout, true); + + //handle the error codes + switch(md.error_code){ + case uhd::rx_metadata_t::ERROR_CODE_NONE: + if (had_an_overflow){ + had_an_overflow = false; + num_dropped_samps += boost::math::iround((md.time_spec - last_time).get_real_secs()*rate); + } + break; + + case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW: + had_an_overflow = true; + last_time = md.time_spec; + num_overflows++; + break; + + default: + std::cerr << "Error code: " << md.error_code << std::endl; + std::cerr << "Unexpected error on recv, continuing..." << std::endl; + break; + } + } +} + +/*********************************************************************** + * TX Hammer + **********************************************************************/ +void tx_hammer(uhd::usrp::multi_usrp::sptr usrp, const std::string &tx_cpu, const std::string &tx_otw){ + uhd::set_thread_priority_safe(); + + //create a transmit streamer + uhd::stream_args_t stream_args(tx_cpu, tx_otw); + for (size_t ch = 0; ch < usrp->get_num_mboards(); ch++) //linear channel mapping + stream_args.channels.push_back(ch); + uhd::tx_streamer::sptr tx_stream = usrp->get_tx_stream(stream_args); + uhd::tx_metadata_t md; + std::vector<std::complex<float> > buff(10000); + + //print pre-test summary + std::cout << boost::format( + "Testing transmit rate %f Msps" + ) % (usrp->get_tx_rate()/1e6) << std::endl; + + //setup variables and allocate buffer + std::srand( time(NULL) ); + while(not boost::this_thread::interruption_requested()){ + size_t total_num_samps = rand() % 100000; + size_t num_acc_samps = 0; + float timeout = 1; + + usrp->set_time_now(uhd::time_spec_t(0.0)); + while(num_acc_samps < total_num_samps){ + + //send a single packet + num_tx_samps += tx_stream->send(&buff, tx_stream->get_max_num_samps(), md, timeout); + + num_acc_samps += std::min(total_num_samps-num_acc_samps, tx_stream->get_max_num_samps()); + } + //send a mini EOB packet + md.end_of_burst = true; + tx_stream->send("", 0, md); + } +} + +void tx_hammer_async_helper(uhd::usrp::multi_usrp::sptr usrp){ + //setup variables and allocate buffer + uhd::async_metadata_t async_md; + + while (not boost::this_thread::interruption_requested()){ + + if (not usrp->get_device()->recv_async_msg(async_md)) continue; + + //handle the error codes + switch(async_md.event_code){ + case uhd::async_metadata_t::EVENT_CODE_BURST_ACK: + return; + + case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW: + case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW_IN_PACKET: + num_underflows++; + break; + + case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR: + case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR_IN_BURST: + num_seq_errors++; + break; + + default: + std::cerr << "Event code: " << async_md.event_code << std::endl; + std::cerr << "Unexpected event on async recv, continuing..." << std::endl; + break; + } + } +} + +/*********************************************************************** + * Main code + dispatcher + **********************************************************************/ +int UHD_SAFE_MAIN(int argc, char *argv[]){ + uhd::set_thread_priority_safe(); + + //variables to be set by po + std::string args; + double duration; + double rx_rate, tx_rate; + std::string rx_otw, tx_otw; + std::string rx_cpu, tx_cpu; + std::string mode; + + //setup the program options + po::options_description desc("Allowed options"); + desc.add_options() + ("help", "help message") + ("args", po::value<std::string>(&args)->default_value(""), "single uhd device address args") + ("duration", po::value<double>(&duration)->default_value(10.0), "if random, specify duration for the test in seconds") + ("rx_rate", po::value<double>(&rx_rate), "specify to perform a RX rate test (sps)") + ("tx_rate", po::value<double>(&tx_rate), "specify to perform a TX rate test (sps)") + ("rx_otw", po::value<std::string>(&rx_otw)->default_value("sc16"), "specify the over-the-wire sample mode for RX") + ("tx_otw", po::value<std::string>(&tx_otw)->default_value("sc16"), "specify the over-the-wire sample mode for TX") + ("rx_cpu", po::value<std::string>(&rx_cpu)->default_value("fc32"), "specify the host/cpu sample mode for RX") + ("tx_cpu", po::value<std::string>(&tx_cpu)->default_value("fc32"), "specify the host/cpu sample mode for TX") + ("mode", po::value<std::string>(&mode)->default_value("none"), "multi-channel sync mode option: none, mimo") + ; + po::variables_map vm; + po::store(po::parse_command_line(argc, argv, desc), vm); + po::notify(vm); + + //print the help message + if (vm.count("help") or (vm.count("rx_rate") + vm.count("tx_rate")) == 0){ + //std::cout << boost::format("UHD Transport Hammer - %s") % desc << std::endl; + std::cout << + "UHD Transport Hammer: a transport layer stress test that continuously\n" + "calls for random amounts of TX and RX samples\n\n"; + std::cout << desc << std::endl << + " Specify --rx_rate for a receive-only test.\n" + " Specify --tx_rate for a transmit-only test.\n" + " Specify both options for a full-duplex test.\n" + << std::endl; + return ~0; + } + + //create a usrp device + std::cout << std::endl; + uhd::device_addrs_t device_addrs = uhd::device::find(args); + if (not device_addrs.empty() and device_addrs.at(0).get("type", "") == "usrp1"){ + std::cerr << "*** Warning! ***" << std::endl; + std::cerr << "Results will be inaccurate on USRP1 due to insufficient features.\n" << 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); + std::cout << boost::format("Using Device: %s") % usrp->get_pp_string() << std::endl; + + if (mode == "mimo"){ + usrp->set_clock_source("mimo", 0); + usrp->set_time_source("mimo", 0); + boost::this_thread::sleep(boost::posix_time::seconds(1)); + } + + boost::thread_group thread_group; + + //spawn the receive test thread + if (vm.count("rx_rate")){ + usrp->set_rx_rate(rx_rate); + thread_group.create_thread(boost::bind(&rx_hammer, usrp, rx_cpu, rx_otw)); + } + + //spawn the transmit test thread + if (vm.count("tx_rate")){ + usrp->set_tx_rate(tx_rate); + thread_group.create_thread(boost::bind(&tx_hammer, usrp, tx_cpu, tx_otw)); + thread_group.create_thread(boost::bind(&tx_hammer_async_helper, usrp)); + } + + //sleep for the required duration + const long secs = long(duration); + const long usecs = long((duration - secs)*1e6); + boost::this_thread::sleep(boost::posix_time::seconds(secs) + boost::posix_time::microseconds(usecs)); + + //interrupt and join the threads + thread_group.interrupt_all(); + thread_group.join_all(); + + //print summary + std::cout << std::endl << boost::format( + "Transport Hammer summary:\n" + " Num received samples: %u\n" + " Num dropped samples: %u\n" + " Num overflows detected: %u\n" + " Num transmitted samples: %u\n" + " Num sequence errors: %u\n" + " Num underflows detected: %u\n" + ) % num_rx_samps % num_dropped_samps % num_overflows % num_tx_samps % num_seq_errors % num_underflows << std::endl; + + //finished + std::cout << std::endl << "Done!" << std::endl << std::endl; + + return 0; +} diff --git a/host/examples/tx_samples_from_file.cpp b/host/examples/tx_samples_from_file.cpp index ea4add403..04a6a63d4 100644 --- a/host/examples/tx_samples_from_file.cpp +++ b/host/examples/tx_samples_from_file.cpp @@ -1,5 +1,5 @@ // -// Copyright 2011 Ettus Research LLC +// Copyright 2011-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 @@ -24,17 +24,23 @@ #include <iostream> #include <fstream> #include <complex> +#include <csignal> namespace po = boost::program_options; +static bool stop_signal_called = false; +void sig_int_handler(int){stop_signal_called = true;} + template<typename samp_type> void send_from_file( uhd::usrp::multi_usrp::sptr usrp, const std::string &cpu_format, + const std::string &wire_format, const std::string &file, size_t samps_per_buff ){ + //create a transmit streamer - uhd::stream_args_t stream_args(cpu_format); + uhd::stream_args_t stream_args(cpu_format, wire_format); uhd::tx_streamer::sptr tx_stream = usrp->get_tx_stream(stream_args); uhd::tx_metadata_t md; @@ -44,7 +50,8 @@ template<typename samp_type> void send_from_file( std::ifstream infile(file.c_str(), std::ifstream::binary); //loop until the entire file has been read - while(not md.end_of_burst){ + + while(not md.end_of_burst and not stop_signal_called){ infile.read((char*)&buff.front(), buff.size()*sizeof(samp_type)); size_t num_tx_samps = infile.gcount()/sizeof(samp_type); @@ -61,9 +68,9 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ uhd::set_thread_priority_safe(); //variables to be set by po - std::string args, file, type, ant, subdev, ref; + std::string args, file, type, ant, subdev, ref, wirefmt; size_t spb; - double rate, freq, gain, bw; + double rate, freq, gain, bw, delay; //setup the program options po::options_description desc("Allowed options"); @@ -80,6 +87,9 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ ("subdev", po::value<std::string>(&subdev), "daughterboard subdevice specification") ("bw", po::value<double>(&bw), "daughterboard IF filter bandwidth in Hz") ("ref", po::value<std::string>(&ref)->default_value("internal"), "waveform type (internal, external, mimo)") + ("wirefmt", po::value<std::string>(&wirefmt)->default_value("sc16"), "wire format (sc8 or sc16)") + ("delay", po::value<double>(&delay)->default_value(0.0), "specify a delay between repeated transmission of file") + ("repeat", "repeatedly transmit file") ; po::variables_map vm; po::store(po::parse_command_line(argc, argv, desc), vm); @@ -91,6 +101,8 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ return ~0; } + bool repeat = vm.count("repeat"); + //create a usrp device std::cout << std::endl; std::cout << boost::format("Creating the usrp device with: %s...") % args << std::endl; @@ -161,11 +173,21 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ UHD_ASSERT_THROW(ref_locked.to_bool()); } + //set sigint if user wants to receive + if(repeat){ + std::signal(SIGINT, &sig_int_handler); + std::cout << "Press Ctrl + C to stop streaming..." << std::endl; + } + //send from file - if (type == "double") send_from_file<std::complex<double> >(usrp, "fc64", file, spb); - else if (type == "float") send_from_file<std::complex<float> >(usrp, "fc32", file, spb); - else if (type == "short") send_from_file<std::complex<short> >(usrp, "sc16", file, spb); - else throw std::runtime_error("Unknown type " + type); + do{ + if (type == "double") send_from_file<std::complex<double> >(usrp, "fc64", wirefmt, file, spb); + else if (type == "float") send_from_file<std::complex<float> >(usrp, "fc32", wirefmt, file, spb); + else if (type == "short") send_from_file<std::complex<short> >(usrp, "sc16", wirefmt, file, spb); + else throw std::runtime_error("Unknown type " + type); + + if(repeat and delay != 0.0) boost::this_thread::sleep(boost::posix_time::milliseconds(delay)); + } while(repeat and not stop_signal_called); //finished std::cout << std::endl << "Done!" << std::endl << std::endl; diff --git a/host/examples/tx_waveforms.cpp b/host/examples/tx_waveforms.cpp index 6a377fdac..3c5eecd65 100644 --- a/host/examples/tx_waveforms.cpp +++ b/host/examples/tx_waveforms.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-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 @@ -24,6 +24,7 @@ #include <boost/math/special_functions/round.hpp> #include <boost/foreach.hpp> #include <boost/format.hpp> +#include <boost/thread.hpp> #include <iostream> #include <complex> #include <csignal> @@ -174,6 +175,8 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ if (vm.count("ant")) usrp->set_tx_antenna(ant, chan); } + boost::this_thread::sleep(boost::posix_time::seconds(1)); //allow for some setup time + //for the const wave, set the wave freq for small samples per period if (wave_freq == 0 and wave_type == "CONST"){ wave_freq = usrp->get_tx_rate()/2; diff --git a/host/examples/txrx_loopback_to_file.cpp b/host/examples/txrx_loopback_to_file.cpp new file mode 100644 index 000000000..495c9f7e4 --- /dev/null +++ b/host/examples/txrx_loopback_to_file.cpp @@ -0,0 +1,447 @@ +// +// Copyright 2010-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 <uhd/utils/thread_priority.hpp> +#include <uhd/utils/safe_main.hpp> +#include <uhd/utils/static.hpp> +#include <uhd/usrp/multi_usrp.hpp> +#include <uhd/exception.hpp> +#include <boost/thread/thread.hpp> +#include <boost/program_options.hpp> +#include <boost/math/special_functions/round.hpp> +#include <boost/foreach.hpp> +#include <boost/format.hpp> +#include <iostream> +#include <fstream> +#include <complex> +#include <csignal> +#include <cmath> + +namespace po = boost::program_options; + +/*********************************************************************** + * Signal handlers + **********************************************************************/ +static bool stop_signal_called = false; +void sig_int_handler(int){stop_signal_called = true;} + +/*********************************************************************** + * Waveform generators + **********************************************************************/ +static const size_t wave_table_len = 8192; + +class wave_table_class{ +public: + wave_table_class(const std::string &wave_type, const float ampl): + _wave_table(wave_table_len) + { + //compute real wave table with 1.0 amplitude + std::vector<double> real_wave_table(wave_table_len); + if (wave_type == "CONST"){ + for (size_t i = 0; i < wave_table_len; i++) + real_wave_table[i] = 1.0; + } + else if (wave_type == "SQUARE"){ + for (size_t i = 0; i < wave_table_len; i++) + real_wave_table[i] = (i < wave_table_len/2)? 0.0 : 1.0; + } + else if (wave_type == "RAMP"){ + for (size_t i = 0; i < wave_table_len; i++) + real_wave_table[i] = 2.0*i/(wave_table_len-1) - 1.0; + } + else if (wave_type == "SINE"){ + static const double tau = 2*std::acos(-1.0); + for (size_t i = 0; i < wave_table_len; i++) + real_wave_table[i] = std::sin((tau*i)/wave_table_len); + } + else throw std::runtime_error("unknown waveform type: " + wave_type); + + //compute i and q pairs with 90% offset and scale to amplitude + for (size_t i = 0; i < wave_table_len; i++){ + const size_t q = (i+(3*wave_table_len)/4)%wave_table_len; + _wave_table[i] = std::complex<float>(ampl*real_wave_table[i], ampl*real_wave_table[q]); + } + } + + inline std::complex<float> operator()(const size_t index) const{ + return _wave_table[index % wave_table_len]; + } + +private: + std::vector<std::complex<float> > _wave_table; +}; + +/*********************************************************************** + * transmit_worker function + * A function to be used as a boost::thread_group thread for transmitting + **********************************************************************/ +void transmit_worker( + std::vector<std::complex<float> > buff, + wave_table_class wave_table, + uhd::tx_streamer::sptr tx_streamer, + uhd::tx_metadata_t metadata, + size_t step, + size_t index, + int num_channels +){ + std::vector<std::complex<float> *> buffs(num_channels, &buff.front()); + + //send data until the signal handler gets called + while(not stop_signal_called){ + //fill the buffer with the waveform + for (size_t n = 0; n < buff.size(); n++){ + buff[n] = wave_table(index += step); + } + + //send the entire contents of the buffer + tx_streamer->send(buffs, buff.size(), metadata); + + metadata.start_of_burst = false; + metadata.has_time_spec = false; + } + + //send a mini EOB packet + metadata.end_of_burst = true; + tx_streamer->send("", 0, metadata); +} + + +/*********************************************************************** + * recv_to_file function + **********************************************************************/ +template<typename samp_type> void recv_to_file( + uhd::usrp::multi_usrp::sptr usrp, + const std::string &cpu_format, + const std::string &wire_format, + const std::string &file, + size_t samps_per_buff, + int num_requested_samples, + float settling_time +){ + int num_total_samps = 0; + //create a receive streamer + uhd::stream_args_t stream_args(cpu_format,wire_format); + uhd::rx_streamer::sptr rx_stream = usrp->get_rx_stream(stream_args); + + uhd::rx_metadata_t md; + std::vector<samp_type> buff(samps_per_buff); + std::ofstream outfile(file.c_str(), std::ofstream::binary); + bool overflow_message = true; + float timeout = settling_time + 0.1; //expected settling time + padding for first recv + + //setup streaming + uhd::stream_cmd_t stream_cmd((num_requested_samples == 0)? + uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS: + uhd::stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_DONE + ); + stream_cmd.num_samps = num_requested_samples; + stream_cmd.stream_now = false; + stream_cmd.time_spec = uhd::time_spec_t(settling_time); + usrp->issue_stream_cmd(stream_cmd); + + while(not stop_signal_called and (num_requested_samples != num_total_samps or num_requested_samples == 0)){ + size_t num_rx_samps = rx_stream->recv(&buff.front(), buff.size(), md, timeout); + timeout = 0.1; //small timeout for subsequent recv + + if (md.error_code == uhd::rx_metadata_t::ERROR_CODE_TIMEOUT) { + std::cout << boost::format("Timeout while streaming") << std::endl; + break; + } + if (md.error_code == uhd::rx_metadata_t::ERROR_CODE_OVERFLOW){ + if (overflow_message){ + overflow_message = false; + std::cerr << boost::format( + "Got an overflow indication. Please consider the following:\n" + " Your write medium must sustain a rate of %fMB/s.\n" + " Dropped samples will not be written to the file.\n" + " Please modify this example for your purposes.\n" + " This message will not appear again.\n" + ) % (usrp->get_rx_rate()*sizeof(samp_type)/1e6); + } + continue; + } + 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)); + } + + num_total_samps += num_rx_samps; + + outfile.write((const char*)&buff.front(), num_rx_samps*sizeof(samp_type)); + } + + outfile.close(); +} + + +/*********************************************************************** + * Main function + **********************************************************************/ +int UHD_SAFE_MAIN(int argc, char *argv[]){ + uhd::set_thread_priority_safe(); + + //transmit variables to be set by po + std::string tx_args, wave_type, tx_ant, tx_subdev, ref, otw; + double tx_rate, tx_freq, tx_gain, wave_freq, tx_bw; + float ampl; + + //receive variables to be set by po + std::string rx_args, file, type, rx_ant, rx_subdev; + size_t total_num_samps, spb; + double rx_rate, rx_freq, rx_gain, rx_bw; + float settling; + + //setup the program options + po::options_description desc("Allowed options"); + desc.add_options() + ("help", "help message") + ("tx-args", po::value<std::string>(&tx_args)->default_value(""), "uhd transmit device address args") + ("rx-args", po::value<std::string>(&rx_args)->default_value(""), "uhd receive device address args") + ("file", po::value<std::string>(&file)->default_value("usrp_samples.dat"), "name of the file to write binary samples to") + ("type", po::value<std::string>(&type)->default_value("short"), "sample type in file: double, float, or short") + ("nsamps", po::value<size_t>(&total_num_samps)->default_value(0), "total number of samples to receive") + ("settling", po::value<float>(&settling)->default_value(float(0.2)), "settling time (seconds) before receiving") + ("spb", po::value<size_t>(&spb)->default_value(0), "samples per buffer, 0 for default") + ("tx-rate", po::value<double>(&tx_rate), "rate of transmit outgoing samples") + ("rx-rate", po::value<double>(&rx_rate), "rate of receive incoming samples") + ("tx-freq", po::value<double>(&tx_freq), "transmit RF center frequency in Hz") + ("rx-freq", po::value<double>(&rx_freq), "receive RF center frequency in Hz") + ("ampl", po::value<float>(&l)->default_value(float(0.3)), "amplitude of the waveform [0 to 0.7]") + ("tx-gain", po::value<double>(&tx_gain), "gain for the transmit RF chain") + ("rx-gain", po::value<double>(&rx_gain), "gain for the receive RF chain") + ("tx-ant", po::value<std::string>(&tx_ant), "daughterboard transmit antenna selection") + ("rx-ant", po::value<std::string>(&rx_ant), "daughterboard receive antenna selection") + ("tx-subdev", po::value<std::string>(&tx_subdev), "daughterboard transmit subdevice specification") + ("rx-subdev", po::value<std::string>(&rx_subdev), "daughterboard receive subdevice specification") + ("tx-bw", po::value<double>(&tx_bw), "daughterboard transmit IF filter bandwidth in Hz") + ("rx-bw", po::value<double>(&rx_bw), "daughterboard receive IF filter bandwidth in Hz") + ("wave-type", po::value<std::string>(&wave_type)->default_value("CONST"), "waveform type (CONST, SQUARE, RAMP, SINE)") + ("wave-freq", po::value<double>(&wave_freq)->default_value(0), "waveform frequency in Hz") + ("ref", po::value<std::string>(&ref)->default_value("internal"), "clock reference (internal, external, mimo)") + ("otw", po::value<std::string>(&otw)->default_value("sc16"), "specify the over-the-wire sample mode") + ; + po::variables_map vm; + po::store(po::parse_command_line(argc, argv, desc), vm); + po::notify(vm); + + //print the help message + if (vm.count("help")){ + std::cout << boost::format("UHD TXRX Loopback to File %s") % desc << std::endl; + return ~0; + } + + //create a usrp device + std::cout << std::endl; + std::cout << boost::format("Creating the transmit usrp device with: %s...") % tx_args << std::endl; + uhd::usrp::multi_usrp::sptr tx_usrp = uhd::usrp::multi_usrp::make(tx_args); + std::cout << std::endl; + std::cout << boost::format("Creating the receive usrp device with: %s...") % rx_args << std::endl; + uhd::usrp::multi_usrp::sptr rx_usrp = uhd::usrp::multi_usrp::make(rx_args); + + //Lock mboard clocks + tx_usrp->set_clock_source(ref); + rx_usrp->set_clock_source(ref); + + //always select the subdevice first, the channel mapping affects the other settings + if (vm.count("tx-subdev")) tx_usrp->set_tx_subdev_spec(tx_subdev); + if (vm.count("rx-subdev")) rx_usrp->set_rx_subdev_spec(rx_subdev); + + std::cout << boost::format("Using Device: %s") % tx_usrp->get_pp_string() << std::endl; + std::cout << boost::format("Using Device: %s") % rx_usrp->get_pp_string() << std::endl; + + //set the transmit sample rate + if (not vm.count("tx-rate")){ + std::cerr << "Please specify the transmit sample rate with --tx-rate" << std::endl; + return ~0; + } + std::cout << boost::format("Setting TX Rate: %f Msps...") % (tx_rate/1e6) << std::endl; + tx_usrp->set_tx_rate(tx_rate); + std::cout << boost::format("Actual TX Rate: %f Msps...") % (tx_usrp->get_tx_rate()/1e6) << std::endl << std::endl; + + //set the receive sample rate + if (not vm.count("rx-rate")){ + std::cerr << "Please specify the sample rate with --rx-rate" << std::endl; + return ~0; + } + std::cout << boost::format("Setting RX Rate: %f Msps...") % (rx_rate/1e6) << std::endl; + rx_usrp->set_rx_rate(rx_rate); + std::cout << boost::format("Actual RX Rate: %f Msps...") % (rx_usrp->get_rx_rate()/1e6) << std::endl << std::endl; + + //set the transmit center frequency + if (not vm.count("tx-freq")){ + std::cerr << "Please specify the transmit center frequency with --tx-freq" << std::endl; + return ~0; + } + + for(size_t chan = 0; chan < tx_usrp->get_tx_num_channels(); chan++) { + std::cout << boost::format("Setting TX Freq: %f MHz...") % (tx_freq/1e6) << std::endl; + tx_usrp->set_tx_freq(tx_freq, chan); + std::cout << boost::format("Actual TX Freq: %f MHz...") % (tx_usrp->get_tx_freq(chan)/1e6) << std::endl << std::endl; + + //set the rf gain + if (vm.count("tx-gain")){ + std::cout << boost::format("Setting TX Gain: %f dB...") % tx_gain << std::endl; + tx_usrp->set_tx_gain(tx_gain, chan); + std::cout << boost::format("Actual TX Gain: %f dB...") % tx_usrp->get_tx_gain(chan) << std::endl << std::endl; + } + + //set the IF filter bandwidth + if (vm.count("tx-bw")){ + std::cout << boost::format("Setting TX Bandwidth: %f MHz...") % tx_bw << std::endl; + tx_usrp->set_tx_bandwidth(tx_bw, chan); + std::cout << boost::format("Actual TX Bandwidth: %f MHz...") % tx_usrp->get_tx_bandwidth(chan) << std::endl << std::endl; + } + + //set the antenna + if (vm.count("tx-ant")) tx_usrp->set_tx_antenna(tx_ant, chan); + } + + //set the receive center frequency + if (not vm.count("rx-freq")){ + std::cerr << "Please specify the center frequency with --rx-freq" << std::endl; + return ~0; + } + std::cout << boost::format("Setting RX Freq: %f MHz...") % (rx_freq/1e6) << std::endl; + rx_usrp->set_rx_freq(rx_freq); + std::cout << boost::format("Actual RX Freq: %f MHz...") % (rx_usrp->get_rx_freq()/1e6) << std::endl << std::endl; + + //set the receive rf gain + if (vm.count("rx_gain")){ + std::cout << boost::format("Setting RX Gain: %f dB...") % rx_gain << std::endl; + rx_usrp->set_rx_gain(rx_gain); + std::cout << boost::format("Actual RX Gain: %f dB...") % rx_usrp->get_rx_gain() << std::endl << std::endl; + } + + //set the receive IF filter bandwidth + if (vm.count("rx_bw")){ + std::cout << boost::format("Setting RX Bandwidth: %f MHz...") % rx_bw << std::endl; + rx_usrp->set_rx_bandwidth(rx_bw); + std::cout << boost::format("Actual RX Bandwidth: %f MHz...") % rx_usrp->get_rx_bandwidth() << std::endl << std::endl; + } + + //set the receive antenna + if (vm.count("ant")) rx_usrp->set_rx_antenna(rx_ant); + + //for the const wave, set the wave freq for small samples per period + if (wave_freq == 0 and wave_type == "CONST"){ + wave_freq = tx_usrp->get_tx_rate()/2; + } + + //error when the waveform is not possible to generate + if (std::abs(wave_freq) > tx_usrp->get_tx_rate()/2){ + throw std::runtime_error("wave freq out of Nyquist zone"); + } + if (tx_usrp->get_tx_rate()/std::abs(wave_freq) > wave_table_len/2){ + throw std::runtime_error("wave freq too small for table"); + } + + //pre-compute the waveform values + const wave_table_class wave_table(wave_type, ampl); + const size_t step = boost::math::iround(wave_freq/tx_usrp->get_tx_rate() * wave_table_len); + size_t index = 0; + + //create a transmit streamer + //linearly map channels (index0 = channel0, index1 = channel1, ...) + uhd::stream_args_t stream_args("fc32", otw); + for (size_t chan = 0; chan < tx_usrp->get_tx_num_channels(); chan++) + stream_args.channels.push_back(chan); //linear mapping + uhd::tx_streamer::sptr tx_stream = tx_usrp->get_tx_stream(stream_args); + + //allocate a buffer which we re-use for each channel + if (spb == 0) spb = tx_stream->get_max_num_samps()*10; + std::vector<std::complex<float> > buff(spb); + int num_channels = tx_usrp->get_tx_num_channels(); + + //setup the metadata flags + uhd::tx_metadata_t md; + md.start_of_burst = true; + md.end_of_burst = false; + md.has_time_spec = true; + md.time_spec = uhd::time_spec_t(0.1); //give us 0.1 seconds to fill the tx buffers + + //Check Ref and LO Lock detect + std::vector<std::string> tx_sensor_names, rx_sensor_names; + tx_sensor_names = tx_usrp->get_tx_sensor_names(0); + if (std::find(tx_sensor_names.begin(), tx_sensor_names.end(), "lo_locked") != tx_sensor_names.end()) { + uhd::sensor_value_t lo_locked = tx_usrp->get_tx_sensor("lo_locked",0); + std::cout << boost::format("Checking TX: %s ...") % lo_locked.to_pp_string() << std::endl; + UHD_ASSERT_THROW(lo_locked.to_bool()); + } + rx_sensor_names = rx_usrp->get_rx_sensor_names(0); + if (std::find(rx_sensor_names.begin(), rx_sensor_names.end(), "lo_locked") != rx_sensor_names.end()) { + uhd::sensor_value_t lo_locked = rx_usrp->get_rx_sensor("lo_locked",0); + std::cout << boost::format("Checking RX: %s ...") % lo_locked.to_pp_string() << std::endl; + UHD_ASSERT_THROW(lo_locked.to_bool()); + } + + tx_sensor_names = tx_usrp->get_mboard_sensor_names(0); + if ((ref == "mimo") and (std::find(tx_sensor_names.begin(), tx_sensor_names.end(), "mimo_locked") != tx_sensor_names.end())) { + uhd::sensor_value_t mimo_locked = tx_usrp->get_mboard_sensor("mimo_locked",0); + std::cout << boost::format("Checking TX: %s ...") % mimo_locked.to_pp_string() << std::endl; + UHD_ASSERT_THROW(mimo_locked.to_bool()); + } + if ((ref == "external") and (std::find(tx_sensor_names.begin(), tx_sensor_names.end(), "ref_locked") != tx_sensor_names.end())) { + uhd::sensor_value_t ref_locked = tx_usrp->get_mboard_sensor("ref_locked",0); + std::cout << boost::format("Checking TX: %s ...") % ref_locked.to_pp_string() << std::endl; + UHD_ASSERT_THROW(ref_locked.to_bool()); + } + + rx_sensor_names = rx_usrp->get_mboard_sensor_names(0); + if ((ref == "mimo") and (std::find(rx_sensor_names.begin(), rx_sensor_names.end(), "mimo_locked") != rx_sensor_names.end())) { + uhd::sensor_value_t mimo_locked = rx_usrp->get_mboard_sensor("mimo_locked",0); + std::cout << boost::format("Checking RX: %s ...") % mimo_locked.to_pp_string() << std::endl; + UHD_ASSERT_THROW(mimo_locked.to_bool()); + } + if ((ref == "external") and (std::find(rx_sensor_names.begin(), rx_sensor_names.end(), "ref_locked") != rx_sensor_names.end())) { + uhd::sensor_value_t ref_locked = rx_usrp->get_mboard_sensor("ref_locked",0); + std::cout << boost::format("Checking RX: %s ...") % ref_locked.to_pp_string() << std::endl; + UHD_ASSERT_THROW(ref_locked.to_bool()); + } + + if (total_num_samps == 0){ + std::signal(SIGINT, &sig_int_handler); + std::cout << "Press Ctrl + C to stop streaming..." << std::endl; + } + + //reset usrp time to prepare for transmit/receive + std::cout << boost::format("Setting device timestamp to 0...") << std::endl; + tx_usrp->set_time_now(uhd::time_spec_t(0.0)); + + //start transmit worker thread + boost::thread_group transmit_thread; + transmit_thread.create_thread(boost::bind(&transmit_worker, buff, wave_table, tx_stream, md, step, index, num_channels)); + + //recv to file + if (type == "double") recv_to_file<std::complex<double> >(rx_usrp, "fc64", otw, file, spb, total_num_samps, settling); + else if (type == "float") recv_to_file<std::complex<float> >(rx_usrp, "fc32", otw, file, spb, total_num_samps, settling); + else if (type == "short") recv_to_file<std::complex<short> >(rx_usrp, "sc16", otw, file, spb, total_num_samps, settling); + else { + //clean up transmit worker + stop_signal_called = true; + transmit_thread.join_all(); + throw std::runtime_error("Unknown type " + type); + } + + //clean up transmit worker + stop_signal_called = true; + transmit_thread.join_all(); + + //finished + std::cout << std::endl << "Done!" << std::endl << std::endl; + return 0; +} diff --git a/host/include/uhd/convert.hpp b/host/include/uhd/convert.hpp index f906ff0e9..c6b005867 100644 --- a/host/include/uhd/convert.hpp +++ b/host/include/uhd/convert.hpp @@ -1,5 +1,5 @@ // -// Copyright 2011 Ettus Research LLC +// Copyright 2011-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 @@ -80,9 +80,13 @@ namespace uhd{ namespace convert{ /*! * Get a converter factory function. * \param id identify the conversion + * \param prio the desired prio or -1 for best * \return the converter factory function */ - UHD_API function_type get_converter(const id_type &id); + UHD_API function_type get_converter( + const id_type &id, + const priority_type prio = -1 + ); /*! * Register the size of a particular item. diff --git a/host/include/uhd/stream.hpp b/host/include/uhd/stream.hpp index 1fb846955..c8ab303ad 100644 --- a/host/include/uhd/stream.hpp +++ b/host/include/uhd/stream.hpp @@ -53,9 +53,9 @@ struct UHD_API stream_args_t{ * - fc64 - complex<double> * - fc32 - complex<float> * - sc16 - complex<int16_t> + * - sc8 - complex<int8_t> * * The following are not implemented, but are listed to demonstrate naming convention: - * - sc8 - complex<int8_t> * - f32 - float * - f64 - double * - s16 - int16_t diff --git a/host/include/uhd/transport/zero_copy.hpp b/host/include/uhd/transport/zero_copy.hpp index f80c738aa..1dc0e8e26 100644 --- a/host/include/uhd/transport/zero_copy.hpp +++ b/host/include/uhd/transport/zero_copy.hpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-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 @@ -22,23 +22,14 @@ #include <boost/utility.hpp> #include <boost/shared_ptr.hpp> #include <boost/intrusive_ptr.hpp> +#include <boost/detail/atomic_count.hpp> namespace uhd{ namespace transport{ - //! Create smart pointer to a reusable managed buffer - template <typename T> UHD_INLINE boost::intrusive_ptr<T> make_managed_buffer(T *p){ - p->_ref_count = 1; //reset the count to 1 reference - return boost::intrusive_ptr<T>(p, false); - } - - /*! - * A managed receive buffer: - * Contains a reference to transport-managed memory, - * and a method to release the memory after reading. - */ - class UHD_API managed_recv_buffer{ + //! Simple managed buffer with release interface + class UHD_API managed_buffer{ public: - typedef boost::intrusive_ptr<managed_recv_buffer> sptr; + managed_buffer(void):_ref_count(0){} /*! * Signal to the transport that we are done with the buffer. @@ -48,84 +39,73 @@ namespace uhd{ namespace transport{ virtual void release(void) = 0; /*! + * Use commit() to re-write the length (for use with send buffers). + * \param num_bytes the number of bytes written into the buffer + */ + UHD_INLINE void commit(size_t num_bytes){ + _length = num_bytes; + } + + /*! * Get a pointer to the underlying buffer. * \return a pointer into memory */ - template <class T> inline T cast(void) const{ - return static_cast<T>(this->get_buff()); + template <class T> UHD_INLINE T cast(void) const{ + return static_cast<T>(_buffer); } /*! * Get the size of the underlying buffer. * \return the number of bytes */ - inline size_t size(void) const{ - return this->get_size(); + UHD_INLINE size_t size(void) const{ + return _length; } - private: - virtual const void *get_buff(void) const = 0; - virtual size_t get_size(void) const = 0; + //! Create smart pointer to a reusable managed buffer + template <typename T> UHD_INLINE boost::intrusive_ptr<T> make( + T *p, void *buffer, size_t length + ){ + _buffer = buffer; + _length = length; + return boost::intrusive_ptr<T>(p); + } + + boost::detail::atomic_count _ref_count; - public: int _ref_count; + protected: + void *_buffer; + size_t _length; }; - UHD_INLINE void intrusive_ptr_add_ref(managed_recv_buffer *p){ + UHD_INLINE void intrusive_ptr_add_ref(managed_buffer *p){ ++(p->_ref_count); } - UHD_INLINE void intrusive_ptr_release(managed_recv_buffer *p){ + UHD_INLINE void intrusive_ptr_release(managed_buffer *p){ if (--(p->_ref_count) == 0) p->release(); } /*! + * A managed receive buffer: + * Contains a reference to transport-managed memory, + * and a method to release the memory after reading. + */ + class UHD_API managed_recv_buffer : public managed_buffer{ + public: + typedef boost::intrusive_ptr<managed_recv_buffer> sptr; + }; + + /*! * A managed send buffer: * Contains a reference to transport-managed memory, * and a method to commit the memory after writing. */ - class UHD_API managed_send_buffer{ + class UHD_API managed_send_buffer : public managed_buffer{ public: typedef boost::intrusive_ptr<managed_send_buffer> sptr; - - /*! - * Signal to the transport that we are done with the buffer. - * This should be called to commit the write to the transport object. - * After calling, the referenced memory should be considered invalid. - * \param num_bytes the number of bytes written into the buffer - */ - virtual void commit(size_t num_bytes) = 0; - - /*! - * Get a pointer to the underlying buffer. - * \return a pointer into memory - */ - template <class T> inline T cast(void) const{ - return static_cast<T>(this->get_buff()); - } - - /*! - * Get the size of the underlying buffer. - * \return the number of bytes - */ - inline size_t size(void) const{ - return this->get_size(); - } - - private: - virtual void *get_buff(void) const = 0; - virtual size_t get_size(void) const = 0; - - public: int _ref_count; }; - UHD_INLINE void intrusive_ptr_add_ref(managed_send_buffer *p){ - ++(p->_ref_count); - } - - UHD_INLINE void intrusive_ptr_release(managed_send_buffer *p){ - if (--(p->_ref_count) == 0) p->commit(0); - } - /*! * A zero-copy interface for transport objects. * Provides a way to get send and receive buffers diff --git a/host/include/uhd/types/ranges.hpp b/host/include/uhd/types/ranges.hpp index f0d0e1c0b..ac632df93 100644 --- a/host/include/uhd/types/ranges.hpp +++ b/host/include/uhd/types/ranges.hpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-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 @@ -19,7 +19,6 @@ #define INCLUDED_UHD_TYPES_RANGES_HPP #include <uhd/config.hpp> -#include <uhd/utils/pimpl.hpp> #include <string> #include <vector> @@ -60,7 +59,7 @@ namespace uhd{ //! Convert this range to a printable string const std::string to_pp_string(void) const; - private: UHD_PIMPL_DECL(impl) _impl; + private: double _start, _stop, _step; }; /*! diff --git a/host/include/uhd/usrp/mboard_eeprom.hpp b/host/include/uhd/usrp/mboard_eeprom.hpp index c47f894be..f064e9956 100644 --- a/host/include/uhd/usrp/mboard_eeprom.hpp +++ b/host/include/uhd/usrp/mboard_eeprom.hpp @@ -1,5 +1,5 @@ // -// Copyright 2010 Ettus Research LLC +// Copyright 2010-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 @@ -34,30 +34,22 @@ namespace uhd{ namespace usrp{ */ struct UHD_API mboard_eeprom_t : uhd::dict<std::string, std::string>{ - //! Possible EEPROM maps types - enum map_type{ - MAP_N100, - MAP_B000, - MAP_B100, - MAP_E100 - }; - //! Make a new empty mboard eeprom mboard_eeprom_t(void); /*! * Make a new mboard EEPROM handler. * \param iface the interface to i2c - * \param map the map type enum + * \param which which EEPROM map to use */ - mboard_eeprom_t(i2c_iface &iface, map_type map); + mboard_eeprom_t(i2c_iface &iface, const std::string &which); /*! * Write the contents of this object to the EEPROM. * \param iface the interface to i2c - * \param map the map type enum + * \param which which EEPROM map to use */ - void commit(i2c_iface &iface, map_type map) const; + void commit(i2c_iface &iface, const std::string &which) const; }; diff --git a/host/include/uhd/usrp/multi_usrp.hpp b/host/include/uhd/usrp/multi_usrp.hpp index 88affa40c..2e83823ba 100644 --- a/host/include/uhd/usrp/multi_usrp.hpp +++ b/host/include/uhd/usrp/multi_usrp.hpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-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 @@ -25,6 +25,7 @@ #define UHD_USRP_MULTI_USRP_COMMAND_TIME_API #define UHD_USRP_MULTI_USRP_BW_RANGE_API #define UHD_USRP_MULTI_USRP_USER_REGS_API +#define UHD_USRP_MULTI_USRP_GET_USRP_INFO_API #include <uhd/config.hpp> #include <uhd/device.hpp> @@ -127,6 +128,24 @@ public: return this->get_device()->get_tx_stream(args); } + /*! + * Returns identifying information about this USRP's configuration. + * Returns motherboard ID, name, and serial. + * Returns daughterboard RX ID, subdev name and spec, serial, and antenna. + * \param chan channel index 0 to N-1 + * \return RX info + */ + virtual dict<std::string, std::string> get_usrp_rx_info(size_t chan = 0) = 0; + + /*! + * Returns identifying information about this USRP's configuration. + * Returns motherboard ID, name, and serial. + * Returns daughterboard TX ID, subdev name and spec, serial, and antenna. + * \param chan channel index 0 to N-1 + * \return TX info + */ + virtual dict<std::string, std::string> get_usrp_tx_info(size_t chan = 0) = 0; + /******************************************************************* * Mboard methods ******************************************************************/ @@ -428,6 +447,13 @@ public: virtual freq_range_t get_rx_freq_range(size_t chan = 0) = 0; /*! + * Get the center frequency range of the RF frontend. + * \param chan the channel index 0 to N-1 + * \return a frequency range object + */ + virtual freq_range_t get_fe_rx_freq_range(size_t chan = 0) = 0; + + /*! * Set the RX gain value for the specified gain element. * For an empty name, distribute across all gain elements. * \param gain the gain in dB @@ -674,6 +700,13 @@ public: virtual freq_range_t get_tx_freq_range(size_t chan = 0) = 0; /*! + * Get the center frequency range of the TX frontend. + * \param chan the channel index 0 to N-1 + * \return a frequency range object + */ + virtual freq_range_t get_fe_tx_freq_range(size_t chan = 0) = 0; + + /*! * Set the TX gain value for the specified gain element. * For an empty name, distribute across all gain elements. * \param gain the gain in dB diff --git a/host/include/uhd/utils/CMakeLists.txt b/host/include/uhd/utils/CMakeLists.txt index 5a434fd9a..de91993fe 100644 --- a/host/include/uhd/utils/CMakeLists.txt +++ b/host/include/uhd/utils/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2010-2011 Ettus Research LLC +# Copyright 2010-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 @@ -19,6 +19,7 @@ INSTALL(FILES algorithm.hpp assert_has.hpp assert_has.ipp + atomic.hpp byteswap.hpp byteswap.ipp csv.hpp diff --git a/host/include/uhd/utils/atomic.hpp b/host/include/uhd/utils/atomic.hpp new file mode 100644 index 000000000..7a81d8d5e --- /dev/null +++ b/host/include/uhd/utils/atomic.hpp @@ -0,0 +1,161 @@ +// +// 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/>. +// + +#ifndef INCLUDED_UHD_UTILS_ATOMIC_HPP +#define INCLUDED_UHD_UTILS_ATOMIC_HPP + +#include <uhd/config.hpp> +#include <uhd/types/time_spec.hpp> +#include <boost/thread/thread.hpp> +#include <boost/interprocess/detail/atomic.hpp> + +#include <boost/version.hpp> +#if BOOST_VERSION >= 104800 +# define BOOST_IPC_DETAIL boost::interprocess::ipcdetail +#else +# define BOOST_IPC_DETAIL boost::interprocess::detail +#endif + +namespace uhd{ + + //! A 32-bit integer that can be atomically accessed + class UHD_API atomic_uint32_t{ + public: + + //! Create a new atomic 32-bit integer, initialized to zero + UHD_INLINE atomic_uint32_t(void){ + this->write(0); + } + + //! Compare with cmp, swap with newval if same, return old value + UHD_INLINE boost::uint32_t cas(boost::uint32_t newval, boost::uint32_t cmp){ + return BOOST_IPC_DETAIL::atomic_cas32(&_num, newval, cmp); + } + + //! Sets the atomic integer to a new value + UHD_INLINE void write(const boost::uint32_t newval){ + BOOST_IPC_DETAIL::atomic_write32(&_num, newval); + } + + //! Gets the current value of the atomic integer + UHD_INLINE boost::uint32_t read(void){ + return BOOST_IPC_DETAIL::atomic_read32(&_num); + } + + //! Increment by 1 and return the old value + UHD_INLINE boost::uint32_t inc(void){ + return BOOST_IPC_DETAIL::atomic_inc32(&_num); + } + + //! Decrement by 1 and return the old value + UHD_INLINE boost::uint32_t dec(void){ + return BOOST_IPC_DETAIL::atomic_dec32(&_num); + } + + private: volatile boost::uint32_t _num; + }; + + /*! + * A reusable barrier to sync multiple threads. + * All threads spin on wait() until count is reset. + */ + class UHD_API reusable_barrier{ + public: + + //! Resize the barrier for N threads + void resize(const size_t size){ + _size = size; + _count.write(size); + } + + /*! + * Force the barrier wait to throw a boost::thread_interrupted + * The threads were not getting the interruption_point on windows. + */ + void interrupt(void) + { + _count.write(boost::uint32_t(~0)); + } + + //! Wait on the barrier condition + UHD_INLINE void wait(void){ + _count.dec(); + _count.cas(_size, 0); + while (_count.read() != _size){ + boost::this_thread::interruption_point(); + if (_count.read() == boost::uint32_t(~0)) + throw boost::thread_interrupted(); + boost::this_thread::yield(); + } + } + + private: + size_t _size; + atomic_uint32_t _count; + }; + + /*! + * Spin-wait on a condition with a timeout. + * \param cond an atomic variable to compare + * \param value compare to atomic for true/false + * \param timeout the timeout in seconds + * \return true for cond == value, false for timeout + */ + UHD_INLINE bool spin_wait_with_timeout( + atomic_uint32_t &cond, + boost::uint32_t value, + const double timeout + ){ + if (cond.read() == value) return true; + const time_spec_t exit_time = time_spec_t::get_system_time() + time_spec_t(timeout); + while (cond.read() != value){ + if (time_spec_t::get_system_time() > exit_time) return false; + boost::this_thread::interruption_point(); + boost::this_thread::yield(); + } + return true; + } + + /*! + * Claimer class to provide synchronization for multi-thread access. + * Claiming enables buffer classes to be used with a buffer queue. + */ + class simple_claimer{ + public: + simple_claimer(void){ + this->release(); + } + + UHD_INLINE void release(void){ + _locked.write(0); + } + + UHD_INLINE bool claim_with_wait(const double timeout){ + if (spin_wait_with_timeout(_locked, 0, timeout)){ + _locked.write(1); + return true; + } + return false; + } + + private: + atomic_uint32_t _locked; + }; + +} //namespace uhd + +#endif /* INCLUDED_UHD_UTILS_ATOMIC_HPP */ diff --git a/host/include/uhd/utils/images.hpp b/host/include/uhd/utils/images.hpp index 8b5a1eedd..a0934fb08 100644 --- a/host/include/uhd/utils/images.hpp +++ b/host/include/uhd/utils/images.hpp @@ -1,5 +1,5 @@ // -// Copyright 2010 Ettus Research LLC +// Copyright 2010,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 @@ -33,6 +33,21 @@ namespace uhd{ */ UHD_API std::string find_image_path(const std::string &image_name); + /*! + * Search for the location of the UHD Images Downloader script. + * \return the full system path to uhd_images_downloader.py + */ + + UHD_API std::string find_images_downloader(void); + + /*! + * Return the error string for recommending using the UHD Images Downloader. + * String depends on OS. + * \return the message suggesting the use of uhd_images_downloader.py + */ + + UHD_API std::string print_images_error(void); + } //namespace uhd #endif /* INCLUDED_UHD_UTILS_IMAGES_HPP */ diff --git a/host/include/uhd/utils/paths.hpp b/host/include/uhd/utils/paths.hpp index 2261f1b77..f5a40b2c9 100644 --- a/host/include/uhd/utils/paths.hpp +++ b/host/include/uhd/utils/paths.hpp @@ -1,5 +1,5 @@ // -// Copyright 2011 Ettus Research LLC +// Copyright 2011-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 @@ -29,6 +29,9 @@ namespace uhd{ //! Get a string representing the system's appdata directory UHD_API std::string get_app_path(void); + //! Get a string representing the system's pkg data directory + UHD_API std::string get_pkg_data_path(void); + } //namespace uhd #endif /* INCLUDED_UHD_UTILS_PATHS_HPP */ diff --git a/host/include/uhd/version.hpp b/host/include/uhd/version.hpp index 9a7226323..ab693f2b0 100644 --- a/host/include/uhd/version.hpp +++ b/host/include/uhd/version.hpp @@ -27,7 +27,7 @@ * The format is oldest API compatible release - ABI compat number. * The compatibility number allows pre-release ABI to be versioned. */ -#define UHD_VERSION_ABI_STRING "3.4.0-0" +#define UHD_VERSION_ABI_STRING "3.4.0-3" namespace uhd{ diff --git a/host/lib/convert/CMakeLists.txt b/host/lib/convert/CMakeLists.txt index c42a0a434..0d9d0983f 100644 --- a/host/lib/convert/CMakeLists.txt +++ b/host/lib/convert/CMakeLists.txt @@ -71,10 +71,14 @@ UNSET(CMAKE_REQUIRED_FLAGS) IF(HAVE_EMMINTRIN_H) SET(convert_with_sse2_sources - ${CMAKE_CURRENT_SOURCE_DIR}/convert_fc32_with_sse2.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/convert_fc64_with_sse2.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/convert_fc32_to_sc8_with_sse2.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/convert_fc64_to_sc8_with_sse2.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/sse2_sc16_to_fc64.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/sse2_sc16_to_fc32.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/sse2_sc8_to_fc64.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/sse2_sc8_to_fc32.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/sse2_fc64_to_sc16.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/sse2_fc32_to_sc16.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/sse2_fc64_to_sc8.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/sse2_fc32_to_sc8.cpp ) SET_SOURCE_FILES_PROPERTIES( ${convert_with_sse2_sources} @@ -117,4 +121,5 @@ LIBUHD_PYTHON_GEN_SOURCE( LIBUHD_APPEND_SOURCES( ${CMAKE_CURRENT_SOURCE_DIR}/convert_with_tables.cpp ${CMAKE_CURRENT_SOURCE_DIR}/convert_impl.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/convert_item32.cpp ) diff --git a/host/lib/convert/convert_common.hpp b/host/lib/convert/convert_common.hpp index 7626e4d87..933978a8f 100644 --- a/host/lib/convert/convert_common.hpp +++ b/host/lib/convert/convert_common.hpp @@ -53,12 +53,12 @@ static const int PRIORITY_EMPTY = -1; #ifdef __ARM_NEON__ static const int PRIORITY_LIBORC = 3; -static const int PRIORITY_SIMD = 1; //neon conversions could be implemented better, orc wins -static const int PRIORITY_TABLE = 2; //tables require large cache, so they are slower on arm +static const int PRIORITY_SIMD = 2; //neon conversions could be implemented better, orc wins +static const int PRIORITY_TABLE = 1; //tables require large cache, so they are slower on arm #else -static const int PRIORITY_LIBORC = 1; +static const int PRIORITY_LIBORC = 2; static const int PRIORITY_SIMD = 3; -static const int PRIORITY_TABLE = 2; +static const int PRIORITY_TABLE = 1; #endif /*********************************************************************** @@ -77,123 +77,92 @@ typedef boost::int8_t s8_t; typedef boost::uint32_t item32_t; -/*********************************************************************** - * Convert complex short buffer to items32 sc16 - **********************************************************************/ -static UHD_INLINE item32_t sc16_to_item32_sc16(sc16_t num, double){ - boost::uint16_t real = num.real(); - boost::uint16_t imag = num.imag(); - return (item32_t(real) << 16) | (item32_t(imag) << 0); -} +typedef item32_t (*xtox_t)(item32_t); /*********************************************************************** - * Convert items32 sc16 buffer to complex short + * Convert xx to items32 sc16 buffer **********************************************************************/ -static UHD_INLINE sc16_t item32_sc16_to_sc16(item32_t item, double){ - return sc16_t( - boost::int16_t(item >> 16), - boost::int16_t(item >> 0) - ); -} - -/*********************************************************************** - * Convert complex float buffer to items32 sc16 - **********************************************************************/ -static UHD_INLINE item32_t fc32_to_item32_sc16(fc32_t num, double scale_factor){ +template <typename T> UHD_INLINE item32_t xx_to_item32_sc16_x1( + const std::complex<T> &num, const double scale_factor +){ boost::uint16_t real = boost::int16_t(num.real()*float(scale_factor)); boost::uint16_t imag = boost::int16_t(num.imag()*float(scale_factor)); return (item32_t(real) << 16) | (item32_t(imag) << 0); } -/*********************************************************************** - * Convert items32 sc16 buffer to complex float - **********************************************************************/ -static UHD_INLINE fc32_t item32_sc16_to_fc32(item32_t item, double scale_factor){ - return fc32_t( - float(boost::int16_t(item >> 16)*float(scale_factor)), - float(boost::int16_t(item >> 0)*float(scale_factor)) - ); -} - -/*********************************************************************** - * Convert complex double buffer to items32 sc16 - **********************************************************************/ -static UHD_INLINE item32_t fc64_to_item32_sc16(fc64_t num, double scale_factor){ - boost::uint16_t real = boost::int16_t(num.real()*scale_factor); - boost::uint16_t imag = boost::int16_t(num.imag()*scale_factor); +template <> UHD_INLINE item32_t xx_to_item32_sc16_x1( + const sc16_t &num, const double +){ + boost::uint16_t real = boost::int16_t(num.real()); + boost::uint16_t imag = boost::int16_t(num.imag()); return (item32_t(real) << 16) | (item32_t(imag) << 0); } -/*********************************************************************** - * Convert items32 sc16 buffer to complex double - **********************************************************************/ -static UHD_INLINE fc64_t item32_sc16_to_fc64(item32_t item, double scale_factor){ - return fc64_t( - float(boost::int16_t(item >> 16)*scale_factor), - float(boost::int16_t(item >> 0)*scale_factor) - ); +template <xtox_t to_wire, typename T> +UHD_INLINE void xx_to_item32_sc16( + const std::complex<T> *input, + item32_t *output, + const size_t nsamps, + const double scale_factor +){ + for (size_t i = 0; i < nsamps; i++){ + const item32_t item = xx_to_item32_sc16_x1(input[i], scale_factor); + output[i] = to_wire(item); + } } /*********************************************************************** - * Convert items32 sc8 buffer to complex char + * Convert items32 sc16 buffer to xx **********************************************************************/ -static UHD_INLINE void item32_sc8_to_sc8(item32_t item, sc8_t &out0, sc8_t &out1, double){ - out0 = sc8_t( - boost::int8_t(item >> 8), - boost::int8_t(item >> 0) - ); - out1 = sc8_t( - boost::int8_t(item >> 24), - boost::int8_t(item >> 16) +template <typename T> UHD_INLINE std::complex<T> item32_sc16_x1_to_xx( + const item32_t item, const double scale_factor +){ + return std::complex<T>( + T(boost::int16_t(item >> 16)*float(scale_factor)), + T(boost::int16_t(item >> 0)*float(scale_factor)) ); } -/*********************************************************************** - * Convert items32 sc8 buffer to complex short - **********************************************************************/ -static UHD_INLINE void item32_sc8_to_sc16(item32_t item, sc16_t &out0, sc16_t &out1, double){ - out0 = sc16_t( - boost::int8_t(item >> 8), - boost::int8_t(item >> 0) - ); - out1 = sc16_t( - boost::int8_t(item >> 24), - boost::int8_t(item >> 16) +template <> UHD_INLINE sc16_t item32_sc16_x1_to_xx( + const item32_t item, const double +){ + return sc16_t( + boost::int16_t(item >> 16), boost::int16_t(item >> 0) ); } -/*********************************************************************** - * Convert items32 sc8 buffer to complex float - **********************************************************************/ -static UHD_INLINE void item32_sc8_to_fc32(item32_t item, fc32_t &out0, fc32_t &out1, double scale_factor){ - out0 = fc32_t( - float(boost::int8_t(item >> 8)*float(scale_factor)), - float(boost::int8_t(item >> 0)*float(scale_factor)) - ); - out1 = fc32_t( - float(boost::int8_t(item >> 24)*float(scale_factor)), - float(boost::int8_t(item >> 16)*float(scale_factor)) - ); +template <xtox_t to_host, typename T> +UHD_INLINE void item32_sc16_to_xx( + const item32_t *input, + std::complex<T> *output, + const size_t nsamps, + const double scale_factor +){ + for (size_t i = 0; i < nsamps; i++){ + const item32_t item_i = to_host(input[i]); + output[i] = item32_sc16_x1_to_xx<T>(item_i, scale_factor); + } } /*********************************************************************** - * Convert items32 sc8 buffer to complex double + * Convert xx to items32 sc8 buffer **********************************************************************/ -static UHD_INLINE void item32_sc8_to_fc64(item32_t item, fc64_t &out0, fc64_t &out1, double scale_factor){ - out0 = fc64_t( - float(boost::int8_t(item >> 8)*scale_factor), - float(boost::int8_t(item >> 0)*scale_factor) - ); - out1 = fc64_t( - float(boost::int8_t(item >> 24)*scale_factor), - float(boost::int8_t(item >> 16)*scale_factor) - ); +template <typename T> UHD_INLINE item32_t xx_to_item32_sc8_x1( + const std::complex<T> &in0, const std::complex<T> &in1, const double scale_factor +){ + boost::uint8_t real0 = boost::int8_t(in0.real()*float(scale_factor)); + boost::uint8_t imag0 = boost::int8_t(in0.imag()*float(scale_factor)); + boost::uint8_t real1 = boost::int8_t(in1.real()*float(scale_factor)); + boost::uint8_t imag1 = boost::int8_t(in1.imag()*float(scale_factor)); + return + (item32_t(real0) << 8) | (item32_t(imag0) << 0) | + (item32_t(real1) << 24) | (item32_t(imag1) << 16) + ; } -/*********************************************************************** - * Convert complex char to items32 sc8 buffer - **********************************************************************/ -static UHD_INLINE item32_t sc8_to_item32_sc8(sc8_t in0, sc8_t in1, double){ +template <> UHD_INLINE item32_t xx_to_item32_sc8_x1( + const sc16_t &in0, const sc16_t &in1, const double +){ boost::uint8_t real0 = boost::int8_t(in0.real()); boost::uint8_t imag0 = boost::int8_t(in0.imag()); boost::uint8_t real1 = boost::int8_t(in1.real()); @@ -204,10 +173,9 @@ static UHD_INLINE item32_t sc8_to_item32_sc8(sc8_t in0, sc8_t in1, double){ ; } -/*********************************************************************** - * Convert complex short to items32 sc8 buffer - **********************************************************************/ -static UHD_INLINE item32_t sc16_to_item32_sc8(sc16_t in0, sc16_t in1, double){ +template <> UHD_INLINE item32_t xx_to_item32_sc8_x1( + const sc8_t &in0, const sc8_t &in1, const double +){ boost::uint8_t real0 = boost::int8_t(in0.real()); boost::uint8_t imag0 = boost::int8_t(in0.imag()); boost::uint8_t real1 = boost::int8_t(in1.real()); @@ -218,32 +186,94 @@ static UHD_INLINE item32_t sc16_to_item32_sc8(sc16_t in0, sc16_t in1, double){ ; } -/*********************************************************************** - * Convert complex float to items32 sc8 buffer - **********************************************************************/ -static UHD_INLINE item32_t fc32_to_item32_sc8(fc32_t in0, fc32_t in1, double scale_factor){ - boost::uint8_t real0 = boost::int8_t(in0.real()*float(scale_factor)); - boost::uint8_t imag0 = boost::int8_t(in0.imag()*float(scale_factor)); - boost::uint8_t real1 = boost::int8_t(in1.real()*float(scale_factor)); - boost::uint8_t imag1 = boost::int8_t(in1.imag()*float(scale_factor)); - return - (item32_t(real0) << 8) | (item32_t(imag0) << 0) | - (item32_t(real1) << 24) | (item32_t(imag1) << 16) - ; +template <xtox_t to_wire, typename T> +UHD_INLINE void xx_to_item32_sc8( + const std::complex<T> *input, + item32_t *output, + const size_t nsamps, + const double scale_factor +){ + const size_t num_pairs = nsamps/2; + for (size_t i = 0, j = 0; i < num_pairs; i++, j+=2){ + const item32_t item = xx_to_item32_sc8_x1(input[j], input[j+1], scale_factor); + output[i] = to_wire(item); + } + + if (nsamps != num_pairs*2){ + const item32_t item = xx_to_item32_sc8_x1(input[nsamps-1], std::complex<T>(0), scale_factor); + output[num_pairs] = to_wire(item); + } } /*********************************************************************** - * Convert complex double to items32 sc8 buffer + * Convert items32 sc8 buffer to xx **********************************************************************/ -static UHD_INLINE item32_t fc64_to_item32_sc8(fc64_t in0, fc64_t in1, double scale_factor){ - boost::uint8_t real0 = boost::int8_t(in0.real()*(scale_factor)); - boost::uint8_t imag0 = boost::int8_t(in0.imag()*(scale_factor)); - boost::uint8_t real1 = boost::int8_t(in1.real()*(scale_factor)); - boost::uint8_t imag1 = boost::int8_t(in1.imag()*(scale_factor)); - return - (item32_t(real0) << 8) | (item32_t(imag0) << 0) | - (item32_t(real1) << 24) | (item32_t(imag1) << 16) - ; +template <typename T> UHD_INLINE void item32_sc8_x1_to_xx( + const item32_t item, std::complex<T> &out0, std::complex<T> &out1, const double scale_factor +){ + out0 = std::complex<T>( + T(boost::int8_t(item >> 8)*float(scale_factor)), + T(boost::int8_t(item >> 0)*float(scale_factor)) + ); + out1 = std::complex<T>( + T(boost::int8_t(item >> 24)*float(scale_factor)), + T(boost::int8_t(item >> 16)*float(scale_factor)) + ); +} + +template <> UHD_INLINE void item32_sc8_x1_to_xx( + const item32_t item, sc16_t &out0, sc16_t &out1, const double +){ + out0 = sc16_t( + boost::int16_t(boost::int8_t(item >> 8)), + boost::int16_t(boost::int8_t(item >> 0)) + ); + out1 = sc16_t( + boost::int16_t(boost::int8_t(item >> 24)), + boost::int16_t(boost::int8_t(item >> 16)) + ); +} + +template <> UHD_INLINE void item32_sc8_x1_to_xx( + const item32_t item, sc8_t &out0, sc8_t &out1, const double +){ + out0 = sc8_t( + boost::int8_t(boost::int8_t(item >> 8)), + boost::int8_t(boost::int8_t(item >> 0)) + ); + out1 = sc8_t( + boost::int8_t(boost::int8_t(item >> 24)), + boost::int8_t(boost::int8_t(item >> 16)) + ); +} + +template <xtox_t to_host, typename T> +UHD_INLINE void item32_sc8_to_xx( + const item32_t *input, + std::complex<T> *output, + const size_t nsamps, + const double scale_factor +){ + input = reinterpret_cast<const item32_t *>(size_t(input) & ~0x3); + std::complex<T> dummy; + size_t num_samps = nsamps; + + if ((size_t(input) & 0x3) != 0){ + const item32_t item0 = to_host(*input++); + item32_sc8_x1_to_xx(item0, dummy, *output++, scale_factor); + num_samps--; + } + + const size_t num_pairs = num_samps/2; + for (size_t i = 0, j = 0; i < num_pairs; i++, j+=2){ + const item32_t item_i = to_host(input[i]); + item32_sc8_x1_to_xx(item_i, output[j], output[j+1], scale_factor); + } + + if (num_samps != num_pairs*2){ + const item32_t item_n = to_host(input[num_pairs]); + item32_sc8_x1_to_xx(item_n, output[num_samps-1], dummy, scale_factor); + } } #endif /* INCLUDED_LIBUHD_CONVERT_COMMON_HPP */ diff --git a/host/lib/convert/convert_impl.cpp b/host/lib/convert/convert_impl.cpp index 12ad54486..dc7f8f9dc 100644 --- a/host/lib/convert/convert_impl.cpp +++ b/host/lib/convert/convert_impl.cpp @@ -22,6 +22,7 @@ #include <uhd/exception.hpp> #include <boost/cstdint.hpp> #include <boost/format.hpp> +#include <boost/foreach.hpp> #include <complex> using namespace uhd; @@ -51,17 +52,9 @@ std::string convert::id_type::to_pp_string(void) const{ } /*********************************************************************** - * Define types for the function tables - **********************************************************************/ -struct fcn_table_entry_type{ - convert::priority_type prio; - convert::function_type fcn; -}; - -/*********************************************************************** * Setup the table registry **********************************************************************/ -typedef uhd::dict<convert::id_type, fcn_table_entry_type> fcn_table_type; +typedef uhd::dict<convert::id_type, uhd::dict<convert::priority_type, convert::function_type> > fcn_table_type; UHD_SINGLETON_FCN(fcn_table_type, get_table); /*********************************************************************** @@ -72,14 +65,7 @@ void uhd::convert::register_converter( const function_type &fcn, const priority_type prio ){ - //get a reference to the function table - fcn_table_type &table = get_table(); - - //register the function if higher priority - if (not table.has_key(id) or table[id].prio < prio){ - table[id].fcn = fcn; - table[id].prio = prio; - } + get_table()[id][prio] = fcn; //----------------------------------------------------------------// UHD_LOGV(always) << "register_converter: " << id.to_pp_string() << std::endl @@ -92,9 +78,26 @@ void uhd::convert::register_converter( /*********************************************************************** * The converter functions **********************************************************************/ -convert::function_type convert::get_converter(const id_type &id){ - if (get_table().has_key(id)) return get_table()[id].fcn; - throw uhd::key_error("Cannot find a conversion routine for " + id.to_pp_string()); +convert::function_type convert::get_converter( + const id_type &id, + const priority_type prio +){ + if (not get_table().has_key(id)) throw uhd::key_error( + "Cannot find a conversion routine for " + id.to_pp_string()); + + //find a matching priority + priority_type best_prio = -1; + BOOST_FOREACH(priority_type prio_i, get_table()[id].keys()){ + if (prio_i == prio) return get_table()[id][prio]; + best_prio = std::max(best_prio, prio_i); + } + + //wanted a specific prio, didnt find + if (prio != -1) throw uhd::key_error( + "Cannot find a conversion routine [with prio] for " + id.to_pp_string()); + + //otherwise, return best prio + return get_table()[id][best_prio]; } /*********************************************************************** diff --git a/host/lib/convert/convert_item32.cpp b/host/lib/convert/convert_item32.cpp new file mode 100644 index 000000000..57bd64860 --- /dev/null +++ b/host/lib/convert/convert_item32.cpp @@ -0,0 +1,44 @@ +// +// 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 "convert_common.hpp" +#include <uhd/utils/byteswap.hpp> + +#define __DECLARE_ITEM32_CONVERTER(cpu_type, wire_type, xe, htoxx, xxtoh) \ + DECLARE_CONVERTER(cpu_type, 1, wire_type ## _item32_ ## xe, 1, PRIORITY_GENERAL){ \ + const cpu_type ## _t *input = reinterpret_cast<const cpu_type ## _t *>(inputs[0]); \ + item32_t *output = reinterpret_cast<item32_t *>(outputs[0]); \ + xx_to_item32_ ## wire_type<htoxx>(input, output, nsamps, scale_factor); \ + } \ + DECLARE_CONVERTER(wire_type ## _item32_ ## xe, 1, cpu_type, 1, PRIORITY_GENERAL){ \ + const item32_t *input = reinterpret_cast<const item32_t *>(inputs[0]); \ + cpu_type ## _t *output = reinterpret_cast<cpu_type ## _t *>(outputs[0]); \ + item32_ ## wire_type ## _to_xx<xxtoh>(input, output, nsamps, scale_factor); \ + } + +#define _DECLARE_ITEM32_CONVERTER(cpu_type, wire_type) \ + __DECLARE_ITEM32_CONVERTER(cpu_type, wire_type, be, uhd::htonx, uhd::ntohx) \ + __DECLARE_ITEM32_CONVERTER(cpu_type, wire_type, le, uhd::htowx, uhd::wtohx) + +#define DECLARE_ITEM32_CONVERTER(cpu_type) \ + _DECLARE_ITEM32_CONVERTER(cpu_type, sc8) \ + _DECLARE_ITEM32_CONVERTER(cpu_type, sc16) + +DECLARE_ITEM32_CONVERTER(sc16) +DECLARE_ITEM32_CONVERTER(fc32) +DECLARE_ITEM32_CONVERTER(fc64) +_DECLARE_ITEM32_CONVERTER(sc8, sc8) diff --git a/host/lib/convert/convert_with_neon.cpp b/host/lib/convert/convert_with_neon.cpp index c7ad62104..e994d97a6 100644 --- a/host/lib/convert/convert_with_neon.cpp +++ b/host/lib/convert/convert_with_neon.cpp @@ -1,5 +1,5 @@ // -// Copyright 2011-2011 Ettus Research LLC +// Copyright 2011-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 @@ -16,6 +16,7 @@ // #include "convert_common.hpp" +#include <uhd/utils/byteswap.hpp> #include <arm_neon.h> using namespace uhd::convert; @@ -36,8 +37,7 @@ DECLARE_CONVERTER(fc32, 1, sc16_item32_le, 1, PRIORITY_SIMD){ vst1_s16((reinterpret_cast<int16_t *>(&output[i])), D9); } - for (; i < nsamps; i++) - output[i] = fc32_to_item32_sc16(input[i], scale_factor); + xx_to_item32_sc16<uhd::htowx>(input+i, output+i, nsamps-i, scale_factor); } DECLARE_CONVERTER(sc16_item32_le, 1, fc32, 1, PRIORITY_SIMD){ @@ -56,6 +56,5 @@ DECLARE_CONVERTER(sc16_item32_le, 1, fc32, 1, PRIORITY_SIMD){ vst1q_f32((reinterpret_cast<float *>(&output[i])), Q4); } - for (; i < nsamps; i++) - output[i] = item32_sc16_to_fc32(input[i], scale_factor); + item32_sc16_to_xx<uhd::htowx>(input+i, output+i, nsamps-i, scale_factor); } diff --git a/host/lib/convert/gen_convert_general.py b/host/lib/convert/gen_convert_general.py index 364c4bd1a..b0790755a 100644 --- a/host/lib/convert/gen_convert_general.py +++ b/host/lib/convert/gen_convert_general.py @@ -48,68 +48,6 @@ DECLARE_CONVERTER(sc16_item32_$(end), 1, item32, 1, PRIORITY_GENERAL){ } """ -TMPL_CONV_GEN2_SC16 = """ -DECLARE_CONVERTER($(cpu_type), 1, sc16_item32_$(end), 1, PRIORITY_GENERAL){ - const $(cpu_type)_t *input = reinterpret_cast<const $(cpu_type)_t *>(inputs[0]); - item32_t *output = reinterpret_cast<item32_t *>(outputs[0]); - - for (size_t i = 0; i < nsamps; i++){ - output[i] = $(to_wire)($(cpu_type)_to_item32_sc16(input[i], scale_factor)); - } -} - -DECLARE_CONVERTER(sc16_item32_$(end), 1, $(cpu_type), 1, PRIORITY_GENERAL){ - const item32_t *input = reinterpret_cast<const item32_t *>(inputs[0]); - $(cpu_type)_t *output = reinterpret_cast<$(cpu_type)_t *>(outputs[0]); - - for (size_t i = 0; i < nsamps; i++){ - output[i] = item32_sc16_to_$(cpu_type)($(to_host)(input[i]), scale_factor); - } -} -""" - -TMPL_CONV_GEN2_SC8 = """ -DECLARE_CONVERTER(sc8_item32_$(end), 1, $(cpu_type), 1, PRIORITY_GENERAL){ - const item32_t *input = reinterpret_cast<const item32_t *>(size_t(inputs[0]) & ~0x3); - $(cpu_type)_t *output = reinterpret_cast<$(cpu_type)_t *>(outputs[0]); - $(cpu_type)_t dummy; - size_t num_samps = nsamps; - - if ((size_t(inputs[0]) & 0x3) != 0){ - const item32_t item0 = $(to_host)(*input++); - item32_sc8_to_$(cpu_type)(item0, dummy, *output++, scale_factor); - num_samps--; - } - - const size_t num_pairs = num_samps/2; - for (size_t i = 0, j = 0; i < num_pairs; i++, j+=2){ - const item32_t item_i = $(to_host)(input[i]); - item32_sc8_to_$(cpu_type)(item_i, output[j], output[j+1], scale_factor); - } - - if (num_samps != num_pairs*2){ - const item32_t item_n = $(to_host)(input[num_pairs]); - item32_sc8_to_$(cpu_type)(item_n, output[num_samps-1], dummy, scale_factor); - } -} - -DECLARE_CONVERTER($(cpu_type), 1, sc8_item32_$(end), 1, PRIORITY_GENERAL){ - const $(cpu_type)_t *input = reinterpret_cast<const $(cpu_type)_t *>(inputs[0]); - item32_t *output = reinterpret_cast<item32_t *>(outputs[0]); - - const size_t num_pairs = nsamps/2; - for (size_t i = 0, j = 0; i < num_pairs; i++, j+=2){ - const item32_t item = $(cpu_type)_to_item32_sc8(input[j], input[j+1], scale_factor); - output[i] = $(to_wire)(item); - } - - if (nsamps != num_pairs*2){ - const item32_t item = $(cpu_type)_to_item32_sc8(input[nsamps-1], 0, scale_factor); - output[num_pairs] = $(to_wire)(item); - } -} -""" - TMPL_CONV_USRP1_COMPLEX = """ DECLARE_CONVERTER($(cpu_type), $(width), sc16_item16_usrp1, 1, PRIORITY_GENERAL){ #for $w in range($width) @@ -176,16 +114,6 @@ if __name__ == '__main__': ('be', 'uhd::ntohx', 'uhd::htonx'), ('le', 'uhd::wtohx', 'uhd::htowx'), ): - for cpu_type in 'fc64', 'fc32', 'sc16': - output += parse_tmpl( - TMPL_CONV_GEN2_SC16, - end=end, to_host=to_host, to_wire=to_wire, cpu_type=cpu_type - ) - for cpu_type in 'fc64', 'fc32', 'sc16', 'sc8': - output += parse_tmpl( - TMPL_CONV_GEN2_SC8, - end=end, to_host=to_host, to_wire=to_wire, cpu_type=cpu_type - ) output += parse_tmpl( TMPL_CONV_GEN2_ITEM32, end=end, to_host=to_host, to_wire=to_wire diff --git a/host/lib/convert/sse2_fc32_to_sc16.cpp b/host/lib/convert/sse2_fc32_to_sc16.cpp new file mode 100644 index 000000000..90bf0ed04 --- /dev/null +++ b/host/lib/convert/sse2_fc32_to_sc16.cpp @@ -0,0 +1,103 @@ +// +// Copyright 2011-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 "convert_common.hpp" +#include <uhd/utils/byteswap.hpp> +#include <emmintrin.h> + +using namespace uhd::convert; + +DECLARE_CONVERTER(fc32, 1, sc16_item32_le, 1, PRIORITY_SIMD){ + const fc32_t *input = reinterpret_cast<const fc32_t *>(inputs[0]); + item32_t *output = reinterpret_cast<item32_t *>(outputs[0]); + + const __m128 scalar = _mm_set_ps1(float(scale_factor)); + + #define convert_fc32_1_to_item32_1_nswap_guts(_al_) \ + for (; i+3 < nsamps; i+=4){ \ + /* load from input */ \ + __m128 tmplo = _mm_load ## _al_ ## ps(reinterpret_cast<const float *>(input+i+0)); \ + __m128 tmphi = _mm_load ## _al_ ## ps(reinterpret_cast<const float *>(input+i+2)); \ + \ + /* convert and scale */ \ + __m128i tmpilo = _mm_cvtps_epi32(_mm_mul_ps(tmplo, scalar)); \ + __m128i tmpihi = _mm_cvtps_epi32(_mm_mul_ps(tmphi, scalar)); \ + \ + /* pack + swap 16-bit pairs */ \ + __m128i tmpi = _mm_packs_epi32(tmpilo, tmpihi); \ + tmpi = _mm_shufflelo_epi16(tmpi, _MM_SHUFFLE(2, 3, 0, 1)); \ + tmpi = _mm_shufflehi_epi16(tmpi, _MM_SHUFFLE(2, 3, 0, 1)); \ + \ + /* store to output */ \ + _mm_storeu_si128(reinterpret_cast<__m128i *>(output+i), tmpi); \ + } \ + + size_t i = 0; + + //dispatch according to alignment + switch (size_t(input) & 0xf){ + case 0x8: + xx_to_item32_sc16<uhd::htowx>(input, output, 1, scale_factor); i++; + case 0x0: + convert_fc32_1_to_item32_1_nswap_guts(_) + break; + default: convert_fc32_1_to_item32_1_nswap_guts(u_) + } + + //convert remainder + xx_to_item32_sc16<uhd::htowx>(input+i, output+i, nsamps-i, scale_factor); +} + +DECLARE_CONVERTER(fc32, 1, sc16_item32_be, 1, PRIORITY_SIMD){ + const fc32_t *input = reinterpret_cast<const fc32_t *>(inputs[0]); + item32_t *output = reinterpret_cast<item32_t *>(outputs[0]); + + const __m128 scalar = _mm_set_ps1(float(scale_factor)); + + #define convert_fc32_1_to_item32_1_bswap_guts(_al_) \ + for (; i+3 < nsamps; i+=4){ \ + /* load from input */ \ + __m128 tmplo = _mm_load ## _al_ ## ps(reinterpret_cast<const float *>(input+i+0)); \ + __m128 tmphi = _mm_load ## _al_ ## ps(reinterpret_cast<const float *>(input+i+2)); \ + \ + /* convert and scale */ \ + __m128i tmpilo = _mm_cvtps_epi32(_mm_mul_ps(tmplo, scalar)); \ + __m128i tmpihi = _mm_cvtps_epi32(_mm_mul_ps(tmphi, scalar)); \ + \ + /* pack + byteswap -> byteswap 16 bit words */ \ + __m128i tmpi = _mm_packs_epi32(tmpilo, tmpihi); \ + tmpi = _mm_or_si128(_mm_srli_epi16(tmpi, 8), _mm_slli_epi16(tmpi, 8)); \ + \ + /* store to output */ \ + _mm_storeu_si128(reinterpret_cast<__m128i *>(output+i), tmpi); \ + } \ + + size_t i = 0; + + //dispatch according to alignment + switch (size_t(input) & 0xf){ + case 0x8: + xx_to_item32_sc16<uhd::htonx>(input, output, 1, scale_factor); i++; + case 0x0: + convert_fc32_1_to_item32_1_bswap_guts(_) + break; + default: convert_fc32_1_to_item32_1_bswap_guts(u_) + } + + //convert remainder + xx_to_item32_sc16<uhd::htonx>(input+i, output+i, nsamps-i, scale_factor); +} diff --git a/host/lib/convert/convert_fc32_to_sc8_with_sse2.cpp b/host/lib/convert/sse2_fc32_to_sc8.cpp index b633f487c..dd884640d 100644 --- a/host/lib/convert/convert_fc32_to_sc8_with_sse2.cpp +++ b/host/lib/convert/sse2_fc32_to_sc8.cpp @@ -21,41 +21,22 @@ using namespace uhd::convert; -UHD_INLINE __m128i pack_sc32_4x_be( +template <const int shuf> +UHD_INLINE __m128i pack_sc32_4x( const __m128 &in0, const __m128 &in1, const __m128 &in2, const __m128 &in3, const __m128 &scalar ){ __m128i tmpi0 = _mm_cvtps_epi32(_mm_mul_ps(in0, scalar)); - tmpi0 = _mm_shuffle_epi32(tmpi0, _MM_SHUFFLE(1, 0, 3, 2)); + tmpi0 = _mm_shuffle_epi32(tmpi0, shuf); __m128i tmpi1 = _mm_cvtps_epi32(_mm_mul_ps(in1, scalar)); - tmpi1 = _mm_shuffle_epi32(tmpi1, _MM_SHUFFLE(1, 0, 3, 2)); + tmpi1 = _mm_shuffle_epi32(tmpi1, shuf); const __m128i lo = _mm_packs_epi32(tmpi0, tmpi1); __m128i tmpi2 = _mm_cvtps_epi32(_mm_mul_ps(in2, scalar)); - tmpi2 = _mm_shuffle_epi32(tmpi2, _MM_SHUFFLE(1, 0, 3, 2)); + tmpi2 = _mm_shuffle_epi32(tmpi2, shuf); __m128i tmpi3 = _mm_cvtps_epi32(_mm_mul_ps(in3, scalar)); - tmpi3 = _mm_shuffle_epi32(tmpi3, _MM_SHUFFLE(1, 0, 3, 2)); - const __m128i hi = _mm_packs_epi32(tmpi2, tmpi3); - - return _mm_packs_epi16(lo, hi); -} - -UHD_INLINE __m128i pack_sc32_4x_le( - const __m128 &in0, const __m128 &in1, - const __m128 &in2, const __m128 &in3, - const __m128 &scalar -){ - __m128i tmpi0 = _mm_cvtps_epi32(_mm_mul_ps(in0, scalar)); - tmpi0 = _mm_shuffle_epi32(tmpi0, _MM_SHUFFLE(2, 3, 0, 1)); - __m128i tmpi1 = _mm_cvtps_epi32(_mm_mul_ps(in1, scalar)); - tmpi1 = _mm_shuffle_epi32(tmpi1, _MM_SHUFFLE(2, 3, 0, 1)); - const __m128i lo = _mm_packs_epi32(tmpi0, tmpi1); - - __m128i tmpi2 = _mm_cvtps_epi32(_mm_mul_ps(in2, scalar)); - tmpi2 = _mm_shuffle_epi32(tmpi2, _MM_SHUFFLE(2, 3, 0, 1)); - __m128i tmpi3 = _mm_cvtps_epi32(_mm_mul_ps(in3, scalar)); - tmpi3 = _mm_shuffle_epi32(tmpi3, _MM_SHUFFLE(2, 3, 0, 1)); + tmpi3 = _mm_shuffle_epi32(tmpi3, shuf); const __m128i hi = _mm_packs_epi32(tmpi2, tmpi3); return _mm_packs_epi16(lo, hi); @@ -66,6 +47,7 @@ DECLARE_CONVERTER(fc32, 1, sc8_item32_be, 1, PRIORITY_SIMD){ item32_t *output = reinterpret_cast<item32_t *>(outputs[0]); const __m128 scalar = _mm_set_ps1(float(scale_factor)); + const int shuf = _MM_SHUFFLE(1, 0, 3, 2); #define convert_fc32_1_to_sc8_item32_1_bswap_guts(_al_) \ for (size_t j = 0; i+7 < nsamps; i+=8, j+=4){ \ @@ -76,7 +58,7 @@ DECLARE_CONVERTER(fc32, 1, sc8_item32_be, 1, PRIORITY_SIMD){ __m128 tmp3 = _mm_load ## _al_ ## ps(reinterpret_cast<const float *>(input+i+6)); \ \ /* convert */ \ - const __m128i tmpi = pack_sc32_4x_be(tmp0, tmp1, tmp2, tmp3, scalar); \ + const __m128i tmpi = pack_sc32_4x<shuf>(tmp0, tmp1, tmp2, tmp3, scalar); \ \ /* store to output */ \ _mm_storeu_si128(reinterpret_cast<__m128i *>(output+j), tmpi); \ @@ -93,16 +75,7 @@ DECLARE_CONVERTER(fc32, 1, sc8_item32_be, 1, PRIORITY_SIMD){ } //convert remainder - const size_t num_pairs = nsamps/2; - for (size_t j = i/2; j < num_pairs; j++, i+=2){ - const item32_t item = fc32_to_item32_sc8(input[i], input[i+1], scale_factor); - output[j] = uhd::byteswap(item); - } - - if (nsamps != num_pairs*2){ - const item32_t item = fc32_to_item32_sc8(input[nsamps-1], 0, scale_factor); - output[num_pairs] = uhd::byteswap(item); - } + xx_to_item32_sc8<uhd::htonx>(input+i, output+(i/2), nsamps-i, scale_factor); } DECLARE_CONVERTER(fc32, 1, sc8_item32_le, 1, PRIORITY_SIMD){ @@ -110,6 +83,7 @@ DECLARE_CONVERTER(fc32, 1, sc8_item32_le, 1, PRIORITY_SIMD){ item32_t *output = reinterpret_cast<item32_t *>(outputs[0]); const __m128 scalar = _mm_set_ps1(float(scale_factor)); + const int shuf = _MM_SHUFFLE(2, 3, 0, 1); #define convert_fc32_1_to_sc8_item32_1_nswap_guts(_al_) \ for (size_t j = 0; i+7 < nsamps; i+=8, j+=4){ \ @@ -120,7 +94,7 @@ DECLARE_CONVERTER(fc32, 1, sc8_item32_le, 1, PRIORITY_SIMD){ __m128 tmp3 = _mm_load ## _al_ ## ps(reinterpret_cast<const float *>(input+i+6)); \ \ /* convert */ \ - const __m128i tmpi = pack_sc32_4x_le(tmp0, tmp1, tmp2, tmp3, scalar); \ + const __m128i tmpi = pack_sc32_4x<shuf>(tmp0, tmp1, tmp2, tmp3, scalar); \ \ /* store to output */ \ _mm_storeu_si128(reinterpret_cast<__m128i *>(output+j), tmpi); \ @@ -137,14 +111,5 @@ DECLARE_CONVERTER(fc32, 1, sc8_item32_le, 1, PRIORITY_SIMD){ } //convert remainder - const size_t num_pairs = nsamps/2; - for (size_t j = i/2; j < num_pairs; j++, i+=2){ - const item32_t item = fc32_to_item32_sc8(input[i], input[i+1], scale_factor); - output[j] = (item); - } - - if (nsamps != num_pairs*2){ - const item32_t item = fc32_to_item32_sc8(input[nsamps-1], 0, scale_factor); - output[num_pairs] = (item); - } + xx_to_item32_sc8<uhd::htowx>(input+i, output+(i/2), nsamps-i, scale_factor); } diff --git a/host/lib/convert/sse2_fc64_to_sc16.cpp b/host/lib/convert/sse2_fc64_to_sc16.cpp new file mode 100644 index 000000000..f030e9168 --- /dev/null +++ b/host/lib/convert/sse2_fc64_to_sc16.cpp @@ -0,0 +1,111 @@ +// +// Copyright 2011-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 "convert_common.hpp" +#include <uhd/utils/byteswap.hpp> +#include <emmintrin.h> + +using namespace uhd::convert; + +DECLARE_CONVERTER(fc64, 1, sc16_item32_le, 1, PRIORITY_SIMD){ + const fc64_t *input = reinterpret_cast<const fc64_t *>(inputs[0]); + item32_t *output = reinterpret_cast<item32_t *>(outputs[0]); + + const __m128d scalar = _mm_set1_pd(scale_factor); + + #define convert_fc64_1_to_item32_1_nswap_guts(_al_) \ + for (; i+3 < nsamps; i+=4){ \ + /* load from input */ \ + __m128d tmp0 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+0)); \ + __m128d tmp1 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+1)); \ + __m128d tmp2 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+2)); \ + __m128d tmp3 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+3)); \ + \ + /* convert and scale */ \ + __m128i tmpi0 = _mm_cvttpd_epi32(_mm_mul_pd(tmp0, scalar)); \ + __m128i tmpi1 = _mm_cvttpd_epi32(_mm_mul_pd(tmp1, scalar)); \ + __m128i tmpilo = _mm_unpacklo_epi64(tmpi0, tmpi1); \ + __m128i tmpi2 = _mm_cvttpd_epi32(_mm_mul_pd(tmp2, scalar)); \ + __m128i tmpi3 = _mm_cvttpd_epi32(_mm_mul_pd(tmp3, scalar)); \ + __m128i tmpihi = _mm_unpacklo_epi64(tmpi2, tmpi3); \ + \ + /* pack + swap 16-bit pairs */ \ + __m128i tmpi = _mm_packs_epi32(tmpilo, tmpihi); \ + tmpi = _mm_shufflelo_epi16(tmpi, _MM_SHUFFLE(2, 3, 0, 1)); \ + tmpi = _mm_shufflehi_epi16(tmpi, _MM_SHUFFLE(2, 3, 0, 1)); \ + \ + /* store to output */ \ + _mm_storeu_si128(reinterpret_cast<__m128i *>(output+i), tmpi); \ + } \ + + size_t i = 0; + + //dispatch according to alignment + if ((size_t(input) & 0xf) == 0){ + convert_fc64_1_to_item32_1_nswap_guts(_) + } + else{ + convert_fc64_1_to_item32_1_nswap_guts(u_) + } + + //convert remainder + xx_to_item32_sc16<uhd::htowx>(input+i, output+i, nsamps-i, scale_factor); +} + +DECLARE_CONVERTER(fc64, 1, sc16_item32_be, 1, PRIORITY_SIMD){ + const fc64_t *input = reinterpret_cast<const fc64_t *>(inputs[0]); + item32_t *output = reinterpret_cast<item32_t *>(outputs[0]); + + const __m128d scalar = _mm_set1_pd(scale_factor); + + #define convert_fc64_1_to_item32_1_bswap_guts(_al_) \ + for (; i+3 < nsamps; i+=4){ \ + /* load from input */ \ + __m128d tmp0 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+0)); \ + __m128d tmp1 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+1)); \ + __m128d tmp2 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+2)); \ + __m128d tmp3 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+3)); \ + \ + /* convert and scale */ \ + __m128i tmpi0 = _mm_cvttpd_epi32(_mm_mul_pd(tmp0, scalar)); \ + __m128i tmpi1 = _mm_cvttpd_epi32(_mm_mul_pd(tmp1, scalar)); \ + __m128i tmpilo = _mm_unpacklo_epi64(tmpi0, tmpi1); \ + __m128i tmpi2 = _mm_cvttpd_epi32(_mm_mul_pd(tmp2, scalar)); \ + __m128i tmpi3 = _mm_cvttpd_epi32(_mm_mul_pd(tmp3, scalar)); \ + __m128i tmpihi = _mm_unpacklo_epi64(tmpi2, tmpi3); \ + \ + /* pack + byteswap -> byteswap 16 bit words */ \ + __m128i tmpi = _mm_packs_epi32(tmpilo, tmpihi); \ + tmpi = _mm_or_si128(_mm_srli_epi16(tmpi, 8), _mm_slli_epi16(tmpi, 8)); \ + \ + /* store to output */ \ + _mm_storeu_si128(reinterpret_cast<__m128i *>(output+i), tmpi); \ + } \ + + size_t i = 0; + + //dispatch according to alignment + if ((size_t(input) & 0xf) == 0){ + convert_fc64_1_to_item32_1_bswap_guts(_) + } + else{ + convert_fc64_1_to_item32_1_bswap_guts(u_) + } + + //convert remainder + xx_to_item32_sc16<uhd::htonx>(input+i, output+i, nsamps-i, scale_factor); +} diff --git a/host/lib/convert/convert_fc64_to_sc8_with_sse2.cpp b/host/lib/convert/sse2_fc64_to_sc8.cpp index 405850601..bf3719e13 100644 --- a/host/lib/convert/convert_fc64_to_sc8_with_sse2.cpp +++ b/host/lib/convert/sse2_fc64_to_sc8.cpp @@ -30,7 +30,7 @@ UHD_INLINE __m128i pack_sc8_item32_4x( return _mm_packs_epi16(lo, hi); } -UHD_INLINE __m128i pack_sc32_4x_be( +UHD_INLINE __m128i pack_sc32_4x( const __m128d &lo, const __m128d &hi, const __m128d &scalar ){ @@ -39,16 +39,6 @@ UHD_INLINE __m128i pack_sc32_4x_be( return _mm_unpacklo_epi64(tmpi_lo, tmpi_hi); } -UHD_INLINE __m128i pack_sc32_4x_le( - const __m128d &lo, const __m128d &hi, - const __m128d &scalar -){ - const __m128i tmpi_lo = _mm_cvttpd_epi32(_mm_mul_pd(lo, scalar)); - const __m128i tmpi_hi = _mm_cvttpd_epi32(_mm_mul_pd(hi, scalar)); - const __m128i tmpi = _mm_unpacklo_epi64(tmpi_lo, tmpi_hi); - return _mm_shuffle_epi32(tmpi, _MM_SHUFFLE(2, 3, 0, 1)); -} - DECLARE_CONVERTER(fc64, 1, sc8_item32_be, 1, PRIORITY_SIMD){ const fc64_t *input = reinterpret_cast<const fc64_t *>(inputs[0]); item32_t *output = reinterpret_cast<item32_t *>(outputs[0]); @@ -69,10 +59,10 @@ DECLARE_CONVERTER(fc64, 1, sc8_item32_be, 1, PRIORITY_SIMD){ \ /* interleave */ \ const __m128i tmpi = pack_sc8_item32_4x( \ - pack_sc32_4x_be(tmp0, tmp1, scalar), \ - pack_sc32_4x_be(tmp2, tmp3, scalar), \ - pack_sc32_4x_be(tmp4, tmp5, scalar), \ - pack_sc32_4x_be(tmp6, tmp7, scalar) \ + pack_sc32_4x(tmp0, tmp1, scalar), \ + pack_sc32_4x(tmp2, tmp3, scalar), \ + pack_sc32_4x(tmp4, tmp5, scalar), \ + pack_sc32_4x(tmp6, tmp7, scalar) \ ); \ \ /* store to output */ \ @@ -90,16 +80,7 @@ DECLARE_CONVERTER(fc64, 1, sc8_item32_be, 1, PRIORITY_SIMD){ } //convert remainder - const size_t num_pairs = nsamps/2; - for (size_t j = i/2; j < num_pairs; j++, i+=2){ - const item32_t item = fc64_to_item32_sc8(input[i], input[i+1], scale_factor); - output[j] = uhd::byteswap(item); - } - - if (nsamps != num_pairs*2){ - const item32_t item = fc64_to_item32_sc8(input[nsamps-1], 0, scale_factor); - output[num_pairs] = uhd::byteswap(item); - } + xx_to_item32_sc8<uhd::htonx>(input+i, output+(i/2), nsamps-i, scale_factor); } DECLARE_CONVERTER(fc64, 1, sc8_item32_le, 1, PRIORITY_SIMD){ @@ -121,12 +102,13 @@ DECLARE_CONVERTER(fc64, 1, sc8_item32_le, 1, PRIORITY_SIMD){ __m128d tmp7 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+7)); \ \ /* interleave */ \ - const __m128i tmpi = pack_sc8_item32_4x( \ - pack_sc32_4x_le(tmp0, tmp1, scalar), \ - pack_sc32_4x_le(tmp2, tmp3, scalar), \ - pack_sc32_4x_le(tmp4, tmp5, scalar), \ - pack_sc32_4x_le(tmp6, tmp7, scalar) \ + __m128i tmpi = pack_sc8_item32_4x( \ + pack_sc32_4x(tmp1, tmp0, scalar), \ + pack_sc32_4x(tmp3, tmp2, scalar), \ + pack_sc32_4x(tmp5, tmp4, scalar), \ + pack_sc32_4x(tmp7, tmp6, scalar) \ ); \ + tmpi = _mm_or_si128(_mm_srli_epi16(tmpi, 8), _mm_slli_epi16(tmpi, 8)); /*byteswap*/\ \ /* store to output */ \ _mm_storeu_si128(reinterpret_cast<__m128i *>(output+j), tmpi); \ @@ -143,14 +125,5 @@ DECLARE_CONVERTER(fc64, 1, sc8_item32_le, 1, PRIORITY_SIMD){ } //convert remainder - const size_t num_pairs = nsamps/2; - for (size_t j = i/2; j < num_pairs; j++, i+=2){ - const item32_t item = fc64_to_item32_sc8(input[i], input[i+1], scale_factor); - output[j] = (item); - } - - if (nsamps != num_pairs*2){ - const item32_t item = fc64_to_item32_sc8(input[nsamps-1], 0, scale_factor); - output[num_pairs] = (item); - } + xx_to_item32_sc8<uhd::htowx>(input+i, output+(i/2), nsamps-i, scale_factor); } diff --git a/host/lib/convert/convert_fc32_with_sse2.cpp b/host/lib/convert/sse2_sc16_to_fc32.cpp index 97a3e8cdc..c03e41585 100644 --- a/host/lib/convert/convert_fc32_with_sse2.cpp +++ b/host/lib/convert/sse2_sc16_to_fc32.cpp @@ -1,5 +1,5 @@ // -// Copyright 2011 Ettus Research LLC +// Copyright 2011-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 @@ -21,91 +21,6 @@ using namespace uhd::convert; -DECLARE_CONVERTER(fc32, 1, sc16_item32_le, 1, PRIORITY_SIMD){ - const fc32_t *input = reinterpret_cast<const fc32_t *>(inputs[0]); - item32_t *output = reinterpret_cast<item32_t *>(outputs[0]); - - const __m128 scalar = _mm_set_ps1(float(scale_factor)); - - #define convert_fc32_1_to_item32_1_nswap_guts(_al_) \ - for (; i+3 < nsamps; i+=4){ \ - /* load from input */ \ - __m128 tmplo = _mm_load ## _al_ ## ps(reinterpret_cast<const float *>(input+i+0)); \ - __m128 tmphi = _mm_load ## _al_ ## ps(reinterpret_cast<const float *>(input+i+2)); \ - \ - /* convert and scale */ \ - __m128i tmpilo = _mm_cvtps_epi32(_mm_mul_ps(tmplo, scalar)); \ - __m128i tmpihi = _mm_cvtps_epi32(_mm_mul_ps(tmphi, scalar)); \ - \ - /* pack + swap 16-bit pairs */ \ - __m128i tmpi = _mm_packs_epi32(tmpilo, tmpihi); \ - tmpi = _mm_shufflelo_epi16(tmpi, _MM_SHUFFLE(2, 3, 0, 1)); \ - tmpi = _mm_shufflehi_epi16(tmpi, _MM_SHUFFLE(2, 3, 0, 1)); \ - \ - /* store to output */ \ - _mm_storeu_si128(reinterpret_cast<__m128i *>(output+i), tmpi); \ - } \ - - size_t i = 0; - - //dispatch according to alignment - switch (size_t(input) & 0xf){ - case 0x8: - output[i] = fc32_to_item32_sc16(input[i], float(scale_factor)); i++; - case 0x0: - convert_fc32_1_to_item32_1_nswap_guts(_) - break; - default: convert_fc32_1_to_item32_1_nswap_guts(u_) - } - - //convert remainder - for (; i < nsamps; i++){ - output[i] = fc32_to_item32_sc16(input[i], float(scale_factor)); - } -} - -DECLARE_CONVERTER(fc32, 1, sc16_item32_be, 1, PRIORITY_SIMD){ - const fc32_t *input = reinterpret_cast<const fc32_t *>(inputs[0]); - item32_t *output = reinterpret_cast<item32_t *>(outputs[0]); - - const __m128 scalar = _mm_set_ps1(float(scale_factor)); - - #define convert_fc32_1_to_item32_1_bswap_guts(_al_) \ - for (; i+3 < nsamps; i+=4){ \ - /* load from input */ \ - __m128 tmplo = _mm_load ## _al_ ## ps(reinterpret_cast<const float *>(input+i+0)); \ - __m128 tmphi = _mm_load ## _al_ ## ps(reinterpret_cast<const float *>(input+i+2)); \ - \ - /* convert and scale */ \ - __m128i tmpilo = _mm_cvtps_epi32(_mm_mul_ps(tmplo, scalar)); \ - __m128i tmpihi = _mm_cvtps_epi32(_mm_mul_ps(tmphi, scalar)); \ - \ - /* pack + byteswap -> byteswap 16 bit words */ \ - __m128i tmpi = _mm_packs_epi32(tmpilo, tmpihi); \ - tmpi = _mm_or_si128(_mm_srli_epi16(tmpi, 8), _mm_slli_epi16(tmpi, 8)); \ - \ - /* store to output */ \ - _mm_storeu_si128(reinterpret_cast<__m128i *>(output+i), tmpi); \ - } \ - - size_t i = 0; - - //dispatch according to alignment - switch (size_t(input) & 0xf){ - case 0x8: - output[i] = uhd::byteswap(fc32_to_item32_sc16(input[i], float(scale_factor))); i++; - case 0x0: - convert_fc32_1_to_item32_1_bswap_guts(_) - break; - default: convert_fc32_1_to_item32_1_bswap_guts(u_) - } - - //convert remainder - for (; i < nsamps; i++){ - output[i] = uhd::byteswap(fc32_to_item32_sc16(input[i], float(scale_factor))); - } -} - DECLARE_CONVERTER(sc16_item32_le, 1, fc32, 1, PRIORITY_SIMD){ const item32_t *input = reinterpret_cast<const item32_t *>(inputs[0]); fc32_t *output = reinterpret_cast<fc32_t *>(outputs[0]); @@ -138,7 +53,7 @@ DECLARE_CONVERTER(sc16_item32_le, 1, fc32, 1, PRIORITY_SIMD){ //dispatch according to alignment switch (size_t(output) & 0xf){ case 0x8: - output[i] = item32_sc16_to_fc32(input[i], float(scale_factor)); i++; + item32_sc16_to_xx<uhd::htowx>(input, output, 1, scale_factor); i++; case 0x0: convert_item32_1_to_fc32_1_nswap_guts(_) break; @@ -146,9 +61,7 @@ DECLARE_CONVERTER(sc16_item32_le, 1, fc32, 1, PRIORITY_SIMD){ } //convert remainder - for (; i < nsamps; i++){ - output[i] = item32_sc16_to_fc32(input[i], float(scale_factor)); - } + item32_sc16_to_xx<uhd::htowx>(input+i, output+i, nsamps-i, scale_factor); } DECLARE_CONVERTER(sc16_item32_be, 1, fc32, 1, PRIORITY_SIMD){ @@ -182,7 +95,7 @@ DECLARE_CONVERTER(sc16_item32_be, 1, fc32, 1, PRIORITY_SIMD){ //dispatch according to alignment switch (size_t(output) & 0xf){ case 0x8: - output[i] = item32_sc16_to_fc32(uhd::byteswap(input[i]), float(scale_factor)); i++; + item32_sc16_to_xx<uhd::htonx>(input, output, 1, scale_factor); i++; case 0x0: convert_item32_1_to_fc32_1_bswap_guts(_) break; @@ -190,7 +103,5 @@ DECLARE_CONVERTER(sc16_item32_be, 1, fc32, 1, PRIORITY_SIMD){ } //convert remainder - for (; i < nsamps; i++){ - output[i] = item32_sc16_to_fc32(uhd::byteswap(input[i]), float(scale_factor)); - } + item32_sc16_to_xx<uhd::htonx>(input+i, output+i, nsamps-i, scale_factor); } diff --git a/host/lib/convert/convert_fc64_with_sse2.cpp b/host/lib/convert/sse2_sc16_to_fc64.cpp index 6e097e380..66068cad9 100644 --- a/host/lib/convert/convert_fc64_with_sse2.cpp +++ b/host/lib/convert/sse2_sc16_to_fc64.cpp @@ -1,5 +1,5 @@ // -// Copyright 2011 Ettus Research LLC +// Copyright 2011-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 @@ -21,99 +21,6 @@ using namespace uhd::convert; -DECLARE_CONVERTER(fc64, 1, sc16_item32_le, 1, PRIORITY_SIMD){ - const fc64_t *input = reinterpret_cast<const fc64_t *>(inputs[0]); - item32_t *output = reinterpret_cast<item32_t *>(outputs[0]); - - const __m128d scalar = _mm_set1_pd(scale_factor); - - #define convert_fc64_1_to_item32_1_nswap_guts(_al_) \ - for (; i+3 < nsamps; i+=4){ \ - /* load from input */ \ - __m128d tmp0 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+0)); \ - __m128d tmp1 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+1)); \ - __m128d tmp2 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+2)); \ - __m128d tmp3 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+3)); \ - \ - /* convert and scale */ \ - __m128i tmpi0 = _mm_cvttpd_epi32(_mm_mul_pd(tmp0, scalar)); \ - __m128i tmpi1 = _mm_cvttpd_epi32(_mm_mul_pd(tmp1, scalar)); \ - __m128i tmpilo = _mm_unpacklo_epi64(tmpi0, tmpi1); \ - __m128i tmpi2 = _mm_cvttpd_epi32(_mm_mul_pd(tmp2, scalar)); \ - __m128i tmpi3 = _mm_cvttpd_epi32(_mm_mul_pd(tmp3, scalar)); \ - __m128i tmpihi = _mm_unpacklo_epi64(tmpi2, tmpi3); \ - \ - /* pack + swap 16-bit pairs */ \ - __m128i tmpi = _mm_packs_epi32(tmpilo, tmpihi); \ - tmpi = _mm_shufflelo_epi16(tmpi, _MM_SHUFFLE(2, 3, 0, 1)); \ - tmpi = _mm_shufflehi_epi16(tmpi, _MM_SHUFFLE(2, 3, 0, 1)); \ - \ - /* store to output */ \ - _mm_storeu_si128(reinterpret_cast<__m128i *>(output+i), tmpi); \ - } \ - - size_t i = 0; - - //dispatch according to alignment - if ((size_t(input) & 0xf) == 0){ - convert_fc64_1_to_item32_1_nswap_guts(_) - } - else{ - convert_fc64_1_to_item32_1_nswap_guts(u_) - } - - //convert remainder - for (; i < nsamps; i++){ - output[i] = fc64_to_item32_sc16(input[i], scale_factor); - } -} - -DECLARE_CONVERTER(fc64, 1, sc16_item32_be, 1, PRIORITY_SIMD){ - const fc64_t *input = reinterpret_cast<const fc64_t *>(inputs[0]); - item32_t *output = reinterpret_cast<item32_t *>(outputs[0]); - - const __m128d scalar = _mm_set1_pd(scale_factor); - - #define convert_fc64_1_to_item32_1_bswap_guts(_al_) \ - for (; i+3 < nsamps; i+=4){ \ - /* load from input */ \ - __m128d tmp0 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+0)); \ - __m128d tmp1 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+1)); \ - __m128d tmp2 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+2)); \ - __m128d tmp3 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+3)); \ - \ - /* convert and scale */ \ - __m128i tmpi0 = _mm_cvttpd_epi32(_mm_mul_pd(tmp0, scalar)); \ - __m128i tmpi1 = _mm_cvttpd_epi32(_mm_mul_pd(tmp1, scalar)); \ - __m128i tmpilo = _mm_unpacklo_epi64(tmpi0, tmpi1); \ - __m128i tmpi2 = _mm_cvttpd_epi32(_mm_mul_pd(tmp2, scalar)); \ - __m128i tmpi3 = _mm_cvttpd_epi32(_mm_mul_pd(tmp3, scalar)); \ - __m128i tmpihi = _mm_unpacklo_epi64(tmpi2, tmpi3); \ - \ - /* pack + byteswap -> byteswap 16 bit words */ \ - __m128i tmpi = _mm_packs_epi32(tmpilo, tmpihi); \ - tmpi = _mm_or_si128(_mm_srli_epi16(tmpi, 8), _mm_slli_epi16(tmpi, 8)); \ - \ - /* store to output */ \ - _mm_storeu_si128(reinterpret_cast<__m128i *>(output+i), tmpi); \ - } \ - - size_t i = 0; - - //dispatch according to alignment - if ((size_t(input) & 0xf) == 0){ - convert_fc64_1_to_item32_1_bswap_guts(_) - } - else{ - convert_fc64_1_to_item32_1_bswap_guts(u_) - } - - //convert remainder - for (; i < nsamps; i++){ - output[i] = uhd::byteswap(fc64_to_item32_sc16(input[i], scale_factor)); - } -} - DECLARE_CONVERTER(sc16_item32_le, 1, fc64, 1, PRIORITY_SIMD){ const item32_t *input = reinterpret_cast<const item32_t *>(inputs[0]); fc64_t *output = reinterpret_cast<fc64_t *>(outputs[0]); @@ -158,9 +65,7 @@ DECLARE_CONVERTER(sc16_item32_le, 1, fc64, 1, PRIORITY_SIMD){ } //convert remainder - for (; i < nsamps; i++){ - output[i] = item32_sc16_to_fc64(input[i], scale_factor); - } + item32_sc16_to_xx<uhd::htowx>(input+i, output+i, nsamps-i, scale_factor); } DECLARE_CONVERTER(sc16_item32_be, 1, fc64, 1, PRIORITY_SIMD){ @@ -206,7 +111,5 @@ DECLARE_CONVERTER(sc16_item32_be, 1, fc64, 1, PRIORITY_SIMD){ } //convert remainder - for (; i < nsamps; i++){ - output[i] = item32_sc16_to_fc64(uhd::byteswap(input[i]), scale_factor); - } + item32_sc16_to_xx<uhd::htonx>(input+i, output+i, nsamps-i, scale_factor); } diff --git a/host/lib/convert/sse2_sc8_to_fc32.cpp b/host/lib/convert/sse2_sc8_to_fc32.cpp new file mode 100644 index 000000000..c0e561814 --- /dev/null +++ b/host/lib/convert/sse2_sc8_to_fc32.cpp @@ -0,0 +1,132 @@ +// +// 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 "convert_common.hpp" +#include <uhd/utils/byteswap.hpp> +#include <emmintrin.h> + +using namespace uhd::convert; + +static const __m128i zeroi = _mm_setzero_si128(); + +template <const int shuf> +UHD_INLINE void unpack_sc32_4x( + const __m128i &in, + __m128 &out0, __m128 &out1, + __m128 &out2, __m128 &out3, + const __m128 &scalar +){ + const __m128i tmplo = _mm_unpacklo_epi8(zeroi, in); /* value in upper 8 bits */ + __m128i tmp0 = _mm_shuffle_epi32(_mm_unpacklo_epi16(zeroi, tmplo), shuf); /* value in upper 16 bits */ + __m128i tmp1 = _mm_shuffle_epi32(_mm_unpackhi_epi16(zeroi, tmplo), shuf); + out0 = _mm_mul_ps(_mm_cvtepi32_ps(tmp0), scalar); + out1 = _mm_mul_ps(_mm_cvtepi32_ps(tmp1), scalar); + + const __m128i tmphi = _mm_unpackhi_epi8(zeroi, in); + __m128i tmp2 = _mm_shuffle_epi32(_mm_unpacklo_epi16(zeroi, tmphi), shuf); + __m128i tmp3 = _mm_shuffle_epi32(_mm_unpackhi_epi16(zeroi, tmphi), shuf); + out2 = _mm_mul_ps(_mm_cvtepi32_ps(tmp2), scalar); + out3 = _mm_mul_ps(_mm_cvtepi32_ps(tmp3), scalar); +} + +DECLARE_CONVERTER(sc8_item32_be, 1, fc32, 1, PRIORITY_SIMD){ + const item32_t *input = reinterpret_cast<const item32_t *>(size_t(inputs[0]) & ~0x3); + fc32_t *output = reinterpret_cast<fc32_t *>(outputs[0]); + + const __m128 scalar = _mm_set_ps1(float(scale_factor)/(1 << 24)); + const int shuf = _MM_SHUFFLE(1, 0, 3, 2); + + size_t i = 0, j = 0; + fc32_t dummy; + size_t num_samps = nsamps; + + if ((size_t(inputs[0]) & 0x3) != 0){ + item32_sc8_to_xx<uhd::ntohx>(input++, output++, 1, scale_factor); + num_samps--; + } + + #define convert_sc8_item32_1_to_fc32_1_bswap_guts(_al_) \ + for (; j+7 < num_samps; j+=8, i+=4){ \ + /* load from input */ \ + __m128i tmpi = _mm_loadu_si128(reinterpret_cast<const __m128i *>(input+i)); \ + \ + /* unpack + swap 8-bit pairs */ \ + __m128 tmp0, tmp1, tmp2, tmp3; \ + unpack_sc32_4x<shuf>(tmpi, tmp0, tmp1, tmp2, tmp3, scalar); \ + \ + /* store to output */ \ + _mm_store ## _al_ ## ps(reinterpret_cast<float *>(output+j+0), tmp0); \ + _mm_store ## _al_ ## ps(reinterpret_cast<float *>(output+j+2), tmp1); \ + _mm_store ## _al_ ## ps(reinterpret_cast<float *>(output+j+4), tmp2); \ + _mm_store ## _al_ ## ps(reinterpret_cast<float *>(output+j+6), tmp3); \ + } + + //dispatch according to alignment + if ((size_t(output) & 0xf) == 0){ + convert_sc8_item32_1_to_fc32_1_bswap_guts(_) + } + else{ + convert_sc8_item32_1_to_fc32_1_bswap_guts(u_) + } + + //convert remainder + item32_sc8_to_xx<uhd::ntohx>(input+i, output+j, num_samps-j, scale_factor); +} + +DECLARE_CONVERTER(sc8_item32_le, 1, fc32, 1, PRIORITY_SIMD){ + const item32_t *input = reinterpret_cast<const item32_t *>(size_t(inputs[0]) & ~0x3); + fc32_t *output = reinterpret_cast<fc32_t *>(outputs[0]); + + const __m128 scalar = _mm_set_ps1(float(scale_factor)/(1 << 24)); + const int shuf = _MM_SHUFFLE(2, 3, 0, 1); + + size_t i = 0, j = 0; + fc32_t dummy; + size_t num_samps = nsamps; + + if ((size_t(inputs[0]) & 0x3) != 0){ + item32_sc8_to_xx<uhd::wtohx>(input++, output++, 1, scale_factor); + num_samps--; + } + + #define convert_sc8_item32_1_to_fc32_1_nswap_guts(_al_) \ + for (; j+7 < num_samps; j+=8, i+=4){ \ + /* load from input */ \ + __m128i tmpi = _mm_loadu_si128(reinterpret_cast<const __m128i *>(input+i)); \ + \ + /* unpack + swap 8-bit pairs */ \ + __m128 tmp0, tmp1, tmp2, tmp3; \ + unpack_sc32_4x<shuf>(tmpi, tmp0, tmp1, tmp2, tmp3, scalar); \ + \ + /* store to output */ \ + _mm_store ## _al_ ## ps(reinterpret_cast<float *>(output+j+0), tmp0); \ + _mm_store ## _al_ ## ps(reinterpret_cast<float *>(output+j+2), tmp1); \ + _mm_store ## _al_ ## ps(reinterpret_cast<float *>(output+j+4), tmp2); \ + _mm_store ## _al_ ## ps(reinterpret_cast<float *>(output+j+6), tmp3); \ + } + + //dispatch according to alignment + if ((size_t(output) & 0xf) == 0){ + convert_sc8_item32_1_to_fc32_1_nswap_guts(_) + } + else{ + convert_sc8_item32_1_to_fc32_1_nswap_guts(u_) + } + + //convert remainder + item32_sc8_to_xx<uhd::wtohx>(input+i, output+j, num_samps-j, scale_factor); +} diff --git a/host/lib/convert/sse2_sc8_to_fc64.cpp b/host/lib/convert/sse2_sc8_to_fc64.cpp new file mode 100644 index 000000000..ef9c0fdb4 --- /dev/null +++ b/host/lib/convert/sse2_sc8_to_fc64.cpp @@ -0,0 +1,151 @@ +// +// 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 "convert_common.hpp" +#include <uhd/utils/byteswap.hpp> +#include <emmintrin.h> + +using namespace uhd::convert; + +static const __m128i zeroi = _mm_setzero_si128(); + +UHD_INLINE void unpack_sc32_8x( + const __m128i &in, + __m128d &out0, __m128d &out1, + __m128d &out2, __m128d &out3, + __m128d &out4, __m128d &out5, + __m128d &out6, __m128d &out7, + const __m128d &scalar +){ + const int shuf = _MM_SHUFFLE(1, 0, 3, 2); + __m128i tmp; + + const __m128i tmplo = _mm_unpacklo_epi8(zeroi, in); /* value in upper 8 bits */ + tmp = _mm_unpacklo_epi16(zeroi, tmplo); /* value in upper 16 bits */ + out0 = _mm_mul_pd(_mm_cvtepi32_pd(tmp), scalar); + tmp = _mm_shuffle_epi32(tmp, shuf); + out1 = _mm_mul_pd(_mm_cvtepi32_pd(tmp), scalar); + tmp = _mm_unpackhi_epi16(zeroi, tmplo); + out2 = _mm_mul_pd(_mm_cvtepi32_pd(tmp), scalar); + tmp = _mm_shuffle_epi32(tmp, shuf); + out3 = _mm_mul_pd(_mm_cvtepi32_pd(tmp), scalar); + + const __m128i tmphi = _mm_unpackhi_epi8(zeroi, in); + tmp = _mm_unpacklo_epi16(zeroi, tmphi); + out4 = _mm_mul_pd(_mm_cvtepi32_pd(tmp), scalar); + tmp = _mm_shuffle_epi32(tmp, shuf); + out5 = _mm_mul_pd(_mm_cvtepi32_pd(tmp), scalar); + tmp = _mm_unpackhi_epi16(zeroi, tmphi); + out6 = _mm_mul_pd(_mm_cvtepi32_pd(tmp), scalar); + tmp = _mm_shuffle_epi32(tmp, shuf); + out7 = _mm_mul_pd(_mm_cvtepi32_pd(tmp), scalar); +} + +DECLARE_CONVERTER(sc8_item32_be, 1, fc64, 1, PRIORITY_SIMD){ + const item32_t *input = reinterpret_cast<const item32_t *>(size_t(inputs[0]) & ~0x3); + fc64_t *output = reinterpret_cast<fc64_t *>(outputs[0]); + + const __m128d scalar = _mm_set1_pd(scale_factor/(1 << 24)); + + size_t i = 0, j = 0; + fc32_t dummy; + size_t num_samps = nsamps; + + if ((size_t(inputs[0]) & 0x3) != 0){ + item32_sc8_to_xx<uhd::ntohx>(input++, output++, 1, scale_factor); + num_samps--; + } + + #define convert_sc8_item32_1_to_fc64_1_bswap_guts(_al_) \ + for (; j+7 < num_samps; j+=8, i+=4){ \ + /* load from input */ \ + __m128i tmpi = _mm_loadu_si128(reinterpret_cast<const __m128i *>(input+i)); \ + \ + /* unpack */ \ + __m128d tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; \ + unpack_sc32_8x(tmpi, tmp1, tmp0, tmp3, tmp2, tmp5, tmp4, tmp7, tmp6, scalar); \ + \ + /* store to output */ \ + _mm_store ## _al_ ## pd(reinterpret_cast<double *>(output+j+0), tmp0); \ + _mm_store ## _al_ ## pd(reinterpret_cast<double *>(output+j+1), tmp1); \ + _mm_store ## _al_ ## pd(reinterpret_cast<double *>(output+j+2), tmp2); \ + _mm_store ## _al_ ## pd(reinterpret_cast<double *>(output+j+3), tmp3); \ + _mm_store ## _al_ ## pd(reinterpret_cast<double *>(output+j+4), tmp4); \ + _mm_store ## _al_ ## pd(reinterpret_cast<double *>(output+j+5), tmp5); \ + _mm_store ## _al_ ## pd(reinterpret_cast<double *>(output+j+6), tmp6); \ + _mm_store ## _al_ ## pd(reinterpret_cast<double *>(output+j+7), tmp7); \ + } + + //dispatch according to alignment + if ((size_t(output) & 0xf) == 0){ + convert_sc8_item32_1_to_fc64_1_bswap_guts(_) + } + else{ + convert_sc8_item32_1_to_fc64_1_bswap_guts(u_) + } + + //convert remainder + item32_sc8_to_xx<uhd::ntohx>(input+i, output+j, num_samps-j, scale_factor); +} + +DECLARE_CONVERTER(sc8_item32_le, 1, fc64, 1, PRIORITY_SIMD){ + const item32_t *input = reinterpret_cast<const item32_t *>(size_t(inputs[0]) & ~0x3); + fc64_t *output = reinterpret_cast<fc64_t *>(outputs[0]); + + const __m128d scalar = _mm_set1_pd(scale_factor/(1 << 24)); + + size_t i = 0, j = 0; + fc32_t dummy; + size_t num_samps = nsamps; + + if ((size_t(inputs[0]) & 0x3) != 0){ + item32_sc8_to_xx<uhd::wtohx>(input++, output++, 1, scale_factor); + num_samps--; + } + + #define convert_sc8_item32_1_to_fc64_1_nswap_guts(_al_) \ + for (; j+7 < num_samps; j+=8, i+=4){ \ + /* load from input */ \ + __m128i tmpi = _mm_loadu_si128(reinterpret_cast<const __m128i *>(input+i)); \ + \ + /* unpack */ \ + __m128d tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; \ + tmpi = _mm_or_si128(_mm_srli_epi16(tmpi, 8), _mm_slli_epi16(tmpi, 8)); /*byteswap*/\ + unpack_sc32_8x(tmpi, tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, scalar); \ + \ + /* store to output */ \ + _mm_store ## _al_ ## pd(reinterpret_cast<double *>(output+j+0), tmp0); \ + _mm_store ## _al_ ## pd(reinterpret_cast<double *>(output+j+1), tmp1); \ + _mm_store ## _al_ ## pd(reinterpret_cast<double *>(output+j+2), tmp2); \ + _mm_store ## _al_ ## pd(reinterpret_cast<double *>(output+j+3), tmp3); \ + _mm_store ## _al_ ## pd(reinterpret_cast<double *>(output+j+4), tmp4); \ + _mm_store ## _al_ ## pd(reinterpret_cast<double *>(output+j+5), tmp5); \ + _mm_store ## _al_ ## pd(reinterpret_cast<double *>(output+j+6), tmp6); \ + _mm_store ## _al_ ## pd(reinterpret_cast<double *>(output+j+7), tmp7); \ + } + + //dispatch according to alignment + if ((size_t(output) & 0xf) == 0){ + convert_sc8_item32_1_to_fc64_1_nswap_guts(_) + } + else{ + convert_sc8_item32_1_to_fc64_1_nswap_guts(u_) + } + + //convert remainder + item32_sc8_to_xx<uhd::wtohx>(input+i, output+j, num_samps-j, scale_factor); +} diff --git a/host/lib/transport/CMakeLists.txt b/host/lib/transport/CMakeLists.txt index 8e8ea5ea8..6524a8412 100644 --- a/host/lib/transport/CMakeLists.txt +++ b/host/lib/transport/CMakeLists.txt @@ -82,7 +82,11 @@ SET_SOURCE_FILES_PROPERTIES( ######################################################################## # Setup UDP ######################################################################## -LIBUHD_APPEND_SOURCES(${CMAKE_CURRENT_SOURCE_DIR}/udp_zero_copy.cpp) +IF(WIN32) + LIBUHD_APPEND_SOURCES(${CMAKE_CURRENT_SOURCE_DIR}/udp_wsa_zero_copy.cpp) +ELSE() + LIBUHD_APPEND_SOURCES(${CMAKE_CURRENT_SOURCE_DIR}/udp_zero_copy.cpp) +ENDIF() #On windows, the boost asio implementation uses the winsock2 library. #Note: we exclude the .lib extension for cygwin and mingw platforms. @@ -97,6 +101,7 @@ CHECK_INCLUDE_FILE_CXX(atlbase.h HAVE_ATLBASE_H) IF(HAVE_ATLBASE_H) SET_SOURCE_FILES_PROPERTIES( ${CMAKE_CURRENT_SOURCE_DIR}/udp_zero_copy.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/udp_wsa_zero_copy.cpp PROPERTIES COMPILE_DEFINITIONS "HAVE_ATLBASE_H" ) ENDIF(HAVE_ATLBASE_H) diff --git a/host/lib/transport/libusb1_zero_copy.cpp b/host/lib/transport/libusb1_zero_copy.cpp index 3e67264cd..28bff9709 100644 --- a/host/lib/transport/libusb1_zero_copy.cpp +++ b/host/lib/transport/libusb1_zero_copy.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-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 @@ -21,6 +21,7 @@ #include <uhd/utils/msg.hpp> #include <uhd/exception.hpp> #include <boost/foreach.hpp> +#include <boost/make_shared.hpp> #include <boost/thread/thread.hpp> #include <list> @@ -61,8 +62,18 @@ static void LIBUSB_CALL libusb_async_cb(libusb_transfer *lut){ * \return true for completion, false for timeout */ UHD_INLINE bool wait_for_completion(libusb_context *ctx, const double timeout, bool &completed){ - const boost::system_time timeout_time = boost::get_system_time() + boost::posix_time::microseconds(long(timeout*1000000)); + //already completed by a previous call? + if (completed) return true; + + //perform a non-blocking event handle + timeval tv; + tv.tv_sec = 0; + tv.tv_usec = 0; + libusb_handle_events_timeout(ctx, &tv); + if (completed) return true; + //finish the rest with a timeout loop + const boost::system_time timeout_time = boost::get_system_time() + boost::posix_time::microseconds(long(timeout*1000000)); while (not completed and (boost::get_system_time() < timeout_time)){ timeval tv; tv.tv_sec = 0; @@ -82,21 +93,18 @@ class libusb_zero_copy_mrb : public managed_recv_buffer{ public: libusb_zero_copy_mrb(libusb_transfer *lut, const size_t frame_size): _ctx(libusb::session::get_global_session()->get_context()), - _lut(lut), _expired(false), _frame_size(frame_size) { /* NOP */ } + _lut(lut), _frame_size(frame_size) { /* NOP */ } void release(void){ - if (_expired) return; completed = false; _lut->length = _frame_size; //always reset length UHD_ASSERT_THROW(libusb_submit_transfer(_lut) == 0); - _expired = true; } sptr get_new(const double timeout, size_t &index){ if (wait_for_completion(_ctx, timeout, completed)){ index++; - _expired = false; - return make_managed_buffer(this); + return make(this, _lut->buffer, _lut->actual_length); } return managed_recv_buffer::sptr(); } @@ -104,12 +112,8 @@ public: bool completed; private: - const void *get_buff(void) const{return _lut->buffer;} - size_t get_size(void) const{return _lut->actual_length;} - libusb_context *_ctx; libusb_transfer *_lut; - bool _expired; const size_t _frame_size; }; @@ -122,22 +126,18 @@ class libusb_zero_copy_msb : public managed_send_buffer{ public: libusb_zero_copy_msb(libusb_transfer *lut, const size_t frame_size): _ctx(libusb::session::get_global_session()->get_context()), - _lut(lut), _expired(false), _frame_size(frame_size) { /* NOP */ } + _lut(lut), _frame_size(frame_size) { completed = true; } - void commit(size_t len){ - if (_expired) return; + void release(void){ completed = false; - _lut->length = len; - if (len == 0) libusb_async_cb(_lut); - else UHD_ASSERT_THROW(libusb_submit_transfer(_lut) == 0); - _expired = true; + _lut->length = size(); + UHD_ASSERT_THROW(libusb_submit_transfer(_lut) == 0); } sptr get_new(const double timeout, size_t &index){ if (wait_for_completion(_ctx, timeout, completed)){ index++; - _expired = false; - return make_managed_buffer(this); + return make(this, _lut->buffer, _frame_size); } return managed_send_buffer::sptr(); } @@ -145,12 +145,8 @@ public: bool completed; private: - void *get_buff(void) const{return _lut->buffer;} - size_t get_size(void) const{return _frame_size;} - libusb_context *_ctx; libusb_transfer *_lut; - bool _expired; const size_t _frame_size; }; @@ -181,13 +177,30 @@ public: _handle->claim_interface(recv_interface); _handle->claim_interface(send_interface); + //flush the buffers out of the recv endpoint + //limit the flushing to at most one second + for (size_t i = 0; i < 100; i++) + { + unsigned char buff[512]; + int transfered = 0; + const int status = libusb_bulk_transfer( + _handle->get(), // dev_handle + (recv_endpoint & 0x7f) | 0x80, // endpoint + static_cast<unsigned char *>(buff), + sizeof(buff), + &transfered, //bytes xfered + 10 //timeout ms + ); + if (status == LIBUSB_ERROR_TIMEOUT) break; + } + //allocate libusb transfer structs and managed receive buffers for (size_t i = 0; i < get_num_recv_frames(); i++){ libusb_transfer *lut = libusb_alloc_transfer(0); UHD_ASSERT_THROW(lut != NULL); - _mrb_pool.push_back(boost::shared_ptr<libusb_zero_copy_mrb>(new libusb_zero_copy_mrb(lut, this->get_recv_frame_size()))); + _mrb_pool.push_back(boost::make_shared<libusb_zero_copy_mrb>(lut, this->get_recv_frame_size())); libusb_fill_bulk_transfer( lut, // transfer @@ -210,7 +223,7 @@ public: libusb_transfer *lut = libusb_alloc_transfer(0); UHD_ASSERT_THROW(lut != NULL); - _msb_pool.push_back(boost::shared_ptr<libusb_zero_copy_msb>(new libusb_zero_copy_msb(lut, this->get_send_frame_size()))); + _msb_pool.push_back(boost::make_shared<libusb_zero_copy_msb>(lut, this->get_send_frame_size())); libusb_fill_bulk_transfer( lut, // transfer @@ -224,7 +237,6 @@ public: ); _all_luts.push_back(lut); - _msb_pool.back()->commit(0); } } diff --git a/host/lib/transport/super_recv_packet_handler.hpp b/host/lib/transport/super_recv_packet_handler.hpp index 205c7a3a3..7a1972690 100644 --- a/host/lib/transport/super_recv_packet_handler.hpp +++ b/host/lib/transport/super_recv_packet_handler.hpp @@ -23,6 +23,8 @@ #include <uhd/convert.hpp> #include <uhd/stream.hpp> #include <uhd/utils/msg.hpp> +#include <uhd/utils/tasks.hpp> +#include <uhd/utils/atomic.hpp> #include <uhd/utils/byteswap.hpp> #include <uhd/types/metadata.hpp> #include <uhd/transport/vrt_if_packet.hpp> @@ -31,6 +33,9 @@ #include <boost/foreach.hpp> #include <boost/function.hpp> #include <boost/format.hpp> +#include <boost/bind.hpp> +#include <boost/make_shared.hpp> +#include <boost/thread/barrier.hpp> #include <iostream> #include <vector> @@ -73,12 +78,25 @@ public: set_alignment_failure_threshold(1000); } + ~recv_packet_handler(void){ + _task_barrier_entry.interrupt(); + _task_barrier_exit.interrupt(); + _task_handlers.clear(); + } + //! Resize the number of transport channels void resize(const size_t size){ if (this->size() == size) return; + _task_handlers.clear(); _props.resize(size); //re-initialize all buffers infos by re-creating the vector _buffers_infos = std::vector<buffers_info_type>(4, buffers_info_type(size)); + _task_barrier_entry.resize(size); + _task_barrier_exit.resize(size); + _task_handlers.resize(size); + for (size_t i = 1/*skip 0*/; i < size; i++){ + _task_handlers[i] = task::make(boost::bind(&recv_packet_handler::converter_thread_task, this, i)); + }; } //! Get the channel width of this handler @@ -125,7 +143,7 @@ public: //! Set the conversion routine for all channels void set_converter(const uhd::convert::id_type &id){ - _io_buffs.resize(id.num_outputs); + _num_outputs = id.num_outputs; _converter = uhd::convert::get_converter(id)(); this->set_scale_factor(1/32767.); //update after setting converter _bytes_per_otw_item = uhd::convert::get_bytes_per_item(id.input_format); @@ -207,7 +225,7 @@ private: handle_overflow_type handle_overflow; }; std::vector<xport_chan_props_type> _props; - std::vector<void *> _io_buffs; //used in conversion + size_t _num_outputs; size_t _bytes_per_otw_item; //used in conversion size_t _bytes_per_cpu_item; //used in conversion uhd::convert::converter::sptr _converter; //used in conversion @@ -512,24 +530,19 @@ private: //extract the number of samples available to copy const size_t nsamps_available = info.data_bytes_to_copy/_bytes_per_otw_item; - const size_t nsamps_to_copy = std::min(nsamps_per_buff*_io_buffs.size(), nsamps_available); + const size_t nsamps_to_copy = std::min(nsamps_per_buff*_num_outputs, nsamps_available); const size_t bytes_to_copy = nsamps_to_copy*_bytes_per_otw_item; - const size_t nsamps_to_copy_per_io_buff = nsamps_to_copy/_io_buffs.size(); + const size_t nsamps_to_copy_per_io_buff = nsamps_to_copy/_num_outputs; - size_t buff_index = 0; - BOOST_FOREACH(per_buffer_info_type &buff_info, info){ + //setup the data to share with converter threads + _convert_nsamps = nsamps_to_copy_per_io_buff; + _convert_buffs = &buffs; + _convert_buffer_offset_bytes = buffer_offset_bytes; + _convert_bytes_to_copy = bytes_to_copy; - //fill a vector with pointers to the io buffers - BOOST_FOREACH(void *&io_buff, _io_buffs){ - io_buff = reinterpret_cast<char *>(buffs[buff_index++]) + buffer_offset_bytes; - } + //perform N channels of conversion + converter_thread_task(0); - //copy-convert the samples from the recv buffer - _converter->conv(buff_info.copy_buff, _io_buffs, nsamps_to_copy_per_io_buff); - - //update the rx copy buffer to reflect the bytes copied - buff_info.copy_buff += bytes_to_copy; - } //update the copy buffer's availability info.data_bytes_to_copy -= bytes_to_copy; @@ -538,15 +551,53 @@ private: metadata.fragment_offset = info.fragment_offset_in_samps; info.fragment_offset_in_samps += nsamps_to_copy; //set for next call - //done with buffers? this action releases buffers in-order - if (not metadata.more_fragments){ - BOOST_FOREACH(per_buffer_info_type &buff_info, info){ - buff_info.buff.reset(); //effectively a release - } + return nsamps_to_copy_per_io_buff; + } + + /******************************************************************* + * Perform one thread's work of the conversion task. + * The entry and exit use a dual synchronization barrier, + * to wait for data to become ready and block until completion. + ******************************************************************/ + UHD_INLINE void converter_thread_task(const size_t index) + { + _task_barrier_entry.wait(); + + //shortcut references to local data structures + buffers_info_type &buff_info = get_curr_buffer_info(); + per_buffer_info_type &info = buff_info[index]; + const rx_streamer::buffs_type &buffs = *_convert_buffs; + + //fill IO buffs with pointers into the output buffer + void *io_buffs[4/*max interleave*/]; + for (size_t i = 0; i < _num_outputs; i++){ + char *b = reinterpret_cast<char *>(buffs[index*_num_outputs + i]); + io_buffs[i] = b + _convert_buffer_offset_bytes; } + const ref_vector<void *> out_buffs(io_buffs, _num_outputs); - return nsamps_to_copy_per_io_buff; + //perform the conversion operation + _converter->conv(info.copy_buff, out_buffs, _convert_nsamps); + + //advance the pointer for the source buffer + info.copy_buff += _convert_bytes_to_copy; + + //release the buffer if fully consumed + if (buff_info.data_bytes_to_copy == _convert_bytes_to_copy){ + info.buff.reset(); //effectively a release + } + + _task_barrier_exit.wait(); } + + //! Shared variables for the worker threads + reusable_barrier _task_barrier_entry, _task_barrier_exit; + std::vector<task::sptr> _task_handlers; + size_t _convert_nsamps; + const rx_streamer::buffs_type *_convert_buffs; + size_t _convert_buffer_offset_bytes; + size_t _convert_bytes_to_copy; + }; class recv_packet_streamer : public recv_packet_handler, public rx_streamer{ diff --git a/host/lib/transport/super_send_packet_handler.hpp b/host/lib/transport/super_send_packet_handler.hpp index 46c98afea..74e893e67 100644 --- a/host/lib/transport/super_send_packet_handler.hpp +++ b/host/lib/transport/super_send_packet_handler.hpp @@ -23,6 +23,8 @@ #include <uhd/convert.hpp> #include <uhd/stream.hpp> #include <uhd/utils/msg.hpp> +#include <uhd/utils/tasks.hpp> +#include <uhd/utils/atomic.hpp> #include <uhd/utils/byteswap.hpp> #include <uhd/types/metadata.hpp> #include <uhd/transport/vrt_if_packet.hpp> @@ -58,12 +60,25 @@ public: this->resize(size); } + ~send_packet_handler(void){ + _task_barrier_entry.interrupt(); + _task_barrier_exit.interrupt(); + _task_handlers.clear(); + } + //! Resize the number of transport channels void resize(const size_t size){ if (this->size() == size) return; + _task_handlers.clear(); _props.resize(size); static const boost::uint64_t zero = 0; _zero_buffs.resize(size, &zero); + _task_barrier_entry.resize(size); + _task_barrier_exit.resize(size); + _task_handlers.resize(size); + for (size_t i = 1/*skip 0*/; i < size; i++){ + _task_handlers[i] = task::make(boost::bind(&send_packet_handler::converter_thread_task, this, i)); + }; } //! Get the channel width of this handler @@ -77,6 +92,12 @@ public: _header_offset_words32 = header_offset_words32; } + //! Set the stream ID for a specific channel (or no SID) + void set_xport_chan_sid(const size_t xport_chan, const bool has_sid, const boost::uint32_t sid = 0){ + _props.at(xport_chan).has_sid = has_sid; + _props.at(xport_chan).sid = sid; + } + //! Set the rate of ticks per second void set_tick_rate(const double rate){ _tick_rate = rate; @@ -98,7 +119,7 @@ public: //! Set the conversion routine for all channels void set_converter(const uhd::convert::id_type &id){ - _io_buffs.resize(id.num_inputs); + _num_inputs = id.num_inputs; _converter = uhd::convert::get_converter(id)(); this->set_scale_factor(32767.); //update after setting converter _bytes_per_otw_item = uhd::convert::get_bytes_per_item(id.output_format); @@ -133,7 +154,7 @@ public: //translate the metadata to vrt if packet info vrt::if_packet_info_t if_packet_info; if_packet_info.packet_type = vrt::if_packet_info_t::PACKET_TYPE_DATA; - if_packet_info.has_sid = false; + //if_packet_info.has_sid = false; //set per channel if_packet_info.has_cid = false; if_packet_info.has_tlr = true; if_packet_info.has_tsi = false; @@ -195,10 +216,14 @@ private: size_t _header_offset_words32; double _tick_rate, _samp_rate; struct xport_chan_props_type{ + xport_chan_props_type(void):has_sid(false){} get_buff_type get_buff; + bool has_sid; + boost::uint32_t sid; + managed_send_buffer::sptr buff; }; std::vector<xport_chan_props_type> _props; - std::vector<const void *> _io_buffs; //used in conversion + size_t _num_inputs; size_t _bytes_per_otw_item; //used in conversion size_t _bytes_per_cpu_item; //used in conversion uhd::convert::converter::sptr _converter; //used in conversion @@ -217,36 +242,77 @@ private: const size_t buffer_offset_bytes = 0 ){ //load the rest of the if_packet_info in here - if_packet_info.num_payload_bytes = nsamps_per_buff*_io_buffs.size()*_bytes_per_otw_item; + if_packet_info.num_payload_bytes = nsamps_per_buff*_num_inputs*_bytes_per_otw_item; if_packet_info.num_payload_words32 = (if_packet_info.num_payload_bytes + 3/*round up*/)/sizeof(boost::uint32_t); if_packet_info.packet_count = _next_packet_seq; - size_t buff_index = 0; + //get a buffer for each channel or timeout BOOST_FOREACH(xport_chan_props_type &props, _props){ - managed_send_buffer::sptr buff = props.get_buff(timeout); - if (buff.get() == NULL) return 0; //timeout - - //fill a vector with pointers to the io buffers - BOOST_FOREACH(const void *&io_buff, _io_buffs){ - io_buff = reinterpret_cast<const char *>(buffs[buff_index++]) + buffer_offset_bytes; - } - boost::uint32_t *otw_mem = buff->cast<boost::uint32_t *>() + _header_offset_words32; - - //pack metadata into a vrt header - _vrt_packer(otw_mem, if_packet_info); - otw_mem += if_packet_info.num_header_words32; + if (not props.buff) props.buff = props.get_buff(timeout); + if (not props.buff) return 0; //timeout + } - //copy-convert the samples into the send buffer - _converter->conv(_io_buffs, otw_mem, nsamps_per_buff); + //setup the data to share with converter threads + _convert_nsamps = nsamps_per_buff; + _convert_buffs = &buffs; + _convert_buffer_offset_bytes = buffer_offset_bytes; + _convert_if_packet_info = &if_packet_info; - //commit the samples to the zero-copy interface - size_t num_bytes_total = (_header_offset_words32+if_packet_info.num_packet_words32)*sizeof(boost::uint32_t); - buff->commit(num_bytes_total); + //perform N channels of conversion + converter_thread_task(0); - } _next_packet_seq++; //increment sequence after commits return nsamps_per_buff; } + + /******************************************************************* + * Perform one thread's work of the conversion task. + * The entry and exit use a dual synchronization barrier, + * to wait for data to become ready and block until completion. + ******************************************************************/ + UHD_INLINE void converter_thread_task(const size_t index) + { + _task_barrier_entry.wait(); + + //shortcut references to local data structures + managed_send_buffer::sptr &buff = _props[index].buff; + vrt::if_packet_info_t if_packet_info = *_convert_if_packet_info; + const tx_streamer::buffs_type &buffs = *_convert_buffs; + + //fill IO buffs with pointers into the output buffer + const void *io_buffs[4/*max interleave*/]; + for (size_t i = 0; i < _num_inputs; i++){ + const char *b = reinterpret_cast<const char *>(buffs[index*_num_inputs + i]); + io_buffs[i] = b + _convert_buffer_offset_bytes; + } + const ref_vector<const void *> in_buffs(io_buffs, _num_inputs); + + //pack metadata into a vrt header + boost::uint32_t *otw_mem = buff->cast<boost::uint32_t *>() + _header_offset_words32; + if_packet_info.has_sid = _props[index].has_sid; + if_packet_info.sid = _props[index].sid; + _vrt_packer(otw_mem, if_packet_info); + otw_mem += if_packet_info.num_header_words32; + + //perform the conversion operation + _converter->conv(in_buffs, otw_mem, _convert_nsamps); + + //commit the samples to the zero-copy interface + const size_t num_vita_words32 = _header_offset_words32+if_packet_info.num_packet_words32; + buff->commit(num_vita_words32*sizeof(boost::uint32_t)); + buff.reset(); //effectively a release + + _task_barrier_exit.wait(); + } + + //! Shared variables for the worker threads + reusable_barrier _task_barrier_entry, _task_barrier_exit; + std::vector<task::sptr> _task_handlers; + size_t _convert_nsamps; + const tx_streamer::buffs_type *_convert_buffs; + size_t _convert_buffer_offset_bytes; + vrt::if_packet_info_t *_convert_if_packet_info; + }; class send_packet_streamer : public send_packet_handler, public tx_streamer{ diff --git a/host/lib/transport/udp_wsa_zero_copy.cpp b/host/lib/transport/udp_wsa_zero_copy.cpp new file mode 100644 index 000000000..6fe4e3cad --- /dev/null +++ b/host/lib/transport/udp_wsa_zero_copy.cpp @@ -0,0 +1,300 @@ +// +// Copyright 2010-2011 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 "udp_common.hpp" +#include <uhd/transport/udp_zero_copy.hpp> +#include <uhd/transport/udp_simple.hpp> //mtu +#include <uhd/transport/buffer_pool.hpp> +#include <uhd/utils/msg.hpp> +#include <uhd/utils/log.hpp> +#include <boost/format.hpp> +#include <vector> + +using namespace uhd; +using namespace uhd::transport; +namespace asio = boost::asio; + +//A reasonable number of frames for send/recv and async/sync +static const size_t DEFAULT_NUM_FRAMES = 32; + +/*********************************************************************** + * Check registry for correct fast-path setting (windows only) + **********************************************************************/ +#ifdef HAVE_ATLBASE_H +#define CHECK_REG_SEND_THRESH +#include <atlbase.h> //CRegKey +static void check_registry_for_fast_send_threshold(const size_t mtu){ + static bool warned = false; + if (warned) return; //only allow one printed warning per process + + CRegKey reg_key; + DWORD threshold = 1024; //system default when threshold is not specified + if ( + reg_key.Open(HKEY_LOCAL_MACHINE, "System\\CurrentControlSet\\Services\\AFD\\Parameters", KEY_READ) != ERROR_SUCCESS or + reg_key.QueryDWORDValue("FastSendDatagramThreshold", threshold) != ERROR_SUCCESS or threshold < mtu + ){ + UHD_MSG(warning) << boost::format( + "The MTU (%d) is larger than the FastSendDatagramThreshold (%d)!\n" + "This will negatively affect the transmit performance.\n" + "See the transport application notes for more detail.\n" + ) % mtu % threshold << std::endl; + warned = true; + } + reg_key.Close(); +} +#endif /*HAVE_ATLBASE_H*/ + +/*********************************************************************** + * Static initialization to take care of WSA init and cleanup + **********************************************************************/ +struct uhd_wsa_control{ + uhd_wsa_control(void){ + WSADATA wsaData; + WSAStartup(MAKEWORD(2, 2), &wsaData); /*windows socket startup */ + } + + ~uhd_wsa_control(void){ + WSACleanup(); + } +}; + +/*********************************************************************** + * Reusable managed receiver buffer: + * - Initialize with memory and a release callback. + * - Call get new with a length in bytes to re-use. + **********************************************************************/ +class udp_zero_copy_asio_mrb : public managed_recv_buffer{ +public: + udp_zero_copy_asio_mrb(void *mem, int sock_fd, const size_t frame_size): + _sock_fd(sock_fd), _frame_size(frame_size) + { + _wsa_buff.buf = reinterpret_cast<char *>(mem); + ZeroMemory(&_overlapped, sizeof(_overlapped)); + _overlapped.hEvent = WSACreateEvent(); + UHD_ASSERT_THROW(_overlapped.hEvent != WSA_INVALID_EVENT); + this->release(); //makes buffer available via get_new + } + + ~udp_zero_copy_asio_mrb(void){ + WSACloseEvent(_overlapped.hEvent); + } + + void release(void){ + _wsa_buff.len = _frame_size; + _flags = 0; + WSARecv(_sock_fd, &_wsa_buff, 1, &_wsa_buff.len, &_flags, &_overlapped, NULL); + } + + UHD_INLINE sptr get_new(const double timeout, size_t &index){ + const DWORD result = WSAWaitForMultipleEvents( + 1, &_overlapped.hEvent, true, DWORD(timeout*1000), true + ); + if (result == WSA_WAIT_TIMEOUT) return managed_recv_buffer::sptr(); + index++; //advances the caller's buffer + + WSAGetOverlappedResult(_sock_fd, &_overlapped, &_wsa_buff.len, true, &_flags); + + WSAResetEvent(_overlapped.hEvent); + return make(this, _wsa_buff.buf, _wsa_buff.len); + } + +private: + int _sock_fd; + const size_t _frame_size; + WSAOVERLAPPED _overlapped; + WSABUF _wsa_buff; + DWORD _flags; +}; + +/*********************************************************************** + * Reusable managed send buffer: + * - committing the buffer calls the asynchronous socket send + * - getting a new buffer performs the blocking wait for completion + **********************************************************************/ +class udp_zero_copy_asio_msb : public managed_send_buffer{ +public: + udp_zero_copy_asio_msb(void *mem, int sock_fd, const size_t frame_size): + _sock_fd(sock_fd), _frame_size(frame_size) + { + _wsa_buff.buf = reinterpret_cast<char *>(mem); + ZeroMemory(&_overlapped, sizeof(_overlapped)); + _overlapped.hEvent = WSACreateEvent(); + UHD_ASSERT_THROW(_overlapped.hEvent != WSA_INVALID_EVENT); + WSASetEvent(_overlapped.hEvent); //makes buffer available via get_new + } + + ~udp_zero_copy_asio_msb(void){ + WSACloseEvent(_overlapped.hEvent); + } + + void release(void){ + _wsa_buff.len = size(); + WSASend(_sock_fd, &_wsa_buff, 1, NULL, 0, &_overlapped, NULL); + } + + UHD_INLINE sptr get_new(const double timeout, size_t &index){ + const DWORD result = WSAWaitForMultipleEvents( + 1, &_overlapped.hEvent, true, DWORD(timeout*1000), true + ); + if (result == WSA_WAIT_TIMEOUT) return managed_send_buffer::sptr(); + index++; //advances the caller's buffer + + WSAResetEvent(_overlapped.hEvent); + _wsa_buff.len = _frame_size; + return make(this, _wsa_buff.buf, _wsa_buff.len); + } + +private: + int _sock_fd; + const size_t _frame_size; + WSAOVERLAPPED _overlapped; + WSABUF _wsa_buff; +}; + +/*********************************************************************** + * Zero Copy UDP implementation with WSA: + * + * This is not a true zero copy implementation as each + * send and recv requires a copy operation to/from userspace. + * + * For receive, use a blocking recv() call on the socket. + * This has better performance than the overlapped IO. + * For send, use overlapped IO to submit async sends. + **********************************************************************/ +class udp_zero_copy_wsa_impl : public udp_zero_copy{ +public: + typedef boost::shared_ptr<udp_zero_copy_wsa_impl> sptr; + + udp_zero_copy_wsa_impl( + const std::string &addr, + const std::string &port, + const device_addr_t &hints + ): + _recv_frame_size(size_t(hints.cast<double>("recv_frame_size", udp_simple::mtu))), + _num_recv_frames(size_t(hints.cast<double>("num_recv_frames", DEFAULT_NUM_FRAMES))), + _send_frame_size(size_t(hints.cast<double>("send_frame_size", udp_simple::mtu))), + _num_send_frames(size_t(hints.cast<double>("num_send_frames", DEFAULT_NUM_FRAMES))), + _recv_buffer_pool(buffer_pool::make(_num_recv_frames, _recv_frame_size)), + _send_buffer_pool(buffer_pool::make(_num_send_frames, _send_frame_size)), + _next_recv_buff_index(0), _next_send_buff_index(0) + { + #ifdef CHECK_REG_SEND_THRESH + check_registry_for_fast_send_threshold(this->get_send_frame_size()); + #endif /*CHECK_REG_SEND_THRESH*/ + + UHD_MSG(status) << boost::format("Creating WSA UDP transport for %s:%s") % addr % port << std::endl; + static uhd_wsa_control uhd_wsa; //makes wsa start happen via lazy initialization + + UHD_ASSERT_THROW(_num_send_frames <= WSA_MAXIMUM_WAIT_EVENTS); + + //resolve the address + asio::io_service io_service; + asio::ip::udp::resolver resolver(io_service); + asio::ip::udp::resolver::query query(asio::ip::udp::v4(), addr, port); + asio::ip::udp::endpoint receiver_endpoint = *resolver.resolve(query); + + //create the socket + _sock_fd = WSASocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP, NULL, 0, WSA_FLAG_OVERLAPPED); + if (_sock_fd == INVALID_SOCKET){ + const DWORD error = WSAGetLastError(); + throw uhd::os_error(str(boost::format("WSASocket() failed with error %d") % error)); + } + + //set the socket non-blocking for recv + //u_long mode = 1; + //ioctlsocket(_sock_fd, FIONBIO, &mode); + + //resize the socket buffers + const int recv_buff_size = int(hints.cast<double>("recv_buff_size", 0.0)); + const int send_buff_size = int(hints.cast<double>("send_buff_size", 0.0)); + if (recv_buff_size > 0) setsockopt(_sock_fd, SOL_SOCKET, SO_RCVBUF, (const char *)&recv_buff_size, sizeof(recv_buff_size)); + if (send_buff_size > 0) setsockopt(_sock_fd, SOL_SOCKET, SO_SNDBUF, (const char *)&send_buff_size, sizeof(send_buff_size)); + + //connect the socket so we can send/recv + const asio::ip::udp::endpoint::data_type &servaddr = *receiver_endpoint.data(); + if (WSAConnect(_sock_fd, (const struct sockaddr *)&servaddr, sizeof(servaddr), NULL, NULL, NULL, NULL) != 0){ + const DWORD error = WSAGetLastError(); + closesocket(_sock_fd); + throw uhd::os_error(str(boost::format("WSAConnect() failed with error %d") % error)); + } + + //allocate re-usable managed receive buffers + for (size_t i = 0; i < get_num_recv_frames(); i++){ + _mrb_pool.push_back(boost::shared_ptr<udp_zero_copy_asio_mrb>( + new udp_zero_copy_asio_mrb(_recv_buffer_pool->at(i), _sock_fd, get_recv_frame_size()) + )); + } + + //allocate re-usable managed send buffers + for (size_t i = 0; i < get_num_send_frames(); i++){ + _msb_pool.push_back(boost::shared_ptr<udp_zero_copy_asio_msb>( + new udp_zero_copy_asio_msb(_send_buffer_pool->at(i), _sock_fd, get_send_frame_size()) + )); + } + } + + ~udp_zero_copy_wsa_impl(void){ + closesocket(_sock_fd); + } + + /******************************************************************* + * Receive implementation: + * Block on the managed buffer's get call and advance the index. + ******************************************************************/ + managed_recv_buffer::sptr get_recv_buff(double timeout){ + if (_next_recv_buff_index == _num_recv_frames) _next_recv_buff_index = 0; + return _mrb_pool[_next_recv_buff_index]->get_new(timeout, _next_recv_buff_index); + } + + size_t get_num_recv_frames(void) const {return _num_recv_frames;} + size_t get_recv_frame_size(void) const {return _recv_frame_size;} + + /******************************************************************* + * Send implementation: + * Block on the managed buffer's get call and advance the index. + ******************************************************************/ + managed_send_buffer::sptr get_send_buff(double timeout){ + if (_next_send_buff_index == _num_send_frames) _next_send_buff_index = 0; + return _msb_pool[_next_send_buff_index]->get_new(timeout, _next_send_buff_index); + } + + size_t get_num_send_frames(void) const {return _num_send_frames;} + size_t get_send_frame_size(void) const {return _send_frame_size;} + +private: + //memory management -> buffers and fifos + const size_t _recv_frame_size, _num_recv_frames; + const size_t _send_frame_size, _num_send_frames; + buffer_pool::sptr _recv_buffer_pool, _send_buffer_pool; + std::vector<boost::shared_ptr<udp_zero_copy_asio_msb> > _msb_pool; + std::vector<boost::shared_ptr<udp_zero_copy_asio_mrb> > _mrb_pool; + size_t _next_recv_buff_index, _next_send_buff_index; + + //socket guts + SOCKET _sock_fd; +}; + +/*********************************************************************** + * UDP zero copy make function + **********************************************************************/ +udp_zero_copy::sptr udp_zero_copy::make( + const std::string &addr, + const std::string &port, + const device_addr_t &hints +){ + return sptr(new udp_zero_copy_wsa_impl(addr, port, hints)); +} diff --git a/host/lib/transport/udp_zero_copy.cpp b/host/lib/transport/udp_zero_copy.cpp index 0ccc92b82..9125be53a 100644 --- a/host/lib/transport/udp_zero_copy.cpp +++ b/host/lib/transport/udp_zero_copy.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-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 @@ -18,12 +18,13 @@ #include "udp_common.hpp" #include <uhd/transport/udp_zero_copy.hpp> #include <uhd/transport/udp_simple.hpp> //mtu -#include <uhd/transport/bounded_buffer.hpp> #include <uhd/transport/buffer_pool.hpp> #include <uhd/utils/msg.hpp> #include <uhd/utils/log.hpp> +#include <uhd/utils/atomic.hpp> #include <boost/format.hpp> -#include <list> +#include <boost/make_shared.hpp> +#include <vector> using namespace uhd; using namespace uhd::transport; @@ -61,66 +62,71 @@ static void check_registry_for_fast_send_threshold(const size_t mtu){ /*********************************************************************** * Reusable managed receiver buffer: - * - Initialize with memory and a release callback. - * - Call get new with a length in bytes to re-use. + * - get_new performs the recv operation **********************************************************************/ class udp_zero_copy_asio_mrb : public managed_recv_buffer{ public: - udp_zero_copy_asio_mrb(void *mem, bounded_buffer<udp_zero_copy_asio_mrb *> &pending): - _mem(mem), _len(0), _pending(pending){/* NOP */} + udp_zero_copy_asio_mrb(void *mem, int sock_fd, const size_t frame_size): + _mem(mem), _sock_fd(sock_fd), _frame_size(frame_size) { /*NOP*/ } void release(void){ - if (_len == 0) return; - _pending.push_with_haste(this); - _len = 0; + _claimer.release(); } - sptr get_new(size_t len){ - _len = len; - return make_managed_buffer(this); - } + UHD_INLINE sptr get_new(const double timeout, size_t &index){ + if (not _claimer.claim_with_wait(timeout)) return sptr(); - template <class T> T cast(void) const{return static_cast<T>(_mem);} + #ifdef MSG_DONTWAIT //try a non-blocking recv() if supported + _len = ::recv(_sock_fd, (char *)_mem, _frame_size, MSG_DONTWAIT); + if (_len > 0){ + index++; //advances the caller's buffer + return make(this, _mem, size_t(_len)); + } + #endif -private: - const void *get_buff(void) const{return _mem;} - size_t get_size(void) const{return _len;} + if (wait_for_recv_ready(_sock_fd, timeout)){ + _len = ::recv(_sock_fd, (char *)_mem, _frame_size, 0); + index++; //advances the caller's buffer + return make(this, _mem, size_t(_len)); + } + _claimer.release(); //undo claim + return sptr(); //null for timeout + } + +private: void *_mem; - size_t _len; - bounded_buffer<udp_zero_copy_asio_mrb *> &_pending; + int _sock_fd; + size_t _frame_size; + ssize_t _len; + simple_claimer _claimer; }; /*********************************************************************** * Reusable managed send buffer: - * - Initialize with memory and a commit callback. - * - Call get new with a length in bytes to re-use. + * - commit performs the send operation **********************************************************************/ class udp_zero_copy_asio_msb : public managed_send_buffer{ public: - udp_zero_copy_asio_msb(void *mem, bounded_buffer<udp_zero_copy_asio_msb *> &pending, int sock_fd): - _mem(mem), _len(0), _pending(pending), _sock_fd(sock_fd){/* NOP */} - - void commit(size_t len){ - if (_len == 0) return; - ::send(_sock_fd, this->cast<const char *>(), len, 0); - _pending.push_with_haste(this); - _len = 0; + udp_zero_copy_asio_msb(void *mem, int sock_fd, const size_t frame_size): + _mem(mem), _sock_fd(sock_fd), _frame_size(frame_size) { /*NOP*/ } + + void release(void){ + UHD_ASSERT_THROW(::send(_sock_fd, (const char *)_mem, size(), 0) == ssize_t(size())); + _claimer.release(); } - sptr get_new(size_t len){ - _len = len; - return make_managed_buffer(this); + UHD_INLINE sptr get_new(const double timeout, size_t &index){ + if (not _claimer.claim_with_wait(timeout)) return sptr(); + index++; //advances the caller's buffer + return make(this, _mem, _frame_size); } private: - void *get_buff(void) const{return _mem;} - size_t get_size(void) const{return _len;} - void *_mem; - size_t _len; - bounded_buffer<udp_zero_copy_asio_msb *> &_pending; int _sock_fd; + size_t _frame_size; + simple_claimer _claimer; }; /*********************************************************************** @@ -145,8 +151,7 @@ public: _num_send_frames(size_t(hints.cast<double>("num_send_frames", DEFAULT_NUM_FRAMES))), _recv_buffer_pool(buffer_pool::make(_num_recv_frames, _recv_frame_size)), _send_buffer_pool(buffer_pool::make(_num_send_frames, _send_frame_size)), - _pending_recv_buffs(_num_recv_frames), - _pending_send_buffs(_num_send_frames) + _next_recv_buff_index(0), _next_send_buff_index(0) { UHD_LOG << boost::format("Creating udp transport for %s %s") % addr % port << std::endl; @@ -167,18 +172,16 @@ public: //allocate re-usable managed receive buffers for (size_t i = 0; i < get_num_recv_frames(); i++){ - _mrb_pool.push_back(udp_zero_copy_asio_mrb( - _recv_buffer_pool->at(i), _pending_recv_buffs + _mrb_pool.push_back(boost::make_shared<udp_zero_copy_asio_mrb>( + _recv_buffer_pool->at(i), _sock_fd, get_recv_frame_size() )); - _pending_recv_buffs.push_with_haste(&_mrb_pool.back()); } //allocate re-usable managed send buffers for (size_t i = 0; i < get_num_send_frames(); i++){ - _msb_pool.push_back(udp_zero_copy_asio_msb( - _send_buffer_pool->at(i), _pending_send_buffs, _sock_fd + _msb_pool.push_back(boost::make_shared<udp_zero_copy_asio_msb>( + _send_buffer_pool->at(i), _sock_fd, get_send_frame_size() )); - _pending_send_buffs.push_with_haste(&_msb_pool.back()); } } @@ -198,29 +201,11 @@ public: /******************************************************************* * Receive implementation: - * - * Perform a non-blocking receive for performance, - * and then fall back to a blocking receive with timeout. - * Return the managed receive buffer with the new length. - * When the caller is finished with the managed buffer, - * the managed receive buffer is released back into the queue. + * Block on the managed buffer's get call and advance the index. ******************************************************************/ managed_recv_buffer::sptr get_recv_buff(double timeout){ - udp_zero_copy_asio_mrb *mrb = NULL; - if (_pending_recv_buffs.pop_with_timed_wait(mrb, timeout)){ - - #ifdef MSG_DONTWAIT //try a non-blocking recv() if supported - ssize_t ret = ::recv(_sock_fd, mrb->cast<char *>(), _recv_frame_size, MSG_DONTWAIT); - if (ret > 0) return mrb->get_new(ret); - #endif - - if (wait_for_recv_ready(_sock_fd, timeout)) return mrb->get_new( - ::recv(_sock_fd, mrb->cast<char *>(), _recv_frame_size, 0) - ); - - _pending_recv_buffs.push_with_haste(mrb); //timeout: return the managed buffer to the queue - } - return managed_recv_buffer::sptr(); + if (_next_recv_buff_index == _num_recv_frames) _next_recv_buff_index = 0; + return _mrb_pool[_next_recv_buff_index]->get_new(timeout, _next_recv_buff_index); } size_t get_num_recv_frames(void) const {return _num_recv_frames;} @@ -228,18 +213,11 @@ public: /******************************************************************* * Send implementation: - * - * Get a managed receive buffer immediately with max length set. - * The caller will fill the buffer and commit it when finished. - * The commit routine will perform a blocking send operation, - * and push the managed send buffer back into the queue. + * Block on the managed buffer's get call and advance the index. ******************************************************************/ managed_send_buffer::sptr get_send_buff(double timeout){ - udp_zero_copy_asio_msb *msb = NULL; - if (_pending_send_buffs.pop_with_timed_wait(msb, timeout)){ - return msb->get_new(_send_frame_size); - } - return managed_send_buffer::sptr(); + if (_next_send_buff_index == _num_send_frames) _next_send_buff_index = 0; + return _msb_pool[_next_send_buff_index]->get_new(timeout, _next_send_buff_index); } size_t get_num_send_frames(void) const {return _num_send_frames;} @@ -250,10 +228,9 @@ private: const size_t _recv_frame_size, _num_recv_frames; const size_t _send_frame_size, _num_send_frames; buffer_pool::sptr _recv_buffer_pool, _send_buffer_pool; - bounded_buffer<udp_zero_copy_asio_mrb *> _pending_recv_buffs; - bounded_buffer<udp_zero_copy_asio_msb *> _pending_send_buffs; - std::list<udp_zero_copy_asio_msb> _msb_pool; - std::list<udp_zero_copy_asio_mrb> _mrb_pool; + std::vector<boost::shared_ptr<udp_zero_copy_asio_msb> > _msb_pool; + std::vector<boost::shared_ptr<udp_zero_copy_asio_mrb> > _mrb_pool; + size_t _next_recv_buff_index, _next_send_buff_index; //asio guts -> socket and service asio::io_service _io_service; diff --git a/host/lib/transport/usb_zero_copy_wrapper.cpp b/host/lib/transport/usb_zero_copy_wrapper.cpp index 3571ed856..d04244ca9 100644 --- a/host/lib/transport/usb_zero_copy_wrapper.cpp +++ b/host/lib/transport/usb_zero_copy_wrapper.cpp @@ -16,45 +16,64 @@ // #include <uhd/transport/usb_zero_copy.hpp> -#include <uhd/transport/bounded_buffer.hpp> #include <uhd/transport/buffer_pool.hpp> #include <uhd/utils/byteswap.hpp> #include <uhd/utils/msg.hpp> +#include <uhd/utils/tasks.hpp> +#include <uhd/utils/atomic.hpp> #include <boost/foreach.hpp> +#include <boost/make_shared.hpp> +#include <boost/thread/mutex.hpp> +#include <boost/thread/condition_variable.hpp> +#include <boost/bind.hpp> #include <vector> #include <iostream> +using namespace uhd; using namespace uhd::transport; +static const boost::posix_time::time_duration AUTOFLUSH_TIMEOUT(boost::posix_time::milliseconds(1)); + /*********************************************************************** * USB zero copy wrapper - managed receive buffer **********************************************************************/ class usb_zero_copy_wrapper_mrb : public managed_recv_buffer{ public: - usb_zero_copy_wrapper_mrb(bounded_buffer<usb_zero_copy_wrapper_mrb *> &queue): - _queue(queue){/*NOP*/} + usb_zero_copy_wrapper_mrb(void){/*NOP*/} void release(void){ - if (not _mrb) return; _mrb.reset(); //decrement ref count, other MRB's may hold a ref - _queue.push_with_haste(this); + _claimer.release(); } - UHD_INLINE sptr get_new(managed_recv_buffer::sptr mrb, const void *mem, size_t len){ + UHD_INLINE sptr get_new( + managed_recv_buffer::sptr &mrb, size_t &offset_bytes, + const double timeout, size_t &index + ){ + if (not mrb or not _claimer.claim_with_wait(timeout)) return sptr(); + + index++; //advances the caller's buffer + + //hold a copy of the buffer shared pointer _mrb = mrb; - _mem = mem; - _len = len; - return make_managed_buffer(this); + + //extract this packet's memory address and length in bytes + char *mem = mrb->cast<char *>() + offset_bytes; + const boost::uint32_t *mem32 = reinterpret_cast<const boost::uint32_t *>(mem); + const size_t words32 = (uhd::wtohx(mem32[0]) & 0xffff); //length in words32 (from VRT header) + const size_t len = words32*sizeof(boost::uint32_t); //length in bytes + + //check if this receive buffer has been exhausted + offset_bytes += len; + if (offset_bytes >= mrb->size()) mrb.reset(); //drop caller's ref + else if (uhd::wtohx(mem32[words32]) == 0) mrb.reset(); + + return make(this, mem, len); } private: - const void *get_buff(void) const{return _mem;} - size_t get_size(void) const{return _len;} - - bounded_buffer<usb_zero_copy_wrapper_mrb *> &_queue; - const void *_mem; - size_t _len; managed_recv_buffer::sptr _mrb; + simple_claimer _claimer; }; /*********************************************************************** @@ -63,16 +82,27 @@ private: class usb_zero_copy_wrapper_msb : public managed_send_buffer{ public: usb_zero_copy_wrapper_msb(const usb_zero_copy::sptr internal, const size_t fragmentation_size): - _internal(internal), _fragmentation_size(fragmentation_size){/*NOP*/} + _internal(internal), _fragmentation_size(fragmentation_size) + { + _ok_to_auto_flush = false; + _task = uhd::task::make(boost::bind(&usb_zero_copy_wrapper_msb::auto_flush, this)); + } - void commit(size_t len){ - if (len == 0) return; + ~usb_zero_copy_wrapper_msb(void) + { + //ensure the task has exited before anything auto deconstructs + _task.reset(); + } + + void release(void){ + boost::mutex::scoped_lock lock(_mutex); + _ok_to_auto_flush = true; //get a reference to the VITA header before incrementing const boost::uint32_t vita_header = reinterpret_cast<const boost::uint32_t *>(_mem_buffer_tip)[0]; - _bytes_in_buffer += len; - _mem_buffer_tip += len; + _bytes_in_buffer += size(); + _mem_buffer_tip += size(); //extract VITA end of packet flag, we must force flush under eof conditions const bool eop = (uhd::wtohx(vita_header) & (0x1 << 24)) != 0; @@ -80,28 +110,53 @@ public: if (eop or full){ _last_send_buff->commit(_bytes_in_buffer); _last_send_buff.reset(); + + //notify the auto-flusher to restart its timed_wait + lock.unlock(); _cond.notify_one(); } } UHD_INLINE sptr get_new(const double timeout){ + boost::mutex::scoped_lock lock(_mutex); + _ok_to_auto_flush = false; + if (not _last_send_buff){ _last_send_buff = _internal->get_send_buff(timeout); if (not _last_send_buff) return sptr(); _mem_buffer_tip = _last_send_buff->cast<char *>(); _bytes_in_buffer = 0; } - return make_managed_buffer(this); + + return make(this, _mem_buffer_tip, _fragmentation_size); } private: - void *get_buff(void) const{return reinterpret_cast<void *>(_mem_buffer_tip);} - size_t get_size(void) const{return _fragmentation_size;} - usb_zero_copy::sptr _internal; const size_t _fragmentation_size; managed_send_buffer::sptr _last_send_buff; size_t _bytes_in_buffer; char *_mem_buffer_tip; + + //private variables for auto flusher + boost::mutex _mutex; + boost::condition_variable _cond; + uhd::task::sptr _task; + bool _ok_to_auto_flush; + + /*! + * The auto flusher ensures that buffers are force committed when + * the user has not called get_new() within a certain time window. + */ + void auto_flush(void) + { + boost::mutex::scoped_lock lock(_mutex); + const bool timeout = not _cond.timed_wait(lock, AUTOFLUSH_TIMEOUT); + if (timeout and _ok_to_auto_flush and _last_send_buff and _bytes_in_buffer != 0) + { + _last_send_buff->commit(_bytes_in_buffer); + _last_send_buff.reset(); + } + } }; /*********************************************************************** @@ -112,44 +167,26 @@ public: usb_zero_copy_wrapper(sptr usb_zc, const size_t frame_boundary): _internal_zc(usb_zc), _frame_boundary(frame_boundary), - _available_recv_buffs(this->get_num_recv_frames()), - _mrb_pool(this->get_num_recv_frames(), usb_zero_copy_wrapper_mrb(_available_recv_buffs)), - _the_only_msb(usb_zero_copy_wrapper_msb(usb_zc, frame_boundary)) + _next_recv_buff_index(0) { - BOOST_FOREACH(usb_zero_copy_wrapper_mrb &mrb, _mrb_pool){ - _available_recv_buffs.push_with_haste(&mrb); + for (size_t i = 0; i < this->get_num_recv_frames(); i++){ + _mrb_pool.push_back(boost::make_shared<usb_zero_copy_wrapper_mrb>()); } + _the_only_msb = boost::make_shared<usb_zero_copy_wrapper_msb>(usb_zc, frame_boundary); } managed_recv_buffer::sptr get_recv_buff(double timeout){ //attempt to get a managed recv buffer - if (not _last_recv_buff.get()){ + if (not _last_recv_buff){ _last_recv_buff = _internal_zc->get_recv_buff(timeout); - _last_recv_offset = 0; + _last_recv_offset = 0; //reset offset into buffer } - //attempt to get a wrapper for a managed recv buffer - usb_zero_copy_wrapper_mrb *wmrb = NULL; - if (_last_recv_buff.get() and _available_recv_buffs.pop_with_timed_wait(wmrb, timeout)){ - //extract this packet's memory address and length in bytes - const char *mem = _last_recv_buff->cast<const char *>() + _last_recv_offset; - const boost::uint32_t *mem32 = reinterpret_cast<const boost::uint32_t *>(mem); - const size_t len = (uhd::wtohx(mem32[0]) & 0xffff)*sizeof(boost::uint32_t); //length in bytes (from VRT header) - - managed_recv_buffer::sptr recv_buff; //the buffer to be returned to the user - recv_buff = wmrb->get_new(_last_recv_buff, mem, len); - _last_recv_offset += len; - - //check if this receive buffer has been exhausted - if (_last_recv_offset >= _last_recv_buff->size()) { - _last_recv_buff.reset(); - } - - return recv_buff; - } - - //otherwise return a null sptr for failure - return managed_recv_buffer::sptr(); + //get the buffer to be returned to the user + if (_next_recv_buff_index == _mrb_pool.size()) _next_recv_buff_index = 0; + return _mrb_pool[_next_recv_buff_index]->get_new( + _last_recv_buff, _last_recv_offset, timeout, _next_recv_buff_index + ); } size_t get_num_recv_frames(void) const{ @@ -161,7 +198,7 @@ public: } managed_send_buffer::sptr get_send_buff(double timeout){ - return _the_only_msb.get_new(timeout); + return _the_only_msb->get_new(timeout); } size_t get_num_send_frames(void) const{ @@ -175,16 +212,13 @@ public: private: sptr _internal_zc; size_t _frame_boundary; - bounded_buffer<usb_zero_copy_wrapper_mrb *> _available_recv_buffs; - std::vector<usb_zero_copy_wrapper_mrb> _mrb_pool; - usb_zero_copy_wrapper_msb _the_only_msb; - - //buffer to store partially-received VRT packets in - buffer_pool::sptr _fragment_mem; + std::vector<boost::shared_ptr<usb_zero_copy_wrapper_mrb> > _mrb_pool; + boost::shared_ptr<usb_zero_copy_wrapper_msb> _the_only_msb; //state for last recv buffer to create multiple managed buffers managed_recv_buffer::sptr _last_recv_buff; size_t _last_recv_offset; + size_t _next_recv_buff_index; }; /*********************************************************************** diff --git a/host/lib/types/ranges.cpp b/host/lib/types/ranges.cpp index 6e39bc688..82a9a84e1 100644 --- a/host/lib/types/ranges.cpp +++ b/host/lib/types/ranges.cpp @@ -1,5 +1,5 @@ // -// Copyright 2011-2011 Ettus Research LLC +// Copyright 2011-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 @@ -27,17 +27,8 @@ using namespace uhd; /*********************************************************************** * range_t implementation code **********************************************************************/ -struct range_t::impl{ - impl(double start, double stop, double step): - start(start), stop(stop), step(step) - { - /* NOP */ - } - double start, stop, step; -}; - range_t::range_t(double value): - _impl(UHD_PIMPL_MAKE(impl, (value, value, 0))) + _start(value), _stop(value), _step(0.0) { /* NOP */ } @@ -45,7 +36,7 @@ range_t::range_t(double value): range_t::range_t( double start, double stop, double step ): - _impl(UHD_PIMPL_MAKE(impl, (start, stop, step))) + _start(start), _stop(stop), _step(step) { if (stop < start){ throw uhd::value_error("cannot make range where stop < start"); @@ -53,15 +44,15 @@ range_t::range_t( } double range_t::start(void) const{ - return _impl->start; + return _start; } double range_t::stop(void) const{ - return _impl->stop; + return _stop; } double range_t::step(void) const{ - return _impl->step; + return _step; } const std::string range_t::to_pp_string(void) const{ diff --git a/host/lib/usrp/b100/CMakeLists.txt b/host/lib/usrp/b100/CMakeLists.txt index 1237f52d1..d2c33b512 100644 --- a/host/lib/usrp/b100/CMakeLists.txt +++ b/host/lib/usrp/b100/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2011 Ettus Research LLC +# Copyright 2011-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 @@ -26,7 +26,6 @@ LIBUHD_REGISTER_COMPONENT("B100" ENABLE_B100 ON "ENABLE_LIBUHD;ENABLE_USB" OFF) IF(ENABLE_B100) LIBUHD_APPEND_SOURCES( - ${CMAKE_CURRENT_SOURCE_DIR}/b100_ctrl.cpp ${CMAKE_CURRENT_SOURCE_DIR}/b100_impl.cpp ${CMAKE_CURRENT_SOURCE_DIR}/clock_ctrl.cpp ${CMAKE_CURRENT_SOURCE_DIR}/codec_ctrl.cpp diff --git a/host/lib/usrp/b100/b100_ctrl.cpp b/host/lib/usrp/b100/b100_ctrl.cpp deleted file mode 100644 index e6136c00e..000000000 --- a/host/lib/usrp/b100/b100_ctrl.cpp +++ /dev/null @@ -1,257 +0,0 @@ -// -// Copyright 2011 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 "b100_ctrl.hpp" -#include <uhd/transport/bounded_buffer.hpp> -#include <uhd/transport/usb_zero_copy.hpp> -#include <uhd/transport/zero_copy.hpp> -#include <uhd/transport/vrt_if_packet.hpp> -#include <uhd/utils/thread_priority.hpp> -#include <uhd/utils/msg.hpp> -#include <uhd/utils/tasks.hpp> -#include <uhd/types/metadata.hpp> -#include <uhd/types/serial.hpp> -#include "ctrl_packet.hpp" -#include <boost/thread/thread.hpp> -#include <boost/bind.hpp> -#include <uhd/exception.hpp> - -using namespace uhd::transport; -using namespace uhd; - -bool b100_ctrl_debug = false; - -class b100_ctrl_impl : public b100_ctrl { -public: - b100_ctrl_impl(uhd::transport::zero_copy_if::sptr ctrl_transport): - sync_ctrl_fifo(2), - _ctrl_transport(ctrl_transport), - _seq(0) - { - viking_marauder = task::make(boost::bind(&b100_ctrl_impl::viking_marauder_loop, this)); - } - - ~b100_ctrl_impl(void){ - //stop the marauder first so it cant access deconstructed objects - viking_marauder.reset(); - } - - int write(boost::uint32_t addr, const ctrl_data_t &data); - ctrl_data_t read(boost::uint32_t addr, size_t len); - - bool get_ctrl_data(ctrl_data_t &pkt_data, double timeout); - - void poke32(wb_addr_type addr, boost::uint32_t data){ - boost::mutex::scoped_lock lock(_ctrl_mutex); - - ctrl_data_t words(2); - words[0] = data & 0x0000FFFF; - words[1] = data >> 16; - this->write(addr, words); - } - - boost::uint32_t peek32(wb_addr_type addr){ - boost::mutex::scoped_lock lock(_ctrl_mutex); - - ctrl_data_t words = this->read(addr, 2); - return boost::uint32_t((boost::uint32_t(words[1]) << 16) | words[0]); - } - - void poke16(wb_addr_type addr, boost::uint16_t data){ - boost::mutex::scoped_lock lock(_ctrl_mutex); - - ctrl_data_t words(1); - words[0] = data; - this->write(addr, words); - } - - boost::uint16_t peek16(wb_addr_type addr){ - boost::mutex::scoped_lock lock(_ctrl_mutex); - - ctrl_data_t words = this->read(addr, 1); - return boost::uint16_t(words[0]); - } - - void set_async_cb(const async_cb_type &async_cb){ - boost::mutex::scoped_lock lock(_async_mutex); - _async_cb = async_cb; - } - -private: - int send_pkt(boost::uint16_t *cmd); - - //änd hërë wë gö ä-Vïkïng för äsynchronous control packets - void viking_marauder_loop(void); - bounded_buffer<ctrl_data_t> sync_ctrl_fifo; - async_cb_type _async_cb; - task::sptr viking_marauder; - - uhd::transport::zero_copy_if::sptr _ctrl_transport; - boost::uint8_t _seq; - boost::mutex _ctrl_mutex, _async_mutex; -}; - -/*********************************************************************** - * helper functions for packing/unpacking control packets - **********************************************************************/ -void pack_ctrl_pkt(boost::uint16_t *pkt_buff, - const ctrl_pkt_t &pkt){ - //first two bits are OP - //next six bits are CALLBACKS - //next 8 bits are SEQUENCE - //next 16 bits are LENGTH (16-bit word) - //next 32 bits are ADDRESS (16-bit word LSW) - //then DATA (28 16-bit words) - pkt_buff[0] = (boost::uint16_t(pkt.pkt_meta.op) << 14) | (boost::uint16_t(pkt.pkt_meta.callbacks) << 8) | pkt.pkt_meta.seq; - pkt_buff[1] = pkt.pkt_meta.len; - pkt_buff[2] = (pkt.pkt_meta.addr & 0x00000FFF); - pkt_buff[3] = 0x0000; //address high bits always 0 on this device - - for(size_t i = 0; i < pkt.data.size(); i++) { - pkt_buff[4+i] = pkt.data[i]; - } -} - -void unpack_ctrl_pkt(const boost::uint16_t *pkt_buff, - ctrl_pkt_t &pkt){ - pkt.pkt_meta.seq = pkt_buff[0] & 0xFF; - pkt.pkt_meta.op = CTRL_PKT_OP_READ; //really this is useless - pkt.pkt_meta.len = pkt_buff[1]; - pkt.pkt_meta.callbacks = 0; //callbacks aren't implemented yet - pkt.pkt_meta.addr = pkt_buff[2] | boost::uint32_t(pkt_buff[3] << 16); - - //let's check this so we don't go pushing 64K of crap onto the pkt - if(pkt.pkt_meta.len > CTRL_PACKET_DATA_LENGTH) { - throw uhd::runtime_error("Received control packet too long"); - } - - for(int i = 4; i < 4+pkt.pkt_meta.len; i++) pkt.data.push_back(pkt_buff[i]); -} - -int b100_ctrl_impl::send_pkt(boost::uint16_t *cmd) { - managed_send_buffer::sptr sbuf = _ctrl_transport->get_send_buff(); - if(!sbuf.get()) { - throw uhd::runtime_error("Control channel send error"); - } - - //FIXME there's a better way to do this - for(size_t i = 0; i < (CTRL_PACKET_LENGTH / sizeof(boost::uint16_t)); i++) { - sbuf->cast<boost::uint16_t *>()[i] = cmd[i]; - } - sbuf->commit(CTRL_PACKET_LENGTH); //fixed size transaction - return 0; -} - -int b100_ctrl_impl::write(boost::uint32_t addr, const ctrl_data_t &data) { - UHD_ASSERT_THROW(data.size() <= (CTRL_PACKET_DATA_LENGTH / sizeof(boost::uint16_t))); - ctrl_pkt_t pkt; - pkt.data = data; - pkt.pkt_meta.op = CTRL_PKT_OP_WRITE; - pkt.pkt_meta.callbacks = 0; - pkt.pkt_meta.seq = _seq++; - pkt.pkt_meta.len = pkt.data.size(); - pkt.pkt_meta.addr = addr; - boost::uint16_t pkt_buff[CTRL_PACKET_LENGTH / sizeof(boost::uint16_t)] = {}; - - pack_ctrl_pkt(pkt_buff, pkt); - size_t result = send_pkt(pkt_buff); - return result; -} - -ctrl_data_t b100_ctrl_impl::read(boost::uint32_t addr, size_t len) { - UHD_ASSERT_THROW(len <= (CTRL_PACKET_DATA_LENGTH / sizeof(boost::uint16_t))); - - ctrl_pkt_t pkt; - pkt.pkt_meta.op = CTRL_PKT_OP_READ; - pkt.pkt_meta.callbacks = 0; - pkt.pkt_meta.seq = _seq++; - pkt.pkt_meta.len = len; - pkt.pkt_meta.addr = addr; - boost::uint16_t pkt_buff[CTRL_PACKET_LENGTH / sizeof(boost::uint16_t)] = {}; - - //flush anything that might be in the queue - while (get_ctrl_data(pkt.data, 0.0)){ - UHD_MSG(error) << "B100: control read found unexpected packet." << std::endl; - } - - pack_ctrl_pkt(pkt_buff, pkt); - send_pkt(pkt_buff); - - //block with timeout waiting for the response to appear - if (not get_ctrl_data(pkt.data, 0.1)) throw uhd::runtime_error( - "B100: timeout waiting for control response packet." - ); - - return pkt.data; -} - -/*********************************************************************** - * Viking marauders go pillaging for asynchronous control packets in the - * control response endpoint. Sync packets go in sync_ctrl_fifo, - * async TX error messages go in async_msg_fifo. sync_ctrl_fifo should - * never have more than 1 message in it, since it's expected that we'll - * wait for a control operation to finish before starting another one. - **********************************************************************/ -void b100_ctrl_impl::viking_marauder_loop(void){ - set_thread_priority_safe(); - - while (not boost::this_thread::interruption_requested()){ - managed_recv_buffer::sptr rbuf = _ctrl_transport->get_recv_buff(1.0); - if(rbuf.get() == NULL) continue; //that's ok, there are plenty of villages to pillage! - const boost::uint16_t *pkt_buf = rbuf->cast<const boost::uint16_t *>(); - - if(pkt_buf[0] >> 8 == CTRL_PACKET_HEADER_MAGIC) { - //so it's got a control packet header, let's parse it. - ctrl_pkt_t pkt; - unpack_ctrl_pkt(pkt_buf, pkt); - - if(pkt.pkt_meta.seq != boost::uint8_t(_seq - 1)) { - UHD_MSG(error) - << "Sequence error on control channel." << std::endl - << "Exiting control loop." << std::endl - ; - return; - } - if(pkt.pkt_meta.len > (CTRL_PACKET_LENGTH - CTRL_PACKET_HEADER_LENGTH)) { - UHD_MSG(error) - << "Control channel packet length too long" << std::endl - << "Exiting control loop." << std::endl - ; - return; - } - - //push it onto the queue - sync_ctrl_fifo.push_with_pop_on_full(pkt.data); - } - else{ //otherwise let the async callback handle it - boost::mutex::scoped_lock lock(_async_mutex); - if (not _async_cb.empty()) _async_cb(rbuf); - } - } -} - -bool b100_ctrl_impl::get_ctrl_data(ctrl_data_t &pkt_data, double timeout){ - boost::this_thread::disable_interruption di; //disable because the wait can throw - return sync_ctrl_fifo.pop_with_timed_wait(pkt_data, timeout); -} - -/*********************************************************************** - * Public make function for b100_ctrl interface - **********************************************************************/ -b100_ctrl::sptr b100_ctrl::make(uhd::transport::zero_copy_if::sptr ctrl_transport){ - return sptr(new b100_ctrl_impl(ctrl_transport)); -} diff --git a/host/lib/usrp/b100/b100_ctrl.hpp b/host/lib/usrp/b100/b100_ctrl.hpp deleted file mode 100644 index 74884d525..000000000 --- a/host/lib/usrp/b100/b100_ctrl.hpp +++ /dev/null @@ -1,70 +0,0 @@ -// -// Copyright 2011 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_B100_CTRL_HPP -#define INCLUDED_B100_CTRL_HPP - -#include "wb_iface.hpp" -#include <uhd/transport/usb_zero_copy.hpp> -#include <uhd/types/serial.hpp> -#include <boost/shared_ptr.hpp> -#include <boost/utility.hpp> -#include "ctrl_packet.hpp" -#include <boost/function.hpp> - -class b100_ctrl : boost::noncopyable, public wb_iface{ -public: - typedef boost::shared_ptr<b100_ctrl> sptr; - typedef boost::function<void(uhd::transport::managed_recv_buffer::sptr)> async_cb_type; - - /*! - * Make a USRP control object from a data transport - * \param ctrl_transport a USB data transport - * \return a new b100 control object - */ - static sptr make(uhd::transport::zero_copy_if::sptr ctrl_transport); - - //! set an async callback for messages - virtual void set_async_cb(const async_cb_type &async_cb) = 0; - - /*! - * Write a byte vector to an FPGA register - * \param addr the FPGA register address - * \param bytes the data to write - * \return 0 on success, error code on failure - */ - virtual int write(boost::uint32_t addr, const ctrl_data_t &data) = 0; - - /*! - * Read a byte vector from an FPGA register (blocking read) - * \param addr the FPGA register address - * \param len the length of the read - * \return a vector of bytes from the register(s) in question - */ - virtual ctrl_data_t read(boost::uint32_t addr, size_t len) = 0; - - /*! - * Get a sync ctrl packet (blocking) - * \param the packet data buffer - * \param the timeout value - * \return true if it got something - */ - virtual bool get_ctrl_data(ctrl_data_t &pkt_data, double timeout) = 0; - -}; - -#endif /* INCLUDED_B100_CTRL_HPP */ diff --git a/host/lib/usrp/b100/b100_impl.cpp b/host/lib/usrp/b100/b100_impl.cpp index 991e6efd3..a5a0ef9b0 100644 --- a/host/lib/usrp/b100/b100_impl.cpp +++ b/host/lib/usrp/b100/b100_impl.cpp @@ -17,12 +17,11 @@ #include "apply_corrections.hpp" #include "b100_impl.hpp" -#include "b100_ctrl.hpp" +#include "b100_regs.hpp" #include "fpga_regs_standard.h" #include "usrp_i2c_addr.h" #include "usrp_commands.h" #include <uhd/transport/usb_control.hpp> -#include "ctrl_packet.hpp" #include <uhd/utils/msg.hpp> #include <uhd/exception.hpp> #include <uhd/utils/static.hpp> @@ -33,8 +32,8 @@ #include <boost/filesystem.hpp> #include <boost/thread/thread.hpp> #include <boost/lexical_cast.hpp> -#include "b100_regs.hpp" #include <cstdio> +#include <iostream> using namespace uhd; using namespace uhd::usrp; @@ -85,13 +84,10 @@ static device_addrs_t b100_find(const device_addr_t &hint) b100_fw_image = find_image_path(hint.get("fw", B100_FW_FILE_NAME)); } catch(...){ - UHD_MSG(warning) << boost::format( - "Could not locate B100 firmware.\n" - "Please install the images package.\n" - ); + UHD_MSG(warning) << boost::format("Could not locate B100 firmware. %s\n") % print_images_error(); return b100_addrs; } - UHD_LOG << "the firmware image: " << b100_fw_image << std::endl; + UHD_LOG << "the firmware image: " << b100_fw_image << std::endl; usb_control::sptr control; try{control = usb_control::make(handle, 0);} @@ -117,7 +113,7 @@ static device_addrs_t b100_find(const device_addr_t &hint) catch(const uhd::exception &){continue;} //ignore claimed fx2_ctrl::sptr fx2_ctrl = fx2_ctrl::make(control); - const mboard_eeprom_t mb_eeprom = mboard_eeprom_t(*fx2_ctrl, mboard_eeprom_t::MAP_B100); + const mboard_eeprom_t mb_eeprom = mboard_eeprom_t(*fx2_ctrl, B100_EEPROM_MAP_KEY); device_addr_t new_addr; new_addr["type"] = "b100"; new_addr["name"] = mb_eeprom["name"]; @@ -190,10 +186,10 @@ b100_impl::b100_impl(const device_addr_t &device_addr){ //create the control transport device_addr_t ctrl_xport_args; - ctrl_xport_args["recv_frame_size"] = boost::lexical_cast<std::string>(CTRL_PACKET_LENGTH); + ctrl_xport_args["recv_frame_size"] = "512"; ctrl_xport_args["num_recv_frames"] = "16"; - ctrl_xport_args["send_frame_size"] = boost::lexical_cast<std::string>(CTRL_PACKET_LENGTH); - ctrl_xport_args["num_send_frames"] = "4"; + ctrl_xport_args["send_frame_size"] = "512"; + ctrl_xport_args["num_send_frames"] = "16"; _ctrl_transport = usb_zero_copy::make( handle, @@ -201,17 +197,22 @@ b100_impl::b100_impl(const device_addr_t &device_addr){ 3, 4, //interface, endpoint ctrl_xport_args ); - while (_ctrl_transport->get_recv_buff(0.0)){} //flush ctrl xport this->enable_gpif(true); //////////////////////////////////////////////////////////////////// - // Initialize FPGA wishbone communication + // Initialize FPGA control communication //////////////////////////////////////////////////////////////////// - _fpga_ctrl = b100_ctrl::make(_ctrl_transport); - _fpga_ctrl->poke32(B100_REG_GLOBAL_RESET, 0); //global fpga reset + fifo_ctrl_excelsior_config fifo_ctrl_config; + fifo_ctrl_config.async_sid_base = B100_TX_ASYNC_SID; + fifo_ctrl_config.num_async_chan = 1; + fifo_ctrl_config.ctrl_sid_base = B100_CTRL_MSG_SID; + fifo_ctrl_config.spi_base = TOREG(SR_SPI); + fifo_ctrl_config.spi_rb = REG_RB_SPI; + _fifo_ctrl = fifo_ctrl_excelsior::make(_ctrl_transport, fifo_ctrl_config); + //perform a test peek operation try{ - _fpga_ctrl->peek32(0); + _fifo_ctrl->peek32(0); } //try reset once in the case of failure catch(const uhd::exception &){ @@ -219,7 +220,7 @@ b100_impl::b100_impl(const device_addr_t &device_addr){ UHD_MSG(warning) << "The control endpoint was left in a bad state.\n" "Attempting endpoint re-enumeration...\n" << std::endl; - _fpga_ctrl.reset(); + _fifo_ctrl.reset(); _ctrl_transport.reset(); _fx2_ctrl->usrp_fx2_reset(); goto b100_impl_constructor_begin; @@ -229,8 +230,7 @@ b100_impl::b100_impl(const device_addr_t &device_addr){ //////////////////////////////////////////////////////////////////// // Initialize peripherals after reset //////////////////////////////////////////////////////////////////// - _fpga_i2c_ctrl = i2c_core_100::make(_fpga_ctrl, B100_REG_SLAVE(3)); - _fpga_spi_ctrl = spi_core_100::make(_fpga_ctrl, B100_REG_SLAVE(2)); + _fpga_i2c_ctrl = i2c_core_200::make(_fifo_ctrl, TOREG(SR_I2C), REG_RB_I2C); //////////////////////////////////////////////////////////////////// // Create data transport @@ -244,6 +244,10 @@ b100_impl::b100_impl(const device_addr_t &device_addr){ data_xport_args["send_frame_size"] = device_addr.get("send_frame_size", "16384"); data_xport_args["num_send_frames"] = device_addr.get("num_send_frames", "16"); + //let packet padder know the LUT size in number of words32 + const size_t rx_lut_size = size_t(data_xport_args.cast<double>("recv_frame_size", 0.0)); + _fifo_ctrl->poke32(TOREG(SR_PADDER+0), rx_lut_size/sizeof(boost::uint32_t)); + _data_transport = usb_zero_copy::make_wrapper( usb_zero_copy::make( handle, // identifier @@ -253,21 +257,21 @@ b100_impl::b100_impl(const device_addr_t &device_addr){ ), B100_MAX_PKT_BYTE_LIMIT ); - while (_data_transport->get_recv_buff(0.0)){} //flush data xport //////////////////////////////////////////////////////////////////// // Initialize the properties tree //////////////////////////////////////////////////////////////////// _tree->create<std::string>("/name").set("B-Series Device"); const fs_path mb_path = "/mboards/0"; - _tree->create<std::string>(mb_path / "name").set("B100 (B-Hundo)"); + _tree->create<std::string>(mb_path / "name").set("B100"); + _tree->create<std::string>(mb_path / "codename").set("B-Hundo"); _tree->create<std::string>(mb_path / "load_eeprom") .subscribe(boost::bind(&fx2_ctrl::usrp_load_eeprom, _fx2_ctrl, _1)); //////////////////////////////////////////////////////////////////// // setup the mboard eeprom //////////////////////////////////////////////////////////////////// - const mboard_eeprom_t mb_eeprom(*_fx2_ctrl, mboard_eeprom_t::MAP_B100); + const mboard_eeprom_t mb_eeprom(*_fx2_ctrl, B100_EEPROM_MAP_KEY); _tree->create<mboard_eeprom_t>(mb_path / "eeprom") .set(mb_eeprom) .subscribe(boost::bind(&b100_impl::set_mb_eeprom, this, _1)); @@ -278,12 +282,17 @@ b100_impl::b100_impl(const device_addr_t &device_addr){ //^^^ clock created up top, just reg props here... ^^^ _tree->create<double>(mb_path / "tick_rate") .publish(boost::bind(&b100_clock_ctrl::get_fpga_clock_rate, _clock_ctrl)) + .subscribe(boost::bind(&fifo_ctrl_excelsior::set_tick_rate, _fifo_ctrl, _1)) .subscribe(boost::bind(&b100_impl::update_tick_rate, this, _1)); + //subscribe the command time while we are at it + _tree->create<time_spec_t>(mb_path / "time/cmd") + .subscribe(boost::bind(&fifo_ctrl_excelsior::set_time, _fifo_ctrl, _1)); + //////////////////////////////////////////////////////////////////// // create codec control objects //////////////////////////////////////////////////////////////////// - _codec_ctrl = b100_codec_ctrl::make(_fpga_spi_ctrl); + _codec_ctrl = b100_codec_ctrl::make(_fifo_ctrl); const fs_path rx_codec_path = mb_path / "rx_codecs/A"; const fs_path tx_codec_path = mb_path / "tx_codecs/A"; _tree->create<std::string>(rx_codec_path / "name").set("ad9522"); @@ -305,8 +314,8 @@ b100_impl::b100_impl(const device_addr_t &device_addr){ //////////////////////////////////////////////////////////////////// // create frontend control objects //////////////////////////////////////////////////////////////////// - _rx_fe = rx_frontend_core_200::make(_fpga_ctrl, B100_REG_SR_ADDR(B100_SR_RX_FRONT)); - _tx_fe = tx_frontend_core_200::make(_fpga_ctrl, B100_REG_SR_ADDR(B100_SR_TX_FRONT)); + _rx_fe = rx_frontend_core_200::make(_fifo_ctrl, TOREG(SR_RX_FE)); + _tx_fe = tx_frontend_core_200::make(_fifo_ctrl, TOREG(SR_TX_FE)); _tree->create<subdev_spec_t>(mb_path / "rx_subdev_spec") .subscribe(boost::bind(&b100_impl::update_rx_subdev_spec, this, _1)); @@ -335,13 +344,17 @@ b100_impl::b100_impl(const device_addr_t &device_addr){ //////////////////////////////////////////////////////////////////// // create rx dsp control objects //////////////////////////////////////////////////////////////////// - _rx_dsps.push_back(rx_dsp_core_200::make( - _fpga_ctrl, B100_REG_SR_ADDR(B100_SR_RX_DSP0), B100_REG_SR_ADDR(B100_SR_RX_CTRL0), B100_RX_SID_BASE + 0 - )); - _rx_dsps.push_back(rx_dsp_core_200::make( - _fpga_ctrl, B100_REG_SR_ADDR(B100_SR_RX_DSP1), B100_REG_SR_ADDR(B100_SR_RX_CTRL1), B100_RX_SID_BASE + 1 - )); - for (size_t dspno = 0; dspno < _rx_dsps.size(); dspno++){ + const size_t num_rx_dsps = _fifo_ctrl->peek32(REG_RB_NUM_RX_DSP); + for (size_t dspno = 0; dspno < num_rx_dsps; dspno++) + { + const size_t sr_off = dspno*32; + _rx_dsps.push_back(rx_dsp_core_200::make( + _fifo_ctrl, + TOREG(SR_RX_DSP0+sr_off), + TOREG(SR_RX_CTRL0+sr_off), + B100_RX_SID_BASE + dspno + )); + _rx_dsps[dspno]->set_link_rate(B100_LINK_RATE_BPS); _tree->access<double>(mb_path / "tick_rate") .subscribe(boost::bind(&rx_dsp_core_200::set_tick_rate, _rx_dsps[dspno], _1)); @@ -364,7 +377,7 @@ b100_impl::b100_impl(const device_addr_t &device_addr){ // create tx dsp control objects //////////////////////////////////////////////////////////////////// _tx_dsp = tx_dsp_core_200::make( - _fpga_ctrl, B100_REG_SR_ADDR(B100_SR_TX_DSP), B100_REG_SR_ADDR(B100_SR_TX_CTRL), B100_TX_ASYNC_SID + _fifo_ctrl, TOREG(SR_TX_DSP), TOREG(SR_TX_CTRL), B100_TX_ASYNC_SID ); _tx_dsp->set_link_rate(B100_LINK_RATE_BPS); _tree->access<double>(mb_path / "tick_rate") @@ -384,12 +397,12 @@ b100_impl::b100_impl(const device_addr_t &device_addr){ // create time control objects //////////////////////////////////////////////////////////////////// time64_core_200::readback_bases_type time64_rb_bases; - time64_rb_bases.rb_hi_now = B100_REG_RB_TIME_NOW_HI; - time64_rb_bases.rb_lo_now = B100_REG_RB_TIME_NOW_LO; - time64_rb_bases.rb_hi_pps = B100_REG_RB_TIME_PPS_HI; - time64_rb_bases.rb_lo_pps = B100_REG_RB_TIME_PPS_LO; + time64_rb_bases.rb_hi_now = REG_RB_TIME_NOW_HI; + time64_rb_bases.rb_lo_now = REG_RB_TIME_NOW_LO; + time64_rb_bases.rb_hi_pps = REG_RB_TIME_PPS_HI; + time64_rb_bases.rb_lo_pps = REG_RB_TIME_PPS_LO; _time64 = time64_core_200::make( - _fpga_ctrl, B100_REG_SR_ADDR(B100_SR_TIME64), time64_rb_bases + _fifo_ctrl, TOREG(SR_TIME64), time64_rb_bases ); _tree->access<double>(mb_path / "tick_rate") .subscribe(boost::bind(&time64_core_200::set_tick_rate, _time64, _1)); @@ -413,7 +426,7 @@ b100_impl::b100_impl(const device_addr_t &device_addr){ //////////////////////////////////////////////////////////////////// // create user-defined control objects //////////////////////////////////////////////////////////////////// - _user = user_settings_core_200::make(_fpga_ctrl, B100_REG_SR_ADDR(B100_SR_USER_REGS)); + _user = user_settings_core_200::make(_fifo_ctrl, TOREG(SR_USER_REGS)); _tree->create<user_settings_core_200::user_reg_t>(mb_path / "user/regs") .subscribe(boost::bind(&user_settings_core_200::set_reg, _user, _1)); @@ -427,6 +440,9 @@ b100_impl::b100_impl(const device_addr_t &device_addr){ tx_db_eeprom.load(*_fpga_i2c_ctrl, I2C_ADDR_TX_A); gdb_eeprom.load(*_fpga_i2c_ctrl, I2C_ADDR_TX_A ^ 5); + //disable rx dc offset if LFRX + if (rx_db_eeprom.id == 0x000f) _tree->access<bool>(rx_fe_path / "dc_offset" / "enable").set(false); + //create the properties and register subscribers _tree->create<dboard_eeprom_t>(mb_path / "dboards/A/rx_eeprom") .set(rx_db_eeprom) @@ -439,7 +455,7 @@ b100_impl::b100_impl(const device_addr_t &device_addr){ .subscribe(boost::bind(&b100_impl::set_db_eeprom, this, "gdb", _1)); //create a new dboard interface and manager - _dboard_iface = make_b100_dboard_iface(_fpga_ctrl, _fpga_i2c_ctrl, _fpga_spi_ctrl, _clock_ctrl, _codec_ctrl); + _dboard_iface = make_b100_dboard_iface(_fifo_ctrl, _fpga_i2c_ctrl, _fifo_ctrl/*spi*/, _clock_ctrl, _codec_ctrl); _tree->create<dboard_iface::sptr>(mb_path / "dboards/A/iface").set(_dboard_iface); _dboard_manager = dboard_manager::make( rx_db_eeprom.id, tx_db_eeprom.id, gdb_eeprom.id, @@ -459,7 +475,11 @@ b100_impl::b100_impl(const device_addr_t &device_addr){ } //initialize io handling - this->io_init(); + _recv_demuxer = recv_packet_demuxer::make(_data_transport, _rx_dsps.size(), B100_RX_SID_BASE); + + //allocate streamer weak ptrs containers + _rx_streamers.resize(_rx_dsps.size()); + _tx_streamers.resize(1/*known to be 1 dsp*/); //////////////////////////////////////////////////////////////////// // do some post-init tasks @@ -484,8 +504,7 @@ b100_impl::b100_impl(const device_addr_t &device_addr){ } b100_impl::~b100_impl(void){ - //set an empty async callback now that we deconstruct - _fpga_ctrl->set_async_cb(b100_ctrl::async_cb_type()); + //NOP } void b100_impl::check_fw_compat(void){ @@ -496,13 +515,14 @@ void b100_impl::check_fw_compat(void){ if (fw_compat_num != B100_FW_COMPAT_NUM){ throw uhd::runtime_error(str(boost::format( "Expected firmware compatibility number 0x%x, but got 0x%x:\n" - "The firmware build is not compatible with the host code build." - ) % B100_FW_COMPAT_NUM % fw_compat_num)); + "The firmware build is not compatible with the host code build.\n" + "%s" + ) % B100_FW_COMPAT_NUM % fw_compat_num % print_images_error())); } } void b100_impl::check_fpga_compat(void){ - const boost::uint32_t fpga_compat_num = _fpga_ctrl->peek32(B100_REG_RB_COMPAT); + const boost::uint32_t fpga_compat_num = _fifo_ctrl->peek32(REG_RB_COMPAT); boost::uint16_t fpga_major = fpga_compat_num >> 16, fpga_minor = fpga_compat_num & 0xffff; if (fpga_major == 0){ //old version scheme fpga_major = fpga_minor; @@ -512,7 +532,8 @@ void b100_impl::check_fpga_compat(void){ throw uhd::runtime_error(str(boost::format( "Expected FPGA compatibility number %d, but got %d:\n" "The FPGA build is not compatible with the host code build." - ) % int(B100_FPGA_COMPAT_NUM) % fpga_major)); + "%s" + ) % int(B100_FPGA_COMPAT_NUM) % fpga_major % print_images_error())); } _tree->create<std::string>("/mboards/0/fpga_version").set(str(boost::format("%u.%u") % fpga_major % fpga_minor)); } @@ -526,7 +547,7 @@ double b100_impl::update_rx_codec_gain(const double gain){ } void b100_impl::set_mb_eeprom(const uhd::usrp::mboard_eeprom_t &mb_eeprom){ - mb_eeprom.commit(*_fx2_ctrl, mboard_eeprom_t::MAP_B100); + mb_eeprom.commit(*_fx2_ctrl, B100_EEPROM_MAP_KEY); } void b100_impl::set_db_eeprom(const std::string &type, const uhd::usrp::dboard_eeprom_t &db_eeprom){ @@ -536,6 +557,19 @@ void b100_impl::set_db_eeprom(const std::string &type, const uhd::usrp::dboard_e } void b100_impl::update_clock_source(const std::string &source){ + + if (source == "pps_sync"){ + _clock_ctrl->use_external_ref(); + _fifo_ctrl->poke32(TOREG(SR_MISC+2), 1); + return; + } + if (source == "_pps_sync_"){ + _clock_ctrl->use_external_ref(); + _fifo_ctrl->poke32(TOREG(SR_MISC+2), 3); + return; + } + _fifo_ctrl->poke32(TOREG(SR_MISC+2), 0); + if (source == "auto") _clock_ctrl->use_auto_ref(); else if (source == "internal") _clock_ctrl->use_internal_ref(); else if (source == "external") _clock_ctrl->use_external_ref(); diff --git a/host/lib/usrp/b100/b100_impl.hpp b/host/lib/usrp/b100/b100_impl.hpp index eab9c750b..250229fb8 100644 --- a/host/lib/usrp/b100/b100_impl.hpp +++ b/host/lib/usrp/b100/b100_impl.hpp @@ -19,20 +19,19 @@ #define INCLUDED_B100_IMPL_HPP #include "fx2_ctrl.hpp" -#include "b100_ctrl.hpp" #include "clock_ctrl.hpp" #include "codec_ctrl.hpp" -#include "spi_core_100.hpp" -#include "i2c_core_100.hpp" +#include "i2c_core_200.hpp" #include "rx_frontend_core_200.hpp" #include "tx_frontend_core_200.hpp" #include "rx_dsp_core_200.hpp" #include "tx_dsp_core_200.hpp" #include "time64_core_200.hpp" +#include "fifo_ctrl_excelsior.hpp" #include "user_settings_core_200.hpp" +#include "recv_packet_demuxer.hpp" #include <uhd/device.hpp> #include <uhd/property_tree.hpp> -#include <uhd/utils/pimpl.hpp> #include <uhd/types/dict.hpp> #include <uhd/types/sensors.hpp> #include <uhd/types/clock_config.hpp> @@ -47,12 +46,14 @@ static const double B100_LINK_RATE_BPS = 256e6/5; //pratical link rate (< 480 Mbps) static const std::string B100_FW_FILE_NAME = "usrp_b100_fw.ihx"; static const std::string B100_FPGA_FILE_NAME = "usrp_b100_fpga.bin"; -static const boost::uint16_t B100_FW_COMPAT_NUM = 0x03; -static const boost::uint16_t B100_FPGA_COMPAT_NUM = 0x09; -static const boost::uint32_t B100_RX_SID_BASE = 2; -static const boost::uint32_t B100_TX_ASYNC_SID = 1; +static const boost::uint16_t B100_FW_COMPAT_NUM = 0x04; +static const boost::uint16_t B100_FPGA_COMPAT_NUM = 11; +static const boost::uint32_t B100_RX_SID_BASE = 30; +static const boost::uint32_t B100_TX_ASYNC_SID = 10; +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"; //! Make a b100 dboard interface uhd::usrp::dboard_iface::sptr make_b100_dboard_iface( @@ -79,8 +80,8 @@ private: uhd::property_tree::sptr _tree; //controllers - spi_core_100::sptr _fpga_spi_ctrl; - i2c_core_100::sptr _fpga_i2c_ctrl; + fifo_ctrl_excelsior::sptr _fifo_ctrl; + i2c_core_200::sptr _fpga_i2c_ctrl; rx_frontend_core_200::sptr _rx_fe; tx_frontend_core_200::sptr _tx_fe; std::vector<rx_dsp_core_200::sptr> _rx_dsps; @@ -89,20 +90,17 @@ private: user_settings_core_200::sptr _user; b100_clock_ctrl::sptr _clock_ctrl; b100_codec_ctrl::sptr _codec_ctrl; - b100_ctrl::sptr _fpga_ctrl; uhd::usrp::fx2_ctrl::sptr _fx2_ctrl; //transports - uhd::transport::zero_copy_if::sptr _data_transport, _ctrl_transport; + uhd::transport::zero_copy_if::sptr _ctrl_transport; + uhd::transport::zero_copy_if::sptr _data_transport; + uhd::usrp::recv_packet_demuxer::sptr _recv_demuxer; //dboard stuff uhd::usrp::dboard_manager::sptr _dboard_manager; uhd::usrp::dboard_iface::sptr _dboard_iface; - //handle io stuff - UHD_PIMPL_DECL(io_impl) _io_impl; - void io_init(void); - //device properties interface uhd::property_tree::sptr get_tree(void) const{ return _tree; @@ -125,7 +123,6 @@ private: void update_clock_source(const std::string &); void enable_gpif(const bool); void clear_fpga_fifo(void); - void handle_async_message(uhd::transport::managed_recv_buffer::sptr); uhd::sensor_value_t get_ref_locked(void); void set_rx_fe_corrections(const double); void set_tx_fe_corrections(const double); diff --git a/host/lib/usrp/b100/b100_regs.hpp b/host/lib/usrp/b100/b100_regs.hpp index 987a09f03..48eb0460d 100644 --- a/host/lib/usrp/b100/b100_regs.hpp +++ b/host/lib/usrp/b100/b100_regs.hpp @@ -15,109 +15,49 @@ // along with this program. If not, see <http://www.gnu.org/licenses/>. // -//////////////////////////////////////////////////////////////// -// -// Memory map for wishbone bus -// -//////////////////////////////////////////////////////////////// - -// All addresses are byte addresses. All accesses are word (16-bit) accesses. -// This means that address bit 0 is usually 0. -// There are 11 bits of address for the control. - #ifndef INCLUDED_B100_REGS_HPP #define INCLUDED_B100_REGS_HPP -///////////////////////////////////////////////////// -// Slave pointers +#include <boost/cstdint.hpp> -#define B100_REG_SLAVE(n) ((n)<<7) +#define TOREG(x) ((x)*4) -///////////////////////////////////////////////////// -// Slave 0 -- Misc Regs +#define localparam static const int -#define B100_REG_MISC_BASE B100_REG_SLAVE(0) +localparam SR_MISC = 0; // 5 +localparam SR_USER_REGS = 5; // 2 +localparam SR_PADDER = 10; // 2 -#define B100_REG_MISC_LED B100_REG_MISC_BASE + 0 -#define B100_REG_MISC_SW B100_REG_MISC_BASE + 2 -#define B100_REG_MISC_CGEN_CTRL B100_REG_MISC_BASE + 4 -#define B100_REG_MISC_CGEN_ST B100_REG_MISC_BASE + 6 +localparam SR_TX_CTRL = 32; // 6 +localparam SR_TX_DSP = 40; // 5 +localparam SR_TX_FE = 48; // 5 -///////////////////////////////////////////////////// -// Slave 1 -- UART -// CLKDIV is 16 bits, others are only 8 +localparam SR_RX_CTRL0 = 96; // 9 +localparam SR_RX_DSP0 = 106; // 7 +localparam SR_RX_FE = 114; // 5 -#define B100_REG_UART_BASE B100_REG_SLAVE(1) +localparam SR_RX_CTRL1 = 128; // 9 +localparam SR_RX_DSP1 = 138; // 7 -#define B100_REG_UART_CLKDIV B100_REG_UART_BASE + 0 -#define B100_REG_UART_TXLEVEL B100_REG_UART_BASE + 2 -#define B100_REG_UART_RXLEVEL B100_REG_UART_BASE + 4 -#define B100_REG_UART_TXCHAR B100_REG_UART_BASE + 6 -#define B100_REG_UART_RXCHAR B100_REG_UART_BASE + 8 +localparam SR_TIME64 = 192; // 6 +localparam SR_SPI = 208; // 3 +localparam SR_I2C = 216; // 1 +localparam SR_GPIO = 224; // 5 -///////////////////////////////////////////////////// -// Slave 2 -- SPI Core -//these are 32-bit registers mapped onto the 16-bit Wishbone bus. -//Using peek32/poke32 should allow transparent use of these registers. -#define B100_REG_SPI_BASE B100_REG_SLAVE(2) +#define REG_RB_TIME_NOW_HI TOREG(10) +#define REG_RB_TIME_NOW_LO TOREG(11) +#define REG_RB_TIME_PPS_HI TOREG(14) +#define REG_RB_TIME_PPS_LO TOREG(15) +#define REG_RB_SPI TOREG(0) +#define REG_RB_COMPAT TOREG(1) +#define REG_RB_GPIO TOREG(3) +#define REG_RB_I2C TOREG(2) +#define REG_RB_NUM_RX_DSP TOREG(6) //spi slave constants #define B100_SPI_SS_AD9862 (1 << 2) #define B100_SPI_SS_TX_DB (1 << 1) #define B100_SPI_SS_RX_DB (1 << 0) -//////////////////////////////////////////////// -// Slave 3 -- I2C Core - -#define B100_REG_I2C_BASE B100_REG_SLAVE(3) - -/////////////////////////////////////////////////// -// Slave 7 -- Readback Mux 32 - -#define B100_REG_RB_MUX_32_BASE B100_REG_SLAVE(7) - -#define B100_REG_RB_TIME_NOW_HI B100_REG_RB_MUX_32_BASE + 0 -#define B100_REG_RB_TIME_NOW_LO B100_REG_RB_MUX_32_BASE + 4 -#define B100_REG_RB_TIME_PPS_HI B100_REG_RB_MUX_32_BASE + 8 -#define B100_REG_RB_TIME_PPS_LO B100_REG_RB_MUX_32_BASE + 12 -#define B100_REG_RB_MISC_TEST32 B100_REG_RB_MUX_32_BASE + 16 -#define B100_REG_RB_COMPAT B100_REG_RB_MUX_32_BASE + 24 -#define B100_REG_RB_GPIO B100_REG_RB_MUX_32_BASE + 28 - -//////////////////////////////////////////////////// -// Slaves 8 & 9 -- Settings Bus -// -// Output-only, no readback, 64 registers total -// Each register must be written 32 bits at a time -// First the address xxx_xx00 and then xxx_xx10 -// 64 total regs in address space -#define B100_SR_RX_CTRL0 0 // 9 regs (+0 to +8) -#define B100_SR_RX_DSP0 10 // 4 regs (+0 to +3) -#define B100_SR_RX_CTRL1 16 // 9 regs (+0 to +8) -#define B100_SR_RX_DSP1 26 // 4 regs (+0 to +3) -#define B100_SR_TX_CTRL 32 // 4 regs (+0 to +3) -#define B100_SR_TX_DSP 38 // 3 regs (+0 to +2) - -#define B100_SR_TIME64 42 // 6 regs (+0 to +5) -#define B100_SR_RX_FRONT 48 // 5 regs (+0 to +4) -#define B100_SR_TX_FRONT 54 // 5 regs (+0 to +4) - -#define B100_SR_REG_TEST32 60 // 1 reg -#define B100_SR_CLEAR_FIFO 61 // 1 reg -#define B100_SR_GLOBAL_RESET 63 // 1 reg -#define B100_SR_USER_REGS 64 // 2 regs - -#define B100_SR_GPIO 128 - -#define B100_REG_SR_ADDR(n) (B100_REG_SLAVE(8) + (4*(n))) - -#define B100_REG_SR_MISC_TEST32 B100_REG_SR_ADDR(B100_SR_REG_TEST32) - -///////////////////////////////////////////////// -// Magic reset regs -//////////////////////////////////////////////// -#define B100_REG_CLEAR_FIFO B100_REG_SR_ADDR(B100_SR_CLEAR_FIFO) -#define B100_REG_GLOBAL_RESET B100_REG_SR_ADDR(B100_SR_GLOBAL_RESET) - -#endif +#endif /*INCLUDED_B100_REGS_HPP*/ diff --git a/host/lib/usrp/b100/ctrl_packet.hpp b/host/lib/usrp/b100/ctrl_packet.hpp deleted file mode 100644 index bab1f0de1..000000000 --- a/host/lib/usrp/b100/ctrl_packet.hpp +++ /dev/null @@ -1,75 +0,0 @@ -// -// Copyright 2011 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_CTRL_PACKET_HPP -#define INCLUDED_CTRL_PACKET_HPP - -#include <uhd/config.hpp> -#include <boost/cstdint.hpp> -#include <uhd/types/serial.hpp> - -typedef std::vector<boost::uint16_t> ctrl_data_t; - -/*! - * Control packet operation type - */ -enum ctrl_pkt_op_t { - CTRL_PKT_OP_WRITE = 1, - CTRL_PKT_OP_READ = 2, - CTRL_PKT_OP_READBACK = 3 -}; - -/*! - * Control packet transaction length - */ -const size_t CTRL_PACKET_LENGTH = 32; -const size_t CTRL_PACKET_HEADER_LENGTH = 8; -const size_t CTRL_PACKET_DATA_LENGTH = 24; //=length-header - -/*! - * Control packet header magic value - */ -const boost::uint8_t CTRL_PACKET_HEADER_MAGIC = 0xAA; - -/*! - * Callback triggers for readback operation - */ -//FIXME: these are not real numbers, callbacks aren't implemented yet -const boost::uint16_t CTRL_PACKET_CALLBACK_SPI = 0x0001; -const boost::uint16_t CTRL_PACKET_CALLBACK_I2C = 0x0002; -//and so on - -/*! - * Metadata structure to describe a control packet - */ -struct UHD_API ctrl_pkt_meta_t { - ctrl_pkt_op_t op; - boost::uint8_t callbacks; - boost::uint8_t seq; - boost::uint16_t len; - boost::uint32_t addr; -}; - -/*! - * Full control packet structure - */ -struct UHD_API ctrl_pkt_t { - ctrl_pkt_meta_t pkt_meta; - ctrl_data_t data; -}; - -#endif /* INCLUDED_CTRL_PACKET_HPP */ diff --git a/host/lib/usrp/b100/dboard_iface.cpp b/host/lib/usrp/b100/dboard_iface.cpp index 39ad5c5ac..25604da72 100644 --- a/host/lib/usrp/b100/dboard_iface.cpp +++ b/host/lib/usrp/b100/dboard_iface.cpp @@ -45,7 +45,7 @@ public: _spi_iface = spi_iface; _clock = clock; _codec = codec; - _gpio = gpio_core_200::make(_wb_iface, B100_REG_SR_ADDR(B100_SR_GPIO), B100_REG_RB_GPIO); + _gpio = gpio_core_200::make(_wb_iface, TOREG(SR_GPIO), REG_RB_GPIO); //init the clock rate shadows this->set_clock_rate(UNIT_RX, _clock->get_fpga_clock_rate()); diff --git a/host/lib/usrp/b100/io_impl.cpp b/host/lib/usrp/b100/io_impl.cpp index 674380cca..723756dcc 100644 --- a/host/lib/usrp/b100/io_impl.cpp +++ b/host/lib/usrp/b100/io_impl.cpp @@ -15,16 +15,10 @@ // along with this program. If not, see <http://www.gnu.org/licenses/>. // -#include "recv_packet_demuxer.hpp" #include "validate_subdev_spec.hpp" -#include "async_packet_handler.hpp" #include "../../transport/super_recv_packet_handler.hpp" #include "../../transport/super_send_packet_handler.hpp" -#include "usrp_commands.h" #include "b100_impl.hpp" -#include "b100_regs.hpp" -#include <uhd/utils/thread_priority.hpp> -#include <uhd/transport/bounded_buffer.hpp> #include <boost/bind.hpp> #include <boost/format.hpp> #include <boost/bind.hpp> @@ -37,66 +31,6 @@ using namespace uhd; using namespace uhd::usrp; using namespace uhd::transport; -/*********************************************************************** - * IO Implementation Details - **********************************************************************/ -struct b100_impl::io_impl{ - io_impl(void): - async_msg_fifo(1000/*messages deep*/) - { /* NOP */ } - - zero_copy_if::sptr data_transport; - bounded_buffer<async_metadata_t> async_msg_fifo; - recv_packet_demuxer::sptr demuxer; - double tick_rate; -}; - -/*********************************************************************** - * Initialize internals within this file - **********************************************************************/ -void b100_impl::io_init(void){ - - //clear fifo state machines - _fpga_ctrl->poke32(B100_REG_CLEAR_FIFO, 0); - - //allocate streamer weak ptrs containers - _rx_streamers.resize(_rx_dsps.size()); - _tx_streamers.resize(1/*known to be 1 dsp*/); - - //create new io impl - _io_impl = UHD_PIMPL_MAKE(io_impl, ()); - _io_impl->demuxer = recv_packet_demuxer::make(_data_transport, _rx_dsps.size(), B100_RX_SID_BASE); - - //now its safe to register the async callback - _fpga_ctrl->set_async_cb(boost::bind(&b100_impl::handle_async_message, this, _1)); -} - -void b100_impl::handle_async_message(managed_recv_buffer::sptr rbuf){ - vrt::if_packet_info_t if_packet_info; - if_packet_info.num_packet_words32 = rbuf->size()/sizeof(boost::uint32_t); - const boost::uint32_t *vrt_hdr = rbuf->cast<const boost::uint32_t *>(); - try{ - vrt::if_hdr_unpack_le(vrt_hdr, if_packet_info); - } - catch(const std::exception &e){ - UHD_MSG(error) << "Error (handle_async_message): " << e.what() << std::endl; - } - - if (if_packet_info.sid == B100_TX_ASYNC_SID and if_packet_info.packet_type != vrt::if_packet_info_t::PACKET_TYPE_DATA){ - - //fill in the async metadata - async_metadata_t metadata; - load_metadata_from_buff(uhd::wtohx<boost::uint32_t>, metadata, if_packet_info, vrt_hdr, _io_impl->tick_rate); - - //push the message onto the queue - _io_impl->async_msg_fifo.push_with_pop_on_full(metadata); - - //print some fastpath messages - standard_async_msg_prints(metadata); - } - else UHD_MSG(error) << "Unknown async packet" << std::endl; -} - void b100_impl::update_rates(void){ const fs_path mb_path = "/mboards/0"; _tree->access<double>(mb_path / "tick_rate").update(); @@ -111,7 +45,6 @@ void b100_impl::update_rates(void){ } void b100_impl::update_tick_rate(const double rate){ - _io_impl->tick_rate = rate; //update the tick rate on all existing streamers -> thread safe for (size_t i = 0; i < _rx_streamers.size(); i++){ @@ -181,8 +114,7 @@ void b100_impl::update_tx_subdev_spec(const uhd::usrp::subdev_spec_t &spec){ bool b100_impl::recv_async_msg( async_metadata_t &async_metadata, double timeout ){ - boost::this_thread::disable_interruption di; //disable because the wait can throw - return _io_impl->async_msg_fifo.pop_with_timed_wait(async_metadata, timeout); + return _fifo_ctrl->pop_async_msg(async_metadata, timeout); } /*********************************************************************** @@ -227,7 +159,7 @@ rx_streamer::sptr b100_impl::get_rx_stream(const uhd::stream_args_t &args_){ _rx_dsps[dsp]->set_nsamps_per_packet(spp); //seems to be a good place to set this _rx_dsps[dsp]->setup(args); my_streamer->set_xport_chan_get_buff(chan_i, boost::bind( - &recv_packet_demuxer::get_recv_buff, _io_impl->demuxer, dsp, _1 + &recv_packet_demuxer::get_recv_buff, _recv_demuxer, dsp, _1 ), true /*flush*/); my_streamer->set_overflow_handler(chan_i, boost::bind( &rx_dsp_core_200::handle_overflow, _rx_dsps[dsp] diff --git a/host/lib/usrp/common/CMakeLists.txt b/host/lib/usrp/common/CMakeLists.txt index 9abd34afa..fa07e3d1d 100644 --- a/host/lib/usrp/common/CMakeLists.txt +++ b/host/lib/usrp/common/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2011 Ettus Research LLC +# Copyright 2011-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 @@ -32,4 +32,5 @@ LIBUHD_APPEND_SOURCES( ${CMAKE_CURRENT_SOURCE_DIR}/apply_corrections.cpp ${CMAKE_CURRENT_SOURCE_DIR}/validate_subdev_spec.cpp ${CMAKE_CURRENT_SOURCE_DIR}/recv_packet_demuxer.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/fifo_ctrl_excelsior.cpp ) diff --git a/host/lib/usrp/common/apply_corrections.cpp b/host/lib/usrp/common/apply_corrections.cpp index b889266f2..3d33e7d11 100644 --- a/host/lib/usrp/common/apply_corrections.cpp +++ b/host/lib/usrp/common/apply_corrections.cpp @@ -54,6 +54,12 @@ static bool fe_cal_comp(fe_cal_t a, fe_cal_t b){ static uhd::dict<std::string, std::vector<fe_cal_t> > fe_cal_cache; +static bool is_same_freq(const double f1, const double f2) +{ + const double epsilon = 0.1; + return ((f1 - epsilon) < f2 and (f1 + epsilon) > f2); +} + static std::complex<double> get_fe_correction( const std::string &key, const double lo_freq ){ @@ -64,6 +70,12 @@ static std::complex<double> get_fe_correction( size_t lo_index = 0; size_t hi_index = datas.size()-1; for (size_t i = 0; i < datas.size(); i++){ + if (is_same_freq(datas[i].lo_freq, lo_freq)) + { + hi_index = i; + lo_index = i; + break; + } if (datas[i].lo_freq > lo_freq){ hi_index = i; break; diff --git a/host/lib/usrp/common/fifo_ctrl_excelsior.cpp b/host/lib/usrp/common/fifo_ctrl_excelsior.cpp new file mode 100644 index 000000000..5e4a1e243 --- /dev/null +++ b/host/lib/usrp/common/fifo_ctrl_excelsior.cpp @@ -0,0 +1,293 @@ +// +// 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 "fifo_ctrl_excelsior.hpp" +#include "async_packet_handler.hpp" +#include <uhd/exception.hpp> +#include <uhd/utils/msg.hpp> +#include <uhd/utils/byteswap.hpp> +#include <uhd/utils/tasks.hpp> +#include <uhd/utils/safe_call.hpp> +#include <uhd/utils/thread_priority.hpp> +#include <uhd/transport/vrt_if_packet.hpp> +#include <uhd/transport/bounded_buffer.hpp> +#include <boost/thread/mutex.hpp> +#include <boost/thread/thread.hpp> +#include <boost/format.hpp> +#include <boost/bind.hpp> + +using namespace uhd; +using namespace uhd::usrp; +using namespace uhd::transport; + +static const size_t POKE32_CMD = (1 << 8); +static const size_t PEEK32_CMD = 0; +static const double ACK_TIMEOUT = 0.5; +static const double MASSIVE_TIMEOUT = 10.0; //for when we wait on a timed command +static const boost::uint32_t MAX_SEQS_OUT = 15; + +#define SPI_DIV _config.spi_base + 0 +#define SPI_CTRL _config.spi_base + 4 +#define SPI_DATA _config.spi_base + 8 +#define SPI_DIVIDER 4 + +struct ctrl_result_t{ + boost::uint32_t msg[2]; +}; + +class fifo_ctrl_excelsior_impl : public fifo_ctrl_excelsior{ +public: + + fifo_ctrl_excelsior_impl(zero_copy_if::sptr xport, const fifo_ctrl_excelsior_config &config): + _xport(xport), + _config(config), + _seq_out(0), + _seq_ack(0), + _timeout(ACK_TIMEOUT), + _async_fifo(1000), + _ctrl_fifo(MAX_SEQS_OUT+1) + { + while (_xport->get_recv_buff(0.0)){} //flush + this->set_time(uhd::time_spec_t(0.0)); + this->set_tick_rate(1.0); //something possible but bogus + _msg_task = task::make(boost::bind(&fifo_ctrl_excelsior_impl::handle_msg, this)); + this->init_spi(); + } + + ~fifo_ctrl_excelsior_impl(void){ + _timeout = ACK_TIMEOUT; //reset timeout to something small + UHD_SAFE_CALL( + this->peek32(0); //dummy peek with the purpose of ack'ing all packets + ) + } + + bool pop_async_msg(async_metadata_t &async_metadata, double timeout){ + boost::this_thread::disable_interruption di; //disable because the wait can throw + return _async_fifo.pop_with_timed_wait(async_metadata, timeout); + } + + void handle_msg(void){ + set_thread_priority_safe(); + while (not boost::this_thread::interruption_requested()){ + this->handle_msg1(); + } + } + + void handle_msg1(void){ + managed_recv_buffer::sptr buff = _xport->get_recv_buff(); + if (not buff) return; + const boost::uint32_t *pkt = buff->cast<const boost::uint32_t *>(); + vrt::if_packet_info_t packet_info; + packet_info.num_packet_words32 = buff->size()/sizeof(boost::uint32_t); + try{ + vrt::if_hdr_unpack_le(pkt, packet_info); + } + catch(const std::exception &ex){ + UHD_MSG(error) << "FIFO ctrl bad VITA packet: " << ex.what() << std::endl; + } + if (packet_info.has_sid and packet_info.sid == _config.ctrl_sid_base){ + ctrl_result_t res = ctrl_result_t(); + res.msg[0] = uhd::wtohx(pkt[packet_info.num_header_words32+0]); + res.msg[1] = uhd::wtohx(pkt[packet_info.num_header_words32+1]); + _ctrl_fifo.push_with_haste(res); + } + else if (packet_info.has_sid and packet_info.sid >= _config.async_sid_base and packet_info.sid <= _config.async_sid_base + _config.num_async_chan){ + async_metadata_t metadata; + load_metadata_from_buff(uhd::wtohx<boost::uint32_t>, metadata, packet_info, pkt, _tick_rate, packet_info.sid - _config.async_sid_base); + _async_fifo.push_with_pop_on_full(metadata); + standard_async_msg_prints(metadata); + } + else{ + UHD_MSG(error) << "FIFO ctrl got unknown SID: " << packet_info.sid << std::endl; + } + } + + /******************************************************************* + * Peek and poke 32 bit implementation + ******************************************************************/ + void poke32(wb_addr_type addr, boost::uint32_t data){ + boost::mutex::scoped_lock lock(_mutex); + + this->send_pkt(addr, data, POKE32_CMD); + + this->wait_for_ack(_seq_out-MAX_SEQS_OUT); + } + + boost::uint32_t peek32(wb_addr_type addr){ + boost::mutex::scoped_lock lock(_mutex); + + this->send_pkt(addr, 0, PEEK32_CMD); + + return this->wait_for_ack(_seq_out); + } + + /******************************************************************* + * Peek and poke 16 bit not implemented + ******************************************************************/ + void poke16(wb_addr_type, boost::uint16_t){ + throw uhd::not_implemented_error("poke16 not implemented in fifo ctrl module"); + } + + boost::uint16_t peek16(wb_addr_type){ + throw uhd::not_implemented_error("peek16 not implemented in fifo ctrl module"); + } + + /******************************************************************* + * FIFO controlled SPI implementation + ******************************************************************/ + void init_spi(void){ + boost::mutex::scoped_lock lock(_mutex); + + this->send_pkt(SPI_DIV, SPI_DIVIDER, POKE32_CMD); + this->wait_for_ack(_seq_out-MAX_SEQS_OUT); + + _ctrl_word_cache = 0; // force update first time around + } + + boost::uint32_t transact_spi( + int which_slave, + const spi_config_t &config, + boost::uint32_t data, + size_t num_bits, + bool readback + ){ + boost::mutex::scoped_lock lock(_mutex); + + //load control word + boost::uint32_t ctrl_word = 0; + ctrl_word |= ((which_slave & 0xffffff) << 0); + ctrl_word |= ((num_bits & 0x3ff) << 24); + if (config.mosi_edge == spi_config_t::EDGE_FALL) ctrl_word |= (1 << 31); + if (config.miso_edge == spi_config_t::EDGE_RISE) ctrl_word |= (1 << 30); + + //load data word (must be in upper bits) + const boost::uint32_t data_out = data << (32 - num_bits); + + //conditionally send control word + if (_ctrl_word_cache != ctrl_word){ + this->send_pkt(SPI_CTRL, ctrl_word, POKE32_CMD); + this->wait_for_ack(_seq_out-MAX_SEQS_OUT); + _ctrl_word_cache = ctrl_word; + } + + //send data word + this->send_pkt(SPI_DATA, data_out, POKE32_CMD); + this->wait_for_ack(_seq_out-MAX_SEQS_OUT); + + //conditional readback + if (readback){ + this->send_pkt(_config.spi_rb, 0, PEEK32_CMD); + return this->wait_for_ack(_seq_out); + } + + return 0; + } + + /******************************************************************* + * Update methods for time + ******************************************************************/ + void set_time(const uhd::time_spec_t &time){ + boost::mutex::scoped_lock lock(_mutex); + _time = time; + _use_time = _time != uhd::time_spec_t(0.0); + if (_use_time) _timeout = MASSIVE_TIMEOUT; //permanently sets larger timeout + } + + void set_tick_rate(const double rate){ + boost::mutex::scoped_lock lock(_mutex); + _tick_rate = rate; + } + +private: + + /******************************************************************* + * Primary control and interaction private methods + ******************************************************************/ + UHD_INLINE void send_pkt(wb_addr_type addr, boost::uint32_t data, int cmd){ + managed_send_buffer::sptr buff = _xport->get_send_buff(0.0); + if (not buff){ + throw uhd::runtime_error("fifo ctrl timed out getting a send buffer"); + } + boost::uint32_t *pkt = buff->cast<boost::uint32_t *>(); + + //load packet info + vrt::if_packet_info_t packet_info; + packet_info.packet_type = vrt::if_packet_info_t::PACKET_TYPE_CONTEXT; + packet_info.num_payload_words32 = 2; + packet_info.num_payload_bytes = packet_info.num_payload_words32*sizeof(boost::uint32_t); + packet_info.packet_count = ++_seq_out; + packet_info.tsf = _time.to_ticks(_tick_rate); + packet_info.sob = false; + packet_info.eob = false; + packet_info.has_sid = false; + packet_info.has_cid = false; + packet_info.has_tsi = false; + packet_info.has_tsf = _use_time; + packet_info.has_tlr = false; + + //load header + vrt::if_hdr_pack_le(pkt, packet_info); + + //load payload + const boost::uint32_t ctrl_word = (addr/4 & 0xff) | cmd | (_seq_out << 16); + pkt[packet_info.num_header_words32+0] = uhd::htowx(ctrl_word); + pkt[packet_info.num_header_words32+1] = uhd::htowx(data); + + //send the buffer over the interface + buff->commit(sizeof(boost::uint32_t)*(packet_info.num_packet_words32)); + } + + UHD_INLINE bool wraparound_lt16(const boost::int16_t i0, const boost::int16_t i1){ + if (((i0 ^ i1) & 0x8000) == 0) //same sign bits + return boost::uint16_t(i0) < boost::uint16_t(i1); + return boost::int16_t(i1 - i0) > 0; + } + + UHD_INLINE boost::uint32_t wait_for_ack(const boost::uint16_t seq_to_ack){ + + while (wraparound_lt16(_seq_ack, seq_to_ack)){ + ctrl_result_t res = ctrl_result_t(); + if (not _ctrl_fifo.pop_with_timed_wait(res, _timeout)){ + throw uhd::runtime_error("fifo ctrl timed out looking for acks"); + } + _seq_ack = res.msg[0] >> 16; + if (_seq_ack == seq_to_ack) return res.msg[1]; + } + + return 0; + } + + zero_copy_if::sptr _xport; + const fifo_ctrl_excelsior_config _config; + boost::mutex _mutex; + boost::uint16_t _seq_out; + boost::uint16_t _seq_ack; + uhd::time_spec_t _time; + bool _use_time; + double _tick_rate; + double _timeout; + boost::uint32_t _ctrl_word_cache; + bounded_buffer<async_metadata_t> _async_fifo; + bounded_buffer<ctrl_result_t> _ctrl_fifo; + task::sptr _msg_task; +}; + + +fifo_ctrl_excelsior::sptr fifo_ctrl_excelsior::make(zero_copy_if::sptr xport, const fifo_ctrl_excelsior_config &config) +{ + return sptr(new fifo_ctrl_excelsior_impl(xport, config)); +} diff --git a/host/lib/usrp/common/fifo_ctrl_excelsior.hpp b/host/lib/usrp/common/fifo_ctrl_excelsior.hpp new file mode 100644 index 000000000..c3ef65a2c --- /dev/null +++ b/host/lib/usrp/common/fifo_ctrl_excelsior.hpp @@ -0,0 +1,63 @@ +// +// 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/>. +// + +#ifndef INCLUDED_B200_CTRL_HPP +#define INCLUDED_B200_CTRL_HPP + +#include <uhd/types/time_spec.hpp> +#include <uhd/types/metadata.hpp> +#include <uhd/types/serial.hpp> +#include <uhd/transport/zero_copy.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/utility.hpp> +#include "wb_iface.hpp" +#include <string> + + +struct fifo_ctrl_excelsior_config +{ + size_t async_sid_base; + size_t num_async_chan; + size_t ctrl_sid_base; + size_t spi_base; + size_t spi_rb; +}; + +/*! + * Provide access to peek, poke, spi, and async messages. + */ +class fifo_ctrl_excelsior : public wb_iface, public uhd::spi_iface{ +public: + typedef boost::shared_ptr<fifo_ctrl_excelsior> sptr; + + //! Make a new control object + static sptr make( + uhd::transport::zero_copy_if::sptr xport, + const fifo_ctrl_excelsior_config &config + ); + + //! Set the command time that will activate + virtual void set_time(const uhd::time_spec_t &time) = 0; + + //! Set the tick rate (converting time into ticks) + virtual void set_tick_rate(const double rate) = 0; + + //! Pop an async message from the queue or timeout + virtual bool pop_async_msg(uhd::async_metadata_t &async_metadata, double timeout) = 0; +}; + +#endif /* INCLUDED_B200_CTRL_HPP */ diff --git a/host/lib/usrp/common/fx2_ctrl.cpp b/host/lib/usrp/common/fx2_ctrl.cpp index 7b8920eb1..5cc701eb0 100644 --- a/host/lib/usrp/common/fx2_ctrl.cpp +++ b/host/lib/usrp/common/fx2_ctrl.cpp @@ -411,6 +411,26 @@ public: return usrp_control_write(request, value, index, 0, 0); } + void write_eeprom( + boost::uint8_t addr, + boost::uint8_t offset, + const byte_vector_t &bytes + ){ + byte_vector_t bytes_with_cmd(bytes.size() + 1); + bytes_with_cmd[0] = offset; + std::copy(bytes.begin(), bytes.end(), &bytes_with_cmd[1]); + this->write_i2c(addr, bytes_with_cmd); + } + + byte_vector_t read_eeprom( + boost::uint8_t addr, + boost::uint8_t offset, + size_t num_bytes + ){ + this->write_i2c(addr, byte_vector_t(1, offset)); + return this->read_i2c(addr, num_bytes); + } + int usrp_i2c_write(boost::uint16_t i2c_addr, unsigned char *buf, boost::uint16_t len) { return usrp_control_write(VRQ_I2C_WRITE, i2c_addr, 0, buf, len); @@ -428,12 +448,7 @@ public: { UHD_ASSERT_THROW(bytes.size() < max_i2c_data_bytes); - unsigned char buff[max_i2c_data_bytes] = {}; - std::copy(bytes.begin(), bytes.end(), buff); - - int ret = this->usrp_i2c_write(addr & 0xff, - buff, - bytes.size()); + int ret = this->usrp_i2c_write(addr, (unsigned char *)&bytes.front(), bytes.size()); if (iface_debug && (ret < 0)) uhd::runtime_error("USRP: failed i2c write"); @@ -443,19 +458,13 @@ public: { UHD_ASSERT_THROW(num_bytes < max_i2c_data_bytes); - unsigned char buff[max_i2c_data_bytes] = {}; - int ret = this->usrp_i2c_read(addr & 0xff, - buff, - num_bytes); + byte_vector_t bytes(num_bytes); + int ret = this->usrp_i2c_read(addr, (unsigned char *)&bytes.front(), num_bytes); if (iface_debug && ((ret < 0) || (unsigned)ret < (num_bytes))) uhd::runtime_error("USRP: failed i2c read"); - byte_vector_t out_bytes; - for (size_t i = 0; i < num_bytes; i++) - out_bytes.push_back(buff[i]); - - return out_bytes; + return bytes; } diff --git a/host/lib/usrp/cores/CMakeLists.txt b/host/lib/usrp/cores/CMakeLists.txt index aa5f0bcbb..3192b0774 100644 --- a/host/lib/usrp/cores/CMakeLists.txt +++ b/host/lib/usrp/cores/CMakeLists.txt @@ -24,6 +24,7 @@ INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) LIBUHD_APPEND_SOURCES( ${CMAKE_CURRENT_SOURCE_DIR}/gpio_core_200.cpp ${CMAKE_CURRENT_SOURCE_DIR}/i2c_core_100.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/i2c_core_200.cpp ${CMAKE_CURRENT_SOURCE_DIR}/spi_core_100.cpp ${CMAKE_CURRENT_SOURCE_DIR}/time64_core_200.cpp ${CMAKE_CURRENT_SOURCE_DIR}/rx_dsp_core_200.cpp diff --git a/host/lib/usrp/cores/gpio_core_200.cpp b/host/lib/usrp/cores/gpio_core_200.cpp index d756097ff..cdab70b8d 100644 --- a/host/lib/usrp/cores/gpio_core_200.cpp +++ b/host/lib/usrp/cores/gpio_core_200.cpp @@ -63,6 +63,7 @@ private: wb_iface::sptr _iface; const size_t _base; const size_t _rb_addr; + uhd::dict<size_t, boost::uint32_t> _update_cache; uhd::dict<unit_t, boost::uint16_t> _pin_ctrl, _gpio_out, _gpio_ddr; uhd::dict<unit_t, uhd::dict<atr_reg_t, boost::uint16_t> > _atr_regs; @@ -90,7 +91,12 @@ private: const boost::uint32_t ctrl = (boost::uint32_t(_pin_ctrl[dboard_iface::UNIT_RX]) << unit2shit(dboard_iface::UNIT_RX)) | (boost::uint32_t(_pin_ctrl[dboard_iface::UNIT_TX]) << unit2shit(dboard_iface::UNIT_TX)); - _iface->poke32(addr, (ctrl & atr_val) | ((~ctrl) & gpio_val)); + const boost::uint32_t val = (ctrl & atr_val) | ((~ctrl) & gpio_val); + if (not _update_cache.has_key(addr) or _update_cache[addr] != val) + { + _iface->poke32(addr, val); + } + _update_cache[addr] = val; } }; diff --git a/host/lib/usrp/cores/i2c_core_200.cpp b/host/lib/usrp/cores/i2c_core_200.cpp new file mode 100644 index 000000000..1b882c54a --- /dev/null +++ b/host/lib/usrp/cores/i2c_core_200.cpp @@ -0,0 +1,158 @@ +// +// Copyright 2011-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 "i2c_core_200.hpp" +#include <uhd/exception.hpp> +#include <uhd/utils/msg.hpp> +#include <boost/thread/thread.hpp> //sleep +#include <boost/thread/mutex.hpp> + +#define REG_I2C_WR_PRESCALER_LO (1 << 3) | 0 +#define REG_I2C_WR_PRESCALER_HI (1 << 3) | 1 +#define REG_I2C_WR_CTRL (1 << 3) | 2 +#define REG_I2C_WR_DATA (1 << 3) | 3 +#define REG_I2C_WR_CMD (1 << 3) | 4 +#define REG_I2C_RD_DATA (0 << 3) | 3 +#define REG_I2C_RD_ST (0 << 3) | 4 + +// +// STA, STO, RD, WR, and IACK bits are cleared automatically +// + +#define I2C_CTRL_EN (1 << 7) // core enable +#define I2C_CTRL_IE (1 << 6) // interrupt enable + +#define I2C_CMD_START (1 << 7) // generate (repeated) start condition +#define I2C_CMD_STOP (1 << 6) // generate stop condition +#define I2C_CMD_RD (1 << 5) // read from slave +#define I2C_CMD_WR (1 << 4) // write to slave +#define I2C_CMD_NACK (1 << 3) // when a rcvr, send ACK (ACK=0) or NACK (ACK=1) +#define I2C_CMD_RSVD_2 (1 << 2) // reserved +#define I2C_CMD_RSVD_1 (1 << 1) // reserved +#define I2C_CMD_IACK (1 << 0) // set to clear pending interrupt + +#define I2C_ST_RXACK (1 << 7) // Received acknowledgement from slave (1 = NAK, 0 = ACK) +#define I2C_ST_BUSY (1 << 6) // 1 after START signal detected; 0 after STOP signal detected +#define I2C_ST_AL (1 << 5) // Arbitration lost. 1 when core lost arbitration +#define I2C_ST_RSVD_4 (1 << 4) // reserved +#define I2C_ST_RSVD_3 (1 << 3) // reserved +#define I2C_ST_RSVD_2 (1 << 2) // reserved +#define I2C_ST_TIP (1 << 1) // Transfer-in-progress +#define I2C_ST_IP (1 << 0) // Interrupt pending + +using namespace uhd; + +class i2c_core_200_impl : public i2c_core_200{ +public: + i2c_core_200_impl(wb_iface::sptr iface, const size_t base, const size_t readback): + _iface(iface), _base(base), _readback(readback) + { + //init I2C FPGA interface. + this->poke(REG_I2C_WR_CTRL, 0x0000); + //set prescalers to operate at 400kHz: WB_CLK is 64MHz... + static const boost::uint32_t i2c_datarate = 400000; + static const boost::uint32_t wishbone_clk = 64000000; //FIXME should go somewhere else + boost::uint16_t prescaler = wishbone_clk / (i2c_datarate*5) - 1; + this->poke(REG_I2C_WR_PRESCALER_LO, prescaler & 0xFF); + this->poke(REG_I2C_WR_PRESCALER_HI, (prescaler >> 8) & 0xFF); + this->poke(REG_I2C_WR_CTRL, I2C_CTRL_EN); //enable I2C core + } + + void write_i2c( + boost::uint8_t addr, + const byte_vector_t &bytes + ){ + this->poke(REG_I2C_WR_DATA, (addr << 1) | 0); //addr and read bit (0) + this->poke(REG_I2C_WR_CMD, I2C_CMD_WR | I2C_CMD_START | (bytes.size() == 0 ? I2C_CMD_STOP : 0)); + + //wait for previous transfer to complete + if (not wait_chk_ack()) { + this->poke(REG_I2C_WR_CMD, I2C_CMD_STOP); + return; + } + + for (size_t i = 0; i < bytes.size(); i++) { + this->poke(REG_I2C_WR_DATA, bytes[i]); + this->poke(REG_I2C_WR_CMD, I2C_CMD_WR | ((i == (bytes.size() - 1)) ? I2C_CMD_STOP : 0)); + if(!wait_chk_ack()) { + this->poke(REG_I2C_WR_CMD, I2C_CMD_STOP); + return; + } + } + } + + byte_vector_t read_i2c( + boost::uint8_t addr, + size_t num_bytes + ){ + byte_vector_t bytes; + if (num_bytes == 0) return bytes; + + while (this->peek(REG_I2C_RD_ST) & I2C_ST_BUSY){ + /* NOP */ + } + + this->poke(REG_I2C_WR_DATA, (addr << 1) | 1); //addr and read bit (1) + this->poke(REG_I2C_WR_CMD, I2C_CMD_WR | I2C_CMD_START); + //wait for previous transfer to complete + if (not wait_chk_ack()) { + this->poke(REG_I2C_WR_CMD, I2C_CMD_STOP); + } + for (size_t i = 0; i < num_bytes; i++) { + this->poke(REG_I2C_WR_CMD, I2C_CMD_RD | ((num_bytes == i+1) ? (I2C_CMD_STOP | I2C_CMD_NACK) : 0)); + i2c_wait(); + bytes.push_back(this->peek(REG_I2C_RD_DATA)); + } + return bytes; + } + +private: + void i2c_wait(void) { + for (size_t i = 0; i < 100; i++){ + if ((this->peek(REG_I2C_RD_ST) & I2C_ST_TIP) == 0) return; + boost::this_thread::sleep(boost::posix_time::milliseconds(1)); + } + UHD_MSG(error) << "i2c_core_200: i2c_wait timeout" << std::endl; + } + + bool wait_chk_ack(void){ + i2c_wait(); + return (this->peek(REG_I2C_RD_ST) & I2C_ST_RXACK) == 0; + } + + void poke(const size_t what, const boost::uint8_t cmd) + { + boost::mutex::scoped_lock lock(_mutex); + _iface->poke32(_base, (what << 8) | cmd); + } + + boost::uint8_t peek(const size_t what) + { + boost::mutex::scoped_lock lock(_mutex); + _iface->poke32(_base, what << 8); + return boost::uint8_t(_iface->peek32(_readback)); + } + + wb_iface::sptr _iface; + const size_t _base; + const size_t _readback; + boost::mutex _mutex; +}; + +i2c_core_200::sptr i2c_core_200::make(wb_iface::sptr iface, const size_t base, const size_t readback){ + return sptr(new i2c_core_200_impl(iface, base, readback)); +} diff --git a/host/lib/usrp/cores/i2c_core_200.hpp b/host/lib/usrp/cores/i2c_core_200.hpp new file mode 100644 index 000000000..508855985 --- /dev/null +++ b/host/lib/usrp/cores/i2c_core_200.hpp @@ -0,0 +1,35 @@ +// +// Copyright 2011-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/>. +// + +#ifndef INCLUDED_LIBUHD_USRP_I2C_CORE_200_HPP +#define INCLUDED_LIBUHD_USRP_I2C_CORE_200_HPP + +#include <uhd/config.hpp> +#include <uhd/types/serial.hpp> +#include <boost/utility.hpp> +#include <boost/shared_ptr.hpp> +#include "wb_iface.hpp" + +class i2c_core_200 : boost::noncopyable, public uhd::i2c_iface{ +public: + typedef boost::shared_ptr<i2c_core_200> sptr; + + //! makes a new i2c core from iface and slave base + static sptr make(wb_iface::sptr iface, const size_t base, const size_t readback); +}; + +#endif /* INCLUDED_LIBUHD_USRP_I2C_CORE_200_HPP */ diff --git a/host/lib/usrp/cores/rx_dsp_core_200.cpp b/host/lib/usrp/cores/rx_dsp_core_200.cpp index b73baa81e..ef6b85de9 100644 --- a/host/lib/usrp/cores/rx_dsp_core_200.cpp +++ b/host/lib/usrp/cores/rx_dsp_core_200.cpp @@ -19,6 +19,7 @@ #include <uhd/types/dict.hpp> #include <uhd/exception.hpp> #include <uhd/utils/msg.hpp> +#include <uhd/utils/safe_call.hpp> #include <uhd/utils/algorithm.hpp> #include <boost/assign/list_of.hpp> #include <boost/thread/thread.hpp> //thread sleep @@ -76,8 +77,17 @@ public: this->clear(); } + ~rx_dsp_core_200_impl(void) + { + UHD_SAFE_CALL + ( + //shutdown any possible streaming + this->clear(); + ) + } + void clear(void){ - _iface->poke32(REG_RX_CTRL_NCHANNELS, 1); //also reset + _iface->poke32(REG_RX_CTRL_NCHANNELS, 0); //also reset _iface->poke32(REG_RX_CTRL_VRT_HDR, 0 | (0x1 << 28) //if data with stream id | (0x1 << 26) //has trailer @@ -174,6 +184,15 @@ public: _iface->poke32(REG_DSP_RX_DECIM, (hb1 << 9) | (hb0 << 8) | (decim & 0xff)); + if (decim > 1 and hb0 == 0 and hb1 == 0) + { + UHD_MSG(warning) << boost::format( + "The requested decimation is odd; the user should expect CIC rolloff.\n" + "Select an even decimation to ensure that a halfband filter is enabled.\n" + "decimation = dsp_rate/samp_rate -> %d = (%f MHz)/(%f MHz)\n" + ) % decim_rate % (_tick_rate/1e6) % (rate/1e6); + } + // Calculate CIC decimation (i.e., without halfband decimators) // Calculate closest multiplier constant to reverse gain absent scale multipliers const double rate_pow = std::pow(double(decim & 0xff), 4); diff --git a/host/lib/usrp/cores/time64_core_200.cpp b/host/lib/usrp/cores/time64_core_200.cpp index e460d1106..11b310362 100644 --- a/host/lib/usrp/cores/time64_core_200.cpp +++ b/host/lib/usrp/cores/time64_core_200.cpp @@ -56,6 +56,10 @@ public: if (_mimo_delay_cycles != 0) _sources.push_back("mimo"); } + void enable_gpsdo(void){ + _sources.push_back("gpsdo"); + } + void set_tick_rate(const double rate){ _tick_rate = rate; } @@ -100,7 +104,7 @@ public: assert_has(_sources, source, "time source"); //setup pps flags - if (source == "external"){ + if (source == "external" or source == "gpsdo"){ _iface->poke32(REG_TIME64_FLAGS, FLAG_TIME64_PPS_SMA | FLAG_TIME64_PPS_POSEDGE); } else if (source == "_external_"){ diff --git a/host/lib/usrp/cores/time64_core_200.hpp b/host/lib/usrp/cores/time64_core_200.hpp index 7571573a5..315f2ba67 100644 --- a/host/lib/usrp/cores/time64_core_200.hpp +++ b/host/lib/usrp/cores/time64_core_200.hpp @@ -1,5 +1,5 @@ // -// Copyright 2011 Ettus Research LLC +// Copyright 2011-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 @@ -42,6 +42,8 @@ public: const size_t mimo_delay_cycles = 0 // 0 means no-mimo ); + virtual void enable_gpsdo(void) = 0; + virtual void set_tick_rate(const double rate) = 0; virtual uhd::time_spec_t get_time_now(void) = 0; diff --git a/host/lib/usrp/cores/tx_dsp_core_200.cpp b/host/lib/usrp/cores/tx_dsp_core_200.cpp index f905a7551..808f13028 100644 --- a/host/lib/usrp/cores/tx_dsp_core_200.cpp +++ b/host/lib/usrp/cores/tx_dsp_core_200.cpp @@ -126,6 +126,15 @@ public: _iface->poke32(REG_DSP_TX_INTERP, (hb1 << 9) | (hb0 << 8) | (interp & 0xff)); + if (interp > 1 and hb0 == 0 and hb1 == 0) + { + UHD_MSG(warning) << boost::format( + "The requested interpolation is odd; the user should expect CIC rolloff.\n" + "Select an even interpolation to ensure that a halfband filter is enabled.\n" + "interpolation = dsp_rate/samp_rate -> %d = (%f MHz)/(%f MHz)\n" + ) % interp_rate % (_tick_rate/1e6) % (rate/1e6); + } + // Calculate CIC interpolation (i.e., without halfband interpolators) // Calculate closest multiplier constant to reverse gain absent scale multipliers const double rate_pow = std::pow(double(interp & 0xff), 3); diff --git a/host/lib/usrp/dboard/db_basic_and_lf.cpp b/host/lib/usrp/dboard/db_basic_and_lf.cpp index fc42a73d5..2b30dab52 100644 --- a/host/lib/usrp/dboard/db_basic_and_lf.cpp +++ b/host/lib/usrp/dboard/db_basic_and_lf.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-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 @@ -108,11 +108,17 @@ basic_rx::basic_rx(ctor_args_t args, double max_freq) : rx_dboard_base(args){ //////////////////////////////////////////////////////////////////// // Register properties //////////////////////////////////////////////////////////////////// - this->get_rx_subtree()->create<std::string>("name").set( - std::string(str(boost::format("%s - %s") - % get_rx_id().to_pp_string() - % get_subdev_name() + if(get_rx_id() == 0x0001){ + this->get_rx_subtree()->create<std::string>("name").set( + std::string(str(boost::format("BasicRX (%s)") % get_subdev_name() ))); + } + else{ + this->get_rx_subtree()->create<std::string>("name").set( + std::string(str(boost::format("LFRX (%s)") % get_subdev_name() + ))); + } + this->get_rx_subtree()->create<int>("gains"); //phony property so this dir exists this->get_rx_subtree()->create<double>("freq/value") .publish(&always_zero_freq); @@ -157,11 +163,17 @@ basic_tx::basic_tx(ctor_args_t args, double max_freq) : tx_dboard_base(args){ //////////////////////////////////////////////////////////////////// // Register properties //////////////////////////////////////////////////////////////////// - this->get_tx_subtree()->create<std::string>("name").set( - std::string(str(boost::format("%s - %s") - % get_tx_id().to_pp_string() - % get_subdev_name() + if(get_tx_id() == 0x0000){ + this->get_tx_subtree()->create<std::string>("name").set( + std::string(str(boost::format("BasicTX (%s)") % get_subdev_name() ))); + } + else{ + this->get_tx_subtree()->create<std::string>("name").set( + std::string(str(boost::format("LFTX (%s)") % get_subdev_name() + ))); + } + this->get_tx_subtree()->create<int>("gains"); //phony property so this dir exists this->get_tx_subtree()->create<double>("freq/value") .publish(&always_zero_freq); diff --git a/host/lib/usrp/dboard/db_dbsrx.cpp b/host/lib/usrp/dboard/db_dbsrx.cpp index 95c5c5d4d..b1cee4aa7 100644 --- a/host/lib/usrp/dboard/db_dbsrx.cpp +++ b/host/lib/usrp/dboard/db_dbsrx.cpp @@ -202,7 +202,7 @@ dbsrx::dbsrx(ctor_args_t args) : rx_dboard_base(args){ // Register properties //////////////////////////////////////////////////////////////////// this->get_rx_subtree()->create<std::string>("name") - .set(get_rx_id().to_pp_string()); + .set("DBSRX"); this->get_rx_subtree()->create<sensor_value_t>("sensors/lo_locked") .publish(boost::bind(&dbsrx::get_locked, this)); BOOST_FOREACH(const std::string &name, dbsrx_gain_ranges.keys()){ diff --git a/host/lib/usrp/dboard/db_dbsrx2.cpp b/host/lib/usrp/dboard/db_dbsrx2.cpp index 517b7b183..013f3178a 100644 --- a/host/lib/usrp/dboard/db_dbsrx2.cpp +++ b/host/lib/usrp/dboard/db_dbsrx2.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-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 @@ -189,7 +189,7 @@ dbsrx2::dbsrx2(ctor_args_t args) : rx_dboard_base(args){ // Register properties //////////////////////////////////////////////////////////////////// this->get_rx_subtree()->create<std::string>("name") - .set(get_rx_id().to_pp_string()); + .set("DBSRX2"); this->get_rx_subtree()->create<sensor_value_t>("sensors/lo_locked") .publish(boost::bind(&dbsrx2::get_locked, this)); BOOST_FOREACH(const std::string &name, dbsrx2_gain_ranges.keys()){ diff --git a/host/lib/usrp/dboard/db_rfx.cpp b/host/lib/usrp/dboard/db_rfx.cpp index 32aa3fe04..cf3b29ddc 100644 --- a/host/lib/usrp/dboard/db_rfx.cpp +++ b/host/lib/usrp/dboard/db_rfx.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-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 @@ -174,7 +174,14 @@ rfx_xcvr::rfx_xcvr( //////////////////////////////////////////////////////////////////// // Register RX properties //////////////////////////////////////////////////////////////////// - this->get_rx_subtree()->create<std::string>("name").set("RFX RX"); + if(get_rx_id() == 0x0024) this->get_rx_subtree()->create<std::string>("name").set("RFX400 RX"); + else if(get_rx_id() == 0x0025) this->get_rx_subtree()->create<std::string>("name").set("RFX900 RX"); + else if(get_rx_id() == 0x0034) this->get_rx_subtree()->create<std::string>("name").set("RFX1800 RX"); + else if(get_rx_id() == 0x0026) this->get_rx_subtree()->create<std::string>("name").set("RFX1200 RX"); + else if(get_rx_id() == 0x002c) this->get_rx_subtree()->create<std::string>("name").set("RFX2200 RX"); + else if(get_rx_id() == 0x0027) this->get_rx_subtree()->create<std::string>("name").set("RFX2400 RX"); + else this->get_rx_subtree()->create<std::string>("name").set("RFX RX"); + this->get_rx_subtree()->create<sensor_value_t>("sensors/lo_locked") .publish(boost::bind(&rfx_xcvr::get_locked, this, dboard_iface::UNIT_RX)); BOOST_FOREACH(const std::string &name, _rx_gain_ranges.keys()){ @@ -203,7 +210,14 @@ rfx_xcvr::rfx_xcvr( //////////////////////////////////////////////////////////////////// // Register TX properties //////////////////////////////////////////////////////////////////// - this->get_tx_subtree()->create<std::string>("name").set("RFX TX"); + if(get_tx_id() == 0x0028) this->get_tx_subtree()->create<std::string>("name").set("RFX400 TX"); + else if(get_tx_id() == 0x0029) this->get_tx_subtree()->create<std::string>("name").set("RFX900 TX"); + else if(get_tx_id() == 0x0035) this->get_tx_subtree()->create<std::string>("name").set("RFX1800 TX"); + else if(get_tx_id() == 0x002a) this->get_tx_subtree()->create<std::string>("name").set("RFX1200 TX"); + else if(get_tx_id() == 0x002d) this->get_tx_subtree()->create<std::string>("name").set("RFX2200 TX"); + else if(get_tx_id() == 0x002b) this->get_tx_subtree()->create<std::string>("name").set("RFX2400 TX"); + else this->get_tx_subtree()->create<std::string>("name").set("RFX TX"); + this->get_tx_subtree()->create<sensor_value_t>("sensors/lo_locked") .publish(boost::bind(&rfx_xcvr::get_locked, this, dboard_iface::UNIT_TX)); this->get_tx_subtree()->create<int>("gains"); //phony property so this dir exists @@ -258,7 +272,7 @@ void rfx_xcvr::set_rx_ant(const std::string &ant){ //set the rx atr regs that change with antenna setting if (ant == "CAL") { - this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY, _power_up | ANT_TXRX | MIXER_DIS); + this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY, _power_up | ANT_TXRX | MIXER_ENB); this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, _power_up | ANT_TXRX | MIXER_ENB); this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_RX_ONLY, _power_up | MIXER_ENB | ANT_TXRX ); } @@ -358,7 +372,7 @@ double rfx_xcvr::set_lo_freq( * The goal here to to loop though possible R dividers, * band select clock dividers, and prescaler values. * Calculate the A and B counters for each set of values. - * The loop exists when it meets all of the constraints. + * The loop exits when it meets all of the constraints. * The resulting loop values are loaded into the registers. * * fvco = [P*B + A] * fref/R diff --git a/host/lib/usrp/dboard/db_sbx_common.cpp b/host/lib/usrp/dboard/db_sbx_common.cpp index d1cd5b373..728cb17e9 100644 --- a/host/lib/usrp/dboard/db_sbx_common.cpp +++ b/host/lib/usrp/dboard/db_sbx_common.cpp @@ -1,5 +1,5 @@ // -// Copyright 2011 Ettus Research LLC +// Copyright 2011-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 @@ -16,7 +16,6 @@ // #include "db_sbx_common.hpp" -#include "adf4350_regs.hpp" using namespace uhd; using namespace uhd::usrp; @@ -127,7 +126,10 @@ sbx_xcvr::sbx_xcvr(ctor_args_t args) : xcvr_dboard_base(args){ //////////////////////////////////////////////////////////////////// // Register RX properties //////////////////////////////////////////////////////////////////// - this->get_rx_subtree()->create<std::string>("name").set("SBX RX"); + if(get_rx_id() == 0x054) this->get_rx_subtree()->create<std::string>("name").set("SBXv3 RX"); + else if(get_rx_id() == 0x065) this->get_rx_subtree()->create<std::string>("name").set("SBXv4 RX"); + else this->get_rx_subtree()->create<std::string>("name").set("SBX RX"); + this->get_rx_subtree()->create<sensor_value_t>("sensors/lo_locked") .publish(boost::bind(&sbx_xcvr::get_locked, this, dboard_iface::UNIT_RX)); BOOST_FOREACH(const std::string &name, sbx_rx_gain_ranges.keys()){ @@ -156,7 +158,10 @@ sbx_xcvr::sbx_xcvr(ctor_args_t args) : xcvr_dboard_base(args){ //////////////////////////////////////////////////////////////////// // Register TX properties //////////////////////////////////////////////////////////////////// - this->get_tx_subtree()->create<std::string>("name").set("SBX TX"); + if(get_tx_id() == 0x055) this->get_tx_subtree()->create<std::string>("name").set("SBXv3 TX"); + else if(get_tx_id() == 0x067) this->get_tx_subtree()->create<std::string>("name").set("SBXv4 TX"); + else this->get_tx_subtree()->create<std::string>("name").set("SBX TX"); + this->get_tx_subtree()->create<sensor_value_t>("sensors/lo_locked") .publish(boost::bind(&sbx_xcvr::get_locked, this, dboard_iface::UNIT_TX)); BOOST_FOREACH(const std::string &name, sbx_tx_gain_ranges.keys()){ @@ -213,8 +218,8 @@ void sbx_xcvr::update_atr(void){ int tx_pga0_iobits = tx_pga0_gain_to_iobits(_tx_gains["PGA0"]); int rx_lo_lpf_en = (_rx_lo_freq == sbx_enable_rx_lo_filter.clip(_rx_lo_freq)) ? LO_LPF_EN : 0; int tx_lo_lpf_en = (_tx_lo_freq == sbx_enable_tx_lo_filter.clip(_tx_lo_freq)) ? LO_LPF_EN : 0; - int rx_ld_led = get_locked(dboard_iface::UNIT_RX).to_bool() ? 0 : RX_LED_LD; - int tx_ld_led = get_locked(dboard_iface::UNIT_TX).to_bool() ? 0 : TX_LED_LD; + int rx_ld_led = _rx_lo_lock_cache ? 0 : RX_LED_LD; + int tx_ld_led = _tx_lo_lock_cache ? 0 : TX_LED_LD; int rx_ant_led = _rx_ant == "TX/RX" ? RX_LED_RX1RX2 : 0; int tx_ant_led = _tx_ant == "TX/RX" ? 0 : TX_LED_TXRX; @@ -283,8 +288,14 @@ void sbx_xcvr::set_tx_ant(const std::string &ant){ **********************************************************************/ double sbx_xcvr::set_lo_freq(dboard_iface::unit_t unit, double target_freq) { const double actual = db_actual->set_lo_freq(unit, target_freq); - if (unit == dboard_iface::UNIT_RX) _rx_lo_freq = actual; - if (unit == dboard_iface::UNIT_TX) _tx_lo_freq = actual; + if (unit == dboard_iface::UNIT_RX){ + _rx_lo_lock_cache = false; + _rx_lo_freq = actual; + } + if (unit == dboard_iface::UNIT_TX){ + _tx_lo_lock_cache = false; + _tx_lo_freq = actual; + } update_atr(); return actual; } @@ -292,6 +303,13 @@ double sbx_xcvr::set_lo_freq(dboard_iface::unit_t unit, double target_freq) { sensor_value_t sbx_xcvr::get_locked(dboard_iface::unit_t unit) { const bool locked = (this->get_iface()->read_gpio(unit) & LOCKDET_MASK) != 0; + + if (unit == dboard_iface::UNIT_RX) _rx_lo_lock_cache = locked; + if (unit == dboard_iface::UNIT_TX) _tx_lo_lock_cache = locked; + + //write the new lock cache setting to atr regs + update_atr(); + return sensor_value_t("LO", locked, "locked", "unlocked"); } diff --git a/host/lib/usrp/dboard/db_sbx_common.hpp b/host/lib/usrp/dboard/db_sbx_common.hpp index 501a7f1fc..2a0e83115 100644 --- a/host/lib/usrp/dboard/db_sbx_common.hpp +++ b/host/lib/usrp/dboard/db_sbx_common.hpp @@ -1,5 +1,5 @@ // -// Copyright 2011 Ettus Research LLC +// Copyright 2011-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 @@ -138,6 +138,7 @@ protected: uhd::dict<std::string, double> _tx_gains, _rx_gains; double _rx_lo_freq, _tx_lo_freq; std::string _tx_ant, _rx_ant; + bool _rx_lo_lock_cache, _tx_lo_lock_cache; void set_rx_ant(const std::string &ant); void set_tx_ant(const std::string &ant); diff --git a/host/lib/usrp/dboard/db_sbx_version3.cpp b/host/lib/usrp/dboard/db_sbx_version3.cpp index 6e20d5882..2765d530c 100644 --- a/host/lib/usrp/dboard/db_sbx_version3.cpp +++ b/host/lib/usrp/dboard/db_sbx_version3.cpp @@ -1,5 +1,5 @@ // -// Copyright 2011 Ettus Research LLC +// Copyright 2011-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 @@ -75,7 +75,6 @@ double sbx_xcvr::sbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar if(ref_freq <= 12.5e6) D = adf4350_regs_t::REFERENCE_DOUBLER_ENABLED; //increase RF divider until acceptable VCO frequency - //start with target_freq*2 because mixer has divide by 2 double vco_freq = target_freq; while (vco_freq < 2.2e9) { vco_freq *= 2; @@ -83,7 +82,7 @@ double sbx_xcvr::sbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar } //use 8/9 prescaler for vco_freq > 3 GHz (pg.18 prescaler) - adf4350_regs_t::prescaler_t prescaler = vco_freq > 3e9 ? adf4350_regs_t::PRESCALER_8_9 : adf4350_regs_t::PRESCALER_4_5; + adf4350_regs_t::prescaler_t prescaler = target_freq > 3e9 ? adf4350_regs_t::PRESCALER_8_9 : adf4350_regs_t::PRESCALER_4_5; /* * The goal here is to loop though possible R dividers, @@ -91,7 +90,7 @@ double sbx_xcvr::sbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar * (frac) dividers. * * Calculate the N and F dividers for each set of values. - * The loop exists when it meets all of the constraints. + * The loop exits when it meets all of the constraints. * The resulting loop values are loaded into the registers. * * from pg.21 @@ -110,7 +109,7 @@ double sbx_xcvr::sbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar if (pfd_freq > 25e6) continue; //ignore fractional part of tuning - N = int(std::floor(vco_freq/pfd_freq)); + N = int(std::floor(target_freq/pfd_freq)); //keep N > minimum int divider requirement if (N < prescaler_to_min_int_div[prescaler]) continue; @@ -125,7 +124,7 @@ double sbx_xcvr::sbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar //Fractional-N calculation MOD = 4095; //max fractional accuracy - FRAC = int((vco_freq/pfd_freq - N)*MOD); + FRAC = int((target_freq/pfd_freq - N)*MOD); //Reference divide-by-2 for 50% duty cycle // if R even, move one divide by 2 to to regs.reference_divide_by_2 @@ -135,12 +134,12 @@ double sbx_xcvr::sbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar } //actual frequency calculation - actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))/RFdiv); + actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))); UHD_LOGV(often) << boost::format("SBX Intermediates: ref=%0.2f, outdiv=%f, fbdiv=%f") % (ref_freq*(1+int(D))/(R*(1+int(T)))) % double(RFdiv*2) % double(N + double(FRAC)/double(MOD)) << std::endl - << boost::format("SBX tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d, LD=%s" - ) % R % BS % N % FRAC % MOD % T % D % RFdiv % self_base->get_locked(unit).to_pp_string() << std::endl + << boost::format("SBX tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d" + ) % R % BS % N % FRAC % MOD % T % D % RFdiv << std::endl << boost::format("SBX Frequencies (MHz): REQ=%0.2f, ACT=%0.2f, VCO=%0.2f, PFD=%0.2f, BAND=%0.2f" ) % (target_freq/1e6) % (actual_freq/1e6) % (vco_freq/1e6) % (pfd_freq/1e6) % (pfd_freq/BS/1e6) << std::endl; @@ -155,6 +154,9 @@ double sbx_xcvr::sbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar regs.frac_12_bit = FRAC; regs.int_16_bit = N; regs.mod_12_bit = MOD; + regs.clock_divider_12_bit = std::max(1, int(std::ceil(400e-6*pfd_freq/MOD))); + regs.feedback_select = adf4350_regs_t::FEEDBACK_SELECT_DIVIDED; + regs.clock_div_mode = adf4350_regs_t::CLOCK_DIV_MODE_RESYNC_ENABLE; regs.prescaler = prescaler; regs.r_counter_10_bit = R; regs.reference_divide_by_2 = T; @@ -163,6 +165,11 @@ double sbx_xcvr::sbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar UHD_ASSERT_THROW(rfdivsel_to_enum.has_key(RFdiv)); regs.rf_divider_select = rfdivsel_to_enum[RFdiv]; + //reset the N and R counter + regs.counter_reset = adf4350_regs_t::COUNTER_RESET_ENABLED; + self_base->get_iface()->write_spi(unit, spi_config_t::EDGE_RISE, regs.get_reg(2), 32); + regs.counter_reset = adf4350_regs_t::COUNTER_RESET_DISABLED; + //write the registers //correct power-up sequence to write registers (5, 4, 3, 2, 1, 0) int addr; diff --git a/host/lib/usrp/dboard/db_sbx_version4.cpp b/host/lib/usrp/dboard/db_sbx_version4.cpp index c8128d5f4..27fd68b05 100644 --- a/host/lib/usrp/dboard/db_sbx_version4.cpp +++ b/host/lib/usrp/dboard/db_sbx_version4.cpp @@ -1,5 +1,5 @@ // -// Copyright 2011 Ettus Research LLC +// Copyright 2011-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 @@ -78,7 +78,6 @@ double sbx_xcvr::sbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar if(ref_freq <= 12.5e6) D = adf4351_regs_t::REFERENCE_DOUBLER_ENABLED; //increase RF divider until acceptable VCO frequency - //start with target_freq*2 because mixer has divide by 2 double vco_freq = target_freq; while (vco_freq < 2.2e9) { vco_freq *= 2; @@ -86,7 +85,7 @@ double sbx_xcvr::sbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar } //use 8/9 prescaler for vco_freq > 3 GHz (pg.18 prescaler) - adf4351_regs_t::prescaler_t prescaler = vco_freq > 3e9 ? adf4351_regs_t::PRESCALER_8_9 : adf4351_regs_t::PRESCALER_4_5; + adf4351_regs_t::prescaler_t prescaler = target_freq > 3e9 ? adf4351_regs_t::PRESCALER_8_9 : adf4351_regs_t::PRESCALER_4_5; /* * The goal here is to loop though possible R dividers, @@ -94,7 +93,7 @@ double sbx_xcvr::sbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar * (frac) dividers. * * Calculate the N and F dividers for each set of values. - * The loop exists when it meets all of the constraints. + * The loop exits when it meets all of the constraints. * The resulting loop values are loaded into the registers. * * from pg.21 @@ -128,7 +127,7 @@ double sbx_xcvr::sbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar //Fractional-N calculation MOD = 4095; //max fractional accuracy - FRAC = int((vco_freq/pfd_freq - N)*MOD); + FRAC = int((target_freq/pfd_freq - N)*MOD); //Reference divide-by-2 for 50% duty cycle // if R even, move one divide by 2 to to regs.reference_divide_by_2 @@ -138,12 +137,12 @@ double sbx_xcvr::sbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar } //actual frequency calculation - actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))/RFdiv); + actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))); UHD_LOGV(often) << boost::format("SBX Intermediates: ref=%0.2f, outdiv=%f, fbdiv=%f") % (ref_freq*(1+int(D))/(R*(1+int(T)))) % double(RFdiv*2) % double(N + double(FRAC)/double(MOD)) << std::endl - << boost::format("SBX tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d, LD=%s" - ) % R % BS % N % FRAC % MOD % T % D % RFdiv % self_base->get_locked(unit).to_pp_string() << std::endl + << boost::format("SBX tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d" + ) % R % BS % N % FRAC % MOD % T % D % RFdiv << std::endl << boost::format("SBX Frequencies (MHz): REQ=%0.2f, ACT=%0.2f, VCO=%0.2f, PFD=%0.2f, BAND=%0.2f" ) % (target_freq/1e6) % (actual_freq/1e6) % (vco_freq/1e6) % (pfd_freq/1e6) % (pfd_freq/BS/1e6) << std::endl; @@ -158,6 +157,9 @@ double sbx_xcvr::sbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar regs.frac_12_bit = FRAC; regs.int_16_bit = N; regs.mod_12_bit = MOD; + regs.clock_divider_12_bit = std::max(1, int(std::ceil(400e-6*pfd_freq/MOD))); + regs.feedback_select = adf4351_regs_t::FEEDBACK_SELECT_DIVIDED; + regs.clock_div_mode = adf4351_regs_t::CLOCK_DIV_MODE_RESYNC_ENABLE; regs.prescaler = prescaler; regs.r_counter_10_bit = R; regs.reference_divide_by_2 = T; @@ -166,6 +168,11 @@ double sbx_xcvr::sbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar UHD_ASSERT_THROW(rfdivsel_to_enum.has_key(RFdiv)); regs.rf_divider_select = rfdivsel_to_enum[RFdiv]; + //reset the N and R counter + regs.counter_reset = adf4351_regs_t::COUNTER_RESET_ENABLED; + self_base->get_iface()->write_spi(unit, spi_config_t::EDGE_RISE, regs.get_reg(2), 32); + regs.counter_reset = adf4351_regs_t::COUNTER_RESET_DISABLED; + //write the registers //correct power-up sequence to write registers (5, 4, 3, 2, 1, 0) int addr; diff --git a/host/lib/usrp/dboard/db_tvrx.cpp b/host/lib/usrp/dboard/db_tvrx.cpp index fd86d5b83..edee46cd5 100644 --- a/host/lib/usrp/dboard/db_tvrx.cpp +++ b/host/lib/usrp/dboard/db_tvrx.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-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 @@ -186,7 +186,7 @@ tvrx::tvrx(ctor_args_t args) : rx_dboard_base(args){ // Register properties //////////////////////////////////////////////////////////////////// this->get_rx_subtree()->create<std::string>("name") - .set(get_rx_id().to_pp_string()); + .set("TVRX"); this->get_rx_subtree()->create<int>("sensors"); //phony property so this dir exists BOOST_FOREACH(const std::string &name, get_tvrx_gain_ranges().keys()){ this->get_rx_subtree()->create<double>("gains/"+name+"/value") diff --git a/host/lib/usrp/dboard/db_tvrx2.cpp b/host/lib/usrp/dboard/db_tvrx2.cpp index 628221527..0bfa5229a 100644 --- a/host/lib/usrp/dboard/db_tvrx2.cpp +++ b/host/lib/usrp/dboard/db_tvrx2.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010 Ettus Research LLC +// Copyright 2010,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 @@ -955,7 +955,7 @@ tvrx2::tvrx2(ctor_args_t args) : rx_dboard_base(args){ // Register properties //////////////////////////////////////////////////////////////////// this->get_rx_subtree()->create<std::string>("name") - .set(get_rx_id().to_pp_string()); + .set("TVRX2"); this->get_rx_subtree()->create<sensor_value_t>("sensors/lo_locked") .publish(boost::bind(&tvrx2::get_locked, this)); this->get_rx_subtree()->create<sensor_value_t>("sensors/rssi") diff --git a/host/lib/usrp/dboard/db_wbx_common.cpp b/host/lib/usrp/dboard/db_wbx_common.cpp index 58ce03d79..503e5aabf 100644 --- a/host/lib/usrp/dboard/db_wbx_common.cpp +++ b/host/lib/usrp/dboard/db_wbx_common.cpp @@ -1,5 +1,5 @@ // -// Copyright 2011 Ettus Research LLC +// Copyright 2011-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 @@ -16,7 +16,6 @@ // #include "db_wbx_common.hpp" -#include "adf4350_regs.hpp" #include <uhd/types/dict.hpp> #include <uhd/types/ranges.hpp> #include <uhd/types/sensors.hpp> diff --git a/host/lib/usrp/dboard/db_wbx_simple.cpp b/host/lib/usrp/dboard/db_wbx_simple.cpp index 3d633a672..4ba30255d 100644 --- a/host/lib/usrp/dboard/db_wbx_simple.cpp +++ b/host/lib/usrp/dboard/db_wbx_simple.cpp @@ -1,5 +1,5 @@ // -// Copyright 2011 Ettus Research LLC +// Copyright 2011-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 @@ -81,8 +81,10 @@ wbx_simple::wbx_simple(ctor_args_t args) : wbx_base(args){ //////////////////////////////////////////////////////////////////// // Register RX properties //////////////////////////////////////////////////////////////////// + this->get_rx_subtree()->access<std::string>("name").set( - this->get_rx_subtree()->access<std::string>("name").get() + " + Simple GDB"); + std::string(str(boost::format("%s+GDB") % this->get_rx_subtree()->access<std::string>("name").get() + ))); this->get_rx_subtree()->create<std::string>("antenna/value") .subscribe(boost::bind(&wbx_simple::set_rx_ant, this, _1)) .set("RX2"); @@ -93,7 +95,8 @@ wbx_simple::wbx_simple(ctor_args_t args) : wbx_base(args){ // Register TX properties //////////////////////////////////////////////////////////////////// this->get_tx_subtree()->access<std::string>("name").set( - this->get_tx_subtree()->access<std::string>("name").get() + " + Simple GDB"); + std::string(str(boost::format("%s+GDB") % this->get_tx_subtree()->access<std::string>("name").get() + ))); this->get_tx_subtree()->create<std::string>("antenna/value") .subscribe(boost::bind(&wbx_simple::set_tx_ant, this, _1)) .set(wbx_tx_antennas.at(0)); diff --git a/host/lib/usrp/dboard/db_wbx_version2.cpp b/host/lib/usrp/dboard/db_wbx_version2.cpp index ad31339e7..5f6118a91 100644 --- a/host/lib/usrp/dboard/db_wbx_version2.cpp +++ b/host/lib/usrp/dboard/db_wbx_version2.cpp @@ -1,5 +1,5 @@ // -// Copyright 2011 Ettus Research LLC +// Copyright 2011-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 @@ -78,7 +78,7 @@ wbx_base::wbx_version2::wbx_version2(wbx_base *_self_wbx_base) { //////////////////////////////////////////////////////////////////// // Register RX properties //////////////////////////////////////////////////////////////////// - this->get_rx_subtree()->create<std::string>("name").set("WBX RX v2"); + this->get_rx_subtree()->create<std::string>("name").set("WBXv2 RX"); this->get_rx_subtree()->create<double>("freq/value") .coerce(boost::bind(&wbx_base::wbx_version2::set_lo_freq, this, dboard_iface::UNIT_RX, _1)) .set((wbx_v2_freq_range.start() + wbx_v2_freq_range.stop())/2.0); @@ -87,7 +87,7 @@ wbx_base::wbx_version2::wbx_version2(wbx_base *_self_wbx_base) { //////////////////////////////////////////////////////////////////// // Register TX properties //////////////////////////////////////////////////////////////////// - this->get_tx_subtree()->create<std::string>("name").set("WBX TX v2"); + this->get_tx_subtree()->create<std::string>("name").set("WBXv2 TX"); BOOST_FOREACH(const std::string &name, wbx_v2_tx_gain_ranges.keys()){ self_base->get_tx_subtree()->create<double>("gains/"+name+"/value") .coerce(boost::bind(&wbx_base::wbx_version2::set_tx_gain, this, _1, name)) @@ -166,6 +166,9 @@ double wbx_base::wbx_version2::set_lo_freq(dboard_iface::unit_t unit, double tar "WBX tune: target frequency %f Mhz" ) % (target_freq/1e6) << std::endl; + //start with target_freq*2 because mixer has divide by 2 + target_freq *= 2; + //map prescaler setting to mininmum integer divider (N) values (pg.18 prescaler) static const uhd::dict<int, int> prescaler_to_min_int_div = map_list_of (0,23) //adf4350_regs_t::PRESCALER_4_5 @@ -193,12 +196,13 @@ double wbx_base::wbx_version2::set_lo_freq(dboard_iface::unit_t unit, double tar if(ref_freq <= 12.5e6) D = adf4350_regs_t::REFERENCE_DOUBLER_ENABLED; //increase RF divider until acceptable VCO frequency - //start with target_freq*2 because mixer has divide by 2 - double vco_freq = target_freq*2; + const bool do_sync = (target_freq/2 > ref_freq); + double vco_freq = target_freq; while (vco_freq < 2.2e9) { vco_freq *= 2; RFdiv *= 2; } + if (do_sync) vco_freq = target_freq; //use 8/9 prescaler for vco_freq > 3 GHz (pg.18 prescaler) adf4350_regs_t::prescaler_t prescaler = vco_freq > 3e9 ? adf4350_regs_t::PRESCALER_8_9 : adf4350_regs_t::PRESCALER_4_5; @@ -209,7 +213,7 @@ double wbx_base::wbx_version2::set_lo_freq(dboard_iface::unit_t unit, double tar * (frac) dividers. * * Calculate the N and F dividers for each set of values. - * The loop exists when it meets all of the constraints. + * The loop exits when it meets all of the constraints. * The resulting loop values are loaded into the registers. * * from pg.21 @@ -253,14 +257,13 @@ double wbx_base::wbx_version2::set_lo_freq(dboard_iface::unit_t unit, double tar } //actual frequency calculation - actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))/RFdiv/2); - + actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))/2/(vco_freq/target_freq)); UHD_LOGV(often) << boost::format("WBX Intermediates: ref=%0.2f, outdiv=%f, fbdiv=%f") % (ref_freq*(1+int(D))/(R*(1+int(T)))) % double(RFdiv*2) % double(N + double(FRAC)/double(MOD)) << std::endl - << boost::format("WBX tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d, LD=%s" - ) % R % BS % N % FRAC % MOD % T % D % RFdiv % self_base->get_locked(unit).to_pp_string() << std::endl + << boost::format("WBX tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d" + ) % R % BS % N % FRAC % MOD % T % D % RFdiv << std::endl << boost::format("WBX Frequencies (MHz): REQ=%0.2f, ACT=%0.2f, VCO=%0.2f, PFD=%0.2f, BAND=%0.2f" ) % (target_freq/1e6) % (actual_freq/1e6) % (vco_freq/1e6) % (pfd_freq/1e6) % (pfd_freq/BS/1e6) << std::endl; @@ -270,6 +273,12 @@ double wbx_base::wbx_version2::set_lo_freq(dboard_iface::unit_t unit, double tar regs.frac_12_bit = FRAC; regs.int_16_bit = N; regs.mod_12_bit = MOD; + if (do_sync) + { + regs.clock_divider_12_bit = std::max(1, int(std::ceil(400e-6*pfd_freq/MOD))); + regs.feedback_select = adf4350_regs_t::FEEDBACK_SELECT_DIVIDED; + regs.clock_div_mode = adf4350_regs_t::CLOCK_DIV_MODE_RESYNC_ENABLE; + } regs.prescaler = prescaler; regs.r_counter_10_bit = R; regs.reference_divide_by_2 = T; @@ -307,6 +316,11 @@ double wbx_base::wbx_version2::set_lo_freq(dboard_iface::unit_t unit, double tar } + //reset the N and R counter + regs.counter_reset = adf4350_regs_t::COUNTER_RESET_ENABLED; + self_base->get_iface()->write_spi(unit, spi_config_t::EDGE_RISE, regs.get_reg(2), 32); + regs.counter_reset = adf4350_regs_t::COUNTER_RESET_DISABLED; + //write the registers //correct power-up sequence to write registers (5, 4, 3, 2, 1, 0) int addr; diff --git a/host/lib/usrp/dboard/db_wbx_version3.cpp b/host/lib/usrp/dboard/db_wbx_version3.cpp index 7ef47edd4..3e8fc8095 100644 --- a/host/lib/usrp/dboard/db_wbx_version3.cpp +++ b/host/lib/usrp/dboard/db_wbx_version3.cpp @@ -1,5 +1,5 @@ // -// Copyright 2011 Ettus Research LLC +// Copyright 2011-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 @@ -84,7 +84,7 @@ wbx_base::wbx_version3::wbx_version3(wbx_base *_self_wbx_base) { //////////////////////////////////////////////////////////////////// // Register RX properties //////////////////////////////////////////////////////////////////// - this->get_rx_subtree()->create<std::string>("name").set("WBX RX v3"); + this->get_rx_subtree()->create<std::string>("name").set("WBXv3 RX"); this->get_rx_subtree()->create<double>("freq/value") .coerce(boost::bind(&wbx_base::wbx_version3::set_lo_freq, this, dboard_iface::UNIT_RX, _1)) .set((wbx_v3_freq_range.start() + wbx_v3_freq_range.stop())/2.0); @@ -93,7 +93,7 @@ wbx_base::wbx_version3::wbx_version3(wbx_base *_self_wbx_base) { //////////////////////////////////////////////////////////////////// // Register TX properties //////////////////////////////////////////////////////////////////// - this->get_tx_subtree()->create<std::string>("name").set("WBX TX v3"); + this->get_tx_subtree()->create<std::string>("name").set("WBXv3 TX"); BOOST_FOREACH(const std::string &name, wbx_v3_tx_gain_ranges.keys()){ self_base->get_tx_subtree()->create<double>("gains/"+name+"/value") .coerce(boost::bind(&wbx_base::wbx_version3::set_tx_gain, this, _1, name)) @@ -198,6 +198,9 @@ double wbx_base::wbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar "WBX tune: target frequency %f Mhz" ) % (target_freq/1e6) << std::endl; + //start with target_freq*2 because mixer has divide by 2 + target_freq *= 2; + //map prescaler setting to mininmum integer divider (N) values (pg.18 prescaler) static const uhd::dict<int, int> prescaler_to_min_int_div = map_list_of (0,23) //adf4350_regs_t::PRESCALER_4_5 @@ -225,12 +228,13 @@ double wbx_base::wbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar if(ref_freq <= 12.5e6) D = adf4350_regs_t::REFERENCE_DOUBLER_ENABLED; //increase RF divider until acceptable VCO frequency - //start with target_freq*2 because mixer has divide by 2 - double vco_freq = target_freq*2; + const bool do_sync = (target_freq/2 > ref_freq); + double vco_freq = target_freq; while (vco_freq < 2.2e9) { vco_freq *= 2; RFdiv *= 2; } + if (do_sync) vco_freq = target_freq; //use 8/9 prescaler for vco_freq > 3 GHz (pg.18 prescaler) adf4350_regs_t::prescaler_t prescaler = vco_freq > 3e9 ? adf4350_regs_t::PRESCALER_8_9 : adf4350_regs_t::PRESCALER_4_5; @@ -241,7 +245,7 @@ double wbx_base::wbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar * (frac) dividers. * * Calculate the N and F dividers for each set of values. - * The loop exists when it meets all of the constraints. + * The loop exits when it meets all of the constraints. * The resulting loop values are loaded into the registers. * * from pg.21 @@ -285,14 +289,13 @@ double wbx_base::wbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar } //actual frequency calculation - actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))/RFdiv/2); - + actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))/2/(vco_freq/target_freq)); UHD_LOGV(often) << boost::format("WBX Intermediates: ref=%0.2f, outdiv=%f, fbdiv=%f") % (ref_freq*(1+int(D))/(R*(1+int(T)))) % double(RFdiv*2) % double(N + double(FRAC)/double(MOD)) << std::endl - << boost::format("WBX tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d, LD=%s" - ) % R % BS % N % FRAC % MOD % T % D % RFdiv % self_base->get_locked(unit).to_pp_string() << std::endl + << boost::format("WBX tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d" + ) % R % BS % N % FRAC % MOD % T % D % RFdiv << std::endl << boost::format("WBX Frequencies (MHz): REQ=%0.2f, ACT=%0.2f, VCO=%0.2f, PFD=%0.2f, BAND=%0.2f" ) % (target_freq/1e6) % (actual_freq/1e6) % (vco_freq/1e6) % (pfd_freq/1e6) % (pfd_freq/BS/1e6) << std::endl; @@ -302,6 +305,12 @@ double wbx_base::wbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar regs.frac_12_bit = FRAC; regs.int_16_bit = N; regs.mod_12_bit = MOD; + if (do_sync) + { + regs.clock_divider_12_bit = std::max(1, int(std::ceil(400e-6*pfd_freq/MOD))); + regs.feedback_select = adf4350_regs_t::FEEDBACK_SELECT_DIVIDED; + regs.clock_div_mode = adf4350_regs_t::CLOCK_DIV_MODE_RESYNC_ENABLE; + } regs.prescaler = prescaler; regs.r_counter_10_bit = R; regs.reference_divide_by_2 = T; @@ -339,6 +348,11 @@ double wbx_base::wbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar } + //reset the N and R counter + regs.counter_reset = adf4350_regs_t::COUNTER_RESET_ENABLED; + self_base->get_iface()->write_spi(unit, spi_config_t::EDGE_RISE, regs.get_reg(2), 32); + regs.counter_reset = adf4350_regs_t::COUNTER_RESET_DISABLED; + //write the registers //correct power-up sequence to write registers (5, 4, 3, 2, 1, 0) int addr; diff --git a/host/lib/usrp/dboard/db_wbx_version4.cpp b/host/lib/usrp/dboard/db_wbx_version4.cpp index 2fc3416ee..17b910de4 100644 --- a/host/lib/usrp/dboard/db_wbx_version4.cpp +++ b/host/lib/usrp/dboard/db_wbx_version4.cpp @@ -1,5 +1,5 @@ // -// Copyright 2011 Ettus Research LLC +// Copyright 2011-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 @@ -85,7 +85,7 @@ wbx_base::wbx_version4::wbx_version4(wbx_base *_self_wbx_base) { //////////////////////////////////////////////////////////////////// // Register RX properties //////////////////////////////////////////////////////////////////// - this->get_rx_subtree()->create<std::string>("name").set("WBX RX v4"); + this->get_rx_subtree()->create<std::string>("name").set("WBXv4 RX"); this->get_rx_subtree()->create<double>("freq/value") .coerce(boost::bind(&wbx_base::wbx_version4::set_lo_freq, this, dboard_iface::UNIT_RX, _1)) .set((wbx_v4_freq_range.start() + wbx_v4_freq_range.stop())/2.0); @@ -94,7 +94,7 @@ wbx_base::wbx_version4::wbx_version4(wbx_base *_self_wbx_base) { //////////////////////////////////////////////////////////////////// // Register TX properties //////////////////////////////////////////////////////////////////// - this->get_tx_subtree()->create<std::string>("name").set("WBX TX v4"); + this->get_tx_subtree()->create<std::string>("name").set("WBXv4 TX"); BOOST_FOREACH(const std::string &name, wbx_v4_tx_gain_ranges.keys()){ self_base->get_tx_subtree()->create<double>("gains/"+name+"/value") .coerce(boost::bind(&wbx_base::wbx_version4::set_tx_gain, this, _1, name)) @@ -200,6 +200,9 @@ double wbx_base::wbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar "WBX tune: target frequency %f Mhz" ) % (target_freq/1e6) << std::endl; + //start with target_freq*2 because mixer has divide by 2 + target_freq *= 2; + //map prescaler setting to mininmum integer divider (N) values (pg.18 prescaler) static const uhd::dict<int, int> prescaler_to_min_int_div = map_list_of (0,23) //adf4351_regs_t::PRESCALER_4_5 @@ -229,12 +232,13 @@ double wbx_base::wbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar if(ref_freq <= 12.5e6) D = adf4351_regs_t::REFERENCE_DOUBLER_ENABLED; //increase RF divider until acceptable VCO frequency - //start with target_freq*2 because mixer has divide by 2 - double vco_freq = target_freq*2; + const bool do_sync = (target_freq/2 > ref_freq); + double vco_freq = target_freq; while (vco_freq < 2.2e9) { vco_freq *= 2; RFdiv *= 2; } + if (do_sync) vco_freq = target_freq; //use 8/9 prescaler for vco_freq > 3 GHz (pg.18 prescaler) adf4351_regs_t::prescaler_t prescaler = vco_freq > 3e9 ? adf4351_regs_t::PRESCALER_8_9 : adf4351_regs_t::PRESCALER_4_5; @@ -289,14 +293,13 @@ double wbx_base::wbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar } //actual frequency calculation - actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))/RFdiv/2); - + actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))/2/(vco_freq/target_freq)); UHD_LOGV(often) << boost::format("WBX Intermediates: ref=%0.2f, outdiv=%f, fbdiv=%f") % (ref_freq*(1+int(D))/(R*(1+int(T)))) % double(RFdiv*2) % double(N + double(FRAC)/double(MOD)) << std::endl - << boost::format("WBX tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d, LD=%s" - ) % R % BS % N % FRAC % MOD % T % D % RFdiv % self_base->get_locked(unit).to_pp_string() << std::endl + << boost::format("WBX tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d" + ) % R % BS % N % FRAC % MOD % T % D % RFdiv << std::endl << boost::format("WBX Frequencies (MHz): REQ=%0.2f, ACT=%0.2f, VCO=%0.2f, PFD=%0.2f, BAND=%0.2f" ) % (target_freq/1e6) % (actual_freq/1e6) % (vco_freq/1e6) % (pfd_freq/1e6) % (pfd_freq/BS/1e6) << std::endl; @@ -306,6 +309,12 @@ double wbx_base::wbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar regs.frac_12_bit = FRAC; regs.int_16_bit = N; regs.mod_12_bit = MOD; + if (do_sync) + { + regs.clock_divider_12_bit = std::max(1, int(std::ceil(400e-6*pfd_freq/MOD))); + regs.feedback_select = adf4351_regs_t::FEEDBACK_SELECT_DIVIDED; + regs.clock_div_mode = adf4351_regs_t::CLOCK_DIV_MODE_RESYNC_ENABLE; + } regs.prescaler = prescaler; regs.r_counter_10_bit = R; regs.reference_divide_by_2 = T; @@ -343,6 +352,11 @@ double wbx_base::wbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar } + //reset the N and R counter + regs.counter_reset = adf4351_regs_t::COUNTER_RESET_ENABLED; + self_base->get_iface()->write_spi(unit, spi_config_t::EDGE_RISE, regs.get_reg(2), 32); + regs.counter_reset = adf4351_regs_t::COUNTER_RESET_DISABLED; + //write the registers //correct power-up sequence to write registers (5, 4, 3, 2, 1, 0) int addr; diff --git a/host/lib/usrp/dboard/db_xcvr2450.cpp b/host/lib/usrp/dboard/db_xcvr2450.cpp index 439e1b35e..348195a6e 100644 --- a/host/lib/usrp/dboard/db_xcvr2450.cpp +++ b/host/lib/usrp/dboard/db_xcvr2450.cpp @@ -228,7 +228,7 @@ xcvr2450::xcvr2450(ctor_args_t args) : xcvr_dboard_base(args){ // Register RX properties //////////////////////////////////////////////////////////////////// this->get_rx_subtree()->create<std::string>("name") - .set(get_rx_id().to_pp_string()); + .set("XCVR2450 RX"); this->get_rx_subtree()->create<sensor_value_t>("sensors/lo_locked") .publish(boost::bind(&xcvr2450::get_locked, this)); this->get_rx_subtree()->create<sensor_value_t>("sensors/rssi") @@ -266,7 +266,7 @@ xcvr2450::xcvr2450(ctor_args_t args) : xcvr_dboard_base(args){ // Register TX properties //////////////////////////////////////////////////////////////////// this->get_tx_subtree()->create<std::string>("name") - .set(get_tx_id().to_pp_string()); + .set("XCVR2450 TX"); this->get_tx_subtree()->create<sensor_value_t>("sensors/lo_locked") .publish(boost::bind(&xcvr2450::get_locked, this)); BOOST_FOREACH(const std::string &name, xcvr_tx_gain_ranges.keys()){ diff --git a/host/lib/usrp/e100/dboard_iface.cpp b/host/lib/usrp/e100/dboard_iface.cpp index 6afc7bc48..532b2dc9e 100644 --- a/host/lib/usrp/e100/dboard_iface.cpp +++ b/host/lib/usrp/e100/dboard_iface.cpp @@ -45,7 +45,7 @@ public: _spi_iface = spi_iface; _clock = clock; _codec = codec; - _gpio = gpio_core_200::make(_wb_iface, E100_REG_SR_ADDR(UE_SR_GPIO), E100_REG_RB_GPIO); + _gpio = gpio_core_200::make(_wb_iface, TOREG(SR_GPIO), REG_RB_GPIO); //init the clock rate shadows this->set_clock_rate(UNIT_RX, _clock->get_fpga_clock_rate()); diff --git a/host/lib/usrp/e100/e100_ctrl.cpp b/host/lib/usrp/e100/e100_ctrl.cpp index eb529c9c1..5a9b93633 100644 --- a/host/lib/usrp/e100/e100_ctrl.cpp +++ b/host/lib/usrp/e100/e100_ctrl.cpp @@ -1,5 +1,5 @@ // -// Copyright 2011 Ettus Research LLC +// Copyright 2011-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 @@ -23,13 +23,16 @@ #include <sys/ioctl.h> //ioctl #include <fcntl.h> //open, close #include <linux/usrp_e.h> //ioctl structures and constants +#include <poll.h> //poll #include <boost/thread/thread.hpp> //sleep #include <boost/thread/mutex.hpp> +#include <boost/thread/condition_variable.hpp> #include <boost/foreach.hpp> #include <boost/format.hpp> #include <fstream> using namespace uhd; +using namespace uhd::transport; /*********************************************************************** * Sysfs GPIO wrapper class @@ -71,6 +74,11 @@ private: }; /*********************************************************************** + * Protection for dual GPIO access - sometimes MISO, sometimes have resp + **********************************************************************/ +static boost::mutex gpio_irq_resp_mutex; + +/*********************************************************************** * Aux spi implementation **********************************************************************/ class aux_spi_iface_impl : public spi_iface{ @@ -87,6 +95,8 @@ public: size_t num_bits, bool readback ){ + boost::mutex::scoped_lock lock(gpio_irq_resp_mutex); + boost::uint32_t rb_bits = 0; this->spi_sen_gpio(0); @@ -244,6 +254,57 @@ uhd::uart_iface::sptr e100_ctrl::make_gps_uart_iface(const std::string &node){ } /*********************************************************************** + * Simple managed buffers + **********************************************************************/ +struct e100_simpl_mrb : managed_recv_buffer +{ + usrp_e_ctl32 data; + e100_ctrl *ctrl; + + void release(void) + { + //NOP + } + + sptr get_new(void) + { + const size_t max_words32 = 8; //.LAST_ADDR(10'h00f)) resp_fifo_to_gpmc + + //load the data struct + data.offset = 0; + data.count = max_words32; + + //call the ioctl + ctrl->ioctl(USRP_E_READ_CTL32, &data); + + if (data.buf[0] == 0 or ~data.buf[0] == 0) return sptr(); //bad VRT hdr, treat like timeout + + return make(this, data.buf, sizeof(data.buf)); + } +}; + +struct e100_simpl_msb : managed_send_buffer +{ + usrp_e_ctl32 data; + e100_ctrl *ctrl; + + void release(void) + { + //load the data struct + data.offset = 0; + data.count = size()/4+1/*1 for header offset*/; + + //call the ioctl + ctrl->ioctl(USRP_E_WRITE_CTL32, &data); + } + + sptr get_new(void) + { + return make(this, data.buf+1, sizeof(data.buf)-4); + } +}; + +/*********************************************************************** * USRP-E100 control implementation **********************************************************************/ class e100_ctrl_impl : public e100_ctrl{ @@ -273,11 +334,24 @@ public: ) % USRP_E_COMPAT_NUMBER % module_compat_num)); } - //perform a global reset after opening - this->poke32(E100_REG_GLOBAL_RESET, 0); + //hit the magic arst condition + //async_reset <= ~EM_NCS6 && ~EM_NWE && (EM_A[9:2] == 8'hff) && EM_D[0]; + usrp_e_ctl16 datax; + datax.offset = 0x3fc; + datax.count = 2; + datax.buf[0] = 1; + datax.buf[1] = 0; + this->ioctl(USRP_E_WRITE_CTL16, &datax); + + std::ofstream edge_file("/sys/class/gpio/gpio147/edge"); + edge_file << "rising" << std::endl << std::flush; + edge_file.close(); + _irq_fd = ::open("/sys/class/gpio/gpio147/value", O_RDONLY); + if (_irq_fd < 0) UHD_MSG(error) << "Unable to open GPIO for IRQ\n"; } ~e100_ctrl_impl(void){ + ::close(_irq_fd); ::close(_node_fd); } @@ -293,58 +367,76 @@ public: )); } } + /******************************************************************* - * Peek and Poke + * The managed buffer interface ******************************************************************/ - void poke32(wb_addr_type addr, boost::uint32_t value){ - //load the data struct - usrp_e_ctl32 data; - data.offset = addr; - data.count = 1; - data.buf[0] = value; - - //call the ioctl - this->ioctl(USRP_E_WRITE_CTL32, &data); + UHD_INLINE bool resp_read(void) + { + //thread stuff ensures that this GPIO isnt shared + boost::mutex::scoped_lock lock(gpio_irq_resp_mutex); + + //perform a read of the GPIO IRQ state + char ch; + ::read(_irq_fd, &ch, sizeof(ch)); + ::lseek(_irq_fd, SEEK_SET, 0); + return ch == '1'; } - void poke16(wb_addr_type addr, boost::uint16_t value){ - //load the data struct - usrp_e_ctl16 data; - data.offset = addr; - data.count = 1; - data.buf[0] = value; + UHD_INLINE bool resp_wait(const double timeout) + { + //perform a check, if it fails, poll + if (this->resp_read()) return true; - //call the ioctl - this->ioctl(USRP_E_WRITE_CTL16, &data); + //poll IRQ GPIO for some action + pollfd pfd; + pfd.fd = _irq_fd; + pfd.events = POLLPRI | POLLERR; + ::poll(&pfd, 1, long(timeout*1000)/*ms*/); + + //perform a GPIO read again for result + return this->resp_read(); } - boost::uint32_t peek32(wb_addr_type addr){ - //load the data struct - usrp_e_ctl32 data; - data.offset = addr; - data.count = 1; + managed_recv_buffer::sptr get_recv_buff(double timeout) + { + if (not this->resp_wait(timeout)) + { + return managed_recv_buffer::sptr(); + } - //call the ioctl - this->ioctl(USRP_E_READ_CTL32, &data); + _mrb.ctrl = this; + return _mrb.get_new(); + } - return data.buf[0]; + managed_send_buffer::sptr get_send_buff(double) + { + _msb.ctrl = this; + return _msb.get_new(); } - boost::uint16_t peek16(wb_addr_type addr){ - //load the data struct - usrp_e_ctl16 data; - data.offset = addr; - data.count = 1; + size_t get_num_recv_frames(void) const{ + return 1; + } - //call the ioctl - this->ioctl(USRP_E_READ_CTL16, &data); + size_t get_recv_frame_size(void) const{ + return sizeof(_mrb.data.buf); + } + + size_t get_num_send_frames(void) const{ + return 1; + } - return data.buf[0]; + size_t get_send_frame_size(void) const{ + return sizeof(_msb.data.buf); } private: int _node_fd; + int _irq_fd; boost::mutex _ioctl_mutex; + e100_simpl_mrb _mrb; + e100_simpl_msb _msb; }; /*********************************************************************** diff --git a/host/lib/usrp/e100/e100_ctrl.hpp b/host/lib/usrp/e100/e100_ctrl.hpp index fd66791d4..72dce134e 100644 --- a/host/lib/usrp/e100/e100_ctrl.hpp +++ b/host/lib/usrp/e100/e100_ctrl.hpp @@ -1,5 +1,5 @@ // -// Copyright 2011 Ettus Research LLC +// 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 @@ -18,12 +18,11 @@ #ifndef INCLUDED_B100_CTRL_HPP #define INCLUDED_B100_CTRL_HPP -#include "wb_iface.hpp" +#include <uhd/transport/zero_copy.hpp> #include <uhd/types/serial.hpp> #include <boost/shared_ptr.hpp> -#include <boost/utility.hpp> -class e100_ctrl : boost::noncopyable, public wb_iface{ +class e100_ctrl : public uhd::transport::zero_copy_if{ public: typedef boost::shared_ptr<e100_ctrl> sptr; @@ -33,7 +32,7 @@ public: //! Make an i2c iface for the i2c device node static uhd::i2c_iface::sptr make_dev_i2c_iface(const std::string &node); - //! Make an i2c iface for the i2c device node + //! Make a spi iface for the spi gpio static uhd::spi_iface::sptr make_aux_spi_iface(void); //! Make a uart iface for the uart device node diff --git a/host/lib/usrp/e100/e100_impl.cpp b/host/lib/usrp/e100/e100_impl.cpp index d610c0b12..a0fa6c47e 100644 --- a/host/lib/usrp/e100/e100_impl.cpp +++ b/host/lib/usrp/e100/e100_impl.cpp @@ -28,6 +28,7 @@ #include <boost/functional/hash.hpp> #include <boost/assign/list_of.hpp> #include <fstream> +#include <ctime> using namespace uhd; using namespace uhd::usrp; @@ -64,7 +65,7 @@ static device_addrs_t e100_find(const device_addr_t &hint){ new_addr["node"] = fs::system_complete(fs::path(hint["node"])).string(); try{ i2c_iface::sptr i2c_iface = e100_ctrl::make_dev_i2c_iface(E100_I2C_DEV_NODE); - const mboard_eeprom_t mb_eeprom(*i2c_iface, mboard_eeprom_t::MAP_E100); + const mboard_eeprom_t mb_eeprom(*i2c_iface, E100_EEPROM_MAP_KEY); new_addr["name"] = mb_eeprom["name"]; new_addr["serial"] = mb_eeprom["serial"]; } @@ -86,15 +87,6 @@ static device_addrs_t e100_find(const device_addr_t &hint){ /*********************************************************************** * Make **********************************************************************/ -static size_t hash_fpga_file(const std::string &file_path){ - size_t hash = 0; - std::ifstream file(file_path.c_str()); - if (not file.good()) throw uhd::io_error("cannot open fpga file for read: " + file_path); - while (file.good()) boost::hash_combine(hash, file.get()); - file.close(); - return hash; -} - static device::sptr e100_make(const device_addr_t &device_addr){ return device::sptr(new e100_impl(device_addr)); } @@ -114,13 +106,9 @@ static const uhd::dict<std::string, std::string> model_to_fpga_file_name = boost e100_impl::e100_impl(const uhd::device_addr_t &device_addr){ _tree = property_tree::make(); - //setup the main interface into fpga - const std::string node = device_addr["node"]; - _fpga_ctrl = e100_ctrl::make(node); - //read the eeprom so we can determine the hardware _dev_i2c_iface = e100_ctrl::make_dev_i2c_iface(E100_I2C_DEV_NODE); - const mboard_eeprom_t mb_eeprom(*_dev_i2c_iface, mboard_eeprom_t::MAP_E100); + const mboard_eeprom_t mb_eeprom(*_dev_i2c_iface, E100_EEPROM_MAP_KEY); //determine the model string for this device const std::string model = device_addr.get("model", mb_eeprom.get("model", "")); @@ -133,20 +121,19 @@ e100_impl::e100_impl(const uhd::device_addr_t &device_addr){ //extract the fpga path and compute hash const std::string default_fpga_file_name = model_to_fpga_file_name[model]; - const std::string e100_fpga_image = find_image_path(device_addr.get("fpga", default_fpga_file_name)); - const boost::uint32_t file_hash = boost::uint32_t(hash_fpga_file(e100_fpga_image)); - - //When the hash does not match: - // - close the device node - // - load the fpga bin file - // - re-open the device node - if (_fpga_ctrl->peek32(E100_REG_RB_MISC_TEST32) != file_hash){ - _fpga_ctrl.reset(); - e100_load_fpga(e100_fpga_image); - _fpga_ctrl = e100_ctrl::make(node); + std::string e100_fpga_image; + try{ + e100_fpga_image = find_image_path(device_addr.get("fpga", default_fpga_file_name)); } + catch(...){ + UHD_MSG(error) << boost::format("Could not find FPGA image. %s\n") % print_images_error(); + throw; + } + e100_load_fpga(e100_fpga_image); - //setup clock control here to ensure that the FPGA has a good clock before we continue + //////////////////////////////////////////////////////////////////// + // Setup the FPGA clock over AUX SPI + //////////////////////////////////////////////////////////////////// bool dboard_clocks_diff = true; if (mb_eeprom.get("revision", "0") == "3") dboard_clocks_diff = false; else if (mb_eeprom.get("revision", "0") == "4") dboard_clocks_diff = true; @@ -158,12 +145,32 @@ e100_impl::e100_impl(const uhd::device_addr_t &device_addr){ _aux_spi_iface = e100_ctrl::make_aux_spi_iface(); _clock_ctrl = e100_clock_ctrl::make(_aux_spi_iface, master_clock_rate, dboard_clocks_diff); + //////////////////////////////////////////////////////////////////// + // setup the main interface into fpga + // - do this after aux spi, because we share gpio147 + //////////////////////////////////////////////////////////////////// + const std::string node = device_addr["node"]; + _fpga_ctrl = e100_ctrl::make(node); + + //////////////////////////////////////////////////////////////////// + // Initialize FPGA control communication + //////////////////////////////////////////////////////////////////// + fifo_ctrl_excelsior_config fifo_ctrl_config; + fifo_ctrl_config.async_sid_base = E100_TX_ASYNC_SID; + fifo_ctrl_config.num_async_chan = 1; + fifo_ctrl_config.ctrl_sid_base = E100_CTRL_MSG_SID; + fifo_ctrl_config.spi_base = TOREG(SR_SPI); + fifo_ctrl_config.spi_rb = REG_RB_SPI; + _fifo_ctrl = fifo_ctrl_excelsior::make(_fpga_ctrl, fifo_ctrl_config); + //Perform wishbone readback tests, these tests also write the hash bool test_fail = false; - UHD_MSG(status) << "Performing wishbone readback test... " << std::flush; + UHD_MSG(status) << "Performing control readback test... " << std::flush; + size_t hash = time(NULL); for (size_t i = 0; i < 100; i++){ - _fpga_ctrl->poke32(E100_REG_SR_MISC_TEST32, file_hash); - test_fail = _fpga_ctrl->peek32(E100_REG_RB_MISC_TEST32) != file_hash; + boost::hash_combine(hash, i); + _fifo_ctrl->poke32(TOREG(SR_MISC+0), boost::uint32_t(hash)); + test_fail = _fifo_ctrl->peek32(REG_RB_CONFIG0) != boost::uint32_t(hash); if (test_fail) break; //exit loop on any failure } UHD_MSG(status) << ((test_fail)? " fail" : "pass") << std::endl; @@ -180,8 +187,7 @@ e100_impl::e100_impl(const uhd::device_addr_t &device_addr){ //////////////////////////////////////////////////////////////////// // Create controller objects //////////////////////////////////////////////////////////////////// - _fpga_i2c_ctrl = i2c_core_100::make(_fpga_ctrl, E100_REG_SLAVE(3)); - _fpga_spi_ctrl = spi_core_100::make(_fpga_ctrl, E100_REG_SLAVE(2)); + _fpga_i2c_ctrl = i2c_core_200::make(_fifo_ctrl, TOREG(SR_I2C), REG_RB_I2C); _data_transport = e100_make_mmap_zero_copy(_fpga_ctrl); //////////////////////////////////////////////////////////////////// @@ -189,7 +195,8 @@ e100_impl::e100_impl(const uhd::device_addr_t &device_addr){ //////////////////////////////////////////////////////////////////// _tree->create<std::string>("/name").set("E-Series Device"); const fs_path mb_path = "/mboards/0"; - _tree->create<std::string>(mb_path / "name").set(str(boost::format("%s (euewanee)") % model)); + _tree->create<std::string>(mb_path / "name").set(model); + _tree->create<std::string>(mb_path / "codename").set("Euwanee"); //////////////////////////////////////////////////////////////////// // setup the mboard eeprom @@ -204,12 +211,17 @@ e100_impl::e100_impl(const uhd::device_addr_t &device_addr){ //^^^ clock created up top, just reg props here... ^^^ _tree->create<double>(mb_path / "tick_rate") .publish(boost::bind(&e100_clock_ctrl::get_fpga_clock_rate, _clock_ctrl)) + .subscribe(boost::bind(&fifo_ctrl_excelsior::set_tick_rate, _fifo_ctrl, _1)) .subscribe(boost::bind(&e100_impl::update_tick_rate, this, _1)); + //subscribe the command time while we are at it + _tree->create<time_spec_t>(mb_path / "time/cmd") + .subscribe(boost::bind(&fifo_ctrl_excelsior::set_time, _fifo_ctrl, _1)); + //////////////////////////////////////////////////////////////////// // create codec control objects //////////////////////////////////////////////////////////////////// - _codec_ctrl = e100_codec_ctrl::make(_fpga_spi_ctrl); + _codec_ctrl = e100_codec_ctrl::make(_fifo_ctrl/*spi*/); const fs_path rx_codec_path = mb_path / "rx_codecs/A"; const fs_path tx_codec_path = mb_path / "tx_codecs/A"; _tree->create<std::string>(rx_codec_path / "name").set("ad9522"); @@ -231,24 +243,37 @@ e100_impl::e100_impl(const uhd::device_addr_t &device_addr){ //////////////////////////////////////////////////////////////////// // Create the GPSDO control //////////////////////////////////////////////////////////////////// - try{ - _gps = gps_ctrl::make(e100_ctrl::make_gps_uart_iface(E100_UART_DEV_NODE)); - } - catch(std::exception &e){ - UHD_MSG(error) << "An error occurred making GPSDO control: " << e.what() << std::endl; - } - if (_gps.get() != NULL and _gps->gps_detected()){ - BOOST_FOREACH(const std::string &name, _gps->get_sensors()){ - _tree->create<sensor_value_t>(mb_path / "sensors" / name) - .publish(boost::bind(&gps_ctrl::get_sensor, _gps, name)); + static const fs::path GPSDO_VOLATILE_PATH("/media/ram/e100_internal_gpsdo.cache"); + if (not fs::exists(GPSDO_VOLATILE_PATH)) + { + UHD_MSG(status) << "Detecting internal GPSDO.... " << std::flush; + try{ + _gps = gps_ctrl::make(e100_ctrl::make_gps_uart_iface(E100_UART_DEV_NODE)); + } + catch(std::exception &e){ + UHD_MSG(error) << "An error occurred making GPSDO control: " << e.what() << std::endl; + } + if (_gps and _gps->gps_detected()) + { + UHD_MSG(status) << "found" << std::endl; + BOOST_FOREACH(const std::string &name, _gps->get_sensors()) + { + _tree->create<sensor_value_t>(mb_path / "sensors" / name) + .publish(boost::bind(&gps_ctrl::get_sensor, _gps, name)); + } + } + else + { + UHD_MSG(status) << "not found" << std::endl; + std::ofstream(GPSDO_VOLATILE_PATH.string().c_str(), std::ofstream::binary) << "42" << std::endl; } } //////////////////////////////////////////////////////////////////// // create frontend control objects //////////////////////////////////////////////////////////////////// - _rx_fe = rx_frontend_core_200::make(_fpga_ctrl, E100_REG_SR_ADDR(UE_SR_RX_FRONT)); - _tx_fe = tx_frontend_core_200::make(_fpga_ctrl, E100_REG_SR_ADDR(UE_SR_TX_FRONT)); + _rx_fe = rx_frontend_core_200::make(_fifo_ctrl, TOREG(SR_RX_FE)); + _tx_fe = tx_frontend_core_200::make(_fifo_ctrl, TOREG(SR_TX_FE)); _tree->create<subdev_spec_t>(mb_path / "rx_subdev_spec") .subscribe(boost::bind(&e100_impl::update_rx_subdev_spec, this, _1)); @@ -277,13 +302,17 @@ e100_impl::e100_impl(const uhd::device_addr_t &device_addr){ //////////////////////////////////////////////////////////////////// // create rx dsp control objects //////////////////////////////////////////////////////////////////// - _rx_dsps.push_back(rx_dsp_core_200::make( - _fpga_ctrl, E100_REG_SR_ADDR(UE_SR_RX_DSP0), E100_REG_SR_ADDR(UE_SR_RX_CTRL0), E100_RX_SID_BASE + 0 - )); - _rx_dsps.push_back(rx_dsp_core_200::make( - _fpga_ctrl, E100_REG_SR_ADDR(UE_SR_RX_DSP1), E100_REG_SR_ADDR(UE_SR_RX_CTRL1), E100_RX_SID_BASE + 1 - )); - for (size_t dspno = 0; dspno < _rx_dsps.size(); dspno++){ + const size_t num_rx_dsps = _fifo_ctrl->peek32(REG_RB_NUM_RX_DSP); + for (size_t dspno = 0; dspno < num_rx_dsps; dspno++) + { + const size_t sr_off = dspno*32; + _rx_dsps.push_back(rx_dsp_core_200::make( + _fifo_ctrl, + TOREG(SR_RX_DSP0+sr_off), + TOREG(SR_RX_CTRL0+sr_off), + E100_RX_SID_BASE + dspno + )); + _rx_dsps[dspno]->set_link_rate(E100_RX_LINK_RATE_BPS); _tree->access<double>(mb_path / "tick_rate") .subscribe(boost::bind(&rx_dsp_core_200::set_tick_rate, _rx_dsps[dspno], _1)); @@ -306,7 +335,7 @@ e100_impl::e100_impl(const uhd::device_addr_t &device_addr){ // create tx dsp control objects //////////////////////////////////////////////////////////////////// _tx_dsp = tx_dsp_core_200::make( - _fpga_ctrl, E100_REG_SR_ADDR(UE_SR_TX_DSP), E100_REG_SR_ADDR(UE_SR_TX_CTRL), E100_TX_ASYNC_SID + _fifo_ctrl, TOREG(SR_TX_DSP), TOREG(SR_TX_CTRL), E100_TX_ASYNC_SID ); _tx_dsp->set_link_rate(E100_TX_LINK_RATE_BPS); _tree->access<double>(mb_path / "tick_rate") @@ -326,12 +355,12 @@ e100_impl::e100_impl(const uhd::device_addr_t &device_addr){ // create time control objects //////////////////////////////////////////////////////////////////// time64_core_200::readback_bases_type time64_rb_bases; - time64_rb_bases.rb_hi_now = E100_REG_RB_TIME_NOW_HI; - time64_rb_bases.rb_lo_now = E100_REG_RB_TIME_NOW_LO; - time64_rb_bases.rb_hi_pps = E100_REG_RB_TIME_PPS_HI; - time64_rb_bases.rb_lo_pps = E100_REG_RB_TIME_PPS_LO; + time64_rb_bases.rb_hi_now = REG_RB_TIME_NOW_HI; + time64_rb_bases.rb_lo_now = REG_RB_TIME_NOW_LO; + time64_rb_bases.rb_hi_pps = REG_RB_TIME_PPS_HI; + time64_rb_bases.rb_lo_pps = REG_RB_TIME_PPS_LO; _time64 = time64_core_200::make( - _fpga_ctrl, E100_REG_SR_ADDR(UE_SR_TIME64), time64_rb_bases + _fifo_ctrl, TOREG(SR_TIME64), time64_rb_bases ); _tree->access<double>(mb_path / "tick_rate") .subscribe(boost::bind(&time64_core_200::set_tick_rate, _time64, _1)); @@ -349,13 +378,14 @@ e100_impl::e100_impl(const uhd::device_addr_t &device_addr){ //setup reference source props _tree->create<std::string>(mb_path / "clock_source/value") .subscribe(boost::bind(&e100_impl::update_clock_source, this, _1)); - static const std::vector<std::string> clock_sources = boost::assign::list_of("internal")("external")("auto"); + std::vector<std::string> clock_sources = boost::assign::list_of("internal")("external")("auto"); + if (_gps and _gps->gps_detected()) clock_sources.push_back("gpsdo"); _tree->create<std::vector<std::string> >(mb_path / "clock_source/options").set(clock_sources); //////////////////////////////////////////////////////////////////// // create user-defined control objects //////////////////////////////////////////////////////////////////// - _user = user_settings_core_200::make(_fpga_ctrl, E100_REG_SR_ADDR(UE_SR_USER_REGS)); + _user = user_settings_core_200::make(_fifo_ctrl, TOREG(SR_USER_REGS)); _tree->create<user_settings_core_200::user_reg_t>(mb_path / "user/regs") .subscribe(boost::bind(&user_settings_core_200::set_reg, _user, _1)); @@ -369,6 +399,9 @@ e100_impl::e100_impl(const uhd::device_addr_t &device_addr){ tx_db_eeprom.load(*_fpga_i2c_ctrl, I2C_ADDR_TX_DB); gdb_eeprom.load(*_fpga_i2c_ctrl, I2C_ADDR_TX_DB ^ 5); + //disable rx dc offset if LFRX + if (rx_db_eeprom.id == 0x000f) _tree->access<bool>(rx_fe_path / "dc_offset" / "enable").set(false); + //create the properties and register subscribers _tree->create<dboard_eeprom_t>(mb_path / "dboards/A/rx_eeprom") .set(rx_db_eeprom) @@ -381,7 +414,7 @@ e100_impl::e100_impl(const uhd::device_addr_t &device_addr){ .subscribe(boost::bind(&e100_impl::set_db_eeprom, this, "gdb", _1)); //create a new dboard interface and manager - _dboard_iface = make_e100_dboard_iface(_fpga_ctrl, _fpga_i2c_ctrl, _fpga_spi_ctrl, _clock_ctrl, _codec_ctrl); + _dboard_iface = make_e100_dboard_iface(_fifo_ctrl, _fpga_i2c_ctrl, _fifo_ctrl/*spi*/, _clock_ctrl, _codec_ctrl); _tree->create<dboard_iface::sptr>(mb_path / "dboards/A/iface").set(_dboard_iface); _dboard_manager = dboard_manager::make( rx_db_eeprom.id, tx_db_eeprom.id, gdb_eeprom.id, @@ -401,7 +434,11 @@ e100_impl::e100_impl(const uhd::device_addr_t &device_addr){ } //initialize io handling - this->io_init(); + _recv_demuxer = recv_packet_demuxer::make(_data_transport, _rx_dsps.size(), E100_RX_SID_BASE); + + //allocate streamer weak ptrs containers + _rx_streamers.resize(_rx_dsps.size()); + _tx_streamers.resize(1/*known to be 1 dsp*/); //////////////////////////////////////////////////////////////////// // do some post-init tasks @@ -425,10 +462,11 @@ e100_impl::e100_impl(const uhd::device_addr_t &device_addr){ _tree->access<std::string>(mb_path / "time_source/value").set("none"); //GPS installed: use external ref, time, and init time spec - if (_gps.get() != NULL and _gps->gps_detected()){ + if (_gps and _gps->gps_detected()){ + _time64->enable_gpsdo(); UHD_MSG(status) << "Setting references to the internal GPSDO" << std::endl; - _tree->access<std::string>(mb_path / "time_source/value").set("external"); - _tree->access<std::string>(mb_path / "clock_source/value").set("external"); + _tree->access<std::string>(mb_path / "time_source/value").set("gpsdo"); + _tree->access<std::string>(mb_path / "clock_source/value").set("gpsdo"); UHD_MSG(status) << "Initializing time to the internal GPSDO" << std::endl; _time64->set_time_next_pps(time_spec_t(time_t(_gps->get_sensor("gps_time").to_int()+1))); } @@ -448,7 +486,7 @@ double e100_impl::update_rx_codec_gain(const double gain){ } void e100_impl::set_mb_eeprom(const uhd::usrp::mboard_eeprom_t &mb_eeprom){ - mb_eeprom.commit(*_dev_i2c_iface, mboard_eeprom_t::MAP_E100); + mb_eeprom.commit(*_dev_i2c_iface, E100_EEPROM_MAP_KEY); } void e100_impl::set_db_eeprom(const std::string &type, const uhd::usrp::dboard_eeprom_t &db_eeprom){ @@ -458,9 +496,23 @@ void e100_impl::set_db_eeprom(const std::string &type, const uhd::usrp::dboard_e } void e100_impl::update_clock_source(const std::string &source){ + + if (source == "pps_sync"){ + _clock_ctrl->use_external_ref(); + _fifo_ctrl->poke32(TOREG(SR_MISC+2), 1); + return; + } + if (source == "_pps_sync_"){ + _clock_ctrl->use_external_ref(); + _fifo_ctrl->poke32(TOREG(SR_MISC+2), 3); + return; + } + _fifo_ctrl->poke32(TOREG(SR_MISC+2), 0); + if (source == "auto") _clock_ctrl->use_auto_ref(); else if (source == "internal") _clock_ctrl->use_internal_ref(); else if (source == "external") _clock_ctrl->use_external_ref(); + else if (source == "gpsdo") _clock_ctrl->use_external_ref(); else throw uhd::runtime_error("unhandled clock configuration reference source: " + source); } @@ -470,7 +522,7 @@ sensor_value_t e100_impl::get_ref_locked(void){ } void e100_impl::check_fpga_compat(void){ - const boost::uint32_t fpga_compat_num = _fpga_ctrl->peek32(E100_REG_RB_COMPAT); + const boost::uint32_t fpga_compat_num = _fifo_ctrl->peek32(REG_RB_COMPAT); boost::uint16_t fpga_major = fpga_compat_num >> 16, fpga_minor = fpga_compat_num & 0xffff; if (fpga_major == 0){ //old version scheme fpga_major = fpga_minor; diff --git a/host/lib/usrp/e100/e100_impl.hpp b/host/lib/usrp/e100/e100_impl.hpp index 1d36cb2ac..6f64d4b80 100644 --- a/host/lib/usrp/e100/e100_impl.hpp +++ b/host/lib/usrp/e100/e100_impl.hpp @@ -18,17 +18,17 @@ #include "e100_ctrl.hpp" #include "clock_ctrl.hpp" #include "codec_ctrl.hpp" -#include "spi_core_100.hpp" -#include "i2c_core_100.hpp" +#include "i2c_core_200.hpp" #include "rx_frontend_core_200.hpp" #include "tx_frontend_core_200.hpp" #include "rx_dsp_core_200.hpp" #include "tx_dsp_core_200.hpp" #include "time64_core_200.hpp" +#include "fifo_ctrl_excelsior.hpp" #include "user_settings_core_200.hpp" +#include "recv_packet_demuxer.hpp" #include <uhd/device.hpp> #include <uhd/property_tree.hpp> -#include <uhd/utils/pimpl.hpp> #include <uhd/usrp/subdev_spec.hpp> #include <uhd/usrp/dboard_eeprom.hpp> #include <uhd/usrp/mboard_eeprom.hpp> @@ -49,10 +49,12 @@ static const double E100_RX_LINK_RATE_BPS = 166e6/3/2*2; static const double E100_TX_LINK_RATE_BPS = 166e6/3/1*2; static const std::string E100_I2C_DEV_NODE = "/dev/i2c-3"; static const std::string E100_UART_DEV_NODE = "/dev/ttyO0"; -static const boost::uint16_t E100_FPGA_COMPAT_NUM = 0x09; -static const boost::uint32_t E100_RX_SID_BASE = 2; -static const boost::uint32_t E100_TX_ASYNC_SID = 1; +static const boost::uint16_t E100_FPGA_COMPAT_NUM = 11; +static const boost::uint32_t E100_RX_SID_BASE = 30; +static const boost::uint32_t E100_TX_ASYNC_SID = 10; +static const boost::uint32_t E100_CTRL_MSG_SID = 20; static const double E100_DEFAULT_CLOCK_RATE = 64e6; +static const std::string E100_EEPROM_MAP_KEY = "E100"; //! load an fpga image from a bin file into the usrp-e fpga extern void e100_load_fpga(const std::string &bin_file); @@ -86,8 +88,8 @@ private: uhd::property_tree::sptr _tree; //controllers - spi_core_100::sptr _fpga_spi_ctrl; - i2c_core_100::sptr _fpga_i2c_ctrl; + fifo_ctrl_excelsior::sptr _fifo_ctrl; + i2c_core_200::sptr _fpga_i2c_ctrl; rx_frontend_core_200::sptr _rx_fe; tx_frontend_core_200::sptr _tx_fe; std::vector<rx_dsp_core_200::sptr> _rx_dsps; @@ -103,15 +105,12 @@ private: //transports uhd::transport::zero_copy_if::sptr _data_transport; + uhd::usrp::recv_packet_demuxer::sptr _recv_demuxer; //dboard stuff uhd::usrp::dboard_manager::sptr _dboard_manager; uhd::usrp::dboard_iface::sptr _dboard_iface; - //handle io stuff - UHD_PIMPL_DECL(io_impl) _io_impl; - void io_init(void); - //device properties interface uhd::property_tree::sptr get_tree(void) const{ return _tree; diff --git a/host/lib/usrp/e100/e100_mmap_zero_copy.cpp b/host/lib/usrp/e100/e100_mmap_zero_copy.cpp index cdb7094f4..58beeb424 100644 --- a/host/lib/usrp/e100/e100_mmap_zero_copy.cpp +++ b/host/lib/usrp/e100/e100_mmap_zero_copy.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-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 @@ -19,6 +19,7 @@ #include <uhd/transport/zero_copy.hpp> #include <uhd/utils/log.hpp> #include <uhd/exception.hpp> +#include <boost/make_shared.hpp> #include <linux/usrp_e.h> #include <sys/mman.h> //mmap #include <unistd.h> //getpagesize @@ -41,23 +42,19 @@ public: _mem(mem), _info(info) { /* NOP */ } void release(void){ - if (_info->flags != RB_USER_PROCESS) return; if (fp_verbose) UHD_LOGV(always) << "recv buff: release" << std::endl; _info->flags = RB_KERNEL; //release the frame } - bool ready(void){return _info->flags & RB_USER;} + UHD_INLINE bool ready(void){return _info->flags & RB_USER;} - sptr get_new(void){ - if (fp_verbose) UHD_LOGV(always) << " make_recv_buff: " << get_size() << std::endl; + UHD_INLINE sptr get_new(void){ + if (fp_verbose) UHD_LOGV(always) << " make_recv_buff: " << _info->len << std::endl; _info->flags = RB_USER_PROCESS; //claim the frame - return make_managed_buffer(this); + return make(this, _mem, _info->len); } private: - const void *get_buff(void) const{return _mem;} - size_t get_size(void) const{return _info->len;} - void *_mem; ring_buffer_info *_info; }; @@ -71,28 +68,24 @@ public: e100_mmap_zero_copy_msb(void *mem, ring_buffer_info *info, size_t len, int fd): _mem(mem), _info(info), _len(len), _fd(fd) { /* NOP */ } - void commit(size_t len){ - if (_info->flags != RB_USER_PROCESS) return; - if (fp_verbose) UHD_LOGV(always) << "send buff: commit " << len << std::endl; - _info->len = len; + void release(void){ + if (fp_verbose) UHD_LOGV(always) << "send buff: commit " << size() << std::endl; + _info->len = size(); _info->flags = RB_USER; //release the frame if (::write(_fd, NULL, 0) < 0){ //notifies the kernel UHD_LOGV(rarely) << UHD_THROW_SITE_INFO("write error") << std::endl; } } - bool ready(void){return _info->flags & RB_KERNEL;} + UHD_INLINE bool ready(void){return _info->flags & RB_KERNEL;} - sptr get_new(void){ - if (fp_verbose) UHD_LOGV(always) << " make_send_buff: " << get_size() << std::endl; + UHD_INLINE sptr get_new(void){ + if (fp_verbose) UHD_LOGV(always) << " make_send_buff: " << _len << std::endl; _info->flags = RB_USER_PROCESS; //claim the frame - return make_managed_buffer(this); + return make(this, _mem, _len); } private: - void *get_buff(void) const{return _mem;} - size_t get_size(void) const{return _len;} - void *_mem; ring_buffer_info *_info; size_t _len; @@ -162,14 +155,14 @@ public: //initialize the managed receive buffers for (size_t i = 0; i < get_num_recv_frames(); i++){ - _mrb_pool.push_back(e100_mmap_zero_copy_mrb( + _mrb_pool.push_back(boost::make_shared<e100_mmap_zero_copy_mrb>( recv_buff + get_recv_frame_size()*i, (*recv_info) + i )); } //initialize the managed send buffers for (size_t i = 0; i < get_num_recv_frames(); i++){ - _msb_pool.push_back(e100_mmap_zero_copy_msb( + _msb_pool.push_back(boost::make_shared<e100_mmap_zero_copy_msb>( send_buff + get_send_frame_size()*i, (*send_info) + i, get_send_frame_size(), _fd )); @@ -183,7 +176,7 @@ public: managed_recv_buffer::sptr get_recv_buff(double timeout){ if (fp_verbose) UHD_LOGV(always) << "get_recv_buff: " << _recv_index << std::endl; - e100_mmap_zero_copy_mrb &mrb = _mrb_pool[_recv_index]; + e100_mmap_zero_copy_mrb &mrb = *_mrb_pool[_recv_index]; //poll/wait for a ready frame if (not mrb.ready()){ @@ -215,7 +208,7 @@ public: managed_send_buffer::sptr get_send_buff(double timeout){ if (fp_verbose) UHD_LOGV(always) << "get_send_buff: " << _send_index << std::endl; - e100_mmap_zero_copy_msb &msb = _msb_pool[_send_index]; + e100_mmap_zero_copy_msb &msb = *_msb_pool[_send_index]; //poll/wait for a ready frame if (not msb.ready()){ @@ -254,8 +247,8 @@ private: size_t _frame_size, _map_size; //re-usable managed buffers - std::vector<e100_mmap_zero_copy_mrb> _mrb_pool; - std::vector<e100_mmap_zero_copy_msb> _msb_pool; + std::vector<boost::shared_ptr<e100_mmap_zero_copy_mrb> > _mrb_pool; + std::vector<boost::shared_ptr<e100_mmap_zero_copy_msb> > _msb_pool; //indexes into sub-sections of mapped memory size_t _recv_index, _send_index; diff --git a/host/lib/usrp/e100/e100_regs.hpp b/host/lib/usrp/e100/e100_regs.hpp index 75be2cfbe..654163dce 100644 --- a/host/lib/usrp/e100/e100_regs.hpp +++ b/host/lib/usrp/e100/e100_regs.hpp @@ -15,55 +15,45 @@ // along with this program. If not, see <http://www.gnu.org/licenses/>. // -//////////////////////////////////////////////////////////////// -// -// Memory map for embedded wishbone bus -// -//////////////////////////////////////////////////////////////// - -// All addresses are byte addresses. All accesses are word (16-bit) accesses. -// This means that address bit 0 is usually 0. -// There are 11 bits of address for the control. - #ifndef INCLUDED_E100_REGS_HPP #define INCLUDED_E100_REGS_HPP -///////////////////////////////////////////////////// -// Slave pointers +#include <boost/cstdint.hpp> -#define E100_REG_SLAVE(n) ((n)<<7) +#define TOREG(x) ((x)*4) -///////////////////////////////////////////////////// -// Slave 0 -- Misc Regs +#define localparam static const int -#define E100_REG_MISC_BASE E100_REG_SLAVE(0) +localparam SR_MISC = 0; // 5 +localparam SR_USER_REGS = 5; // 2 -#define E100_REG_MISC_LED E100_REG_MISC_BASE + 0 -#define E100_REG_MISC_SW E100_REG_MISC_BASE + 2 -#define E100_REG_MISC_CGEN_CTRL E100_REG_MISC_BASE + 4 -#define E100_REG_MISC_CGEN_ST E100_REG_MISC_BASE + 6 -#define E100_REG_MISC_TEST E100_REG_MISC_BASE + 8 -#define E100_REG_MISC_RX_LEN E100_REG_MISC_BASE + 10 -#define E100_REG_MISC_TX_LEN E100_REG_MISC_BASE + 12 -#define E100_REG_MISC_XFER_RATE E100_REG_MISC_BASE + 14 +localparam SR_TX_CTRL = 32; // 6 +localparam SR_TX_DSP = 40; // 5 +localparam SR_TX_FE = 48; // 5 -///////////////////////////////////////////////////// -// Slave 1 -- UART -// CLKDIV is 16 bits, others are only 8 +localparam SR_RX_CTRL0 = 96; // 9 +localparam SR_RX_DSP0 = 106; // 7 +localparam SR_RX_FE = 114; // 5 -#define E100_REG_UART_BASE E100_REG_SLAVE(1) +localparam SR_RX_CTRL1 = 128; // 9 +localparam SR_RX_DSP1 = 138; // 7 -#define E100_REG_UART_CLKDIV E100_REG_UART_BASE + 0 -#define E100_REG_UART_TXLEVEL E100_REG_UART_BASE + 2 -#define E100_REG_UART_RXLEVEL E100_REG_UART_BASE + 4 -#define E100_REG_UART_TXCHAR E100_REG_UART_BASE + 6 -#define E100_REG_UART_RXCHAR E100_REG_UART_BASE + 8 +localparam SR_TIME64 = 192; // 6 +localparam SR_SPI = 208; // 3 +localparam SR_I2C = 216; // 1 +localparam SR_GPIO = 224; // 5 -///////////////////////////////////////////////////// -// Slave 2 -- SPI Core -//these are 32-bit registers mapped onto the 16-bit Wishbone bus. -//Using peek32/poke32 should allow transparent use of these registers. -#define E100_REG_SPI_BASE E100_REG_SLAVE(2) +#define REG_RB_TIME_NOW_HI TOREG(10) +#define REG_RB_TIME_NOW_LO TOREG(11) +#define REG_RB_TIME_PPS_HI TOREG(14) +#define REG_RB_TIME_PPS_LO TOREG(15) +#define REG_RB_SPI TOREG(0) +#define REG_RB_COMPAT TOREG(1) +#define REG_RB_GPIO TOREG(3) +#define REG_RB_I2C TOREG(2) +#define REG_RB_CONFIG0 TOREG(4) +#define REG_RB_CONFIG1 TOREG(5) +#define REG_RB_NUM_RX_DSP TOREG(6) //spi slave constants #define UE_SPI_SS_AD9522 (1 << 3) @@ -71,67 +61,5 @@ #define UE_SPI_SS_TX_DB (1 << 1) #define UE_SPI_SS_RX_DB (1 << 0) -//////////////////////////////////////////////// -// Slave 3 -- I2C Core - -#define E100_REG_I2C_BASE E100_REG_SLAVE(3) - -//////////////////////////////////////////////// -// Slave 5 -- Error messages buffer - -#define E100_REG_ERR_BUFF E100_REG_SLAVE(5) - -/////////////////////////////////////////////////// -// Slave 7 -- Readback Mux 32 - -#define E100_REG_RB_MUX_32_BASE E100_REG_SLAVE(7) - -#define E100_REG_RB_TIME_NOW_HI E100_REG_RB_MUX_32_BASE + 0 -#define E100_REG_RB_TIME_NOW_LO E100_REG_RB_MUX_32_BASE + 4 -#define E100_REG_RB_TIME_PPS_HI E100_REG_RB_MUX_32_BASE + 8 -#define E100_REG_RB_TIME_PPS_LO E100_REG_RB_MUX_32_BASE + 12 -#define E100_REG_RB_MISC_TEST32 E100_REG_RB_MUX_32_BASE + 16 -#define E100_REG_RB_ERR_STATUS E100_REG_RB_MUX_32_BASE + 20 -#define E100_REG_RB_COMPAT E100_REG_RB_MUX_32_BASE + 24 -#define E100_REG_RB_GPIO E100_REG_RB_MUX_32_BASE + 28 - -//////////////////////////////////////////////////// -// Slave 8 -- Settings Bus -// -// Output-only, no readback, 64 registers total -// Each register must be written 64 bits at a time -// First the address xxx_xx00 and then xxx_xx10 - -// 64 total regs in address space -#define UE_SR_RX_CTRL0 0 // 9 regs (+0 to +8) -#define UE_SR_RX_DSP0 10 // 4 regs (+0 to +3) -#define UE_SR_RX_CTRL1 16 // 9 regs (+0 to +8) -#define UE_SR_RX_DSP1 26 // 4 regs (+0 to +3) -#define UE_SR_ERR_CTRL 30 // 1 reg -#define UE_SR_TX_CTRL 32 // 4 regs (+0 to +3) -#define UE_SR_TX_DSP 38 // 3 regs (+0 to +2) - -#define UE_SR_TIME64 42 // 6 regs (+0 to +5) -#define UE_SR_RX_FRONT 48 // 5 regs (+0 to +4) -#define UE_SR_TX_FRONT 54 // 5 regs (+0 to +4) - -#define UE_SR_REG_TEST32 60 // 1 reg -#define UE_SR_CLEAR_FIFO 61 // 1 reg -#define UE_SR_GLOBAL_RESET 63 // 1 reg -#define UE_SR_USER_REGS 64 // 2 regs - -#define UE_SR_GPIO 128 - -#define E100_REG_SR_ADDR(n) (E100_REG_SLAVE(8) + (4*(n))) - -#define E100_REG_SR_MISC_TEST32 E100_REG_SR_ADDR(UE_SR_REG_TEST32) -#define E100_REG_SR_ERR_CTRL E100_REG_SR_ADDR(UE_SR_ERR_CTRL) - -///////////////////////////////////////////////// -// Magic reset regs -//////////////////////////////////////////////// -#define E100_REG_CLEAR_FIFO E100_REG_SR_ADDR(UE_SR_CLEAR_FIFO) -#define E100_REG_GLOBAL_RESET E100_REG_SR_ADDR(UE_SR_GLOBAL_RESET) - -#endif +#endif /*INCLUDED_E100_REGS_HPP*/ diff --git a/host/lib/usrp/e100/include/linux/usrp_e.h b/host/lib/usrp/e100/include/linux/usrp_e.h index 37e9ee31a..f0512a40c 100644 --- a/host/lib/usrp/e100/include/linux/usrp_e.h +++ b/host/lib/usrp/e100/include/linux/usrp_e.h @@ -1,6 +1,6 @@ /* - * Copyright (C) 2010 Ettus Research, LLC + * Copyright (C) 2010-2012 Ettus Research, LLC * * Written by Philip Balister <philip@opensdr.com> * @@ -36,7 +36,7 @@ struct usrp_e_ctl32 { #define USRP_E_GET_RB_INFO _IOR(USRP_E_IOC_MAGIC, 0x27, struct usrp_e_ring_buffer_size_t) #define USRP_E_GET_COMPAT_NUMBER _IO(USRP_E_IOC_MAGIC, 0x28) -#define USRP_E_COMPAT_NUMBER 3 +#define USRP_E_COMPAT_NUMBER 4 /* Flag defines */ #define RB_USER (1<<0) diff --git a/host/lib/usrp/e100/io_impl.cpp b/host/lib/usrp/e100/io_impl.cpp index 332fe76ae..4d521e222 100644 --- a/host/lib/usrp/e100/io_impl.cpp +++ b/host/lib/usrp/e100/io_impl.cpp @@ -15,27 +15,16 @@ // along with this program. If not, see <http://www.gnu.org/licenses/>. // -#include "recv_packet_demuxer.hpp" #include "validate_subdev_spec.hpp" #include "async_packet_handler.hpp" #include "../../transport/super_recv_packet_handler.hpp" #include "../../transport/super_send_packet_handler.hpp" -#include <linux/usrp_e.h> //ioctl structures and constants #include "e100_impl.hpp" -#include "e100_regs.hpp" #include <uhd/utils/msg.hpp> #include <uhd/utils/log.hpp> #include <uhd/utils/tasks.hpp> -#include <uhd/utils/thread_priority.hpp> -#include <uhd/transport/bounded_buffer.hpp> #include <boost/bind.hpp> #include <boost/format.hpp> -#include <boost/bind.hpp> -#include <boost/thread/thread.hpp> -#include <poll.h> //poll -#include <fcntl.h> //open, close -#include <sstream> -#include <fstream> #include <boost/make_shared.hpp> using namespace uhd; @@ -44,134 +33,7 @@ using namespace uhd::transport; static const size_t vrt_send_header_offset_words32 = 1; -/*********************************************************************** - * io impl details (internal to this file) - * - pirate crew of 1 - * - bounded buffer - * - thread loop - * - vrt packet handler states - **********************************************************************/ -struct e100_impl::io_impl{ - io_impl(void): - false_alarm(0), async_msg_fifo(1000/*messages deep*/) - { /* NOP */ } - - double tick_rate; //set by update tick rate method - e100_ctrl::sptr iface; //so handle irq can peek and poke - void handle_irq(void); - size_t false_alarm; - //The data transport is listed first so that it is deconstructed last, - //which is after the states and booty which may hold managed buffers. - recv_packet_demuxer::sptr demuxer; - - //a pirate's life is the life for me! - void recv_pirate_loop( - spi_iface::sptr //keep a sptr to iface which shares gpio147 - ){ - //open the GPIO and set it up for an IRQ - std::ofstream edge_file("/sys/class/gpio/gpio147/edge"); - edge_file << "rising" << std::endl << std::flush; - edge_file.close(); - int fd = ::open("/sys/class/gpio/gpio147/value", O_RDONLY); - if (fd < 0) UHD_MSG(error) << "Unable to open GPIO for IRQ\n"; - - while (not boost::this_thread::interruption_requested()){ - pollfd pfd; - pfd.fd = fd; - pfd.events = POLLPRI | POLLERR; - ssize_t ret = ::poll(&pfd, 1, 100/*ms*/); - if (ret > 0) this->handle_irq(); - } - - //cleanup before thread exit - ::close(fd); - } - bounded_buffer<async_metadata_t> async_msg_fifo; - task::sptr pirate_task; -}; - -void e100_impl::io_impl::handle_irq(void){ - //check the status of the async msg buffer - const boost::uint32_t status = iface->peek32(E100_REG_RB_ERR_STATUS); - if ((status & 0x3) == 0){ //not done or error - //This could be a false-alarm because spi readback is mixed in. - //So we just sleep for a bit rather than interrupt continuously. - if (false_alarm++ > 3) boost::this_thread::sleep(boost::posix_time::milliseconds(1)); - return; - } - false_alarm = 0; //its a real message, reset the count... - //std::cout << boost::format("status: 0x%x") % status << std::endl; - - //load the data struct and call the ioctl - usrp_e_ctl32 data; - data.offset = E100_REG_ERR_BUFF; - data.count = status >> 16; - iface->ioctl(USRP_E_READ_CTL32, &data); - //for (size_t i = 0; i < data.count; i++){ - //data.buf[i] = iface->peek32(E100_REG_ERR_BUFF + i*sizeof(boost::uint32_t)); - //std::cout << boost::format(" buff[%u] = 0x%08x\n") % i % data.buf[i]; - //} - - //unpack the vrt header and process below... - vrt::if_packet_info_t if_packet_info; - if_packet_info.num_packet_words32 = data.count; - try{vrt::if_hdr_unpack_le(data.buf, if_packet_info);} - catch(const std::exception &e){ - UHD_MSG(error) << "Error unpacking vrt header:\n" << e.what() << std::endl; - goto prepare; - } - - //handle a tx async report message - if (if_packet_info.sid == E100_TX_ASYNC_SID and if_packet_info.packet_type != vrt::if_packet_info_t::PACKET_TYPE_DATA){ - - //fill in the async metadata - async_metadata_t metadata; - load_metadata_from_buff(uhd::wtohx<boost::uint32_t>, metadata, if_packet_info, data.buf, tick_rate); - - //push the message onto the queue - async_msg_fifo.push_with_pop_on_full(metadata); - - //print some fastpath messages - standard_async_msg_prints(metadata); - } - - //prepare for the next round - prepare: - iface->poke32(E100_REG_SR_ERR_CTRL, 1 << 0); //clear - while ((iface->peek32(E100_REG_RB_ERR_STATUS) & (1 << 2)) == 0){} //wait for idle - iface->poke32(E100_REG_SR_ERR_CTRL, 1 << 1); //start -} - -/*********************************************************************** - * Helper Functions - **********************************************************************/ -void e100_impl::io_init(void){ - - //create new io impl - _io_impl = UHD_PIMPL_MAKE(io_impl, ()); - _io_impl->demuxer = recv_packet_demuxer::make(_data_transport, _rx_dsps.size(), E100_RX_SID_BASE); - _io_impl->iface = _fpga_ctrl; - - //clear fifo state machines - _fpga_ctrl->poke32(E100_REG_CLEAR_FIFO, 0); - - //allocate streamer weak ptrs containers - _rx_streamers.resize(_rx_dsps.size()); - _tx_streamers.resize(1/*known to be 1 dsp*/); - - //prepare the async msg buffer for incoming messages - _fpga_ctrl->poke32(E100_REG_SR_ERR_CTRL, 1 << 0); //clear - while ((_fpga_ctrl->peek32(E100_REG_RB_ERR_STATUS) & (1 << 2)) == 0){} //wait for idle - _fpga_ctrl->poke32(E100_REG_SR_ERR_CTRL, 1 << 1); //start - - //spawn a pirate, yarrr! - _io_impl->pirate_task = task::make(boost::bind( - &e100_impl::io_impl::recv_pirate_loop, _io_impl.get(), _aux_spi_iface - )); -} - void e100_impl::update_tick_rate(const double rate){ - _io_impl->tick_rate = rate; //update the tick rate on all existing streamers -> thread safe for (size_t i = 0; i < _rx_streamers.size(); i++){ @@ -254,8 +116,7 @@ void e100_impl::update_tx_subdev_spec(const uhd::usrp::subdev_spec_t &spec){ bool e100_impl::recv_async_msg( async_metadata_t &async_metadata, double timeout ){ - boost::this_thread::disable_interruption di; //disable because the wait can throw - return _io_impl->async_msg_fifo.pop_with_timed_wait(async_metadata, timeout); + return _fifo_ctrl->pop_async_msg(async_metadata, timeout); } /*********************************************************************** @@ -300,7 +161,7 @@ rx_streamer::sptr e100_impl::get_rx_stream(const uhd::stream_args_t &args_){ _rx_dsps[dsp]->set_nsamps_per_packet(spp); //seems to be a good place to set this _rx_dsps[dsp]->setup(args); my_streamer->set_xport_chan_get_buff(chan_i, boost::bind( - &recv_packet_demuxer::get_recv_buff, _io_impl->demuxer, dsp, _1 + &recv_packet_demuxer::get_recv_buff, _recv_demuxer, dsp, _1 ), true /*flush*/); my_streamer->set_overflow_handler(chan_i, boost::bind( &rx_dsp_core_200::handle_overflow, _rx_dsps[dsp] diff --git a/host/lib/usrp/mboard_eeprom.cpp b/host/lib/usrp/mboard_eeprom.cpp index 785d30296..1f4abc27e 100644 --- a/host/lib/usrp/mboard_eeprom.cpp +++ b/host/lib/usrp/mboard_eeprom.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-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 @@ -84,17 +84,20 @@ static std::string uint16_bytes_to_string(const byte_vector_t &bytes){ **********************************************************************/ static const boost::uint8_t N100_EEPROM_ADDR = 0x50; -static const uhd::dict<std::string, boost::uint8_t> USRP_N100_OFFSETS = boost::assign::map_list_of - ("hardware", 0x00) - ("mac-addr", 0x02) - ("ip-addr", 0x0C) - //leave space here for other addresses (perhaps) - ("revision", 0x12) - ("product", 0x14) - ("gpsdo", 0x17) - ("serial", 0x18) - ("name", 0x18 + SERIAL_LEN) -; +struct n100_eeprom_map{ + boost::uint16_t hardware; + boost::uint8_t mac_addr[6]; + boost::uint32_t subnet; + boost::uint32_t ip_addr; + boost::uint16_t _pad0; + boost::uint16_t revision; + boost::uint16_t product; + unsigned char _pad1; + unsigned char gpsdo; + unsigned char serial[SERIAL_LEN]; + unsigned char name[NAME_MAX_LEN]; + boost::uint32_t gateway; +}; enum n200_gpsdo_type{ N200_GPSDO_NONE = 0, @@ -105,30 +108,36 @@ enum n200_gpsdo_type{ static void load_n100(mboard_eeprom_t &mb_eeprom, i2c_iface &iface){ //extract the hardware number mb_eeprom["hardware"] = uint16_bytes_to_string( - iface.read_eeprom(N100_EEPROM_ADDR, USRP_N100_OFFSETS["hardware"], 2) + iface.read_eeprom(N100_EEPROM_ADDR, offsetof(n100_eeprom_map, hardware), 2) ); //extract the revision number mb_eeprom["revision"] = uint16_bytes_to_string( - iface.read_eeprom(N100_EEPROM_ADDR, USRP_N100_OFFSETS["revision"], 2) + iface.read_eeprom(N100_EEPROM_ADDR, offsetof(n100_eeprom_map, revision), 2) ); //extract the product code mb_eeprom["product"] = uint16_bytes_to_string( - iface.read_eeprom(N100_EEPROM_ADDR, USRP_N100_OFFSETS["product"], 2) + iface.read_eeprom(N100_EEPROM_ADDR, offsetof(n100_eeprom_map, product), 2) ); //extract the addresses mb_eeprom["mac-addr"] = mac_addr_t::from_bytes(iface.read_eeprom( - N100_EEPROM_ADDR, USRP_N100_OFFSETS["mac-addr"], 6 + N100_EEPROM_ADDR, offsetof(n100_eeprom_map, mac_addr), 6 )).to_string(); boost::asio::ip::address_v4::bytes_type ip_addr_bytes; - byte_copy(iface.read_eeprom(N100_EEPROM_ADDR, USRP_N100_OFFSETS["ip-addr"], 4), ip_addr_bytes); + byte_copy(iface.read_eeprom(N100_EEPROM_ADDR, offsetof(n100_eeprom_map, ip_addr), 4), ip_addr_bytes); mb_eeprom["ip-addr"] = boost::asio::ip::address_v4(ip_addr_bytes).to_string(); + byte_copy(iface.read_eeprom(N100_EEPROM_ADDR, offsetof(n100_eeprom_map, subnet), 4), ip_addr_bytes); + mb_eeprom["subnet"] = boost::asio::ip::address_v4(ip_addr_bytes).to_string(); + + byte_copy(iface.read_eeprom(N100_EEPROM_ADDR, offsetof(n100_eeprom_map, gateway), 4), ip_addr_bytes); + mb_eeprom["gateway"] = boost::asio::ip::address_v4(ip_addr_bytes).to_string(); + //gpsdo capabilities - boost::uint8_t gpsdo_byte = iface.read_eeprom(N100_EEPROM_ADDR, USRP_N100_OFFSETS["gpsdo"], 1).at(0); + boost::uint8_t gpsdo_byte = iface.read_eeprom(N100_EEPROM_ADDR, offsetof(n100_eeprom_map, gpsdo), 1).at(0); switch(n200_gpsdo_type(gpsdo_byte)){ case N200_GPSDO_INTERNAL: mb_eeprom["gpsdo"] = "internal"; break; case N200_GPSDO_ONBOARD: mb_eeprom["gpsdo"] = "onboard"; break; @@ -137,12 +146,12 @@ static void load_n100(mboard_eeprom_t &mb_eeprom, i2c_iface &iface){ //extract the serial mb_eeprom["serial"] = bytes_to_string(iface.read_eeprom( - N100_EEPROM_ADDR, USRP_N100_OFFSETS["serial"], SERIAL_LEN + N100_EEPROM_ADDR, offsetof(n100_eeprom_map, serial), SERIAL_LEN )); //extract the name mb_eeprom["name"] = bytes_to_string(iface.read_eeprom( - N100_EEPROM_ADDR, USRP_N100_OFFSETS["name"], NAME_MAX_LEN + N100_EEPROM_ADDR, offsetof(n100_eeprom_map, name), NAME_MAX_LEN )); //Empty serial correction: use the mac address to determine serial. @@ -158,32 +167,44 @@ static void load_n100(mboard_eeprom_t &mb_eeprom, i2c_iface &iface){ static void store_n100(const mboard_eeprom_t &mb_eeprom, i2c_iface &iface){ //parse the revision number if (mb_eeprom.has_key("hardware")) iface.write_eeprom( - N100_EEPROM_ADDR, USRP_N100_OFFSETS["hardware"], + N100_EEPROM_ADDR, offsetof(n100_eeprom_map, hardware), string_to_uint16_bytes(mb_eeprom["hardware"]) ); //parse the revision number if (mb_eeprom.has_key("revision")) iface.write_eeprom( - N100_EEPROM_ADDR, USRP_N100_OFFSETS["revision"], + N100_EEPROM_ADDR, offsetof(n100_eeprom_map, revision), string_to_uint16_bytes(mb_eeprom["revision"]) ); //parse the product code if (mb_eeprom.has_key("product")) iface.write_eeprom( - N100_EEPROM_ADDR, USRP_N100_OFFSETS["product"], + N100_EEPROM_ADDR, offsetof(n100_eeprom_map, product), string_to_uint16_bytes(mb_eeprom["product"]) ); //store the addresses if (mb_eeprom.has_key("mac-addr")) iface.write_eeprom( - N100_EEPROM_ADDR, USRP_N100_OFFSETS["mac-addr"], + N100_EEPROM_ADDR, offsetof(n100_eeprom_map, mac_addr), mac_addr_t::from_string(mb_eeprom["mac-addr"]).to_bytes() ); if (mb_eeprom.has_key("ip-addr")){ byte_vector_t ip_addr_bytes(4); byte_copy(boost::asio::ip::address_v4::from_string(mb_eeprom["ip-addr"]).to_bytes(), ip_addr_bytes); - iface.write_eeprom(N100_EEPROM_ADDR, USRP_N100_OFFSETS["ip-addr"], ip_addr_bytes); + iface.write_eeprom(N100_EEPROM_ADDR, offsetof(n100_eeprom_map, ip_addr), ip_addr_bytes); + } + + if (mb_eeprom.has_key("subnet")){ + byte_vector_t ip_addr_bytes(4); + byte_copy(boost::asio::ip::address_v4::from_string(mb_eeprom["subnet"]).to_bytes(), ip_addr_bytes); + iface.write_eeprom(N100_EEPROM_ADDR, offsetof(n100_eeprom_map, subnet), ip_addr_bytes); + } + + if (mb_eeprom.has_key("gateway")){ + byte_vector_t ip_addr_bytes(4); + byte_copy(boost::asio::ip::address_v4::from_string(mb_eeprom["gateway"]).to_bytes(), ip_addr_bytes); + iface.write_eeprom(N100_EEPROM_ADDR, offsetof(n100_eeprom_map, gateway), ip_addr_bytes); } //gpsdo capabilities @@ -191,18 +212,18 @@ static void store_n100(const mboard_eeprom_t &mb_eeprom, i2c_iface &iface){ boost::uint8_t gpsdo_byte = N200_GPSDO_NONE; if (mb_eeprom["gpsdo"] == "internal") gpsdo_byte = N200_GPSDO_INTERNAL; if (mb_eeprom["gpsdo"] == "onboard") gpsdo_byte = N200_GPSDO_ONBOARD; - iface.write_eeprom(N100_EEPROM_ADDR, USRP_N100_OFFSETS["gpsdo"], byte_vector_t(1, gpsdo_byte)); + iface.write_eeprom(N100_EEPROM_ADDR, offsetof(n100_eeprom_map, gpsdo), byte_vector_t(1, gpsdo_byte)); } //store the serial if (mb_eeprom.has_key("serial")) iface.write_eeprom( - N100_EEPROM_ADDR, USRP_N100_OFFSETS["serial"], + N100_EEPROM_ADDR, offsetof(n100_eeprom_map, serial), string_to_bytes(mb_eeprom["serial"], SERIAL_LEN) ); //store the name if (mb_eeprom.has_key("name")) iface.write_eeprom( - N100_EEPROM_ADDR, USRP_N100_OFFSETS["name"], + N100_EEPROM_ADDR, offsetof(n100_eeprom_map, name), string_to_bytes(mb_eeprom["name"], NAME_MAX_LEN) ); } @@ -426,20 +447,16 @@ mboard_eeprom_t::mboard_eeprom_t(void){ /* NOP */ } -mboard_eeprom_t::mboard_eeprom_t(i2c_iface &iface, map_type map){ - switch(map){ - case MAP_N100: load_n100(*this, iface); break; - case MAP_B000: load_b000(*this, iface); break; - case MAP_B100: load_b100(*this, iface); break; - case MAP_E100: load_e100(*this, iface); break; - } +mboard_eeprom_t::mboard_eeprom_t(i2c_iface &iface, const std::string &which){ + if (which == "N100") load_n100(*this, iface); + if (which == "B000") load_b000(*this, iface); + if (which == "B100") load_b100(*this, iface); + if (which == "E100") load_e100(*this, iface); } -void mboard_eeprom_t::commit(i2c_iface &iface, map_type map) const{ - switch(map){ - case MAP_N100: store_n100(*this, iface); break; - case MAP_B000: store_b000(*this, iface); break; - case MAP_B100: store_b100(*this, iface); break; - case MAP_E100: store_e100(*this, iface); break; - } +void mboard_eeprom_t::commit(i2c_iface &iface, const std::string &which) const{ + if (which == "N100") store_n100(*this, iface); + if (which == "B000") store_b000(*this, iface); + if (which == "B100") store_b100(*this, iface); + if (which == "E100") store_e100(*this, iface); } diff --git a/host/lib/usrp/multi_usrp.cpp b/host/lib/usrp/multi_usrp.cpp index 93c0eada6..0331cf93a 100644 --- a/host/lib/usrp/multi_usrp.cpp +++ b/host/lib/usrp/multi_usrp.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-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 @@ -21,6 +21,10 @@ #include <uhd/exception.hpp> #include <uhd/utils/msg.hpp> #include <uhd/utils/gain_group.hpp> +#include <uhd/usrp/dboard_id.hpp> +#include <uhd/usrp/mboard_eeprom.hpp> +#include <uhd/usrp/dboard_eeprom.hpp> +#include <boost/assign/list_of.hpp> #include <boost/thread.hpp> #include <boost/foreach.hpp> #include <boost/format.hpp> @@ -125,6 +129,9 @@ static tune_result_t tune_xx_subdev_and_dsp( //------------------------------------------------------------------ double lo_offset = 0.0; if (rf_fe_subtree->access<bool>("use_lo_offset").get()){ + //If the frontend has lo_offset value and range properties, trust it for lo_offset + if (rf_fe_subtree->exists("lo_offset/value")) lo_offset = rf_fe_subtree->access<double>("lo_offset/value").get(); + //If the local oscillator will be in the passband, use an offset. //But constrain the LO offset by the width of the filter bandwidth. const double rate = dsp_subtree->access<double>("rate/value").get(); @@ -143,6 +150,14 @@ static tune_result_t tune_xx_subdev_and_dsp( break; case tune_request_t::POLICY_MANUAL: + //If the rf_fe understands lo_offset settings, infer the desired lo_offset and set it + // Side effect: In TVRX2 for example, after setting the lo_offset (if_freq) with a + // POLICY_MANUAL, there is no way for the user to automatically get back to default + // if_freq without deconstruct/reconstruct the rf_fe objects. + if (rf_fe_subtree->exists("lo_offset/value")) { + rf_fe_subtree->access<double>("lo_offset/value").set(tune_request.rf_freq - tune_request.target_freq); + } + target_rf_freq = tune_request.rf_freq; rf_fe_subtree->access<double>("freq/value").set(target_rf_freq); break; @@ -214,6 +229,44 @@ public: return _dev; } + dict<std::string, std::string> get_usrp_rx_info(size_t chan){ + mboard_chan_pair mcp = rx_chan_to_mcp(chan); + dict<std::string, std::string> usrp_info; + + mboard_eeprom_t mb_eeprom = _tree->access<mboard_eeprom_t>(mb_root(mcp.mboard) / "eeprom").get(); + dboard_eeprom_t db_eeprom = _tree->access<dboard_eeprom_t>(rx_rf_fe_root(mcp.chan).branch_path().branch_path() / "rx_eeprom").get(); + + usrp_info["mboard_id"] = _tree->access<std::string>(mb_root(mcp.mboard) / "name").get(); + usrp_info["mboard_name"] = mb_eeprom["name"]; + usrp_info["mboard_serial"] = mb_eeprom["serial"]; + usrp_info["rx_id"] = db_eeprom.id.to_pp_string(); + usrp_info["rx_subdev_name"] = _tree->access<std::string>(rx_rf_fe_root(mcp.chan) / "name").get(); + usrp_info["rx_subdev_spec"] = _tree->access<subdev_spec_t>(mb_root(mcp.mboard) / "rx_subdev_spec").get().to_string(); + usrp_info["rx_serial"] = db_eeprom.serial; + usrp_info["rx_antenna"] = _tree->access<std::string>(rx_rf_fe_root(mcp.chan) / "antenna" / "value").get(); + + return usrp_info; + } + + dict<std::string, std::string> get_usrp_tx_info(size_t chan){ + mboard_chan_pair mcp = tx_chan_to_mcp(chan); + dict<std::string, std::string> usrp_info; + + mboard_eeprom_t mb_eeprom = _tree->access<mboard_eeprom_t>(mb_root(mcp.mboard) / "eeprom").get(); + dboard_eeprom_t db_eeprom = _tree->access<dboard_eeprom_t>(tx_rf_fe_root(mcp.chan).branch_path().branch_path() / "tx_eeprom").get(); + + usrp_info["mboard_id"] = _tree->access<std::string>(mb_root(mcp.mboard) / "name").get(); + usrp_info["mboard_name"] = mb_eeprom["name"]; + usrp_info["mboard_serial"] = mb_eeprom["serial"]; + usrp_info["tx_id"] = db_eeprom.id.to_pp_string(); + usrp_info["tx_subdev_name"] = _tree->access<std::string>(tx_rf_fe_root(mcp.chan) / "name").get(); + usrp_info["tx_subdev_spec"] = _tree->access<subdev_spec_t>(mb_root(mcp.mboard) / "tx_subdev_spec").get().to_string(); + usrp_info["tx_serial"] = db_eeprom.serial; + usrp_info["tx_antenna"] = _tree->access<std::string>(tx_rf_fe_root(mcp.chan) / "antenna" / "value").get(); + + return usrp_info; + } + /******************************************************************* * Mboard methods ******************************************************************/ @@ -356,12 +409,27 @@ public: return true; } - void set_command_time(const time_spec_t &, size_t){ - throw uhd::not_implemented_error("Not implemented yet, but we have a very good idea of how to do it."); + void set_command_time(const time_spec_t &time_spec, size_t mboard){ + if (mboard != ALL_MBOARDS){ + if (not _tree->exists(mb_root(mboard) / "time/cmd")){ + throw uhd::not_implemented_error("timed command feature not implemented on this hardware"); + } + _tree->access<time_spec_t>(mb_root(mboard) / "time/cmd").set(time_spec); + return; + } + for (size_t m = 0; m < get_num_mboards(); m++){ + set_command_time(time_spec, m); + } } - void clear_command_time(size_t){ - throw uhd::not_implemented_error("Not implemented yet, but we have a very good idea of how to do it."); + void clear_command_time(size_t mboard){ + if (mboard != ALL_MBOARDS){ + _tree->access<time_spec_t>(mb_root(mboard) / "time/cmd").set(time_spec_t(0.0)); + return; + } + for (size_t m = 0; m < get_num_mboards(); m++){ + clear_command_time(m); + } } void issue_stream_cmd(const stream_cmd_t &stream_cmd, size_t chan){ @@ -522,6 +590,10 @@ public: ); } + freq_range_t get_fe_rx_freq_range(size_t chan){ + return _tree->access<meta_range_t>(rx_rf_fe_root(chan) / "freq" / "range").get(); + } + void set_rx_gain(double gain, const std::string &name, size_t chan){ return rx_gain_group(chan)->set_value(gain, name); } @@ -621,10 +693,6 @@ public: return _tree->access<subdev_spec_t>(mb_root(mboard) / "tx_subdev_spec").get(); } - std::string get_tx_subdev_name(size_t chan){ - return _tree->access<std::string>(tx_rf_fe_root(chan) / "name").get(); - } - size_t get_tx_num_channels(void){ size_t sum = 0; for (size_t m = 0; m < get_num_mboards(); m++){ @@ -633,6 +701,10 @@ public: return sum; } + std::string get_tx_subdev_name(size_t chan){ + return _tree->access<std::string>(tx_rf_fe_root(chan) / "name").get(); + } + void set_tx_rate(double rate, size_t chan){ if (chan != ALL_CHANS){ _tree->access<double>(tx_dsp_root(chan) / "rate" / "value").set(rate); @@ -670,6 +742,10 @@ public: ); } + freq_range_t get_fe_tx_freq_range(size_t chan){ + return _tree->access<meta_range_t>(tx_rf_fe_root(chan) / "freq" / "range").get(); + } + void set_tx_gain(double gain, const std::string &name, size_t chan){ return tx_gain_group(chan)->set_value(gain, name); } @@ -832,7 +908,7 @@ private: const subdev_spec_pair_t spec = get_tx_subdev_spec(mcp.mboard).at(mcp.chan); gain_group::sptr gg = gain_group::make(); BOOST_FOREACH(const std::string &name, _tree->list(mb_root(mcp.mboard) / "tx_codecs" / spec.db_name / "gains")){ - gg->register_fcns("ADC-"+name, make_gain_fcns_from_subtree(_tree->subtree(mb_root(mcp.mboard) / "tx_codecs" / spec.db_name / "gains" / name)), 1 /* high prio */); + gg->register_fcns("DAC-"+name, make_gain_fcns_from_subtree(_tree->subtree(mb_root(mcp.mboard) / "tx_codecs" / spec.db_name / "gains" / name)), 1 /* high prio */); } BOOST_FOREACH(const std::string &name, _tree->list(tx_rf_fe_root(chan) / "gains")){ gg->register_fcns(name, make_gain_fcns_from_subtree(_tree->subtree(tx_rf_fe_root(chan) / "gains" / name)), 0 /* low prio */); diff --git a/host/lib/usrp/usrp1/codec_ctrl.cpp b/host/lib/usrp/usrp1/codec_ctrl.cpp index c82569ea3..7383c9833 100644 --- a/host/lib/usrp/usrp1/codec_ctrl.cpp +++ b/host/lib/usrp/usrp1/codec_ctrl.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-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 @@ -27,6 +27,7 @@ #include <boost/format.hpp> #include <boost/tuple/tuple.hpp> #include <boost/math/special_functions/round.hpp> +#include <boost/math/special_functions/sign.hpp> #include <boost/assign/list_of.hpp> #include <iomanip> @@ -375,6 +376,12 @@ double usrp1_codec_ctrl_impl::fine_tune(double codec_rate, double target_freq) void usrp1_codec_ctrl_impl::set_duc_freq(double freq, double rate) { double codec_rate = rate * 2; + + //correct for outside of rate (wrap around) + freq = std::fmod(freq, rate); + if (std::abs(freq) > rate/2.0) + freq -= boost::math::sign(freq)*rate; + double coarse_freq = coarse_tune(codec_rate, freq); double fine_freq = fine_tune(codec_rate / 4, freq - coarse_freq); diff --git a/host/lib/usrp/usrp1/io_impl.cpp b/host/lib/usrp/usrp1/io_impl.cpp index d256df660..1d8b9bd76 100644 --- a/host/lib/usrp/usrp1/io_impl.cpp +++ b/host/lib/usrp/usrp1/io_impl.cpp @@ -73,8 +73,8 @@ public: /* NOP */ } - void commit(size_t size){ - if (size != 0) this->_commit_cb(_curr_buff, _next_buff, size); + void release(void){ + this->_commit_cb(_curr_buff, _next_buff, size()); } sptr get_new( @@ -83,13 +83,13 @@ public: ){ _curr_buff = curr_buff; _next_buff = next_buff; - return make_managed_buffer(this); + return make(this, + _curr_buff.buff->cast<char *>() + _curr_buff.offset, + _curr_buff.buff->size() - _curr_buff.offset + ); } private: - void *get_buff(void) const{return _curr_buff.buff->cast<char *>() + _curr_buff.offset;} - size_t get_size(void) const{return _curr_buff.buff->size() - _curr_buff.offset;} - offset_send_buffer _curr_buff, _next_buff; commit_cb_type _commit_cb; }; diff --git a/host/lib/usrp/usrp1/usrp1_impl.cpp b/host/lib/usrp/usrp1/usrp1_impl.cpp index 30986ac66..a5e51b7d2 100644 --- a/host/lib/usrp/usrp1/usrp1_impl.cpp +++ b/host/lib/usrp/usrp1/usrp1_impl.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-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 @@ -89,11 +89,7 @@ static device_addrs_t usrp1_find(const device_addr_t &hint) usrp1_fw_image = find_image_path(hint.get("fw", "usrp1_fw.ihx")); } catch(...){ - UHD_MSG(warning) << boost::format( - "Could not locate USRP1 firmware.\n" - "Please install the images package.\n" - ); - return usrp1_addrs; + UHD_MSG(warning) << boost::format("Could not locate USRP1 firmware. %s") % print_images_error(); } UHD_LOG << "USRP1 firmware image: " << usrp1_fw_image << std::endl; @@ -121,7 +117,7 @@ static device_addrs_t usrp1_find(const device_addr_t &hint) catch(const uhd::exception &){continue;} //ignore claimed fx2_ctrl::sptr fx2_ctrl = fx2_ctrl::make(control); - const mboard_eeprom_t mb_eeprom(*fx2_ctrl, mboard_eeprom_t::MAP_B000); + const mboard_eeprom_t mb_eeprom(*fx2_ctrl, USRP1_EEPROM_MAP_KEY); device_addr_t new_addr; new_addr["type"] = "usrp1"; new_addr["name"] = mb_eeprom["name"]; @@ -214,14 +210,20 @@ usrp1_impl::usrp1_impl(const device_addr_t &device_addr){ _tree = property_tree::make(); _tree->create<std::string>("/name").set("USRP1 Device"); const fs_path mb_path = "/mboards/0"; - _tree->create<std::string>(mb_path / "name").set("USRP1 (Classic)"); + _tree->create<std::string>(mb_path / "name").set("USRP1"); _tree->create<std::string>(mb_path / "load_eeprom") .subscribe(boost::bind(&fx2_ctrl::usrp_load_eeprom, _fx2_ctrl, _1)); //////////////////////////////////////////////////////////////////// + // create user-defined control objects + //////////////////////////////////////////////////////////////////// + _tree->create<std::pair<boost::uint8_t, boost::uint32_t> >(mb_path / "user" / "regs") + .subscribe(boost::bind(&usrp1_impl::set_reg, this, _1)); + + //////////////////////////////////////////////////////////////////// // setup the mboard eeprom //////////////////////////////////////////////////////////////////// - const mboard_eeprom_t mb_eeprom(*_fx2_ctrl, mboard_eeprom_t::MAP_B000); + const mboard_eeprom_t mb_eeprom(*_fx2_ctrl, USRP1_EEPROM_MAP_KEY); _tree->create<mboard_eeprom_t>(mb_path / "eeprom") .set(mb_eeprom) .subscribe(boost::bind(&usrp1_impl::set_mb_eeprom, this, _1)); @@ -358,6 +360,9 @@ usrp1_impl::usrp1_impl(const device_addr_t &device_addr){ tx_db_eeprom.load(*_fx2_ctrl, (db == "A")? (I2C_ADDR_TX_A) : (I2C_ADDR_TX_B)); gdb_eeprom.load(*_fx2_ctrl, (db == "A")? (I2C_ADDR_TX_A ^ 5) : (I2C_ADDR_TX_B ^ 5)); + //disable rx dc offset if LFRX + if (rx_db_eeprom.id == 0x000f) _tree->access<bool>(mb_path / "rx_frontends" / db / "dc_offset" / "enable").set(false); + //create the properties and register subscribers _tree->create<dboard_eeprom_t>(mb_path / "dboards" / db/ "rx_eeprom") .set(rx_db_eeprom) @@ -452,7 +457,7 @@ bool usrp1_impl::has_tx_halfband(void){ * Properties callback methods below **********************************************************************/ void usrp1_impl::set_mb_eeprom(const uhd::usrp::mboard_eeprom_t &mb_eeprom){ - mb_eeprom.commit(*_fx2_ctrl, mboard_eeprom_t::MAP_B000); + mb_eeprom.commit(*_fx2_ctrl, USRP1_EEPROM_MAP_KEY); } void usrp1_impl::set_db_eeprom(const std::string &db, const std::string &type, const uhd::usrp::dboard_eeprom_t &db_eeprom){ @@ -501,3 +506,8 @@ std::complex<double> usrp1_impl::set_rx_dc_offset(const std::string &db, const s return std::complex<double>(double(i_off) * (1ul << 31), double(q_off) * (1ul << 31)); } + +void usrp1_impl::set_reg(const std::pair<boost::uint8_t, boost::uint32_t> ®) +{ + _iface->poke32(reg.first, reg.second); +} diff --git a/host/lib/usrp/usrp1/usrp1_impl.hpp b/host/lib/usrp/usrp1/usrp1_impl.hpp index 581f4cbdd..9461f0081 100644 --- a/host/lib/usrp/usrp1/usrp1_impl.hpp +++ b/host/lib/usrp/usrp1/usrp1_impl.hpp @@ -38,6 +38,8 @@ #ifndef INCLUDED_USRP1_IMPL_HPP #define INCLUDED_USRP1_IMPL_HPP +static const std::string USRP1_EEPROM_MAP_KEY = "B000"; + /*! * USRP1 implementation guts: * The implementation details are encapsulated here. @@ -133,6 +135,8 @@ private: void vandal_conquest_loop(void); + void set_reg(const std::pair<boost::uint8_t, boost::uint32_t> ®); + //handle the enables bool _rx_enabled, _tx_enabled; void enable_rx(bool enb){ diff --git a/host/lib/usrp/usrp2/CMakeLists.txt b/host/lib/usrp/usrp2/CMakeLists.txt index 10f7407b0..da39d9df1 100644 --- a/host/lib/usrp/usrp2/CMakeLists.txt +++ b/host/lib/usrp/usrp2/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2011 Ettus Research LLC +# Copyright 2011-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 @@ -32,5 +32,6 @@ IF(ENABLE_USRP2) ${CMAKE_CURRENT_SOURCE_DIR}/io_impl.cpp ${CMAKE_CURRENT_SOURCE_DIR}/usrp2_iface.cpp ${CMAKE_CURRENT_SOURCE_DIR}/usrp2_impl.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/usrp2_fifo_ctrl.cpp ) ENDIF(ENABLE_USRP2) diff --git a/host/lib/usrp/usrp2/clock_ctrl.cpp b/host/lib/usrp/usrp2/clock_ctrl.cpp index b2912c70c..0ae3b0bd8 100644 --- a/host/lib/usrp/usrp2/clock_ctrl.cpp +++ b/host/lib/usrp/usrp2/clock_ctrl.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-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 @@ -35,8 +35,9 @@ static const bool enb_test_clk = false; */ class usrp2_clock_ctrl_impl : public usrp2_clock_ctrl{ public: - usrp2_clock_ctrl_impl(usrp2_iface::sptr iface){ + usrp2_clock_ctrl_impl(usrp2_iface::sptr iface, uhd::spi_iface::sptr spiface){ _iface = iface; + _spiface = spiface; clk_regs = usrp2_clk_regs_t(_iface->get_rev()); _ad9510_regs.cp_current_setting = ad9510_regs_t::CP_CURRENT_SETTING_3_0MA; @@ -331,7 +332,7 @@ private: */ void write_reg(boost::uint8_t addr){ boost::uint32_t data = _ad9510_regs.get_write_reg(addr); - _iface->write_spi(SPI_SS_AD9510, spi_config_t::EDGE_RISE, data, 24); + _spiface->write_spi(SPI_SS_AD9510, spi_config_t::EDGE_RISE, data, 24); } /*! @@ -377,7 +378,7 @@ private: } usrp2_iface::sptr _iface; - + uhd::spi_iface::sptr _spiface; usrp2_clk_regs_t clk_regs; ad9510_regs_t _ad9510_regs; }; @@ -385,6 +386,6 @@ private: /*********************************************************************** * Public make function for the ad9510 clock control **********************************************************************/ -usrp2_clock_ctrl::sptr usrp2_clock_ctrl::make(usrp2_iface::sptr iface){ - return sptr(new usrp2_clock_ctrl_impl(iface)); +usrp2_clock_ctrl::sptr usrp2_clock_ctrl::make(usrp2_iface::sptr iface, uhd::spi_iface::sptr spiface){ + return sptr(new usrp2_clock_ctrl_impl(iface, spiface)); } diff --git a/host/lib/usrp/usrp2/clock_ctrl.hpp b/host/lib/usrp/usrp2/clock_ctrl.hpp index 9ccbc959e..067e1e35d 100644 --- a/host/lib/usrp/usrp2/clock_ctrl.hpp +++ b/host/lib/usrp/usrp2/clock_ctrl.hpp @@ -1,5 +1,5 @@ // -// Copyright 2010 Ettus Research LLC +// Copyright 2010-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 @@ -29,10 +29,11 @@ public: /*! * Make a clock config for the ad9510 ic. - * \param _iface a pointer to the usrp2 interface object + * \param iface a pointer to the usrp2 interface object + * \param spiface the interface to spi * \return a new clock control object */ - static sptr make(usrp2_iface::sptr iface); + static sptr make(usrp2_iface::sptr iface, uhd::spi_iface::sptr spiface); /*! * Get the master clock frequency for the fpga. diff --git a/host/lib/usrp/usrp2/codec_ctrl.cpp b/host/lib/usrp/usrp2/codec_ctrl.cpp index 06bf83b15..b53c4d9df 100644 --- a/host/lib/usrp/usrp2/codec_ctrl.cpp +++ b/host/lib/usrp/usrp2/codec_ctrl.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-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 @@ -32,8 +32,9 @@ using namespace uhd; */ class usrp2_codec_ctrl_impl : public usrp2_codec_ctrl{ public: - usrp2_codec_ctrl_impl(usrp2_iface::sptr iface){ + usrp2_codec_ctrl_impl(usrp2_iface::sptr iface, uhd::spi_iface::sptr spiface){ _iface = iface; + _spiface = spiface; //setup the ad9777 dac _ad9777_regs.x_1r_2r_mode = ad9777_regs_t::X_1R_2R_MODE_1R; @@ -189,11 +190,12 @@ private: ad9777_regs_t _ad9777_regs; ads62p44_regs_t _ads62p44_regs; usrp2_iface::sptr _iface; + uhd::spi_iface::sptr _spiface; void send_ad9777_reg(boost::uint8_t addr){ boost::uint16_t reg = _ad9777_regs.get_write_reg(addr); UHD_LOGV(always) << "send_ad9777_reg: " << std::hex << reg << std::endl; - _iface->write_spi( + _spiface->write_spi( SPI_SS_AD9777, spi_config_t::EDGE_RISE, reg, 16 ); @@ -201,7 +203,7 @@ private: void send_ads62p44_reg(boost::uint8_t addr) { boost::uint16_t reg = _ads62p44_regs.get_write_reg(addr); - _iface->write_spi( + _spiface->write_spi( SPI_SS_ADS62P44, spi_config_t::EDGE_FALL, reg, 16 ); @@ -211,6 +213,6 @@ private: /*********************************************************************** * Public make function for the usrp2 codec control **********************************************************************/ -usrp2_codec_ctrl::sptr usrp2_codec_ctrl::make(usrp2_iface::sptr iface){ - return sptr(new usrp2_codec_ctrl_impl(iface)); +usrp2_codec_ctrl::sptr usrp2_codec_ctrl::make(usrp2_iface::sptr iface, uhd::spi_iface::sptr spiface){ + return sptr(new usrp2_codec_ctrl_impl(iface, spiface)); } diff --git a/host/lib/usrp/usrp2/codec_ctrl.hpp b/host/lib/usrp/usrp2/codec_ctrl.hpp index ca300e2b1..b0d815be2 100644 --- a/host/lib/usrp/usrp2/codec_ctrl.hpp +++ b/host/lib/usrp/usrp2/codec_ctrl.hpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-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 @@ -28,10 +28,11 @@ public: /*! * Make a codec control for the DAC and ADC. - * \param _iface a pointer to the usrp2 interface object + * \param iface a pointer to the usrp2 interface object + * \param spiface the interface to spi * \return a new codec control object */ - static sptr make(usrp2_iface::sptr iface); + static sptr make(usrp2_iface::sptr iface, uhd::spi_iface::sptr spiface); /*! * Set the modulation mode for the DAC. diff --git a/host/lib/usrp/usrp2/dboard_iface.cpp b/host/lib/usrp/usrp2/dboard_iface.cpp index bc510c8a1..edd9ef242 100644 --- a/host/lib/usrp/usrp2/dboard_iface.cpp +++ b/host/lib/usrp/usrp2/dboard_iface.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-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 @@ -16,7 +16,7 @@ // #include "gpio_core_200.hpp" -#include "usrp2_iface.hpp" +#include <uhd/types/serial.hpp> #include "clock_ctrl.hpp" #include "usrp2_regs.hpp" //wishbone address constants #include <uhd/usrp/dboard_iface.hpp> @@ -35,7 +35,12 @@ using namespace boost::assign; class usrp2_dboard_iface : public dboard_iface{ public: - usrp2_dboard_iface(usrp2_iface::sptr iface, usrp2_clock_ctrl::sptr clock_ctrl); + usrp2_dboard_iface( + wb_iface::sptr wb_iface, + uhd::i2c_iface::sptr i2c_iface, + uhd::spi_iface::sptr spi_iface, + usrp2_clock_ctrl::sptr clock_ctrl + ); ~usrp2_dboard_iface(void); special_props_t get_special_props(void){ @@ -79,7 +84,8 @@ public: ); private: - usrp2_iface::sptr _iface; + uhd::i2c_iface::sptr _i2c_iface; + uhd::spi_iface::sptr _spi_iface; usrp2_clock_ctrl::sptr _clock_ctrl; gpio_core_200::sptr _gpio; @@ -92,22 +98,28 @@ private: * Make Function **********************************************************************/ dboard_iface::sptr make_usrp2_dboard_iface( - usrp2_iface::sptr iface, + wb_iface::sptr wb_iface, + uhd::i2c_iface::sptr i2c_iface, + uhd::spi_iface::sptr spi_iface, usrp2_clock_ctrl::sptr clock_ctrl ){ - return dboard_iface::sptr(new usrp2_dboard_iface(iface, clock_ctrl)); + return dboard_iface::sptr(new usrp2_dboard_iface(wb_iface, i2c_iface, spi_iface, clock_ctrl)); } /*********************************************************************** * Structors **********************************************************************/ usrp2_dboard_iface::usrp2_dboard_iface( - usrp2_iface::sptr iface, + wb_iface::sptr wb_iface, + uhd::i2c_iface::sptr i2c_iface, + uhd::spi_iface::sptr spi_iface, usrp2_clock_ctrl::sptr clock_ctrl -){ - _iface = iface; - _clock_ctrl = clock_ctrl; - _gpio = gpio_core_200::make(_iface, U2_REG_SR_ADDR(SR_GPIO), U2_REG_GPIO_RB); +): + _i2c_iface(i2c_iface), + _spi_iface(spi_iface), + _clock_ctrl(clock_ctrl) +{ + _gpio = gpio_core_200::make(wb_iface, U2_REG_SR_ADDR(SR_GPIO), U2_REG_GPIO_RB); //reset the aux dacs _dac_regs[UNIT_RX] = ad5623_regs_t(); @@ -202,7 +214,7 @@ void usrp2_dboard_iface::write_spi( boost::uint32_t data, size_t num_bits ){ - _iface->write_spi(unit_to_spi_dev[unit], config, data, num_bits); + _spi_iface->write_spi(unit_to_spi_dev[unit], config, data, num_bits); } boost::uint32_t usrp2_dboard_iface::read_write_spi( @@ -211,18 +223,18 @@ boost::uint32_t usrp2_dboard_iface::read_write_spi( boost::uint32_t data, size_t num_bits ){ - return _iface->read_spi(unit_to_spi_dev[unit], config, data, num_bits); + return _spi_iface->read_spi(unit_to_spi_dev[unit], config, data, num_bits); } /*********************************************************************** * I2C **********************************************************************/ void usrp2_dboard_iface::write_i2c(boost::uint8_t addr, const byte_vector_t &bytes){ - return _iface->write_i2c(addr, bytes); + return _i2c_iface->write_i2c(addr, bytes); } byte_vector_t usrp2_dboard_iface::read_i2c(boost::uint8_t addr, size_t num_bytes){ - return _iface->read_i2c(addr, num_bytes); + return _i2c_iface->read_i2c(addr, num_bytes); } /*********************************************************************** @@ -233,7 +245,7 @@ void usrp2_dboard_iface::_write_aux_dac(unit_t unit){ (UNIT_RX, SPI_SS_RX_DAC) (UNIT_TX, SPI_SS_TX_DAC) ; - _iface->write_spi( + _spi_iface->write_spi( unit_to_spi_dac[unit], spi_config_t::EDGE_FALL, _dac_regs[unit].get_reg(), 24 ); @@ -281,11 +293,11 @@ double usrp2_dboard_iface::read_aux_adc(unit_t unit, aux_adc_t which){ } ad7922_regs.chn = ad7922_regs.mod; //normal mode: mod == chn //write and read spi - _iface->write_spi( + _spi_iface->write_spi( unit_to_spi_adc[unit], config, ad7922_regs.get_reg(), 16 ); - ad7922_regs.set_reg(boost::uint16_t(_iface->read_spi( + ad7922_regs.set_reg(boost::uint16_t(_spi_iface->read_spi( unit_to_spi_adc[unit], config, ad7922_regs.get_reg(), 16 ))); diff --git a/host/lib/usrp/usrp2/fw_common.h b/host/lib/usrp/usrp2/fw_common.h index 0babf7445..7e3de1497 100644 --- a/host/lib/usrp/usrp2/fw_common.h +++ b/host/lib/usrp/usrp2/fw_common.h @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-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 @@ -30,13 +30,20 @@ extern "C" { #endif //fpga and firmware compatibility numbers -#define USRP2_FPGA_COMPAT_NUM 9 -#define USRP2_FW_COMPAT_NUM 11 -#define USRP2_FW_VER_MINOR 2 +#define USRP2_FPGA_COMPAT_NUM 10 +#define USRP2_FW_COMPAT_NUM 12 +#define USRP2_FW_VER_MINOR 3 //used to differentiate control packets over data port #define USRP2_INVALID_VRT_HEADER 0 +typedef struct{ + uint32_t sequence; + uint32_t vrt_hdr; + uint32_t ip_addr; + uint32_t udp_port; +} usrp2_stream_ctrl_t; + // udp ports for the usrp2 communication // Dynamic and/or private ports: 49152-65535 #define USRP2_UDP_CTRL_PORT 49152 @@ -44,12 +51,14 @@ extern "C" { #define USRP2_UDP_RX_DSP0_PORT 49156 #define USRP2_UDP_TX_DSP0_PORT 49157 #define USRP2_UDP_RX_DSP1_PORT 49158 +#define USRP2_UDP_FIFO_CRTL_PORT 49159 #define USRP2_UDP_UART_BASE_PORT 49170 #define USRP2_UDP_UART_GPS_PORT 49172 // Map for virtual firmware regs (not very big so we can keep it here for now) #define U2_FW_REG_LOCK_TIME 0 #define U2_FW_REG_LOCK_GPID 1 +#define U2_FW_REG_HAS_GPSDO 3 #define U2_FW_REG_VER_MINOR 7 //////////////////////////////////////////////////////////////////////// @@ -65,6 +74,8 @@ extern "C" { //////////////////////////////////////////////////////////////////////// #define USRP2_EE_MBOARD_REV 0x00 //2 bytes, little-endian (historic, don't blame me) #define USRP2_EE_MBOARD_MAC_ADDR 0x02 //6 bytes +#define USRP2_EE_MBOARD_GATEWAY 0x38 //uint32, big-endian +#define USRP2_EE_MBOARD_SUBNET 0x08 //uint32, big-endian #define USRP2_EE_MBOARD_IP_ADDR 0x0C //uint32, big-endian #define USRP2_EE_MBOARD_BOOTLOADER_FLAGS 0xF7 diff --git a/host/lib/usrp/usrp2/io_impl.cpp b/host/lib/usrp/usrp2/io_impl.cpp index d32ffb62c..e06cf8f6f 100644 --- a/host/lib/usrp/usrp2/io_impl.cpp +++ b/host/lib/usrp/usrp2/io_impl.cpp @@ -21,6 +21,7 @@ #include "../../transport/super_send_packet_handler.hpp" #include "usrp2_impl.hpp" #include "usrp2_regs.hpp" +#include "fw_common.h" #include <uhd/utils/log.hpp> #include <uhd/utils/msg.hpp> #include <uhd/utils/tasks.hpp> @@ -31,6 +32,7 @@ #include <boost/thread/thread.hpp> #include <boost/format.hpp> #include <boost/bind.hpp> +#include <boost/asio.hpp> #include <boost/thread/mutex.hpp> #include <boost/make_shared.hpp> #include <iostream> @@ -361,6 +363,61 @@ bool usrp2_impl::recv_async_msg( } /*********************************************************************** + * Stream destination programmer + **********************************************************************/ +void usrp2_impl::program_stream_dest( + zero_copy_if::sptr &xport, const uhd::stream_args_t &args +){ + //perform an initial flush of transport + while (xport->get_recv_buff(0.0)){} + + //program the stream command + usrp2_stream_ctrl_t stream_ctrl = usrp2_stream_ctrl_t(); + stream_ctrl.sequence = uhd::htonx(boost::uint32_t(0 /* don't care seq num */)); + stream_ctrl.vrt_hdr = uhd::htonx(boost::uint32_t(USRP2_INVALID_VRT_HEADER)); + + //user has provided an alternative address and port for destination + if (args.args.has_key("addr") and args.args.has_key("port")){ + UHD_MSG(status) << boost::format( + "Programming streaming destination for custom address.\n" + "IPv4 Address: %s, UDP Port: %s\n" + ) % args.args["addr"] % args.args["port"] << std::endl; + + asio::io_service io_service; + asio::ip::udp::resolver resolver(io_service); + asio::ip::udp::resolver::query query(asio::ip::udp::v4(), args.args["addr"], args.args["port"]); + asio::ip::udp::endpoint endpoint = *resolver.resolve(query); + stream_ctrl.ip_addr = uhd::htonx(boost::uint32_t(endpoint.address().to_v4().to_ulong())); + stream_ctrl.udp_port = uhd::htonx(boost::uint32_t(endpoint.port())); + + for (size_t i = 0; i < 3; i++){ + UHD_MSG(status) << "ARP attempt " << i << std::endl; + managed_send_buffer::sptr send_buff = xport->get_send_buff(); + std::memcpy(send_buff->cast<void *>(), &stream_ctrl, sizeof(stream_ctrl)); + send_buff->commit(sizeof(stream_ctrl)); + send_buff.reset(); + boost::this_thread::sleep(boost::posix_time::milliseconds(300)); + managed_recv_buffer::sptr recv_buff = xport->get_recv_buff(0.0); + if (recv_buff and recv_buff->size() >= sizeof(boost::uint32_t)){ + const boost::uint32_t result = uhd::ntohx(recv_buff->cast<const boost::uint32_t *>()[0]); + if (result == 0){ + UHD_MSG(status) << "Success! " << std::endl; + return; + } + } + } + throw uhd::runtime_error("Device failed to ARP when programming alternative streaming destination."); + } + + else{ + //send the partial stream control without destination + managed_send_buffer::sptr send_buff = xport->get_send_buff(); + std::memcpy(send_buff->cast<void *>(), &stream_ctrl, sizeof(stream_ctrl)); + send_buff->commit(sizeof(stream_ctrl)/2); + } +} + +/*********************************************************************** * Receive streamer **********************************************************************/ rx_streamer::sptr usrp2_impl::get_rx_stream(const uhd::stream_args_t &args_){ @@ -406,6 +463,7 @@ rx_streamer::sptr usrp2_impl::get_rx_stream(const uhd::stream_args_t &args_){ const size_t dsp = chan + _mbc[mb].rx_chan_occ - num_chan_so_far; _mbc[mb].rx_dsps[dsp]->set_nsamps_per_packet(spp); //seems to be a good place to set this _mbc[mb].rx_dsps[dsp]->setup(args); + this->program_stream_dest(_mbc[mb].rx_dsp_xports[dsp], args); my_streamer->set_xport_chan_get_buff(chan_i, boost::bind( &zero_copy_if::get_recv_buff, _mbc[mb].rx_dsp_xports[dsp], _1 ), true /*flush*/); diff --git a/host/lib/usrp/usrp2/usrp2_fifo_ctrl.cpp b/host/lib/usrp/usrp2/usrp2_fifo_ctrl.cpp new file mode 100644 index 000000000..efb88eb82 --- /dev/null +++ b/host/lib/usrp/usrp2/usrp2_fifo_ctrl.cpp @@ -0,0 +1,244 @@ +// +// 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 "usrp2_regs.hpp" +#include <uhd/exception.hpp> +#include <uhd/utils/msg.hpp> +#include <uhd/utils/safe_call.hpp> +#include <uhd/transport/vrt_if_packet.hpp> +#include "usrp2_fifo_ctrl.hpp" +#include <boost/thread/mutex.hpp> +#include <boost/thread/thread.hpp> +#include <boost/asio.hpp> //htonl +#include <boost/format.hpp> + +using namespace uhd; +using namespace uhd::transport; + +static const size_t POKE32_CMD = (1 << 8); +static const size_t PEEK32_CMD = 0; +static const double ACK_TIMEOUT = 0.5; +static const double MASSIVE_TIMEOUT = 10.0; //for when we wait on a timed command +static const boost::uint32_t MAX_SEQS_OUT = 15; + +#define SPI_DIV SR_SPI_CORE + 0 +#define SPI_CTRL SR_SPI_CORE + 1 +#define SPI_DATA SR_SPI_CORE + 2 +#define SPI_READBACK 0 +// spi clock rate = master_clock/(div+1)/2 (10MHz in this case) +#define SPI_DIVIDER 4 + +class usrp2_fifo_ctrl_impl : public usrp2_fifo_ctrl{ +public: + + usrp2_fifo_ctrl_impl(zero_copy_if::sptr xport): + _xport(xport), + _seq_out(0), + _seq_ack(0), + _timeout(ACK_TIMEOUT) + { + while (_xport->get_recv_buff(0.0)){} //flush + this->set_time(uhd::time_spec_t(0.0)); + this->set_tick_rate(1.0); //something possible but bogus + this->init_spi(); + } + + ~usrp2_fifo_ctrl_impl(void){ + _timeout = ACK_TIMEOUT; //reset timeout to something small + UHD_SAFE_CALL( + this->peek32(0); //dummy peek with the purpose of ack'ing all packets + ) + } + + /******************************************************************* + * Peek and poke 32 bit implementation + ******************************************************************/ + void poke32(wb_addr_type addr, boost::uint32_t data){ + boost::mutex::scoped_lock lock(_mutex); + + this->send_pkt((addr - SETTING_REGS_BASE)/4, data, POKE32_CMD); + + this->wait_for_ack(_seq_out-MAX_SEQS_OUT); + } + + boost::uint32_t peek32(wb_addr_type addr){ + boost::mutex::scoped_lock lock(_mutex); + + this->send_pkt((addr - READBACK_BASE)/4, 0, PEEK32_CMD); + + return this->wait_for_ack(_seq_out); + } + + /******************************************************************* + * Peek and poke 16 bit not implemented + ******************************************************************/ + void poke16(wb_addr_type, boost::uint16_t){ + throw uhd::not_implemented_error("poke16 not implemented in fifo ctrl module"); + } + + boost::uint16_t peek16(wb_addr_type){ + throw uhd::not_implemented_error("peek16 not implemented in fifo ctrl module"); + } + + /******************************************************************* + * FIFO controlled SPI implementation + ******************************************************************/ + void init_spi(void){ + boost::mutex::scoped_lock lock(_mutex); + + this->send_pkt(SPI_DIV, SPI_DIVIDER, POKE32_CMD); + this->wait_for_ack(_seq_out-MAX_SEQS_OUT); + + _ctrl_word_cache = 0; // force update first time around + } + + boost::uint32_t transact_spi( + int which_slave, + const spi_config_t &config, + boost::uint32_t data, + size_t num_bits, + bool readback + ){ + boost::mutex::scoped_lock lock(_mutex); + + //load control word + boost::uint32_t ctrl_word = 0; + ctrl_word |= ((which_slave & 0xffffff) << 0); + ctrl_word |= ((num_bits & 0x3ff) << 24); + if (config.mosi_edge == spi_config_t::EDGE_FALL) ctrl_word |= (1 << 31); + if (config.miso_edge == spi_config_t::EDGE_RISE) ctrl_word |= (1 << 30); + + //load data word (must be in upper bits) + const boost::uint32_t data_out = data << (32 - num_bits); + + //conditionally send control word + if (_ctrl_word_cache != ctrl_word){ + this->send_pkt(SPI_CTRL, ctrl_word, POKE32_CMD); + this->wait_for_ack(_seq_out-MAX_SEQS_OUT); + _ctrl_word_cache = ctrl_word; + } + + //send data word + this->send_pkt(SPI_DATA, data_out, POKE32_CMD); + this->wait_for_ack(_seq_out-MAX_SEQS_OUT); + + //conditional readback + if (readback){ + this->send_pkt(SPI_READBACK, 0, PEEK32_CMD); + return this->wait_for_ack(_seq_out); + } + + return 0; + } + + /******************************************************************* + * Update methods for time + ******************************************************************/ + void set_time(const uhd::time_spec_t &time){ + boost::mutex::scoped_lock lock(_mutex); + _time = time; + _use_time = _time != uhd::time_spec_t(0.0); + if (_use_time) _timeout = MASSIVE_TIMEOUT; //permanently sets larger timeout + } + + void set_tick_rate(const double rate){ + boost::mutex::scoped_lock lock(_mutex); + _tick_rate = rate; + } + +private: + + /******************************************************************* + * Primary control and interaction private methods + ******************************************************************/ + UHD_INLINE void send_pkt(wb_addr_type addr, boost::uint32_t data, int cmd){ + managed_send_buffer::sptr buff = _xport->get_send_buff(0.0); + if (not buff){ + throw uhd::runtime_error("fifo ctrl timed out getting a send buffer"); + } + boost::uint32_t *trans = buff->cast<boost::uint32_t *>(); + trans[0] = htonl(++_seq_out); + boost::uint32_t *pkt = trans + 1; + + //load packet info + vrt::if_packet_info_t packet_info; + packet_info.packet_type = vrt::if_packet_info_t::PACKET_TYPE_CONTEXT; + packet_info.num_payload_words32 = 2; + packet_info.num_payload_bytes = packet_info.num_payload_words32*sizeof(boost::uint32_t); + packet_info.packet_count = _seq_out; + packet_info.tsf = _time.to_ticks(_tick_rate); + packet_info.sob = false; + packet_info.eob = false; + packet_info.has_sid = false; + packet_info.has_cid = false; + packet_info.has_tsi = false; + packet_info.has_tsf = _use_time; + packet_info.has_tlr = false; + + //load header + vrt::if_hdr_pack_be(pkt, packet_info); + + //load payload + const boost::uint32_t ctrl_word = (addr & 0xff) | cmd | (_seq_out << 16); + pkt[packet_info.num_header_words32+0] = htonl(ctrl_word); + pkt[packet_info.num_header_words32+1] = htonl(data); + + //send the buffer over the interface + buff->commit(sizeof(boost::uint32_t)*(packet_info.num_packet_words32+1)); + } + + UHD_INLINE bool wraparound_lt16(const boost::int16_t i0, const boost::int16_t i1){ + if (((i0 ^ i1) & 0x8000) == 0) //same sign bits + return boost::uint16_t(i0) < boost::uint16_t(i1); + return boost::int16_t(i1 - i0) > 0; + } + + UHD_INLINE boost::uint32_t wait_for_ack(const boost::uint16_t seq_to_ack){ + + while (wraparound_lt16(_seq_ack, seq_to_ack)){ + managed_recv_buffer::sptr buff = _xport->get_recv_buff(_timeout); + if (not buff){ + throw uhd::runtime_error("fifo ctrl timed out looking for acks"); + } + const boost::uint32_t *pkt = buff->cast<const boost::uint32_t *>(); + vrt::if_packet_info_t packet_info; + packet_info.num_packet_words32 = buff->size()/sizeof(boost::uint32_t); + vrt::if_hdr_unpack_be(pkt, packet_info); + _seq_ack = ntohl(pkt[packet_info.num_header_words32+0]) >> 16; + if (_seq_ack == seq_to_ack){ + return ntohl(pkt[packet_info.num_header_words32+1]); + } + } + + return 0; + } + + zero_copy_if::sptr _xport; + boost::mutex _mutex; + boost::uint16_t _seq_out; + boost::uint16_t _seq_ack; + uhd::time_spec_t _time; + bool _use_time; + double _tick_rate; + double _timeout; + boost::uint32_t _ctrl_word_cache; +}; + + +usrp2_fifo_ctrl::sptr usrp2_fifo_ctrl::make(zero_copy_if::sptr xport){ + return sptr(new usrp2_fifo_ctrl_impl(xport)); +} diff --git a/host/lib/usrp/usrp2/usrp2_fifo_ctrl.hpp b/host/lib/usrp/usrp2/usrp2_fifo_ctrl.hpp new file mode 100644 index 000000000..b48d05aa2 --- /dev/null +++ b/host/lib/usrp/usrp2/usrp2_fifo_ctrl.hpp @@ -0,0 +1,47 @@ +// +// 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/>. +// + +#ifndef INCLUDED_USRP2_FIFO_CTRL_HPP +#define INCLUDED_USRP2_FIFO_CTRL_HPP + +#include <uhd/types/time_spec.hpp> +#include <uhd/types/serial.hpp> +#include <uhd/transport/zero_copy.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/utility.hpp> +#include "wb_iface.hpp" +#include <string> + +/*! + * The usrp2 FIFO control class: + * Provide high-speed peek/poke interface. + */ +class usrp2_fifo_ctrl : public wb_iface, public uhd::spi_iface{ +public: + typedef boost::shared_ptr<usrp2_fifo_ctrl> sptr; + + //! Make a new FIFO control object + static sptr make(uhd::transport::zero_copy_if::sptr xport); + + //! Set the command time that will activate + virtual void set_time(const uhd::time_spec_t &time) = 0; + + //! Set the tick rate (converting time into ticks) + virtual void set_tick_rate(const double rate) = 0; +}; + +#endif /* INCLUDED_USRP2_FIFO_CTRL_HPP */ diff --git a/host/lib/usrp/usrp2/usrp2_iface.cpp b/host/lib/usrp/usrp2/usrp2_iface.cpp index 123910166..8804433e7 100644 --- a/host/lib/usrp/usrp2/usrp2_iface.cpp +++ b/host/lib/usrp/usrp2/usrp2_iface.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-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 @@ -16,11 +16,13 @@ // #include "usrp2_regs.hpp" +#include "usrp2_impl.hpp" #include "fw_common.h" #include "usrp2_iface.hpp" #include <uhd/exception.hpp> #include <uhd/utils/msg.hpp> #include <uhd/utils/tasks.hpp> +#include <uhd/utils/images.hpp> #include <uhd/utils/safe_call.hpp> #include <uhd/types/dict.hpp> #include <boost/thread.hpp> @@ -31,12 +33,14 @@ #include <boost/bind.hpp> #include <boost/tokenizer.hpp> #include <boost/functional/hash.hpp> +#include <boost/filesystem.hpp> #include <algorithm> #include <iostream> using namespace uhd; using namespace uhd::usrp; using namespace uhd::transport; +namespace fs = boost::filesystem; static const double CTRL_RECV_TIMEOUT = 1.0; static const size_t CTRL_RECV_RETRIES = 3; @@ -105,7 +109,7 @@ public: throw uhd::runtime_error("firmware not responding"); _protocol_compat = ntohl(ctrl_data.proto_ver); - mb_eeprom = mboard_eeprom_t(*this, mboard_eeprom_t::MAP_N100); + mb_eeprom = mboard_eeprom_t(*this, USRP2_EEPROM_MAP_KEY); } ~usrp2_iface_impl(void){UHD_SAFE_CALL( @@ -118,12 +122,12 @@ public: void lock_device(bool lock){ if (lock){ - this->get_reg<boost::uint32_t, USRP2_REG_ACTION_FW_POKE32>(U2_FW_REG_LOCK_GPID, boost::uint32_t(get_gpid())); + this->pokefw(U2_FW_REG_LOCK_GPID, boost::uint32_t(get_gpid())); _lock_task = task::make(boost::bind(&usrp2_iface_impl::lock_task, this)); } else{ _lock_task.reset(); //shutdown the task - this->get_reg<boost::uint32_t, USRP2_REG_ACTION_FW_POKE32>(U2_FW_REG_LOCK_TIME, 0); //unlock + this->pokefw(U2_FW_REG_LOCK_TIME, 0); //unlock } } @@ -131,8 +135,8 @@ public: //never assume lock with fpga image mismatch if ((this->peek32(U2_REG_COMPAT_NUM_RB) >> 16) != USRP2_FPGA_COMPAT_NUM) return false; - boost::uint32_t lock_time = this->get_reg<boost::uint32_t, USRP2_REG_ACTION_FW_PEEK32>(U2_FW_REG_LOCK_TIME); - boost::uint32_t lock_gpid = this->get_reg<boost::uint32_t, USRP2_REG_ACTION_FW_PEEK32>(U2_FW_REG_LOCK_GPID); + boost::uint32_t lock_time = this->peekfw(U2_FW_REG_LOCK_TIME); + boost::uint32_t lock_gpid = this->peekfw(U2_FW_REG_LOCK_GPID); //may not be the right tick rate, but this is ok for locking purposes const boost::uint32_t lock_timeout_time = boost::uint32_t(3*100e6); @@ -148,7 +152,7 @@ public: void lock_task(void){ //re-lock in task - this->get_reg<boost::uint32_t, USRP2_REG_ACTION_FW_POKE32>(U2_FW_REG_LOCK_TIME, this->get_curr_time()); + this->pokefw(U2_FW_REG_LOCK_TIME, this->get_curr_time()); //sleep for a bit boost::this_thread::sleep(boost::posix_time::milliseconds(1500)); } @@ -176,6 +180,16 @@ public: return this->get_reg<boost::uint16_t, USRP2_REG_ACTION_FPGA_PEEK16>(addr); } + void pokefw(wb_addr_type addr, boost::uint32_t data) + { + this->get_reg<boost::uint32_t, USRP2_REG_ACTION_FW_POKE32>(addr, data); + } + + boost::uint32_t peekfw(wb_addr_type addr) + { + return this->get_reg<boost::uint32_t, USRP2_REG_ACTION_FW_PEEK32>(addr); + } + template <class T, usrp2_reg_action_t action> T get_reg(wb_addr_type addr, T data = 0){ //setup the out data @@ -311,8 +325,10 @@ public: "\nPlease update the firmware and FPGA images for your device.\n" "See the application notes for USRP2/N-Series for instructions.\n" "Expected protocol compatibility number %s, but got %d:\n" - "The firmware build is not compatible with the host code build." - ) % ((lo == hi)? (boost::format("%d") % hi) : (boost::format("[%d to %d]") % lo % hi)) % compat)); + "The firmware build is not compatible with the host code build.\n" + "%s\n" + ) % ((lo == hi)? (boost::format("%d") % hi) : (boost::format("[%d to %d]") % lo % hi)) + % compat % this->images_warn_help_message())); } if (len >= sizeof(usrp2_ctrl_data_t) and ntohl(ctrl_data_in->seq) == _ctrl_seq_num){ return *ctrl_data_in; @@ -340,13 +356,13 @@ public: const std::string get_cname(void){ switch(this->get_rev()){ - case USRP2_REV3: return "USRP2-REV3"; - case USRP2_REV4: return "USRP2-REV4"; - case USRP_N200: return "USRP-N200"; - case USRP_N210: return "USRP-N210"; - case USRP_N200_R4: return "USRP-N200-REV4"; - case USRP_N210_R4: return "USRP-N210-REV4"; - case USRP_NXXX: return "USRP-N???"; + case USRP2_REV3: return "USRP2 r3"; + case USRP2_REV4: return "USRP2 r4"; + case USRP_N200: return "N200"; + case USRP_N210: return "N210"; + case USRP_N200_R4: return "N200r4"; + case USRP_N210_R4: return "N210r4"; + case USRP_NXXX: return "N???"; } UHD_THROW_INVALID_CODE_PATH(); } @@ -356,6 +372,58 @@ public: return str(boost::format("%u.%u") % _protocol_compat % minor); } + std::string images_warn_help_message(void){ + //determine the images names + std::string fw_image, fpga_image; + switch(this->get_rev()){ + case USRP2_REV3: fpga_image = "usrp2_fpga.bin"; fw_image = "usrp2_fw.bin"; break; + case USRP2_REV4: fpga_image = "usrp2_fpga.bin"; fw_image = "usrp2_fw.bin"; break; + case USRP_N200: fpga_image = "usrp_n200_r2_fpga.bin"; fw_image = "usrp_n200_fw.bin"; break; + case USRP_N210: fpga_image = "usrp_n210_r2_fpga.bin"; fw_image = "usrp_n210_fw.bin"; break; + case USRP_N200_R4: fpga_image = "usrp_n200_r4_fpga.bin"; fw_image = "usrp_n200_fw.bin"; break; + case USRP_N210_R4: fpga_image = "usrp_n210_r4_fpga.bin"; fw_image = "usrp_n210_fw.bin"; break; + default: break; + } + if (fw_image.empty() or fpga_image.empty()) return ""; + + //does your platform use sudo? + std::string sudo; + #if defined(UHD_PLATFORM_LINUX) || defined(UHD_PLATFORM_MACOS) + sudo = "sudo "; + #endif + + + //look up the real FS path to the images + std::string fw_image_path, fpga_image_path; + try{ + fw_image_path = uhd::find_image_path(fw_image); + fpga_image_path = uhd::find_image_path(fpga_image); + } + catch(const std::exception &){ + return str(boost::format("Could not find %s and %s in your images path!\n%s") % fw_image % fpga_image % print_images_error()); + } + + //escape char for multi-line cmd + newline + indent? + #ifdef UHD_PLATFORM_WIN32 + const std::string ml = "^\n "; + #else + const std::string ml = "\\\n "; + #endif + + //create the burner commands + if (this->get_rev() == USRP2_REV3 or this->get_rev() == USRP2_REV4){ + const std::string card_burner = (fs::path(fw_image_path).branch_path().branch_path() / "utils" / "usrp2_card_burner.py").string(); + const std::string card_burner_cmd = str(boost::format("\"%s%s\" %s--fpga=\"%s\" %s--fw=\"%s\"") % sudo % card_burner % ml % fpga_image_path % ml % fw_image_path); + return str(boost::format("%s\n%s") % print_images_error() % card_burner_cmd); + } + else{ + const std::string addr = _ctrl_transport->get_recv_addr(); + const std::string net_burner_path = (fs::path(fw_image_path).branch_path().branch_path() / "utils" / "usrp_n2xx_simple_net_burner").string(); + const std::string net_burner_cmd = str(boost::format("\"%s\" %s--addr=\"%s\"") % net_burner_path % ml % addr); + return str(boost::format("%s\n%s") % print_images_error() % net_burner_cmd); + } + } + private: //this lovely lady makes it all possible udp_simple::sptr _ctrl_transport; diff --git a/host/lib/usrp/usrp2/usrp2_iface.hpp b/host/lib/usrp/usrp2/usrp2_iface.hpp index 9aa1a16aa..ed4de02d5 100644 --- a/host/lib/usrp/usrp2/usrp2_iface.hpp +++ b/host/lib/usrp/usrp2/usrp2_iface.hpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-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 @@ -43,6 +43,12 @@ public: */ static sptr make(uhd::transport::udp_simple::sptr ctrl_transport); + //! poke a register in the virtual fw table + virtual void pokefw(wb_addr_type addr, boost::uint32_t data) = 0; + + //! peek a register in the virtual fw table + virtual boost::uint32_t peekfw(wb_addr_type addr) = 0; + //! The list of possible revision types enum rev_type { USRP2_REV3 = 3, @@ -69,6 +75,9 @@ public: //! A version string for firmware virtual const std::string get_fw_version_string(void) = 0; + //! Construct a helpful warning message for images + virtual std::string images_warn_help_message(void) = 0; + //motherboard eeprom map structure uhd::usrp::mboard_eeprom_t mb_eeprom; }; diff --git a/host/lib/usrp/usrp2/usrp2_impl.cpp b/host/lib/usrp/usrp2/usrp2_impl.cpp index e6e8ca675..21f166aa1 100644 --- a/host/lib/usrp/usrp2/usrp2_impl.cpp +++ b/host/lib/usrp/usrp2/usrp2_impl.cpp @@ -368,8 +368,9 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr){ "\nPlease update the firmware and FPGA images for your device.\n" "See the application notes for USRP2/N-Series for instructions.\n" "Expected FPGA compatibility number %d, but got %d:\n" - "The FPGA build is not compatible with the host code build." - ) % int(USRP2_FPGA_COMPAT_NUM) % fpga_major)); + "The FPGA build is not compatible with the host code build.\n" + "%s\n" + ) % int(USRP2_FPGA_COMPAT_NUM) % fpga_major % _mbc[mb].iface->images_warn_help_message())); } _tree->create<std::string>(mb_path / "fpga_version").set(str(boost::format("%u.%u") % fpga_major % fpga_minor)); @@ -391,8 +392,28 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr){ _mbc[mb].tx_dsp_xport = make_xport( addr, BOOST_STRINGIZE(USRP2_UDP_TX_DSP0_PORT), device_args_i, "send" ); + UHD_LOG << "Making transport for Control..." << std::endl; + _mbc[mb].fifo_ctrl_xport = make_xport( + addr, BOOST_STRINGIZE(USRP2_UDP_FIFO_CRTL_PORT), device_addr_t(), "" + ); //set the filter on the router to take dsp data from this port - _mbc[mb].iface->poke32(U2_REG_ROUTER_CTRL_PORTS, USRP2_UDP_TX_DSP0_PORT); + _mbc[mb].iface->poke32(U2_REG_ROUTER_CTRL_PORTS, (USRP2_UDP_FIFO_CRTL_PORT << 16) | USRP2_UDP_TX_DSP0_PORT); + + //create the fifo control interface for high speed register access + _mbc[mb].fifo_ctrl = usrp2_fifo_ctrl::make(_mbc[mb].fifo_ctrl_xport); + switch(_mbc[mb].iface->get_rev()){ + case usrp2_iface::USRP_N200: + case usrp2_iface::USRP_N210: + case usrp2_iface::USRP_N200_R4: + case usrp2_iface::USRP_N210_R4: + _mbc[mb].wbiface = _mbc[mb].fifo_ctrl; + _mbc[mb].spiface = _mbc[mb].fifo_ctrl; + break; + default: + _mbc[mb].wbiface = _mbc[mb].iface; + _mbc[mb].spiface = _mbc[mb].iface; + break; + } //////////////////////////////////////////////////////////////// // setup the mboard eeprom @@ -404,7 +425,7 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr){ //////////////////////////////////////////////////////////////// // create clock control objects //////////////////////////////////////////////////////////////// - _mbc[mb].clock = usrp2_clock_ctrl::make(_mbc[mb].iface); + _mbc[mb].clock = usrp2_clock_ctrl::make(_mbc[mb].iface, _mbc[mb].spiface); _tree->create<double>(mb_path / "tick_rate") .publish(boost::bind(&usrp2_clock_ctrl::get_master_clock_rate, _mbc[mb].clock)) .subscribe(boost::bind(&usrp2_impl::update_tick_rate, this, _1)); @@ -416,7 +437,7 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr){ const fs_path tx_codec_path = mb_path / "tx_codecs/A"; _tree->create<int>(rx_codec_path / "gains"); //phony property so this dir exists _tree->create<int>(tx_codec_path / "gains"); //phony property so this dir exists - _mbc[mb].codec = usrp2_codec_ctrl::make(_mbc[mb].iface); + _mbc[mb].codec = usrp2_codec_ctrl::make(_mbc[mb].iface, _mbc[mb].spiface); switch(_mbc[mb].iface->get_rev()){ case usrp2_iface::USRP_N200: case usrp2_iface::USRP_N210: @@ -442,19 +463,48 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr){ } _tree->create<std::string>(tx_codec_path / "name").set("ad9777"); - //////////////////////////////////////////////////////////////// - // create gpsdo control objects - //////////////////////////////////////////////////////////////// - if (_mbc[mb].iface->mb_eeprom["gpsdo"] == "internal"){ - _mbc[mb].gps = gps_ctrl::make(udp_simple::make_uart(udp_simple::make_connected( - addr, BOOST_STRINGIZE(USRP2_UDP_UART_GPS_PORT) - ))); - if(_mbc[mb].gps->gps_detected()) { - BOOST_FOREACH(const std::string &name, _mbc[mb].gps->get_sensors()){ + //////////////////////////////////////////////////////////////////// + // Create the GPSDO control + //////////////////////////////////////////////////////////////////// + static const boost::uint32_t dont_look_for_gpsdo = 0x1234abcdul; + + //disable check for internal GPSDO when not the following: + switch(_mbc[mb].iface->get_rev()){ + case usrp2_iface::USRP_N200: + case usrp2_iface::USRP_N210: + case usrp2_iface::USRP_N200_R4: + case usrp2_iface::USRP_N210_R4: + break; + default: + _mbc[mb].iface->pokefw(U2_FW_REG_HAS_GPSDO, dont_look_for_gpsdo); + } + + //otherwise if not disabled, look for the internal GPSDO + if (_mbc[mb].iface->peekfw(U2_FW_REG_HAS_GPSDO) != dont_look_for_gpsdo) + { + UHD_MSG(status) << "Detecting internal GPSDO.... " << std::flush; + try{ + _mbc[mb].gps = gps_ctrl::make(udp_simple::make_uart(udp_simple::make_connected( + addr, BOOST_STRINGIZE(USRP2_UDP_UART_GPS_PORT) + ))); + } + catch(std::exception &e){ + UHD_MSG(error) << "An error occurred making GPSDO control: " << e.what() << std::endl; + } + if (_mbc[mb].gps and _mbc[mb].gps->gps_detected()) + { + UHD_MSG(status) << "found" << std::endl; + BOOST_FOREACH(const std::string &name, _mbc[mb].gps->get_sensors()) + { _tree->create<sensor_value_t>(mb_path / "sensors" / name) .publish(boost::bind(&gps_ctrl::get_sensor, _mbc[mb].gps, name)); } } + else + { + UHD_MSG(status) << "not found" << std::endl; + _mbc[mb].iface->pokefw(U2_FW_REG_HAS_GPSDO, dont_look_for_gpsdo); + } } //////////////////////////////////////////////////////////////// @@ -469,10 +519,10 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr){ // create frontend control objects //////////////////////////////////////////////////////////////// _mbc[mb].rx_fe = rx_frontend_core_200::make( - _mbc[mb].iface, U2_REG_SR_ADDR(SR_RX_FRONT) + _mbc[mb].wbiface, U2_REG_SR_ADDR(SR_RX_FRONT) ); _mbc[mb].tx_fe = tx_frontend_core_200::make( - _mbc[mb].iface, U2_REG_SR_ADDR(SR_TX_FRONT) + _mbc[mb].wbiface, U2_REG_SR_ADDR(SR_TX_FRONT) ); _tree->create<subdev_spec_t>(mb_path / "rx_subdev_spec") @@ -503,10 +553,10 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr){ // create rx dsp control objects //////////////////////////////////////////////////////////////// _mbc[mb].rx_dsps.push_back(rx_dsp_core_200::make( - _mbc[mb].iface, U2_REG_SR_ADDR(SR_RX_DSP0), U2_REG_SR_ADDR(SR_RX_CTRL0), USRP2_RX_SID_BASE + 0, true + _mbc[mb].wbiface, U2_REG_SR_ADDR(SR_RX_DSP0), U2_REG_SR_ADDR(SR_RX_CTRL0), USRP2_RX_SID_BASE + 0, true )); _mbc[mb].rx_dsps.push_back(rx_dsp_core_200::make( - _mbc[mb].iface, U2_REG_SR_ADDR(SR_RX_DSP1), U2_REG_SR_ADDR(SR_RX_CTRL1), USRP2_RX_SID_BASE + 1, true + _mbc[mb].wbiface, U2_REG_SR_ADDR(SR_RX_DSP1), U2_REG_SR_ADDR(SR_RX_CTRL1), USRP2_RX_SID_BASE + 1, true )); for (size_t dspno = 0; dspno < _mbc[mb].rx_dsps.size(); dspno++){ _mbc[mb].rx_dsps[dspno]->set_link_rate(USRP2_LINK_RATE_BPS); @@ -531,7 +581,7 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr){ // create tx dsp control objects //////////////////////////////////////////////////////////////// _mbc[mb].tx_dsp = tx_dsp_core_200::make( - _mbc[mb].iface, U2_REG_SR_ADDR(SR_TX_DSP), U2_REG_SR_ADDR(SR_TX_CTRL), USRP2_TX_ASYNC_SID + _mbc[mb].wbiface, U2_REG_SR_ADDR(SR_TX_DSP), U2_REG_SR_ADDR(SR_TX_CTRL), USRP2_TX_ASYNC_SID ); _mbc[mb].tx_dsp->set_link_rate(USRP2_LINK_RATE_BPS); _tree->access<double>(mb_path / "tick_rate") @@ -565,7 +615,7 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr){ time64_rb_bases.rb_hi_pps = U2_REG_TIME64_HI_RB_PPS; time64_rb_bases.rb_lo_pps = U2_REG_TIME64_LO_RB_PPS; _mbc[mb].time64 = time64_core_200::make( - _mbc[mb].iface, U2_REG_SR_ADDR(SR_TIME64), time64_rb_bases, mimo_clock_sync_delay_cycles + _mbc[mb].wbiface, U2_REG_SR_ADDR(SR_TIME64), time64_rb_bases, mimo_clock_sync_delay_cycles ); _tree->access<double>(mb_path / "tick_rate") .subscribe(boost::bind(&time64_core_200::set_tick_rate, _mbc[mb].time64, _1)); @@ -583,13 +633,26 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr){ //setup reference source props _tree->create<std::string>(mb_path / "clock_source/value") .subscribe(boost::bind(&usrp2_impl::update_clock_source, this, mb, _1)); - static const std::vector<std::string> clock_sources = boost::assign::list_of("internal")("external")("mimo"); + 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); + //plug timed commands into tree here + switch(_mbc[mb].iface->get_rev()){ + case usrp2_iface::USRP_N200: + case usrp2_iface::USRP_N210: + case usrp2_iface::USRP_N200_R4: + case usrp2_iface::USRP_N210_R4: + _tree->create<time_spec_t>(mb_path / "time/cmd") + .subscribe(boost::bind(&usrp2_fifo_ctrl::set_time, _mbc[mb].fifo_ctrl, _1)); + default: break; //otherwise, do not register + } + _tree->access<double>(mb_path / "tick_rate") + .subscribe(boost::bind(&usrp2_fifo_ctrl::set_tick_rate, _mbc[mb].fifo_ctrl, _1)); //////////////////////////////////////////////////////////////////// // create user-defined control objects //////////////////////////////////////////////////////////////////// - _mbc[mb].user = user_settings_core_200::make(_mbc[mb].iface, U2_REG_SR_ADDR(SR_USER_REGS)); + _mbc[mb].user = user_settings_core_200::make(_mbc[mb].wbiface, U2_REG_SR_ADDR(SR_USER_REGS)); _tree->create<user_settings_core_200::user_reg_t>(mb_path / "user/regs") .subscribe(boost::bind(&user_settings_core_200::set_reg, _mbc[mb].user, _1)); @@ -603,6 +666,9 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr){ tx_db_eeprom.load(*_mbc[mb].iface, USRP2_I2C_ADDR_TX_DB); gdb_eeprom.load(*_mbc[mb].iface, USRP2_I2C_ADDR_TX_DB ^ 5); + //disable rx dc offset if LFRX + if (rx_db_eeprom.id == 0x000f) _tree->access<bool>(rx_fe_path / "dc_offset" / "enable").set(false); + //create the properties and register subscribers _tree->create<dboard_eeprom_t>(mb_path / "dboards/A/rx_eeprom") .set(rx_db_eeprom) @@ -615,7 +681,7 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr){ .subscribe(boost::bind(&usrp2_impl::set_db_eeprom, this, mb, "gdb", _1)); //create a new dboard interface and manager - _mbc[mb].dboard_iface = make_usrp2_dboard_iface(_mbc[mb].iface, _mbc[mb].clock); + _mbc[mb].dboard_iface = make_usrp2_dboard_iface(_mbc[mb].wbiface, _mbc[mb].iface/*i2c*/, _mbc[mb].spiface, _mbc[mb].clock); _tree->create<dboard_iface::sptr>(mb_path / "dboards/A/iface").set(_mbc[mb].dboard_iface); _mbc[mb].dboard_manager = dboard_manager::make( rx_db_eeprom.id, tx_db_eeprom.id, gdb_eeprom.id, @@ -657,10 +723,11 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr){ _tree->access<std::string>(root / "time_source/value").set("none"); //GPS installed: use external ref, time, and init time spec - if (_mbc[mb].gps.get() and _mbc[mb].gps->gps_detected()){ + if (_mbc[mb].gps and _mbc[mb].gps->gps_detected()){ + _mbc[mb].time64->enable_gpsdo(); UHD_MSG(status) << "Setting references to the internal GPSDO" << std::endl; - _tree->access<std::string>(root / "time_source/value").set("external"); - _tree->access<std::string>(root / "clock_source/value").set("external"); + _tree->access<std::string>(root / "time_source/value").set("gpsdo"); + _tree->access<std::string>(root / "clock_source/value").set("gpsdo"); UHD_MSG(status) << "Initializing time to the internal GPSDO" << std::endl; _mbc[mb].time64->set_time_next_pps(time_spec_t(time_t(_mbc[mb].gps->get_sensor("gps_time").to_int()+1))); } @@ -675,7 +742,7 @@ usrp2_impl::~usrp2_impl(void){UHD_SAFE_CALL( )} void usrp2_impl::set_mb_eeprom(const std::string &mb, const uhd::usrp::mboard_eeprom_t &mb_eeprom){ - mb_eeprom.commit(*(_mbc[mb].iface), mboard_eeprom_t::MAP_N100); + mb_eeprom.commit(*(_mbc[mb].iface), USRP2_EEPROM_MAP_KEY); } void usrp2_impl::set_db_eeprom(const std::string &mb, const std::string &type, const uhd::usrp::dboard_eeprom_t &db_eeprom){ @@ -685,12 +752,12 @@ void usrp2_impl::set_db_eeprom(const std::string &mb, const std::string &type, c } sensor_value_t usrp2_impl::get_mimo_locked(const std::string &mb){ - const bool lock = (_mbc[mb].iface->peek32(U2_REG_IRQ_RB) & (1<<10)) != 0; + const bool lock = (_mbc[mb].wbiface->peek32(U2_REG_IRQ_RB) & (1<<10)) != 0; return sensor_value_t("MIMO", lock, "locked", "unlocked"); } sensor_value_t usrp2_impl::get_ref_locked(const std::string &mb){ - const bool lock = (_mbc[mb].iface->peek32(U2_REG_IRQ_RB) & (1<<11)) != 0; + const bool lock = (_mbc[mb].wbiface->peek32(U2_REG_IRQ_RB) & (1<<11)) != 0; return sensor_value_t("Ref", lock, "locked", "unlocked"); } @@ -729,14 +796,16 @@ meta_range_t usrp2_impl::get_tx_dsp_freq_range(const std::string &mb){ } void usrp2_impl::update_clock_source(const std::string &mb, const std::string &source){ + //NOTICE: U2_REG_MISC_CTRL_CLOCK is on the wb clock, and cannot be set from fifo_ctrl //clock source ref 10mhz switch(_mbc[mb].iface->get_rev()){ case usrp2_iface::USRP_N200: case usrp2_iface::USRP_N210: case usrp2_iface::USRP_N200_R4: case usrp2_iface::USRP_N210_R4: - if (source == "internal") _mbc[mb].iface->poke32(U2_REG_MISC_CTRL_CLOCK, 0x12); + if (source == "internal") _mbc[mb].iface->poke32(U2_REG_MISC_CTRL_CLOCK, 0x12); else if (source == "external") _mbc[mb].iface->poke32(U2_REG_MISC_CTRL_CLOCK, 0x1C); + else if (source == "gpsdo") _mbc[mb].iface->poke32(U2_REG_MISC_CTRL_CLOCK, 0x1C); else if (source == "mimo") _mbc[mb].iface->poke32(U2_REG_MISC_CTRL_CLOCK, 0x15); else throw uhd::value_error("unhandled clock configuration reference source: " + source); _mbc[mb].clock->enable_external_ref(true); //USRP2P has an internal 10MHz TCXO @@ -744,7 +813,7 @@ void usrp2_impl::update_clock_source(const std::string &mb, const std::string &s case usrp2_iface::USRP2_REV3: case usrp2_iface::USRP2_REV4: - if (source == "internal") _mbc[mb].iface->poke32(U2_REG_MISC_CTRL_CLOCK, 0x10); + if (source == "internal") _mbc[mb].iface->poke32(U2_REG_MISC_CTRL_CLOCK, 0x10); else if (source == "external") _mbc[mb].iface->poke32(U2_REG_MISC_CTRL_CLOCK, 0x1C); else if (source == "mimo") _mbc[mb].iface->poke32(U2_REG_MISC_CTRL_CLOCK, 0x15); else throw uhd::value_error("unhandled clock configuration reference source: " + source); diff --git a/host/lib/usrp/usrp2/usrp2_impl.hpp b/host/lib/usrp/usrp2/usrp2_impl.hpp index e5065c02d..a6c0d87cf 100644 --- a/host/lib/usrp/usrp2/usrp2_impl.hpp +++ b/host/lib/usrp/usrp2/usrp2_impl.hpp @@ -18,7 +18,9 @@ #ifndef INCLUDED_USRP2_IMPL_HPP #define INCLUDED_USRP2_IMPL_HPP +#include "gpio_core_200.hpp" #include "usrp2_iface.hpp" +#include "usrp2_fifo_ctrl.hpp" #include "clock_ctrl.hpp" #include "codec_ctrl.hpp" #include "rx_frontend_core_200.hpp" @@ -51,15 +53,13 @@ static const size_t mimo_clock_sync_delay_cycles = 138; static const size_t USRP2_SRAM_BYTES = size_t(1 << 20); static const boost::uint32_t USRP2_TX_ASYNC_SID = 2; static const boost::uint32_t USRP2_RX_SID_BASE = 3; +static const std::string USRP2_EEPROM_MAP_KEY = "N100"; -/*! - * Make a usrp2 dboard interface. - * \param iface the usrp2 interface object - * \param clk_ctrl the clock control object - * \return a sptr to a new dboard interface - */ +//! Make a usrp2 dboard interface. uhd::usrp::dboard_iface::sptr make_usrp2_dboard_iface( - usrp2_iface::sptr iface, + wb_iface::sptr wb_iface, + uhd::i2c_iface::sptr i2c_iface, + uhd::spi_iface::sptr spi_iface, usrp2_clock_ctrl::sptr clk_ctrl ); @@ -82,6 +82,9 @@ private: uhd::property_tree::sptr _tree; struct mb_container_type{ usrp2_iface::sptr iface; + usrp2_fifo_ctrl::sptr fifo_ctrl; + uhd::spi_iface::sptr spiface; + wb_iface::sptr wbiface; usrp2_clock_ctrl::sptr clock; usrp2_codec_ctrl::sptr codec; uhd::gps_ctrl::sptr gps; @@ -95,6 +98,7 @@ private: user_settings_core_200::sptr user; std::vector<uhd::transport::zero_copy_if::sptr> rx_dsp_xports; uhd::transport::zero_copy_if::sptr tx_dsp_xport; + uhd::transport::zero_copy_if::sptr fifo_ctrl_xport; uhd::usrp::dboard_manager::sptr dboard_manager; uhd::usrp::dboard_iface::sptr dboard_iface; size_t rx_chan_occ, tx_chan_occ; @@ -129,6 +133,7 @@ private: double set_tx_dsp_freq(const std::string &, const double); uhd::meta_range_t get_tx_dsp_freq_range(const std::string &); void update_clock_source(const std::string &, const std::string &); + void program_stream_dest(uhd::transport::zero_copy_if::sptr &, const uhd::stream_args_t &); }; #endif /* INCLUDED_USRP2_IMPL_HPP */ diff --git a/host/lib/usrp/usrp2/usrp2_regs.hpp b/host/lib/usrp/usrp2/usrp2_regs.hpp index e14798ecb..7fe83e709 100644 --- a/host/lib/usrp/usrp2/usrp2_regs.hpp +++ b/host/lib/usrp/usrp2/usrp2_regs.hpp @@ -36,10 +36,10 @@ // Setting register offsets //////////////////////////////////////////////////////////////////////// #define SR_MISC 0 // 7 regs -#define SR_SIMTIMER 8 // 2 +#define SR_USER_REGS 8 // 2 #define SR_TIME64 10 // 6 #define SR_BUF_POOL 16 // 4 -#define SR_USER_REGS 20 // 2 +#define SR_SPI_CORE 20 // 3 #define SR_RX_FRONT 24 // 5 #define SR_RX_CTRL0 32 // 9 #define SR_RX_DSP0 48 // 7 diff --git a/host/lib/utils/images.cpp b/host/lib/utils/images.cpp index a124cc208..251cadeaa 100644 --- a/host/lib/utils/images.cpp +++ b/host/lib/utils/images.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-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 @@ -17,16 +17,18 @@ #include <uhd/utils/images.hpp> #include <uhd/exception.hpp> +#include <uhd/utils/paths.hpp> #include <boost/foreach.hpp> #include <boost/filesystem.hpp> #include <vector> +#include <iostream> namespace fs = boost::filesystem; std::vector<fs::path> get_image_paths(void); //defined in paths.cpp /*********************************************************************** - * Find a image in the image paths + * Find an image in the image paths **********************************************************************/ std::string uhd::find_image_path(const std::string &image_name){ if (fs::exists(image_name)){ @@ -36,5 +38,17 @@ std::string uhd::find_image_path(const std::string &image_name){ fs::path image_path = path / image_name; if (fs::exists(image_path)) return image_path.string(); } - throw uhd::io_error("Could not find path for image: " + image_name); + throw uhd::io_error("Could not find path for image: " + image_name + "\n\n" + uhd::print_images_error()); +} + +std::string uhd::find_images_downloader(void){ + return fs::path((fs::path(get_pkg_data_path()) / "utils" / "uhd_images_downloader.py")).string(); +} + +std::string uhd::print_images_error(void){ + #ifdef UHD_PLATFORM_WIN32 + return "As an Administrator, please run:\n\n\"" + find_images_downloader() + "\""; + #else + return "Please run:\n\nsudo \"" + find_images_downloader() + "\""; + #endif } diff --git a/host/lib/utils/log.cpp b/host/lib/utils/log.cpp index 31d11796e..d6d1786c7 100644 --- a/host/lib/utils/log.cpp +++ b/host/lib/utils/log.cpp @@ -1,5 +1,5 @@ // -// Copyright 2011 Ettus Research LLC +// 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 @@ -18,6 +18,7 @@ #include <uhd/utils/log.hpp> #include <uhd/utils/msg.hpp> #include <uhd/utils/static.hpp> +#include <uhd/utils/paths.hpp> #include <boost/filesystem.hpp> #include <boost/format.hpp> #include <boost/thread/mutex.hpp> @@ -35,12 +36,6 @@ namespace boost{ namespace interprocess{ #else #include <boost/interprocess/sync/file_lock.hpp> #endif -#ifdef BOOST_MSVC -#define USE_GET_TEMP_PATH -#include <Windows.h> //GetTempPath -#endif -#include <stdio.h> //P_tmpdir -#include <cstdlib> //getenv #include <fstream> #include <sstream> #include <cctype> @@ -50,42 +45,6 @@ namespace pt = boost::posix_time; namespace ip = boost::interprocess; /*********************************************************************** - * Helper function to get the system's temporary path - **********************************************************************/ -static fs::path get_temp_path(void){ - const char *tmp_path = NULL; - - //try the official uhd temp path environment variable - tmp_path = std::getenv("UHD_TEMP_PATH"); - if (tmp_path != NULL) return tmp_path; - - //try the windows function if available - #ifdef USE_GET_TEMP_PATH - char lpBuffer[2048]; - if (GetTempPath(sizeof(lpBuffer), lpBuffer)) return lpBuffer; - #endif - - //try windows environment variables - tmp_path = std::getenv("TMP"); - if (tmp_path != NULL) return tmp_path; - - tmp_path = std::getenv("TEMP"); - if (tmp_path != NULL) return tmp_path; - - //try the stdio define if available - #ifdef P_tmpdir - return P_tmpdir; - #endif - - //try unix environment variables - tmp_path = std::getenv("TMPDIR"); - if (tmp_path != NULL) return tmp_path; - - //give up and use the unix default - return "/tmp"; -} - -/*********************************************************************** * Global resources for the logger **********************************************************************/ class log_resource_type{ @@ -119,7 +78,7 @@ public: void log_to_file(const std::string &log_msg){ boost::mutex::scoped_lock lock(_mutex); if (_file_lock == NULL){ - const std::string log_path = (get_temp_path() / "uhd.log").string(); + const std::string log_path = (fs::path(uhd::get_tmp_path()) / "uhd.log").string(); _file_stream.open(log_path.c_str(), std::fstream::out | std::fstream::app); _file_lock = new ip::file_lock(log_path.c_str()); } diff --git a/host/lib/utils/paths.cpp b/host/lib/utils/paths.cpp index 4fc877d5d..26fa6d1c7 100644 --- a/host/lib/utils/paths.cpp +++ b/host/lib/utils/paths.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-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 @@ -26,6 +26,10 @@ #include <vector> #include <cstdlib> //getenv #include <cstdio> //P_tmpdir +#ifdef BOOST_MSVC +#define USE_GET_TEMP_PATH +#include <windows.h> //GetTempPath +#endif namespace fs = boost::filesystem; @@ -67,19 +71,20 @@ static std::vector<fs::path> get_env_paths(const std::string &var_name){ /*********************************************************************** * Get a list of special purpose paths **********************************************************************/ -static fs::path get_uhd_pkg_data_path(void){ - return fs::path(get_env_var("UHD_PKG_DATA_PATH", UHD_PKG_DATA_PATH)); +std::string uhd::get_pkg_data_path(void) +{ + return get_env_var("UHD_PKG_DATA_PATH", UHD_PKG_DATA_PATH); } std::vector<fs::path> get_image_paths(void){ std::vector<fs::path> paths = get_env_paths("UHD_IMAGE_PATH"); - paths.push_back(get_uhd_pkg_data_path() / "images"); + paths.push_back(fs::path(uhd::get_pkg_data_path()) / "images"); return paths; } std::vector<fs::path> get_module_paths(void){ std::vector<fs::path> paths = get_env_paths("UHD_MODULE_PATH"); - paths.push_back(get_uhd_pkg_data_path() / "modules"); + paths.push_back(fs::path(uhd::get_pkg_data_path()) / "modules"); return paths; } @@ -87,13 +92,35 @@ std::vector<fs::path> get_module_paths(void){ * Implement the functions in paths.hpp **********************************************************************/ std::string uhd::get_tmp_path(void){ - const char *tmp_path = std::getenv("TMP"); + const char *tmp_path = NULL; + + //try the official uhd temp path environment variable + tmp_path = std::getenv("UHD_TEMP_PATH"); + if (tmp_path != NULL) return tmp_path; + + //try the windows function if available + #ifdef USE_GET_TEMP_PATH + char lpBuffer[2048]; + if (GetTempPath(sizeof(lpBuffer), lpBuffer)) return lpBuffer; + #endif + + //try windows environment variables + tmp_path = std::getenv("TMP"); if (tmp_path != NULL) return tmp_path; + tmp_path = std::getenv("TEMP"); + if (tmp_path != NULL) return tmp_path; + + //try the stdio define if available #ifdef P_tmpdir if (P_tmpdir != NULL) return P_tmpdir; #endif + //try unix environment variables + tmp_path = std::getenv("TMPDIR"); + if (tmp_path != NULL) return tmp_path; + + //give up and use the unix default return "/tmp"; } diff --git a/host/tests/convert_test.cpp b/host/tests/convert_test.cpp index 6b0ae53a9..4b0226e3d 100644 --- a/host/tests/convert_test.cpp +++ b/host/tests/convert_test.cpp @@ -19,6 +19,7 @@ #include <boost/test/unit_test.hpp> #include <boost/foreach.hpp> #include <boost/cstdint.hpp> +#include <boost/assign/list_of.hpp> #include <complex> #include <vector> #include <cstdlib> @@ -45,7 +46,9 @@ template <typename Range> static void loopback( convert::id_type &in_id, convert::id_type &out_id, const Range &input, - Range &output + Range &output, + const int prio_in = -1, + const int prio_out = -1 ){ //item32 is largest device type std::vector<boost::uint32_t> interm(nsamps); @@ -54,12 +57,12 @@ template <typename Range> static void loopback( std::vector<void *> output0(1, &interm[0]), output1(1, &output[0]); //convert to intermediate type - convert::converter::sptr c0 = convert::get_converter(in_id)(); + convert::converter::sptr c0 = convert::get_converter(in_id, prio_in)(); c0->set_scalar(32767.); c0->conv(input0, output0, nsamps); //convert back to host type - convert::converter::sptr c1 = convert::get_converter(out_id)(); + convert::converter::sptr c1 = convert::get_converter(out_id, prio_out)(); c1->set_scalar(1/32767.); c1->conv(input1, output1, nsamps); } @@ -133,10 +136,21 @@ static void test_convert_types_for_floats( convert::id_type out_id = id; std::swap(out_id.input_format, out_id.output_format); std::swap(out_id.num_inputs, out_id.num_outputs); - loopback(nsamps, in_id, out_id, input, output); - for (size_t i = 0; i < nsamps; i++){ - MY_CHECK_CLOSE(input[i].real(), output[i].real(), value_type(1./32767)); - MY_CHECK_CLOSE(input[i].imag(), output[i].imag(), value_type(1./32767)); + + //make a list of all prio: best/generic combos + typedef std::pair<int, int> int_pair_t; + std::vector<int_pair_t> prios = boost::assign::list_of + (int_pair_t(0, 0)) (int_pair_t(-1, 0)) + (int_pair_t(0, -1)) (int_pair_t(-1, -1)) + ; + + //loopback foreach prio combo (generic vs best) + BOOST_FOREACH(const int_pair_t &prio, prios){ + loopback(nsamps, in_id, out_id, input, output, prio.first, prio.second); + for (size_t i = 0; i < nsamps; i++){ + MY_CHECK_CLOSE(input[i].real(), output[i].real(), value_type(1./32767)); + MY_CHECK_CLOSE(input[i].imag(), output[i].imag(), value_type(1./32767)); + } } } diff --git a/host/tests/sph_recv_test.cpp b/host/tests/sph_recv_test.cpp index 9b45d7016..5a40029dc 100644 --- a/host/tests/sph_recv_test.cpp +++ b/host/tests/sph_recv_test.cpp @@ -50,16 +50,11 @@ public: sptr get_new(boost::shared_array<char> mem, size_t len){ _mem = mem; - _len = len; - return make_managed_buffer(this); + return make(this, _mem.get(), len); } private: - const void *get_buff(void) const{return _mem.get();} - size_t get_size(void) const{return _len;} - boost::shared_array<char> _mem; - size_t _len; }; /*********************************************************************** @@ -89,8 +84,8 @@ public: uhd::transport::managed_recv_buffer::sptr get_recv_buff(double){ if (_mems.empty()) return uhd::transport::managed_recv_buffer::sptr(); //timeout - _mrbs.push_back(dummy_mrb()); - uhd::transport::managed_recv_buffer::sptr mrb = _mrbs.back().get_new(_mems.front(), _lens.front()); + _mrbs.push_back(boost::shared_ptr<dummy_mrb>(new dummy_mrb())); + uhd::transport::managed_recv_buffer::sptr mrb = _mrbs.back()->get_new(_mems.front(), _lens.front()); _mems.pop_front(); _lens.pop_front(); return mrb; @@ -99,7 +94,7 @@ public: private: std::list<boost::shared_array<char> > _mems; std::list<size_t> _lens; - std::list<dummy_mrb> _mrbs; //list means no-realloc + std::vector<boost::shared_ptr<dummy_mrb> > _mrbs; std::string _end; }; diff --git a/host/tests/sph_send_test.cpp b/host/tests/sph_send_test.cpp index c31399d12..603b36c85 100644 --- a/host/tests/sph_send_test.cpp +++ b/host/tests/sph_send_test.cpp @@ -31,23 +31,17 @@ **********************************************************************/ class dummy_msb : public uhd::transport::managed_send_buffer{ public: - void commit(size_t len){ - if (len == 0) return; - *_len = len; + void release(void){ + //NOP } sptr get_new(boost::shared_array<char> mem, size_t *len){ _mem = mem; - _len = len; - return make_managed_buffer(this); + return make(this, mem.get(), *len); } private: - void *get_buff(void) const{return _mem.get();} - size_t get_size(void) const{return *_len;} - boost::shared_array<char> _mem; - size_t *_len; }; /*********************************************************************** @@ -74,17 +68,17 @@ public: } uhd::transport::managed_send_buffer::sptr get_send_buff(double){ - _msbs.push_back(dummy_msb()); + _msbs.push_back(boost::shared_ptr<dummy_msb>(new dummy_msb())); _mems.push_back(boost::shared_array<char>(new char[1000])); _lens.push_back(1000); - uhd::transport::managed_send_buffer::sptr mrb = _msbs.back().get_new(_mems.back(), &_lens.back()); + uhd::transport::managed_send_buffer::sptr mrb = _msbs.back()->get_new(_mems.back(), &_lens.back()); return mrb; } private: std::list<boost::shared_array<char> > _mems; std::list<size_t> _lens; - std::list<dummy_msb> _msbs; //list means no-realloc + std::vector<boost::shared_ptr<dummy_msb> > _msbs; std::string _end; }; diff --git a/host/usrp_e_utils/CMakeLists.txt b/host/usrp_e_utils/CMakeLists.txt deleted file mode 100644 index 0d6763174..000000000 --- a/host/usrp_e_utils/CMakeLists.txt +++ /dev/null @@ -1,42 +0,0 @@ -# -# Copyright 2011 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/>. -# - -######################################################################## -# USRP embedded utilities that get installed into the share path -######################################################################## -LIBUHD_REGISTER_COMPONENT("USRP-E Utils" ENABLE_USRP_E_UTILS OFF "LINUX" OFF) - -IF(ENABLE_USRP_E_UTILS) - ENABLE_LANGUAGE(C) - INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) - INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/lib/usrp/e100) - INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/lib/usrp/e100/include) - - SET(usrp_e_utils_sources - usrp-e-loopback.cpp - usrp-e-wb-test.cpp - ) - - #for each source: build an executable and install - FOREACH(util_source ${usrp_e_utils_sources}) - GET_FILENAME_COMPONENT(util_name ${util_source} NAME_WE) - ADD_EXECUTABLE(${util_name} ${util_source}) - TARGET_LINK_LIBRARIES(${util_name} ${Boost_LIBRARIES}) - INSTALL(TARGETS ${util_name} RUNTIME DESTINATION ${PKG_LIB_DIR}/usrp_e_utils) - ENDFOREACH(util_source) - -ENDIF(ENABLE_USRP_E_UTILS) diff --git a/host/usrp_e_utils/common.hpp b/host/usrp_e_utils/common.hpp deleted file mode 100644 index 989aecd06..000000000 --- a/host/usrp_e_utils/common.hpp +++ /dev/null @@ -1,64 +0,0 @@ -// -// Copyright 2011 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 <sys/ioctl.h> //ioctl -#include <fcntl.h> //open, close - -#include <linux/usrp_e.h> -#include "e100_regs.hpp" - -static int fp; - -static inline int peek16(int reg){ - int ret; - struct usrp_e_ctl16 d; - - d.offset = reg; - d.count = 1; - ret = ioctl(fp, USRP_E_READ_CTL16, &d); - return d.buf[0]; -} - -static inline void poke16(int reg, int val){ - int ret; - struct usrp_e_ctl16 d; - - d.offset = reg; - d.count = 1; - d.buf[0] = val; - ret = ioctl(fp, USRP_E_WRITE_CTL16, &d); -} - -static inline int peek32(int reg){ - int ret; - struct usrp_e_ctl32 d; - - d.offset = reg; - d.count = 1; - ret = ioctl(fp, USRP_E_READ_CTL32, &d); - return d.buf[0]; -} - -static inline void poke32(int reg, int val){ - int ret; - struct usrp_e_ctl32 d; - - d.offset = reg; - d.count = 1; - d.buf[0] = val; - ret = ioctl(fp, USRP_E_WRITE_CTL32, &d); -} diff --git a/host/usrp_e_utils/usrp-e-loopback.cpp b/host/usrp_e_utils/usrp-e-loopback.cpp deleted file mode 100644 index d4220adc3..000000000 --- a/host/usrp_e_utils/usrp-e-loopback.cpp +++ /dev/null @@ -1,298 +0,0 @@ -// -// Copyright 2011 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 "common.hpp" -#include <cstdlib> -#include <cstdio> -#include <cstring> -#include <ctime> -#include <iostream> -#include <boost/thread/thread.hpp> -#include <boost/format.hpp> -#include <boost/cstdint.hpp> -#include <sys/mman.h> //mmap -#include <unistd.h> //getpagesize -#include <poll.h> //poll - -static const size_t bytes_per_frame = 2048; - -static const int poll_timeout_ms = 100; - -struct loopback_pkt_hdr_type{ - boost::uint32_t words32; - boost::uint32_t checksum; - boost::uint64_t seq_num; -}; - -struct loopback_pkt_type{ - loopback_pkt_hdr_type hdr; - boost::uint32_t data[(bytes_per_frame-sizeof(loopback_pkt_hdr_type))/sizeof(boost::uint32_t)]; -}; - -static ring_buffer_info (*recv_info)[]; -static ring_buffer_info (*send_info)[]; -static loopback_pkt_type (*recv_buff)[]; -static loopback_pkt_type (*send_buff)[]; - -static struct usrp_e_ring_buffer_size_t rb_size; - -static bool running = true; - -static boost::uint64_t seq_errors = 0; -static boost::uint64_t checksum_errors = 0; -static boost::uint64_t sent_words32 = 0; -static boost::uint64_t recvd_words32 = 0; - -static inline void print_pkt(const loopback_pkt_type &pkt){ - std::cout << std::endl; - std::cout << "pkt.hdr.words32 " << pkt.hdr.words32 << std::endl; - std::cout << "pkt.hdr.checksum " << pkt.hdr.checksum << std::endl; - std::cout << "pkt.hdr.seq_num " << pkt.hdr.seq_num << std::endl; -} - -boost::uint32_t my_checksum(void *buff, size_t size32){ - boost::uint32_t x = 0; - for (size_t i = 0; i < size32; i++){ - x += reinterpret_cast<boost::uint32_t *>(buff)[i]; - x ^= reinterpret_cast<boost::uint32_t *>(buff)[i]; - } - return x; -} - -/*********************************************************************** - * Read thread - recv frames and verify checksum - **********************************************************************/ -static void read_thread(void){ - std::cout << "start read thread... " << std::endl; - - boost::uint64_t seq_num = 0; - size_t index = 0; - - while (running){ - - loopback_pkt_type &pkt = (*recv_buff)[index]; - ring_buffer_info &info = (*recv_info)[index]; - - //wait for frame available - if (not (info.flags & RB_USER)){ - pollfd pfd; - pfd.fd = fp; - pfd.events = POLLIN; - if (poll(&pfd, 1, poll_timeout_ms) <= 0){ - std::cout << "Read poll timeout, exiting read thread!" << std::endl; - running = false; - return; - } - } - info.flags = RB_USER_PROCESS; - - //print_pkt(pkt); - - //handle checksum - const boost::uint32_t expected_checksum = pkt.hdr.checksum; - pkt.hdr.checksum = 0; //set to zero for calculation - const boost::uint32_t actual_checksum = my_checksum(&pkt, pkt.hdr.words32); - if (expected_checksum != actual_checksum){ - checksum_errors++; - std::cerr << "C"; - } - else{ - recvd_words32 += pkt.hdr.words32; - } - - //handle sequence - if (seq_num != pkt.hdr.seq_num){ - seq_errors++; - std::cerr << "S"; - } - seq_num = pkt.hdr.seq_num+1; - - //release the packet - info.flags = RB_KERNEL; - - //increment index and wrap around to zero - index++; - if (index == size_t(rb_size.num_rx_frames)) index = 0; - } - -} - -/*********************************************************************** - * Write thread - send frames and calculate checksum - **********************************************************************/ -static void write_thread(const size_t num_words32){ - std::cout << "start write thread... " << std::endl; - - srandom(std::time(NULL)); - - boost::uint64_t seq_num = 0; - size_t index = 0; - - //write into tmp and memcopy into pkt to avoid cache issues - loopback_pkt_type pkt_tmp; - - while (running){ - - ring_buffer_info &info = (*send_info)[index]; - - //wait for frame available - if (not (info.flags & RB_KERNEL)){ - pollfd pfd; - pfd.fd = fp; - pfd.events = POLLOUT; - if (poll(&pfd, 1, poll_timeout_ms) <= 0){ - std::cout << "Write poll timeout, exiting write thread!" << std::endl; - running = false; - return; - } - } - - //fill packet header and body - const boost::uint32_t seed = random(); - pkt_tmp.hdr.words32 = sizeof(pkt_tmp.hdr)/sizeof(boost::uint32_t) + num_words32; - pkt_tmp.hdr.checksum = 0; //set to zero for checksum() - pkt_tmp.hdr.seq_num = seq_num++; - for (size_t i = 0; i < num_words32; i++) pkt_tmp.data[i] = seed + i; - pkt_tmp.hdr.checksum = my_checksum(&pkt_tmp, pkt_tmp.hdr.words32); - sent_words32 += pkt_tmp.hdr.words32; - - loopback_pkt_type &pkt = (*send_buff)[index]; - std::memcpy(&pkt, &pkt_tmp, pkt_tmp.hdr.words32*sizeof(boost::uint32_t)); - - //print_pkt(pkt); - - //commit the packet - info.len = pkt_tmp.hdr.words32*sizeof(boost::uint32_t); - info.flags = RB_USER; - ::write(fp, NULL, 0); - - //increment index and wrap around to zero - index++; - if (index == size_t(rb_size.num_tx_frames)) index = 0; - } -} - -/*********************************************************************** - * Setup memory mapped ring buffer - **********************************************************************/ -static void setup_ring(void){ - std::cout << "setup memory mapped ring buffer... " << std::flush; - - //calculate various sizes - const size_t page_size = getpagesize(); - ioctl(fp, USRP_E_GET_RB_INFO, &rb_size); - const size_t map_size = (rb_size.num_pages_rx_flags + rb_size.num_pages_tx_flags) * page_size + - (rb_size.num_rx_frames + rb_size.num_tx_frames) * (page_size >> 1); - - //call into memory map - void *mem = ::mmap(0, map_size, PROT_READ|PROT_WRITE, MAP_SHARED, fp, 0); - if (mem == MAP_FAILED) { - std::cerr << "mmap failed" << std::endl; - std::exit(-1); - } - - //calculate the memory offsets for info and buffers - const size_t recv_info_off = 0; - const size_t recv_buff_off = recv_info_off + (rb_size.num_pages_rx_flags * page_size); - const size_t send_info_off = recv_buff_off + (rb_size.num_rx_frames * page_size/2); - const size_t send_buff_off = send_info_off + (rb_size.num_pages_tx_flags * page_size); - - //set the internal pointers for info and buffers - typedef ring_buffer_info (*rbi_pta)[]; - typedef loopback_pkt_type (*pkt_pta)[]; - char *rb_ptr = reinterpret_cast<char *>(mem); - recv_info = reinterpret_cast<rbi_pta>(rb_ptr + recv_info_off); - recv_buff = reinterpret_cast<pkt_pta>(rb_ptr + recv_buff_off); - send_info = reinterpret_cast<rbi_pta>(rb_ptr + send_info_off); - send_buff = reinterpret_cast<pkt_pta>(rb_ptr + send_buff_off); - - std::cout << "done" << std::endl; -} - -/*********************************************************************** - * Main - **********************************************************************/ -#include <boost/program_options.hpp> - -int main(int argc, char *argv[]){ - - //variables to be set by po - double duration; - size_t nwords; - - //setup the program options - namespace po = boost::program_options; - po::options_description desc("Allowed options"); - desc.add_options() - ("help", "help message") - ("duration", po::value<double>(&duration)->default_value(10), "number of seconds to run loopback") - ("nwords", po::value<size_t>(&nwords)->default_value(400), "number of words32 to send per packet") - ; - po::variables_map vm; - po::store(po::parse_command_line(argc, argv, desc), vm); - po::notify(vm); - - //print the help message - if (vm.count("help")){ - std::cout << boost::format("UHD USRP-E-Loopback %s") % desc << std::endl; - return ~0; - } - - if ((fp = ::open("/dev/usrp_e0", O_RDWR)) < 0){ - std::cerr << "Open failed" << std::endl; - return -1; - } - - //set the mode to loopback - poke16(E100_REG_MISC_XFER_RATE, (1<<8) | (1<<9)); - - //clear FIFO state in FPGA and kernel - poke32(E100_REG_CLEAR_FIFO, 0); - ::close(fp); - if ((fp = ::open("/dev/usrp_e0", O_RDWR)) < 0){ - std::cerr << "Open failed" << std::endl; - return -1; - } - - //setup the ring buffer - setup_ring(); - - //spawn threads - boost::thread_group tg; - tg.create_thread(boost::bind(&read_thread)); - tg.create_thread(boost::bind(&write_thread, nwords)); - - const boost::system_time start_time = boost::get_system_time(); - const boost::system_time finish_time = start_time + boost::posix_time::milliseconds(long(duration*1000)); - while (boost::get_system_time() < finish_time){ - boost::this_thread::sleep(boost::posix_time::milliseconds(1000)); - std::cerr << "."; - } - running = false; - tg.join_all(); - - std::cout << std::endl; - std::cout << "seq_errors " << seq_errors << std::endl; - std::cout << "checksum_errors " << checksum_errors << std::endl; - std::cout << "sent_words32 " << sent_words32 << std::endl; - std::cout << "recvd_words32 " << recvd_words32 << std::endl; - std::cout << "approx send rate " << (sent_words32/duration)/1e6 << "Msps" << std::endl; - std::cout << "approx recv rate " << (recvd_words32/duration)/1e6 << "Msps" << std::endl; - - ::close(fp); - return seq_errors + checksum_errors; -} diff --git a/host/usrp_e_utils/usrp-e-wb-test.cpp b/host/usrp_e_utils/usrp-e-wb-test.cpp deleted file mode 100644 index 2b793dc06..000000000 --- a/host/usrp_e_utils/usrp-e-wb-test.cpp +++ /dev/null @@ -1,68 +0,0 @@ -// -// Copyright 2011 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 "common.hpp" -#include <cstdlib> -#include <cstdio> -#include <ctime> -#include <iostream> - -static const size_t num_test_iters = 10000000; - -int main(int, char *[]){ - - srandom(time(NULL)); //seed random() - - if ((fp = ::open("/dev/usrp_e0", O_RDWR)) < 0){ - std::cerr << "Open failed" << std::endl; - return -1; - } - - size_t num_pass = 0, num_fail = 0; - for (size_t i = 0; i < num_test_iters; i++){ - if(i%1000000 == 0) { - std::cout << "num pass: " << num_pass; - std::cout << "\tnum fail: " << num_fail << std::endl; - } - //make random values - int random_test32 = ::random(); - int random_test16 = ::random() & 0xffff; - //int random_secs = ::random(); - - //set a bunch of registers - poke16(E100_REG_MISC_TEST, random_test16); - poke32(E100_REG_SR_MISC_TEST32, random_test32); - //poke32(E100_REG_TIME64_TICKS, 0); - //poke32(E100_REG_TIME64_IMM, 1); //immediate - //poke32(E100_REG_TIME64_SECS, random_secs); - - //read a bunch of registers - if ( - (peek16(E100_REG_MISC_TEST) == random_test16) and - (peek32(E100_REG_RB_MISC_TEST32) == random_test32) and -// (peek32(E100_REG_RB_TIME_NOW_SECS) == random_secs) and -// (peek32(E100_REG_RB_TIME_NOW_TICKS) < 1000000) and - true) num_pass++; - else num_fail++; - } - - std::cout << "num pass: " << num_pass << std::endl; - std::cout << "num fail: " << num_fail << std::endl; - - ::close(fp); - return 0; -} diff --git a/host/utils/CMakeLists.txt b/host/utils/CMakeLists.txt index 0ecd6b4e7..9e997071e 100644 --- a/host/utils/CMakeLists.txt +++ b/host/utils/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2010-2011 Ettus Research LLC +# Copyright 2010-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 @@ -38,8 +38,10 @@ ENDFOREACH(util_source) # Utilities that get installed into the share path ######################################################################## SET(util_share_sources + query_gpsdo_sensors.cpp usrp_burn_db_eeprom.cpp usrp_burn_mb_eeprom.cpp + usrp_n2xx_simple_net_burner.cpp ) IF(ENABLE_USB) @@ -64,6 +66,17 @@ FOREACH(util_source ${util_share_sources}) INSTALL(TARGETS ${util_name} RUNTIME DESTINATION ${PKG_LIB_DIR}/utils COMPONENT utilities) ENDFOREACH(util_source) +#UHD images downloader configuration +CONFIGURE_FILE( + ${CMAKE_CURRENT_SOURCE_DIR}/uhd_images_downloader.py.in + ${CMAKE_CURRENT_BINARY_DIR}/uhd_images_downloader.py +@ONLY) +INSTALL(PROGRAMS + ${CMAKE_CURRENT_BINARY_DIR}/uhd_images_downloader.py + DESTINATION ${PKG_LIB_DIR}/utils + COMPONENT utilities +) + IF(ENABLE_USRP2) IF(WIN32 AND UHD_RELEASE_MODE) #include dd.exe FILE(DOWNLOAD @@ -76,8 +89,14 @@ IF(ENABLE_USRP2) COMPONENT utilities ) ENDIF(WIN32 AND UHD_RELEASE_MODE) + IF(LINUX) + INSTALL(PROGRAMS + usrp2_recovery.py + DESTINATION ${PKG_LIB_DIR}/utils + COMPONENT utilities + ) + ENDIF(LINUX) INSTALL(PROGRAMS - usrp2_recovery.py usrp2_card_burner.py usrp2_card_burner_gui.py usrp_n2xx_net_burner.py diff --git a/host/utils/FastSendDatagramThreshold.reg b/host/utils/FastSendDatagramThreshold.reg Binary files differnew file mode 100755 index 000000000..c0665d09e --- /dev/null +++ b/host/utils/FastSendDatagramThreshold.reg diff --git a/host/utils/query_gpsdo_sensors.cpp b/host/utils/query_gpsdo_sensors.cpp new file mode 100644 index 000000000..d459fd0ec --- /dev/null +++ b/host/utils/query_gpsdo_sensors.cpp @@ -0,0 +1,121 @@ +// +// 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 <uhd/utils/paths.hpp> +#include <uhd/utils/thread_priority.hpp> +#include <uhd/utils/safe_main.hpp> +#include <uhd/usrp/multi_usrp.hpp> +#include <boost/filesystem.hpp> +#include <boost/program_options.hpp> +#include <boost/format.hpp> +#include <iostream> +#include <complex> +#include <boost/thread.hpp> +#include <string> +#include <cmath> +#include <cstdlib> + +namespace po = boost::program_options; +namespace fs = boost::filesystem; + +void print_notes(void) { + // Helpful notes + std::cout << boost::format("**************************************Helpful Notes on Clock/PPS Selection**************************************\n"); + std::cout << boost::format("As you can see, the default 10 MHz Reference and 1 PPS signals are now from the GPSDO.\n"); + std::cout << boost::format("If you would like to use the internal reference(TCXO) in other applications, you must configure that explicitly.\n"); + std::cout << boost::format("You can no longer select the external SMAs for 10 MHz or 1 PPS signaling.\n"); + std::cout << boost::format("****************************************************************************************************************\n"); +} + +int UHD_SAFE_MAIN(int argc, char *argv[]){ + uhd::set_thread_priority_safe(); + + std::string args; + + //Set up program options + po::options_description desc("Allowed options"); + desc.add_options() + ("help", "help message") + ("args", po::value<std::string>(&args)->default_value(""), "Specify a single USRP.") + ; + po::variables_map vm; + po::store(po::parse_command_line(argc, argv, desc), vm); + po::notify(vm); + + //Print the help message + if (vm.count("help")) { + std::cout << boost::format("Query GPSDO Sensors %s") % desc << std::endl; + return EXIT_FAILURE; + } + + //Create a USRP device + std::cout << boost::format("\nCreating the USRP device with: %s...\n") % args; + uhd::usrp::multi_usrp::sptr usrp = uhd::usrp::multi_usrp::make(args); + std::cout << boost::format("Using Device: %s\n") % usrp->get_pp_string(); + + print_notes(); + + + //Verify GPS sensors are present (i.e. EEPROM has been burnt) + std::vector<std::string> sensor_names = usrp->get_mboard_sensor_names(0); + + if(std::find(sensor_names.begin(), sensor_names.end(), "gps_locked") == sensor_names.end()) { + std::cout << boost::format("\ngps_locked sensor not found. This could mean that you have not installed the GPSDO correctly.\n\n"); + std::cout << boost::format("Visit this page if the problem persists:\n"); + std::cout << boost::format("http://files.ettus.com/uhd_docs/manual/html/gpsdo.html\n\n"); + exit(EXIT_FAILURE); + } + + //Check for GPS lock + uhd::sensor_value_t gps_locked = usrp->get_mboard_sensor("gps_locked",0); + if(not gps_locked.to_bool()) { + std::cout << boost::format("\nGPS does not have lock. Wait a few minutes and try again.\n"); + std::cout << boost::format("NMEA strings and device time may not be accurate until lock is achieved.\n\n"); + } else + std::cout << boost::format("GPS Locked\n"); + + //Check for 10 MHz lock + if(std::find(sensor_names.begin(), sensor_names.end(), "ref_locked") != sensor_names.end()) { + uhd::sensor_value_t gps_locked = usrp->get_mboard_sensor("ref_locked",0); + if(not gps_locked.to_bool()) { + std::cout << boost::format("USRP NOT Locked to GPSDO 10 MHz Reference.\n"); + std::cout << boost::format("Double check installation instructions: https://www.ettus.com/content/files/gpsdo-kit_2.pdf\n\n"); + } else + std::cout << boost::format("USRP Locked to GPSDO 10 MHz Reference.\n"); + }else + std::cout << boost::format("ref_locked sensor not present on this board.\n"); + + //Check PPS and compare UHD device time to GPS time + uhd::sensor_value_t gps_time = usrp->get_mboard_sensor("gps_time"); + const uhd::time_spec_t last_pps_time = usrp->get_time_last_pps(); + if (last_pps_time.get_full_secs() == gps_time.to_int()) { + std::cout << boost::format("GPS and UHD Device time are aligned.\n"); + } else + std::cout << boost::format("\nGPS and UHD Device time are NOT aligned. Try re-running the program. Double check 1 PPS connection from GPSDO.\n\n"); + + //print NMEA strings + std::cout << boost::format("Printing available NMEA strings:\n"); + uhd::sensor_value_t gga_string = usrp->get_mboard_sensor("gps_gpgga"); + uhd::sensor_value_t rmc_string = usrp->get_mboard_sensor("gps_gprmc"); + std::cout << boost::format("%s\n%s\n%s\n") % gga_string.to_pp_string() % rmc_string.to_pp_string() % gps_time.to_pp_string(); + std::cout << boost::format("UHD Device time: %.0f seconds\n") % (last_pps_time.get_real_secs()); + + //finished + std::cout << boost::format("\nDone!\n\n"); + + return EXIT_SUCCESS; +} diff --git a/host/utils/uhd_images_downloader.py.in b/host/utils/uhd_images_downloader.py.in new file mode 100644 index 000000000..a57f9dc48 --- /dev/null +++ b/host/utils/uhd_images_downloader.py.in @@ -0,0 +1,107 @@ +#!/usr/bin/env python +# +# 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/>. +# + +import atexit +from optparse import OptionParser +import os +import os.path +import shutil +import sys +import tempfile +import urllib2 +import zipfile + +class temp_dir(): + + def __enter__(self): + self.name = tempfile.mkdtemp() + return self.name + def __exit__(self, type, value, traceback): + os.removedirs(self.name) + +if __name__ == "__main__": + + #Command line options + parser = OptionParser() + parser.add_option("--install-location", type="string", default="@CMAKE_INSTALL_PREFIX@/share/uhd/images", help="Set custom install location for images, [default=%default]") + parser.add_option("--buffer-size", type="int", default=8192, help="Set download buffer size, [default=%default]",) + (options, args) = parser.parse_args() + + #Configuring image download info + images_src = "@UHD_IMAGES_DOWNLOAD_SRC@" + filename = images_src.split("/")[-1] + + with temp_dir() as dirname: + os.chdir(dirname) + + #Configuring image destination + if options.install_location != "": + images_dir = options.install_location + else: + images_dir = "@CMAKE_INSTALL_PREFIX@/share/uhd/images" + + u = urllib2.urlopen(images_src) + f = open(filename, "wb") + meta = u.info() + filesize = float(meta.getheaders("Content-Length")[0]) + + print "Downloading images from: %s" % images_src + + filesize_dl = 0.0 + + #Downloading file + while True: + buffer = u.read(options.buffer_size) + if not buffer: + break + + filesize_dl -= len(buffer) + f.write(buffer) + + status = r"%2.2f MB/%2.2f MB (%3.2f" % (-filesize_dl/1e6, filesize/1e6, (-filesize_dl*100.)/filesize) + r"%)" + status += chr(8)*(len(status)+1) + print status, + + f.close() + + #Extracting contents of zip file + if os.path.exists("tempdir"): + shutil.rmtree("tempdir") + os.mkdir("tempdir") + + images_zip = zipfile.ZipFile(filename) + images_zip.extractall("tempdir") + + #Removing images currently in images_dir + if os.path.exists(images_dir): + try: + shutil.rmtree(images_dir) + except: + sys.stderr.write("\nMake sure you have write permissions in the images directory.\n") + sys.exit(0) + + #Copying downloaded images into images_dir + shutil.copytree("tempdir/%s/share/uhd/images" % filename[:-4],images_dir) + + #Removing tempdir and zip file + shutil.rmtree("tempdir") + images_zip.close() + os.remove(filename) + + os.chdir(images_dir) + print "\nImages successfully installed to: %s" % images_dir diff --git a/host/utils/uhd_usrp_probe.cpp b/host/utils/uhd_usrp_probe.cpp index 1bd49a5ff..5b3702fb4 100644 --- a/host/utils/uhd_usrp_probe.cpp +++ b/host/utils/uhd_usrp_probe.cpp @@ -69,9 +69,9 @@ static std::string prop_names_to_pp_string(const std::vector<std::string> &prop_ return ss.str(); } -static std::string get_subdev_pp_string(const std::string &type, property_tree::sptr tree, const fs_path &path){ +static std::string get_frontend_pp_string(const std::string &type, property_tree::sptr tree, const fs_path &path){ std::stringstream ss; - ss << boost::format("%s Subdev: %s") % type % path.leaf() << std::endl; + ss << boost::format("%s Frontend: %s") % type % path.leaf() << std::endl; //ss << std::endl; ss << boost::format("Name: %s") % (tree->access<std::string>(path / "name").get()) << std::endl; @@ -123,7 +123,7 @@ static std::string get_dboard_pp_string(const std::string &type, property_tree:: if (not gdb_eeprom.serial.empty()) ss << boost::format("Serial: %s") % gdb_eeprom.serial << std::endl; } BOOST_FOREACH(const std::string &name, tree->list(path / (prefix + "_frontends"))){ - ss << make_border(get_subdev_pp_string(type, tree, path / (prefix + "_frontends") / name)); + ss << make_border(get_frontend_pp_string(type, tree, path / (prefix + "_frontends") / name)); } ss << make_border(get_codec_pp_string(type, tree, path.branch_path().branch_path() / (prefix + "_codecs") / path.leaf())); return ss.str(); @@ -137,6 +137,12 @@ static std::string get_mboard_pp_string(property_tree::sptr tree, const fs_path BOOST_FOREACH(const std::string &key, mb_eeprom.keys()){ if (not mb_eeprom[key].empty()) ss << boost::format("%s: %s") % key % mb_eeprom[key] << std::endl; } + if (tree->exists(path / "fw_version")){ + ss << "FW Version: " << tree->access<std::string>(path / "fw_version").get() << std::endl; + } + if (tree->exists(path / "fpga_version")){ + ss << "FPGA Version: " << tree->access<std::string>(path / "fpga_version").get() << std::endl; + } ss << std::endl; ss << "Time sources: " << prop_names_to_pp_string(tree->access<std::vector<std::string> >(path / "time_source" / "options").get()) << std::endl; ss << "Clock sources: " << prop_names_to_pp_string(tree->access<std::vector<std::string> >(path / "clock_source" / "options").get()) << std::endl; diff --git a/host/utils/usrp_cal_utils.hpp b/host/utils/usrp_cal_utils.hpp index 825d94d64..364b68bbe 100644 --- a/host/utils/usrp_cal_utils.hpp +++ b/host/utils/usrp_cal_utils.hpp @@ -1,5 +1,5 @@ // -// Copyright 2010 Ettus Research LLC +// Copyright 2011-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 @@ -68,7 +68,13 @@ static inline void set_optimum_defaults(uhd::usrp::multi_usrp::sptr usrp){ const uhd::fs_path tx_fe_path = "/mboards/0/dboards/A/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 or tx_name.find("SBX") != std::string::npos){ + if (tx_name.find("WBX") != std::string::npos){ + usrp->set_tx_gain(0); + } + else if (tx_name.find("SBX") != std::string::npos){ + usrp->set_tx_gain(0); + } + else if (tx_name.find("RFX") != std::string::npos){ usrp->set_tx_gain(0); } else{ @@ -77,7 +83,13 @@ static inline void set_optimum_defaults(uhd::usrp::multi_usrp::sptr usrp){ const uhd::fs_path rx_fe_path = "/mboards/0/dboards/A/tx_frontends/0"; const std::string rx_name = tree->access<std::string>(rx_fe_path / "name").get(); - if (rx_name.find("WBX") != std::string::npos or rx_name.find("SBX") != std::string::npos){ + if (rx_name.find("WBX") != std::string::npos){ + usrp->set_rx_gain(25); + } + else if (rx_name.find("SBX") != std::string::npos){ + usrp->set_rx_gain(25); + } + else if (rx_name.find("RFX") != std::string::npos){ usrp->set_rx_gain(25); } else{ diff --git a/host/utils/usrp_n2xx_net_burner.py b/host/utils/usrp_n2xx_net_burner.py index f2cfb8ecf..8f16de501 100755 --- a/host/utils/usrp_n2xx_net_burner.py +++ b/host/utils/usrp_n2xx_net_burner.py @@ -222,7 +222,9 @@ def enumerate_devices(): pkt = sock.recv(UDP_MAX_XFER_BYTES) (proto_ver, pktid, rxseq, ip_addr) = unpack_flash_ip_fmt(pkt) if(pktid == update_id_t.USRP2_FW_UPDATE_ID_OHAI_OMG): - yield socket.inet_ntoa(struct.pack("<L", socket.ntohl(ip_addr))) + use_addr = socket.inet_ntoa(struct.pack("<L", socket.ntohl(ip_addr))) + burner = burner_socket(use_addr, True) + yield "%s (%s)" % (socket.inet_ntoa(struct.pack("<L", socket.ntohl(ip_addr))), n2xx_revs[burner.get_hw_rev()][0]) except socket.timeout: still_goin = False @@ -230,12 +232,13 @@ def enumerate_devices(): # Burner class, holds a socket and send/recv routines ######################################################################## class burner_socket(object): - def __init__(self, addr): + def __init__(self, addr, quiet): self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + self._quiet = quiet self._sock.settimeout(UDP_TIMEOUT) self._sock.connect((addr, UDP_FW_UPDATE_PORT)) self.set_callbacks(lambda *a: None, lambda *a: None) - self.init_update() #check that the device is there + self.init_update(quiet) #check that the device is there self.get_hw_rev() def set_callbacks(self, progress_cb, status_cb): @@ -247,13 +250,13 @@ class burner_socket(object): return self._sock.recv(UDP_MAX_XFER_BYTES) #just here to validate comms - def init_update(self): + def init_update(self,quiet): out_pkt = pack_flash_args_fmt(USRP2_FW_PROTO_VERSION, update_id_t.USRP2_FW_UPDATE_ID_OHAI_LOL, seq(), 0, 0) try: in_pkt = self.send_and_recv(out_pkt) except socket.timeout: raise Exception("No response from device") (proto_ver, pktid, rxseq, ip_addr) = unpack_flash_ip_fmt(in_pkt) if pktid == update_id_t.USRP2_FW_UPDATE_ID_OHAI_OMG: - print("USRP-N2XX found.") + if not quiet: print("USRP-N2XX found.") else: raise Exception("Invalid reply received from device.") @@ -488,6 +491,7 @@ if __name__=='__main__': if options.list: print('Possible network devices:') print(' ' + '\n '.join(enumerate_devices())) + #enumerate_devices() exit() if not options.addr: raise Exception('no address specified') @@ -500,7 +504,7 @@ if __name__=='__main__': response = raw_input("""Type "yes" to continue, or anything else to quit: """) if response != "yes": sys.exit(0) - burner = burner_socket(addr=options.addr) + burner = burner_socket(addr=options.addr,quiet=False) if options.read: if options.fw: diff --git a/host/utils/usrp_n2xx_net_burner_gui.py b/host/utils/usrp_n2xx_net_burner_gui.py index e2b79e72c..a9150bd88 100755 --- a/host/utils/usrp_n2xx_net_burner_gui.py +++ b/host/utils/usrp_n2xx_net_burner_gui.py @@ -96,7 +96,11 @@ class DeviceEntryWidget(tkinter.Frame): tkinter.Button(self, text="Rescan for Devices", command=self._reload_cb).pack() self._hints = tkinter.Listbox(self) + self._hints_addrs_only = tkinter.Listbox(self) + self._hints.bind("<<ListboxSelect>>", self._listbox_cb) + self._hints_addrs_only.bind("<<ListboxSelect>>", self._listbox_cb) + self._reload_cb() self._hints.pack(expand=tkinter.YES, fill=tkinter.X) @@ -112,10 +116,11 @@ class DeviceEntryWidget(tkinter.Frame): self._hints.delete(0, tkinter.END) for hint in usrp_n2xx_net_burner.enumerate_devices(): self._hints.insert(tkinter.END, hint) + self._hints_addrs_only.insert(tkinter.END, hint.split(" (")[0]) def _listbox_cb(self, event): try: - sel = self._hints.get(self._hints.curselection()[0]) + sel = self._hints_addrs_only.get(self._hints.curselection()[0]) self._entry.delete(0, tkinter.END) self._entry.insert(0, sel) except Exception as e: print(e) @@ -196,7 +201,7 @@ class USRPN2XXNetBurnerApp(tkinter.Frame): self._disable_input() try: #make a new burner object and attempt the burner operation - burner = usrp_n2xx_net_burner.burner_socket(addr=addr) + burner = usrp_n2xx_net_burner.burner_socket(addr=addr,quiet=False) for (image_type, fw_img, fpga_img) in (('FPGA', '', fpga), ('Firmware', fw, '')): #setup callbacks that update the gui diff --git a/host/utils/usrp_n2xx_simple_net_burner.cpp b/host/utils/usrp_n2xx_simple_net_burner.cpp new file mode 100644 index 000000000..ce2e9a9fc --- /dev/null +++ b/host/utils/usrp_n2xx_simple_net_burner.cpp @@ -0,0 +1,518 @@ +// +// 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 <map> +#include <fstream> +#include <time.h> +#include <vector> + +#include <boost/foreach.hpp> +#include <boost/asio.hpp> +#include <boost/program_options.hpp> +#include <boost/assign.hpp> +#include <boost/assign/list_of.hpp> +#include <boost/format.hpp> +#include <boost/algorithm/string/erase.hpp> +#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/utils/byteswap.hpp> +#include <uhd/utils/images.hpp> +#include <uhd/utils/safe_main.hpp> +#include <uhd/utils/safe_call.hpp> + +namespace po = boost::program_options; +using namespace boost::algorithm; +using namespace uhd; +using namespace uhd::transport; + +//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") +; + +//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; + +//For non-standard images not covered by uhd::find_image_path() +bool does_image_exist(std::string image_filepath){ + + std::ifstream ifile((char*)image_filepath.c_str()); + return ifile; +} + +/*********************************************************************** + * Custom filename validation functions + **********************************************************************/ + +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)); + + //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){ + + //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)); + + //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))); + } +} + +/*********************************************************************** + * Grabbing and validating image binaries + **********************************************************************/ + +int grab_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 + 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)); + } + + //Check sequence of bytes in image + 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){ + is_good = true; + break; + } + } + + if(!is_good) throw std::runtime_error("Not a valid FPGA image."); + + //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]; + + //Checking validity of image + 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)); + } + + //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."); + + //Return image size + return fw_image_size; +} + +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); + 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){ + 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))); + } + + return flash_info; +} + +/*********************************************************************** + * Image burning functions + **********************************************************************/ + +void erase_image(udp_simple::sptr udp_transport, bool is_fw, boost::uint32_t memory_size){ + + //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."); + } + + //Setting 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); + + //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.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); + } + + //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(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))); + } + + //Check for erase completion + erase_pkt.id = htonx<boost::uint32_t>(USRP2_FW_UPDATE_ID_R_U_DONE_ERASING_LOL); + 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; + 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))); + } + } +} + +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; + + //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]; + 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.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++){ + 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))); + } + + current_addr += FLASH_DATA_PACKET_SIZE; + } + 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; + + //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]; + + //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]; + 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.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++){ + 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))); + } + for(int j = 0; j < FLASH_DATA_PACKET_SIZE; j++) from_usrp[current_index+j] = update_data_in->data.flash_args.data[j]; + + current_addr += FLASH_DATA_PACKET_SIZE; + current_index += FLASH_DATA_PACKET_SIZE; + } + for(int i = 0; i < image_size; i++) if(from_usrp[i] != image[i]) throw std::runtime_error("Image write failed."); + + 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.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){ + throw std::runtime_error("USRP reset failed."); //There should be no response to this UDP packet + } + else std::cout << "Resetting USRP." << std::endl; +} + +int UHD_SAFE_MAIN(int argc, char *argv[]){ + + //Establish user options + std::string fw_path; + std::string ip_addr; + std::string fpga_path; + + po::options_description desc("Allowed options:"); + desc.add_options() + ("help", "Display this help message.") + ("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.") + ("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 + 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; + } + + bool burn_fpga = (vm.count("no_fpga") == 0); + bool burn_fw = (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); + + if(!burn_fpga && !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); + } + + std::cout << boost::format(" * %s (%s)\n") % udp_bc_transport->get_recv_addr() % filename_map[hw_rev]; + } + + } + return EXIT_FAILURE; + } + 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 = ""; + + //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 validate_custom_fpga_file(filename_map[hw_rev], fpga_path); + + grab_fpga_image(fpga_path); + } + if(burn_fw){ + if(!use_custom_fw) fw_path = find_image_path(default_fw_filename); + else validate_custom_fw_file(filename_map[hw_rev], fw_path); + + grab_fw_image(fw_path); + } + + std::cout << "Will burn the following images:" << std::endl; + if(burn_fw) std::cout << boost::format(" * Firmware: %s\n") % fw_path; + if(burn_fpga) std::cout << boost::format(" * FPGA: %s\n") % fpga_path; + std::cout << std::endl; + + boost::uint32_t* flash_info = get_flash_info(ip_addr); + std::cout << boost::format("Querying %s for flash information.\n") % filename_map[hw_rev]; + std::cout << boost::format(" * Flash size: %3.2f\n") % flash_info[1]; + std::cout << boost::format(" * Sector size: %3.2f\n\n") % flash_info[0]; + + //Burning images + + if(burn_fpga){ + erase_image(udp_transport, false, flash_info[1]); + write_image(udp_transport, false, fpga_image, flash_info[1], fpga_image_size); + verify_image(udp_transport, false, fpga_image, flash_info[1], fpga_image_size); + } + if(burn_fpga and burn_fw) std::cout << std::endl; //Formatting + if(burn_fw){ + erase_image(udp_transport, true, flash_info[1]); + write_image(udp_transport, true, fw_image, flash_info[1], fw_image_size); + verify_image(udp_transport, true, fw_image, flash_info[1], fw_image_size); + } + + //Prompt user to reset USRP + std::string user_response = ""; + bool reset = false; + while(user_response != "yes" and user_response != "no" and user_response != "y" and user_response != "n"){ + std::cout << std::endl << "Image burning successful. Reset USRP (yes/no)? "; + std::getline(std::cin, user_response); + std::transform(user_response.begin(), user_response.end(), user_response.begin(), ::tolower); + reset = (user_response == "yes" or user_response == "y"); + } + 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 new file mode 100644 index 000000000..f386c3620 --- /dev/null +++ b/host/utils/usrp_simple_burner_utils.hpp @@ -0,0 +1,99 @@ +// +// 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; |