From 1b149f561370687ad65e3aa644a402f00dbd16ea Mon Sep 17 00:00:00 2001 From: Martin Braun Date: Tue, 7 Oct 2014 11:32:14 +0200 Subject: Initial commit E300 support. --- host/docs/mainpage.dox | 1 + host/docs/usrp_e3x0.dox | 302 ++++++ host/lib/convert/CMakeLists.txt | 3 + host/lib/convert/convert_neon.S | 37 + host/lib/convert/convert_with_neon.cpp | 36 +- host/lib/usrp/CMakeLists.txt | 1 + host/lib/usrp/dboard/CMakeLists.txt | 1 + host/lib/usrp/dboard/db_e3x0.cpp | 64 ++ host/lib/usrp/e300/CMakeLists.txt | 55 + host/lib/usrp/e300/e300_async_serial.cpp | 245 +++++ host/lib/usrp/e300/e300_async_serial.hpp | 113 ++ host/lib/usrp/e300/e300_common.cpp | 59 ++ host/lib/usrp/e300/e300_common.hpp | 31 + host/lib/usrp/e300/e300_defaults.hpp | 82 ++ host/lib/usrp/e300/e300_eeprom_manager.cpp | 236 +++++ host/lib/usrp/e300/e300_eeprom_manager.hpp | 125 +++ host/lib/usrp/e300/e300_fifo_config.cpp | 429 ++++++++ host/lib/usrp/e300/e300_fifo_config.hpp | 52 + host/lib/usrp/e300/e300_fpga_defs.hpp | 29 + host/lib/usrp/e300/e300_global_regs.cpp | 131 +++ host/lib/usrp/e300/e300_global_regs.hpp | 78 ++ host/lib/usrp/e300/e300_i2c.cpp | 409 +++++++ host/lib/usrp/e300/e300_i2c.hpp | 77 ++ host/lib/usrp/e300/e300_impl.cpp | 1347 ++++++++++++++++++++++++ host/lib/usrp/e300/e300_impl.hpp | 296 ++++++ host/lib/usrp/e300/e300_io_impl.cpp | 599 +++++++++++ host/lib/usrp/e300/e300_network.cpp | 642 +++++++++++ host/lib/usrp/e300/e300_network.hpp | 43 + host/lib/usrp/e300/e300_regs.hpp | 69 ++ host/lib/usrp/e300/e300_remote_codec_ctrl.cpp | 148 +++ host/lib/usrp/e300/e300_remote_codec_ctrl.hpp | 59 ++ host/lib/usrp/e300/e300_sensor_manager.cpp | 289 +++++ host/lib/usrp/e300/e300_sensor_manager.hpp | 77 ++ host/lib/usrp/e300/e300_spi.cpp | 127 +++ host/lib/usrp/e300/e300_spi.hpp | 34 + host/lib/usrp/e300/e300_sysfs_hooks.cpp | 121 +++ host/lib/usrp/e300/e300_ublox_control.hpp | 50 + host/lib/usrp/e300/e300_ublox_control_impl.cpp | 505 +++++++++ host/lib/usrp/e300/e300_ublox_control_impl.hpp | 457 ++++++++ host/utils/CMakeLists.txt | 7 + host/utils/query_gpsdo_sensors.cpp | 21 +- host/utils/usrp_e3x0_network_mode.cpp | 80 ++ 42 files changed, 7558 insertions(+), 9 deletions(-) create mode 100644 host/docs/usrp_e3x0.dox create mode 100644 host/lib/convert/convert_neon.S create mode 100644 host/lib/usrp/dboard/db_e3x0.cpp create mode 100644 host/lib/usrp/e300/CMakeLists.txt create mode 100644 host/lib/usrp/e300/e300_async_serial.cpp create mode 100644 host/lib/usrp/e300/e300_async_serial.hpp create mode 100644 host/lib/usrp/e300/e300_common.cpp create mode 100644 host/lib/usrp/e300/e300_common.hpp create mode 100644 host/lib/usrp/e300/e300_defaults.hpp create mode 100644 host/lib/usrp/e300/e300_eeprom_manager.cpp create mode 100644 host/lib/usrp/e300/e300_eeprom_manager.hpp create mode 100644 host/lib/usrp/e300/e300_fifo_config.cpp create mode 100644 host/lib/usrp/e300/e300_fifo_config.hpp create mode 100644 host/lib/usrp/e300/e300_fpga_defs.hpp create mode 100644 host/lib/usrp/e300/e300_global_regs.cpp create mode 100644 host/lib/usrp/e300/e300_global_regs.hpp create mode 100644 host/lib/usrp/e300/e300_i2c.cpp create mode 100644 host/lib/usrp/e300/e300_i2c.hpp create mode 100644 host/lib/usrp/e300/e300_impl.cpp create mode 100644 host/lib/usrp/e300/e300_impl.hpp create mode 100644 host/lib/usrp/e300/e300_io_impl.cpp create mode 100644 host/lib/usrp/e300/e300_network.cpp create mode 100644 host/lib/usrp/e300/e300_network.hpp create mode 100644 host/lib/usrp/e300/e300_regs.hpp create mode 100644 host/lib/usrp/e300/e300_remote_codec_ctrl.cpp create mode 100644 host/lib/usrp/e300/e300_remote_codec_ctrl.hpp create mode 100644 host/lib/usrp/e300/e300_sensor_manager.cpp create mode 100644 host/lib/usrp/e300/e300_sensor_manager.hpp create mode 100644 host/lib/usrp/e300/e300_spi.cpp create mode 100644 host/lib/usrp/e300/e300_spi.hpp create mode 100644 host/lib/usrp/e300/e300_sysfs_hooks.cpp create mode 100644 host/lib/usrp/e300/e300_ublox_control.hpp create mode 100644 host/lib/usrp/e300/e300_ublox_control_impl.cpp create mode 100644 host/lib/usrp/e300/e300_ublox_control_impl.hpp create mode 100644 host/utils/usrp_e3x0_network_mode.cpp (limited to 'host') diff --git a/host/docs/mainpage.dox b/host/docs/mainpage.dox index 597938c35..17f01cfc0 100644 --- a/host/docs/mainpage.dox +++ b/host/docs/mainpage.dox @@ -39,6 +39,7 @@ devices and how to use the API to connect to them through your own software. ## USRP E-Series Devices \li \subpage page_usrp_e1x0 +\li \subpage page_usrp_e3x0 \li \subpage page_gpsdo ## USRP X-Series Devices diff --git a/host/docs/usrp_e3x0.dox b/host/docs/usrp_e3x0.dox new file mode 100644 index 000000000..bfa9ebf11 --- /dev/null +++ b/host/docs/usrp_e3x0.dox @@ -0,0 +1,302 @@ +/*! \page page_usrp_e3x0 USRP-E3x0 Series Device Manual + +\tableofcontents + +\section e3x0_feature_list Comparative features list + +- Hardware Capabilities: + Integrated RF frontend (70 MHz - 6 GHz) + - External PPS reference input + - External 10 MHz reference input + - Configurable clock rate + - Internal GPIO connector with UHD API control + - 2 USB 2.0 Host ports + - Internal GPSDO + - Soundcard mono input / stereo output + - USB UART + - Zynq-7020 FPGA +- 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 sample modes (sc16) + - Up to 10 MHz of RF bandwidth with 16-bit samples + +\section e3x0_getting_started Getting started + +This will run you through the first steps relevant to get your USRP E300/310 +up and running. + +\subsection e3x0_first_boot First boot + +After unpacking and assembling your USRP E300/E310 insert the micro sd card into the micro sd card slot. + +There are two different methods to connect to the device + +- using the onboard serial to usb connector +- using the gigabit ethernet connector and a ssh client on your host computer + +For the first boot, booting with the serial cable connected to the device +is recommended, as it allows to review and modify the network configuration, +and allows to enter the bootloader in case of issues during the boot. + + +\subsubsection e3x0_first_boot_serial Serial connection + +To use the serial connection together with a Linux or OSX machine (most other UNIX variants come with a version of screen, too) +a terminal emulator such as screen can be used: + + $ sudo screen /dev/ttyUSB0 115200 + +The exact device node /dev/ttyUSB0 depends on your operating system's driver and other USB devices that might be already connected. +It can be usually found by perusing the output of dmesg or journalctl, after connecting the USRP E300/E310 device to your host computer. + +An example of a dmesg output for the serial to usb converter: + + 924.102764] usb 1-1: FTDI USB Serial Device converter now attached to ttyUSB0 + + +On Microsoft Windows the serial connection can be established using a tool such as Putty by selecting a baudrate of 115200 and the corresponding serial port for the serial to usb converter. + +In both cases you should see boot messages fly by and finally end up with a login prompt similar to the following: + +TODO!! + +Note: The username is 'root' and the default password is empty. + +You should be presented with a shell similar to the following + + root@ettus-e300:~# + + +\subsubsection e3x0_first_boot_ssh SSH connection + +The USRP E300/E310 device relies on the DHCP protocol to automatically obtain an IP address. +In case your network setup does not include a DHCP server, refer to the section \ref e3x0_first_boot_serial or configure a DHCP server to hand out IP addresses on your network. + +After the device obtained an IP address you can log in from a Linux or OSX machine by typing: + + $ ssh root@192.168.10.42 + +where the IP address depends on your local network setup. + +On Microsoft Windows again the connection can be established using a tool such as Putty, by selecting a username of root without password. + +You should be presented with a shell similar to the following + + root@ettus-e300:~# + +\section e3x0_hw Hardware Setup + +\section e3x0_load_fpga_imgs 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. + +\subsection e3x0_load_fpga_imgs_jtag Use JTAG to load FPGA images + +The USRP-E Series device features an on-board JTAG connector that can be accessed on the PCB +of the device. The iMPACT tool in the Xilinx Programming Tools (ISE, iMPACT) package can be used to load an image over the JTAG interface. This can be useful for unbricking devices. + +If you have iMPACT installed, you can use the `impact_jtag_programmer.sh` tool to install images. Make sure your e3x0 is powered on and connected to your computer using the internal JTAG connector. Then run the tool: + + /impact_jtag_programmer.sh --fpga-path= + +\subsection e3x0_setup_change_ip 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-E Series devices with the same host computer +- to set a known IP address into USRP (in case you forgot) + +\section e3x0_addressing Addressing the Device + +\subsection e3x0_addressing_singledev 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 or resolvable hostname. +See the application notes on \ref page_identification. +Use this addressing scheme with the uhd::usrp::multi_usrp interface (not a typo!). + +Example device address string representation for a USRP-E Series device with IPv4 address 192.168.10.2: + + addr=192.168.10.2 + +\subsection e3x0_addressing_multidevcfg 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 \\. +Use this addressing scheme with the uhd::usrp::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 + + +\section e3x0_comm_problems Communication Problems + +When setting up a development machine for the first time, +you may have various difficulties communicating with the USRP device. +The following tips are designed to help narrow down and diagnose the problem. + +\subsection e3x0_comm_problems_runtimeerr 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. + +\subsection e3x0_comm_problems_firewall 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**. + +\subsection e3x0_comm_problems_ping 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 + +\subsection e3x0_comm_problems_monitor Monitor the host network traffic +Use Wireshark to monitor packets sent to and received from the device. + +\subsection e3x0_comm_problems_leds Observe Ethernet port LEDs +When there is network traffic arriving at the Ethernet port, LEDs will light up. +You can use this to make sure the network connection is correctly set up, e.g. +by pinging the USRP and making sure the LEDs start to blink. + +\section e3x0_hw Hardware Notes + +\subsection e3x0_hw_fpanel Front Panel + +\image html e3x0_fp_overlay.png "e3x0" + +- **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 + +\subsection e3x0_hw_rear_panel Rear Panel + +\image html e3x0_rp_overlay.png "e3x0 Rear Panel" + +- **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 + +\subsection e3x0_hw_e3x0_hw_ref10M 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. + +\subsection e3x0_hw_pps 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: + +- `` are device address arguments (optional if only one USRP device is on your machine) + + cd /lib/uhd/examples + ./test_pps_input --args=\ + +\subsection e3x0_hw_gpsdo Internal GPSDO + +Please see \ref page_gpsdo_e3x0 for information on configuring and using the internal GPSDO. + +\subsection e3x0_hw_gpio Internal GPIO + +### Connector + +\image html e3x0_gpio_conn.png "e3x0 GPIO Connector" + +### 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] +TODO: + + +Please see the \ref page_gpio_api for information on configuring and using the GPIO bus. + +\subsection e3x0_hw_chipscope Debugging custom FPGA designs with Xilinx Chipscope + +Xilinx chipscope allows for debugging custom FPGA designs similar to a logic analyzer. +USRP-E series devices can be used with Xilinx chipscope using the internal JTAG connector. + +Further information on how to use Chipscope can be found in the Xilinx Chipscope Pro Software and Cores User Guide (UG029). + +\section e3x0_misc Miscellaneous + +\subsection e3x0_misc_multirx 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). + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp} +usrp->set_rx_subdev_spec("A:RX1 A:RX2"); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +\subsection e3x0_misc_sensors Available Sensors + +The following sensors are available for the USRP-E Series motherboards; +they can be queried through the API. + +- **fe_locked** - rx / tx frontend pll locked +- **temp** - processor temperature value +- Other sensors are added when the GPSDO is enabled + +*/ +// vim:ft=doxygen: diff --git a/host/lib/convert/CMakeLists.txt b/host/lib/convert/CMakeLists.txt index 363555f45..bec88b520 100644 --- a/host/lib/convert/CMakeLists.txt +++ b/host/lib/convert/CMakeLists.txt @@ -94,8 +94,11 @@ IF(CMAKE_COMPILER_IS_GNUCXX) ENDIF(CMAKE_COMPILER_IS_GNUCXX) IF(HAVE_ARM_NEON_H) + ENABLE_LANGUAGE(ASM) + LIBUHD_APPEND_SOURCES( ${CMAKE_CURRENT_SOURCE_DIR}/convert_with_neon.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/convert_neon.S ) ENDIF() diff --git a/host/lib/convert/convert_neon.S b/host/lib/convert/convert_neon.S new file mode 100644 index 000000000..8cbe82bde --- /dev/null +++ b/host/lib/convert/convert_neon.S @@ -0,0 +1,37 @@ +// +// 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 . +// + + .arch armv7-a + .fpu neon + .syntax unified + .text + .align 2 + .global neon_item32_sc16_swap_16n + .type neon_item32_sc16_swap_16n, %function +neon_item32_sc16_swap_16n: +.loop_swap: + vld2.16 {q0, q1}, [r0]! + vld2.16 {q2, q3}, [r0]! + vswp q0, q1 + vswp q2, q3 + vst2.16 {q0, q1}, [r1]! + vst2.16 {q2, q3}, [r1]! + subs r2, #1 + bne .loop_swap + bx lr + .size neon_item32_sc16_swap_16n, .-neon_item32_sc16_swap_16n + .section .note.GNU-stack,"",%progbits diff --git a/host/lib/convert/convert_with_neon.cpp b/host/lib/convert/convert_with_neon.cpp index e994d97a6..f1c7773ec 100644 --- a/host/lib/convert/convert_with_neon.cpp +++ b/host/lib/convert/convert_with_neon.cpp @@ -1,5 +1,5 @@ // -// Copyright 2011-2012 Ettus Research LLC +// Copyright 2011-2014 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -19,6 +19,12 @@ #include #include +extern "C" { +void neon_item32_sc16_swap_16n(void *, void *, int iter); +} + +static const int SIMD_WIDTH = 16; + using namespace uhd::convert; DECLARE_CONVERTER(fc32, 1, sc16_item32_le, 1, PRIORITY_SIMD){ @@ -58,3 +64,31 @@ DECLARE_CONVERTER(sc16_item32_le, 1, fc32, 1, PRIORITY_SIMD){ item32_sc16_to_xx(input+i, output+i, nsamps-i, scale_factor); } + +DECLARE_CONVERTER(sc16, 1, sc16_item32_le, 1, PRIORITY_SIMD){ + const sc16_t *input = reinterpret_cast(inputs[0]); + item32_t *output = reinterpret_cast(outputs[0]); + + size_t i = nsamps / SIMD_WIDTH; + + if (i) + neon_item32_sc16_swap_16n((void *) input, (void *) output, i); + + i *= SIMD_WIDTH; + + xx_to_item32_sc16(input+i, output+i, nsamps-i, scale_factor); +} + +DECLARE_CONVERTER(sc16_item32_le, 1, sc16, 1, PRIORITY_SIMD){ + const item32_t *input = reinterpret_cast(inputs[0]); + sc16_t *output = reinterpret_cast(outputs[0]); + + size_t i = nsamps / SIMD_WIDTH; + + if (i) + neon_item32_sc16_swap_16n((void *) input, (void *) output, i); + + i *= SIMD_WIDTH; + + item32_sc16_to_xx(input+i, output+i, nsamps-i, scale_factor); +} diff --git a/host/lib/usrp/CMakeLists.txt b/host/lib/usrp/CMakeLists.txt index c8c2e6a8d..f6788b5ef 100644 --- a/host/lib/usrp/CMakeLists.txt +++ b/host/lib/usrp/CMakeLists.txt @@ -37,5 +37,6 @@ INCLUDE_SUBDIRECTORY(usrp1) INCLUDE_SUBDIRECTORY(usrp2) INCLUDE_SUBDIRECTORY(b100) INCLUDE_SUBDIRECTORY(e100) +INCLUDE_SUBDIRECTORY(e300) INCLUDE_SUBDIRECTORY(x300) INCLUDE_SUBDIRECTORY(b200) diff --git a/host/lib/usrp/dboard/CMakeLists.txt b/host/lib/usrp/dboard/CMakeLists.txt index 9e8653608..3c5bb4fa8 100644 --- a/host/lib/usrp/dboard/CMakeLists.txt +++ b/host/lib/usrp/dboard/CMakeLists.txt @@ -37,5 +37,6 @@ LIBUHD_APPEND_SOURCES( ${CMAKE_CURRENT_SOURCE_DIR}/db_tvrx.cpp ${CMAKE_CURRENT_SOURCE_DIR}/db_dbsrx2.cpp ${CMAKE_CURRENT_SOURCE_DIR}/db_tvrx2.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/db_e3x0.cpp ) diff --git a/host/lib/usrp/dboard/db_e3x0.cpp b/host/lib/usrp/dboard/db_e3x0.cpp new file mode 100644 index 000000000..523927d49 --- /dev/null +++ b/host/lib/usrp/dboard/db_e3x0.cpp @@ -0,0 +1,64 @@ +// +// 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 . +// + +#include +#include +#include + +namespace uhd { namespace usrp { + +/*********************************************************************** + * The E310 dboard + * AD9361 Interface, thus two subdevs + **********************************************************************/ +class e310_dboard : public xcvr_dboard_base{ +public: + e310_dboard(ctor_args_t args) : xcvr_dboard_base(args) {} + + ~e310_dboard(void) {} +}; + +/*********************************************************************** + * The E310 dboard + * AD9364 Interface, thus one subdev + **********************************************************************/ +class e300_dboard : public xcvr_dboard_base{ +public: + e300_dboard(ctor_args_t args) : xcvr_dboard_base(args) {} + + ~e300_dboard(void) {} +}; + +/*********************************************************************** + * Register the E310 dboards + **********************************************************************/ +static dboard_base::sptr make_e310_dboard(dboard_base::ctor_args_t args){ + return dboard_base::sptr(new e310_dboard(args)); +} + +static dboard_base::sptr make_e300_dboard(dboard_base::ctor_args_t args){ + return dboard_base::sptr(new e300_dboard(args)); +} + +}} // namespace + +using namespace uhd::usrp; + +UHD_STATIC_BLOCK(reg_e3x0_dboards){ + dboard_manager::register_dboard(0x0110, &make_e310_dboard, "E310 MIMO XCVR"); + dboard_manager::register_dboard(0x0100, &make_e300_dboard, "E300 SISO XCVR"); +} diff --git a/host/lib/usrp/e300/CMakeLists.txt b/host/lib/usrp/e300/CMakeLists.txt new file mode 100644 index 000000000..9ee9b5521 --- /dev/null +++ b/host/lib/usrp/e300/CMakeLists.txt @@ -0,0 +1,55 @@ +# +# 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 . +# + +######################################################################## +# This file included, use CMake directory variables +######################################################################## + +######################################################################## +# Conditionally configure the USRP-E300 support +######################################################################## +find_package(UDev) + +LIBUHD_REGISTER_COMPONENT("E300" ENABLE_E300 OFF "ENABLE_LIBUHD" OFF) + +IF(ENABLE_E300) + LIST(APPEND E300_SOURCES + ${CMAKE_CURRENT_SOURCE_DIR}/e300_impl.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/e300_io_impl.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/e300_fifo_config.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/e300_sysfs_hooks.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/e300_network.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/e300_global_regs.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/e300_spi.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/e300_sensor_manager.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/e300_i2c.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/e300_eeprom_manager.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/e300_common.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/e300_async_serial.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/e300_ublox_control_impl.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/e300_remote_codec_ctrl.cpp + ) + LIBUHD_APPEND_SOURCES(${E300_SOURCES}) + IF(UDEV_FOUND) + INCLUDE_DIRECTORIES(${UDEV_INCLUDE_DIR}) + LIBUHD_APPEND_LIBS(${UDEV_LIBS}) + SET_SOURCE_FILES_PROPERTIES( + ${E300_SOURCES} + PROPERTIES COMPILE_DEFINITIONS "E300_NATIVE=1" + ) + ENDIF(UDEV_FOUND) +ENDIF(ENABLE_E300) diff --git a/host/lib/usrp/e300/e300_async_serial.cpp b/host/lib/usrp/e300/e300_async_serial.cpp new file mode 100644 index 000000000..cdf18f7f7 --- /dev/null +++ b/host/lib/usrp/e300/e300_async_serial.cpp @@ -0,0 +1,245 @@ +// +// 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 . +// + +#include "e300_async_serial.hpp" + +namespace uhd { namespace usrp { namespace gps { + +async_serial::async_serial() + : _io(), + _port(_io), + _background_thread(), + _open(false), + _error(false) +{ +} + +async_serial::async_serial( + const std::string &node, + const size_t baud_rate, + boost::asio::serial_port_base::parity opt_parity, + boost::asio::serial_port_base::character_size opt_csize, + boost::asio::serial_port_base::flow_control opt_flow, + boost::asio::serial_port_base::stop_bits opt_stop) + : _io(), + _port(_io), + _background_thread(), + _open(false), + _error(false) +{ + open(node, baud_rate, opt_parity, opt_csize, opt_flow, opt_stop); +} + +void async_serial::open( + const std::string &node, + const size_t baud_rate, + boost::asio::serial_port_base::parity opt_parity, + boost::asio::serial_port_base::character_size opt_csize, + boost::asio::serial_port_base::flow_control opt_flow, + boost::asio::serial_port_base::stop_bits opt_stop) +{ + if(is_open()) + close(); + + _set_error_status(true); + _port.open(node); + _port.set_option( + boost::asio::serial_port_base::baud_rate(baud_rate)); + _port.set_option(opt_parity); + _port.set_option(opt_csize); + _port.set_option(opt_flow); + _port.set_option(opt_stop); + + _io.post(boost::bind(&async_serial::_do_read, this)); + + boost::thread t(boost::bind(&boost::asio::io_service::run, &_io)); + _background_thread.swap(t); + _set_error_status(false); + _open=true; +} + +bool async_serial::is_open() const +{ + return _open; +} + +bool async_serial::error_status() const +{ + boost::lock_guard l(_error_mutex); + return _error; +} + +void async_serial::close() +{ + if(!is_open()) + return; + + _open=false; + _io.post(boost::bind(&async_serial::_do_close, this)); + _background_thread.join(); + _io.reset(); + if(error_status()) + throw(boost::system::system_error(boost::system::error_code(), + "Error while closing the device")); +} + +void async_serial::write(const char *data, size_t size) +{ + { + boost::lock_guard l(_write_queue_mutex); + _write_queue.insert(_write_queue.end(), data, data+size); + } + _io.post(boost::bind(&async_serial::_do_write, this)); +} + +void async_serial::write(const std::vector &data) +{ + { + boost::lock_guard l(_write_queue_mutex); + _write_queue.insert( + _write_queue.end(), + data.begin(), + data.end()); + } + _io.post(boost::bind(&async_serial::_do_write, this)); +} + +void async_serial::write_string(const std::string &s) +{ + { + boost::lock_guard l(_write_queue_mutex); + _write_queue.insert( + _write_queue.end(), + s.begin(), + s.end()); + } + _io.post(boost::bind(&async_serial::_do_write, this)); +} + +async_serial::~async_serial() +{ + if(is_open()) { + try { + close(); + } catch(...) { + //Don't throw from a destructor + } + } +} + +void async_serial::_do_read() +{ + _port.async_read_some(boost::asio::buffer( + _read_buffer,READ_BUFFER_SIZE), + boost::bind(&async_serial::_read_end, + this, + boost::asio::placeholders::error, + boost::asio::placeholders::bytes_transferred)); +} + +void async_serial::_read_end( + const boost::system::error_code& error, + size_t bytes_transferred) +{ + if(error) { + if(is_open()) { + _do_close(); + _set_error_status(true); + } + } else { + if(_callback) + _callback( + _read_buffer, + bytes_transferred); + _do_read(); + } +} + +void async_serial::_do_write() +{ + // if a write operation is already in progress, do nothing + if(_write_buffer == 0) { + boost::lock_guard l(_write_queue_mutex); + _write_buffer_size=_write_queue.size(); + _write_buffer.reset(new char[_write_queue.size()]); + std::copy(_write_queue.begin(),_write_queue.end(), + _write_buffer.get()); + _write_queue.clear(); + async_write( + _port, boost::asio::buffer(_write_buffer.get(), + _write_buffer_size), + boost::bind( + &async_serial::_write_end, + this, + boost::asio::placeholders::error)); + } +} + +void async_serial::_write_end(const boost::system::error_code& error) +{ + if(!error) { + boost::lock_guard l(_write_queue_mutex); + if(_write_queue.empty()) { + _write_buffer.reset(); + _write_buffer_size=0; + return; + } + _write_buffer_size = _write_queue.size(); + _write_buffer.reset(new char[_write_queue.size()]); + std::copy(_write_queue.begin(),_write_queue.end(), + _write_buffer.get()); + _write_queue.clear(); + async_write( + _port, + boost::asio::buffer(_write_buffer.get(), + _write_buffer_size), + boost::bind( + &async_serial::_write_end, + this, + boost::asio::placeholders::error)); + } else { + _set_error_status(true); + _do_close(); + } +} + +void async_serial::_do_close() +{ + boost::system::error_code ec; + _port.cancel(ec); + if(ec) + _set_error_status(true); + _port.close(ec); + if(ec) + _set_error_status(true); +} + +void async_serial::_set_error_status(const bool e) +{ + boost::lock_guard l(_error_mutex); + _error = e; +} + + +void async_serial::set_read_callback( + const boost::function &callback) +{ + _callback = callback; +} + + +}}} // namespace diff --git a/host/lib/usrp/e300/e300_async_serial.hpp b/host/lib/usrp/e300/e300_async_serial.hpp new file mode 100644 index 000000000..fafc7de3d --- /dev/null +++ b/host/lib/usrp/e300/e300_async_serial.hpp @@ -0,0 +1,113 @@ +// +// 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 . +// + +#ifndef INCLUDED_ASYNC_SERIAL_HPP +#define INCLUDED_ASYNC_SERIAL_HPP + +#include +#include +#include +#include +#include +#include + +namespace uhd { namespace usrp { namespace gps { + +class async_serial : private boost::noncopyable +{ +public: + async_serial(); + ~async_serial(); + + async_serial(const std::string &node, const size_t baud_rate, + boost::asio::serial_port_base::parity opt_parity= + boost::asio::serial_port_base::parity( + boost::asio::serial_port_base::parity::none), + boost::asio::serial_port_base::character_size opt_csize= + boost::asio::serial_port_base::character_size(8), + boost::asio::serial_port_base::flow_control opt_flow= + boost::asio::serial_port_base::flow_control( + boost::asio::serial_port_base::flow_control::none), + boost::asio::serial_port_base::stop_bits opt_stop= + boost::asio::serial_port_base::stop_bits( + boost::asio::serial_port_base::stop_bits::one)); + + void open(const std::string& node, const size_t baud_rate, + boost::asio::serial_port_base::parity opt_parity= + boost::asio::serial_port_base::parity( + boost::asio::serial_port_base::parity::none), + boost::asio::serial_port_base::character_size opt_csize= + boost::asio::serial_port_base::character_size(8), + boost::asio::serial_port_base::flow_control opt_flow= + boost::asio::serial_port_base::flow_control( + boost::asio::serial_port_base::flow_control::none), + boost::asio::serial_port_base::stop_bits opt_stop= + boost::asio::serial_port_base::stop_bits( + boost::asio::serial_port_base::stop_bits::one)); + + bool is_open(void) const; + + bool error_status(void) const; + + void close(void); + + void write(const char *data, const size_t size); + void write(const std::vector &data); + + void write_string(const std::string &s); + + static const size_t READ_BUFFER_SIZE=512; + + void set_read_callback( + const boost::function& callback); + + void clear_callback(); + +private: // methods + void _do_read(); + + void _read_end( + const boost::system::error_code &error, + size_t bytes_transferred); + + void _do_write(); + + void _write_end(const boost::system::error_code &error); + + void _do_close(); + + void _set_error_status(const bool e); +private: // members + boost::asio::io_service _io; + boost::asio::serial_port _port; + boost::thread _background_thread; + bool _open; + bool _error; + mutable boost::mutex _error_mutex; + + std::vector _write_queue; + boost::shared_array _write_buffer; + size_t _write_buffer_size; + boost::mutex _write_queue_mutex; + char _read_buffer[READ_BUFFER_SIZE]; + + boost::function _callback; +}; + +}}} // namespace + +#endif //INCLUDED_ASYNC_SERIAL_HPP diff --git a/host/lib/usrp/e300/e300_common.cpp b/host/lib/usrp/e300/e300_common.cpp new file mode 100644 index 000000000..97e906be7 --- /dev/null +++ b/host/lib/usrp/e300/e300_common.cpp @@ -0,0 +1,59 @@ +// +// 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 . +// +#include + +#include "e300_fifo_config.hpp" +#include "e300_fifo_config.hpp" + +#include "e300_common.hpp" + +#include +#include + +namespace uhd { namespace usrp { namespace e300 { + +namespace common { + +void load_fpga_image(const std::string &path) +{ + if (not boost::filesystem::exists("/dev/xdevcfg")) + ::system("mknod /dev/xdevcfg c 259 0"); + + UHD_MSG(status) << "Loading FPGA image: " << path << "..." << std::flush; + + std::ifstream fpga_file(path.c_str(), std::ios_base::binary); + UHD_ASSERT_THROW(fpga_file.good()); + + std::FILE *wfile; + wfile = std::fopen("/dev/xdevcfg", "wb"); + UHD_ASSERT_THROW(!(wfile == NULL)); + + char buff[16384]; // devcfg driver can't handle huge writes + do { + fpga_file.read(buff, sizeof(buff)); + std::fwrite(buff, 1, fpga_file.gcount(), wfile); + } while (fpga_file); + + fpga_file.close(); + std::fclose(wfile); + + UHD_MSG(status) << " done" << std::endl; +} + +} + +}}} diff --git a/host/lib/usrp/e300/e300_common.hpp b/host/lib/usrp/e300/e300_common.hpp new file mode 100644 index 000000000..d9a0afd9e --- /dev/null +++ b/host/lib/usrp/e300/e300_common.hpp @@ -0,0 +1,31 @@ +// +// Copyright 2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +#ifndef INCLUDED_E300_COMMON_HPP +#define INCLUDED_E300_COMMON_HPP + +namespace uhd { namespace usrp { namespace e300 { + +namespace common { + +void load_fpga_image(const std::string &path); + +}; + +}}} + +#endif // INCLUDED_E300_COMMON_HPP diff --git a/host/lib/usrp/e300/e300_defaults.hpp b/host/lib/usrp/e300/e300_defaults.hpp new file mode 100644 index 000000000..9c0e5df1d --- /dev/null +++ b/host/lib/usrp/e300/e300_defaults.hpp @@ -0,0 +1,82 @@ +// +// Copyright 2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +#ifndef INCLUDED_E300_DEFAULTS_HPP +#define INCLUDED_E300_DEFAULTS_HPP + +#include "ad9361_client.h" + +namespace uhd { namespace usrp { namespace e300 { + +static const double DEFAULT_TICK_RATE = 32e6; +static const double MAX_TICK_RATE = 50e6; +static const double MIN_TICK_RATE = 1e6; + +static const double DEFAULT_TX_SAMP_RATE = 1.0e6; +static const double DEFAULT_RX_SAMP_RATE = 1.0e6; +static const double DEFAULT_DDC_FREQ = 0.0; +static const double DEFAULT_DUC_FREQ = 0.0; + +static const double DEFAULT_FE_GAIN = 0.0; +static const double DEFAULT_FE_FREQ = 1.0e9; +static const double DEFAULT_FE_BW = 56e6; + +static const std::string DEFAULT_TIME_SRC = "none"; +static const std::string DEFAULT_CLOCK_SRC = "internal"; + +static const size_t DEFAULT_RX_DATA_FRAME_SIZE = 4096; +static const size_t DEFAULT_RX_DATA_NUM_FRAMES = 32; + +static const size_t DEFAULT_TX_DATA_FRAME_SIZE = 2048; +static const size_t DEFAULT_TX_DATA_NUM_FRAMES = 32; + +static const size_t DEFAULT_CTRL_FRAME_SIZE = 64; +static const size_t DEFAULT_CTRL_NUM_FRAMES = 32; + +static const size_t MAX_NET_RX_DATA_FRAME_SIZE = 1200; +static const size_t MAX_NET_TX_DATA_FRAME_SIZE = 1200; + +class e300_ad9361_client_t : public ad9361_params { +public: + ~e300_ad9361_client_t() {} + double get_band_edge(frequency_band_t band) { + switch (band) { + case AD9361_RX_BAND0: return 1.2e9; + case AD9361_RX_BAND1: return 2.6e9; + case AD9361_TX_BAND0: return 2940.0e6; + default: return 0; + } + } + clocking_mode_t get_clocking_mode() { + return AD9361_XTAL_N_CLK_PATH; + } + digital_interface_mode_t get_digital_interface_mode() { + return AD9361_DDR_FDD_LVCMOS; + } + digital_interface_delays_t get_digital_interface_timing() { + digital_interface_delays_t delays; + delays.rx_clk_delay = 0; + delays.rx_data_delay = 0xF; + delays.tx_clk_delay = 0; + delays.tx_data_delay = 0xF; + return delays; + } +}; + +}}} // namespace + +#endif // INCLUDED_E300_DEFAULTS_HPP diff --git a/host/lib/usrp/e300/e300_eeprom_manager.cpp b/host/lib/usrp/e300/e300_eeprom_manager.cpp new file mode 100644 index 000000000..af1235b6b --- /dev/null +++ b/host/lib/usrp/e300/e300_eeprom_manager.cpp @@ -0,0 +1,236 @@ +// +// 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 . +// + +#include "e300_eeprom_manager.hpp" +#include +#include + +namespace uhd { namespace usrp { namespace e300 { + +static const std::string _bytes_to_string(const uint8_t* bytes, size_t max_len) +{ + std::string out; + for (size_t i = 0; i < max_len; i++) { + if (bytes[i] < 32 or bytes[i] > 127) return out; + out += bytes[i]; + } + return out; +} + +static void _string_to_bytes(const std::string &string, size_t max_len, uint8_t* buffer) +{ + byte_vector_t bytes; + const size_t len = std::min(string.size(), max_len); + for (size_t i = 0; i < len; i++){ + buffer[i] = string[i]; + } + if (len < max_len - 1) + buffer[len] = '\0'; +} + +e300_eeprom_manager::e300_eeprom_manager(i2c::sptr i2c) : _i2c(i2c) +{ + read_mb_eeprom(); + read_db_eeprom(); +} + +e300_eeprom_manager::~e300_eeprom_manager(void) +{ +} + +const mboard_eeprom_t& e300_eeprom_manager::read_mb_eeprom(void) +{ + boost::mutex::scoped_lock(_mutex); + + std::vector bytes; + bytes.resize(sizeof(mb_eeprom_map_t)); + mb_eeprom_map_t *map_ptr = reinterpret_cast(&bytes[0]); + memset(map_ptr, 0xff, sizeof(mb_eeprom_map_t)); + + // get the old contents + for(size_t i = 0; i < sizeof(mb_eeprom_map_t); i++) + bytes[i] = _i2c->get_i2c_reg8(MB_ADDR, i); + + mb_eeprom_map_t &map = *map_ptr; + + _mb_eeprom["product"] = boost::lexical_cast( + uhd::ntohx(map.hw_product)); + _mb_eeprom["revision"] = boost::lexical_cast( + uhd::ntohx(map.hw_revision)); + _mb_eeprom["serial"] = _bytes_to_string( + map.serial, MB_SERIAL_LEN); + + byte_vector_t mac_addr(map.mac_addr, map.mac_addr + 6); + _mb_eeprom["mac-addr"] = mac_addr_t::from_bytes(mac_addr).to_string(); + + _mb_eeprom["name"] = _bytes_to_string( + map.user_name, MB_NAME_LEN); + + return _mb_eeprom; +} + +const dboard_eeprom_t& e300_eeprom_manager::read_db_eeprom(void) +{ + boost::mutex::scoped_lock(_mutex); + + std::vector bytes; + bytes.resize(sizeof(db_eeprom_map_t)); + db_eeprom_map_t *map_ptr = reinterpret_cast(&bytes[0]); + memset(map_ptr, 0xff, sizeof(db_eeprom_map_t)); + + // get the old contents + for(size_t i = 0; i < sizeof(db_eeprom_map_t); i++) + bytes[i] = _i2c->get_i2c_reg16(DB_ADDR, i); + + db_eeprom_map_t &map = *map_ptr; + + _db_eeprom.id = uhd::usrp::dboard_id_t::from_uint16( + uhd::ntohx(map.hw_product)); + + _db_eeprom.revision = boost::lexical_cast( + uhd::ntohx(map.hw_revision)); + _db_eeprom.serial = _bytes_to_string( + map.serial, DB_SERIAL_LEN); + + return _db_eeprom; +} + +void e300_eeprom_manager::write_db_eeprom(const dboard_eeprom_t& eeprom) +{ + boost::mutex::scoped_lock(_mutex); + _db_eeprom = eeprom; + std::vector bytes; + bytes.resize(sizeof(db_eeprom_map_t)); + + + db_eeprom_map_t *map_ptr = reinterpret_cast(&bytes[0]); + memset(map_ptr, 0xff, sizeof(db_eeprom_map_t)); + + // get the old contents + for(size_t i = 0; i < sizeof(db_eeprom_map_t); i++) + bytes[i] = _i2c->get_i2c_reg16(DB_ADDR, i); + + db_eeprom_map_t &map = *map_ptr; + + if (_db_eeprom.id != dboard_id_t::none()) { + map.hw_product = uhd::htonx( + _db_eeprom.id.to_uint16()); + } + + if (not _db_eeprom.revision.empty()) { + map.hw_revision = uhd::htonx( + boost::lexical_cast(_db_eeprom.revision)); + } + + if (not _db_eeprom.serial.empty()) { + _string_to_bytes(_db_eeprom.serial, DB_SERIAL_LEN, map.serial); + } + for(size_t i = 0; i < sizeof(mb_eeprom_map_t); i++) + _i2c->set_i2c_reg16(DB_ADDR, i, bytes[i]); +} + +void e300_eeprom_manager::write_mb_eeprom(const mboard_eeprom_t& eeprom) +{ + boost::mutex::scoped_lock(_mutex); + _mb_eeprom = eeprom; + std::vector bytes; + bytes.resize(sizeof(mb_eeprom_map_t)); + + + mb_eeprom_map_t *map_ptr = reinterpret_cast(&bytes[0]); + memset(map_ptr, 0xff, sizeof(mb_eeprom_map_t)); + + // get the old contents + for(size_t i = 0; i < sizeof(mb_eeprom_map_t); i++) + bytes[i] = _i2c->get_i2c_reg8(MB_ADDR, i); + + mb_eeprom_map_t &map = *map_ptr; + + if (_mb_eeprom.has_key("product")) { + map.hw_product = uhd::htonx( + boost::lexical_cast(_mb_eeprom["product"])); + } + if (_mb_eeprom.has_key("revision")) { + map.hw_revision = uhd::htonx( + boost::lexical_cast(_mb_eeprom["revision"])); + } + if (_mb_eeprom.has_key("serial")) { + _string_to_bytes(_mb_eeprom["serial"], MB_SERIAL_LEN, map.serial); + } + if (_mb_eeprom.has_key("mac-addr")) { + byte_vector_t mac_addr = mac_addr_t::from_string(_mb_eeprom["mac-addr"]).to_bytes(); + std::copy(mac_addr.begin(), mac_addr.end(), map.mac_addr); + } + + //store the name + if (_mb_eeprom.has_key("name")) { + _string_to_bytes(_mb_eeprom["name"], MB_NAME_LEN, map.user_name); + } + + for(size_t i = 0; i < sizeof(mb_eeprom_map_t); i++) + _i2c->set_i2c_reg8(MB_ADDR, i, bytes[i]); + +} + +e300_eeprom_manager::mboard_t e300_eeprom_manager::get_mb_type(void) const +{ + boost::mutex::scoped_lock(_mutex); + boost::uint16_t pid = boost::lexical_cast( + _mb_eeprom["product"]); + return get_mb_type(pid); +} + +e300_eeprom_manager::mboard_t e300_eeprom_manager::get_mb_type( + boost::uint16_t pid) +{ + switch (pid) { + case E300_MB_PID: + return USRP_E300_MB; + + case E310_MB_PID: + return USRP_E310_MB; + + default: + return UNKNOWN; + }; +} + + +std::string e300_eeprom_manager::get_mb_type_string(void) const +{ + boost::mutex::scoped_lock(_mutex); + boost::uint16_t product = boost::lexical_cast( + _mb_eeprom["product"]); + switch (product) { + case E300_MB_PID: + return "E300"; + + case E310_MB_PID: + return "E310"; + + default: + return "UNKNOWN"; + }; +} + +i2c::sptr e300_eeprom_manager::get_i2c_sptr(void) +{ + return _i2c; +} + + +}}} // namespace diff --git a/host/lib/usrp/e300/e300_eeprom_manager.hpp b/host/lib/usrp/e300/e300_eeprom_manager.hpp new file mode 100644 index 000000000..e77f25ed5 --- /dev/null +++ b/host/lib/usrp/e300/e300_eeprom_manager.hpp @@ -0,0 +1,125 @@ +// +// Copyright 2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +#ifndef INCLUDED_E300_EEPROM_MANAGER_HPP +#define INCLUDED_E300_EEPROM_MANAGER_HPP + +#include +#include +#include +#include +#include + +#include "e300_i2c.hpp" + +namespace uhd { namespace usrp { namespace e300 { + +static const boost::uint16_t E300_MB_PID = 0x77d1; +static const boost::uint16_t E310_MB_PID = 0x77d2; + +static const boost::uint16_t E300_DB_PID = 0x0100; +static const boost::uint16_t E310_DB_PID = 0x0110; + +class e300_eeprom_manager : boost::noncopyable +{ +public: + typedef boost::shared_ptr sptr; + e300_eeprom_manager(i2c::sptr i2c); + ~e300_eeprom_manager(); + + // mboard + const mboard_eeprom_t& read_mb_eeprom(); + void write_mb_eeprom(const mboard_eeprom_t& eeprom); + + UHD_INLINE const mboard_eeprom_t& get_mb_eeprom() + { + return _mb_eeprom; + } + + // dboard + const dboard_eeprom_t& read_db_eeprom(); + void write_db_eeprom(const dboard_eeprom_t& eeprom); + + UHD_INLINE const dboard_eeprom_t& get_db_eeprom() + { + return _db_eeprom; + } + + + i2c::sptr get_i2c_sptr(void); + + enum mboard_t {USRP_E300_MB, USRP_E310_MB, UNKNOWN}; + + mboard_t get_mb_type(void) const; + static mboard_t get_mb_type(boost::uint16_t pid); + std::string get_mb_type_string(void) const; + +private: // types + const static size_t MB_SERIAL_LEN = 6; + const static size_t MB_NAME_LEN = 32; + const static size_t MB_ADDR = 0x51; + + const static size_t DB_SERIAL_LEN = 6; + const static size_t DB_ADDR = 0x50; + + struct mb_eeprom_map_t + { + // Data format version + boost::uint16_t data_version_major; + boost::uint16_t data_version_minor; + + // NIC mac address + boost::uint8_t mac_addr[6]; + + // HW identification info + boost::uint16_t hw_product; + boost::uint16_t hw_revision; + + // serial + boost::uint8_t serial[MB_SERIAL_LEN]; + boost::uint8_t pad[20 - MB_SERIAL_LEN]; + + //User specific + boost::uint8_t user_name[MB_NAME_LEN]; + }; + + struct db_eeprom_map_t + { + // Data format version + boost::uint16_t data_version_major; + boost::uint16_t data_version_minor; + + // HW identification info + boost::uint16_t hw_product; + boost::uint16_t hw_revision; + + // serial + boost::uint8_t serial[MB_SERIAL_LEN]; + boost::uint8_t pad[20 - MB_SERIAL_LEN]; + }; + +private: // members + mboard_eeprom_t _mb_eeprom; + dboard_eeprom_t _db_eeprom; + i2c::sptr _i2c; + + boost::mutex _mutex; +}; + +}}} //namespace + +#endif // INCLUDED_E300_EEPROM_MANAGER_HPP diff --git a/host/lib/usrp/e300/e300_fifo_config.cpp b/host/lib/usrp/e300/e300_fifo_config.cpp new file mode 100644 index 000000000..ac4ace7f2 --- /dev/null +++ b/host/lib/usrp/e300/e300_fifo_config.cpp @@ -0,0 +1,429 @@ +// +// 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 . +// + +#ifdef E300_NATIVE + +#include +#include + +// constants coded into the fpga parameters +static const size_t ZF_CONFIG_BASE = 0x40000000; +static const size_t ZF_PAGE_WIDTH = 10; +static const size_t H2S_STREAMS_WIDTH = 4; +static const size_t H2S_CMDFIFO_DEPTH = 5; +static const size_t S2H_STREAMS_WIDTH = 4; +static const size_t S2H_CMDFIFO_DEPTH = 5; + +// calculate more useful constants for this module +static const size_t ZF_PAGE_SIZE(1 << ZF_PAGE_WIDTH); +static const size_t H2S_NUM_STREAMS(1 << H2S_STREAMS_WIDTH); +static const size_t H2S_NUM_CMDS(1 << H2S_CMDFIFO_DEPTH); +static const size_t S2H_NUM_STREAMS(1 << S2H_STREAMS_WIDTH); +static const size_t S2H_NUM_CMDS(1 << S2H_CMDFIFO_DEPTH); + +//offsetsinto the arbiter memory map +static const size_t ARBITER_WR_CLEAR = 0; +static const size_t ARBITER_RD_SIG = 0; +static const size_t ARBITER_WR_ADDR = 4; +static const size_t ARBITER_WR_SIZE = 8; +static const size_t ARBITER_WR_STS_RDY = 12; +static const size_t ARBITER_WR_STS = 16; +static const size_t ARBITER_RB_STATUS = 16; +static const size_t ARBITER_RB_STATUS_OCC = 20; +static const size_t ARBITER_RB_ADDR_SPACE = 24; +static const size_t ARBITER_RB_SIZE_SPACE = 28; + +// registers for the wb32_iface +static const size_t SR_CORE_READBACK = 0; + + +static UHD_INLINE size_t S2H_BASE(const size_t base) +{ + return base + ZF_PAGE_SIZE * 0; +} + +static UHD_INLINE size_t H2S_BASE(const size_t base) +{ + return base + ZF_PAGE_SIZE * 1; +} + +static UHD_INLINE size_t REG_BASE(const size_t base) +{ + return base + ZF_PAGE_SIZE * 2; +} + +static UHD_INLINE size_t DST_BASE(const size_t base) +{ + return base + ZF_PAGE_SIZE * 3; +} + +static UHD_INLINE size_t ZF_STREAM_OFF(const size_t which) +{ + return which * 32; +} + +#include "e300_fifo_config.hpp" +#include //mmap +#include //open, close +#include //poll +#include +#include +#include +#include //sleep +#include //timeout +#include +#include + +//locking stuff for shared irq +#include +#include + +struct e300_fifo_poll_waiter +{ + e300_fifo_poll_waiter(const int fd): + fd(fd) + { + //NOP + } + + void wait(const double timeout) + { + if (_poll_claimed.cas(1, 0)) + { + boost::mutex::scoped_lock l(mutex); + cond.wait(l); + } + else + { + struct pollfd fds[1]; + fds[0].fd = fd; + fds[0].events = POLLIN; + ::poll(fds, 1, long(timeout*1000)); + if (fds[0].revents & POLLIN) + ::read(fd, NULL, 0); + + _poll_claimed.write(0); + cond.notify_all(); + } + } + + uhd::atomic_uint32_t _poll_claimed; + boost::condition_variable cond; + boost::mutex mutex; + int fd; +}; + +static const size_t DEFAULT_FRAME_SIZE = 2048; +static const size_t DEFAULT_NUM_FRAMES = 32; + +using namespace uhd; +using namespace uhd::transport; + +struct __mem_addrz_t +{ + size_t which, phys, data, ctrl; +}; + +/*********************************************************************** + * peek n' poke mmapped space + **********************************************************************/ +UHD_INLINE void zf_poke32(const boost::uint32_t addr, const boost::uint32_t data) +{ + volatile boost::uint32_t *p = reinterpret_cast(addr); + *p = data; +} + +UHD_INLINE boost::uint32_t zf_peek32(const boost::uint32_t addr) +{ + volatile const boost::uint32_t *p = reinterpret_cast(addr); + return *p; +} + +/*********************************************************************** + * managed buffer + **********************************************************************/ +struct e300_fifo_mb : managed_buffer +{ + e300_fifo_mb(const __mem_addrz_t &addrs, const size_t len): + ctrl_base(addrs.ctrl), phys_mem(addrs.phys), mem((void *)addrs.data), len(len){} + + void release(void) + { + UHD_ASSERT_THROW(zf_peek32(ctrl_base+ARBITER_RB_ADDR_SPACE) > 0); + UHD_ASSERT_THROW(zf_peek32(ctrl_base+ARBITER_RB_SIZE_SPACE) > 0); + zf_poke32(ctrl_base + ARBITER_WR_ADDR, phys_mem); + zf_poke32(ctrl_base + ARBITER_WR_SIZE, this->size()); + } + + template + UHD_INLINE typename T::sptr get_new(void) + { + return make(reinterpret_cast(this), mem, len); + } + + const size_t ctrl_base; + const size_t phys_mem; + void *const mem; + const size_t len; +}; + +/*********************************************************************** + * transport + **********************************************************************/ +class e300_transport : public zero_copy_if +{ +public: + e300_transport( + boost::shared_ptr allocator, + const __mem_addrz_t &addrs, + const size_t num_frames, + const size_t frame_size, + e300_fifo_poll_waiter *waiter, + const bool auto_release + ): + _allocator(allocator), + _addrs(addrs), + _num_frames(num_frames), + _frame_size(frame_size), + _index(0), + _waiter(waiter) + { + //UHD_MSG(status) << boost::format("phys 0x%x") % addrs.phys << std::endl; + //UHD_MSG(status) << boost::format("data 0x%x") % addrs.data << std::endl; + //UHD_MSG(status) << boost::format("ctrl 0x%x") % addrs.ctrl << std::endl; + + const boost::uint32_t sig = zf_peek32(_addrs.ctrl + ARBITER_RD_SIG); + UHD_ASSERT_THROW((sig >> 16) == 0xACE0); + + zf_poke32(_addrs.ctrl + ARBITER_WR_CLEAR, 1); + for (size_t i = 0; i < num_frames; i++) + { + //create a managed buffer at the given offset + __mem_addrz_t mb_addrs = addrs; + mb_addrs.phys += (i*frame_size); + mb_addrs.data += (i*frame_size); + boost::shared_ptr mb(new e300_fifo_mb(mb_addrs, frame_size)); + + //setup the buffers so they are "positioned for use" + const size_t sts_good = (1 << 7) | (_addrs.which & 0xf); + if (auto_release) mb->get_new(); //release for read + else zf_poke32(_addrs.ctrl + ARBITER_WR_STS, sts_good); //poke an ok into the sts fifo + + _buffs.push_back(mb); + } + } + + ~e300_transport(void) + { + //NOP + } + + template + UHD_INLINE typename T::sptr get_buff(const double timeout) + { + const time_spec_t exit_time = time_spec_t::get_system_time() + time_spec_t(timeout); + do + { + if (zf_peek32(_addrs.ctrl + ARBITER_RB_STATUS_OCC)) + { + const boost::uint32_t sts = zf_peek32(_addrs.ctrl + ARBITER_RB_STATUS); + UHD_ASSERT_THROW((sts >> 7) & 0x1); //assert OK + UHD_ASSERT_THROW((sts & 0xf) == _addrs.which); //expected tag + zf_poke32(_addrs.ctrl + ARBITER_WR_STS_RDY, 1); //pop from sts fifo + if (_index == _num_frames) + _index = 0; + return _buffs[_index++]->get_new(); + } + _waiter->wait(timeout); + //boost::this_thread::sleep(boost::posix_time::milliseconds(1)); + } + while (time_spec_t::get_system_time() < exit_time); + + return typename T::sptr(); + } + + managed_recv_buffer::sptr get_recv_buff(const double timeout) + { + return this->get_buff(timeout); + } + + size_t get_num_recv_frames(void) const + { + return _num_frames; + } + + size_t get_recv_frame_size(void) const + { + return _frame_size; + } + + managed_send_buffer::sptr get_send_buff(const double timeout) + { + return this->get_buff(timeout); + } + + size_t get_num_send_frames(void) const + { + return _num_frames; + } + + size_t get_send_frame_size(void) const + { + return _frame_size; + } + +private: + boost::shared_ptr _allocator; + const __mem_addrz_t _addrs; + const size_t _num_frames; + const size_t _frame_size; + size_t _index; + e300_fifo_poll_waiter *_waiter; + std::vector > _buffs; +}; + +/*********************************************************************** + * memory mapping + **********************************************************************/ +class e300_fifo_interface_impl : public virtual e300_fifo_interface +{ +public: + e300_fifo_interface_impl(const e300_fifo_config_t &config): + _config(config), + _bytes_in_use(0), + _recv_entries_in_use(std::vector(S2H_NUM_STREAMS, 0)), + _send_entries_in_use(std::vector(H2S_NUM_STREAMS, 0)) + { + //open the file descriptor to our kernel module + const std::string dev = "/dev/axi_fpga"; + _fd = ::open(dev.c_str(), O_RDWR|O_SYNC); + if (_fd < 0) + { + throw uhd::runtime_error("e300: failed to open " + dev); + } + + //mmap the control and data regions into virtual space + //UHD_VAR(_config.ctrl_length); + //UHD_VAR(_config.buff_length); + //UHD_VAR(_config.phys_addr); + _buff = ::mmap(NULL, _config.ctrl_length + _config.buff_length, PROT_READ|PROT_WRITE, MAP_SHARED, _fd, 0); + if (_buff == MAP_FAILED) + { + ::close(_fd); + throw uhd::runtime_error("e300: failed to mmap " + dev); + } + + //segment the memory according to zynq fifo arbiter + _ctrl_space = size_t(_buff); + _data_space = size_t(_buff) + _config.ctrl_length; + + //zero out the data region + std::memset((void *)_data_space, 0, _config.buff_length); + + //create a poll _waiter for the transports + _waiter = new e300_fifo_poll_waiter(_fd); + } + + virtual ~e300_fifo_interface_impl(void) + { + delete _waiter; + UHD_LOG << "cleanup: munmap" << std::endl; + ::munmap(_buff, _config.ctrl_length + _config.buff_length); + ::close(_fd); + } + + uhd::transport::zero_copy_if::sptr make_recv_xport( + const size_t which_stream, + const uhd::transport::zero_copy_xport_params ¶ms) + { + return this->_make_xport(which_stream, params, true); + } + + uhd::transport::zero_copy_if::sptr make_send_xport( + const size_t which_stream, + const uhd::transport::zero_copy_xport_params ¶ms) + { + return this->_make_xport(which_stream, params, false); + } + + size_t get_global_regs_base() const + { + return REG_BASE(_ctrl_space); + } + +private: + uhd::transport::zero_copy_if::sptr _make_xport( + const size_t which_stream, + const uhd::transport::zero_copy_xport_params ¶ms, + const bool is_recv) + { + boost::mutex::scoped_lock lock(_setup_mutex); + + const size_t frame_size = is_recv ? params.recv_frame_size : params.send_frame_size; + const size_t num_frames = is_recv ? params.num_recv_frames : params.num_send_frames; + size_t &entries_in_use = (is_recv)? _recv_entries_in_use.at(which_stream) + : _send_entries_in_use.at(which_stream); + + __mem_addrz_t addrs; + addrs.which = which_stream; + addrs.phys = _config.phys_addr + _bytes_in_use; + addrs.data = _data_space + _bytes_in_use; + addrs.ctrl = ((is_recv)? S2H_BASE(_ctrl_space) : H2S_BASE(_ctrl_space)) + ZF_STREAM_OFF(which_stream); + + uhd::transport::zero_copy_if::sptr xport; + if (is_recv) xport.reset(new e300_transport(shared_from_this(), addrs, num_frames, frame_size, _waiter, is_recv)); + else xport.reset(new e300_transport(shared_from_this(), addrs, num_frames, frame_size, _waiter, is_recv)); + + _bytes_in_use += num_frames*frame_size; + entries_in_use += num_frames; + + UHD_ASSERT_THROW(_recv_entries_in_use.at(which_stream) <= S2H_NUM_CMDS); + UHD_ASSERT_THROW(_send_entries_in_use.at(which_stream) <= H2S_NUM_CMDS); + UHD_ASSERT_THROW(_bytes_in_use <= _config.buff_length); + + + return xport; + } + + e300_fifo_config_t _config; + e300_fifo_poll_waiter *_waiter; + size_t _bytes_in_use; + int _fd; + void *_buff; + size_t _ctrl_space; + size_t _data_space; + std::vector _recv_entries_in_use; + std::vector _send_entries_in_use; + boost::mutex _setup_mutex; +}; + +e300_fifo_interface::sptr e300_fifo_interface::make(const e300_fifo_config_t &config) +{ + return e300_fifo_interface::sptr(new e300_fifo_interface_impl(config)); +} + +#else //E300_NATIVE + +#include "e300_fifo_config.hpp" +#include + +e300_fifo_interface::sptr e300_fifo_interface::make(const e300_fifo_config_t &) +{ + throw uhd::assertion_error("e300_fifo_interface::make() !E300_NATIVE"); +} + +#endif //E300_NATIVE diff --git a/host/lib/usrp/e300/e300_fifo_config.hpp b/host/lib/usrp/e300/e300_fifo_config.hpp new file mode 100644 index 000000000..967f451ca --- /dev/null +++ b/host/lib/usrp/e300/e300_fifo_config.hpp @@ -0,0 +1,52 @@ +// +// 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 . +// + +#ifndef INCLUDED_E300_FIFO_CONFIG_HPP +#define INCLUDED_E300_FIFO_CONFIG_HPP + +#include +#include +#include +#include + +struct e300_fifo_config_t +{ + size_t ctrl_length; + size_t buff_length; + size_t phys_addr; +}; + +e300_fifo_config_t e300_read_sysfs(void); +std::string e300_get_sysfs_attr(const std::string &node, const std::string &attr); + +struct e300_fifo_interface : boost::enable_shared_from_this +{ + typedef boost::shared_ptr sptr; + static sptr make(const e300_fifo_config_t &config); + + virtual uhd::transport::zero_copy_if::sptr make_recv_xport( + const size_t which_stream, + const uhd::transport::zero_copy_xport_params ¶ms) = 0; + + virtual uhd::transport::zero_copy_if::sptr make_send_xport( + const size_t which_stream, + const uhd::transport::zero_copy_xport_params &parms) = 0; + + virtual size_t get_global_regs_base(void) const = 0; +}; + +#endif /* INCLUDED_E300_FIFO_CONFIG_HPP */ diff --git a/host/lib/usrp/e300/e300_fpga_defs.hpp b/host/lib/usrp/e300/e300_fpga_defs.hpp new file mode 100644 index 000000000..d58fd63a9 --- /dev/null +++ b/host/lib/usrp/e300/e300_fpga_defs.hpp @@ -0,0 +1,29 @@ +// +// Copyright 2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +#ifndef INCLUDED_E300_FPGA_DEFS_HPP +#define INCLUDED_E300_FPGA_DEFS_HPP +namespace uhd { namespace usrp { namespace e300 { namespace fpga { + +static const size_t NUM_RADIOS = 2; + +static const boost::uint32_t COMPAT_MAJOR = 4; +static const boost::uint32_t COMPAT_MINOR = 0; + +}}}} // namespace + +#endif // INCLUDED_E300_FPGA_DEFS_HPP diff --git a/host/lib/usrp/e300/e300_global_regs.cpp b/host/lib/usrp/e300/e300_global_regs.cpp new file mode 100644 index 000000000..3ba895826 --- /dev/null +++ b/host/lib/usrp/e300/e300_global_regs.cpp @@ -0,0 +1,131 @@ +// +// 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 . +// + +#include "e300_global_regs.hpp" + +#include +#include +#include +#include +#include + +namespace uhd { namespace usrp { namespace e300 { + +class global_regs_local_impl : public global_regs +{ +public: + global_regs_local_impl(const size_t ctrl_base) : _ctrl_base(ctrl_base) + { + } + + virtual ~global_regs_local_impl(void) + { + } + + boost::uint32_t peek32(const uhd::wb_iface::wb_addr_type addr) + { + // setup readback register + _poke32(_ctrl_base + global_regs::SR_CORE_READBACK, addr); + return _peek32(_ctrl_base); + } + + void poke32(const uhd::wb_iface::wb_addr_type addr, const boost::uint32_t data) + { + _poke32(_ctrl_base + static_cast(addr), data); + } + + +private: + const size_t _ctrl_base; + + UHD_INLINE void _poke32(const boost::uint32_t addr, const boost::uint32_t data) + { + volatile boost::uint32_t *p = reinterpret_cast(addr); + *p = data; + } + + UHD_INLINE boost::uint32_t _peek32(const boost::uint32_t addr) + { + volatile const boost::uint32_t *p = reinterpret_cast(addr); + return *p; + } +}; + +global_regs::sptr global_regs::make(const size_t ctrl_base) +{ + return sptr(new global_regs_local_impl(ctrl_base)); +} + +class global_regs_zc_impl : public global_regs +{ +public: + global_regs_zc_impl(uhd::transport::zero_copy_if::sptr xport) : _xport(xport) + { + } + + virtual ~global_regs_zc_impl(void) + { + } + + boost::uint32_t peek32(const uhd::wb_iface::wb_addr_type addr) + { + global_regs_transaction_t transaction; + transaction.is_poke = uhd::htonx(0); + transaction.addr = uhd::htonx( + static_cast(addr)); + { + uhd::transport::managed_send_buffer::sptr buff = _xport->get_send_buff(10.0); + if (not buff or buff->size() < sizeof(transaction)) + throw std::runtime_error("global_regs_zc_impl send timeout"); + std::memcpy(buff->cast(), &transaction, sizeof(transaction)); + buff->commit(sizeof(transaction)); + } + { + uhd::transport::managed_recv_buffer::sptr buff = _xport->get_recv_buff(10.0); + if (not buff or buff->size() < sizeof(transaction)) + throw std::runtime_error("global_regs_zc_impl recv timeout"); + std::memcpy(&transaction, buff->cast(), sizeof(transaction)); + } + return uhd::ntohx(transaction.data); + } + + void poke32(const uhd::wb_iface::wb_addr_type addr, const boost::uint32_t data) + { + global_regs_transaction_t transaction; + transaction.is_poke = uhd::htonx(1); + transaction.addr = uhd::htonx( + static_cast(addr)); + transaction.data = uhd::htonx(data); + { + uhd::transport::managed_send_buffer::sptr buff = _xport->get_send_buff(10.0); + if (not buff or buff->size() < sizeof(transaction)) + throw uhd::runtime_error("global_regs_zc_impl send timeout"); + std::memcpy(buff->cast(), &transaction, sizeof(transaction)); + buff->commit(sizeof(transaction)); + } + } + +private: + uhd::transport::zero_copy_if::sptr _xport; +}; + +global_regs::sptr global_regs::make(uhd::transport::zero_copy_if::sptr xport) +{ + return sptr(new global_regs_zc_impl(xport)); +} + +}}}; diff --git a/host/lib/usrp/e300/e300_global_regs.hpp b/host/lib/usrp/e300/e300_global_regs.hpp new file mode 100644 index 000000000..12693da79 --- /dev/null +++ b/host/lib/usrp/e300/e300_global_regs.hpp @@ -0,0 +1,78 @@ +// +// Copyright 2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +#ifndef INCLUDED_E300_GLOBAL_REGS_HPP +#define INCLUDED_E300_GLOBAL_REGS_HPP + +#include +#include + +namespace uhd { namespace usrp { namespace e300 { + +struct global_regs_transaction_t { + boost::uint32_t is_poke; + boost::uint32_t addr; + boost::uint32_t data; + boost::uint32_t pad; +}; + +class global_regs : boost::noncopyable, public virtual uhd::wb_iface +{ +public: + typedef boost::shared_ptr sptr; + + static sptr make(const size_t ctrl_base); + static sptr make(uhd::transport::zero_copy_if::sptr xport); + + static const size_t SR_CORE_READBACK = 0; + static const size_t SR_CORE_MISC = 4; + static const size_t SR_CORE_TEST = 28; + static const size_t SR_CORE_XB_LOCAL = 32; + + // leave some room for registers, + // xbar starts with an offset of one + // 1K page. A part of which is used for + // DST_LOOKUP for DST_LOOKUP + + static const size_t SR_CORE_DST = 1024; + static const size_t SR_CORE_XBAR = 2048; + + static const size_t RB32_CORE_MISC = 1; + static const size_t RB32_CORE_COMPAT = 2; + static const size_t RB32_CORE_GITHASH = 3; + static const size_t RB32_CORE_PLL = 4; + static const size_t RB32_CORE_TEST = 24; + + // PPS selection + static const size_t PPS_GPS = 0; + static const size_t PPS_INT = 2; + static const size_t PPS_EXT = 3; +}; + +UHD_INLINE boost::uint32_t XB_ADDR(const boost::uint32_t addr) +{ + return global_regs::SR_CORE_XBAR + (addr << 2); +} + +UHD_INLINE boost::uint32_t DST_ADDR(const boost::uint32_t addr) +{ + return global_regs::SR_CORE_DST + (addr << 2); +} + +}}}; + +#endif /* INCLUDED_E300_GLOBAL_REGS_HPP */ diff --git a/host/lib/usrp/e300/e300_i2c.cpp b/host/lib/usrp/e300/e300_i2c.cpp new file mode 100644 index 000000000..d8f535a98 --- /dev/null +++ b/host/lib/usrp/e300/e300_i2c.cpp @@ -0,0 +1,409 @@ +// +// 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 . +// + +#include +#include +#include + + +#include "e300_i2c.hpp" +#include + +namespace uhd { namespace usrp { namespace e300 { + +class zc_impl : public i2c +{ +public: + zc_impl(uhd::transport::zero_copy_if::sptr xport) : _xport(xport) + { + } + + virtual ~zc_impl(void) + { + } + + void set_i2c_reg8( + const boost::uint8_t addr, + const boost::uint8_t reg, + const boost::uint8_t value) + { + i2c_transaction_t transaction; + transaction.type = WRITE | ONEBYTE; + transaction.addr = addr; + transaction.reg = uhd::htonx(reg); + transaction.data = value; + { + uhd::transport::managed_send_buffer::sptr buff = _xport->get_send_buff(10.0); + if (not buff or buff->size() < sizeof(transaction)) + throw uhd::runtime_error("i2c_zc_impl send timeout"); + std::memcpy(buff->cast(), &transaction, sizeof(transaction)); + buff->commit(sizeof(transaction)); + } + } + + boost::uint8_t get_i2c_reg8( + const boost::uint8_t addr, + const boost::uint8_t reg) + { + i2c_transaction_t transaction; + transaction.type = READ | ONEBYTE; + transaction.addr = addr; + transaction.reg = uhd::htonx(reg); + { + uhd::transport::managed_send_buffer::sptr buff = _xport->get_send_buff(10.0); + if (not buff or buff->size() < sizeof(transaction)) + throw std::runtime_error("i2c_zc_impl send timeout"); + std::memcpy(buff->cast(), &transaction, sizeof(transaction)); + buff->commit(sizeof(transaction)); + } + { + uhd::transport::managed_recv_buffer::sptr buff = _xport->get_recv_buff(10.0); + if (not buff or buff->size() < sizeof(transaction)) + throw std::runtime_error("i2c_zc_impl recv timeout"); + std::memcpy(&transaction, buff->cast(), sizeof(transaction)); + } + return transaction.data; + } + + void set_i2c_reg16( + const boost::uint8_t addr, + const boost::uint16_t reg, + const boost::uint8_t value) + { + i2c_transaction_t transaction; + transaction.type = WRITE | TWOBYTE; + transaction.addr = addr; + transaction.reg = uhd::htonx(reg); + transaction.data = value; + { + uhd::transport::managed_send_buffer::sptr buff = _xport->get_send_buff(10.0); + if (not buff or buff->size() < sizeof(transaction)) + throw uhd::runtime_error("i2c_zc_impl send timeout"); + std::memcpy(buff->cast(), &transaction, sizeof(transaction)); + buff->commit(sizeof(transaction)); + } + } + + boost::uint8_t get_i2c_reg16( + const boost::uint8_t addr, + const boost::uint16_t reg) + { + i2c_transaction_t transaction; + transaction.type = READ | TWOBYTE; + transaction.addr = addr; + transaction.reg = uhd::htonx(reg); + { + uhd::transport::managed_send_buffer::sptr buff = _xport->get_send_buff(10.0); + if (not buff or buff->size() < sizeof(transaction)) + throw std::runtime_error("i2c_zc_impl send timeout"); + std::memcpy(buff->cast(), &transaction, sizeof(transaction)); + buff->commit(sizeof(transaction)); + } + { + uhd::transport::managed_recv_buffer::sptr buff = _xport->get_recv_buff(10.0); + if (not buff or buff->size() < sizeof(transaction)) + throw std::runtime_error("i2c_zc_impl recv timeout"); + std::memcpy(&transaction, buff->cast(), sizeof(transaction)); + } + return transaction.data; + } + + +private: + uhd::transport::zero_copy_if::sptr _xport; +}; + +i2c::sptr i2c::make_zc(uhd::transport::zero_copy_if::sptr xport) +{ + return sptr(new zc_impl(xport)); +} + +class simple_udp_impl : public i2c +{ +public: + simple_udp_impl(const std::string &ip_addr, const std::string &port) + { + _xport = uhd::transport::udp_simple::make_connected(ip_addr, port); + } + + virtual ~simple_udp_impl(void) + { + } + + void set_i2c_reg8( + const boost::uint8_t addr, + const boost::uint8_t reg, + const boost::uint8_t value) + { + i2c_transaction_t transaction; + transaction.type = i2c::WRITE | ONEBYTE; + transaction.addr = addr; + transaction.reg = uhd::htonx(reg); + transaction.data = value; + + _xport->send( + boost::asio::buffer( + &transaction, + sizeof(transaction))); + } + + boost::uint8_t get_i2c_reg8( + const boost::uint8_t addr, + const boost::uint8_t reg) + { + i2c_transaction_t transaction; + transaction.type = i2c::READ | ONEBYTE; + transaction.addr = addr; + transaction.reg = uhd::htonx(reg); + transaction.data = 0; + + _xport->send( + boost::asio::buffer( + &transaction, + sizeof(transaction))); + + boost::uint8_t buff[sizeof(i2c_transaction_t)] = {}; + const size_t nbytes = _xport->recv( + boost::asio::buffer(buff), 0.100); + if (not (nbytes == sizeof(transaction))) + throw std::runtime_error("i2c_simple_udp_impl recv timeout"); + i2c_transaction_t *reply = reinterpret_cast(buff); + return reply->data; + } + + void set_i2c_reg16( + const boost::uint8_t addr, + const boost::uint16_t reg, + const boost::uint8_t value) + { + i2c_transaction_t transaction; + transaction.type = i2c::WRITE | TWOBYTE; + transaction.addr = addr; + transaction.reg = uhd::htonx(reg); + transaction.data = value; + + _xport->send( + boost::asio::buffer( + &transaction, + sizeof(transaction))); + } + + boost::uint8_t get_i2c_reg16( + const boost::uint8_t addr, + const boost::uint16_t reg) + { + i2c_transaction_t transaction; + transaction.type = i2c::READ | TWOBYTE; + transaction.addr = addr; + transaction.reg = uhd::htonx(reg); + transaction.data = 0; + + _xport->send( + boost::asio::buffer( + &transaction, + sizeof(transaction))); + + boost::uint8_t buff[sizeof(i2c_transaction_t)] = {}; + const size_t nbytes = _xport->recv( + boost::asio::buffer(buff), 0.100); + if (not (nbytes == sizeof(transaction))) + throw std::runtime_error("i2c_simple_udp_impl recv timeout"); + i2c_transaction_t *reply = reinterpret_cast(buff); + return reply->data; + } + +private: + uhd::transport::udp_simple::sptr _xport; +}; + +i2c::sptr i2c::make_simple_udp( + const std::string &ip_addr, + const std::string &port) +{ + return sptr(new simple_udp_impl(ip_addr,port)); +} + +}}} // namespace + +#ifdef E300_NATIVE + +#include +#include +#include +#include +#include + +#include +#include + +namespace uhd { namespace usrp { namespace e300 { + +class i2cdev_impl : public i2c +{ +public: + i2cdev_impl(const std::string &device) + { + _fd = ::open(device.c_str(), O_RDWR); + if (_fd < 0) + throw uhd::system_error("open failed."); + } + + virtual ~i2cdev_impl(void) + { + close(_fd); + } + + void set_i2c_reg8( + const boost::uint8_t addr, + const boost::uint8_t reg, + const boost::uint8_t value) + { + boost::uint8_t outbuf[2]; + i2c_rdwr_ioctl_data packets; + i2c_msg messages[1]; + + messages[0].addr = addr; + messages[0].flags = 0; + messages[0].len = sizeof(outbuf); + messages[0].buf = outbuf; + + outbuf[0] = reg; + outbuf[1] = value; + + packets.msgs = messages; + packets.nmsgs = 1; + + if(::ioctl(_fd, I2C_RDWR, &packets) < 0) { + throw std::runtime_error("ioctl failed"); + } + // this is ugly + boost::this_thread::sleep(boost::posix_time::milliseconds(5)); + } + + boost::uint8_t get_i2c_reg8( + const boost::uint8_t addr, + const boost::uint8_t reg) + { + i2c_rdwr_ioctl_data packets; + i2c_msg messages[2]; + + boost::uint8_t outbuf = reg; + messages[0].addr = addr; + messages[0].flags = 0; + messages[0].len = sizeof(outbuf); + messages[0].buf = &outbuf; + + boost::uint8_t inbuf; + messages[1].addr = addr; + messages[1].flags = I2C_M_RD; + messages[1].len = sizeof(inbuf); + messages[1].buf = &inbuf; + + packets.msgs = messages; + packets.nmsgs = 2; + + if(::ioctl(_fd, I2C_RDWR, &packets) < 0) { + throw std::runtime_error("ioctl failed."); + } + + return inbuf; + } + + // the daughterboard uses 16 bit addresses + void set_i2c_reg16( + const boost::uint8_t addr, + const boost::uint16_t reg, + const boost::uint8_t value) + { + boost::uint8_t outbuf[3]; + i2c_rdwr_ioctl_data packets; + i2c_msg messages[1]; + + messages[0].addr = addr; + messages[0].flags = 0; + messages[0].len = sizeof(outbuf); + messages[0].buf = outbuf; + + outbuf[0] = (reg >> 8) & 0xff; + outbuf[1] = reg & 0xff; + outbuf[2] = value; + + packets.msgs = messages; + packets.nmsgs = 1; + + if(::ioctl(_fd, I2C_RDWR, &packets) < 0) { + throw std::runtime_error("ioctl failed"); + } + // this is ugly + boost::this_thread::sleep(boost::posix_time::milliseconds(5)); + } + + + // the daughterboard eeprom uses 16 bit addresses + boost::uint8_t get_i2c_reg16( + const boost::uint8_t addr, + const boost::uint16_t reg) + { + i2c_rdwr_ioctl_data packets; + i2c_msg messages[2]; + + // always little endian + boost::uint8_t outbuf[2]; + outbuf[0] = (reg >> 8) & 0xff; + outbuf[1] = reg & 0xff; + + messages[0].addr = addr; + messages[0].flags = 0; + messages[0].len = sizeof(outbuf); + messages[0].buf = outbuf; + + boost::uint8_t inbuf; + messages[1].addr = addr; + messages[1].flags = I2C_M_RD; + messages[1].len = sizeof(inbuf); + messages[1].buf = &inbuf; + + packets.msgs = messages; + packets.nmsgs = 2; + + if(::ioctl(_fd, I2C_RDWR, &packets) < 0) { + throw std::runtime_error("ioctl failed."); + } + + return inbuf; + } + +private: + int _fd; +}; + +}}} // namespace + +using namespace uhd::usrp::e300; + +i2c::sptr i2c::make_i2cdev(const std::string &device) +{ + return sptr(new i2cdev_impl(device)); +} +#else +using namespace uhd::usrp::e300; + +i2c::sptr i2c::make_i2cdev(const std::string &) +{ + throw uhd::assertion_error("i2c::make() !E300_NATIVE"); +} +#endif // E300_NATIVE diff --git a/host/lib/usrp/e300/e300_i2c.hpp b/host/lib/usrp/e300/e300_i2c.hpp new file mode 100644 index 000000000..6cca7ab70 --- /dev/null +++ b/host/lib/usrp/e300/e300_i2c.hpp @@ -0,0 +1,77 @@ +// +// Copyright 2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +#ifndef INCLUDED_E300_I2C_HPP +#define INCLUDED_E300_I2C_HPP + +#include +#include +#include + +#include + +namespace uhd { namespace usrp { namespace e300 { + +struct i2c_transaction_t { + boost::uint16_t reg; + boost::uint8_t addr; + boost::uint8_t data; + boost::uint8_t type; +}; + +class i2c : public boost::noncopyable +{ +public: + typedef boost::shared_ptr sptr; + + static sptr make_i2cdev(const std::string &device); + static sptr make_zc(uhd::transport::zero_copy_if::sptr xport); + static sptr make_simple_udp( + const std::string &ip_addr, + const std::string &port); + + virtual boost::uint8_t get_i2c_reg8( + const boost::uint8_t addr, + const boost::uint8_t reg) = 0; + + virtual boost::uint8_t get_i2c_reg16( + const boost::uint8_t addr, + const boost::uint16_t reg) = 0; + + virtual void set_i2c_reg8( + const boost::uint8_t addr, + const boost::uint8_t reg, + const boost::uint8_t value) = 0; + + virtual void set_i2c_reg16( + const boost::uint8_t addr, + const boost::uint16_t reg, + const boost::uint8_t value) = 0; + + + static const boost::uint8_t DB_EEPROM_ADDR = 0x50; + static const boost::uint8_t MB_EEPROM_ADDR = 0x51; + + static const boost::uint8_t WRITE = 0x1; + static const boost::uint8_t READ = 0x0; + static const boost::uint8_t TWOBYTE = 0x4; + static const boost::uint8_t ONEBYTE = 0x2; +}; + +}}}; + +#endif // INCLUDED_E300_I2C_HPP diff --git a/host/lib/usrp/e300/e300_impl.cpp b/host/lib/usrp/e300/e300_impl.cpp new file mode 100644 index 000000000..0f4cb5852 --- /dev/null +++ b/host/lib/usrp/e300/e300_impl.cpp @@ -0,0 +1,1347 @@ +// +// 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 . +// + +#include "e300_impl.hpp" +#include "e300_defaults.hpp" +#include "e300_fpga_defs.hpp" +#include "e300_spi.hpp" +#include "e300_regs.hpp" +#include "e300_eeprom_manager.hpp" +#include "e300_sensor_manager.hpp" +#include "e300_common.hpp" +#include "e300_remote_codec_ctrl.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include //sleep +#include +#include + +using namespace uhd; +using namespace uhd::usrp; +using namespace uhd::transport; +namespace fs = boost::filesystem; +namespace asio = boost::asio; + +//! mapping of frontend to radio perif index +static const size_t FE0 = 1; +static const size_t FE1 = 0; + +namespace uhd { namespace usrp { namespace e300 { + +/*********************************************************************** + * Discovery + **********************************************************************/ + +static std::vector discover_ip_addrs( + const std::string& addr_hint, const std::string& port) +{ + std::vector addrs; + + // Create a UDP transport to communicate: + // Some devices will cause a throw when opened for a broadcast address. + // We print and recover so the caller can loop through all bcast addrs. + uhd::transport::udp_simple::sptr udp_bcast_xport; + try { + udp_bcast_xport = uhd::transport::udp_simple::make_broadcast(addr_hint, port); + } catch(const std::exception &e) { + UHD_MSG(error) << boost::format("Cannot open UDP transport on %s for discovery\n%s") + % addr_hint % e.what() << std::endl; + return addrs; + } catch(...) { + UHD_MSG(error) << "E300 Network discovery unknown error" << std::endl; + return addrs; + } + + // TODO: Do not abuse the I2C transport here ... + // we send a read request to i2c address 0x51, + // to read register 0 + i2c_transaction_t req; + req.type = i2c::READ | i2c::ONEBYTE; + req.addr = 0x51; // mboard's eeprom address, we don't really care + req.reg = 4; + + // send dummy request + try { + udp_bcast_xport->send(boost::asio::buffer(&req, sizeof(req))); + } catch (const std::exception &ex) { + UHD_MSG(error) << "E300 Network discovery error " << ex.what() << std::endl; + return addrs; + } catch(...) { + UHD_MSG(error) << "E300 Network discovery unknown error" << std::endl; + return addrs; + } + + // loop for replies until timeout + while (true) { + boost::uint8_t buff[sizeof(i2c_transaction_t)] = {}; + const size_t nbytes = udp_bcast_xport->recv(boost::asio::buffer(buff), 0.050); + if (nbytes == 0) + break; //No more responses + + const i2c_transaction_t *reply = reinterpret_cast(buff); + if (req.addr == reply->addr) + addrs.push_back(udp_bcast_xport->get_recv_addr()); + } + + return addrs; +} + +static bool is_loopback(const if_addrs_t &if_addrs) +{ + return if_addrs.inet == asio::ip::address_v4::loopback().to_string(); +} + +static device_addrs_t e300_find(const device_addr_t &multi_dev_hint) +{ + // handle multi device discovery + device_addrs_t hints = separate_device_addr(multi_dev_hint); + + if (hints.size() > 1) { + device_addrs_t found_devices; + std::string err_msg; + BOOST_FOREACH(const device_addr_t &hint_i, hints) + { + device_addrs_t found_devices_i = e300_find(hint_i); + if(found_devices_i.size() != 1) + err_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 err_msg.empty()) + throw uhd::value_error(err_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 e300_addrs; + + // return an empty list of addresses when type is set to non-e300 + if (hint.has_key("type") and hint["type"] != "e3x0") + return e300_addrs; + + const bool loopback_only = + get_if_addrs().size() == 1 and is_loopback(get_if_addrs().at(0)); + + // if we don't have connectivity, we might as well skip the network part + if (not loopback_only) { + // if no address or node has been specified, send a broadcast + if ((not hint.has_key("addr")) and (not hint.has_key("node"))) { + BOOST_FOREACH(const if_addrs_t &if_addrs, get_if_addrs()) + { + // avoid the loopback device + if (is_loopback(if_addrs)) + 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 ad append results + device_addrs_t new_e300_addrs = e300_find(new_hint); + e300_addrs.insert(e300_addrs.begin(), + new_e300_addrs.begin(), new_e300_addrs.end()); + + } + return e300_addrs; + } + + std::vector ip_addrs = discover_ip_addrs( + hint["addr"], E300_SERVER_I2C_PORT); + + BOOST_FOREACH(const std::string &ip_addr, ip_addrs) + { + device_addr_t new_addr; + new_addr["type"] = "e3x0"; + new_addr["addr"] = ip_addr; + + // see if we can read the eeprom + try { + e300_eeprom_manager eeprom_manager( + i2c::make_simple_udp(new_addr["addr"], E300_SERVER_I2C_PORT)); + const mboard_eeprom_t eeprom = eeprom_manager.get_mb_eeprom(); + new_addr["name"] = eeprom["name"]; + new_addr["serial"] = eeprom["serial"]; + new_addr["product"] = eeprom["product"]; + } catch (...) { + // set these values as empty string, so the device may still be found + // and the filters 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"])) + { + e300_addrs.push_back(new_addr); + } + } + } + + // finally search locally + // if device node is not provided, + // use the default one + if (not hint.has_key("node")) { + device_addr_t new_addr = hint; + new_addr["node"] = "/dev/axi_fpga"; + return e300_find(new_addr); + } + + // use the given node + if (fs::exists(hint["node"])) { + device_addr_t new_addr; + new_addr["type"] = "e3x0"; + new_addr["node"] = fs::system_complete(fs::path(hint["node"])).string(); + + try { + e300_eeprom_manager eeprom_manager(i2c::make_i2cdev(E300_I2CDEV_DEVICE)); + const mboard_eeprom_t eeprom = eeprom_manager.get_mb_eeprom(); + new_addr["name"] = eeprom["name"]; + new_addr["serial"] = eeprom["serial"]; + new_addr["product"] = eeprom["product"]; + } catch (...) { + // set these values as empty string, so the device may still be found + // and the filters 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"])) + { + e300_addrs.push_back(new_addr); + } + } + + return e300_addrs; +} + + +/*********************************************************************** + * Make + **********************************************************************/ +static device::sptr e300_make(const device_addr_t &device_addr) +{ + UHD_LOG << "e300_make with args " << device_addr.to_pp_string() << std::endl; + if(device_addr.has_key("server")) + throw uhd::runtime_error( + str(boost::format("Please run the server executable \"%s\"") + % "usrp_e3x0_network_mode")); + else + return device::sptr(new e300_impl(device_addr)); +} + +/*********************************************************************** + * Structors + **********************************************************************/ +e300_impl::e300_impl(const uhd::device_addr_t &device_addr) + : _device_addr(device_addr) + , _xport_path(device_addr.has_key("addr") ? ETH : AXI) + , _sid_framer(0) +{ + _type = uhd::device::USRP; + + _async_md.reset(new async_md_type(1000/*messages deep*/)); + + //////////////////////////////////////////////////////////////////// + // load the fpga image + //////////////////////////////////////////////////////////////////// + if (_xport_path == AXI) { + if (not device_addr.has_key("no_reload_fpga")) { + // Load FPGA image if provided via args + if (device_addr.has_key("fpga")) { + common::load_fpga_image(device_addr["fpga"]); + // Else load the FPGA image based on the product ID + } else { + //extract the FPGA path for the e300 + const boost::uint16_t pid = boost::lexical_cast( + device_addr["product"]); + std::string fpga_image; + switch(e300_eeprom_manager::get_mb_type(pid)) { + case e300_eeprom_manager::USRP_E310_MB: + fpga_image = find_image_path(E310_FPGA_FILE_NAME); + break; + case e300_eeprom_manager::USRP_E300_MB: + fpga_image = find_image_path(E300_FPGA_FILE_NAME); + break; + case e300_eeprom_manager::UNKNOWN: + default: + UHD_MSG(warning) << "Unknown motherboard type, loading e300 image." + << std::endl; + fpga_image = find_image_path(E300_FPGA_FILE_NAME); + break; + } + common::load_fpga_image(fpga_image); + } + } + } + + //////////////////////////////////////////////////////////////////// + // setup fifo xports + //////////////////////////////////////////////////////////////////// + _ctrl_xport_params.recv_frame_size = e300::DEFAULT_CTRL_FRAME_SIZE; + _ctrl_xport_params.num_recv_frames = e300::DEFAULT_CTRL_NUM_FRAMES; + _ctrl_xport_params.send_frame_size = e300::DEFAULT_CTRL_FRAME_SIZE; + _ctrl_xport_params.num_send_frames = e300::DEFAULT_CTRL_NUM_FRAMES; + + _data_xport_params.recv_frame_size = e300::DEFAULT_RX_DATA_FRAME_SIZE; + _data_xport_params.num_recv_frames = e300::DEFAULT_RX_DATA_NUM_FRAMES; + _data_xport_params.send_frame_size = e300::DEFAULT_TX_DATA_FRAME_SIZE; + _data_xport_params.num_send_frames = e300::DEFAULT_TX_DATA_NUM_FRAMES; + + // until we figure out why this goes wrong we'll keep this hack around + if (_xport_path == ETH) { + _data_xport_params.recv_frame_size = + std::min(e300::MAX_NET_RX_DATA_FRAME_SIZE, _data_xport_params.recv_frame_size); + _data_xport_params.send_frame_size = + std::min(e300::MAX_NET_TX_DATA_FRAME_SIZE, _data_xport_params.send_frame_size); + } + udp_zero_copy::buff_params dummy_buff_params_out; + + if (_xport_path == ETH) { + zero_copy_if::sptr codec_xport = + udp_zero_copy::make(device_addr["addr"], E300_SERVER_CODEC_PORT, _ctrl_xport_params, dummy_buff_params_out, device_addr); + _codec_ctrl = e300_remote_codec_ctrl::make(codec_xport); + zero_copy_if::sptr gregs_xport = + udp_zero_copy::make(device_addr["addr"], E300_SERVER_GREGS_PORT, _ctrl_xport_params, dummy_buff_params_out, device_addr); + _global_regs = global_regs::make(gregs_xport); + + zero_copy_if::sptr i2c_xport; + i2c_xport = udp_zero_copy::make(device_addr["addr"], E300_SERVER_I2C_PORT, _ctrl_xport_params, dummy_buff_params_out, device_addr); + _eeprom_manager = boost::make_shared(i2c::make_zc(i2c_xport)); + + uhd::transport::zero_copy_xport_params sensor_xport_params; + sensor_xport_params.recv_frame_size = 128; + sensor_xport_params.num_recv_frames = 10; + sensor_xport_params.send_frame_size = 128; + sensor_xport_params.num_send_frames = 10; + + zero_copy_if::sptr sensors_xport; + sensors_xport = udp_zero_copy::make(device_addr["addr"], E300_SERVER_SENSOR_PORT, sensor_xport_params, dummy_buff_params_out, device_addr); + _sensor_manager = e300_sensor_manager::make_proxy(sensors_xport); + + } else { + e300_fifo_config_t fifo_cfg; + try { + fifo_cfg = e300_read_sysfs(); + } catch (uhd::lookup_error &e) { + throw uhd::runtime_error("Failed to get driver parameters from sysfs."); + } + _fifo_iface = e300_fifo_interface::make(fifo_cfg); + _global_regs = global_regs::make(_fifo_iface->get_global_regs_base()); + + ad9361_params::sptr client_settings = boost::make_shared(); + _codec_ctrl = ad9361_ctrl::make_spi(client_settings, spi::make(E300_SPIDEV_DEVICE), 1); + // This is horrible ... why do I have to sleep here? + boost::this_thread::sleep(boost::posix_time::milliseconds(100)); + _eeprom_manager = boost::make_shared(i2c::make_i2cdev(E300_I2CDEV_DEVICE)); + } + + UHD_MSG(status) << "Detecting internal GPSDO.... " << std::flush; + if (_xport_path == AXI) { + try { + _gps = gps::ublox::ubx::control::make("/dev/ttyPS1", 9600); + } catch (std::exception &e) { + UHD_MSG(error) << "An error occured making GPSDO control: " << e.what() << std::endl; + } + _sensor_manager = e300_sensor_manager::make_local(_gps); + } + UHD_MSG(status) << (_sensor_manager->get_gps_found() ? "found" : "not found") << std::endl; + + // Verify we can talk to the e300 core control registers ... + UHD_MSG(status) << "Initializing core control..." << std::endl; + this->_register_loopback_self_test(_global_regs); + + // Verify fpga compatibility version matches at least for the major + if (_get_version(FPGA_MAJOR) != fpga::COMPAT_MAJOR) { + throw uhd::runtime_error(str(boost::format( + "Expected FPGA compatibility number %lu.x, but got %lu.%lu:\n" + "The FPGA build is not compatible with the host code build.\n" + "%s" + ) % fpga::COMPAT_MAJOR + % _get_version(FPGA_MAJOR) % _get_version(FPGA_MINOR) + % print_images_error())); + } + + //////////////////////////////////////////////////////////////////// + // Initialize the properties tree + //////////////////////////////////////////////////////////////////// + _tree = property_tree::make(); + _tree->create("/name").set("E-Series Device"); + const fs_path mb_path = "/mboards/0"; + _tree->create(mb_path / "name") + .set(_eeprom_manager->get_mb_type_string()); + + _tree->create(mb_path / "codename").set("Troll"); + + _tree->create(mb_path / "fpga_version").set( + str(boost::format("%u.%u") + % _get_version(FPGA_MAJOR) + % _get_version(FPGA_MINOR))); + + _tree->create(mb_path / "fpga_version_hash").set( + _get_version_hash()); + + //////////////////////////////////////////////////////////////////// + // and do the misc mboard sensors + //////////////////////////////////////////////////////////////////// + _tree->create(mb_path / "sensors"); + BOOST_FOREACH(const std::string &name, _sensor_manager->get_sensors()) + { + _tree->create(mb_path / "sensors" / name) + .publish(boost::bind(&e300_sensor_manager::get_sensor, _sensor_manager, name)); + } + + //////////////////////////////////////////////////////////////////// + // setup the mboard eeprom + //////////////////////////////////////////////////////////////////// + _tree->create(mb_path / "eeprom") + .set(_eeprom_manager->get_mb_eeprom()) // set first... + .subscribe(boost::bind( + &e300_eeprom_manager::write_mb_eeprom, + _eeprom_manager, _1)); + + //////////////////////////////////////////////////////////////////// + // clocking + //////////////////////////////////////////////////////////////////// + _tree->create(mb_path / "tick_rate") + .coerce(boost::bind(&e300_impl::_set_tick_rate, this, _1)) + .publish(boost::bind(&e300_impl::_get_tick_rate, this)) + .subscribe(boost::bind(&e300_impl::_update_tick_rate, this, _1)); + + //default some chains on -- needed for setup purposes + _codec_ctrl->set_active_chains(true, false, true, false); + _codec_ctrl->set_clock_rate(50e6); + + //////////////////////////////////////////////////////////////////// + // setup radios + //////////////////////////////////////////////////////////////////// + for(size_t instance = 0; instance < fpga::NUM_RADIOS; instance++) + this->_setup_radio(instance); + + _codec_ctrl->data_port_loopback(true); + + // Radio 0 loopback through AD9361 + this->_codec_loopback_self_test(_radio_perifs[0].ctrl); + // Radio 1 loopback through AD9361 + this->_codec_loopback_self_test(_radio_perifs[1].ctrl); + + _codec_ctrl->data_port_loopback(false); + + //////////////////////////////////////////////////////////////////// + // internal gpios + //////////////////////////////////////////////////////////////////// + gpio_core_200::sptr fp_gpio = gpio_core_200::make(_radio_perifs[0].ctrl, TOREG(SR_FP_GPIO), RB32_FP_GPIO); + const std::vector 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(mb_path / "gpio" / "INT0" / attr) + .subscribe(boost::bind(&e300_impl::_set_internal_gpio, this, fp_gpio, attr, _1)) + .set(0); + } + _tree->create(mb_path / "gpio" / "INT0" / "READBACK") + .publish(boost::bind(&e300_impl::_get_internal_gpio, this, fp_gpio, "READBACK")); + + + //////////////////////////////////////////////////////////////////// + // register the time keepers - only one can be the highlander + //////////////////////////////////////////////////////////////////// + _tree->create(mb_path / "time" / "now") + .publish(boost::bind(&time_core_3000::get_time_now, _radio_perifs[0].time64)) + .subscribe(boost::bind(&time_core_3000::set_time_now, _radio_perifs[0].time64, _1)) + .subscribe(boost::bind(&time_core_3000::set_time_now, _radio_perifs[1].time64, _1)); + _tree->create(mb_path / "time" / "pps") + .publish(boost::bind(&time_core_3000::get_time_last_pps, _radio_perifs[0].time64)) + .subscribe(boost::bind(&time_core_3000::set_time_next_pps, _radio_perifs[0].time64, _1)) + .subscribe(boost::bind(&time_core_3000::set_time_next_pps, _radio_perifs[1].time64, _1)); + //setup time source props + _tree->create(mb_path / "time_source" / "value") + .subscribe(boost::bind(&e300_impl::_update_time_source, this, _1)); + static const std::vector time_sources = boost::assign::list_of("none")("external")("gpsdo"); + _tree->create >(mb_path / "time_source" / "options").set(time_sources); + //setup reference source props + _tree->create(mb_path / "clock_source" / "value") + .subscribe(boost::bind(&e300_impl::_update_clock_source, this, _1)); + static const std::vector clock_sources = boost::assign::list_of("internal"); + // not implemented ("external")("gpsdo"); + _tree->create >(mb_path / "clock_source" / "options").set(clock_sources); + + //////////////////////////////////////////////////////////////////// + // dboard eeproms but not really + //////////////////////////////////////////////////////////////////// + dboard_eeprom_t db_eeprom; + _tree->create(mb_path / "dboards" / "A" / "rx_eeprom") + .set(_eeprom_manager->get_db_eeprom()) + .subscribe(boost::bind( + &e300_eeprom_manager::write_db_eeprom, + _eeprom_manager, _1)); + + _tree->create(mb_path / "dboards" / "A" / "tx_eeprom") + .set(_eeprom_manager->get_db_eeprom()) + .subscribe(boost::bind( + &e300_eeprom_manager::write_db_eeprom, + _eeprom_manager, _1)); + + _tree->create(mb_path / "dboards" / "A" / "gdb_eeprom").set(db_eeprom); + + //////////////////////////////////////////////////////////////////// + // create RF frontend interfacing + //////////////////////////////////////////////////////////////////// + { + const fs_path codec_path = mb_path / ("rx_codecs") / "A"; + _tree->create(codec_path / "name").set("E3x0 RX dual ADC"); + _tree->create(codec_path / "gains"); //empty cuz gains are in frontend + } + { + const fs_path codec_path = mb_path / ("tx_codecs") / "A"; + _tree->create(codec_path / "name").set("E3x0 TX dual DAC"); + _tree->create(codec_path / "gains"); //empty cuz gains are in frontend + } + + //////////////////////////////////////////////////////////////////// + // create frontend mapping + //////////////////////////////////////////////////////////////////// + + std::vector default_map(2, 0); + default_map[0] = 0; // set A->0 + default_map[1] = 1; // set B->1, even if there's only A + + _tree->create >(mb_path / "rx_chan_dsp_mapping").set(default_map); + _tree->create >(mb_path / "tx_chan_dsp_mapping").set(default_map); + + _tree->create(mb_path / "rx_subdev_spec") + .set(subdev_spec_t()) + .subscribe(boost::bind(&e300_impl::_update_subdev_spec, this, "rx", _1)); + _tree->create(mb_path / "tx_subdev_spec") + .set(subdev_spec_t()) + .subscribe(boost::bind(&e300_impl::_update_subdev_spec, this, "tx", _1)); + + //////////////////////////////////////////////////////////////////// + // do some post-init tasks + //////////////////////////////////////////////////////////////////// + + // init the clock rate to something reasonable + _tree->access(mb_path / "tick_rate").set( + device_addr.cast("master_clock_rate", e300::DEFAULT_TICK_RATE)); + + // subdev spec contains full width of selections + subdev_spec_t rx_spec, tx_spec; + BOOST_FOREACH(const std::string &fe, _tree->list(mb_path / "dboards" / "A" / "rx_frontends")) + { + rx_spec.push_back(subdev_spec_pair_t("A", fe)); + } + BOOST_FOREACH(const std::string &fe, _tree->list(mb_path / "dboards" / "A" / "tx_frontends")) + { + tx_spec.push_back(subdev_spec_pair_t("A", fe)); + } + _tree->access(mb_path / "rx_subdev_spec").set(rx_spec); + _tree->access(mb_path / "tx_subdev_spec").set(tx_spec); + + if (_sensor_manager->get_gps_found()) { + _tree->access(mb_path / "clock_source" / "value").set("gpsdo"); + _tree->access(mb_path / "time_source" / "value").set("gpsdo"); + UHD_MSG(status) << "References initialized to GPSDO sources" << std::endl; + const time_t tp = time_t(_sensor_manager->get_gps_time().to_int()); + _tree->access(mb_path / "time" / "pps").set(time_spec_t(tp)); + //wait for time to be set (timeout after 1 second) + for (int i = 0; i < 10; i++) + { + boost::this_thread::sleep(boost::posix_time::milliseconds(100)); + if(tp == (_tree->access(mb_path / "time" / "pps").get()).get_full_secs()) + break; + } + } else { + // init to default time and clock source + _tree->access(mb_path / "clock_source" / "value").set( + e300::DEFAULT_CLOCK_SRC); + _tree->access(mb_path / "time_source" / "value").set( + e300::DEFAULT_TIME_SRC); + + UHD_MSG(status) << "References initialized to internal sources" << std::endl; + } +} + +boost::uint8_t e300_impl::_get_internal_gpio( + gpio_core_200::sptr gpio, + const std::string &) +{ + return boost::uint32_t(gpio->read_gpio(dboard_iface::UNIT_RX)); +} + +void e300_impl::_set_internal_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); + else if (attr == "DDR") + return gpio->set_gpio_ddr(dboard_iface::UNIT_RX, value); + else if (attr == "OUT") + return gpio->set_gpio_out(dboard_iface::UNIT_RX, value); + else if (attr == "ATR_0X") + return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE, value); + else if (attr == "ATR_RX") + return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_RX_ONLY, value); + else if (attr == "ATR_TX") + return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY, value); + else if (attr == "ATR_XX") + return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, value); +} + +uhd::sensor_value_t e300_impl::_get_fe_pll_lock(const bool is_tx) +{ + const boost::uint32_t st = + _global_regs->peek32(global_regs::RB32_CORE_PLL); + const bool locked = is_tx ? st & 0x1 : st & 0x2; + return sensor_value_t("LO", locked, "locked", "unlocked"); +} + +e300_impl::~e300_impl(void) +{ + /* NOP */ +} + +void e300_impl::_enforce_tick_rate_limits( + const size_t chan_count, + const double tick_rate, + const std::string &direction) +{ + const size_t max_chans = 2; + if (chan_count > max_chans) { + throw uhd::value_error(boost::str( + boost::format("cannot not setup %d %s channels (maximum is %d)") + % chan_count + % direction + % max_chans + )); + } else { + const double max_tick_rate = ad9361_device_t::AD9361_MAX_CLOCK_RATE / ((chan_count <= 1) ? 1 : 2); + if (tick_rate - max_tick_rate >= 1.0) + { + throw uhd::value_error(boost::str( + boost::format("current master clock rate (%.6f MHz) exceeds maximum possible master clock rate (%.6f MHz) when using %d %s channels") + % (tick_rate/1e6) + % (max_tick_rate/1e6) + % chan_count + % direction + )); + } + } +} + +double e300_impl::_set_tick_rate(const double rate) +{ + UHD_MSG(status) << "Asking for clock rate " << rate/1e6 << " MHz\n"; + _tick_rate = _codec_ctrl->set_clock_rate(rate); + UHD_MSG(status) << "Actually got clock rate " << _tick_rate/1e6 << " MHz\n"; + + BOOST_FOREACH(radio_perifs_t &perif, _radio_perifs) + { + perif.time64->set_tick_rate(_tick_rate); + perif.time64->self_test(); + } + return _tick_rate; +} + +void e300_impl::_load_fpga_image(const std::string &path) +{ + if (not fs::exists("/dev/xdevcfg")) + { + ::system("mknod /dev/xdevcfg c 259 0"); + //throw uhd::runtime_error("no xdevcfg, please run: mknod /dev/xdevcfg c 259 0"); + } + + UHD_MSG(status) << "Loading FPGA image: " << path << "..." << std::flush; + + std::ifstream fpga_file(path.c_str(), std::ios_base::binary); + UHD_ASSERT_THROW(fpga_file.good()); + + std::FILE *wfile; + wfile = std::fopen("/dev/xdevcfg", "wb"); + UHD_ASSERT_THROW(!(wfile == NULL)); + + char buff[16384]; // devcfg driver can't handle huge writes + do { + fpga_file.read(buff, sizeof(buff)); + std::fwrite(buff, 1, fpga_file.gcount(), wfile); + } while (fpga_file); + + fpga_file.close(); + std::fclose(wfile); + + UHD_MSG(status) << " done" << std::endl; +} + +void e300_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; +} + +boost::uint32_t e300_impl::_get_version(compat_t which) +{ + const boost::uint16_t compat_num + = _global_regs->peek32(global_regs::RB32_CORE_COMPAT); + + switch(which) { + case FPGA_MINOR: + return compat_num & 0xff; + case FPGA_MAJOR: + return (compat_num & 0xff00) >> 8; + default: + throw uhd::value_error("Requested unknown version."); + }; +} + +std::string e300_impl::_get_version_hash(void) +{ + const boost::uint32_t git_hash + = _global_regs->peek32(global_regs::RB32_CORE_GITHASH); + return str(boost::format("%7x%s") + % (git_hash & 0x0FFFFFFF) + % ((git_hash & 0xF000000) ? "-dirty" : "")); +} + +void e300_impl::_codec_loopback_self_test(wb_iface::sptr iface) +{ + bool test_fail = false; + UHD_ASSERT_THROW(bool(iface)); + UHD_MSG(status) << "Performing CODEC loopback test... " << std::flush; + size_t hash = time(NULL); + for (size_t i = 0; i < 100; i++) + { + boost::hash_combine(hash, i); + const boost::uint32_t word32 = boost::uint32_t(hash) & 0xfff0fff0; + iface->poke32(TOREG(SR_CODEC_IDLE), word32); + iface->peek64(RB64_CODEC_READBACK); //enough idleness for loopback to propagate + const boost::uint64_t rb_word64 = iface->peek64(RB64_CODEC_READBACK); + const boost::uint32_t rb_tx = boost::uint32_t(rb_word64 >> 32); + const boost::uint32_t rb_rx = boost::uint32_t(rb_word64 & 0xffffffff); + test_fail = word32 != rb_tx or word32 != rb_rx; + if (test_fail) break; //exit loop on any failure + } + UHD_MSG(status) << ((test_fail)? " fail" : "pass") << std::endl; + + /* Zero out the idle data. */ + iface->poke32(TOREG(SR_CODEC_IDLE), 0); +} + +boost::uint32_t e300_impl::_allocate_sid(const sid_config_t &config) +{ + const boost::uint32_t stream = (config.dst_prefix | (config.router_dst_there << 2)) & 0xff; + + const boost::uint32_t sid = 0 + | (E300_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 E300 to recognize it's own local address. + _global_regs->poke32(global_regs::SR_CORE_XB_LOCAL, config.router_addr_there); + + // Program CAM entry for outgoing packets matching a E300 resource (e.g. Radio). + // This type of packet matches the XB_LOCAL address and is looked up in the upper + // half of the CAM + _global_regs->poke32(XB_ADDR(256 + stream), + config.router_dst_there); + + // Program CAM entry for returning packets to us (for example GR host via zynq_fifo) + // This type of packet does not match the XB_LOCAL address and is looked up in the lower half of the CAM + _global_regs->poke32(XB_ADDR(E300_DEVICE_HERE), + config.router_dst_here); + + UHD_LOG << std::hex + << "done router config for sid 0x" << sid + << std::dec << std::endl; + + //increment for next setup + _sid_framer++; + + return sid; +} + +void e300_impl::_setup_dest_mapping(const boost::uint32_t sid, const size_t which_stream) +{ + UHD_LOG << boost::format("Setting up dest map for 0x%lx to be stream %d") + % (sid & 0xff) % which_stream << std::endl; + _global_regs->poke32(DST_ADDR(sid & 0xff), which_stream); +} + +void e300_impl::_update_time_source(const std::string &source) +{ + UHD_MSG(status) << boost::format("Setting time source to %s") % source << std::endl; + if (source == "none" or source == "internal") { + _misc.pps_sel = global_regs::PPS_INT; + } else if (source == "gpsdo") { + _misc.pps_sel = global_regs::PPS_GPS; + } else if (source == "external") { + _misc.pps_sel = global_regs::PPS_EXT; + } else { + throw uhd::key_error("update_time_source: unknown source: " + source); + } + _update_gpio_state(); +} + +size_t e300_impl::_get_axi_dma_channel( + boost::uint8_t destination, + boost::uint8_t prefix) +{ + static const boost::uint32_t RADIO_GRP_SIZE = 4; + static const boost::uint32_t RADIO0_GRP = 0; + static const boost::uint32_t RADIO1_GRP = 1; + + boost::uint32_t radio_grp = (destination == E300_XB_DST_R0) ? RADIO0_GRP : RADIO1_GRP; + return ((radio_grp * RADIO_GRP_SIZE) + prefix); +} + +boost::uint16_t e300_impl::_get_udp_port( + boost::uint8_t destination, + boost::uint8_t prefix) +{ + if (destination == E300_XB_DST_R0) { + if (prefix == E300_RADIO_DEST_PREFIX_CTRL) + return boost::lexical_cast(E300_SERVER_CTRL_PORT0); + else if (prefix == E300_RADIO_DEST_PREFIX_TX) + return boost::lexical_cast(E300_SERVER_TX_PORT0); + else if (prefix == E300_RADIO_DEST_PREFIX_RX) + return boost::lexical_cast(E300_SERVER_RX_PORT0); + } else if (destination == E300_XB_DST_R1) { + if (prefix == E300_RADIO_DEST_PREFIX_CTRL) + return boost::lexical_cast(E300_SERVER_CTRL_PORT1); + else if (prefix == E300_RADIO_DEST_PREFIX_TX) + return boost::lexical_cast(E300_SERVER_TX_PORT1); + else if (prefix == E300_RADIO_DEST_PREFIX_RX) + return boost::lexical_cast(E300_SERVER_RX_PORT1); + } + throw uhd::value_error(str(boost::format("No UDP port defined for combination: %u %u") % destination % prefix)); +} + +e300_impl::both_xports_t e300_impl::_make_transport( + const boost::uint8_t &destination, + const boost::uint8_t &prefix, + const uhd::transport::zero_copy_xport_params ¶ms, + boost::uint32_t &sid) +{ + both_xports_t xports; + + sid_config_t config; + config.router_addr_there = E300_DEVICE_THERE; + config.dst_prefix = prefix; + config.router_dst_there = destination; + config.router_dst_here = E300_XB_DST_AXI; + sid = this->_allocate_sid(config); + + // in local mode + if (_xport_path == AXI) { + // lookup which dma channel we need + // to use to create our transport + const size_t stream = _get_axi_dma_channel( + destination, + prefix); + + xports.send = + _fifo_iface->make_send_xport(stream, params); + xports.recv = + _fifo_iface->make_recv_xport(stream, params); + + // in network mode + } else if (_xport_path == ETH) { + // lookup which udp port we need + // to use to create our transport + const boost::uint16_t port = _get_udp_port( + destination, + prefix); + + udp_zero_copy::buff_params dummy_buff_params_out; + xports.send = udp_zero_copy::make( + _device_addr["addr"], + str(boost::format("%u") % port), params, + dummy_buff_params_out, + _device_addr); + + // use the same xport in both directions + xports.recv = xports.send; + } + + // configure the return path + _setup_dest_mapping(sid, _get_axi_dma_channel(destination, prefix)); + + return xports; +} + +void e300_impl::_update_clock_source(const std::string &) +{ +} + +void e300_impl::_update_antenna_sel(const size_t &which, const std::string &ant) +{ + if (ant != "TX/RX" and ant != "RX2") + throw uhd::value_error("e300: unknown RX antenna option: " + ant); + _radio_perifs[which].ant_rx2 = (ant == "RX2"); + this->_update_atrs(); +} + +void e300_impl::_update_fe_lo_freq(const std::string &fe, const double freq) +{ + if (fe[0] == 'R') + _settings.rx_freq = freq; + if (fe[0] == 'T') + _settings.tx_freq = freq; + this->_update_atrs(); + _update_bandsel(fe, freq); +} + +void e300_impl::_setup_radio(const size_t dspno) +{ + radio_perifs_t &perif = _radio_perifs[dspno]; + const fs_path mb_path = "/mboards/0"; + + //////////////////////////////////////////////////////////////////// + // crossbar config for ctrl xports + //////////////////////////////////////////////////////////////////// + + // make a transport, grab a sid + boost::uint32_t ctrl_sid; + both_xports_t ctrl_xports = _make_transport( + dspno ? E300_XB_DST_R1 : E300_XB_DST_R0, + E300_RADIO_DEST_PREFIX_CTRL, + _ctrl_xport_params, + ctrl_sid); + + this->_setup_dest_mapping( + ctrl_sid, + dspno ? E300_R1_CTRL_STREAM + : E300_R0_CTRL_STREAM); + + //////////////////////////////////////////////////////////////////// + // radio control + //////////////////////////////////////////////////////////////////// + perif.ctrl = radio_ctrl_core_3000::make( + false/*lilE*/, + ctrl_xports.send, + ctrl_xports.recv, + ctrl_sid, + dspno ? "1" : "0"); + this->_register_loopback_self_test(perif.ctrl); + perif.atr = gpio_core_200_32wo::make(perif.ctrl, TOREG(SR_GPIO)); + + //////////////////////////////////////////////////////////////////// + // front end corrections + //////////////////////////////////////////////////////////////////// + std::string slot_name = (dspno == 0) ? "A" : "B"; + perif.rx_fe = rx_frontend_core_200::make(perif.ctrl, TOREG(SR_RX_FRONT)); + const fs_path rx_fe_path = mb_path / "rx_frontends" / slot_name; + _tree->create >(rx_fe_path / "dc_offset" / "value") + .coerce(boost::bind(&rx_frontend_core_200::set_dc_offset, perif.rx_fe, _1)) + .set(std::complex(0.0, 0.0)); + _tree->create(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 >(rx_fe_path / "iq_balance" / "value") + .subscribe(boost::bind(&rx_frontend_core_200::set_iq_balance, perif.rx_fe, _1)) + .set(std::complex(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" / slot_name; + _tree->create >(tx_fe_path / "dc_offset" / "value") + .coerce(boost::bind(&tx_frontend_core_200::set_dc_offset, perif.tx_fe, _1)) + .set(std::complex(0.0, 0.0)); + _tree->create >(tx_fe_path / "iq_balance" / "value") + .subscribe(boost::bind(&tx_frontend_core_200::set_iq_balance, perif.tx_fe, _1)) + .set(std::complex(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(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(rx_dsp_path / "rate" / "range") + .publish(boost::bind(&rx_dsp_core_3000::get_host_rates, perif.ddc)); + _tree->create(rx_dsp_path / "rate" / "value") + .coerce(boost::bind(&rx_dsp_core_3000::set_host_rate, perif.ddc, _1)) + .subscribe(boost::bind(&e300_impl::_update_rx_samp_rate, this, dspno, _1)) + .set(e300::DEFAULT_RX_SAMP_RATE); + _tree->create(rx_dsp_path / "freq" / "value") + .coerce(boost::bind(&rx_dsp_core_3000::set_freq, perif.ddc, _1)) + .set(e300::DEFAULT_DDC_FREQ); + _tree->create(rx_dsp_path / "freq" / "range") + .publish(boost::bind(&rx_dsp_core_3000::get_freq_range, perif.ddc)); + _tree->create(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(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(tx_dsp_path / "rate" / "range") + .publish(boost::bind(&tx_dsp_core_3000::get_host_rates, perif.duc)); + _tree->create(tx_dsp_path / "rate" / "value") + .coerce(boost::bind(&tx_dsp_core_3000::set_host_rate, perif.duc, _1)) + .subscribe(boost::bind(&e300_impl::_update_tx_samp_rate, this, dspno, _1)) + .set(e300::DEFAULT_TX_SAMP_RATE); + _tree->create(tx_dsp_path / "freq" / "value") + .coerce(boost::bind(&tx_dsp_core_3000::set_freq, perif.duc, _1)) + .set(e300::DEFAULT_DUC_FREQ); + _tree->create(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 + //////////////////////////////////////////////////////////////////// + static const std::vector data_directions = boost::assign::list_of("rx")("tx"); + BOOST_FOREACH(const std::string& direction, data_directions) + { + const std::string key = boost::to_upper_copy(direction) + std::string(((dspno == FE0)? "1" : "2")); + const fs_path rf_fe_path + = mb_path / "dboards" / "A" / (direction + "_frontends") / ((dspno == 0) ? "A" : "B"); + + _tree->create(rf_fe_path / "name").set("FE-"+key); + _tree->create(rf_fe_path / "sensors"); //empty TODO + _tree->create(rf_fe_path / "sensors" / "lo_locked") + .publish(boost::bind(&e300_impl::_get_fe_pll_lock, this, direction == "tx")); + BOOST_FOREACH(const std::string &name, ad9361_ctrl::get_gain_names(key)) + { + _tree->create(rf_fe_path / "gains" / name / "range") + .set(ad9361_ctrl::get_gain_range(key)); + + _tree->create(rf_fe_path / "gains" / name / "value") + .coerce(boost::bind(&ad9361_ctrl::set_gain, _codec_ctrl, key, _1)) + .set(e300::DEFAULT_FE_GAIN); + } + _tree->create(rf_fe_path / "connection").set("IQ"); + _tree->create(rf_fe_path / "enabled").set(true); + _tree->create(rf_fe_path / "use_lo_offset").set(false); + _tree->create(rf_fe_path / "bandwidth" / "value") + .coerce(boost::bind(&ad9361_ctrl::set_bw_filter, _codec_ctrl, key, _1)) + .set(e300::DEFAULT_FE_BW); + _tree->create(rf_fe_path / "bandwidth" / "range") + .publish(boost::bind(&ad9361_ctrl::get_bw_filter_range, key)); + _tree->create(rf_fe_path / "freq" / "value") + .coerce(boost::bind(&ad9361_ctrl::tune, _codec_ctrl, key, _1)) + .subscribe(boost::bind(&e300_impl::_update_fe_lo_freq, this, key, _1)) + .set(e300::DEFAULT_FE_FREQ); + _tree->create(rf_fe_path / "freq" / "range") + .publish(boost::bind(&ad9361_ctrl::get_rf_freq_range)); + + //setup antenna stuff + if (key[0] == 'R') { + static const std::vector ants = boost::assign::list_of("TX/RX")("RX2"); + _tree->create >(rf_fe_path / "antenna" / "options").set(ants); + _tree->create(rf_fe_path / "antenna" / "value") + .subscribe(boost::bind(&e300_impl::_update_antenna_sel, this, dspno, _1)) + .set("RX2"); + + } + if (key[0] == 'T') { + static const std::vector ants(1, "TX/RX"); + _tree->create >(rf_fe_path / "antenna" / "options").set(ants); + _tree->create(rf_fe_path / "antenna" / "value").set("TX/RX"); + } + } +} + +void e300_impl::_update_enables(void) +{ + //extract settings from state variables + const bool enb_tx1 = bool(_radio_perifs[FE0].tx_streamer.lock()); + const bool enb_rx1 = bool(_radio_perifs[FE0].rx_streamer.lock()); + const bool enb_tx2 = bool(_radio_perifs[FE1].tx_streamer.lock()); + const bool enb_rx2 = bool(_radio_perifs[FE1].rx_streamer.lock()); + const size_t num_rx = (enb_rx1 ? 1 : 0) + (enb_rx2 ? 1:0); + const size_t num_tx = (enb_tx1 ? 1 : 0) + (enb_tx2 ? 1:0); + const bool mimo = num_rx == 2 or num_tx == 2; + + //setup the active chains in the codec + _codec_ctrl->set_active_chains(enb_tx1, enb_tx2, enb_rx1, enb_rx2); + if ((num_rx + num_tx) == 0) + _codec_ctrl->set_active_chains( + true, false, true, false); // enable something + + //set_active_chains could cause a clock rate change - reset dcm + _reset_codec_mmcm(); + + //figure out if mimo is enabled based on new state + _misc.mimo = (mimo)? 1 : 0; + _update_gpio_state(); + + //atrs change based on enables + _update_atrs(); +} + +void e300_impl::_update_gpio_state(void) +{ + boost::uint32_t misc_reg = 0 + | (_misc.pps_sel << gpio_t::PPS_SEL) + | (_misc.mimo << gpio_t::MIMO) + | (_misc.codec_arst << gpio_t::CODEC_ARST) + | (_misc.tx_bandsels << gpio_t::TX_BANDSEL) + | (_misc.rx_bandsel_a << gpio_t::RX_BANDSELA) + | (_misc.rx_bandsel_b << gpio_t::RX_BANDSELB) + | (_misc.rx_bandsel_c << gpio_t::RX_BANDSELC); + _global_regs->poke32(global_regs::SR_CORE_MISC, misc_reg); +} + +void e300_impl::_reset_codec_mmcm(void) +{ + _misc.codec_arst = 1; + _update_gpio_state(); + boost::this_thread::sleep(boost::posix_time::milliseconds(10)); + _misc.codec_arst = 0; + _update_gpio_state(); +} + +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// +//////////////// ATR SETUP FOR FRONTEND CONTROL VIA GPIO /////////////// +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// + +void e300_impl::_update_bandsel(const std::string& which, double freq) +{ + if(which[0] == 'R') { + if (freq < 450e6) { + _misc.rx_bandsel_a = 44; // 4 | (5 << 3) + _misc.rx_bandsel_b = 0; // 0 | (0 << 2) + _misc.rx_bandsel_c = 6; // 2 | (1 << 2) + } else if (freq < 700e6) { + _misc.rx_bandsel_a = 26; // 2 | (3 << 3) + _misc.rx_bandsel_b = 0; // 0 | (0 << 2) + _misc.rx_bandsel_c = 15; // 3 | (3 << 2) + } else if (freq < 1200e6) { + _misc.rx_bandsel_a = 8; // 0 | (1 << 3) + _misc.rx_bandsel_b = 0; // 0 | (0 << 2) + _misc.rx_bandsel_c = 9; // 1 | (2 << 2) + } else if (freq < 1800e6) { + _misc.rx_bandsel_a = 1; // 1 | (0 << 3) + _misc.rx_bandsel_b = 6; // 2 | (1 << 2) + _misc.rx_bandsel_c = 0; // 0 | (0 << 2) + } else if (freq < 2350e6){ + _misc.rx_bandsel_a = 19; // 3 | (2 << 3) + _misc.rx_bandsel_b = 15; // 3 | (3 << 2) + _misc.rx_bandsel_c = 0; // 0 | (0 << 2) + } else if (freq < 2600e6){ + _misc.rx_bandsel_a = 37; // 5 | (4 << 3) + _misc.rx_bandsel_b = 9; // 1 | (2 << 2) + _misc.rx_bandsel_c = 0; // 0 | (0 << 2) + } else { + _misc.rx_bandsel_a = 0; + _misc.rx_bandsel_b = 0; + _misc.rx_bandsel_c = 0; + } + _update_gpio_state(); + } else if(which[0] == 'T') { + if (freq < 117.7e6) + _misc.tx_bandsels = 7; + else if (freq < 178.2e6) + _misc.tx_bandsels = 6; + else if (freq < 284.3e6) + _misc.tx_bandsels = 5; + else if (freq < 453.7e6) + _misc.tx_bandsels = 4; + else if (freq < 723.8e6) + _misc.tx_bandsels = 3; + else if (freq < 1154.9e6) + _misc.tx_bandsels = 2; + else if (freq < 1842.6e6) + _misc.tx_bandsels = 1; + else if (freq < 2940.0e6) + _misc.tx_bandsels = 0; + else + _misc.tx_bandsels = 7; + _update_gpio_state(); + } else { + UHD_THROW_INVALID_CODE_PATH(); + } +} + + +void e300_impl::_update_atrs(void) +{ + for (size_t instance = 0; instance < fpga::NUM_RADIOS; instance++) + { + // if we're not ready, no point ... + if (not _radio_perifs[instance].atr) + return; + + radio_perifs_t &perif = _radio_perifs[instance]; + const bool enb_rx = bool(perif.rx_streamer.lock()); + const bool enb_tx = bool(perif.tx_streamer.lock()); + const bool rx_ant_rx2 = perif.ant_rx2; + + const bool rx_low_band = _settings.rx_freq < 2.6e9; + const bool tx_low_band = _settings.tx_freq < 2940.0e6; + + // VCRX + int vcrx_v1_rxing = 1; + int vcrx_v2_rxing = 0; + int vcrx_v1_txing = 1; + int vcrx_v2_txing = 0; + + if (rx_low_band) { + vcrx_v1_rxing = rx_ant_rx2 ? 0 : 1; + vcrx_v2_rxing = rx_ant_rx2 ? 1 : 0; + vcrx_v1_txing = 0; + vcrx_v2_txing = 1; + } else { + vcrx_v1_rxing = rx_ant_rx2 ? 1 : 0; + vcrx_v2_rxing = rx_ant_rx2 ? 0 : 1; + vcrx_v1_txing = 1; + vcrx_v2_txing = 0; + } + + // VCTX + int vctxrx_v1_rxing = 0; + int vctxrx_v2_rxing = 1; + int vctxrx_v1_txing = 0; + int vctxrx_v2_txing = 1; + + if (tx_low_band) { + vctxrx_v1_rxing = rx_ant_rx2 ? 0 : 0; + vctxrx_v2_rxing = rx_ant_rx2 ? 0 : 1; + vctxrx_v1_txing = 1; + vctxrx_v2_txing = 0; + } else { + vctxrx_v1_rxing = rx_ant_rx2 ? 0 : 0; + vctxrx_v2_rxing = rx_ant_rx2 ? 0 : 1; + vctxrx_v1_txing = 1; + vctxrx_v2_txing = 1; + } + //swapped for routing reasons, reswap it here + if (instance == 1) { + std::swap(vctxrx_v1_rxing, vctxrx_v2_rxing); + std::swap(vctxrx_v1_txing, vctxrx_v2_txing); + } + + int tx_enable_a = (!tx_low_band and enb_tx) ? 1 : 0; + int tx_enable_b = (tx_low_band and enb_tx) ? 1 : 0; + + //----------------- LEDS ----------------------------// + const int led_rx2 = rx_ant_rx2 ? 1 : 0; + const int led_txrx = !rx_ant_rx2 ? 1 : 0; + const int led_tx = 1; + + const int rx_leds = (led_rx2 << LED_RX_RX) | (led_txrx << LED_TXRX_RX); + const int tx_leds = (led_tx << LED_TXRX_TX); + const int xx_leds = tx_leds | (1 << LED_RX_RX); //forced to rx2 + + const int rx_selects = 0 + | (vcrx_v1_rxing << VCRX_V1) + | (vcrx_v2_rxing << VCRX_V2) + | (vctxrx_v1_rxing << VCTXRX_V1) + | (vctxrx_v2_rxing << VCTXRX_V2) + ; + const int tx_selects = 0 + | (vcrx_v1_txing << VCRX_V1) + | (vcrx_v2_txing << VCRX_V2) + | (vctxrx_v1_txing << VCTXRX_V1) + | (vctxrx_v2_txing << VCTXRX_V2) + ; + const int tx_enables = 0 + | (tx_enable_a << TX_ENABLEA) + | (tx_enable_b << TX_ENABLEB) + ; + + //default selects + int oo_reg = rx_selects; + int rx_reg = rx_selects; + int tx_reg = tx_selects; + int fd_reg = tx_selects; //tx selects dominate in fd mode + + //add in leds and tx enables based on fe enable + if (enb_rx) + rx_reg |= rx_leds; + if (enb_rx) + fd_reg |= xx_leds; + if (enb_tx) + tx_reg |= tx_enables | tx_leds; + if (enb_tx) + fd_reg |= tx_enables | xx_leds; + + gpio_core_200_32wo::sptr atr = _radio_perifs[instance].atr; + atr->set_atr_reg(dboard_iface::ATR_REG_IDLE, oo_reg); + atr->set_atr_reg(dboard_iface::ATR_REG_RX_ONLY, rx_reg); + atr->set_atr_reg(dboard_iface::ATR_REG_TX_ONLY, tx_reg); + atr->set_atr_reg(dboard_iface::ATR_REG_FULL_DUPLEX, fd_reg); + } +} + +}}} // namespace + +UHD_STATIC_BLOCK(register_e300_device) +{ + device::register_device(&uhd::usrp::e300::e300_find, &uhd::usrp::e300::e300_make, uhd::device::USRP); +} diff --git a/host/lib/usrp/e300/e300_impl.hpp b/host/lib/usrp/e300/e300_impl.hpp new file mode 100644 index 000000000..9cfd80afd --- /dev/null +++ b/host/lib/usrp/e300/e300_impl.hpp @@ -0,0 +1,296 @@ +// +// 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 . +// + +#ifndef INCLUDED_E300_IMPL_HPP +#define INCLUDED_E300_IMPL_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "e300_fifo_config.hpp" +#include "radio_ctrl_core_3000.hpp" +#include "rx_frontend_core_200.hpp" +#include "tx_frontend_core_200.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 "ad9361_ctrl.hpp" +#include "gpio_core_200.hpp" + +#include "e300_global_regs.hpp" +#include "e300_i2c.hpp" +#include "e300_eeprom_manager.hpp" +#include "e300_sensor_manager.hpp" +#include "e300_ublox_control.hpp" + +namespace uhd { namespace usrp { namespace e300 { + +static const std::string E300_FPGA_FILE_NAME = "usrp_e300_fpga.bit"; +static const std::string E310_FPGA_FILE_NAME = "usrp_e310_fpga.bit"; + +static const std::string E300_TEMP_SYSFS = "iio:device0"; +static const std::string E300_SPIDEV_DEVICE = "/dev/spidev0.1"; +static const std::string E300_I2CDEV_DEVICE = "/dev/i2c-0"; + +static std::string E300_SERVER_RX_PORT0 = "21756"; +static std::string E300_SERVER_TX_PORT0 = "21757"; +static std::string E300_SERVER_CTRL_PORT0 = "21758"; + +static std::string E300_SERVER_RX_PORT1 = "21856"; +static std::string E300_SERVER_TX_PORT1 = "21857"; +static std::string E300_SERVER_CTRL_PORT1 = "21858"; + + +static std::string E300_SERVER_CODEC_PORT = "21759"; +static std::string E300_SERVER_GREGS_PORT = "21760"; +static std::string E300_SERVER_I2C_PORT = "21761"; +static std::string E300_SERVER_SENSOR_PORT = "21762"; + +static const double E300_RX_SW_BUFF_FULLNESS = 0.9; //Buffer should be half full + +// crossbar settings +static const boost::uint8_t E300_RADIO_DEST_PREFIX_TX = 0; +static const boost::uint8_t E300_RADIO_DEST_PREFIX_CTRL = 1; +static const boost::uint8_t E300_RADIO_DEST_PREFIX_RX = 2; + +static const boost::uint8_t E300_XB_DST_AXI = 0; +static const boost::uint8_t E300_XB_DST_R0 = 1; +static const boost::uint8_t E300_XB_DST_R1 = 2; +static const boost::uint8_t E300_XB_DST_CE0 = 3; +static const boost::uint8_t E300_XB_DST_CE1 = 4; + +static const boost::uint8_t E300_DEVICE_THERE = 2; +static const boost::uint8_t E300_DEVICE_HERE = 0; + +static const size_t E300_R0_CTRL_STREAM = (0 << 2) | E300_RADIO_DEST_PREFIX_CTRL; +static const size_t E300_R0_TX_DATA_STREAM = (0 << 2) | E300_RADIO_DEST_PREFIX_TX; +static const size_t E300_R0_RX_DATA_STREAM = (0 << 2) | E300_RADIO_DEST_PREFIX_RX; + +static const size_t E300_R1_CTRL_STREAM = (1 << 2) | E300_RADIO_DEST_PREFIX_CTRL; +static const size_t E300_R1_TX_DATA_STREAM = (1 << 2) | E300_RADIO_DEST_PREFIX_TX; +static const size_t E300_R1_RX_DATA_STREAM = (1 << 2) | E300_RADIO_DEST_PREFIX_RX; + + +/*! + * USRP-E300 implementation guts: + * The implementation details are encapsulated here. + * Handles properties on the mboard, dboard, dsps... + */ +class e300_impl : public uhd::device +{ +public: + //structors + e300_impl(const uhd::device_addr_t &); + virtual ~e300_impl(void); + + //the io interface + boost::mutex _stream_spawn_mutex; + uhd::rx_streamer::sptr get_rx_stream(const uhd::stream_args_t &); + uhd::tx_streamer::sptr get_tx_stream(const uhd::stream_args_t &); + + typedef uhd::transport::bounded_buffer async_md_type; + boost::shared_ptr _async_md; + + bool recv_async_msg(uhd::async_metadata_t &, double); + +private: // types + // sid convenience struct + 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; + }; + + // perifs in the radio core + struct radio_perifs_t + { + radio_ctrl_core_3000::sptr ctrl; + gpio_core_200_32wo::sptr atr; + 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; + rx_frontend_core_200::sptr rx_fe; + tx_frontend_core_200::sptr tx_fe; + + boost::weak_ptr rx_streamer; + boost::weak_ptr tx_streamer; + + bool ant_rx2; + }; + + //frontend cache so we can update gpios + struct fe_control_settings_t + { + fe_control_settings_t(void) + { + rx_freq = 1e9; + tx_freq = 1e9; + } + double rx_freq; + double tx_freq; + }; + + // convenience struct + struct both_xports_t + { + uhd::transport::zero_copy_if::sptr recv; + uhd::transport::zero_copy_if::sptr send; + }; + + enum xport_t {AXI, ETH}; + + enum compat_t {FPGA_MAJOR, FPGA_MINOR}; + + struct gpio_t + { + gpio_t() : pps_sel(global_regs::PPS_INT), + mimo(0), codec_arst(0), tx_bandsels(0), + rx_bandsel_a(0), rx_bandsel_b(0), rx_bandsel_c(0) + {} + + boost::uint32_t pps_sel; + boost::uint32_t mimo; + boost::uint32_t codec_arst; + + boost::uint32_t tx_bandsels; + boost::uint32_t rx_bandsel_a; + boost::uint32_t rx_bandsel_b; + boost::uint32_t rx_bandsel_c; + + static const size_t PPS_SEL = 0; + static const size_t MIMO = 2; + static const size_t CODEC_ARST = 3; + static const size_t TX_BANDSEL = 4; + static const size_t RX_BANDSELA = 7; + static const size_t RX_BANDSELB = 13; + static const size_t RX_BANDSELC = 17; + }; + +private: // methods + void _load_fpga_image(const std::string &path); + + void _register_loopback_self_test(uhd::wb_iface::sptr iface); + + boost::uint32_t _get_version(compat_t which); + std::string _get_version_hash(void); + + void _setup_radio(const size_t which_radio); + + boost::uint32_t _allocate_sid(const sid_config_t &config); + + void _setup_dest_mapping( + const boost::uint32_t sid, + const size_t which_stream); + + size_t _get_axi_dma_channel( + boost::uint8_t destination, + boost::uint8_t prefix); + + boost::uint16_t _get_udp_port( + boost::uint8_t destination, + boost::uint8_t prefix); + + both_xports_t _make_transport( + const boost::uint8_t &destination, + const boost::uint8_t &prefix, + const uhd::transport::zero_copy_xport_params ¶ms, + boost::uint32_t &sid); + + double _get_tick_rate(void){return _tick_rate;} + double _set_tick_rate(const double rate); + + void _update_gpio_state(void); + void _update_enables(void); + void _reset_codec_mmcm(void); + void _update_bandsel(const std::string& which, double freq); + + void _check_tick_rate_with_current_streamers(const double rate); + void _enforce_tick_rate_limits( + const size_t change, + const double tick_rate, + const std::string &direction); + + void _update_tick_rate(const double); + void _update_rx_samp_rate(const size_t, const double); + void _update_tx_samp_rate(const size_t, const double); + + void _update_time_source(const std::string &source); + void _update_clock_source(const std::string &); + + void _update_subdev_spec( + const std::string &txrx, + const uhd::usrp::subdev_spec_t &spec); + + void _codec_loopback_self_test(uhd::wb_iface::sptr iface); + + void _update_atrs(void); + void _update_antenna_sel(const size_t &fe, const std::string &ant); + void _update_fe_lo_freq(const std::string &fe, const double freq); + + // overflow handling is special for MIMO case + void _handle_overflow( + radio_perifs_t &perif, + boost::weak_ptr streamer); + + + // get frontend lock sensor + uhd::sensor_value_t _get_fe_pll_lock(const bool is_tx); + + // internal gpios + boost::uint8_t _get_internal_gpio( + gpio_core_200::sptr, + const std::string &); + + void _set_internal_gpio( + gpio_core_200::sptr gpio, + const std::string &attr, + const boost::uint32_t value); + +private: // members + uhd::device_addr_t _device_addr; + xport_t _xport_path; + e300_fifo_interface::sptr _fifo_iface; + size_t _sid_framer; + radio_perifs_t _radio_perifs[2]; + double _tick_rate; + ad9361_ctrl::sptr _codec_ctrl; + fe_control_settings_t _settings; + global_regs::sptr _global_regs; + e300_sensor_manager::sptr _sensor_manager; + e300_eeprom_manager::sptr _eeprom_manager; + uhd::transport::zero_copy_xport_params _data_xport_params; + uhd::transport::zero_copy_xport_params _ctrl_xport_params; + gpio_t _misc; + gps::ublox::ubx::control::sptr _gps; +}; + +}}} // namespace + +#endif /* INCLUDED_E300_IMPL_HPP */ diff --git a/host/lib/usrp/e300/e300_io_impl.cpp b/host/lib/usrp/e300/e300_io_impl.cpp new file mode 100644 index 000000000..dcb6f2afe --- /dev/null +++ b/host/lib/usrp/e300/e300_io_impl.cpp @@ -0,0 +1,599 @@ +// +// 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 . +// + +#include "e300_regs.hpp" +#include "e300_impl.hpp" +#include "e300_fpga_defs.hpp" +#include "validate_subdev_spec.hpp" +#include "../../transport/super_recv_packet_handler.hpp" +#include "../../transport/super_send_packet_handler.hpp" +#include "async_packet_handler.hpp" +#include +#include +#include +#include +#include + +using namespace uhd; +using namespace uhd::usrp; +using namespace uhd::transport; + +namespace uhd { namespace usrp { namespace e300 { + +static const boost::uint32_t HW_SEQ_NUM_MASK = 0xfff; + +/*********************************************************************** + * update streamer rates + **********************************************************************/ +void e300_impl::_check_tick_rate_with_current_streamers(const double rate) +{ + size_t max_tx_chan_count = 0, max_rx_chan_count = 0; + BOOST_FOREACH(radio_perifs_t &perif, _radio_perifs) + { + { + boost::shared_ptr rx_streamer = + boost::dynamic_pointer_cast( + perif.rx_streamer.lock()); + if (rx_streamer) + max_rx_chan_count = std::max( + max_rx_chan_count, + rx_streamer->get_num_channels()); + } + + { + boost::shared_ptr tx_streamer = + boost::dynamic_pointer_cast( + perif.tx_streamer.lock()); + if (tx_streamer) + max_tx_chan_count = std::max( + max_tx_chan_count, + tx_streamer->get_num_channels()); + } + } + _enforce_tick_rate_limits(max_rx_chan_count, rate, "RX"); + _enforce_tick_rate_limits(max_tx_chan_count, rate, "TX"); +} + +void e300_impl::_update_tick_rate(const double rate) +{ + _check_tick_rate_with_current_streamers(rate); + + BOOST_FOREACH(radio_perifs_t &perif, _radio_perifs) + { + boost::shared_ptr my_streamer = + boost::dynamic_pointer_cast(perif.rx_streamer.lock()); + if (my_streamer) + my_streamer->set_tick_rate(rate); + perif.framer->set_tick_rate(_tick_rate); + } + BOOST_FOREACH(radio_perifs_t &perif, _radio_perifs) + { + boost::shared_ptr my_streamer = + boost::dynamic_pointer_cast(perif.tx_streamer.lock()); + if (my_streamer) + my_streamer->set_tick_rate(rate); + perif.deframer->set_tick_rate(_tick_rate); + } +} + +void e300_impl::_update_rx_samp_rate(const size_t dspno, const double rate) +{ + boost::shared_ptr my_streamer = + boost::dynamic_pointer_cast(_radio_perifs[dspno].rx_streamer.lock()); + if (my_streamer) + my_streamer->set_samp_rate(rate); +} + +void e300_impl::_update_tx_samp_rate(const size_t dspno, const double rate) +{ + boost::shared_ptr my_streamer = + boost::dynamic_pointer_cast(_radio_perifs[dspno].tx_streamer.lock()); + if (my_streamer) + my_streamer->set_samp_rate(rate); +} + +/*********************************************************************** + * frontend selection + **********************************************************************/ +void e300_impl::_update_subdev_spec( + const std::string &txrx, + const uhd::usrp::subdev_spec_t &spec) +{ + //sanity checking + if (spec.size()) + validate_subdev_spec(_tree, spec, "rx"); + + UHD_ASSERT_THROW(spec.size() <= fpga::NUM_RADIOS); + + if (spec.size() >= 1) + { + UHD_ASSERT_THROW(spec[0].db_name == "A"); + UHD_ASSERT_THROW(spec[0].sd_name == "A" or spec[0].sd_name == "B"); + } + if (spec.size() == 2) + { + UHD_ASSERT_THROW(spec[1].db_name == "A"); + UHD_ASSERT_THROW( + (spec[0].sd_name == "A" and spec[1].sd_name == "B") or + (spec[0].sd_name == "B" and spec[1].sd_name == "A") + ); + } + + std::vector chan_to_dsp_map(spec.size(), 0); + for (size_t i = 0; i < spec.size(); i++) + chan_to_dsp_map[i] = (spec[i].sd_name == "A") ? 0 : 1; + _tree->access >("/mboards/0" / (txrx + "_chan_dsp_mapping")).set(chan_to_dsp_map); + + const fs_path mb_path = "/mboards/0"; + + if (txrx == "tx") { + for (size_t i = 0; i < spec.size(); i++) + { + const std::string conn = _tree->access( + mb_path / "dboards" / spec[i].db_name / + ("tx_frontends") / spec[i].sd_name / "connection").get(); + _radio_perifs[i].tx_fe->set_mux(conn); + } + + } else { + for (size_t i = 0; i < spec.size(); i++) + { + const std::string conn = _tree->access( + mb_path / "dboards" / spec[i].db_name / + ("rx_frontends") / spec[i].sd_name / "connection").get(); + + const bool fe_swapped = (conn == "QI" or conn == "Q"); + _radio_perifs[i].ddc->set_mux(conn, fe_swapped); + _radio_perifs[i].rx_fe->set_mux(fe_swapped); + } + } + + this->_update_enables(); +} + +/*********************************************************************** + * VITA stuff + **********************************************************************/ +static void e300_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 e300_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 + **********************************************************************/ +struct e300_rx_fc_cache_t +{ + e300_rx_fc_cache_t(): + last_seq_in(0){} + size_t last_seq_in; +}; + +void e300_impl::_handle_overflow( + radio_perifs_t &perif, + boost::weak_ptr streamer) +{ + boost::shared_ptr my_streamer = + boost::dynamic_pointer_cast(streamer.lock()); + + //If the rx_streamer has expired then overflow handling makes no sense. + if (not my_streamer) + return; + + 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); + } +} + + +static void handle_rx_flowctrl( + const boost::uint32_t sid, + zero_copy_if::sptr xport, + boost::shared_ptr fc_cache, + const size_t last_seq) +{ + static const size_t RXFC_PACKET_LEN_IN_WORDS = 2; + static const size_t RXFC_CMD_CODE_OFFSET = 0; + static const size_t RXFC_SEQ_NUM_OFFSET = 1; + + managed_send_buffer::sptr buff = xport->get_send_buff(1.0); + if (not buff) + { + throw uhd::runtime_error("handle_rx_flowctrl timed out getting a send buffer"); + } + boost::uint32_t *pkt = buff->cast(); + + //recover seq32 + size_t& seq_sw = fc_cache->last_seq_in; + const size_t seq_hw = seq_sw & HW_SEQ_NUM_MASK; + if (last_seq < seq_hw) + seq_sw += (HW_SEQ_NUM_MASK + 1); + seq_sw &= ~HW_SEQ_NUM_MASK; + seq_sw |= 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 = RXFC_PACKET_LEN_IN_WORDS; + packet_info.num_payload_bytes = packet_info.num_payload_words32*sizeof(boost::uint32_t); + packet_info.packet_count = seq_sw; + 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 + e300_if_hdr_pack_le(pkt, packet_info); + + //load payload + pkt[packet_info.num_header_words32+RXFC_CMD_CODE_OFFSET] = uhd::htowx(0); + pkt[packet_info.num_header_words32+RXFC_SEQ_NUM_OFFSET] = uhd::htowx(seq_sw); + + //send the buffer over the interface + buff->commit(sizeof(boost::uint32_t)*(packet_info.num_packet_words32)); +} + + +/*********************************************************************** + * TX flow control handler + **********************************************************************/ +struct e300_tx_fc_cache_t +{ + e300_tx_fc_cache_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 seq_queue; + boost::shared_ptr async_queue; + boost::shared_ptr old_async_queue; +}; + +#define E300_ASYNC_EVENT_CODE_FLOW_CTRL 0 + +typedef boost::function tick_rate_retriever_t; + + +static void handle_tx_async_msgs(boost::shared_ptr fc_cache, + zero_copy_if::sptr xport, + boost::function get_tick_rate) +{ + 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(); + + //unpacking can fail + try + { + e300_if_hdr_unpack_le(packet_buff, if_packet_info); + } + 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 (uhd::wtohx(packet_buff[if_packet_info.num_header_words32+0]) == 0) + { + const size_t seq = uhd::wtohx(packet_buff[if_packet_info.num_header_words32+1]); + fc_cache->seq_queue.push_with_haste(seq); + return; + } + + //fill in the async metadata + async_metadata_t metadata; + load_metadata_from_buff(uhd::wtohx, + metadata, if_packet_info, packet_buff, + get_tick_rate(), fc_cache->stream_channel); + + //The FC response and the burst ack are two indicators that the radio + //consumed packets. Use them to update the FC metadata + if (metadata.event_code == E300_ASYNC_EVENT_CODE_FLOW_CTRL or + metadata.event_code == async_metadata_t::EVENT_CODE_BURST_ACK + ) { + const size_t seq = metadata.user_payload[0]; + fc_cache->seq_queue.push_with_pop_on_full(seq); + } + + //FC responses don't propagate up to the user so filter them here + if (metadata.event_code != E300_ASYNC_EVENT_CODE_FLOW_CTRL) { + fc_cache->async_queue->push_with_pop_on_full(metadata); + metadata.channel = fc_cache->device_channel; + fc_cache->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 fc_cache, + zero_copy_if::sptr xport, + const size_t fc_window, + const double timeout +){ + while (true) + { + const size_t delta = (fc_cache->last_seq_out & HW_SEQ_NUM_MASK) - (fc_cache->last_seq_ack & HW_SEQ_NUM_MASK); + if ((delta & HW_SEQ_NUM_MASK) <= fc_window) + break; + + const bool ok = fc_cache->seq_queue.pop_with_timed_wait(fc_cache->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) { + fc_cache->last_seq_out++; //update seq, this will actually be a send + } + + return buff; +} + +/*********************************************************************** + * Async Data + **********************************************************************/ +bool e300_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 e300_impl::get_rx_stream(const uhd::stream_args_t &args_) +{ + boost::mutex::scoped_lock lock(_stream_spawn_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("e300_impl::get_rx_stream only supports otw_format sc16"); + } + args.otw_format = "sc16"; + args.channels = args.channels.empty()? std::vector(1, 0) : args.channels; + + boost::shared_ptr my_streamer; + for (size_t stream_i = 0; stream_i < args.channels.size(); stream_i++) + { + + const size_t radio_index = _tree->access >("/mboards/0/rx_chan_dsp_mapping") + .get().at(args.channels[stream_i]); + + radio_perifs_t &perif = _radio_perifs[radio_index]; + + // make a transport, grab a sid + boost::uint32_t data_sid; + both_xports_t data_xports = _make_transport( + radio_index ? E300_XB_DST_R1 : E300_XB_DST_R0, + E300_RADIO_DEST_PREFIX_RX, + _data_xport_params, + data_sid); + + //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 = data_xports.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("spp", bpp/bpi)); + + //make the new streamer given the samples per packet + if (not my_streamer) + my_streamer = boost::make_shared(spp); + my_streamer->resize(args.channels.size()); + + //init some streamer stuff + my_streamer->set_vrt_unpacker(&e300_if_hdr_unpack_le); + + //set the converter + uhd::convert::id_type id; + id.input_format = args.otw_format + "_item32_le"; + id.num_inputs = 1; + id.output_format = args.cpu_format; + id.num_outputs = 1; + my_streamer->set_converter(id); + + 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); + my_streamer->set_xport_chan_get_buff(stream_i, boost::bind( + &zero_copy_if::get_recv_buff, data_xports.recv, _1 + ), true /*flush*/); + my_streamer->set_overflow_handler(stream_i, + boost::bind(&rx_vita_core_3000::handle_overflow, perif.framer) + ); + + //setup flow control + const size_t fc_window = data_xports.recv->get_num_recv_frames(); + perif.framer->configure_flow_control(fc_window); + boost::shared_ptr fc_cache(new e300_rx_fc_cache_t()); + my_streamer->set_xport_handle_flowctrl(stream_i, + boost::bind(&handle_rx_flowctrl, data_sid, data_xports.send, fc_cache, _1), + static_cast(static_cast(fc_window) * E300_RX_SW_BUFF_FULLNESS), + true/*init*/); + + my_streamer->set_issue_stream_cmd(stream_i, + boost::bind(&rx_vita_core_3000::issue_stream_command, perif.framer, _1) + ); + perif.rx_streamer = my_streamer; //store weak pointer + + //sets all tick and samp rates on this streamer + this->_update_tick_rate(this->_get_tick_rate()); + _tree->access(str(boost::format("/mboards/0/rx_dsps/%u/rate/value") % radio_index)).update(); + + } + _update_enables(); + return my_streamer; +} + +/*********************************************************************** + * Transmit streamer + **********************************************************************/ +tx_streamer::sptr e300_impl::get_tx_stream(const uhd::stream_args_t &args_) +{ + boost::mutex::scoped_lock lock(_stream_spawn_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("e300_impl::get_tx_stream only supports otw_format sc16"); + } + args.otw_format = "sc16"; + args.channels = args.channels.empty()? std::vector(1, 0) : args.channels; + + + //shared async queue for all channels in streamer + boost::shared_ptr async_md(new async_md_type(1000/*messages deep*/)); + + boost::shared_ptr my_streamer; + + for (size_t stream_i = 0; stream_i < args.channels.size(); stream_i++) + { + const size_t radio_index = _tree->access >("/mboards/0/tx_chan_dsp_mapping") + .get().at(args.channels[stream_i]); + + + radio_perifs_t &perif = _radio_perifs[radio_index]; + + + // make a transport, grab a sid + boost::uint32_t data_sid; + both_xports_t data_xports = _make_transport( + radio_index ? E300_XB_DST_R1 : E300_XB_DST_R0, + E300_RADIO_DEST_PREFIX_TX, + _data_xport_params, + data_sid); + + //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 = data_xports.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("spp", bpp/bpi)); + + //make the new streamer given the samples per packet + if (not my_streamer) + my_streamer = boost::make_shared(spp); + my_streamer->resize(args.channels.size()); + + //init some streamer stuff + my_streamer->set_vrt_packer(&e300_if_hdr_pack_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_le"; + id.num_outputs = 1; + my_streamer->set_converter(id); + + perif.deframer->clear(); + perif.deframer->setup(args); + perif.duc->setup(args); + + //flow control setup + const size_t fc_window = data_xports.send->get_num_send_frames(); + perif.deframer->configure_flow_control(0/*cycs off*/, fc_window/8/*pkts*/); + boost::shared_ptr fc_cache(new e300_tx_fc_cache_t()); + fc_cache->stream_channel = stream_i; + fc_cache->device_channel = args.channels[stream_i]; + fc_cache->async_queue = async_md; + fc_cache->old_async_queue = _async_md; + + tick_rate_retriever_t get_tick_rate_fn = boost::bind(&e300_impl::_get_tick_rate, this); + + task::sptr task = task::make(boost::bind(&handle_tx_async_msgs, + fc_cache, data_xports.recv, + get_tick_rate_fn)); + + my_streamer->set_xport_chan_get_buff( + stream_i, + boost::bind(&get_tx_buff_with_flowctrl, task, fc_cache, data_xports.send, fc_window, _1) + ); + + 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 + perif.tx_streamer = my_streamer; //store weak pointer + + //sets all tick and samp rates on this streamer + this->_update_tick_rate(this->_get_tick_rate()); + _tree->access(str(boost::format("/mboards/0/tx_dsps/%u/rate/value") % radio_index)).update(); + } + _update_enables(); + return my_streamer; +} +}}} // namespace diff --git a/host/lib/usrp/e300/e300_network.cpp b/host/lib/usrp/e300/e300_network.cpp new file mode 100644 index 000000000..883ff0c4f --- /dev/null +++ b/host/lib/usrp/e300/e300_network.cpp @@ -0,0 +1,642 @@ +// +// 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 . +// + +#include "e300_network.hpp" + +#ifdef E300_NATIVE + +#include "e300_impl.hpp" + +#include "ad9361_ctrl.hpp" + +#include "e300_sensor_manager.hpp" +#include "e300_fifo_config.hpp" +#include "e300_spi.hpp" +#include "e300_i2c.hpp" +#include "e300_defaults.hpp" +#include "e300_common.hpp" +#include "e300_remote_codec_ctrl.hpp" + +#include +#include +#include + +#include +#include +#include +#include + +#include + +using namespace uhd; +using namespace uhd::transport; +namespace asio = boost::asio; +namespace fs = boost::filesystem; + +namespace uhd { namespace usrp { namespace e300 { + +static const size_t E300_NETWORK_DEBUG = false; + +static inline bool wait_for_recv_ready(int sock_fd, const size_t timeout_ms) +{ + //setup timeval for timeout + timeval tv; + tv.tv_sec = 0; + tv.tv_usec = timeout_ms*1000; + + //setup rset for timeout + fd_set rset; + FD_ZERO(&rset); + FD_SET(sock_fd, &rset); + + //call select with timeout on receive socket + return ::select(sock_fd+1, &rset, NULL, NULL, &tv) > 0; +} + +static boost::mutex endpoint_mutex; + +/*********************************************************************** + * Receive tunnel - forwards recv interface to send socket + **********************************************************************/ +static void e300_recv_tunnel( + const std::string &name, + uhd::transport::zero_copy_if::sptr recver, + boost::shared_ptr sender, + asio::ip::udp::endpoint *endpoint, + bool *running +) +{ + asio::ip::udp::endpoint _tx_endpoint; + try + { + while (*running) + { + //step 1 - get the buffer + managed_recv_buffer::sptr buff = recver->get_recv_buff(); + if (not buff) continue; + if (E300_NETWORK_DEBUG) UHD_MSG(status) << name << " got " << buff->size() << std::endl; + + //step 1.5 -- update endpoint + { + boost::mutex::scoped_lock l(endpoint_mutex); + _tx_endpoint = *endpoint; + } + + //step 2 - send to the socket + sender->send_to(asio::buffer(buff->cast(), buff->size()), _tx_endpoint); + } + } + catch(const std::exception &ex) + { + UHD_MSG(error) << "e300_recv_tunnel exit " << name << " " << ex.what() << std::endl; + } + catch(...) + { + UHD_MSG(error) << "e300_recv_tunnel exit " << name << std::endl; + } + UHD_MSG(status) << "e300_recv_tunnel exit " << name << std::endl; + *running = false; +} + +/*********************************************************************** + * Send tunnel - forwards recv socket to send interface + **********************************************************************/ +static void e300_send_tunnel( + const std::string &name, + boost::shared_ptr recver, + uhd::transport::zero_copy_if::sptr sender, + asio::ip::udp::endpoint *endpoint, + bool *running +) +{ + asio::ip::udp::endpoint _rx_endpoint; + try + { + while (*running) + { + //step 1 - get the buffer + managed_send_buffer::sptr buff = sender->get_send_buff(); + if (not buff) continue; + + //step 2 - recv from socket + while (not wait_for_recv_ready(recver->native(), 100) and *running){} + if (not *running) break; + const size_t num_bytes = recver->receive_from(asio::buffer(buff->cast(), buff->size()), _rx_endpoint); + if (E300_NETWORK_DEBUG) UHD_MSG(status) << name << " got " << num_bytes << std::endl; + + //step 2.5 -- update endpoint + { + boost::mutex::scoped_lock l(endpoint_mutex); + *endpoint = _rx_endpoint; + } + + //step 3 - commit the buffer + buff->commit(num_bytes); + } + } + catch(const std::exception &ex) + { + UHD_MSG(error) << "e300_send_tunnel exit " << name << " " << ex.what() << std::endl; + } + catch(...) + { + UHD_MSG(error) << "e300_send_tunnel exit " << name << std::endl; + } + UHD_MSG(status) << "e300_send_tunnel exit " << name << std::endl; + *running = false; +} + +static void e300_codec_ctrl_tunnel( + const std::string &name, + boost::shared_ptr socket, + ad9361_ctrl::sptr _codec_ctrl, + asio::ip::udp::endpoint *endpoint, + bool *running +) +{ + asio::ip::udp::endpoint _endpoint; + try + { + while (*running) + { + uint8_t in_buff[64] = {}; + uint8_t out_buff[64] = {}; + + const size_t num_bytes = socket->receive_from(asio::buffer(in_buff), *endpoint); + + typedef e300_remote_codec_ctrl::transaction_t codec_xact_t; + + if (num_bytes < sizeof(codec_xact_t)) { + std::cout << "Received short packet of " << num_bytes << std::endl; + continue; + } + + codec_xact_t *in = reinterpret_cast(in_buff); + codec_xact_t *out = reinterpret_cast(out_buff); + std::memcpy(out, in, sizeof(codec_xact_t)); + + std::string which_str; + switch (uhd::ntohx(in->which)) { + case codec_xact_t::CHAIN_TX1: + which_str = "TX1"; break; + case codec_xact_t::CHAIN_TX2: + which_str = "TX2"; break; + case codec_xact_t::CHAIN_RX1: + which_str = "RX1"; break; + case codec_xact_t::CHAIN_RX2: + which_str = "RX2"; break; + default: + which_str = ""; break; + } + + switch (uhd::ntohx(in->action)) { + case codec_xact_t::ACTION_SET_GAIN: + out->gain = _codec_ctrl->set_gain(which_str, in->gain); + break; + case codec_xact_t::ACTION_SET_CLOCK_RATE: + out->rate = _codec_ctrl->set_clock_rate(in->rate); + break; + case codec_xact_t::ACTION_SET_ACTIVE_CHANS: + _codec_ctrl->set_active_chains( + uhd::ntohx(in->bits) & (1<<0), + uhd::ntohx(in->bits) & (1<<1), + uhd::ntohx(in->bits) & (1<<2), + uhd::ntohx(in->bits) & (1<<3)); + break; + case codec_xact_t::ACTION_TUNE: + out->freq = _codec_ctrl->tune(which_str, in->freq); + break; + case codec_xact_t::ACTION_SET_LOOPBACK: + _codec_ctrl->data_port_loopback( + uhd::ntohx(in->bits) & 1); + break; + default: + UHD_MSG(status) << "Got unknown request?!" << std::endl; + //Zero out actions to fail this request on client + out->action = uhd::htonx(0); + } + + socket->send_to(asio::buffer(out_buff, 64), *endpoint); + } + } + catch(const std::exception &ex) + { + UHD_MSG(error) << "e300_ctrl_tunnel exit " << name << " " << ex.what() << std::endl; + } + catch(...) + { + UHD_MSG(error) << "e300_ctrl_tunnel exit " << name << std::endl; + } + UHD_MSG(status) << "e300_ctrl_tunnel exit " << name << std::endl; + *running = false; +} + +static void e300_global_regs_tunnel( + const std::string &name, + boost::shared_ptr socket, + global_regs::sptr regs, + asio::ip::udp::endpoint *endpoint, + bool *running +) +{ + UHD_ASSERT_THROW(regs); + asio::ip::udp::endpoint _endpoint; + try + { + while (*running) + { + uint8_t in_buff[16] = {}; + + const size_t num_bytes = socket->receive_from(asio::buffer(in_buff), *endpoint); + + if (num_bytes < 16) { + std::cout << "Received short packet: " << num_bytes << std::endl; + continue; + } + + global_regs_transaction_t *in = + reinterpret_cast(in_buff); + + if(uhd::ntohx(in->is_poke)) { + regs->poke32(uhd::ntohx(in->addr), uhd::ntohx(in->data)); + } + else { + in->data = uhd::htonx(regs->peek32(uhd::ntohx(in->addr))); + socket->send_to(asio::buffer(in_buff, 16), *endpoint); + } + } + } + catch(const std::exception &ex) + { + UHD_MSG(error) << "e300_gregs_tunnel exit " << name << " " << ex.what() << std::endl; + } + catch(...) + { + UHD_MSG(error) << "e300_gregs_tunnel exit " << name << std::endl; + } + UHD_MSG(status) << "e300_gregs_tunnel exit " << name << std::endl; + *running = false; +} + +static void e300_sensor_tunnel( + const std::string &name, + boost::shared_ptr socket, + e300_sensor_manager::sptr sensor_manager, + asio::ip::udp::endpoint *endpoint, + bool *running +) +{ + asio::ip::udp::endpoint _endpoint; + try + { + while (*running) + { + uint8_t in_buff[128] = {}; + + const size_t num_bytes = socket->receive_from(asio::buffer(in_buff), *endpoint); + + if (num_bytes < sizeof(sensor_transaction_t)) { + std::cout << "Received short packet: " << num_bytes << std::endl; + continue; + } + + uhd::usrp::e300::sensor_transaction_t *in = + reinterpret_cast(in_buff); + + if (uhd::ntohx(in->which) == ZYNQ_TEMP) { + sensor_value_t temp = sensor_manager->get_mb_temp(); + // TODO: This is ugly ... use proper serialization + in->value = uhd::htonx( + e300_sensor_manager::pack_float_in_uint32_t(temp.to_real())); + } else if (uhd::ntohx(in->which) == GPS_FOUND) { + in->value = uhd::htonx( + sensor_manager->get_gps_found() ? 1 : 0); + + } else if (uhd::ntohx(in->which) == GPS_LOCK) { + in->value = uhd::htonx( + sensor_manager->get_gps_lock().to_bool() ? 1 : 0); + } else if (uhd::ntohx(in->which) == GPS_TIME) { + in->value = uhd::htonx( + sensor_manager->get_gps_time().to_int()); + } else + UHD_MSG(status) << "Got unknown request?!" << std::endl; + + socket->send_to(asio::buffer(in_buff, sizeof(sensor_transaction_t)), *endpoint); + } + } + catch(const std::exception &ex) + { + UHD_MSG(error) << "e300_sensor_tunnel exit " << name << " " << ex.what() << std::endl; + } + catch(...) + { + UHD_MSG(error) << "e300_sensor_tunnel exit " << name << std::endl; + } + UHD_MSG(status) << "e300_sensor_tunnel exit " << name << std::endl; + *running = false; +} + +static void e300_i2c_tunnel( + const std::string &name, + boost::shared_ptr socket, + uhd::usrp::e300::i2c::sptr i2c, + asio::ip::udp::endpoint *endpoint, + bool *running +) +{ + UHD_ASSERT_THROW(i2c); + asio::ip::udp::endpoint _endpoint; + try + { + while (*running) + { + uint8_t in_buff[sizeof(uhd::usrp::e300::i2c_transaction_t)]; + + const size_t num_bytes = socket->receive_from(asio::buffer(in_buff), *endpoint); + + if (num_bytes < sizeof(uhd::usrp::e300::i2c_transaction_t)) { + std::cout << "Received short packet: " << num_bytes << std::endl; + continue; + } + + uhd::usrp::e300::i2c_transaction_t *in = + reinterpret_cast(in_buff); + + // byte addressed accesses go through here + if(in->type & i2c::ONEBYTE) { + if(in->type & i2c::WRITE) { + i2c->set_i2c_reg8( + in->addr, + uhd::ntohx(in->reg), in->data); + } else { + in->data = i2c->get_i2c_reg8(in->addr, uhd::ntohx(in->reg)); + socket->send_to(asio::buffer(in_buff, sizeof(in_buff)), *endpoint); + } + + // 2 byte addressed accesses go through here + } else if (in->type & i2c::TWOBYTE) { + if(in->type & i2c::WRITE) { + i2c->set_i2c_reg16( + in->addr, + uhd::ntohx(in->reg), in->data); + } else { + in->data = i2c->get_i2c_reg16(in->addr, uhd::ntohx(in->reg)); + socket->send_to(asio::buffer(in_buff, sizeof(in_buff)), *endpoint); + } + + } else { + UHD_MSG(error) << "e300_i2c_tunnel could not handle message." << std::endl; + } + } + } + catch(const std::exception &ex) + { + UHD_MSG(error) << "e300_i2c_tunnel exit " << name << " " << ex.what() << std::endl; + } + catch(...) + { + UHD_MSG(error) << "e300_i2c_tunnel exit " << name << std::endl; + } + UHD_MSG(status) << "e300_i2c_tunnel exit " << name << std::endl; + *running = false; +} + + + + +class network_server_impl : public network_server +{ +public: + network_server_impl(const uhd::device_addr_t &device_addr); + virtual ~network_server_impl(void); + void run(void); + +private: + struct xports_t + { + uhd::transport::zero_copy_if::sptr send_ctrl_xport; + uhd::transport::zero_copy_if::sptr recv_ctrl_xport; + uhd::transport::zero_copy_if::sptr tx_data_xport; + uhd::transport::zero_copy_if::sptr tx_flow_xport; + uhd::transport::zero_copy_if::sptr rx_data_xport; + uhd::transport::zero_copy_if::sptr rx_flow_xport; + }; + +private: + void _run_server( + const std::string &port, + const std::string &what, + const size_t fe); + +private: + boost::shared_ptr _fifo_iface; + xports_t _xports[2]; + boost::shared_ptr _codec_ctrl; + boost::shared_ptr _global_regs; + boost::shared_ptr _sensor_manager; + boost::shared_ptr _eeprom_manager; +}; + +network_server_impl::~network_server_impl(void) +{ +} + +/*********************************************************************** + * The UDP server itself + **********************************************************************/ +void network_server_impl::_run_server( + const std::string &port, + const std::string &what, + const size_t fe) +{ + asio::io_service io_service; + asio::ip::udp::resolver resolver(io_service); + asio::ip::udp::resolver::query query(asio::ip::udp::v4(), "0.0.0.0", port); + asio::ip::udp::endpoint endpoint = *resolver.resolve(query); + + //boost::shared_ptr acceptor(new asio::ip::udp::acceptor(io_service, endpoint)); + while (not boost::this_thread::interruption_requested()) + { + UHD_MSG(status) << "e300 run server on port " << port << " for " << what << std::endl; + try + { + //while (not wait_for_recv_ready(acceptor->native(), 100)) + //{ + // if (boost::this_thread::interruption_requested()) return; + //} + boost::shared_ptr socket; + socket.reset(new asio::ip::udp::socket(io_service, endpoint)); + //acceptor->accept(*socket); + UHD_MSG(status) << "e300 socket accept on port " << port << " for " << what << std::endl; + //asio::ip::udp::no_delay option(true); + //socket->set_option(option); + boost::thread_group tg; + bool running = true; + xports_t &perif = _xports[fe]; + if (what == "RX") { + tg.create_thread(boost::bind(&e300_recv_tunnel, "RX data tunnel", perif.rx_data_xport, socket, &endpoint, &running)); + tg.create_thread(boost::bind(&e300_send_tunnel, "RX flow tunnel", socket, perif.rx_flow_xport, &endpoint, &running)); + } + if (what == "TX") { + tg.create_thread(boost::bind(&e300_recv_tunnel, "TX flow tunnel", perif.tx_flow_xport, socket, &endpoint, &running)); + tg.create_thread(boost::bind(&e300_send_tunnel, "TX data tunnel", socket, perif.tx_data_xport, &endpoint, &running)); + } + if (what == "CTRL") { + tg.create_thread(boost::bind(&e300_recv_tunnel, "response tunnel", perif.recv_ctrl_xport, socket, &endpoint, &running)); + tg.create_thread(boost::bind(&e300_send_tunnel, "control tunnel", socket, perif.send_ctrl_xport, &endpoint, &running)); + } + if (what == "CODEC") { + tg.create_thread(boost::bind(&e300_codec_ctrl_tunnel, "CODEC tunnel", socket, _codec_ctrl, &endpoint, &running)); + } + if (what == "I2C") { + tg.create_thread(boost::bind(&e300_i2c_tunnel, "I2C tunnel", socket, _eeprom_manager->get_i2c_sptr(), &endpoint, &running)); + } + if (what == "GREGS") { + tg.create_thread(boost::bind(&e300_global_regs_tunnel, "GREGS tunnel", socket, _global_regs, &endpoint, &running)); + } + if (what == "SENSOR") { + tg.create_thread(boost::bind(&e300_sensor_tunnel, "SENSOR tunnel", socket, _sensor_manager, &endpoint, &running)); + } + + tg.join_all(); + socket->close(); + socket.reset(); + } + catch(...){} + } +} + +void network_server_impl::run() +{ + for(;;) + { + boost::thread_group tg; + tg.create_thread(boost::bind(&network_server_impl::_run_server, this, E300_SERVER_RX_PORT0, "RX",0)); + tg.create_thread(boost::bind(&network_server_impl::_run_server, this, E300_SERVER_TX_PORT0, "TX",0)); + tg.create_thread(boost::bind(&network_server_impl::_run_server, this, E300_SERVER_CTRL_PORT0, "CTRL",0)); + + tg.create_thread(boost::bind(&network_server_impl::_run_server, this, E300_SERVER_RX_PORT1, "RX",1)); + tg.create_thread(boost::bind(&network_server_impl::_run_server, this, E300_SERVER_TX_PORT1, "TX",1)); + tg.create_thread(boost::bind(&network_server_impl::_run_server, this, E300_SERVER_CTRL_PORT1, "CTRL",1)); + + tg.create_thread(boost::bind(&network_server_impl::_run_server, this, E300_SERVER_SENSOR_PORT, "SENSOR", 0 /*don't care */)); + + tg.create_thread(boost::bind(&network_server_impl::_run_server, this, E300_SERVER_CODEC_PORT, "CODEC", 0 /*don't care */)); + tg.create_thread(boost::bind(&network_server_impl::_run_server, this, E300_SERVER_GREGS_PORT, "GREGS", 0 /*don't care */)); + tg.create_thread(boost::bind(&network_server_impl::_run_server, this, E300_SERVER_I2C_PORT, "I2C", 0 /*don't care */)); + tg.join_all(); + } +} +network_server_impl::network_server_impl(const uhd::device_addr_t &device_addr) +{ + _eeprom_manager = boost::make_shared(i2c::make_i2cdev(E300_I2CDEV_DEVICE)); + if (not device_addr.has_key("no_reload_fpga")) { + // Load FPGA image if provided via args + if (device_addr.has_key("fpga")) { + common::load_fpga_image(device_addr["fpga"]); + // Else load the FPGA image based on the product ID + } else { + //extract the FPGA path for the e300 + const boost::uint16_t pid = boost::lexical_cast( + _eeprom_manager->get_mb_eeprom()["product"]); + std::string fpga_image; + switch(e300_eeprom_manager::get_mb_type(pid)) { + case e300_eeprom_manager::USRP_E310_MB: + fpga_image = find_image_path(E310_FPGA_FILE_NAME); + break; + case e300_eeprom_manager::USRP_E300_MB: + fpga_image = find_image_path(E300_FPGA_FILE_NAME); + break; + case e300_eeprom_manager::UNKNOWN: + default: + UHD_MSG(warning) << "Unknown motherboard type, loading e300 image." + << std::endl; + fpga_image = find_image_path(E300_FPGA_FILE_NAME); + break; + } + common::load_fpga_image(fpga_image); + } + } + + uhd::transport::zero_copy_xport_params ctrl_xport_params; + ctrl_xport_params.recv_frame_size = e300::DEFAULT_CTRL_FRAME_SIZE; + ctrl_xport_params.num_recv_frames = e300::DEFAULT_CTRL_NUM_FRAMES; + ctrl_xport_params.send_frame_size = e300::DEFAULT_CTRL_FRAME_SIZE; + ctrl_xport_params.num_send_frames = e300::DEFAULT_CTRL_NUM_FRAMES; + + uhd::transport::zero_copy_xport_params data_xport_params; + data_xport_params.recv_frame_size = device_addr.cast("recv_frame_size", e300::DEFAULT_RX_DATA_FRAME_SIZE); + data_xport_params.num_recv_frames = device_addr.cast("num_recv_frames", e300::DEFAULT_RX_DATA_NUM_FRAMES); + data_xport_params.send_frame_size = device_addr.cast("send_frame_size", e300::DEFAULT_TX_DATA_FRAME_SIZE); + data_xport_params.num_send_frames = device_addr.cast("num_send_frames", e300::DEFAULT_TX_DATA_NUM_FRAMES); + // until we figure out why this goes wrong we'll keep this hack around + data_xport_params.recv_frame_size = + std::min(e300::MAX_NET_RX_DATA_FRAME_SIZE, data_xport_params.recv_frame_size); + data_xport_params.send_frame_size = + std::min(e300::MAX_NET_TX_DATA_FRAME_SIZE, data_xport_params.send_frame_size); + + + e300_fifo_config_t fifo_cfg; + try { + fifo_cfg = e300_read_sysfs(); + } catch (uhd::lookup_error &e) { + throw uhd::runtime_error("Failed to get driver parameters from sysfs."); + } + _fifo_iface = e300_fifo_interface::make(fifo_cfg); + _global_regs = global_regs::make(_fifo_iface->get_global_regs_base()); + + // static mapping, boooohhhhhh + _xports[0].send_ctrl_xport = _fifo_iface->make_send_xport(E300_R0_CTRL_STREAM, ctrl_xport_params); + _xports[0].recv_ctrl_xport = _fifo_iface->make_recv_xport(E300_R0_CTRL_STREAM, ctrl_xport_params); + _xports[0].tx_data_xport = _fifo_iface->make_send_xport(E300_R0_TX_DATA_STREAM, data_xport_params); + _xports[0].tx_flow_xport = _fifo_iface->make_recv_xport(E300_R0_TX_DATA_STREAM, ctrl_xport_params); + _xports[0].rx_data_xport = _fifo_iface->make_recv_xport(E300_R0_RX_DATA_STREAM, data_xport_params); + _xports[0].rx_flow_xport = _fifo_iface->make_send_xport(E300_R0_RX_DATA_STREAM, ctrl_xport_params); + + _xports[1].send_ctrl_xport = _fifo_iface->make_send_xport(E300_R1_CTRL_STREAM, ctrl_xport_params); + _xports[1].recv_ctrl_xport = _fifo_iface->make_recv_xport(E300_R1_CTRL_STREAM, ctrl_xport_params); + _xports[1].tx_data_xport = _fifo_iface->make_send_xport(E300_R1_TX_DATA_STREAM, data_xport_params); + _xports[1].tx_flow_xport = _fifo_iface->make_recv_xport(E300_R1_TX_DATA_STREAM, ctrl_xport_params); + _xports[1].rx_data_xport = _fifo_iface->make_recv_xport(E300_R1_RX_DATA_STREAM, data_xport_params); + _xports[1].rx_flow_xport = _fifo_iface->make_send_xport(E300_R1_RX_DATA_STREAM, ctrl_xport_params); + + ad9361_params::sptr client_settings = boost::make_shared(); + _codec_ctrl = ad9361_ctrl::make_spi(client_settings, spi::make(E300_SPIDEV_DEVICE), 1); + // This is horrible ... why do I have to sleep here? + boost::this_thread::sleep(boost::posix_time::milliseconds(100)); + _sensor_manager = e300_sensor_manager::make_local( + gps::ublox::ubx::control::make("/dev/ttyPS1", 9600)); +} + +}}} // namespace + +using namespace uhd::usrp::e300; + +network_server::sptr network_server::make(const uhd::device_addr_t &device_addr) +{ + return sptr(new network_server_impl(device_addr)); +} + +#else + +using namespace uhd::usrp::e300; + +network_server::sptr network_server::make(const uhd::device_addr_t &) +{ + throw uhd::assertion_error("network_server::make() !E300_NATIVE"); +} +#endif diff --git a/host/lib/usrp/e300/e300_network.hpp b/host/lib/usrp/e300/e300_network.hpp new file mode 100644 index 000000000..5a9df0ca7 --- /dev/null +++ b/host/lib/usrp/e300/e300_network.hpp @@ -0,0 +1,43 @@ +// +// Copyright 2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +#ifndef INCLUDED_E300_NETWORK_HPP +#define INCLUDED_E300_NETWORK_HPP + +#include +#include + +#include + + +static const std::string E310_FPGA_FILE_NAME = "usrp_e310_fpga.bit"; +static const std::string E300_FPGA_FILE_NAME = "usrp_e300_fpga.bit"; + +namespace uhd { namespace usrp { namespace e300 { + +class UHD_API network_server : boost::noncopyable +{ +public: + typedef boost::shared_ptr sptr; + virtual void run(void) = 0; + + static sptr make(const uhd::device_addr_t &device_addr); +}; + + +}}} +#endif // INCLUDED_E300_NETWORK_HPP diff --git a/host/lib/usrp/e300/e300_regs.hpp b/host/lib/usrp/e300/e300_regs.hpp new file mode 100644 index 000000000..f99a19b8e --- /dev/null +++ b/host/lib/usrp/e300/e300_regs.hpp @@ -0,0 +1,69 @@ +// +// Copyright 2012-2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +#ifndef INCLUDED_E300_REGS_HPP +#define INCLUDED_E300_REGS_HPP + +#include + +#define TOREG(x) ((x)*4) + +#define localparam static const int + +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 SR_CODEC_IDLE = 250; + + +localparam RB32_SPI = 4; +localparam RB64_TIME_NOW = 8; +localparam RB64_TIME_PPS = 16; +localparam RB32_TEST = 24; +localparam RB32_FP_GPIO = 32; +localparam RB64_CODEC_READBACK = 40; +localparam RB32_RADIO_NUM = 48; + +localparam ST_RX_ENABLE = 20; +localparam ST_TX_ENABLE = 19; + +localparam LED_TXRX_TX = 18; +localparam LED_TXRX_RX = 17; +localparam LED_RX_RX = 16; +localparam VCRX_V2 = 15; +localparam VCRX_V1 = 14; +localparam VCTXRX_V2 = 13; +localparam VCTXRX_V1 = 12; +localparam TX_ENABLEB = 11; +localparam TX_ENABLEA = 10; +localparam RXC_BANDSEL = 8; +localparam RXB_BANDSEL = 6; +localparam RX_BANDSEL = 3; +localparam TX_BANDSEL = 0; + +#endif /* INCLUDED_E300_REGS_HPP */ diff --git a/host/lib/usrp/e300/e300_remote_codec_ctrl.cpp b/host/lib/usrp/e300/e300_remote_codec_ctrl.cpp new file mode 100644 index 000000000..bcc8ee4cf --- /dev/null +++ b/host/lib/usrp/e300/e300_remote_codec_ctrl.cpp @@ -0,0 +1,148 @@ +// +// 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 . +// + +#include "e300_remote_codec_ctrl.hpp" + +#include +#include +#include +#include +#include + +namespace uhd { namespace usrp { namespace e300 { + +class e300_remote_codec_ctrl_impl : public e300_remote_codec_ctrl +{ +public: + e300_remote_codec_ctrl_impl(uhd::transport::zero_copy_if::sptr xport) : _xport(xport) + { + } + + virtual ~e300_remote_codec_ctrl_impl(void) + { + } + + double set_gain(const std::string &which, const double value) + { + _clear(); + _args.action = uhd::htonx(transaction_t::ACTION_SET_GAIN); + if (which == "TX1") _args.which = uhd::htonx(transaction_t::CHAIN_TX1); + else if (which == "TX2") _args.which = uhd::htonx(transaction_t::CHAIN_TX2); + else if (which == "RX1") _args.which = uhd::htonx(transaction_t::CHAIN_RX1); + else if (which == "RX2") _args.which = uhd::htonx(transaction_t::CHAIN_RX2); + else throw std::runtime_error("e300_remote_codec_ctrl_impl incorrect chain string."); + _args.gain = value; + + _transact(); + return _retval.gain; + } + + double set_clock_rate(const double rate) + { + _clear(); + _args.action = uhd::htonx( + transaction_t::ACTION_SET_CLOCK_RATE); + _args.which = uhd::htonx( + transaction_t::CHAIN_NONE); /*Unused*/ + _args.rate = rate; + + _transact(); + return _retval.gain; + } + + void set_active_chains(bool tx1, bool tx2, bool rx1, bool rx2) + { + _clear(); + _args.action = uhd::htonx( + transaction_t::ACTION_SET_ACTIVE_CHANS); + /*Unused*/ + _args.which = uhd::htonx( + transaction_t::CHAIN_NONE); + _args.bits = uhd::htonx( + (tx1 ? (1<<0) : 0) | + (tx2 ? (1<<1) : 0) | + (rx1 ? (1<<2) : 0) | + (rx2 ? (1<<3) : 0)); + + _transact(); + } + + double tune(const std::string &which, const double value) + { + _clear(); + _args.action = uhd::htonx(transaction_t::ACTION_TUNE); + if (which == "TX1") _args.which = uhd::htonx(transaction_t::CHAIN_TX1); + else if (which == "TX2") _args.which = uhd::htonx(transaction_t::CHAIN_TX2); + else if (which == "RX1") _args.which = uhd::htonx(transaction_t::CHAIN_RX1); + else if (which == "RX2") _args.which = uhd::htonx(transaction_t::CHAIN_RX2); + else throw std::runtime_error("e300_remote_codec_ctrl_impl incorrect chain string."); + _args.freq = value; + + _transact(); + return _retval.freq; + } + + void data_port_loopback(const bool on) + { + _clear(); + _args.action = uhd::htonx(transaction_t::ACTION_SET_LOOPBACK); + _args.which = uhd::htonx(transaction_t::CHAIN_NONE); /*Unused*/ + _args.bits = uhd::htonx(on ? 1 : 0); + + _transact(); + } + +private: + void _transact() { + { + uhd::transport::managed_send_buffer::sptr buff = _xport->get_send_buff(10.0); + if (not buff or buff->size() < sizeof(_args)) + throw std::runtime_error("e300_remote_codec_ctrl_impl send timeout"); + std::memcpy(buff->cast(), &_args, sizeof(_args)); + buff->commit(sizeof(_args)); + } + { + uhd::transport::managed_recv_buffer::sptr buff = _xport->get_recv_buff(10.0); + if (not buff or buff->size() < sizeof(_retval)) + throw std::runtime_error("e300_remote_codec_ctrl_impl recv timeout"); + std::memcpy(&_retval, buff->cast(), sizeof(_retval)); + } + + if (_args.action != _retval.action) + throw std::runtime_error("e300_remote_codec_ctrl_impl trancation failed."); + } + + void _clear() { + _args.action = 0; + _args.which = 0; + _args.bits = 0; + _retval.action = 0; + _retval.which = 0; + _retval.bits = 0; + } + + uhd::transport::zero_copy_if::sptr _xport; + transaction_t _args; + transaction_t _retval; +}; + +ad9361_ctrl::sptr e300_remote_codec_ctrl::make(uhd::transport::zero_copy_if::sptr xport) +{ + return sptr(new e300_remote_codec_ctrl_impl(xport)); +} + +}}}; diff --git a/host/lib/usrp/e300/e300_remote_codec_ctrl.hpp b/host/lib/usrp/e300/e300_remote_codec_ctrl.hpp new file mode 100644 index 000000000..015ad8323 --- /dev/null +++ b/host/lib/usrp/e300/e300_remote_codec_ctrl.hpp @@ -0,0 +1,59 @@ +// +// Copyright 2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +#ifndef INCLUDED_E300_REMOTE_CODEC_CTRL_HPP +#define INCLUDED_E300_REMOTE_CODEC_CTRL_HPP + +#include "ad9361_ctrl.hpp" +#include + +namespace uhd { namespace usrp { namespace e300 { + +class e300_remote_codec_ctrl : public uhd::usrp::ad9361_ctrl +{ +public: + struct transaction_t { + boost::uint32_t action; + boost::uint32_t which; + union { + double rate; + double gain; + double freq; + boost::uint64_t bits; + }; + + //Actions + static const boost::uint32_t ACTION_SET_GAIN = 10; + static const boost::uint32_t ACTION_SET_CLOCK_RATE = 11; + static const boost::uint32_t ACTION_SET_ACTIVE_CHANS = 12; + static const boost::uint32_t ACTION_TUNE = 13; + static const boost::uint32_t ACTION_SET_LOOPBACK = 14; + + //Values for "which" + static const boost::uint32_t CHAIN_NONE = 0; + static const boost::uint32_t CHAIN_TX1 = 1; + static const boost::uint32_t CHAIN_TX2 = 2; + static const boost::uint32_t CHAIN_RX1 = 3; + static const boost::uint32_t CHAIN_RX2 = 4; + }; + + static sptr make(uhd::transport::zero_copy_if::sptr xport); +}; + +}}}; + +#endif /* INCLUDED_E300_REMOTE_CODEC_CTRL_HPP */ diff --git a/host/lib/usrp/e300/e300_sensor_manager.cpp b/host/lib/usrp/e300/e300_sensor_manager.cpp new file mode 100644 index 000000000..5e65b8fd3 --- /dev/null +++ b/host/lib/usrp/e300/e300_sensor_manager.cpp @@ -0,0 +1,289 @@ +// +// 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 . +// + +#include "e300_sensor_manager.hpp" + +#include +#include +#include + +#include +#include +#include +#include + +namespace uhd { namespace usrp { namespace e300 { + +class e300_sensor_proxy : public e300_sensor_manager +{ +public: + e300_sensor_proxy( + uhd::transport::zero_copy_if::sptr xport) : _xport(xport) + { + } + + std::vector get_sensors() + { + return boost::assign::list_of("temp")("gps_locked")("gps_time"); + } + + uhd::sensor_value_t get_sensor(const std::string &key) + { + if (key == "temp") + return get_mb_temp(); + else if (key == "gps_locked") + return get_gps_lock(); + else if (key == "gps_time") + return get_gps_time(); + else + throw uhd::lookup_error( + str(boost::format("Invalid sensor %s requested.") % key)); + } + + uhd::sensor_value_t get_mb_temp(void) + { + boost::mutex::scoped_lock(_mutex); + sensor_transaction_t transaction; + transaction.which = uhd::htonx(ZYNQ_TEMP); + { + uhd::transport::managed_send_buffer::sptr buff + = _xport->get_send_buff(1.0); + if (not buff or buff->size() < sizeof(transaction)) { + throw uhd::runtime_error("sensor proxy send timeout"); + } + std::memcpy( + buff->cast(), + &transaction, + sizeof(transaction)); + buff->commit(sizeof(transaction)); + } + { + uhd::transport::managed_recv_buffer::sptr buff + = _xport->get_recv_buff(1.0); + + if (not buff or buff->size() < sizeof(transaction)) + throw uhd::runtime_error("sensor proxy recv timeout"); + + std::memcpy( + &transaction, + buff->cast(), + sizeof(transaction)); + } + UHD_ASSERT_THROW(uhd::ntohx(transaction.which) == ZYNQ_TEMP); + // TODO: Use proper serialization here ... + return sensor_value_t( + "temp", + e300_sensor_manager::unpack_float_from_uint32_t( + uhd::ntohx(transaction.value)), + "C"); + } + + uhd::sensor_value_t get_gps_time(void) + { + boost::mutex::scoped_lock(_mutex); + sensor_transaction_t transaction; + transaction.which = uhd::htonx(GPS_TIME); + { + uhd::transport::managed_send_buffer::sptr buff + = _xport->get_send_buff(1.0); + if (not buff or buff->size() < sizeof(transaction)) { + throw uhd::runtime_error("sensor proxy send timeout"); + } + std::memcpy( + buff->cast(), + &transaction, + sizeof(transaction)); + buff->commit(sizeof(transaction)); + } + { + uhd::transport::managed_recv_buffer::sptr buff + = _xport->get_recv_buff(1.0); + + if (not buff or buff->size() < sizeof(transaction)) + throw uhd::runtime_error("sensor proxy recv timeout"); + + std::memcpy( + &transaction, + buff->cast(), + sizeof(transaction)); + } + UHD_ASSERT_THROW(uhd::ntohx(transaction.which) == GPS_TIME); + // TODO: Use proper serialization here ... + return sensor_value_t("GPS epoch time", int(uhd::ntohx(transaction.value)), "seconds"); + } + + bool get_gps_found(void) + { + boost::mutex::scoped_lock(_mutex); + sensor_transaction_t transaction; + transaction.which = uhd::htonx(GPS_FOUND); + { + uhd::transport::managed_send_buffer::sptr buff + = _xport->get_send_buff(1.0); + if (not buff or buff->size() < sizeof(transaction)) { + throw uhd::runtime_error("sensor proxy send timeout"); + } + std::memcpy( + buff->cast(), + &transaction, + sizeof(transaction)); + buff->commit(sizeof(transaction)); + } + { + uhd::transport::managed_recv_buffer::sptr buff + = _xport->get_recv_buff(1.0); + + if (not buff or buff->size() < sizeof(transaction)) + throw uhd::runtime_error("sensor proxy recv timeout"); + + std::memcpy( + &transaction, + buff->cast(), + sizeof(transaction)); + } + UHD_ASSERT_THROW(uhd::ntohx(transaction.which) == GPS_FOUND); + // TODO: Use proper serialization here ... + return static_cast(uhd::ntohx(transaction.value)); + } + + uhd::sensor_value_t get_gps_lock(void) + { + boost::mutex::scoped_lock(_mutex); + sensor_transaction_t transaction; + transaction.which = uhd::htonx(GPS_LOCK); + { + uhd::transport::managed_send_buffer::sptr buff + = _xport->get_send_buff(1.0); + if (not buff or buff->size() < sizeof(transaction)) { + throw uhd::runtime_error("sensor proxy send timeout"); + } + std::memcpy( + buff->cast(), + &transaction, + sizeof(transaction)); + buff->commit(sizeof(transaction)); + } + { + uhd::transport::managed_recv_buffer::sptr buff + = _xport->get_recv_buff(1.0); + + if (not buff or buff->size() < sizeof(transaction)) + throw uhd::runtime_error("sensor proxy recv timeout"); + + std::memcpy( + &transaction, + buff->cast(), + sizeof(transaction)); + } + UHD_ASSERT_THROW(uhd::ntohx(transaction.which) == GPS_LOCK); + // TODO: Use proper serialization here ... + return sensor_value_t("GPS lock status", static_cast(uhd::ntohx(transaction.value)), "locked", "unlocked"); + } + +private: + uhd::transport::zero_copy_if::sptr _xport; + boost::mutex _mutex; +}; + +}}} // namespace + +using namespace uhd::usrp::e300; + +e300_sensor_manager::sptr e300_sensor_manager::make_proxy( + uhd::transport::zero_copy_if::sptr xport) +{ + return sptr(new e300_sensor_proxy(xport)); +} + +#ifdef E300_NATIVE +#include "e300_fifo_config.hpp" + +namespace uhd { namespace usrp { namespace e300 { + +static const std::string E300_TEMP_SYSFS = "iio:device0"; + +class e300_sensor_local : public e300_sensor_manager +{ +public: + e300_sensor_local(uhd::gps_ctrl::sptr gps_ctrl) : _gps_ctrl(gps_ctrl) + { + } + + std::vector get_sensors() + { + return boost::assign::list_of("temp")("gps_locked")("gps_time"); + } + + uhd::sensor_value_t get_sensor(const std::string &key) + { + if (key == "temp") + return get_mb_temp(); + else if (key == "gps_locked") + return get_gps_lock(); + else if (key == "gps_time") + return get_gps_time(); + else + throw uhd::lookup_error( + str(boost::format("Invalid sensor %s requested.") % key)); + } + + uhd::sensor_value_t get_mb_temp(void) + { + double scale = boost::lexical_cast( + e300_get_sysfs_attr(E300_TEMP_SYSFS, "in_temp0_scale")); + unsigned long raw = boost::lexical_cast( + e300_get_sysfs_attr(E300_TEMP_SYSFS, "in_temp0_raw")); + unsigned long offset = boost::lexical_cast( + e300_get_sysfs_attr(E300_TEMP_SYSFS, "in_temp0_offset")); + return sensor_value_t("temp", (raw + offset) * scale / 1000, "C"); + } + + bool get_gps_found(void) + { + return _gps_ctrl->gps_detected(); + } + + uhd::sensor_value_t get_gps_lock(void) + { + return _gps_ctrl->get_sensor("gps_locked"); + } + + uhd::sensor_value_t get_gps_time(void) + { + return _gps_ctrl->get_sensor("gps_time"); + } + +private: + gps_ctrl::sptr _gps_ctrl; +}; +}}} + +using namespace uhd::usrp::e300; +e300_sensor_manager::sptr e300_sensor_manager::make_local( + uhd::gps_ctrl::sptr gps_ctrl) +{ + return sptr(new e300_sensor_local(gps_ctrl)); +} + +#else +using namespace uhd::usrp::e300; +e300_sensor_manager::sptr e300_sensor_manager::make_local( + uhd::gps_ctrl::sptr gps_ctrl) +{ + throw uhd::assertion_error("e300_sensor_manager::make_local() !E300_NATIVE"); +} +#endif diff --git a/host/lib/usrp/e300/e300_sensor_manager.hpp b/host/lib/usrp/e300/e300_sensor_manager.hpp new file mode 100644 index 000000000..503a7bb63 --- /dev/null +++ b/host/lib/usrp/e300/e300_sensor_manager.hpp @@ -0,0 +1,77 @@ +// +// 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 . +// + +#include +#include + +#include +#include +#include +#include + +#ifndef INCLUDED_E300_SENSOR_MANAGER_HPP +#define INCLUDED_E300_SENSOR_MANAGER_HPP + +namespace uhd { namespace usrp { namespace e300 { + +struct sensor_transaction_t { + boost::uint32_t which; + union { + boost::uint32_t value; + boost::uint32_t value64; + }; +}; + + + +enum sensor {ZYNQ_TEMP=0, GPS_FOUND=1, GPS_TIME=2, + GPS_LOCK=3}; + +class e300_sensor_manager : boost::noncopyable +{ +public: + typedef boost::shared_ptr sptr; + virtual bool get_gps_found(void) = 0; + + virtual uhd::sensor_value_t get_sensor(const std::string &key) = 0; + virtual std::vector get_sensors(void) = 0; + + virtual uhd::sensor_value_t get_mb_temp(void) = 0; + virtual uhd::sensor_value_t get_gps_lock(void) = 0; + virtual uhd::sensor_value_t get_gps_time(void) = 0; + + static sptr make_proxy(uhd::transport::zero_copy_if::sptr xport); + static sptr make_local(uhd::gps_ctrl::sptr gps_ctrl); + + // Note: This is a hack + static boost::uint32_t pack_float_in_uint32_t(const float &v) + { + const boost::uint32_t *cast = reinterpret_cast(&v); + return *cast; + } + + static float unpack_float_from_uint32_t(const boost::uint32_t &v) + { + const float *cast = reinterpret_cast(&v); + return *cast; + } +}; + + +}}} // namespace + +#endif // INCLUDED_E300_SENSOR_MANAGER_HPP diff --git a/host/lib/usrp/e300/e300_spi.cpp b/host/lib/usrp/e300/e300_spi.cpp new file mode 100644 index 000000000..9a2daf4a7 --- /dev/null +++ b/host/lib/usrp/e300/e300_spi.cpp @@ -0,0 +1,127 @@ +// +// 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 . +// + +#include +#include +#include "e300_spi.hpp" + +#ifdef E300_NATIVE +#include +#include + +#include +#include +#include +#include + +namespace uhd { namespace usrp { namespace e300 { + +class spidev_impl : public spi +{ +public: + + spidev_impl(const std::string &device) + : _mode(SPI_CPHA), + _speed(2000000), + _bits(8), + _delay(0) + { + int ret; + _fd = open(device.c_str(), O_RDWR); + if (_fd < 0) + throw uhd::runtime_error(str(boost::format("Could not open spidev device %s") % device)); + + ret = ioctl(_fd, SPI_IOC_WR_MODE, &_mode); + if (ret == -1) + throw uhd::runtime_error("Could not set spidev mode"); + + ret = ioctl(_fd, SPI_IOC_RD_MODE, &_mode); + if (ret == -1) + throw uhd::runtime_error("Could not get spidev mode"); + + ret = ioctl(_fd, SPI_IOC_WR_BITS_PER_WORD, &_bits); + if (ret == -1) + throw uhd::runtime_error("Could not set spidev bits per word"); + + ret = ioctl(_fd, SPI_IOC_RD_BITS_PER_WORD, &_bits); + if (ret == -1) + throw uhd::runtime_error("Could not get spidev bits per word"); + + ret = ioctl(_fd, SPI_IOC_WR_MAX_SPEED_HZ, &_speed); + if (ret == -1) + throw uhd::runtime_error("Could not set spidev max speed"); + + ret = ioctl(_fd, SPI_IOC_RD_MAX_SPEED_HZ, &_speed); + if (ret == -1) + throw uhd::runtime_error("Could not get spidev max speed"); + } + + virtual ~spidev_impl() + { + close(_fd); + } + + boost::uint32_t transact_spi(int, const uhd::spi_config_t &, + boost::uint32_t data, size_t num_bits, + bool) + { + int ret(0); + struct spi_ioc_transfer tr; + + uint8_t *tx_data = reinterpret_cast(&data); + + + UHD_ASSERT_THROW(num_bits == 24); + uint8_t tx[] = {tx_data[2], tx_data[1], tx_data[0]}; + + uint8_t rx[3]; + tr.tx_buf = (unsigned long) &tx[0]; + tr.rx_buf = (unsigned long) &rx[0]; + tr.len = num_bits >> 3; + tr.bits_per_word = _bits; + tr.speed_hz = _speed; + tr.delay_usecs = _delay; + + ret = ioctl(_fd, SPI_IOC_MESSAGE(1), &tr); + if (ret < 1) + throw uhd::runtime_error("Could not send spidev message"); + + return rx[2]; + } + +private: + int _fd; + boost::uint8_t _mode; + boost::uint32_t _speed; + boost::uint8_t _bits; + boost::uint16_t _delay; +}; + +spi::sptr spi::make(const std::string &device) +{ + return spi::sptr(new spidev_impl(device)); +} +}}}; +#else +namespace uhd { namespace usrp { namespace e300 { + +spi::sptr spi::make(const std::string &) +{ + throw uhd::assertion_error("spi::make() !E300_NATIVE"); +} +}}}; +#endif //E300_NATIVE diff --git a/host/lib/usrp/e300/e300_spi.hpp b/host/lib/usrp/e300/e300_spi.hpp new file mode 100644 index 000000000..67e990aaa --- /dev/null +++ b/host/lib/usrp/e300/e300_spi.hpp @@ -0,0 +1,34 @@ +// +// Copyright 2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +#ifndef INCLUDED_E300_SPI_HPP +#define INCLUDED_E300_SPI_HPP + +#include + +namespace uhd { namespace usrp { namespace e300 { + +class spi : public virtual uhd::spi_iface +{ +public: + typedef boost::shared_ptr sptr; + static sptr make(const std::string &device); +}; + +}}}; + +#endif /* INCLUDED_E300_SPI_HPP */ diff --git a/host/lib/usrp/e300/e300_sysfs_hooks.cpp b/host/lib/usrp/e300/e300_sysfs_hooks.cpp new file mode 100644 index 000000000..fdeaf0858 --- /dev/null +++ b/host/lib/usrp/e300/e300_sysfs_hooks.cpp @@ -0,0 +1,121 @@ +// +// 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 . +// + +#ifdef E300_NATIVE + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include + +#include +#include + +static const std::string E300_AXI_FPGA_SYSFS = "40000000.axi-fpga"; +static const std::string E300_XDEV_SYSFS = "f8007000.ps7-dev-cfg"; + +std::string e300_get_sysfs_attr(const std::string &node, const std::string &attr) +{ + udev *udev; + udev_enumerate *enumerate; + udev_list_entry *devices, *dev_list_entry; + udev_device *dev; + std::string retstring; + + udev = udev_new(); + + if (!udev) { + throw uhd::lookup_error("Failed to get udev handle."); + } + + enumerate = udev_enumerate_new(udev); + udev_enumerate_add_match_sysname(enumerate, node.c_str()); + udev_enumerate_scan_devices(enumerate); + devices = udev_enumerate_get_list_entry(enumerate); + + udev_list_entry_foreach(dev_list_entry, devices) + { + const char *path; + + path = udev_list_entry_get_name(dev_list_entry); + dev = udev_device_new_from_syspath(udev, path); + + retstring = udev_device_get_sysattr_value(dev, attr.c_str()); + if (retstring.size()) + break; + } + + udev_enumerate_unref(enumerate); + udev_unref(udev); + + return retstring; +} + +static bool e300_fpga_loaded_successfully(void) +{ + return boost::lexical_cast(e300_get_sysfs_attr(E300_XDEV_SYSFS, "prog_done")); +} + +#include "e300_fifo_config.hpp" +#include + +e300_fifo_config_t e300_read_sysfs(void) +{ + + if (not e300_fpga_loaded_successfully()) + { + throw uhd::runtime_error("E300 FPGA load failed!"); + } + + e300_fifo_config_t config; + + config.buff_length = boost::lexical_cast( + e300_get_sysfs_attr(E300_AXI_FPGA_SYSFS, "buffer_length")); + config.ctrl_length = boost::lexical_cast( + e300_get_sysfs_attr(E300_AXI_FPGA_SYSFS, "control_length")); + config.phys_addr = boost::lexical_cast( + e300_get_sysfs_attr(E300_AXI_FPGA_SYSFS, "phys_addr")); + + return config; +} + +#else //E300_NATIVE + +#include "e300_fifo_config.hpp" +#include + +e300_fifo_config_t e300_read_sysfs(void) +{ + throw uhd::assertion_error("e300_read_sysfs() !E300_NATIVE"); +} + +std::string e300_get_sysfs_attr(const std::string &, const std::string &) +{ + throw uhd::assertion_error("e300_sysfs_attr() !E300_NATIVE"); +} + +#endif //E300_NATIVE diff --git a/host/lib/usrp/e300/e300_ublox_control.hpp b/host/lib/usrp/e300/e300_ublox_control.hpp new file mode 100644 index 000000000..8705d6c52 --- /dev/null +++ b/host/lib/usrp/e300/e300_ublox_control.hpp @@ -0,0 +1,50 @@ +#ifndef INCLUDED_UHD_USRP_UBLOX_CONTROL_HPP +#define INCLUDED_UHD_USRP_UBLOX_CONTROL_HPP + +#include +#include +#include +#include +#include +#include + +#include "e300_async_serial.hpp" + +namespace uhd { namespace usrp { namespace gps { + +namespace ublox { namespace ubx { + +class control : public virtual uhd::gps_ctrl +{ +public: + typedef boost::shared_ptr sptr; + + static sptr make(const std::string &node, const size_t baud_rate); + + virtual void configure_message_rate( + const boost::uint16_t msg, + const boost::uint8_t rate) = 0; + + virtual void configure_antenna( + const boost::uint16_t flags, + const boost::uint16_t pins) = 0; + + virtual void configure_pps( + const boost::uint32_t interval, + const boost::uint32_t length, + const boost::int8_t status, + const boost::uint8_t time_ref, + const boost::uint8_t flags, + const boost::int16_t antenna_delay, + const boost::int16_t rf_group_delay, + const boost::int32_t user_delay) = 0; + + virtual void configure_rates( + boost::uint16_t meas_rate, + boost::uint16_t nav_rate, + boost::uint16_t time_ref) = 0; +}; +}} // namespace ublox::ubx + +}}} // namespace +#endif // INCLUDED_UHD_USRP_UBLOX_CONTROL_HPP diff --git a/host/lib/usrp/e300/e300_ublox_control_impl.cpp b/host/lib/usrp/e300/e300_ublox_control_impl.cpp new file mode 100644 index 000000000..a0ec10271 --- /dev/null +++ b/host/lib/usrp/e300/e300_ublox_control_impl.cpp @@ -0,0 +1,505 @@ +#include +#include +#include +#include +#include +#include +#include "boost/date_time/posix_time/posix_time.hpp" + +#include + + +#include +#include +#include + +#include "e300_ublox_control.hpp" + +#ifdef E300_NATIVE +#include "e300_ublox_control_impl.hpp" + + +namespace uhd { namespace usrp { namespace gps { + +namespace ublox { namespace ubx { + +control_impl::control_impl(const std::string &node, const size_t baud_rate) +{ + _decode_init(); + _serial = boost::make_shared(node, baud_rate); + _serial->set_read_callback(boost::bind(&control_impl::_rx_callback, this, _1, _2)); + + _detect(); + + configure_message_rate(MSG_GLL, 0); + configure_message_rate(MSG_GSV, 0); + configure_message_rate(MSG_GGA, 0); + configure_message_rate(MSG_GSA, 0); + configure_message_rate(MSG_RMC, 0); + configure_message_rate(MSG_VTG, 0); + configure_message_rate(MSG_NAV_TIMEUTC, 1); + configure_message_rate(MSG_NAV_SOL, 1); + + configure_antenna(0x001b, 0x8251); + + configure_pps(0xf4240, 0x3d090, 1, 0 /* utc */, 1, 0, 0, 0); + + _sensors = boost::assign::list_of("gps_locked")("gps_time"); +} + +bool control_impl::gps_detected(void) +{ + return _detected; +} + +void control_impl::_detect(void) +{ + _send_message(MSG_MON_VER, NULL, 0); +} + +std::vector control_impl::get_sensors(void) +{ + return _sensors; +} + +uhd::sensor_value_t control_impl::get_sensor(std::string key) +{ + if (key == "gps_time") { + return sensor_value_t("GPS epoch time", int(_get_epoch_time()), "seconds"); + } else if (key == "gps_locked") { + bool lock; + _locked.wait_and_see(lock); + return sensor_value_t("GPS lock status", lock, "locked", "unlocked"); + } else + throw uhd::key_error(str(boost::format("sensor %s unknown.") % key)); +} + +std::time_t control_impl::_get_epoch_time(void) +{ + boost::posix_time::ptime ptime; + _ptime.wait_and_see(ptime); + return (ptime - boost::posix_time::from_time_t(0)).total_seconds(); +} + +control_impl::~control_impl(void) +{ + // turn it all off again + configure_antenna(0x001a, 0x8251); + configure_pps(0xf4240, 0x3d090, 1, 1, 0, 0, 0, 0); +} + +void control_impl::_decode_init(void) +{ + _decode_state = DECODE_SYNC1; + _rx_ck_a = 0; + _rx_ck_b = 0; + _rx_payload_length = 0; + _rx_payload_index = 0; +} + +void control_impl::_add_byte_to_checksum(const boost::uint8_t b) +{ + _rx_ck_a = _rx_ck_a + b; + _rx_ck_b = _rx_ck_b + _rx_ck_a; +} + +void control_impl::_calc_checksum( + const boost::uint8_t *buffer, + const boost::uint16_t length, + checksum_t &checksum) +{ + for (size_t i = 0; i < length; i++) + { + checksum.ck_a = checksum.ck_a + buffer[i]; + checksum.ck_b = checksum.ck_b + checksum.ck_a; + } +} + +void control_impl::configure_rates( + boost::uint16_t meas_rate, + boost::uint16_t nav_rate, + boost::uint16_t time_ref) +{ + payload_tx_cfg_rate_t cfg_rate; + cfg_rate.meas_rate = uhd::htowx(meas_rate); + cfg_rate.nav_rate = uhd::htowx(nav_rate); + cfg_rate.time_ref = uhd::htowx(time_ref); + + _send_message( + MSG_CFG_RATE, + reinterpret_cast(&cfg_rate), + sizeof(cfg_rate)); + + _wait_for_ack(MSG_CFG_RATE, 1.0); +} + +void control_impl::configure_message_rate( + const boost::uint16_t msg, + const uint8_t rate) +{ + payload_tx_cfg_msg_t cfg_msg; + cfg_msg.msg = uhd::htowx(msg); + cfg_msg.rate[0] = 0;//rate; + cfg_msg.rate[1] = rate; + cfg_msg.rate[2] = 0;//rate; + cfg_msg.rate[3] = 0;//rate; + cfg_msg.rate[4] = 0;//rate; + cfg_msg.rate[5] = 0;//rate; + _send_message( + MSG_CFG_MSG, + reinterpret_cast(&cfg_msg), + sizeof(cfg_msg)); + + _wait_for_ack(MSG_CFG_MSG, 1.0); +} + +void control_impl::configure_antenna( + const boost::uint16_t flags, + const boost::uint16_t pins) +{ + payload_tx_cfg_ant_t cfg_ant; + cfg_ant.pins = uhd::htowx(pins); + cfg_ant.flags = uhd::htowx(flags); + _send_message( + MSG_CFG_ANT, + reinterpret_cast(&cfg_ant), + sizeof(cfg_ant)); + if (_wait_for_ack(MSG_CFG_ANT, 1.0) < 0) { + throw uhd::runtime_error("Didn't get an ACK for antenna configuration."); + } + +} + +void control_impl::configure_pps( + const boost::uint32_t interval, + const boost::uint32_t length, + const boost::int8_t status, + const boost::uint8_t time_ref, + const boost::uint8_t flags, + const boost::int16_t antenna_delay, + const boost::int16_t rf_group_delay, + const boost::int32_t user_delay) +{ + payload_tx_cfg_tp_t cfg_tp; + cfg_tp.interval = uhd::htowx(interval); + cfg_tp.length = uhd::htowx(length); + cfg_tp.status = status; + cfg_tp.time_ref = time_ref; + cfg_tp.flags = flags; + cfg_tp.antenna_delay = uhd::htowx(antenna_delay); + cfg_tp.rf_group_delay = uhd::htowx(rf_group_delay); + cfg_tp.user_delay = uhd::htowx(user_delay); + _send_message( + MSG_CFG_TP, + reinterpret_cast(&cfg_tp), + sizeof(cfg_tp)); + if (_wait_for_ack(MSG_CFG_TP, 1.0) < 0) { + throw uhd::runtime_error("Didn't get an ACK for PPS configuration."); + } +} + + +void control_impl::_rx_callback(const char *data, unsigned int len) +{ + //std::cout << "IN RX CALLBACK" << std::flush << std::endl; + std::vector v(data, data+len); + BOOST_FOREACH(const char &c, v) + { + _parse_char(c); + } +} + +void control_impl::_parse_char(const boost::uint8_t b) +{ + int ret = 0; + + switch (_decode_state) { + + // we're expecting the first sync byte + case DECODE_SYNC1: + if (b == SYNC1) { // sync1 found goto next step + _decode_state = DECODE_SYNC2; + } // else stay around + break; + + // we're expecting the second sync byte + case DECODE_SYNC2: + if (b == SYNC2) { // sync2 found goto next step + _decode_state = DECODE_CLASS; + } else { + // failed, reset + _decode_init(); + } + break; + + // we're expecting the class byte + case DECODE_CLASS: + _add_byte_to_checksum(b); + _rx_msg = b; + _decode_state = DECODE_ID; + break; + + // we're expecting the id byte + case DECODE_ID: + _add_byte_to_checksum(b); + _rx_msg |= (b << 8); + _decode_state = DECODE_LENGTH1; + break; + + // we're expecting the first length byte + case DECODE_LENGTH1: + _add_byte_to_checksum(b); + _rx_payload_length = b; + _decode_state = DECODE_LENGTH2; + break; + + // we're expecting the second length byte + case DECODE_LENGTH2: + _add_byte_to_checksum(b); + _rx_payload_length |= (b << 8); + if(_payload_rx_init()) { + _decode_init(); // we failed, give up for this one + } else { + _decode_state = _rx_payload_length ? + DECODE_PAYLOAD : DECODE_CHKSUM1; + } + break; + + // we're expecting payload + case DECODE_PAYLOAD: + _add_byte_to_checksum(b); + switch(_rx_msg) { + default: + ret = _payload_rx_add(b); + break; + }; + if (ret < 0) { + // we couldn't deal with the payload, discard the whole thing + _decode_init(); + } else if (ret > 0) { + // payload was complete, let's check the checksum; + _decode_state = DECODE_CHKSUM1; + } else { + // more payload expected, don't move + } + ret = 0; + break; + + case DECODE_CHKSUM1: + if (_rx_ck_a != b) { + // checksum didn't match, barf + std::cout << boost::format("Failed checksum byte1 %lx != %lx") + % int(_rx_ck_a) % int(b) << std::endl; + _decode_init(); + } else { + _decode_state = DECODE_CHKSUM2; + } + break; + + case DECODE_CHKSUM2: + if (_rx_ck_b != b) { + // checksum didn't match, barf + std::cout << boost::format("Failed checksum byte2 %lx != %lx") + % int(_rx_ck_b) % int(b) << std::endl; + + } else { + ret = _payload_rx_done(); // payload done + } + _decode_init(); + break; + + default: + break; + }; +} + +int control_impl::_payload_rx_init(void) +{ + int ret = 0; + + _rx_state = RXMSG_HANDLE; // by default handle + switch(_rx_msg) { + + case MSG_NAV_SOL: + if (not (_rx_payload_length == sizeof(payload_rx_nav_sol_t))) + _rx_state = RXMSG_ERROR_LENGTH; + break; + + case MSG_NAV_TIMEUTC: + if (not (_rx_payload_length == sizeof(payload_rx_nav_timeutc_t))) + _rx_state = RXMSG_ERROR_LENGTH; + break; + + case MSG_MON_VER: + break; // always take this one + + case MSG_ACK_ACK: + if (not (_rx_payload_length == sizeof(payload_rx_ack_ack_t))) + _rx_state = RXMSG_ERROR_LENGTH; + break; + + case MSG_ACK_NAK: + if (not (_rx_payload_length == sizeof(payload_rx_ack_nak_t))) + _rx_state = RXMSG_ERROR_LENGTH; + break; + + default: + _rx_state = RXMSG_DISABLE; + break; + }; + + switch (_rx_state) { + case RXMSG_HANDLE: // handle message + case RXMSG_IGNORE: // ignore message but don't report error + ret = 0; + break; + case RXMSG_DISABLE: // ignore message but don't report error + case RXMSG_ERROR_LENGTH: // the length doesn't match + ret = -1; + break; + default: // invalid, error + ret = -1; + break; + }; + + return ret; +} + +int control_impl::_payload_rx_add(const boost::uint8_t b) +{ + int ret = 0; + _buf.raw[_rx_payload_index] = b; + if (++_rx_payload_index >= _rx_payload_length) + ret = 1; + return ret; +} + +int control_impl::_payload_rx_done(void) +{ + int ret = 0; + if (_rx_state != RXMSG_HANDLE) { + return 0; + } + + switch (_rx_msg) { + case MSG_MON_VER: + _detected = true; + break; + + case MSG_MON_HW: + std::cout << "MON-HW" << std::endl; + break; + + case MSG_ACK_ACK: + if ((_ack_state == ACK_WAITING) and (_buf.payload_rx_ack_ack.msg == _ack_waiting_msg)) + _ack_state = ACK_GOT_ACK; + break; + + case MSG_ACK_NAK: + if ((_ack_state == ACK_WAITING) and (_buf.payload_rx_ack_nak.msg == _ack_waiting_msg)) + _ack_state = ACK_GOT_NAK; + + break; + + case MSG_CFG_ANT: + break; + + case MSG_NAV_TIMEUTC: + _ptime.update(boost::posix_time::ptime( + boost::gregorian::date( + boost::gregorian::greg_year(uhd::wtohx( + _buf.payload_rx_nav_timeutc.year)), + boost::gregorian::greg_month(_buf.payload_rx_nav_timeutc.month), + boost::gregorian::greg_day(_buf.payload_rx_nav_timeutc.day)), + (boost::posix_time::hours(_buf.payload_rx_nav_timeutc.hour) + + boost::posix_time::minutes(_buf.payload_rx_nav_timeutc.min) + + boost::posix_time::seconds(_buf.payload_rx_nav_timeutc.sec)))); + break; + + case MSG_NAV_SOL: + _locked.update(_buf.payload_rx_nav_sol.gps_fix > 0); + break; + + default: + std::cout << boost::format("Got unknown message %lx , with good checksum [") % int(_rx_msg); + for(size_t i = 0; i < _rx_payload_length; i++) + std::cout << boost::format("%lx, ") % int(_buf.raw[i]); + std::cout << "]"<< std::endl; + break; + }; + return ret; +} + +void control_impl::_send_message( + const boost::uint16_t msg, + const boost::uint8_t *payload, + const boost::uint16_t len) +{ + header_t header = {SYNC1, SYNC2, msg, len}; + checksum_t checksum = {0, 0}; + + // calculate checksums, first header without sync + // then payload + _calc_checksum( + reinterpret_cast(&header) + 2, + sizeof(header) - 2, checksum); + if (payload) + _calc_checksum(payload, len, checksum); + + _serial->write( + reinterpret_cast(&header), + sizeof(header)); + + if (payload) + _serial->write((const char *) payload, len); + + _serial->write( + reinterpret_cast(&checksum), + sizeof(checksum)); +} + +int control_impl::_wait_for_ack( + const boost::uint16_t msg, + const double timeout) +{ + int ret = -1; + + _ack_state = ACK_WAITING; + _ack_waiting_msg = msg; + + boost::system_time timeout_time = + boost::get_system_time() + + boost::posix_time::milliseconds(timeout * 1000.0); + + do { + if(_ack_state == ACK_GOT_ACK) + return 0; + else if (_ack_state == ACK_GOT_NAK) { + return -1; + } + boost::this_thread::sleep(boost::posix_time::milliseconds(20)); + } while (boost::get_system_time() < timeout_time); + + // we get here ... it's a timeout + _ack_state = ACK_IDLE; + return ret; +} + + +}} // namespace ublox::ubx +}}} // namespace + +using namespace uhd::usrp::gps::ublox::ubx; + +control::sptr control::make(const std::string &node, const size_t baud_rate) +{ + return control::sptr(new control_impl(node, baud_rate)); +} +#else +using namespace uhd::usrp::gps::ublox::ubx; + +control::sptr control::make(const std::string &node, const size_t baud_rate) +{ + throw uhd::assertion_error("control::sptr::make: !E300_NATIVE"); +} +#endif // E300_NATIVE diff --git a/host/lib/usrp/e300/e300_ublox_control_impl.hpp b/host/lib/usrp/e300/e300_ublox_control_impl.hpp new file mode 100644 index 000000000..a1dcbfe6c --- /dev/null +++ b/host/lib/usrp/e300/e300_ublox_control_impl.hpp @@ -0,0 +1,457 @@ +#ifndef INCLUDED_UHD_USRP_UBLOX_CONTROL_IMPL_HPP +#define INCLUDED_UHD_USRP_UBLOX_CONTROL_IMPL_HPP + +#include +#include +#include +#include +#include +#include + +#include "e300_async_serial.hpp" + +namespace uhd { namespace usrp { namespace gps { + +namespace ublox { namespace ubx { +// ublox binary sync words +static const boost::uint8_t SYNC1 = 0xB5; +static const boost::uint8_t SYNC2 = 0x62; + +// message classes +static const boost::uint8_t CLASS_NAV = 0x01; +static const boost::uint8_t CLASS_ACK = 0x05; +static const boost::uint8_t CLASS_CFG = 0x06; +static const boost::uint8_t CLASS_MON = 0x0a; +static const boost::uint8_t CLASS_NMEA = 0xf0; + +// Message IDs +static const boost::uint8_t ID_NAV_POSLLH = 0x02; +static const boost::uint8_t ID_NAV_SOL = 0x06; +static const boost::uint8_t ID_NAV_PVT = 0x07; +static const boost::uint8_t ID_NAV_VELNED = 0x12; +static const boost::uint8_t ID_NAV_TIMEUTC = 0x21; +static const boost::uint8_t ID_NAV_SVINFO = 0x30; +static const boost::uint8_t ID_ACK_NAK = 0x00; +static const boost::uint8_t ID_ACK_ACK = 0x01; +static const boost::uint8_t ID_CFG_PRT = 0x00; +static const boost::uint8_t ID_CFG_ANT = 0x13; +static const boost::uint8_t ID_CFG_TP = 0x07; +static const boost::uint8_t ID_CFG_MSG = 0x01; +static const boost::uint8_t ID_CFG_RATE = 0x08; +static const boost::uint8_t ID_CFG_NAV5 = 0x24; +static const boost::uint8_t ID_MON_VER = 0x04; +static const boost::uint8_t ID_MON_HW = 0x09; +static const boost::uint8_t ID_GGA = 0x00; +static const boost::uint8_t ID_GLL = 0x01; +static const boost::uint8_t ID_GSA = 0x02; +static const boost::uint8_t ID_GSV = 0x03; +static const boost::uint8_t ID_RMC = 0x04; +static const boost::uint8_t ID_VTG = 0x05; +static const boost::uint8_t ID_GST = 0x07; + +// Message Classes & IDs // +static const boost::uint16_t MSG_NAV_POSLLH + = CLASS_NAV | (ID_NAV_POSLLH << 8); +static const boost::uint16_t MSG_NAV_SOL + = CLASS_NAV | (ID_NAV_SOL << 8); +static const boost::uint16_t MSG_NAV_PVT + = CLASS_NAV | (ID_NAV_PVT << 8); +static const boost::uint16_t MSG_NAV_VELNED + = CLASS_NAV | (ID_NAV_VELNED << 8); +static const boost::uint16_t MSG_NAV_TIMEUTC + = CLASS_NAV | (ID_NAV_TIMEUTC << 8); +static const boost::uint16_t MSG_NAV_SVINFO + = CLASS_NAV | (ID_NAV_SVINFO << 8); +static const boost::uint16_t MSG_ACK_NAK + = CLASS_ACK | (ID_ACK_NAK << 8); +static const boost::uint16_t MSG_ACK_ACK + = CLASS_ACK | (ID_ACK_ACK << 8); +static const boost::uint16_t MSG_CFG_PRT + = CLASS_CFG | (ID_CFG_PRT << 8); +static const boost::uint16_t MSG_CFG_ANT + = CLASS_CFG | (ID_CFG_ANT << 8); +static const boost::uint16_t MSG_CFG_TP + = CLASS_CFG | (ID_CFG_TP << 8); +static const boost::uint16_t MSG_CFG_MSG + = CLASS_CFG | (ID_CFG_MSG << 8); +static const boost::uint16_t MSG_CFG_RATE + = CLASS_CFG | (ID_CFG_RATE << 8); +static const boost::uint16_t MSG_CFG_NAV5 + = CLASS_CFG | (ID_CFG_NAV5 << 8); +static const boost::uint16_t MSG_MON_HW + = CLASS_MON | (ID_MON_HW << 8); +static const boost::uint16_t MSG_MON_VER + = CLASS_MON | (ID_MON_VER << 8); + +// NMEA ones +static const boost::uint16_t MSG_GGA + = CLASS_NMEA | (ID_GGA << 8); +static const boost::uint16_t MSG_GLL + = CLASS_NMEA | (ID_GLL << 8); +static const boost::uint16_t MSG_GSA + = CLASS_NMEA | (ID_GSA << 8); +static const boost::uint16_t MSG_GSV + = CLASS_NMEA | (ID_GSV << 8); +static const boost::uint16_t MSG_RMC + = CLASS_NMEA | (ID_RMC << 8); +static const boost::uint16_t MSG_VTG + = CLASS_NMEA | (ID_VTG << 8); + +// header +struct header_t +{ + boost::uint8_t sync1; + boost::uint8_t sync2; + boost::uint16_t msg; + boost::uint16_t length; +}; + +// checksum +struct checksum_t +{ + boost::uint8_t ck_a; + boost::uint8_t ck_b; +}; + +// rx rx mon-hw (ubx6) +struct payload_rx_mon_hw_t +{ + boost::uint32_t pin_sel; + boost::uint32_t pin_bank; + boost::uint32_t pin_dir; + boost::uint32_t pin_val; + boost::uint16_t noise_per_ms; + boost::uint16_t agc_cnt; + boost::uint8_t a_status; + boost::uint8_t a_power; + boost::uint8_t flags; + boost::uint8_t reserved1; + boost::uint32_t used_mask; + boost::uint8_t vp[25]; + boost::uint8_t jam_ind; + boost::uint16_t reserved3; + boost::uint32_t pin_irq; + boost::uint32_t pullh; + boost::uint32_t pulll; +}; + +// rx mon-ver +struct payload_rx_mon_ver_part1_t +{ + char sw_version[30]; + char hw_version[10]; +}; + +struct payload_rx_mon_ver_part2_t +{ + boost::uint8_t extension[30]; +}; + +// rx ack-ack +typedef union { + boost::uint16_t msg; + struct { + boost::uint8_t cls_id; + boost::uint8_t msg_id; + }; +} payload_rx_ack_ack_t; + +// rx ack-nak +typedef union { + boost::uint16_t msg; + struct { + boost::uint8_t cls_id; + boost::uint8_t msg_id; + }; +} payload_rx_ack_nak_t; + +// tx cfg-prt (uart) +struct payload_tx_cfg_prt_t +{ + boost::uint8_t port_id; + boost::uint8_t reserved0; + boost::uint16_t tx_ready; + boost::uint32_t mode; + boost::uint32_t baud_rate; + boost::uint16_t in_proto_mask; + boost::uint16_t out_proto_mask; + boost::uint16_t flags; + boost::uint16_t reserved5; +}; + +// tx cfg-rate +struct payload_tx_cfg_rate_t +{ + boost::uint16_t meas_rate; + boost::uint16_t nav_rate; + boost::uint16_t time_ref; +}; + +// tx cfg-msg +struct payload_tx_cfg_msg_t +{ + boost::uint16_t msg; + boost::uint8_t rate[6]; +}; + + +// tx cfg-ant +struct payload_tx_cfg_ant_t +{ + boost::uint16_t flags; + boost::uint16_t pins; +}; + +// tx cfg-tp +struct payload_tx_cfg_tp_t +{ + boost::uint32_t interval; + boost::uint32_t length; + boost::int8_t status; + boost::uint8_t time_ref; + boost::uint8_t flags; + boost::uint8_t reserved1; + boost::int16_t antenna_delay; + boost::int16_t rf_group_delay; + boost::int32_t user_delay; +}; + +struct payload_rx_nav_sol_t +{ + boost::uint32_t i_tow; + boost::int32_t f_tow; + boost::int16_t week; + boost::uint8_t gps_fix; + boost::uint8_t flags; + boost::int32_t ecef_x; + boost::int32_t ecef_y; + boost::int32_t ecef_z; + boost::uint32_t p_acc; + boost::int32_t ecef_vx; + boost::int32_t ecef_vy; + boost::int32_t ecef_vz; + boost::uint32_t s_acc; + boost::uint16_t p_dop; + boost::uint8_t reserved1; + boost::uint8_t num_sv; + boost::uint32_t reserved2; +}; + +struct payload_rx_nav_timeutc_t +{ + boost::uint32_t i_tow; + boost::uint32_t t_acc; + boost::int32_t nano; + boost::uint16_t year; + boost::uint8_t month; + boost::uint8_t day; + boost::uint8_t hour; + boost::uint8_t min; + boost::uint8_t sec; + boost::uint8_t valid; +}; + +typedef union { + payload_rx_mon_hw_t payload_rx_mon_hw; + + payload_rx_mon_ver_part1_t payload_rx_mon_ver_part1; + payload_rx_mon_ver_part2_t payload_rx_mon_ver_part2; + + payload_rx_ack_ack_t payload_rx_ack_ack; + payload_rx_ack_nak_t payload_rx_ack_nak; + + payload_tx_cfg_prt_t payload_tx_cfg_prt; + payload_tx_cfg_ant_t payload_tx_cfg_ant; + payload_tx_cfg_rate_t payload_tx_cfg_rate; + + payload_tx_cfg_msg_t payload_tx_cfg_msg; + + payload_rx_nav_timeutc_t payload_rx_nav_timeutc; + payload_rx_nav_sol_t payload_rx_nav_sol; + boost::uint8_t raw[]; +} buf_t; + + +template +class sensor_entry +{ +public: + sensor_entry() : _seen(false) + { + } + + void update(const T &val) + { + boost::mutex::scoped_lock l(_mutex); + _value = val; + _seen = false; + l.unlock(); + _cond.notify_one(); + } + + bool seen() const + { + boost::mutex::scoped_lock l(_mutex); + return _seen; + } + + bool try_and_see(T &val) + { + boost::mutex::scoped_lock l(_mutex); + if (_seen) + return false; + + val = _value; + _seen = true; + return true; + } + + void wait_and_see(T &val) + { + boost::mutex::scoped_lock l(_mutex); + while(_seen) + { + _cond.wait(l); + //std::cout << "Already seen ... " << std::endl; + } + val = _value; + _seen = true; + } + +private: // members + T _value; + boost::mutex _mutex; + boost::condition_variable _cond; + bool _seen; +}; + +class control_impl : public control +{ +public: + control_impl(const std::string &node, const size_t baud_rate); + + virtual ~control_impl(void); + + void configure_message_rate( + const boost::uint16_t msg, + const boost::uint8_t rate); + + void configure_antenna( + const boost::uint16_t flags, + const boost::uint16_t pins); + + void configure_pps( + const boost::uint32_t interval, + const boost::uint32_t length, + const boost::int8_t status, + const boost::uint8_t time_ref, + const boost::uint8_t flags, + const boost::int16_t antenna_delay, + const boost::int16_t rf_group_delay, + const boost::int32_t user_delay); + + void configure_rates( + boost::uint16_t meas_rate, + boost::uint16_t nav_rate, + boost::uint16_t time_ref); + + // gps_ctrl interface + bool gps_detected(void); + std::vector get_sensors(void); + uhd::sensor_value_t get_sensor(std::string key); + +private: // types + enum decoder_state_t { + DECODE_SYNC1 = 0, + DECODE_SYNC2, + DECODE_CLASS, + DECODE_ID, + DECODE_LENGTH1, + DECODE_LENGTH2, + DECODE_PAYLOAD, + DECODE_CHKSUM1, + DECODE_CHKSUM2, + }; + + enum rxmsg_state_t { + RXMSG_IGNORE = 0, + RXMSG_HANDLE, + RXMSG_DISABLE, + RXMSG_ERROR_LENGTH + }; + + enum ack_state_t { + ACK_IDLE = 0, + ACK_WAITING, + ACK_GOT_ACK, + ACK_GOT_NAK + }; + +private: // methods + std::time_t _get_epoch_time(void); + + void _decode_init(void); + + void _add_byte_to_checksum(const boost::uint8_t b); + + void _detect(void); + + void _send_message( + const boost::uint16_t msg, + const boost::uint8_t *payload, + const boost::uint16_t len); + + int _wait_for_ack( + const boost::uint16_t msg, + const double timeout); + + void _calc_checksum( + const boost::uint8_t *buffer, + const boost::uint16_t length, + checksum_t &checksum); + + void _rx_callback(const char *data, unsigned len); + + void _parse_char(const boost::uint8_t b); + + int _payload_rx_init(void); + + int _payload_rx_add(const boost::uint8_t b); + + int _payload_rx_done(void); + +private: // members + // gps_ctrl stuff + bool _detected; + std::vector _sensors; + + sensor_entry _locked; + sensor_entry _ptime; + + // decoder state + decoder_state_t _decode_state; + rxmsg_state_t _rxmsg_state; + + ack_state_t _ack_state; + boost::uint16_t _ack_waiting_msg; + + boost::uint8_t _rx_ck_a; + boost::uint8_t _rx_ck_b; + + boost::uint16_t _rx_payload_length; + size_t _rx_payload_index; + boost::uint16_t _rx_msg; + + rxmsg_state_t _rx_state; + + boost::shared_ptr _serial; + + // this has to be at the end of the + // class to be valid C++ + buf_t _buf; +}; + +}} // namespace ublox::ubx + +}}} // namespace +#endif // INCLUDED_UHD_USRP_UBLOX_CONTROL_IMPL_HPP diff --git a/host/utils/CMakeLists.txt b/host/utils/CMakeLists.txt index 11bb2488a..f693ee7a6 100644 --- a/host/utils/CMakeLists.txt +++ b/host/utils/CMakeLists.txt @@ -35,6 +35,13 @@ SET(x3xx_burner_sources cdecode.c ) +find_package(UDev) +IF(ENABLE_E300) + IF(UDEV_FOUND) + LIST(APPEND util_runtime_sources usrp_e3x0_network_mode.cpp) + ENDIF(UDEV_FOUND) +ENDIF(ENABLE_E300) + #for each source: build an executable and install FOREACH(util_source ${util_runtime_sources}) GET_FILENAME_COMPONENT(util_name ${util_source} NAME_WE) diff --git a/host/utils/query_gpsdo_sensors.cpp b/host/utils/query_gpsdo_sensors.cpp index 9a1556650..05f918eb4 100644 --- a/host/utils/query_gpsdo_sensors.cpp +++ b/host/utils/query_gpsdo_sensors.cpp @@ -107,15 +107,20 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ const time_t pc_clock_time = time(NULL); const uhd::time_spec_t last_pps_time = usrp->get_time_last_pps(); if (last_pps_time.to_ticks(1.0) == gps_time.to_int()) { - std::cout << boost::format("GPS and UHD Device time are aligned.\n"); - } else - std::cout << boost::format("\nGPS and UHD Device time are NOT aligned. Try re-running the program. Double check 1 PPS connection from GPSDO.\n\n"); - + std::cout << boost::format("GPS and UHD Device time are aligned.\n"); + } else { + std::cout << boost::format("\nGPS and UHD Device time are NOT aligned last_pps: %ld vs gps: %ld. Try re-running the program. Double check 1 PPS connection from GPSDO.\n\n") % last_pps_time.to_ticks(1.0) % gps_time.to_int() << std::endl; + } + //print NMEA strings - std::cout << boost::format("Printing available NMEA strings:\n"); - uhd::sensor_value_t gga_string = usrp->get_mboard_sensor("gps_gpgga"); - uhd::sensor_value_t rmc_string = usrp->get_mboard_sensor("gps_gprmc"); - std::cout << boost::format("%s\n%s\n%s\n") % gga_string.to_pp_string() % rmc_string.to_pp_string() % gps_time.to_pp_string(); + try { + uhd::sensor_value_t gga_string = usrp->get_mboard_sensor("gps_gpgga"); + uhd::sensor_value_t rmc_string = usrp->get_mboard_sensor("gps_gprmc"); + std::cout << boost::format("Printing available NMEA strings:\n"); + std::cout << boost::format("%s\n%s\n%s\n") % gga_string.to_pp_string() % rmc_string.to_pp_string() % gps_time.to_pp_string(); + } catch (std::exception &e) { + std::cout << "NMEA strings not implemented for this device." << std::endl; + } std::cout << boost::format("UHD Device time: %.0f seconds\n") % (last_pps_time.get_real_secs()); std::cout << boost::format("PC Clock time: %.0f seconds\n") % pc_clock_time; diff --git a/host/utils/usrp_e3x0_network_mode.cpp b/host/utils/usrp_e3x0_network_mode.cpp new file mode 100644 index 000000000..dae4b6ff7 --- /dev/null +++ b/host/utils/usrp_e3x0_network_mode.cpp @@ -0,0 +1,80 @@ +// +// 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 . +// + +#include "../lib/usrp/e300/e300_network.hpp" +#include +#include + +#include +#include + +#include +#include +#include + +#include + +namespace po = boost::program_options; + +static void check_network_ok(void) +{ + using namespace uhd::transport; + using namespace boost::asio::ip; + std::vector addrs = get_if_addrs(); + + if(addrs.size() == 1 and addrs.at(0).inet == address_v4::loopback().to_string()) + throw uhd::runtime_error( + "No network address except for loopback found.\n" + "Make sure your DHCP server is working or configure a static IP"); +} + +int main(int argc, char *argv[]) +{ + po::options_description desc("Allowed options"); + desc.add_options() + ("help", "help message") + ("fpga", po::value(), "fpga image to load") + ; + + po::variables_map vm; + po::store(po::parse_command_line(argc, argv, desc), vm); + po::notify(vm); + + //print the help message + if (vm.count("help")){ + std::cout << boost::format("UHD E3x0 Network Mode %s") % desc << std::endl; + return EXIT_FAILURE; + } + uhd::device_addr_t args; + if(vm.count("fpga")) { + args["fpga"] = vm["fpga"].as(); + } + + try { + check_network_ok(); + uhd::usrp::e300::network_server::sptr server = uhd::usrp::e300::network_server::make(args); + server->run(); + } catch (uhd::assertion_error &e) { + UHD_MSG(error) << "This executable is supposed to run on the device, not on the host." << std::endl + << "Please refer to the manual section on operating your e3x0 device in network mode." << std::endl; + return EXIT_FAILURE; + } catch (uhd::runtime_error &e) { + UHD_MSG(error) << e.what() << std::endl; + return EXIT_FAILURE; + } + return EXIT_SUCCESS; +} -- cgit v1.2.3