diff options
author | Ben Hilburn <ben.hilburn@ettus.com> | 2014-02-04 11:04:07 -0800 |
---|---|---|
committer | Ben Hilburn <ben.hilburn@ettus.com> | 2014-02-04 11:04:07 -0800 |
commit | 178ac3f1c9950d383c8f64b3df464c0f943c4a23 (patch) | |
tree | 318ed621a7b59b7d34d4ce6e4a92f73f0bcef509 /host | |
parent | 2718ac110fa931cc29daf7cb3dc5ab6230ee02ab (diff) | |
download | uhd-178ac3f1c9950d383c8f64b3df464c0f943c4a23.tar.gz uhd-178ac3f1c9950d383c8f64b3df464c0f943c4a23.tar.bz2 uhd-178ac3f1c9950d383c8f64b3df464c0f943c4a23.zip |
Merging USRP X300 and X310 support!!
Diffstat (limited to 'host')
151 files changed, 14775 insertions, 551 deletions
diff --git a/host/CMakeLists.txt b/host/CMakeLists.txt index ded4bb54c..cd4d87ce5 100644 --- a/host/CMakeLists.txt +++ b/host/CMakeLists.txt @@ -137,6 +137,7 @@ SET(BOOST_REQUIRED_COMPONENTS system thread unit_test_framework + serialization ) IF(UNIX AND NOT BOOST_ROOT AND EXISTS "/usr/lib64") @@ -213,8 +214,8 @@ UHD_INSTALL(FILES # Images download directory for utils/uhd_images_downloader.py ######################################################################## -SET(UHD_IMAGES_MD5SUM "0bab468817baa28024c4bf966d923b31") -SET(UHD_IMAGES_DOWNLOAD_SRC "http://files.ettus.com/binaries/maint_images/archive/uhd-images_003.006.002-release.zip") +SET(UHD_IMAGES_MD5SUM "cf0d5744a4cfe289554309159d1d5259") +SET(UHD_IMAGES_DOWNLOAD_SRC "http://files.ettus.com/binaries/maint_images/archive/uhd-images_003.007.000-release.zip") ######################################################################## # Register top level components diff --git a/host/cmake/Modules/FindUDev.cmake b/host/cmake/Modules/FindUDev.cmake new file mode 100644 index 000000000..4c8390db6 --- /dev/null +++ b/host/cmake/Modules/FindUDev.cmake @@ -0,0 +1,19 @@ +# - Try to find UDev +# Once done this will define +# +# UDEV_FOUND - system has UDev +# UDEV_INCLUDE_DIR - the libudev include directory +# UDEV_LIBS - The libudev libraries + +# Copyright (c) 2010, Rafael Fernández López, <ereslibre@kde.org> +# +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. + +find_path(UDEV_INCLUDE_DIR libudev.h) +find_library(UDEV_LIBS udev) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(UDev DEFAULT_MSG UDEV_INCLUDE_DIR UDEV_LIBS) + +mark_as_advanced(UDEV_INCLUDE_DIR UDEV_LIBS) diff --git a/host/cmake/Modules/UHDVersion.cmake b/host/cmake/Modules/UHDVersion.cmake index b0d04fe70..41c3ac20b 100644 --- a/host/cmake/Modules/UHDVersion.cmake +++ b/host/cmake/Modules/UHDVersion.cmake @@ -1,5 +1,5 @@ # -# Copyright 2010-2013 Ettus Research LLC +# Copyright 2010-2014 Ettus Research LLC # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -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 006) -SET(UHD_VERSION_PATCH 002) +SET(UHD_VERSION_MINOR 007) +SET(UHD_VERSION_PATCH 000) ######################################################################## # Set up trimmed version numbers for DLL resource files and packages diff --git a/host/docs/CMakeLists.txt b/host/docs/CMakeLists.txt index fa6e98918..0163a5c20 100644 --- a/host/docs/CMakeLists.txt +++ b/host/docs/CMakeLists.txt @@ -25,8 +25,10 @@ SET(manual_sources calibration.rst coding.rst dboards.rst + gpio_api.rst gpsdo.rst gpsdo_b2x0.rst + gpsdo_x3x0.rst general.rst images.rst stream.rst @@ -37,6 +39,7 @@ SET(manual_sources usrp_b100.rst usrp_b200.rst usrp_e1x0.rst + usrp_x3x0.rst ) ######################################################################## @@ -81,6 +84,14 @@ IF(ENABLE_MANUAL) #make the html manual a build-time dependency ADD_CUSTOM_TARGET(manual_html ALL DEPENDS ${manual_html_files}) UHD_INSTALL(FILES ${manual_sources} DESTINATION ${PKG_DOC_DIR}/manual/rst COMPONENT manual) + + #resources for html manual + ADD_CUSTOM_COMMAND( + TARGET manual_html POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/res ${CMAKE_CURRENT_BINARY_DIR}/res + ) + UHD_INSTALL(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/res DESTINATION ${PKG_DOC_DIR}/manual/html COMPONENT manual) + ENDIF(ENABLE_MANUAL) ######################################################################## @@ -128,6 +139,7 @@ SET(man_page_sources uhd_images_downloader.1 uhd_usrp_probe.1 usrp_n2xx_simple_net_burner.1 + usrp_x3xx_fpga_burner.1 usrp2_card_burner.1 ) @@ -137,7 +149,12 @@ SET(man_page_sources MESSAGE(STATUS "") FIND_PACKAGE(GZip) -LIBUHD_REGISTER_COMPONENT("Man Pages" ENABLE_MAN_PAGES ON "GZIP_FOUND;LINUX" OFF) +# No elegant way in CMake to reverse a boolean +IF(NOT WIN32) + SET(NOT_WIN32 TRUE) +ENDIF(NOT WIN32) + +LIBUHD_REGISTER_COMPONENT("Man Pages" ENABLE_MAN_PAGES ON "GZIP_FOUND;NOT_WIN32" OFF) IF(ENABLE_MAN_PAGES) #Generate man pages diff --git a/host/docs/calibration.rst b/host/docs/calibration.rst index 8ba3477b8..23bef01b7 100644 --- a/host/docs/calibration.rst +++ b/host/docs/calibration.rst @@ -5,14 +5,15 @@ UHD - Calibration Application Notes .. contents:: Table of Contents ------------------------------------------------------------------------ -Self-calibration +Self-Calibration ------------------------------------------------------------------------ -UHD software 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. -UHD software will automatically apply corrections at runtime when the user re-tunes the daughterboard LO. -Calibration results are specific to an individual RF board. +UHD software 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. UHD software will automatically apply corrections at runtime when +the user re-tunes the daughterboard LO. Calibration results are specific to an +individual RF board. **Note:** When a calibration table is present, @@ -27,10 +28,10 @@ UHD software comes with the following calibration utilities: The following RF frontends are supported by the self-calibration utilities: - * RFX transceiver board - * WBX transceiver board - * SBX transceiver board - * CBX transceiver board + * RFX Series transceiver boards + * WBX Series transceiver boards + * SBX Series transceiver boards + * CBX Series transceiver boards ******************************************** Calibration Utilities @@ -54,7 +55,7 @@ may not have a serial number. If this is the case, run the following command to into the daughterboard's EEPROM: :: - <install dir>/share/uhd/utils/usrp_burn_db_eeprom --ser=<desired serial> --args=<optional device args> + <install dir>/lib/uhd/utils/usrp_burn_db_eeprom --ser=<desired serial> --args=<optional device args> ******************************************** Calibration Data diff --git a/host/docs/dboards.rst b/host/docs/dboards.rst index 4b5a074a8..d6cbc6151 100644 --- a/host/docs/dboards.rst +++ b/host/docs/dboards.rst @@ -26,15 +26,15 @@ The boards have no tunable elements or programmable gains. Through the magic of aliasing, you can down-convert signals greater than the Nyquist rate of the ADC. -BasicRX Bandwidth (Hz): +BasicRX Bandwidth: -* **For Real-Mode (A or B frontend)**: 250M -* **For Complex (AB or BA frontend)**: 500M +* **For Real-Mode (A or B frontend)**: 250 MHz +* **For Complex (AB or BA frontend)**: 500 MHz -LFRX Bandwidth (Hz): +LFRX Bandwidth: -* **For Real-Mode (A or B frontend)**: 33M -* **For Complex (AB or BA frontend)**: 66M +* **For Real-Mode (A or B frontend)**: 33 MHz +* **For Complex (AB or BA frontend)**: 66 MHz ^^^^^^^^^^^^^^^^^^^^^^^^^^^ Basic TX and LFTX @@ -50,15 +50,15 @@ The boards have no tunable elements or programmable gains. Through the magic of aliasing, you can up-convert signals greater than the Nyquist rate of the DAC. -BasicTX Bandwidth (Hz): 250M +BasicTX Bandwidth (Hz): -* **For Real-Mode (A or B frontend**): 250M -* **For Complex (AB or BA frontend)**: 500M +* **For Real-Mode (A or B frontend**): 250 MHz +* **For Complex (AB or BA frontend)**: 500 MHz -LFTX Bandwidth (Hz): 33M +LFTX Bandwidth (Hz): -* **For Real-Mode (A or B frontend)**: 33M -* **For Complex (AB or BA frontend)**: 66M +* **For Real-Mode (A or B frontend)**: 33 MHz +* **For Complex (AB or BA frontend)**: 66 MHz ^^^^^^^^^^^^^^^^^^^^^^^^^^^ DBSRX @@ -77,7 +77,7 @@ Receive Gains: * **GC1**, Range: 0-56dB * **GC2**, Range: 0-24dB -Bandwidth (Hz): 8M-66M +Bandwidth: 8 MHz - 66 MHz Sensors: @@ -100,7 +100,7 @@ Receive Gains: * **GC1**, Range: 0-73dB * **BBG**, Range: 0-15dB -Bandwidth (Hz): 8M-80M +Bandwidth (Hz): 8 MHz -80 MHz Sensors: @@ -128,10 +128,10 @@ the receive antenna will always be set to RX2, regardless of the settings. Receive Gains: **PGA0**, Range: 0-70dB (except RFX400 range is 0-45dB) -Bandwidths (Hz): +Bandwidth: -* **RX**: 40M -* **TX**: 40M +* **RX**: 40 MHz +* **TX**: 40 MHz Sensors: @@ -142,10 +142,10 @@ XCVR 2450 ^^^^^^^^^^^^^^^^^^^^^^^^^^^ The XCVR2450 has 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 XCVR2450 has a non-contiguous tuning range consisting of a -high band (4.9-6.0GHz) and a low band (2.4-2.5GHz). +high band (4.9-6.0 GHz) and a low band (2.4-2.5 GHz). Transmit Antennas: **J1** or **J2** @@ -170,10 +170,10 @@ Receive Gains: * **LNA**, Range: 0-30.5dB * **VGA**, Range: 0-62dB -Bandwidths (Hz): +Bandwidths: -* **RX**: 15M, 19M, 28M, 36M; (each +-0, 5, or 10%) -* **TX**: 24M, 36M, 48M +* **RX**: 15 MHz, 19 MHz, 28 MHz, 36 MHz; (each +-0, 5, or 10%) +* **TX**: 24 MHz, 36 MHz, 48 MHz Sensors: @@ -183,12 +183,18 @@ Sensors: ^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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**. -The WBX Series boards have independent receive and transmit LO's and synthesizers -allowing full-duplex operation on different transmit and receive frequencies. +Features: + +* 2 quadrature frontends (1 transmit, 1 receive) + + * Defaults to direct conversion + * Can be used in low IF mode through lo_offset with **uhd::tune_request_t** + +* Independent recieve and transmit LO's and synthesizers + + * Allows for full-duplex operation on different transmit and receive frequencies + * Can be set to use Integer-N tuning for better spur performance with **uhd::tune_request_t** Transmit Antennas: **TX/RX** @@ -196,18 +202,16 @@ Receive Antennas: **TX/RX** or **RX2** * **Frontend 0:** Complex baseband signal for selected antenna -The user may set the receive antenna to be TX/RX or RX2. -However, when using an WBX board in full-duplex mode, -the receive antenna will always be set to RX2, regardless of the settings. +* **Note:** The user may set the receive antenna to be TX/RX or RX2. However, when using a WBX board in full-duplex mode, the receive antenna will always be set to RX2, regardless of the settings. Transmit Gains: **PGA0**, Range: 0-25dB Receive Gains: **PGA0**, Range: 0-31.5dB -Bandwidths (Hz): +Bandwidths: -* **RX**: 40M -* **TX**: 40M +* **WBX**: 40 MHz, RX & TX +* **WBX-120**: 120 MHz, RX & TX Sensors: @@ -216,12 +220,18 @@ Sensors: ^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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**. -The SBX Series boards have independent receive and transmit LO's and synthesizers -allowing full-duplex operation on different transmit and receive frequencies. +Features: + +* 2 quadrature frontends (1 transmit, 1 receive) + + * Defaults to direct conversion + * Can be used in low IF mode through lo_offset with **uhd::tune_request_t** + +* Independent recieve and transmit LO's and synthesizers + + * Allows for full-duplex operation on different transmit and receive frequencies + * Can be set to use Integer-N tuning for better spur performance with **uhd::tune_request_t** Transmit Antennas: **TX/RX** @@ -229,18 +239,16 @@ Receive Antennas: **TX/RX** or **RX2** * **Frontend 0:** Complex baseband signal for selected antenna -The user may set the receive antenna to be TX/RX or RX2. -However, when using an SBX board in full-duplex mode, -the receive antenna will always be set to RX2, regardless of the settings. +* **Note:** The user may set the receive antenna to be TX/RX or RX2. However, when using an SBX board in full-duplex mode, the receive antenna will always be set to RX2, regardless of the settings. Transmit Gains: **PGA0**, Range: 0-31.5dB Receive Gains: **PGA0**, Range: 0-31.5dB -Bandwidths (Hz): +Bandwidths: -* **RX**: 40M -* **TX**: 40M +* **SBX**: 40 MHz, RX & TX +* **SBX-120**: 120 MHz, RX & TX Sensors: @@ -248,16 +256,56 @@ Sensors: LEDs: -* All LEDs flash when dboard control is initialized +* All LEDs flash when daughterboard control is initialized * **TX LD**: Transmit Synthesizer Lock Detect * **TX/RX**: Receiver on TX/RX antenna port (No TX) * **RX LD**: Receive Synthesizer Lock Detect * **RX1/RX2**: Receiver on RX2 antenna port ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -CBX +CBX Series ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -See SBX Series for more details. + +Features: + +* 2 quadrature frontends (1 transmit, 1 receive) + + * Defaults to direct conversion + * Can be used in low IF mode through lo_offset with **uhd::tune_request_t** + +* Independent recieve and transmit LO's and synthesizers + + * Allows for full-duplex operation on different transmit and receive frequencies + * Can be set to use Integer-N tuning for better spur performance with **uhd::tune_request_t** + +Transmit Antennas: **TX/RX** + +Receive Antennas: **TX/RX** or **RX2** + +* **Frontend 0:** Complex baseband signal for selected antenna + +* **Note:** The user may set the receive antenna to be TX/RX or RX2. However, when using a CBX board in full-duplex mode, the receive antenna will always be set to RX2, regardless of the settings. + +Transmit Gains: **PGA0**, Range: 0-31.5dB + +Receive Gains: **PGA0**, Range: 0-31.5dB + +Bandwidths: + +* **CBX**: 40 MHz, RX & TX +* **CBX-120**: 120 MHz, RX & TX + +Sensors: + +* **lo_locked**: boolean for LO lock state + +LEDs: + +* All LEDs flash when daughterboard control is initialized +* **TX LD**: Transmit Synthesizer Lock Detect +* **TX/RX**: Receiver on TX/RX antenna port (No TX) +* **RX LD**: Receive Synthesizer Lock Detect +* **RX1/RX2**: Receiver on RX2 antenna port ^^^^^^^^^^^^^^^^^^^^^^^^^^^ TVRX @@ -274,7 +322,7 @@ Receive Gains: * **RF**, Range: -13.3-50.3dB (frequency-dependent) * **IF**, Range: -1.5-32.5dB -Bandwidth: 6MHz +Bandwidth: 6 MHz ^^^^^^^^^^^^^^^^^^^^^^^^^^^ TVRX2 @@ -294,7 +342,7 @@ Receive Gains: * **IF**, Range: 0.0-30.0dB -Bandwidth: 1.7MHz, 6MHz, 7MHz, 8MHz, 10MHz +Bandwidth: 1.7 MHz, 6 MHz, 7 MHz, 8 MHz, 10 MHz Sensors: @@ -302,15 +350,6 @@ Sensors: * **rssi**: float for measured RSSI in dBm * **temperature**: float for measured temperature in degC ------------------------------------------------------------------------- -Daughterboard Modifications ------------------------------------------------------------------------- - -Sometimes, daughterboards will require modification -to work on certain frequencies or to work with certain hardware. -Modification usually involves moving/removing an SMT component -and burning a new daughterboard ID into the EEPROM. - ^^^^^^^^^^^^^^^^^^^^^^^^^^^ DBSRX - Mod ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -324,20 +363,20 @@ 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**, 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** With the daughterboard plugged-in, run the following commands: :: - cd <install-path>/share/uhd/utils + cd <install-path>/lib/uhd/utils ./usrp_burn_db_eeprom --id=0x000d --unit=RX --args=<args> --slot=<slot> * **<args>** are device address arguments (optional if only one USRP device is on your machine) @@ -357,14 +396,14 @@ 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. +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** With the daughterboard plugged-in, run the following commands: :: - cd <install-path>/share/uhd/utils + cd <install-path>/lib/uhd/utils ./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> diff --git a/host/docs/general.rst b/host/docs/general.rst index b116ba816..01d0f270c 100644 --- a/host/docs/general.rst +++ b/host/docs/general.rst @@ -28,6 +28,17 @@ 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 the UHD software handle the rest. +The **tune_request_t** object can also be used with certain daughterboards to use +Integer-N tuning instead of the default fractional tuning, allowing for better spur +performance. The daughterboards that support this functionality are: + +* WBX (all revisions) +* WBX-120 +* SBX (all revisions) +* SBX-120 +* CBX +* CBX-120 + Tuning the receive chain: :: @@ -38,10 +49,11 @@ Tuning the receive chain: //advanced tuning with tune_request_t uhd::tune_request_t tune_req(target_frequency_in_hz, desired_lo_offset); + tune_req.args = uhd::device_addr_t("mode_n=int-n"); //to use Int-N tuning //fill in any additional/optional tune request fields... usrp->set_rx_freq(tune_req); -More information can be fonud in *tune_request.hpp*. +More information can be found in `tune_request.hpp <./../../doxygen/html/structuhd_1_1tune__request__t.html>`_. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ RF front-end settling time @@ -142,7 +154,8 @@ When the device's internal buffers become full, streaming is shut off, and an inline message packet is sent to the host. In this case the character "O" is printed to stdout as an indication. If the device was in continuous streaming mode, -the UHD software will automatically restart streaming. +the UHD software will automatically restart streaming when the buffer has +space again. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Underflow notes diff --git a/host/docs/gpio_api.rst b/host/docs/gpio_api.rst new file mode 100644 index 000000000..9fd86d081 --- /dev/null +++ b/host/docs/gpio_api.rst @@ -0,0 +1,129 @@ +======================================================================== +UHD - X3x0 GPIO API +======================================================================== + +.. contents:: Table of Contents + +------------------------------------------------------------------------ +The X3x0 Front Panel GPIO +------------------------------------------------------------------------ +The X3x0 is the first USRP device to offer an auxiliary GPIO connection on the +motherboard itself (independent of the daughterboards). These GPIO pins are +controlled directly by the FPGA, where they are controlled by an ATR (Automatic +Transmit / Receive). This allows them to be toggled simultaneously with other +radio-level changes (e.g., enabling or disabling a TX or RX mixer). + + +^^^^^^^^^^^^^^^^ +Front Panel GPIO +^^^^^^^^^^^^^^^^ + +Connector +::::::::: + +.. image:: ./res/x3x0_gpio_conn.png + :scale: 75% + :align: left + +Pin Mapping +::::::::::: + +* Pin 1: +3.3V +* Pin 2: Data[0] +* Pin 3: Data[1] +* Pin 4: Data[2] +* Pin 5: Data[3] +* Pin 6: Data[4] +* Pin 7: Data[5] +* Pin 8: Data[6] +* Pin 9: Data[7] +* Pin 10: Data[8] +* Pin 11: Data[9] +* Pin 12: Data[10] +* Pin 13: Data[11] +* Pin 14: 0V +* Pin 15: 0V + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Explaining ATR +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +ATR works by defining the value of the GPIO pins for certain states of the +radio. This is the "automatic" part of it. For example, you can tell UHD that +when the radio is transmitting and receiving (full duplex), GPIO6 should be +high, but when it is only transmitting, GPI06 should be low. This state machine +is set up using a series of GPIO attributes, with paired values and a mask, +which you will want to define for the GPIO pins you intend to use. To set up +the ATR, you use the **multi_usrp** function *set_gpio_attr*. + +* **CTRL**: Is this pin controlled by ATR (automatic), or by manual control + only? +* **DDR**: "Data Direction Register" - defines whether or not a GPIO is an + output or an input. +* **OUT**: Manually set the value of a pin (only to be used in non-ATR mode). +* **ATR_0X**: The status of the pins when the radio is **idle**. +* **ATR_RX**: The status of the pins when the radio is only **receiving**. +* **ATR_TX**: The status of the pins when the radio is only **transmitting**. +* **ATR_XX**: The status of the pins when the radio is in **full-duplex** mode. + +The counterpart to setting the ATR (the "getter"), is called *get_gpio_attr*. +It has the exact same attributes as above, and has one more: + +* **READBACK**: Readback the GPIOs marked as inputs. + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +An Example +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +The front panel X3x0 GPIO bank is enumerated in the motherboard property tree +("*<mb_path>/gpio/FP0/*"), and so is easily accessible through the standard +**multi_usrp** UHD interface. + +You can discover this using the *get_gpio_banks* function in **multi_usrp**. +This will tell you that there is a GPIO bank on your X3x0 called "FP0". This is +the bank we want to set-up. + +Let's say we want to use GPIO6 for an external amp. We want it to be +automatically controlled by ATR as an output, and we want it to be high when we +are transmitting, and low in all other cases. We are also using GPIO4, which +we want to control manually, as an output. We can set this up with the following +code: + +:: + + // set up our masks, defining the pin numbers + #define AMP_GPIO_MASK (1 << 6) + #define MAN_GPIO_MASK (1 << 4) + + #define ATR_MASKS (AMP_GPIO_MASK | MAN_GPIO_MASK) + + // set up our values for ATR control: 1 for ATR, 0 for manual + #define ATR_CONTROL (AMP_GPIO_MASK & ~MAN_GPIO_MASK) + + // set up the GPIO directions: 1 for output, 0 for input + #define GPIO_DDR (AMP_GPIO_MASK & ~MAN_GPIO_MASK) + + // assume an existing USRP device handle, called "usrp_x300" + + // now, let's do the basic ATR setup + usrp_x300->set_gpio_attr("FP0", "CTRL", ATR_CONTROL, ATR_MASKS); + usrp_x300->set_gpio_attr("FP0", "DDR", GPIO_DDR, ATR_MASKS); + + // let's manually set GPIO4 high + usrp_x300->set_gpio_attr("FP0", "OUT", 1, MAN_GPIO_MASK); + + // finally, let's set up GPIO6 as we described above + usrp_x300->set_gpio_attr("FP0", "ATR_0X", 0, AMP_GPIO_MASK); + usrp_x300->set_gpio_attr("FP0", "ATR_RX", 0, AMP_GPIO_MASK); + usrp_x300->set_gpio_attr("FP0", "ATR_TX", 0, AMP_GPIO_MASK); + usrp_x300->set_gpio_attr("FP0", "ATR_XX", 0, AMP_GPIO_MASK); + +After the above code is run, the ATR in the FPGA will automatically control +GPIO6, as we have described, based on the radio state, and we have direct +manual control over GPIO4. + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Further Information +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +For more information, see the Doxygen API documentation: + +* `multi_usrp API <./../../doxygen/html/classuhd_1_1usrp_1_1multi__usrp.html>`_ diff --git a/host/docs/gpsdo.rst b/host/docs/gpsdo.rst index 3b718fc20..8ffff8672 100644 --- a/host/docs/gpsdo.rst +++ b/host/docs/gpsdo.rst @@ -4,8 +4,13 @@ UHD - Internal GPSDO Application Notes (USRP-N2x0/E1X0 Models) .. contents:: Table of Contents -This application note describes the use of integrated GPS-disciplined -oscillators with Ettus Research USRP devices. +This application note describes the use of integrated GPS-disciplined oscillators (GPSDOs) for +the USRP N-Series and E1xx. For information regarding the GPSDO that is compatible with +the USRP X-Series, please see: + +`USRP-X3x0 Internal GPSDO Device Manual <./gpsdo_x3x0.html>`_ + +======= ------------------------------------------------------------------------ Specifications @@ -16,11 +21,11 @@ Specifications * **Holdover**: <11us over 3h * **Phase noise**: - * **1Hz:** -80dBc/Hz - * **10Hz:** -110dBc/Hz - * **100Hz:** -135dBc/Hz - * **1kHz:** -145dBc/Hz - * **10kHz:** <-145dBc/Hz + * **1Hz:** -80 dBc/Hz + * **10Hz:** -110 dBc/Hz + * **100Hz:** -135 dBc/Hz + * **1kHz:** -145 dBc/Hz + * **10kHz:** <-145 dBc/Hz **Antenna Types:** @@ -29,7 +34,7 @@ The GPSDO is capable of supplying a 3V for active GPS antennas or supporting pas ------------------------------------------------------------------------ Installation Instructions ------------------------------------------------------------------------ -Installation instructions can be found here: +Instructions for mounting the GPSDO kit onto your USRP device can be found here: `http://www.ettus.com/content/files/gpsdo-kit_2.pdf <http://www.ettus.com/content/files/gpsdo-kit_2.pdf>`_ ******************************************** @@ -40,7 +45,7 @@ Post-installation Task (N-Series only) 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 10 MHz and PPS signals for reference or MIMO use (see the `Synchronization Application Notes <./sync.html>`_), it is not necessary to perform this step. @@ -49,7 +54,7 @@ To configure the USRP to communicate with the GPSDO, use the :: - cd <install-path>/share/uhd/utils + cd <install-path>/lib/uhd/utils ./usrp_burn_mb_eeprom --args=<optional device args> --key=gpsdo --val=internal -- restore original setting -- @@ -61,7 +66,7 @@ Using the GPSDO in Your Application By default, if a GPSDO is detected at startup, the USRP will be configured to use it as a frequency and time reference. The internal VITA timestamp will be initialized to the GPS time, and the internal oscillator will be -phase-locked to the 10MHz GPSDO reference. If the GPSDO is not locked to +phase-locked to the 10 MHz 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 diff --git a/host/docs/gpsdo_b2x0.rst b/host/docs/gpsdo_b2x0.rst index 8bc363e97..a0815d23a 100644 --- a/host/docs/gpsdo_b2x0.rst +++ b/host/docs/gpsdo_b2x0.rst @@ -1,5 +1,5 @@ ======================================================================== -UHD - Internal GPSDO Application Notes (USRP-B2X0 Models) +UHD - Internal GPSDO Application Notes (USRP-B2x0 Models) ======================================================================== .. contents:: Table of Contents @@ -17,21 +17,21 @@ Specifications **Phase noise**: -+------------+-------------+------------+ -| | TCXO | OXCO | -+============+=============+============+ -| **1Hz** | -65dBc/Hz | -75dBc/Hz | -+------------+-------------+------------+ -| **10Hz** | -102dBc/Hz | -110dBc/Hz | -+------------+-------------+------------+ -| **100Hz** | -132dBc/Hz | -132dBc/Hz | -+------------+-------------+------------+ -| **1kHz** | -148dBc/Hz | -142dBc/Hz | -+------------+-------------+------------+ -| **10kHz** | -152dBc/Hz | -145dBc/Hz | -+------------+-------------+------------+ -| **100kHz** | <-155dBc/Hz | -150dBc/Hz | -+------------+-------------+------------+ ++------------+-------------+ +| | TCXO | ++============+=============+ +| **1Hz** | -65dBc/Hz | ++------------+-------------+ +| **10Hz** | -102dBc/Hz | ++------------+-------------+ +| **100Hz** | -132dBc/Hz | ++------------+-------------+ +| **1kHz** | -148dBc/Hz | ++------------+-------------+ +| **10kHz** | -152dBc/Hz | ++------------+-------------+ +| **100kHz** | <-155dBc/Hz | ++------------+-------------+ **Antenna Types:** diff --git a/host/docs/gpsdo_x3x0.rst b/host/docs/gpsdo_x3x0.rst new file mode 100644 index 000000000..0d4d31f3b --- /dev/null +++ b/host/docs/gpsdo_x3x0.rst @@ -0,0 +1,82 @@ +======================================================================== +UHD - Internal GPSDO Application Notes (USRP-X3x0 Models) +======================================================================== + +.. contents:: Table of Contents + +This application note describes the use of the board-mounted GPS Disciplined OCXO, +as used with the USRP X300/X310. For information regarding the GPSDO that is +compatible with the USRP N2xx or E1xx, please see: + +`USRP-N2x0/E1x0 Internal GPSDO Device Manual <./gpsdo.html>`_ + +------------------------------------------------------------------------ +Specifications +------------------------------------------------------------------------ +* **Receiver type**: 50 channel with WAAS, EGNOS, MSAS +* **10 MHz ADEV**: 5e-11 over >24h +* **1PPS RMS jitter**: <50ns 1-sigma +* **Holdover**: <20us over 3h + +**Phase noise**: + ++------------+------------+ +| | OCXO | ++============+============+ +| **1Hz** | -75dBc/Hz | ++------------+------------+ +| **10Hz** | -110dBc/Hz | ++------------+------------+ +| **100Hz** | -132dBc/Hz | ++------------+------------+ +| **1kHz** | -142dBc/Hz | ++------------+------------+ +| **10kHz** | -145dBc/Hz | ++------------+------------+ +| **100kHz** | -150dBc/Hz | ++------------+------------+ + +**Antenna Types:** + +The GPSDO is capable of supplying a 3V for active GPS antennas or supporting passive antennas. + +------------------------------------------------------------------------ +Installation Instructions +------------------------------------------------------------------------ +To install the GPSDO, you must insert it into the slot on the board +near the 10 MHz Reference SMA. Keep in mind that the two sides of the +GPSDO have a different number of pins. When inserting the GPSDO, make +sure to press down firmly and evenly. When turning on the USRP B2X0 device, +a green LED should illuminate on the GPSDO. This signifies that the unit +has successfully been placed. + +**NOTE: The pins on the GPSDO are very fragile. Be sure to press down +evenly, or the pins may bend or break. Once the GPSDO is in place, +we very highly discourage further removal, as this also risks damaging +the pins.** + +------------------------------------------------------------------------ +Using the GPSDO in Your Application +------------------------------------------------------------------------ +By default, if a GPSDO is detected at startup, the USRP will be configured +to use it as a frequency and time reference. The internal VITA timestamp +will be initialized to the GPS time, and the internal oscillator will be +phase-locked to the 10MHz GPSDO reference. If the GPSDO is not locked to +satellites, the VITA time will not be initialized. + +GPS data is obtained through the **mboard_sensors** interface. To retrieve +the current GPS time, use the **gps_time** sensor: + +:: + + usrp->get_mboard_sensor("gps_time"); + +The returned value will be the current epoch time, in seconds since +January 1, 1970. This value is readily converted into human-readable +format using the **time.h** library in C, **boost::posix_time** in C++, etc. + +Other information can be fetched as well. You can query the lock status +with the **gps_locked** sensor, as well as obtain raw NMEA sentences using +the **gps_gprmc**, and **gps_gpgga** sensors. Location +information can be parsed out of the **gps_gpgga** sensor by using **gpsd** or +another NMEA parser. diff --git a/host/docs/identification.rst b/host/docs/identification.rst index ba9b30898..cbae25082 100644 --- a/host/docs/identification.rst +++ b/host/docs/identification.rst @@ -1,38 +1,40 @@ -======================================================================== +================================= UHD - Device Identification Notes -======================================================================== +================================= .. contents:: Table of Contents ------------------------------------------------------------------------- +------------------------ Identifying USRP Devices ------------------------------------------------------------------------- +------------------------ 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 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 -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^^^^^^^^^ Every device has several ways of identifying it on the host system: -+------------+------------+--------------------------------------------+ -| Identifier | Key | Notes | -+============+============+============================================+ -| Serial | serial | globally unique identifier | -+------------+------------+--------------------------------------------+ -| Address | addr | unique identifier on a network | -+------------+------------+--------------------------------------------+ -| Name | name | optional user-set identifier | -+------------+------------+--------------------------------------------+ -| Type | type | hardware series identifier | -+------------+------------+--------------------------------------------+ - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ++------------+----------+-----------------------------------------------------------+------------------------------- +| Identifier | Key | Notes | Example ++============+==========+===========================================================+=============================== +| Serial | serial | globally unique identifier | 12345678 ++------------+----------+-----------------------------------------------------------+---------------------------- +| Address | addr | unique identifier on a network | 192.168.10.2 ++------------+----------+-----------------------------------------------------------+------------------------------- +| Resource | resource | unique identifier for USRP RIO devices (over PCI Express) | RIO0 ++------------+----------+-----------------------------------------------------------+------------------------------- +| Name | name | optional user-set identifier | my_usrp1 (User-defined value) ++------------+----------+-----------------------------------------------------------+---------------------------- +| Type | type | hardware series identifier | usrp1, usrp2, ++------------+----------+-----------------------------------------------------------+---------------------------- + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Device discovery via command line -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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. @@ -52,9 +54,9 @@ Device address arguments can be supplied to narrow the scope of the search. uhd_find_devices --args="serial=12345678" -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Device discovery through the API -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The **device::find()** API call searches for devices and returns a list of discovered devices. :: @@ -76,21 +78,22 @@ The **hint** argument can be populated to narrow the scope of the search. hint["serial"] = "12345678"; uhd::device_addrs_t dev_addrs = uhd::device::find(hint); -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^ Device properties -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^ 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:** + :: uhd_usrp_probe --args <device-specific-address-args> ------------------------------------------------------------------------- +-------------------- Naming a USRP Device ------------------------------------------------------------------------- +-------------------- For convenience purposes, users may assign a custom name to their USRP device. The USRP device can then be identified via name, rather than a difficult to remember serial or address. @@ -100,22 +103,24 @@ A name has the following properties: * is 0-20 characters * is not required to be unique -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^ Set a custom name -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^ Run the following commands: + :: - cd <install-path>/share/uhd/utils + cd <install-path>/lib/uhd/utils ./usrp_burn_mb_eeprom --args=<optional device args> --key=name --val=lab1_xcvr -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^^ Discovery via name -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^^ The keyword **name** can be used to narrow the scope of the search. Example with the find devices utility: + :: uhd_find_devices --args="name=lab1_xcvr" diff --git a/host/docs/images.rst b/host/docs/images.rst index 95e2d96b0..37fbabf4b 100644 --- a/host/docs/images.rst +++ b/host/docs/images.rst @@ -12,9 +12,12 @@ 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. -* **USRP-N Series:** The user must manually transfer the images over ethernet. +* **USRP-N Series:** The user programs an image into on-board storage, which + then is automatically loaded at runtime. * **USRP-E Series:** The host code will automatically load the FPGA at runtime. * **USRP-B Series:** The host code will automatically load the FPGA at runtime. +* **USRP-X Series:** The user programs an image into on-board storage, which + then is automatically loaded at runtime. ------------------------------------------------------------------------ Pre-built Images @@ -25,8 +28,6 @@ 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: * bundled with UHD software in a platform-specific installer @@ -36,11 +37,10 @@ The pre-built images come in two forms: 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. +The UHD images downloader downloads UHD images 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 can be found at: **<install-path>/lib/uhd/utils/uhd_images_downloader.py** By default, it installs images to: **<install-path>/share/uhd/images** @@ -79,10 +79,25 @@ The build commands for a particular image can be found in **<uhd-repo-path>/imag ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Xilinx FPGA builds ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Xilinx ISE 12.x and up is required to build the Xilinx FPGA images. +USRP Xilinx FPGA images are built with two different versions of ISE, depending +on the device. + 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**. + +**Xilinx ISE 14.4** +* USRP X3x0 Series + +See **<uhd-repo-path>/fpga/usrp3/top/**. + +**Xilinx ISE 12.2** +* USRP N2x0 +* USRP B2x0 +* USRP B1x0 +* USRP E1x0 +* USRP2 + See **<uhd-repo-path>/fpga/usrp2/top/**. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -97,7 +112,7 @@ See **<uhd-repo-path>/firmware/zpu**. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Altera FPGA builds ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Quartus is required to build the Altera FPGA images. +Quartus is required to build the Altera FPGA image for the USRP1. Pre-built images can also be found in **<uhd-repo-path>/fpga/usrp1/rbf**. See **<uhd-repo-path>/fpga/usrp1/toplevel/***. diff --git a/host/docs/index.rst b/host/docs/index.rst index 65069059f..cbab651c1 100644 --- a/host/docs/index.rst +++ b/host/docs/index.rst @@ -18,25 +18,48 @@ Building and Installing UHD Software * `Installation Guide (Windows) <http://code.ettus.com/redmine/ettus/projects/uhd/wiki/UHD_Windows>`_ ^^^^^^^^^^^^^^^^^^^^^ -Application Notes +General UHD Manuals ^^^^^^^^^^^^^^^^^^^^^ * `General Application Notes <./general.html>`_ * `Device Identification Notes <./identification.html>`_ * `Firmware and FPGA Image Notes <./images.html>`_ -* `USRP1 Application Notes <./usrp1.html>`_ -* `USRP2 Application Notes <./usrp2.html>`_ -* `USRP-N2X0 Series Application Notes <./usrp2.html>`_ -* `USRP-B100 Series Application Notes <./usrp_b100.html>`_ -* `USRP-B2X0 Series Application Notes <./usrp_b200.html>`_ -* `USRP-E1X0 Series Application Notes <./usrp_e1x0.html>`_ * `Daughterboard Application Notes <./dboards.html>`_ * `Transport Application Notes <./transport.html>`_ * `Synchronization Application Notes <./sync.html>`_ -* `Internal GPSDO Application Notes (USRP-N2X0/E1X0 Models) <./gpsdo.html>`_ -* `Internal GPSDO Application Notes (USRP-B2X0 Models) <./gpsdo_b2x0.html>`_ * `Calibration Application Notes <./calibration.html>`_ ^^^^^^^^^^^^^^^^^^^^^ +USRP N-Series Devices +^^^^^^^^^^^^^^^^^^^^^ +* `USRP-N2x0 Series Device Manual <./usrp2.html>`_ +* `USRP-N2x0 Internal GPSDO Device Manual <./gpsdo_x3x0.html>`_ + +^^^^^^^^^^^^^^^^^^^^^ +USRP B-Series Devices +^^^^^^^^^^^^^^^^^^^^^ +* `USRP-B100 Series Device Manual <./usrp_b100.html>`_ +* `USRP-B2x0 Series Device Manual <./usrp_b200.html>`_ +* `USRP1 Device Manual <./usrp1.html>`_ + +^^^^^^^^^^^^^^^^^^^^^ +USRP E-Series Devices +^^^^^^^^^^^^^^^^^^^^^ +* `USRP-E1x0 Series Device Manual <./usrp_e1x0.html>`_ +* `USRP-E1x0 Internal GPSDO Device Manual <./gpsdo.html>`_ + +^^^^^^^^^^^^^^^^^^^^^ +USRP X-Series Devices +^^^^^^^^^^^^^^^^^^^^^ +* `USRP-X3x0 Series Device Manual <./usrp_x3x0.html>`_ +* `USRP-X3x0 Internal GPSDO Device Manual <./gpsdo_x3x0.html>`_ +* `USRP-X3x0 Front Panel GPIO API <./gpio_api.html>`_ + +^^^^^^^^^^^^^^^^^^^^^ +USRP Legacy Series +^^^^^^^^^^^^^^^^^^^^^ +* `USRP2 Device Manual <./usrp2.html>`_ + +^^^^^^^^^^^^^^^^^^^^^ API Documentation ^^^^^^^^^^^^^^^^^^^^^ * `Doxygen <./../../doxygen/html/index.html>`_ diff --git a/host/docs/res/x3x0_fp_overlay.png b/host/docs/res/x3x0_fp_overlay.png Binary files differnew file mode 100644 index 000000000..8ab88d4a3 --- /dev/null +++ b/host/docs/res/x3x0_fp_overlay.png diff --git a/host/docs/res/x3x0_gpio_conn.png b/host/docs/res/x3x0_gpio_conn.png Binary files differnew file mode 100644 index 000000000..0147bf422 --- /dev/null +++ b/host/docs/res/x3x0_gpio_conn.png diff --git a/host/docs/res/x3x0_rp_overlay.png b/host/docs/res/x3x0_rp_overlay.png Binary files differnew file mode 100644 index 000000000..c34936ac3 --- /dev/null +++ b/host/docs/res/x3x0_rp_overlay.png diff --git a/host/docs/stream.rst b/host/docs/stream.rst index a00ef88ee..337d7ca40 100644 --- a/host/docs/stream.rst +++ b/host/docs/stream.rst @@ -16,7 +16,7 @@ A TX stream allows the user to transmit samples to the device. 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. +On all second generation hardware (and later), samples are encapsulated into VRT IF data packets. These packets also provide sample decoration such as stream time and burst flags. Sample decoration is exposed to the user in the form of RX and TX metadata structs. @@ -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 further documentation. TODO: provide example of convert API diff --git a/host/docs/sync.rst b/host/docs/sync.rst index a1f6cb7df..21be60a20 100644 --- a/host/docs/sync.rst +++ b/host/docs/sync.rst @@ -153,7 +153,7 @@ For transmit, a burst is started when the user calls send(). The metadata should size_t num_tx_samps = tx_streamer->send(buffs, samps_to_send, md); ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Align LOs in the front-end (SBX/WBX + N-Series) +Align LOs in the front-end (SBX, WBX, CBX) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Using timed commands, multiple frontends can be tuned at a specific time. This timed-tuning ensures that the phase offsets between VCO/PLL chains @@ -163,7 +163,7 @@ will remain constant after each re-tune. See notes below: * 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 + * Due to a 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 diff --git a/host/docs/transport.rst b/host/docs/transport.rst index 79d2743a4..88f787893 100644 --- a/host/docs/transport.rst +++ b/host/docs/transport.rst @@ -7,14 +7,14 @@ UHD - Transport Application Notes ------------------------------------------------------------------------ Introduction ------------------------------------------------------------------------ -A transport is the layer between the packet interface and a device IO interface. -The advanced user can pass optional parameters -into the underlying transport layer through the device address. -These optional parameters control how the transport object allocates memory, -resizes kernel buffers, spawns threads, etc. -When not spcified, the transport layer will use values for these parameters -that are known to perform well on a variety of systems. -The transport parameters are defined below for the various transports in the UHD software: +A transport is the layer between the packet interface and a device IO +interface. The advanced user can pass optional parameters into the underlying +transport layer through the device address. These optional parameters control +how the transport object allocates memory, resizes kernel buffers, spawns +threads, etc. When not spcified, the transport layer will use values for these +parameters that are known to perform well on a variety of systems. The +transport parameters are defined below for the various transports in the UHD +software: ------------------------------------------------------------------------ UDP Transport (Sockets) @@ -147,7 +147,7 @@ so that non-root users may access the device: :: - cd <install-path>/share/uhd/utils + cd <install-path>/lib/uhd/utils sudo cp uhd-usrp.rules /etc/udev/rules.d/ sudo udevadm control --reload-rules sudo udevadm trigger diff --git a/host/docs/uhd_cal_rx_iq_balance.1 b/host/docs/uhd_cal_rx_iq_balance.1 index 073d8ce63..8c2c87ece 100644 --- a/host/docs/uhd_cal_rx_iq_balance.1 +++ b/host/docs/uhd_cal_rx_iq_balance.1 @@ -1,4 +1,4 @@ -.TH "uhd_cal_rx_iq_balance" "1" "3.5.1" "UHD" "User Commands" +.TH "uhd_cal_rx_iq_balance" "1" "3.7.0" "UHD" "User Commands" .SH NAME uhd_cal_rx_iq_balance \- Generate RX IQ Balance Calibration Table for a UHD Device .SH DESCRIPTION diff --git a/host/docs/uhd_cal_tx_dc_offset.1 b/host/docs/uhd_cal_tx_dc_offset.1 index d64120242..f0479d7ec 100644 --- a/host/docs/uhd_cal_tx_dc_offset.1 +++ b/host/docs/uhd_cal_tx_dc_offset.1 @@ -1,4 +1,4 @@ -.TH "uhd_cal_tx_dc_offset" "1" "3.5.1" "UHD" "User Commands" +.TH "uhd_cal_tx_dc_offset" "1" "3.7.0" "UHD" "User Commands" .SH NAME uhd_cal_tx_dc_offset \- Generate TX DC Offset Calibration Table for a UHD Device .SH DESCRIPTION diff --git a/host/docs/uhd_cal_tx_iq_balance.1 b/host/docs/uhd_cal_tx_iq_balance.1 index f4fdbd12f..bb8970f4c 100644 --- a/host/docs/uhd_cal_tx_iq_balance.1 +++ b/host/docs/uhd_cal_tx_iq_balance.1 @@ -1,4 +1,4 @@ -.TH "uhd_cal_tx_iq_balance" "1" "3.5.1" "UHD" "User Commands" +.TH "uhd_cal_tx_iq_balance" "1" "3.7.0" "UHD" "User Commands" .SH NAME uhd_cal_tx_iq_balance \- Generate TX IQ Balance Calibration Table for a UHD Device .SH DESCRIPTION diff --git a/host/docs/uhd_find_devices.1 b/host/docs/uhd_find_devices.1 index 08f9a789c..416f807b9 100644 --- a/host/docs/uhd_find_devices.1 +++ b/host/docs/uhd_find_devices.1 @@ -1,4 +1,4 @@ -.TH "uhd_find_devices" 1 "3.5.1" UHD "User Commands" +.TH "uhd_find_devices" 1 "3.7.0" UHD "User Commands" .SH NAME uhd_find_devices \- USRP Hardware Driver Discovery Utility .SH DESCRIPTION diff --git a/host/docs/uhd_images_downloader.1 b/host/docs/uhd_images_downloader.1 index 28310dd37..dd0fe208d 100644 --- a/host/docs/uhd_images_downloader.1 +++ b/host/docs/uhd_images_downloader.1 @@ -1,4 +1,4 @@ -.TH "uhd_images_downloader" 1 "3.5.1" UHD "User Commands" +.TH "uhd_images_downloader" 1 "3.7.0" UHD "User Commands" .SH NAME uhd_images_downloader \- USRP Hardware Driver firmware/FPGA downloader .SH DESCRIPTION @@ -12,7 +12,7 @@ matching this UHD driver release onto the host system. If the uhd-images package is installed, there is no need to run this installer. .SH SYNOPSIS .B uhd_images_downloader -Usage: uhd_images_downloader.py [options] +Usage: uhd_images_downloader [options] .SH OPTIONS This program works best without any arguments. .PP @@ -38,7 +38,7 @@ usrp2_card_burner(1) usrp_n2xx_simple_net_burner(1) This manual page was written by Maitland Bottoms and Nicholas Corgan for the Debian project (but may be used by others). .SH COPYRIGHT -Copyright (c) 2012 Ettus Research LLC +Copyright (c) 2012,2014 Ettus Research LLC .LP 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 diff --git a/host/docs/uhd_usrp_probe.1 b/host/docs/uhd_usrp_probe.1 index a6c7150ee..ee30b2350 100644 --- a/host/docs/uhd_usrp_probe.1 +++ b/host/docs/uhd_usrp_probe.1 @@ -1,4 +1,4 @@ -.TH "uhd_usrp_probe" 1 "3.5.1" UHD "User Commands" +.TH "uhd_usrp_probe" 1 "3.7.0" UHD "User Commands" .SH NAME uhd_usrp_probe \- USRP Hardware Driver Peripheral Report Utility .SH DESCRIPTION diff --git a/host/docs/usrp1.rst b/host/docs/usrp1.rst index b6fd24b09..be74fe00a 100644 --- a/host/docs/usrp1.rst +++ b/host/docs/usrp1.rst @@ -1,5 +1,5 @@ ======================================================================== -UHD - USRP1 Application Notes +UHD - USRP1 Device Manual ======================================================================== .. contents:: Table of Contents @@ -8,12 +8,20 @@ UHD - USRP1 Application Notes 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 +**Hardware Capabilities:** + * 2 transceiver card slots + * 64 MHz fixed clock rate + +**FPGA Capabilities:** + * 2 RX DDC chains in FPGA + * 2 TX DUC chains in FPGA (no TX CORDIC -> uses DAC) + * sc16 sample modes - RX & TX + + - Up to 8 MHz of RF BW with 16-bit samples + + * sc8 sample mode - RX only + + - Up to 16 MHz of RF BW with 8-bit samples ------------------------------------------------------------------------ Specify a Non-standard Image @@ -93,7 +101,7 @@ so UHD software can initialize with the correct clock rate. Run the following commands to record the setting into the EEPROM: :: - cd <install-path>/share/uhd/utils + cd <install-path>/lib/uhd/utils ./usrp_burn_mb_eeprom --args=<optional device args> --key=mcr --val=<rate> The user may override the clock rate specified in the EEPROM by using a device address: diff --git a/host/docs/usrp2.rst b/host/docs/usrp2.rst index 8ef31af1a..361d98f01 100644 --- a/host/docs/usrp2.rst +++ b/host/docs/usrp2.rst @@ -1,5 +1,5 @@ ======================================================================== -UHD - USRP2 and N2X0 Series Application Notes +UHD - USRP2 and N2x0 Series Device Manual ======================================================================== .. contents:: Table of Contents @@ -8,23 +8,29 @@ UHD - USRP2 and N2X0 Series Application Notes 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 +**Hardware Capabilities:** + * 1 transceiver card slot + * External PPS reference input + * External 10 MHz reference input + * MIMO cable shared reference + * Fixed 100 MHz clock rate + * Internal GPSDO option (N2x0 only) + +**FPGA Capabilities:** + * 2 RX DDC chains in FPGA + * 1 TX DUC chain in FPGA + * Timed commands in FPGA (N2x0 only) + * Timed sampling in FPGA + * 16-bit and 8-bit sample modes (sc8 and sc16) + + * Up to 25 MHz of RF BW with 16-bit samples + * Up to 50 MHz of RF BW with 8-bit samples ------------------------------------------------------------------------ Load the Images onto the SD card (USRP2 only) ------------------------------------------------------------------------ **Warning!** -Use **usrp2_card_burner.py** with caution. If you specify the wrong device node, +Use **usrp2_card_burner** with caution. If you specify the wrong device node, you could overwrite your hard drive. Make sure that **--dev=** specifies the SD card. **Warning!** @@ -41,11 +47,11 @@ Use the card burner tool (UNIX) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ :: - sudo <install-path>/share/uhd/utils/usrp2_card_burner_gui.py + sudo <install-path>/lib/uhd/utils/usrp2_card_burner_gui.py -- OR -- - cd <install-path>/share/uhd/utils + cd <install-path>/lib/uhd/utils 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> @@ -58,51 +64,46 @@ Use the card burner tool (Windows) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ :: - <path_to_python.exe> <install-path>/share/uhd/utils/usrp2_card_burner_gui.py + <path_to_python.exe> <install-path>/lib/uhd/utils/usrp2_card_burner_gui.py ------------------------------------------------------------------------ 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. +The USRP-N Series can be reprogrammed over the network to update or change the firmware and FPGA images. When updating images, always burn both the FPGA and firmware images before power cycling. This ensures that when the device reboots, it has a compatible set of images to boot into. -**Note:** -Different hardware revisions require different FPGA images. -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 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ :: - <install-path>/share/uhd/utils/usrp_n2xx_net_burner_gui.py + Use default images: + usrp_n2xx_simple_net_burner --addr=<IP address> - -- OR -- + Use custom-built images: + usrp_n2xx_simple_net_burner --addr=<IP address> --fw=<firmware path> --fpga=<FPGA path> + +**Note:** +Different hardware revisions require different FPGA images. +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. - cd <install-path>/share/uhd/utils - ./usrp_n2xx_net_burner.py --addr=<ip address> --fw=<path for firmware image> - ./usrp_n2xx_net_burner.py --addr=<ip address> --fpga=<path to FPGA image> +For users who would prefer a graphical utility, a Python-based alternative exists. -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Use the net burner tool (Windows) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Use the graphical net burner tool (Linux) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ :: - <path_to_python.exe> <install-path>/share/uhd/utils/usrp_n2xx_net_burner_gui.py + <install-path>/lib/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. +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Use the graphical net burner tool (Windows) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +:: -The utility can be found at: **<install-path>/share/uhd/utils/usrp_n2xx_simple_net_burner** + <path_to_python.exe> <install-path>/lib/uhd/utils/usrp_n2xx_net_burner_gui.py ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Device recovery and bricking @@ -190,7 +191,7 @@ and the network must be setup properly as described above. Run the following commands: :: - cd <install-path>/share/uhd/utils + cd <install-path>/lib/uhd/utils ./usrp_burn_mb_eeprom --args=<optional device args> --key=ip-addr --val=192.168.10.3 **Method 2 (Linux Only):** @@ -199,7 +200,7 @@ It uses raw Ethernet packets to bypass the IP/UDP layer to communicate with the Run the following commands: :: - cd <install-path>/share/uhd/utils + cd <install-path>/lib/uhd/utils sudo ./usrp2_recovery.py --ifc=eth0 --new-ip=192.168.10.3 ------------------------------------------------------------------------ @@ -271,11 +272,11 @@ Addressing the Device ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Single device configuration ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -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. +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>`_. Please note that this +addressing scheme should also be used with the **multi_usrp** interface. Example device address string representation for a USRP2 with IPv4 address **192.168.10.2**: @@ -313,7 +314,7 @@ This device will be referred to as the slave, and the other device, the master. * External clocking is optional and should only be supplied to the master device. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Shared ethernet mode +Shared Ethernet mode ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ In shared Ethernet mode, only one device in the configuration can be attached to the Ethernet. @@ -322,7 +323,7 @@ only one device in the configuration can be attached to the Ethernet. * Master and slave must have different IPv4 addresses in the same subnet. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Dual ethernet mode +Dual Ethernet mode ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ In dual Ethernet mode, both devices in the configuration must be attached to the Ethernet. @@ -358,7 +359,7 @@ have been programmed into the device's EEPROM. Run the following commands: :: - cd <install-path>/share/uhd/utils + cd <install-path>/lib/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 @@ -400,7 +401,7 @@ The LEDs on the front panel can be useful in debugging hardware and software iss The LEDs reveal the following about the state of the device: * **LED A:** transmitting -* **LED B:** mimo cable link +* **LED B:** MIMO cable link * **LED C:** receiving * **LED D:** firmware loaded * **LED E:** reference lock @@ -408,13 +409,13 @@ The LEDs reveal the following about the state of the device: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Ref Clock - 10MHz +Ref Clock - 10 MHz ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Using an external 10MHz reference clock, a square wave will offer the best phase +Using an external 10 MHz 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 +* **USRP2** 5 to 15 dBm +* **N2XX** 0 to 15 dBm ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -431,7 +432,7 @@ Test the PPS input with the following app: :: - cd <install-path>/share/uhd/examples + cd <install-path>/lib/uhd/examples ./test_pps_input --args=<args> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -466,7 +467,9 @@ which has been aptly named slot **A**. In the following example, a TVRX2 is installed. Channel 0 is sourced from subdevice **RX1**, -and channel 1 is sourced from subdevice **RX2**: +and channel 1 is sourced from subdevice **RX2** (**RX1** and **RX2** +are the antenna ports on the TVRX2 daughterboard): + :: usrp->set_rx_subdev_spec("A:RX1 A:RX2"); diff --git a/host/docs/usrp2_card_burner.1 b/host/docs/usrp2_card_burner.1 index d6a12b073..b6e1954d8 100644 --- a/host/docs/usrp2_card_burner.1 +++ b/host/docs/usrp2_card_burner.1 @@ -1,4 +1,4 @@ -.TH "usrp2_card_burner" 1 "3.5.1" UHD "User Commands" +.TH "usrp2_card_burner" 1 "3.7.0" UHD "User Commands" .SH NAME usrp2_card_burner - USRP N-Series FPGA/Firmware Burner .SH DESCRIPTION @@ -34,12 +34,12 @@ GR-UHD documentation: .LP Other UHD programs: .sp -uhd_images_downloader(1) usrp_n2xx_simple_net_burner(1) +uhd_images_downloader(1) usrp_n2xx_simple_net_burner(1) usrp_x3xx_fpga_burner(1) .SH AUTHOR This manual page was written by Nicholas Corgan for the Debian project (but may be used by others). .SH COPYRIGHT -Copyright (c) 2012 Ettus Research LLC +Copyright (c) 2012,2014 Ettus Research LLC .LP 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 diff --git a/host/docs/usrp_b100.rst b/host/docs/usrp_b100.rst index ac0942f5c..223ad5a90 100644 --- a/host/docs/usrp_b100.rst +++ b/host/docs/usrp_b100.rst @@ -1,5 +1,5 @@ ======================================================================== -UHD - USRP-B100 Series Application Notes +UHD - USRP-B100 Series Device Manual ======================================================================== .. contents:: Table of Contents @@ -8,15 +8,21 @@ UHD - USRP-B100 Series Application Notes Comparative features list ------------------------------------------------------------------------ -* 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 +**Hardware Capabilities:** + * 1 transceiver card slot + * External PPS reference input + * External 10 MHz reference input + * Configurable clock rate (defaults 64 MHz) + +**FPGA Capabilities:** + * 1 RX DDC chain in FPGA + * 1 TX DUC chain in FPGA + * Timed commands in FPGA + * Timed sampling in FPGA + * sc8 and sc16 sample modes + + * Up to 8 MHz of RF BW with 16-bit samples + * Up to 16 MHz of RF BW with 8-bit samples ------------------------------------------------------------------------ Specify a Non-standard Image @@ -38,17 +44,17 @@ Example device address string representations to specify non-standard images: Changing the Master Clock Rate ------------------------------------------------------------------------ The master clock rate of the B100 feeds both the FPGA DSP and the codec chip. -Hundreds of rates between 32MHz and 64MHz are available. +Hundreds of rates between 32 MHz and 64 MHz 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 +* **64 MHz:** maximum rate of the codec chip +* **61.44 MHz:** good for UMTS/WCDMA applications +* **52 MHz:** good for GSM applications ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Set 61.44MHz - uses external VCXO +Set 61.44 MHz - uses external VCXO ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -To use the 61.44MHz clock rate, the USRP embedded will require one jumper to be moved, +To use the 61.44 MHz clock rate, the USRP embedded will require one jumper to be moved, and X4 must be populated with a 61.44 MHz oscillator. * **J15** is a three pin header, move the jumper to (pin1, pin2) @@ -64,7 +70,7 @@ 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 UHD software, -specify the a special device address argument, +specify the special device address argument, where the key is **master_clock_rate** and the value is a rate in Hz. Example: :: @@ -82,9 +88,9 @@ The LEDs on the front panel can be useful in debugging hardware and software iss The LEDs reveal the following about the state of the device: * **LED A:** transmitting -* **LED B:** fpga loaded +* **LED B:** FPGA loaded * **LED C:** receiving -* **LED D:** fpga loaded +* **LED D:** FPGA loaded * **LED E:** reference lock * **LED F:** board power diff --git a/host/docs/usrp_b200.rst b/host/docs/usrp_b200.rst index 81e66684e..b282ec424 100644 --- a/host/docs/usrp_b200.rst +++ b/host/docs/usrp_b200.rst @@ -1,5 +1,5 @@ ======================================================================== -UHD - USRP-B2X0 Series Application Notes +UHD - USRP-B2x0 Series Device Manual ======================================================================== .. contents:: Table of Contents @@ -8,35 +8,26 @@ UHD - USRP-B2X0 Series Application Notes Comparative features list - B200 ------------------------------------------------------------------------ -* integrated RF frontend (RF coverage from 70 MHz - 6 GHz) -* 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 -* Internal GPSDO option +**Hardware Capabilities:** + * Integrated RF frontend (70 MHz - 6 GHz) + * External PPS reference input + * External 10 MHz reference input + * Configurable clock rate + * Internal GPSDO option + * B210 Only: ------------------------------------------------------------------------- -Comparative features list - B210 ------------------------------------------------------------------------- + * MICTOR Debug Connector + * JTAG Connector -* integrated MIMO frontend (RF coverage from 70 MHz - 6 GHz) -* 2 RX DDC chains in FPGA -* 2 TX DUC chains in FPGA -* Timed commands in FPGA -* Timed sampling in FPGA -* External PPS reference -* External 10MHz reference -* Configurable clock rate -* Internal GPSDO option +**FPGA Capabilities:** + * Timed commands in FPGA + * Timed sampling in FPGA ------------------------------------------------------------------------ Specify a Non-standard Image ------------------------------------------------------------------------ UHD software will automatically select the USRP B2X0 images from the installed images package. -The image selection can be overridden with the **--fpga=** and **--fw=** device address parameters. +The image selection can be overridden with the **fpga** and **fw** device address parameters. Example device address string representations to specify non-standard images: @@ -81,7 +72,7 @@ Frontend gain ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ All frontends have individual analog gain controls. The receive frontends have 73 dB of available gain; -and the transmit frontends have 89 dB of available gain. +and the transmit frontends have 89.5 dB of available gain. Gain settings are application specific, -but its recommended that users consider using at least +but it is recommended that users consider using at least half of the available gain to get reasonable dynamic range. diff --git a/host/docs/usrp_e1x0.rst b/host/docs/usrp_e1x0.rst index fc929e639..ea2d05a3c 100644 --- a/host/docs/usrp_e1x0.rst +++ b/host/docs/usrp_e1x0.rst @@ -1,5 +1,5 @@ ======================================================================== -UHD - USRP-E1X0 Series Application Notes +UHD - USRP-E1x0 Series Device Manual ======================================================================== .. contents:: Table of Contents @@ -8,23 +8,29 @@ UHD - USRP-E1X0 Series Application Notes 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 +**Hardware Capabilities:** + * 1 transceiver card slot + * Internal PPS reference input + * Internal 10 MHz reference input + * Configurable clock rate (defaults to 64 MHz) + * Internal GPSDO option + +**FPGA Capabilities:** + * 2 RX DDC chains in FPGA + * 1 TX DUC chain in FPGA + * Timed commands in FPGA + * Timed sampling in FPGA + * sc8 and sc16 sample modes + + * Up to 8 MHz of RF BW with 16-bit samples + * Up to 16 MHz of RF BW with 8-bit samples ------------------------------------------------------------------------ Specify a Non-standard Image ------------------------------------------------------------------------ UHD software 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: @@ -37,17 +43,17 @@ image: 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 +chip. Hundreds of rates between 32 MHz and 64 MHz 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 +* **64 MHz:** maximum rate of the codec chip +* **61.44 MHz:** good for UMTS/WCDMA applications +* **52 MHz:** good for GSM applications ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Set 61.44MHz - uses external VCXO ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -To use the 61.44MHz clock rate with the USRP-Embedded, two jumpers must be moved +To use the 61.44 MHz 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). @@ -94,22 +100,22 @@ a connector. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PPS - Pulse Per Second ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -An exteral PPS signal for timestamp synchronization can be supplied by soldering +An external 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 +* **Amplitude:** 3.3 to 5 Vpp -Test the PPS input with the following app: - -* **<args** are device address arguments (optional if only one USRP device is on your machine). +Test the PPS input with the following app (**<args>** are device address +arguments, optional if only one USRP device is on your machine): :: - cd <install-path>/share/uhd/examples + cd <install-path>/lib/uhd/examples ./test_pps_input --args=<args> + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Internal GPSDO ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -117,7 +123,7 @@ Please see the `Internal GPSDO Application Notes <./gpsdo.html>`_ for information on configuring and using the internal GPSDO. UHD software will always try to detect an installed GPSDO at runtime. -There is not a special EEPROM value to burn for GPSDO detection. +It is not necessary to burn a special EEPROM value for GPSDO detection. ------------------------------------------------------------------------ Hardware Setup Notes @@ -132,7 +138,7 @@ issues. The LEDs reveal the following about the state of the device: * **LED A:** transmitting * **LED B:** PPS signal * **LED C:** receiving -* **LED D:** fpga loaded +* **LED D:** FPGA loaded * **LED E:** reference lock * **LED F:** board power diff --git a/host/docs/usrp_n2xx_simple_net_burner.1 b/host/docs/usrp_n2xx_simple_net_burner.1 index 6e8e38fae..8df38b7bd 100644 --- a/host/docs/usrp_n2xx_simple_net_burner.1 +++ b/host/docs/usrp_n2xx_simple_net_burner.1 @@ -1,4 +1,4 @@ -.TH "usrp_n2xx_simple_net_burner" 1 "3.5.1" UHD "User Commands" +.TH "usrp_n2xx_simple_net_burner" 1 "3.7.0" UHD "User Commands" .SH NAME usrp_n2xx_simple_net_burner - USRP N-Series FPGA/Firmware Burner .SH DESCRIPTION @@ -41,12 +41,12 @@ GR-UHD documentation: .LP Other UHD programs: .sp -uhd_images_downloader(1) usrp2_card_burner(1) +uhd_images_downloader(1) usrp2_card_burner(1) usrp_x3xx_fpga_burner(1) .SH AUTHOR This manual page was written by Nicholas Corgan for the Debian project (but may be used by others). .SH COPYRIGHT -Copyright (c) 2012 Ettus Research LLC +Copyright (c) 2012,2014 Ettus Research LLC .LP 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 diff --git a/host/docs/usrp_x3x0.rst b/host/docs/usrp_x3x0.rst new file mode 100644 index 000000000..058230b5a --- /dev/null +++ b/host/docs/usrp_x3x0.rst @@ -0,0 +1,688 @@ +=============================== +UHD - X3x0 Series Device Manual +=============================== + +.. contents:: Table of Contents + +------------------------- +Comparative features list +------------------------- + +**Hardware Capabilities:** + * 2 transceiver card slots + * Dual SFP+ Transceivers (can be used with 1 GigE, 10 GigE) + * PCI Express over cable (MXI) gen1 x4 + * External PPS input & output + * External 10 MHz input & output + * Expandable via 2nd SFP+ interface + * Supported master clock rates: 200 MHz, 184.32 MHz + * External GPIO Connector with UHD API control + * External USB Connection for built-in JTAG debugger + * Internal GPSDO option + * Kintex-7 FPGA (X310: XC7K410T, X300: XC7K325T) + +**FPGA Capabilities:** + * 2 RX DDC chains in FPGA + * 2 TX DUC chain in FPGA + * Timed commands in FPGA + * Timed sampling in FPGA + * 16-bit and 8-bit sample modes (sc8 and sc16) + * Up to 120 MHz of RF bandwidth with 16-bit samples + +-------------- +Hardware Setup +-------------- + +^^^^^^^^^^^^^^^^^^^^^^^^^ +Gigabit Ethernet (1 GigE) +^^^^^^^^^^^^^^^^^^^^^^^^^ + +Installing the USRP X300/X310 +::::::::::::::::::::::::::::: +* Prior to installing the module, the host PC can remain powered on. +* Plug a 1 Gigabit SFP+ Transceiver into Ethernet Port 0 on the USRP X300/x310 device. +* Use the Ethernet cable to connect the SFP+ transciever on the device to the host computer. For maximum throughput, Ettus Research recommends that you connect each device to its own dedicated Gigabit Ethernet interface on the host computer. +* Connect the AC/DC power supply to the device and plug the supply into a wall outlet. +* The OS will automatically recognize the device (e.g. when running uhd_find_devices). + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Ten Gigabit Ethernet (10 GigE) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Installing the Host Ethernet Interface +:::::::::::::::::::::::::::::::::::::: +Ettus Research recommends the Intel Ethernet Converged Network Adapter X520-DA2 interface for communication with the USRP X300/X310 device. +Installation instructions for this interface are available on the official Intel website. + +Installing the USRP X300/X310 +::::::::::::::::::::::::::::: +* Prior to installing the module, the host PC can remain powered on. +* Use a 10 Gigabit SFP+ cable to connect Ethernet Port 1 on the USRP X300/X310 device to the host computer. For maximum throughput, Ettus Research recommends that you connect the device to its own dedicated Ten Gigabit, Ettus Research recommended Ethernet interface on the host computer. +* Connect the AC/DC power supply to the device and plug the supply into a wall outlet. +* The OS will automatically recognize the device (e.g. when running uhd_find_devices). + +The LEDs on the front panel can be useful in debugging hardware and software issues (see Section "Front Panel") + +^^^^^^^^^^^^^^^^^^^^^ +PCI Express (Desktop) +^^^^^^^^^^^^^^^^^^^^^ +*Important Note: The USRP X-Series provides PCIe connectivity over MXI cable. +We will use the 'MXI' nomenclature for the rest of this manual.* + +Installing the PCI Express Interface Kit +:::::::::::::::::::::::::::::::::::::::: +Follow the instructions listed in the `Set Up Your MXI-Express x4 System <http://www.ni.com/pdf/manuals/371976c.pdf>`_ +document to setup the NI PCIe-8371 module. + +Installing the USRP X300/X310 +::::::::::::::::::::::::::::: +* Prior to installing the module, make sure that the PC is powered off. +* Using a MXI-Express Cable connect the USRP X300/X310 to the NI PCIe-8371. +* Connect the AC/DC power supply to the device and plug the supply into a wall outlet. +* Power on the USRP X300/X310 device using the power switch located in the bottom-right corner of the front panel. +* Power on the PC (The OS automatically recognizes the new device) + +NOTE: The USRP device is not hot-pluggable over PCI Express. Any connection changes with only be detected by your +computer after a successful reboot. + +Troubleshooting +::::::::::::::: +Two possible failure modes are your computer not booting when connected to your +USRP device through MXI-Express, and Windows not properly discovering your +devices (for example, there is a yellow exclamation point on a PCI to PCI +bridge in Windows Device Manager, despite drivers for all devices being +installed). These situations often are due to programming errors in PCI Express +device configuration of the BIOS. To use this software, you need a MXI-Express +device that supports Mode 1 operation. +Refer to `NI MXI-Express BIOS Compatibility Software Readme <http://download.ni.com/support/softlib//PXI/MXIe%20Compatibility%20Software/1.5.0/readme.html#SupportedHardware>`_ +for more information. + +The BIOS Compatibility Software can be downloaded for Windows from the `MXI-Express BIOS Compatibility Software <http://www.ni.com/download/mxi-express-bios-compatibility-software-1.5/3764/en/>`_ page + +^^^^^^^^^^^^^^^^^^^^ +PCI Express (Laptop) +^^^^^^^^^^^^^^^^^^^^ +*Important Note: The USRP X-Series provides PCIe connectivity over MXI cable. +We will use the 'MXI' nomenclature for the rest of this manual.* + +Installing the PCI Express Card +::::::::::::::::::::::::::::::: +Follow the instructions listed in the “Installing an NI ExpressCard-8360 Host Card” section of the +`Set Up Your MXI-Express x1 System <http://www.ni.com/pdf/manuals/373259d.pdf#page=10>`_ +document to setup the NI ExpressCard-8360B module. + +Installing the USRP X300/X310 +::::::::::::::::::::::::::::: +Because a laptop computer is not grounded, follow this procedure to safely connect a laptop +computer to your USRP device. + +* Connect the AC/DC power supply to the device and plug the supply into a wall outlet. Ensure that the USRP device is powered off. +* Touch the NI ExpressCard-8360B and a metal part of the USRP device simultaneously. Do not install the NI ExpressCard-8360B into the laptop computer yet. +* Connect the cable to the NI ExpressCard-8360B and USRP. +* Plug the NI ExpressCard-8360B into an available ExpressCard slot. If your laptop computer is already running (or hibernating, suspended, etc) when you install an NI ExpressCard-8360B, you must reboot to detect the USRP. Otherwise, the USRP is detected when you start your computer. + +NOTE: The USRP device is not hot-pluggable over PCI Express. Any connection changes will only be detected by your computer after a successful reboot. + +-------------------------------- +On-Board JTAG Programmer +-------------------------------- +The USRP X3x0 includes an on-board JTAG programmer, built into the motherboard. +To connect to this JTAG device, simply connect your computer to the USB JTAG +port on the front of the X3x0 device. You may now use the JTAG programmer in +the same way you would use any other, including: + +* `Xilinx Programming Tools (ISE, iMPACT) <http://www.xilinx.com/support/download/index.htm>`_ +* `Xilinx Chipscope <http://www.xilinx.com/tools/cspro.htm>`_ +* `Digilent ADEPT <https://www.digilentinc.com/Products/Detail.cfm?NavPath=2,66,828&Prod=ADEPT2>`_ + +In order to use the JTAG programmer with the Xilinx tools, the Digilent drivers and plugin have to be installed first. +Although recent versions of ISE ship with the driver, it has to still be manually installed. + +Note: Sometimes the ISE shipped versions are newer than the ones available via Digilent's website. It is therefore advisable to +use the ISE provided plugin and drivers. + +To install first locate your ISE installation path (default is /opt/Xilinx/<Version>). + +**LINUX** +:: + + sudo <ise install path>/ISE_DS/common/bin/lin64/digilent/install_digilent.sh + +Afterwards either reboot or force udev to reload its rules by: +:: + + sudo udevadm control --reload + +The USRP-X series device should now be usable with all the tools mentioned above. + +-------------------------------- +Load FPGA Images onto the Device +-------------------------------- +The USRP-X Series device ships with a bitstream pre-programmed in the flash, +which is automatically loaded onto the FPGA during device power-up. However, +a new FPGA image can be configured over the PCI Express interface or the +on-board USB-JTAG programmer. This process can be seen as a "one-time load", in +that if you power-cycle the device, it will not retain the FPGA image. + +Please note that this process is *different* than replacing the FPGA image +stored in the flash, which will then be automatically loaded the next time the +device is reset. + +^^^^^^^^^^^^^^^^^^ +FPGA Image Flavors +^^^^^^^^^^^^^^^^^^ +The USRP-X Series devices contains two SFP+ ports for the two Ethernet channels. +Because the SFP+ ports support both 1 Gigabit (SFP) and 10 Gigabit (SFP+) +transceivers, several FPGA images are shipped with UHD to determine the +behavior of the above interfaces. + ++---------------------+------------------------+------------------------+ +| FPGA Image Flavor | SFP+ Port 0 Interface | SFP+ Port 1 Interface | ++=====================+========================+========================+ +| HGS (Default) | 1 Gigabit Ethernet | 10 Gigabit Ethernet | ++---------------------+------------------------+------------------------+ +| XGS | 10 Gigabit Ethernet | 10 Gigabit Ethernet | ++---------------------+------------------------+------------------------+ + +FPGA images are shipped in 2 formats: + +* **LVBITX**: LabVIEW FPGA configuration bitstream format (for use over PCI Express and Ethernet) +* **BIT**: Xilinx configuration bitstream format (for use over Ethernet and JTAG) + +To get the latest images, simply use the uhd_images_downloader script: + +**UNIX:** + +:: + + uhd_images_downloader + +**Windows:** + +:: + + <path_to_python.exe> <install-path>/bin/uhd_images_downloader.py + + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Use PCI Express to load FPGA images +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +UHD requires a valid LabVIEW FPGA configuration bitstream file (LVBITX) to use the USRP-X Series +device over the PCI Express bus. LabVIEW FPGA is **NOT** required to use UHD with a USRP-X Series device. +Because FPGA configuration is a part of normal operation over PCI Express, there is no setup required +before running UHD. + +The **fpga** tag can be set in the optional device args passed to indicate the FPGA image flavor to UHD. +If the above tag is specified, UHD will attempt to load the FPGA image with the requested flavor from the +UHD images directory. If the tag is not specified, UHD will automatically detect the flavor of the image +and attempt to load the corresponding configuration bitstream onto the device. Note that if UHD detects +that the requested image is already loaded onto the FPGA then it will not reload it. + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Use JTAG to load FPGA images +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +The USRP-X Series device features an on-board USB-JTAG programmer that can be accessed on the front-panel +of the device. The iMPACT tool in the `Xilinx Programming Tools <http://www.xilinx.com/support/download/index.htm>`_ package can be used to load an image over +the JTAG interface. + +--------------------------------------- +Load the Images onto the On-board Flash +--------------------------------------- +To change the FPGA image stored in the on-board flash, the USRP-X Series device +can be reprogrammed over the network or PCI Express. Once you have programmed an +image into the flash, that image will be automatically loaded on the FPGA +during the device boot-up sequence. + +**Note:** +Different hardware revisions require different FPGA images. +Determine the revision number from the sticker on the rear of the device. +If you are manually specifying an FPGA path, the utility will not try to +detect your device information, and you will need to use this number to +select which image to burn. + +**Note:** +The burner utility will default to using the appropriate BIT file if no custom +FPGA image path is specified, but it is compatible with BIN, BIT, and LVBITX +images. + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Use the burner tool over Ethernet +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +:: + + Automatic FPGA path, detect image type: + usrp_x3xx_fpga_burner --addr=<IP address> + + Automatic FPGA path, select image type: + usrp_x3xx_fpga_burner --addr=<IP address> --type=<HGS or XGS> + + Manual FPGA path: + usrp_x3xx_fpga_burner --addr=<IP address> --fpga-path=<path to FPGA image> + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Use the burner tool over PCI Express +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +:: + + Automatic FPGA path, detect image type: + usrp_x3xx_fpga_burner --resource=<NI-RIO resource> + + Automatic FPGA path, select image type: + usrp_x3xx_fpga_burner --resource=<NI-RIO resource> --type=<HGS or XGS> + + Manual FPGA path: + usrp_x3xx_fpga_burner --resource=<NI-RIO resource> --fpga-path=<path to FPGA image> + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Device recovery and bricking +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +It is possible to put the device into an unusable state by loading bad images. +Fortunately, the USRP-X Series device can be loaded with a good image temporarily using the USB-JTAG interface. +Once booted into the safe image, the user can once again load images onto the device over Ethernet or PCI Express. + +---------------- +Setup Networking +---------------- +The USRP-X Series only supports Gigabit and Ten Gigabit Ethernet and will not work with a 10/100 Mbps interface. + +**Please note that 10 Gigabit Ethernet defines the protocol, not necessary the +medium. For example, you may use 10GigE over optical with optical SFP+ +transceiver modules.** + +^^^^^^^^^^^^^^^^^^^^^^^^ +Setup the host interface +^^^^^^^^^^^^^^^^^^^^^^^^ +The USRP-X Series communicates at the IP/UDP layer over the Gigabit and Ten Gigabit Ethernet. +The default IP address for the USRP X300/X310 device depends on the Ethernet Port and interface used. +You must configure the host Ethernet interface with a static IP address on the same subnet as the connected +device to enable communication, as shown in the following table: + ++---------------+-------------------------+----------------+----------------+---------------+ +| Ethernet | USRP | Default USRP | Host Static | Host Static | +| Interface | Ethernet Port | IP Address | IP Address | Subnet Mask | ++===============+=========================+================+================+===============+ +| Gigabit | Port 0 (HGS Image) | 192.168.10.2 | 192.168.10.1 | 255.255.255.0 | ++---------------+-------------------------+----------------+----------------+---------------+ +| Ten Gigabit | Port 1 (HGS/XGS Image) | 192.168.40.2 | 192.168.40.1 | 255.255.255.0 | ++---------------+-------------------------+----------------+----------------+---------------+ +| Ten Gigabit | Port 0 (XGS Image) | 192.168.30.2 | 192.168.30.1 | 255.255.255.0 | ++---------------+-------------------------+----------------+----------------+---------------+ + + +On a Linux system, you can add a static IP address very easily by using the +'ip' command + +:: + + sudo ip addr add 192.168.10.1/24 dev <interface> + +Note that **<interface>** is usually something like **eth0**. You can discover the +names of the network interfaces in your computer by running: + +:: + + ip addr show + +**Note:** +When using UHD software, if an IP address for the USRP-X Series device is not specified, +the software will use UDP broadcast packets to locate the USRP-X Series device. +On some systems, the firewall will block UDP broadcast packets. +It is recommended that you change or disable your firewall settings. + +On many Linux distributions, NetworkManager or similar tools may control the network interface. +It is important to deactivate these tools for your device before continuing! + +^^^^^^^^^^^^^^^ +Setting the MTU +^^^^^^^^^^^^^^^ +As UHD by default uses receive and transmit frames larger than the standard MTU of 1500 Bytes, +the NIC needs to be configured to use a larger MTU when used with the USRP X series devices. + +:: + + sudo ip link set mtu 8192 dev <interface> + +Upon initialization UHD will probe for the maximum possible path MTU along the path between the USRP X series device +and the host, both in receive and transmit direction. + +If the network hardware does not support MTUs as large as 8000 Bytes, passing the **send_frame_size** and **receive_frame_size** +arguments will make UHD use smaller MTUs: + +:: + + uhd_usrp_probe --args='send_frame_size=<max send MTU>, recv_frame_size=<max receive MTU>' + +**Note:** This will most likely have a severe performance penalty. + + +^^^^^^^^^^^^^^^^^^^^^^^^^ +Multiple devices per host +^^^^^^^^^^^^^^^^^^^^^^^^^ +For maximum throughput, one Ethernet interface per USRP is recommended, +although multiple devices may be connected via an Ethernet switch. +In any case, each Ethernet interface should have its own subnet, +and the corresponding USRP device should be assigned an address in that subnet. +Example: + +**Configuration for USRP-X Series device 0:** + +* Ethernet interface IPv4 address: **192.168.10.1** +* Ethernet interface subnet mask: **255.255.255.0** +* USRP-X Series device IPv4 address: **192.168.10.2** + +**Configuration for USRP-X Series device 1:** + +* Ethernet interface IPv4 address: **192.168.110.1** +* Ethernet interface subnet mask: **255.255.255.0** +* USRP-X Series device IPv4 address: **192.168.110.2** + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Change the USRP's IP address +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +You may need to change the USRP's IP address for several reasons: + +* to satisfy your particular network configuration +* to use multiple USRP-X Series devices on the same host computer +* to set a known IP address into USRP (in case you forgot) + +To change the USRP's IP address, +you must know the current address of the USRP, +and the network must be setup properly as described above. +Run the following commands: + +**UNIX:** + +:: + + cd <install-path>/lib/uhd/utils + ./usrp_burn_mb_eeprom --args=<optional device args> --key=ip-addr --val=192.168.10.3 + +**Windows:** + +:: + + cd <install-path>\lib\uhd\utils + usrp_burn_mb_eeprom.exe --args=<optional device args> --key=ip-addr --val=192.168.10.3 + +--------------------- +Addressing the Device +--------------------- + +^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Single device configuration +^^^^^^^^^^^^^^^^^^^^^^^^^^^ +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, NI-RIO resource name or by other means. +See the application notes on `device identification <./identification.html>`_. +Use this addressing scheme with the **multi_usrp** interface (not a typo!). + +Example device address string representation for a USRP-X Series device with IPv4 address **192.168.10.2**: + +:: + + addr=192.168.10.2 + +Example device address string representation for a USRP-X Series device with RIO resource name **RIO0** over PCI Express: + +:: + + resource=RIO0 + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Multiple device configuration +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +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. + +* 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 USRPs with IPv4 addresses **192.168.10.2** and **192.168.20.2**: + +:: + + addr0=192.168.10.2, addr1=192.168.20.2 + + +---------------------- +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. + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +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 device. For +example, if your network interface is set to **192.168.20.1**, and the USRP device 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 that of your USRP device. Instructions for setting your IP address are in the +previous section of this documentation. + +^^^^^^^^^^^^^^^ +Firewall issues +^^^^^^^^^^^^^^^ +When the IP address is not specified, +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 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**. + +^^^^^^^^^^^^^^^ +Ping the device +^^^^^^^^^^^^^^^ +The USRP device will reply to ICMP echo requests ("ping"). +A successful ping response means that the device has booted properly +and that it is using the expected IP address. + +:: + + ping 192.168.10.2 + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +USRP device not enumerated (Linux) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +UHD requires the RIO device manager service to be running in order to +communicate with an X-Series USRP over PCIe. This service is installed as +a part of the USRP RIO (or NI-USRP) installer. On Linux, the service is not +started at system boot time, and is left to the user to control. To start it, +run the following command: + +:: + + sudo niusrprio_pcie start + +If the device still does not enumerate after starting the device manager, make sure that the host computer +has successfully detected it. You can do so by running the following command: + +:: + + lspci -k -d 1093:c4c4 + +A device similar to the following should be detected: + +:: + + $ lspci -k -d 1093:c4c4 + 04:00.0 Signal processing controller: National Instruments ... + Subsystem: National Instruments Device 76ca + Kernel driver in use: niusrpriok_shipped + +* A USRP X300 should appear with 'Subsystem: National Instruments Device 7736' +* A USRP X310 should appear with 'Subsystem: National Instruments Device 76ca' + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +USRP device not enumerated (Windows) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +UHD requires the RIO device manager service to be running in order to +communicate with an X-Series USRP over PCIe. +This service is installed as a part of the USRP RIO (or NI-USRP) installer. On Windows, it can be found in +the **Services** section in the Control Panel and it is started at system boot time. To ensure that the +service is indeed started, navigate to the Services tag in the Windows Task Manager and ensure that the +status of **niusrpriorpc** is "Running". + +If the device still does not enumerate after starting the device manager, make sure that the host computer +has successfully detected it. You can do so by checking if your device shows up in the Windows Device Manager. + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Monitor the host network traffic +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Use Wireshark to monitor packets sent to and received from the device. + +-------------- +Hardware Notes +-------------- + +^^^^^^^^^^^ +Front Panel +^^^^^^^^^^^ + +.. image:: ./res/x3x0_fp_overlay.png + :scale: 80% + :align: left + +* **JTAG**: USB connector for the on-board USB-JTAG programmer +* **RF A Group** + + * **TX/RX LED**: Indicates that data is streaming on the TX/RX channel on daughterboard A + * **RX2 LED**: Indicates that data is streaming on the RX2 channel on daughterboard A + +* **REF**: Indicates that the external Reference Clock is locked +* **PPS**: Indicates a valid PPS signal by pulsing once per second +* **AUX I/O**: Front panel GPIO connector. +* **GPS**: Indicates that GPS reference is locked +* **LINK**: Indicates that the host computer is communicating with the device (Activity) + +* **RF B Group** + + * **TX/RX LED**: Indicates that data is streaming on the TX/RX channel on daughterboard B + * **RX2 LED**: Indicates that data is streaming on the RX2 channel on daughterboard B + +* **PWR**: Power switch + +^^^^^^^^^^ +Rear Panel +^^^^^^^^^^ + +.. image:: ./res/x3x0_rp_overlay.png + :scale: 80% + :align: left + + +* **PWR**: Connector for the USRP-X Series power supply +* **1G/10G ETH**: SFP+ ports for Ethernet interfaces +* **REF OUT**: Output port for the exported reference clock +* **REF IN**: Reference clock input +* **PCIe x4**: Connector for Cabled PCI Express link +* **PPS/TRIG OUT**: Output port for the PPS signal +* **PPS/TRIG IN**: Input port for the PPS signal +* **GPS**: Connection for the GPS antenna + +^^^^^^^^^^^^^^^^^^ +Ref Clock - 10 MHz +^^^^^^^^^^^^^^^^^^ +Using an external 10 MHz reference clock, a square wave will offer the best phase +noise performance, but a sinusoid is acceptable. The power level of the reference clock cannot exceed +15 dBm. + +^^^^^^^^^^^^^^^^^^^^^^ +PPS - Pulse Per Second +^^^^^^^^^^^^^^^^^^^^^^ +Using a PPS signal for timestamp synchronization requires a square wave signal with the following a 5Vpp amplitude. + +To test the PPS input, you can use the following tool from the UHD examples: + +* **<args>** are device address arguments (optional if only one USRP device is on your machine) + +:: + + cd <install-path>/lib/uhd/examples + ./test_pps_input --args=<args> + +^^^^^^^^^^^^^^ +Internal GPSDO +^^^^^^^^^^^^^^ +Please see the `Internal GPSDO Application Notes <./gpsdo_x3x0.html>`_ +for information on configuring and using the internal GPSDO. + +^^^^^^^^^^^^^^^^ +Front Panel GPIO +^^^^^^^^^^^^^^^^ + +Connector +::::::::: + +.. image:: ./res/x3x0_gpio_conn.png + :scale: 75% + :align: left + +Pin Mapping +::::::::::: + +* Pin 1: +3.3V +* Pin 2: Data[0] +* Pin 3: Data[1] +* Pin 4: Data[2] +* Pin 5: Data[3] +* Pin 6: Data[4] +* Pin 7: Data[5] +* Pin 8: Data[6] +* Pin 9: Data[7] +* Pin 10: Data[8] +* Pin 11: Data[9] +* Pin 12: Data[10] +* Pin 13: Data[11] +* Pin 14: 0V +* Pin 15: 0V + + +Please see the `GPIO API Notes <./gpio_api.html>`_ for information on configuring and using the GPIO bus. + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Debugging custom FPGA designs with Xilinx Chipscope +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Xilinx chipscope allows for debugging custom FPGA designs similar to a logic analyzer. +USRP-X series devices can be used with Xilinx chipscope using the onboard USB JTAG connector. + +Further information on how to use Chipscope can be found in the Xilinx Chipscope Pro Software and Cores User Guide (UG029). + +------------- +Miscellaneous +------------- + +^^^^^^^^^^^^^^^^^^^^ +Multiple RX channels +^^^^^^^^^^^^^^^^^^^^ +There are two complete DDC and DUC DSP 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** or **TX** +subdevice specification. + +In the following example, a TVRX2 is installed. +Channel 0 is sourced from subdevice **RX1**, +and channel 1 is sourced from subdevice **RX2** (**RX1** and **RX2** are antenna connectors on the TVRX2 daughterboard). + +:: + + usrp->set_rx_subdev_spec("A:RX1 A:RX2"); + + +^^^^^^^^^^^^^^^^^ +Available Sensors +^^^^^^^^^^^^^^^^^ +The following sensors are available for the USRP-X Series motherboards; +they can be queried through the API. + +* **ref_locked** - clock reference locked (internal/external) +* Other sensors are added when the GPSDO is enabled diff --git a/host/docs/usrp_x3xx_fpga_burner.1 b/host/docs/usrp_x3xx_fpga_burner.1 new file mode 100644 index 000000000..3a8a27e69 --- /dev/null +++ b/host/docs/usrp_x3xx_fpga_burner.1 @@ -0,0 +1,65 @@ +.TH "usrp_x3xx_fpga_burner" 1 "3.7.0" UHD "User Commands" +.SH NAME +usrp_x3xx_fpga_burner - USRP X-Series FPGA Burner +.SH DESCRIPTION +Burn an FPGA image onto a connected USRP X-Series device. The program takes +two main arguments. The first is either an IP address or an NI-RIO resource +to decide the method of loading the FPGA image. The other is either the +type of image to burn, in which case the program will choose the default image +of that type and model, or a custom FPGA image path. +.SH SYNOPSIS +.B usrp_x3xx_fpga_burner [OPTIONS] +.SH OPTIONS +.IP "Device IP Address:" +--addr=\fI"Address"\fR +. IP "Device NI-RIO Resource:" +--resource=\fI"Resource"\fR +. IP "RPC Port:" +--rpc-port=\fI"Port"\fR (default=5444) +. IP "Image Type (1G, HGS, or XGS):" +--type=\fI"Type"\fR +. IP "Custom FPGA path:" +--fpga-path=\fI"Path"\fR +. IP "Initialize USRP with image currently burned onto device (Ethernet only):" +--configure +. IP "Verify image during Ethernet burn (takes longer):" +--verify +. IP "List connected USRP X-Series devices:" +--list +.IP "This help information:" +--help +.SH EXAMPLES +.SS Burning a 1-Gigabit image over Ethernet +.sp +usrp_x3xx_fpga_burner --addr=192.168.10.2 --type=1G +.SS Burning a Hybrid image over PCIe +usrp_x3xx_fpga_burner --resource=RIO0 --type=HGS +.SS Burning a custom FPGA image over Ethernet +usrp_x3xx_fpga_burner --addr=192.168.10.2 --fpga=path="custom_image.bit" +.ft +.fi +.SH SEE ALSO +UHD documentation: +.B http://files.ettus.com/uhd_docs/manual/html/index.html +.LP +GR-UHD documentation: +.B http://gnuradio.org/doc/doxygen/page_uhd.html +.LP +Other UHD programs: +.sp +uhd_images_downloader(1) usrp2_card_burner(1) usrp_n2xx_simple_net_burner(1) +.SH AUTHOR +This manual page was written by Nicholas Corgan +for the Debian project (but may be used by others). +.SH COPYRIGHT +Copyright (c) 2014 Ettus Research LLC +.LP +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. +.LP +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. diff --git a/host/examples/CMakeLists.txt b/host/examples/CMakeLists.txt index 53e913070..4f394bbef 100644 --- a/host/examples/CMakeLists.txt +++ b/host/examples/CMakeLists.txt @@ -36,6 +36,7 @@ SET(example_sources tx_waveforms.cpp txrx_loopback_to_file.cpp latency_test.cpp + fpgpio.cpp ) #for each source: build an executable and install diff --git a/host/examples/benchmark_rate.cpp b/host/examples/benchmark_rate.cpp index 3b2ab8a1f..ea49d48d9 100644 --- a/host/examples/benchmark_rate.cpp +++ b/host/examples/benchmark_rate.cpp @@ -66,8 +66,18 @@ void benchmark_rx_rate(uhd::usrp::multi_usrp::sptr usrp, const std::string &rx_c cmd.time_spec = usrp->get_time_now() + uhd::time_spec_t(0.05); cmd.stream_now = (buffs.size() == 1); rx_stream->issue_stream_cmd(cmd); + while (not boost::this_thread::interruption_requested()){ - num_rx_samps += rx_stream->recv(buffs, max_samps_per_packet, md)*rx_stream->get_num_channels(); + try { + num_rx_samps += rx_stream->recv(buffs, max_samps_per_packet, md)*rx_stream->get_num_channels(); + } + catch (...) { + /* apparently, the boost thread interruption can sometimes result in + throwing exceptions not of type boost::exception, this catch allows + this thread to still attempt to issue the STREAM_MODE_STOP_CONTINUOUS + */ + break; + } //handle the error codes switch(md.error_code){ @@ -78,10 +88,13 @@ void benchmark_rx_rate(uhd::usrp::multi_usrp::sptr usrp, const std::string &rx_c } break; + // ERROR_CODE_OVERFLOW can indicate overflow or sequence error case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW: - had_an_overflow = true; last_time = md.time_spec; - num_overflows++; + had_an_overflow = true; + // check out_of_sequence flag to see if it was a sequence error or overflow + if (!md.out_of_sequence) + num_overflows++; break; default: @@ -89,7 +102,6 @@ void benchmark_rx_rate(uhd::usrp::multi_usrp::sptr usrp, const std::string &rx_c std::cerr << "Unexpected error on recv, continuing..." << std::endl; break; } - } rx_stream->issue_stream_cmd(uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS); } @@ -226,7 +238,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ boost::split(channel_strings, channel_list, boost::is_any_of("\"',")); for(size_t ch = 0; ch < channel_strings.size(); ch++){ size_t chan = boost::lexical_cast<int>(channel_strings[ch]); - if(chan >= usrp->get_tx_num_channels() or chan >= usrp->get_tx_num_channels()){ + if(chan >= usrp->get_tx_num_channels() or chan >= usrp->get_rx_num_channels()){ throw std::runtime_error("Invalid channel(s) specified."); } else channel_nums.push_back(boost::lexical_cast<int>(channel_strings[ch])); @@ -275,6 +287,6 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ //finished std::cout << std::endl << "Done!" << std::endl << std::endl; - + return EXIT_SUCCESS; } diff --git a/host/examples/fpgpio.cpp b/host/examples/fpgpio.cpp new file mode 100644 index 000000000..89a6ecec3 --- /dev/null +++ b/host/examples/fpgpio.cpp @@ -0,0 +1,374 @@ +// +// Copyright 2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +// Example for front panel GPIO. +// Bits are set as follows: +// FPGPIO[0] = ATR output 1 at idle +// FPGPIO[1] = ATR output 1 during RX +// FPGPIO[2] = ATR output 1 during TX +// FPGPIO[3] = ATR output 1 during full duplex +// FPGPIO[4] = output +// FPGPIO[5] = input +// FPGPIO[6] = input +// FPGPIO[7] = input +// FPGPIO[8] = input +// FPGPIO[9] = input +// FPGPIO[10] = input +// The example cycles through idle, TX, RX, and full duplex, spending 2 seconds for each. +// Outputs can be physically looped back to inputs for verification testing. + +#include <uhd/utils/thread_priority.hpp> +#include <uhd/utils/safe_main.hpp> +#include <uhd/usrp/multi_usrp.hpp> +#include <uhd/convert.hpp> +#include <boost/program_options.hpp> +#include <boost/format.hpp> +#include <boost/thread.hpp> +#include <csignal> +#include <iostream> + +#define FPGPIO_DEFAULT_CPU_FORMAT "fc32" +#define FPGPIO_DEFAULT_OTW_FORMAT "sc16" +#define FPGPIO_DEFAULT_RX_RATE 1e6 +#define FPGPIO_DEFAULT_TX_RATE 1e6 +#define FPGPIO_DEFAULT_DWELL_TIME 2.0 +#define FPGPIO_NUM_BITS 11 +#define FPGPIO_BIT(x) (1 << x) + +namespace po = boost::program_options; + +static bool stop_signal_called = false; +void sig_int_handler(int){stop_signal_called = true;} + +std::string to_bit_string(boost::uint16_t val) +{ + std::string out; + for (int i = FPGPIO_NUM_BITS - 1; i >= 0; i--) + { + std::string bit = ((val >> i) & 1) ? "1" : "0"; + out += " "; + out += bit; + } + return out; +} + +void output_reg_values(const std::string bank, const uhd::usrp::multi_usrp::sptr &usrp) +{ + std::cout << (boost::format("Bit ")); + for (int i = FPGPIO_NUM_BITS - 1; i >= 0; i--) + std::cout << (boost::format(" %s%d") % (i < 10 ? " " : "") % i); + std::cout << std::endl; + std::cout << "CTRL: " << to_bit_string(uint16_t(usrp->get_gpio_attr(bank, std::string("CTRL")))) << std::endl; + std::cout << "DDR: " << to_bit_string(uint16_t(usrp->get_gpio_attr(bank, std::string("DDR")))) << std::endl; + std::cout << "ATR_0X: " << to_bit_string(uint16_t(usrp->get_gpio_attr(bank, std::string("ATR_0X")))) << std::endl; + std::cout << "ATR_RX: " << to_bit_string(uint16_t(usrp->get_gpio_attr(bank, std::string("ATR_RX")))) << std::endl; + std::cout << "ATR_TX: " << to_bit_string(uint16_t(usrp->get_gpio_attr(bank, std::string("ATR_TX")))) << std::endl; + std::cout << "ATR_XX: " << to_bit_string(uint16_t(usrp->get_gpio_attr(bank, std::string("ATR_XX")))) << std::endl; + std::cout << "OUT: " << to_bit_string(uint16_t(usrp->get_gpio_attr(bank, std::string("OUT")))) << std::endl; + std::cout << "READBACK: " << to_bit_string(uint16_t(usrp->get_gpio_attr(bank, std::string("READBACK")))) << std::endl; +} + +int UHD_SAFE_MAIN(int argc, char *argv[]){ + uhd::set_thread_priority_safe(); + + //variables to be set by po + std::string args; + std::string cpu, otw; + double rx_rate, tx_rate, dwell; + const std::string fpgpio = "FP0"; + + //setup the program options + po::options_description desc("Allowed options"); + desc.add_options() + ("help", "help message") + ("args", po::value<std::string>(&args)->default_value(""), "multi uhd device address args") + ("repeat", "repeat loop until Ctrl-C is pressed") + ("cpu", po::value<std::string>(&cpu)->default_value(FPGPIO_DEFAULT_CPU_FORMAT), "cpu data format") + ("otw", po::value<std::string>(&otw)->default_value(FPGPIO_DEFAULT_OTW_FORMAT), "over the wire data format") + ("rx_rate", po::value<double>(&rx_rate)->default_value(FPGPIO_DEFAULT_RX_RATE), "rx sample rate") + ("tx_rate", po::value<double>(&tx_rate)->default_value(FPGPIO_DEFAULT_TX_RATE), "tx sample rate") + ("dwell", po::value<double>(&dwell)->default_value(FPGPIO_DEFAULT_DWELL_TIME), "dwell time in seconds for each test case") + ; + 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("Front Panel GPIO %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; + + //print out initial unconfigured state of FP GPIO + std::cout << "Unconfigured GPIO values:" << std::endl; + output_reg_values(fpgpio, usrp); + + //configure GPIO registers + uint32_t ctrl = 0; // default all as manual + uint32_t ddr = 0; // default all as input + uint32_t atr_idle = 0; + uint32_t atr_rx = 0; + uint32_t atr_tx = 0; + uint32_t atr_duplex = 0; + uint32_t mask = 0x7ff; + + //set up FPGPIO outputs: + //FPGPIO[0] = ATR output 1 at idle + ctrl |= FPGPIO_BIT(0); + atr_idle |= FPGPIO_BIT(0); + ddr |= FPGPIO_BIT(0); + + //FPGPIO[1] = ATR output 1 during RX + ctrl |= FPGPIO_BIT(1); + ddr |= FPGPIO_BIT(1); + atr_rx |= FPGPIO_BIT(1); + + //FPGPIO[2] = ATR output 1 during TX + ctrl |= FPGPIO_BIT(2); + ddr |= FPGPIO_BIT(2); + atr_tx |= FPGPIO_BIT(2); + + //FPGPIO[3] = ATR output 1 during full duplex + ctrl |= FPGPIO_BIT(3); + ddr |= FPGPIO_BIT(3); + atr_duplex |= FPGPIO_BIT(3); + + //FPGPIO[4] = output + ddr |= FPGPIO_BIT(4); + + //set data direction register (DDR) + usrp->set_gpio_attr(fpgpio, std::string("DDR"), ddr, mask); + + //set ATR registers + usrp->set_gpio_attr(fpgpio, std::string("ATR_0X"), atr_idle, mask); + usrp->set_gpio_attr(fpgpio, std::string("ATR_RX"), atr_rx, mask); + usrp->set_gpio_attr(fpgpio, std::string("ATR_TX"), atr_tx, mask); + usrp->set_gpio_attr(fpgpio, std::string("ATR_XX"), atr_duplex, mask); + + //set control register + usrp->set_gpio_attr(fpgpio, std::string("CTRL"), ctrl, mask); + + //print out initial state of FP GPIO + std::cout << "\nConfigured GPIO values:" << std::endl; + output_reg_values(fpgpio, usrp); + std::cout << std::endl; + + //set up streams + uhd::stream_args_t rx_args(cpu, otw); + uhd::stream_args_t tx_args(cpu, otw); + uhd::rx_streamer::sptr rx_stream = usrp->get_rx_stream(rx_args); + uhd::tx_streamer::sptr tx_stream = usrp->get_tx_stream(tx_args); + uhd::stream_cmd_t rx_cmd(uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS); + rx_cmd.stream_now = true; + usrp->set_rx_rate(rx_rate); + usrp->set_tx_rate(tx_rate); + + //set up buffers for tx and rx + const size_t max_samps_per_packet = rx_stream->get_max_num_samps(); + const size_t nsamps_per_buff = max_samps_per_packet; + std::vector<char> rx_buff(max_samps_per_packet*uhd::convert::get_bytes_per_item(cpu)); + std::vector<char> tx_buff(max_samps_per_packet*uhd::convert::get_bytes_per_item(cpu)); + std::vector<void *> rx_buffs, tx_buffs; + for (size_t ch = 0; ch < rx_stream->get_num_channels(); ch++) + rx_buffs.push_back(&rx_buff.front()); //same buffer for each channel + for (size_t ch = 0; ch < tx_stream->get_num_channels(); ch++) + tx_buffs.push_back(&tx_buff.front()); //same buffer for each channel + + uhd::rx_metadata_t rx_md; + uhd::tx_metadata_t tx_md; + tx_md.has_time_spec = false; + tx_md.start_of_burst = true; + uhd::time_spec_t stop_time; + double timeout = 0.01; + uhd::time_spec_t dwell_time(dwell); + int loop = 0; + boost::uint32_t rb, expected; + + //register singal handler + std::signal(SIGINT, &sig_int_handler); + + //Test the mask - only need to test once with no dwell time + std::cout << "\nTesting mask..." << std::flush; + //send a value of all 1's to the DDR with a mask for only bit 10 + usrp->set_gpio_attr(fpgpio, std::string("DDR"), ~0, FPGPIO_BIT(10)); + //bit 10 should now be 1, but all the other bits should be unchanged + rb = usrp->get_gpio_attr(fpgpio, std::string("DDR")) & mask; + expected = ddr | FPGPIO_BIT(10); + if (rb == expected) + std::cout << "pass" << std::endl; + else + std::cout << "fail" << std::endl; + std::cout << std::endl; + output_reg_values(fpgpio, usrp); + usrp->set_gpio_attr(fpgpio, std::string("DDR"), ddr, mask); + + while (not stop_signal_called) + { + int failures = 0; + + if (vm.count("repeat")) + std::cout << "Press Ctrl + C to quit..." << std::endl; + + // test user controlled GPIO and ATR idle by setting bit 4 high for 1 second + std::cout << "\nTesting user controlled GPIO and ATR idle output..." << std::flush; + usrp->set_gpio_attr(fpgpio, "OUT", 1 << 4, 1 << 4); + stop_time = usrp->get_time_now() + dwell_time; + while (not stop_signal_called and usrp->get_time_now() < stop_time) + { + boost::this_thread::sleep(boost::posix_time::milliseconds(100)); + } + rb = usrp->get_gpio_attr(fpgpio, "READBACK"); + expected = FPGPIO_BIT(4) | FPGPIO_BIT(0); + if ((rb & expected) != expected) + { + ++failures; + std::cout << "fail" << std::endl; + if ((rb & FPGPIO_BIT(0)) == 0) + std::cout << "Bit 0 should be set, but is not" << std::endl; + if ((rb & FPGPIO_BIT(4)) == 0) + std::cout << "Bit 4 should be set, but is not" << std::endl; + } else { + std::cout << "pass" << std::endl; + } + std::cout << std::endl; + output_reg_values(fpgpio, usrp); + usrp->set_gpio_attr(fpgpio, "OUT", 0, FPGPIO_BIT(4)); + if (stop_signal_called) + break; + + // test ATR RX by receiving for 1 second + std::cout << "\nTesting ATR RX output..." << std::flush; + rx_cmd.stream_mode = uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS; + rx_stream->issue_stream_cmd(rx_cmd); + stop_time = usrp->get_time_now() + dwell_time; + while (not stop_signal_called and usrp->get_time_now() < stop_time) + { + try { + rx_stream->recv(rx_buffs, nsamps_per_buff, rx_md, timeout); + } catch(...){} + } + rb = usrp->get_gpio_attr(fpgpio, "READBACK"); + expected = FPGPIO_BIT(1); + if ((rb & expected) != expected) + { + ++failures; + std::cout << "fail" << std::endl; + std::cout << "Bit 1 should be set, but is not" << std::endl; + } else { + std::cout << "pass" << std::endl; + } + std::cout << std::endl; + output_reg_values(fpgpio, usrp); + rx_stream->issue_stream_cmd(uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS); + //clear out any data left in the rx stream + try { + rx_stream->recv(rx_buffs, nsamps_per_buff, rx_md, timeout); + } catch(...){} + if (stop_signal_called) + break; + + // test ATR TX by transmitting for 1 second + std::cout << "\nTesting ATR TX output..." << std::flush; + stop_time = usrp->get_time_now() + dwell_time; + tx_md.start_of_burst = true; + tx_md.end_of_burst = false; + while (not stop_signal_called and usrp->get_time_now() < stop_time) + { + try { + tx_stream->send(tx_buffs, nsamps_per_buff, tx_md, timeout); + tx_md.start_of_burst = false; + } catch(...){} + } + rb = usrp->get_gpio_attr(fpgpio, "READBACK"); + expected = FPGPIO_BIT(2); + if ((rb & expected) != expected) + { + ++failures; + std::cout << "fail" << std::endl; + std::cout << "Bit 2 should be set, but is not" << std::endl; + } else { + std::cout << "pass" << std::endl; + } + std::cout << std::endl; + output_reg_values(fpgpio, usrp); + tx_md.end_of_burst = true; + try { + tx_stream->send(tx_buffs, nsamps_per_buff, tx_md, timeout); + } catch(...){} + if (stop_signal_called) + break; + + // test ATR RX by transmitting and receiving for 1 second + std::cout << "\nTesting ATR full duplex output..." << std::flush; + rx_cmd.stream_mode = uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS; + rx_stream->issue_stream_cmd(rx_cmd); + tx_md.start_of_burst = true; + tx_md.end_of_burst = false; + stop_time = usrp->get_time_now() + dwell_time; + while (not stop_signal_called and usrp->get_time_now() < stop_time) + { + try { + tx_stream->send(rx_buffs, nsamps_per_buff, tx_md, timeout); + tx_md.start_of_burst = false; + rx_stream->recv(tx_buffs, nsamps_per_buff, rx_md, timeout); + } catch(...){} + } + rb = usrp->get_gpio_attr(fpgpio, "READBACK"); + expected = FPGPIO_BIT(3); + if ((rb & expected) != expected) + { + ++failures; + std::cout << "fail" << std::endl; + std::cout << "Bit 3 should be set, but is not" << std::endl; + } else { + std::cout << "pass" << std::endl; + } + std::cout << std::endl; + output_reg_values(fpgpio, usrp); + rx_stream->issue_stream_cmd(uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS); + tx_md.end_of_burst = true; + try { + tx_stream->send(tx_buffs, nsamps_per_buff, tx_md, timeout); + } catch(...){} + //clear out any data left in the rx stream + try { + rx_stream->recv(rx_buffs, nsamps_per_buff, rx_md, timeout); + } catch(...){} + + std::cout << std::endl; + if (failures) + std::cout << failures << " tests failed" << std::endl; + else + std::cout << "All tests passed!" << std::endl; + + if (!vm.count("repeat")) + break; + + std::cout << (boost::format("\nLoop %d completed") % ++loop) << std::endl; + } + + //finished + std::cout << std::endl << "Done!" << std::endl << std::endl; + + return EXIT_SUCCESS; +} diff --git a/host/examples/rx_timed_samples.cpp b/host/examples/rx_timed_samples.cpp index 0eea2ffee..cc9216cb7 100644 --- a/host/examples/rx_timed_samples.cpp +++ b/host/examples/rx_timed_samples.cpp @@ -20,6 +20,7 @@ #include <uhd/usrp/multi_usrp.hpp> #include <boost/program_options.hpp> #include <boost/format.hpp> +#include <boost/algorithm/string.hpp> #include <iostream> #include <complex> @@ -30,19 +31,24 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ //variables to be set by po std::string args; + std::string wire; double seconds_in_future; size_t total_num_samps; double rate; + std::string channel_list; //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") + ("wire", po::value<std::string>(&wire)->default_value(""), "the over the wire type, sc16, sc8, etc") ("secs", po::value<double>(&seconds_in_future)->default_value(1.5), "number of seconds in the future to receive") ("nsamps", po::value<size_t>(&total_num_samps)->default_value(10000), "total number of samples to receive") ("rate", po::value<double>(&rate)->default_value(100e6/16), "rate of incoming samples") ("dilv", "specify to disable inner-loop verbose") + ("channels", po::value<std::string>(&channel_list)->default_value("0"), "which channel(s) to use (specify \"0\", \"1\", \"0,1\", etc)") + ; po::variables_map vm; po::store(po::parse_command_line(argc, argv, desc), vm); @@ -62,6 +68,18 @@ 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; + //detect which channels to use + std::vector<std::string> channel_strings; + std::vector<size_t> channel_nums; + boost::split(channel_strings, channel_list, boost::is_any_of("\"',")); + for(size_t ch = 0; ch < channel_strings.size(); ch++){ + size_t chan = boost::lexical_cast<int>(channel_strings[ch]); + if(chan >= usrp->get_tx_num_channels() or chan >= usrp->get_rx_num_channels()){ + throw std::runtime_error("Invalid channel(s) specified."); + } + else channel_nums.push_back(boost::lexical_cast<int>(channel_strings[ch])); + } + //set the rx sample rate std::cout << boost::format("Setting RX Rate: %f Msps...") % (rate/1e6) << std::endl; usrp->set_rx_rate(rate); @@ -71,7 +89,8 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ usrp->set_time_now(uhd::time_spec_t(0.0)); //create a receive streamer - uhd::stream_args_t stream_args("fc32"); //complex floats + uhd::stream_args_t stream_args("fc32", wire); //complex floats + stream_args.channels = channel_nums; uhd::rx_streamer::sptr rx_stream = usrp->get_rx_stream(stream_args); //setup streaming @@ -90,6 +109,9 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ //allocate buffer to receive with samples std::vector<std::complex<float> > buff(rx_stream->get_max_num_samps()); + std::vector<void *> buffs; + for (size_t ch = 0; ch < rx_stream->get_num_channels(); ch++) + buffs.push_back(&buff.front()); //same buffer for each channel //the first call to recv() will block this many seconds before receiving double timeout = seconds_in_future + 0.1; //timeout (delay before receive + padding) @@ -98,7 +120,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ while(num_acc_samps < total_num_samps){ //receive a single packet size_t num_rx_samps = rx_stream->recv( - &buff.front(), buff.size(), md, timeout, true + buffs, buff.size(), md, timeout, true ); //use a small timeout for subsequent packets diff --git a/host/examples/transport_hammer.cpp b/host/examples/transport_hammer.cpp index a44e81a82..4b949e5bd 100644 --- a/host/examples/transport_hammer.cpp +++ b/host/examples/transport_hammer.cpp @@ -83,7 +83,8 @@ void rx_hammer(uhd::usrp::multi_usrp::sptr usrp, const std::string &rx_cpu, uhd: case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW: had_an_overflow = true; last_time = md.time_spec; - num_overflows++; + if (!md.out_of_sequence) + num_overflows++; break; default: diff --git a/host/examples/tx_samples_from_file.cpp b/host/examples/tx_samples_from_file.cpp index 6926f8690..000f5086b 100644 --- a/host/examples/tx_samples_from_file.cpp +++ b/host/examples/tx_samples_from_file.cpp @@ -71,7 +71,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ //variables to be set by po std::string args, file, type, ant, subdev, ref, wirefmt; size_t spb; - double rate, freq, gain, bw, delay; + double rate, freq, gain, bw, delay, lo_off; //setup the program options po::options_description desc("Allowed options"); @@ -83,6 +83,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ ("spb", po::value<size_t>(&spb)->default_value(10000), "samples per buffer") ("rate", po::value<double>(&rate), "rate of outgoing samples") ("freq", po::value<double>(&freq), "RF center frequency in Hz") + ("lo_off", po::value<double>(&lo_off), "Offset for frontend LO in Hz (optional)") ("gain", po::value<double>(&gain), "gain for the RF chain") ("ant", po::value<std::string>(&ant), "daughterboard antenna selection") ("subdev", po::value<std::string>(&subdev), "daughterboard subdevice specification") @@ -133,7 +134,9 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ return ~0; } std::cout << boost::format("Setting TX Freq: %f MHz...") % (freq/1e6) << std::endl; - uhd::tune_request_t tune_request(freq); + uhd::tune_request_t tune_request; + if(vm.count("lo_off")) tune_request = uhd::tune_request_t(freq, lo_off); + else tune_request = uhd::tune_request_t(freq); if(vm.count("int-n")) tune_request.args = uhd::device_addr_t("mode_n=int-n"); usrp->set_tx_freq(tune_request); std::cout << boost::format("Actual TX Freq: %f MHz...") % (usrp->get_tx_freq()/1e6) << std::endl << std::endl; diff --git a/host/examples/tx_timed_samples.cpp b/host/examples/tx_timed_samples.cpp index 8826deadd..667ae66a9 100644 --- a/host/examples/tx_timed_samples.cpp +++ b/host/examples/tx_timed_samples.cpp @@ -31,6 +31,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ //variables to be set by po std::string args; + std::string wire; double seconds_in_future; size_t total_num_samps; double rate; @@ -41,6 +42,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ desc.add_options() ("help", "help message") ("args", po::value<std::string>(&args)->default_value(""), "single uhd device address args") + ("wire", po::value<std::string>(&wire)->default_value(""), "the over the wire type, sc16, sc8, etc") ("secs", po::value<double>(&seconds_in_future)->default_value(1.5), "number of seconds in the future to transmit") ("nsamps", po::value<size_t>(&total_num_samps)->default_value(10000), "total number of samples to transmit") ("rate", po::value<double>(&rate)->default_value(100e6/16), "rate of outgoing samples") @@ -74,7 +76,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ usrp->set_time_now(uhd::time_spec_t(0.0)); //create a transmit streamer - uhd::stream_args_t stream_args("fc32"); //complex floats + uhd::stream_args_t stream_args("fc32", wire); //complex floats uhd::tx_streamer::sptr tx_stream = usrp->get_tx_stream(stream_args); //allocate buffer with data to send diff --git a/host/include/uhd/device.hpp b/host/include/uhd/device.hpp index de39383c9..1090c243c 100644 --- a/host/include/uhd/device.hpp +++ b/host/include/uhd/device.hpp @@ -21,6 +21,7 @@ #include <uhd/config.hpp> #include <uhd/stream.hpp> #include <uhd/deprecated.hpp> +#include <uhd/property_tree.hpp> #include <uhd/types/device_addr.hpp> #include <boost/utility.hpp> #include <boost/shared_ptr.hpp> @@ -83,10 +84,12 @@ public: virtual tx_streamer::sptr get_tx_stream(const stream_args_t &args) = 0; //! Get access to the underlying property structure - virtual boost::shared_ptr<property_tree> get_tree(void) const = 0; + uhd::property_tree::sptr get_tree(void) const; #include <uhd/device_deprecated.ipp> +protected: + uhd::property_tree::sptr _tree; }; } //namespace uhd diff --git a/host/include/uhd/transport/CMakeLists.txt b/host/include/uhd/transport/CMakeLists.txt index 1031da817..c22554e97 100644 --- a/host/include/uhd/transport/CMakeLists.txt +++ b/host/include/uhd/transport/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2010-2011 Ettus Research LLC +# Copyright 2010-2013 Ettus Research LLC # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -23,6 +23,7 @@ UHD_INSTALL(FILES if_addrs.hpp udp_simple.hpp udp_zero_copy.hpp + tcp_zero_copy.hpp usb_control.hpp usb_zero_copy.hpp usb_device_handle.hpp diff --git a/host/include/uhd/transport/nirio/nifpga_lvbitx.h b/host/include/uhd/transport/nirio/nifpga_lvbitx.h new file mode 100644 index 000000000..598f7fcbe --- /dev/null +++ b/host/include/uhd/transport/nirio/nifpga_lvbitx.h @@ -0,0 +1,59 @@ +// +// Copyright 2013-2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// 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_TRANSPORT_NIRIO_NIFPGA_LVBITX_H +#define INCLUDED_UHD_TRANSPORT_NIRIO_NIFPGA_LVBITX_H + +#include <uhd/transport/nirio/nirio_resource_manager.h> +#include <uhd/transport/nirio/niriok_proxy.h> +#include <boost/smart_ptr.hpp> + +namespace uhd { namespace niusrprio { + +class UHD_API nifpga_lvbitx { +public: + typedef boost::shared_ptr<nifpga_lvbitx> sptr; + + virtual ~nifpga_lvbitx() {}; + + virtual const char* get_bitfile_path() = 0; + virtual const char* get_signature() = 0; + virtual const char* get_bitstream_checksum() = 0; + + virtual size_t get_input_fifo_count() = 0; + virtual const char** get_input_fifo_names() = 0; + + virtual size_t get_output_fifo_count() = 0; + virtual const char** get_output_fifo_names() = 0; + + virtual size_t get_control_count() = 0; + virtual const char** get_control_names() = 0; + + virtual size_t get_indicator_count() = 0; + virtual const char** get_indicator_names() = 0; + + virtual void init_register_info(nirio_register_info_vtr& vtr) = 0; + virtual void init_fifo_info(nirio_fifo_info_vtr& vtr) = 0; + +protected: + std::string _get_bitstream_checksum(const std::string& file_path); + std::string _get_fpga_images_dir(const std::string search_paths); +}; +}} + +#endif /* INCLUDED_UHD_TRANSPORT_NIRIO_NIFPGA_LVBITX_H */ + diff --git a/host/include/uhd/transport/nirio/nirio_driver_iface.h b/host/include/uhd/transport/nirio/nirio_driver_iface.h new file mode 100644 index 000000000..46a1146de --- /dev/null +++ b/host/include/uhd/transport/nirio/nirio_driver_iface.h @@ -0,0 +1,535 @@ +// +// Copyright 2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_UHD_TRANSPORT_NIRIO_NIRIO_DRIVER_IFACE_H +#define INCLUDED_UHD_TRANSPORT_NIRIO_NIRIO_DRIVER_IFACE_H + +#include <stddef.h> +#include <stdint.h> +#include <string> +#include <uhd/transport/nirio/status.h> +#include <uhd/config.hpp> +#if defined(UHD_PLATFORM_WIN32) + #include <Windows.h> + #pragma warning(disable:4201) // nonstandard extension used : nameless struct/union + #include <WinIoCtl.h> + #pragma warning(default:4201) +#elif defined(UHD_PLATFORM_MACOS) || defined(UHD_PLATFORM_BSD) + #include <IOKit/IOKitLib.h> +#endif + +// CTL_CODE macro for non-win OSes +#ifndef UHD_PLATFORM_WIN32 + #define CTL_CODE(a,controlCode,b,c) (controlCode) +#endif + +namespace nirio_driver_iface { + +const uint32_t NIRIO_IOCTL_BASE = 0x800; + +const uint32_t NIRIO_IOCTL_SYNCOP = + CTL_CODE(FILE_DEVICE_UNKNOWN, + NIRIO_IOCTL_BASE + 4, + METHOD_OUT_DIRECT, + FILE_READ_DATA | FILE_WRITE_DATA); + ///< The synchronous operation code. Note: We + /// must use METHOD_OUT_DIRECT on the syncOp() + /// IOCTL to ensure the contents of the output + /// block are available in the kernel. + +const uint32_t NIRIO_IOCTL_GET_IFACE_NUM = + CTL_CODE(FILE_DEVICE_UNKNOWN, + NIRIO_IOCTL_BASE + 6, + METHOD_BUFFERED, + FILE_READ_DATA); ///< Get the interface number for a device + +const uint32_t NIRIO_IOCTL_GET_SESSION = + CTL_CODE(FILE_DEVICE_UNKNOWN, + NIRIO_IOCTL_BASE + 8, + METHOD_BUFFERED, + FILE_READ_ACCESS); ///< Gets a previously opened session to a device + +const uint32_t NIRIO_IOCTL_POST_OPEN = + CTL_CODE(FILE_DEVICE_UNKNOWN, + NIRIO_IOCTL_BASE + 9, + METHOD_BUFFERED, + FILE_READ_ACCESS); ///< Called after opening a session + +const uint32_t NIRIO_IOCTL_PRE_CLOSE = + CTL_CODE(FILE_DEVICE_UNKNOWN, + NIRIO_IOCTL_BASE + 10, + METHOD_BUFFERED, + FILE_READ_ACCESS); ///< Called before closing a session + + +// ------------------------------- +// Function Codes: defined as integers rather than enums because they +// are going to be carried accross boundaries so size matters + +namespace NIRIO_FUNC +{ + const uint32_t GET32 = 0x00000001; + const uint32_t SET32 = 0x00000002; + const uint32_t SET_DRIVER_CONFIG = 0x00000007; + const uint32_t FIFO = 0x00000008; + const uint32_t IO = 0x0000000A; + const uint32_t FIFO_STOP_ALL = 0x0000000C; + const uint32_t ADD_RESOURCE = 0x0000000D; + const uint32_t GET_STRING = 0x0000000E; + const uint32_t SET_STRING = 0x0000000F; + const uint32_t DOWNLOAD = 0x00000013; + const uint32_t RESET = 0x00000014; +} + +namespace NIRIO_RESOURCE +{ + const uint32_t INPUT_FIFO = 0xD0000001; + const uint32_t OUTPUT_FIFO = 0xD0000002; +} + +namespace NIRIO_FIFO +{ + const uint32_t CONFIGURE = 0x80000001; + const uint32_t START = 0x80000002; + const uint32_t STOP = 0x80000003; + const uint32_t READ = 0x80000004; + const uint32_t WRITE = 0x80000005; + const uint32_t WAIT = 0x80000006; + const uint32_t GRANT = 0x80000007; +} + +namespace NIRIO_IO +{ + const uint32_t POKE64 = 0xA0000005; + const uint32_t POKE32 = 0xA0000006; + const uint32_t POKE16 = 0xA0000007; + const uint32_t POKE8 = 0xA0000008; + const uint32_t PEEK64 = 0xA0000009; + const uint32_t PEEK32 = 0xA000000A; + const uint32_t PEEK16 = 0xA000000B; + const uint32_t PEEK8 = 0xA000000C; + const uint32_t READ_BLOCK = 0xA000000D; + const uint32_t WRITE_BLOCK = 0xA000000E; + const uint32_t GET_IO_WINDOW = 0xA000000F; + const uint32_t GET_IO_WINDOW_SIZE = 0xA0000010; +} + +struct nirio_ioctl_packet_t { + nirio_ioctl_packet_t( + void* const _outBuf, + const uint32_t _outSize, + const int32_t _statusCode) + { + outBuf._64BitField = 0; + outBuf.pointer = _outBuf; + outSize = _outSize; + statusCode = _statusCode; + }; + + union { + void* pointer; + uint64_t _64BitField; + } outBuf; + + uint32_t outSize; + int32_t statusCode; +}; + +struct nirio_ioctl_block_t +{ + uint64_t inBuf; + uint64_t outBuf; + uint32_t inBufLength; + uint32_t outBufLength; + uint32_t bytesReturned; + uint32_t padding; +}; + +struct nirio_syncop_in_params_t +{ + uint32_t function; + uint32_t subfunction; + + union + { + struct + { + uint32_t attribute; + uint32_t value; + } attribute32; + + struct + { + uint32_t attribute; + uint64_t value; + } attribute64; + + struct + { + uint32_t attribute; + } attributeStr; + + struct + { + uint32_t attribute; + } download; + + union + { + struct + { + uint32_t reserved_field_0_0_0; + } reserved_field_0_0; + struct + { + uint32_t reserved_field_0_1_0; + uint32_t reserved_field_0_1_1; + } reserved_field_0_1; + struct + { + uint32_t reserved_field_0_2_0; + } reserved_field_0_2; + } reserved_field_0; + + union + { + struct + { + uint32_t channel; + uint32_t baseAddress; + uint32_t depthInSamples; + uint32_t version; + } fifo; + struct + { + uint32_t channel; + uint32_t baseAddress; + uint32_t depthInSamples; + uint32_t version; + uint32_t scalarType; + uint32_t bitWidth; + } fifoWithDataType; + struct + { + uint64_t rangeBaseAddress; + uint32_t rangeSizeInBytes; + uint32_t rangeAttribute; + } atomic; // obsolete + } add; + + struct + { + uint32_t channel; + + union + { + struct + { + uint32_t requestedDepth; + uint8_t requiresActuals; + } config; + struct + { + uint32_t timeout; + } read; + struct + { + uint32_t timeout; + uint32_t scalarType; + uint32_t bitWidth; + } readWithDataType; + struct + { + uint32_t timeout; + } write; + struct + { + uint32_t timeout; + uint32_t scalarType; + uint32_t bitWidth; + } writeWithDataType; + struct + { + uint32_t elementsRequested; + uint32_t scalarType; + uint32_t bitWidth; + uint32_t timeout; + uint8_t output; + } wait; + struct + { + uint32_t elements; + } grant; + } op; + } fifo; + + struct + { + uint64_t reserved_field_1_0; + uint32_t reserved_field_1_1; + uint32_t reserved_field_1_2; + } reserved_field_1; // Obsolete + + struct + { + uint32_t offset; + union + { + uint64_t value64; + uint32_t value32; + uint16_t value16; + uint8_t value8; + } value; + union + { + uint32_t sizeToMap; + } memoryMappedIoWindow; + } io; + + struct + { + uint32_t reserved_field_2_0; + uint32_t reserved_field_2_1; + } reserved_field_2; + + struct + { + uint32_t reserved_field_3_0; + } reserved_field_3; + + union + { + struct + { + uint32_t reserved_field_4_0; + int32_t reserved_field_4_1; + } wait; + } reserved_field_4; + + } params; + + uint32_t inbufByteLen; + + union + { + const void* pointer; + uint64_t _64BitField; + } inbuf; +}; + +static inline void init_syncop_in_params(nirio_syncop_in_params_t& param, const void* const buf, const uint32_t len) +{ + param.inbuf._64BitField = 0; + param.inbuf.pointer = buf; + param.inbufByteLen = len; +} + + +struct nirio_syncop_out_params_t +{ + union + { + struct + { + uint32_t value; + } attribute32; + + struct + { + uint64_t value; + } attribute64; + + union + { + struct + { + uint32_t reserved_field_0_0; + } enable; + } reserved_field_0; + + struct + { + union + { + struct + { + uint32_t actualDepth; + uint32_t actualSize; + } config; + struct + { + uint32_t numberRead; + uint32_t numberRemaining; + } read; + struct + { + uint32_t numberRemaining; + } write; + struct + { + union + { + void* pointer; + uint64_t _64BitField; + } elements; + } wait; + } op; + } fifo; + + struct + { + union + { + union + { + uint64_t value64; + uint32_t value32; + uint16_t value16; + uint8_t value8; + } value; + union + { + void* memoryMappedAddress; + uint64_t _64BitField; + } memoryMappedIoWindow; + union + { + uint32_t size; + } memoryMappedIoWindowSize; + }; + } io; + + uint32_t stringLength; + + struct + { + uint32_t reserved_field_1_0; + } reserved_field_1; + + } params; + + uint32_t outbufByteLen; + + union + { + void* pointer; + uint64_t _64BitField; + } outbuf; +}; + +static inline void init_syncop_out_params(nirio_syncop_out_params_t& param, void* buf, uint32_t len) +{ + param.outbuf._64BitField = 0; + param.outbuf.pointer = buf; + param.outbufByteLen = len; +} + + + +//Device handle definition +#if defined(UHD_PLATFORM_LINUX) + typedef int rio_dev_handle_t; +#elif defined(UHD_PLATFORM_WIN32) + typedef HANDLE rio_dev_handle_t; +#elif defined(UHD_PLATFORM_MACOS) || defined(UHD_PLATFORM_BSD) + typedef io_connect_t rio_dev_handle_t; +#else + #error OS not supported by nirio_driver_iface. +#endif +static const rio_dev_handle_t INVALID_RIO_HANDLE = ((rio_dev_handle_t)-1); + +//Memory mapping container definition +#if defined(UHD_PLATFORM_LINUX) + struct rio_mmap_t { + rio_mmap_t() : addr(NULL), size(0) {} + void *addr; + size_t size; + + bool is_null() { return (size == 0 || addr == NULL); } + }; +#elif defined(UHD_PLATFORM_WIN32) + enum access_mode_t { + ACCESS_MODE_READ, + ACCESS_MODE_WRITE + }; + + struct rio_mmap_params_t + { + uint64_t mapped_va_ptr; + uint64_t map_ready_event_handle; + uint32_t size; + uint16_t memoryType; + uint8_t access_mode; + }; + + struct rio_mmap_threadargs_t + { + rio_dev_handle_t device_handle; + rio_mmap_params_t params; + nirio_status status; + }; + + struct rio_mmap_t + { + rio_mmap_t() : addr(NULL) {} + void *addr; + HANDLE map_thread_handle; + rio_mmap_threadargs_t map_thread_args; + + bool is_null() { return addr == NULL; } + }; +#elif defined(UHD_PLATFORM_MACOS) || defined(UHD_PLATFORM_BSD) + struct rio_mmap_t { + rio_mmap_t() : addr(NULL) {} + void *addr; + + bool is_null() { return addr == NULL; } + }; +#else + #error OS not supported by nirio_driver_iface. +#endif + + nirio_status rio_open( + const std::string& device_path, + rio_dev_handle_t& device_handle); + + void rio_close( + rio_dev_handle_t& device_handle); + + bool rio_isopen( + rio_dev_handle_t device_handle); + + nirio_status rio_ioctl( + rio_dev_handle_t device_handle, + uint32_t ioctl_code, + const void *write_buf, + size_t write_buf_len, + void *read_buf, + size_t read_buf_len); + + nirio_status rio_mmap( + rio_dev_handle_t device_handle, + uint16_t memory_type, + size_t size, + bool writable, + rio_mmap_t &map); + + nirio_status rio_munmap( + rio_mmap_t &map); +} + +#endif diff --git a/host/include/uhd/transport/nirio/nirio_err_template.h b/host/include/uhd/transport/nirio/nirio_err_template.h new file mode 100644 index 000000000..9235c0384 --- /dev/null +++ b/host/include/uhd/transport/nirio/nirio_err_template.h @@ -0,0 +1,66 @@ +// +// Copyright 2013-2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// 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/>. +// + +/* + * NIRIO_ERR_INFO is undefined on purpose. + * This header is designed to be included with a custom definition of NIRIO_ERR_INFO + * To "generate" code in multiple files. This prevents duplication of code. + */ + +NIRIO_ERR_INFO(NiRio_Status_Success, 0, "No errors or warnings.") +NIRIO_ERR_INFO(NiRio_Status_FifoTimeout, -50400, "The timeout expired before the FIFO operation could complete.") +NIRIO_ERR_INFO(NiRio_Status_MemoryFull, -52000, "A memory allocation failed. Try again after rebooting.") +NIRIO_ERR_INFO(NiRio_Status_SoftwareFault, -52003, "An unexpected software error occurred.") +NIRIO_ERR_INFO(NiRio_Status_InvalidParameter, -52005, "A parameter to a function was not valid. This could be a NULL pointer, a bad value, etc.") +NIRIO_ERR_INFO(NiRio_Status_ResourceNotFound, -52006, "A required resource was not found. The NiFpga.* library, the RIO resource, or some other resource may be missing.") +NIRIO_ERR_INFO(NiRio_Status_ResourceNotInitialized, -52010, "A required resource was not properly initialized. This could occur if NiFpga_Initialize was not called or a required NiFpga_IrqContext was not reserved.") +NIRIO_ERR_INFO(NiRio_Status_FpgaAlreadyRunning, -61003, "The FPGA is already running.") +NIRIO_ERR_INFO(NiRio_Status_DeviceTypeMismatch, -61024, "The bitfile was not compiled for the specified resource's device type.") +NIRIO_ERR_INFO(NiRio_Status_CommunicationTimeout, -61046, "An error was detected in the communication between the host computer and the USRP device. This could be due to a hardware failure on the bus.") +NIRIO_ERR_INFO(NiRio_Status_IrqTimeout, -61060, "The timeout expired before any of the IRQs were asserted.") +NIRIO_ERR_INFO(NiRio_Status_CorruptBitfile, -61070, "The LVBITX configuration bitstream seems to be corrupt.") +NIRIO_ERR_INFO(NiRio_Status_BadDepth, -61072, "The requested FIFO depth is invalid. It is either 0 or an amount not supported by the hardware.") +NIRIO_ERR_INFO(NiRio_Status_BadReadWriteCount, -61073, "The number of FIFO elements is invalid. Either the number is greater than the depth of the host memory DMA FIFO, or more elements were requested for release than had been acquired.") +NIRIO_ERR_INFO(NiRio_Status_ClockLostLock, -61083, "A hardware clocking error occurred.") +NIRIO_ERR_INFO(NiRio_Status_FpgaBusy, -61141, "The operation could not be performed because the FPGA is busy.") +NIRIO_ERR_INFO(NiRio_Status_FpgaBusyFpgaInterfaceCApi, -61200, "The operation could not be performed because the FPGA is busy.") +NIRIO_ERR_INFO(NiRio_Status_FpgaBusyScanInterface, -61201, "The operation could not be performed because the chassis is in Scan Interface programming mode.") +NIRIO_ERR_INFO(NiRio_Status_FpgaBusyFpgaInterface, -61202, "The operation could not be performed because the FPGA is busy operating in FPGA Interface mode. Stop all activities on the FPGA before requesting this operation.") +NIRIO_ERR_INFO(NiRio_Status_FpgaBusyInteractive, -61203, "The operation could not be performed because the FPGA is busy operating in FPGA Interactive mode. Stop all activities on the FPGA before requesting this operation.") +NIRIO_ERR_INFO(NiRio_Status_FpgaBusyEmulation, -61204, "The operation could not be performed because the FPGA is busy operating in FPGA Emulation mode. Stop all activities on the FPGA before requesting this operation.") +NIRIO_ERR_INFO(NiRio_Status_GatedClockHandshakingViolation, -61216, "A gated clock has violated the handshaking protocol.") +NIRIO_ERR_INFO(NiRio_Status_RegionsOutstandingForSession, -61217, "A session cannot be closed, reset, nor can a bitfile be downloaded while DMA FIFO region references are still outstanding for the specified session.") +NIRIO_ERR_INFO(NiRio_Status_ElementsNotPermissibleToBeAcquired, -61219, "There are currently fewer unacquired elements left in the FIFO than are being requested. Release some acquired elements before acquiring more elements.") +NIRIO_ERR_INFO(NiRio_Status_InternalError, -61499, "An unexpected internal error occurred.") +NIRIO_ERR_INFO(NiRio_Status_AccessDenied, -63033, "Access to the local or remote system was denied.") +NIRIO_ERR_INFO(NiRio_Status_RpcConnectionError, -63040, "A connection could not be established to the specified remote device manager. Ensure that the devices are on, that NI-USRPRIO software is installed, and that the USRPRIO server is running and properly configured.") +NIRIO_ERR_INFO(NiRio_Status_RpcOperationError, -63042, "A fault on the network caused the RPC operation to fail.") +NIRIO_ERR_INFO(NiRio_Status_RpcSessionError, -63043, "The RPC session to the remote device manager is invalid. Ensure that the device is connected and try restarting the server.") +NIRIO_ERR_INFO(NiRio_Status_FifoReserved, -63082, "The operation could not complete because another session is accessing the FIFO. Close the other session and retry.") +NIRIO_ERR_INFO(NiRio_Status_FifoElementsCurrentlyAcquired, -63083, "A Configure FIFO, Stop FIFO, Read FIFO, or Write FIFO function was called while the host had acquired elements of the FIFO. Release all acquired elements before configuring, stopping, reading, or writing.") +NIRIO_ERR_INFO(NiRio_Status_MisalignedAccess, -63084, "A function was called using a misaligned address. The address must be a multiple of the size of the datatype.") +NIRIO_ERR_INFO(NiRio_Status_BitfileReadError, -63101, "A valid .lvbitx bitfile is required. If you are using a valid .lvbitx bitfile, the bitfile may not be compatible with the software you are using.") +NIRIO_ERR_INFO(NiRio_Status_SignatureMismatch, -63106, "The specified signature does not match the signature of the bitfile. If the bitfile has been recompiled, regenerate the C API and rebuild the application.") +NIRIO_ERR_INFO(NiRio_Status_IncompatibleBitfile, -63107, "The bitfile you are trying to use is not compatible with the version of NI-RIO installed on the target and/or the host.") +NIRIO_ERR_INFO(NiRio_Status_InvalidResourceName, -63192, "Either the supplied resource name is invalid as a RIO resource name, or the device was not found.") +NIRIO_ERR_INFO(NiRio_Status_FeatureNotSupported, -63193, "The requested feature is not supported.") +NIRIO_ERR_INFO(NiRio_Status_VersionMismatch, -63194, "Software version mismatch.") +NIRIO_ERR_INFO(NiRio_Status_InvalidSession, -63195, "The session is invalid or has been closed.") +NIRIO_ERR_INFO(NiRio_Status_OutOfHandles, -63198, "The maximum number of open FPGA sessions has been reached. Close some open sessions.") +NIRIO_ERR_INFO(NiRio_Status_DeviceLocked, -63031, "The operation is not allowed because another session in a different process is accessing the device. Close all other sessions and retry.") + + diff --git a/host/include/uhd/transport/nirio/nirio_fifo.h b/host/include/uhd/transport/nirio/nirio_fifo.h new file mode 100644 index 000000000..f7abb396f --- /dev/null +++ b/host/include/uhd/transport/nirio/nirio_fifo.h @@ -0,0 +1,132 @@ +// +// Copyright 2013-2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// 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_TRANSPORT_NIRIO_NIRIO_FIFO_H +#define INCLUDED_UHD_TRANSPORT_NIRIO_NIRIO_FIFO_H + +#include <uhd/transport/nirio/nirio_driver_iface.h> +#include <uhd/transport/nirio/niriok_proxy.h> +#include <uhd/transport/nirio/status.h> +#include <boost/noncopyable.hpp> +#include <boost/smart_ptr.hpp> +#include <string> +#include <boost/thread/recursive_mutex.hpp> +#include <boost/thread/thread.hpp> + +namespace uhd { namespace niusrprio { + +enum fifo_direction_t { + INPUT_FIFO, + OUTPUT_FIFO +}; + +enum nirio_scalar_t { + SCALAR_I8 = 1UL, + SCALAR_I16 = 2UL, + SCALAR_I32 = 3UL, + SCALAR_I64 = 4UL, + SCALAR_U8 = 5UL, + SCALAR_U16 = 6UL, + SCALAR_U32 = 7UL, + SCALAR_U64 = 8UL +}; + +struct datatype_info_t { + datatype_info_t(nirio_scalar_t t, uint32_t w):scalar_type(t),width(w) {} + nirio_scalar_t scalar_type; + uint32_t width; +}; + +template <typename data_t> +class nirio_fifo : private boost::noncopyable +{ +public: + typedef boost::shared_ptr< nirio_fifo<data_t> > sptr; + + nirio_fifo( + niriok_proxy& riok_proxy, + fifo_direction_t direction, + const std::string& name, + uint32_t fifo_instance); + virtual ~nirio_fifo(); + + nirio_status initialize( + const size_t requested_depth, + size_t& actual_depth, + size_t& actual_size); + + void finalize(); + + inline const std::string& get_name() const { return _name; } + inline uint32_t get_channel() const { return _fifo_channel; } + inline uint32_t get_direction() const { return _fifo_direction; } + inline uint32_t get_scalar_type() const { return _datatype_info.scalar_type; } + + nirio_status start(); + + nirio_status stop(); + + nirio_status acquire( + data_t*& elements, + const size_t elements_requested, + const uint32_t timeout, + size_t& elements_acquired, + size_t& elements_remaining); + + nirio_status release(const size_t elements); + + nirio_status read( + data_t* buf, + const uint32_t num_elements, + uint32_t timeout, + uint32_t& num_read, + uint32_t& num_remaining); + + nirio_status write( + const data_t* buf, + const uint32_t num_elements, + uint32_t timeout, + uint32_t& num_remaining); + +private: //Methods + bool _is_initialized(); + datatype_info_t _get_datatype_info(); + nirio_status _get_transfer_count(uint64_t& transfer_count); + nirio_status _ensure_transfer_completed(uint32_t timeout_ms); + +private: //Members + std::string _name; + fifo_direction_t _fifo_direction; + uint32_t _fifo_channel; + datatype_info_t _datatype_info; + size_t _acquired_pending; + nirio_driver_iface::rio_mmap_t _mem_map; + boost::recursive_mutex _mutex; + niriok_proxy* _riok_proxy_ptr; + + uint64_t _expected_xfer_count; + uint32_t _dma_base_addr; + + static const uint32_t FIFO_LOCK_TIMEOUT_IN_MS = 5000; +}; + +#include "nirio_fifo.ipp" + +}} + +#endif /* INCLUDED_UHD_TRANSPORT_NIRIO_NIRIO_FIFO_H */ diff --git a/host/include/uhd/transport/nirio/nirio_fifo.ipp b/host/include/uhd/transport/nirio/nirio_fifo.ipp new file mode 100644 index 000000000..80a0c2a89 --- /dev/null +++ b/host/include/uhd/transport/nirio/nirio_fifo.ipp @@ -0,0 +1,378 @@ +// +// Copyright 2013-2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// 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/>. +// + +#ifdef __clang__ + #pragma GCC diagnostic push ignored "-Wmissing-field-initializers" +#elif defined(__GNUC__) + #pragma GCC diagnostic ignored "-Wmissing-field-initializers" +#endif + +template <typename data_t> +nirio_fifo<data_t>::nirio_fifo( + niriok_proxy& riok_proxy, + fifo_direction_t direction, + const std::string& name, + uint32_t fifo_instance) : + _name(name), + _fifo_direction(direction), + _fifo_channel(fifo_instance), + _datatype_info(_get_datatype_info()), + _acquired_pending(0), + _mem_map(), + _riok_proxy_ptr(&riok_proxy), + _expected_xfer_count(0), + _dma_base_addr(0) +{ + nirio_status status = 0; + nirio_status_chain(_riok_proxy_ptr->set_attribute(ADDRESS_SPACE, BUS_INTERFACE), status); + uint32_t base_addr, addr_space_word; + nirio_status_chain(_riok_proxy_ptr->peek(0x1C, base_addr), status); + nirio_status_chain(_riok_proxy_ptr->peek(0xC, addr_space_word), status); + _dma_base_addr = base_addr + (_fifo_channel * (1<<((addr_space_word>>16)&0xF))); +} + +template <typename data_t> +nirio_fifo<data_t>::~nirio_fifo() +{ + finalize(); +} + +template <typename data_t> +nirio_status nirio_fifo<data_t>::initialize( + const size_t requested_depth, + size_t& actual_depth, + size_t& actual_size) +{ + nirio_status status = NiRio_Status_Success; + if (!_riok_proxy_ptr) return NiRio_Status_ResourceNotInitialized; + boost::unique_lock<boost::recursive_mutex> lock(_mutex); + + nirio_driver_iface::nirio_syncop_in_params_t in = {}; + nirio_driver_iface::nirio_syncop_out_params_t out = {}; + + //Forcefully stop the fifo if it is running + in.function = nirio_driver_iface::NIRIO_FUNC::FIFO; + in.subfunction = nirio_driver_iface::NIRIO_FIFO::STOP; + status = _riok_proxy_ptr->sync_operation(&in, sizeof(in), &out, sizeof(out)); + + in.function = nirio_driver_iface::NIRIO_FUNC::FIFO; + in.subfunction = nirio_driver_iface::NIRIO_FIFO::CONFIGURE; + + in.params.fifo.channel = _fifo_channel; + in.params.fifo.op.config.requestedDepth = static_cast<uint32_t>(requested_depth); + in.params.fifo.op.config.requiresActuals = 1; + + status = _riok_proxy_ptr->sync_operation(&in, sizeof(in), &out, sizeof(out)); + if (nirio_status_fatal(status)) return status; + + actual_depth = out.params.fifo.op.config.actualDepth; + actual_size = out.params.fifo.op.config.actualSize; + + status = _riok_proxy_ptr->map_fifo_memory(_fifo_channel, actual_size, _mem_map); + return status; +} + +template <typename data_t> +void nirio_fifo<data_t>::finalize() +{ + boost::unique_lock<boost::recursive_mutex> lock(_mutex); + if (!_mem_map.is_null()) { + stop(); + _riok_proxy_ptr->unmap_fifo_memory(_mem_map); + } +} + +template <typename data_t> +nirio_status nirio_fifo<data_t>::start() +{ + nirio_status status = NiRio_Status_Success; + if (!_riok_proxy_ptr) return NiRio_Status_ResourceNotInitialized; + + boost::unique_lock<boost::recursive_mutex> lock(_mutex); + + nirio_driver_iface::nirio_syncop_in_params_t in = {}; + nirio_driver_iface::nirio_syncop_out_params_t out = {}; + + in.function = nirio_driver_iface::NIRIO_FUNC::FIFO; + in.subfunction = nirio_driver_iface::NIRIO_FIFO::START; + + status = _riok_proxy_ptr->sync_operation(&in, sizeof(in), &out, sizeof(out)); + if (nirio_status_not_fatal(status)) { + _acquired_pending = 0; + _expected_xfer_count = 0; + } + return status; +} + +template <typename data_t> +nirio_status nirio_fifo<data_t>::stop() +{ + nirio_status status = NiRio_Status_Success; + if (!_riok_proxy_ptr) return NiRio_Status_ResourceNotInitialized; + + boost::unique_lock<boost::recursive_mutex> lock(_mutex); + if (_acquired_pending > 0) release(_acquired_pending); + + nirio_driver_iface::nirio_syncop_in_params_t in = {}; + nirio_driver_iface::nirio_syncop_out_params_t out = {}; + + in.function = nirio_driver_iface::NIRIO_FUNC::FIFO; + in.subfunction = nirio_driver_iface::NIRIO_FIFO::STOP; + + status = _riok_proxy_ptr->sync_operation(&in, sizeof(in), &out, sizeof(out)); + + return status; +} + +template <typename data_t> +nirio_status nirio_fifo<data_t>::acquire( + data_t*& elements, + const size_t elements_requested, + const uint32_t timeout, + size_t& elements_acquired, + size_t& elements_remaining) +{ + nirio_status status = NiRio_Status_Success; + if (!_riok_proxy_ptr || _mem_map.is_null()) return NiRio_Status_ResourceNotInitialized; + + boost::unique_lock<boost::recursive_mutex> lock(_mutex); + + nirio_driver_iface::nirio_syncop_in_params_t in = {}; + uint32_t stuffed[2]; + nirio_driver_iface::nirio_syncop_out_params_t out = {}; + init_syncop_out_params(out, stuffed, sizeof(stuffed)); + + in.function = nirio_driver_iface::NIRIO_FUNC::FIFO; + in.subfunction = nirio_driver_iface::NIRIO_FIFO::WAIT; + + in.params.fifo.channel = _fifo_channel; + in.params.fifo.op.wait.elementsRequested = static_cast<uint32_t>(elements_requested); + in.params.fifo.op.wait.scalarType = static_cast<uint32_t>(_datatype_info.scalar_type); + in.params.fifo.op.wait.bitWidth = _datatype_info.width * 8; + in.params.fifo.op.wait.output = _fifo_direction == OUTPUT_FIFO; + in.params.fifo.op.wait.timeout = timeout; + + status = _riok_proxy_ptr->sync_operation(&in, sizeof(in), &out, sizeof(out)); + + if (nirio_status_not_fatal(status)) { + elements = static_cast<data_t*>(out.params.fifo.op.wait.elements.pointer); + elements_acquired = stuffed[0]; + elements_remaining = stuffed[1]; + _acquired_pending = elements_acquired; + + if (UHD_NIRIO_RX_FIFO_XFER_CHECK_EN && + _riok_proxy_ptr->get_rio_quirks().rx_fifo_xfer_check_en() && + get_direction() == INPUT_FIFO + ) { + _expected_xfer_count += static_cast<uint64_t>(elements_requested * sizeof(data_t)); + status = _ensure_transfer_completed(timeout); + } + } + + return status; +} + +template <typename data_t> +nirio_status nirio_fifo<data_t>::release(const size_t elements) +{ + nirio_status status = NiRio_Status_Success; + if (!_riok_proxy_ptr) return NiRio_Status_ResourceNotInitialized; + + boost::unique_lock<boost::recursive_mutex> lock(_mutex); + + nirio_driver_iface::nirio_syncop_in_params_t in = {}; + nirio_driver_iface::nirio_syncop_out_params_t out = {}; + + in.function = nirio_driver_iface::NIRIO_FUNC::FIFO; + in.subfunction = nirio_driver_iface::NIRIO_FIFO::GRANT; + + in.params.fifo.channel = _fifo_channel; + in.params.fifo.op.grant.elements = static_cast<uint32_t>(elements); + + status = _riok_proxy_ptr->sync_operation(&in, sizeof(in), &out, sizeof(out)); + _acquired_pending = 0; + + return status; +} + +template <typename data_t> +nirio_status nirio_fifo<data_t>::read( + data_t* buf, + const uint32_t num_elements, + uint32_t timeout, + uint32_t& num_read, + uint32_t& num_remaining) +{ + nirio_status status = NiRio_Status_Success; + if (!_riok_proxy_ptr) return NiRio_Status_ResourceNotInitialized; + + boost::unique_lock<boost::recursive_mutex> lock(_mutex); + + nirio_driver_iface::nirio_syncop_in_params_t in = {}; + nirio_driver_iface::nirio_syncop_out_params_t out = {}; + init_syncop_out_params(out, buf, num_elements * _datatype_info.width); + + in.function = nirio_driver_iface::NIRIO_FUNC::FIFO; + in.subfunction = nirio_driver_iface::NIRIO_FIFO::READ; + + in.params.fifo.channel = _fifo_channel; + in.params.fifo.op.readWithDataType.timeout = timeout; + in.params.fifo.op.readWithDataType.scalarType = static_cast<uint32_t>(_datatype_info.scalar_type); + in.params.fifo.op.readWithDataType.bitWidth = _datatype_info.width * 8; + + status = _riok_proxy_ptr->sync_operation(&in, sizeof(in), &out, sizeof(out)); + + if (nirio_status_not_fatal(status) || status == NiRio_Status_FifoTimeout) { + num_read = out.params.fifo.op.read.numberRead; + num_remaining = out.params.fifo.op.read.numberRemaining; + } + + return status; +} + +template <typename data_t> +nirio_status nirio_fifo<data_t>::write( + const data_t* buf, + const uint32_t num_elements, + uint32_t timeout, + uint32_t& num_remaining) +{ + nirio_status status = NiRio_Status_Success; + if (!_riok_proxy_ptr) return NiRio_Status_ResourceNotInitialized; + + boost::unique_lock<boost::recursive_mutex> lock(_mutex); + + nirio_driver_iface::nirio_syncop_in_params_t in = {}; + init_syncop_in_params(in, buf, num_elements * _datatype_info.width); + nirio_driver_iface::nirio_syncop_out_params_t out = {}; + + in.function = nirio_driver_iface::NIRIO_FUNC::FIFO; + in.subfunction = nirio_driver_iface::NIRIO_FIFO::WRITE; + + in.params.fifo.channel = _fifo_channel; + in.params.fifo.op.writeWithDataType.timeout = timeout; + in.params.fifo.op.readWithDataType.scalarType = static_cast<uint32_t>(_datatype_info.scalar_type); + in.params.fifo.op.readWithDataType.bitWidth = _datatype_info.width * 8; + + status = _riok_proxy_ptr->sync_operation(&in, sizeof(in), &out, sizeof(out)); + + if (nirio_status_not_fatal(status) || status == NiRio_Status_FifoTimeout) { + num_remaining = out.params.fifo.op.write.numberRemaining; + } + + return status; +} + +template <typename data_t> +nirio_status nirio_fifo<data_t>::_get_transfer_count(uint64_t& transfer_count) +{ + //_riok_proxy_ptr must be valid and _mutex must be locked + + nirio_status status = NiRio_Status_Success; + uint32_t lower_half = 0, upper_half = 0; + nirio_status_chain(_riok_proxy_ptr->peek(_dma_base_addr + 0xA8, lower_half), status); //Latches both halves + nirio_status_chain(_riok_proxy_ptr->peek(_dma_base_addr + 0xAC, upper_half), status); + + if (nirio_status_not_fatal(status)) { + transfer_count = lower_half | (((uint64_t)upper_half) << 32); + } + return status; +} + +template <typename data_t> +nirio_status nirio_fifo<data_t>::_ensure_transfer_completed(uint32_t timeout_ms) +{ + //_riok_proxy_ptr must be valid and _mutex must be locked + + static const size_t MIN_TIMEOUT_IN_US = 2000; + + nirio_status status = NiRio_Status_Success; + uint64_t actual_xfer_count = 0; + nirio_status_chain(_get_transfer_count(actual_xfer_count), status); + + //We count the elapsed time using a simple counter instead of the high + //resolution timebase for efficieny reasons. The call to fetch the time + //requires a user-kernel transition which has a large overhead compared + //to a simple mem read. As a tradeoff, we deal with a less precise timeout. + size_t approx_us_elapsed = 0; + while ( + nirio_status_not_fatal(status) && + (_expected_xfer_count > actual_xfer_count) && + approx_us_elapsed++ < std::max<size_t>(MIN_TIMEOUT_IN_US, timeout_ms * 1000) + ) { + boost::this_thread::sleep(boost::posix_time::microseconds(1)); + nirio_status_chain(_get_transfer_count(actual_xfer_count), status); + } + + if (_expected_xfer_count > actual_xfer_count) { + nirio_status_chain(NiRio_Status_CommunicationTimeout, status); + } + + return status; +} + +template <> +inline datatype_info_t nirio_fifo<int8_t>::_get_datatype_info() +{ + return datatype_info_t(SCALAR_I8, 1); +} + +template <> +inline datatype_info_t nirio_fifo<int16_t>::_get_datatype_info() +{ + return datatype_info_t(SCALAR_I16, 2); +} + +template <> +inline datatype_info_t nirio_fifo<int32_t>::_get_datatype_info() +{ + return datatype_info_t(SCALAR_I32, 4); +} + +template <> +inline datatype_info_t nirio_fifo<int64_t>::_get_datatype_info() +{ + return datatype_info_t(SCALAR_I64, 8); +} + +template <> +inline datatype_info_t nirio_fifo<uint8_t>::_get_datatype_info() +{ + return datatype_info_t(SCALAR_U8, 1); +} + +template <> +inline datatype_info_t nirio_fifo<uint16_t>::_get_datatype_info() +{ + return datatype_info_t(SCALAR_U16, 2); +} + +template <> +inline datatype_info_t nirio_fifo<uint32_t>::_get_datatype_info() +{ + return datatype_info_t(SCALAR_U32, 4); +} + +template <> +inline datatype_info_t nirio_fifo<uint64_t>::_get_datatype_info() +{ + return datatype_info_t(SCALAR_U64, 8); +} + +#ifdef __GNUC__ + #pragma GCC diagnostic pop +#endif diff --git a/host/include/uhd/transport/nirio/nirio_quirks.h b/host/include/uhd/transport/nirio/nirio_quirks.h new file mode 100644 index 000000000..326eeeb8c --- /dev/null +++ b/host/include/uhd/transport/nirio/nirio_quirks.h @@ -0,0 +1,78 @@ +// +// Copyright 2013-2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// 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_TRANSPORT_NIRIO_NIRIO_QUIRKS_H +#define INCLUDED_UHD_TRANSPORT_NIRIO_NIRIO_QUIRKS_H + +#include <set> +#include <uhd/utils/log.hpp> + +//Quirk#1: We need to verify RX zero-copy data transfers from the RIO +// driver if we are in full duplex mode. +// This option allows disabling this quirk by compiling it out. +#define UHD_NIRIO_RX_FIFO_XFER_CHECK_EN 1 + +namespace uhd { namespace niusrprio { + +class nirio_quirks { +public: + nirio_quirks() : _tx_stream_count(0) { + } + + UHD_INLINE void register_tx_streams(const uint32_t tx_stream_indices[]) { + for (size_t i = 0; i < sizeof(tx_stream_indices)/sizeof(*tx_stream_indices); i++) { + _tx_stream_fifo_indices.insert(tx_stream_indices[i]); + } + } + + UHD_INLINE void add_tx_fifo(uint32_t index) { + if (_tx_stream_fifo_indices.find(index) != _tx_stream_fifo_indices.end()) { + if (_tx_stream_count == 0) { + UHD_LOG << "NI-RIO RX FIFO Transfer Check Quirk Enabled."; + } + _tx_stream_count++; + } + } + + UHD_INLINE void remove_tx_fifo(uint32_t index) { + if (_tx_stream_fifo_indices.find(index) != _tx_stream_fifo_indices.end()) { + _tx_stream_count--; + if (_tx_stream_count == 0) { + UHD_LOG << "NI-RIO RX FIFO Transfer Check Quirk Disabled."; + } + } + } + + //Quirk#1: We need to verify RX zero-copy data transfers from the RIO + // driver if we are in full duplex mode. + // This function returns true if the quirk is enabled for the + // current uhd_device configuration (dynamic) + UHD_INLINE bool rx_fifo_xfer_check_en() const { + //This function must be as fast as possible because it is in a high + //throughput data path + return _tx_stream_count > 0; + } + +private: + std::set<uint32_t> _tx_stream_fifo_indices; + size_t _tx_stream_count; +}; + +}} + +#endif /* INCLUDED_UHD_TRANSPORT_NIRIO_NIRIO_QUIRKS_H */ diff --git a/host/include/uhd/transport/nirio/nirio_resource_manager.h b/host/include/uhd/transport/nirio/nirio_resource_manager.h new file mode 100644 index 000000000..a10b3d532 --- /dev/null +++ b/host/include/uhd/transport/nirio/nirio_resource_manager.h @@ -0,0 +1,145 @@ +// +// Copyright 2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + + +#ifndef INCLUDED_UHD_TRANSPORT_NIRIO_NIRIO_RESOURCE_MANAGER_H +#define INCLUDED_UHD_TRANSPORT_NIRIO_NIRIO_RESOURCE_MANAGER_H + +#include <uhd/transport/nirio/nirio_fifo.h> +#include <uhd/transport/nirio/niriok_proxy.h> +#include <vector> +#include <map> +#include <string> + +namespace uhd { namespace niusrprio +{ +enum register_direction_t { + CONTROL, + INDICATOR +}; + +struct nirio_register_info_t { + nirio_register_info_t( + uint32_t arg_offset, + const char* arg_name, + register_direction_t arg_direction) : + offset(arg_offset), + name(arg_name), + direction(arg_direction) + {} + + uint32_t offset; + std::string name; + register_direction_t direction; +}; + +typedef std::vector<nirio_register_info_t> nirio_register_info_vtr; + +struct nirio_fifo_info_t { + nirio_fifo_info_t( + uint32_t arg_channel, + const char* arg_name, + fifo_direction_t arg_direction, + uint32_t arg_base_addr, + uint32_t arg_depth, + nirio_scalar_t arg_scalar_type, + uint32_t arg_width, + uint32_t arg_version) : + channel(arg_channel), + name(arg_name), + direction(arg_direction), + base_addr(arg_base_addr), + depth(arg_depth), + scalar_type(arg_scalar_type), + width(arg_width), + version(arg_version) + {} + + uint32_t channel; + std::string name; + fifo_direction_t direction; + uint32_t base_addr; + uint32_t depth; + nirio_scalar_t scalar_type; + uint32_t width; + uint32_t version; +}; + +typedef std::vector<nirio_fifo_info_t> nirio_fifo_info_vtr; + + +class nirio_resource_manager +{ +public: + nirio_resource_manager(niriok_proxy& proxy); + virtual ~nirio_resource_manager(); + + nirio_status initialize(const nirio_register_info_vtr& reg_info_vtr, const nirio_fifo_info_vtr& fifo_info_vtr); + void finalize(); + + nirio_status get_register_offset(const char* register_name, uint32_t& offset); + + template<typename data_t> + nirio_status create_tx_fifo(const char* fifo_name, boost::shared_ptr< nirio_fifo<data_t> >& fifo) + { + nirio_fifo_info_t* fifo_info_ptr = _lookup_fifo_info(fifo_name); + if (fifo_info_ptr) { + fifo.reset(new nirio_fifo<data_t>(_kernel_proxy, OUTPUT_FIFO, fifo_info_ptr->name, fifo_info_ptr->channel)); + } else { + return NiRio_Status_ResourceNotFound; + } + + if (fifo->get_channel() != fifo_info_ptr->channel) return NiRio_Status_InvalidParameter; + if (fifo->get_scalar_type() != fifo_info_ptr->scalar_type) return NiRio_Status_InvalidParameter; + + return NiRio_Status_Success; + } + + template<typename data_t> + nirio_status create_rx_fifo(const char* fifo_name, boost::shared_ptr< nirio_fifo<data_t> >& fifo) + { + nirio_fifo_info_t* fifo_info_ptr = _lookup_fifo_info(fifo_name); + if (fifo_info_ptr) { + fifo.reset(new nirio_fifo<data_t>(_kernel_proxy, INPUT_FIFO, fifo_info_ptr->name, fifo_info_ptr->channel)); + } else { + return NiRio_Status_ResourceNotFound; + } + + if (fifo->get_channel() != fifo_info_ptr->channel) return NiRio_Status_InvalidParameter; + if (fifo->get_scalar_type() != fifo_info_ptr->scalar_type) return NiRio_Status_InvalidParameter; + + return NiRio_Status_Success; + } + +private: + nirio_resource_manager (const nirio_resource_manager&); + nirio_resource_manager& operator = (const nirio_resource_manager&); + + typedef std::map<const std::string, nirio_fifo_info_t> fifo_info_map_t; + typedef std::map<const std::string, nirio_register_info_t> register_info_map_t; + + nirio_status _add_fifo_resource(const nirio_fifo_info_t& fifo_info); + nirio_status _set_driver_config(); + nirio_fifo_info_t* _lookup_fifo_info(const char* fifo_name); + + niriok_proxy& _kernel_proxy; + fifo_info_map_t _fifo_info_map; + register_info_map_t _reg_info_map; +}; + +}} +#endif /* INCLUDED_UHD_TRANSPORT_NIRIO_NIRIO_RESOURCE_MANAGER_H */ diff --git a/host/include/uhd/transport/nirio/niriok_proxy.h b/host/include/uhd/transport/nirio/niriok_proxy.h new file mode 100644 index 000000000..a6b6183d1 --- /dev/null +++ b/host/include/uhd/transport/nirio/niriok_proxy.h @@ -0,0 +1,160 @@ +// +// Copyright 2013-2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// 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_TRANSPORT_NIRIO_NIRIO_INTERFACE_H +#define INCLUDED_UHD_TRANSPORT_NIRIO_NIRIO_INTERFACE_H + +#include <boost/smart_ptr.hpp> +#include <boost/noncopyable.hpp> +#include <uhd/transport/nirio/nirio_driver_iface.h> +#include <uhd/transport/nirio/nirio_quirks.h> + +namespace uhd { namespace niusrprio +{ + enum nirio_version_t { CURRENT, OLDEST_COMPATIBLE }; + + enum nirio_device_attr_32_t { + INTERFACE_NUMBER = 1UL, + PRODUCT_NUMBER = 2UL, + VENDOR_NUMBER = 3UL, + SERIAL_NUMBER = 4UL, + BUS_NUMBER = 10UL, + DEVICE_NUMBER = 11UL, + FUNCTION_NUMBER = 12UL, + CURRENT_VERSION = 14UL, + OLDEST_COMPATIBLE_VERSION = 15UL, + ADDRESS_SPACE = 25UL, + IS_FPGA_PROGRAMMED = 48UL, + DEFAULT_FPGA_SIGNATURE_OFFSET = 53UL, + DEFAULT_FPGA_RESET_OFFSET = 54UL, + DEFAULT_FPGA_RESET_SIZE = 55UL, + DEFAULT_FPGA_CONTROL_OFFSET = 56UL, + DEFAULT_FPGA_INTERRUPT_OFFSET = 57UL, + }; + + enum nirio_device_attr_str_t { + PRODUCT_NAME = 0UL, + FPGA_TARGET_CLASS = 6UL, + SAVED_BITFILE = 7UL, + }; + + enum nirio_addr_space_t { + INVALID = 0, + BUS_INTERFACE = 1, + FPGA = 2, + BAR_WINDOW = 3, + }; + + + class UHD_API niriok_proxy : public boost::noncopyable { + public: + typedef boost::shared_ptr<niriok_proxy> sptr; + + niriok_proxy(); + virtual ~niriok_proxy(); + + //File operations + nirio_status open(const std::string& interface_path); + void close(void); + + nirio_status reset(); + + inline uint32_t get_interface_num() { return _interface_num; } + + nirio_status get_cached_session( + uint32_t& session); + + nirio_status get_version( + nirio_version_t type, + uint32_t& major, + uint32_t& upgrade, + uint32_t& maintenance, + char& phase, + uint32_t& build); + + nirio_status sync_operation( + const void *writeBuffer, + size_t writeBufferLength, + void *readBuffer, + size_t readBufferLength); + + nirio_status get_attribute( + const nirio_device_attr_32_t attribute, + uint32_t& attrValue); + + nirio_status get_attribute( + const nirio_device_attr_str_t attribute, + char* buf, + const uint32_t bufLen, + uint32_t& stringLen); + + nirio_status set_attribute( + const nirio_device_attr_32_t attribute, + const uint32_t value); + + nirio_status set_attribute( + const nirio_device_attr_str_t attribute, + const char* const buffer); + + nirio_status peek(uint32_t offset, uint32_t& value); + + nirio_status peek(uint32_t offset, uint64_t& value); + + nirio_status poke(uint32_t offset, const uint32_t& value); + + nirio_status poke(uint32_t offset, const uint64_t& value); + + nirio_status map_fifo_memory( + uint32_t fifo_instance, + size_t size, + nirio_driver_iface::rio_mmap_t& map); + + nirio_status unmap_fifo_memory( + nirio_driver_iface::rio_mmap_t& map); + + nirio_quirks& get_rio_quirks() { + return _rio_quirks; + } + + private: //Members + nirio_driver_iface::rio_dev_handle_t _device_handle; + uint32_t _interface_num; + nirio_quirks _rio_quirks; + }; + + class niriok_scoped_addr_space : public boost::noncopyable { + public: + explicit niriok_scoped_addr_space(niriok_proxy& proxy, nirio_addr_space_t addr_space, nirio_status& status) : + driver_proxy(proxy) + { + cache_status = driver_proxy.get_attribute(ADDRESS_SPACE, cached_addr_space); + nirio_status_chain(driver_proxy.set_attribute(ADDRESS_SPACE, addr_space), status); + } + + ~niriok_scoped_addr_space() { + if (nirio_status_not_fatal(cache_status)) + driver_proxy.set_attribute(ADDRESS_SPACE, cached_addr_space); + } + + private: + niriok_proxy& driver_proxy; + uint32_t cached_addr_space; + nirio_status cache_status; + }; +}} + +#endif /* INCLUDED_UHD_TRANSPORT_NIRIO_NIRIO_INTERFACE_H */ diff --git a/host/include/uhd/transport/nirio/niusrprio_session.h b/host/include/uhd/transport/nirio/niusrprio_session.h new file mode 100644 index 000000000..c9a61ae76 --- /dev/null +++ b/host/include/uhd/transport/nirio/niusrprio_session.h @@ -0,0 +1,125 @@ +// +// Copyright 2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + + +#ifndef INCLUDED_UHD_TRANSPORT_NIRIO_NIUSRPRIO_SESSION_H +#define INCLUDED_UHD_TRANSPORT_NIRIO_NIUSRPRIO_SESSION_H + +#include <uhd/transport/nirio/rpc/usrprio_rpc_client.hpp> +#include <uhd/transport/nirio/niriok_proxy.h> +#include <uhd/transport/nirio/nirio_resource_manager.h> +#include <uhd/transport/nirio/nifpga_lvbitx.h> +#include <boost/noncopyable.hpp> +#include <boost/smart_ptr.hpp> +#include <boost/thread/recursive_mutex.hpp> +#include <string> + +namespace uhd { namespace niusrprio { + +class UHD_API niusrprio_session : private boost::noncopyable +{ +public: + typedef boost::shared_ptr<niusrprio_session> sptr; + typedef uhd::usrprio_rpc::usrprio_device_info device_info; + typedef uhd::usrprio_rpc::usrprio_device_info_vtr device_info_vtr; + + static nirio_status enumerate( + const std::string& rpc_port_name, + device_info_vtr& device_info_vtr); + + niusrprio_session( + const std::string& resource_name, + const std::string& port_name); + virtual ~niusrprio_session(); + + nirio_status open( + nifpga_lvbitx::sptr lvbitx, + bool force_download = false); + + void close(bool skip_reset = false); + + nirio_status reset(); + + template<typename data_t> + nirio_status create_tx_fifo( + const char* fifo_name, + boost::shared_ptr< nirio_fifo<data_t> >& fifo) + { + if (!_session_open) return NiRio_Status_ResourceNotInitialized; + return _resource_manager.create_tx_fifo(fifo_name, fifo); + } + + template<typename data_t> + nirio_status create_tx_fifo( + uint32_t fifo_instance, + boost::shared_ptr< nirio_fifo<data_t> >& fifo) + { + if ((size_t)fifo_instance >= _lvbitx->get_output_fifo_count()) return NiRio_Status_InvalidParameter; + return create_tx_fifo(_lvbitx->get_output_fifo_names()[fifo_instance], fifo); + } + + template<typename data_t> + nirio_status create_rx_fifo( + const char* fifo_name, + boost::shared_ptr< nirio_fifo<data_t> >& fifo) + { + if (!_session_open) return NiRio_Status_ResourceNotInitialized; + return _resource_manager.create_rx_fifo(fifo_name, fifo); + } + + template<typename data_t> + nirio_status create_rx_fifo( + uint32_t fifo_instance, + boost::shared_ptr< nirio_fifo<data_t> >& fifo) + { + if ((size_t)fifo_instance >= _lvbitx->get_input_fifo_count()) return NiRio_Status_InvalidParameter; + return create_rx_fifo(_lvbitx->get_input_fifo_names()[fifo_instance], fifo); + } + + niriok_proxy& get_kernel_proxy() { + return _riok_proxy; + } + + nirio_status download_bitstream_to_flash(const std::string& bitstream_path); + + //Static + static niriok_proxy::sptr create_kernel_proxy( + const std::string& resource_name, + const std::string& rpc_port_name); + +private: + nirio_status _verify_signature(); + std::string _read_bitstream_checksum(); + nirio_status _write_bitstream_checksum(const std::string& checksum); + nirio_status _wait_for_device_available(); + + std::string _resource_name; + nifpga_lvbitx::sptr _lvbitx; + std::string _interface_path; + bool _session_open; + niriok_proxy _riok_proxy; + nirio_resource_manager _resource_manager; + usrprio_rpc::usrprio_rpc_client _rpc_client; + boost::recursive_mutex _session_mutex; + + static const uint32_t SESSION_LOCK_TIMEOUT_IN_MS = 3000; + static const uint32_t SESSION_LOCK_RETRY_INT_IN_MS = 500; +}; + +}} + +#endif /* INCLUDED_UHD_TRANSPORT_NIRIO_NIUSRPRIO_SESSION_H */ diff --git a/host/include/uhd/transport/nirio/rpc/rpc_client.hpp b/host/include/uhd/transport/nirio/rpc/rpc_client.hpp new file mode 100644 index 000000000..f90034e37 --- /dev/null +++ b/host/include/uhd/transport/nirio/rpc/rpc_client.hpp @@ -0,0 +1,94 @@ +// +// Copyright 2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_RPC_CLIENT_HPP +#define INCLUDED_RPC_CLIENT_HPP + +#include <boost/asio.hpp> +#include <boost/smart_ptr.hpp> +#include <boost/thread/thread.hpp> +#include <boost/thread/condition_variable.hpp> +#include "rpc_common.hpp" +#include <uhd/utils/log.hpp> + +namespace uhd { namespace usrprio_rpc { + +class rpc_client : private boost::noncopyable +{ +public: + static const boost::uint32_t CURRENT_VERSION = 1; + static const boost::uint32_t OLDEST_COMPATIBLE_VERSION = 1; + + rpc_client( + const std::string& server, + const std::string& port, + boost::uint32_t process_id, + boost::uint32_t host_id); + ~rpc_client(); + + const boost::system::error_code& call( + func_id_t func_id, + const func_args_writer_t& in_args, + func_args_reader_t &out_args, + boost::posix_time::milliseconds timeout); + + inline const boost::system::error_code& status() const { + return _exec_err; + } + + /* [Possible boost::system::error_code values] + * boost::asio::error::connection_aborted: Network connection failure + * boost::asio::error::eof: Network connection closed cleanly + * boost::asio::error::connection_refused: Software handshake failure (version mismatch, etc) + * boost::asio::error::timed_out: RPC call timed out + * boost::asio::error::operation_aborted: RPC call aborted but connection alive + */ + +private: + void _handle_response_hdr(const boost::system::error_code& err, size_t transferred, size_t expected); + void _handle_response_data(const boost::system::error_code& err, size_t transferred, size_t expected); + void _wait_for_next_response_header(); + + inline void _stop_io_service() { + if (_io_service_thread.get()) { + UHD_LOG << "rpc_client stopping..." << std::endl; + _io_service.stop(); + _io_service_thread->join(); + _io_service_thread.reset(); + UHD_LOG << "rpc_client stopped." << std::endl; + } + } + + //Services + boost::asio::io_service _io_service; + boost::scoped_ptr<boost::thread> _io_service_thread; + boost::asio::ip::tcp::socket _socket; + //Handshake info + hshake_args_t _hshake_args_client; + hshake_args_t _hshake_args_server; + //In-out function args + func_xport_buf_t _request; + func_xport_buf_t _response; + //Synchronization + boost::mutex _mutex; + boost::condition_variable _exec_gate; + boost::system::error_code _exec_err; +}; + +}} + +#endif /* INCLUDED_RPC_CLIENT_HPP */ diff --git a/host/include/uhd/transport/nirio/rpc/rpc_common.hpp b/host/include/uhd/transport/nirio/rpc/rpc_common.hpp new file mode 100644 index 000000000..ea5b3337d --- /dev/null +++ b/host/include/uhd/transport/nirio/rpc/rpc_common.hpp @@ -0,0 +1,158 @@ +// +// Copyright 2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_RPC_COMMON_HPP +#define INCLUDED_RPC_COMMON_HPP + +#define USE_BINARY_ARCHIVE 0 + +#include <string> +#include <iostream> +#include <sstream> +#include <vector> +#if (USE_BINARY_ARCHIVE) + #include <boost/archive/binary_oarchive.hpp> + #include <boost/archive/binary_iarchive.hpp> +#else + #include <boost/archive/text_oarchive.hpp> + #include <boost/archive/text_iarchive.hpp> +#endif + +namespace uhd { namespace usrprio_rpc { + +//[Over-the-wire] IDs +typedef boost::int32_t func_id_t; +typedef boost::uint64_t client_id_t; + +#define build_client_id(host_id, process_id) \ + ((static_cast<boost::uint64_t>(host_id) << 32) | static_cast<boost::uint64_t>(process_id)) +#define get_process_id_from_client_id(client_id) \ + (static_cast<boost::int32_t>(client_id)) +#define get_host_id_from_client_id(client_id) \ + (static_cast<boost::uint32_t>(client_id >> 32)) + +//[Over-the-wire] Handshake format +struct hshake_args_t { + boost::uint32_t version; + boost::uint32_t oldest_comp_version; + boost::int32_t boost_archive_version; + client_id_t client_id; +}; + +//[Over-the-wire] Header for RPC request and response +class func_args_header_t { +public: + func_id_t func_id; + client_id_t client_id; + boost::uint32_t func_args_size; + + static bool match_function(const func_args_header_t& a, const func_args_header_t& b) { + return ((a.func_id == b.func_id) && (a.client_id == b.client_id)); + } +}; + +//[Internal] Storage for RPC header and arguments +class func_xport_buf_t { +public: + func_args_header_t header; + std::vector<char> data; +}; + +//[Internal] Serializer for RPC input parameters +class func_args_writer_t { +public: + func_args_writer_t() : _stream(), _archive(_stream, boost::archive::no_header) {} + + template<typename data_t> + void push(const data_t& d) { + _archive << d; + } + + template<typename data_t> + func_args_writer_t& operator<< (const data_t& data) { + push(data); + return *this; + } + + void store(std::vector<char>& data) const { + const std::string& str = _stream.str(); + data.resize(str.length()); + data.assign((char*)str.c_str(), ((char*)str.c_str()) + str.length()); + } + +private: + std::ostringstream _stream; +#if (USE_BINARY_ARCHIVE) + boost::archive::binary_oarchive _archive; +#else + boost::archive::text_oarchive _archive; +#endif +}; + +//[Internal] Deserializer for RPC output parameters +class func_args_reader_t { +public: + func_args_reader_t() : _stream(), _archive() {} + + template<typename data_t> + void pull(data_t& d) const { + if (_archive) (*_archive) >> d; + } + + template<typename data_t> + const func_args_reader_t& operator>> (data_t& data) const { + pull(data); + return *this; + } + + void load(const std::vector<char>& data) { + _stream.str(std::string(data.begin(), data.end())); +#if (USE_BINARY_ARCHIVE) + _archive.reset(new boost::archive::binary_iarchive(_stream, boost::archive::no_header)); +#else + _archive.reset(new boost::archive::text_iarchive(_stream, boost::archive::no_header)); +#endif + } + +private: + std::istringstream _stream; +#if (USE_BINARY_ARCHIVE) + boost::scoped_ptr<boost::archive::binary_iarchive> _archive; +#else + boost::scoped_ptr<boost::archive::text_iarchive> _archive; +#endif +}; + +class boost_serialization_archive_utils { +public: + static boost::int32_t get_version() { + #if (USE_BINARY_ARCHIVE) + typedef boost::archive::binary_oarchive archive_t; + #else + typedef boost::archive::text_oarchive archive_t; + #endif + std::ostringstream stream; + archive_t dummy_archive(stream, boost::archive::no_header); + return static_cast<boost::int32_t>(dummy_archive.get_library_version()); + } +}; + +}} + +#undef USE_BINARY_ARCHIVE + +#endif /* INCLUDED_RPC_COMMON_HPP */ diff --git a/host/include/uhd/transport/nirio/rpc/usrprio_rpc_client.hpp b/host/include/uhd/transport/nirio/rpc/usrprio_rpc_client.hpp new file mode 100644 index 000000000..4b205cceb --- /dev/null +++ b/host/include/uhd/transport/nirio/rpc/usrprio_rpc_client.hpp @@ -0,0 +1,70 @@ +// +// Copyright 2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_USRPRIO_RPC_CLIENT_HPP +#define INCLUDED_USRPRIO_RPC_CLIENT_HPP + +#include <uhd/transport/nirio/rpc/rpc_common.hpp> +#include <uhd/transport/nirio/rpc/rpc_client.hpp> +#include <uhd/transport/nirio/rpc/usrprio_rpc_common.hpp> +#include <uhd/transport/nirio/status.h> + +namespace uhd { namespace usrprio_rpc { + +class UHD_API usrprio_rpc_client { +public: + usrprio_rpc_client( + std::string server, + std::string port); + ~usrprio_rpc_client(); + + inline void set_rpc_timeout(boost::posix_time::milliseconds timeout_in_ms) { + _timeout = timeout_in_ms; + } + + inline nirio_status get_ctor_status() { + return _ctor_status; + } + + nirio_status niusrprio_enumerate( + NIUSRPRIO_ENUMERATE_ARGS); + nirio_status niusrprio_open_session( + NIUSRPRIO_OPEN_SESSION_ARGS); + nirio_status niusrprio_close_session( + NIUSRPRIO_CLOSE_SESSION_ARGS); + nirio_status niusrprio_reset_device( + NIUSRPRIO_RESET_SESSION_ARGS); + nirio_status niusrprio_download_bitstream_to_fpga( + NIUSRPRIO_DOWNLOAD_BITSTREAM_TO_FPGA_ARGS); + nirio_status niusrprio_get_interface_path( + NIUSRPRIO_GET_INTERFACE_PATH_ARGS); + nirio_status niusrprio_download_fpga_to_flash( + NIUSRPRIO_DOWNLOAD_FPGA_TO_FLASH_ARGS); + + static const boost::int64_t DEFAULT_TIMEOUT_IN_MS = 5000; + +private: + static nirio_status _boost_error_to_nirio_status(const boost::system::error_code& err); + + rpc_client _rpc_client; + boost::posix_time::milliseconds _timeout; + nirio_status _ctor_status; +}; + +}} + +#endif /* INCLUDED_USRPRIO_RPC_CLIENT_HPP */ diff --git a/host/include/uhd/transport/nirio/rpc/usrprio_rpc_common.hpp b/host/include/uhd/transport/nirio/rpc/usrprio_rpc_common.hpp new file mode 100644 index 000000000..362d9a174 --- /dev/null +++ b/host/include/uhd/transport/nirio/rpc/usrprio_rpc_common.hpp @@ -0,0 +1,85 @@ +// +// Copyright 2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_USRPRIO_RPC_COMMON_HPP +#define INCLUDED_USRPRIO_RPC_COMMON_HPP + +#include <uhd/transport/nirio/rpc/rpc_common.hpp> + +namespace uhd { namespace usrprio_rpc { + +//Function IDs + +static const func_id_t NIUSRPRIO_FUNC_BASE = 0x100; + +static const func_id_t NIUSRPRIO_ENUMERATE = NIUSRPRIO_FUNC_BASE + 0; +static const func_id_t NIUSRPRIO_OPEN_SESSION = NIUSRPRIO_FUNC_BASE + 1; +static const func_id_t NIUSRPRIO_CLOSE_SESSION = NIUSRPRIO_FUNC_BASE + 2; +static const func_id_t NIUSRPRIO_RESET_SESSION = NIUSRPRIO_FUNC_BASE + 3; +static const func_id_t NIUSRPRIO_DOWNLOAD_BITSTREAM_TO_FPGA = NIUSRPRIO_FUNC_BASE + 4; +static const func_id_t NIUSRPRIO_GET_INTERFACE_PATH = NIUSRPRIO_FUNC_BASE + 5; +static const func_id_t NIUSRPRIO_DOWNLOAD_FPGA_TO_FLASH = NIUSRPRIO_FUNC_BASE + 6; + +//Function Args + +struct usrprio_device_info { + boost::uint32_t interface_num; + std::string resource_name; + std::string pcie_serial_num; + std::string interface_path; + + template <typename Archive> + void serialize(Archive& ar, const unsigned int version) + { + if (version || !version) { //Suppress unused warning + ar & interface_num; + ar & resource_name; + ar & pcie_serial_num; + ar & interface_path; + } + } +}; +typedef std::vector<usrprio_device_info> usrprio_device_info_vtr; + +#define NIUSRPRIO_ENUMERATE_ARGS \ + usrprio_device_info_vtr& device_info_vtr + +#define NIUSRPRIO_OPEN_SESSION_ARGS \ + const std::string& resource, \ + const std::string& path, \ + const std::string& signature, \ + const boost::uint16_t& download_fpga + +#define NIUSRPRIO_CLOSE_SESSION_ARGS \ + const std::string& resource + +#define NIUSRPRIO_RESET_SESSION_ARGS \ + const std::string& resource + +#define NIUSRPRIO_DOWNLOAD_BITSTREAM_TO_FPGA_ARGS \ + const std::string& resource + +#define NIUSRPRIO_GET_INTERFACE_PATH_ARGS \ + const std::string& resource, \ + std::string& interface_path + +#define NIUSRPRIO_DOWNLOAD_FPGA_TO_FLASH_ARGS \ + const std::string& resource, \ + const std::string& bitstream_path +}} + +#endif /* INCLUDED_USRPRIO_RPC_COMMON_HPP */ diff --git a/host/include/uhd/transport/nirio/status.h b/host/include/uhd/transport/nirio/status.h new file mode 100644 index 000000000..e4117ac73 --- /dev/null +++ b/host/include/uhd/transport/nirio/status.h @@ -0,0 +1,65 @@ +// +// Copyright 2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + + +#ifndef INCLUDED_UHD_TRANSPORT_NIRIO_STATUS_H +#define INCLUDED_UHD_TRANSPORT_NIRIO_STATUS_H + +#include <stdio.h> +#include <stddef.h> +#include <stdint.h> +#include <string> +#include <uhd/exception.hpp> + +#define ENABLE_EXTENDED_ERROR_INFO false + +typedef int32_t nirio_status; + +namespace uhd { namespace niusrprio { +struct nirio_err_info { + nirio_err_info(nirio_status arg_code, const char* arg_msg): code(arg_code), msg(arg_msg) {} + + nirio_status code; + const char* msg; + + static const nirio_err_info NIRIO_ERROR_TABLE[]; + static const size_t NIRIO_ERROR_TABLE_SIZE; +}; + +const std::string lookup_err_msg(nirio_status code); + +void nirio_status_to_exception(const nirio_status& status, const std::string& message); +}} + +#define nirio_status_fatal(status) ((status) < 0) +#define nirio_status_not_fatal(status) ((status) >= 0) + +#define nirio_status_chain(func, status) \ + if (nirio_status_not_fatal(status)) { \ + status = (func); \ + if (ENABLE_EXTENDED_ERROR_INFO && nirio_status_fatal(status)) { \ + fprintf(stderr,"ERROR: The following function call returned status code %d\n%s\n%s:%d\n",status,#func,__FILE__,__LINE__); \ + } \ + } \ + + +#define NIRIO_ERR_INFO(CONST_NAME, ERR_CODE, ERR_MSG) \ + static const nirio_status CONST_NAME = ERR_CODE; +#include "nirio_err_template.h" +#undef NIRIO_ERR_INFO + +#endif /* INCLUDED_UHD_TRANSPORT_NIRIO_STATUS_H */ diff --git a/host/include/uhd/transport/nirio_zero_copy.hpp b/host/include/uhd/transport/nirio_zero_copy.hpp new file mode 100644 index 000000000..5b16b1975 --- /dev/null +++ b/host/include/uhd/transport/nirio_zero_copy.hpp @@ -0,0 +1,43 @@ +// +// Copyright 2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_UHD_TRANSPORT_NIRIO_ZERO_COPY_HPP +#define INCLUDED_UHD_TRANSPORT_NIRIO_ZERO_COPY_HPP + +#include <uhd/transport/nirio/niusrprio_session.h> +#include <uhd/config.hpp> +#include <uhd/transport/zero_copy.hpp> +#include <uhd/types/device_addr.hpp> +#include <boost/shared_ptr.hpp> + +namespace uhd{ namespace transport{ + +class UHD_API nirio_zero_copy : public virtual zero_copy_if{ +public: + typedef boost::shared_ptr<nirio_zero_copy> sptr; + + static sptr make( + uhd::niusrprio::niusrprio_session::sptr fpga_session, + const uint32_t instance, + const zero_copy_xport_params &default_buff_args, + const device_addr_t &hints = device_addr_t() + ); +}; + +}} //namespace + +#endif /* INCLUDED_UHD_TRANSPORT_NIRIO_ZERO_COPY_HPP */ diff --git a/host/include/uhd/transport/tcp_zero_copy.hpp b/host/include/uhd/transport/tcp_zero_copy.hpp new file mode 100644 index 000000000..a9878a396 --- /dev/null +++ b/host/include/uhd/transport/tcp_zero_copy.hpp @@ -0,0 +1,57 @@ +// +// Copyright 2010-2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// 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_TRANSPORT_TCP_ZERO_COPY_HPP +#define INCLUDED_UHD_TRANSPORT_TCP_ZERO_COPY_HPP + +#include <uhd/config.hpp> +#include <uhd/transport/zero_copy.hpp> +#include <uhd/types/device_addr.hpp> +#include <boost/shared_ptr.hpp> + +namespace uhd{ namespace transport{ + +/*! + * The zero copy TCP transport. + * This transport provides the uhd zero copy interface + * on top of a standard tcp socket from boost asio. + */ +struct UHD_API tcp_zero_copy : public virtual zero_copy_if +{ + /*! + * Make a new zero copy TCP transport: + * This transport is for sending and receiving + * between this host and a single endpoint. + * The primary usage for this transport will be data transactions. + * + * The address will be resolved, it can be a host name or ipv4. + * The port will be resolved, it can be a port type or number. + * + * \param addr a string representing the destination address + * \param port a string representing the destination port + * \param hints optional parameters to pass to the underlying transport + */ + static zero_copy_if::sptr make( + const std::string &addr, + const std::string &port, + const device_addr_t &hints = device_addr_t() + ); +}; + +}} //namespace + +#endif /* INCLUDED_UHD_TRANSPORT_TCP_ZERO_COPY_HPP */ diff --git a/host/include/uhd/transport/udp_zero_copy.hpp b/host/include/uhd/transport/udp_zero_copy.hpp index bbba97b21..8fb5115c2 100644 --- a/host/include/uhd/transport/udp_zero_copy.hpp +++ b/host/include/uhd/transport/udp_zero_copy.hpp @@ -37,6 +37,11 @@ namespace uhd{ namespace transport{ */ class UHD_API udp_zero_copy : public virtual zero_copy_if{ public: + struct buff_params { + size_t recv_buff_size; + size_t send_buff_size; + }; + typedef boost::shared_ptr<udp_zero_copy> sptr; /*! @@ -56,6 +61,8 @@ public: static sptr make( const std::string &addr, const std::string &port, + const zero_copy_xport_params &default_buff_args, + udp_zero_copy::buff_params& buff_params_out, const device_addr_t &hints = device_addr_t() ); }; diff --git a/host/include/uhd/transport/zero_copy.hpp b/host/include/uhd/transport/zero_copy.hpp index 40d7b8c59..9ff3042aa 100644 --- a/host/include/uhd/transport/zero_copy.hpp +++ b/host/include/uhd/transport/zero_copy.hpp @@ -29,7 +29,14 @@ namespace uhd{ namespace transport{ //! Simple managed buffer with release interface class UHD_API managed_buffer{ public: - managed_buffer(void):_ref_count(0),_buffer(NULL),_length(0){} + managed_buffer(void):_ref_count(0),_buffer(NULL),_length(0) + { +#ifdef UHD_TXRX_DEBUG_PRINTS + _mb_num = s_buffer_count; + // From Boost website: atomic_count seems only to have precrement operator. + ++s_buffer_count; +#endif + } virtual ~managed_buffer(void) {} @@ -76,9 +83,27 @@ namespace uhd{ namespace transport{ boost::detail::atomic_count _ref_count; typedef boost::intrusive_ptr<managed_buffer> sptr; + int ref_count(){ + return (int) _ref_count; + } + +#ifdef UHD_TXRX_DEBUG_PRINTS + int num() const{ + return _mb_num; + } +#endif + protected: void *_buffer; size_t _length; +#ifdef UHD_TXRX_DEBUG_PRINTS + int _mb_num; +#endif + + private: +#ifdef UHD_TXRX_DEBUG_PRINTS + static boost::detail::atomic_count s_buffer_count; +#endif }; UHD_INLINE void intrusive_ptr_add_ref(managed_buffer *p){ @@ -110,6 +135,16 @@ namespace uhd{ namespace transport{ }; /*! + * Transport parameters + */ + struct zero_copy_xport_params { + size_t recv_frame_size; + size_t send_frame_size; + size_t num_recv_frames; + size_t num_send_frames; + }; + + /*! * A zero-copy interface for transport objects. * Provides a way to get send and receive buffers * with memory managed by the transport object. @@ -119,6 +154,11 @@ namespace uhd{ namespace transport{ typedef boost::shared_ptr<zero_copy_if> sptr; /*! + * Clean up tasks before releasing the transport object. + */ + virtual ~zero_copy_if() {}; + + /*! * Get a new receive buffer from this transport object. * \param timeout the timeout to get the buffer in seconds * \return a managed buffer, or null sptr on timeout/error diff --git a/host/include/uhd/types/metadata.hpp b/host/include/uhd/types/metadata.hpp index 788999900..6a79720d0 100644 --- a/host/include/uhd/types/metadata.hpp +++ b/host/include/uhd/types/metadata.hpp @@ -30,6 +30,26 @@ namespace uhd{ * The receive routines will convert IF data headers into metadata. */ struct UHD_API rx_metadata_t{ + + //! Default constructor. + rx_metadata_t() + { + reset(); + } + + //! Reset values. + void reset() + { + has_time_spec = false; + time_spec = time_spec_t(0.0); + more_fragments = false; + fragment_offset = 0; + start_of_burst = false; + end_of_burst = false; + error_code = ERROR_CODE_NONE; + out_of_sequence = false; + } + //! Has time specification? bool has_time_spec; @@ -80,13 +100,23 @@ namespace uhd{ ERROR_CODE_LATE_COMMAND = 0x2, //! Expected another stream command. ERROR_CODE_BROKEN_CHAIN = 0x4, - //! An internal receive buffer has filled. + /*! + * An internal receive buffer has filled or a sequence error has been detected. + * So, why is this overloaded? Simple: legacy support. It would have been much cleaner + * to create a separate error code for a sequence error, but that would have broken + * legacy applications. So, the out_of_sequence flag was added to differentiate between + * the two error cases. In either case, data is missing between this time_spec and the + * and the time_spec of the next successful receive. + */ ERROR_CODE_OVERFLOW = 0x8, //! Multi-channel alignment failed. ERROR_CODE_ALIGNMENT = 0xc, //! The packet could not be parsed. ERROR_CODE_BAD_PACKET = 0xf } error_code; + + //! Out of sequence. The transport has either dropped a packet or received data out of order. + bool out_of_sequence; }; /*! diff --git a/host/include/uhd/types/tune_request.hpp b/host/include/uhd/types/tune_request.hpp index dd6123a9f..d1f3b4463 100644 --- a/host/include/uhd/types/tune_request.hpp +++ b/host/include/uhd/types/tune_request.hpp @@ -96,7 +96,7 @@ namespace uhd{ * - mode_n: Allows the user to tell the daughterboard tune code * to choose between an integer N diviver or fractional N divider. * Default is fractional N on boards that support fractional N tuning. - * Fractional N provides greater tuning acuracy at the expense of spurs. + * Fractional N provides greater tuning accuracy at the expense of spurs. * Possible options for this key: "integer" or "fractional". */ device_addr_t args; diff --git a/host/include/uhd/usrp/gps_ctrl.hpp b/host/include/uhd/usrp/gps_ctrl.hpp index abe5c37ba..bbccac8bb 100644 --- a/host/include/uhd/usrp/gps_ctrl.hpp +++ b/host/include/uhd/usrp/gps_ctrl.hpp @@ -32,7 +32,7 @@ public: typedef boost::shared_ptr<gps_ctrl> sptr; /*! - * Make a GPS config for NMEA GPS devices + * Make a GPS config for internal GPSDOs or generic NMEA GPS devices */ static sptr make(uart_iface::sptr uart); diff --git a/host/include/uhd/usrp/multi_usrp.hpp b/host/include/uhd/usrp/multi_usrp.hpp index d60f0e60c..5b4991202 100644 --- a/host/include/uhd/usrp/multi_usrp.hpp +++ b/host/include/uhd/usrp/multi_usrp.hpp @@ -339,6 +339,26 @@ public: virtual std::vector<std::string> get_clock_sources(const size_t mboard) = 0; /*! + * Send the clock source to an output connector. + * This call is only applicable on devices with reference outputs. + * By default, the reference output will be enabled for ease of use. + * This call may be used to enable or disable the output. + * \param enb true to output the clock source. + * \param mboard which motherboard to set + */ + virtual void set_clock_source_out(const bool enb, const size_t mboard = ALL_MBOARDS) = 0; + + /*! + * Send the time source to an output connector. + * This call is only applicable on devices with PPS outputs. + * By default, the PPS output will be enabled for ease of use. + * This call may be used to enable or disable the output. + * \param enb true to output the time source. + * \param mboard which motherboard to set + */ + virtual void set_time_source_out(const bool enb, const size_t mboard = ALL_MBOARDS) = 0; + + /*! * Get the number of USRP motherboards in this configuration. */ virtual size_t get_num_mboards(void) = 0; @@ -856,6 +876,53 @@ public: */ virtual void set_tx_iq_balance(const std::complex<double> &correction, size_t chan = ALL_CHANS) = 0; + /******************************************************************* + * GPIO methods + ******************************************************************/ + + /*! + * Enumerate gpio banks on the specified device. + * \param mboard the motherboard index 0 to M-1 + * \return a list of string for each bank name + */ + virtual std::vector<std::string> get_gpio_banks(const size_t mboard) = 0; + + /*! + * Set a GPIO attribute on a particular GPIO bank. + * Possible attribute names: + * - CTRL - 1 for ATR mode 0 for GPIO mode + * - DDR - 1 for output 0 for input + * - OUT - GPIO output level (not ATR mode) + * - ATR_0X - ATR idle state + * - ATR_RX - ATR receive only state + * - ATR_TX - ATR transmit only state + * - ATR_XX - ATR full duplex state + * \param bank the name of a GPIO bank + * \param attr the name of a GPIO attribute + * \param value the new value for this GPIO bank + * \param mask the bit mask to effect which pins are changed + * \param mboard the motherboard index 0 to M-1 + */ + virtual void set_gpio_attr(const std::string &bank, const std::string &attr, const boost::uint32_t value, const boost::uint32_t mask = 0xffffffff, const size_t mboard = 0) = 0; + + /*! + * Get a GPIO attribute on a particular GPIO bank. + * Possible attribute names: + * - CTRL - 1 for ATR mode 0 for GPIO mode + * - DDR - 1 for output 0 for input + * - OUT - GPIO output level (not ATR mode) + * - ATR_0X - ATR idle state + * - ATR_RX - ATR receive only state + * - ATR_TX - ATR transmit only state + * - ATR_XX - ATR full duplex state + * - READBACK - readback input GPIOs + * \param bank the name of a GPIO bank + * \param attr the name of a GPIO attribute + * \param mboard the motherboard index 0 to M-1 + * \return the value set for this attribute + */ + virtual boost::uint32_t get_gpio_attr(const std::string &bank, const std::string &attr, const size_t mboard = 0) = 0; + }; }} diff --git a/host/include/uhd/utils/CMakeLists.txt b/host/include/uhd/utils/CMakeLists.txt index e86826435..c0991b3ce 100644 --- a/host/include/uhd/utils/CMakeLists.txt +++ b/host/include/uhd/utils/CMakeLists.txt @@ -30,6 +30,7 @@ UHD_INSTALL(FILES msg_task.hpp paths.hpp pimpl.hpp + platform.hpp safe_call.hpp safe_main.hpp static.hpp diff --git a/host/include/uhd/utils/platform.hpp b/host/include/uhd/utils/platform.hpp new file mode 100644 index 000000000..de778f61c --- /dev/null +++ b/host/include/uhd/utils/platform.hpp @@ -0,0 +1,36 @@ +// +// 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/>. +// + +#ifndef INCLUDED_UHD_UTILS_PLATFORM_HPP +#define INCLUDED_UHD_UTILS_PLATFORM_HPP + +#include <boost/cstdint.hpp> + +namespace uhd { + + /* Returns the process ID of the current process */ + boost::int32_t get_process_id(); + + /* Returns a unique identifier for the current machine */ + boost::uint32_t get_host_id(); + + /* Get a unique identifier for the current machine and process */ + boost::uint32_t get_process_hash(); + +} //namespace uhd + +#endif /* INCLUDED_UHD_UTILS_PLATFORM_HPP */ diff --git a/host/include/uhd/version.hpp b/host/include/uhd/version.hpp index a62ee8285..1bb89dd84 100644 --- a/host/include/uhd/version.hpp +++ b/host/include/uhd/version.hpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2013 Ettus Research LLC +// Copyright 2010-2014 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -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.6.0-1" +#define UHD_VERSION_ABI_STRING "3.7.0-0" namespace uhd{ diff --git a/host/lib/convert/convert_pack_sc12.cpp b/host/lib/convert/convert_pack_sc12.cpp index 92cd5d152..081f60a89 100644 --- a/host/lib/convert/convert_pack_sc12.cpp +++ b/host/lib/convert/convert_pack_sc12.cpp @@ -80,13 +80,26 @@ struct convert_star_1_to_sc12_item32_1 : public converter void operator()(const input_type &inputs, const output_type &outputs, const size_t nsamps) { const std::complex<type> *input = reinterpret_cast<const std::complex<type> *>(inputs[0]); - item32_sc12_3x *output = reinterpret_cast<item32_sc12_3x *>(size_t(outputs[0]) & ~0x3); + + /* + * Effectively outputs will point to a managed_buffer instance. These buffers are 32 bit aligned. + * For a detailed description see comments in 'convert_unpack_sc12.cpp'. + */ + const size_t head_samps = size_t(inputs[0]) & 0x3; + size_t rewind = 0; + switch(head_samps) + { + case 0: break; + case 1: rewind = 9; break; + case 2: rewind = 6; break; + case 3: rewind = 3; break; + } + item32_sc12_3x *output = reinterpret_cast<item32_sc12_3x *>(size_t(outputs[0]) - rewind); //helper variables size_t i = 0, o = 0; //handle the head case - const size_t head_samps = size_t(outputs[0]) & 0x3; switch (head_samps) { case 0: break; //no head diff --git a/host/lib/device.cpp b/host/lib/device.cpp index c5bc047da..ff5163f2d 100644 --- a/host/lib/device.cpp +++ b/host/lib/device.cpp @@ -153,3 +153,9 @@ device::sptr device::make(const device_addr_t &hint, size_t which){ return dev; } } + +uhd::property_tree::sptr +device::get_tree(void) const +{ + return _tree; +} diff --git a/host/lib/ic_reg_maps/CMakeLists.txt b/host/lib/ic_reg_maps/CMakeLists.txt index b6e121db3..889c725db 100644 --- a/host/lib/ic_reg_maps/CMakeLists.txt +++ b/host/lib/ic_reg_maps/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2010-2011 Ettus Research LLC +# Copyright 2010-2013 Ettus Research LLC # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -98,6 +98,11 @@ LIBUHD_PYTHON_GEN_SOURCE( ) LIBUHD_PYTHON_GEN_SOURCE( + ${CMAKE_CURRENT_SOURCE_DIR}/gen_ads62p48_regs.py + ${CMAKE_CURRENT_BINARY_DIR}/ads62p48_regs.hpp +) + +LIBUHD_PYTHON_GEN_SOURCE( ${CMAKE_CURRENT_SOURCE_DIR}/gen_tuner_4937di5_regs.py ${CMAKE_CURRENT_BINARY_DIR}/tuner_4937di5_regs.hpp ) @@ -107,4 +112,9 @@ LIBUHD_PYTHON_GEN_SOURCE( ${CMAKE_CURRENT_BINARY_DIR}/tda18272hnm_regs.hpp ) +LIBUHD_PYTHON_GEN_SOURCE( + ${CMAKE_CURRENT_SOURCE_DIR}/gen_lmk04816_regs.py + ${CMAKE_CURRENT_BINARY_DIR}/lmk04816_regs.hpp +) + UNSET(LIBUHD_PYTHON_GEN_SOURCE_DEPS) diff --git a/host/lib/ic_reg_maps/gen_ads62p48_regs.py b/host/lib/ic_reg_maps/gen_ads62p48_regs.py new file mode 100644 index 000000000..c38ce8ff1 --- /dev/null +++ b/host/lib/ic_reg_maps/gen_ads62p48_regs.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python +# +# Copyright 2013 Ettus Research LLC +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +######################################################################## +# Template for raw text data describing registers +# name addr[bit range inclusive] default optional enums +######################################################################## +REGS_TMPL="""\ +reset 0[7] 0 +serial_readout 0[0] 0 +enable_low_speed_mode 0x20[2] 0 +ref 0x3f[6:5] 0 internal=0, external=3 +standby 0x3f[1] 0 normal, standby +power_down 0x40[3:0] 0 pins=0, normal=8, chb=9, cha=10, chab=11, global=12, chb_standby=13, cha_standby=14, mux=15 +lvds_cmos 0x41[7] 0 parallel_cmos, ddr_lvds +clk_out_pos_edge 0x44[7:5] 0 normal=0, plus4_26=5, minus4_26=7, minus7_26=6 +clk_out_neg_edge 0x44[4:2] 0 normal=0, plus4_26=5, minus4_26=7, minus7_26=6 +channel_control 0x50[6] 0 common, independent +data_format 0x50[2:1] 2 2s_compliment=2, offset_binary=3 +custom_pattern_low 0x51[7:0] 0 +custom_pattern_high 0x52[5:0] 0 +enable_offset_corr_chA 0x53[6] 0 +gain_chA 0x55[7:4] 0 +offset_corr_time_const_chA 0x55[3:0] 0 +fine_gain_adjust_chA 0x57[6:0] 0 +test_patterns_chA 0x62[2:0] 0 normal, zeros, ones, toggle, ramp, custom +offset_pedestal_chA 0x63[5:0] 0 +enable_offset_corr_chB 0x66[6] 0 +gain_chB 0x68[7:4] 0 +offset_corr_time_const_chB 0x68[3:0] 0 +fine_gain_adjust_chB 0x6A[6:0] 0 +test_patterns_chB 0x75[2:0] 0 normal, zeros, ones, toggle, ramp, custom +offset_pedestal_chB 0x76[5:0] 0 +""" + +######################################################################## +# Template for methods in the body of the struct +######################################################################## +BODY_TMPL="""\ +boost::uint8_t get_reg(boost::uint8_t addr){ + boost::uint8_t reg = 0; + switch(addr){ + #for $addr in sorted(set(map(lambda r: r.get_addr(), $regs))) + case $addr: + #for $reg in filter(lambda r: r.get_addr() == addr, $regs) + reg |= (boost::uint8_t($reg.get_name()) & $reg.get_mask()) << $reg.get_shift(); + #end for + break; + #end for + } + return reg; +} + +boost::uint16_t get_write_reg(boost::uint8_t addr){ + return (boost::uint16_t(addr) << 8) | get_reg(addr); +} + +boost::uint16_t get_read_reg(boost::uint8_t addr){ + return (boost::uint16_t(addr) << 8) | (1 << 7); +} +""" + +if __name__ == '__main__': + import common; common.generate( + name='ads62p48_regs', + regs_tmpl=REGS_TMPL, + body_tmpl=BODY_TMPL, + file=__file__, + ) diff --git a/host/lib/ic_reg_maps/gen_lmk04816_regs.py b/host/lib/ic_reg_maps/gen_lmk04816_regs.py new file mode 100644 index 000000000..e89a82671 --- /dev/null +++ b/host/lib/ic_reg_maps/gen_lmk04816_regs.py @@ -0,0 +1,407 @@ +#Copyright 2010 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/>. +# + +######################################################################## +# Template for raw text data describing registers +# name addr[bit range inclusive] default optional enums +######################################################################## +REGS_TMPL="""\ +######################################################################## +## address 0 +######################################################################## +address0 0[0:4] 0 +CLKout0_1_DIV 0[5:15] 25 +CLKout0_1_HS 0[16] 0 +RESET 0[17] 0 no_reset, reset +CLKout0_1_DDLY 0[18:27] 0 five +CLKout0_ADLY_SEL 0[28] 0 d_pd, d_ev_x, d_odd_y, d_both +CLKout1_ADLY_SEL 0[29] 0 d_pd, d_ev_x, d_odd_y, d_both +Required_0 0[30] 0 +CLKout0_1_PD 0[31] 1 power_up, power_down +######################################################################## +## address 1 +######################################################################## +address1 1[0:4] 1 +CLKout2_3_DIV 1[5:15] 25 +CLKout2_3_HS 1[16] 0 +Powerdown 1[17] 0 normal, disabled +CLKout2_3_DDLY 1[18:27] 0 +CLKout2_ADLY_SEL 1[28] 0 d_pd, d_ev_x, d_odd_y, d_both +CLKout3_ADLY_SEL 1[29] 0 d_pd, d_ev_x, d_odd_y, d_both +Required_1 1[30] 0 +CLKout2_3_PD 1[31] 1 power_up, power_down +######################################################################## +## address 2 +######################################################################## +address2 2[0:4] 2 +CLKout4_5_DIV 2[5:15] 25 +CLKout4_5_HS 2[16] 0 +Required_2_17 2[17] 0 +CLKout4_5_DDLY 2[18:27] 0 +CLKout4_ADLY_SEL 2[28] 0 d_pd, d_ev_x, d_odd_y, d_both +CLKout5_ADLY_SEL 2[29] 0 d_pd, d_ev_x, d_odd_y, d_both +Required_2_30 2[30] 0 +CLKout4_5_PD 2[31] 1 power_up, power_down +######################################################################## +## address 3 +######################################################################## +address3 3[0:4] 3 +CLKout6_7_DIV 3[5:15] 1 +CLKout6_7_HS 3[16] 0 +Required_3_17 3[17] 0 +CLKout6_7_DDLY 3[18:27] 0 +CLKout6_ADLY_SEL 3[28] 0 d_pd, d_ev_x, d_odd_y, d_both +CLKout7_ADLY_SEL 3[29] 0 d_pd, d_ev_x, d_odd_y, d_both +CLKout6_7_OSCin_Sel 3[30] 1 VCO,OSCin +CLKout6_7_PD 3[31] 0 power_up, power_down +######################################################################## +## address 4 +######################################################################## +address4 4[0:4] 4 +CLKout8_9_DIV 4[5:15] 25 +CLKout8_9_HS 4[16] 0 +Required_4_17 4[17] 0 +CLKout8_9_DDLY 4[18:27] 0 +CLKout8_ADLY_SEL 4[28] 0 d_pd, d_ev_x, d_odd_y, d_both +CLKout9_ADLY_SEL 4[29] 0 d_pd, d_ev_x, d_odd_y, d_both +CLKout8_9_OSCin_Sel 4[30] 0 VCO, OSCin +CLKout8_9_PD 4[31] 0 power_up, power_down +######################################################################## +## address 5 +######################################################################## +address5 5[0:4] 5 +CLKout10_11_DIV 5[5:15] 25 +CLKout10_11_HS 5[16] 0 +Required_5_17 5[17] 0 +CLKout10_11_DDLY 5[18:27] 0 +CLKout10_ADLY_SEL 5[28] 0 d_pd, d_ev_x, d_odd_y, d_both +CLKout11_ADLY_SEL 5[29] 0 d_pd, d_ev_x, d_odd_y, d_both +Required_5_30 5[30] 0 +CLKout10_11_PD 5[31] 1 normal, power_down +######################################################################## +## address 6 +######################################################################## +#set $CLKoutX_TYPE_ENUMS = "p_down=0, LVDS=1, LVPECL_700mVpp=2, LVPECL_1200mVpp=3, LVPECL_1600mVpp=4, LVPECL_200mVpp=5, LVCMOS=6, LVCMOS_IN=7, LVCMOS_NN=8, LVCMOS_II=9, LVCMOS_LN=10, LVCMOS_LI=11, LVCMOS_NL=12, LVCMOS_IL=13, LVCMOS_LL=1" +address6 6[0:4] 6 +CLKout0_1_ADLY 6[5:9] 0 +Required_6_10 6[10] 0 +CLKout2_3_ADLY 6[11:15] 0 +CLKout0_TYPE 6[16:19] 0 $(CLKoutX_TYPE_ENUMS) +CLKout1_TYPE 6[20:23] 0 $(CLKoutX_TYPE_ENUMS) +CLKout2_TYPE 6[24:27] 0 $(CLKoutX_TYPE_ENUMS) +CLKout3_TYPE 6[28:31] 0 $(CLKoutX_TYPE_ENUMS) +######################################################################## +## address 7 +######################################################################## +address7 7[0:4] 7 +CLKout4_5_ADLY 7[5:9] 0 +Required_7_10 7[10] 0 +CLKout6_7_ADLY 7[11:15] 0 +CLKout4_TYPE 7[16:19] 0 $(CLKoutX_TYPE_ENUMS) +CLKout5_TYPE 7[20:23] 0 $(CLKoutX_TYPE_ENUMS) +CLKout6_TYPE 7[24:27] 0 $(CLKoutX_TYPE_ENUMS) +CLKout7_TYPE 7[28:31] 0 $(CLKoutX_TYPE_ENUMS) +######################################################################## +## address 8 +######################################################################## +address8 8[0:4] 8 +CLKout8_9_ADLY 8[5:9] 0 +Required_8_10 8[10] 0 +CLKout10_11_ADLY 8[11:15] 0 $(CLKoutX_TYPE_ENUMS) +CLKout8_TYPE 8[16:19] 0 $(CLKoutX_TYPE_ENUMS) +CLKout9_TYPE 8[20:23] 0 $(CLKoutX_TYPE_ENUMS) +CLKout10_TYPE 8[24:27] 0 $(CLKoutX_TYPE_ENUMS) +CLKout11_TYPE 8[28:31] 0 $(CLKoutX_TYPE_ENUMS) +######################################################################## +## address 9 +######################################################################## +address9 9[0:4] 9 +Required_9_5 9[5] 0 +Required_9_6 9[6] 1 +Required_9_7 9[7] 0 +Required_9_8 9[8] 1 +Required_9_9 9[9] 0 +Required_9_10 9[10] 1 +Required_9_11 9[11] 0 +Required_9_12 9[12] 1 +Required_9_13 9[13] 0 +Required_9_14 9[14] 1 +Required_9_15 9[15] 0 +Required_9_16 9[16] 1 +Required_9_17 9[17] 0 +Required_9_18 9[18] 1 +Required_9_19 9[19] 0 +Required_9_20 9[20] 1 +Required_9_21 9[21] 0 +Required_9_22 9[22] 1 +Required_9_23 9[23] 0 +Required_9_24 9[24] 1 +Required_9_25 9[25] 0 +Required_9_26 9[26] 1 +Required_9_27 9[27] 0 +Required_9_28 9[28] 1 +Required_9_29 9[29] 0 +Required_9_30 9[30] 1 +Required_9_31 9[31] 0 +######################################################################## +## address 10 +######################################################################## +address10 10[0:4] 10 +FEEDBACK_MUX 10[5:7] 0 +VCO_DIV 10[8:10] 2 +EN_FEEDBACK_MUX 10[11] 0 powered_down, enabled +VCO_MUX 10[12] 0 just_vco, vco_divider +Required_10_13 10[13] 0 +Required_10_14 10[14] 1 +Required_10_15 10[15] 0 +OSCout_DIV 10[16:18] 0 +PD_OSCin 10[19] 0 normal, power_down +OSCout10_MUX 10[20] 0 bypass_div, divided +Required_10_21 10[21] 0 +EN_OSCout0 10[22] 1 disabled, enabled +Required_10_23 10[23] 0 +OSCout0_TYPE 10[24:27] 0 +Required_10_28 10[28] 1 +Required_10_range 10[29:31] 0 +######################################################################## +## address 11 +######################################################################## +address11 11[0:4] 11 +EN_PLL2_XTAL 11[5] 0 osc_disabled=0,osc_enabled=1 +Required_11 11[6:11] 0 +SYNC_TYPE 11[12:14] 1 input=0,in_pull_up=1,in_pull_down=2,out_push_pull=3,out_inverted=4,out_open_sources=5,out_open_drains=6 +SYNC_EN_AUTO 11[15] 0 man_sync=0,sync_int_gen=1 +SYNC_POL_INV 11[16] 1 sync_high=0,sync_low=1 +SYNC_QUAL 11[17] 0 not_qual=0,fb_mux=1 +SYNC_CLKin2_MUX 11[18:19] 0 log_low=0,CLKin2_LOS=1,CLKin2_Selected=2,uWire_RB=3 +NO_SYNC_CLKout0_1 11[20] 0 clock_xy_sync=0,clock_xy_nosync=1 +NO_SYNC_CLKout2_3 11[21] 0 clock_xy_sync=0,clock_xy_nosync=1 +NO_SYNC_CLKout4_5 11[22] 1 clock_xy_sync=0,clock_xy_nosync=1 +NO_SYNC_CLKout6_7 11[23] 1 clock_xy_sync=0,clock_xy_nosync=1 +NO_SYNC_CLKout8_9 11[24] 0 clock_xy_sync=0,clock_xy_nosync=1 +NO_SYNC_CLKout10_11 11[25] 0 clock_xy_sync=0,clock_xy_nosync=1 +EN_SYNC 11[26] 1 disable=0,enable=1 +MODE 11[27:31] 0 dual_int=0, dual_int_zer_delay=2,dual_ext=3,dual_ext_zer_delay=5,pll_two_int=6,pll_two_int_zer_delay=8,pll_two_ext=11,clock_dist=16 +######################################################################## +## address 12 +######################################################################## +address12 12[0:4] 12 +HOLDOVER_MODE 12[6:7] 2 disabled=1,enabled=2 +EN_TRACK 12[8] 1 disabled=0,enabled=1 +Required_12_range917 12[9:17] 0 +Required_12_range1819 12[18:19] 1 +Required_12_20 12[20] 0 +Required_LE_12 12[21] 0 +SYNC_PLL1_DLD 12[22] 0 sync_mode_noforce=0,sync_mode_force=1 +SYNC_PLL2_DLD 12[23] 0 sync_mode_noforce=0,sync_mode_force=1 +LD_TYPE 12[24:26] 3 out_push_pull=3,out_inverted=4,out_open_sources=5,out_open_drains=6 +LD_MUX 12[27:31] 3 log_low=0,pll1_dld=1,pll2_dld=2,both=3,holdover=4,locked_dac=5,uWire_RB=7,rail=8,low=9,high=10,pll1_N=11,pll1_Nhalf=12,pll2_N=13,pll2_Nhalf=14,pll1_R=15,pll1_Rhalf=16,pll2_R=17,pll2_Rhalf=18 +######################################################################## +## address 13 +######################################################################## +address13 13[0:4] 13 +EN_CLKin0 13[5] 1 no_valid_use=0,valid_use=1 +EN_CLKin1 13[6] 1 no_valid_use=0,valid_use=1 +EN_CLKin2 13[7] 1 no_valid_use=0,valid_use=1 +CLKin_Sel_INV 13[8] 0 active_high=0,active_low=1 +CLKin_Select_MODE 13[9:11] 3 CLKin0_man=0,CLKin1_man=1,CLKin2_man=2,pin_sel_mode=3,auto_mode=4,auto_mode_w_next_block_sel=6 +Status_CLKin0_MUX 13[14:12] 0 log_low=0,CLKin0_LOS=1,CLKin0_Selected=2,dac_lock=3,dac_low=4,dac_high=5,uWire_RB=6 +DISABLE_DLD1_DET 13[15] 0 pll1_dld_cause_event=0,pll1_dld_not_cause=1 +Status_CLKin0_TYPE 13[16:18] 2 input=0,in_pull_up=1,in_pull_down=2,out_push_pull=3,out_inverted=4,out_open_sources=5,out_open_drains=6 +Required_13_19 13[19] 0 +Status_CLKin1_MUX 13[20:22] 0 log_low=0,CLKin1_LOS=1,CLKin1_Selected=2,DAC_lock=3,DAC_low=4,DAC_high=5,uWire_RB=6 +Required_19_23 13[23] 0 +HOLDOVER_TYPE 13[24:26] 3 out_push_pull=3,out_inverted=4,out_open_sources=5,out_open_drains=6 +HOLDOVER_MUX 13[27:31] 7 log_low=0,pll1_dld=1,pll2_dld=2,both=3,holdover=4,locked_dac=5,uWire_RB=7,rail=8,low=9,high=10,pll1_N=11,pll1_Nhalf=12,pll2_N=13,pll2_Nhalf=14,pll1_R=15,pll1_Rhalf=16,pll2_R=17,pll2_Rhalf=18 +######################################################################## +## address 14 +######################################################################## +address14 14[0:4] 14 +EN_VTUNE_RAIL_DET 14[5] 0 disabled=0,enabled=1 +DAC_LOW_TRIP 14[6:11] 0 +Required_14_1213 14[12:13] 0 +DAC_HIGH_TRIP 14[14:19] 0 +CLKin0_BUF_TYPE 14[20] 0 bipolar=0,cmos=1 +CLKin1_BUF_TYPE 14[21] 0 bipolar=0,cmos=1 +CLKin2_BUF_TYPE 14[22] 0 bipolar=0,cmos=1 +Required_14_23 14[23] 0 +Status_CLKin1_TYPE 14[24:26] 2 input=0,in_pull_up=1,in_pull_down=2,out_push_pull=3,out_inverted=4,out_open_sources=5,out_open_drains=6 +Required_14_27 14[27] 0 +EN_LOS 14[28] 1 disable_timeout=0,enable_timeout=1 +Required_14_29 14[29] 0 +LOS_TIMEOUT 14[30:31] 0 1200ns_at_4p2KHz=0,206ns_at_2p5MHz=1,52p9at10MHz=2,23p7_at_22MHz=3 +######################################################################## +## address 15 +######################################################################## +address15 15[0:4] 15 +FORCE_HOLDOVER 15[5] 0 disabled=0,enabled=1 +HOLDOVER_DLD_CNT 15[6:19] 512 +EN_MAN_DAC 15[20] 0 disabled=0,enabled=1 +Required_15 15[21] 0 +MAN_DAC 15[22:31] 512 +######################################################################## +## address 16 +######################################################################## +address16 16[0:4] 16 +Required_16_5 16[5] 0 +Required_16_6 16[6] 0 +Required_16_7 16[7] 0 +Required_16_8 16[8] 0 +Required_16_9 16[9] 0 +Required_16_10 16[10] 1 +Required_16_11 16[11] 0 +Required_16_12 16[12] 0 +Required_16_13 16[13] 0 +Required_16_14 16[14] 0 +Required_16_15 16[15] 0 +Required_16_16 16[16] 1 +Required_16_17 16[17] 0 +Required_16_18 16[18] 1 +Required_16_19 16[19] 0 +Required_16_20 16[20] 1 +Required_16_21 16[21] 0 +Required_16_22 16[22] 1 +Required_16_23 16[23] 0 +Required_16_24 16[24] 1 +Required_16_25 16[25] 0 +Required_16_26 16[26] 0 +Required_16_27 16[27] 0 +Required_16_28 16[28] 0 +Required_16_29 16[29] 0 +XTAL_LVL 16[30:31] 0 1p65VPP=0, 1p75VPP=1, 1p90VPP=2, 2p05VPP=3 +######################################################################## +## address 24 +######################################################################## +address24 24[0:4] 24 +PLL1_WND_SIZE 24[6:7] 3 5p5ns=0, 10ns=1, 18p6=2, 40ns=3 +PLL1_R_DLY 24[8:10] 0 0ps=0, 205ps=1, 410ps=2, 615ps=3, 820ps=4, 1025ps=5, 1230ps=6, 1345ps=7 +Required_24_11 24[11] 0 +PLL2_N_DLY 24[12:14] 0 0ps=0, 205ps=1, 410ps=2, 615ps=3, 820ps=4, 1025ps=5, 1230ps=6, 1345ps=7 +Required_24_15 24[15] 0 +PLL2_R3_LF 24[16:18] 0 200ohm=0, 1kilo_ohm=1, 2kilo_ohm=2, 4kilo_ohm=3, 16kilo_ohm=4 +Required_24_19 24[19] 0 +PLL2_R4_LF 24[20:22] 0 200ohm=0, 1kilo_ohm=1, 2kilo_ohm=2, 4kilo_ohm=3, 16kilo_ohm=4 +Required_24_23 24[23] 0 +PLL2_C3_LF 24[24:27] 0 10pF=0, 11pF=1, 15pF=2, 16pF=3, 19pF=4, 20pF=5, 24pF=6, 25pF=7, 29pF=8, 30pF=9, 33pF=10, 34pF=11, 38pF=12, 39pF=13 +PLL2_C4_LF 24[28:31] 0 10pF=0, 15pF=1, 29pF=2, 34pF=3, 47pF=4, 52pF=5, 66pF=6, 71pF=7, 103pF=8, 108pF=9, 122pF=10, 126pF=11, 141pF=12, 146pF=13 +######################################################################### +## address 25 +######################################################################### +addres25 25[0:4] 25 +PLL2_CP_TRI_25 25[5] 0 +PLL1_DLD_CNT_25 25[6:19] 1024 +Required_25 25[20:21] 0 +DAC_CLK_DIV 25[22:31] 4 +######################################################################### +## address 26 +######################################################################### +address26 26[0:4] 26 +PLL2_CP_TRI_26 26[5] 0 PLL2_cpout2_active=0,PLL2cpout2T=1 +PLL2_DLD_CNT_26 26[6:19] 81 +Required_26_20 26[20] 0 +Required_26_21 26[21] 1 +Required_26_22 26[22] 0 +Required_26_23 26[23] 1 +Required_26_24 26[24] 1 +Required_26_25 26[25] 1 +PLL2_CP_GAIN_26 26[26:27] 3 100uA=0, 400uA=1, 1600uA=2, 3200uA=3 +PLL2_CP_POL_26 26[28] 0 neg_slope=0, pos_slope=1 +EN_PLL2_REF_2X 26[29] 0 normal_freq_ref=0, doubled_freq_ref=1 +PLL2_WND_SIZE 26[30:31] 2 3p7ns=2 +######################################################################### +## address 27 +######################################################################### +address27 27[0:4] 27 +PLL1_CP_TRI_27 27[5] 0 PLL2_cpout2_active=0,PLL2_cpout2TRI=1 +PLL1_R_27 27[6:19] 96 +CLKin0_PreR_DIV 27[20:21] 0 div1=0,div2=1,div4=2,div8=3 +CLKin1_PreR_DIV 27[22:23] 0 div1=0,div2=1,div4=2,div8=3 +CLKin2_PreR_DIV 27[24:25] 0 div1=0,div2=1,div4=2,div8=3 +PLL1_CP_GAIN_27 27[26:27] 0 100uA=0, 200uA=1, 400uA=2, 1600uA=3 +PLL1_CP_POL_27 27[28] 1 neg_slove=0, pos_slope=1 +Reqiured_27 27[29:31] 0 +######################################################################### +## address 28 +######################################################################### +address28 28[0:4] 28 +Required_28 28[5] 0 +PLL1_N_28 28[6:19] 192 +PLL2_R_28 28[20:31] 4 +######################################################################### +## address 29 +######################################################################### +address29 29[0:4] 29 +PLL2_N_CAL_29 29[5:22] 48 +PLL2_FAST_PDF_29 29[23] 1 100MHz_less=0, 100MHz_more=1 +OSCin_FREQ_29 29[24:26] 1 zero_to_63MHz=0, 63_to_127MHz=1, 127_to_255MHz=2, 255_to_400MHz=4 +Required29 29[27:31] 0 +######################################################################### +## address 30 +######################################################################### +address30 30[0:4] 30 +PLL2_N_30 30[5:22] 48 +Required_30_522 30[23] 0 +PLL2_P_30 30[24:26] 2 div_8=0, div_2=1, div_2a=2, div_3=3, div_4=4, div_5=5, div_6=6, div_7=7 +Required_2731 30[27:31] 0 +######################################################################### +## address 31 +######################################################################### +address31 31[0:4] 31 +uWire_LOCK 31[5] 0 reg_locked=0, reg_unlocked=1 +Required_615 31[6:15] 0 +READBACK_ADDR 31[16:20] 31 R0=0, R1=1, R2=2, R3=3, R4=4, R5=5, R6=6, R7=7, R8=8, R10=10, R11=11, R12=12, R13=13, R14=14, R15=15, R24=24, R25=25, R26=26, R27=27, R28=28, R29=29, R30=30, R31=31 +READBACK_LE 31[21] 0 LE_low=0, LE_high=1 +Required_2231 31[22:31] 0 +""" +######################################################################## +# Template for methods in the body of the struct +######################################################################## + +BODY_TMPL = """\ + + + + +boost::uint32_t get_reg(int addr){ + boost::uint32_t reg = 0; + switch(addr){ + #for $addr in sorted(set(map(lambda r: r.get_addr(), $regs))) + case $addr: + #for $reg in filter(lambda r: r.get_addr() == addr, $regs) + reg |= (boost::uint32_t($reg.get_name()) & $reg.get_mask()) << $reg.get_shift(); + #end for + break; + #end for + } + return reg; +} +""" + + + + + +if __name__ == '__main__': + import common; common.generate( + name='lmk04816_regs', + regs_tmpl=REGS_TMPL, + body_tmpl=BODY_TMPL, + file=__file__, + ) + + + + diff --git a/host/lib/transport/CMakeLists.txt b/host/lib/transport/CMakeLists.txt index 963edcf85..5920f3d78 100644 --- a/host/lib/transport/CMakeLists.txt +++ b/host/lib/transport/CMakeLists.txt @@ -20,6 +20,11 @@ ######################################################################## ######################################################################## +# Include subdirectories (different than add) +######################################################################## +INCLUDE_SUBDIRECTORY(nirio) + +######################################################################## # Setup libusb ######################################################################## MESSAGE(STATUS "") @@ -119,7 +124,18 @@ LIBUHD_PYTHON_GEN_SOURCE( ) LIBUHD_APPEND_SOURCES( + ${CMAKE_CURRENT_SOURCE_DIR}/tcp_zero_copy.cpp ${CMAKE_CURRENT_SOURCE_DIR}/buffer_pool.cpp ${CMAKE_CURRENT_SOURCE_DIR}/if_addrs.cpp ${CMAKE_CURRENT_SOURCE_DIR}/udp_simple.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/nirio_zero_copy.cpp ) + +# Verbose Debug output for send/recv +SET( UHD_TXRX_DEBUG_PRINTS OFF CACHE BOOL "Use verbose debug output for send/recv" ) +OPTION( UHD_TXRX_DEBUG_PRINTS "Use verbose debug output for send/recv" "" ) +IF(UHD_TXRX_DEBUG_PRINTS) + MESSAGE(STATUS "Using verbose debug output for send/recv") + ADD_DEFINITIONS(-DUHD_TXRX_DEBUG_PRINTS) +ENDIF() + diff --git a/host/lib/transport/buffer_pool.cpp b/host/lib/transport/buffer_pool.cpp index 971bbb75a..0fcebecb1 100644 --- a/host/lib/transport/buffer_pool.cpp +++ b/host/lib/transport/buffer_pool.cpp @@ -16,11 +16,21 @@ // #include <uhd/transport/buffer_pool.hpp> +#include <uhd/transport/zero_copy.hpp> #include <boost/shared_array.hpp> #include <vector> using namespace uhd::transport; +#ifdef UHD_TXRX_DEBUG_PRINTS +/* + * This is the implementation for the static variable 's_buffer_count' + * located in uhd/transport/zero_copy.hpp. + * It is used in the managed_buffer class. + */ +boost::detail::atomic_count managed_buffer::s_buffer_count(0); +#endif // UHD_TXRX_DEBUG_PRINTS + //! pad the byte count to a multiple of alignment static size_t pad_to_boundary(const size_t bytes, const size_t alignment){ return bytes + (alignment - bytes)%alignment; diff --git a/host/lib/transport/libusb1_zero_copy.cpp b/host/lib/transport/libusb1_zero_copy.cpp index 2d18e1623..4ea2032e3 100644 --- a/host/lib/transport/libusb1_zero_copy.cpp +++ b/host/lib/transport/libusb1_zero_copy.cpp @@ -31,6 +31,12 @@ #include <boost/thread/condition_variable.hpp> #include <list> +#ifdef UHD_TXRX_DEBUG_PRINTS +#include <vector> +#include <fstream> +#include <boost/format.hpp> +#endif + using namespace uhd; using namespace uhd::transport; @@ -69,12 +75,23 @@ struct lut_result_t completed = 0; status = LIBUSB_TRANSFER_COMPLETED; actual_length = 0; +#ifdef UHD_TXRX_DEBUG_PRINTS + start_time = 0; + buff_num = -1; +#endif } int completed; libusb_transfer_status status; int actual_length; boost::mutex mut; boost::condition_variable usb_transfer_complete; + +#ifdef UHD_TXRX_DEBUG_PRINTS + // These are fore debugging + long start_time; + int buff_num; + bool is_recv; +#endif }; // Created to be used as an argument to boost::condition_variable::timed_wait() function @@ -84,6 +101,14 @@ struct lut_result_completed { bool operator()() const {return (_result.completed ? true : false);} }; +#ifdef UHD_TXRX_DEBUG_PRINTS +static std::string dbg_prefix("libusb1_zero_copy,"); +static void libusb1_zerocopy_dbg_print_err(std::string msg){ + msg = dbg_prefix + msg; + fprintf(stderr, "%s\n", msg.c_str()); +} +#endif + /*! * All libusb callback functions should be marked with the LIBUSB_CALL macro * to ensure that they are compiled with the same calling convention as libusb. @@ -98,6 +123,10 @@ static void LIBUSB_CALL libusb_async_cb(libusb_transfer *lut) r->actual_length = lut->actual_length; r->completed = 1; r->usb_transfer_complete.notify_one(); // wake up thread waiting in wait_for_completion() member function below +#ifdef UHD_TXRX_DEBUG_PRINTS + long end_time = boost::get_system_time().time_of_day().total_microseconds(); + libusb1_zerocopy_dbg_print_err( (boost::format("libusb_async_cb,%s,%i,%i,%i,%ld,%ld") % (r->is_recv ? "rx":"tx") % r->buff_num % r->actual_length % r->status % end_time % r->start_time).str() ); +#endif } /*********************************************************************** @@ -113,11 +142,18 @@ public: _ctx(libusb::session::get_global_session()->get_context()), _lut(lut), _frame_size(frame_size) { /* NOP */ } - void release(void){_release_cb(this);} + void release(void){ + _release_cb(this); + } UHD_INLINE void submit(void) { - _lut->length = (_is_recv)? _frame_size : size(); //always set length + _lut->length = (_is_recv)? _frame_size : size(); //always set length +#ifdef UHD_TXRX_DEBUG_PRINTS + result.start_time = boost::get_system_time().time_of_day().total_microseconds(); + result.buff_num = num(); + result.is_recv = _is_recv; +#endif const int ret = libusb_submit_transfer(_lut); if (ret != 0) throw uhd::runtime_error(str(boost::format( "usb %s submit failed: %s") % _name % libusb_error_name(ret))); @@ -160,7 +196,6 @@ public: } private: - boost::function<void(libusb_zero_copy_mb *)> _release_cb; const bool _is_recv; const std::string _name; @@ -214,7 +249,7 @@ public: UHD_ASSERT_THROW(lut != NULL); _mb_pool.push_back(boost::make_shared<libusb_zero_copy_mb>( - lut, this->get_frame_size(), boost::bind(&libusb_zero_copy_single::enqueue_damn_buffer, this, _1), is_recv, name + lut, this->get_frame_size(), boost::bind(&libusb_zero_copy_single::enqueue_buffer, this, _1), is_recv, name )); libusb_fill_bulk_transfer( @@ -304,7 +339,7 @@ private: //! why 2 queues? there is room in the future to have > N buffers but only N in flight boost::circular_buffer<libusb_zero_copy_mb *> _enqueued, _released; - void enqueue_damn_buffer(libusb_zero_copy_mb *mb) + void enqueue_buffer(libusb_zero_copy_mb *mb) { boost::mutex::scoped_lock l(_mutex); _released.push_back(mb); diff --git a/host/lib/transport/nirio/CMakeLists.txt b/host/lib/transport/nirio/CMakeLists.txt new file mode 100644 index 000000000..6a33da6c5 --- /dev/null +++ b/host/lib/transport/nirio/CMakeLists.txt @@ -0,0 +1,48 @@ +# +# Copyright 2013-2014 Ettus Research LLC +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# 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/>. +# + +######################################################################## +# This file included, use CMake directory variables +######################################################################## + +######################################################################## +# Include subdirectories (different than add) +######################################################################## +INCLUDE_SUBDIRECTORY(lvbitx) +INCLUDE_SUBDIRECTORY(rpc) + +######################################################################## +# Append to the list of sources for lib uhd +######################################################################## + +LIBUHD_APPEND_SOURCES( + ${CMAKE_CURRENT_SOURCE_DIR}/nifpga_lvbitx.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/niusrprio_session.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/niriok_proxy.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/nirio_resource_manager.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/status.cpp +) + +IF(WIN32) + LIBUHD_APPEND_SOURCES(${CMAKE_CURRENT_SOURCE_DIR}/nirio_driver_iface_win.cpp) +ELSE(WIN32) + IF(APPLE) + LIBUHD_APPEND_SOURCES(${CMAKE_CURRENT_SOURCE_DIR}/nirio_driver_iface_macos.cpp) + ELSE(APPLE) + LIBUHD_APPEND_SOURCES(${CMAKE_CURRENT_SOURCE_DIR}/nirio_driver_iface_linux.cpp) + ENDIF(APPLE) +ENDIF(WIN32) diff --git a/host/lib/transport/nirio/lvbitx/CMakeLists.txt b/host/lib/transport/nirio/lvbitx/CMakeLists.txt new file mode 100644 index 000000000..35cfaa456 --- /dev/null +++ b/host/lib/transport/nirio/lvbitx/CMakeLists.txt @@ -0,0 +1,65 @@ +# +# Copyright 2013 Ettus Research LLC +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +MACRO(LIBUHD_LVBITX_GEN_SOURCE_AND_BITSTREAM lvbitx binfile) + GET_FILENAME_COMPONENT(lvbitxprefix ${lvbitx} NAME_WE) + + IF( ${binfile} STREQUAL "OFF" ) + SET( GEN_OPTIONS ) + MESSAGE( STATUS " Using ${lvbitx} for codegen" ) + ELSE( ${binfile} STREQUAL "OFF" ) + SET( GEN_OPTIONS --merge-bin=${CMAKE_SOURCE_DIR}/../binaries/${binfile} --output-lvbitx-path=${CMAKE_SOURCE_DIR}/../binaries ) + MESSAGE( STATUS " Merging ${lvbitx} with ${binfile} for codegen" ) + ENDIF( ${binfile} STREQUAL "OFF" ) + + SET(OUTPUT_PATH_OPT --output-src-path=${CMAKE_CURRENT_BINARY_DIR}) + SET(IMAGES_PATH_OPT --uhd-images-path=${FPGA_IMAGES_DIR}) + + ADD_CUSTOM_COMMAND( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${lvbitxprefix}_lvbitx.hpp + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${lvbitxprefix}_lvbitx.cpp + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/process-lvbitx.py + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/template_lvbitx.hpp + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/template_lvbitx.cpp + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${lvbitx} + COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/process-lvbitx.py ${OUTPUT_PATH_OPT} ${IMAGES_PATH_OPT} ${GEN_OPTIONS} ${CMAKE_CURRENT_SOURCE_DIR}/${lvbitx} + COMMENT "Generating ${CMAKE_CURRENT_BINARY_DIR}/${lvbitxprefix}_lvbitx.cpp" + ) + + #make libuhd depend on the output file + LIBUHD_APPEND_SOURCES(${CMAKE_CURRENT_BINARY_DIR}/${lvbitxprefix}_lvbitx.cpp) +ENDMACRO(LIBUHD_LVBITX_GEN_SOURCE_AND_BITSTREAM) + +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) + +######################################################################## +# Generation code +######################################################################## + +MESSAGE(STATUS "") +MESSAGE(STATUS "Processing NI-RIO FPGA LVBITX Bitstreams...") + +FILE(TO_NATIVE_PATH ${CMAKE_INSTALL_PREFIX}/share/uhd/images default_images_dir) +SET( FPGA_IMAGES_DIR ${default_images_dir} CACHE STRING "Path to installed FPGA image files." ) +OPTION( FPGA_IMAGES_DIR "Path to installed FPGA image files." "" ) +MESSAGE( STATUS " LVBITX install directory: ${FPGA_IMAGES_DIR}" ) + +# X300 Stuff +LIBUHD_LVBITX_GEN_SOURCE_AND_BITSTREAM(x300.lvbitx_base OFF) + +# X310 Stuff +LIBUHD_LVBITX_GEN_SOURCE_AND_BITSTREAM(x310.lvbitx_base OFF) diff --git a/host/lib/transport/nirio/lvbitx/process-lvbitx.py b/host/lib/transport/nirio/lvbitx/process-lvbitx.py new file mode 100755 index 000000000..a3d88d0bb --- /dev/null +++ b/host/lib/transport/nirio/lvbitx/process-lvbitx.py @@ -0,0 +1,185 @@ +#!/usr/bin/python +# +# Copyright 2013-2014 Ettus Research LLC +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# 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/>. +# + +from xml.etree import ElementTree +from collections import namedtuple +import optparse +import base64 +import hashlib +import os +import sys + +# Parse options +parser = optparse.OptionParser() +parser.add_option("--uhd-images-path", type="string", dest="uhd_images_path", help="Install location for UHD images", default='') +parser.add_option("--merge-bin", type="string", dest="merge_bin", help="Path to bin file that needs to be merged with the LVBITX before exporting", default=None) +parser.add_option("--output-bin", action="store_true", dest="output_bin", help="Generate a binary FPGA programming bitstream file", default=False) +parser.add_option("--output-lvbitx-path", type="string", dest="output_lvbitx_path", help="Output path for autogenerated LVBITX file", default=None) +parser.add_option("--output-src-path", type="string", dest="output_src_path", help="Output path for autogenerated src file", default=None) +(options, args) = parser.parse_args() + +# Args +if (len(args) < 1): + print 'ERROR: Please specify the input LVBITX file name' + sys.exit(1) + +lvbitx_filename = args[0] +input_filename = os.path.abspath(lvbitx_filename) +autogen_src_path = os.path.abspath(options.output_src_path) if (options.output_src_path is not None) else os.path.dirname(input_filename) +class_name = os.path.splitext(os.path.basename(input_filename))[0] + +if (not os.path.isfile(input_filename)): + print 'ERROR: FPGA File ' + input_filename + ' could not be accessed or is not a file.' + sys.exit(1) +if (options.merge_bin is not None and not os.path.isfile(os.path.abspath(options.merge_bin))): + print 'ERROR: FPGA Bin File ' + options.merge_bin + ' could not be accessed or is not a file.' + sys.exit(1) +if (not os.path.exists(autogen_src_path)): + print 'ERROR: Output path ' + autogen_src_path + ' could not be accessed.' + sys.exit(1) +if (options.output_lvbitx_path is not None and input_filename == os.path.join(autogen_src_path, class_name + '.lvbitx')): + print 'ERROR: Input and output LVBITX files were the same. Choose a difference input file or output path.' + sys.exit(1) + +# Get XML Tree Node +tree = ElementTree.parse(input_filename) +root = tree.getroot() +codegen_transform = {} + +# General info +codegen_transform['autogen_msg'] = '// Auto-generated file: DO NOT EDIT!\n// Generated from a LabVIEW FPGA LVBITX image using "process-lvbitx.py"' +codegen_transform['lvbitx_search_paths'] = options.uhd_images_path.replace('\\', '\\\\') +codegen_transform['lvbitx_classname'] = class_name +codegen_transform['lvbitx_classname_u'] = class_name.upper() +bitstream_version = root.find('BitstreamVersion').text + +# Enumerate registers (controls and indicators) +register_list = root.find('VI').find('RegisterList') + +reg_init_seq = '' +control_list = '' +indicator_list = '' +control_idx = 0 +indicator_idx = 0 +for register in register_list.findall('Register'): + reg_type = 'INDICATOR' if (register.find('Indicator').text.lower() == 'true') else 'CONTROL' + reg_name = '\"' + register.find('Name').text + '\"' + + if (reg_type == 'INDICATOR'): + indicator_list += '\n ' + reg_name + ',' + idx = indicator_idx + indicator_idx += 1 + else: + control_list += '\n ' + reg_name + ',' + idx = control_idx + control_idx += 1 + + reg_init_seq += '\n vtr.push_back(nirio_register_info_t(' + reg_init_seq += hex(int(register.find('Offset').text)) + ', ' + reg_init_seq += reg_type + 'S[' + str(idx) + '], ' + reg_init_seq += reg_type + reg_init_seq += ')); //' + reg_name + + +codegen_transform['register_init'] = reg_init_seq +codegen_transform['control_list'] = control_list +codegen_transform['indicator_list'] = indicator_list + +# Enumerate FIFOs +nifpga_metadata = root.find('Project').find('CompilationResultsTree').find('CompilationResults').find('NiFpga') +dma_channel_list = nifpga_metadata.find('DmaChannelAllocationList') +reg_block_list = nifpga_metadata.find('RegisterBlockList') + +fifo_init_seq = '' +out_fifo_list = '' +in_fifo_list = '' +out_fifo_idx = 0 +in_fifo_idx = 0 +for dma_channel in dma_channel_list: + fifo_name = '\"' + dma_channel.attrib['name'] + '\"' + direction = 'OUTPUT_FIFO' if (dma_channel.find('Direction').text == 'HostToTarget') else 'INPUT_FIFO' + for reg_block in reg_block_list.findall('RegisterBlock'): + if (reg_block.attrib['name'] == dma_channel.find('BaseAddressTag').text): + base_addr = reg_block.find('Offset').text + break + + if (direction == 'OUTPUT_FIFO'): + out_fifo_list += '\n ' + fifo_name + ',' + idx = out_fifo_idx + out_fifo_idx += 1 + else: + in_fifo_list += '\n ' + fifo_name + ',' + idx = in_fifo_idx + in_fifo_idx += 1 + + fifo_init_seq += '\n vtr.push_back(nirio_fifo_info_t(' + fifo_init_seq += dma_channel.find('Number').text + ', ' + fifo_init_seq += direction + 'S[' + str(idx) + '], ' + fifo_init_seq += direction + ', ' + fifo_init_seq += str.lower(base_addr) + ', ' + fifo_init_seq += dma_channel.find('NumberOfElements').text + ', ' + fifo_init_seq += 'SCALAR_' + dma_channel.find('DataType').find('SubType').text + ', ' + fifo_init_seq += dma_channel.find('DataType').find('WordLength').text + ', ' + fifo_init_seq += bitstream_version + fifo_init_seq += ')); //' + fifo_name + + +codegen_transform['fifo_init'] = fifo_init_seq +codegen_transform['out_fifo_list'] = out_fifo_list +codegen_transform['in_fifo_list'] = in_fifo_list + +# Merge bitstream into LVBITX +if (options.merge_bin is not None): + with open(os.path.abspath(options.merge_bin), 'rb') as bin_file: + bitstream = bin_file.read() + bitstream_md5 = hashlib.md5(bitstream).hexdigest() + bitstream_b64 = base64.b64encode(bitstream) + bitstream_b64_lb = '' + for i in range(0, len(bitstream_b64), 76): + bitstream_b64_lb += bitstream_b64[i:i+76] + '\n' + + root.find('Bitstream').text = bitstream_b64_lb + root.find('BitstreamMD5').text = bitstream_md5 + +codegen_transform['lvbitx_signature'] = str.upper(root.find('SignatureRegister').text) + +# Write BIN file +bitstream = base64.b64decode(root.find('Bitstream').text) +if (options.output_lvbitx_path is not None and hashlib.md5(bitstream).hexdigest() != root.find('BitstreamMD5').text): + print 'ERROR: The MD5 sum for the output LVBITX was incorrect. Make sure that the bitstream in the input LVBITX or BIN file is valid.' + sys.exit(1) +if (options.output_bin): + fpga_bin_file = open(os.path.join(options.output_lvbitx_path, class_name + '.bin'), 'w') + fpga_bin_file.write(bitstream) + fpga_bin_file.close() + +# Save LVBITX +if (options.output_lvbitx_path is not None): + tree.write(os.path.join(options.output_lvbitx_path, class_name + '_fpga.lvbitx'), encoding="utf-8", xml_declaration=True, default_namespace=None, method="xml") + +# Save HPP and CPP +with open(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'template_lvbitx.hpp'), 'r') as template_file: + template_string = template_file.read() +with open(os.path.join(autogen_src_path, class_name + '_lvbitx.hpp'), 'w') as source_file: + source_file.write(template_string.format(**codegen_transform)) + +with open(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'template_lvbitx.cpp'), 'r') as template_file: + template_string = template_file.read() +with open(os.path.join(autogen_src_path, class_name + '_lvbitx.cpp'), 'w') as source_file: + source_file.write(template_string.format(**codegen_transform)) + diff --git a/host/lib/transport/nirio/lvbitx/template_lvbitx.cpp b/host/lib/transport/nirio/lvbitx/template_lvbitx.cpp new file mode 100644 index 000000000..a1899c771 --- /dev/null +++ b/host/lib/transport/nirio/lvbitx/template_lvbitx.cpp @@ -0,0 +1,86 @@ +{autogen_msg} + +#include "{lvbitx_classname}_lvbitx.hpp" +#include <string> +#include <iostream> +#include <fstream> +#include <streambuf> +#include <boost/filesystem/path.hpp> +#include <boost/algorithm/string.hpp> +#include <boost/regex.hpp> + +namespace uhd {{ namespace niusrprio {{ + +#define SEARCH_PATHS "{lvbitx_search_paths}" + +const char* {lvbitx_classname}_lvbitx::CONTROLS[] = {{{control_list} +}}; + +const char* {lvbitx_classname}_lvbitx::INDICATORS[] = {{{indicator_list} +}}; + +const char* {lvbitx_classname}_lvbitx::OUTPUT_FIFOS[] = {{{out_fifo_list} +}}; + +const char* {lvbitx_classname}_lvbitx::INPUT_FIFOS[] = {{{in_fifo_list} +}}; + +{lvbitx_classname}_lvbitx::{lvbitx_classname}_lvbitx(const std::string& option) +{{ + boost::filesystem::path fpga_path(_get_fpga_images_dir(SEARCH_PATHS)); + fpga_path /= "usrp_{lvbitx_classname}_fpga_" + option + ".lvbitx"; + _fpga_file_name = fpga_path.string(); + _bitstream_checksum = _get_bitstream_checksum(_fpga_file_name); +}} + +const char* {lvbitx_classname}_lvbitx::get_bitfile_path() {{ + return _fpga_file_name.c_str(); +}} + +const char* {lvbitx_classname}_lvbitx::get_signature() {{ + return "{lvbitx_signature}"; +}} + +const char* {lvbitx_classname}_lvbitx::get_bitstream_checksum() {{ + return _bitstream_checksum.c_str(); +}} + +size_t {lvbitx_classname}_lvbitx::get_input_fifo_count() {{ + return sizeof(INPUT_FIFOS)/sizeof(*INPUT_FIFOS); +}} + +const char** {lvbitx_classname}_lvbitx::get_input_fifo_names() {{ + return INPUT_FIFOS; +}} + +size_t {lvbitx_classname}_lvbitx::get_output_fifo_count() {{ + return sizeof(OUTPUT_FIFOS)/sizeof(*OUTPUT_FIFOS); +}} + +const char** {lvbitx_classname}_lvbitx::get_output_fifo_names() {{ + return OUTPUT_FIFOS; +}} + +size_t {lvbitx_classname}_lvbitx::get_control_count() {{ + return sizeof(CONTROLS)/sizeof(*CONTROLS); +}} + +const char** {lvbitx_classname}_lvbitx::get_control_names() {{ + return CONTROLS; +}} + +size_t {lvbitx_classname}_lvbitx::get_indicator_count() {{ + return sizeof(INDICATORS)/sizeof(*INDICATORS); +}} + +const char** {lvbitx_classname}_lvbitx::get_indicator_names() {{ + return INDICATORS; +}} + +void {lvbitx_classname}_lvbitx::init_register_info(nirio_register_info_vtr& vtr) {{ {register_init} +}} + +void {lvbitx_classname}_lvbitx::init_fifo_info(nirio_fifo_info_vtr& vtr) {{ {fifo_init} +}} + +}}}} diff --git a/host/lib/transport/nirio/lvbitx/template_lvbitx.hpp b/host/lib/transport/nirio/lvbitx/template_lvbitx.hpp new file mode 100644 index 000000000..d8872e15c --- /dev/null +++ b/host/lib/transport/nirio/lvbitx/template_lvbitx.hpp @@ -0,0 +1,48 @@ +{autogen_msg} + +#ifndef INCLUDED_{lvbitx_classname_u}_LVBITX_HPP +#define INCLUDED_{lvbitx_classname_u}_LVBITX_HPP + +#include <uhd/transport/nirio/nifpga_lvbitx.h> + +namespace uhd {{ namespace niusrprio {{ + +class {lvbitx_classname}_lvbitx : public nifpga_lvbitx {{ +public: + {lvbitx_classname}_lvbitx(const std::string& option); + + virtual ~{lvbitx_classname}_lvbitx() {{}}; + + virtual const char* get_bitfile_path(); + virtual const char* get_signature(); + virtual const char* get_bitstream_checksum(); + + virtual size_t get_input_fifo_count(); + virtual const char** get_input_fifo_names(); + + virtual size_t get_output_fifo_count(); + virtual const char** get_output_fifo_names(); + + virtual size_t get_control_count(); + virtual const char** get_control_names(); + + virtual size_t get_indicator_count(); + virtual const char** get_indicator_names(); + + virtual void init_register_info(nirio_register_info_vtr& vtr); + virtual void init_fifo_info(nirio_fifo_info_vtr& vtr); + + static const char* CONTROLS[]; + static const char* INDICATORS[]; + static const char* OUTPUT_FIFOS[]; + static const char* INPUT_FIFOS[]; + +private: + std::string _fpga_file_name; + std::string _bitstream_checksum; +}}; + +}}}} + +#endif /* INCLUDED_{lvbitx_classname_u}_LVBITX_HPP */ + diff --git a/host/lib/transport/nirio/lvbitx/x300.lvbitx_base b/host/lib/transport/nirio/lvbitx/x300.lvbitx_base new file mode 100755 index 000000000..c264e7157 --- /dev/null +++ b/host/lib/transport/nirio/lvbitx/x300.lvbitx_base @@ -0,0 +1,469 @@ +<?xml version="1.0" encoding="utf-8"?> +<Bitfile> + <BitfileVersion>4.0</BitfileVersion> + <Documentation> + <BuildSpecVersion/> + <BuildSpecDescription/> + </Documentation> + <SignatureRegister>97C6D9F4F4829001B83378F93CAB0C94</SignatureRegister> + <SignatureGuids>7BAD6AEB9741248079F13147B3F8AD94</SignatureGuids> + <SignatureNames>AE54C47F787D92DB46F7DC973338D786</SignatureNames> + <TimeStamp/> + <CompilationStatus/> + <BitstreamVersion>2</BitstreamVersion> + <VI> + <Name>USRP_X3x0_Top.vi</Name> + <RegisterList> + <Register> + <Name>ViSignature</Name> + <Hidden>true</Hidden> + <Indicator>true</Indicator> + <Datatype> + <Array> + <Name/> + <Size>4</Size> + <Type> + <U32> + <Name/> + </U32> + </Type> + </Array> + </Datatype> + <FlattenedType/> + <Grouping/> + <Offset>262132</Offset> + <SizeInBits>128</SizeInBits> + <Class>0</Class> + <Internal>true</Internal> + <TypedefPath/> + <TypedefRelativePath/> + <ID>0</ID> + <Bidirectional>false</Bidirectional> + <Synchronous>false</Synchronous> + <MechanicalAction>Switch When Pressed</MechanicalAction> + <AccessMayTimeout>false</AccessMayTimeout> + <RegisterNode>false</RegisterNode> + <SubControlList/> + </Register> + <Register> + <Name>DiagramReset</Name> + <Hidden>false</Hidden> + <Indicator>false</Indicator> + <Datatype> + <U32> + <Name/> + </U32> + </Datatype> + <FlattenedType/> + <Grouping/> + <Offset>262140</Offset> + <SizeInBits>32</SizeInBits> + <Class>0</Class> + <Internal>true</Internal> + <TypedefPath/> + <TypedefRelativePath/> + <ID>0</ID> + <Bidirectional>true</Bidirectional> + <Synchronous>false</Synchronous> + <MechanicalAction>Switch When Pressed</MechanicalAction> + <AccessMayTimeout>false</AccessMayTimeout> + <RegisterNode>false</RegisterNode> + <SubControlList/> + </Register> + <Register> + <Name>ViControl</Name> + <Hidden>false</Hidden> + <Indicator>false</Indicator> + <Datatype> + <U32> + <Name/> + </U32> + </Datatype> + <FlattenedType/> + <Grouping/> + <Offset>262136</Offset> + <SizeInBits>32</SizeInBits> + <Class>0</Class> + <Internal>true</Internal> + <TypedefPath/> + <TypedefRelativePath/> + <ID>0</ID> + <Bidirectional>true</Bidirectional> + <Synchronous>false</Synchronous> + <MechanicalAction>Switch When Pressed</MechanicalAction> + <AccessMayTimeout>false</AccessMayTimeout> + <RegisterNode>false</RegisterNode> + <SubControlList/> + </Register> + <Register> + <Name>InterruptEnable</Name> + <Hidden>false</Hidden> + <Indicator>false</Indicator> + <Datatype> + <U32> + <Name/> + </U32> + </Datatype> + <FlattenedType/> + <Grouping/> + <Offset>262116</Offset> + <SizeInBits>32</SizeInBits> + <Class>0</Class> + <Internal>true</Internal> + <TypedefPath/> + <TypedefRelativePath/> + <ID>0</ID> + <Bidirectional>true</Bidirectional> + <Synchronous>false</Synchronous> + <MechanicalAction>Switch When Pressed</MechanicalAction> + <AccessMayTimeout>false</AccessMayTimeout> + <RegisterNode>false</RegisterNode> + <SubControlList/> + </Register> + <Register> + <Name>InterruptMask</Name> + <Hidden>false</Hidden> + <Indicator>false</Indicator> + <Datatype> + <U32> + <Name/> + </U32> + </Datatype> + <FlattenedType/> + <Grouping/> + <Offset>262124</Offset> + <SizeInBits>32</SizeInBits> + <Class>0</Class> + <Internal>true</Internal> + <TypedefPath/> + <TypedefRelativePath/> + <ID>0</ID> + <Bidirectional>true</Bidirectional> + <Synchronous>false</Synchronous> + <MechanicalAction>Switch When Pressed</MechanicalAction> + <AccessMayTimeout>false</AccessMayTimeout> + <RegisterNode>false</RegisterNode> + <SubControlList/> + </Register> + <Register> + <Name>InterruptStatus</Name> + <Hidden>false</Hidden> + <Indicator>false</Indicator> + <Datatype> + <U32> + <Name/> + </U32> + </Datatype> + <FlattenedType/> + <Grouping/> + <Offset>262128</Offset> + <SizeInBits>32</SizeInBits> + <Class>0</Class> + <Internal>true</Internal> + <TypedefPath/> + <TypedefRelativePath/> + <ID>0</ID> + <Bidirectional>true</Bidirectional> + <Synchronous>false</Synchronous> + <MechanicalAction>Switch When Pressed</MechanicalAction> + <AccessMayTimeout>false</AccessMayTimeout> + <RegisterNode>false</RegisterNode> + <SubControlList/> + </Register> + </RegisterList> + <Icon> + <ImageType>0</ImageType> + <ImageDepth>8</ImageDepth> + <Image>////////////////////////////////////////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//wAA/////////////////////////wAAAAAAAAAAAP//AAD/+fn5+fn5+fn5+fn59/ks+SzgAAAAAAAAAAAA//8AAP/5///////////////3+Sz5LP8AAAAAAAAAAAD//wAA//n/6OTo////6OTo//f8K/ws/wAAAAAAAAAAAP//AAD/+f/k/+T////k/+T/9ywsLCzgAAAAAAAAAAAA//8AAP/56OT/5Oj/6OT/5Oj3K/wrLP8AAAAAAAAAAAD//wAA//nk6P/o5P/k6P/o5Pf8CPws/wAAAAAAAAAAAP//AAD/+eT////k/+T////k9/wI/Cz/AAAAAAAAAAAA//8AAP/5/////+jk6P/////3K/wrLP8AAAAAAAAAAAD//wAA//n///////////////csLCws/wAAAAAAAAAAAP//AAD/9/f39/f39/f39/f39ywsg4P/AAAAAAAAAAAA//8AAP8sLCwsLCwsLCwsLCwsLCyDBYODAAAAAAAAAAD//wAA/yz8LCwsLCz8LCwsLCMjI4MFBQWDgwAAAAAAAP//AAD//PD8LCws/CP8LCMjLCwsgwUF/wUFg4MAAAAA//8AAP8s7ywsLCwjLCwjLCwsLCyDBf///wUFBYMjIwD//wAA///w////I///I////////4MFBf8FBYODAAAAAP//AAAAAO8AAAAjAAAjAADw7+/wgwUFBYODAAAAAAAA//8AAAAAAPAAAAAjIwAA7wAAAACDBYODAAAAAAAAAAD//wAAAAAAAO/vAAAAAPAAAAAAAIODAAAAAAAAAAAAAP//AAAAAAAAAADw7/DvAAAAAAAAAAAAAAAAAP8AAAAA//8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//wAAAAD//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAAAAP//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8AAAAA//8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wAAAAD//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAAAAP//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8AAAAA//8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///////////////////////////////////////////w==</Image> + <Mask>//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////8=</Mask> + <Colors>AP///wD//8wA//+ZAP//ZgD//zMA//8AAP/M/wD/zMwA/8yZAP/MZgD/zDMA/8wAAP+Z/wD/mcwA/5mZAP+ZZgD/mTMA/5kAAP9m/wD/ZswA/2aZAP9mZgD/ZjMA/2YAAP8z/wD/M8wA/zOZAP8zZgD/MzMA/zMAAP8A/wD/AMwA/wCZAP8AZgD/ADMA/wAAAMz//wDM/8wAzP+ZAMz/ZgDM/zMAzP8AAMzM/wDMzMwAzMyZAMzMZgDMzDMAzMwAAMyZ/wDMmcwAzJmZAMyZZgDMmTMAzJkAAMxm/wDMZswAzGaZAMxmZgDMZjMAzGYAAMwz/wDMM8wAzDOZAMwzZgDMMzMAzDMAAMwA/wDMAMwAzACZAMwAZgDMADMAzAAAAJn//wCZ/8wAmf+ZAJn/ZgCZ/zMAmf8AAJnM/wCZzMwAmcyZAJnMZgCZzDMAmcwAAJmZ/wCZmcwAmZmZAJmZZgCZmTMAmZkAAJlm/wCZZswAmWaZAJlmZgCZZjMAmWYAAJkz/wCZM8wAmTOZAJkzZgCZMzMAmTMAAJkA/wCZAMwAmQCZAJkAZgCZADMAmQAAAGb//wBm/8wAZv+ZAGb/ZgBm/zMAZv8AAGbM/wBmzMwAZsyZAGbMZgBmzDMAZswAAGaZ/wBmmcwAZpmZAGaZZgBmmTMAZpkAAGZm/wBmZswAZmaZAGZmZgBmZjMAZmYAAGYz/wBmM8wAZjOZAGYzZgBmMzMAZjMAAGYA/wBmAMwAZgCZAGYAZgBmADMAZgAAADP//wAz/8wAM/+ZADP/ZgAz/zMAM/8AADPM/wAzzMwAM8yZADPMZgAzzDMAM8wAADOZ/wAzmcwAM5mZADOZZgAzmTMAM5kAADNm/wAzZswAM2aZADNmZgAzZjMAM2YAADMz/wAzM8wAMzOZADMzZgAzMzMAMzMAADMA/wAzAMwAMwCZADMAZgAzADMAMwAAAAD//wAA/8wAAP+ZAAD/ZgAA/zMAAP8AAADM/wAAzMwAAMyZAADMZgAAzDMAAMwAAACZ/wAAmcwAAJmZAACZZgAAmTMAAJkAAABm/wAAZswAAGaZAABmZgAAZjMAAGYAAAAz/wAAM8wAADOZAAAzZgAAMzMAADMAAAAA/wAAAMwAAACZAAAAZgAAADMA7gAAAN0AAAC7AAAAqgAAAIgAAAB3AAAAVQAAAEQAAAAiAAAAEQAAAADuAAAA3QAAALsAAACqAAAAiAAAAHcAAABVAAAARAAAACIAAAARAAAAAO4AAADdAAAAuwAAAKoAAACIAAAAdwAAAFUAAABEAAAAIgAAABEA7u7uAN3d3QC7u7sAqqqqAIiIiAB3d3cAVVVVAERERAAiIiIAERERAAAAAA==</Colors> + <Rectangle> + <Left>0</Left> + <Top>0</Top> + <Right>32</Right> + <Bottom>32</Bottom> + </Rectangle> + </Icon> + </VI> + <Project> + <TargetClass>294XR; 295XR</TargetClass> + <AutoRunWhenDownloaded>false</AutoRunWhenDownloaded> + <CompilationResultsTree> + <CompilationResults> + <NiFpga> + <BaseAddressOnDevice>0</BaseAddressOnDevice> + <DmaChannelAllocationList> + <Channel name="RX FIFO 0"> + <BaseAddressTag>NiLvFpgaFIFO 0</BaseAddressTag> + <ControlSet>0</ControlSet> + <DataType> + <Delta>1.000000000000000000000000000000000000000000000000000000</Delta> + <IntegerWordLength>64</IntegerWordLength> + <Maximum>18446744073709551600.00000000000000000000000000000000000</Maximum> + <Minimum>0.000000000000000000000000000000000000000000000000000000</Minimum> + <Signed>false</Signed> + <SubType>U64</SubType> + <WordLength>64</WordLength> + </DataType> + <Direction>TargetToHost</Direction> + <Implementation>niFpgaTargetToHost</Implementation> + <Number>0</Number> + <NumberOfElements>1023</NumberOfElements> + <UserVisible>true</UserVisible> + </Channel> + <Channel name="RX FIFO 1"> + <BaseAddressTag>NiLvFpgaFIFO 1</BaseAddressTag> + <ControlSet>1</ControlSet> + <DataType> + <Delta>1.000000000000000000000000000000000000000000000000000000</Delta> + <IntegerWordLength>64</IntegerWordLength> + <Maximum>18446744073709551600.00000000000000000000000000000000000</Maximum> + <Minimum>0.000000000000000000000000000000000000000000000000000000</Minimum> + <Signed>false</Signed> + <SubType>U64</SubType> + <WordLength>64</WordLength> + </DataType> + <Direction>TargetToHost</Direction> + <Implementation>niFpgaTargetToHost</Implementation> + <Number>1</Number> + <NumberOfElements>1023</NumberOfElements> + <UserVisible>true</UserVisible> + </Channel> + <Channel name="RX FIFO 2"> + <BaseAddressTag>NiLvFpgaFIFO 2</BaseAddressTag> + <ControlSet>2</ControlSet> + <DataType> + <Delta>1.000000000000000000000000000000000000000000000000000000</Delta> + <IntegerWordLength>64</IntegerWordLength> + <Maximum>18446744073709551600.00000000000000000000000000000000000</Maximum> + <Minimum>0.000000000000000000000000000000000000000000000000000000</Minimum> + <Signed>false</Signed> + <SubType>U64</SubType> + <WordLength>64</WordLength> + </DataType> + <Direction>TargetToHost</Direction> + <Implementation>niFpgaTargetToHost</Implementation> + <Number>2</Number> + <NumberOfElements>1023</NumberOfElements> + <UserVisible>true</UserVisible> + </Channel> + <Channel name="RX FIFO 3"> + <BaseAddressTag>NiLvFpgaFIFO 3</BaseAddressTag> + <ControlSet>3</ControlSet> + <DataType> + <Delta>1.000000000000000000000000000000000000000000000000000000</Delta> + <IntegerWordLength>64</IntegerWordLength> + <Maximum>18446744073709551600.00000000000000000000000000000000000</Maximum> + <Minimum>0.000000000000000000000000000000000000000000000000000000</Minimum> + <Signed>false</Signed> + <SubType>U64</SubType> + <WordLength>64</WordLength> + </DataType> + <Direction>TargetToHost</Direction> + <Implementation>niFpgaTargetToHost</Implementation> + <Number>3</Number> + <NumberOfElements>1023</NumberOfElements> + <UserVisible>true</UserVisible> + </Channel> + <Channel name="RX FIFO 4"> + <BaseAddressTag>NiLvFpgaFIFO 4</BaseAddressTag> + <ControlSet>4</ControlSet> + <DataType> + <Delta>1.000000000000000000000000000000000000000000000000000000</Delta> + <IntegerWordLength>64</IntegerWordLength> + <Maximum>18446744073709551600.00000000000000000000000000000000000</Maximum> + <Minimum>0.000000000000000000000000000000000000000000000000000000</Minimum> + <Signed>false</Signed> + <SubType>U64</SubType> + <WordLength>64</WordLength> + </DataType> + <Direction>TargetToHost</Direction> + <Implementation>niFpgaTargetToHost</Implementation> + <Number>4</Number> + <NumberOfElements>1023</NumberOfElements> + <UserVisible>true</UserVisible> + </Channel> + <Channel name="RX FIFO 5"> + <BaseAddressTag>NiLvFpgaFIFO 5</BaseAddressTag> + <ControlSet>5</ControlSet> + <DataType> + <Delta>1.000000000000000000000000000000000000000000000000000000</Delta> + <IntegerWordLength>64</IntegerWordLength> + <Maximum>18446744073709551600.00000000000000000000000000000000000</Maximum> + <Minimum>0.000000000000000000000000000000000000000000000000000000</Minimum> + <Signed>false</Signed> + <SubType>U64</SubType> + <WordLength>64</WordLength> + </DataType> + <Direction>TargetToHost</Direction> + <Implementation>niFpgaTargetToHost</Implementation> + <Number>5</Number> + <NumberOfElements>1023</NumberOfElements> + <UserVisible>true</UserVisible> + </Channel> + <Channel name="TX FIFO 0"> + <BaseAddressTag>NiLvFpgaFIFO 6</BaseAddressTag> + <ControlSet>6</ControlSet> + <DataType> + <Delta>1.000000000000000000000000000000000000000000000000000000</Delta> + <IntegerWordLength>64</IntegerWordLength> + <Maximum>18446744073709551600.00000000000000000000000000000000000</Maximum> + <Minimum>0.000000000000000000000000000000000000000000000000000000</Minimum> + <Signed>false</Signed> + <SubType>U64</SubType> + <WordLength>64</WordLength> + </DataType> + <Direction>HostToTarget</Direction> + <Implementation>niFpgaHostToTarget</Implementation> + <Number>6</Number> + <NumberOfElements>1029</NumberOfElements> + <UserVisible>true</UserVisible> + </Channel> + <Channel name="TX FIFO 1"> + <BaseAddressTag>NiLvFpgaFIFO 7</BaseAddressTag> + <ControlSet>7</ControlSet> + <DataType> + <Delta>1.000000000000000000000000000000000000000000000000000000</Delta> + <IntegerWordLength>64</IntegerWordLength> + <Maximum>18446744073709551600.00000000000000000000000000000000000</Maximum> + <Minimum>0.000000000000000000000000000000000000000000000000000000</Minimum> + <Signed>false</Signed> + <SubType>U64</SubType> + <WordLength>64</WordLength> + </DataType> + <Direction>HostToTarget</Direction> + <Implementation>niFpgaHostToTarget</Implementation> + <Number>7</Number> + <NumberOfElements>1029</NumberOfElements> + <UserVisible>true</UserVisible> + </Channel> + <Channel name="TX FIFO 2"> + <BaseAddressTag>NiLvFpgaFIFO 8</BaseAddressTag> + <ControlSet>8</ControlSet> + <DataType> + <Delta>1.000000000000000000000000000000000000000000000000000000</Delta> + <IntegerWordLength>64</IntegerWordLength> + <Maximum>18446744073709551600.00000000000000000000000000000000000</Maximum> + <Minimum>0.000000000000000000000000000000000000000000000000000000</Minimum> + <Signed>false</Signed> + <SubType>U64</SubType> + <WordLength>64</WordLength> + </DataType> + <Direction>HostToTarget</Direction> + <Implementation>niFpgaHostToTarget</Implementation> + <Number>8</Number> + <NumberOfElements>1029</NumberOfElements> + <UserVisible>true</UserVisible> + </Channel> + <Channel name="TX FIFO 3"> + <BaseAddressTag>NiLvFpgaFIFO 9</BaseAddressTag> + <ControlSet>9</ControlSet> + <DataType> + <Delta>1.000000000000000000000000000000000000000000000000000000</Delta> + <IntegerWordLength>64</IntegerWordLength> + <Maximum>18446744073709551600.00000000000000000000000000000000000</Maximum> + <Minimum>0.000000000000000000000000000000000000000000000000000000</Minimum> + <Signed>false</Signed> + <SubType>U64</SubType> + <WordLength>64</WordLength> + </DataType> + <Direction>HostToTarget</Direction> + <Implementation>niFpgaHostToTarget</Implementation> + <Number>9</Number> + <NumberOfElements>1029</NumberOfElements> + <UserVisible>true</UserVisible> + </Channel> + <Channel name="TX FIFO 4"> + <BaseAddressTag>NiLvFpgaFIFO 10</BaseAddressTag> + <ControlSet>10</ControlSet> + <DataType> + <Delta>1.000000000000000000000000000000000000000000000000000000</Delta> + <IntegerWordLength>64</IntegerWordLength> + <Maximum>18446744073709551600.00000000000000000000000000000000000</Maximum> + <Minimum>0.000000000000000000000000000000000000000000000000000000</Minimum> + <Signed>false</Signed> + <SubType>U64</SubType> + <WordLength>64</WordLength> + </DataType> + <Direction>HostToTarget</Direction> + <Implementation>niFpgaHostToTarget</Implementation> + <Number>10</Number> + <NumberOfElements>1029</NumberOfElements> + <UserVisible>true</UserVisible> + </Channel> + <Channel name="TX FIFO 5"> + <BaseAddressTag>NiLvFpgaFIFO 11</BaseAddressTag> + <ControlSet>11</ControlSet> + <DataType> + <Delta>1.000000000000000000000000000000000000000000000000000000</Delta> + <IntegerWordLength>64</IntegerWordLength> + <Maximum>18446744073709551600.00000000000000000000000000000000000</Maximum> + <Minimum>0.000000000000000000000000000000000000000000000000000000</Minimum> + <Signed>false</Signed> + <SubType>U64</SubType> + <WordLength>64</WordLength> + </DataType> + <Direction>HostToTarget</Direction> + <Implementation>niFpgaHostToTarget</Implementation> + <Number>11</Number> + <NumberOfElements>1029</NumberOfElements> + <UserVisible>true</UserVisible> + </Channel> + </DmaChannelAllocationList> + <RegisterBlockList> + <RegisterBlock name="NiLvFpgaFIFO 0"> + <Offset>0xFF80</Offset> + </RegisterBlock> + <RegisterBlock name="NiLvFpgaFIFO 1"> + <Offset>0xFF40</Offset> + </RegisterBlock> + <RegisterBlock name="NiLvFpgaFIFO 2"> + <Offset>0xFF00</Offset> + </RegisterBlock> + <RegisterBlock name="NiLvFpgaFIFO 3"> + <Offset>0xFEC0</Offset> + </RegisterBlock> + <RegisterBlock name="NiLvFpgaFIFO 4"> + <Offset>0xFE80</Offset> + </RegisterBlock> + <RegisterBlock name="NiLvFpgaFIFO 5"> + <Offset>0xFE40</Offset> + </RegisterBlock> + <RegisterBlock name="NiLvFpgaFIFO 6"> + <Offset>0xFE00</Offset> + </RegisterBlock> + <RegisterBlock name="NiLvFpgaFIFO 7"> + <Offset>0xFDC0</Offset> + </RegisterBlock> + <RegisterBlock name="NiLvFpgaFIFO 8"> + <Offset>0xFD80</Offset> + </RegisterBlock> + <RegisterBlock name="NiLvFpgaFIFO 9"> + <Offset>0xFD40</Offset> + </RegisterBlock> + <RegisterBlock name="NiLvFpgaFIFO 10"> + <Offset>0xFD00</Offset> + </RegisterBlock> + <RegisterBlock name="NiLvFpgaFIFO 11"> + <Offset>0xFCC0</Offset> + </RegisterBlock> + </RegisterBlockList> + <UsedBaseClockList> + <BaseClock name="ReliableClkIn"> + </BaseClock> + <BaseClock name="ChinchClk"> + </BaseClock> + <BaseClock name="40 MHz Onboard Clock"> + </BaseClock> + </UsedBaseClockList> + <version>1</version> + </NiFpga> + </CompilationResults> + </CompilationResultsTree> + <MultipleUserClocks>false</MultipleUserClocks> + <AllowImplicitEnableRemoval>false</AllowImplicitEnableRemoval> + </Project> + <ClientData/> + <BitstreamMD5>a72ba1716893a0bd02f88dac3ab28e1b</BitstreamMD5> + <Bitstream>a72ba1716893a0bd02f88dac3ab28e1b</Bitstream> +</Bitfile> diff --git a/host/lib/transport/nirio/lvbitx/x310.lvbitx_base b/host/lib/transport/nirio/lvbitx/x310.lvbitx_base new file mode 100755 index 000000000..c264e7157 --- /dev/null +++ b/host/lib/transport/nirio/lvbitx/x310.lvbitx_base @@ -0,0 +1,469 @@ +<?xml version="1.0" encoding="utf-8"?> +<Bitfile> + <BitfileVersion>4.0</BitfileVersion> + <Documentation> + <BuildSpecVersion/> + <BuildSpecDescription/> + </Documentation> + <SignatureRegister>97C6D9F4F4829001B83378F93CAB0C94</SignatureRegister> + <SignatureGuids>7BAD6AEB9741248079F13147B3F8AD94</SignatureGuids> + <SignatureNames>AE54C47F787D92DB46F7DC973338D786</SignatureNames> + <TimeStamp/> + <CompilationStatus/> + <BitstreamVersion>2</BitstreamVersion> + <VI> + <Name>USRP_X3x0_Top.vi</Name> + <RegisterList> + <Register> + <Name>ViSignature</Name> + <Hidden>true</Hidden> + <Indicator>true</Indicator> + <Datatype> + <Array> + <Name/> + <Size>4</Size> + <Type> + <U32> + <Name/> + </U32> + </Type> + </Array> + </Datatype> + <FlattenedType/> + <Grouping/> + <Offset>262132</Offset> + <SizeInBits>128</SizeInBits> + <Class>0</Class> + <Internal>true</Internal> + <TypedefPath/> + <TypedefRelativePath/> + <ID>0</ID> + <Bidirectional>false</Bidirectional> + <Synchronous>false</Synchronous> + <MechanicalAction>Switch When Pressed</MechanicalAction> + <AccessMayTimeout>false</AccessMayTimeout> + <RegisterNode>false</RegisterNode> + <SubControlList/> + </Register> + <Register> + <Name>DiagramReset</Name> + <Hidden>false</Hidden> + <Indicator>false</Indicator> + <Datatype> + <U32> + <Name/> + </U32> + </Datatype> + <FlattenedType/> + <Grouping/> + <Offset>262140</Offset> + <SizeInBits>32</SizeInBits> + <Class>0</Class> + <Internal>true</Internal> + <TypedefPath/> + <TypedefRelativePath/> + <ID>0</ID> + <Bidirectional>true</Bidirectional> + <Synchronous>false</Synchronous> + <MechanicalAction>Switch When Pressed</MechanicalAction> + <AccessMayTimeout>false</AccessMayTimeout> + <RegisterNode>false</RegisterNode> + <SubControlList/> + </Register> + <Register> + <Name>ViControl</Name> + <Hidden>false</Hidden> + <Indicator>false</Indicator> + <Datatype> + <U32> + <Name/> + </U32> + </Datatype> + <FlattenedType/> + <Grouping/> + <Offset>262136</Offset> + <SizeInBits>32</SizeInBits> + <Class>0</Class> + <Internal>true</Internal> + <TypedefPath/> + <TypedefRelativePath/> + <ID>0</ID> + <Bidirectional>true</Bidirectional> + <Synchronous>false</Synchronous> + <MechanicalAction>Switch When Pressed</MechanicalAction> + <AccessMayTimeout>false</AccessMayTimeout> + <RegisterNode>false</RegisterNode> + <SubControlList/> + </Register> + <Register> + <Name>InterruptEnable</Name> + <Hidden>false</Hidden> + <Indicator>false</Indicator> + <Datatype> + <U32> + <Name/> + </U32> + </Datatype> + <FlattenedType/> + <Grouping/> + <Offset>262116</Offset> + <SizeInBits>32</SizeInBits> + <Class>0</Class> + <Internal>true</Internal> + <TypedefPath/> + <TypedefRelativePath/> + <ID>0</ID> + <Bidirectional>true</Bidirectional> + <Synchronous>false</Synchronous> + <MechanicalAction>Switch When Pressed</MechanicalAction> + <AccessMayTimeout>false</AccessMayTimeout> + <RegisterNode>false</RegisterNode> + <SubControlList/> + </Register> + <Register> + <Name>InterruptMask</Name> + <Hidden>false</Hidden> + <Indicator>false</Indicator> + <Datatype> + <U32> + <Name/> + </U32> + </Datatype> + <FlattenedType/> + <Grouping/> + <Offset>262124</Offset> + <SizeInBits>32</SizeInBits> + <Class>0</Class> + <Internal>true</Internal> + <TypedefPath/> + <TypedefRelativePath/> + <ID>0</ID> + <Bidirectional>true</Bidirectional> + <Synchronous>false</Synchronous> + <MechanicalAction>Switch When Pressed</MechanicalAction> + <AccessMayTimeout>false</AccessMayTimeout> + <RegisterNode>false</RegisterNode> + <SubControlList/> + </Register> + <Register> + <Name>InterruptStatus</Name> + <Hidden>false</Hidden> + <Indicator>false</Indicator> + <Datatype> + <U32> + <Name/> + </U32> + </Datatype> + <FlattenedType/> + <Grouping/> + <Offset>262128</Offset> + <SizeInBits>32</SizeInBits> + <Class>0</Class> + <Internal>true</Internal> + <TypedefPath/> + <TypedefRelativePath/> + <ID>0</ID> + <Bidirectional>true</Bidirectional> + <Synchronous>false</Synchronous> + <MechanicalAction>Switch When Pressed</MechanicalAction> + <AccessMayTimeout>false</AccessMayTimeout> + <RegisterNode>false</RegisterNode> + <SubControlList/> + </Register> + </RegisterList> + <Icon> + <ImageType>0</ImageType> + <ImageDepth>8</ImageDepth> + <Image>////////////////////////////////////////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//wAA/////////////////////////wAAAAAAAAAAAP//AAD/+fn5+fn5+fn5+fn59/ks+SzgAAAAAAAAAAAA//8AAP/5///////////////3+Sz5LP8AAAAAAAAAAAD//wAA//n/6OTo////6OTo//f8K/ws/wAAAAAAAAAAAP//AAD/+f/k/+T////k/+T/9ywsLCzgAAAAAAAAAAAA//8AAP/56OT/5Oj/6OT/5Oj3K/wrLP8AAAAAAAAAAAD//wAA//nk6P/o5P/k6P/o5Pf8CPws/wAAAAAAAAAAAP//AAD/+eT////k/+T////k9/wI/Cz/AAAAAAAAAAAA//8AAP/5/////+jk6P/////3K/wrLP8AAAAAAAAAAAD//wAA//n///////////////csLCws/wAAAAAAAAAAAP//AAD/9/f39/f39/f39/f39ywsg4P/AAAAAAAAAAAA//8AAP8sLCwsLCwsLCwsLCwsLCyDBYODAAAAAAAAAAD//wAA/yz8LCwsLCz8LCwsLCMjI4MFBQWDgwAAAAAAAP//AAD//PD8LCws/CP8LCMjLCwsgwUF/wUFg4MAAAAA//8AAP8s7ywsLCwjLCwjLCwsLCyDBf///wUFBYMjIwD//wAA///w////I///I////////4MFBf8FBYODAAAAAP//AAAAAO8AAAAjAAAjAADw7+/wgwUFBYODAAAAAAAA//8AAAAAAPAAAAAjIwAA7wAAAACDBYODAAAAAAAAAAD//wAAAAAAAO/vAAAAAPAAAAAAAIODAAAAAAAAAAAAAP//AAAAAAAAAADw7/DvAAAAAAAAAAAAAAAAAP8AAAAA//8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//wAAAAD//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAAAAP//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8AAAAA//8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wAAAAD//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAAAAP//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8AAAAA//8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///////////////////////////////////////////w==</Image> + <Mask>//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////8=</Mask> + <Colors>AP///wD//8wA//+ZAP//ZgD//zMA//8AAP/M/wD/zMwA/8yZAP/MZgD/zDMA/8wAAP+Z/wD/mcwA/5mZAP+ZZgD/mTMA/5kAAP9m/wD/ZswA/2aZAP9mZgD/ZjMA/2YAAP8z/wD/M8wA/zOZAP8zZgD/MzMA/zMAAP8A/wD/AMwA/wCZAP8AZgD/ADMA/wAAAMz//wDM/8wAzP+ZAMz/ZgDM/zMAzP8AAMzM/wDMzMwAzMyZAMzMZgDMzDMAzMwAAMyZ/wDMmcwAzJmZAMyZZgDMmTMAzJkAAMxm/wDMZswAzGaZAMxmZgDMZjMAzGYAAMwz/wDMM8wAzDOZAMwzZgDMMzMAzDMAAMwA/wDMAMwAzACZAMwAZgDMADMAzAAAAJn//wCZ/8wAmf+ZAJn/ZgCZ/zMAmf8AAJnM/wCZzMwAmcyZAJnMZgCZzDMAmcwAAJmZ/wCZmcwAmZmZAJmZZgCZmTMAmZkAAJlm/wCZZswAmWaZAJlmZgCZZjMAmWYAAJkz/wCZM8wAmTOZAJkzZgCZMzMAmTMAAJkA/wCZAMwAmQCZAJkAZgCZADMAmQAAAGb//wBm/8wAZv+ZAGb/ZgBm/zMAZv8AAGbM/wBmzMwAZsyZAGbMZgBmzDMAZswAAGaZ/wBmmcwAZpmZAGaZZgBmmTMAZpkAAGZm/wBmZswAZmaZAGZmZgBmZjMAZmYAAGYz/wBmM8wAZjOZAGYzZgBmMzMAZjMAAGYA/wBmAMwAZgCZAGYAZgBmADMAZgAAADP//wAz/8wAM/+ZADP/ZgAz/zMAM/8AADPM/wAzzMwAM8yZADPMZgAzzDMAM8wAADOZ/wAzmcwAM5mZADOZZgAzmTMAM5kAADNm/wAzZswAM2aZADNmZgAzZjMAM2YAADMz/wAzM8wAMzOZADMzZgAzMzMAMzMAADMA/wAzAMwAMwCZADMAZgAzADMAMwAAAAD//wAA/8wAAP+ZAAD/ZgAA/zMAAP8AAADM/wAAzMwAAMyZAADMZgAAzDMAAMwAAACZ/wAAmcwAAJmZAACZZgAAmTMAAJkAAABm/wAAZswAAGaZAABmZgAAZjMAAGYAAAAz/wAAM8wAADOZAAAzZgAAMzMAADMAAAAA/wAAAMwAAACZAAAAZgAAADMA7gAAAN0AAAC7AAAAqgAAAIgAAAB3AAAAVQAAAEQAAAAiAAAAEQAAAADuAAAA3QAAALsAAACqAAAAiAAAAHcAAABVAAAARAAAACIAAAARAAAAAO4AAADdAAAAuwAAAKoAAACIAAAAdwAAAFUAAABEAAAAIgAAABEA7u7uAN3d3QC7u7sAqqqqAIiIiAB3d3cAVVVVAERERAAiIiIAERERAAAAAA==</Colors> + <Rectangle> + <Left>0</Left> + <Top>0</Top> + <Right>32</Right> + <Bottom>32</Bottom> + </Rectangle> + </Icon> + </VI> + <Project> + <TargetClass>294XR; 295XR</TargetClass> + <AutoRunWhenDownloaded>false</AutoRunWhenDownloaded> + <CompilationResultsTree> + <CompilationResults> + <NiFpga> + <BaseAddressOnDevice>0</BaseAddressOnDevice> + <DmaChannelAllocationList> + <Channel name="RX FIFO 0"> + <BaseAddressTag>NiLvFpgaFIFO 0</BaseAddressTag> + <ControlSet>0</ControlSet> + <DataType> + <Delta>1.000000000000000000000000000000000000000000000000000000</Delta> + <IntegerWordLength>64</IntegerWordLength> + <Maximum>18446744073709551600.00000000000000000000000000000000000</Maximum> + <Minimum>0.000000000000000000000000000000000000000000000000000000</Minimum> + <Signed>false</Signed> + <SubType>U64</SubType> + <WordLength>64</WordLength> + </DataType> + <Direction>TargetToHost</Direction> + <Implementation>niFpgaTargetToHost</Implementation> + <Number>0</Number> + <NumberOfElements>1023</NumberOfElements> + <UserVisible>true</UserVisible> + </Channel> + <Channel name="RX FIFO 1"> + <BaseAddressTag>NiLvFpgaFIFO 1</BaseAddressTag> + <ControlSet>1</ControlSet> + <DataType> + <Delta>1.000000000000000000000000000000000000000000000000000000</Delta> + <IntegerWordLength>64</IntegerWordLength> + <Maximum>18446744073709551600.00000000000000000000000000000000000</Maximum> + <Minimum>0.000000000000000000000000000000000000000000000000000000</Minimum> + <Signed>false</Signed> + <SubType>U64</SubType> + <WordLength>64</WordLength> + </DataType> + <Direction>TargetToHost</Direction> + <Implementation>niFpgaTargetToHost</Implementation> + <Number>1</Number> + <NumberOfElements>1023</NumberOfElements> + <UserVisible>true</UserVisible> + </Channel> + <Channel name="RX FIFO 2"> + <BaseAddressTag>NiLvFpgaFIFO 2</BaseAddressTag> + <ControlSet>2</ControlSet> + <DataType> + <Delta>1.000000000000000000000000000000000000000000000000000000</Delta> + <IntegerWordLength>64</IntegerWordLength> + <Maximum>18446744073709551600.00000000000000000000000000000000000</Maximum> + <Minimum>0.000000000000000000000000000000000000000000000000000000</Minimum> + <Signed>false</Signed> + <SubType>U64</SubType> + <WordLength>64</WordLength> + </DataType> + <Direction>TargetToHost</Direction> + <Implementation>niFpgaTargetToHost</Implementation> + <Number>2</Number> + <NumberOfElements>1023</NumberOfElements> + <UserVisible>true</UserVisible> + </Channel> + <Channel name="RX FIFO 3"> + <BaseAddressTag>NiLvFpgaFIFO 3</BaseAddressTag> + <ControlSet>3</ControlSet> + <DataType> + <Delta>1.000000000000000000000000000000000000000000000000000000</Delta> + <IntegerWordLength>64</IntegerWordLength> + <Maximum>18446744073709551600.00000000000000000000000000000000000</Maximum> + <Minimum>0.000000000000000000000000000000000000000000000000000000</Minimum> + <Signed>false</Signed> + <SubType>U64</SubType> + <WordLength>64</WordLength> + </DataType> + <Direction>TargetToHost</Direction> + <Implementation>niFpgaTargetToHost</Implementation> + <Number>3</Number> + <NumberOfElements>1023</NumberOfElements> + <UserVisible>true</UserVisible> + </Channel> + <Channel name="RX FIFO 4"> + <BaseAddressTag>NiLvFpgaFIFO 4</BaseAddressTag> + <ControlSet>4</ControlSet> + <DataType> + <Delta>1.000000000000000000000000000000000000000000000000000000</Delta> + <IntegerWordLength>64</IntegerWordLength> + <Maximum>18446744073709551600.00000000000000000000000000000000000</Maximum> + <Minimum>0.000000000000000000000000000000000000000000000000000000</Minimum> + <Signed>false</Signed> + <SubType>U64</SubType> + <WordLength>64</WordLength> + </DataType> + <Direction>TargetToHost</Direction> + <Implementation>niFpgaTargetToHost</Implementation> + <Number>4</Number> + <NumberOfElements>1023</NumberOfElements> + <UserVisible>true</UserVisible> + </Channel> + <Channel name="RX FIFO 5"> + <BaseAddressTag>NiLvFpgaFIFO 5</BaseAddressTag> + <ControlSet>5</ControlSet> + <DataType> + <Delta>1.000000000000000000000000000000000000000000000000000000</Delta> + <IntegerWordLength>64</IntegerWordLength> + <Maximum>18446744073709551600.00000000000000000000000000000000000</Maximum> + <Minimum>0.000000000000000000000000000000000000000000000000000000</Minimum> + <Signed>false</Signed> + <SubType>U64</SubType> + <WordLength>64</WordLength> + </DataType> + <Direction>TargetToHost</Direction> + <Implementation>niFpgaTargetToHost</Implementation> + <Number>5</Number> + <NumberOfElements>1023</NumberOfElements> + <UserVisible>true</UserVisible> + </Channel> + <Channel name="TX FIFO 0"> + <BaseAddressTag>NiLvFpgaFIFO 6</BaseAddressTag> + <ControlSet>6</ControlSet> + <DataType> + <Delta>1.000000000000000000000000000000000000000000000000000000</Delta> + <IntegerWordLength>64</IntegerWordLength> + <Maximum>18446744073709551600.00000000000000000000000000000000000</Maximum> + <Minimum>0.000000000000000000000000000000000000000000000000000000</Minimum> + <Signed>false</Signed> + <SubType>U64</SubType> + <WordLength>64</WordLength> + </DataType> + <Direction>HostToTarget</Direction> + <Implementation>niFpgaHostToTarget</Implementation> + <Number>6</Number> + <NumberOfElements>1029</NumberOfElements> + <UserVisible>true</UserVisible> + </Channel> + <Channel name="TX FIFO 1"> + <BaseAddressTag>NiLvFpgaFIFO 7</BaseAddressTag> + <ControlSet>7</ControlSet> + <DataType> + <Delta>1.000000000000000000000000000000000000000000000000000000</Delta> + <IntegerWordLength>64</IntegerWordLength> + <Maximum>18446744073709551600.00000000000000000000000000000000000</Maximum> + <Minimum>0.000000000000000000000000000000000000000000000000000000</Minimum> + <Signed>false</Signed> + <SubType>U64</SubType> + <WordLength>64</WordLength> + </DataType> + <Direction>HostToTarget</Direction> + <Implementation>niFpgaHostToTarget</Implementation> + <Number>7</Number> + <NumberOfElements>1029</NumberOfElements> + <UserVisible>true</UserVisible> + </Channel> + <Channel name="TX FIFO 2"> + <BaseAddressTag>NiLvFpgaFIFO 8</BaseAddressTag> + <ControlSet>8</ControlSet> + <DataType> + <Delta>1.000000000000000000000000000000000000000000000000000000</Delta> + <IntegerWordLength>64</IntegerWordLength> + <Maximum>18446744073709551600.00000000000000000000000000000000000</Maximum> + <Minimum>0.000000000000000000000000000000000000000000000000000000</Minimum> + <Signed>false</Signed> + <SubType>U64</SubType> + <WordLength>64</WordLength> + </DataType> + <Direction>HostToTarget</Direction> + <Implementation>niFpgaHostToTarget</Implementation> + <Number>8</Number> + <NumberOfElements>1029</NumberOfElements> + <UserVisible>true</UserVisible> + </Channel> + <Channel name="TX FIFO 3"> + <BaseAddressTag>NiLvFpgaFIFO 9</BaseAddressTag> + <ControlSet>9</ControlSet> + <DataType> + <Delta>1.000000000000000000000000000000000000000000000000000000</Delta> + <IntegerWordLength>64</IntegerWordLength> + <Maximum>18446744073709551600.00000000000000000000000000000000000</Maximum> + <Minimum>0.000000000000000000000000000000000000000000000000000000</Minimum> + <Signed>false</Signed> + <SubType>U64</SubType> + <WordLength>64</WordLength> + </DataType> + <Direction>HostToTarget</Direction> + <Implementation>niFpgaHostToTarget</Implementation> + <Number>9</Number> + <NumberOfElements>1029</NumberOfElements> + <UserVisible>true</UserVisible> + </Channel> + <Channel name="TX FIFO 4"> + <BaseAddressTag>NiLvFpgaFIFO 10</BaseAddressTag> + <ControlSet>10</ControlSet> + <DataType> + <Delta>1.000000000000000000000000000000000000000000000000000000</Delta> + <IntegerWordLength>64</IntegerWordLength> + <Maximum>18446744073709551600.00000000000000000000000000000000000</Maximum> + <Minimum>0.000000000000000000000000000000000000000000000000000000</Minimum> + <Signed>false</Signed> + <SubType>U64</SubType> + <WordLength>64</WordLength> + </DataType> + <Direction>HostToTarget</Direction> + <Implementation>niFpgaHostToTarget</Implementation> + <Number>10</Number> + <NumberOfElements>1029</NumberOfElements> + <UserVisible>true</UserVisible> + </Channel> + <Channel name="TX FIFO 5"> + <BaseAddressTag>NiLvFpgaFIFO 11</BaseAddressTag> + <ControlSet>11</ControlSet> + <DataType> + <Delta>1.000000000000000000000000000000000000000000000000000000</Delta> + <IntegerWordLength>64</IntegerWordLength> + <Maximum>18446744073709551600.00000000000000000000000000000000000</Maximum> + <Minimum>0.000000000000000000000000000000000000000000000000000000</Minimum> + <Signed>false</Signed> + <SubType>U64</SubType> + <WordLength>64</WordLength> + </DataType> + <Direction>HostToTarget</Direction> + <Implementation>niFpgaHostToTarget</Implementation> + <Number>11</Number> + <NumberOfElements>1029</NumberOfElements> + <UserVisible>true</UserVisible> + </Channel> + </DmaChannelAllocationList> + <RegisterBlockList> + <RegisterBlock name="NiLvFpgaFIFO 0"> + <Offset>0xFF80</Offset> + </RegisterBlock> + <RegisterBlock name="NiLvFpgaFIFO 1"> + <Offset>0xFF40</Offset> + </RegisterBlock> + <RegisterBlock name="NiLvFpgaFIFO 2"> + <Offset>0xFF00</Offset> + </RegisterBlock> + <RegisterBlock name="NiLvFpgaFIFO 3"> + <Offset>0xFEC0</Offset> + </RegisterBlock> + <RegisterBlock name="NiLvFpgaFIFO 4"> + <Offset>0xFE80</Offset> + </RegisterBlock> + <RegisterBlock name="NiLvFpgaFIFO 5"> + <Offset>0xFE40</Offset> + </RegisterBlock> + <RegisterBlock name="NiLvFpgaFIFO 6"> + <Offset>0xFE00</Offset> + </RegisterBlock> + <RegisterBlock name="NiLvFpgaFIFO 7"> + <Offset>0xFDC0</Offset> + </RegisterBlock> + <RegisterBlock name="NiLvFpgaFIFO 8"> + <Offset>0xFD80</Offset> + </RegisterBlock> + <RegisterBlock name="NiLvFpgaFIFO 9"> + <Offset>0xFD40</Offset> + </RegisterBlock> + <RegisterBlock name="NiLvFpgaFIFO 10"> + <Offset>0xFD00</Offset> + </RegisterBlock> + <RegisterBlock name="NiLvFpgaFIFO 11"> + <Offset>0xFCC0</Offset> + </RegisterBlock> + </RegisterBlockList> + <UsedBaseClockList> + <BaseClock name="ReliableClkIn"> + </BaseClock> + <BaseClock name="ChinchClk"> + </BaseClock> + <BaseClock name="40 MHz Onboard Clock"> + </BaseClock> + </UsedBaseClockList> + <version>1</version> + </NiFpga> + </CompilationResults> + </CompilationResultsTree> + <MultipleUserClocks>false</MultipleUserClocks> + <AllowImplicitEnableRemoval>false</AllowImplicitEnableRemoval> + </Project> + <ClientData/> + <BitstreamMD5>a72ba1716893a0bd02f88dac3ab28e1b</BitstreamMD5> + <Bitstream>a72ba1716893a0bd02f88dac3ab28e1b</Bitstream> +</Bitfile> diff --git a/host/lib/transport/nirio/nifpga_lvbitx.cpp b/host/lib/transport/nirio/nifpga_lvbitx.cpp new file mode 100644 index 000000000..289a44d4a --- /dev/null +++ b/host/lib/transport/nirio/nifpga_lvbitx.cpp @@ -0,0 +1,138 @@ +// +// Copyright 2013-2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// 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/transport/nirio/nifpga_lvbitx.h> +#include <string> +#include <iostream> +#include <fstream> +#include <streambuf> +#include <boost/foreach.hpp> +#include <boost/format.hpp> +#include <boost/filesystem.hpp> +#include <boost/algorithm/string.hpp> +#include <boost/regex.hpp> + +namespace uhd { namespace niusrprio { + +std::string nifpga_lvbitx::_get_bitstream_checksum(const std::string& file_path) +{ + std::string checksum; + std::ifstream lvbitx_stream(file_path.c_str()); + if (lvbitx_stream.is_open()) { + std::string lvbitx_contents; + lvbitx_stream.seekg(0, std::ios::end); + lvbitx_contents.reserve(static_cast<size_t>(lvbitx_stream.tellg())); + lvbitx_stream.seekg(0, std::ios::beg); + lvbitx_contents.assign((std::istreambuf_iterator<char>(lvbitx_stream)), std::istreambuf_iterator<char>()); + try { + boost::smatch md5_match; + if (boost::regex_search(lvbitx_contents, md5_match, boost::regex("<BitstreamMD5>([a-zA-Z0-9]{32})<\\/BitstreamMD5>", boost::regex::icase))) { + checksum = std::string(md5_match[1].first, md5_match[1].second); + } + } catch (boost::exception&) { + checksum = ""; + } + } + boost::to_upper(checksum); + return checksum; +} + +#ifdef UHD_PLATFORM_WIN32 +#include <windows.h> + +std::string _get_path_from_registry(const std::string& registry_key_path) +{ + boost::smatch reg_key_match; + //If a substring in the search path is enclosed in [] (square brackets) then it is interpreted as a registry path + if (not boost::regex_search(registry_key_path, reg_key_match, boost::regex("\\[(.+)\\](.*)", boost::regex::icase))) + return std::string(); + std::string reg_key_path = std::string(reg_key_match[1].first, reg_key_match[1].second); + std::string path_suffix = std::string(reg_key_match[2].first, reg_key_match[2].second); + + //Split the registry path into parent, key-path and value. + boost::smatch reg_parent_match; + if (not boost::regex_search(reg_key_path, reg_parent_match, boost::regex("^(.+?)\\\\(.+)\\\\(.+)$", boost::regex::icase))) + return std::string(); + std::string reg_parent = std::string(reg_parent_match[1].first, reg_parent_match[1].second); + std::string reg_path = std::string(reg_parent_match[2].first, reg_parent_match[2].second); + std::string reg_val_name = std::string(reg_parent_match[3].first, reg_parent_match[3].second); + + HKEY hkey_parent = HKEY_LOCAL_MACHINE; + if (reg_parent == "HKEY_LOCAL_MACHINE") hkey_parent = HKEY_LOCAL_MACHINE; + else if (reg_parent == "HKEY_CURRENT_USER") hkey_parent = HKEY_CURRENT_USER; + else if (reg_parent == "HKEY_CLASSES_ROOT") hkey_parent = HKEY_CLASSES_ROOT; + else if (reg_parent == "HKEY_CURRENT_CONFIG") hkey_parent = HKEY_CURRENT_CONFIG; + else if (reg_parent == "HKEY_USERS") hkey_parent = HKEY_CURRENT_USER; + + TCHAR value_buff[1024]; + DWORD value_buff_size = 1024*sizeof(TCHAR); + + //Get a handle to the key location + HKEY hkey_location; + if (RegOpenKeyExA(hkey_parent, reg_path.c_str(), NULL, KEY_QUERY_VALUE, &hkey_location) != ERROR_SUCCESS) + return std::string(); + + //Query key value + DWORD dw_type = REG_SZ; + if(RegQueryValueExA(hkey_location, reg_val_name.c_str(), NULL, &dw_type, (LPBYTE)value_buff, &value_buff_size) == ERROR_SUCCESS) { + RegCloseKey(hkey_location); + if (value_buff_size >= 1024*sizeof(TCHAR)) { + return std::string(); + } else { + std::string return_value(value_buff, value_buff_size-1); //value_buff_size includes the null terminator + return_value += path_suffix; + return return_value; + } + } else { + return std::string(); + } +} + +#endif /*UHD_PLATFORM_WIN32*/ + +std::string nifpga_lvbitx::_get_fpga_images_dir(const std::string search_paths) +{ + std::vector<std::string> search_path_vtr; + boost::split(search_path_vtr, search_paths, boost::is_any_of(",")); + + std::string lvbitx_dir; + //Traverse through the list of search paths. Priority: lexical + BOOST_FOREACH(std::string& search_path, search_path_vtr) { + boost::algorithm::trim(search_path); + if (search_path.empty()) continue; + +#ifdef UHD_PLATFORM_WIN32 + lvbitx_dir = _get_path_from_registry(search_path); + if (lvbitx_dir.empty()) { + //Could not read from the registry due to missing key, invalid values, etc + //Just use the search path. The is_directory check will fail if this is a + //registry path and we will move on to the next item in the list. + lvbitx_dir = search_path; + } +#else + lvbitx_dir = search_path; +#endif + + //If the current directory exists then stop traversing the search path list. + if (boost::filesystem::is_directory(lvbitx_dir)) break; + } + + return lvbitx_dir; +} + + +}} diff --git a/host/lib/transport/nirio/nirio_driver_iface_linux.cpp b/host/lib/transport/nirio/nirio_driver_iface_linux.cpp new file mode 100644 index 000000000..b1f4bb49f --- /dev/null +++ b/host/lib/transport/nirio/nirio_driver_iface_linux.cpp @@ -0,0 +1,111 @@ +// +// Copyright 2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/transport/nirio/nirio_driver_iface.h> +#include <stdio.h> +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <sys/mman.h> + +namespace nirio_driver_iface { + +nirio_status rio_open( + const std::string& device_path, + rio_dev_handle_t& device_handle) +{ + device_handle = ::open(device_path.c_str(), O_RDWR | O_CLOEXEC); + return (device_handle < 0) ? NiRio_Status_InvalidParameter : NiRio_Status_Success; +} + +void rio_close(rio_dev_handle_t& device_handle) +{ + ::close(device_handle); + device_handle = -1; +} + +bool rio_isopen(rio_dev_handle_t device_handle) +{ + return (device_handle >= 0); +} + +nirio_status rio_ioctl( + rio_dev_handle_t device_handle, + uint32_t ioctl_code, + const void *write_buf, + size_t write_buf_len, + void *read_buf, + size_t read_buf_len) +{ + nirio_ioctl_block_t ioctl_block = {0,0,0,0,0,0}; + + // two-casts necessary to prevent pointer sign-extension + ioctl_block.inBuf = static_cast<uint64_t>(reinterpret_cast<uintptr_t>(write_buf)); + ioctl_block.inBufLength = write_buf_len; + ioctl_block.outBuf = static_cast<uint64_t>(reinterpret_cast<uintptr_t>(read_buf)); + ioctl_block.outBufLength = read_buf_len; + + int status = ::ioctl(device_handle, ioctl_code, &ioctl_block); + if (status == -1) { + switch (errno) { + case EINVAL: return NiRio_Status_InvalidParameter; + case EFAULT: return NiRio_Status_MemoryFull; + default: return NiRio_Status_SoftwareFault; + } + } else { + return NiRio_Status_Success; + } +} + +nirio_status rio_mmap( + rio_dev_handle_t device_handle, + uint16_t memory_type, + size_t size, + bool writable, + rio_mmap_t &map) +{ + int access_mode = PROT_READ; //Write-only mode not supported + if (writable) access_mode |= PROT_WRITE; + map.addr = ::mmap(NULL, size, access_mode, MAP_SHARED, device_handle, (off_t) memory_type * sysconf(_SC_PAGESIZE)); + map.size = size; + + if (map.addr == MAP_FAILED) { + map.addr = NULL; + map.size = 0; + switch (errno) { + case EINVAL: return NiRio_Status_InvalidParameter; + case EFAULT: return NiRio_Status_MemoryFull; + default: return NiRio_Status_SoftwareFault; + } + } + return NiRio_Status_Success; +} + +nirio_status rio_munmap(rio_mmap_t &map) +{ + nirio_status status = 0; + if (map.addr != NULL) { + status = ::munmap(map.addr, map.size); + + map.addr = NULL; + map.size = 0; + } + return status; +} + +} diff --git a/host/lib/transport/nirio/nirio_driver_iface_macos.cpp b/host/lib/transport/nirio/nirio_driver_iface_macos.cpp new file mode 100644 index 000000000..1a1142525 --- /dev/null +++ b/host/lib/transport/nirio/nirio_driver_iface_macos.cpp @@ -0,0 +1,63 @@ +// +// Copyright 2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// +#include <uhd/transport/nirio/nirio_driver_iface.h> + +namespace nirio_driver_iface { + +nirio_status rio_open( + const std::string& device_path, + rio_dev_handle_t& device_handle) +{ + return NiRio_Status_FeatureNotSupported; +} + +void rio_close(rio_dev_handle_t& device_handle) +{ +} + +bool rio_isopen(rio_dev_handle_t device_handle) +{ + return false; +} + +nirio_status rio_ioctl( + rio_dev_handle_t device_handle, + uint32_t ioctl_code, + const void *write_buf, + size_t write_buf_len, + void *read_buf, + size_t read_buf_len) +{ + return NiRio_Status_FeatureNotSupported; +} + +nirio_status rio_mmap( + rio_dev_handle_t device_handle, + uint16_t memory_type, + size_t size, + bool writable, + rio_mmap_t &map) +{ + return NiRio_Status_FeatureNotSupported; +} + +nirio_status rio_munmap(rio_mmap_t &map) +{ + return NiRio_Status_FeatureNotSupported; +} + +} diff --git a/host/lib/transport/nirio/nirio_driver_iface_win.cpp b/host/lib/transport/nirio/nirio_driver_iface_win.cpp new file mode 100644 index 000000000..b47c6ce85 --- /dev/null +++ b/host/lib/transport/nirio/nirio_driver_iface_win.cpp @@ -0,0 +1,148 @@ +// +// Copyright 2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/transport/nirio/nirio_driver_iface.h> +#include <process.h> + +#define NIRIO_IOCTL_MAP_MEMORY CTL_CODE(FILE_DEVICE_UNKNOWN, 0xF00, METHOD_BUFFERED, (FILE_READ_ACCESS | FILE_WRITE_ACCESS)) +#define NIRIO_IOCTL_UNMAP_MEMORY CTL_CODE(FILE_DEVICE_UNKNOWN, 0xF01, METHOD_BUFFERED, (FILE_READ_ACCESS | FILE_WRITE_ACCESS)) + +namespace nirio_driver_iface { + +nirio_status rio_open( + const std::string& device_path, + rio_dev_handle_t& device_handle) +{ + device_handle = CreateFileA(device_path.c_str(), GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, /* default security */ + OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL /* template file */); + + return (device_handle == INVALID_HANDLE_VALUE) ? NiRio_Status_InvalidParameter : NiRio_Status_Success; +} + +void rio_close(rio_dev_handle_t& device_handle) +{ + ::CloseHandle(device_handle); + device_handle = INVALID_HANDLE_VALUE; +} + +bool rio_isopen(rio_dev_handle_t device_handle) +{ + return (device_handle != INVALID_HANDLE_VALUE); +} + +nirio_status rio_ioctl( + rio_dev_handle_t device_handle, + uint32_t ioctl_code, + const void *write_buf, + size_t write_buf_len, + void *read_buf, + size_t read_buf_len) +{ + if (!rio_isopen(device_handle)) return NiRio_Status_ResourceNotInitialized; + + /* Note, if the file handle was opened with the OVERLAPPED flag, you must + * supply an OVERLAPPED structure to ReadFile, WriteFile, and + * DeviceIoControl, even when doing synchronous IO. */ + OVERLAPPED zeroedOverlapped = {0}; + DWORD outLen = 0; + int_fast32_t lastError = 0; + + if (!(DeviceIoControl(device_handle, ioctl_code, + const_cast<void*>(write_buf), static_cast<DWORD>(write_buf_len), + read_buf, static_cast<DWORD>(read_buf_len), + &outLen, &zeroedOverlapped ))) + { + lastError = GetLastError(); + return NiRio_Status_SoftwareFault; + } + + return NiRio_Status_Success; +} + +unsigned int __stdcall memory_map_thread_routine(void *context) +{ + rio_mmap_threadargs_t *args = (rio_mmap_threadargs_t*)context; + args->status = rio_ioctl(args->device_handle, NIRIO_IOCTL_MAP_MEMORY, &(args->params), sizeof(args->params), NULL, 0); + if (nirio_status_fatal(args->status)) + { + SetEvent(reinterpret_cast<HANDLE>(args->params.map_ready_event_handle)); + } + return 0; +} + +nirio_status rio_mmap( + rio_dev_handle_t device_handle, + uint16_t memory_type, + size_t size, + bool writable, + rio_mmap_t &map) +{ + if (!rio_isopen(device_handle)) return NiRio_Status_ResourceNotInitialized; + + access_mode_t access_mode = writable ? ACCESS_MODE_WRITE : ACCESS_MODE_READ; + + uint64_t mapped_addr = 0; + map.map_thread_args.device_handle = device_handle; + map.map_thread_args.status = NiRio_Status_Success; + map.map_thread_args.params.memoryType = memory_type; + map.map_thread_args.params.size = (uint32_t)size; + map.map_thread_args.params.mapped_va_ptr = reinterpret_cast<uintptr_t>(&mapped_addr); + map.map_thread_args.params.access_mode = (uint8_t)access_mode; + HANDLE map_ready_event_handle = CreateEventA(NULL, TRUE, FALSE, NULL); + if (map_ready_event_handle == NULL) { + map.addr = NULL; + return NiRio_Status_SoftwareFault; + } + map.map_thread_args.params.map_ready_event_handle = reinterpret_cast<uint64_t>(map_ready_event_handle); + map.map_thread_handle = (HANDLE) _beginthreadex(NULL, 0, memory_map_thread_routine, &(map.map_thread_args), 0, NULL); + + nirio_status status = NiRio_Status_Success; + if (map.map_thread_handle == NULL) { + map.addr = NULL; + return NiRio_Status_SoftwareFault; + } else { + WaitForSingleObject(map_ready_event_handle, INFINITE); + map.addr = reinterpret_cast<void*>(mapped_addr); + if (map.addr == NULL) { + WaitForSingleObject(map.map_thread_handle, INFINITE); + CloseHandle(map.map_thread_handle); + nirio_status_chain(map.map_thread_args.status, status); + } + } + CloseHandle(map_ready_event_handle); + return status; +} + +nirio_status rio_munmap(rio_mmap_t &map) +{ + if (!rio_isopen(map.map_thread_args.device_handle)) return NiRio_Status_ResourceNotInitialized; + + nirio_status status = NiRio_Status_Success; + if (map.addr != NULL) { + uint64_t mapped_addr = reinterpret_cast<uintptr_t>(map.addr); + status = rio_ioctl(map.map_thread_args.device_handle, NIRIO_IOCTL_UNMAP_MEMORY, &mapped_addr, sizeof(mapped_addr), NULL, 0); + if (nirio_status_not_fatal(status)) { + WaitForSingleObject(map.map_thread_handle, INFINITE); + } + CloseHandle(map.map_thread_handle); + map.addr = NULL; + } + return status; +} + +} diff --git a/host/lib/transport/nirio/nirio_resource_manager.cpp b/host/lib/transport/nirio/nirio_resource_manager.cpp new file mode 100644 index 000000000..85e789087 --- /dev/null +++ b/host/lib/transport/nirio/nirio_resource_manager.cpp @@ -0,0 +1,120 @@ +// +// Copyright 2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + + +#include <uhd/transport/nirio/nirio_resource_manager.h> + +#ifdef __clang__ + #pragma GCC diagnostic push ignored "-Wmissing-field-initializers" +#elif defined(__GNUC__) + #pragma GCC diagnostic ignored "-Wmissing-field-initializers" +#endif + +namespace uhd { namespace niusrprio +{ + +nirio_resource_manager::nirio_resource_manager( + niriok_proxy& proxy) : _kernel_proxy(proxy), _fifo_info_map(), _reg_info_map() +{ +} + +nirio_resource_manager::~nirio_resource_manager() +{ + finalize(); +} + +nirio_status nirio_resource_manager::initialize( + const nirio_register_info_vtr& reg_info_vtr, + const nirio_fifo_info_vtr& fifo_info_vtr) +{ + nirio_status status = 0; + for (nirio_fifo_info_vtr::const_iterator it = fifo_info_vtr.begin(); it != fifo_info_vtr.end(); it++) { + const nirio_fifo_info_t& fifo_info = *it; + status = _add_fifo_resource(fifo_info); + if (nirio_status_fatal(status)) return status; + + _fifo_info_map.insert(fifo_info_map_t::value_type(fifo_info.name, fifo_info)); + } + for (nirio_register_info_vtr::const_iterator it = reg_info_vtr.begin(); it != reg_info_vtr.end(); it++) { + const nirio_register_info_t& reg_info = *it; + + _reg_info_map.insert(register_info_map_t::value_type(reg_info.name, reg_info)); + } + return _set_driver_config(); +} + +void nirio_resource_manager::finalize() +{ + _fifo_info_map.clear(); +} + +nirio_status nirio_resource_manager::get_register_offset( + const char* register_name, + uint32_t& offset) +{ + register_info_map_t::const_iterator it = _reg_info_map.find(fifo_info_map_t::key_type(register_name)); + if (it == _reg_info_map.end()) return NiRio_Status_InvalidParameter; + + offset = (*it).second.offset; + + return NiRio_Status_Success; +} + + +nirio_status nirio_resource_manager::_add_fifo_resource( + const nirio_fifo_info_t& fifo_info) +{ + nirio_driver_iface::nirio_syncop_in_params_t in = {}; + nirio_driver_iface::nirio_syncop_out_params_t out = {}; + + in.function = nirio_driver_iface::NIRIO_FUNC::ADD_RESOURCE; + in.subfunction = (fifo_info.direction == OUTPUT_FIFO) ? + nirio_driver_iface::NIRIO_RESOURCE::OUTPUT_FIFO : + nirio_driver_iface::NIRIO_RESOURCE::INPUT_FIFO; + + in.params.add.fifoWithDataType.channel = fifo_info.channel; + in.params.add.fifoWithDataType.baseAddress = fifo_info.base_addr; + in.params.add.fifoWithDataType.depthInSamples = fifo_info.depth; + in.params.add.fifoWithDataType.scalarType = fifo_info.scalar_type; + in.params.add.fifoWithDataType.bitWidth = fifo_info.width; + in.params.add.fifoWithDataType.version = fifo_info.version; + + return _kernel_proxy.sync_operation(&in, sizeof(in), &out, sizeof(out)); +} + +nirio_status nirio_resource_manager::_set_driver_config() +{ + nirio_driver_iface::nirio_syncop_in_params_t in = {}; + nirio_driver_iface::nirio_syncop_out_params_t out = {}; + in.function = nirio_driver_iface::NIRIO_FUNC::SET_DRIVER_CONFIG; + in.subfunction = 0; + + return _kernel_proxy.sync_operation(&in, sizeof(in), &out, sizeof(out)); +} + +nirio_fifo_info_t* nirio_resource_manager::_lookup_fifo_info(const char* fifo_name) { + fifo_info_map_t::iterator it = _fifo_info_map.find(fifo_info_map_t::key_type(fifo_name)); + if (it == _fifo_info_map.end()) return NULL; + + return &((*it).second); +} + +}} + +#ifdef __GNUC__ + #pragma GCC diagnostic pop +#endif diff --git a/host/lib/transport/nirio/niriok_proxy.cpp b/host/lib/transport/nirio/niriok_proxy.cpp new file mode 100644 index 000000000..031623c9a --- /dev/null +++ b/host/lib/transport/nirio/niriok_proxy.cpp @@ -0,0 +1,300 @@ +// +// Copyright 2013-2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// 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/transport/nirio/niriok_proxy.h> +#include <cstring> + +#define NI_VENDOR_NUM 0x1093 + +#define VERSION_BUILD_SHIFT 0 +#define VERSION_PHASE_SHIFT 14 +#define VERSION_MAINT_SHIFT 16 +#define VERSION_UPGRD_SHIFT 20 +#define VERSION_MAJOR_SHIFT 24 +#define VERSION_BUILD_MASK 0x00003FFF +#define VERSION_PHASE_MASK 0x0000C000 +#define VERSION_MAINT_MASK 0x000F0000 +#define VERSION_UPGRD_MASK 0x00F00000 +#define VERSION_MAJOR_MASK 0xFF000000 + +#define GET_FIFO_MEMORY_TYPE(fifo_inst) (static_cast<uint16_t>(0x0100 | static_cast<uint16_t>(fifo_inst))) + +#ifdef __clang__ + #pragma GCC diagnostic push ignored "-Wmissing-field-initializers" +#elif defined(__GNUC__) + #pragma GCC diagnostic ignored "-Wmissing-field-initializers" +#endif + +namespace uhd { namespace niusrprio +{ + //------------------------------------------------------- + // niriok_proxy + //------------------------------------------------------- + niriok_proxy::niriok_proxy(): _device_handle(nirio_driver_iface::INVALID_RIO_HANDLE) + { + } + + niriok_proxy::~niriok_proxy() + { + close(); + } + + nirio_status niriok_proxy::open(const std::string& interface_path) + { + if (interface_path.empty()) return NiRio_Status_ResourceNotFound; + + //close if already open. + close(); + + nirio_status status = NiRio_Status_Success; + nirio_status_chain(nirio_driver_iface::rio_open( + interface_path, _device_handle), status); + if (nirio_status_not_fatal(status)) { + nirio_status_chain(nirio_driver_iface::rio_ioctl(_device_handle, + nirio_driver_iface::NIRIO_IOCTL_POST_OPEN, + NULL, 0, NULL, 0), status); + nirio_driver_iface::nirio_ioctl_packet_t out(&_interface_num, sizeof(_interface_num), 0); + nirio_status_chain(nirio_driver_iface::rio_ioctl(_device_handle, + nirio_driver_iface::NIRIO_IOCTL_GET_IFACE_NUM, + NULL, 0, + &out, sizeof(out)), status); + + if (nirio_status_fatal(status)) close(); + } + return status; + } + + void niriok_proxy::close(void) + { + if(nirio_driver_iface::rio_isopen(_device_handle)) + { + nirio_driver_iface::rio_ioctl( + _device_handle, nirio_driver_iface::NIRIO_IOCTL_PRE_CLOSE, NULL, 0, NULL, 0); + nirio_driver_iface::rio_close(_device_handle); + } + } + + nirio_status niriok_proxy::reset() + { + nirio_driver_iface::nirio_syncop_in_params_t in = {}; + nirio_driver_iface::nirio_syncop_out_params_t out = {}; + + in.function = nirio_driver_iface::NIRIO_FUNC::RESET; + + return sync_operation(&in, sizeof(in), &out, sizeof(out)); + } + + nirio_status niriok_proxy::get_cached_session( + uint32_t& session) + { + nirio_driver_iface::nirio_ioctl_packet_t out(&session, sizeof(session), 0); + return nirio_driver_iface::rio_ioctl(_device_handle, + nirio_driver_iface::NIRIO_IOCTL_GET_SESSION, + NULL, 0, + &out, sizeof(out)); + } + + nirio_status niriok_proxy::get_version( + nirio_version_t type, + uint32_t& major, + uint32_t& upgrade, + uint32_t& maintenance, + char& phase, + uint32_t& build) + { + nirio_device_attr_32_t version_attr = (type==CURRENT)?CURRENT_VERSION:OLDEST_COMPATIBLE_VERSION; + uint32_t raw_version = 0; + nirio_status status = get_attribute(version_attr, raw_version); + + major = (raw_version & VERSION_MAJOR_MASK) >> VERSION_MAJOR_SHIFT; + upgrade = (raw_version & VERSION_UPGRD_MASK) >> VERSION_UPGRD_SHIFT; + maintenance = (raw_version & VERSION_MAINT_MASK) >> VERSION_MAINT_SHIFT; + build = (raw_version & VERSION_BUILD_MASK) >> VERSION_BUILD_SHIFT; + + uint32_t phase_num = (raw_version & VERSION_PHASE_MASK) >> VERSION_PHASE_SHIFT; + switch (phase_num) { + case 0: phase = 'd'; break; + case 1: phase = 'a'; break; + case 2: phase = 'b'; break; + case 3: phase = 'f'; break; + } + + return status; + } + + nirio_status niriok_proxy::sync_operation( + const void *writeBuffer, + size_t writeBufferLength, + void *readBuffer, + size_t readBufferLength) + { + nirio_driver_iface::nirio_ioctl_packet_t out(readBuffer, readBufferLength, 0); + nirio_status ioctl_status = nirio_driver_iface::rio_ioctl(_device_handle, + nirio_driver_iface::NIRIO_IOCTL_SYNCOP, + writeBuffer, writeBufferLength, + &out, sizeof(out)); + if (nirio_status_fatal(ioctl_status)) return ioctl_status; + + return out.statusCode; + } + + nirio_status niriok_proxy::get_attribute( + const nirio_device_attr_32_t attribute, + uint32_t& attrValue) + { + nirio_driver_iface::nirio_syncop_in_params_t in = {}; + nirio_driver_iface::nirio_syncop_out_params_t out = {}; + + in.function = nirio_driver_iface::NIRIO_FUNC::GET32; + in.params.attribute32.attribute = attribute; + + nirio_status status = sync_operation(&in, sizeof(in), &out, sizeof(out)); + + attrValue = out.params.attribute32.value; + return status; + } + + nirio_status niriok_proxy::get_attribute( + const nirio_device_attr_str_t attribute, + char *buf, + const uint32_t bufLen, + uint32_t& stringLen) + { + nirio_driver_iface::nirio_syncop_in_params_t in = {}; + nirio_driver_iface::nirio_syncop_out_params_t out = {}; + nirio_driver_iface::init_syncop_out_params(out, buf, bufLen); + + in.function = nirio_driver_iface::NIRIO_FUNC::GET_STRING; + in.params.attributeStr.attribute = attribute; + + nirio_status status = sync_operation(&in, sizeof(in), &out, sizeof(out)); + + stringLen = out.params.stringLength; + return status; + } + + nirio_status niriok_proxy::set_attribute( + const nirio_device_attr_32_t attribute, + const uint32_t value) + { + nirio_driver_iface::nirio_syncop_in_params_t in = {}; + nirio_driver_iface::nirio_syncop_out_params_t out = {}; + + in.function = nirio_driver_iface::NIRIO_FUNC::SET32; + in.params.attribute32.attribute = attribute; + in.params.attribute32.value = value; + + return sync_operation(&in, sizeof(in), &out, sizeof(out)); + } + + nirio_status niriok_proxy::set_attribute( + const nirio_device_attr_str_t attribute, + const char* const buffer) + { + nirio_driver_iface::nirio_syncop_in_params_t in = {}; + nirio_driver_iface::init_syncop_in_params(in, buffer, strlen(buffer) + 1); + nirio_driver_iface::nirio_syncop_out_params_t out = {}; + + in.function = nirio_driver_iface::NIRIO_FUNC::SET_STRING; + in.params.attributeStr.attribute = attribute; + + return sync_operation(&in, sizeof(in), &out, sizeof(out)); + } + + nirio_status niriok_proxy::peek(uint32_t offset, uint32_t& value) + { + if (offset % 4 != 0) return NiRio_Status_MisalignedAccess; + + nirio_driver_iface::nirio_syncop_in_params_t in = {}; + nirio_driver_iface::nirio_syncop_out_params_t out = {}; + + in.function = nirio_driver_iface::NIRIO_FUNC::IO; + in.subfunction = nirio_driver_iface::NIRIO_IO::PEEK32; + in.params.io.offset = offset; + + nirio_status status = sync_operation(&in, sizeof(in), &out, sizeof(out)); + value = out.params.io.value.value32; + return status; + } + + nirio_status niriok_proxy::peek(uint32_t offset, uint64_t& value) + { + if (offset % 8 != 0) return NiRio_Status_MisalignedAccess; + + nirio_driver_iface::nirio_syncop_in_params_t in = {}; + nirio_driver_iface::nirio_syncop_out_params_t out = {}; + + in.function = nirio_driver_iface::NIRIO_FUNC::IO; + in.subfunction = nirio_driver_iface::NIRIO_IO::PEEK64; + in.params.io.offset = offset; + + nirio_status status = sync_operation(&in, sizeof(in), &out, sizeof(out)); + value = out.params.io.value.value64; + return status; + } + + nirio_status niriok_proxy::poke(uint32_t offset, const uint32_t& value) + { + if (offset % 4 != 0) return NiRio_Status_MisalignedAccess; + + nirio_driver_iface::nirio_syncop_in_params_t in = {}; + nirio_driver_iface::nirio_syncop_out_params_t out = {}; + + in.function = nirio_driver_iface::NIRIO_FUNC::IO; + in.subfunction = nirio_driver_iface::NIRIO_IO::POKE32; + in.params.io.offset = offset; + in.params.io.value.value32 = value; + + return sync_operation(&in, sizeof(in), &out, sizeof(out)); + } + + nirio_status niriok_proxy::poke(uint32_t offset, const uint64_t& value) + { + if (offset % 8 != 0) return NiRio_Status_MisalignedAccess; + + nirio_driver_iface::nirio_syncop_in_params_t in = {}; + nirio_driver_iface::nirio_syncop_out_params_t out = {}; + + in.function = nirio_driver_iface::NIRIO_FUNC::IO; + in.subfunction = nirio_driver_iface::NIRIO_IO::POKE64; + in.params.io.offset = offset; + in.params.io.value.value64 = value; + + return sync_operation(&in, sizeof(in), &out, sizeof(out)); + } + + nirio_status niriok_proxy::map_fifo_memory( + uint32_t fifo_instance, + size_t size, + nirio_driver_iface::rio_mmap_t& map) + { + return nirio_driver_iface::rio_mmap(_device_handle, + GET_FIFO_MEMORY_TYPE(fifo_instance), + size, true, map); + } + + nirio_status niriok_proxy::unmap_fifo_memory( + nirio_driver_iface::rio_mmap_t& map) + { + return nirio_driver_iface::rio_munmap(map); + } +}} + +#ifdef __GNUC__ + #pragma GCC diagnostic pop +#endif diff --git a/host/lib/transport/nirio/niusrprio_session.cpp b/host/lib/transport/nirio/niusrprio_session.cpp new file mode 100644 index 000000000..a07bc4fdf --- /dev/null +++ b/host/lib/transport/nirio/niusrprio_session.cpp @@ -0,0 +1,199 @@ +// +// Copyright 2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + + +#include <uhd/transport/nirio/niusrprio_session.h> +#include <uhd/transport/nirio/nirio_fifo.h> +#include <uhd/transport/nirio/status.h> +#include <boost/format.hpp> +#include <boost/algorithm/string.hpp> +#include <boost/lexical_cast.hpp> +#include <stdio.h> +#include <fstream> +//@TODO: Move the register defs required by the class to a common location +#include "../../usrp/x300/x300_regs.hpp" + +namespace uhd { namespace niusrprio { + +niusrprio_session::niusrprio_session(const std::string& resource_name, const std::string& rpc_port_name) : + _resource_name(resource_name), + _session_open(false), + _resource_manager(_riok_proxy), + _rpc_client("localhost", rpc_port_name) +{ +} + +niusrprio_session::~niusrprio_session() +{ + close(); +} + +nirio_status niusrprio_session::enumerate(const std::string& rpc_port_name, device_info_vtr& device_info_vtr) +{ + usrprio_rpc::usrprio_rpc_client temp_rpc_client("localhost", rpc_port_name); + nirio_status status = temp_rpc_client.get_ctor_status(); + nirio_status_chain(temp_rpc_client.niusrprio_enumerate(device_info_vtr), status); + return status; +} + +nirio_status niusrprio_session::open( + nifpga_lvbitx::sptr lvbitx, + bool force_download) +{ + boost::unique_lock<boost::recursive_mutex> lock(_session_mutex); + + _lvbitx = lvbitx; + + nirio_status status = NiRio_Status_Success; + std::string bitfile_path(_lvbitx->get_bitfile_path()); + std::string signature(_lvbitx->get_signature()); + + //Make sure that the RPC client connected to the server properly + nirio_status_chain(_rpc_client.get_ctor_status(), status); + //Get a handle to the kernel driver + nirio_status_chain(_rpc_client.niusrprio_get_interface_path(_resource_name, _interface_path), status); + nirio_status_chain(_riok_proxy.open(_interface_path), status); + + if (nirio_status_not_fatal(status)) { + //Bitfile build for a particular LVFPGA interface will have the same signature + //because the API of the bitfile does not change. Two files with the same signature + //can however have different bitstreams because of non-LVFPGA code differences. + //That is why we need another identifier to qualify the signature. The BIN + //checksum is a good candidate. + std::string lvbitx_checksum(_lvbitx->get_bitstream_checksum()); + boost::uint16_t download_fpga = (force_download || (_read_bitstream_checksum() != lvbitx_checksum)) ? 1 : 0; + + nirio_status_chain(_rpc_client.niusrprio_open_session( + _resource_name, bitfile_path, signature, download_fpga), status); + _session_open = nirio_status_not_fatal(status); + + if (nirio_status_not_fatal(status)) { + nirio_register_info_vtr reg_vtr; + nirio_fifo_info_vtr fifo_vtr; + _lvbitx->init_register_info(reg_vtr); + _lvbitx->init_fifo_info(fifo_vtr); + _resource_manager.initialize(reg_vtr, fifo_vtr); + + nirio_status_chain(_verify_signature(), status); + nirio_status_chain(_write_bitstream_checksum(lvbitx_checksum), status); + } + } + + return status; +} + +void niusrprio_session::close(bool skip_reset) +{ + boost::unique_lock<boost::recursive_mutex> lock(_session_mutex); + + if (_session_open) { + nirio_status status = NiRio_Status_Success; + if (!skip_reset) reset(); + nirio_status_chain(_rpc_client.niusrprio_close_session(_resource_name), status); + _session_open = false; + } +} + +nirio_status niusrprio_session::reset() +{ + boost::unique_lock<boost::recursive_mutex> lock(_session_mutex); + return _rpc_client.niusrprio_reset_device(_resource_name); +} + +nirio_status niusrprio_session::download_bitstream_to_flash(const std::string& bitstream_path) +{ + boost::unique_lock<boost::recursive_mutex> lock(_session_mutex); + return _rpc_client.niusrprio_download_fpga_to_flash(_resource_name, bitstream_path); +} + +niriok_proxy::sptr niusrprio_session::create_kernel_proxy( + const std::string& resource_name, + const std::string& rpc_port_name) +{ + usrprio_rpc::usrprio_rpc_client temp_rpc_client("localhost", rpc_port_name); + nirio_status status = temp_rpc_client.get_ctor_status(); + + std::string interface_path; + nirio_status_chain(temp_rpc_client.niusrprio_get_interface_path(resource_name, interface_path), status); + + niriok_proxy::sptr proxy; + if (nirio_status_not_fatal(status)) { + proxy.reset(new niriok_proxy()); + if (proxy) nirio_status_chain(proxy->open(interface_path), status); + } + + return proxy; +} + +nirio_status niusrprio_session::_verify_signature() +{ + //Validate the signature using the kernel proxy + nirio_status status = NiRio_Status_Success; + boost::uint32_t sig_offset = 0; + nirio_status_chain(_riok_proxy.get_attribute(DEFAULT_FPGA_SIGNATURE_OFFSET, sig_offset), status); + niriok_scoped_addr_space(_riok_proxy, FPGA, status); + std::string signature; + for (boost::uint32_t i = 0; i < 8; i++) { + boost::uint32_t quarter_sig; + nirio_status_chain(_riok_proxy.peek(sig_offset, quarter_sig), status); + signature += boost::str(boost::format("%08x") % quarter_sig); + } + + std::string expected_signature(_lvbitx->get_signature()); + boost::to_upper(signature); + boost::to_upper(expected_signature); + if (signature.find(expected_signature) == std::string::npos) { + nirio_status_chain(NiRio_Status_SignatureMismatch, status); + } + + return status; +} + +std::string niusrprio_session::_read_bitstream_checksum() +{ + nirio_status status = NiRio_Status_Success; + niriok_scoped_addr_space(_riok_proxy, BUS_INTERFACE, status); + std::string usr_signature; + for (boost::uint32_t i = 0; i < FPGA_USR_SIG_REG_SIZE; i+=4) { + boost::uint32_t quarter_sig; + nirio_status_chain(_riok_proxy.peek(FPGA_USR_SIG_REG_BASE + i, quarter_sig), status); + usr_signature += boost::str(boost::format("%08x") % quarter_sig); + } + boost::to_upper(usr_signature); + + return usr_signature; +} + +nirio_status niusrprio_session::_write_bitstream_checksum(const std::string& checksum) +{ + nirio_status status = NiRio_Status_Success; + niriok_scoped_addr_space(_riok_proxy, BUS_INTERFACE, status); + for (boost::uint32_t i = 0; i < FPGA_USR_SIG_REG_SIZE; i+=4) { + boost::uint32_t quarter_sig; + try { + std::stringstream ss; + ss << std::hex << checksum.substr(i*2,8); + ss >> quarter_sig; + } catch (std::exception&) { + quarter_sig = 0; + } + nirio_status_chain(_riok_proxy.poke(FPGA_USR_SIG_REG_BASE + i, quarter_sig), status); + } + return status; +} + +}} diff --git a/host/lib/transport/nirio/rpc/CMakeLists.txt b/host/lib/transport/nirio/rpc/CMakeLists.txt new file mode 100644 index 000000000..02c16d2ff --- /dev/null +++ b/host/lib/transport/nirio/rpc/CMakeLists.txt @@ -0,0 +1,29 @@ +# +# Copyright 2013 Ettus Research LLC +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +######################################################################## +# This file included, use CMake directory variables +######################################################################## + +######################################################################## +# Append to the list of sources for lib uhd +######################################################################## + +LIBUHD_APPEND_SOURCES( + ${CMAKE_CURRENT_SOURCE_DIR}/rpc_client.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/usrprio_rpc_client.cpp +) diff --git a/host/lib/transport/nirio/rpc/rpc_client.cpp b/host/lib/transport/nirio/rpc/rpc_client.cpp new file mode 100644 index 000000000..a5f8cf412 --- /dev/null +++ b/host/lib/transport/nirio/rpc/rpc_client.cpp @@ -0,0 +1,201 @@ +/// +// Copyright 2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/transport/nirio/rpc/rpc_client.hpp> +#include <boost/bind.hpp> +#include <boost/format.hpp> + +#define CHAIN_BLOCKING_XFER(func, exp, status) \ + if (status) { \ + status = (static_cast<size_t>((func)) == exp); \ + } else { \ + UHD_LOG << "rpc_client operation skipped: " #func "\n"; \ + } \ + +namespace uhd { namespace usrprio_rpc { + +using boost::asio::ip::tcp; + +rpc_client::rpc_client ( + const std::string& server, + const std::string& port, + boost::uint32_t process_id, + boost::uint32_t host_id +) : _socket(_io_service) +{ + //Fill in handshake info + _hshake_args_client.version = CURRENT_VERSION; + _hshake_args_client.oldest_comp_version = OLDEST_COMPATIBLE_VERSION; + _hshake_args_client.client_id = build_client_id(host_id, process_id); + _hshake_args_client.boost_archive_version = boost_serialization_archive_utils::get_version(); + + try { + //Synchronous resolve + connect + tcp::resolver resolver(_io_service); + tcp::resolver::query query(tcp::v4(), server, port); + tcp::resolver::iterator iterator = resolver.resolve(query); + boost::asio::connect(_socket, iterator); + UHD_LOG << "rpc_client connected to server." << std::endl; + + try { + //Perform handshake + bool status = true; + CHAIN_BLOCKING_XFER( + boost::asio::write(_socket, boost::asio::buffer(&_hshake_args_client, sizeof(_hshake_args_client))), + sizeof(_hshake_args_client), status); + CHAIN_BLOCKING_XFER( + boost::asio::read(_socket, boost::asio::buffer(&_hshake_args_server, sizeof(_hshake_args_server))), + sizeof(_hshake_args_server), status); + + _request.header.client_id = _hshake_args_server.client_id; + + if (_hshake_args_server.version >= _hshake_args_client.oldest_comp_version && + _hshake_args_client.version >= _hshake_args_server.oldest_comp_version && + status) + { + UHD_LOG << "rpc_client bound to server." << std::endl; + _wait_for_next_response_header(); + + //Spawn a thread for the io_service callback handler. This thread will run until rpc_client is destroyed. + _io_service_thread.reset(new boost::thread(boost::bind(&boost::asio::io_service::run, &_io_service))); + } else { + UHD_LOG << "rpc_client handshake failed." << std::endl; + _exec_err.assign(boost::asio::error::connection_refused, boost::system::system_category()); + } + UHD_LOG << boost::format("rpc_client archive = %d, rpc_server archive = %d\n.") % + _hshake_args_client.boost_archive_version % + _hshake_args_server.boost_archive_version; + } catch (boost::exception&) { + UHD_LOG << "rpc_client handshake aborted." << std::endl; + _exec_err.assign(boost::asio::error::connection_refused, boost::system::system_category()); + } + } catch (boost::exception&) { + UHD_LOG << "rpc_client connection request cancelled/aborted." << std::endl; + _exec_err.assign(boost::asio::error::connection_aborted, boost::system::system_category()); + } +} + +rpc_client::~rpc_client () { + _stop_io_service(); +} + +const boost::system::error_code& rpc_client::call( + func_id_t func_id, + const func_args_writer_t& in_args, + func_args_reader_t &out_args, + boost::posix_time::milliseconds timeout +) +{ + boost::mutex::scoped_lock lock(_mutex); + + if (_io_service_thread.get()) { + _request.header.func_id = func_id; + in_args.store(_request.data); + _request.header.func_args_size = _request.data.size(); + + _exec_err.clear(); + + //Send function call header and args + bool status = true; + try { + CHAIN_BLOCKING_XFER( + boost::asio::write(_socket, boost::asio::buffer(&_request.header, sizeof(_request.header))), + sizeof(_request.header), status); + CHAIN_BLOCKING_XFER( + boost::asio::write(_socket, boost::asio::buffer(&(*_request.data.begin()), _request.data.size())), + _request.data.size(), status); + } catch (boost::exception&) { + status = false; + } + + //Wait for response using condition variable + if (status) { + if (!_exec_gate.timed_wait(lock, timeout)) { + UHD_LOG << "rpc_client function timed out." << std::endl; + _exec_err.assign(boost::asio::error::timed_out, boost::system::system_category()); + } + } else { + UHD_LOG << "rpc_client connection dropped." << std::endl; + _exec_err.assign(boost::asio::error::connection_aborted, boost::system::system_category()); + _stop_io_service(); + } + + //Verify that we are talking to the correct endpoint + if ((_request.header.client_id != _response.header.client_id) && !_exec_err) { + UHD_LOG << "rpc_client confused about who its talking to." << std::endl; + _exec_err.assign(boost::asio::error::operation_aborted, boost::system::system_category()); + } + + if (!_exec_err) out_args.load(_response.data); + } + + return _exec_err; +} + +void rpc_client::_handle_response_hdr(const boost::system::error_code& err, size_t transferred, size_t expected) +{ + boost::mutex::scoped_lock lock(_mutex); + _exec_err = err; + if (!_exec_err && (transferred == expected)) { + //Response header received. Verify that it is expected + if (func_args_header_t::match_function(_request.header, _response.header)) { + _response.data.resize(_response.header.func_args_size); + + //Wait for response data + boost::asio::async_read(_socket, + boost::asio::buffer(&(*_response.data.begin()), _response.data.size()), + boost::bind(&rpc_client::_handle_response_data, this, + boost::asio::placeholders::error, + boost::asio::placeholders::bytes_transferred, + _response.data.size())); + } else { + //Unexpected response. Ignore it. + UHD_LOG << "rpc_client received garbage responses." << std::endl; + _exec_err.assign(boost::asio::error::operation_aborted, boost::system::system_category()); + + _wait_for_next_response_header(); + } + } + + if (_exec_err) _exec_gate.notify_all(); +} + +void rpc_client::_handle_response_data(const boost::system::error_code& err, size_t transferred, size_t expected) +{ + boost::mutex::scoped_lock lock(_mutex); + _exec_err = err; + if (transferred != expected) { + _exec_err.assign(boost::asio::error::operation_aborted, boost::system::system_category()); + } + + _exec_gate.notify_all(); + + _wait_for_next_response_header(); +} + +void rpc_client::_wait_for_next_response_header() { + //_mutex must be locked when this call is made + boost::asio::async_read( + _socket, + boost::asio::buffer(&_response.header, sizeof(_response.header)), + boost::bind(&rpc_client::_handle_response_hdr, this, + boost::asio::placeholders::error, + boost::asio::placeholders::bytes_transferred, + sizeof(_response.header))); +} + +}} diff --git a/host/lib/transport/nirio/rpc/usrprio_rpc_client.cpp b/host/lib/transport/nirio/rpc/usrprio_rpc_client.cpp new file mode 100644 index 000000000..1a1f1cd21 --- /dev/null +++ b/host/lib/transport/nirio/rpc/usrprio_rpc_client.cpp @@ -0,0 +1,229 @@ +// +// Copyright 2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/transport/nirio/rpc/usrprio_rpc_client.hpp> +#include <uhd/utils/platform.hpp> + +namespace uhd { namespace usrprio_rpc { + +usrprio_rpc_client::usrprio_rpc_client( + std::string server, + std::string port +) : _rpc_client(server, port, uhd::get_process_id(), uhd::get_host_id()), + _timeout(boost::posix_time::milliseconds(DEFAULT_TIMEOUT_IN_MS)) +{ + _ctor_status = _rpc_client.status() ? NiRio_Status_RpcConnectionError : NiRio_Status_Success; +} + +usrprio_rpc_client::~usrprio_rpc_client() +{ +} + +nirio_status usrprio_rpc_client::niusrprio_enumerate(NIUSRPRIO_ENUMERATE_ARGS) +/* +#define NIUSRPRIO_ENUMERATE_ARGS \ + usrprio_device_info_vtr& device_info_vtr +*/ +{ + usrprio_rpc::func_args_writer_t in_args; + usrprio_rpc::func_args_reader_t out_args; + nirio_status status = NiRio_Status_Success; + boost::uint32_t vtr_size = 0; + + status = _boost_error_to_nirio_status( + _rpc_client.call(NIUSRPRIO_ENUMERATE, in_args, out_args, _timeout)); + + if (nirio_status_not_fatal(status)) { + out_args >> status; + out_args >> vtr_size; + } + if (nirio_status_not_fatal(status) && vtr_size > 0) { + device_info_vtr.resize(vtr_size); + for (size_t i = 0; i < (size_t)vtr_size; i++) { + usrprio_device_info info; + out_args >> info; + device_info_vtr[i] = info; + } + } + return status; +} + +nirio_status usrprio_rpc_client::niusrprio_open_session(NIUSRPRIO_OPEN_SESSION_ARGS) +/* +#define NIUSRPRIO_OPEN_SESSION_ARGS \ + const std::string& resource, \ + const std::string& path, \ + const std::string& signature, \ + const boost::uint16_t& download_fpga +*/ +{ + usrprio_rpc::func_args_writer_t in_args; + usrprio_rpc::func_args_reader_t out_args; + nirio_status status = NiRio_Status_Success; + + in_args << resource; + in_args << path; + in_args << signature; + in_args << download_fpga; + + //Open needs a longer timeout because the FPGA download can take upto 6 secs and the NiFpga libload can take 4. + static const boost::uint32_t OPEN_TIMEOUT = 15000; + status = _boost_error_to_nirio_status( + _rpc_client.call(NIUSRPRIO_OPEN_SESSION, in_args, out_args, boost::posix_time::milliseconds(OPEN_TIMEOUT))); + + if (nirio_status_not_fatal(status)) { + out_args >> status; + } + + return status; +} + +nirio_status usrprio_rpc_client::niusrprio_close_session(NIUSRPRIO_CLOSE_SESSION_ARGS) +/* +#define NIUSRPRIO_CLOSE_SESSION_ARGS \ + const std::string& resource +*/ +{ + usrprio_rpc::func_args_writer_t in_args; + usrprio_rpc::func_args_reader_t out_args; + nirio_status status = NiRio_Status_Success; + + in_args << resource; + + status = _boost_error_to_nirio_status( + _rpc_client.call(NIUSRPRIO_CLOSE_SESSION, in_args, out_args, _timeout)); + + if (nirio_status_not_fatal(status)) { + out_args >> status; + } + + return status; +} + +nirio_status usrprio_rpc_client::niusrprio_reset_device(NIUSRPRIO_RESET_SESSION_ARGS) +/* +#define NIUSRPRIO_RESET_SESSION_ARGS \ + const std::string& resource +*/ +{ + usrprio_rpc::func_args_writer_t in_args; + usrprio_rpc::func_args_reader_t out_args; + nirio_status status = NiRio_Status_Success; + + in_args << resource; + + status = _boost_error_to_nirio_status( + _rpc_client.call(NIUSRPRIO_RESET_SESSION, in_args, out_args, _timeout)); + + if (nirio_status_not_fatal(status)) { + out_args >> status; + } + + return status; +} + +nirio_status usrprio_rpc_client::niusrprio_get_interface_path(NIUSRPRIO_GET_INTERFACE_PATH_ARGS) +/* +#define NIUSRPRIO_GET_INTERFACE_PATH_ARGS \ + const std::string& resource, \ + std::string& interface_path +*/ +{ + usrprio_rpc::func_args_writer_t in_args; + usrprio_rpc::func_args_reader_t out_args; + nirio_status status = NiRio_Status_Success; + + in_args << resource; + + status = _boost_error_to_nirio_status( + _rpc_client.call(NIUSRPRIO_GET_INTERFACE_PATH, in_args, out_args, _timeout)); + + if (nirio_status_not_fatal(status)) { + out_args >> status; + out_args >> interface_path; + } + + return status; +} + +nirio_status usrprio_rpc_client::niusrprio_download_fpga_to_flash(NIUSRPRIO_DOWNLOAD_FPGA_TO_FLASH_ARGS) +/* +#define NIUSRPRIO_DOWNLOAD_FPGA_TO_FLASH_ARGS \ + const boost::uint32_t& interface_num, \ + const std::string& bitstream_path +*/ +{ + usrprio_rpc::func_args_writer_t in_args; + usrprio_rpc::func_args_reader_t out_args; + nirio_status status = NiRio_Status_Success; + + in_args << resource; + in_args << bitstream_path; + + static const boost::uint32_t DOWNLOAD_FPGA_TIMEOUT = 1200000; + status = _boost_error_to_nirio_status( + _rpc_client.call(NIUSRPRIO_DOWNLOAD_FPGA_TO_FLASH, in_args, out_args, + boost::posix_time::milliseconds(DOWNLOAD_FPGA_TIMEOUT))); + + if (nirio_status_not_fatal(status)) { + out_args >> status; + } + + return status; +} + +nirio_status usrprio_rpc_client::niusrprio_download_bitstream_to_fpga(NIUSRPRIO_DOWNLOAD_BITSTREAM_TO_FPGA_ARGS) +/* +#define NIUSRPRIO_DOWNLOAD_BITSTREAM_TO_FPGA_ARGS \ + const std::string& resource +*/ +{ + usrprio_rpc::func_args_writer_t in_args; + usrprio_rpc::func_args_reader_t out_args; + nirio_status status = NiRio_Status_Success; + + in_args << resource; + + status = _boost_error_to_nirio_status( + _rpc_client.call(NIUSRPRIO_DOWNLOAD_BITSTREAM_TO_FPGA, in_args, out_args, _timeout)); + + if (nirio_status_not_fatal(status)) { + out_args >> status; + } + + return status; +} + +nirio_status usrprio_rpc_client::_boost_error_to_nirio_status(const boost::system::error_code& err) { + if (err) { + switch (err.value()) { + case boost::asio::error::connection_aborted: + case boost::asio::error::connection_refused: + case boost::asio::error::eof: + return NiRio_Status_RpcSessionError; + case boost::asio::error::timed_out: + case boost::asio::error::operation_aborted: + return NiRio_Status_RpcOperationError; + default: + return NiRio_Status_SoftwareFault; + } + } else { + return NiRio_Status_Success; + } +} + +}} diff --git a/host/lib/transport/nirio/status.cpp b/host/lib/transport/nirio/status.cpp new file mode 100644 index 000000000..f3f8d4cd1 --- /dev/null +++ b/host/lib/transport/nirio/status.cpp @@ -0,0 +1,55 @@ +// +// Copyright 2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + + +#include <uhd/transport/nirio/status.h> +#include <boost/format.hpp> + +namespace uhd { namespace niusrprio { + +#define NIRIO_ERR_INFO(CONST_NAME, ERR_CODE, ERR_MSG) \ + nirio_err_info(ERR_CODE, ERR_MSG), + +const nirio_err_info nirio_err_info::NIRIO_ERROR_TABLE[] = { + #include "../../../include/uhd/transport/nirio/nirio_err_template.h" +}; + +#undef NIRIO_ERR_INFO + +const size_t nirio_err_info::NIRIO_ERROR_TABLE_SIZE = sizeof(NIRIO_ERROR_TABLE)/sizeof(*NIRIO_ERROR_TABLE); + +const std::string lookup_err_msg(nirio_status code) { + std::string error_msg = (boost::format("Unknown error. (Error code %d)") % code).str(); + for (size_t i = 0; i < nirio_err_info::NIRIO_ERROR_TABLE_SIZE; i++) { + if (nirio_err_info::NIRIO_ERROR_TABLE[i].code == code) { + error_msg = (boost::format("%s (Error code %d)") % nirio_err_info::NIRIO_ERROR_TABLE[i].msg % code).str(); + break; + } + } + return error_msg; +} + +void nirio_status_to_exception(const nirio_status& status, const std::string& message) { + if (nirio_status_fatal(status)) { + throw uhd::runtime_error((boost::format("%s %s") % message % lookup_err_msg(status)).str()); + } +} + +}} + + + diff --git a/host/lib/transport/nirio_zero_copy.cpp b/host/lib/transport/nirio_zero_copy.cpp new file mode 100644 index 000000000..7b1e32fe0 --- /dev/null +++ b/host/lib/transport/nirio_zero_copy.cpp @@ -0,0 +1,352 @@ +// +// Copyright 2013-2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// 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/transport/nirio_zero_copy.hpp> +#include <stdio.h> +#include <uhd/transport/nirio/nirio_fifo.h> +#include <uhd/transport/nirio/nirio_fifo.h> +#include <uhd/transport/buffer_pool.hpp> +#include <uhd/utils/msg.hpp> +#include <uhd/utils/log.hpp> +#include <uhd/utils/atomic.hpp> +#include <boost/format.hpp> +#include <boost/make_shared.hpp> +#include <boost/thread/thread.hpp> //sleep +#include <vector> +#include <algorithm> // std::max +//@TODO: Move the register defs required by the class to a common location +#include "../usrp/x300/x300_regs.hpp" + +using namespace uhd; +using namespace uhd::transport; +using namespace uhd::niusrprio; + +typedef uint64_t fifo_data_t; + +class nirio_zero_copy_mrb : public managed_recv_buffer +{ +public: + nirio_zero_copy_mrb(nirio_fifo<fifo_data_t>& fifo, const size_t frame_size): + _fifo(fifo), _frame_size(frame_size) { } + + void release(void) + { + _fifo.release(_frame_size / sizeof(fifo_data_t)); + } + + UHD_INLINE sptr get_new(const double timeout, size_t &index) + { + nirio_status status = 0; + size_t elems_acquired, elems_remaining; + nirio_status_chain(_fifo.acquire( + _typed_buffer, _frame_size / sizeof(fifo_data_t), + static_cast<uint32_t>(timeout*1000), + elems_acquired, elems_remaining), status); + _length = elems_acquired * sizeof(fifo_data_t); + _buffer = static_cast<void*>(_typed_buffer); + + if (nirio_status_not_fatal(status)) { + index++; //Advances the caller's buffer + return make(this, _buffer, _length); + } else if (status == NiRio_Status_CommunicationTimeout) { + nirio_status_to_exception(status, "NI-RIO PCIe data transfer failed."); + return sptr(); + } else { + return sptr(); //NULL for timeout or error. + } + } + +private: + nirio_fifo<fifo_data_t>& _fifo; + fifo_data_t* _typed_buffer; + const size_t _frame_size; + size_t _num_frames; +}; + +class nirio_zero_copy_msb : public managed_send_buffer +{ +public: + nirio_zero_copy_msb(nirio_fifo<fifo_data_t>& fifo, const size_t frame_size): + _fifo(fifo), _frame_size(frame_size) { } + + void release(void) + { + _fifo.release(_frame_size / sizeof(fifo_data_t)); + } + + UHD_INLINE sptr get_new(const double timeout, size_t &index) + { + nirio_status status = 0; + size_t elems_acquired, elems_remaining; + nirio_status_chain(_fifo.acquire( + _typed_buffer, _frame_size / sizeof(fifo_data_t), + static_cast<uint32_t>(timeout*1000), + elems_acquired, elems_remaining), status); + _length = elems_acquired * sizeof(fifo_data_t); + _buffer = static_cast<void*>(_typed_buffer); + + if (nirio_status_not_fatal(status)) { + index++; //Advances the caller's buffer + return make(this, _buffer, _length); + } else if (status == NiRio_Status_CommunicationTimeout) { + nirio_status_to_exception(status, "NI-RIO PCIe data transfer failed."); + return sptr(); + } else { + return sptr(); //NULL for timeout or error. + } + } + +private: + nirio_fifo<fifo_data_t>& _fifo; + fifo_data_t* _typed_buffer; + const size_t _frame_size; + size_t _num_frames; +}; + +class nirio_zero_copy_impl : public nirio_zero_copy { +public: + typedef boost::shared_ptr<nirio_zero_copy_impl> sptr; + + nirio_zero_copy_impl( + uhd::niusrprio::niusrprio_session::sptr fpga_session, + uint32_t instance, + const zero_copy_xport_params& xport_params + ): + _fpga_session(fpga_session), + _fifo_instance(instance), + _xport_params(xport_params), + _next_recv_buff_index(0), _next_send_buff_index(0) + { + UHD_LOG << boost::format("Creating PCIe transport for channel %d") % instance << std::endl; + UHD_LOG << boost::format("nirio zero-copy RX transport configured with frame size = %u, #frames = %u, buffer size = %u\n") + % _xport_params.recv_frame_size % _xport_params.num_recv_frames % + (_xport_params.recv_frame_size * _xport_params.num_recv_frames); + UHD_LOG << boost::format("nirio zero-copy TX transport configured with frame size = %u, #frames = %u, buffer size = %u\n") + % _xport_params.send_frame_size % _xport_params.num_send_frames % (_xport_params.send_frame_size * _xport_params.num_send_frames); + + _recv_buffer_pool = buffer_pool::make(_xport_params.num_recv_frames, _xport_params.recv_frame_size); + _send_buffer_pool = buffer_pool::make(_xport_params.num_send_frames, _xport_params.send_frame_size); + + nirio_status status = 0; + size_t actual_depth = 0, actual_size = 0; + + //Configure frame width + nirio_status_chain( + _proxy().poke(PCIE_TX_DMA_REG(DMA_FRAME_SIZE_REG, _fifo_instance), + static_cast<uint32_t>(_xport_params.send_frame_size/sizeof(fifo_data_t))), + status); + nirio_status_chain( + _proxy().poke(PCIE_RX_DMA_REG(DMA_FRAME_SIZE_REG, _fifo_instance), + static_cast<uint32_t>(_xport_params.recv_frame_size/sizeof(fifo_data_t))), + status); + //Config 32-bit word flipping and Reset DMA streams + nirio_status_chain( + _proxy().poke(PCIE_TX_DMA_REG(DMA_CTRL_STATUS_REG, _fifo_instance), + DMA_CTRL_SW_BUF_U32 | DMA_CTRL_RESET), + status); + nirio_status_chain( + _proxy().poke(PCIE_RX_DMA_REG(DMA_CTRL_STATUS_REG, _fifo_instance), + DMA_CTRL_SW_BUF_U32 | DMA_CTRL_RESET), + status); + + //Create FIFOs + nirio_status_chain( + _fpga_session->create_rx_fifo(_fifo_instance, _recv_fifo), + status); + nirio_status_chain( + _fpga_session->create_tx_fifo(_fifo_instance, _send_fifo), + status); + + if ((_recv_fifo.get() != NULL) && (_send_fifo.get() != NULL)) { + //Initialize FIFOs + nirio_status_chain( + _recv_fifo->initialize( + (_xport_params.recv_frame_size*_xport_params.num_recv_frames)/sizeof(fifo_data_t), + actual_depth, actual_size), + status); + nirio_status_chain( + _send_fifo->initialize( + (_xport_params.send_frame_size*_xport_params.num_send_frames)/sizeof(fifo_data_t), + actual_depth, actual_size), + status); + + _proxy().get_rio_quirks().add_tx_fifo(_fifo_instance); + + nirio_status_chain(_recv_fifo->start(), status); + nirio_status_chain(_send_fifo->start(), status); + + if (nirio_status_not_fatal(status)) { + //Flush RX kernel buffers in case some cruft was + //left behind from the last run + _flush_rx_buff(); + + //allocate re-usable managed receive buffers + for (size_t i = 0; i < get_num_recv_frames(); i++){ + _mrb_pool.push_back(boost::shared_ptr<nirio_zero_copy_mrb>(new nirio_zero_copy_mrb( + *_recv_fifo, 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<nirio_zero_copy_msb>(new nirio_zero_copy_msb( + *_send_fifo, get_send_frame_size()))); + } + } + } else { + nirio_status_chain(NiRio_Status_ResourceNotInitialized, status); + } + + nirio_status_to_exception(status, "Could not create nirio_zero_copy transport."); + } + + virtual ~nirio_zero_copy_impl() + { + _proxy().get_rio_quirks().remove_tx_fifo(_fifo_instance); + + //Reset DMA streams (Teardown, so don't status chain) + _proxy().poke(PCIE_TX_DMA_REG(DMA_CTRL_STATUS_REG, _fifo_instance), DMA_CTRL_RESET); + _proxy().poke(PCIE_RX_DMA_REG(DMA_CTRL_STATUS_REG, _fifo_instance), DMA_CTRL_RESET); + + _flush_rx_buff(); + + //Stop DMA channels. Stop is called in the fifo dtor but + //it doesn't hurt to do it here. + _send_fifo->stop(); + _recv_fifo->stop(); + } + + /******************************************************************* + * 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 == _xport_params.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 _xport_params.num_recv_frames;} + size_t get_recv_frame_size(void) const {return _xport_params.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 == _xport_params.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 _xport_params.num_send_frames;} + size_t get_send_frame_size(void) const {return _xport_params.send_frame_size;} + +private: + + UHD_INLINE niriok_proxy& _proxy() { return _fpga_session->get_kernel_proxy(); } + + UHD_INLINE void _flush_rx_buff() + { + nirio_status flush_status = 0; + while (nirio_status_not_fatal(flush_status)) { + static const size_t NUM_ELEMS_TO_FLUSH = 1; + static const uint32_t FLUSH_TIMEOUT_IN_MS = 0; + + fifo_data_t* flush_data_ptr = NULL; + size_t flush_elems_acquired = 0, flush_elems_remaining = 0; + flush_status = _recv_fifo->acquire( + flush_data_ptr, NUM_ELEMS_TO_FLUSH, FLUSH_TIMEOUT_IN_MS, + flush_elems_acquired, flush_elems_remaining); + if (nirio_status_not_fatal(flush_status)) { + _recv_fifo->release(flush_elems_acquired); + } + } + } + + //memory management -> buffers and fifos + niusrprio::niusrprio_session::sptr _fpga_session; + uint32_t _fifo_instance; + nirio_fifo<fifo_data_t>::sptr _recv_fifo, _send_fifo; + const zero_copy_xport_params _xport_params; + buffer_pool::sptr _recv_buffer_pool, _send_buffer_pool; + std::vector<boost::shared_ptr<nirio_zero_copy_msb> > _msb_pool; + std::vector<boost::shared_ptr<nirio_zero_copy_mrb> > _mrb_pool; + size_t _next_recv_buff_index, _next_send_buff_index; +}; + + +nirio_zero_copy::sptr nirio_zero_copy::make( + uhd::niusrprio::niusrprio_session::sptr fpga_session, + const uint32_t instance, + const zero_copy_xport_params& default_buff_args, + const device_addr_t &hints +){ + //Initialize xport_params + zero_copy_xport_params xport_params = default_buff_args; + + //The kernel buffer for this transport must be (num_frames * frame_size) big. Unlike ethernet, + //where the kernel buffer size is independent of the circular buffer size for the transport, + //it is possible for users to over constrain the system when they set the num_frames and the buff_size + //So we give buff_size priority over num_frames and throw an error if they conflict. + + //RX + xport_params.recv_frame_size = size_t(hints.cast<double>("recv_frame_size", default_buff_args.recv_frame_size)); + + size_t usr_num_recv_frames = static_cast<size_t>( + hints.cast<double>("num_recv_frames", default_buff_args.num_recv_frames)); + size_t usr_recv_buff_size = static_cast<size_t>( + hints.cast<double>("recv_buff_size", default_buff_args.num_recv_frames)); + + if (hints.has_key("num_recv_frames") and hints.has_key("recv_buff_size")) { + if (usr_recv_buff_size < xport_params.recv_frame_size) + throw uhd::value_error("recv_buff_size must be equal to or greater than (num_recv_frames * recv_frame_size)"); + + if ((usr_recv_buff_size/xport_params.recv_frame_size) != usr_num_recv_frames) + throw uhd::value_error("Conflicting values for recv_buff_size and num_recv_frames"); + } + + if (hints.has_key("recv_buff_size")) { + xport_params.num_recv_frames = std::max<size_t>(1, usr_recv_buff_size/xport_params.recv_frame_size); //Round down + } else if (hints.has_key("num_recv_frames")) { + xport_params.num_recv_frames = usr_num_recv_frames; + } + + //TX + xport_params.send_frame_size = size_t(hints.cast<double>("send_frame_size", default_buff_args.send_frame_size)); + + size_t usr_num_send_frames = static_cast<size_t>( + hints.cast<double>("num_send_frames", default_buff_args.num_send_frames)); + size_t usr_send_buff_size = static_cast<size_t>( + hints.cast<double>("send_buff_size", default_buff_args.num_send_frames)); + + if (hints.has_key("num_send_frames") and hints.has_key("send_buff_size")) { + if (usr_send_buff_size < xport_params.send_frame_size) + throw uhd::value_error("send_buff_size must be equal to or greater than (num_send_frames * send_frame_size)"); + + if ((usr_send_buff_size/xport_params.send_frame_size) != usr_num_send_frames) + throw uhd::value_error("Conflicting values for send_buff_size and num_send_frames"); + } + + if (hints.has_key("send_buff_size")) { + xport_params.num_send_frames = std::max<size_t>(1, usr_send_buff_size/xport_params.send_frame_size); //Round down + } else if (hints.has_key("num_send_frames")) { + xport_params.num_send_frames = usr_num_send_frames; + } + + return nirio_zero_copy::sptr(new nirio_zero_copy_impl(fpga_session, instance, xport_params)); +} + diff --git a/host/lib/transport/super_recv_packet_handler.hpp b/host/lib/transport/super_recv_packet_handler.hpp index 5080182d6..5fdf2594d 100644 --- a/host/lib/transport/super_recv_packet_handler.hpp +++ b/host/lib/transport/super_recv_packet_handler.hpp @@ -39,6 +39,13 @@ #include <iostream> #include <vector> +// Included for debugging +#ifdef UHD_TXRX_DEBUG_PRINTS +#include <boost/format.hpp> +#include <boost/thread/thread.hpp> +#include "boost/date_time/posix_time/posix_time.hpp" +#endif + namespace uhd{ namespace transport{ namespace sph{ UHD_INLINE boost::uint32_t get_context_code( @@ -76,6 +83,10 @@ public: _queue_error_for_next_call(false), _buffers_infos_index(0) { + #ifdef ERROR_INJECT_DROPPED_PACKETS + recvd_packets = 0; + #endif + this->resize(size); set_alignment_failure_threshold(1000); } @@ -142,6 +153,32 @@ public: } /*! + * Flush all transports in the streamer: + * This calls into get_and_process_single_packet(), + * so the sequence and flow control are handled. + * However, the packet payload is discarded. + */ + void flush_all(const double timeout = 0.0) + { + increment_buffer_info(); //increment to next buffer + + for (size_t i = 0; i < _props.size(); i++) + { + while (true) //while (_props.at(i).get_buff(timeout)); + { + //receive a single packet from the transport + try + { + if (get_and_process_single_packet(i, + get_prev_buffer_info(), + get_curr_buffer_info(), + timeout) == PACKET_TIMEOUT_ERROR) break; + }catch(...){} + } + } + } + + /*! * Set the function to handle flow control * \param xport_chan which transport channel * \param handle_flowctrl the callback function @@ -213,7 +250,12 @@ public: buffs, nsamps_per_buff, metadata, timeout ); - if (one_packet) return accum_num_samps; + if (one_packet){ +#ifdef UHD_TXRX_DEBUG_PRINTS + dbg_gather_data(nsamps_per_buff, accum_num_samps, metadata, timeout, one_packet); +#endif + return accum_num_samps; + } //first recv had an error code set, return immediately if (metadata.error_code != rx_metadata_t::ERROR_CODE_NONE) return accum_num_samps; @@ -232,11 +274,13 @@ public: } accum_num_samps += num_samps; } +#ifdef UHD_TXRX_DEBUG_PRINTS + dbg_gather_data(nsamps_per_buff, accum_num_samps, metadata, timeout, one_packet); +#endif return accum_num_samps; } private: - vrt_unpacker_type _vrt_unpacker; size_t _header_offset_words32; double _tick_rate, _samp_rate; @@ -264,6 +308,13 @@ private: //! information stored for a received buffer struct per_buffer_info_type{ + void reset() + { + buff.reset(); + vrt_hdr = NULL; + time = time_spec_t(0.0); + copy_buff = NULL; + } managed_recv_buffer::sptr buff; const boost::uint32_t *vrt_hdr; vrt::if_packet_info_t ifpi; @@ -280,6 +331,17 @@ private: data_bytes_to_copy(0), fragment_offset_in_samps(0) {/* NOP */} + void reset() + { + indexes_todo.set(); + alignment_time = time_spec_t(0.0); + alignment_time_valid = false; + data_bytes_to_copy = 0; + fragment_offset_in_samps = 0; + metadata.reset(); + for (size_t i = 0; i < size(); i++) + at(i).reset(); + } boost::dynamic_bitset<> indexes_todo; //used in alignment logic time_spec_t alignment_time; //used in alignment logic bool alignment_time_valid; //used in alignment logic @@ -305,6 +367,10 @@ private: PACKET_SEQUENCE_ERROR }; + #ifdef ERROR_INJECT_DROPPED_PACKETS + int recvd_packets; + #endif + /******************************************************************* * Get and process a single packet from the transport: * Receive a single packet at the given index. @@ -322,6 +388,16 @@ private: buff = _props[index].get_buff(timeout); if (buff.get() == NULL) return PACKET_TIMEOUT_ERROR; + #ifdef ERROR_INJECT_DROPPED_PACKETS + if (++recvd_packets > 1000) + { + recvd_packets = 0; + buff.reset(); + buff = _props[index].get_buff(timeout); + if (buff.get() == NULL) return PACKET_TIMEOUT_ERROR; + } + #endif + //bounds check before extract size_t num_packet_words32 = buff->size()/sizeof(boost::uint32_t); if (num_packet_words32 <= _header_offset_words32){ @@ -339,7 +415,7 @@ private: //handle flow control if (_props[index].handle_flowctrl) { - if ((info.ifpi.packet_count % _props[index].fc_update_window/2) == 0) + if ((info.ifpi.packet_count % _props[index].fc_update_window) == 0) { _props[index].handle_flowctrl(info.ifpi.packet_count); } @@ -411,7 +487,10 @@ private: ******************************************************************/ UHD_INLINE void get_aligned_buffs(double timeout){ + get_prev_buffer_info().reset(); // no longer need the previous info - reset it for future use + increment_buffer_info(); //increment to next buffer + buffers_info_type &prev_info = get_prev_buffer_info(); buffers_info_type &curr_info = get_curr_buffer_info(); buffers_info_type &next_info = get_next_buffer_info(); @@ -440,12 +519,6 @@ private: "The receive packet handler caught an exception.\n%s" ) % e.what() << std::endl; std::swap(curr_info, next_info); //save progress from curr -> next - curr_info.metadata.has_time_spec = false; - curr_info.metadata.time_spec = time_spec_t(0.0); - curr_info.metadata.more_fragments = false; - curr_info.metadata.fragment_offset = 0; - curr_info.metadata.start_of_burst = false; - curr_info.metadata.end_of_burst = false; curr_info.metadata.error_code = rx_metadata_t::ERROR_CODE_BAD_PACKET; return; } @@ -470,10 +543,6 @@ private: std::swap(curr_info, next_info); //save progress from curr -> next curr_info.metadata.has_time_spec = next_info[index].ifpi.has_tsf; curr_info.metadata.time_spec = next_info[index].time; - curr_info.metadata.more_fragments = false; - curr_info.metadata.fragment_offset = 0; - curr_info.metadata.start_of_burst = false; - curr_info.metadata.end_of_burst = false; curr_info.metadata.error_code = rx_metadata_t::error_code_t(get_context_code(next_info[index].vrt_hdr, next_info[index].ifpi)); if (curr_info.metadata.error_code == rx_metadata_t::ERROR_CODE_OVERFLOW){ _props[index].handle_overflow(); @@ -483,12 +552,6 @@ private: case PACKET_TIMEOUT_ERROR: std::swap(curr_info, next_info); //save progress from curr -> next - curr_info.metadata.has_time_spec = false; - curr_info.metadata.time_spec = time_spec_t(0.0); - curr_info.metadata.more_fragments = false; - curr_info.metadata.fragment_offset = 0; - curr_info.metadata.start_of_burst = false; - curr_info.metadata.end_of_burst = false; curr_info.metadata.error_code = rx_metadata_t::ERROR_CODE_TIMEOUT; return; @@ -498,10 +561,7 @@ private: curr_info.metadata.has_time_spec = prev_info.metadata.has_time_spec; curr_info.metadata.time_spec = prev_info.metadata.time_spec + time_spec_t::from_ticks( prev_info[index].ifpi.num_payload_words32*sizeof(boost::uint32_t)/_bytes_per_otw_item, _samp_rate); - curr_info.metadata.more_fragments = false; - curr_info.metadata.fragment_offset = 0; - curr_info.metadata.start_of_burst = false; - curr_info.metadata.end_of_burst = false; + curr_info.metadata.out_of_sequence = true; curr_info.metadata.error_code = rx_metadata_t::ERROR_CODE_OVERFLOW; UHD_MSG(fastpath) << "D"; return; @@ -516,12 +576,6 @@ private: "However, a timestamp match could not be determined.\n" ) % iterations << std::endl; std::swap(curr_info, next_info); //save progress from curr -> next - curr_info.metadata.has_time_spec = false; - curr_info.metadata.time_spec = time_spec_t(0.0); - curr_info.metadata.more_fragments = false; - curr_info.metadata.fragment_offset = 0; - curr_info.metadata.start_of_burst = false; - curr_info.metadata.end_of_burst = false; curr_info.metadata.error_code = rx_metadata_t::ERROR_CODE_ALIGNMENT; _props[index].handle_overflow(); return; @@ -554,13 +608,8 @@ private: const size_t buffer_offset_bytes = 0 ){ //get the next buffer if the current one has expired - if (get_curr_buffer_info().data_bytes_to_copy == 0){ - - //reset current buffer info members for reuse - get_curr_buffer_info().fragment_offset_in_samps = 0; - get_curr_buffer_info().alignment_time_valid = false; - get_curr_buffer_info().indexes_todo.set(); - + if (get_curr_buffer_info().data_bytes_to_copy == 0) + { //perform receive with alignment logic get_aligned_buffs(timeout); } @@ -641,6 +690,65 @@ private: size_t _convert_buffer_offset_bytes; size_t _convert_bytes_to_copy; + /* + * This last section is only for debugging purposes. + * It causes a lot of prints to stderr which can be piped to a file. + * Gathered data can be used to post process it with external tools. + */ +#ifdef UHD_TXRX_DEBUG_PRINTS + struct dbg_recv_stat_t { + dbg_recv_stat_t(long wc, size_t nspb, size_t nsr, uhd::rx_metadata_t md, double to, bool op, double rate): + wallclock(wc), nsamps_per_buff(nspb), nsamps_recv(nsr), metadata(md), timeout(to), one_packet(op), samp_rate(rate) + {} + long wallclock; + size_t nsamps_per_buff; + size_t nsamps_recv; + uhd::rx_metadata_t metadata; + double timeout; + bool one_packet; + double samp_rate; + // Create a formatted print line for all the info gathered in this struct. + std::string print_line() { + boost::format fmt("recv,%ld,%f,%i,%i,%s,%i,%s,%s,%s,%i,%s,%ld"); + fmt % wallclock; + fmt % timeout % (int)nsamps_per_buff % (int) nsamps_recv; + fmt % (one_packet ? "true":"false"); + fmt % metadata.error_code; + fmt % (metadata.start_of_burst ? "true":"false") % (metadata.end_of_burst ? "true":"false"); + fmt % (metadata.more_fragments ? "true":"false") % (int)metadata.fragment_offset; + fmt % (metadata.has_time_spec ? "true":"false") % metadata.time_spec.to_ticks(samp_rate); + return fmt.str(); + } + }; + + void dbg_gather_data(const size_t nsamps_per_buff, const size_t nsamps_recv, + uhd::rx_metadata_t &metadata, const double timeout, + const bool one_packet, + bool dbg_print_directly = true + ) + { + // Initialize a struct with all available data. It can return a formatted string with all infos if wanted. + dbg_recv_stat_t data(boost::get_system_time().time_of_day().total_microseconds(), + nsamps_per_buff, + nsamps_recv, + metadata, + timeout, + one_packet, + _samp_rate + ); + if(dbg_print_directly) { + dbg_print_err(data.print_line()); + } + } + + + + void dbg_print_err(std::string msg) { + std::string dbg_prefix("super_recv_packet_handler,"); + msg = dbg_prefix + msg; + fprintf(stderr, "%s\n", msg.c_str()); + } +#endif }; 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 ae483d1f3..c2810842e 100644 --- a/host/lib/transport/super_send_packet_handler.hpp +++ b/host/lib/transport/super_send_packet_handler.hpp @@ -35,7 +35,18 @@ #include <iostream> #include <vector> -namespace uhd{ namespace transport{ namespace sph{ +#ifdef UHD_TXRX_DEBUG_PRINTS +// Included for debugging +#include <boost/format.hpp> +#include <boost/thread/thread.hpp> +#include "boost/date_time/posix_time/posix_time.hpp" +#include <map> +#include <fstream> +#endif + +namespace uhd { +namespace transport { +namespace sph { /*********************************************************************** * Super send packet handler @@ -56,7 +67,7 @@ public: * \param size the number of transport channels */ send_packet_handler(const size_t size = 1): - _next_packet_seq(0) + _next_packet_seq(0), _cached_metadata(false) { this->set_enable_trailer(true); this->resize(size); @@ -183,6 +194,23 @@ public: if_packet_info.sob = metadata.start_of_burst; if_packet_info.eob = metadata.end_of_burst; + /* + * Metadata is cached when we get a send requesting a start of burst with no samples. + * It is applied here on the next call to send() that actually has samples to send. + */ + if (_cached_metadata && nsamps_per_buff != 0) + { + // If the new metada has a time_spec, do not use the cached time_spec. + if (!metadata.has_time_spec) + { + if_packet_info.has_tsf = _metadata_cache.has_time_spec; + if_packet_info.tsf = _metadata_cache.time_spec.to_ticks(_tick_rate); + } + if_packet_info.sob = _metadata_cache.start_of_burst; + if_packet_info.eob = _metadata_cache.end_of_burst; + _cached_metadata = false; + } + if (nsamps_per_buff <= _max_samples_per_packet){ //TODO remove this code when sample counts of zero are supported by hardware @@ -190,13 +218,27 @@ public: static const boost::uint64_t zero = 0; _zero_buffs.resize(buffs.size(), &zero); - if (nsamps_per_buff == 0) return send_one_packet( - _zero_buffs, 1, if_packet_info, timeout - ) & 0x0; + if (nsamps_per_buff == 0) + { + // if this is a start of a burst and there are no samples + if (metadata.start_of_burst) + { + // cache metadata and apply on the next send() + _metadata_cache = metadata; + _cached_metadata = true; + return 0; + } else { + // send requests with no samples are handled here (such as end of burst) + return send_one_packet(_zero_buffs, 1, if_packet_info, timeout) & 0x0; + } + } #endif - return send_one_packet(buffs, nsamps_per_buff, if_packet_info, timeout); - } + size_t nsamps_sent = send_one_packet(buffs, nsamps_per_buff, if_packet_info, timeout); +#ifdef UHD_TXRX_DEBUG_PRINTS + dbg_print_send(nsamps_per_buff, nsamps_sent, metadata, timeout); +#endif + return nsamps_sent; } size_t total_num_samps_sent = 0; //false until final fragment @@ -226,11 +268,14 @@ public: //send the final fragment with the helper function if_packet_info.eob = metadata.end_of_burst; - return total_num_samps_sent + send_one_packet( - buffs, final_length, - if_packet_info, timeout, - total_num_samps_sent*_bytes_per_cpu_item - ); + size_t nsamps_sent = total_num_samps_sent + + send_one_packet(buffs, final_length, if_packet_info, timeout, + total_num_samps_sent * _bytes_per_cpu_item); +#ifdef UHD_TXRX_DEBUG_PRINTS + dbg_print_send(nsamps_per_buff, nsamps_sent, metadata, timeout); + +#endif + return nsamps_sent; } private: @@ -255,6 +300,53 @@ private: size_t _next_packet_seq; bool _has_tlr; async_receiver_type _async_receiver; + bool _cached_metadata; + uhd::tx_metadata_t _metadata_cache; + +#ifdef UHD_TXRX_DEBUG_PRINTS + struct dbg_send_stat_t { + dbg_send_stat_t(long wc, size_t nspb, size_t nss, uhd::tx_metadata_t md, double to, double rate): + wallclock(wc), nsamps_per_buff(nspb), nsamps_sent(nss), metadata(md), timeout(to), samp_rate(rate) + {} + long wallclock; + size_t nsamps_per_buff; + size_t nsamps_sent; + uhd::tx_metadata_t metadata; + double timeout; + double samp_rate; + // Create a formatted print line for all the info gathered in this struct. + std::string print_line() { + boost::format fmt("send,%ld,%f,%i,%i,%s,%s,%s,%ld"); + fmt % wallclock; + fmt % timeout % (int)nsamps_per_buff % (int) nsamps_sent; + fmt % (metadata.start_of_burst ? "true":"false") % (metadata.end_of_burst ? "true":"false"); + fmt % (metadata.has_time_spec ? "true":"false") % metadata.time_spec.to_ticks(samp_rate); + return fmt.str(); + } + }; + + void dbg_print_send(size_t nsamps_per_buff, size_t nsamps_sent, + const uhd::tx_metadata_t &metadata, const double timeout, + bool dbg_print_directly = true) + { + dbg_send_stat_t data(boost::get_system_time().time_of_day().total_microseconds(), + nsamps_per_buff, + nsamps_sent, + metadata, + timeout, + _samp_rate + ); + if(dbg_print_directly){ + dbg_print_err(data.print_line()); + } + } + void dbg_print_err(std::string msg) { + msg = "super_send_packet_handler," + msg; + fprintf(stderr, "%s\n", msg.c_str()); + } + + +#endif /******************************************************************* * Send a single packet: @@ -266,6 +358,7 @@ private: const double timeout, 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*_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); @@ -374,6 +467,8 @@ private: size_t _max_num_samps; }; -}}} //namespace +} // namespace sph +} // namespace transport +} // namespace uhd #endif /* INCLUDED_LIBUHD_TRANSPORT_SUPER_SEND_PACKET_HANDLER_HPP */ diff --git a/host/lib/transport/tcp_zero_copy.cpp b/host/lib/transport/tcp_zero_copy.cpp new file mode 100644 index 000000000..402bda1e8 --- /dev/null +++ b/host/lib/transport/tcp_zero_copy.cpp @@ -0,0 +1,225 @@ +// +// Copyright 2010-2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// 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/tcp_zero_copy.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 <boost/make_shared.hpp> +#include <boost/thread/thread.hpp> //sleep +#include <vector> + +using namespace uhd; +using namespace uhd::transport; +namespace asio = boost::asio; + +static const size_t DEFAULT_NUM_FRAMES = 32; +static const size_t DEFAULT_FRAME_SIZE = 2048; + +/*********************************************************************** + * Reusable managed receiver buffer: + * - get_new performs the recv operation + **********************************************************************/ +class tcp_zero_copy_asio_mrb : public managed_recv_buffer{ +public: + tcp_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){ + _claimer.release(); + } + + UHD_INLINE sptr get_new(const double timeout, size_t &index){ + if (not _claimer.claim_with_wait(timeout)) return sptr(); + + #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 + + 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; + int _sock_fd; + size_t _frame_size; + ssize_t _len; + simple_claimer _claimer; +}; + +/*********************************************************************** + * Reusable managed send buffer: + * - commit performs the send operation + **********************************************************************/ +class tcp_zero_copy_asio_msb : public managed_send_buffer{ +public: + tcp_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){ + //Retry logic because send may fail with ENOBUFS. + //This is known to occur at least on some OSX systems. + //But it should be safe to always check for the error. + while (true) + { + this->commit(_frame_size); //always full size frames to avoid pkt coalescing + const ssize_t ret = ::send(_sock_fd, (const char *)_mem, size(), 0); + if (ret == ssize_t(size())) break; + if (ret == -1 and errno == ENOBUFS) + { + boost::this_thread::sleep(boost::posix_time::microseconds(1)); + continue; //try to send again + } + UHD_ASSERT_THROW(ret == ssize_t(size())); + } + _claimer.release(); + } + + 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 *_mem; + int _sock_fd; + size_t _frame_size; + simple_claimer _claimer; +}; + +/*********************************************************************** + * Zero Copy TCP implementation with ASIO: + * This is the portable zero copy implementation for systems + * where a faster, platform specific solution is not available. + * However, it is not a true zero copy implementation as each + * send and recv requires a copy operation to/from userspace. + **********************************************************************/ +class tcp_zero_copy_asio_impl : public tcp_zero_copy{ +public: + typedef boost::shared_ptr<tcp_zero_copy_asio_impl> sptr; + + tcp_zero_copy_asio_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", DEFAULT_FRAME_SIZE))), + _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", DEFAULT_FRAME_SIZE))), + _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) + { + UHD_LOG << boost::format("Creating tcp transport for %s %s") % addr % port << std::endl; + + //resolve the address + asio::ip::tcp::resolver resolver(_io_service); + asio::ip::tcp::resolver::query query(asio::ip::tcp::v4(), addr, port); + asio::ip::tcp::endpoint receiver_endpoint = *resolver.resolve(query); + + //create, open, and connect the socket + _socket.reset(new asio::ip::tcp::socket(_io_service)); + _socket->connect(receiver_endpoint); + _sock_fd = _socket->native(); + + //packets go out ASAP + asio::ip::tcp::no_delay option(true); + _socket->set_option(option); + + //allocate re-usable managed receive buffers + for (size_t i = 0; i < get_num_recv_frames(); i++){ + _mrb_pool.push_back(boost::make_shared<tcp_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::make_shared<tcp_zero_copy_asio_msb>( + _send_buffer_pool->at(i), _sock_fd, get_send_frame_size() + )); + } + } + + /******************************************************************* + * 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<tcp_zero_copy_asio_msb> > _msb_pool; + std::vector<boost::shared_ptr<tcp_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; + boost::shared_ptr<asio::ip::tcp::socket> _socket; + int _sock_fd; +}; + +/*********************************************************************** + * TCP zero copy make function + **********************************************************************/ +zero_copy_if::sptr tcp_zero_copy::make( + const std::string &addr, + const std::string &port, + const device_addr_t &hints +){ + zero_copy_if::sptr xport; + xport.reset(new tcp_zero_copy_asio_impl(addr, port, hints)); + while (xport->get_recv_buff(0.0)){} //flush + return xport; +} diff --git a/host/lib/transport/udp_wsa_zero_copy.cpp b/host/lib/transport/udp_wsa_zero_copy.cpp index 6fe4e3cad..031d26374 100644 --- a/host/lib/transport/udp_wsa_zero_copy.cpp +++ b/host/lib/transport/udp_wsa_zero_copy.cpp @@ -182,14 +182,15 @@ public: udp_zero_copy_wsa_impl( const std::string &addr, const std::string &port, + zero_copy_xport_params& xport_params, 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)), + _recv_frame_size(xport_params.recv_frame_size), + _num_recv_frames(xport_params.num_recv_frames), + _send_frame_size(xport_params.send_frame_size), + _num_send_frames(xport_params.num_send_frames), + _recv_buffer_pool(buffer_pool::make(xport_params.num_recv_frames, xport_params.recv_frame_size)), + _send_buffer_pool(buffer_pool::make(xport_params.num_send_frames, xport_params.send_frame_size)), _next_recv_buff_index(0), _next_send_buff_index(0) { #ifdef CHECK_REG_SEND_THRESH @@ -294,7 +295,17 @@ private: udp_zero_copy::sptr udp_zero_copy::make( const std::string &addr, const std::string &port, + const zero_copy_xport_params &default_buff_args, + udp_zero_copy::buff_params& buff_params_out, const device_addr_t &hints ){ - return sptr(new udp_zero_copy_wsa_impl(addr, port, hints)); + //Initialize xport_params + zero_copy_xport_params xport_params = default_buff_args; + + xport_params.recv_frame_size = size_t(hints.cast<double>("recv_frame_size", default_buff_args.recv_frame_size)); + xport_params.num_recv_frames = size_t(hints.cast<double>("num_recv_frames", default_buff_args.num_recv_frames)); + xport_params.send_frame_size = size_t(hints.cast<double>("send_frame_size", default_buff_args.send_frame_size)); + xport_params.num_send_frames = size_t(hints.cast<double>("num_send_frames", default_buff_args.num_send_frames)); + + return sptr(new udp_zero_copy_wsa_impl(addr, port, xport_params, hints)); } diff --git a/host/lib/transport/udp_zero_copy.cpp b/host/lib/transport/udp_zero_copy.cpp index 7b6a476f5..adc7d5585 100644 --- a/host/lib/transport/udp_zero_copy.cpp +++ b/host/lib/transport/udp_zero_copy.cpp @@ -158,14 +158,14 @@ public: udp_zero_copy_asio_impl( const std::string &addr, const std::string &port, - const device_addr_t &hints + const zero_copy_xport_params& xport_params ): - _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)), + _recv_frame_size(xport_params.recv_frame_size), + _num_recv_frames(xport_params.num_recv_frames), + _send_frame_size(xport_params.send_frame_size), + _num_send_frames(xport_params.num_send_frames), + _recv_buffer_pool(buffer_pool::make(xport_params.num_recv_frames, xport_params.recv_frame_size)), + _send_buffer_pool(buffer_pool::make(xport_params.num_send_frames, xport_params.send_frame_size)), _next_recv_buff_index(0), _next_send_buff_index(0) { UHD_LOG << boost::format("Creating udp transport for %s %s") % addr % port << std::endl; @@ -256,11 +256,12 @@ private: /*********************************************************************** * UDP zero copy make function **********************************************************************/ -template<typename Opt> static void resize_buff_helper( +template<typename Opt> static size_t resize_buff_helper( udp_zero_copy_asio_impl::sptr udp_trans, const size_t target_size, const std::string &name ){ + size_t actual_size = 0; std::string help_message; #if defined(UHD_PLATFORM_LINUX) help_message = str(boost::format( @@ -270,7 +271,7 @@ template<typename Opt> static void resize_buff_helper( //resize the buffer if size was provided if (target_size > 0){ - size_t actual_size = udp_trans->resize_buff<Opt>(target_size); + actual_size = udp_trans->resize_buff<Opt>(target_size); UHD_LOG << boost::format( "Target %s sock buff size: %d bytes\n" "Actual %s sock buff size: %d bytes" @@ -282,24 +283,54 @@ template<typename Opt> static void resize_buff_helper( "See the transport application notes on buffer resizing.\n%s" ) % name % target_size % actual_size % help_message; } + + return actual_size; } udp_zero_copy::sptr udp_zero_copy::make( const std::string &addr, const std::string &port, + const zero_copy_xport_params &default_buff_args, + udp_zero_copy::buff_params& buff_params_out, const device_addr_t &hints ){ - udp_zero_copy_asio_impl::sptr udp_trans( - new udp_zero_copy_asio_impl(addr, port, hints) - ); + //Initialize xport_params + zero_copy_xport_params xport_params = default_buff_args; + + xport_params.recv_frame_size = size_t(hints.cast<double>("recv_frame_size", default_buff_args.recv_frame_size)); + xport_params.num_recv_frames = size_t(hints.cast<double>("num_recv_frames", default_buff_args.num_recv_frames)); + xport_params.send_frame_size = size_t(hints.cast<double>("send_frame_size", default_buff_args.send_frame_size)); + xport_params.num_send_frames = size_t(hints.cast<double>("num_send_frames", default_buff_args.num_send_frames)); //extract buffer size hints from the device addr - size_t recv_buff_size = size_t(hints.cast<double>("recv_buff_size", 0.0)); - size_t send_buff_size = size_t(hints.cast<double>("send_buff_size", 0.0)); + size_t usr_recv_buff_size = size_t(hints.cast<double>("recv_buff_size", 0.0)); + size_t usr_send_buff_size = size_t(hints.cast<double>("send_buff_size", 0.0)); + + if (hints.has_key("recv_buff_size")) { + if (usr_recv_buff_size < xport_params.recv_frame_size * xport_params.num_recv_frames) { + throw uhd::value_error((boost::format( + "recv_buff_size must be equal to or greater than (num_recv_frames * recv_frame_size) where num_recv_frames=%d, recv_frame_size=%d") + % xport_params.num_recv_frames % xport_params.recv_frame_size).str()); + } + } + + if (hints.has_key("send_buff_size")) { + if (usr_send_buff_size < xport_params.send_frame_size * xport_params.num_send_frames) { + throw uhd::value_error((boost::format( + "send_buff_size must be equal to or greater than (num_send_frames * send_frame_size) where num_send_frames=%d, send_frame_size=%d") + % xport_params.num_send_frames % xport_params.send_frame_size).str()); + } + } + + udp_zero_copy_asio_impl::sptr udp_trans( + new udp_zero_copy_asio_impl(addr, port, xport_params) + ); //call the helper to resize send and recv buffers - resize_buff_helper<asio::socket_base::receive_buffer_size>(udp_trans, recv_buff_size, "recv"); - resize_buff_helper<asio::socket_base::send_buffer_size> (udp_trans, send_buff_size, "send"); + buff_params_out.recv_buff_size = + resize_buff_helper<asio::socket_base::receive_buffer_size>(udp_trans, usr_recv_buff_size, "recv"); + buff_params_out.send_buff_size = + resize_buff_helper<asio::socket_base::send_buffer_size> (udp_trans, usr_send_buff_size, "send"); return udp_trans; } diff --git a/host/lib/transport/xport_benchmarker.cpp b/host/lib/transport/xport_benchmarker.cpp new file mode 100644 index 000000000..d58dbea47 --- /dev/null +++ b/host/lib/transport/xport_benchmarker.cpp @@ -0,0 +1,155 @@ +// +// Copyright 2010-2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// 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 "xport_benchmarker.hpp" + +namespace uhd { namespace transport { + +const device_addr_t& xport_benchmarker::benchmark_throughput_chdr +( + zero_copy_if::sptr tx_transport, + zero_copy_if::sptr rx_transport, + boost::uint32_t sid, + bool big_endian, + boost::uint32_t duration_ms) +{ + vrt::if_packet_info_t pkt_info; + _initialize_chdr(tx_transport, rx_transport, sid, pkt_info); + _reset_counters(); + boost::posix_time::ptime start_time(boost::posix_time::microsec_clock::local_time()); + + _tx_thread.reset(new boost::thread(boost::bind(&xport_benchmarker::_stream_tx, this, tx_transport.get(), &pkt_info, big_endian))); + _rx_thread.reset(new boost::thread(boost::bind(&xport_benchmarker::_stream_rx, this, rx_transport.get(), &pkt_info, big_endian))); + + boost::this_thread::sleep(boost::posix_time::milliseconds(duration_ms)); + + _tx_thread->interrupt(); + _rx_thread->interrupt(); + _tx_thread->join(); + _rx_thread->join(); + + boost::posix_time::ptime stop_time(boost::posix_time::microsec_clock::local_time()); + double duration_s = ((double)(stop_time-start_time).total_microseconds())/1e6; + + boost::uint64_t tx_bytes = pkt_info.num_payload_words32*sizeof(uint32_t)*_num_tx_packets; + boost::uint64_t rx_bytes = pkt_info.num_payload_words32*sizeof(uint32_t)*_num_rx_packets; + double tx_rate = (((double)tx_bytes)/duration_s); + double rx_rate = (((double)rx_bytes)/duration_s); + + _results["TX-Bytes"] = (boost::format("%.2fMB") % (tx_bytes/(1024*1024))).str(); + _results["RX-Bytes"] = (boost::format("%.2fMB") % (rx_bytes/(1024*1024))).str(); + _results["TX-Throughput"] = (boost::format("%.2fMB/s") % (tx_rate/(1024*1024))).str(); + _results["RX-Throughput"] = (boost::format("%.2fMB/s") % (rx_rate/(1024*1024))).str(); + _results["TX-Timeouts"] = boost::lexical_cast<std::string>(_num_tx_timeouts); + _results["RX-Timeouts"] = boost::lexical_cast<std::string>(_num_rx_timeouts); + _results["Data-Errors"] = boost::lexical_cast<std::string>(_num_data_errors); + + return _results; +} + +void xport_benchmarker::_stream_tx(zero_copy_if* transport, vrt::if_packet_info_t* pkt_info, bool big_endian) +{ + while (not boost::this_thread::interruption_requested()) { + managed_send_buffer::sptr buff = transport->get_send_buff(_tx_timeout); + if (buff) { + boost::uint32_t *packet_buff = buff->cast<boost::uint32_t *>(); + //Populate packet + if (big_endian) { + vrt::if_hdr_pack_be(packet_buff, *pkt_info); + } else { + vrt::if_hdr_pack_le(packet_buff, *pkt_info); + } + //send the buffer over the interface + buff->commit(sizeof(boost::uint32_t)*(pkt_info->num_packet_words32)); + _num_tx_packets++; + } else { + _num_tx_timeouts++; + } + } +} + +void xport_benchmarker::_stream_rx(zero_copy_if* transport, const vrt::if_packet_info_t* exp_pkt_info, bool big_endian) +{ + while (not boost::this_thread::interruption_requested()) { + managed_recv_buffer::sptr buff = transport->get_recv_buff(_rx_timeout); + if (buff) { + //Extract packet info + vrt::if_packet_info_t pkt_info; + pkt_info.link_type = exp_pkt_info->link_type; + pkt_info.num_packet_words32 = buff->size()/sizeof(boost::uint32_t); + const boost::uint32_t *packet_buff = buff->cast<const boost::uint32_t *>(); + + _num_rx_packets++; + + //unpacking can fail + try { + if (big_endian) { + vrt::if_hdr_unpack_be(packet_buff, pkt_info); + } else { + vrt::if_hdr_unpack_le(packet_buff, pkt_info); + } + + if (exp_pkt_info->packet_type != pkt_info.packet_type || + exp_pkt_info->num_payload_bytes != pkt_info.num_payload_bytes) { + _num_data_errors++; + } + } catch(const std::exception &ex) { + _num_data_errors++; + } + } else { + _num_rx_timeouts++; + } + } +} + +void xport_benchmarker::_reset_counters(void) +{ + _num_tx_packets = 0; + _num_rx_packets = 0; + _num_tx_timeouts = 0; + _num_rx_timeouts = 0; + _num_data_errors = 0; +} + +void xport_benchmarker::_initialize_chdr( + zero_copy_if::sptr tx_transport, + zero_copy_if::sptr rx_transport, + boost::uint32_t sid, + vrt::if_packet_info_t& pkt_info) +{ + _tx_timeout = 0.5; + _rx_timeout = 0.5; + + size_t frame_size = std::min(tx_transport->get_send_frame_size(), rx_transport->get_recv_frame_size()); + + pkt_info.link_type = vrt::if_packet_info_t::LINK_TYPE_CHDR; + pkt_info.packet_type = vrt::if_packet_info_t::PACKET_TYPE_DATA; + pkt_info.num_packet_words32 = (frame_size/sizeof(boost::uint32_t)); + pkt_info.num_payload_words32 = pkt_info.num_packet_words32 - 2; + pkt_info.num_payload_bytes = pkt_info.num_payload_words32*sizeof(boost::uint32_t); + pkt_info.packet_count = 0; + pkt_info.sob = false; + pkt_info.eob = false; + pkt_info.sid = sid; + pkt_info.has_sid = true; + pkt_info.has_cid = false; + pkt_info.has_tsi = false; + pkt_info.has_tsf = false; + pkt_info.has_tlr = false; +} + +}} diff --git a/host/lib/transport/xport_benchmarker.hpp b/host/lib/transport/xport_benchmarker.hpp new file mode 100644 index 000000000..9fca8d1fb --- /dev/null +++ b/host/lib/transport/xport_benchmarker.hpp @@ -0,0 +1,77 @@ +// +// Copyright 2010-2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// 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_XPORT_BENCHMARKER_HPP +#define INCLUDED_LIBUHD_XPORT_BENCHMARKER_HPP + +#include <uhd/transport/zero_copy.hpp> +#include <uhd/types/device_addr.hpp> +#include <uhd/utils/msg.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/thread/thread.hpp> +#include <uhd/transport/vrt_if_packet.hpp> + +namespace uhd { namespace transport { + +//Test class to benchmark a low-level transport object with a VITA/C-VITA data stream +class xport_benchmarker : boost::noncopyable { +public: + const device_addr_t& benchmark_throughput_chdr( + zero_copy_if::sptr tx_transport, + zero_copy_if::sptr rx_transport, + boost::uint32_t sid, + bool big_endian, + boost::uint32_t duration_ms); + +private: + void _stream_tx( + zero_copy_if* transport, + vrt::if_packet_info_t* pkt_info, + bool big_endian); + + void _stream_rx( + zero_copy_if* transport, + const vrt::if_packet_info_t* exp_pkt_info, + bool big_endian); + + void _initialize_chdr( + zero_copy_if::sptr tx_transport, + zero_copy_if::sptr rx_transport, + boost::uint32_t sid, + vrt::if_packet_info_t& pkt_info); + + void _reset_counters(void); + + boost::shared_ptr<boost::thread> _tx_thread; + boost::shared_ptr<boost::thread> _rx_thread; + + boost::uint64_t _num_tx_packets; + boost::uint64_t _num_rx_packets; + boost::uint64_t _num_tx_timeouts; + boost::uint64_t _num_rx_timeouts; + boost::uint64_t _num_data_errors; + + double _tx_timeout; + double _rx_timeout; + + device_addr_t _results; +}; + + +}} //namespace + +#endif /* INCLUDED_LIBUHD_XPORT_BENCHMARKER_HPP */ diff --git a/host/lib/usrp/CMakeLists.txt b/host/lib/usrp/CMakeLists.txt index f8c817df5..c8c2e6a8d 100644 --- a/host/lib/usrp/CMakeLists.txt +++ b/host/lib/usrp/CMakeLists.txt @@ -37,4 +37,5 @@ INCLUDE_SUBDIRECTORY(usrp1) INCLUDE_SUBDIRECTORY(usrp2) INCLUDE_SUBDIRECTORY(b100) INCLUDE_SUBDIRECTORY(e100) +INCLUDE_SUBDIRECTORY(x300) INCLUDE_SUBDIRECTORY(b200) diff --git a/host/lib/usrp/b100/b100_impl.cpp b/host/lib/usrp/b100/b100_impl.cpp index 305ba42a7..a47856b07 100644 --- a/host/lib/usrp/b100/b100_impl.cpp +++ b/host/lib/usrp/b100/b100_impl.cpp @@ -52,9 +52,9 @@ static device_addrs_t b100_find(const device_addr_t &hint) //return an empty list of addresses when type is set to non-b100 if (hint.has_key("type") and hint["type"] != "b100") return b100_addrs; - //Return an empty list of addresses when an address is specified, - //since an address is intended for a different, non-USB, device. - if (hint.has_key("addr")) return b100_addrs; + //Return an empty list of addresses when an address or resource is specified, + //since an address and resource is intended for a different, non-USB, device. + if (hint.has_key("addr") || hint.has_key("resource")) return b100_addrs; unsigned int vid, pid; diff --git a/host/lib/usrp/b100/b100_impl.hpp b/host/lib/usrp/b100/b100_impl.hpp index ab83c80a3..7d71d5ec3 100644 --- a/host/lib/usrp/b100/b100_impl.hpp +++ b/host/lib/usrp/b100/b100_impl.hpp @@ -105,8 +105,6 @@ public: bool recv_async_msg(uhd::async_metadata_t &, double); private: - uhd::property_tree::sptr _tree; - //controllers fifo_ctrl_excelsior::sptr _fifo_ctrl; i2c_core_200::sptr _fpga_i2c_ctrl; @@ -129,11 +127,6 @@ private: uhd::usrp::dboard_manager::sptr _dboard_manager; uhd::usrp::dboard_iface::sptr _dboard_iface; - //device properties interface - uhd::property_tree::sptr get_tree(void) const{ - return _tree; - } - std::vector<boost::weak_ptr<uhd::rx_streamer> > _rx_streamers; std::vector<boost::weak_ptr<uhd::tx_streamer> > _tx_streamers; diff --git a/host/lib/usrp/b200/b200_impl.cpp b/host/lib/usrp/b200/b200_impl.cpp index ab437af6b..8ed8e99af 100644 --- a/host/lib/usrp/b200/b200_impl.cpp +++ b/host/lib/usrp/b200/b200_impl.cpp @@ -53,9 +53,9 @@ static device_addrs_t b200_find(const device_addr_t &hint) //return an empty list of addresses when type is set to non-b200 if (hint.has_key("type") and hint["type"] != "b200") return b200_addrs; - //Return an empty list of addresses when an address is specified, - //since an address is intended for a different, non-USB, device. - if (hint.has_key("addr")) return b200_addrs; + //Return an empty list of addresses when an address or resource is specified, + //since an address and resource is intended for a different, non-USB, device. + if (hint.has_key("addr") || hint.has_key("resource")) return b200_addrs; unsigned int vid, pid; @@ -332,7 +332,7 @@ b200_impl::b200_impl(const device_addr_t &device_addr) data_xport_args // param hints ); while (_data_transport->get_recv_buff(0.0)){} //flush ctrl xport - _demux.reset(new recv_packet_demuxer_3000(_data_transport)); + _demux = recv_packet_demuxer_3000::make(_data_transport); //////////////////////////////////////////////////////////////////// // Init codec - turns on clocks diff --git a/host/lib/usrp/b200/b200_impl.hpp b/host/lib/usrp/b200/b200_impl.hpp index bee42679b..c88d14ad5 100644 --- a/host/lib/usrp/b200/b200_impl.hpp +++ b/host/lib/usrp/b200/b200_impl.hpp @@ -80,8 +80,9 @@ static const boost::uint32_t B200_LOCAL_RESP_SID = FLIP_SID(B200_LOCAL_CTRL_SID) **********************************************************************/ //! Implementation guts -struct b200_impl : public uhd::device +class b200_impl : public uhd::device { +public: //structors b200_impl(const uhd::device_addr_t &); ~b200_impl(void); @@ -91,8 +92,7 @@ struct b200_impl : public uhd::device uhd::tx_streamer::sptr get_tx_stream(const uhd::stream_args_t &args); bool recv_async_msg(uhd::async_metadata_t &, double); - uhd::property_tree::sptr _tree; - +private: //controllers b200_iface::sptr _iface; radio_ctrl_core_3000::sptr _local_ctrl; @@ -104,13 +104,7 @@ struct b200_impl : public uhd::device //transports uhd::transport::zero_copy_if::sptr _data_transport; uhd::transport::zero_copy_if::sptr _ctrl_transport; - boost::shared_ptr<uhd::usrp::recv_packet_demuxer_3000> _demux; - - //device properties interface - uhd::property_tree::sptr get_tree(void) const - { - return _tree; - } + uhd::usrp::recv_packet_demuxer_3000::sptr _demux; boost::weak_ptr<uhd::rx_streamer> _rx_streamer; boost::weak_ptr<uhd::tx_streamer> _tx_streamer; diff --git a/host/lib/usrp/common/recv_packet_demuxer_3000.hpp b/host/lib/usrp/common/recv_packet_demuxer_3000.hpp index 77bdebab0..ec930f3ad 100644 --- a/host/lib/usrp/common/recv_packet_demuxer_3000.hpp +++ b/host/lib/usrp/common/recv_packet_demuxer_3000.hpp @@ -28,11 +28,18 @@ #include <uhd/utils/byteswap.hpp> #include <queue> #include <map> +#include <boost/enable_shared_from_this.hpp> namespace uhd{ namespace usrp{ - struct recv_packet_demuxer_3000 + struct recv_packet_demuxer_3000 : boost::enable_shared_from_this<recv_packet_demuxer_3000> { + typedef boost::shared_ptr<recv_packet_demuxer_3000> sptr; + static sptr make(transport::zero_copy_if::sptr xport) + { + return sptr(new recv_packet_demuxer_3000(xport)); + } + recv_packet_demuxer_3000(transport::zero_copy_if::sptr xport): _xport(xport) {/*NOP*/} @@ -120,6 +127,8 @@ namespace uhd{ namespace usrp{ } } + transport::zero_copy_if::sptr make_proxy(const boost::uint32_t sid); + typedef std::queue<transport::managed_recv_buffer::sptr> queue_type_t; std::map<boost::uint32_t, queue_type_t> _queues; transport::zero_copy_if::sptr _xport; @@ -130,6 +139,42 @@ namespace uhd{ namespace usrp{ boost::mutex mutex; }; + struct recv_packet_demuxer_proxy_3000 : transport::zero_copy_if + { + recv_packet_demuxer_proxy_3000(recv_packet_demuxer_3000::sptr demux, transport::zero_copy_if::sptr xport, const boost::uint32_t sid): + _demux(demux), _xport(xport), _sid(sid) + { + _demux->realloc_sid(_sid); //causes clear + } + + ~recv_packet_demuxer_proxy_3000(void) + { + _demux->realloc_sid(_sid); //causes clear + } + + size_t get_num_recv_frames(void) const {return _xport->get_num_recv_frames();} + size_t get_recv_frame_size(void) const {return _xport->get_recv_frame_size();} + transport::managed_recv_buffer::sptr get_recv_buff(double timeout) + { + return _demux->get_recv_buff(_sid, timeout); + } + size_t get_num_send_frames(void) const {return _xport->get_num_send_frames();} + size_t get_send_frame_size(void) const {return _xport->get_send_frame_size();} + transport::managed_send_buffer::sptr get_send_buff(double timeout) + { + return _xport->get_send_buff(timeout); + } + + recv_packet_demuxer_3000::sptr _demux; + transport::zero_copy_if::sptr _xport; + const boost::uint32_t _sid; + }; + + inline transport::zero_copy_if::sptr recv_packet_demuxer_3000::make_proxy(const boost::uint32_t sid) + { + return transport::zero_copy_if::sptr(new recv_packet_demuxer_proxy_3000(this->shared_from_this(), _xport, sid)); + } + }} //namespace uhd::usrp #endif /* INCLUDED_LIBUHD_USRP_COMMON_RECV_PACKET_DEMUXER_3000_HPP */ diff --git a/host/lib/usrp/cores/gpio_core_200.cpp b/host/lib/usrp/cores/gpio_core_200.cpp index 51c23aa4b..e55f1f51e 100644 --- a/host/lib/usrp/cores/gpio_core_200.cpp +++ b/host/lib/usrp/cores/gpio_core_200.cpp @@ -45,8 +45,8 @@ public: void set_gpio_ddr(const unit_t unit, const boost::uint16_t value){ _gpio_ddr[unit] = value; //shadow _iface->poke32(REG_GPIO_DDR, //update the 32 bit register - (boost::uint32_t(_gpio_ddr[dboard_iface::UNIT_RX]) << unit2shit(dboard_iface::UNIT_RX)) | - (boost::uint32_t(_gpio_ddr[dboard_iface::UNIT_TX]) << unit2shit(dboard_iface::UNIT_TX)) + (boost::uint32_t(_gpio_ddr[dboard_iface::UNIT_RX]) << shift_by_unit(dboard_iface::UNIT_RX)) | + (boost::uint32_t(_gpio_ddr[dboard_iface::UNIT_TX]) << shift_by_unit(dboard_iface::UNIT_TX)) ); } @@ -56,7 +56,7 @@ public: } boost::uint16_t read_gpio(const unit_t unit){ - return boost::uint16_t(_iface->peek32(_rb_addr) >> unit2shit(unit)); + return boost::uint16_t(_iface->peek32(_rb_addr) >> shift_by_unit(unit)); } private: @@ -68,7 +68,7 @@ private: 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; - unsigned unit2shit(const unit_t unit){ + unsigned shift_by_unit(const unit_t unit){ return (unit == dboard_iface::UNIT_RX)? 0 : 16; } @@ -81,16 +81,16 @@ private: void update(const atr_reg_t atr, const size_t addr){ const boost::uint32_t atr_val = - (boost::uint32_t(_atr_regs[dboard_iface::UNIT_RX][atr]) << unit2shit(dboard_iface::UNIT_RX)) | - (boost::uint32_t(_atr_regs[dboard_iface::UNIT_TX][atr]) << unit2shit(dboard_iface::UNIT_TX)); + (boost::uint32_t(_atr_regs[dboard_iface::UNIT_RX][atr]) << shift_by_unit(dboard_iface::UNIT_RX)) | + (boost::uint32_t(_atr_regs[dboard_iface::UNIT_TX][atr]) << shift_by_unit(dboard_iface::UNIT_TX)); const boost::uint32_t gpio_val = - (boost::uint32_t(_gpio_out[dboard_iface::UNIT_RX]) << unit2shit(dboard_iface::UNIT_RX)) | - (boost::uint32_t(_gpio_out[dboard_iface::UNIT_TX]) << unit2shit(dboard_iface::UNIT_TX)); + (boost::uint32_t(_gpio_out[dboard_iface::UNIT_RX]) << shift_by_unit(dboard_iface::UNIT_RX)) | + (boost::uint32_t(_gpio_out[dboard_iface::UNIT_TX]) << shift_by_unit(dboard_iface::UNIT_TX)); 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)); + (boost::uint32_t(_pin_ctrl[dboard_iface::UNIT_RX]) << shift_by_unit(dboard_iface::UNIT_RX)) | + (boost::uint32_t(_pin_ctrl[dboard_iface::UNIT_TX]) << shift_by_unit(dboard_iface::UNIT_TX)); const boost::uint32_t val = (ctrl & atr_val) | ((~ctrl) & gpio_val); if (not _update_cache.has_key(addr) or _update_cache[addr] != val) { diff --git a/host/lib/usrp/cores/radio_ctrl_core_3000.cpp b/host/lib/usrp/cores/radio_ctrl_core_3000.cpp index 27fac3471..2005bcf33 100644 --- a/host/lib/usrp/cores/radio_ctrl_core_3000.cpp +++ b/host/lib/usrp/cores/radio_ctrl_core_3000.cpp @@ -88,9 +88,6 @@ public: boost::mutex::scoped_lock lock(_mutex); UHD_LOGV(always) << _name << std::hex << " addr 0x" << addr << std::dec << std::endl; this->send_pkt(SR_READBACK, addr/8); - this->wait_for_ack(false); - - this->send_pkt(0); const boost::uint64_t res = this->wait_for_ack(true); const boost::uint32_t lo = boost::uint32_t(res & 0xffffffff); const boost::uint32_t hi = boost::uint32_t(res >> 32); @@ -103,9 +100,6 @@ public: UHD_LOGV(always) << _name << std::hex << " addr 0x" << addr << std::dec << std::endl; this->send_pkt(SR_READBACK, addr/8); - this->wait_for_ack(false); - - this->send_pkt(0); return this->wait_for_ack(true); } diff --git a/host/lib/usrp/cores/rx_dsp_core_3000.cpp b/host/lib/usrp/cores/rx_dsp_core_3000.cpp index 525916032..67d0be017 100644 --- a/host/lib/usrp/cores/rx_dsp_core_3000.cpp +++ b/host/lib/usrp/cores/rx_dsp_core_3000.cpp @@ -32,6 +32,8 @@ #define REG_DSP_RX_SCALE_IQ _dsp_base + 4 #define REG_DSP_RX_DECIM _dsp_base + 8 #define REG_DSP_RX_MUX _dsp_base + 12 +#define REG_DSP_RX_COEFFS _dsp_base + 16 +//FIXME: Add code to support REG_DSP_RX_COEFFS #define FLAG_DSP_RX_MUX_SWAP_IQ (1 << 0) #define FLAG_DSP_RX_MUX_REAL_MODE (1 << 1) @@ -88,6 +90,9 @@ public: uhd::meta_range_t get_host_rates(void){ meta_range_t range; + for (int rate = 1024; rate > 512; rate -= 8){ + range.push_back(range_t(_tick_rate/rate)); + } for (int rate = 512; rate > 256; rate -= 4){ range.push_back(range_t(_tick_rate/rate)); } @@ -105,7 +110,7 @@ public: size_t decim = decim_rate; //determine which half-band filters are activated - int hb0 = 0, hb1 = 0; + int hb0 = 0, hb1 = 0, hb2 = 0, hb_enable=0; if (decim % 2 == 0){ hb0 = 1; decim /= 2; @@ -114,14 +119,30 @@ public: hb1 = 1; decim /= 2; } + if (decim % 2 == 0){ + hb2 = 1; + decim /= 2; + } + + // Encode Halfband config for setting register programming. + if (hb2) { // Implies HB1 and HB0 also asserted + hb_enable=3; + } else if (hb1) { // Implies HB0 is also asserted + hb_enable=2; + } else if (hb0) { + hb_enable=1; + } else { + hb_enable=0; + } - _iface->poke32(REG_DSP_RX_DECIM, (hb1 << 9) | (hb0 << 8) | (decim & 0xff)); + _iface->poke32(REG_DSP_RX_DECIM, (hb_enable << 8) | (decim & 0xff)); - if (decim > 1 and hb0 == 0 and hb1 == 0) + if (decim > 1 and hb0 == 0 and hb1 == 0 and hb2 == 0) { UHD_MSG(warning) << boost::format( - "The requested decimation is odd; the user should expect CIC rolloff.\n" + "The requested decimation is odd; the user should expect passband CIC rolloff.\n" "Select an even decimation to ensure that a halfband filter is enabled.\n" + "Decimations factorable by 4 will enable 2 halfbands, those factorable by 8 will enable 3 halfbands.\n" "decimation = dsp_rate/samp_rate -> %d = (%f MHz)/(%f MHz)\n" ) % decim_rate % (_tick_rate/1e6) % (rate/1e6); } @@ -137,7 +158,7 @@ public: void update_scalar(void){ const double factor = 1.0 + std::max(ceil_log2(_scaling_adjustment), 0.0); - const double target_scalar = (1 << 17)*_scaling_adjustment/_dsp_extra_scaling/factor; + const double target_scalar = (1 << 15)*_scaling_adjustment/_dsp_extra_scaling/factor; const boost::int32_t actual_scalar = boost::math::iround(target_scalar); _fxpt_scalar_correction = target_scalar/actual_scalar*factor; //should be small _iface->poke32(REG_DSP_RX_SCALE_IQ, actual_scalar); diff --git a/host/lib/usrp/dboard/db_sbx_common.cpp b/host/lib/usrp/dboard/db_sbx_common.cpp index 49e30949e..b67910e9a 100644 --- a/host/lib/usrp/dboard/db_sbx_common.cpp +++ b/host/lib/usrp/dboard/db_sbx_common.cpp @@ -33,6 +33,7 @@ UHD_STATIC_BLOCK(reg_sbx_dboards){ dboard_manager::register_dboard(0x0054, 0x0055, &make_sbx, "SBX"); dboard_manager::register_dboard(0x0065, 0x0064, &make_sbx, "SBX v4"); dboard_manager::register_dboard(0x0067, 0x0066, &make_sbx, "CBX"); + dboard_manager::register_dboard(0x0069, 0x0068, &make_sbx, "SBX v5"); dboard_manager::register_dboard(0x0083, 0x0082, &make_sbx, "SBX-120"); dboard_manager::register_dboard(0x0085, 0x0084, &make_sbx, "CBX-120"); } @@ -127,6 +128,10 @@ sbx_xcvr::sbx_xcvr(ctor_args_t args) : xcvr_dboard_base(args){ db_actual = sbx_versionx_sptr(new cbx(this)); freq_range = cbx_freq_range; break; + case 0x0069: + db_actual = sbx_versionx_sptr(new sbx_version4(this)); + freq_range = sbx_freq_range; + break; case 0x0083: db_actual = sbx_versionx_sptr(new sbx_version4(this)); freq_range = sbx_freq_range; diff --git a/host/lib/usrp/dboard/db_tvrx2.cpp b/host/lib/usrp/dboard/db_tvrx2.cpp index 0bfa5229a..c593c5437 100644 --- a/host/lib/usrp/dboard/db_tvrx2.cpp +++ b/host/lib/usrp/dboard/db_tvrx2.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010,2012 Ettus Research LLC +// Copyright 2010,2012-2013 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -1005,6 +1005,17 @@ tvrx2::tvrx2(ctor_args_t args) : rx_dboard_base(args){ _freq_scalar = (4*16.0e6)/(this->get_iface()->get_clock_rate(dboard_iface::UNIT_RX)); } else if (ref_clock == 100e6) { + + this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, REFCLOCK_DIV8); + + UHD_LOGV(often) << boost::format( + "TVRX2 (%s): Dividing Refclock by 6" + ) % (get_subdev_name()) << std::endl; + + _freq_scalar = (6*16.0e6)/this->get_iface()->get_clock_rate(dboard_iface::UNIT_RX); + } else if (ref_clock == 200e6) { + UHD_MSG(warning) << boost::format("ref_clock was 200e6, setting ref_clock divider for 100e6.") % ref_clock << std::endl; + this->get_iface()->set_clock_rate(dboard_iface::UNIT_RX, 100e6); this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, REFCLOCK_DIV6); UHD_LOGV(often) << boost::format( @@ -1014,7 +1025,7 @@ tvrx2::tvrx2(ctor_args_t args) : rx_dboard_base(args){ _freq_scalar = (6*16.0e6)/this->get_iface()->get_clock_rate(dboard_iface::UNIT_RX); } else { this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, REFCLOCK_DIV6); - UHD_MSG(warning) << boost::format("Unsupported ref_clock %0.2f, valid options 64e6 and 100e6") % ref_clock << std::endl; + UHD_MSG(warning) << boost::format("Unsupported ref_clock %0.2f, valid options 64e6, 100e6, 200e6") % ref_clock << std::endl; _freq_scalar = 1.0; } diff --git a/host/lib/usrp/e100/e100_impl.cpp b/host/lib/usrp/e100/e100_impl.cpp index 619ea8f8e..b49ba64a2 100644 --- a/host/lib/usrp/e100/e100_impl.cpp +++ b/host/lib/usrp/e100/e100_impl.cpp @@ -51,6 +51,10 @@ static device_addrs_t e100_find(const device_addr_t &hint){ //return an empty list of addresses when type is set to non-usrp-e if (hint.has_key("type") and hint["type"] != "e100") return e100_addrs; + //Return an empty list of addresses when a resource is specified, + //since a resource is intended for a different, non-USB, device. + if (hint.has_key("resource")) return e100_addrs; + //device node not provided, assume its 0 if (not hint.has_key("node")){ device_addr_t new_addr = hint; diff --git a/host/lib/usrp/e100/e100_impl.hpp b/host/lib/usrp/e100/e100_impl.hpp index 813f9cc8f..d499c4c03 100644 --- a/host/lib/usrp/e100/e100_impl.hpp +++ b/host/lib/usrp/e100/e100_impl.hpp @@ -85,8 +85,6 @@ public: bool recv_async_msg(uhd::async_metadata_t &, double); private: - uhd::property_tree::sptr _tree; - //controllers fifo_ctrl_excelsior::sptr _fifo_ctrl; i2c_core_200::sptr _fpga_i2c_ctrl; @@ -111,11 +109,6 @@ private: uhd::usrp::dboard_manager::sptr _dboard_manager; uhd::usrp::dboard_iface::sptr _dboard_iface; - //device properties interface - uhd::property_tree::sptr get_tree(void) const{ - return _tree; - } - std::vector<boost::weak_ptr<uhd::rx_streamer> > _rx_streamers; std::vector<boost::weak_ptr<uhd::tx_streamer> > _tx_streamers; diff --git a/host/lib/usrp/gps_ctrl.cpp b/host/lib/usrp/gps_ctrl.cpp index 98bc90a3f..4bf8eb3a3 100644 --- a/host/lib/usrp/gps_ctrl.cpp +++ b/host/lib/usrp/gps_ctrl.cpp @@ -38,7 +38,7 @@ using namespace boost::algorithm; using namespace boost::this_thread; /*! - * A GPS control for NMEA compatible GPSes + * A control for GPSDO devices */ class gps_ctrl_impl : public gps_ctrl{ @@ -63,7 +63,7 @@ private: } std::string update_cached_sensors(const std::string sensor) { - if(not gps_detected() || (gps_type != GPS_TYPE_ER_GPSDO)) { + if(not gps_detected() || (gps_type != GPS_TYPE_INTERNAL_GPSDO)) { UHD_MSG(error) << "get_stat(): unsupported GPS or no GPS detected"; return std::string(); } @@ -105,18 +105,19 @@ public: bool i_heard_some_nmea = false, i_heard_something_weird = false; gps_type = GPS_TYPE_NONE; + //first we look for an internal GPSDO _flush(); //get whatever junk is in the rx buffer right now, and throw it away _send("HAAAY GUYYYYS\n"); //to elicit a response from the GPSDO //wait for _send(...) to return - sleep(milliseconds(200)); + sleep(milliseconds(GPSDO_STUPID_DELAY_MS)); //then we loop until we either timeout, or until we get a response that indicates we're a JL device const boost::system_time comm_timeout = boost::get_system_time() + milliseconds(GPS_COMM_TIMEOUT_MS); while(boost::get_system_time() < comm_timeout) { reply = _recv(); if(reply.find("Command Error") != std::string::npos) { - gps_type = GPS_TYPE_ER_GPSDO; + gps_type = GPS_TYPE_INTERNAL_GPSDO; break; } else if(reply.substr(0, 3) == "$GP") i_heard_some_nmea = true; //but keep looking for that "Command Error" response @@ -124,24 +125,25 @@ public: sleep(milliseconds(GPS_TIMEOUT_DELAY_MS)); } - if((i_heard_some_nmea) && (gps_type != GPS_TYPE_ER_GPSDO)) gps_type = GPS_TYPE_GENERIC_NMEA; + if((i_heard_some_nmea) && (gps_type != GPS_TYPE_INTERNAL_GPSDO)) gps_type = GPS_TYPE_GENERIC_NMEA; if((gps_type == GPS_TYPE_NONE) && i_heard_something_weird) { UHD_MSG(error) << "GPS invalid reply \"" << reply << "\", assuming none available" << std::endl; } switch(gps_type) { - case GPS_TYPE_ER_GPSDO: - UHD_MSG(status) << "Found an Ettus Research NMEA-capable GPSDO" << std::endl; + case GPS_TYPE_INTERNAL_GPSDO: + UHD_MSG(status) << "Found an internal GPSDO" << std::endl; init_gpsdo(); break; case GPS_TYPE_GENERIC_NMEA: - if(gps_type == GPS_TYPE_GENERIC_NMEA) UHD_MSG(status) << "Found a generic NMEA GPS device" << std::endl; + UHD_MSG(status) << "Found a generic NMEA GPS device" << std::endl; break; case GPS_TYPE_NONE: default: + UHD_MSG(status) << "No GPSDO found" << std::endl; break; } @@ -189,19 +191,19 @@ private: //issue some setup stuff so it spits out the appropriate data //none of these should issue replies so we don't bother looking for them //we have to sleep between commands because the JL device, despite not acking, takes considerable time to process each command. - sleep(milliseconds(200)); + sleep(milliseconds(GPSDO_STUPID_DELAY_MS)); _send("SYST:COMM:SER:ECHO OFF\n"); - sleep(milliseconds(200)); + sleep(milliseconds(GPSDO_STUPID_DELAY_MS)); _send("SYST:COMM:SER:PRO OFF\n"); - sleep(milliseconds(200)); + sleep(milliseconds(GPSDO_STUPID_DELAY_MS)); _send("GPS:GPGGA 1\n"); - sleep(milliseconds(200)); + sleep(milliseconds(GPSDO_STUPID_DELAY_MS)); _send("GPS:GGAST 0\n"); - sleep(milliseconds(200)); + sleep(milliseconds(GPSDO_STUPID_DELAY_MS)); _send("GPS:GPRMC 1\n"); - sleep(milliseconds(200)); + sleep(milliseconds(GPSDO_STUPID_DELAY_MS)); _send("SERV:TRAC 0\n"); - sleep(milliseconds(200)); + sleep(milliseconds(GPSDO_STUPID_DELAY_MS)); } //retrieve a raw NMEA sentence @@ -304,7 +306,7 @@ private: //enable servo reporting _send("SERV:TRAC 1\n"); - sleep(milliseconds(200)); + sleep(milliseconds(GPSDO_STUPID_DELAY_MS)); std::string reply; @@ -339,7 +341,7 @@ private: } enum { - GPS_TYPE_ER_GPSDO, + GPS_TYPE_INTERNAL_GPSDO, GPS_TYPE_GENERIC_NMEA, GPS_TYPE_NONE } gps_type; @@ -351,6 +353,7 @@ private: static const int GPS_SERVO_FRESHNESS = 2500; static const int GPS_LOCK_FRESHNESS = 2500; static const int GPS_TIMEOUT_DELAY_MS = 200; + static const int GPSDO_STUPID_DELAY_MS = 200; }; /*********************************************************************** diff --git a/host/lib/usrp/mboard_eeprom.cpp b/host/lib/usrp/mboard_eeprom.cpp index dc25379f9..68c084589 100644 --- a/host/lib/usrp/mboard_eeprom.cpp +++ b/host/lib/usrp/mboard_eeprom.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2012 Ettus Research LLC +// Copyright 2010-2013 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -229,6 +229,137 @@ static void store_n100(const mboard_eeprom_t &mb_eeprom, i2c_iface &iface){ } /*********************************************************************** + * Implementation of X300 load/store + **********************************************************************/ +static const boost::uint8_t X300_EEPROM_ADDR = 0x50; + +struct x300_eeprom_map +{ + //indentifying numbers + unsigned char revision[2]; + unsigned char product[2]; + boost::uint8_t _pad0[4]; + + //all the mac addrs + boost::uint8_t mac_addr0[6]; + boost::uint8_t _pad1[2]; + boost::uint8_t mac_addr1[6]; + boost::uint8_t _pad2[2]; + + //all the IP addrs + boost::uint32_t gateway; + boost::uint32_t subnet[4]; + boost::uint32_t ip_addr[4]; + boost::uint8_t _pad3[16]; + + //names and serials + unsigned char name[NAME_MAX_LEN]; + unsigned char serial[SERIAL_LEN]; +}; + +static void load_x300(mboard_eeprom_t &mb_eeprom, i2c_iface &iface) +{ + //extract the revision number + mb_eeprom["revision"] = uint16_bytes_to_string( + iface.read_eeprom(X300_EEPROM_ADDR, offsetof(x300_eeprom_map, revision), 2) + ); + + //extract the product code + mb_eeprom["product"] = uint16_bytes_to_string( + iface.read_eeprom(X300_EEPROM_ADDR, offsetof(x300_eeprom_map, product), 2) + ); + + //extract the mac addresses + mb_eeprom["mac-addr0"] = mac_addr_t::from_bytes(iface.read_eeprom( + X300_EEPROM_ADDR, offsetof(x300_eeprom_map, mac_addr0), 6 + )).to_string(); + mb_eeprom["mac-addr1"] = mac_addr_t::from_bytes(iface.read_eeprom( + X300_EEPROM_ADDR, offsetof(x300_eeprom_map, mac_addr1), 6 + )).to_string(); + + //extract the ip addresses + boost::asio::ip::address_v4::bytes_type ip_addr_bytes; + byte_copy(iface.read_eeprom(X300_EEPROM_ADDR, offsetof(x300_eeprom_map, gateway), 4), ip_addr_bytes); + mb_eeprom["gateway"] = boost::asio::ip::address_v4(ip_addr_bytes).to_string(); + for (size_t i = 0; i < 4; i++) + { + const std::string n(1, i+'0'); + byte_copy(iface.read_eeprom(X300_EEPROM_ADDR, offsetof(x300_eeprom_map, ip_addr)+(i*4), 4), ip_addr_bytes); + mb_eeprom["ip-addr"+n] = boost::asio::ip::address_v4(ip_addr_bytes).to_string(); + + byte_copy(iface.read_eeprom(X300_EEPROM_ADDR, offsetof(x300_eeprom_map, subnet)+(i*4), 4), ip_addr_bytes); + mb_eeprom["subnet"+n] = boost::asio::ip::address_v4(ip_addr_bytes).to_string(); + } + + //extract the serial + mb_eeprom["serial"] = bytes_to_string(iface.read_eeprom( + X300_EEPROM_ADDR, offsetof(x300_eeprom_map, serial), SERIAL_LEN + )); + + //extract the name + mb_eeprom["name"] = bytes_to_string(iface.read_eeprom( + X300_EEPROM_ADDR, offsetof(x300_eeprom_map, name), NAME_MAX_LEN + )); +} + +static void store_x300(const mboard_eeprom_t &mb_eeprom, i2c_iface &iface) +{ + //parse the revision number + if (mb_eeprom.has_key("revision")) iface.write_eeprom( + X300_EEPROM_ADDR, offsetof(x300_eeprom_map, revision), + string_to_uint16_bytes(mb_eeprom["revision"]) + ); + + //parse the product code + if (mb_eeprom.has_key("product")) iface.write_eeprom( + X300_EEPROM_ADDR, offsetof(x300_eeprom_map, product), + string_to_uint16_bytes(mb_eeprom["product"]) + ); + + //store the mac addresses + if (mb_eeprom.has_key("mac-addr0")) iface.write_eeprom( + X300_EEPROM_ADDR, offsetof(x300_eeprom_map, mac_addr0), + mac_addr_t::from_string(mb_eeprom["mac-addr0"]).to_bytes() + ); + if (mb_eeprom.has_key("mac-addr1")) iface.write_eeprom( + X300_EEPROM_ADDR, offsetof(x300_eeprom_map, mac_addr1), + mac_addr_t::from_string(mb_eeprom["mac-addr1"]).to_bytes() + ); + + //store the ip addresses + byte_vector_t ip_addr_bytes(4); + if (mb_eeprom.has_key("gateway")){ + byte_copy(boost::asio::ip::address_v4::from_string(mb_eeprom["gateway"]).to_bytes(), ip_addr_bytes); + iface.write_eeprom(X300_EEPROM_ADDR, offsetof(x300_eeprom_map, gateway), ip_addr_bytes); + } + for (size_t i = 0; i < 4; i++) + { + const std::string n(1, i+'0'); + if (mb_eeprom.has_key("ip-addr"+n)){ + byte_copy(boost::asio::ip::address_v4::from_string(mb_eeprom["ip-addr"+n]).to_bytes(), ip_addr_bytes); + iface.write_eeprom(X300_EEPROM_ADDR, offsetof(x300_eeprom_map, ip_addr)+(i*4), ip_addr_bytes); + } + + if (mb_eeprom.has_key("subnet"+n)){ + byte_copy(boost::asio::ip::address_v4::from_string(mb_eeprom["subnet"+n]).to_bytes(), ip_addr_bytes); + iface.write_eeprom(X300_EEPROM_ADDR, offsetof(x300_eeprom_map, subnet)+(i*4), ip_addr_bytes); + } + } + + //store the serial + if (mb_eeprom.has_key("serial")) iface.write_eeprom( + X300_EEPROM_ADDR, offsetof(x300_eeprom_map, serial), + string_to_bytes(mb_eeprom["serial"], SERIAL_LEN) + ); + + //store the name + if (mb_eeprom.has_key("name")) iface.write_eeprom( + X300_EEPROM_ADDR, offsetof(x300_eeprom_map, name), + string_to_bytes(mb_eeprom["name"], NAME_MAX_LEN) + ); +} + +/*********************************************************************** * Implementation of B000 load/store **********************************************************************/ static const boost::uint8_t B000_EEPROM_ADDR = 0x50; @@ -512,6 +643,7 @@ mboard_eeprom_t::mboard_eeprom_t(void){ mboard_eeprom_t::mboard_eeprom_t(i2c_iface &iface, const std::string &which){ if (which == "N100") load_n100(*this, iface); + if (which == "X300") load_x300(*this, iface); if (which == "B000") load_b000(*this, iface); if (which == "B100") load_b100(*this, iface); if (which == "B200") load_b200(*this, iface); @@ -520,6 +652,7 @@ mboard_eeprom_t::mboard_eeprom_t(i2c_iface &iface, const std::string &which){ void mboard_eeprom_t::commit(i2c_iface &iface, const std::string &which) const{ if (which == "N100") store_n100(*this, iface); + if (which == "X300") store_x300(*this, iface); if (which == "B000") store_b000(*this, iface); if (which == "B100") store_b100(*this, iface); if (which == "B200") store_b200(*this, iface); diff --git a/host/lib/usrp/multi_usrp.cpp b/host/lib/usrp/multi_usrp.cpp index 4bd7d1bfe..182e774fc 100644 --- a/host/lib/usrp/multi_usrp.cpp +++ b/host/lib/usrp/multi_usrp.cpp @@ -517,6 +517,46 @@ public: return _tree->access<std::vector<std::string> >(mb_root(mboard) / "clock_source" / "options").get(); } + void set_clock_source_out(const bool enb, const size_t mboard) + { + if (mboard != ALL_MBOARDS) + { + if (_tree->exists(mb_root(mboard) / "clock_source" / "output")) + { + _tree->access<bool>(mb_root(mboard) / "clock_source" / "output").set(enb); + } + else + { + throw uhd::runtime_error("multi_usrp::set_clock_source_out - not supported on this device"); + } + return; + } + for (size_t m = 0; m < get_num_mboards(); m++) + { + this->set_clock_source_out(enb, m); + } + } + + void set_time_source_out(const bool enb, const size_t mboard) + { + if (mboard != ALL_MBOARDS) + { + if (_tree->exists(mb_root(mboard) / "time_source" / "output")) + { + _tree->access<bool>(mb_root(mboard) / "time_source" / "output").set(enb); + } + else + { + throw uhd::runtime_error("multi_usrp::set_time_source_out - not supported on this device"); + } + return; + } + for (size_t m = 0; m < get_num_mboards(); m++) + { + this->set_time_source_out(enb, m); + } + } + size_t get_num_mboards(void){ return _tree->list("/mboards").size(); } @@ -910,6 +950,74 @@ public: } } + /******************************************************************* + * GPIO methods + ******************************************************************/ + std::vector<std::string> get_gpio_banks(const size_t mboard) + { + std::vector<std::string> banks; + if (_tree->exists(mb_root(mboard) / "gpio")) + { + BOOST_FOREACH(const std::string &name, _tree->list(mb_root(mboard) / "gpio")) + { + banks.push_back(name); + } + } + BOOST_FOREACH(const std::string &name, _tree->list(mb_root(mboard) / "dboards")) + { + banks.push_back("RX"+name); + banks.push_back("TX"+name); + } + return banks; + } + + void set_gpio_attr(const std::string &bank, const std::string &attr, const boost::uint32_t value, const boost::uint32_t mask, const size_t mboard) + { + if (_tree->exists(mb_root(mboard) / "gpio" / bank)) + { + const boost::uint32_t current = _tree->access<boost::uint32_t>(mb_root(mboard) / "gpio" / bank / attr).get(); + const boost::uint32_t new_value = (current & ~mask) | (value & mask); + _tree->access<boost::uint32_t>(mb_root(mboard) / "gpio" / bank / attr).set(new_value); + return; + } + if (bank.size() > 2 and bank[1] == 'X') + { + const std::string name = bank.substr(2); + const dboard_iface::unit_t unit = (bank[0] == 'R')? dboard_iface::UNIT_RX : dboard_iface::UNIT_TX; + dboard_iface::sptr iface = _tree->access<dboard_iface::sptr>(mb_root(mboard) / "dboards" / name / "iface").get(); + if (attr == "CTRL") iface->set_pin_ctrl(unit, boost::uint16_t(value), boost::uint16_t(mask)); + if (attr == "DDR") iface->set_gpio_ddr(unit, boost::uint16_t(value), boost::uint16_t(mask)); + if (attr == "OUT") iface->set_gpio_out(unit, boost::uint16_t(value), boost::uint16_t(mask)); + if (attr == "ATR_0X") iface->set_atr_reg(unit, dboard_iface::ATR_REG_IDLE, boost::uint16_t(value), boost::uint16_t(mask)); + if (attr == "ATR_RX") iface->set_atr_reg(unit, dboard_iface::ATR_REG_RX_ONLY, boost::uint16_t(value), boost::uint16_t(mask)); + if (attr == "ATR_TX") iface->set_atr_reg(unit, dboard_iface::ATR_REG_TX_ONLY, boost::uint16_t(value), boost::uint16_t(mask)); + if (attr == "ATR_XX") iface->set_atr_reg(unit, dboard_iface::ATR_REG_FULL_DUPLEX, boost::uint16_t(value), boost::uint16_t(mask)); + } + } + + boost::uint32_t get_gpio_attr(const std::string &bank, const std::string &attr, const size_t mboard) + { + if (_tree->exists(mb_root(mboard) / "gpio" / bank)) + { + return _tree->access<boost::uint64_t>(mb_root(mboard) / "gpio" / bank / attr).get(); + } + if (bank.size() > 2 and bank[1] == 'X') + { + const std::string name = bank.substr(2); + const dboard_iface::unit_t unit = (bank[0] == 'R')? dboard_iface::UNIT_RX : dboard_iface::UNIT_TX; + dboard_iface::sptr iface = _tree->access<dboard_iface::sptr>(mb_root(mboard) / "dboards" / name / "iface").get(); + if (attr == "CTRL") return iface->get_pin_ctrl(unit); + if (attr == "DDR") return iface->get_gpio_ddr(unit); + if (attr == "OUT") return iface->get_gpio_out(unit); + if (attr == "ATR_0X") return iface->get_atr_reg(unit, dboard_iface::ATR_REG_IDLE); + if (attr == "ATR_RX") return iface->get_atr_reg(unit, dboard_iface::ATR_REG_RX_ONLY); + if (attr == "ATR_TX") return iface->get_atr_reg(unit, dboard_iface::ATR_REG_TX_ONLY); + if (attr == "ATR_XX") return iface->get_atr_reg(unit, dboard_iface::ATR_REG_FULL_DUPLEX); + if (attr == "READBACK") return iface->read_gpio(unit); + } + return 0; + } + private: device::sptr _dev; property_tree::sptr _tree; diff --git a/host/lib/usrp/usrp1/usrp1_impl.cpp b/host/lib/usrp/usrp1/usrp1_impl.cpp index 625926f36..3b902b343 100644 --- a/host/lib/usrp/usrp1/usrp1_impl.cpp +++ b/host/lib/usrp/usrp1/usrp1_impl.cpp @@ -55,9 +55,9 @@ static device_addrs_t usrp1_find(const device_addr_t &hint) //return an empty list of addresses when type is set to non-usrp1 if (hint.has_key("type") and hint["type"] != "usrp1") return usrp1_addrs; - //Return an empty list of addresses when an address is specified, - //since an address is intended for a different, non-USB, device. - if (hint.has_key("addr")) return usrp1_addrs; + //Return an empty list of addresses when an address or resource is specified, + //since an address and resource is intended for a different, non-USB, device. + if (hint.has_key("addr") || hint.has_key("resource")) return usrp1_addrs; unsigned int vid, pid; diff --git a/host/lib/usrp/usrp1/usrp1_impl.hpp b/host/lib/usrp/usrp1/usrp1_impl.hpp index 0be8ebca9..da9fe8b16 100644 --- a/host/lib/usrp/usrp1/usrp1_impl.hpp +++ b/host/lib/usrp/usrp1/usrp1_impl.hpp @@ -84,13 +84,6 @@ public: bool recv_async_msg(uhd::async_metadata_t &, double); private: - uhd::property_tree::sptr _tree; - - //device properties interface - uhd::property_tree::sptr get_tree(void) const{ - return _tree; - } - //controllers uhd::usrp::fx2_ctrl::sptr _fx2_ctrl; usrp1_iface::sptr _iface; diff --git a/host/lib/usrp/usrp2/usrp2_iface.cpp b/host/lib/usrp/usrp2/usrp2_iface.cpp index 5f97045e1..7297a30d1 100644 --- a/host/lib/usrp/usrp2/usrp2_iface.cpp +++ b/host/lib/usrp/usrp2/usrp2_iface.cpp @@ -36,6 +36,7 @@ #include <boost/filesystem.hpp> #include <algorithm> #include <iostream> +#include <uhd/utils/platform.hpp> using namespace uhd; using namespace uhd::usrp; @@ -62,33 +63,6 @@ static const boost::uint32_t MIN_PROTO_COMPAT_I2C = 7; static const boost::uint32_t MIN_PROTO_COMPAT_REG = 10; static const boost::uint32_t MIN_PROTO_COMPAT_UART = 7; -//Define get_gpid() to get a globally unique identifier for this process. -//The gpid is implemented as a hash of the pid and a unique machine identifier. -#ifdef UHD_PLATFORM_WIN32 -#include <Windows.h> -static inline size_t get_gpid(void){ - //extract volume serial number - char szVolName[MAX_PATH+1], szFileSysName[MAX_PATH+1]; - DWORD dwSerialNumber, dwMaxComponentLen, dwFileSysFlags; - GetVolumeInformation("C:\\", szVolName, MAX_PATH, - &dwSerialNumber, &dwMaxComponentLen, - &dwFileSysFlags, szFileSysName, sizeof(szFileSysName)); - - size_t hash = 0; - boost::hash_combine(hash, GetCurrentProcessId()); - boost::hash_combine(hash, dwSerialNumber); - return hash; -} -#else -#include <unistd.h> -static inline size_t get_gpid(void){ - size_t hash = 0; - boost::hash_combine(hash, getpid()); - boost::hash_combine(hash, gethostid()); - return hash; -} -#endif - class usrp2_iface_impl : public usrp2_iface{ public: /*********************************************************************** @@ -122,7 +96,7 @@ public: void lock_device(bool lock){ if (lock){ - this->pokefw(U2_FW_REG_LOCK_GPID, boost::uint32_t(get_gpid())); + this->pokefw(U2_FW_REG_LOCK_GPID, get_process_hash()); _lock_task = task::make(boost::bind(&usrp2_iface_impl::lock_task, this)); } else{ @@ -147,7 +121,7 @@ public: if (time_diff >= lock_timeout_time) return false; //otherwise only lock if the device hash is different that ours - return lock_gpid != boost::uint32_t(get_gpid()); + return lock_gpid != get_process_hash(); } void lock_task(void){ diff --git a/host/lib/usrp/usrp2/usrp2_impl.cpp b/host/lib/usrp/usrp2/usrp2_impl.cpp index 3afb3aac7..16d9b9a54 100644 --- a/host/lib/usrp/usrp2/usrp2_impl.cpp +++ b/host/lib/usrp/usrp2/usrp2_impl.cpp @@ -41,6 +41,9 @@ using namespace uhd::usrp; 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; + /*********************************************************************** * Discovery over the udp transport **********************************************************************/ @@ -49,13 +52,16 @@ static device_addrs_t usrp2_find(const device_addr_t &hint_){ device_addrs_t hints = separate_device_addr(hint_); if (hints.size() > 1){ device_addrs_t found_devices; + std::string error_msg; BOOST_FOREACH(const device_addr_t &hint_i, hints){ device_addrs_t found_devices_i = usrp2_find(hint_i); - if (found_devices_i.size() != 1) throw uhd::value_error(str(boost::format( + if (found_devices_i.size() != 1) error_msg += str(boost::format( "Could not resolve device hint \"%s\" to a single device." - ) % hint_i.to_string())); - found_devices.push_back(found_devices_i[0]); + ) % hint_i.to_string()); + else found_devices.push_back(found_devices_i[0]); } + if (found_devices.empty()) return device_addrs_t(); + if (not error_msg.empty()) throw uhd::value_error(error_msg); return device_addrs_t(1, combine_device_addrs(found_devices)); } @@ -68,6 +74,10 @@ static device_addrs_t usrp2_find(const device_addr_t &hint_){ //return an empty list of addresses when type is set to non-usrp2 if (hint.has_key("type") and hint["type"] != "usrp2") return usrp2_addrs; + //Return an empty list of addresses when a resource is specified, + //since a resource is intended for a different, non-USB, device. + if (hint.has_key("resource")) return usrp2_addrs; + //if no address was specified, send a broadcast on each interface if (not hint.has_key("addr")){ BOOST_FOREACH(const if_addrs_t &if_addrs, get_if_addrs()){ @@ -278,8 +288,15 @@ static zero_copy_if::sptr make_xport( filtered_hints[key] = hints[key]; } + zero_copy_xport_params default_buff_args; + default_buff_args.send_frame_size = transport::udp_simple::mtu; + default_buff_args.recv_frame_size = transport::udp_simple::mtu; + default_buff_args.num_send_frames = DEFAULT_NUM_FRAMES; + default_buff_args.num_recv_frames = DEFAULT_NUM_FRAMES; + //make the transport object with the filtered hints - zero_copy_if::sptr xport = udp_zero_copy::make(addr, port, filtered_hints); + udp_zero_copy::buff_params ignored_params; + zero_copy_if::sptr xport = udp_zero_copy::make(addr, port, default_buff_args, ignored_params, filtered_hints); //Send a small data packet so the usrp2 knows the udp source port. //This setup must happen before further initialization occurs diff --git a/host/lib/usrp/usrp2/usrp2_impl.hpp b/host/lib/usrp/usrp2/usrp2_impl.hpp index f9988287f..d7b53e56b 100644 --- a/host/lib/usrp/usrp2/usrp2_impl.hpp +++ b/host/lib/usrp/usrp2/usrp2_impl.hpp @@ -79,7 +79,6 @@ public: bool recv_async_msg(uhd::async_metadata_t &, double); private: - uhd::property_tree::sptr _tree; struct mb_container_type{ usrp2_iface::sptr iface; usrp2_fifo_ctrl::sptr fifo_ctrl; @@ -115,11 +114,6 @@ private: void set_rx_fe_corrections(const std::string &mb, const double); void set_tx_fe_corrections(const std::string &mb, const double); - //device properties interface - uhd::property_tree::sptr get_tree(void) const{ - return _tree; - } - //io impl methods and members UHD_PIMPL_DECL(io_impl) _io_impl; void io_init(void); diff --git a/host/lib/usrp/x300/CMakeLists.txt b/host/lib/usrp/x300/CMakeLists.txt new file mode 100644 index 000000000..a588f901b --- /dev/null +++ b/host/lib/usrp/x300/CMakeLists.txt @@ -0,0 +1,38 @@ +# +# Copyright 2013 Ettus Research LLC +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +######################################################################## +# This file included, use CMake directory variables +######################################################################## + +######################################################################## +# Conditionally configure the X300 support +######################################################################## +LIBUHD_REGISTER_COMPONENT("X300" ENABLE_X300 ON "ENABLE_LIBUHD" OFF) + +IF(ENABLE_X300) + LIBUHD_APPEND_SOURCES( + ${CMAKE_CURRENT_SOURCE_DIR}/x300_impl.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/x300_fw_ctrl.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/x300_fw_uart.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/x300_adc_ctrl.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/x300_dac_ctrl.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/x300_io_impl.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/x300_dboard_iface.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/x300_clock_ctrl.cpp + ) +ENDIF(ENABLE_X300) diff --git a/host/lib/usrp/x300/x300_adc_ctrl.cpp b/host/lib/usrp/x300/x300_adc_ctrl.cpp new file mode 100644 index 000000000..75bfb048c --- /dev/null +++ b/host/lib/usrp/x300/x300_adc_ctrl.cpp @@ -0,0 +1,133 @@ +// +// Copyright 2010-2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// 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 "x300_adc_ctrl.hpp" +#include "ads62p48_regs.hpp" +#include <uhd/types/ranges.hpp> +#include <uhd/utils/log.hpp> +#include <uhd/utils/safe_call.hpp> +#include <uhd/exception.hpp> +#include <boost/foreach.hpp> + +using namespace uhd; + +/*! + * A X300 codec control specific to the ads62p48 ic. + */ +class x300_adc_ctrl_impl : public x300_adc_ctrl +{ +public: + x300_adc_ctrl_impl(uhd::spi_iface::sptr iface, const size_t slaveno): + _iface(iface), _slaveno(slaveno) + { + //power-up adc + _ads62p48_regs.reset = 1; + this->send_ads62p48_reg(0x00); //issue a reset to the ADC + _ads62p48_regs.reset = 0; + + _ads62p48_regs.enable_low_speed_mode = 0; + _ads62p48_regs.ref = ads62p48_regs_t::REF_INTERNAL; + _ads62p48_regs.standby = ads62p48_regs_t::STANDBY_NORMAL; + _ads62p48_regs.power_down = ads62p48_regs_t::POWER_DOWN_NORMAL; + _ads62p48_regs.lvds_cmos = ads62p48_regs_t::LVDS_CMOS_DDR_LVDS; + _ads62p48_regs.channel_control = ads62p48_regs_t::CHANNEL_CONTROL_INDEPENDENT; + _ads62p48_regs.data_format = ads62p48_regs_t::DATA_FORMAT_2S_COMPLIMENT; + _ads62p48_regs.clk_out_pos_edge = ads62p48_regs_t::CLK_OUT_POS_EDGE_MINUS7_26; + _ads62p48_regs.clk_out_neg_edge = ads62p48_regs_t::CLK_OUT_NEG_EDGE_MINUS7_26; + + + this->send_ads62p48_reg(0); + this->send_ads62p48_reg(0x20); + this->send_ads62p48_reg(0x3f); + this->send_ads62p48_reg(0x40); + this->send_ads62p48_reg(0x41); + this->send_ads62p48_reg(0x44); + this->send_ads62p48_reg(0x50); + this->send_ads62p48_reg(0x51); + this->send_ads62p48_reg(0x52); + this->send_ads62p48_reg(0x53); + this->send_ads62p48_reg(0x55); + this->send_ads62p48_reg(0x57); + this->send_ads62p48_reg(0x62); + this->send_ads62p48_reg(0x63); + this->send_ads62p48_reg(0x66); + this->send_ads62p48_reg(0x68); + this->send_ads62p48_reg(0x6a); + this->send_ads62p48_reg(0x75); + this->send_ads62p48_reg(0x76); + + } + + double set_gain(const double &gain) + { + const meta_range_t gain_range = meta_range_t(0, 6.0, 0.5); + const int gain_bits = int((gain_range.clip(gain)*2.0) + 0.5); + _ads62p48_regs.gain_chA = gain_bits; + _ads62p48_regs.gain_chB = gain_bits; + this->send_ads62p48_reg(0x55); + this->send_ads62p48_reg(0x68); + return gain_bits/2; + } + + void set_test_word(const std::string &patterna, const std::string &patternb, const boost::uint32_t num) + { + _ads62p48_regs.custom_pattern_low = num & 0xff; + _ads62p48_regs.custom_pattern_high = num >> 8; + if (patterna == "ones") _ads62p48_regs.test_patterns_chA = ads62p48_regs_t::TEST_PATTERNS_CHA_ONES; + if (patterna == "zeros") _ads62p48_regs.test_patterns_chA = ads62p48_regs_t::TEST_PATTERNS_CHA_ZEROS; + if (patterna == "custom") _ads62p48_regs.test_patterns_chA = ads62p48_regs_t::TEST_PATTERNS_CHA_CUSTOM; + if (patterna == "ramp") _ads62p48_regs.test_patterns_chA = ads62p48_regs_t::TEST_PATTERNS_CHA_RAMP; + if (patterna == "normal") _ads62p48_regs.test_patterns_chA = ads62p48_regs_t::TEST_PATTERNS_CHA_NORMAL; + if (patternb == "ones") _ads62p48_regs.test_patterns_chB = ads62p48_regs_t::TEST_PATTERNS_CHB_ONES; + if (patternb == "zeros") _ads62p48_regs.test_patterns_chB = ads62p48_regs_t::TEST_PATTERNS_CHB_ZEROS; + if (patternb == "custom") _ads62p48_regs.test_patterns_chB = ads62p48_regs_t::TEST_PATTERNS_CHB_CUSTOM; + if (patterna == "ramp") _ads62p48_regs.test_patterns_chB = ads62p48_regs_t::TEST_PATTERNS_CHB_RAMP; + if (patterna == "normal") _ads62p48_regs.test_patterns_chB = ads62p48_regs_t::TEST_PATTERNS_CHB_NORMAL; + this->send_ads62p48_reg(0x51); + this->send_ads62p48_reg(0x52); + this->send_ads62p48_reg(0x62); + this->send_ads62p48_reg(0x75); + } + + ~x300_adc_ctrl_impl(void) + { + _ads62p48_regs.power_down = ads62p48_regs_t::POWER_DOWN_GLOBAL; + UHD_SAFE_CALL + ( + this->send_ads62p48_reg(0x40); + ) + } + +private: + ads62p48_regs_t _ads62p48_regs; + uhd::spi_iface::sptr _iface; + const size_t _slaveno; + + void send_ads62p48_reg(boost::uint8_t addr) + { + boost::uint16_t reg = _ads62p48_regs.get_write_reg(addr); + _iface->write_spi(_slaveno, spi_config_t::EDGE_FALL, reg, 16); + } +}; + +/*********************************************************************** + * Public make function for the ADC control + **********************************************************************/ +x300_adc_ctrl::sptr x300_adc_ctrl::make(uhd::spi_iface::sptr iface, const size_t slaveno) +{ + return sptr(new x300_adc_ctrl_impl(iface, slaveno)); +} diff --git a/host/lib/usrp/x300/x300_adc_ctrl.hpp b/host/lib/usrp/x300/x300_adc_ctrl.hpp new file mode 100644 index 000000000..fce40a434 --- /dev/null +++ b/host/lib/usrp/x300/x300_adc_ctrl.hpp @@ -0,0 +1,44 @@ +// +// Copyright 2010-2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// 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_X300_ADC_CTRL_HPP +#define INCLUDED_X300_ADC_CTRL_HPP + +#include <uhd/types/serial.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/utility.hpp> + +class x300_adc_ctrl : boost::noncopyable +{ +public: + typedef boost::shared_ptr<x300_adc_ctrl> sptr; + + /*! + * Make a codec control for the ADC. + * \param iface a pointer to the interface object + * \param spiface the interface to spi + * \return a new codec control object + */ + static sptr make(uhd::spi_iface::sptr iface, const size_t slaveno); + + virtual double set_gain(const double &) = 0; + + virtual void set_test_word(const std::string &patterna, const std::string &patternb, const boost::uint32_t = 0) = 0; + +}; + +#endif /* INCLUDED_X300_ADC_CTRL_HPP */ diff --git a/host/lib/usrp/x300/x300_clock_ctrl.cpp b/host/lib/usrp/x300/x300_clock_ctrl.cpp new file mode 100644 index 000000000..1a4cd4668 --- /dev/null +++ b/host/lib/usrp/x300/x300_clock_ctrl.cpp @@ -0,0 +1,396 @@ +// +// Copyright 2013-2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// 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 "lmk04816_regs.hpp" +#include "x300_clock_ctrl.hpp" +#include <uhd/utils/safe_call.hpp> +#include <boost/cstdint.hpp> +#include <boost/format.hpp> +#include <stdexcept> +#include <cmath> +#include <cstdlib> + +using namespace uhd; + +class x300_clock_ctrl_impl : public x300_clock_ctrl { + +public: + +~x300_clock_ctrl_impl(void) {} + +x300_clock_ctrl_impl(uhd::spi_iface::sptr spiface, + const size_t slaveno, + const size_t hw_rev, + const double master_clock_rate, + const double system_ref_rate): + _spiface(spiface), + _slaveno(slaveno), + _hw_rev(hw_rev), + _master_clock_rate(master_clock_rate), + _system_ref_rate(system_ref_rate) +{ + set_master_clock_rate(master_clock_rate); +} + +void sync_clocks(void) { + //soft sync: + //put the sync IO into output mode - FPGA must be input + //write low, then write high - this triggers a soft sync + _lmk04816_regs.SYNC_POL_INV = lmk04816_regs_t::SYNC_POL_INV_SYNC_LOW; + this->write_regs(11); + _lmk04816_regs.SYNC_POL_INV = lmk04816_regs_t::SYNC_POL_INV_SYNC_HIGH; + this->write_regs(11); +} + +double get_master_clock_rate(void) { + return _master_clock_rate; +} + +double get_sysref_clock_rate(void) { + return _system_ref_rate; +} + +double get_refout_clock_rate(void) { + //We support only one reference output rate + return 10e6; +} + +void set_dboard_rate(const x300_clock_which_t, double rate) { + if(not doubles_are_equal(rate, get_master_clock_rate())) { + throw uhd::not_implemented_error("x3xx set dboard clock rate does not support setting an arbitrary clock rate"); + } +} + +std::vector<double> get_dboard_rates(const x300_clock_which_t) { + /* Right now, the only supported daughterboard clock rate is the master clock + * rate. TODO Implement divider settings for lower clock rates for legacy + * daughterboard support. */ + + std::vector<double> rates; + rates.push_back(get_master_clock_rate()); + return rates; +} + +void set_ref_out(const bool enable) { + // TODO Implement divider configuration to allow for configurable output + // rates + if (enable) + _lmk04816_regs.CLKout10_TYPE = lmk04816_regs_t::CLKOUT10_TYPE_LVDS; + else + _lmk04816_regs.CLKout10_TYPE = lmk04816_regs_t::CLKOUT10_TYPE_P_DOWN; + this->write_regs(8); +} + +void write_regs(boost::uint8_t addr) { + boost::uint32_t data = _lmk04816_regs.get_reg(addr); + _spiface->write_spi(_slaveno, spi_config_t::EDGE_RISE, data,32); +} + + +private: + +void set_master_clock_rate(double clock_rate) { + /* The X3xx has two primary rates. The first is the + * _system_ref_rate, which is sourced from the "clock_source"/"value" field + * of the property tree, and whose value can be 10e6, 30.72e6, or 200e6. + * The _system_ref_rate is the input to the clocking system, and + * what comes out is a disciplined master clock running at the + * _master_clock_rate. As such, only certain combinations of + * system reference rates and master clock rates are supported. + * Additionally, a subset of these will operate in "zero delay" mode. */ + + enum opmode_t { INVALID, + m10M_200M_NOZDEL, // used for debug purposes only + m10M_200M_ZDEL, // Normal mode + m30_72M_184_32M_ZDEL, // LTE with external ref, aka CPRI Mode + m10M_184_32M_NOZDEL, // LTE with 10 MHz ref + m10M_120M_ZDEL }; // NI USRP 120 MHz Clocking + + /* The default clocking mode is 10MHz reference generating a 200 MHz master + * clock, in zero-delay mode. */ + opmode_t clocking_mode = INVALID; + + if(doubles_are_equal(_system_ref_rate, 10e6)) { + if(doubles_are_equal(clock_rate, 184.32e6)) { + /* 10MHz reference, 184.32 MHz master clock out, NOT Zero Delay. */ + clocking_mode = m10M_184_32M_NOZDEL; + } else if(doubles_are_equal(clock_rate, 200e6)) { + /* 10MHz reference, 200 MHz master clock out, Zero Delay */ + clocking_mode = m10M_200M_ZDEL; + } else if(doubles_are_equal(clock_rate, 120e6)) { + /* 10MHz reference, 120 MHz master clock rate, Zero Delay */ + clocking_mode = m10M_120M_ZDEL; + } + } else if(doubles_are_equal(_system_ref_rate, 30.72e6)) { + if(doubles_are_equal(clock_rate, 184.32e6)) { + /* 30.72MHz reference, 184.32 MHz master clock out, Zero Delay */ + clocking_mode = m30_72M_184_32M_ZDEL; + } + } + + if(clocking_mode == INVALID) { + throw uhd::runtime_error(str(boost::format("A master clock rate of %f cannot be derived from a system reference rate of %f") % clock_rate % _system_ref_rate)); + } + + // For 200 MHz output, the VCO is run at 2400 MHz + // For the LTE/CPRI rate of 184.32 MHz, the VCO runs at 2580.48 MHz + + int vco_div = 0; + + // Note: PLL2 N2 prescaler is enabled for all cases + // PLL2 reference doubler is enabled for all cases + + /* All LMK04816 settings are from the LMK datasheet for our clocking + * architecture. Please refer to the datasheet for more information. */ + switch (clocking_mode) { + case m10M_200M_NOZDEL: + vco_div = 12; + _lmk04816_regs.MODE = lmk04816_regs_t::MODE_DUAL_INT; + + // PLL1 - 2 MHz compare frequency + _lmk04816_regs.PLL1_N_28 = 48; + _lmk04816_regs.PLL1_R_27 = 5; + _lmk04816_regs.PLL1_CP_GAIN_27 = lmk04816_regs_t::PLL1_CP_GAIN_27_100UA; + + // PLL2 - 48 MHz compare frequency + _lmk04816_regs.PLL2_N_30 = 25; + _lmk04816_regs.PLL2_P_30 = lmk04816_regs_t::PLL2_P_30_DIV_2A; + _lmk04816_regs.PLL2_R_28 = 4; + _lmk04816_regs.PLL2_CP_GAIN_26 = lmk04816_regs_t::PLL2_CP_GAIN_26_3200UA; + + break; + + case m10M_200M_ZDEL: + vco_div = 12; + _lmk04816_regs.MODE = lmk04816_regs_t::MODE_DUAL_INT_ZER_DELAY; + + // PLL1 - 2 MHz compare frequency + _lmk04816_regs.PLL1_N_28 = 100; + _lmk04816_regs.PLL1_R_27 = 5; + _lmk04816_regs.PLL1_CP_GAIN_27 = lmk04816_regs_t::PLL1_CP_GAIN_27_100UA; + + // PLL2 - 96 MHz compare frequency + _lmk04816_regs.PLL2_N_30 = 5; + _lmk04816_regs.PLL2_P_30 = lmk04816_regs_t::PLL2_P_30_DIV_5; + _lmk04816_regs.PLL2_R_28 = 2; + + if(_hw_rev <= 4) + _lmk04816_regs.PLL2_CP_GAIN_26 = lmk04816_regs_t::PLL2_CP_GAIN_26_1600UA; + else + _lmk04816_regs.PLL2_CP_GAIN_26 = lmk04816_regs_t::PLL2_CP_GAIN_26_400UA; + + break; + + case m30_72M_184_32M_ZDEL: + vco_div=14; + _lmk04816_regs.MODE = lmk04816_regs_t::MODE_DUAL_INT_ZER_DELAY; + + // PLL1 - 2.048 MHz compare frequency + _lmk04816_regs.PLL1_N_28 = 90; + _lmk04816_regs.PLL1_R_27 = 15; + _lmk04816_regs.PLL1_CP_GAIN_27 = lmk04816_regs_t::PLL1_CP_GAIN_27_100UA; + + // PLL2 - 7.68 MHz compare frequency + _lmk04816_regs.PLL2_N_30 = 168; + _lmk04816_regs.PLL2_P_30 = lmk04816_regs_t::PLL2_P_30_DIV_2A; + _lmk04816_regs.PLL2_R_28 = 25; + _lmk04816_regs.PLL2_CP_GAIN_26 = lmk04816_regs_t::PLL2_CP_GAIN_26_3200UA; + + _lmk04816_regs.PLL2_R3_LF = lmk04816_regs_t::PLL2_R3_LF_1KILO_OHM; + _lmk04816_regs.PLL2_C3_LF = lmk04816_regs_t::PLL2_C3_LF_39PF; + + _lmk04816_regs.PLL2_R4_LF = lmk04816_regs_t::PLL2_R4_LF_1KILO_OHM; + _lmk04816_regs.PLL2_C4_LF = lmk04816_regs_t::PLL2_C4_LF_34PF; + + break; + + case m10M_184_32M_NOZDEL: + vco_div=14; + _lmk04816_regs.MODE = lmk04816_regs_t::MODE_DUAL_INT; + + // PLL1 - 2 MHz compare frequency + _lmk04816_regs.PLL1_N_28 = 48; + _lmk04816_regs.PLL1_R_27 = 5; + _lmk04816_regs.PLL1_CP_GAIN_27 = lmk04816_regs_t::PLL1_CP_GAIN_27_100UA; + + // PLL2 - 7.68 MHz compare frequency + _lmk04816_regs.PLL2_N_30 = 168; + _lmk04816_regs.PLL2_P_30 = lmk04816_regs_t::PLL2_P_30_DIV_2A; + _lmk04816_regs.PLL2_R_28 = 25; + _lmk04816_regs.PLL2_CP_GAIN_26 = lmk04816_regs_t::PLL2_CP_GAIN_26_3200UA; + + _lmk04816_regs.PLL2_R3_LF = lmk04816_regs_t::PLL2_R3_LF_4KILO_OHM; + _lmk04816_regs.PLL2_C3_LF = lmk04816_regs_t::PLL2_C3_LF_39PF; + + _lmk04816_regs.PLL2_R4_LF = lmk04816_regs_t::PLL2_R4_LF_1KILO_OHM; + _lmk04816_regs.PLL2_C4_LF = lmk04816_regs_t::PLL2_C4_LF_71PF; + + break; + + case m10M_120M_ZDEL: + vco_div = 20; + _lmk04816_regs.MODE = lmk04816_regs_t::MODE_DUAL_INT_ZER_DELAY; + + // PLL1 - 2 MHz compare frequency + _lmk04816_regs.PLL1_N_28 = 60; + _lmk04816_regs.PLL1_R_27 = 5; + _lmk04816_regs.PLL1_CP_GAIN_27 = lmk04816_regs_t::PLL1_CP_GAIN_27_100UA; + + // PLL2 - 96 MHz compare frequency + _lmk04816_regs.PLL2_N_30 = 5; + _lmk04816_regs.PLL2_P_30 = lmk04816_regs_t::PLL2_P_30_DIV_5; + _lmk04816_regs.PLL2_R_28 = 2; + + if(_hw_rev <= 4) + _lmk04816_regs.PLL2_CP_GAIN_26 = lmk04816_regs_t::PLL2_CP_GAIN_26_1600UA; + else + _lmk04816_regs.PLL2_CP_GAIN_26 = lmk04816_regs_t::PLL2_CP_GAIN_26_400UA; + + break; + + default: + UHD_THROW_INVALID_CODE_PATH(); + break; + }; + + /* Reset the LMK clock controller. */ + _lmk04816_regs.RESET = lmk04816_regs_t::RESET_RESET; + this->write_regs(0); + _lmk04816_regs.RESET = lmk04816_regs_t::RESET_NO_RESET; + this->write_regs(0); + + /* Initial power-up */ + _lmk04816_regs.CLKout0_1_PD = lmk04816_regs_t::CLKOUT0_1_PD_POWER_UP; + this->write_regs(0); + _lmk04816_regs.CLKout0_1_DIV = vco_div; + this->write_regs(0); + + // Register 1 + _lmk04816_regs.CLKout2_3_PD = lmk04816_regs_t::CLKOUT2_3_PD_POWER_UP; + _lmk04816_regs.CLKout2_3_DIV = vco_div; + // Register 2 + _lmk04816_regs.CLKout4_5_PD = lmk04816_regs_t::CLKOUT4_5_PD_POWER_UP; + _lmk04816_regs.CLKout4_5_DIV = vco_div; + // Register 3 + _lmk04816_regs.CLKout6_7_DIV = vco_div; + _lmk04816_regs.CLKout6_7_OSCin_Sel = lmk04816_regs_t::CLKOUT6_7_OSCIN_SEL_VCO; + // Register 4 + _lmk04816_regs.CLKout8_9_DIV = vco_div; + // Register 5 + _lmk04816_regs.CLKout10_11_PD = lmk04816_regs_t::CLKOUT10_11_PD_NORMAL; + _lmk04816_regs.CLKout10_11_DIV = vco_div; + + // Register 6 + _lmk04816_regs.CLKout0_TYPE = lmk04816_regs_t::CLKOUT0_TYPE_LVDS; //FPGA + _lmk04816_regs.CLKout1_TYPE = lmk04816_regs_t::CLKOUT1_TYPE_P_DOWN; //CPRI feedback clock, use LVDS + _lmk04816_regs.CLKout2_TYPE = lmk04816_regs_t::CLKOUT2_TYPE_LVPECL_700MVPP; //DB_0_RX + _lmk04816_regs.CLKout3_TYPE = lmk04816_regs_t::CLKOUT3_TYPE_LVPECL_700MVPP; //DB_1_RX + // Register 7 + _lmk04816_regs.CLKout4_TYPE = lmk04816_regs_t::CLKOUT4_TYPE_LVPECL_700MVPP; //DB_1_TX + _lmk04816_regs.CLKout5_TYPE = lmk04816_regs_t::CLKOUT5_TYPE_LVPECL_700MVPP; //DB_0_TX + _lmk04816_regs.CLKout6_TYPE = lmk04816_regs_t::CLKOUT6_TYPE_LVPECL_700MVPP; //DB0_DAC + _lmk04816_regs.CLKout7_TYPE = lmk04816_regs_t::CLKOUT7_TYPE_LVPECL_700MVPP; //DB1_DAC + _lmk04816_regs.CLKout8_TYPE = lmk04816_regs_t::CLKOUT8_TYPE_LVPECL_700MVPP; //DB0_ADC + // Register 8 + _lmk04816_regs.CLKout9_TYPE = lmk04816_regs_t::CLKOUT9_TYPE_LVPECL_700MVPP; //DB1_ADC + _lmk04816_regs.CLKout10_TYPE = lmk04816_regs_t::CLKOUT10_TYPE_LVDS; //REF_CLKOUT + _lmk04816_regs.CLKout11_TYPE = lmk04816_regs_t::CLKOUT11_TYPE_P_DOWN; //Debug header, use LVPECL + + + // Register 10 + _lmk04816_regs.EN_OSCout0 = lmk04816_regs_t::EN_OSCOUT0_DISABLED; //Debug header + _lmk04816_regs.FEEDBACK_MUX = 0; //use output 0 (FPGA clock) for feedback + _lmk04816_regs.EN_FEEDBACK_MUX = lmk04816_regs_t::EN_FEEDBACK_MUX_ENABLED; + + // Register 11 + // MODE set in individual cases above + _lmk04816_regs.SYNC_QUAL = lmk04816_regs_t::SYNC_QUAL_FB_MUX; + _lmk04816_regs.EN_SYNC = lmk04816_regs_t::EN_SYNC_ENABLE; + _lmk04816_regs.NO_SYNC_CLKout0_1 = lmk04816_regs_t::NO_SYNC_CLKOUT0_1_CLOCK_XY_SYNC; + _lmk04816_regs.NO_SYNC_CLKout2_3 = lmk04816_regs_t::NO_SYNC_CLKOUT2_3_CLOCK_XY_SYNC; + _lmk04816_regs.NO_SYNC_CLKout4_5 = lmk04816_regs_t::NO_SYNC_CLKOUT4_5_CLOCK_XY_SYNC; + _lmk04816_regs.NO_SYNC_CLKout8_9 = lmk04816_regs_t::NO_SYNC_CLKOUT8_9_CLOCK_XY_SYNC; + _lmk04816_regs.NO_SYNC_CLKout10_11 = lmk04816_regs_t::NO_SYNC_CLKOUT10_11_CLOCK_XY_SYNC; + _lmk04816_regs.SYNC_EN_AUTO = lmk04816_regs_t::SYNC_EN_AUTO_SYNC_INT_GEN; + _lmk04816_regs.SYNC_POL_INV = lmk04816_regs_t::SYNC_POL_INV_SYNC_LOW; + _lmk04816_regs.SYNC_TYPE = lmk04816_regs_t::SYNC_TYPE_INPUT; + + // Register 12 + _lmk04816_regs.LD_MUX = lmk04816_regs_t::LD_MUX_BOTH; + + /* Input Clock Configurations */ + // Register 13 + _lmk04816_regs.EN_CLKin0 = lmk04816_regs_t::EN_CLKIN0_NO_VALID_USE; // This is not connected + _lmk04816_regs.EN_CLKin2 = lmk04816_regs_t::EN_CLKIN2_NO_VALID_USE; // Used only for CPRI + _lmk04816_regs.Status_CLKin1_MUX = lmk04816_regs_t::STATUS_CLKIN1_MUX_UWIRE_RB; + _lmk04816_regs.CLKin_Select_MODE = lmk04816_regs_t::CLKIN_SELECT_MODE_CLKIN1_MAN; + _lmk04816_regs.HOLDOVER_MUX = lmk04816_regs_t::HOLDOVER_MUX_PLL1_R; + // Register 14 + _lmk04816_regs.Status_CLKin1_TYPE = lmk04816_regs_t::STATUS_CLKIN1_TYPE_OUT_PUSH_PULL; + _lmk04816_regs.Status_CLKin0_TYPE = lmk04816_regs_t::STATUS_CLKIN0_TYPE_OUT_PUSH_PULL; + + // Register 26 + // PLL2_CP_GAIN_26 set above in individual cases + _lmk04816_regs.PLL2_CP_POL_26 = lmk04816_regs_t::PLL2_CP_POL_26_NEG_SLOPE; + _lmk04816_regs.EN_PLL2_REF_2X = lmk04816_regs_t::EN_PLL2_REF_2X_DOUBLED_FREQ_REF; + + // Register 27 + // PLL1_CP_GAIN_27 set in individual cases above + // PLL1_R_27 set in the individual cases above + + // Register 28 + // PLL1_N_28 and PLL2_R_28 are set in the individual cases above + + // Register 29 + _lmk04816_regs.PLL2_N_CAL_29 = _lmk04816_regs.PLL2_N_30; // N_CAL should always match N + _lmk04816_regs.OSCin_FREQ_29 = lmk04816_regs_t::OSCIN_FREQ_29_63_TO_127MHZ; + + // Register 30 + // PLL2_P_30 set in individual cases above + // PLL2_N_30 set in individual cases above + + /* Write the configuration values into the LMK */ + for (size_t i = 1; i <= 16; ++i) { + this->write_regs(i); + } + for (size_t i = 24; i <= 31; ++i) { + this->write_regs(i); + } + + this->sync_clocks(); +} + +UHD_INLINE bool doubles_are_equal(double a, double b) { + return (std::fabs(a - b) < std::numeric_limits<double>::epsilon()); +} + +const spi_iface::sptr _spiface; +const size_t _slaveno; +const size_t _hw_rev; +const double _master_clock_rate; +const double _system_ref_rate; +lmk04816_regs_t _lmk04816_regs; +}; + +x300_clock_ctrl::sptr x300_clock_ctrl::make(uhd::spi_iface::sptr spiface, + const size_t slaveno, + const size_t hw_rev, + const double master_clock_rate, + const double system_ref_rate) { + return sptr(new x300_clock_ctrl_impl(spiface, slaveno, hw_rev, + master_clock_rate, system_ref_rate)); +} diff --git a/host/lib/usrp/x300/x300_clock_ctrl.hpp b/host/lib/usrp/x300/x300_clock_ctrl.hpp new file mode 100644 index 000000000..0e3caf900 --- /dev/null +++ b/host/lib/usrp/x300/x300_clock_ctrl.hpp @@ -0,0 +1,83 @@ +// +// Copyright 2013-2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// 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_X300_CLOCK_CTRL_HPP +#define INCLUDED_X300_CLOCK_CTRL_HPP + +#include <uhd/types/serial.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/utility.hpp> + + +enum x300_clock_which_t +{ + X300_CLOCK_WHICH_ADC0, + X300_CLOCK_WHICH_ADC1, + X300_CLOCK_WHICH_DAC0, + X300_CLOCK_WHICH_DAC1, + X300_CLOCK_WHICH_DB0_RX, + X300_CLOCK_WHICH_DB0_TX, + X300_CLOCK_WHICH_DB1_RX, + X300_CLOCK_WHICH_DB1_TX, + X300_CLOCK_WHICH_TEST, +}; + +class x300_clock_ctrl : boost::noncopyable +{ +public: + + typedef boost::shared_ptr<x300_clock_ctrl> sptr; + + static sptr make(uhd::spi_iface::sptr spiface, + const size_t slaveno, + const size_t hw_rev, + const double master_clock_rate, + const double system_ref_rate); + + /*! Get the master clock rate of the device. + * \return the clock frequency in Hz + */ + virtual double get_master_clock_rate(void) = 0; + + /*! Get the system reference rate of the device. + * \return the clock frequency in Hz + */ + virtual double get_sysref_clock_rate(void) = 0; + + /*! Get the current reference output rate + * \return the clock frequency in Hz + */ + virtual double get_refout_clock_rate(void) = 0; + + /*! Set the clock rate on the given daughterboard clock. + * \param rate the new clock rate + * \throw exception when rate invalid + */ + virtual void set_dboard_rate(const x300_clock_which_t which, double rate) = 0; + + /*! Get a list of possible daughterboard clock rates. + * \return a list of clock rates in Hz + */ + virtual std::vector<double> get_dboard_rates(const x300_clock_which_t which) = 0; + + /*! Turn the reference output on/off + * \param true = on, false = off + */ + virtual void set_ref_out(const bool) = 0; +}; + +#endif /* INCLUDED_X300_CLOCK_CTRL_HPP */ diff --git a/host/lib/usrp/x300/x300_dac_ctrl.cpp b/host/lib/usrp/x300/x300_dac_ctrl.cpp new file mode 100644 index 000000000..5eae9cc48 --- /dev/null +++ b/host/lib/usrp/x300/x300_dac_ctrl.cpp @@ -0,0 +1,146 @@ +// +// Copyright 2010-2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// 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 "x300_dac_ctrl.hpp" +#include "x300_regs.hpp" +#include <uhd/types/time_spec.hpp> +#include <uhd/utils/msg.hpp> +#include <uhd/utils/log.hpp> +#include <uhd/utils/safe_call.hpp> +#include <uhd/exception.hpp> +#include <boost/foreach.hpp> +#include <boost/thread/thread.hpp> //sleep + +using namespace uhd; + +#define write_ad9146_reg(addr, data) \ + _iface->write_spi(_slaveno, spi_config_t::EDGE_RISE, ((addr) << 8) | (data), 16) +#define read_ad9146_reg(addr) \ + (_iface->read_spi(_slaveno, spi_config_t::EDGE_RISE, ((addr) << 8) | (1 << 15), 16) & 0xff) + +/*! + * A X300 codec control specific to the ad9146 ic. + */ +class x300_dac_ctrl_impl : public x300_dac_ctrl +{ +public: + x300_dac_ctrl_impl(uhd::spi_iface::sptr iface, const size_t slaveno, const double refclk): + _iface(iface), _slaveno(slaveno) + { + write_ad9146_reg(0x00, 0x20); // Take DAC into reset. + write_ad9146_reg(0x00, 0x80); // Enable SPI reads and come out of reset + write_ad9146_reg(0x1e, 0x01); // Data path config - set for proper operation + + // Calculate N0 to be VCO friendly. + // Aim for VCO between 1 and 2GHz, assert otherwise. + // const int N1 = 4; + const int N1 = 4; + int N0_val, N0; + for (N0_val = 0; N0_val < 3; N0_val++) + { + N0 = (1 << N0_val); //1, 2, 4 + if ((refclk * N0 * N1) >= 1e9) break; + } + UHD_ASSERT_THROW((refclk * N0 * N1) >= 1e9); + UHD_ASSERT_THROW((refclk * N0 * N1) <= 2e9); + + /* Start PLL */ + //write_ad9146_reg(0x0C, 0xD1); // Narrow PLL loop filter, Midrange charge pump. + write_ad9146_reg(0x0D, 0xD1 | (N0_val << 2)); // N1=4, N2=16, N0 as calculated + //write_ad9146_reg(0x0D, 0x90 | (N0_val << 2)); // N1=2, N2=8, N0 as calculated + write_ad9146_reg(0x0A, 0xCF); // Auto init VCO band training as per datasheet + write_ad9146_reg(0x0A, 0xA0); // See above. + + // Verify PLL is Locked. 1 sec timeout. + // NOTE: Data sheet inconsistant about which pins give PLL lock status. FIXME! + const time_spec_t exit_time = time_spec_t::get_system_time() + time_spec_t(1.0); + while (true) + { + const size_t reg_e = read_ad9146_reg(0x0E); /* Expect bit 7 = 1 */ + if ((exit_time < time_spec_t::get_system_time()) && ((reg_e & (1 << 7)) == 0)) + throw uhd::runtime_error("x300_dac_ctrl: timeout waiting for DAC PLL to lock"); + else if ((reg_e & ((1 << 7) | (1 << 6))) != 0) break; + boost::this_thread::sleep(boost::posix_time::milliseconds(10)); + } + + /* Skew DCI signal to find stable data eye */ + //write_ad9146_reg(0x16, 0x04); //Disable delay in DCI + //write_ad9146_reg(0x16, 0x00); //165ps delay in DCI + //write_ad9146_reg(0x16, 0x01); //375ps delay in DCI + write_ad9146_reg(0x16, 0x02); //615ps delay in DCI + //write_ad9146_reg(0x16, 0x03); //720ps delay in DCI + + write_ad9146_reg(0x03, 0x00); // 2's comp, I first, byte wide interface + + //fpga wants I,Q in the sample word: + //first transaction goes into low bits + //second transaction goes into high bits + //therefore, we want Q to go first (bit 6 == 1) + write_ad9146_reg(0x03, (1 << 6)); //2s comp, i first, byte mode + + write_ad9146_reg(0x10, 0x48); // Disable SYNC mode. + write_ad9146_reg(0x17, 0x04); // FIFO write pointer offset + write_ad9146_reg(0x18, 0x02); // Request soft FIFO align + write_ad9146_reg(0x18, 0x00); // (See above) + write_ad9146_reg(0x1B, 0xE4); // Bypass: Modulator, InvSinc, IQ Bal + + /* Configure interpolation filters */ + write_ad9146_reg(0x1C, 0x00); // Configure HB1 + write_ad9146_reg(0x1D, 0x00); // Configure HB2 + + } + + + ~x300_dac_ctrl_impl(void) + { + UHD_SAFE_CALL + ( + write_ad9146_reg(0x1, 0xf); //total power down + write_ad9146_reg(0x2, 0xf); //total power down + ) + } + + void arm_dac_sync(void) + { + // + // Attempt to synchronize AD9146's + // + write_ad9146_reg(0x10, 0xCF); // Enable SYNC mode. Sync Averaging set to 128. + + const time_spec_t exit_time = time_spec_t::get_system_time() + time_spec_t(1.0); + while (true) + { + const size_t reg_12 = read_ad9146_reg(0x12); /* Expect bit 7 = 0, bit 6 = 1 */ + if ((exit_time < time_spec_t::get_system_time()) && (((reg_12 & (1 << 6)) == 0) || ((reg_12 & (1 << 7)) != 0))) + throw uhd::runtime_error("x300_dac_ctrl: timeout waiting for backend synchronization"); + else if (((reg_12 & (1 << 6)) != 0) && ((reg_12 & (1 << 7)) == 0)) break; + boost::this_thread::sleep(boost::posix_time::milliseconds(10)); + } + } + +private: + uhd::spi_iface::sptr _iface; + const size_t _slaveno; +}; + +/*********************************************************************** + * Public make function for the DAC control + **********************************************************************/ +x300_dac_ctrl::sptr x300_dac_ctrl::make(uhd::spi_iface::sptr iface, const size_t slaveno, const double clock_rate) +{ + return sptr(new x300_dac_ctrl_impl(iface, slaveno, clock_rate)); +} diff --git a/host/lib/usrp/x300/x300_dac_ctrl.hpp b/host/lib/usrp/x300/x300_dac_ctrl.hpp new file mode 100644 index 000000000..0db7e1e35 --- /dev/null +++ b/host/lib/usrp/x300/x300_dac_ctrl.hpp @@ -0,0 +1,42 @@ +// +// Copyright 2010-2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// 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_X300_DAC_CTRL_HPP +#define INCLUDED_X300_DAC_CTRL_HPP + +#include <uhd/types/serial.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/utility.hpp> + +class x300_dac_ctrl : boost::noncopyable +{ +public: + typedef boost::shared_ptr<x300_dac_ctrl> sptr; + + /*! + * Make a codec control for the DAC. + * \param iface a pointer to the interface object + * \param spiface the interface to spi + * \return a new codec control object + */ + static sptr make(uhd::spi_iface::sptr iface, const size_t slaveno, const double clock_rate); + + // ! Arm the sync feature in DAC + virtual void arm_dac_sync(void) = 0; +}; + +#endif /* INCLUDED_X300_DAC_CTRL_HPP */ diff --git a/host/lib/usrp/x300/x300_dboard_iface.cpp b/host/lib/usrp/x300/x300_dboard_iface.cpp new file mode 100644 index 000000000..43da7ca08 --- /dev/null +++ b/host/lib/usrp/x300/x300_dboard_iface.cpp @@ -0,0 +1,333 @@ +// +// Copyright 2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include "x300_impl.hpp" +#include "x300_regs.hpp" +#include <uhd/usrp/dboard_iface.hpp> +#include <uhd/utils/safe_call.hpp> +#include <boost/assign/list_of.hpp> +#include <boost/math/special_functions/round.hpp> +#include "ad7922_regs.hpp" //aux adc +#include "ad5623_regs.hpp" //aux dac + +using namespace uhd; +using namespace uhd::usrp; +using namespace boost::assign; + +class x300_dboard_iface : public dboard_iface +{ +public: + x300_dboard_iface(const x300_dboard_iface_config_t &config); + ~x300_dboard_iface(void); + + special_props_t get_special_props(void) + { + special_props_t props; + props.soft_clock_divider = false; + props.mangle_i2c_addrs = (_config.dboard_slot == 1); + return props; + } + + void write_aux_dac(unit_t, aux_dac_t, double); + double read_aux_adc(unit_t, aux_adc_t); + + void _set_pin_ctrl(unit_t, boost::uint16_t); + void _set_atr_reg(unit_t, atr_reg_t, boost::uint16_t); + void _set_gpio_ddr(unit_t, boost::uint16_t); + void _set_gpio_out(unit_t, boost::uint16_t); + + void set_gpio_debug(unit_t, int); + boost::uint16_t read_gpio(unit_t); + + void write_i2c(boost::uint16_t, const byte_vector_t &); + byte_vector_t read_i2c(boost::uint16_t, size_t); + + void set_clock_rate(unit_t, double); + double get_clock_rate(unit_t); + std::vector<double> get_clock_rates(unit_t); + void set_clock_enabled(unit_t, bool); + double get_codec_rate(unit_t); + + void write_spi( + unit_t unit, + const spi_config_t &config, + boost::uint32_t data, + size_t num_bits + ); + + boost::uint32_t read_write_spi( + unit_t unit, + const spi_config_t &config, + boost::uint32_t data, + size_t num_bits + ); + + const x300_dboard_iface_config_t _config; + uhd::dict<unit_t, ad5623_regs_t> _dac_regs; + uhd::dict<unit_t, double> _clock_rates; + void _write_aux_dac(unit_t); + +}; + +/*********************************************************************** + * Make Function + **********************************************************************/ +dboard_iface::sptr x300_make_dboard_iface(const x300_dboard_iface_config_t &config) +{ + return dboard_iface::sptr(new x300_dboard_iface(config)); +} + +/*********************************************************************** + * Structors + **********************************************************************/ +x300_dboard_iface::x300_dboard_iface(const x300_dboard_iface_config_t &config): + _config(config) +{ + //reset the aux dacs + _dac_regs[UNIT_RX] = ad5623_regs_t(); + _dac_regs[UNIT_TX] = ad5623_regs_t(); + BOOST_FOREACH(unit_t unit, _dac_regs.keys()) + { + _dac_regs[unit].data = 1; + _dac_regs[unit].addr = ad5623_regs_t::ADDR_ALL; + _dac_regs[unit].cmd = ad5623_regs_t::CMD_RESET; + this->_write_aux_dac(unit); + } + + this->set_clock_enabled(UNIT_RX, false); + this->set_clock_enabled(UNIT_TX, false); + + this->set_clock_rate(UNIT_RX, _config.clock->get_master_clock_rate()); + this->set_clock_rate(UNIT_TX, _config.clock->get_master_clock_rate()); + + + //some test code + /* + { + + this->write_aux_dac(UNIT_TX, AUX_DAC_A, .1); + this->write_aux_dac(UNIT_TX, AUX_DAC_B, 1); + this->write_aux_dac(UNIT_RX, AUX_DAC_A, 2); + this->write_aux_dac(UNIT_RX, AUX_DAC_B, 3); + while (1) + { + UHD_VAR(this->read_aux_adc(UNIT_TX, AUX_ADC_A)); + UHD_VAR(this->read_aux_adc(UNIT_TX, AUX_ADC_B)); + UHD_VAR(this->read_aux_adc(UNIT_RX, AUX_ADC_A)); + UHD_VAR(this->read_aux_adc(UNIT_RX, AUX_ADC_B)); + sleep(1); + } + } + */ + +} + +x300_dboard_iface::~x300_dboard_iface(void) +{ + UHD_SAFE_CALL + ( + this->set_clock_enabled(UNIT_RX, false); + this->set_clock_enabled(UNIT_TX, false); + ) +} + +/*********************************************************************** + * Clocks + **********************************************************************/ +void x300_dboard_iface::set_clock_rate(unit_t unit, double rate) +{ + _clock_rates[unit] = rate; //set to shadow + switch(unit) + { + case UNIT_RX: + _config.clock->set_dboard_rate(_config.which_rx_clk, rate); + return; + case UNIT_TX: + _config.clock->set_dboard_rate(_config.which_tx_clk, rate); + return; + } +} + +double x300_dboard_iface::get_clock_rate(unit_t unit) +{ + return _clock_rates[unit]; //get from shadow +} + +std::vector<double> x300_dboard_iface::get_clock_rates(unit_t unit) +{ + switch(unit) + { + case UNIT_RX: + return _config.clock->get_dboard_rates(_config.which_rx_clk); + case UNIT_TX: + return _config.clock->get_dboard_rates(_config.which_tx_clk); + default: + UHD_THROW_INVALID_CODE_PATH(); + } +} + +void x300_dboard_iface::set_clock_enabled(unit_t unit, bool enb) +{ + // TODO Variable DBoard clock control needs to be implemented for X300. +} + +double x300_dboard_iface::get_codec_rate(unit_t) +{ + return _config.clock->get_master_clock_rate(); +} + +/*********************************************************************** + * GPIO + **********************************************************************/ +void x300_dboard_iface::_set_pin_ctrl(unit_t unit, boost::uint16_t value) +{ + return _config.gpio->set_pin_ctrl(unit, value); +} + +void x300_dboard_iface::_set_gpio_ddr(unit_t unit, boost::uint16_t value) +{ + return _config.gpio->set_gpio_ddr(unit, value); +} + +void x300_dboard_iface::_set_gpio_out(unit_t unit, boost::uint16_t value) +{ + return _config.gpio->set_gpio_out(unit, value); +} + +boost::uint16_t x300_dboard_iface::read_gpio(unit_t unit) +{ + return _config.gpio->read_gpio(unit); +} + +void x300_dboard_iface::_set_atr_reg(unit_t unit, atr_reg_t atr, boost::uint16_t value) +{ + return _config.gpio->set_atr_reg(unit, atr, value); +} + +void x300_dboard_iface::set_gpio_debug(unit_t, int) +{ + throw uhd::not_implemented_error("no set_gpio_debug implemented"); +} + +/*********************************************************************** + * SPI + **********************************************************************/ +#define toslaveno(unit) \ + (((unit) == dboard_iface::UNIT_TX)? _config.tx_spi_slaveno : _config.rx_spi_slaveno) + +void x300_dboard_iface::write_spi( + unit_t unit, + const spi_config_t &config, + boost::uint32_t data, + size_t num_bits +){ + _config.spi->write_spi(toslaveno(unit), config, data, num_bits); +} + +boost::uint32_t x300_dboard_iface::read_write_spi( + unit_t unit, + const spi_config_t &config, + boost::uint32_t data, + size_t num_bits +){ + return _config.spi->read_spi(toslaveno(unit), config, data, num_bits); +} + +/*********************************************************************** + * I2C + **********************************************************************/ +void x300_dboard_iface::write_i2c(boost::uint16_t addr, const byte_vector_t &bytes) +{ + return _config.i2c->write_i2c(addr, bytes); +} + +byte_vector_t x300_dboard_iface::read_i2c(boost::uint16_t addr, size_t num_bytes) +{ + return _config.i2c->read_i2c(addr, num_bytes); +} + +/*********************************************************************** + * Aux DAX/ADC + **********************************************************************/ +void x300_dboard_iface::_write_aux_dac(unit_t unit) +{ + static const uhd::dict<unit_t, int> unit_to_spi_dac = map_list_of + (UNIT_RX, DB_RX_LSDAC_SEN) + (UNIT_TX, DB_TX_LSDAC_SEN) + ; + _config.spi->write_spi( + unit_to_spi_dac[unit], spi_config_t::EDGE_FALL, + _dac_regs[unit].get_reg(), 24 + ); +} + +void x300_dboard_iface::write_aux_dac(unit_t unit, aux_dac_t which, double value) +{ + _dac_regs[unit].data = boost::math::iround(4095*value/3.3); + _dac_regs[unit].cmd = ad5623_regs_t::CMD_WR_UP_DAC_CHAN_N; + + typedef uhd::dict<aux_dac_t, ad5623_regs_t::addr_t> aux_dac_to_addr; + static const uhd::dict<unit_t, aux_dac_to_addr> unit_to_which_to_addr = map_list_of + (UNIT_RX, map_list_of + (AUX_DAC_A, ad5623_regs_t::ADDR_DAC_A) + (AUX_DAC_B, ad5623_regs_t::ADDR_DAC_B) + (AUX_DAC_C, ad5623_regs_t::ADDR_DAC_B) + (AUX_DAC_D, ad5623_regs_t::ADDR_DAC_A) + ) + (UNIT_TX, map_list_of + (AUX_DAC_A, ad5623_regs_t::ADDR_DAC_A) + (AUX_DAC_B, ad5623_regs_t::ADDR_DAC_B) + (AUX_DAC_C, ad5623_regs_t::ADDR_DAC_B) + (AUX_DAC_D, ad5623_regs_t::ADDR_DAC_A) + ) + ; + _dac_regs[unit].addr = unit_to_which_to_addr[unit][which]; + this->_write_aux_dac(unit); +} + +double x300_dboard_iface::read_aux_adc(unit_t unit, aux_adc_t which) +{ + static const uhd::dict<unit_t, int> unit_to_spi_adc = map_list_of + (UNIT_RX, DB_RX_LSADC_SEN) + (UNIT_TX, DB_TX_LSADC_SEN) + ; + + //setup spi config args + spi_config_t config; + config.mosi_edge = spi_config_t::EDGE_FALL; + config.miso_edge = spi_config_t::EDGE_RISE; + + //setup the spi registers + ad7922_regs_t ad7922_regs; + switch(which){ + case AUX_ADC_A: ad7922_regs.mod = 0; break; + case AUX_ADC_B: ad7922_regs.mod = 1; break; + } ad7922_regs.chn = ad7922_regs.mod; //normal mode: mod == chn + + //write and read spi + _config.spi->write_spi( + unit_to_spi_adc[unit], config, + ad7922_regs.get_reg(), 16 + ); + ad7922_regs.set_reg(boost::uint16_t(_config.spi->read_spi( + unit_to_spi_adc[unit], config, + ad7922_regs.get_reg(), 16 + ))); + + //convert to voltage and return + return 3.3*ad7922_regs.result/4095; +} diff --git a/host/lib/usrp/x300/x300_fw_common.h b/host/lib/usrp/x300/x300_fw_common.h new file mode 100644 index 000000000..c470e9bff --- /dev/null +++ b/host/lib/usrp/x300/x300_fw_common.h @@ -0,0 +1,125 @@ +// +// Copyright 2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_X300_FW_COMMON_H +#define INCLUDED_X300_FW_COMMON_H + +#include <stdint.h> + +/*! + * Structs and constants for x300 communication. + * This header is shared by the firmware and host code. + * Therefore, this header may only contain valid C code. + */ +#ifdef __cplusplus +extern "C" { +#endif + +#define X300_FW_COMPAT_MAJOR 3 +#define X300_FW_COMPAT_MINOR 0 +#define X300_FPGA_COMPAT_MAJOR 3 + +//shared memory sections - in between the stack and the program space +#define X300_FW_SHMEM_BASE 0x6000 +#define X300_FW_SHMEM_COMPAT_NUM 0 +#define X300_FW_SHMEM_GPSDO_STATUS 1 +#define X300_FW_SHMEM_UART_RX_INDEX 2 +#define X300_FW_SHMEM_UART_TX_INDEX 3 +#define X300_FW_SHMEM_CLAIM_STATUS 5 +#define X300_FW_SHMEM_CLAIM_TIME 6 +#define X300_FW_SHMEM_CLAIM_SRC 7 +#define X300_FW_SHMEM_UART_RX_ADDR 8 +#define X300_FW_SHMEM_UART_TX_ADDR 9 +#define X300_FW_SHMEM_UART_WORDS32 10 +#define X300_FW_SHMEM_ROUTE_MAP_ADDR 11 +#define X300_FW_SHMEM_ROUTE_MAP_LEN 12 + +#define X300_FW_NUM_BYTES (1 << 15) //64k +#define X300_FW_COMMS_MTU (1 << 13) //8k +#define X300_FW_COMMS_UDP_PORT 49152 + +#define X300_VITA_UDP_PORT 49153 +#define X300_GPSDO_UDP_PORT 49156 +#define X300_FPGA_PROG_UDP_PORT 49157 +#define X300_MTU_DETECT_UDP_PORT 49158 + +#define X300_DEFAULT_MAC_ADDR_0 {0x00, 0x50, 0xC2, 0x85, 0x3f, 0xff} +#define X300_DEFAULT_MAC_ADDR_1 {0x00, 0x50, 0xC2, 0x85, 0x3f, 0x33} + +#define X300_DEFAULT_GATEWAY (192 << 24 | 168 << 16 | 10 << 8 | 1 << 0) + +#define X300_DEFAULT_IP_ETH0_1G (192 << 24 | 168 << 16 | 10 << 8 | 2 << 0) +#define X300_DEFAULT_IP_ETH1_1G (192 << 24 | 168 << 16 | 20 << 8 | 2 << 0) +#define X300_DEFAULT_IP_ETH0_10G (192 << 24 | 168 << 16 | 30 << 8 | 2 << 0) +#define X300_DEFAULT_IP_ETH1_10G (192 << 24 | 168 << 16 | 40 << 8 | 2 << 0) + +#define X300_DEFAULT_NETMASK_ETH0_1G (255 << 24 | 255 << 16 | 255 << 8 | 0 << 0) +#define X300_DEFAULT_NETMASK_ETH1_1G (255 << 24 | 255 << 16 | 255 << 8 | 0 << 0) +#define X300_DEFAULT_NETMASK_ETH0_10G (255 << 24 | 255 << 16 | 255 << 8 | 0 << 0) +#define X300_DEFAULT_NETMASK_ETH1_10G (255 << 24 | 255 << 16 | 255 << 8 | 0 << 0) + +#define X300_FW_COMMS_FLAGS_ACK (1 << 0) +#define X300_FW_COMMS_FLAGS_ERROR (1 << 1) +#define X300_FW_COMMS_FLAGS_POKE32 (1 << 2) +#define X300_FW_COMMS_FLAGS_PEEK32 (1 << 3) + +#define X300_FPGA_PROG_FLAGS_ACK (1 << 0) +#define X300_FPGA_PROG_FLAGS_ERROR (1 << 1) +#define X300_FPGA_PROG_FLAGS_INIT (1 << 2) +#define X300_FPGA_PROG_FLAGS_CLEANUP (1 << 3) +#define X300_FPGA_PROG_FLAGS_ERASE (1 << 4) +#define X300_FPGA_PROG_FLAGS_VERIFY (1 << 5) +#define X300_FPGA_PROG_CONFIGURE (1 << 6) +#define X300_FPGA_PROG_CONFIG_STATUS (1 << 7) + +#define X300_MTU_DETECT_ECHO_REQUEST (1 << 0) +#define X300_MTU_DETECT_ECHO_REPLY (1 << 1) +#define X300_MTU_DETECT_ERROR (1 << 2) + +typedef struct +{ + uint32_t flags; + uint32_t sequence; + uint32_t addr; + uint32_t data; +} x300_fw_comms_t; + +typedef struct +{ + uint32_t flags; + uint32_t sector; + uint32_t index; + uint32_t size; + uint16_t data[128]; +} x300_fpga_prog_t; + +typedef struct +{ + uint32_t flags; +} x300_fpga_prog_flags_t; + +typedef struct +{ + uint32_t flags; + uint32_t size; +} x300_mtu_t; + +#ifdef __cplusplus +} +#endif + +#endif /* INCLUDED_X300_FW_COMMON_H */ diff --git a/host/lib/usrp/x300/x300_fw_ctrl.cpp b/host/lib/usrp/x300/x300_fw_ctrl.cpp new file mode 100644 index 000000000..67c314d3f --- /dev/null +++ b/host/lib/usrp/x300/x300_fw_ctrl.cpp @@ -0,0 +1,300 @@ +// +// Copyright 2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/types/wb_iface.hpp> +#include "x300_fw_common.h" +#include <uhd/transport/udp_simple.hpp> +#include <uhd/utils/byteswap.hpp> +#include <uhd/utils/msg.hpp> +#include <uhd/exception.hpp> +#include <boost/format.hpp> +#include <boost/thread/mutex.hpp> +#include <uhd/transport/nirio/status.h> +#include <uhd/transport/nirio/niriok_proxy.h> +#include "x300_regs.hpp" +#include <boost/date_time/posix_time/posix_time.hpp> +#include <boost/thread/thread.hpp> + +using namespace uhd; +using namespace uhd::niusrprio; + +class x300_ctrl_iface : public wb_iface +{ +public: + enum {num_retries = 3}; + + void flush(void) + { + boost::mutex::scoped_lock lock(reg_access); + __flush(); + } + + void poke32(const wb_addr_type addr, const boost::uint32_t data) + { + for (size_t i = 1; i <= num_retries; i++) + { + boost::mutex::scoped_lock lock(reg_access); + try + { + return this->__poke32(addr, data); + } + catch(const std::exception &ex) + { + const std::string error_msg = str(boost::format( + "x300 fw communication failure #%u\n%s") % i % ex.what()); + UHD_MSG(error) << error_msg << std::endl; + if (i == num_retries) throw uhd::io_error(error_msg); + } + } + } + + boost::uint32_t peek32(const wb_addr_type addr) + { + for (size_t i = 1; i <= num_retries; i++) + { + boost::mutex::scoped_lock lock(reg_access); + try + { + boost::uint32_t data = this->__peek32(addr); + return data; + } + catch(const std::exception &ex) + { + const std::string error_msg = str(boost::format( + "x300 fw communication failure #%u\n%s") % i % ex.what()); + UHD_MSG(error) << error_msg << std::endl; + if (i == num_retries) throw uhd::io_error(error_msg); + } + } + return 0; + } + +protected: + virtual void __poke32(const wb_addr_type addr, const boost::uint32_t data) = 0; + virtual boost::uint32_t __peek32(const wb_addr_type addr) = 0; + virtual void __flush() = 0; + + boost::mutex reg_access; +}; + + +//----------------------------------------------------- +// Ethernet impl +//----------------------------------------------------- +class x300_ctrl_iface_enet : public x300_ctrl_iface +{ +public: + x300_ctrl_iface_enet(uhd::transport::udp_simple::sptr udp): + udp(udp), seq(0) + { + try + { + this->peek32(0); + } + catch(...){} + } + +protected: + virtual void __poke32(const wb_addr_type addr, const boost::uint32_t data) + { + //load request struct + x300_fw_comms_t request = x300_fw_comms_t(); + request.flags = uhd::htonx<boost::uint32_t>(X300_FW_COMMS_FLAGS_ACK | X300_FW_COMMS_FLAGS_POKE32); + request.sequence = uhd::htonx<boost::uint32_t>(seq++); + request.addr = uhd::htonx(addr); + request.data = uhd::htonx(data); + + //send request + __flush(); + udp->send(boost::asio::buffer(&request, sizeof(request))); + + //recv reply + x300_fw_comms_t reply = x300_fw_comms_t(); + const size_t nbytes = udp->recv(boost::asio::buffer(&reply, sizeof(reply)), 1.0); + if (nbytes == 0) throw uhd::io_error("x300 fw poke32 - reply timed out"); + + //sanity checks + const size_t flags = uhd::ntohx<boost::uint32_t>(reply.flags); + UHD_ASSERT_THROW(nbytes == sizeof(reply)); + UHD_ASSERT_THROW(not (flags & X300_FW_COMMS_FLAGS_ERROR)); + UHD_ASSERT_THROW(flags & X300_FW_COMMS_FLAGS_POKE32); + UHD_ASSERT_THROW(flags & X300_FW_COMMS_FLAGS_ACK); + UHD_ASSERT_THROW(reply.sequence == request.sequence); + UHD_ASSERT_THROW(reply.addr == request.addr); + UHD_ASSERT_THROW(reply.data == request.data); + } + + virtual boost::uint32_t __peek32(const wb_addr_type addr) + { + //load request struct + x300_fw_comms_t request = x300_fw_comms_t(); + request.flags = uhd::htonx<boost::uint32_t>(X300_FW_COMMS_FLAGS_ACK | X300_FW_COMMS_FLAGS_PEEK32); + request.sequence = uhd::htonx<boost::uint32_t>(seq++); + request.addr = uhd::htonx(addr); + request.data = 0; + + //send request + __flush(); + udp->send(boost::asio::buffer(&request, sizeof(request))); + + //recv reply + x300_fw_comms_t reply = x300_fw_comms_t(); + const size_t nbytes = udp->recv(boost::asio::buffer(&reply, sizeof(reply)), 1.0); + if (nbytes == 0) throw uhd::io_error("x300 fw peek32 - reply timed out"); + + //sanity checks + const size_t flags = uhd::ntohx<boost::uint32_t>(reply.flags); + UHD_ASSERT_THROW(nbytes == sizeof(reply)); + UHD_ASSERT_THROW(not (flags & X300_FW_COMMS_FLAGS_ERROR)); + UHD_ASSERT_THROW(flags & X300_FW_COMMS_FLAGS_PEEK32); + UHD_ASSERT_THROW(flags & X300_FW_COMMS_FLAGS_ACK); + UHD_ASSERT_THROW(reply.sequence == request.sequence); + UHD_ASSERT_THROW(reply.addr == request.addr); + + //return result! + return uhd::ntohx<boost::uint32_t>(reply.data); + } + + virtual void __flush(void) + { + char buff[X300_FW_COMMS_MTU] = {}; + while (udp->recv(boost::asio::buffer(buff), 0.0)){} //flush + } + +private: + uhd::transport::udp_simple::sptr udp; + size_t seq; +}; + + +//----------------------------------------------------- +// PCIe impl +//----------------------------------------------------- +class x300_ctrl_iface_pcie : public x300_ctrl_iface +{ +public: + x300_ctrl_iface_pcie(niriok_proxy& drv_proxy): + _drv_proxy(drv_proxy) + { + nirio_status status = 0; + nirio_status_chain(_drv_proxy.set_attribute(ADDRESS_SPACE, BUS_INTERFACE), status); + + //Verify that the Ettus FPGA loaded in the device. This may not be true if the + //user is switching to UHD after using LabVIEW FPGA. + boost::uint32_t pcie_fpga_signature = 0; + _drv_proxy.peek(FPGA_PCIE_SIG_REG, pcie_fpga_signature); + if (pcie_fpga_signature != FPGA_X3xx_SIG_VALUE) + throw uhd::io_error("cannot create x300_ctrl_iface_pcie. incorrect/no fpga image"); + + //Also, poll on the ZPU_STATUS bit to ensure all the state machines in the FPGA are + //ready to accept register transaction requests. + boost::uint32_t reg_data = 0xffffffff; + boost::posix_time::ptime start_time = boost::posix_time::microsec_clock::local_time(); + boost::posix_time::time_duration elapsed; + + do { + boost::this_thread::sleep(boost::posix_time::microsec(500)); //Avoid flooding the bus + elapsed = boost::posix_time::microsec_clock::local_time() - start_time; + nirio_status_chain(_drv_proxy.peek(PCIE_ZPU_STATUS_REG(0), reg_data), status); + } while ( + nirio_status_not_fatal(status) && + (reg_data & PCIE_ZPU_STATUS_SUSPENDED) && + elapsed.total_milliseconds() < INIT_TIMEOUT_IN_MS); + + nirio_status_to_exception(status, "Could not initialize x300_ctrl_iface_pcie."); + + try + { + this->peek32(0); + } + catch(...){} + } + +protected: + virtual void __poke32(const wb_addr_type addr, const boost::uint32_t data) + { + nirio_status status = 0; + boost::uint32_t reg_data = 0xffffffff; + boost::posix_time::ptime start_time = boost::posix_time::microsec_clock::local_time(); + boost::posix_time::time_duration elapsed; + + nirio_status_chain(_drv_proxy.poke(PCIE_ZPU_DATA_REG(addr), data), status); + if (nirio_status_not_fatal(status)) { + do { + boost::this_thread::sleep(boost::posix_time::microsec(50)); //Avoid flooding the bus + elapsed = boost::posix_time::microsec_clock::local_time() - start_time; + nirio_status_chain(_drv_proxy.peek(PCIE_ZPU_STATUS_REG(addr), reg_data), status); + } while ( + nirio_status_not_fatal(status) && + ((reg_data & (PCIE_ZPU_STATUS_BUSY | PCIE_ZPU_STATUS_SUSPENDED)) != 0) && + elapsed.total_milliseconds() < READ_TIMEOUT_IN_MS); + } + + if (nirio_status_fatal(status)) + throw uhd::io_error("x300 fw poke32 - hardware IO error"); + if (elapsed.total_milliseconds() > READ_TIMEOUT_IN_MS) + throw uhd::io_error("x300 fw poke32 - operation timed out"); + } + + virtual boost::uint32_t __peek32(const wb_addr_type addr) + { + nirio_status status = 0; + boost::uint32_t reg_data = 0xffffffff; + boost::posix_time::ptime start_time = boost::posix_time::microsec_clock::local_time(); + boost::posix_time::time_duration elapsed; + + nirio_status_chain(_drv_proxy.poke(PCIE_ZPU_READ_REG(addr), PCIE_ZPU_READ_START), status); + if (nirio_status_not_fatal(status)) { + do { + boost::this_thread::sleep(boost::posix_time::microsec(50)); //Avoid flooding the bus + elapsed = boost::posix_time::microsec_clock::local_time() - start_time; + nirio_status_chain(_drv_proxy.peek(PCIE_ZPU_STATUS_REG(addr), reg_data), status); + } while ( + nirio_status_not_fatal(status) && + ((reg_data & (PCIE_ZPU_STATUS_BUSY | PCIE_ZPU_STATUS_SUSPENDED)) != 0) && + elapsed.total_milliseconds() < READ_TIMEOUT_IN_MS); + } + nirio_status_chain(_drv_proxy.peek(PCIE_ZPU_DATA_REG(addr), reg_data), status); + + if (nirio_status_fatal(status)) + throw uhd::io_error("x300 fw peek32 - hardware IO error"); + if (elapsed.total_milliseconds() > READ_TIMEOUT_IN_MS) + throw uhd::io_error("x300 fw peek32 - operation timed out"); + + return reg_data; + } + + virtual void __flush(void) + { + __peek32(0); + } + +private: + niriok_proxy& _drv_proxy; + static const uint32_t READ_TIMEOUT_IN_MS = 10; + static const uint32_t INIT_TIMEOUT_IN_MS = 5000; +}; + +wb_iface::sptr x300_make_ctrl_iface_enet(uhd::transport::udp_simple::sptr udp) +{ + return wb_iface::sptr(new x300_ctrl_iface_enet(udp)); +} + +wb_iface::sptr x300_make_ctrl_iface_pcie(niriok_proxy& drv_proxy) +{ + return wb_iface::sptr(new x300_ctrl_iface_pcie(drv_proxy)); +} diff --git a/host/lib/usrp/x300/x300_fw_uart.cpp b/host/lib/usrp/x300/x300_fw_uart.cpp new file mode 100644 index 000000000..943b2d9fa --- /dev/null +++ b/host/lib/usrp/x300/x300_fw_uart.cpp @@ -0,0 +1,104 @@ +// +// Copyright 2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include "x300_impl.hpp" +#include <uhd/types/wb_iface.hpp> +#include "x300_regs.hpp" +#include <uhd/utils/msg.hpp> +#include <uhd/types/serial.hpp> +#include <uhd/exception.hpp> +#include <boost/format.hpp> +#include <boost/foreach.hpp> +#include <boost/thread/thread.hpp> + +using namespace uhd; + +struct x300_uart_iface : uart_iface +{ + x300_uart_iface(wb_iface::sptr iface): + rxoffset(0), txoffset(0), txword32(0), rxpool(0), txpool(0), poolsize(0) + { + _iface = iface; + rxoffset = _iface->peek32(SR_ADDR(X300_FW_SHMEM_BASE, X300_FW_SHMEM_UART_RX_INDEX)); + txoffset = _iface->peek32(SR_ADDR(X300_FW_SHMEM_BASE, X300_FW_SHMEM_UART_TX_INDEX)); + rxpool = _iface->peek32(SR_ADDR(X300_FW_SHMEM_BASE, X300_FW_SHMEM_UART_RX_ADDR)); + txpool = _iface->peek32(SR_ADDR(X300_FW_SHMEM_BASE, X300_FW_SHMEM_UART_TX_ADDR)); + poolsize = _iface->peek32(SR_ADDR(X300_FW_SHMEM_BASE, X300_FW_SHMEM_UART_WORDS32)); + //this->write_uart("HELLO UART\n"); + //this->read_uart(0.1); + } + + void putchar(const char ch) + { + txoffset = (txoffset + 1) % (poolsize*4); + const int shift = ((txoffset%4) * 8); + if (shift == 0) txword32 = 0; + txword32 |= boost::uint32_t(ch) << shift; + _iface->poke32(SR_ADDR(txpool, txoffset/4), txword32); + _iface->poke32(SR_ADDR(X300_FW_SHMEM_BASE, X300_FW_SHMEM_UART_TX_INDEX), txoffset); + } + + void write_uart(const std::string &buff) + { + BOOST_FOREACH(const char ch, buff) + { + if (ch == '\n') this->putchar('\r'); + this->putchar(ch); + } + } + + int getchar(void) + { + if (_iface->peek32(SR_ADDR(X300_FW_SHMEM_BASE, X300_FW_SHMEM_UART_RX_INDEX)) != rxoffset) + { + const int shift = ((rxoffset%4) * 8); + const char ch = _iface->peek32(SR_ADDR(rxpool, rxoffset/4)) >> shift; + rxoffset = (rxoffset + 1) % (poolsize*4); + return ch; + } + return -1; + } + + std::string read_uart(double timeout) + { + const boost::system_time exit_time = boost::get_system_time() + boost::posix_time::microseconds(long(timeout*1e6)); + std::string buff; + while (true) + { + const int ch = this->getchar(); + if (ch == -1) + { + if (boost::get_system_time() > exit_time) break; + boost::this_thread::sleep(boost::posix_time::milliseconds(1)); + continue; + } + if (ch == '\r') continue; + buff += std::string(1, (char)ch); + if (ch == '\n') break; + } + //UHD_VAR(buff); + return buff; + } + + wb_iface::sptr _iface; + boost::uint32_t rxoffset, txoffset, txword32, rxpool, txpool, poolsize; +}; + +uart_iface::sptr x300_make_uart_iface(wb_iface::sptr iface) +{ + return uart_iface::sptr(new x300_uart_iface(iface)); +} diff --git a/host/lib/usrp/x300/x300_impl.cpp b/host/lib/usrp/x300/x300_impl.cpp new file mode 100644 index 000000000..f62967018 --- /dev/null +++ b/host/lib/usrp/x300/x300_impl.cpp @@ -0,0 +1,1481 @@ +// +// Copyright 2013-2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// 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 "x300_impl.hpp" +#include "x300_regs.hpp" +#include "x300_lvbitx.hpp" +#include "x310_lvbitx.hpp" +#include <boost/algorithm/string.hpp> +#include <boost/asio.hpp> +#include "apply_corrections.hpp" +#include <uhd/utils/static.hpp> +#include <uhd/utils/msg.hpp> +#include <uhd/utils/images.hpp> +#include <uhd/utils/safe_call.hpp> +#include <uhd/usrp/subdev_spec.hpp> +#include <uhd/transport/if_addrs.hpp> +#include <boost/foreach.hpp> +#include <boost/bind.hpp> +#include <boost/functional/hash.hpp> +#include <boost/assign/list_of.hpp> +#include <fstream> +#include <uhd/transport/udp_zero_copy.hpp> +#include <uhd/transport/nirio_zero_copy.hpp> +#include <uhd/transport/nirio/niusrprio_session.h> +#include <uhd/utils/platform.hpp> + +#define NIUSRPRIO_DEFAULT_RPC_PORT "5444" + +#define X300_REV(x) (x - "A" + 1) + +using namespace uhd; +using namespace uhd::usrp; +using namespace uhd::transport; +using namespace uhd::niusrprio; +namespace asio = boost::asio; + +/*********************************************************************** + * Discovery over the udp and pcie transport + **********************************************************************/ +static std::string get_fpga_option(wb_iface::sptr zpu_ctrl) { + //1G = {0:1G, 1:1G} w/ DRAM, HG = {0:1G, 1:10G} w/ DRAM, XG = {0:10G, 1:10G} w/ DRAM + //HGS = {0:1G, 1:10G} w/ SRAM, XGS = {0:10G, 1:10G} w/ SRAM + + //In the default configuration, UHD does not support the HG and XG images so + //they are never autodetected. + bool eth0XG = (zpu_ctrl->peek32(SR_ADDR(SET0_BASE, ZPU_RB_ETH_TYPE0)) == 0x1); + bool eth1XG = (zpu_ctrl->peek32(SR_ADDR(SET0_BASE, ZPU_RB_ETH_TYPE1)) == 0x1); + return (eth0XG && eth1XG) ? "XGS" : (eth1XG ? "HGS" : "1G"); +} + +//@TODO: Refactor the find functions to collapse common code for ethernet and PCIe +static device_addrs_t x300_find_with_addr(const device_addr_t &hint) +{ + udp_simple::sptr comm = udp_simple::make_broadcast( + hint["addr"], BOOST_STRINGIZE(X300_FW_COMMS_UDP_PORT)); + + //load request struct + x300_fw_comms_t request = x300_fw_comms_t(); + request.flags = uhd::htonx<boost::uint32_t>(X300_FW_COMMS_FLAGS_ACK); + request.sequence = uhd::htonx<boost::uint32_t>(std::rand()); + + //send request + comm->send(asio::buffer(&request, sizeof(request))); + + //loop for replies until timeout + device_addrs_t addrs; + while (true) + { + char buff[X300_FW_COMMS_MTU] = {}; + const size_t nbytes = comm->recv(asio::buffer(buff), 0.050); + if (nbytes == 0) break; + const x300_fw_comms_t *reply = (const x300_fw_comms_t *)buff; + if (request.flags != reply->flags) break; + if (request.sequence != reply->sequence) break; + device_addr_t new_addr; + new_addr["type"] = "x300"; + new_addr["addr"] = comm->get_recv_addr(); + + //Attempt to read the name from the EEPROM and perform filtering. + //This operation can throw due to compatibility mismatch. + try + { + wb_iface::sptr zpu_ctrl = x300_make_ctrl_iface_enet(udp_simple::make_connected(new_addr["addr"], BOOST_STRINGIZE(X300_FW_COMMS_UDP_PORT))); + if (x300_impl::is_claimed(zpu_ctrl)) continue; //claimed by another process + new_addr["fpga"] = get_fpga_option(zpu_ctrl); + + i2c_core_100_wb32::sptr zpu_i2c = i2c_core_100_wb32::make(zpu_ctrl, I2C1_BASE); + i2c_iface::sptr eeprom16 = zpu_i2c->eeprom16(); + const mboard_eeprom_t mb_eeprom(*eeprom16, "X300"); + new_addr["name"] = mb_eeprom["name"]; + new_addr["serial"] = mb_eeprom["serial"]; + switch (x300_impl::get_mb_type_from_eeprom(mb_eeprom)) { + case x300_impl::USRP_X300_MB: + new_addr["product"] = "X300"; + break; + case x300_impl::USRP_X310_MB: + new_addr["product"] = "X310"; + break; + default: + break; + } + } + catch(const std::exception &) + { + //set these values as empty string so the device may still be found + //and the filter's below can still operate on the discovered device + new_addr["name"] = ""; + new_addr["serial"] = ""; + } + //filter the discovered device below by matching optional keys + if ( + (not hint.has_key("name") or hint["name"] == new_addr["name"]) and + (not hint.has_key("serial") or hint["serial"] == new_addr["serial"]) and + (not hint.has_key("product") or hint["product"] == new_addr["product"]) + ){ + addrs.push_back(new_addr); + } + } + + return addrs; +} + +static device_addrs_t x300_find_pcie(const device_addr_t &hint, bool explicit_query) +{ + std::string rpc_port_name(NIUSRPRIO_DEFAULT_RPC_PORT); + if (hint.has_key("niusrpriorpc_port")) { + rpc_port_name = hint["niusrpriorpc_port"]; + } + + device_addrs_t addrs; + niusrprio_session::device_info_vtr dev_info_vtr; + nirio_status status = niusrprio_session::enumerate(rpc_port_name, dev_info_vtr); + if (explicit_query) nirio_status_to_exception(status, "x300_find_pcie: Error enumerating NI-RIO devices."); + + BOOST_FOREACH(niusrprio_session::device_info &dev_info, dev_info_vtr) + { + device_addr_t new_addr; + new_addr["type"] = "x300"; + new_addr["resource"] = dev_info.resource_name; + std::string resource_d(dev_info.resource_name); + boost::to_upper(resource_d); + + switch (x300_impl::get_mb_type_from_pcie(resource_d, rpc_port_name)) { + case x300_impl::USRP_X300_MB: + new_addr["product"] = "X300"; + break; + case x300_impl::USRP_X310_MB: + new_addr["product"] = "X310"; + break; + default: + continue; + } + + niriok_proxy kernel_proxy; + kernel_proxy.open(dev_info.interface_path); + + //Attempt to read the name from the EEPROM and perform filtering. + //This operation can throw due to compatibility mismatch. + try + { + //This call could throw an exception if the user is switching to using UHD + //after LabVIEW FPGA. In that case, skip reading the name and serial and pick + //a default FPGA flavor. During make, a new image will be loaded and everything + //will be OK + wb_iface::sptr zpu_ctrl = x300_make_ctrl_iface_pcie(kernel_proxy); + if (x300_impl::is_claimed(zpu_ctrl)) continue; //claimed by another process + + //Attempt to autodetect the FPGA type + if (not hint.has_key("fpga")) { + new_addr["fpga"] = get_fpga_option(zpu_ctrl); + } + + i2c_core_100_wb32::sptr zpu_i2c = i2c_core_100_wb32::make(zpu_ctrl, I2C1_BASE); + i2c_iface::sptr eeprom16 = zpu_i2c->eeprom16(); + const mboard_eeprom_t mb_eeprom(*eeprom16, "X300"); + new_addr["name"] = mb_eeprom["name"]; + new_addr["serial"] = mb_eeprom["serial"]; + } + catch(const std::exception &) + { + //set these values as empty string so the device may still be found + //and the filter's below can still operate on the discovered device + if (not hint.has_key("fpga")) { + new_addr["fpga"] = "HGS"; + } + new_addr["name"] = ""; + new_addr["serial"] = ""; + } + kernel_proxy.close(); + + //filter the discovered device below by matching optional keys + std::string resource_i = hint.has_key("resource") ? hint["resource"] : ""; + boost::to_upper(resource_i); + + if ( + (not hint.has_key("resource") or resource_i == resource_d) and + (not hint.has_key("name") or hint["name"] == new_addr["name"]) and + (not hint.has_key("serial") or hint["serial"] == new_addr["serial"]) and + (not hint.has_key("product") or hint["product"] == new_addr["product"]) + ){ + addrs.push_back(new_addr); + } + } + return addrs; +} + +static device_addrs_t x300_find(const device_addr_t &hint_) +{ + //handle the multi-device discovery + device_addrs_t hints = separate_device_addr(hint_); + if (hints.size() > 1) + { + device_addrs_t found_devices; + std::string error_msg; + BOOST_FOREACH(const device_addr_t &hint_i, hints) + { + device_addrs_t found_devices_i = x300_find(hint_i); + if (found_devices_i.size() != 1) error_msg += str(boost::format( + "Could not resolve device hint \"%s\" to a single device." + ) % hint_i.to_string()); + else found_devices.push_back(found_devices_i[0]); + } + if (found_devices.empty()) return device_addrs_t(); + if (not error_msg.empty()) throw uhd::value_error(error_msg); + + return device_addrs_t(1, combine_device_addrs(found_devices)); + } + + //initialize the hint for a single device case + UHD_ASSERT_THROW(hints.size() <= 1); + hints.resize(1); //in case it was empty + device_addr_t hint = hints[0]; + device_addrs_t addrs; + if (hint.has_key("type") and hint["type"] != "x300") return addrs; + + + //use the address given + if (hint.has_key("addr")) + { + device_addrs_t reply_addrs; + try + { + reply_addrs = x300_find_with_addr(hint); + } + catch(const std::exception &ex) + { + UHD_MSG(error) << "X300 Network discovery error " << ex.what() << std::endl; + } + catch(...) + { + UHD_MSG(error) << "X300 Network discovery unknown error " << std::endl; + } + BOOST_FOREACH(const device_addr_t &reply_addr, reply_addrs) + { + device_addrs_t new_addrs = x300_find_with_addr(reply_addr); + addrs.insert(addrs.begin(), new_addrs.begin(), new_addrs.end()); + } + return addrs; + } + + if (!hint.has_key("resource")) + { + //otherwise, no address was specified, send a broadcast on each interface + BOOST_FOREACH(const if_addrs_t &if_addrs, get_if_addrs()) + { + //avoid the loopback device + if (if_addrs.inet == asio::ip::address_v4::loopback().to_string()) continue; + + //create a new hint with this broadcast address + device_addr_t new_hint = hint; + new_hint["addr"] = if_addrs.bcast; + + //call discover with the new hint and append results + device_addrs_t new_addrs = x300_find(new_hint); + addrs.insert(addrs.begin(), new_addrs.begin(), new_addrs.end()); + } + } + + device_addrs_t pcie_addrs = x300_find_pcie(hint, hint.has_key("resource")); + if (not pcie_addrs.empty()) addrs.insert(addrs.end(), pcie_addrs.begin(), pcie_addrs.end()); + + return addrs; +} + +/*********************************************************************** + * Make + **********************************************************************/ +static device::sptr x300_make(const device_addr_t &device_addr) +{ + return device::sptr(new x300_impl(device_addr)); +} + +UHD_STATIC_BLOCK(register_x300_device) +{ + device::register_device(&x300_find, &x300_make); +} + +static void x300_load_fw(wb_iface::sptr fw_reg_ctrl, const std::string &file_name) +{ + UHD_MSG(status) << "Loading firmware " << file_name << std::flush; + + //load file into memory + std::ifstream fw_file(file_name.c_str()); + boost::uint32_t fw_file_buff[X300_FW_NUM_BYTES/sizeof(boost::uint32_t)]; + fw_file.read((char *)fw_file_buff, sizeof(fw_file_buff)); + fw_file.close(); + + //Poke the fw words into the WB boot loader + fw_reg_ctrl->poke32(SR_ADDR(BOOT_LDR_BASE, BL_ADDRESS), 0); + for (size_t i = 0; i < X300_FW_NUM_BYTES; i+=sizeof(boost::uint32_t)) + { + //@TODO: FIXME: Since x300_ctrl_iface acks each write and traps exceptions, the first try for the last word + // written will print an error because it triggers a FW reload and fails to reply. + fw_reg_ctrl->poke32(SR_ADDR(BOOT_LDR_BASE, BL_DATA), uhd::byteswap(fw_file_buff[i/sizeof(boost::uint32_t)])); + if ((i & 0x1fff) == 0) UHD_MSG(status) << "." << std::flush; + } + + UHD_MSG(status) << " done!" << std::endl; +} + +x300_impl::x300_impl(const uhd::device_addr_t &dev_addr) +{ + UHD_MSG(status) << "X300 initialization sequence..." << std::endl; + _async_md.reset(new async_md_type(1000/*messages deep*/)); + _tree = uhd::property_tree::make(); + _tree->create<std::string>("/name").set("X-Series Device"); + _sid_framer = 0; + + const device_addrs_t device_args = separate_device_addr(dev_addr); + _mb.resize(device_args.size()); + for (size_t i = 0; i < device_args.size(); i++) + { + this->setup_mb(i, device_args[i]); + } +} + +void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr) +{ + const fs_path mb_path = "/mboards/"+boost::lexical_cast<std::string>(mb_i); + mboard_members_t &mb = _mb[mb_i]; + + mb.addr = dev_addr.has_key("resource") ? dev_addr["resource"] : dev_addr["addr"]; + mb.xport_path = dev_addr.has_key("resource") ? "nirio" : "eth"; + mb.if_pkt_is_big_endian = mb.xport_path != "nirio"; + + if (mb.xport_path == "nirio") + { + nirio_status status = 0; + + std::string rpc_port_name(NIUSRPRIO_DEFAULT_RPC_PORT); + if (dev_addr.has_key("niusrpriorpc_port")) { + rpc_port_name = dev_addr["niusrpriorpc_port"]; + } + UHD_MSG(status) << boost::format("Connecting to niusrpriorpc at localhost:%s...\n") % rpc_port_name; + + //Instantiate the correct lvbitx object + nifpga_lvbitx::sptr lvbitx; + switch (get_mb_type_from_pcie(dev_addr["resource"], rpc_port_name)) { + case USRP_X300_MB: + lvbitx.reset(new x300_lvbitx(dev_addr["fpga"])); + break; + case USRP_X310_MB: + lvbitx.reset(new x310_lvbitx(dev_addr["fpga"])); + break; + default: + nirio_status_to_exception(status, "Motherboard detection error. Please ensure that you \ + have a valid USRP X3x0, NI USRP-294xR or NI USRP-295xR device and that all the device \ + driver have been loaded."); + } + + //Load the lvbitx onto the device + UHD_MSG(status) << boost::format("Using LVBITX bitfile %s...\n") % lvbitx->get_bitfile_path(); + mb.rio_fpga_interface.reset(new niusrprio_session(dev_addr["resource"], rpc_port_name)); + nirio_status_chain(mb.rio_fpga_interface->open(lvbitx, dev_addr.has_key("download-fpga")), status); + nirio_status_to_exception(status, "x300_impl: Could not initialize RIO session."); + + //Tell the quirks object which FIFOs carry TX stream data + const uint32_t tx_data_fifos[2] = {X300_RADIO_DEST_PREFIX_TX, X300_RADIO_DEST_PREFIX_TX + 3}; + mb.rio_fpga_interface->get_kernel_proxy().get_rio_quirks().register_tx_streams(tx_data_fifos); + } + + BOOST_FOREACH(const std::string &key, dev_addr.keys()) + { + if (key.find("recv") != std::string::npos) mb.recv_args[key] = dev_addr[key]; + if (key.find("send") != std::string::npos) mb.send_args[key] = dev_addr[key]; + } + + const std::vector<std::string> DB_NAMES = boost::assign::list_of("A")("B"); + + //create basic communication + UHD_MSG(status) << "Setup basic communication..." << std::endl; + if (mb.xport_path == "nirio") { + mb.zpu_ctrl = x300_make_ctrl_iface_pcie(mb.rio_fpga_interface->get_kernel_proxy()); + } else { + mb.zpu_ctrl = x300_make_ctrl_iface_enet(udp_simple::make_connected(mb.addr, + BOOST_STRINGIZE(X300_FW_COMMS_UDP_PORT))); + } + + if (mb.xport_path == "eth") + { + mtu_result_t user_set; + user_set.recv_mtu = dev_addr.has_key("recv_frame_size") \ + ? boost::lexical_cast<size_t>(dev_addr["recv_frame_size"]) \ + : X300_ETH_DATA_FRAME_SIZE; + user_set.send_mtu = dev_addr.has_key("send_frame_size") \ + ? boost::lexical_cast<size_t>(dev_addr["send_frame_size"]) \ + : X300_ETH_DATA_FRAME_SIZE; + + // Detect the MTU on the path to the USRP + mtu_result_t result; + try { + result = determine_mtu(mb.addr, user_set); + } catch(std::exception &e) { + UHD_MSG(error) << e.what() << std::endl; + } + + #if defined UHD_PLATFORM_LINUX + const std::string mtu_tool("ip link"); + #elif defined UHD_PLATFORM_WIN32 + const std::string mtu_tool("netsh"); + #else + const std::string mtu_tool("ifconfig"); + #endif + + if(result.recv_mtu < user_set.recv_mtu) { + UHD_MSG(warning) + << boost::format("The receive path contains entities that do not support MTUs >= one recv frame's size (%lu).") + % user_set.recv_mtu << std::endl + << boost::format("Please verify your NIC's MTU setting using '%s' or set the recv_frame_size argument.") + % mtu_tool << std::endl; + } + + if(result.send_mtu < user_set.send_mtu) { + UHD_MSG(warning) + << boost::format("The send path contains entities that do not support MTUs >= one send frame's size (%lu).") + % user_set.send_mtu << std::endl + << boost::format("Please verify your NIC's MTU setting using '%s' or set the send_frame_size argument.") + % mtu_tool << std::endl; + } + } + + mb.claimer_task = uhd::task::make(boost::bind(&x300_impl::claimer_loop, this, mb.zpu_ctrl)); + + //extract the FW path for the X300 + //and live load fw over ethernet link + if (dev_addr.has_key("fw")) + { + const std::string x300_fw_image = find_image_path( + dev_addr.has_key("fw")? dev_addr["fw"] : X300_FW_FILE_NAME + ); + x300_load_fw(mb.zpu_ctrl, x300_fw_image); + } + + //check compat -- good place to do after conditional loading + this->check_fw_compat(mb_path, mb.zpu_ctrl); + this->check_fpga_compat(mb_path, mb.zpu_ctrl); + + //low speed perif access + mb.zpu_spi = spi_core_3000::make(mb.zpu_ctrl, SR_ADDR(SET0_BASE, ZPU_SR_SPI), + SR_ADDR(SET0_BASE, ZPU_RB_SPI)); + mb.zpu_i2c = i2c_core_100_wb32::make(mb.zpu_ctrl, I2C1_BASE); + mb.zpu_i2c->set_clock_rate(X300_BUS_CLOCK_RATE); + + //////////////////////////////////////////////////////////////////// + // print network routes mapping + //////////////////////////////////////////////////////////////////// + /* + const uint32_t routes_addr = mb.zpu_ctrl->peek32(SR_ADDR(X300_FW_SHMEM_BASE, X300_FW_SHMEM_ROUTE_MAP_ADDR)); + const uint32_t routes_len = mb.zpu_ctrl->peek32(SR_ADDR(X300_FW_SHMEM_BASE, X300_FW_SHMEM_ROUTE_MAP_LEN)); + UHD_VAR(routes_len); + for (size_t i = 0; i < routes_len; i+=1) + { + const uint32_t node_addr = mb.zpu_ctrl->peek32(SR_ADDR(routes_addr, i*2+0)); + const uint32_t nbor_addr = mb.zpu_ctrl->peek32(SR_ADDR(routes_addr, i*2+1)); + if (node_addr != 0 and nbor_addr != 0) + { + UHD_MSG(status) << boost::format("%u: %s -> %s") + % i + % asio::ip::address_v4(node_addr).to_string() + % asio::ip::address_v4(nbor_addr).to_string() + << std::endl; + } + } + */ + + //////////////////////////////////////////////////////////////////// + // setup the mboard eeprom + //////////////////////////////////////////////////////////////////// + UHD_MSG(status) << "Loading values from EEPROM..." << std::endl; + i2c_iface::sptr eeprom16 = mb.zpu_i2c->eeprom16(); + if (dev_addr.has_key("blank_eeprom")) + { + UHD_MSG(warning) << "Obliterating the motherboard EEPROM..." << std::endl; + eeprom16->write_eeprom(0x50, 0, byte_vector_t(256, 0xff)); + } + const mboard_eeprom_t mb_eeprom(*eeprom16, "X300"); + _tree->create<mboard_eeprom_t>(mb_path / "eeprom") + .set(mb_eeprom) + .subscribe(boost::bind(&x300_impl::set_mb_eeprom, this, mb.zpu_i2c, _1)); + + //////////////////////////////////////////////////////////////////// + // parse the product number + //////////////////////////////////////////////////////////////////// + std::string product_name = "X300?"; + switch (get_mb_type_from_eeprom(mb_eeprom)) { + case USRP_X300_MB: + product_name = "X300"; + break; + case USRP_X310_MB: + product_name = "X310"; + break; + default: + break; + } + _tree->create<std::string>(mb_path / "name").set(product_name); + _tree->create<std::string>(mb_path / "codename").set("Yetti"); + + //////////////////////////////////////////////////////////////////// + // determine routing based on address match + //////////////////////////////////////////////////////////////////// + mb.router_dst_here = X300_XB_DST_E0; //some default if eeprom not match + if (mb.xport_path == "nirio") { + mb.router_dst_here = X300_XB_DST_PCI; + } else { + if (mb.addr == mb_eeprom["ip-addr0"]) mb.router_dst_here = X300_XB_DST_E0; + else if (mb.addr == mb_eeprom["ip-addr1"]) mb.router_dst_here = X300_XB_DST_E1; + else if (mb.addr == mb_eeprom["ip-addr2"]) mb.router_dst_here = X300_XB_DST_E0; + else if (mb.addr == mb_eeprom["ip-addr3"]) mb.router_dst_here = X300_XB_DST_E1; + else if (mb.addr == boost::asio::ip::address_v4(boost::uint32_t(X300_DEFAULT_IP_ETH0_1G)).to_string()) mb.router_dst_here = X300_XB_DST_E0; + else if (mb.addr == boost::asio::ip::address_v4(boost::uint32_t(X300_DEFAULT_IP_ETH1_1G)).to_string()) mb.router_dst_here = X300_XB_DST_E1; + else if (mb.addr == boost::asio::ip::address_v4(boost::uint32_t(X300_DEFAULT_IP_ETH0_10G)).to_string()) mb.router_dst_here = X300_XB_DST_E0; + else if (mb.addr == boost::asio::ip::address_v4(boost::uint32_t(X300_DEFAULT_IP_ETH1_10G)).to_string()) mb.router_dst_here = X300_XB_DST_E1; + } + + //////////////////////////////////////////////////////////////////// + // read dboard eeproms + //////////////////////////////////////////////////////////////////// + for (size_t i = 0; i < 8; i++) + { + if (i == 0 or i == 2) continue; //not used + mb.db_eeproms[i].load(*mb.zpu_i2c, 0x50 | i); + } + + //////////////////////////////////////////////////////////////////// + // create clock control objects + //////////////////////////////////////////////////////////////////// + UHD_MSG(status) << "Setup RF frontend clocking..." << std::endl; + + // Init shadow and clock source; the device comes up with it's internal + // clock source before locking to something else (if requested). + mb.clock_control_regs__clock_source = 0; + mb.clock_control_regs__pps_select = 0; + mb.clock_control_regs__pps_out_enb = 0; + mb.clock_control_regs__tcxo_enb = 1; + mb.clock_control_regs__gpsdo_pwr = 1; + this->update_clock_source(mb, "internal"); + this->update_clock_control(mb); + + size_t hw_rev = 0; + if(mb_eeprom.has_key("revision") and not mb_eeprom["revision"].empty()) { + try { + hw_rev = boost::lexical_cast<size_t>(mb_eeprom["revision"]); + } catch(...) { + UHD_MSG(warning) << "Revision in EEPROM is invalid! Please reprogram your EEPROM." << std::endl; + } + } else { + UHD_MSG(warning) << "No revision detected MB EEPROM must be reprogrammed!" << std::endl; + } + + if(hw_rev == 0) { + UHD_MSG(warning) << "Defaulting to X300 RevD Clock Settings. This will result in non-optimal lock times." << std::endl; + hw_rev = X300_REV("D"); + } + + mb.clock = x300_clock_ctrl::make(mb.zpu_spi, + 1 /*slaveno*/, + hw_rev, + dev_addr.cast<double>("master_clock_rate", X300_DEFAULT_TICK_RATE), + dev_addr.cast<double>("system_ref_rate", X300_DEFAULT_SYSREF_RATE)); + + //////////////////////////////////////////////////////////////////// + // create clock properties + //////////////////////////////////////////////////////////////////// + _tree->create<double>(mb_path / "tick_rate") + .publish(boost::bind(&x300_clock_ctrl::get_master_clock_rate, mb.clock)); + + _tree->create<time_spec_t>(mb_path / "time" / "cmd"); + + UHD_MSG(status) << "Radio 1x clock:" << (mb.clock->get_master_clock_rate()/1e6) + << std::endl; + + //////////////////////////////////////////////////////////////////// + // Create the GPSDO control + //////////////////////////////////////////////////////////////////// + static const boost::uint32_t dont_look_for_gpsdo = 0x1234abcdul; + + //otherwise if not disabled, look for the internal GPSDO + if (mb.zpu_ctrl->peek32(SR_ADDR(X300_FW_SHMEM_BASE, X300_FW_SHMEM_GPSDO_STATUS)) != dont_look_for_gpsdo) + { + UHD_MSG(status) << "Detecting internal GPSDO.... " << std::flush; + try + { + mb.gps = gps_ctrl::make(x300_make_uart_iface(mb.zpu_ctrl)); + } + catch(std::exception &e) + { + UHD_MSG(error) << "An error occurred making GPSDO control: " << e.what() << std::endl; + } + if (mb.gps and mb.gps->gps_detected()) + { + BOOST_FOREACH(const std::string &name, mb.gps->get_sensors()) + { + _tree->create<sensor_value_t>(mb_path / "sensors" / name) + .publish(boost::bind(&gps_ctrl::get_sensor, mb.gps, name)); + } + } + else + { + mb.zpu_ctrl->poke32(SR_ADDR(X300_FW_SHMEM_BASE, X300_FW_SHMEM_GPSDO_STATUS), dont_look_for_gpsdo); + } + } + + //////////////////////////////////////////////////////////////////// + //clear router? + //////////////////////////////////////////////////////////////////// + for (size_t i = 0; i < 512; i++) { + mb.zpu_ctrl->poke32(SR_ADDR(SETXB_BASE, i), 0); + } + + //////////////////////////////////////////////////////////////////// + // setup radios + //////////////////////////////////////////////////////////////////// + UHD_MSG(status) << "Initialize Radio control..." << std::endl; + this->setup_radio(mb_i, 0, DB_NAMES[0]); + this->setup_radio(mb_i, 1, DB_NAMES[1]); + + //////////////////////////////////////////////////////////////////// + // front panel gpio + //////////////////////////////////////////////////////////////////// + mb.fp_gpio = gpio_core_200::make(mb.radio_perifs[0].ctrl, TOREG(SR_FP_GPIO), RB32_FP_GPIO); + const std::vector<std::string> GPIO_ATTRS = boost::assign::list_of("CTRL")("DDR")("OUT")("ATR_0X")("ATR_RX")("ATR_TX")("ATR_XX"); + BOOST_FOREACH(const std::string &attr, GPIO_ATTRS) + { + _tree->create<boost::uint32_t>(mb_path / "gpio" / "FP0" / attr) + .set(0) + .subscribe(boost::bind(&x300_impl::set_fp_gpio, this, mb.fp_gpio, attr, _1)); + } + _tree->create<boost::uint32_t>(mb_path / "gpio" / "FP0" / "READBACK") + .publish(boost::bind(&x300_impl::get_fp_gpio, this, mb.fp_gpio, "READBACK")); + + //////////////////////////////////////////////////////////////////// + // register the time keepers - only one can be the highlander + //////////////////////////////////////////////////////////////////// + _tree->create<time_spec_t>(mb_path / "time" / "now") + .publish(boost::bind(&time_core_3000::get_time_now, mb.radio_perifs[0].time64)) + .subscribe(boost::bind(&time_core_3000::set_time_now, mb.radio_perifs[0].time64, _1)) + .subscribe(boost::bind(&time_core_3000::set_time_now, mb.radio_perifs[1].time64, _1)); + _tree->create<time_spec_t>(mb_path / "time" / "pps") + .publish(boost::bind(&time_core_3000::get_time_last_pps, mb.radio_perifs[0].time64)) + .subscribe(boost::bind(&time_core_3000::set_time_next_pps, mb.radio_perifs[0].time64, _1)) + .subscribe(boost::bind(&time_core_3000::set_time_next_pps, mb.radio_perifs[1].time64, _1)); + + //////////////////////////////////////////////////////////////////// + // setup time sources and properties + //////////////////////////////////////////////////////////////////// + _tree->create<std::string>(mb_path / "time_source" / "value") + .subscribe(boost::bind(&x300_impl::update_time_source, this, boost::ref(mb), _1)); + static const std::vector<std::string> time_sources = boost::assign::list_of("internal")("external")("gpsdo"); + _tree->create<std::vector<std::string> >(mb_path / "time_source" / "options").set(time_sources); + + //setup the time output, default to ON + _tree->create<bool>(mb_path / "time_source" / "output") + .subscribe(boost::bind(&x300_impl::set_time_source_out, this, boost::ref(mb), _1)) + .set(true); + + //////////////////////////////////////////////////////////////////// + // setup clock sources and properties + //////////////////////////////////////////////////////////////////// + _tree->create<std::string>(mb_path / "clock_source" / "value") + .subscribe(boost::bind(&x300_impl::update_clock_source, this, boost::ref(mb), _1)); + + static const std::vector<std::string> clock_source_options = boost::assign::list_of("internal")("external")("gpsdo"); + _tree->create<std::vector<std::string> >(mb_path / "clock_source" / "options").set(clock_source_options); + + //setup external reference options. default to 10 MHz input reference + _tree->create<std::string>(mb_path / "clock_source" / "external"); + static const std::vector<double> external_freq_options = boost::assign::list_of(10e6)(30.72e6)(200e6); + _tree->create<std::vector<double> >(mb_path / "clock_source" / "external" / "freq" / "options") + .set(external_freq_options); + _tree->create<double>(mb_path / "clock_source" / "external" / "value") + .set(mb.clock->get_sysref_clock_rate()); + // FIXME the external clock source settings need to be more robust + + //setup the clock output, default to ON + _tree->create<bool>(mb_path / "clock_source" / "output") + .subscribe(boost::bind(&x300_clock_ctrl::set_ref_out, mb.clock, _1)); + + + //////////////////////////////////////////////////////////////////// + // create frontend mapping + //////////////////////////////////////////////////////////////////// + _tree->create<subdev_spec_t>(mb_path / "rx_subdev_spec") + .subscribe(boost::bind(&x300_impl::update_rx_subdev_spec, this, mb_i, _1)); + _tree->create<subdev_spec_t>(mb_path / "tx_subdev_spec") + .subscribe(boost::bind(&x300_impl::update_tx_subdev_spec, this, mb_i, _1)); + + //////////////////////////////////////////////////////////////////// + // and do the misc mboard sensors + //////////////////////////////////////////////////////////////////// + _tree->create<sensor_value_t>(mb_path / "sensors" / "ref_locked") + .publish(boost::bind(&x300_impl::get_ref_locked, this, mb.zpu_ctrl)); + + //////////////////////////////////////////////////////////////////// + // create clock properties + //////////////////////////////////////////////////////////////////// + _tree->access<double>(mb_path / "tick_rate") + .subscribe(boost::bind(&x300_impl::set_tick_rate, this, boost::ref(mb), _1)) + .subscribe(boost::bind(&x300_impl::update_tick_rate, this, boost::ref(mb), _1)) + .set(mb.clock->get_master_clock_rate()); + + //////////////////////////////////////////////////////////////////// + // do some post-init tasks + //////////////////////////////////////////////////////////////////// + subdev_spec_t rx_fe_spec, tx_fe_spec; + rx_fe_spec.push_back(subdev_spec_pair_t("A", + _tree->list(mb_path / "dboards" / "A" / "rx_frontends").at(0))); + rx_fe_spec.push_back(subdev_spec_pair_t("B", + _tree->list(mb_path / "dboards" / "B" / "rx_frontends").at(0))); + tx_fe_spec.push_back(subdev_spec_pair_t("A", + _tree->list(mb_path / "dboards" / "A" / "tx_frontends").at(0))); + tx_fe_spec.push_back(subdev_spec_pair_t("B", + _tree->list(mb_path / "dboards" / "B" / "tx_frontends").at(0))); + + _tree->access<subdev_spec_t>(mb_path / "rx_subdev_spec").set(rx_fe_spec); + _tree->access<subdev_spec_t>(mb_path / "tx_subdev_spec").set(tx_fe_spec); + + //GPS installed: use external ref, time, and init time spec + if (mb.gps and mb.gps->gps_detected()) + { + UHD_MSG(status) << "Setting references to the internal GPSDO" << std::endl; + _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; + const time_t tp = time_t(mb.gps->get_sensor("gps_time").to_int()+1); + _tree->access<time_spec_t>(mb_path / "time" / "pps").set(time_spec_t(tp)); + } + else + { + _tree->access<std::string>(mb_path / "time_source" / "value").set("external"); + _tree->access<std::string>(mb_path / "clock_source" / "value").set("external"); + boost::this_thread::sleep(boost::posix_time::milliseconds(100)); + if (this->get_ref_locked(mb.zpu_ctrl).to_bool()) + { + UHD_MSG(status) << "Setting references to external sources" << std::endl; + } + else + { + UHD_MSG(status) << "Setting references to internal sources" << std::endl; + _tree->access<std::string>(mb_path / "time_source" / "value").set("internal"); + _tree->access<std::string>(mb_path / "clock_source" / "value").set("internal"); + } + } +} + +x300_impl::~x300_impl(void) +{ + try + { + BOOST_FOREACH(mboard_members_t &mb, _mb) + { + mb.radio_perifs[0].ctrl->poke32(TOREG(SR_MISC_OUTS), (1 << 2)); //disable/reset ADC/DAC + mb.radio_perifs[1].ctrl->poke32(TOREG(SR_MISC_OUTS), (1 << 2)); //disable/reset ADC/DAC + + //kill the claimer task and unclaim the device + mb.claimer_task.reset(); + mb.zpu_ctrl->poke32(SR_ADDR(X300_FW_SHMEM_BASE, X300_FW_SHMEM_CLAIM_TIME), 0); + mb.zpu_ctrl->poke32(SR_ADDR(X300_FW_SHMEM_BASE, X300_FW_SHMEM_CLAIM_SRC), 0); + } + } + catch(...) + { + UHD_SAFE_CALL(throw;) + } +} + +static void check_adc(wb_iface::sptr iface, const boost::uint32_t val) +{ + boost::uint32_t adc_rb = iface->peek32(RB32_RX); + adc_rb ^= 0xfffc0000; //adapt for I inversion in FPGA + //UHD_MSG(status) << "adc_rb " << std::hex << adc_rb << " val " << std::hex << val << std::endl; + UHD_ASSERT_THROW(adc_rb == val); +} + +void x300_impl::setup_radio(const size_t mb_i, const size_t i, const std::string &db_name) +{ + const fs_path mb_path = "/mboards/"+boost::lexical_cast<std::string>(mb_i); + mboard_members_t &mb = _mb[mb_i]; + radio_perifs_t &perif = mb.radio_perifs[i]; + const size_t dspno = i; + + //////////////////////////////////////////////////////////////////// + // radio control + //////////////////////////////////////////////////////////////////// + uint8_t dest = (i == 0)? X300_XB_DST_R0 : X300_XB_DST_R1; + boost::uint32_t ctrl_sid; + both_xports_t xport = this->make_transport(mb_i, dest, X300_RADIO_DEST_PREFIX_CTRL, device_addr_t(), ctrl_sid); + perif.ctrl = radio_ctrl_core_3000::make(mb.if_pkt_is_big_endian, xport.recv, xport.send, ctrl_sid, db_name); + perif.ctrl->poke32(TOREG(SR_MISC_OUTS), (1 << 2)); //reset adc + dac + perif.ctrl->poke32(TOREG(SR_MISC_OUTS), (1 << 1) | (1 << 0)); //out of reset + dac enable + + this->register_loopback_self_test(perif.ctrl); + + perif.spi = spi_core_3000::make(perif.ctrl, TOREG(SR_SPI), RB32_SPI); + perif.adc = x300_adc_ctrl::make(perif.spi, DB_ADC_SEN); + perif.dac = x300_dac_ctrl::make(perif.spi, DB_DAC_SEN, mb.clock->get_master_clock_rate()); + perif.leds = gpio_core_200_32wo::make(perif.ctrl, TOREG(SR_LEDS)); + + _tree->access<time_spec_t>(mb_path / "time" / "cmd") + .subscribe(boost::bind(&radio_ctrl_core_3000::set_time, perif.ctrl, _1)); + _tree->access<double>(mb_path / "tick_rate") + .subscribe(boost::bind(&radio_ctrl_core_3000::set_tick_rate, perif.ctrl, _1)); + + //////////////////////////////////////////////////////////////// + // ADC self test + //////////////////////////////////////////////////////////////// + perif.adc->set_test_word("ones", "ones"); check_adc(perif.ctrl, 0xfffcfffc); + perif.adc->set_test_word("zeros", "zeros"); check_adc(perif.ctrl, 0x00000000); + perif.adc->set_test_word("ones", "zeros"); check_adc(perif.ctrl, 0xfffc0000); + perif.adc->set_test_word("zeros", "ones"); check_adc(perif.ctrl, 0x0000fffc); + for (size_t k = 0; k < 14; k++) + { + perif.adc->set_test_word("zeros", "custom", 1 << k); + check_adc(perif.ctrl, 1 << (k+2)); + } + for (size_t k = 0; k < 14; k++) + { + perif.adc->set_test_word("custom", "zeros", 1 << k); + check_adc(perif.ctrl, 1 << (k+18)); + } + perif.adc->set_test_word("normal", "normal"); + + //////////////////////////////////////////////////////////////// + // Sync DAC's for MIMO + //////////////////////////////////////////////////////////////// + UHD_MSG(status) << "Sync DAC's." << std::endl; + perif.dac->arm_dac_sync(); // Put DAC into data Sync mode + perif.ctrl->poke32(TOREG(SR_DACSYNC), 0x1); // Arm FRAMEP/N sync pulse + + + //////////////////////////////////////////////////////////////// + // create codec control objects + //////////////////////////////////////////////////////////////// + _tree->create<int>(mb_path / "rx_codecs" / db_name / "gains"); //phony property so this dir exists + _tree->create<int>(mb_path / "tx_codecs" / db_name / "gains"); //phony property so this dir exists + _tree->create<std::string>(mb_path / "rx_codecs" / db_name / "name").set("ads62p48"); + _tree->create<std::string>(mb_path / "tx_codecs" / db_name / "name").set("ad9146"); + + _tree->create<meta_range_t>(mb_path / "rx_codecs" / db_name / "gains" / "digital" / "range").set(meta_range_t(0, 6.0, 0.5)); + _tree->create<double>(mb_path / "rx_codecs" / db_name / "gains" / "digital" / "value") + .subscribe(boost::bind(&x300_adc_ctrl::set_gain, perif.adc, _1)).set(0); + + //////////////////////////////////////////////////////////////////// + // front end corrections + //////////////////////////////////////////////////////////////////// + perif.rx_fe = rx_frontend_core_200::make(perif.ctrl, TOREG(SR_RX_FRONT)); + const fs_path rx_fe_path = mb_path / "rx_frontends" / db_name; + _tree->create<std::complex<double> >(rx_fe_path / "dc_offset" / "value") + .coerce(boost::bind(&rx_frontend_core_200::set_dc_offset, perif.rx_fe, _1)) + .set(std::complex<double>(0.0, 0.0)); + _tree->create<bool>(rx_fe_path / "dc_offset" / "enable") + .subscribe(boost::bind(&rx_frontend_core_200::set_dc_offset_auto, perif.rx_fe, _1)) + .set(true); + _tree->create<std::complex<double> >(rx_fe_path / "iq_balance" / "value") + .subscribe(boost::bind(&rx_frontend_core_200::set_iq_balance, perif.rx_fe, _1)) + .set(std::complex<double>(0.0, 0.0)); + + perif.tx_fe = tx_frontend_core_200::make(perif.ctrl, TOREG(SR_TX_FRONT)); + const fs_path tx_fe_path = mb_path / "tx_frontends" / db_name; + _tree->create<std::complex<double> >(tx_fe_path / "dc_offset" / "value") + .coerce(boost::bind(&tx_frontend_core_200::set_dc_offset, perif.tx_fe, _1)) + .set(std::complex<double>(0.0, 0.0)); + _tree->create<std::complex<double> >(tx_fe_path / "iq_balance" / "value") + .subscribe(boost::bind(&tx_frontend_core_200::set_iq_balance, perif.tx_fe, _1)) + .set(std::complex<double>(0.0, 0.0)); + + + + //////////////////////////////////////////////////////////////////// + // create rx dsp control objects + //////////////////////////////////////////////////////////////////// + perif.framer = rx_vita_core_3000::make(perif.ctrl, TOREG(SR_RX_CTRL)); + perif.ddc = rx_dsp_core_3000::make(perif.ctrl, TOREG(SR_RX_DSP)); + perif.ddc->set_link_rate(10e9/8); //whatever + _tree->access<double>(mb_path / "tick_rate") + .subscribe(boost::bind(&rx_vita_core_3000::set_tick_rate, perif.framer, _1)) + .subscribe(boost::bind(&rx_dsp_core_3000::set_tick_rate, perif.ddc, _1)); + const fs_path rx_dsp_path = mb_path / "rx_dsps" / str(boost::format("%u") % dspno); + _tree->create<meta_range_t>(rx_dsp_path / "rate" / "range") + .publish(boost::bind(&rx_dsp_core_3000::get_host_rates, perif.ddc)); + _tree->create<double>(rx_dsp_path / "rate" / "value") + .coerce(boost::bind(&rx_dsp_core_3000::set_host_rate, perif.ddc, _1)) + .subscribe(boost::bind(&x300_impl::update_rx_samp_rate, this, boost::ref(mb), dspno, _1)) + .set(1e6); + _tree->create<double>(rx_dsp_path / "freq" / "value") + .coerce(boost::bind(&rx_dsp_core_3000::set_freq, perif.ddc, _1)) + .set(0.0); + _tree->create<meta_range_t>(rx_dsp_path / "freq" / "range") + .publish(boost::bind(&rx_dsp_core_3000::get_freq_range, perif.ddc)); + _tree->create<stream_cmd_t>(rx_dsp_path / "stream_cmd") + .subscribe(boost::bind(&rx_vita_core_3000::issue_stream_command, perif.framer, _1)); + + //////////////////////////////////////////////////////////////////// + // create tx dsp control objects + //////////////////////////////////////////////////////////////////// + perif.deframer = tx_vita_core_3000::make(perif.ctrl, TOREG(SR_TX_CTRL)); + perif.duc = tx_dsp_core_3000::make(perif.ctrl, TOREG(SR_TX_DSP)); + perif.duc->set_link_rate(10e9/8); //whatever + _tree->access<double>(mb_path / "tick_rate") + .subscribe(boost::bind(&tx_vita_core_3000::set_tick_rate, perif.deframer, _1)) + .subscribe(boost::bind(&tx_dsp_core_3000::set_tick_rate, perif.duc, _1)); + const fs_path tx_dsp_path = mb_path / "tx_dsps" / str(boost::format("%u") % dspno); + _tree->create<meta_range_t>(tx_dsp_path / "rate" / "range") + .publish(boost::bind(&tx_dsp_core_3000::get_host_rates, perif.duc)); + _tree->create<double>(tx_dsp_path / "rate" / "value") + .coerce(boost::bind(&tx_dsp_core_3000::set_host_rate, perif.duc, _1)) + .subscribe(boost::bind(&x300_impl::update_tx_samp_rate, this, boost::ref(mb), dspno, _1)) + .set(1e6); + _tree->create<double>(tx_dsp_path / "freq" / "value") + .coerce(boost::bind(&tx_dsp_core_3000::set_freq, perif.duc, _1)) + .set(0.0); + _tree->create<meta_range_t>(tx_dsp_path / "freq" / "range") + .publish(boost::bind(&tx_dsp_core_3000::get_freq_range, perif.duc)); + + //////////////////////////////////////////////////////////////////// + // create time control objects + //////////////////////////////////////////////////////////////////// + time_core_3000::readback_bases_type time64_rb_bases; + time64_rb_bases.rb_now = RB64_TIME_NOW; + time64_rb_bases.rb_pps = RB64_TIME_PPS; + perif.time64 = time_core_3000::make(perif.ctrl, TOREG(SR_TIME), time64_rb_bases); + + //////////////////////////////////////////////////////////////////// + // create RF frontend interfacing + //////////////////////////////////////////////////////////////////// + const size_t j = (db_name == "B")? 0x2 : 0x0; + _tree->create<dboard_eeprom_t>(mb_path / "dboards" / db_name / "rx_eeprom") + .set(mb.db_eeproms[X300_DB0_RX_EEPROM | j]) + .subscribe(boost::bind(&x300_impl::set_db_eeprom, this, mb.zpu_i2c, (0x50 | X300_DB0_RX_EEPROM | j), _1)); + _tree->create<dboard_eeprom_t>(mb_path / "dboards" / db_name / "tx_eeprom") + .set(mb.db_eeproms[X300_DB0_TX_EEPROM | j]) + .subscribe(boost::bind(&x300_impl::set_db_eeprom, this, mb.zpu_i2c, (0x50 | X300_DB0_TX_EEPROM | j), _1)); + _tree->create<dboard_eeprom_t>(mb_path / "dboards" / db_name / "gdb_eeprom") + .set(mb.db_eeproms[X300_DB0_GDB_EEPROM | j]) + .subscribe(boost::bind(&x300_impl::set_db_eeprom, this, mb.zpu_i2c, (0x50 | X300_DB0_GDB_EEPROM | j), _1)); + + //create a new dboard interface + x300_dboard_iface_config_t db_config; + db_config.gpio = gpio_core_200::make(perif.ctrl, TOREG(SR_GPIO), RB32_GPIO); + db_config.spi = perif.spi; + db_config.rx_spi_slaveno = DB_RX_SEN; + db_config.tx_spi_slaveno = DB_TX_SEN; + db_config.i2c = mb.zpu_i2c; + db_config.clock = mb.clock; + db_config.which_rx_clk = (db_name == "A")? X300_CLOCK_WHICH_DB0_RX : X300_CLOCK_WHICH_DB1_RX; + db_config.which_tx_clk = (db_name == "A")? X300_CLOCK_WHICH_DB0_TX : X300_CLOCK_WHICH_DB1_TX; + db_config.dboard_slot = (db_name == "A")? 0 : 1; + _dboard_ifaces[db_name] = x300_make_dboard_iface(db_config); + + //create a new dboard manager + _tree->create<dboard_iface::sptr>(mb_path / "dboards" / db_name / "iface").set(_dboard_ifaces[db_name]); + _dboard_managers[db_name] = dboard_manager::make( + mb.db_eeproms[X300_DB0_RX_EEPROM | j].id, + mb.db_eeproms[X300_DB0_TX_EEPROM | j].id, + mb.db_eeproms[X300_DB0_GDB_EEPROM | j].id, + _dboard_ifaces[db_name], + _tree->subtree(mb_path / "dboards" / db_name) + ); + + //now that dboard is created -- register into rx antenna event + const std::string fe_name = _tree->list(mb_path / "dboards" / db_name / "rx_frontends").front(); + _tree->access<std::string>(mb_path / "dboards" / db_name / "rx_frontends" / fe_name / "antenna" / "value") + .subscribe(boost::bind(&x300_impl::update_atr_leds, this, mb.radio_perifs[i].leds, _1)); + this->update_atr_leds(mb.radio_perifs[i].leds, ""); //init anyway, even if never called + + //bind frontend corrections to the dboard freq props + const fs_path db_rx_fe_path = mb_path / "dboards" / db_name / "rx_frontends"; + BOOST_FOREACH(const std::string &name, _tree->list(db_rx_fe_path)) + { + _tree->access<double>(db_rx_fe_path / name / "freq" / "value") + .subscribe(boost::bind(&x300_impl::set_rx_fe_corrections, this, mb_path, db_name, _1)); + } +} + +void x300_impl::set_rx_fe_corrections(const uhd::fs_path &mb_path, const std::string &fe_name, const double lo_freq) +{ + apply_rx_fe_corrections(this->get_tree()->subtree(mb_path), fe_name, lo_freq); +} + +boost::uint32_t get_pcie_dma_channel(boost::uint8_t destination, boost::uint8_t prefix) +{ + static const boost::uint32_t RADIO_GRP_SIZE = 3; + static const boost::uint32_t RADIO0_GRP = 0; + static const boost::uint32_t RADIO1_GRP = 1; + + boost::uint32_t radio_grp = (destination == X300_XB_DST_R0) ? RADIO0_GRP : RADIO1_GRP; + return ((radio_grp * RADIO_GRP_SIZE) + prefix); +} + + +x300_impl::both_xports_t x300_impl::make_transport( + const size_t mb_index, + const uint8_t& destination, + const uint8_t& prefix, + const uhd::device_addr_t& args, + boost::uint32_t& sid +) +{ + mboard_members_t &mb = _mb[mb_index]; + const std::string& addr = mb.addr; + const std::string& xport_path = mb.xport_path; + both_xports_t xports; + + sid_config_t config; + config.router_addr_there = X300_DEVICE_THERE; + config.dst_prefix = prefix; + config.router_dst_there = destination; + config.router_dst_here = mb.router_dst_here; + sid = this->allocate_sid(mb, config); + + static const uhd::device_addr_t DEFAULT_XPORT_ARGS; + + const uhd::device_addr_t& xport_args = + (prefix != X300_RADIO_DEST_PREFIX_CTRL) ? args : DEFAULT_XPORT_ARGS; + + zero_copy_xport_params default_buff_args; + if (xport_path == "nirio") { + default_buff_args.send_frame_size = + (prefix == X300_RADIO_DEST_PREFIX_TX) ? X300_PCIE_DATA_FRAME_SIZE : X300_PCIE_MSG_FRAME_SIZE; + default_buff_args.recv_frame_size = + (prefix == X300_RADIO_DEST_PREFIX_RX) ? X300_PCIE_DATA_FRAME_SIZE : X300_PCIE_MSG_FRAME_SIZE; + default_buff_args.num_send_frames = + (prefix == X300_RADIO_DEST_PREFIX_TX) ? X300_PCIE_DATA_NUM_FRAMES : X300_PCIE_MSG_NUM_FRAMES; + default_buff_args.num_recv_frames = + (prefix == X300_RADIO_DEST_PREFIX_RX) ? X300_PCIE_DATA_NUM_FRAMES : X300_PCIE_MSG_NUM_FRAMES; + + xports.recv = nirio_zero_copy::make( + mb.rio_fpga_interface, get_pcie_dma_channel(destination, prefix), default_buff_args, xport_args); + xports.send = xports.recv; + + //For the nirio transport, buffer size is depends on the frame size and num frames + xports.recv_buff_size = xports.recv->get_num_recv_frames() * xports.recv->get_recv_frame_size(); + xports.send_buff_size = xports.send->get_num_send_frames() * xports.send->get_send_frame_size(); + } else { + + default_buff_args.send_frame_size = + (prefix == X300_RADIO_DEST_PREFIX_TX) ? X300_ETH_DATA_FRAME_SIZE : X300_ETH_MSG_FRAME_SIZE; + default_buff_args.recv_frame_size = + (prefix == X300_RADIO_DEST_PREFIX_RX) ? X300_ETH_DATA_FRAME_SIZE : X300_ETH_MSG_FRAME_SIZE; + default_buff_args.num_send_frames = + (prefix == X300_RADIO_DEST_PREFIX_TX) ? X300_ETH_DATA_NUM_FRAMES : X300_ETH_MSG_NUM_FRAMES; + default_buff_args.num_recv_frames = + (prefix == X300_RADIO_DEST_PREFIX_RX) ? X300_ETH_DATA_NUM_FRAMES : X300_ETH_MSG_NUM_FRAMES; + + //make a new transport - fpga has no idea how to talk to use on this yet + udp_zero_copy::buff_params buff_params; + xports.recv = udp_zero_copy::make(addr, BOOST_STRINGIZE(X300_VITA_UDP_PORT), default_buff_args, buff_params, xport_args); + xports.send = xports.recv; + + //For the UDP transport the buffer size if the size of the socket buffer in the kernel + xports.recv_buff_size = buff_params.recv_buff_size; + xports.send_buff_size = buff_params.send_buff_size; + } + + //always program the framer if this is a socket, even its caching was used + if (xport_path != "nirio") + { + //clear the ethernet dispatcher's udp port + //NOT clearing this, the dispatcher is now intelligent + //_zpu_ctrl->poke32(SR_ADDR(SET0_BASE, (ZPU_SR_ETHINT0+8+3)), 0); + + //send a mini packet with SID into the ZPU + //ZPU will reprogram the ethernet framer + UHD_LOG << "programming packet for new xport on " + << addr << std::hex << "sid 0x" << sid << std::dec << std::endl; + //YES, get a __send__ buffer from the __recv__ socket + //-- this is the only way to program the framer for recv: + managed_send_buffer::sptr buff = xports.recv->get_send_buff(); + buff->cast<boost::uint32_t *>()[0] = 0; //eth dispatch looks for != 0 + buff->cast<boost::uint32_t *>()[1] = uhd::htonx(sid); + buff->commit(8); + buff.reset(); + + //reprogram the ethernet dispatcher's udp port (should be safe to always set) + UHD_LOG << "reprogram the ethernet dispatcher's udp port" << std::endl; + mb.zpu_ctrl->poke32(SR_ADDR(SET0_BASE, (ZPU_SR_ETHINT0+8+3)), X300_VITA_UDP_PORT); + mb.zpu_ctrl->poke32(SR_ADDR(SET0_BASE, (ZPU_SR_ETHINT1+8+3)), X300_VITA_UDP_PORT); + + //Do a peek to an arbitrary address to guarantee that the + //ethernet framer has been programmed before we return. + mb.zpu_ctrl->peek32(0); + } + return xports; +} + + +boost::uint32_t x300_impl::allocate_sid(mboard_members_t &mb, const sid_config_t &config) +{ + const std::string &xport_path = mb.xport_path; + const boost::uint32_t stream = (config.dst_prefix | (config.router_dst_there << 2)) & 0xff; + + const boost::uint32_t sid = 0 + | (X300_DEVICE_HERE << 24) + | (_sid_framer << 16) + | (config.router_addr_there << 8) + | (stream << 0) + ; + UHD_LOG << std::hex + << " sid 0x" << sid + << " framer 0x" << _sid_framer + << " stream 0x" << stream + << " router_dst_there 0x" << int(config.router_dst_there) + << " router_addr_there 0x" << int(config.router_addr_there) + << std::dec << std::endl; + + // Program the X300 to recognise it's own local address. + mb.zpu_ctrl->poke32(SR_ADDR(SET0_BASE, ZPU_SR_XB_LOCAL), config.router_addr_there); + // Program CAM entry for outgoing packets matching a X300 resource (for example a Radio) + // This type of packet does matches the XB_LOCAL address and is looked up in the upper half of the CAM + mb.zpu_ctrl->poke32(SR_ADDR(SETXB_BASE, 256 + (stream)), config.router_dst_there); + // Program CAM entry for returning packets to us (for example GR host via Eth0) + // This type of packet does not match the XB_LOCAL address and is looked up in the lower half of the CAM + mb.zpu_ctrl->poke32(SR_ADDR(SETXB_BASE, 0 + (X300_DEVICE_HERE)), config.router_dst_here); + + if (xport_path == "nirio") { + uint32_t router_config_word = ((_sid_framer & 0xff) << 16) | //Return SID + get_pcie_dma_channel(config.router_dst_there, config.dst_prefix); //Dest + mb.rio_fpga_interface->get_kernel_proxy().poke(PCIE_ROUTER_REG(0), router_config_word); + } + + UHD_LOG << std::hex + << "done router config for sid 0x" << sid + << std::dec << std::endl; + + //increment for next setup + _sid_framer++; + + return sid; +} + +void x300_impl::update_atr_leds(gpio_core_200_32wo::sptr leds, const std::string &rx_ant) +{ + const bool is_txrx = (rx_ant == "TX/RX"); + const int rx_led = (1 << 2); + const int txrx_led = (1 << 1); + const int tx_led = (1 << 0); + leds->set_atr_reg(dboard_iface::ATR_REG_IDLE, 0); + leds->set_atr_reg(dboard_iface::ATR_REG_RX_ONLY, is_txrx? txrx_led : rx_led); + leds->set_atr_reg(dboard_iface::ATR_REG_TX_ONLY, tx_led); + leds->set_atr_reg(dboard_iface::ATR_REG_FULL_DUPLEX, rx_led | tx_led); +} + +void x300_impl::set_tick_rate(mboard_members_t &mb, const double rate) +{ + BOOST_FOREACH(radio_perifs_t &perif, mb.radio_perifs) + perif.time64->set_tick_rate(rate); +} + +void x300_impl::register_loopback_self_test(wb_iface::sptr iface) +{ + bool test_fail = false; + UHD_MSG(status) << "Performing register loopback test... " << std::flush; + size_t hash = time(NULL); + for (size_t i = 0; i < 100; i++) + { + boost::hash_combine(hash, i); + iface->poke32(TOREG(SR_TEST), boost::uint32_t(hash)); + test_fail = iface->peek32(RB32_TEST) != boost::uint32_t(hash); + if (test_fail) break; //exit loop on any failure + } + UHD_MSG(status) << ((test_fail)? " fail" : "pass") << std::endl; +} + +void x300_impl::set_time_source_out(mboard_members_t &mb, const bool enb) +{ + mb.clock_control_regs__pps_out_enb = enb? 1 : 0; + this->update_clock_control(mb); +} + +void x300_impl::update_clock_control(mboard_members_t &mb) +{ + const size_t reg = mb.clock_control_regs__clock_source + | (mb.clock_control_regs__pps_select << 2) + | (mb.clock_control_regs__pps_out_enb << 3) + | (mb.clock_control_regs__tcxo_enb << 4) + | (mb.clock_control_regs__gpsdo_pwr << 5) + ; + mb.zpu_ctrl->poke32(SR_ADDR(SET0_BASE, ZPU_SR_CLOCK_CTRL), reg); +} + +void x300_impl::update_clock_source(mboard_members_t &mb, const std::string &source) +{ + mb.clock_control_regs__clock_source = 0; + if (source == "internal") { + mb.clock_control_regs__clock_source = 0x2; + + mb.clock_control_regs__tcxo_enb = (source == "internal")? 1 : 0; + } else if (source == "external") { + mb.clock_control_regs__clock_source = 0x0; + } else if (source == "gpsdo") { + mb.clock_control_regs__clock_source = 0x3; + } else { + throw uhd::key_error("update_clock_source: unknown source: " + source); + } + + this->update_clock_control(mb); +} + +void x300_impl::update_time_source(mboard_members_t &mb, const std::string &source) +{ + if (source == "internal") { + // no action needed + } else if (source == "external") { + mb.clock_control_regs__pps_select = (source == "external")? 1 : 0; + } else if (source == "gpsdo") { + // no action needed + } else { + throw uhd::key_error("update_time_source: unknown source: " + source); + } + + this->update_clock_control(mb); +} + +sensor_value_t x300_impl::get_ref_locked(wb_iface::sptr ctrl) +{ + const bool lock = (ctrl->peek32(SR_ADDR(SET0_BASE, ZPU_RB_CLK_STATUS)) & (1 << 2)) != 0; + return sensor_value_t("Ref", lock, "locked", "unlocked"); +} + +void x300_impl::set_db_eeprom(i2c_iface::sptr i2c, const size_t addr, const uhd::usrp::dboard_eeprom_t &db_eeprom) +{ + db_eeprom.store(*i2c, addr); +} + +void x300_impl::set_mb_eeprom(i2c_iface::sptr i2c, const mboard_eeprom_t &mb_eeprom) +{ + i2c_iface::sptr eeprom16 = i2c->eeprom16(); + mb_eeprom.commit(*eeprom16, "X300"); +} + +boost::uint32_t x300_impl::get_fp_gpio(gpio_core_200::sptr gpio, const std::string &) +{ + return boost::uint32_t(gpio->read_gpio(dboard_iface::UNIT_RX)); +} + +void x300_impl::set_fp_gpio(gpio_core_200::sptr gpio, const std::string &attr, const boost::uint32_t value) +{ + if (attr == "CTRL") return gpio->set_pin_ctrl(dboard_iface::UNIT_RX, value); + if (attr == "DDR") return gpio->set_gpio_ddr(dboard_iface::UNIT_RX, value); + if (attr == "OUT") return gpio->set_gpio_out(dboard_iface::UNIT_RX, value); + if (attr == "ATR_0X") return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE, value); + if (attr == "ATR_RX") return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_RX_ONLY, value); + if (attr == "ATR_TX") return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY, value); + if (attr == "ATR_XX") return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, value); +} + +/*********************************************************************** + * claimer logic + **********************************************************************/ + +void x300_impl::claimer_loop(wb_iface::sptr iface) +{ + iface->poke32(SR_ADDR(X300_FW_SHMEM_BASE, X300_FW_SHMEM_CLAIM_TIME), time(NULL)); + iface->poke32(SR_ADDR(X300_FW_SHMEM_BASE, X300_FW_SHMEM_CLAIM_SRC), get_process_hash()); + boost::this_thread::sleep(boost::posix_time::milliseconds(1500)); //1.5 seconds +} + +bool x300_impl::is_claimed(wb_iface::sptr iface) +{ + //If timed out then device is definitely unclaimed + if (iface->peek32(SR_ADDR(X300_FW_SHMEM_BASE, X300_FW_SHMEM_CLAIM_STATUS)) == 0) + return false; + + //otherwise check claim src to determine if another thread with the same src has claimed the device + return iface->peek32(SR_ADDR(X300_FW_SHMEM_BASE, X300_FW_SHMEM_CLAIM_SRC)) != get_process_hash(); +} + +/*********************************************************************** + * MTU detection + **********************************************************************/ +x300_impl::mtu_result_t x300_impl::determine_mtu(const std::string &addr, const mtu_result_t &user_mtu) +{ + udp_simple::sptr udp = udp_simple::make_connected(addr, BOOST_STRINGIZE(X300_MTU_DETECT_UDP_PORT)); + + std::vector<boost::uint8_t> buffer(std::max(user_mtu.recv_mtu, user_mtu.send_mtu)); + x300_mtu_t *request = reinterpret_cast<x300_mtu_t *>(&buffer.front()); + static const double echo_timeout = 0.020; //20 ms + + //test holler - check if its supported in this fw version + request->flags = uhd::htonx<boost::uint32_t>(X300_MTU_DETECT_ECHO_REQUEST); + request->size = uhd::htonx<boost::uint32_t>(sizeof(x300_mtu_t)); + udp->send(boost::asio::buffer(buffer, sizeof(x300_mtu_t))); + udp->recv(boost::asio::buffer(buffer), echo_timeout); + if (!(uhd::ntohx<boost::uint32_t>(request->flags) & X300_MTU_DETECT_ECHO_REPLY)) + throw uhd::not_implemented_error("Holler protocol not implemented"); + + size_t min_recv_mtu = sizeof(x300_mtu_t); + size_t max_recv_mtu = user_mtu.recv_mtu; + size_t min_send_mtu = sizeof(x300_mtu_t); + size_t max_send_mtu = user_mtu.send_mtu; + + UHD_MSG(status) << "Determining receive MTU ... "; + while (min_recv_mtu < max_recv_mtu) + { + size_t test_mtu = (max_recv_mtu/2 + min_recv_mtu/2 + 3) & ~3; + + request->flags = uhd::htonx<boost::uint32_t>(X300_MTU_DETECT_ECHO_REQUEST); + request->size = uhd::htonx<boost::uint32_t>(test_mtu); + udp->send(boost::asio::buffer(buffer, sizeof(x300_mtu_t))); + + size_t len = udp->recv(boost::asio::buffer(buffer), echo_timeout); + + if (len >= test_mtu) + min_recv_mtu = test_mtu; + else + max_recv_mtu = test_mtu - 4; + + } + UHD_MSG(status) << min_recv_mtu << std::endl; + + UHD_MSG(status) << "Determining send MTU ... "; + while (min_send_mtu < max_send_mtu) + { + size_t test_mtu = (max_send_mtu/2 + min_send_mtu/2 + 3) & ~3; + + request->flags = uhd::htonx<boost::uint32_t>(X300_MTU_DETECT_ECHO_REQUEST); + request->size = uhd::htonx<boost::uint32_t>(sizeof(x300_mtu_t)); + udp->send(boost::asio::buffer(buffer, test_mtu)); + + size_t len = udp->recv(boost::asio::buffer(buffer), echo_timeout); + if (len >= sizeof(x300_mtu_t)) + len = uhd::ntohx<boost::uint32_t>(request->size); + + if (len >= test_mtu) + min_send_mtu = test_mtu; + else + max_send_mtu = test_mtu - 4; + } + UHD_MSG(status) << min_send_mtu << std::endl; + + mtu_result_t mtu; + mtu.recv_mtu = min_recv_mtu; + mtu.send_mtu = min_send_mtu; + return mtu; +} + +/*********************************************************************** + * compat checks + **********************************************************************/ + +void x300_impl::check_fw_compat(const fs_path &mb_path, wb_iface::sptr iface) +{ + boost::uint32_t compat_num = iface->peek32(SR_ADDR(X300_FW_SHMEM_BASE, X300_FW_SHMEM_COMPAT_NUM)); + boost::uint32_t compat_major = (compat_num >> 16); + boost::uint32_t compat_minor = (compat_num & 0xffff); + + if (compat_major != X300_FW_COMPAT_MAJOR) + { + throw uhd::runtime_error(str(boost::format( + "Expected firmware compatibility number 0x%x, but got 0x%x.%x:\n" + "The firmware build is not compatible with the host code build.\n" + "%s" + ) % int(X300_FW_COMPAT_MAJOR) % compat_major % compat_minor + % print_images_error())); + } + _tree->create<std::string>(mb_path / "fw_version").set(str(boost::format("%u.%u") + % compat_major % compat_minor)); +} + +void x300_impl::check_fpga_compat(const fs_path &mb_path, wb_iface::sptr iface) +{ + boost::uint32_t compat_num = iface->peek32(SR_ADDR(SET0_BASE, ZPU_RB_COMPAT_NUM)); + boost::uint32_t compat_major = (compat_num >> 16); + boost::uint32_t compat_minor = (compat_num & 0xffff); + + if (compat_major != X300_FPGA_COMPAT_MAJOR) + { + throw uhd::runtime_error(str(boost::format( + "Expected FPGA compatibility number 0x%x, but got 0x%x.%x:\n" + "The FPGA build is not compatible with the host code build.\n" + "%s" + ) % int(X300_FPGA_COMPAT_MAJOR) % compat_major % compat_minor + % print_images_error())); + } + _tree->create<std::string>(mb_path / "fpga_version").set(str(boost::format("%u.%u") + % compat_major % compat_minor)); +} + +x300_impl::x300_mboard_t x300_impl::get_mb_type_from_pcie(const std::string& resource, const std::string& rpc_port) +{ + x300_mboard_t mb_type = UNKNOWN; + + //Detect the PCIe product ID to distinguish between X300 and X310 + nirio_status status = NiRio_Status_Success; + boost::uint32_t pid; + niriok_proxy::sptr discovery_proxy = + niusrprio_session::create_kernel_proxy(resource, rpc_port); + if (discovery_proxy) { + nirio_status_chain(discovery_proxy->get_attribute(PRODUCT_NUMBER, pid), status); + discovery_proxy->close(); + if (nirio_status_not_fatal(status)) { + //The PCIe ID -> MB mapping may be different from the EEPROM -> MB mapping + switch (pid) { + case X300_USRP_PCIE_SSID: + mb_type = USRP_X300_MB; break; + case X310_USRP_PCIE_SSID: + case X310_2940R_PCIE_SSID: + case X310_2942R_PCIE_SSID: + case X310_2943R_PCIE_SSID: + case X310_2944R_PCIE_SSID: + case X310_2950R_PCIE_SSID: + case X310_2952R_PCIE_SSID: + case X310_2953R_PCIE_SSID: + case X310_2954R_PCIE_SSID: + mb_type = USRP_X310_MB; break; + default: + mb_type = UNKNOWN; break; + } + } + } + + return mb_type; +} + +x300_impl::x300_mboard_t x300_impl::get_mb_type_from_eeprom(const uhd::usrp::mboard_eeprom_t& mb_eeprom) +{ + x300_mboard_t mb_type = UNKNOWN; + if (not mb_eeprom["product"].empty()) + { + boost::uint16_t product_num = 0; + try { + product_num = boost::lexical_cast<boost::uint16_t>(mb_eeprom["product"]); + } catch (const boost::bad_lexical_cast &) { + product_num = 0; + } + + switch (product_num) { + //The PCIe ID -> MB mapping may be different from the EEPROM -> MB mapping + case X300_USRP_PCIE_SSID: + mb_type = USRP_X300_MB; break; + case X310_USRP_PCIE_SSID: + case X310_2940R_PCIE_SSID: + case X310_2942R_PCIE_SSID: + case X310_2943R_PCIE_SSID: + case X310_2944R_PCIE_SSID: + case X310_2950R_PCIE_SSID: + case X310_2952R_PCIE_SSID: + case X310_2953R_PCIE_SSID: + case X310_2954R_PCIE_SSID: + mb_type = USRP_X310_MB; break; + default: + UHD_MSG(warning) << "X300 unknown product code in EEPROM: " << product_num << std::endl; + mb_type = UNKNOWN; break; + } + } + return mb_type; +} + diff --git a/host/lib/usrp/x300/x300_impl.hpp b/host/lib/usrp/x300/x300_impl.hpp new file mode 100644 index 000000000..37f8cc468 --- /dev/null +++ b/host/lib/usrp/x300/x300_impl.hpp @@ -0,0 +1,306 @@ +// +// Copyright 2013-2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// 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_X300_IMPL_HPP +#define INCLUDED_X300_IMPL_HPP + +#include <uhd/property_tree.hpp> +#include <uhd/device.hpp> +#include <uhd/usrp/mboard_eeprom.hpp> +#include <uhd/usrp/dboard_manager.hpp> +#include <uhd/usrp/dboard_eeprom.hpp> +#include <uhd/usrp/subdev_spec.hpp> +#include <uhd/types/sensors.hpp> +#include "x300_clock_ctrl.hpp" +#include "x300_fw_common.h" +#include <uhd/transport/udp_simple.hpp> //mtu +#include <uhd/utils/tasks.hpp> +#include "spi_core_3000.hpp" +#include "x300_adc_ctrl.hpp" +#include "x300_dac_ctrl.hpp" +#include "rx_vita_core_3000.hpp" +#include "tx_vita_core_3000.hpp" +#include "time_core_3000.hpp" +#include "rx_dsp_core_3000.hpp" +#include "tx_dsp_core_3000.hpp" +#include "i2c_core_100_wb32.hpp" +#include "radio_ctrl_core_3000.hpp" +#include "rx_frontend_core_200.hpp" +#include "tx_frontend_core_200.hpp" +#include "gpio_core_200.hpp" +#include <boost/weak_ptr.hpp> +#include <uhd/usrp/gps_ctrl.hpp> +#include <uhd/usrp/mboard_eeprom.hpp> +#include <uhd/transport/bounded_buffer.hpp> +#include <uhd/transport/nirio/niusrprio_session.h> +#include <uhd/transport/vrt_if_packet.hpp> +#include "recv_packet_demuxer_3000.hpp" + +static const std::string X300_FW_FILE_NAME = "usrp_x300_fw.bin"; + +static const double X300_DEFAULT_TICK_RATE = 200e6; //Hz +static const double X300_BUS_CLOCK_RATE = 175e6; //Hz + +static const size_t X300_TX_HW_BUFF_SIZE = 0x90000; //576KiB +static const size_t X300_TX_FC_RESPONSE_FREQ = 8; //per flow-control window + +static const size_t X300_RX_SW_BUFF_SIZE_ETH = 0x2000000; //32MiB For an ~8k MTU any size >32MiB is just wasted buffer space +static const size_t X300_RX_SW_BUFF_SIZE_ETH_MACOS = 0x100000; //1Mib +static const double X300_RX_SW_BUFF_FULL_FACTOR = 0.90; //Buffer should ideally be 90% full. +static const size_t X300_RX_FC_REQUEST_FREQ = 32; //per flow-control window + +static const size_t X300_PCIE_DATA_FRAME_SIZE = 8192; //bytes +static const size_t X300_PCIE_DATA_NUM_FRAMES = 2048; +static const size_t X300_PCIE_MSG_FRAME_SIZE = 256; //bytes +static const size_t X300_PCIE_MSG_NUM_FRAMES = 32; + +static const size_t X300_ETH_DATA_FRAME_SIZE = 8000; //bytes +static const size_t X300_ETH_DATA_NUM_FRAMES = 32; +static const size_t X300_ETH_MSG_FRAME_SIZE = uhd::transport::udp_simple::mtu; //bytes +static const size_t X300_ETH_MSG_NUM_FRAMES = 32; +static const double X300_DEFAULT_SYSREF_RATE = 10e6; + +#define X300_RADIO_DEST_PREFIX_TX 0 +#define X300_RADIO_DEST_PREFIX_CTRL 1 +#define X300_RADIO_DEST_PREFIX_RX 2 + +#define X300_XB_DST_E0 0 +#define X300_XB_DST_E1 1 +#define X300_XB_DST_R0 2 +#define X300_XB_DST_R1 3 +#define X300_XB_DST_CE0 4 +#define X300_XB_DST_CE1 5 +#define X300_XB_DST_CE2 5 +#define X300_XB_DST_PCI 7 + +#define X300_DEVICE_THERE 2 +#define X300_DEVICE_HERE 0 + +//eeprom addrs for various boards +enum +{ + X300_DB0_RX_EEPROM = 0x5, + X300_DB0_TX_EEPROM = 0x4, + X300_DB0_GDB_EEPROM = 0x1, + X300_DB1_RX_EEPROM = 0x7, + X300_DB1_TX_EEPROM = 0x6, + X300_DB1_GDB_EEPROM = 0x3, +}; + +struct x300_dboard_iface_config_t +{ + gpio_core_200::sptr gpio; + spi_core_3000::sptr spi; + size_t rx_spi_slaveno; + size_t tx_spi_slaveno; + i2c_core_100_wb32::sptr i2c; + x300_clock_ctrl::sptr clock; + x300_clock_which_t which_rx_clk; + x300_clock_which_t which_tx_clk; + boost::uint8_t dboard_slot; +}; + +uhd::usrp::dboard_iface::sptr x300_make_dboard_iface(const x300_dboard_iface_config_t &); +uhd::uart_iface::sptr x300_make_uart_iface(uhd::wb_iface::sptr iface); + +uhd::wb_iface::sptr x300_make_ctrl_iface_enet(uhd::transport::udp_simple::sptr udp); +uhd::wb_iface::sptr x300_make_ctrl_iface_pcie(uhd::niusrprio::niriok_proxy& drv_proxy); + +class x300_impl : public uhd::device +{ +public: + typedef uhd::transport::bounded_buffer<uhd::async_metadata_t> async_md_type; + + x300_impl(const uhd::device_addr_t &); + void setup_mb(const size_t which, const uhd::device_addr_t &); + ~x300_impl(void); + + //the io interface + uhd::rx_streamer::sptr get_rx_stream(const uhd::stream_args_t &); + uhd::tx_streamer::sptr get_tx_stream(const uhd::stream_args_t &); + + //support old async call + bool recv_async_msg(uhd::async_metadata_t &, double); + + // used by x300_find_with_addr to find X300 devices. + static bool is_claimed(uhd::wb_iface::sptr); + + enum x300_mboard_t { + USRP_X300_MB, USRP_X310_MB, UNKNOWN + }; + static x300_mboard_t get_mb_type_from_pcie(const std::string& resource, const std::string& rpc_port); + static x300_mboard_t get_mb_type_from_eeprom(const uhd::usrp::mboard_eeprom_t& mb_eeprom); + +private: + boost::shared_ptr<async_md_type> _async_md; + + //perifs in the radio core + struct radio_perifs_t + { + radio_ctrl_core_3000::sptr ctrl; + spi_core_3000::sptr spi; + x300_adc_ctrl::sptr adc; + x300_dac_ctrl::sptr dac; + time_core_3000::sptr time64; + rx_vita_core_3000::sptr framer; + rx_dsp_core_3000::sptr ddc; + tx_vita_core_3000::sptr deframer; + tx_dsp_core_3000::sptr duc; + gpio_core_200_32wo::sptr leds; + rx_frontend_core_200::sptr rx_fe; + tx_frontend_core_200::sptr tx_fe; + }; + + //overflow recovery impl + void handle_overflow(radio_perifs_t &perif, boost::weak_ptr<uhd::rx_streamer> streamer); + + //vector of member objects per motherboard + struct mboard_members_t + { + uhd::dict<size_t, boost::weak_ptr<uhd::rx_streamer> > rx_streamers; + uhd::dict<size_t, boost::weak_ptr<uhd::tx_streamer> > tx_streamers; + + uhd::task::sptr claimer_task; + std::string addr; + std::string xport_path; + int router_dst_here; + uhd::device_addr_t send_args; + uhd::device_addr_t recv_args; + bool if_pkt_is_big_endian; + uhd::niusrprio::niusrprio_session::sptr rio_fpga_interface; + + //perifs in the zpu + uhd::wb_iface::sptr zpu_ctrl; + spi_core_3000::sptr zpu_spi; + i2c_core_100_wb32::sptr zpu_i2c; + + //perifs in each radio + radio_perifs_t radio_perifs[2]; + uhd::usrp::dboard_eeprom_t db_eeproms[8]; + + //per mboard frontend mapping + uhd::usrp::subdev_spec_t rx_fe_map; + uhd::usrp::subdev_spec_t tx_fe_map; + + //other perifs on mboard + x300_clock_ctrl::sptr clock; + uhd::gps_ctrl::sptr gps; + gpio_core_200::sptr fp_gpio; + + //clock control register bits + int clock_control_regs__clock_source; + int clock_control_regs__pps_select; + int clock_control_regs__pps_out_enb; + int clock_control_regs__tcxo_enb; + int clock_control_regs__gpsdo_pwr; + }; + std::vector<mboard_members_t> _mb; + + //task for periodically reclaiming the device from others + void claimer_loop(uhd::wb_iface::sptr); + + boost::mutex _transport_setup_mutex; + + void register_loopback_self_test(uhd::wb_iface::sptr iface); + + void setup_radio(const size_t, const size_t which_radio, const std::string &db_name); + + size_t _sid_framer; + struct sid_config_t + { + boost::uint8_t router_addr_there; + boost::uint8_t dst_prefix; //2bits + boost::uint8_t router_dst_there; + boost::uint8_t router_dst_here; + }; + boost::uint32_t allocate_sid(mboard_members_t &mb, const sid_config_t &config); + + struct both_xports_t + { + uhd::transport::zero_copy_if::sptr recv; + uhd::transport::zero_copy_if::sptr send; + size_t recv_buff_size; + size_t send_buff_size; + }; + both_xports_t make_transport( + const size_t mb_index, + const uint8_t& destination, + const uint8_t& prefix, + const uhd::device_addr_t& args, + boost::uint32_t& sid); + + struct mtu_result_t + { + size_t recv_mtu; + size_t send_mtu; + }; + + mtu_result_t determine_mtu(const std::string &addr, const mtu_result_t &user_mtu); + + //////////////////////////////////////////////////////////////////// + // + //Caching for transport interface re-use -- like sharing a DMA. + //The cache is optionally used by make_transport by use-case. + //The cache maps an ID string to a transport-ish object. + //The ID string identifies a purpose for the transport. + // + //For recv, there is a demux cache, which maps a ID string + //to a recv demux object. When a demux is used, the underlying transport + //must never be used outside of the demux. Use demux->make_proxy(sid). + // + uhd::dict<std::string, uhd::usrp::recv_packet_demuxer_3000::sptr> _demux_cache; + // + //For send, there is a shared send xport, which maps an ID string + //to a transport capable of sending buffers. Send transports + //can be shared amongst multiple callers, unlike recv. + // + uhd::dict<std::string, uhd::transport::zero_copy_if::sptr> _send_cache; + // + //////////////////////////////////////////////////////////////////// + + uhd::dict<std::string, uhd::usrp::dboard_manager::sptr> _dboard_managers; + uhd::dict<std::string, uhd::usrp::dboard_iface::sptr> _dboard_ifaces; + + void set_rx_fe_corrections(const uhd::fs_path &mb_path, const std::string &fe_name, const double lo_freq); + + void update_rx_subdev_spec(const size_t, const uhd::usrp::subdev_spec_t &spec); + void update_tx_subdev_spec(const size_t, const uhd::usrp::subdev_spec_t &spec); + + void set_tick_rate(mboard_members_t &, const double); + void update_tick_rate(mboard_members_t &, const double); + void update_rx_samp_rate(mboard_members_t&, const size_t, const double); + void update_tx_samp_rate(mboard_members_t&, const size_t, const double); + + void update_clock_control(mboard_members_t&); + void set_time_source_out(mboard_members_t&, const bool); + void update_clock_source(mboard_members_t&, const std::string &); + void update_time_source(mboard_members_t&, const std::string &); + + uhd::sensor_value_t get_ref_locked(uhd::wb_iface::sptr); + + void set_db_eeprom(uhd::i2c_iface::sptr i2c, const size_t, const uhd::usrp::dboard_eeprom_t &); + void set_mb_eeprom(uhd::i2c_iface::sptr i2c, const uhd::usrp::mboard_eeprom_t &); + + void check_fw_compat(const uhd::fs_path &mb_path, uhd::wb_iface::sptr iface); + void check_fpga_compat(const uhd::fs_path &mb_path, uhd::wb_iface::sptr iface); + + void update_atr_leds(gpio_core_200_32wo::sptr, const std::string &ant); + boost::uint32_t get_fp_gpio(gpio_core_200::sptr, const std::string &); + void set_fp_gpio(gpio_core_200::sptr, const std::string &, const boost::uint32_t); +}; + +#endif /* INCLUDED_X300_IMPL_HPP */ diff --git a/host/lib/usrp/x300/x300_init.sh b/host/lib/usrp/x300/x300_init.sh new file mode 100644 index 000000000..96c346c12 --- /dev/null +++ b/host/lib/usrp/x300/x300_init.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +utils/usrp_burn_mb_eeprom --key=serial --val="x310rC12" +utils/usrp_burn_mb_eeprom --key=name --val="dillen" + +utils/usrp_burn_mb_eeprom --key=product --val="30410" +utils/usrp_burn_mb_eeprom --key=revision --val="3" + +mac0=$(python -c "import random; mac = [0x00, 0x16, 0x3e, random.randint(0x00, 0x7f),random.randint(0x00, 0xff),random.randint(0x00, 0xff)]; print ':'.join(map(lambda x: '%02x' % x, mac))") +mac1=$(python -c "import random; mac = [0x00, 0x16, 0x3e, random.randint(0x00, 0x7f),random.randint(0x00, 0xff),random.randint(0x00, 0xff)]; print ':'.join(map(lambda x: '%02x' % x, mac))") + +utils/usrp_burn_mb_eeprom --key=mac-addr0 --val="${mac0}" +utils/usrp_burn_mb_eeprom --key=mac-addr1 --val="${mac1}" + +utils/usrp_burn_mb_eeprom --key=gateway --val="192.168.10.1" + +utils/usrp_burn_mb_eeprom --key=subnet0 --val="255.255.255.0" +utils/usrp_burn_mb_eeprom --key=subnet1 --val="255.255.255.0" +utils/usrp_burn_mb_eeprom --key=subnet2 --val="255.255.255.0" +utils/usrp_burn_mb_eeprom --key=subnet3 --val="255.255.255.0" + +utils/usrp_burn_mb_eeprom --key=ip-addr0 --val="192.168.10.2" +utils/usrp_burn_mb_eeprom --key=ip-addr1 --val="192.168.10.3" +utils/usrp_burn_mb_eeprom --key=ip-addr2 --val="192.168.10.2" +utils/usrp_burn_mb_eeprom --key=ip-addr3 --val="192.168.10.3" diff --git a/host/lib/usrp/x300/x300_io_impl.cpp b/host/lib/usrp/x300/x300_io_impl.cpp new file mode 100644 index 000000000..00a31b8d6 --- /dev/null +++ b/host/lib/usrp/x300/x300_io_impl.cpp @@ -0,0 +1,652 @@ +// +// Copyright 2013-2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// 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 "x300_regs.hpp" +#include "x300_impl.hpp" +#include "validate_subdev_spec.hpp" +#include "../../transport/super_recv_packet_handler.hpp" +#include "../../transport/super_send_packet_handler.hpp" +#include <uhd/transport/nirio_zero_copy.hpp> +#include "async_packet_handler.hpp" +#include <uhd/transport/bounded_buffer.hpp> +#include <boost/bind.hpp> +#include <uhd/utils/tasks.hpp> +#include <uhd/utils/log.hpp> +#include <boost/foreach.hpp> +#include <boost/make_shared.hpp> + +using namespace uhd; +using namespace uhd::usrp; +using namespace uhd::transport; + +/*********************************************************************** + * update streamer rates + **********************************************************************/ +void x300_impl::update_tick_rate(mboard_members_t &mb, const double rate) +{ + BOOST_FOREACH(const size_t &dspno, mb.rx_streamers.keys()) + { + boost::shared_ptr<sph::recv_packet_streamer> my_streamer = + boost::dynamic_pointer_cast<sph::recv_packet_streamer>(mb.rx_streamers[dspno].lock()); + if (my_streamer) my_streamer->set_tick_rate(rate); + } + BOOST_FOREACH(const size_t &dspno, mb.tx_streamers.keys()) + { + boost::shared_ptr<sph::send_packet_streamer> my_streamer = + boost::dynamic_pointer_cast<sph::send_packet_streamer>(mb.tx_streamers[dspno].lock()); + if (my_streamer) my_streamer->set_tick_rate(rate); + } +} + +void x300_impl::update_rx_samp_rate(mboard_members_t &mb, const size_t dspno, const double rate) +{ + if (not mb.rx_streamers.has_key(dspno)) return; + boost::shared_ptr<sph::recv_packet_streamer> my_streamer = + boost::dynamic_pointer_cast<sph::recv_packet_streamer>(mb.rx_streamers[dspno].lock()); + if (not my_streamer) return; + my_streamer->set_samp_rate(rate); + const double adj = mb.radio_perifs[dspno].ddc->get_scaling_adjustment(); + my_streamer->set_scale_factor(adj); +} + +void x300_impl::update_tx_samp_rate(mboard_members_t &mb, const size_t dspno, const double rate) +{ + if (not mb.tx_streamers.has_key(dspno)) return; + boost::shared_ptr<sph::send_packet_streamer> my_streamer = + boost::dynamic_pointer_cast<sph::send_packet_streamer>(mb.tx_streamers[dspno].lock()); + if (not my_streamer) return; + my_streamer->set_samp_rate(rate); + const double adj = mb.radio_perifs[dspno].duc->get_scaling_adjustment(); + my_streamer->set_scale_factor(adj); +} + +/*********************************************************************** + * Setup dboard muxing for IQ + **********************************************************************/ +void x300_impl::update_rx_subdev_spec(const size_t mb_i, const subdev_spec_t &spec) +{ + const std::string mb_name = boost::lexical_cast<std::string>(mb_i); + fs_path root = "/mboards/"+mb_name+"/dboards"; + + //sanity checking + validate_subdev_spec(_tree, spec, "rx", mb_name); + UHD_ASSERT_THROW(spec.size() <= 2); + if (spec.size() > 0) UHD_ASSERT_THROW(spec[0].db_name == "A"); + if (spec.size() > 1) UHD_ASSERT_THROW(spec[1].db_name == "B"); + + //setup mux for this spec + for (size_t i = 0; i < 2; i++) + { + //extract db name + const std::string db_name = (i == 0)? "A" : "B"; + if (i < spec.size()) UHD_ASSERT_THROW(spec[i].db_name == db_name); + + //extract fe name + std::string fe_name; + if (i < spec.size()) fe_name = spec[i].sd_name; + else fe_name = _tree->list(root / db_name / "rx_frontends").front(); + + //extract connection + const std::string conn = _tree->access<std::string>(root / db_name / "rx_frontends" / fe_name / "connection").get(); + + //swap condition + const bool fe_swapped = (conn == "QI" or conn == "Q"); + _mb[mb_i].radio_perifs[i].ddc->set_mux(conn, fe_swapped); + //see usrp/io_impl.cpp if multiple DSPs share the frontend: + _mb[mb_i].radio_perifs[i].rx_fe->set_mux(fe_swapped); + } + + _mb[mb_i].rx_fe_map = spec; +} + +void x300_impl::update_tx_subdev_spec(const size_t mb_i, const subdev_spec_t &spec) +{ + const std::string mb_name = boost::lexical_cast<std::string>(mb_i); + fs_path root = "/mboards/"+mb_name+"/dboards"; + + //sanity checking + validate_subdev_spec(_tree, spec, "tx", mb_name); + UHD_ASSERT_THROW(spec.size() <= 2); + if (spec.size() > 0) UHD_ASSERT_THROW(spec[0].db_name == "A"); + if (spec.size() > 1) UHD_ASSERT_THROW(spec[1].db_name == "B"); + + //set the mux for this spec + for (size_t i = 0; i < 2; i++) + { + //extract db name + const std::string db_name = (i == 0)? "A" : "B"; + if (i < spec.size()) UHD_ASSERT_THROW(spec[i].db_name == db_name); + + //extract fe name + std::string fe_name; + if (i < spec.size()) fe_name = spec[i].sd_name; + else fe_name = _tree->list(root / db_name / "tx_frontends").front(); + + //extract connection + const std::string conn = _tree->access<std::string>(root / db_name / "tx_frontends" / fe_name / "connection").get(); + + //swap condition + _mb[mb_i].radio_perifs[i].tx_fe->set_mux(conn); + + } + + _mb[mb_i].tx_fe_map = spec; +} + +/*********************************************************************** + * VITA stuff + **********************************************************************/ +static void x300_if_hdr_unpack_be( + const boost::uint32_t *packet_buff, + vrt::if_packet_info_t &if_packet_info +){ + if_packet_info.link_type = vrt::if_packet_info_t::LINK_TYPE_CHDR; + return vrt::if_hdr_unpack_be(packet_buff, if_packet_info); +} + +static void x300_if_hdr_pack_be( + boost::uint32_t *packet_buff, + vrt::if_packet_info_t &if_packet_info +){ + if_packet_info.link_type = vrt::if_packet_info_t::LINK_TYPE_CHDR; + return vrt::if_hdr_pack_be(packet_buff, if_packet_info); +} + +static void x300_if_hdr_unpack_le( + const boost::uint32_t *packet_buff, + vrt::if_packet_info_t &if_packet_info +){ + if_packet_info.link_type = vrt::if_packet_info_t::LINK_TYPE_CHDR; + return vrt::if_hdr_unpack_le(packet_buff, if_packet_info); +} + +static void x300_if_hdr_pack_le( + boost::uint32_t *packet_buff, + vrt::if_packet_info_t &if_packet_info +){ + if_packet_info.link_type = vrt::if_packet_info_t::LINK_TYPE_CHDR; + return vrt::if_hdr_pack_le(packet_buff, if_packet_info); +} + +/*********************************************************************** + * RX flow control handler + **********************************************************************/ +static size_t get_rx_flow_control_window(size_t frame_size, size_t sw_buff_size, const device_addr_t& rx_args) +{ + double fullness_factor = rx_args.cast<double>("recv_buff_fullness", X300_RX_SW_BUFF_FULL_FACTOR); + + if (fullness_factor < 0.01 || fullness_factor > 1) { + throw uhd::value_error("recv_buff_fullness must be between 0.01 and 1 inclusive (1% to 100%)"); + } + + size_t window_in_pkts = (static_cast<size_t>(sw_buff_size * fullness_factor) / frame_size); + if (window_in_pkts == 0) { + throw uhd::value_error("recv_buff_size must be larger than the recv_frame_size."); + } + return window_in_pkts; +} + +static void handle_rx_flowctrl(const boost::uint32_t sid, zero_copy_if::sptr xport, bool big_endian, boost::shared_ptr<boost::uint32_t> seq32_state, const size_t last_seq) +{ + managed_send_buffer::sptr buff = xport->get_send_buff(0.0); + if (not buff) + { + throw uhd::runtime_error("handle_rx_flowctrl timed out getting a send buffer"); + } + boost::uint32_t *pkt = buff->cast<boost::uint32_t *>(); + + //recover seq32 + boost::uint32_t &seq32 = *seq32_state; + const size_t seq12 = seq32 & 0xfff; + if (last_seq < seq12) seq32 += (1 << 12); + seq32 &= ~0xfff; + seq32 |= last_seq; + + //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 = seq32; + packet_info.sob = false; + packet_info.eob = false; + packet_info.sid = sid; + packet_info.has_sid = true; + packet_info.has_cid = false; + packet_info.has_tsi = false; + packet_info.has_tsf = false; + packet_info.has_tlr = false; + + //load header + if (big_endian) + x300_if_hdr_pack_be(pkt, packet_info); + else + x300_if_hdr_pack_le(pkt, packet_info); + + //load payload + pkt[packet_info.num_header_words32+0] = uhd::htonx<boost::uint32_t>(0); + pkt[packet_info.num_header_words32+1] = uhd::htonx<boost::uint32_t>(seq32); + + //send the buffer over the interface + buff->commit(sizeof(boost::uint32_t)*(packet_info.num_packet_words32)); +} + + +/*********************************************************************** + * TX flow control handler + **********************************************************************/ +struct x300_tx_fc_guts_t +{ + x300_tx_fc_guts_t(void): + stream_channel(0), + device_channel(0), + last_seq_out(0), + last_seq_ack(0), + seq_queue(1){} + size_t stream_channel; + size_t device_channel; + size_t last_seq_out; + size_t last_seq_ack; + bounded_buffer<size_t> seq_queue; + boost::shared_ptr<x300_impl::async_md_type> async_queue; + boost::shared_ptr<x300_impl::async_md_type> old_async_queue; +}; + +static size_t get_tx_flow_control_window(size_t frame_size, const device_addr_t& tx_args) +{ + double hw_buff_size = tx_args.cast<double>("send_buff_size", X300_TX_HW_BUFF_SIZE); + size_t window_in_pkts = (static_cast<size_t>(hw_buff_size) / frame_size); + if (window_in_pkts == 0) { + throw uhd::value_error("send_buff_size must be larger than the send_frame_size."); + } + return window_in_pkts; +} + +static void handle_tx_async_msgs(boost::shared_ptr<x300_tx_fc_guts_t> guts, zero_copy_if::sptr xport, bool big_endian, x300_clock_ctrl::sptr clock) +{ + managed_recv_buffer::sptr buff = xport->get_recv_buff(); + if (not buff) return; + + //extract packet info + vrt::if_packet_info_t if_packet_info; + if_packet_info.num_packet_words32 = buff->size()/sizeof(boost::uint32_t); + const boost::uint32_t *packet_buff = buff->cast<const boost::uint32_t *>(); + + //unpacking can fail + uint32_t (*endian_conv)(uint32_t) = uhd::ntohx; + try + { + if (big_endian) + { + x300_if_hdr_unpack_be(packet_buff, if_packet_info); + endian_conv = uhd::ntohx; + } + else + { + x300_if_hdr_unpack_le(packet_buff, if_packet_info); + endian_conv = uhd::wtohx; + } + } + catch(const std::exception &ex) + { + UHD_MSG(error) << "Error parsing async message packet: " << ex.what() << std::endl; + return; + } + + //catch the flow control packets and react + if (endian_conv(packet_buff[if_packet_info.num_header_words32+0]) == 0) + { + const size_t seq = endian_conv(packet_buff[if_packet_info.num_header_words32+1]); + guts->seq_queue.push_with_haste(seq); + return; + } + + //fill in the async metadata + async_metadata_t metadata; + load_metadata_from_buff( + endian_conv, metadata, if_packet_info, packet_buff, + clock->get_master_clock_rate(), guts->stream_channel); + guts->async_queue->push_with_pop_on_full(metadata); + metadata.channel = guts->device_channel; + guts->old_async_queue->push_with_pop_on_full(metadata); + standard_async_msg_prints(metadata); +} + +static managed_send_buffer::sptr get_tx_buff_with_flowctrl( + task::sptr /*holds ref*/, + boost::shared_ptr<x300_tx_fc_guts_t> guts, + zero_copy_if::sptr xport, + size_t fc_pkt_window, + const double timeout +){ + while (true) + { + const size_t delta = (guts->last_seq_out & 0xfff) - (guts->last_seq_ack & 0xfff); + if ((delta & 0xfff) <= fc_pkt_window) break; + + const bool ok = guts->seq_queue.pop_with_timed_wait(guts->last_seq_ack, timeout); + if (not ok) return managed_send_buffer::sptr(); //timeout waiting for flow control + } + + managed_send_buffer::sptr buff = xport->get_send_buff(timeout); + if (buff) guts->last_seq_out++; //update seq, this will actually be a send + return buff; +} + +/*********************************************************************** + * Async Data + **********************************************************************/ +bool x300_impl::recv_async_msg( + async_metadata_t &async_metadata, double timeout +){ + return _async_md->pop_with_timed_wait(async_metadata, timeout); +} + +/*********************************************************************** + * Receive streamer + **********************************************************************/ +rx_streamer::sptr x300_impl::get_rx_stream(const uhd::stream_args_t &args_) +{ + boost::mutex::scoped_lock lock(_transport_setup_mutex); + stream_args_t args = args_; + + //setup defaults for unspecified values + if (not args.otw_format.empty() and args.otw_format != "sc16") + { + throw uhd::value_error("x300_impl::get_rx_stream only supports otw_format sc16"); + } + args.otw_format = "sc16"; + args.channels = args.channels.empty()? std::vector<size_t>(1, 0) : args.channels; + + boost::shared_ptr<sph::recv_packet_streamer> my_streamer; + for (size_t stream_i = 0; stream_i < args.channels.size(); stream_i++) + { + const size_t chan = args.channels[stream_i]; + size_t mb_chan = chan, mb_index = 0; + BOOST_FOREACH(mboard_members_t &mb, _mb) + { + if (mb_chan < mb.rx_fe_map.size()) break; + else mb_chan -= mb.rx_fe_map.size(); + mb_index++; + } + mboard_members_t &mb = _mb[mb_index]; + radio_perifs_t &perif = mb.radio_perifs[mb_chan]; + + //setup the dsp transport hints (default to a large recv buff) + device_addr_t device_addr = mb.recv_args; + if (not device_addr.has_key("recv_buff_size")) + { + if (mb.xport_path != "nirio") { + //For the ethernet transport, the buffer has to be set before creating + //the transport because it is independent of the frame size and # frames + //For nirio, the buffer size is not configurable by the user + #if defined(UHD_PLATFORM_MACOS) || defined(UHD_PLATFORM_BSD) + //limit buffer resize on macos or it will error + device_addr["recv_buff_size"] = boost::lexical_cast<std::string>(X300_RX_SW_BUFF_SIZE_ETH_MACOS); + #elif defined(UHD_PLATFORM_LINUX) || defined(UHD_PLATFORM_WIN32) + //set to half-a-second of buffering at max rate + device_addr["recv_buff_size"] = boost::lexical_cast<std::string>(X300_RX_SW_BUFF_SIZE_ETH); + #endif + } + } + + //allocate sid and create transport + uint8_t dest = (mb_chan == 0)? X300_XB_DST_R0 : X300_XB_DST_R1; + boost::uint32_t data_sid; + UHD_LOG << "creating rx stream " << device_addr.to_string() << std::endl; + both_xports_t xport = this->make_transport(mb_index, dest, X300_RADIO_DEST_PREFIX_RX, device_addr, data_sid); + UHD_LOG << boost::format("data_sid = 0x%08x, actual recv_buff_size = %d\n") % data_sid % xport.recv_buff_size << std::endl; + + //calculate packet size + static const size_t hdr_size = 0 + + vrt::num_vrl_words32*sizeof(boost::uint32_t) + + vrt::max_if_hdr_words32*sizeof(boost::uint32_t) + + sizeof(vrt::if_packet_info_t().tlr) //forced to have trailer + - sizeof(vrt::if_packet_info_t().cid) //no class id ever used + - sizeof(vrt::if_packet_info_t().tsi) //no int time ever used + ; + const size_t bpp = xport.recv->get_recv_frame_size() - hdr_size; + const size_t bpi = convert::get_bytes_per_item(args.otw_format); + const size_t spp = unsigned(args.args.cast<double>("spp", bpp/bpi)); + + //make the new streamer given the samples per packet + if (not my_streamer) my_streamer = boost::make_shared<sph::recv_packet_streamer>(spp); + my_streamer->resize(args.channels.size()); + + //init some streamer stuff + std::string conv_endianness; + if (mb.if_pkt_is_big_endian) { + my_streamer->set_vrt_unpacker(&x300_if_hdr_unpack_be); + conv_endianness = "be"; + } else { + my_streamer->set_vrt_unpacker(&x300_if_hdr_unpack_le); + conv_endianness = "le"; + } + + //set the converter + uhd::convert::id_type id; + id.input_format = args.otw_format + "_item32_" + conv_endianness; + id.num_inputs = 1; + id.output_format = args.cpu_format; + id.num_outputs = 1; + my_streamer->set_converter(id); + + perif.framer->clear(); + perif.framer->set_nsamps_per_packet(spp); //seems to be a good place to set this + perif.framer->set_sid((data_sid << 16) | (data_sid >> 16)); + perif.framer->setup(args); + perif.ddc->setup(args); + + //flow control setup + const size_t fc_window = get_rx_flow_control_window(xport.recv->get_recv_frame_size(), xport.recv_buff_size, device_addr); + const size_t fc_handle_window = std::max<size_t>(1, fc_window / X300_RX_FC_REQUEST_FREQ); + + UHD_LOG << "RX Flow Control Window = " << fc_window << ", RX Flow Control Handler Window = " << fc_handle_window << std::endl; + + perif.framer->configure_flow_control(fc_window); + + boost::shared_ptr<boost::uint32_t> seq32(new boost::uint32_t(0)); + //Give the streamer a functor to get the recv_buffer + //bind requires a zero_copy_if::sptr to add a streamer->xport lifetime dependency + my_streamer->set_xport_chan_get_buff( + stream_i, + boost::bind(&zero_copy_if::get_recv_buff, xport.recv, _1), + true /*flush*/ + ); + //Give the streamer a functor to handle overflows + //bind requires a weak_ptr to break the a streamer->streamer circular dependency + //Using "this" is OK because we know that x300_impl will outlive the streamer + my_streamer->set_overflow_handler( + stream_i, + boost::bind(&x300_impl::handle_overflow, this, boost::ref(perif), boost::weak_ptr<uhd::rx_streamer>(my_streamer)) + ); + //Give the streamer a functor to send flow control messages + //handle_rx_flowctrl is static and has no lifetime issues + my_streamer->set_xport_handle_flowctrl( + stream_i, boost::bind(&handle_rx_flowctrl, data_sid, xport.send, mb.if_pkt_is_big_endian, seq32, _1), + fc_handle_window, + true/*init*/ + ); + //Give the streamer a functor issue stream cmd + //bind requires a rx_vita_core_3000::sptr to add a streamer->framer lifetime dependency + my_streamer->set_issue_stream_cmd( + stream_i, boost::bind(&rx_vita_core_3000::issue_stream_command, perif.framer, _1) + ); + + //Store a weak pointer to prevent a streamer->x300_impl->streamer circular dependency + mb.rx_streamers[mb_chan] = boost::weak_ptr<sph::recv_packet_streamer>(my_streamer); + + //sets all tick and samp rates on this streamer + const fs_path mb_path = "/mboards/"+boost::lexical_cast<std::string>(mb_index); + _tree->access<double>(mb_path / "tick_rate").update(); + _tree->access<double>(mb_path / "rx_dsps" / boost::lexical_cast<std::string>(mb_chan) / "rate" / "value").update(); + } + + return my_streamer; +} + +void x300_impl::handle_overflow(x300_impl::radio_perifs_t &perif, boost::weak_ptr<uhd::rx_streamer> streamer) +{ + boost::shared_ptr<sph::recv_packet_streamer> my_streamer = + boost::dynamic_pointer_cast<sph::recv_packet_streamer>(streamer.lock()); + if (not my_streamer) return; //If the rx_streamer has expired then overflow handling makes no sense. + + if (my_streamer->get_num_channels() == 1) + { + perif.framer->handle_overflow(); + return; + } + + ///////////////////////////////////////////////////////////// + // MIMO overflow recovery time + ///////////////////////////////////////////////////////////// + //find out if we were in continuous mode before stopping + const bool in_continuous_streaming_mode = perif.framer->in_continuous_streaming_mode(); + //stop streaming + my_streamer->issue_stream_cmd(stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS); + //flush transports + my_streamer->flush_all(0.001); + //restart streaming + if (in_continuous_streaming_mode) + { + stream_cmd_t stream_cmd(stream_cmd_t::STREAM_MODE_START_CONTINUOUS); + stream_cmd.stream_now = false; + stream_cmd.time_spec = perif.time64->get_time_now() + time_spec_t(0.01); + my_streamer->issue_stream_cmd(stream_cmd); + } +} + +/*********************************************************************** + * Transmit streamer + **********************************************************************/ +tx_streamer::sptr x300_impl::get_tx_stream(const uhd::stream_args_t &args_) +{ + boost::mutex::scoped_lock lock(_transport_setup_mutex); + stream_args_t args = args_; + + //setup defaults for unspecified values + if (not args.otw_format.empty() and args.otw_format != "sc16") + { + throw uhd::value_error("x300_impl::get_rx_stream only supports otw_format sc16"); + } + args.otw_format = "sc16"; + args.channels = args.channels.empty()? std::vector<size_t>(1, 0) : args.channels; + + //shared async queue for all channels in streamer + boost::shared_ptr<async_md_type> async_md(new async_md_type(1000/*messages deep*/)); + + boost::shared_ptr<sph::send_packet_streamer> my_streamer; + for (size_t stream_i = 0; stream_i < args.channels.size(); stream_i++) + { + const size_t chan = args.channels[stream_i]; + size_t mb_chan = chan, mb_index = 0; + BOOST_FOREACH(mboard_members_t &mb, _mb) + { + if (mb_chan < mb.tx_fe_map.size()) break; + else mb_chan -= mb.tx_fe_map.size(); + mb_index++; + } + mboard_members_t &mb = _mb[mb_index]; + radio_perifs_t &perif = mb.radio_perifs[mb_chan]; + + //setup the dsp transport hints (TODO) + device_addr_t device_addr = mb.send_args; + + //allocate sid and create transport + uint8_t dest = (mb_chan == 0)? X300_XB_DST_R0 : X300_XB_DST_R1; + boost::uint32_t data_sid; + UHD_LOG << "creating tx stream " << device_addr.to_string() << std::endl; + both_xports_t xport = this->make_transport(mb_index, dest, X300_RADIO_DEST_PREFIX_TX, device_addr, data_sid); + UHD_LOG << boost::format("data_sid = 0x%08x\n") % data_sid << std::endl; + + //calculate packet size + static const size_t hdr_size = 0 + + vrt::num_vrl_words32*sizeof(boost::uint32_t) + + vrt::max_if_hdr_words32*sizeof(boost::uint32_t) + //+ sizeof(vrt::if_packet_info_t().tlr) //forced to have trailer + - sizeof(vrt::if_packet_info_t().cid) //no class id ever used + - sizeof(vrt::if_packet_info_t().tsi) //no int time ever used + ; + const size_t bpp = xport.send->get_send_frame_size() - hdr_size; + const size_t bpi = convert::get_bytes_per_item(args.otw_format); + const size_t spp = unsigned(args.args.cast<double>("spp", bpp/bpi)); + + //make the new streamer given the samples per packet + if (not my_streamer) my_streamer = boost::make_shared<sph::send_packet_streamer>(spp); + my_streamer->resize(args.channels.size()); + + std::string conv_endianness; + if (mb.if_pkt_is_big_endian) { + my_streamer->set_vrt_packer(&x300_if_hdr_pack_be); + conv_endianness = "be"; + } else { + my_streamer->set_vrt_packer(&x300_if_hdr_pack_le); + conv_endianness = "le"; + } + + //set the converter + uhd::convert::id_type id; + id.input_format = args.cpu_format; + id.num_inputs = 1; + id.output_format = args.otw_format + "_item32_" + conv_endianness; + id.num_outputs = 1; + my_streamer->set_converter(id); + + perif.deframer->clear(); + perif.deframer->setup(args); + perif.duc->setup(args); + + //flow control setup + size_t fc_window = get_tx_flow_control_window(xport.send->get_send_frame_size(), device_addr); //In packets + const size_t fc_handle_window = std::max<size_t>(1, fc_window/X300_TX_FC_RESPONSE_FREQ); + + UHD_LOG << "TX Flow Control Window = " << fc_window << ", TX Flow Control Handler Window = " << fc_handle_window << std::endl; + + perif.deframer->configure_flow_control(0/*cycs off*/, fc_handle_window); + boost::shared_ptr<x300_tx_fc_guts_t> guts(new x300_tx_fc_guts_t()); + guts->stream_channel = stream_i; + guts->device_channel = chan; + guts->async_queue = async_md; + guts->old_async_queue = _async_md; + task::sptr task = task::make(boost::bind(&handle_tx_async_msgs, guts, xport.recv, mb.if_pkt_is_big_endian, mb.clock)); + + //Give the streamer a functor to get the send buffer + //get_tx_buff_with_flowctrl is static so bind has no lifetime issues + //xport.send (sptr) is required to add streamer->data-transport lifetime dependency + //task (sptr) is required to add a streamer->async-handler lifetime dependency + my_streamer->set_xport_chan_get_buff( + stream_i, + boost::bind(&get_tx_buff_with_flowctrl, task, guts, xport.send, fc_window, _1) + ); + //Give the streamer a functor handled received async messages + my_streamer->set_async_receiver( + boost::bind(&async_md_type::pop_with_timed_wait, async_md, _1, _2) + ); + my_streamer->set_xport_chan_sid(stream_i, true, data_sid); + my_streamer->set_enable_trailer(false); //TODO not implemented trailer support yet + + //Store a weak pointer to prevent a streamer->x300_impl->streamer circular dependency + mb.tx_streamers[mb_chan] = boost::weak_ptr<sph::send_packet_streamer>(my_streamer); + + //sets all tick and samp rates on this streamer + const fs_path mb_path = "/mboards/"+boost::lexical_cast<std::string>(mb_index); + _tree->access<double>(mb_path / "tick_rate").update(); + _tree->access<double>(mb_path / "tx_dsps" / boost::lexical_cast<std::string>(mb_chan) / "rate" / "value").update(); + } + + return my_streamer; +} diff --git a/host/lib/usrp/x300/x300_regs.hpp b/host/lib/usrp/x300/x300_regs.hpp new file mode 100644 index 000000000..e4ae76a38 --- /dev/null +++ b/host/lib/usrp/x300/x300_regs.hpp @@ -0,0 +1,152 @@ +// +// Copyright 2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_X300_REGS_HPP +#define INCLUDED_X300_REGS_HPP + +#include <boost/cstdint.hpp> + +#define TOREG(x) ((x)*4) + +#define localparam static const int + +localparam SR_DACSYNC = 5; +localparam SR_LOOPBACK = 6; +localparam SR_TEST = 7; +localparam SR_SPI = 8; +localparam SR_GPIO = 16; +localparam SR_MISC_OUTS = 24; +localparam SR_READBACK = 32; +localparam SR_TX_CTRL = 64; +localparam SR_RX_CTRL = 96; +localparam SR_TIME = 128; +localparam SR_RX_DSP = 144; +localparam SR_TX_DSP = 184; +localparam SR_LEDS = 196; +localparam SR_FP_GPIO = 200; +localparam SR_RX_FRONT = 208; +localparam SR_TX_FRONT = 216; + +localparam RB32_GPIO = 0; +localparam RB32_SPI = 4; +localparam RB64_TIME_NOW = 8; +localparam RB64_TIME_PPS = 16; +localparam RB32_TEST = 24; +localparam RB32_RX = 28; +localparam RB32_FP_GPIO = 32; + +localparam BL_ADDRESS = 0; +localparam BL_DATA = 1; + +//wishbone settings map - relevant to host code +#define SET0_BASE 0xa000 +#define SETXB_BASE 0xb000 +#define BOOT_LDR_BASE 0xFA00 +#define I2C0_BASE 0xfe00 +#define I2C1_BASE 0xff00 +#define SR_ADDR(base, offset) ((base) + (offset)*4) + +localparam ZPU_SR_LEDS = 00; +localparam ZPU_SR_PHY_RST = 01; +localparam ZPU_SR_CLOCK_CTRL = 02; +localparam ZPU_SR_XB_LOCAL = 03; +localparam ZPU_SR_SPI = 32; +localparam ZPU_SR_ETHINT0 = 40; +localparam ZPU_SR_ETHINT1 = 56; + +localparam ZPU_RB_SPI = 2; +localparam ZPU_RB_CLK_STATUS = 3; +localparam ZPU_RB_COMPAT_NUM = 6; +localparam ZPU_RB_ETH_TYPE0 = 4; +localparam ZPU_RB_ETH_TYPE1 = 5; + +//spi slaves on radio +#define DB_DAC_SEN (1 << 7) +#define DB_ADC_SEN (1 << 6) +#define DB_RX_LSADC_SEN (1 << 5) +#define DB_RX_LSDAC_SEN (1 << 4) +#define DB_TX_LSADC_SEN (1 << 3) +#define DB_TX_LSDAC_SEN (1 << 2) +#define DB_RX_SEN (1 << 1) +#define DB_TX_SEN (1 << 0) + +//------------------------------------------------------------------- +// PCIe Registers +//------------------------------------------------------------------- + +static const uint32_t X300_PCIE_VID = 0x1093; +static const uint32_t X300_PCIE_PID = 0xC4C4; +static const uint32_t X300_USRP_PCIE_SSID = 0x7736; +static const uint32_t X310_USRP_PCIE_SSID = 0x76CA; +static const uint32_t X310_2940R_PCIE_SSID = 0x772B; +static const uint32_t X310_2942R_PCIE_SSID = 0x772C; +static const uint32_t X310_2943R_PCIE_SSID = 0x772D; +static const uint32_t X310_2944R_PCIE_SSID = 0x772E; +static const uint32_t X310_2950R_PCIE_SSID = 0x772F; +static const uint32_t X310_2952R_PCIE_SSID = 0x7730; +static const uint32_t X310_2953R_PCIE_SSID = 0x7731; +static const uint32_t X310_2954R_PCIE_SSID = 0x7732; + +static const uint32_t FPGA_X3xx_SIG_VALUE = 0x58333030; + +static const uint32_t PCIE_FPGA_ADDR_BASE = 0xC0000; +#define PCIE_FPGA_REG(X) (PCIE_FPGA_ADDR_BASE + X) + +static const uint32_t FPGA_PCIE_SIG_REG = PCIE_FPGA_REG(0x0000); +static const uint32_t FPGA_CNTR_LO_REG = PCIE_FPGA_REG(0x0004); +static const uint32_t FPGA_CNTR_HI_REG = PCIE_FPGA_REG(0x0008); +static const uint32_t FPGA_CNTR_FREQ_REG = PCIE_FPGA_REG(0x000C); +static const uint32_t FPGA_USR_SIG_REG_BASE = PCIE_FPGA_REG(0x0030); +static const uint32_t FPGA_USR_SIG_REG_SIZE = 16; + +static const uint32_t PCIE_TX_DMA_REG_BASE = PCIE_FPGA_REG(0x0200); +static const uint32_t PCIE_RX_DMA_REG_BASE = PCIE_FPGA_REG(0x0400); + +static const uint32_t DMA_REG_GRP_SIZE = 16; +static const uint32_t DMA_CTRL_STATUS_REG = 0x0; +static const uint32_t DMA_FRAME_SIZE_REG = 0x4; +static const uint32_t DMA_SAMPLE_COUNT_REG = 0x8; +static const uint32_t DMA_PKT_COUNT_REG = 0xC; + +#define PCIE_TX_DMA_REG(REG, CHAN) (PCIE_TX_DMA_REG_BASE + (CHAN*DMA_REG_GRP_SIZE) + REG) +#define PCIE_RX_DMA_REG(REG, CHAN) (PCIE_RX_DMA_REG_BASE + (CHAN*DMA_REG_GRP_SIZE) + REG) + +static const uint32_t DMA_CTRL_RESET = 1; +static const uint32_t DMA_CTRL_SW_BUF_U64 = (3 << 4); +static const uint32_t DMA_CTRL_SW_BUF_U32 = (2 << 4); +static const uint32_t DMA_CTRL_SW_BUF_U16 = (1 << 4); +static const uint32_t DMA_CTRL_SW_BUF_U8 = (0 << 4); +static const uint32_t DMA_STATUS_ERROR = 1; + +static const uint32_t PCIE_ROUTER_REG_BASE = PCIE_FPGA_REG(0x0500); +#define PCIE_ROUTER_REG(X) (PCIE_ROUTER_REG_BASE + X) + +static const uint32_t PCIE_ZPU_DATA_BASE = 0x30000; +static const uint32_t PCIE_ZPU_READ_BASE = 0x20000; //Trig and Status share the same base +static const uint32_t PCIE_ZPU_STATUS_BASE = 0x20000; + +#define PCIE_ZPU_DATA_REG(X) (PCIE_FPGA_REG(PCIE_ZPU_DATA_BASE) + X) +#define PCIE_ZPU_READ_REG(X) (PCIE_FPGA_REG(PCIE_ZPU_READ_BASE) + X) +#define PCIE_ZPU_STATUS_REG(X) (PCIE_FPGA_REG(PCIE_ZPU_STATUS_BASE) + X) + +static const uint32_t PCIE_ZPU_READ_START = 0x0; +static const uint32_t PCIE_ZPU_READ_CLOBBER = 0x80000000; +static const uint32_t PCIE_ZPU_STATUS_BUSY = 0x1; +static const uint32_t PCIE_ZPU_STATUS_SUSPENDED = 0x80000000; + + +#endif /* INCLUDED_X300_REGS_HPP */ diff --git a/host/lib/utils/CMakeLists.txt b/host/lib/utils/CMakeLists.txt index a28e1f9ef..2252f3221 100644 --- a/host/lib/utils/CMakeLists.txt +++ b/host/lib/utils/CMakeLists.txt @@ -87,6 +87,7 @@ CHECK_CXX_SOURCE_COMPILES(" UNSET(CMAKE_REQUIRED_LIBRARIES) CHECK_CXX_SOURCE_COMPILES(" + #define WIN32_LEAN_AND_MEAN #include <windows.h> int main(){ LoadLibrary(0); @@ -136,6 +137,7 @@ LIBUHD_APPEND_SOURCES( ${CMAKE_CURRENT_SOURCE_DIR}/log.cpp ${CMAKE_CURRENT_SOURCE_DIR}/msg.cpp ${CMAKE_CURRENT_SOURCE_DIR}/paths.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/platform.cpp ${CMAKE_CURRENT_SOURCE_DIR}/static.cpp ${CMAKE_CURRENT_SOURCE_DIR}/tasks.cpp ${CMAKE_CURRENT_SOURCE_DIR}/thread_priority.cpp diff --git a/host/lib/utils/platform.cpp b/host/lib/utils/platform.cpp new file mode 100644 index 000000000..e2f92039e --- /dev/null +++ b/host/lib/utils/platform.cpp @@ -0,0 +1,58 @@ +// +// 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/platform.hpp> +#include <uhd/config.hpp> +#include <boost/functional/hash.hpp> +#ifdef UHD_PLATFORM_WIN32 +#include <Windows.h> +#else +#include <unistd.h> +#endif + +namespace uhd { + + boost::int32_t get_process_id() { +#ifdef UHD_PLATFORM_WIN32 + return boost::int32_t(GetCurrentProcessId()); +#else + return boost::int32_t(getpid()); +#endif + } + + boost::uint32_t get_host_id() { +#ifdef UHD_PLATFORM_WIN32 + //extract volume serial number + char szVolName[MAX_PATH+1], szFileSysName[MAX_PATH+1]; + DWORD dwSerialNumber, dwMaxComponentLen, dwFileSysFlags; + GetVolumeInformation("C:\\", szVolName, MAX_PATH, + &dwSerialNumber, &dwMaxComponentLen, + &dwFileSysFlags, szFileSysName, sizeof(szFileSysName)); + + return boost::uint32_t(dwSerialNumber); +#else + return boost::uint32_t(gethostid()); +#endif + } + + boost::uint32_t get_process_hash() { + size_t hash = 0; + boost::hash_combine(hash, uhd::get_process_id()); + boost::hash_combine(hash, uhd::get_host_id()); + return boost::uint32_t(hash); + } +} diff --git a/host/tests/convert_test.cpp b/host/tests/convert_test.cpp index 4b0226e3d..8ef1e74cc 100644 --- a/host/tests/convert_test.cpp +++ b/host/tests/convert_test.cpp @@ -50,8 +50,8 @@ template <typename Range> static void loopback( const int prio_in = -1, const int prio_out = -1 ){ - //item32 is largest device type - std::vector<boost::uint32_t> interm(nsamps); + //make this buffer large enough for all test types + std::vector<boost::uint64_t> interm(nsamps); std::vector<const void *> input0(1, &input[0]), input1(1, &interm[0]); std::vector<void *> output0(1, &interm[0]), output1(1, &output[0]); @@ -148,8 +148,8 @@ static void test_convert_types_for_floats( 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)); + MY_CHECK_CLOSE(input[i].real(), output[i].real(), value_type(1./(1 << 14))); + MY_CHECK_CLOSE(input[i].imag(), output[i].imag(), value_type(1./(1 << 14))); } } } @@ -207,6 +207,66 @@ BOOST_AUTO_TEST_CASE(test_convert_types_le_fc64){ } /*********************************************************************** + * Test float to/from sc12 conversion loopback + **********************************************************************/ + +BOOST_AUTO_TEST_CASE(test_convert_types_le_sc12_with_fc32){ + convert::id_type id; + id.input_format = "fc32"; + id.num_inputs = 1; + id.output_format = "sc12_item32_le"; + id.num_outputs = 1; + + //try various lengths to test edge cases + for (size_t nsamps = 1; nsamps < 16; nsamps++){ + test_convert_types_for_floats<fc32_t>(nsamps, id, 1./16); + } +} + +BOOST_AUTO_TEST_CASE(test_convert_types_be_sc12_with_fc32){ + convert::id_type id; + id.input_format = "fc32"; + id.num_inputs = 1; + id.output_format = "sc12_item32_be"; + id.num_outputs = 1; + + //try various lengths to test edge cases + for (size_t nsamps = 1; nsamps < 16; nsamps++){ + test_convert_types_for_floats<fc32_t>(nsamps, id, 1./16); + } +} + +/*********************************************************************** + * Test float to/from fc32 conversion loopback + **********************************************************************/ + +BOOST_AUTO_TEST_CASE(test_convert_types_le_fc32_with_fc32){ + convert::id_type id; + id.input_format = "fc32"; + id.num_inputs = 1; + id.output_format = "fc32_item32_le"; + id.num_outputs = 1; + + //try various lengths to test edge cases + for (size_t nsamps = 1; nsamps < 16; nsamps++){ + test_convert_types_for_floats<fc32_t>(nsamps, id); + } +} + +BOOST_AUTO_TEST_CASE(test_convert_types_be_fc32_with_fc32){ + convert::id_type id; + id.input_format = "fc32"; + id.num_inputs = 1; + id.output_format = "fc32_item32_be"; + id.num_outputs = 1; + + //try various lengths to test edge cases + for (size_t nsamps = 1; nsamps < 16; nsamps++){ + test_convert_types_for_floats<fc32_t>(nsamps, id); + } +} + +/*********************************************************************** * Test float to short conversion loopback **********************************************************************/ BOOST_AUTO_TEST_CASE(test_convert_types_fc32_to_sc16){ diff --git a/host/tests/sph_recv_test.cpp b/host/tests/sph_recv_test.cpp index 316e24779..5dd0761db 100644 --- a/host/tests/sph_recv_test.cpp +++ b/host/tests/sph_recv_test.cpp @@ -227,6 +227,7 @@ BOOST_AUTO_TEST_CASE(test_sph_recv_one_channel_sequence_error){ if (i == NUM_PKTS_TO_TEST/2){ //must get the soft overflow here BOOST_REQUIRE(metadata.error_code == uhd::rx_metadata_t::ERROR_CODE_OVERFLOW); + BOOST_REQUIRE(metadata.out_of_sequence == true); BOOST_CHECK_TS_CLOSE(metadata.time_spec, uhd::time_spec_t::from_ticks(num_accum_samps, SAMP_RATE)); num_accum_samps += 10 + i%10; } @@ -495,6 +496,7 @@ BOOST_AUTO_TEST_CASE(test_sph_recv_multi_channel_sequence_error){ if (i == NUM_PKTS_TO_TEST/2){ //must get the soft overflow here BOOST_REQUIRE(metadata.error_code == uhd::rx_metadata_t::ERROR_CODE_OVERFLOW); + BOOST_REQUIRE(metadata.out_of_sequence == true); BOOST_CHECK_TS_CLOSE(metadata.time_spec, uhd::time_spec_t::from_ticks(num_accum_samps, SAMP_RATE)); num_accum_samps += 10 + i%10; } diff --git a/host/utils/CMakeLists.txt b/host/utils/CMakeLists.txt index abf2a546b..7604a7d37 100644 --- a/host/utils/CMakeLists.txt +++ b/host/utils/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2010-2012 Ettus Research LLC +# Copyright 2010-2013 Ettus Research LLC # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -15,6 +15,8 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. # +SET(CMAKE_C_COMPILE_OBJECT ${CMAKE_CXX_COMPILE_OBJECT}) + ######################################################################## # Utilities that get installed into the runtime path ######################################################################## @@ -25,6 +27,12 @@ SET(util_runtime_sources uhd_cal_tx_dc_offset.cpp uhd_cal_tx_iq_balance.cpp usrp_n2xx_simple_net_burner.cpp + nirio_programmer.cpp +) + +SET(x3xx_burner_sources + usrp_x3xx_fpga_burner.cpp + cdecode.c ) #for each source: build an executable and install @@ -35,6 +43,10 @@ FOREACH(util_source ${util_runtime_sources}) UHD_INSTALL(TARGETS ${util_name} RUNTIME DESTINATION ${RUNTIME_DIR} COMPONENT utilities) ENDFOREACH(util_source) +ADD_EXECUTABLE(usrp_x3xx_fpga_burner ${x3xx_burner_sources}) +TARGET_LINK_LIBRARIES(usrp_x3xx_fpga_burner uhd ${Boost_LIBRARIES}) +UHD_INSTALL(TARGETS usrp_x3xx_fpga_burner RUNTIME DESTINATION ${RUNTIME_DIR} COMPONENT utilities) + ######################################################################## # Utilities that get installed into the share path ######################################################################## @@ -43,7 +55,6 @@ SET(util_share_sources usrp_burn_db_eeprom.cpp usrp_burn_mb_eeprom.cpp ) - IF(ENABLE_USB) LIST(APPEND util_share_sources fx2_init_eeprom.cpp @@ -71,6 +82,7 @@ FOREACH(util_source ${util_share_sources}) ENDFOREACH(util_source) UHD_INSTALL(TARGETS usrp_n2xx_simple_net_burner RUNTIME DESTINATION ${PKG_LIB_DIR}/utils COMPONENT utilities) +UHD_INSTALL(TARGETS usrp_x3xx_fpga_burner RUNTIME DESTINATION ${PKG_LIB_DIR}/utils COMPONENT utilities) #UHD images downloader configuration CONFIGURE_FILE( diff --git a/host/utils/cdecode.c b/host/utils/cdecode.c new file mode 100644 index 000000000..0a9b5c46b --- /dev/null +++ b/host/utils/cdecode.c @@ -0,0 +1,80 @@ +/*
+cdecoder.c - c source to a base64 decoding algorithm implementation
+
+This is part of the libb64 project, and has been placed in the public domain.
+For details, see http://sourceforge.net/projects/libb64
+*/
+
+#include "cdecode.h"
+
+int base64_decode_value(char value_in){
+ static const char decoding[] = {62,-1,-1,-1,63,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-2,-1,-1,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51};
+ static const char decoding_size = sizeof(decoding);
+ value_in -= 43;
+ if ((signed char)value_in < 0 || value_in > decoding_size) return -1;
+ return decoding[(int)value_in];
+}
+
+void base64_init_decodestate(base64_decodestate* state_in){
+ state_in->step = step_a;
+ state_in->plainchar = 0;
+}
+
+size_t base64_decode_block(const char* code_in, const size_t length_in, char* plaintext_out, base64_decodestate* state_in){
+ const char* codechar = code_in;
+ char* plainchar = plaintext_out;
+ char fragment;
+
+ *plainchar = state_in->plainchar;
+
+ switch (state_in->step){
+ while (1){
+ case step_a:
+ do{
+ if (codechar == code_in+length_in){
+ state_in->step = step_a;
+ state_in->plainchar = *plainchar;
+ return plainchar - plaintext_out;
+ }
+ fragment = (char)base64_decode_value(*codechar++);
+ } while ((signed char)fragment < 0);
+ *plainchar = (fragment & 0x03f) << 2;
+
+ case step_b:
+ do{
+ if (codechar == code_in+length_in){
+ state_in->step = step_b;
+ state_in->plainchar = *plainchar;
+ return plainchar - plaintext_out;
+ }
+ fragment = (char)base64_decode_value(*codechar++);
+ } while ((signed char)fragment < 0);
+ *plainchar++ |= (fragment & 0x030) >> 4;
+ *plainchar = (fragment & 0x00f) << 4;
+ case step_c:
+ do{
+ if (codechar == code_in+length_in)
+ {
+ state_in->step = step_c;
+ state_in->plainchar = *plainchar;
+ return plainchar - plaintext_out;
+ }
+ fragment = (char)base64_decode_value(*codechar++);
+ } while ((signed char)fragment < 0);
+ *plainchar++ |= (fragment & 0x03c) >> 2;
+ *plainchar = (fragment & 0x003) << 6;
+ case step_d:
+ do{
+ if (codechar == code_in+length_in){
+ state_in->step = step_d;
+ state_in->plainchar = *plainchar;
+ return plainchar - plaintext_out;
+ }
+ fragment = (char)base64_decode_value(*codechar++);
+ } while ((signed char)fragment < 0);
+ *plainchar++ |= (fragment & 0x03f);
+ }
+ }
+ /* control should not reach here */
+ return plainchar - plaintext_out;
+}
diff --git a/host/utils/cdecode.h b/host/utils/cdecode.h new file mode 100644 index 000000000..e1eee301f --- /dev/null +++ b/host/utils/cdecode.h @@ -0,0 +1,28 @@ +/* +cdecode.h - c header for a base64 decoding algorithm + +This is part of the libb64 project, and has been placed in the public domain. +For details, see http://sourceforge.net/projects/libb64 +*/ + +#ifndef BASE64_CDECODE_H +#define BASE64_CDECODE_H + +#include <stddef.h> + +typedef enum{ + step_a, step_b, step_c, step_d +} base64_decodestep; + +typedef struct{ + base64_decodestep step; + char plainchar; +} base64_decodestate; + +void base64_init_decodestate(base64_decodestate* state_in); + +int base64_decode_value(char value_in); + +size_t base64_decode_block(const char* code_in, const size_t length_in, char* plaintext_out, base64_decodestate* state_in); + +#endif /* BASE64_CDECODE_H */ diff --git a/host/utils/nirio_programmer.cpp b/host/utils/nirio_programmer.cpp new file mode 100644 index 000000000..98db862eb --- /dev/null +++ b/host/utils/nirio_programmer.cpp @@ -0,0 +1,275 @@ + +#include <uhd/transport/nirio/niusrprio_session.h> +#include <uhd/transport/nirio/niriok_proxy.h> +#include <uhd/transport/nirio/nifpga_lvbitx.h> +#include <stdio.h> +#include <stdlib.h> +#include <fcntl.h> +#include <iostream> +#include <fstream> +#include <streambuf> +#include <boost/program_options.hpp> +#include <boost/format.hpp> +#include <boost/thread/thread.hpp> +#include <boost/algorithm/string.hpp> +#include <boost/regex.hpp> + +using namespace uhd::niusrprio; +using namespace uhd::usrprio_rpc; + +class dummy_lvbitx : public nifpga_lvbitx { +public: + dummy_lvbitx(const std::string& fpga_lvbitx_path) : _fpga_lvbitx_path(fpga_lvbitx_path) { + std::ifstream lvbitx_stream(_fpga_lvbitx_path.c_str()); + if (lvbitx_stream.is_open()) { + std::string lvbitx_contents; + lvbitx_stream.seekg(0, std::ios::end); + lvbitx_contents.reserve(static_cast<size_t>(lvbitx_stream.tellg())); + lvbitx_stream.seekg(0, std::ios::beg); + lvbitx_contents.assign((std::istreambuf_iterator<char>(lvbitx_stream)), std::istreambuf_iterator<char>()); + try { + boost::smatch md5_match; + if (boost::regex_search(lvbitx_contents, md5_match, boost::regex("<BitstreamMD5>([a-zA-Z0-9]{32})<\\/BitstreamMD5>", boost::regex::icase))) { + _bitstream_checksum = std::string(md5_match[1].first, md5_match[1].second); + } + boost::to_upper(_bitstream_checksum); + } catch (boost::exception&) { + _bitstream_checksum = ""; + } + try { + boost::smatch sig_match; + if (boost::regex_search(lvbitx_contents, sig_match, boost::regex("<SignatureRegister>([a-zA-Z0-9]{32})<\\/SignatureRegister>", boost::regex::icase))) { + _signature = std::string(sig_match[1].first, sig_match[1].second); + } + boost::to_upper(_signature); + } catch (boost::exception&) { + _signature = ""; + } + } + } + ~dummy_lvbitx() {} + + virtual const char* get_bitfile_path() { return _fpga_lvbitx_path.c_str(); } + virtual const char* get_signature() { return _signature.c_str(); } + virtual const char* get_bitstream_checksum() { return _bitstream_checksum.c_str(); } + + virtual size_t get_input_fifo_count() { return 0; } + virtual const char** get_input_fifo_names() { return NULL; } + + virtual size_t get_output_fifo_count() { return 0; } + virtual const char** get_output_fifo_names() { return NULL; } + + virtual size_t get_control_count() { return 0; } + virtual const char** get_control_names() { return NULL; } + + virtual size_t get_indicator_count() { return 0; } + virtual const char** get_indicator_names() { return NULL; } + + virtual void init_register_info(nirio_register_info_vtr& vtr) { vtr.clear(); } + virtual void init_fifo_info(nirio_fifo_info_vtr& vtr) { vtr.clear(); } + +private: + std::string _fpga_lvbitx_path; + std::string _bitstream_checksum; + std::string _signature; +}; + +int main(int argc, char *argv[]) +{ + nirio_status status = NiRio_Status_Success; + + //Setup the program options + uint32_t interface_num, peek_addr, poke_addr, poke_data; + std::string rpc_port, fpga_lvbitx_path, flash_path, peek_tokens_str, poke_tokens_str; + + namespace po = boost::program_options; + po::options_description desc("Allowed options"); + desc.add_options() + ("help", "help message") + ("interface", po::value<uint32_t>(&interface_num)->default_value(0), "The interface number to communicate with.") + ("port", po::value<std::string>(&rpc_port)->default_value("5444"), "Port to communicate with RPC server.") + ("fpga", po::value<std::string>(&fpga_lvbitx_path)->default_value(""), "The absolute path to the LVBITX file to download to the FPGA.") + ("flash", po::value<std::string>(&flash_path)->default_value(""), "The path to the image to download to the flash OR 'erase' to erase the FPGA image from flash.") + ("peek", po::value<std::string>(&peek_tokens_str)->default_value(""), "Peek32.") + ("poke", po::value<std::string>(&poke_tokens_str)->default_value(""), "Poke32.") + ("stats", "Dump interface and DMA stats.") + ; + 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("USRP-NIRIO-Programmer\n\n %s") % desc << std::endl; + return ~0; + } + + std::string resource_name = boost::str(boost::format("RIO%u") % interface_num); + + //Download LVBITX image + if (fpga_lvbitx_path != "") + { + printf("Downloading image %s to FPGA as %s...", fpga_lvbitx_path.c_str(), resource_name.c_str()); + fflush(stdout); + uhd::niusrprio::niusrprio_session fpga_session(resource_name, rpc_port); + uhd::niusrprio::nifpga_lvbitx::sptr lvbitx(new dummy_lvbitx(fpga_lvbitx_path)); + nirio_status_chain(fpga_session.open(lvbitx, true), status); + //Download BIN to flash or erase + if (flash_path != "erase") { + if (flash_path != "") { + printf("Writing FPGA image %s to flash...", flash_path.c_str()); + fflush(stdout); + nirio_status_chain(fpga_session.download_bitstream_to_flash(flash_path), status); + printf("DONE\n"); + } + } else { + printf("Erasing FPGA image from flash..."); + fflush(stdout); + nirio_status_chain(fpga_session.download_bitstream_to_flash(""), status); + printf("DONE\n"); + } + fpga_session.close(); + printf("DONE\n"); + } + + fflush(stdout); + usrprio_rpc_client temp_rpc_client("localhost", rpc_port); + std::string interface_path; + nirio_status_chain(temp_rpc_client.niusrprio_get_interface_path(resource_name, interface_path), status); + if (interface_path.empty()) { + printf("ERROR: Could not open a proxy to interface %u. If it exists, try downloading an LVBITX to the FPGA first.\n", interface_num); + exit(EXIT_FAILURE); + } + + niriok_proxy dev_proxy; + dev_proxy.open(interface_path); + + if (poke_tokens_str != ""){ + std::stringstream ss; + std::vector<std::string> poke_tokens; + boost::split(poke_tokens, poke_tokens_str, boost::is_any_of(":")); + ss.clear(); + ss << std::hex << poke_tokens[1]; + ss >> poke_addr; + ss.clear(); + ss << std::hex << poke_tokens[2]; + ss >> poke_data; + + niriok_scoped_addr_space(dev_proxy, poke_tokens[0]=="c"?BUS_INTERFACE:FPGA, status); + if (poke_tokens[0]=="z") { + nirio_status_chain(dev_proxy.poke(poke_addr, (uint32_t)0x70000 + poke_addr), status); + } else { + nirio_status_chain(dev_proxy.poke(poke_addr, poke_data), status); + } + printf("[POKE] %s:0x%x <= 0x%x (%u)\n", poke_tokens[0]=="c"?"Chinch":(poke_tokens[0]=="z"?"ZPU":"FPGA"), poke_addr, poke_data, poke_data); + } + + if (peek_tokens_str != ""){ + std::stringstream ss; + std::vector<std::string> peek_tokens; + boost::split(peek_tokens, peek_tokens_str, boost::is_any_of(":")); + ss.clear(); + ss << std::hex << peek_tokens[1]; + ss >> peek_addr; + + niriok_scoped_addr_space(dev_proxy, peek_tokens[0]=="c"?BUS_INTERFACE:FPGA, status); + uint32_t reg_val; + if (peek_tokens[0]=="z") { + nirio_status_chain(dev_proxy.poke((uint32_t)0x60000 + peek_addr, (uint32_t)0), status); + do { + nirio_status_chain(dev_proxy.peek((uint32_t)0x60000 + peek_addr, reg_val), status); + } while (reg_val != 0); + nirio_status_chain(dev_proxy.peek((uint32_t)0x70000 + peek_addr, reg_val), status); + } else { + nirio_status_chain(dev_proxy.peek(peek_addr, reg_val), status); + } + + printf("[PEEK] %s:0x%x = 0x%x (%u)\n", peek_tokens[0]=="c"?"Chinch":(peek_tokens[0]=="z"?"ZPU":"FPGA"), peek_addr, reg_val, reg_val); + } + + //Display attributes + if (vm.count("stats")){ + printf("[Interface %u]\n", interface_num); + uint32_t attr_val; + nirio_status_chain(dev_proxy.get_attribute(IS_FPGA_PROGRAMMED, attr_val), status); + printf("* Is FPGA Programmed? = %s\n", (attr_val==1)?"YES":"NO"); + + std::string signature; + for (int i = 0; i < 4; i++) { + nirio_status_chain(dev_proxy.peek(0x3FFF4, attr_val), status); + signature += boost::str(boost::format("%08x") % attr_val); + } + printf("* FPGA Signature = %s\n", signature.c_str()); + + std::string checksum; + for (int i = 0; i < 4; i++) { + nirio_status_chain(dev_proxy.peek(0x40030 + (i * 4), attr_val), status); + checksum += boost::str(boost::format("%08x") % attr_val); + } + printf("* FPGA Bitstream Checksum = %s\n", checksum.c_str()); + + uint32_t reg_val; + nirio_status_chain(dev_proxy.set_attribute(ADDRESS_SPACE, BUS_INTERFACE), status); + nirio_status_chain(dev_proxy.peek(0, reg_val), status); + printf("* Chinch Signature = %x\n", reg_val); + nirio_status_chain(dev_proxy.set_attribute(ADDRESS_SPACE, FPGA), status); + nirio_status_chain(dev_proxy.peek(0, reg_val), status); + printf("* PCIe FPGA Signature = %x\n", reg_val); + + printf("\n[DMA Stream Stats]\n"); + + nirio_status_chain(dev_proxy.set_attribute(ADDRESS_SPACE, FPGA), status); + + printf("------------------------------------------------------------------------------------------------"); + printf("\nChannel => |"); + for (uint32_t i = 0; i < 6; i++) { + printf("%11u |", i); + } + printf("\n------------------------------------------------------------------------------------------------"); + printf("\nTX Status |"); + for (uint32_t i = 0; i < 6; i++) { + nirio_status_chain(dev_proxy.peek(0x40200 + (i * 16), reg_val), status); + printf("%s |", reg_val==0 ? " Good" : " Error"); + } + printf("\nRX Status |"); + for (uint32_t i = 0; i < 6; i++) { + nirio_status_chain(dev_proxy.peek(0x40400 + (i * 16), reg_val), status); + printf("%s |", reg_val==0 ? " Good" : " Error"); + } + printf("\nTX Frm Size |"); + for (uint32_t i = 0; i < 6; i++) { + nirio_status_chain(dev_proxy.peek(0x40204 + (i * 16), reg_val), status); + printf("%11u |", reg_val); + } + printf("\nRX Frm Size |"); + for (uint32_t i = 0; i < 6; i++) { + nirio_status_chain(dev_proxy.peek(0x40404 + (i * 16), reg_val), status); + printf("%11u |", reg_val); + } + printf("\nTX Pkt Count |"); + for (uint32_t i = 0; i < 6; i++) { + nirio_status_chain(dev_proxy.peek(0x4020C + (i * 16), reg_val), status); + printf("%11u |", reg_val); + } + printf("\nTX Samp Count |"); + for (uint32_t i = 0; i < 6; i++) { + nirio_status_chain(dev_proxy.peek(0x40208 + (i * 16), reg_val), status); + printf("%11u |", reg_val); + } + printf("\nRX Pkt Count |"); + for (uint32_t i = 0; i < 6; i++) { + nirio_status_chain(dev_proxy.peek(0x4040C + (i * 16), reg_val), status); + printf("%11u |", reg_val); + } + printf("\nRX Samp Count |"); + for (uint32_t i = 0; i < 6; i++) { + nirio_status_chain(dev_proxy.peek(0x40408 + (i * 16), reg_val), status); + printf("%11u |", reg_val); + } + printf("\n------------------------------------------------------------------------------------------------\n"); + } + + exit(EXIT_SUCCESS); +} + + diff --git a/host/utils/usrp_cal_utils.hpp b/host/utils/usrp_cal_utils.hpp index bab6ddd91..40626dfaa 100644 --- a/host/utils/usrp_cal_utils.hpp +++ b/host/utils/usrp_cal_utils.hpp @@ -53,7 +53,7 @@ static inline void set_optimum_defaults(uhd::usrp::multi_usrp::sptr usrp){ const uhd::fs_path mb_path = "/mboards/0"; const std::string mb_name = tree->access<std::string>(mb_path / "name").get(); - if (mb_name.find("USRP2") != std::string::npos or mb_name.find("N200") != std::string::npos or mb_name.find("N210") != std::string::npos){ + if (mb_name.find("USRP2") != std::string::npos or mb_name.find("N200") != std::string::npos or mb_name.find("N210") != std::string::npos or mb_name.find("X300") != std::string::npos or mb_name.find("X310") != std::string::npos){ usrp->set_tx_rate(12.5e6); usrp->set_rx_rate(12.5e6); } diff --git a/host/utils/usrp_n2xx_simple_net_burner.cpp b/host/utils/usrp_n2xx_simple_net_burner.cpp index 1898ee9ae..290612f8b 100644 --- a/host/utils/usrp_n2xx_simple_net_burner.cpp +++ b/host/utils/usrp_n2xx_simple_net_burner.cpp @@ -511,6 +511,8 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ verify_image(udp_transport, true, fw_image, flash_info[1], fw_image_size); } + delete(flash_info); + //Reset USRP N2XX bool reset = false; if(auto_reboot) reset = true; diff --git a/host/utils/usrp_x3xx_fpga_burner.cpp b/host/utils/usrp_x3xx_fpga_burner.cpp new file mode 100644 index 000000000..07bc63559 --- /dev/null +++ b/host/utils/usrp_x3xx_fpga_burner.cpp @@ -0,0 +1,498 @@ +// +// Copyright 2013-2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// 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 <stdexcept> +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include <vector> + +#include <boost/foreach.hpp> +#include <boost/asio.hpp> +#include <boost/program_options.hpp> +#include <boost/property_tree/ptree.hpp> +#include <boost/property_tree/xml_parser.hpp> +#include <boost/assign.hpp> +#include <boost/cstdint.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 <uhd/exception.hpp> +#include <uhd/transport/if_addrs.hpp> +#include <uhd/transport/nirio/niusrprio_session.h> +#include <uhd/transport/udp_simple.hpp> +#include <uhd/device.hpp> +#include <uhd/types/device_addr.hpp> +#include <uhd/utils/byteswap.hpp> +#include <uhd/utils/images.hpp> +#include <uhd/utils/safe_main.hpp> +#include <uhd/utils/safe_call.hpp> + +#ifdef _MSC_VER +extern "C" { +#endif +#include "cdecode.h" +#ifdef _MSC_VER +} +#endif + +#define X300_FPGA_BIN_SIZE_BYTES 15877916 +#define X300_FPGA_BIT_MAX_SIZE_BYTES 15878022 +#define X300_FPGA_PROG_UDP_PORT 49157 +#define X300_FLASH_SECTOR_SIZE 131072 +#define X300_PACKET_SIZE_BYTES 256 +#define X300_FPGA_SECTOR_START 32 +#define X300_MAX_RESPONSE_BYTES 128 +#define UDP_TIMEOUT 3 +#define FPGA_LOAD_TIMEOUT 15 + +#define X300_FPGA_PROG_FLAGS_ACK 1 +#define X300_FPGA_PROG_FLAGS_ERROR 2 +#define X300_FPGA_PROG_FLAGS_INIT 4 +#define X300_FPGA_PROG_FLAGS_CLEANUP 8 +#define X300_FPGA_PROG_FLAGS_ERASE 16 +#define X300_FPGA_PROG_FLAGS_VERIFY 32 +#define X300_FPGA_PROG_CONFIGURE 64 +#define X300_FPGA_PROG_CONFIG_STATUS 128 + +namespace fs = boost::filesystem; +namespace po = boost::program_options; + +using namespace uhd; +using namespace uhd::transport; + +typedef struct { + boost::uint32_t flags; + boost::uint32_t sector; + boost::uint32_t index; + boost::uint32_t size; + boost::uint16_t data[128]; +} x300_fpga_update_data_t; + +boost::uint8_t x300_data_in_mem[udp_simple::mtu]; +boost::uint8_t intermediary_packet_data[X300_PACKET_SIZE_BYTES]; + +boost::uint8_t bitswap(uint8_t b){ + b = ((b & 0xF0) >> 4) | ((b & 0x0F) << 4); + b = ((b & 0xCC) >> 2) | ((b & 0x33) << 2); + b = ((b & 0xAA) >> 1) | ((b & 0x55) << 1); + + return b; +} + +void list_usrps(){ + device_addrs_t found_devices = device::find(device_addr_t("type=x300")); + + std::cout << "Available X3x0 devices:" << std::endl; + BOOST_FOREACH(const device_addr_t &dev, found_devices){ + std::string dev_string; + if(dev.has_key("addr")){ + dev_string = str(boost::format(" * %s (%s, addr: %s)") + % dev["product"] + % dev["fpga"] + % dev["addr"]); + } + else{ + dev_string = str(boost::format(" * %s (%s, resource: %s)") + % dev["product"] + % dev["fpga"] + % dev["resource"]); + } + std::cout << dev_string << std::endl; + } +} + +device_addr_t find_usrp_with_ethernet(std::string ip_addr, bool output){ + if(output) std::cout << "Attempting to find X3x0 with IP address: " << ip_addr << std::endl; + const device_addr_t dev = device_addr_t(str(boost::format("addr=%s") % ip_addr)); + device_addrs_t found_devices = device::find(dev); + + if(found_devices.size() < 1) { + throw std::runtime_error("Could not find X3x0 with the specified address!"); + } + else if(found_devices.size() > 1) { + throw std::runtime_error("Found multiple X3x0 units with the specified address!"); + } + else { + if(output) std::cout << (boost::format("Found %s (%s).\n\n") + % found_devices[0]["product"] + % found_devices[0]["fpga"]); + } + return found_devices[0]; +} + +device_addr_t find_usrp_with_pcie(std::string resource, bool output){ + if(output) std::cout << "Attempting to find X3x0 with resource: " << resource << std::endl; + const device_addr_t dev = device_addr_t(str(boost::format("resource=%s") % resource)); + device_addrs_t found_devices = device::find(dev); + + if(found_devices.size() < 1) { + throw std::runtime_error("Could not find X3x0 with the specified resource!"); + } + else { + if(output) std::cout << (boost::format("Found %s (%s).\n\n") + % found_devices[0]["product"] + % found_devices[0]["fpga"]); + } + return found_devices[0]; +} + +std::string get_default_image_path(std::string model, std::string image_type){ + std::transform(model.begin(), model.end(), model.begin(), ::tolower); + + std::string image_name = str(boost::format("usrp_%s_fpga_%s.bit") + % model.c_str() % image_type.c_str()); + + return find_image_path(image_name); +} + +void extract_from_lvbitx(std::string lvbitx_path, std::vector<char> &bitstream){ + boost::property_tree::ptree pt; + boost::property_tree::xml_parser::read_xml(lvbitx_path.c_str(), pt, + boost::property_tree::xml_parser::no_comments | + boost::property_tree::xml_parser::trim_whitespace); + std::string const encoded_bitstream(pt.get<std::string>("Bitfile.Bitstream")); + std::vector<char> decoded_bitstream(encoded_bitstream.size()); + + base64_decodestate decode_state; + base64_init_decodestate(&decode_state); + size_t const decoded_size = base64_decode_block(encoded_bitstream.c_str(), + encoded_bitstream.size(), &decoded_bitstream.front(), &decode_state); + decoded_bitstream.resize(decoded_size); + bitstream.swap(decoded_bitstream); +} + +void ethernet_burn(udp_simple::sptr udp_transport, std::string fpga_path, bool verify){ + boost::uint32_t max_size; + std::vector<char> bitstream; + + if(fs::extension(fpga_path) == ".bit") max_size = X300_FPGA_BIT_MAX_SIZE_BYTES; + else max_size = X300_FPGA_BIN_SIZE_BYTES; //Use for both .bin and .lvbitx + + bool is_lvbitx = (fs::extension(fpga_path) == ".lvbitx"); + + size_t fpga_image_size; + FILE* file; + if((file = fopen(fpga_path.c_str(), "rb"))){ + fseek(file, 0, SEEK_END); + if(is_lvbitx){ + extract_from_lvbitx(fpga_path, bitstream); + fpga_image_size = bitstream.size(); + } + else fpga_image_size = ftell(file); + if(fpga_image_size > max_size){ + fclose(file); + throw std::runtime_error(str(boost::format("FPGA size is too large (%d > %d).") + % fpga_image_size % max_size)); + } + rewind(file); + } + else{ + throw std::runtime_error(str(boost::format("Could not find FPGA image at location: %s") + % fpga_path.c_str())); + } + + const x300_fpga_update_data_t *update_data_in = reinterpret_cast<const x300_fpga_update_data_t *>(x300_data_in_mem); + + x300_fpga_update_data_t ack_packet; + ack_packet.flags = htonx<boost::uint32_t>(X300_FPGA_PROG_FLAGS_ACK | X300_FPGA_PROG_FLAGS_INIT); + ack_packet.sector = 0; + ack_packet.size = 0; + ack_packet.index = 0; + memset(ack_packet.data, 0, sizeof(ack_packet.data)); + udp_transport->send(boost::asio::buffer(&ack_packet, sizeof(ack_packet))); + + udp_transport->recv(boost::asio::buffer(x300_data_in_mem), UDP_TIMEOUT); + if((ntohl(update_data_in->flags) & X300_FPGA_PROG_FLAGS_ERROR) != X300_FPGA_PROG_FLAGS_ERROR){ + std::cout << "Burning image: " << fpga_path << std::endl; + if(verify) std::cout << "NOTE: Verifying image. Burning will take much longer." << std::endl; + std::cout << std::endl; + } + else{ + throw std::runtime_error("Failed to start image burning! Did you specify the correct IP address? If so, power-cycle the device and try again."); + } + + std::cout << "Progress: " << std::flush; + + int percentage = -1; + int last_percentage = -1; + size_t current_pos = 0; + + //Each sector + for(size_t i = 0; i < fpga_image_size; i += X300_FLASH_SECTOR_SIZE){ + + //Print percentage at beginning of first sector after each 10% + percentage = int(double(i)/double(fpga_image_size)*100); + if((percentage != last_percentage) and (percentage % 10 == 0)){ //Don't print same percentage twice + std::cout << percentage << "%..." << std::flush; + } + last_percentage = percentage; + + //Each packet + for(size_t j = i; (j < fpga_image_size and j < (i+X300_FLASH_SECTOR_SIZE)); j += X300_PACKET_SIZE_BYTES){ + x300_fpga_update_data_t send_packet; + + send_packet.flags = X300_FPGA_PROG_FLAGS_ACK; + if(verify) send_packet.flags |= X300_FPGA_PROG_FLAGS_VERIFY; + if(j == i) send_packet.flags |= X300_FPGA_PROG_FLAGS_ERASE; //Erase the sector before writing + send_packet.flags = htonx<boost::uint32_t>(send_packet.flags); + + send_packet.sector = htonx<boost::uint32_t>(X300_FPGA_SECTOR_START + (i/X300_FLASH_SECTOR_SIZE)); + send_packet.index = htonx<boost::uint32_t>((j % X300_FLASH_SECTOR_SIZE) / 2); + send_packet.size = htonx<boost::uint32_t>(X300_PACKET_SIZE_BYTES / 2); + memset(intermediary_packet_data,0,X300_PACKET_SIZE_BYTES); + memset(send_packet.data,0,X300_PACKET_SIZE_BYTES); + if(!is_lvbitx) current_pos = ftell(file); + + if(current_pos + X300_PACKET_SIZE_BYTES > fpga_image_size){ + if(is_lvbitx){ + memcpy(intermediary_packet_data, (&bitstream[current_pos]), (bitstream.size()-current_pos+1)); + } + else{ + size_t len = fread(intermediary_packet_data, sizeof(boost::uint8_t), (fpga_image_size-current_pos), file); + if(len != (fpga_image_size-current_pos)){ + throw std::runtime_error("Error reading from file!"); + } + } + } + else{ + if(is_lvbitx){ + memcpy(intermediary_packet_data, (&bitstream[current_pos]), X300_PACKET_SIZE_BYTES); + current_pos += X300_PACKET_SIZE_BYTES; + } + else{ + size_t len = fread(intermediary_packet_data, sizeof(boost::uint8_t), X300_PACKET_SIZE_BYTES, file); + if(len != X300_PACKET_SIZE_BYTES){ + throw std::runtime_error("Error reading from file!"); + } + } + } + + for(size_t k = 0; k < X300_PACKET_SIZE_BYTES; k++){ + intermediary_packet_data[k] = bitswap(intermediary_packet_data[k]); + } + + memcpy(send_packet.data, intermediary_packet_data, X300_PACKET_SIZE_BYTES); + + for(size_t k = 0; k < (X300_PACKET_SIZE_BYTES/2); k++){ + send_packet.data[k] = htonx<boost::uint16_t>(send_packet.data[k]); + } + + udp_transport->send(boost::asio::buffer(&send_packet, sizeof(send_packet))); + + udp_transport->recv(boost::asio::buffer(x300_data_in_mem), UDP_TIMEOUT); + const x300_fpga_update_data_t *update_data_in = reinterpret_cast<const x300_fpga_update_data_t *>(x300_data_in_mem); + + if((ntohl(update_data_in->flags) & X300_FPGA_PROG_FLAGS_ERROR) == X300_FPGA_PROG_FLAGS_ERROR){ + throw std::runtime_error("Transfer or data verification failed!"); + } + } + } + fclose(file); + + //Send clean-up signal + x300_fpga_update_data_t cleanup_packet; + cleanup_packet.flags = htonx<boost::uint32_t>(X300_FPGA_PROG_FLAGS_ACK | X300_FPGA_PROG_FLAGS_CLEANUP); + cleanup_packet.sector = 0; + cleanup_packet.size = 0; + cleanup_packet.index = 0; + memset(cleanup_packet.data, 0, sizeof(cleanup_packet.data)); + udp_transport->send(boost::asio::buffer(&cleanup_packet, sizeof(cleanup_packet))); + + udp_transport->recv(boost::asio::buffer(x300_data_in_mem), UDP_TIMEOUT); + const x300_fpga_update_data_t *cleanup_data_in = reinterpret_cast<const x300_fpga_update_data_t *>(x300_data_in_mem); + + if((ntohl(cleanup_data_in->flags) & X300_FPGA_PROG_FLAGS_ERROR) == X300_FPGA_PROG_FLAGS_ERROR){ + throw std::runtime_error("Transfer or data verification failed!"); + } + + std::cout << "100%" << std::endl; +} + +void pcie_burn(std::string resource, std::string rpc_port, std::string fpga_path) +{ + std::cout << "Burning image: " << fpga_path << std::endl; + std::cout << "This will take 3-10 minutes." << std::endl; + + nirio_status status = NiRio_Status_Success; + + uhd::niusrprio::niusrprio_session fpga_session(resource, rpc_port); + nirio_status_chain(fpga_session.download_bitstream_to_flash(fpga_path), status); + + if(nirio_status_fatal(status)) throw std::runtime_error("Failed to burn FPGA image!"); +} + +bool configure_fpga(udp_simple::sptr udp_transport, std::string ip_addr){ + x300_fpga_update_data_t configure_packet; + configure_packet.flags = htonx<boost::uint32_t>(X300_FPGA_PROG_CONFIGURE | X300_FPGA_PROG_FLAGS_ACK); + configure_packet.sector = 0; + configure_packet.size = 0; + configure_packet.index = 0; + memset(configure_packet.data, 0, sizeof(configure_packet.data)); + udp_transport->send(boost::asio::buffer(&configure_packet, sizeof(configure_packet))); + + udp_transport->recv(boost::asio::buffer(x300_data_in_mem), UDP_TIMEOUT); + const x300_fpga_update_data_t *configure_data_in = reinterpret_cast<const x300_fpga_update_data_t *>(x300_data_in_mem); + bool successful = false; + + if((ntohl(configure_data_in->flags) & X300_FPGA_PROG_FLAGS_ERROR) == X300_FPGA_PROG_FLAGS_ERROR){ + throw std::runtime_error("Transfer or data verification failed!"); + } + else{ + std::cout << std::endl << "Waiting for X3x0 to configure FPGA image and reload." << std::endl; + boost::this_thread::sleep(boost::posix_time::milliseconds(5000)); + + x300_fpga_update_data_t config_status_packet; + configure_packet.flags = htonx<boost::uint32_t>(X300_FPGA_PROG_CONFIG_STATUS); + config_status_packet.sector = 0; + config_status_packet.size = 0; + config_status_packet.index = 0; + memset(config_status_packet.data, 0, sizeof(config_status_packet.data)); + for(int i = 0; i < 5; i++){ + udp_transport->send(boost::asio::buffer(&config_status_packet, sizeof(config_status_packet))); + udp_transport->recv(boost::asio::buffer(x300_data_in_mem), 1); + const x300_fpga_update_data_t *config_status_data_in = reinterpret_cast<const x300_fpga_update_data_t *>(x300_data_in_mem); + + if((ntohl(config_status_data_in->flags) & X300_FPGA_PROG_FLAGS_ERROR) != X300_FPGA_PROG_FLAGS_ERROR + and udp_transport->get_recv_addr() == ip_addr){ + successful = true; + break; + } + successful = false; //If it worked, the break would skip this + } + } + return successful; +} + +int UHD_SAFE_MAIN(int argc, char *argv[]){ + memset(intermediary_packet_data, 0, X300_PACKET_SIZE_BYTES); + std::string ip_addr, resource, fpga_path, image_type, rpc_port; + + po::options_description desc("Allowed options"); + desc.add_options() + ("help", "Display this help message.") + ("addr", po::value<std::string>(&ip_addr), "Specify an IP address.") + ("resource", po::value<std::string>(&resource), "Specify an NI-RIO resource.") + ("rpc-port", po::value<std::string>(&rpc_port)->default_value("5444"), "Specify a port to communicate with the RPC server.") + ("type", po::value<std::string>(&image_type), "Specify an image type (1G, HGS, XGS), leave blank for current type.") + ("fpga-path", po::value<std::string>(&fpga_path), "Specify an FPGA path (overrides --type option).") + ("configure", "Initialize FPGA with image currently burned to flash (Ethernet only).") + ("verify", "Verify data downloaded to flash (Ethernet only, download will take much longer)") + ("list", "List all available X3x0 devices.") + ; + po::variables_map vm; + po::store(po::parse_command_line(argc, argv, desc), vm); + po::notify(vm); + + //Print help message + if(vm.count("help")){ + std::cout << "USRP X3x0 FPGA Burner" << std::endl << std::endl; + + std::cout << "Burns an FPGA image onto a USRP X300/X310. To burn the image" << std::endl + << "over Ethernet, specify an IP address with the --addr option," << std::endl + << "or to burn over PCIe, specify an NI-RIO resource (ex. RIO0)" << std::endl + << "with the --resource option." << std::endl << std::endl; + + std::cout << desc << std::endl; + return EXIT_SUCCESS; + } + + //List all available devices + if(vm.count("list")){ + list_usrps(); + return EXIT_SUCCESS; + } + + /* + * The user must specify whether to burn the image over Ethernet or PCI-e. + */ + if(not (vm.count("addr") xor vm.count("resource"))){ + throw std::runtime_error("You must specify addr OR resource!"); + } + + /* + * With settings validated, find X3x0 with specified arguments. + */ + device_addr_t dev = (vm.count("addr")) ? find_usrp_with_ethernet(ip_addr, true) + : find_usrp_with_pcie(resource, true); + + /* + * If custom FPGA path is given, ignore specified type and let FPGA + * figure it out. + */ + if(vm.count("fpga-path")){ + //Expand tilde usage if applicable + #ifndef UHD_PLATFORM_WIN32 + if(fpga_path.find("~/") == 0) fpga_path.replace(0,1,getenv("HOME")); + #endif + } + else{ + if(vm.count("type")){ + //Make sure the specified type is 1G, HGS, or XGS + if((image_type != "1G") and (image_type != "HGS") and (image_type != "XGS")){ + throw std::runtime_error("--type must be 1G, HGS, or XGS!"); + } + else fpga_path = get_default_image_path(dev["product"], image_type); + } + else{ + //Use default image of currently present FPGA type + fpga_path = get_default_image_path(dev["product"], dev["fpga"]); + } + } + + + /* + * Check validity of image through extension + */ + std::string ext = fs::extension(fpga_path.c_str()); + if(ext != ".bin" and ext != ".bit" and ext != ".lvbitx"){ + throw std::runtime_error("The image filename must end in .bin, .bit, or .lvbitx."); + } + + if(vm.count("addr")){ + udp_simple::sptr udp_transport = udp_simple::make_connected(ip_addr, BOOST_STRINGIZE(X300_FPGA_PROG_UDP_PORT)); + + ethernet_burn(udp_transport, fpga_path, vm.count("verify")); + + if(vm.count("configure")){ + if(configure_fpga(udp_transport, ip_addr)) std::cout << "Successfully configured FPGA!" << std::endl; + else throw std::runtime_error("FPGA configuring failed!"); + } + } + else pcie_burn(resource, rpc_port, fpga_path); + + /* + * Attempt to find USRP after burning + */ + std::cout << std::endl << "Attempting to find device..." << std::flush; + boost::this_thread::sleep(boost::posix_time::milliseconds(2000)); //Sometimes needed for Ethernet to reconnect + device_addr_t found_usrp = (vm.count("addr")) ? find_usrp_with_ethernet(ip_addr, false) + : find_usrp_with_pcie(resource, false); + std::cout << "found!" << std::endl; //If unsuccessful, runtime error would occur in find functions + std::cout << "Successfully burned FPGA image!" << std::endl << std::endl; + + if(vm.count("addr")) std::cout << str(boost::format("Power-cycle the USRP %s to use the new image.") % found_usrp["product"]) << std::endl; + else std::cout << str(boost::format("Power-cycle the USRP %s and reboot your machine to use the new image.") % found_usrp["product"]) << std::endl; + + return EXIT_SUCCESS; +} |