diff options
44 files changed, 1643 insertions, 588 deletions
diff --git a/host/LICENSE b/host/LICENSE index 9aa03b39b..b91233b22 100644 --- a/host/LICENSE +++ b/host/LICENSE @@ -1,3 +1,7 @@ +This LICENSE file applies only to this directory and all subdirectories. Other +top-level directories in the UHD(tm) Software distribution are not necessarily +covered by this license. + This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or diff --git a/host/docs/gpsdo.rst b/host/docs/gpsdo.rst new file mode 100644 index 000000000..5afd9d78d --- /dev/null +++ b/host/docs/gpsdo.rst @@ -0,0 +1,87 @@ +======================================================================== +UHD - Internal GPSDO Application Notes (USRP-N2x0/E1X0 Models) +======================================================================== + +.. contents:: Table of Contents + +This application note describes the use of integrated GPS-disciplined oscillators (GPSDOs) for +the USRP N-Series and E1xx. For information regarding the GPSDO that is compatible with +the USRP X-Series, please see: + +`USRP-X3x0 Internal GPSDO Device Manual <./gpsdo_x3x0.html>`_ + +======= + +------------------------------------------------------------------------ +Specifications +------------------------------------------------------------------------ +* **Receiver type**: 50 channel with WAAS, EGNOS, MSAS +* **10MHz ADEV**: 1e-11 over >24h +* **1PPS RMS jitter**: <50ns 1-sigma +* **Holdover**: <11us over 3h +* **Phase noise**: + + * **1Hz:** -80 dBc/Hz + * **10Hz:** -110 dBc/Hz + * **100Hz:** -135 dBc/Hz + * **1kHz:** -145 dBc/Hz + * **10kHz:** <-145 dBc/Hz + +**Antenna Types:** + +The GPSDO is capable of supplying a 3V for active GPS antennas or supporting passive antennas. + +------------------------------------------------------------------------ +Installation Instructions +------------------------------------------------------------------------ +Instructions for mounting the GPSDO kit onto your USRP device can be found here: +`http://www.ettus.com/content/files/gpsdo-kit_2.pdf <http://www.ettus.com/content/files/gpsdo-kit_2.pdf>`_ + +******************************************** +Post-installation Task (N-Series only) +******************************************** + +**Note:** The following instructions are only necessary for UHD 3.4.* and below. + +This is necessary if you require absolute GPS time in your application +or need to communicate with the GPSDO to obtain location, satellite info, etc. +If you only require 10 MHz and PPS signals for reference or MIMO use +(see the `Synchronization Application Notes <./sync.html>`_), +it is not necessary to perform this step. + +To configure the USRP to communicate with the GPSDO, use the +**usrp_burn_mb_eeprom** utility: + +:: + + cd <install-path>/lib/uhd/utils + ./usrp_burn_mb_eeprom --args=<optional device args> --values="gpsdo=internal" + + -- restore original setting -- + ./usrp_burn_mb_eeprom --args=<optional device args> --values="gpsdo=none" + +------------------------------------------------------------------------ +Using the GPSDO in Your Application +------------------------------------------------------------------------ +By default, if a GPSDO is detected at startup, the USRP will be configured +to use it as a frequency and time reference. The internal VITA timestamp +will be initialized to the GPS time, and the internal oscillator will be +phase-locked to the 10 MHz GPSDO reference. If the GPSDO is not locked to +satellites, the VITA time will not be initialized. + +GPS data is obtained through the **mboard_sensors** interface. To retrieve +the current GPS time, use the **gps_time** sensor: + +:: + + usrp->get_mboard_sensor("gps_time"); + +The returned value will be the current epoch time, in seconds since +January 1, 1970. This value is readily converted into human-readable +format using the **time.h** library in C, **boost::posix_time** in C++, etc. + +Other information can be fetched as well. You can query the lock status +with the **gps_locked** sensor, as well as obtain raw NMEA sentences using +the **gps_gprmc**, and **gps_gpgga** sensors. Location +information can be parsed out of the **gps_gpgga** sensor by using **gpsd** or +another NMEA parser. diff --git a/host/docs/identification.rst b/host/docs/identification.rst new file mode 100644 index 000000000..65b4e5e99 --- /dev/null +++ b/host/docs/identification.rst @@ -0,0 +1,130 @@ +================================= +UHD - Device Identification Notes +================================= + +.. contents:: Table of Contents + +------------------------ +Identifying USRP Devices +------------------------ +Devices are addressed through key/value string pairs. +These string pairs can be used to narrow down the search for a specific device or group of devices. +Most UHD utility applications and examples have an **--args** parameter that takes a device address, which is expressed as a delimited string. + +See the documentation in **types/device_addr.hpp** for reference. + +^^^^^^^^^^^^^^^^^^^^^^^^^ +Common device identifiers +^^^^^^^^^^^^^^^^^^^^^^^^^ +Every device has several ways of identifying it on the host system: + ++------------+----------+-----------------------------------------------------------+------------------------------- +| Identifier | Key | Notes | Example ++============+==========+===========================================================+=============================== +| Serial | serial | globally unique identifier | 12345678 ++------------+----------+-----------------------------------------------------------+---------------------------- +| Address | addr | unique identifier on a network | 192.168.10.2 ++------------+----------+-----------------------------------------------------------+------------------------------- +| Resource | resource | unique identifier for USRP RIO devices (over PCI Express) | RIO0 ++------------+----------+-----------------------------------------------------------+------------------------------- +| Name | name | optional user-set identifier | my_usrp1 (User-defined value) ++------------+----------+-----------------------------------------------------------+---------------------------- +| Type | type | hardware series identifier | usrp1, usrp2, ++------------+----------+-----------------------------------------------------------+---------------------------- + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Device discovery via command line +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Devices attached to your system can be discovered using the **uhd_find_devices** program. +This program scans your system for supported devices and prints +out an enumerated list of discovered devices and their addresses. +The list of discovered devices can be narrowed down by specifying device address args. + +:: + + uhd_find_devices + +Device address arguments can be supplied to narrow the scope of the search. + +:: + + uhd_find_devices --args="type=usrp1" + + -- OR -- + + uhd_find_devices --args="serial=12345678" + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Device discovery through the API +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +The **device::find()** API call searches for devices and returns a list of discovered devices. + +:: + + uhd::device_addr_t hint; //an empty hint discovers all devices + uhd::device_addrs_t dev_addrs = uhd::device::find(hint); + +The **hint** argument can be populated to narrow the scope of the search. + +:: + + uhd::device_addr_t hint; + hint["type"] = "usrp1"; + uhd::device_addrs_t dev_addrs = uhd::device::find(hint); + + -- OR -- + + uhd::device_addr_t hint; + hint["serial"] = "12345678"; + uhd::device_addrs_t dev_addrs = uhd::device::find(hint); + +^^^^^^^^^^^^^^^^^ +Device properties +^^^^^^^^^^^^^^^^^ +Properties of devices attached to your system can be probed with the **uhd_usrp_probe** program. +This program constructs an instance of the device and prints out its properties, +such as detected daughterboards, frequency range, gain ranges, etc... + +**Usage:** + +:: + + uhd_usrp_probe --args <device-specific-address-args> + +-------------------- +Naming a USRP Device +-------------------- +For convenience purposes, users may assign a custom name to their USRP device. +The USRP device can then be identified via name, rather than a difficult to remember serial or address. + +A name has the following properties: + +* is composed of ASCII characters +* is 0-20 characters +* is not required to be unique + +^^^^^^^^^^^^^^^^^ +Set a custom name +^^^^^^^^^^^^^^^^^ + +Run the following commands: + +:: + + cd <install-path>/lib/uhd/utils + ./usrp_burn_mb_eeprom --args=<optional device args> --values="name=lab1_xcvr" + +^^^^^^^^^^^^^^^^^^ +Discovery via name +^^^^^^^^^^^^^^^^^^ + +The keyword **name** can be used to narrow the scope of the search. +Example with the find devices utility: + +:: + + uhd_find_devices --args="name=lab1_xcvr" + + -- OR -- + + uhd_find_devices --args="type=usrp1, name=lab1_xcvr" diff --git a/host/docs/usrp1.rst b/host/docs/usrp1.rst new file mode 100644 index 000000000..ecf90502b --- /dev/null +++ b/host/docs/usrp1.rst @@ -0,0 +1,111 @@ +======================================================================== +UHD - USRP1 Device Manual +======================================================================== + +.. contents:: Table of Contents + +------------------------------------------------------------------------ +Comparative features list +------------------------------------------------------------------------ + +**Hardware Capabilities:** + * 2 transceiver card slots + * 64 MHz fixed clock rate + +**FPGA Capabilities:** + * 2 RX DDC chains in FPGA + * 2 TX DUC chains in FPGA (no TX CORDIC -> uses DAC) + * sc16 sample modes - RX & TX + + - Up to 8 MHz of RF BW with 16-bit samples + + * sc8 sample mode - RX only + + - Up to 16 MHz of RF BW with 8-bit samples + +------------------------------------------------------------------------ +Specify a Non-standard Image +------------------------------------------------------------------------ +The standard USRP1 images installer comes with two FPGA images: + * **usrp1_fpga.rbf:** 2 DDCs + 2 DUCs + * **usrp1_fpga_4rx.rbf:** 4 DDCs + 0 DUCs + +By default, the USRP1 uses the FPGA image with 2 DDCs and 2 DUCs. +However, a device address parameter can be used to override +the FPGA image selection to use an alternate or a custom FPGA image. +See the images application notes for installing custom images. + +Example device address string representations to specify non-standard firmware and/or FPGA images: + +:: + + fpga=usrp1_fpga_4rx.rbf + + -- OR -- + + fw=usrp1_fw_custom.ihx + + -- OR -- + + fpga=usrp1_fpga_4rx.rbf, fw=usrp1_fw_custom.ihx + +------------------------------------------------------------------------ +Missing and Emulated Features +------------------------------------------------------------------------ +The USRP1 FPGA does not have the necessary space to support the advanced +streaming capabilities that are possible with the newer USRP devices. +Some of these features are emulated in software to support the API. + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +List of emulated features +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +* Setting the current device time +* Getting the current device time +* Transmitting at a specific time +* Transmitting a specific number of samples +* Receiving at a specific time +* Receiving a specific number of samples +* End of burst flags for transmit/receive +* Notification on late stream command +* Notification on late transmit packet +* Notification on underflow or overflow +* Notification on broken chain error + +**Note:** +These emulated features rely on the host system's clock for timed operations +and therefore may not have sufficient precision for the application. + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +List of missing features +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +* Start of burst flags for transmit/receive + +------------------------------------------------------------------------ +Hardware Setup Notes +------------------------------------------------------------------------ + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +External clock modification +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +The USRP device can be modified to accept an external clock reference instead of the 64MHz onboard reference. + * Solder SMA (**LTI-SASF54GT**) connector to **J2001**. + * Move 0 ohm 0603 resistor **R2029** to **R2030**. + * Move 0.01uF 0603 capacitor **C925** to **C926**. + * Remove 0.01uF 0603 capacitor **C924**. + +The new external clock needs to be a square wave between +7dBm and +15dBm + +After the hardware modification, +the user should burn the setting into the EEPROM, +so UHD software can initialize with the correct clock rate. +Run the following commands to record the setting into the EEPROM: +:: + + cd <install-path>/lib/uhd/utils + ./usrp_burn_mb_eeprom --args=<optional device args> --values="mcr=<rate>" + +The user may override the clock rate specified in the EEPROM by using a device address: +Example: +:: + + uhd_usrp_probe --args="mcr=52e6" diff --git a/host/docs/usrp2.rst b/host/docs/usrp2.rst new file mode 100644 index 000000000..1070a23fc --- /dev/null +++ b/host/docs/usrp2.rst @@ -0,0 +1,474 @@ +======================================================================== +UHD - USRP2 and N2x0 Series Device Manual +======================================================================== + +.. contents:: Table of Contents + +------------------------------------------------------------------------ +Comparative features list +------------------------------------------------------------------------ + +**Hardware Capabilities:** + * 1 transceiver card slot + * External PPS reference input + * External 10 MHz reference input + * MIMO cable shared reference + * Fixed 100 MHz clock rate + * Internal GPSDO option (N2x0 only) + +**FPGA Capabilities:** + * 2 RX DDC chains in FPGA + * 1 TX DUC chain in FPGA + * Timed commands in FPGA (N2x0 only) + * Timed sampling in FPGA + * 16-bit and 8-bit sample modes (sc8 and sc16) + + * Up to 25 MHz of RF BW with 16-bit samples + * Up to 50 MHz of RF BW with 8-bit samples + +------------------------------------------------------------------------ +Load the Images onto the SD card (USRP2 only) +------------------------------------------------------------------------ +**Warning!** +Use **usrp2_card_burner** with caution. If you specify the wrong device node, +you could overwrite your hard drive. Make sure that **--dev=** specifies the SD card. + +**Warning!** +It is possible to use 3rd party SD cards with the USRP2. +However, certain types of SD cards will not interface with the CPLD: + +* Cards can be SDHC, which is not a supported interface. +* Cards can have unexpected timing characteristics. + +For these reasons, we recommend that you use the SD card that was supplied with the USRP2. + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Use the card burner tool (UNIX) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +:: + + sudo <install-path>/lib/uhd/utils/usrp2_card_burner_gui.py + + -- OR -- + + cd <install-path>/lib/uhd/utils + sudo ./usrp2_card_burner.py --dev=/dev/sd<XXX> --fpga=<path_to_fpga_image> + sudo ./usrp2_card_burner.py --dev=/dev/sd<XXX> --fw=<path_to_firmware_image> + +Use the **--list** option to get a list of possible raw devices. +The list result will filter out disk partitions and devices too large to be the sd card. +The list option has been implemented on Linux, Mac OS X, and Windows. + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Use the card burner tool (Windows) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +:: + + <path_to_python.exe> <install-path>/lib/uhd/utils/usrp2_card_burner_gui.py + +------------------------------------------------------------------------ +Load the Images onto the On-board Flash (USRP-N Series only) +------------------------------------------------------------------------ +The USRP-N Series can be reprogrammed over the network to update or change the firmware and FPGA images. +When updating images, always burn both the FPGA and firmware images before power cycling. +This ensures that when the device reboots, it has a compatible set of images to boot into. + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Use the net burner tool +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +:: + + Use default images: + usrp_n2xx_simple_net_burner --addr=<IP address> + + Use custom-built images: + usrp_n2xx_simple_net_burner --addr=<IP address> --fw=<firmware path> --fpga=<FPGA path> + +**Note:** +Different hardware revisions require different FPGA images. +Determine the revision number from the sticker on the rear of the chassis. +Use this number to select the correct FPGA image for your device. + +For users who would prefer a graphical utility, a Python-based alternative exists. + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Use the graphical net burner tool (Linux) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +:: + + <install-path>/lib/uhd/utils/usrp_n2xx_net_burner_gui.py + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Use the graphical net burner tool (Windows) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +:: + + <path_to_python.exe> <install-path>/lib/uhd/utils/usrp_n2xx_net_burner_gui.py + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Device recovery and bricking +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Its possible to put the device into an unusable state by loading bad images. +Fortunately, the USRP-N Series can be booted into a safe (read-only) image. +Once booted into the safe image, the user can once again load images onto the device. + +The safe-mode button is a pushbutton switch (S2) located inside the enclosure. +To boot into the safe image, hold-down the safe-mode button while power-cycling the device. +Continue to hold-down the button until the front-panel LEDs blink and remain solid. + +When in safe-mode, the USRP-N device will always have the IP address **192.168.10.2**. + +------------------------------------------------------------------------ +Setup Networking +------------------------------------------------------------------------ +The USRP2 only supports Gigabit Ethernet +and will not work with a 10/100 Mbps interface. +However, a 10/100 Mbps interface can be connected indirectly +to a USRP2 through a Gigabit Ethernet switch. + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Setup the host interface +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +The USRP2 communicates at the IP/UDP layer over the gigabit ethernet. +The default IP address of the USRP2 is **192.168.10.2**. +You will need to configure the host's Ethernet interface with a static IP +address to enable communication. An address of **192.168.10.1** and a subnet +mask of **255.255.255.0** is recommended. + +On a Linux system, you can set a static IP address very easily by using the +'ifconfig' command: +:: + + sudo ifconfig <interface> 192.168.10.1 + +Note that **<interface>** is usually something like **eth0**. You can discover the +names of the network interfaces in your computer by running **ifconfig** without +any parameters: +:: + + ifconfig -a + +**Note:** +When using UHD software, if an IP address for the USRP2 is not specified, +the software will use UDP broadcast packets to locate the USRP2. +On some systems, the firewall will block UDP broadcast packets. +It is recommended that you change or disable your firewall settings. + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Multiple devices per host +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +For maximum throughput, one Ethernet interface per USRP2 is recommended, +although multiple devices may be connected via a Gigabit Ethernet switch. +In any case, each Ethernet interface should have its own subnet, +and the corresponding USRP2 device should be assigned an address in that subnet. +Example: + +**Configuration for USRP2 device 0:** + +* Ethernet interface IPv4 address: **192.168.10.1** +* Ethernet interface subnet mask: **255.255.255.0** +* USRP2 device IPv4 address: **192.168.10.2** + +**Configuration for USRP2 device 1:** + +* Ethernet interface IPv4 address: **192.168.20.1** +* Ethernet interface subnet mask: **255.255.255.0** +* USRP2 device IPv4 address: **192.168.20.2** + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Change the USRP2's IP address +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +You may need to change the USRP2's IP address for several reasons: + +* to satisfy your particular network configuration +* to use multiple USRP2s on the same host computer +* to set a known IP address into USRP2 (in case you forgot) + +**Method 1:** +To change the USRP2's IP address, +you must know the current address of the USRP2, +and the network must be setup properly as described above. +Run the following commands: +:: + + cd <install-path>/lib/uhd/utils + ./usrp_burn_mb_eeprom --args=<optional device args> --values="ip-addr=192.168.10.3" + +**Method 2 (Linux Only):** +This method assumes that you do not know the IP address of your USRP2. +It uses raw Ethernet packets to bypass the IP/UDP layer to communicate with the USRP2. +Run the following commands: +:: + + cd <install-path>/lib/uhd/utils + sudo ./usrp2_recovery.py --ifc=eth0 --new-ip=192.168.10.3 + +------------------------------------------------------------------------ +Communication Problems +------------------------------------------------------------------------ +When setting up a development machine for the first time, +you may have various difficulties communicating with the USRP device. +The following tips are designed to help narrow down and diagnose the problem. + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +RuntimeError: no control response +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +This is a common error that occurs when you have set the subnet of your network +interface to a different subnet than the network interface of the USRP device. For +example, if your network interface is set to **192.168.20.1**, and the USRP device is +**192.168.10.2** (note the difference in the third numbers of the IP addresses), you +will likely see a 'no control response' error message. + +Fixing this is simple - just set the your host PC's IP address to the same +subnet as that of your USRP device. Instructions for setting your IP address are in the +previous section of this documentation. + + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Firewall issues +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +When the IP address is not specified, +the device discovery broadcasts UDP packets from each ethernet interface. +Many firewalls will block the replies to these broadcast packets. +If disabling your system's firewall +or specifying the IP address yields a discovered device, +then your firewall may be blocking replies to UDP broadcast packets. +If this is the case, we recommend that you disable the firewall +or create a rule to allow all incoming packets with UDP source port **49152**. + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Ping the device +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +The USRP device will reply to ICMP echo requests. +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 + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Monitor the serial output +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Read the serial port to get debug verbose output from the embedded microcontroller. +The microcontroller prints useful information about IP addresses, +MAC addresses, control packets, fast-path settings, and bootloading. +Use a standard USB to 3.3v-level serial converter at 230400 baud. +Connect **GND** to the converter ground, and connect **TXD** to the converter receive. +The **RXD** pin can be left unconnected as this is only a one-way communication. + +* **USRP2:** Serial port located on the rear edge +* **N210:** Serial port located on the left side + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Monitor the host network traffic +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Use Wireshark to monitor packets sent to and received from the device. + +------------------------------------------------------------------------ +Addressing the Device +------------------------------------------------------------------------ + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Single device configuration +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +In a single-device configuration, the USRP device must have a unique IPv4 +address on the host computer. The USRP can be identified through its IPv4 +address, resolvable hostname, or by other means. See the application notes on +`device identification <./identification.html>`_. Please note that this +addressing scheme should also be used with the **multi_usrp** interface. + +Example device address string representation for a USRP2 with IPv4 address **192.168.10.2**: + +:: + + addr=192.168.10.2 + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Multiple device configuration +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +In a multi-device configuration, +each USRP device must have a unique IPv4 address on the host computer. +The device address parameter keys must be suffixed with the device index. +Each parameter key should be of the format <key><index>. +Use this addressing scheme with the **multi_usrp** interface. + +* The order in which devices are indexed corresponds to the indexing of the transmit and receive channels. +* The key indexing provides the same granularity of device identification as in the single device case. + +Example device address string representation for 2 USRP2s with IPv4 addresses **192.168.10.2** and **192.168.20.2**: +:: + + addr0=192.168.10.2, addr1=192.168.20.2 + +------------------------------------------------------------------------ +Using the MIMO Cable +------------------------------------------------------------------------ +The MIMO cable allows two USRP devices to share reference clocks, +time synchronization, and the Ethernet interface. +One of the devices will sync its clock and time references to the MIMO cable. +This device will be referred to as the slave, and the other device, the master. + +* The slave device acquires the clock and time references from the master device. +* The master and slave may be used individually or in a multi-device configuration. +* External clocking is optional and should only be supplied to the master device. + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Shared Ethernet mode +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +In shared Ethernet mode, +only one device in the configuration can be attached to the Ethernet. + +* Clock reference, time reference, and data are communicated over the MIMO cable. +* Master and slave must have different IPv4 addresses in the same subnet. + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Dual Ethernet mode +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +In dual Ethernet mode, +both devices in the configuration must be attached to the Ethernet. + +* Only clock reference and time reference are communicated over the MIMO cable. +* The master and slave must have different IPv4 addresses in different subnets. + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Configuring the slave +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +In order for the slave to synchronize to the master over MIMO cable, +the following clock configuration must be set on the slave device: +:: + + usrp->set_time_source("mimo", slave_index); + usrp->set_clock_source("mimo", slave_index); + + +------------------------------------------------------------------------ +Alternative stream destination +------------------------------------------------------------------------ +It is possible to program the USRP device to send RX packets to an alternative IP/UDP destination. + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Set the subnet and gateway +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +To use an alternative streaming destination, +the device needs to be able to determine if the destination address +is within its subnet, and ARP appropriately. +Therefore, the user should ensure that subnet and gateway addresses +have been programmed into the device's EEPROM. + +Run the following commands: +:: + + cd <install-path>/lib/uhd/utils + ./usrp_burn_mb_eeprom --args=<optional device args> --values="subnet=255.255.255.0,gateway=192.168.10.1" + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Create a receive streamer +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Set the stream args "addr" and "port" values to the alternative destination. +Packets will be sent to this destination when the user issues a stream command. + +:: + + //create a receive streamer, host type does not matter + uhd::stream_args_t stream_args("fc32"); + + //resolvable address and port for a remote udp socket + stream_args.args["addr"] = "192.168.10.42"; + stream_args.args["port"] = "12345"; + + //create the streamer + uhd::rx_streamer::sptr rx_stream = usrp->get_rx_stream(stream_args); + + //issue stream command + uhd::stream_cmd_t stream_cmd(uhd::stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_DONE); + stream_cmd.num_samps = total_num_samps; + stream_cmd.stream_now = true; + usrp->issue_stream_cmd(stream_cmd); + +**Note:** +Calling recv() on this streamer object should yield a timeout. + +------------------------------------------------------------------------ +Hardware Setup Notes +------------------------------------------------------------------------ + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Front panel LEDs +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +The LEDs on the front panel can be useful in debugging hardware and software issues. +The LEDs reveal the following about the state of the device: + +* **LED A:** transmitting +* **LED B:** MIMO cable link +* **LED C:** receiving +* **LED D:** firmware loaded +* **LED E:** reference lock +* **LED F:** CPLD loaded + + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +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 reference clock requires the following power level: + +* **USRP2** 5 to 15 dBm +* **N2XX** 0 to 15 dBm + + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +PPS - Pulse Per Second +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Using a PPS signal for timestamp synchronization requires a square wave signal with the following amplitude: + +* **USRP2** 5Vpp +* **N2XX** 3.3 to 5Vpp + +Test the PPS input with the following app: + +* **<args>** are device address arguments (optional if only one USRP device is on your machine) + +:: + + cd <install-path>/lib/uhd/examples + ./test_pps_input --args=<args> + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Internal GPSDO +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Please see the `Internal GPSDO Application Notes <./gpsdo.html>`_ +for information on configuring and using the internal GPSDO. + +------------------------------------------------------------------------ +Miscellaneous +------------------------------------------------------------------------ + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Available Sensors +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +The following sensors are available for the USRP2/N-Series motherboards; +they can be queried through the API. + +* **mimo_locked** - clock reference locked over the MIMO cable +* **ref_locked** - clock reference locked (internal/external) +* other sensors are added when the GPSDO is enabled + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Multiple RX channels +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +There are two complete DDC chains in the FPGA. +In the single channel case, only one chain is ever used. +To receive from both channels, +the user must set the **RX** subdevice specification. +This hardware has only one daughterboard slot, +which has been aptly named slot **A**. + +In the following example, a TVRX2 is installed. +Channel 0 is sourced from subdevice **RX1**, +and channel 1 is sourced from subdevice **RX2** (**RX1** and **RX2** +are the antenna ports on the TVRX2 daughterboard): + +:: + + usrp->set_rx_subdev_spec("A:RX1 A:RX2"); diff --git a/host/docs/usrp_x3x0.dox b/host/docs/usrp_x3x0.dox index 7996bc7e1..42574334b 100644 --- a/host/docs/usrp_x3x0.dox +++ b/host/docs/usrp_x3x0.dox @@ -91,7 +91,7 @@ number, you will have to update the FPGA image before you can start using your U usrp_x3xx_fpga_burner --addr=192.168.10.2 --fpga-path <path_to_images>/usrp_x310_fpga_HGS.bit - The process of updating the FPGA will take several minutes. Make sure the process of flashing the image does not get interrupted. + The process of updating the FPGA image will take several minutes. Make sure the process of flashing the image does not get interrupted. See \ref x3x0_flash for more details. diff --git a/host/examples/benchmark_rate.cpp b/host/examples/benchmark_rate.cpp index ea49d48d9..9e9aa67e9 100644 --- a/host/examples/benchmark_rate.cpp +++ b/host/examples/benchmark_rate.cpp @@ -98,7 +98,7 @@ void benchmark_rx_rate(uhd::usrp::multi_usrp::sptr usrp, const std::string &rx_c break; default: - std::cerr << "Error code: " << md.error_code << std::endl; + std::cerr << "Receiver error: " << md.strerror() << std::endl; std::cerr << "Unexpected error on recv, continuing..." << std::endl; break; } diff --git a/host/examples/rx_multi_samples.cpp b/host/examples/rx_multi_samples.cpp index 9e5970978..a50b5f0e0 100644 --- a/host/examples/rx_multi_samples.cpp +++ b/host/examples/rx_multi_samples.cpp @@ -172,8 +172,8 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ if (md.error_code == uhd::rx_metadata_t::ERROR_CODE_TIMEOUT) break; if (md.error_code != uhd::rx_metadata_t::ERROR_CODE_NONE){ throw std::runtime_error(str(boost::format( - "Unexpected error code 0x%x" - ) % md.error_code)); + "Receiver error %s" + ) % md.strerror())); } if(verbose) std::cout << boost::format( diff --git a/host/examples/rx_samples_to_file.cpp b/host/examples/rx_samples_to_file.cpp index 0d42404d3..de3640794 100644 --- a/host/examples/rx_samples_to_file.cpp +++ b/host/examples/rx_samples_to_file.cpp @@ -101,18 +101,15 @@ template<typename samp_type> void recv_to_file( continue; } if (md.error_code != uhd::rx_metadata_t::ERROR_CODE_NONE){ - std::string error = str(boost::format( - "Unexpected error code 0x%x" - ) % md.error_code); - - if (continue_on_bad_packet){ - std::cerr << error << std::endl; - continue; - } - else - throw std::runtime_error(error); + std::string error = str(boost::format("Receiver error: %s") % md.strerror()); + if (continue_on_bad_packet){ + std::cerr << error << std::endl; + continue; + } + else + throw std::runtime_error(error); } - + if (enable_size_map){ SizeMap::iterator it = mapSizes.find(num_rx_samps); if (it == mapSizes.end()) diff --git a/host/examples/rx_timed_samples.cpp b/host/examples/rx_timed_samples.cpp index cc9216cb7..30535907f 100644 --- a/host/examples/rx_timed_samples.cpp +++ b/host/examples/rx_timed_samples.cpp @@ -130,8 +130,8 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ if (md.error_code == uhd::rx_metadata_t::ERROR_CODE_TIMEOUT) break; if (md.error_code != uhd::rx_metadata_t::ERROR_CODE_NONE){ throw std::runtime_error(str(boost::format( - "Unexpected error code 0x%x" - ) % md.error_code)); + "Receiver error %s" + ) % md.strerror())); } if(verbose) std::cout << boost::format( diff --git a/host/examples/test_timed_commands.cpp b/host/examples/test_timed_commands.cpp index 8c6011c68..3da4bc707 100644 --- a/host/examples/test_timed_commands.cpp +++ b/host/examples/test_timed_commands.cpp @@ -139,8 +139,8 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ const size_t num_rx_samps = rx_stream->recv(&buff.front(), buff.size(), md, 1.0); if (md.error_code != uhd::rx_metadata_t::ERROR_CODE_NONE){ throw std::runtime_error(str(boost::format( - "Unexpected error code 0x%x" - ) % md.error_code)); + "Receiver error %s" + ) % md.strerror())); } std::cout << boost::format( " Received packet: %u samples, %u full secs, %f frac secs" diff --git a/host/examples/transport_hammer.cpp b/host/examples/transport_hammer.cpp index 4b949e5bd..3f233b2a5 100644 --- a/host/examples/transport_hammer.cpp +++ b/host/examples/transport_hammer.cpp @@ -88,7 +88,7 @@ void rx_hammer(uhd::usrp::multi_usrp::sptr usrp, const std::string &rx_cpu, uhd: break; default: - std::cerr << "Error code: " << md.error_code << std::endl; + std::cerr << "Receiver error: " << md.strerror() << std::endl; std::cerr << "Unexpected error on recv, continuing..." << std::endl; break; } diff --git a/host/examples/txrx_loopback_to_file.cpp b/host/examples/txrx_loopback_to_file.cpp index 3d3cf1dfc..a62ccd7b2 100644 --- a/host/examples/txrx_loopback_to_file.cpp +++ b/host/examples/txrx_loopback_to_file.cpp @@ -181,8 +181,8 @@ template<typename samp_type> void recv_to_file( } if (md.error_code != uhd::rx_metadata_t::ERROR_CODE_NONE){ throw std::runtime_error(str(boost::format( - "Unexpected error code 0x%x" - ) % md.error_code)); + "Receiver error %s" + ) % md.strerror())); } num_total_samps += num_rx_samps; diff --git a/host/include/uhd/transport/nirio/nirio_fifo.h b/host/include/uhd/transport/nirio/nirio_fifo.h index f7abb396f..fc1de245d 100644 --- a/host/include/uhd/transport/nirio/nirio_fifo.h +++ b/host/include/uhd/transport/nirio/nirio_fifo.h @@ -104,16 +104,20 @@ public: uint32_t& num_remaining); private: //Methods - bool _is_initialized(); datatype_info_t _get_datatype_info(); nirio_status _get_transfer_count(uint64_t& transfer_count); nirio_status _ensure_transfer_completed(uint32_t timeout_ms); private: //Members + enum fifo_state_t { + UNMAPPED, MAPPED, STARTED + }; + std::string _name; fifo_direction_t _fifo_direction; uint32_t _fifo_channel; datatype_info_t _datatype_info; + fifo_state_t _state; size_t _acquired_pending; nirio_driver_iface::rio_mmap_t _mem_map; boost::recursive_mutex _mutex; diff --git a/host/include/uhd/transport/nirio/nirio_fifo.ipp b/host/include/uhd/transport/nirio/nirio_fifo.ipp index 80a0c2a89..437e3a1fc 100644 --- a/host/include/uhd/transport/nirio/nirio_fifo.ipp +++ b/host/include/uhd/transport/nirio/nirio_fifo.ipp @@ -31,6 +31,7 @@ nirio_fifo<data_t>::nirio_fifo( _fifo_direction(direction), _fifo_channel(fifo_instance), _datatype_info(_get_datatype_info()), + _state(UNMAPPED), _acquired_pending(0), _mem_map(), _riok_proxy_ptr(&riok_proxy), @@ -61,28 +62,37 @@ nirio_status nirio_fifo<data_t>::initialize( if (!_riok_proxy_ptr) return NiRio_Status_ResourceNotInitialized; boost::unique_lock<boost::recursive_mutex> lock(_mutex); - nirio_driver_iface::nirio_syncop_in_params_t in = {}; - nirio_driver_iface::nirio_syncop_out_params_t out = {}; + if (_state == UNMAPPED) { + nirio_driver_iface::nirio_syncop_in_params_t in = {}; + nirio_driver_iface::nirio_syncop_out_params_t out = {}; - //Forcefully stop the fifo if it is running - in.function = nirio_driver_iface::NIRIO_FUNC::FIFO; - in.subfunction = nirio_driver_iface::NIRIO_FIFO::STOP; - status = _riok_proxy_ptr->sync_operation(&in, sizeof(in), &out, sizeof(out)); + //Forcefully stop the fifo if it is running + in.function = nirio_driver_iface::NIRIO_FUNC::FIFO; + in.subfunction = nirio_driver_iface::NIRIO_FIFO::STOP; + in.params.fifo.channel = _fifo_channel; + _riok_proxy_ptr->sync_operation(&in, sizeof(in), &out, sizeof(out)); //Cleanup operation. Ignore status. - in.function = nirio_driver_iface::NIRIO_FUNC::FIFO; - in.subfunction = nirio_driver_iface::NIRIO_FIFO::CONFIGURE; + //Configure the FIFO now that we know it is stopped + in.function = nirio_driver_iface::NIRIO_FUNC::FIFO; + in.subfunction = nirio_driver_iface::NIRIO_FIFO::CONFIGURE; + in.params.fifo.channel = _fifo_channel; + in.params.fifo.op.config.requestedDepth = static_cast<uint32_t>(requested_depth); + in.params.fifo.op.config.requiresActuals = 1; + status = _riok_proxy_ptr->sync_operation(&in, sizeof(in), &out, sizeof(out)); - in.params.fifo.channel = _fifo_channel; - in.params.fifo.op.config.requestedDepth = static_cast<uint32_t>(requested_depth); - in.params.fifo.op.config.requiresActuals = 1; + if (nirio_status_fatal(status)) return status; - status = _riok_proxy_ptr->sync_operation(&in, sizeof(in), &out, sizeof(out)); - if (nirio_status_fatal(status)) return status; + actual_depth = out.params.fifo.op.config.actualDepth; + actual_size = out.params.fifo.op.config.actualSize; - actual_depth = out.params.fifo.op.config.actualDepth; - actual_size = out.params.fifo.op.config.actualSize; + status = _riok_proxy_ptr->map_fifo_memory(_fifo_channel, actual_size, _mem_map); - status = _riok_proxy_ptr->map_fifo_memory(_fifo_channel, actual_size, _mem_map); + if (nirio_status_not_fatal(status)) { + _state = MAPPED; + } + } else { + status = NiRio_Status_SoftwareFault; + } return status; } @@ -90,9 +100,13 @@ template <typename data_t> void nirio_fifo<data_t>::finalize() { boost::unique_lock<boost::recursive_mutex> lock(_mutex); - if (!_mem_map.is_null()) { - stop(); + + //If the FIFO is started, the stop will change the state to MAPPED. + stop(); + + if (_state == MAPPED) { _riok_proxy_ptr->unmap_fifo_memory(_mem_map); + _state = UNMAPPED; //Assume teardown succeeded } } @@ -104,16 +118,25 @@ nirio_status nirio_fifo<data_t>::start() boost::unique_lock<boost::recursive_mutex> lock(_mutex); - nirio_driver_iface::nirio_syncop_in_params_t in = {}; - nirio_driver_iface::nirio_syncop_out_params_t out = {}; + if (_state == STARTED) { + //Do nothing. Already started. + } else if (_state == MAPPED) { + nirio_driver_iface::nirio_syncop_in_params_t in = {}; + nirio_driver_iface::nirio_syncop_out_params_t out = {}; - in.function = nirio_driver_iface::NIRIO_FUNC::FIFO; - in.subfunction = nirio_driver_iface::NIRIO_FIFO::START; + in.function = nirio_driver_iface::NIRIO_FUNC::FIFO; + in.subfunction = nirio_driver_iface::NIRIO_FIFO::START; - status = _riok_proxy_ptr->sync_operation(&in, sizeof(in), &out, sizeof(out)); - if (nirio_status_not_fatal(status)) { - _acquired_pending = 0; - _expected_xfer_count = 0; + in.params.fifo.channel = _fifo_channel; + + status = _riok_proxy_ptr->sync_operation(&in, sizeof(in), &out, sizeof(out)); + if (nirio_status_not_fatal(status)) { + _state = STARTED; + _acquired_pending = 0; + _expected_xfer_count = 0; + } + } else { + status = NiRio_Status_ResourceNotInitialized; } return status; } @@ -125,15 +148,22 @@ nirio_status nirio_fifo<data_t>::stop() if (!_riok_proxy_ptr) return NiRio_Status_ResourceNotInitialized; boost::unique_lock<boost::recursive_mutex> lock(_mutex); - if (_acquired_pending > 0) release(_acquired_pending); - nirio_driver_iface::nirio_syncop_in_params_t in = {}; - nirio_driver_iface::nirio_syncop_out_params_t out = {}; + if (_state == STARTED) { + if (_acquired_pending > 0) release(_acquired_pending); + + nirio_driver_iface::nirio_syncop_in_params_t in = {}; + nirio_driver_iface::nirio_syncop_out_params_t out = {}; - in.function = nirio_driver_iface::NIRIO_FUNC::FIFO; - in.subfunction = nirio_driver_iface::NIRIO_FIFO::STOP; + in.function = nirio_driver_iface::NIRIO_FUNC::FIFO; + in.subfunction = nirio_driver_iface::NIRIO_FIFO::STOP; - status = _riok_proxy_ptr->sync_operation(&in, sizeof(in), &out, sizeof(out)); + in.params.fifo.channel = _fifo_channel; + + status = _riok_proxy_ptr->sync_operation(&in, sizeof(in), &out, sizeof(out)); + + _state = MAPPED; //Assume teardown succeeded + } return status; } @@ -151,36 +181,40 @@ nirio_status nirio_fifo<data_t>::acquire( boost::unique_lock<boost::recursive_mutex> lock(_mutex); - nirio_driver_iface::nirio_syncop_in_params_t in = {}; - uint32_t stuffed[2]; - nirio_driver_iface::nirio_syncop_out_params_t out = {}; - init_syncop_out_params(out, stuffed, sizeof(stuffed)); - - in.function = nirio_driver_iface::NIRIO_FUNC::FIFO; - in.subfunction = nirio_driver_iface::NIRIO_FIFO::WAIT; - - in.params.fifo.channel = _fifo_channel; - in.params.fifo.op.wait.elementsRequested = static_cast<uint32_t>(elements_requested); - in.params.fifo.op.wait.scalarType = static_cast<uint32_t>(_datatype_info.scalar_type); - in.params.fifo.op.wait.bitWidth = _datatype_info.width * 8; - in.params.fifo.op.wait.output = _fifo_direction == OUTPUT_FIFO; - in.params.fifo.op.wait.timeout = timeout; - - status = _riok_proxy_ptr->sync_operation(&in, sizeof(in), &out, sizeof(out)); - - if (nirio_status_not_fatal(status)) { - elements = static_cast<data_t*>(out.params.fifo.op.wait.elements.pointer); - elements_acquired = stuffed[0]; - elements_remaining = stuffed[1]; - _acquired_pending = elements_acquired; - - if (UHD_NIRIO_RX_FIFO_XFER_CHECK_EN && - _riok_proxy_ptr->get_rio_quirks().rx_fifo_xfer_check_en() && - get_direction() == INPUT_FIFO - ) { - _expected_xfer_count += static_cast<uint64_t>(elements_requested * sizeof(data_t)); - status = _ensure_transfer_completed(timeout); + if (_state == STARTED) { + nirio_driver_iface::nirio_syncop_in_params_t in = {}; + uint32_t stuffed[2]; + nirio_driver_iface::nirio_syncop_out_params_t out = {}; + init_syncop_out_params(out, stuffed, sizeof(stuffed)); + + in.function = nirio_driver_iface::NIRIO_FUNC::FIFO; + in.subfunction = nirio_driver_iface::NIRIO_FIFO::WAIT; + + in.params.fifo.channel = _fifo_channel; + in.params.fifo.op.wait.elementsRequested = static_cast<uint32_t>(elements_requested); + in.params.fifo.op.wait.scalarType = static_cast<uint32_t>(_datatype_info.scalar_type); + in.params.fifo.op.wait.bitWidth = _datatype_info.width * 8; + in.params.fifo.op.wait.output = _fifo_direction == OUTPUT_FIFO; + in.params.fifo.op.wait.timeout = timeout; + + status = _riok_proxy_ptr->sync_operation(&in, sizeof(in), &out, sizeof(out)); + + if (nirio_status_not_fatal(status)) { + elements = static_cast<data_t*>(out.params.fifo.op.wait.elements.pointer); + elements_acquired = stuffed[0]; + elements_remaining = stuffed[1]; + _acquired_pending = elements_acquired; + + if (UHD_NIRIO_RX_FIFO_XFER_CHECK_EN && + _riok_proxy_ptr->get_rio_quirks().rx_fifo_xfer_check_en() && + get_direction() == INPUT_FIFO + ) { + _expected_xfer_count += static_cast<uint64_t>(elements_requested * sizeof(data_t)); + status = _ensure_transfer_completed(timeout); + } } + } else { + status = NiRio_Status_ResourceNotInitialized; } return status; @@ -194,17 +228,21 @@ nirio_status nirio_fifo<data_t>::release(const size_t elements) boost::unique_lock<boost::recursive_mutex> lock(_mutex); - nirio_driver_iface::nirio_syncop_in_params_t in = {}; - nirio_driver_iface::nirio_syncop_out_params_t out = {}; + if (_state == STARTED) { + nirio_driver_iface::nirio_syncop_in_params_t in = {}; + nirio_driver_iface::nirio_syncop_out_params_t out = {}; - in.function = nirio_driver_iface::NIRIO_FUNC::FIFO; - in.subfunction = nirio_driver_iface::NIRIO_FIFO::GRANT; + in.function = nirio_driver_iface::NIRIO_FUNC::FIFO; + in.subfunction = nirio_driver_iface::NIRIO_FIFO::GRANT; - in.params.fifo.channel = _fifo_channel; - in.params.fifo.op.grant.elements = static_cast<uint32_t>(elements); + in.params.fifo.channel = _fifo_channel; + in.params.fifo.op.grant.elements = static_cast<uint32_t>(elements); - status = _riok_proxy_ptr->sync_operation(&in, sizeof(in), &out, sizeof(out)); - _acquired_pending = 0; + status = _riok_proxy_ptr->sync_operation(&in, sizeof(in), &out, sizeof(out)); + _acquired_pending = 0; + } else { + status = NiRio_Status_ResourceNotInitialized; + } return status; } @@ -222,23 +260,27 @@ nirio_status nirio_fifo<data_t>::read( boost::unique_lock<boost::recursive_mutex> lock(_mutex); - nirio_driver_iface::nirio_syncop_in_params_t in = {}; - nirio_driver_iface::nirio_syncop_out_params_t out = {}; - init_syncop_out_params(out, buf, num_elements * _datatype_info.width); + if (_state == STARTED) { + nirio_driver_iface::nirio_syncop_in_params_t in = {}; + nirio_driver_iface::nirio_syncop_out_params_t out = {}; + init_syncop_out_params(out, buf, num_elements * _datatype_info.width); - in.function = nirio_driver_iface::NIRIO_FUNC::FIFO; - in.subfunction = nirio_driver_iface::NIRIO_FIFO::READ; + in.function = nirio_driver_iface::NIRIO_FUNC::FIFO; + in.subfunction = nirio_driver_iface::NIRIO_FIFO::READ; - in.params.fifo.channel = _fifo_channel; - in.params.fifo.op.readWithDataType.timeout = timeout; - in.params.fifo.op.readWithDataType.scalarType = static_cast<uint32_t>(_datatype_info.scalar_type); - in.params.fifo.op.readWithDataType.bitWidth = _datatype_info.width * 8; + in.params.fifo.channel = _fifo_channel; + in.params.fifo.op.readWithDataType.timeout = timeout; + in.params.fifo.op.readWithDataType.scalarType = static_cast<uint32_t>(_datatype_info.scalar_type); + in.params.fifo.op.readWithDataType.bitWidth = _datatype_info.width * 8; - status = _riok_proxy_ptr->sync_operation(&in, sizeof(in), &out, sizeof(out)); + status = _riok_proxy_ptr->sync_operation(&in, sizeof(in), &out, sizeof(out)); - if (nirio_status_not_fatal(status) || status == NiRio_Status_FifoTimeout) { - num_read = out.params.fifo.op.read.numberRead; - num_remaining = out.params.fifo.op.read.numberRemaining; + if (nirio_status_not_fatal(status) || status == NiRio_Status_FifoTimeout) { + num_read = out.params.fifo.op.read.numberRead; + num_remaining = out.params.fifo.op.read.numberRemaining; + } + } else { + status = NiRio_Status_ResourceNotInitialized; } return status; @@ -256,22 +298,26 @@ nirio_status nirio_fifo<data_t>::write( boost::unique_lock<boost::recursive_mutex> lock(_mutex); - nirio_driver_iface::nirio_syncop_in_params_t in = {}; - init_syncop_in_params(in, buf, num_elements * _datatype_info.width); - nirio_driver_iface::nirio_syncop_out_params_t out = {}; + if (_state == STARTED) { + nirio_driver_iface::nirio_syncop_in_params_t in = {}; + init_syncop_in_params(in, buf, num_elements * _datatype_info.width); + nirio_driver_iface::nirio_syncop_out_params_t out = {}; - in.function = nirio_driver_iface::NIRIO_FUNC::FIFO; - in.subfunction = nirio_driver_iface::NIRIO_FIFO::WRITE; + in.function = nirio_driver_iface::NIRIO_FUNC::FIFO; + in.subfunction = nirio_driver_iface::NIRIO_FIFO::WRITE; - in.params.fifo.channel = _fifo_channel; - in.params.fifo.op.writeWithDataType.timeout = timeout; - in.params.fifo.op.readWithDataType.scalarType = static_cast<uint32_t>(_datatype_info.scalar_type); - in.params.fifo.op.readWithDataType.bitWidth = _datatype_info.width * 8; + in.params.fifo.channel = _fifo_channel; + in.params.fifo.op.writeWithDataType.timeout = timeout; + in.params.fifo.op.readWithDataType.scalarType = static_cast<uint32_t>(_datatype_info.scalar_type); + in.params.fifo.op.readWithDataType.bitWidth = _datatype_info.width * 8; - status = _riok_proxy_ptr->sync_operation(&in, sizeof(in), &out, sizeof(out)); + status = _riok_proxy_ptr->sync_operation(&in, sizeof(in), &out, sizeof(out)); - if (nirio_status_not_fatal(status) || status == NiRio_Status_FifoTimeout) { - num_remaining = out.params.fifo.op.write.numberRemaining; + if (nirio_status_not_fatal(status) || status == NiRio_Status_FifoTimeout) { + num_remaining = out.params.fifo.op.write.numberRemaining; + } + } else { + status = NiRio_Status_ResourceNotInitialized; } return status; diff --git a/host/include/uhd/transport/nirio/nirio_quirks.h b/host/include/uhd/transport/nirio/nirio_quirks.h index 326eeeb8c..ed4f72e7f 100644 --- a/host/include/uhd/transport/nirio/nirio_quirks.h +++ b/host/include/uhd/transport/nirio/nirio_quirks.h @@ -24,8 +24,8 @@ //Quirk#1: We need to verify RX zero-copy data transfers from the RIO // driver if we are in full duplex mode. -// This option allows disabling this quirk by compiling it out. -#define UHD_NIRIO_RX_FIFO_XFER_CHECK_EN 1 +// This option allows enabling this quirk. +#define UHD_NIRIO_RX_FIFO_XFER_CHECK_EN 0 namespace uhd { namespace niusrprio { diff --git a/host/include/uhd/types/metadata.hpp b/host/include/uhd/types/metadata.hpp index 6a79720d0..51a2b7c43 100644 --- a/host/include/uhd/types/metadata.hpp +++ b/host/include/uhd/types/metadata.hpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2012 Ettus Research LLC +// Copyright 2010-2012,2014 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -117,6 +117,20 @@ namespace uhd{ //! Out of sequence. The transport has either dropped a packet or received data out of order. bool out_of_sequence; + + /*! + * Convert a rx_metadata_t into a pretty print string. + * + * \param compact Set to false for a more verbose output. + * \return a printable string representing the metadata. + */ + std::string to_pp_string(bool compact=true) const; + + /*! + * Similar to C's strerror() function, creates a std::string describing the error code. + * \return a printable string representing the error. + */ + std::string strerror(void) const; }; /*! diff --git a/host/include/uhd/usrp/multi_usrp.hpp b/host/include/uhd/usrp/multi_usrp.hpp index aac40efe5..883e4da3d 100644 --- a/host/include/uhd/usrp/multi_usrp.hpp +++ b/host/include/uhd/usrp/multi_usrp.hpp @@ -119,14 +119,10 @@ public: virtual device::sptr get_device(void) = 0; //! Convenience method to get a RX streamer. See also uhd::device::get_rx_stream(). - rx_streamer::sptr get_rx_stream(const stream_args_t &args){ - return this->get_device()->get_rx_stream(args); - } + virtual rx_streamer::sptr get_rx_stream(const stream_args_t &args) = 0; //! Convenience method to get a TX streamer. See also uhd::device::get_rx_stream(). - tx_streamer::sptr get_tx_stream(const stream_args_t &args){ - return this->get_device()->get_tx_stream(args); - } + virtual tx_streamer::sptr get_tx_stream(const stream_args_t &args) = 0; /*! * Returns identifying information about this USRP's configuration. diff --git a/host/include/uhd/utils/CMakeLists.txt b/host/include/uhd/utils/CMakeLists.txt index c0991b3ce..e9633286f 100644 --- a/host/include/uhd/utils/CMakeLists.txt +++ b/host/include/uhd/utils/CMakeLists.txt @@ -22,6 +22,7 @@ UHD_INSTALL(FILES atomic.hpp byteswap.hpp byteswap.ipp + cast.hpp csv.hpp gain_group.hpp images.hpp diff --git a/host/include/uhd/utils/cast.hpp b/host/include/uhd/utils/cast.hpp new file mode 100644 index 000000000..9db92c526 --- /dev/null +++ b/host/include/uhd/utils/cast.hpp @@ -0,0 +1,43 @@ +// +// Copyright 2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_UHD_UTILS_CAST_HPP +#define INCLUDED_UHD_UTILS_CAST_HPP + +#include <uhd/config.hpp> +#include <string> +#include <sstream> + +namespace uhd{ namespace cast{ + //! Convert a hexadecimal string into a value. + // + // Example: + // boost::uint16_t x = hexstr_cast<boost::uint16_t>("0xDEADBEEF"); + // Uses stringstream. + template<typename T> inline T hexstr_cast(const std::string &in) + { + T x; + std::stringstream ss; + ss << std::hex << in; + ss >> x; + return x; + } + +}} //namespace uhd::cast + +#endif /* INCLUDED_UHD_UTILS_CAST_HPP */ + diff --git a/host/lib/convert/convert_impl.cpp b/host/lib/convert/convert_impl.cpp index dc7f8f9dc..c7907ed83 100644 --- a/host/lib/convert/convert_impl.cpp +++ b/host/lib/convert/convert_impl.cpp @@ -134,6 +134,7 @@ UHD_STATIC_BLOCK(convert_register_item_sizes){ convert::register_bytes_per_item("sc64", sizeof(std::complex<boost::int64_t>)); convert::register_bytes_per_item("sc32", sizeof(std::complex<boost::int32_t>)); convert::register_bytes_per_item("sc16", sizeof(std::complex<boost::int16_t>)); + convert::register_bytes_per_item("sc12", 3 * sizeof(std::complex<boost::int8_t>)); convert::register_bytes_per_item("sc8", sizeof(std::complex<boost::int8_t>)); //register standard real types diff --git a/host/lib/types/CMakeLists.txt b/host/lib/types/CMakeLists.txt index b69c8e487..7fc6bdd94 100644 --- a/host/lib/types/CMakeLists.txt +++ b/host/lib/types/CMakeLists.txt @@ -82,6 +82,7 @@ SET_SOURCE_FILES_PROPERTIES( LIBUHD_APPEND_SOURCES( ${CMAKE_CURRENT_SOURCE_DIR}/device_addr.cpp ${CMAKE_CURRENT_SOURCE_DIR}/mac_addr.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/metadata.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ranges.cpp ${CMAKE_CURRENT_SOURCE_DIR}/sensors.cpp ${CMAKE_CURRENT_SOURCE_DIR}/serial.cpp diff --git a/host/lib/types/metadata.cpp b/host/lib/types/metadata.cpp new file mode 100644 index 000000000..fec2ac564 --- /dev/null +++ b/host/lib/types/metadata.cpp @@ -0,0 +1,92 @@ +// +// Copyright 2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <string> +#include <sstream> +#include <boost/format.hpp> +#include <uhd/exception.hpp> +#include <uhd/types/metadata.hpp> +#include <uhd/types/time_spec.hpp> + +using namespace uhd; + +std::string rx_metadata_t::to_pp_string(bool compact) const +{ + std::stringstream ss; + + if (compact) { + if (has_time_spec) { + ss << "Time: " << time_spec.get_real_secs() << " s\n"; + } + if (more_fragments) { + ss << "Fragmentation offset: " << fragment_offset << "\n"; + } + if (start_of_burst) { + ss << "Start of burst.\n" << fragment_offset; + } + if (end_of_burst) { + ss << "End of burst.\n" << fragment_offset; + } + if (error_code != ERROR_CODE_NONE) { + ss << strerror() << "\n"; + } + } else { + ss << "Has timespec: " << (has_time_spec ? "Yes" : "No") + << "\tTime of first sample: " << time_spec.get_real_secs() + << "\nFragmented: " << (more_fragments ? "Yes" : "No") + << " Fragmentation offset: " << fragment_offset + << "\nStart of burst: " << (start_of_burst ? "Yes" : "No") + << "\tEnd of burst: " << (end_of_burst ? "Yes" : "No") + << "\nError Code: " << strerror() + << "\tOut of sequence: " << (out_of_sequence ? "Yes" : "No"); + } + + return ss.str(); +} + +std::string rx_metadata_t::strerror() const +{ + std::string errstr = ""; + switch(this->error_code) { + case ERROR_CODE_NONE: + errstr = "ERROR_CODE_NONE"; + break; + case ERROR_CODE_TIMEOUT: + errstr = "ERROR_CODE_TIMEOUT"; + break; + case ERROR_CODE_LATE_COMMAND: + errstr = "ERROR_CODE_LATE_COMMAND"; + break; + case ERROR_CODE_BROKEN_CHAIN: + errstr = "ERROR_CODE_BROKEN_CHAIN (Expected another stream command)"; + break; + case ERROR_CODE_OVERFLOW: + errstr = "ERROR_CODE_OVERFLOW "; + errstr += (this->out_of_sequence ? "(Out of sequence error)" : "(Overflow)"); + break; + case ERROR_CODE_ALIGNMENT: + errstr = "ERROR_CODE_ALIGNMENT (Multi-channel alignment failed)"; + break; + case ERROR_CODE_BAD_PACKET: + errstr = "ERROR_CODE_BAD_PACKET"; + break; + default: + errstr = std::string(str(boost::format("Unknown error code: 0x%x") % error_code)); + } + + return errstr; +} diff --git a/host/lib/usrp/b100/b100_impl.cpp b/host/lib/usrp/b100/b100_impl.cpp index a47856b07..baf2b6ae3 100644 --- a/host/lib/usrp/b100/b100_impl.cpp +++ b/host/lib/usrp/b100/b100_impl.cpp @@ -20,6 +20,7 @@ #include "b100_regs.hpp" #include <uhd/transport/usb_control.hpp> #include <uhd/utils/msg.hpp> +#include <uhd/utils/cast.hpp> #include <uhd/exception.hpp> #include <uhd/utils/static.hpp> #include <uhd/utils/images.hpp> @@ -56,11 +57,11 @@ static device_addrs_t b100_find(const device_addr_t &hint) //since an address and resource is intended for a different, non-USB, device. if (hint.has_key("addr") || hint.has_key("resource")) return b100_addrs; - unsigned int vid, pid; + boost::uint16_t vid, pid; if(hint.has_key("vid") && hint.has_key("pid") && hint.has_key("type") && hint["type"] == "b100") { - sscanf(hint.get("vid").c_str(), "%x", &vid); - sscanf(hint.get("pid").c_str(), "%x", &pid); + vid = uhd::cast::hexstr_cast<boost::uint16_t>(hint.get("vid")); + pid = uhd::cast::hexstr_cast<boost::uint16_t>(hint.get("pid")); } else { vid = B100_VENDOR_ID; pid = B100_PRODUCT_ID; @@ -515,6 +516,7 @@ b100_impl::b100_impl(const device_addr_t &device_addr){ _tree->access<subdev_spec_t>(mb_path / "tx_subdev_spec").set(subdev_spec_t("A:" + _tree->list(mb_path / "dboards/A/tx_frontends").at(0))); _tree->access<std::string>(mb_path / "clock_source/value").set("internal"); _tree->access<std::string>(mb_path / "time_source/value").set("none"); + _tree->create<double>(mb_path / "link_max_rate").set(B100_MAX_RATE_USB2); } b100_impl::~b100_impl(void){ diff --git a/host/lib/usrp/b100/b100_impl.hpp b/host/lib/usrp/b100/b100_impl.hpp index 7d71d5ec3..b6752681e 100644 --- a/host/lib/usrp/b100/b100_impl.hpp +++ b/host/lib/usrp/b100/b100_impl.hpp @@ -54,6 +54,7 @@ static const boost::uint32_t B100_CTRL_MSG_SID = 20; static const double B100_DEFAULT_TICK_RATE = 64e6; static const size_t B100_MAX_PKT_BYTE_LIMIT = 2048; static const std::string B100_EEPROM_MAP_KEY = "B100"; +static const size_t B100_MAX_RATE_USB2 = 32000000; // bytes/s #define I2C_ADDR_TX_A (I2C_DEV_EEPROM | 0x4) #define I2C_ADDR_RX_A (I2C_DEV_EEPROM | 0x5) diff --git a/host/lib/usrp/b200/b200_impl.cpp b/host/lib/usrp/b200/b200_impl.cpp index 9dd3a424d..98141dbaa 100644 --- a/host/lib/usrp/b200/b200_impl.cpp +++ b/host/lib/usrp/b200/b200_impl.cpp @@ -19,6 +19,7 @@ #include "b200_regs.hpp" #include <uhd/transport/usb_control.hpp> #include <uhd/utils/msg.hpp> +#include <uhd/utils/cast.hpp> #include <uhd/exception.hpp> #include <uhd/utils/static.hpp> #include <uhd/utils/images.hpp> @@ -58,11 +59,11 @@ static device_addrs_t b200_find(const device_addr_t &hint) //since an address and resource is intended for a different, non-USB, device. if (hint.has_key("addr") || hint.has_key("resource")) return b200_addrs; - unsigned int vid, pid; + boost::uint16_t vid, pid; if(hint.has_key("vid") && hint.has_key("pid") && hint.has_key("type") && hint["type"] == "b200") { - sscanf(hint.get("vid").c_str(), "%x", &vid); - sscanf(hint.get("pid").c_str(), "%x", &pid); + vid = uhd::cast::hexstr_cast<boost::uint16_t>(hint.get("vid")); + pid = uhd::cast::hexstr_cast<boost::uint16_t>(hint.get("pid")); } else { vid = B200_VENDOR_ID; pid = B200_PRODUCT_ID; @@ -160,9 +161,9 @@ b200_impl::b200_impl(const device_addr_t &device_addr) boost::uint16_t vid = B200_VENDOR_ID; boost::uint16_t pid = B200_PRODUCT_ID; if (device_addr.has_key("vid")) - sscanf(device_addr.get("vid").c_str(), "%hx", &vid); + vid = uhd::cast::hexstr_cast<boost::uint16_t>(device_addr.get("vid")); if (device_addr.has_key("pid")) - sscanf(device_addr.get("pid").c_str(), "%hx", &pid); + pid = uhd::cast::hexstr_cast<boost::uint16_t>(device_addr.get("pid")); std::vector<usb_device_handle::sptr> device_list = usb_device_handle::get_device_list(vid, pid); @@ -250,6 +251,7 @@ b200_impl::b200_impl(const device_addr_t &device_addr) ctrl_xport_args ); while (_ctrl_transport->get_recv_buff(0.0)){} //flush ctrl xport + _tree->create<double>(mb_path / "link_max_rate").set((usb_speed == 3) ? B200_MAX_RATE_USB3 : B200_MAX_RATE_USB2); //////////////////////////////////////////////////////////////////// // Async task structure diff --git a/host/lib/usrp/b200/b200_impl.hpp b/host/lib/usrp/b200/b200_impl.hpp index 7d98a8f8d..c3508c550 100644 --- a/host/lib/usrp/b200/b200_impl.hpp +++ b/host/lib/usrp/b200/b200_impl.hpp @@ -47,11 +47,13 @@ static const boost::uint8_t B200_FW_COMPAT_NUM_MAJOR = 0x04; static const boost::uint8_t B200_FW_COMPAT_NUM_MINOR = 0x00; static const boost::uint16_t B200_FPGA_COMPAT_NUM = 0x03; -static const double B200_LINK_RATE_BPS = (5e9)/8; //practical link rate (5 Gbps) static const double B200_BUS_CLOCK_RATE = 100e6; static const double B200_DEFAULT_TICK_RATE = 32e6; static const boost::uint32_t B200_GPSDO_ST_NONE = 0x83; +static const size_t B200_MAX_RATE_USB2 = 32000000; // bytes/s +static const size_t B200_MAX_RATE_USB3 = 500000000; // bytes/s + #define FLIP_SID(sid) (((sid)<<16)|((sid)>>16)) static const boost::uint32_t B200_CTRL0_MSG_SID = 0x00000010; diff --git a/host/lib/usrp/dboard/db_tvrx2.cpp b/host/lib/usrp/dboard/db_tvrx2.cpp index c593c5437..c74c64471 100644 --- a/host/lib/usrp/dboard/db_tvrx2.cpp +++ b/host/lib/usrp/dboard/db_tvrx2.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010,2012-2013 Ettus Research LLC +// Copyright 2010,2012-2014 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -1005,8 +1005,8 @@ tvrx2::tvrx2(ctor_args_t args) : rx_dboard_base(args){ _freq_scalar = (4*16.0e6)/(this->get_iface()->get_clock_rate(dboard_iface::UNIT_RX)); } else if (ref_clock == 100e6) { - - this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, REFCLOCK_DIV8); + + this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, REFCLOCK_DIV6); UHD_LOGV(often) << boost::format( "TVRX2 (%s): Dividing Refclock by 6" diff --git a/host/lib/usrp/multi_usrp.cpp b/host/lib/usrp/multi_usrp.cpp index f08709669..4883b2410 100644 --- a/host/lib/usrp/multi_usrp.cpp +++ b/host/lib/usrp/multi_usrp.cpp @@ -24,6 +24,7 @@ #include <uhd/usrp/dboard_id.hpp> #include <uhd/usrp/mboard_eeprom.hpp> #include <uhd/usrp/dboard_eeprom.hpp> +#include <uhd/convert.hpp> #include <boost/assign/list_of.hpp> #include <boost/thread.hpp> #include <boost/foreach.hpp> @@ -103,6 +104,8 @@ static meta_range_t make_overall_tune_range( return range; } + + /*********************************************************************** * Gain helper functions **********************************************************************/ @@ -589,6 +592,11 @@ public: /******************************************************************* * RX methods ******************************************************************/ + rx_streamer::sptr get_rx_stream(const stream_args_t &args) { + _check_link_rate(args, false); + return this->get_device()->get_rx_stream(args); + } + void set_rx_subdev_spec(const subdev_spec_t &spec, size_t mboard){ if (mboard != ALL_MBOARDS){ _tree->access<subdev_spec_t>(mb_root(mboard) / "rx_subdev_spec").set(spec); @@ -770,6 +778,11 @@ public: /******************************************************************* * TX methods ******************************************************************/ + tx_streamer::sptr get_tx_stream(const stream_args_t &args) { + _check_link_rate(args, true); + return this->get_device()->get_tx_stream(args); + } + void set_tx_subdev_spec(const subdev_spec_t &spec, size_t mboard){ if (mboard != ALL_MBOARDS){ _tree->access<subdev_spec_t>(mb_root(mboard) / "tx_subdev_spec").set(spec); @@ -1178,6 +1191,34 @@ private: } return gg; } + + //! \param is_tx True for tx + // Assumption is that all mboards use the same link + bool _check_link_rate(const stream_args_t &args, bool is_tx) { + bool link_rate_is_ok = true; + size_t bytes_per_sample = convert::get_bytes_per_item(args.otw_format); + double max_link_rate = 0; + double sum_rate = 0; + BOOST_FOREACH(const size_t chan, args.channels) { + mboard_chan_pair mcp = is_tx ? tx_chan_to_mcp(chan) : rx_chan_to_mcp(chan); + if (_tree->exists(mb_root(mcp.mboard) / "link_max_rate")) { + max_link_rate = std::max( + max_link_rate, + _tree->access<double>(mb_root(mcp.mboard) / "link_max_rate").get() + ); + } + sum_rate += is_tx ? get_tx_rate(chan) : get_rx_rate(chan); + } + if (max_link_rate > 0 and (max_link_rate / bytes_per_sample) < sum_rate) { + UHD_MSG(warning) << boost::format( + "The total sum of rates (%f MSps on %u channels) exceeds the maximum capacity of the connection.\n" + "This can cause %s." + ) % (sum_rate/1e6) % args.channels.size() % (is_tx ? "underruns (U)" : "overflows (O)") << std::endl; + link_rate_is_ok = false; + } + + return link_rate_is_ok; + } }; /*********************************************************************** diff --git a/host/lib/usrp/usrp1/usrp1_impl.cpp b/host/lib/usrp/usrp1/usrp1_impl.cpp index 3b902b343..0ba2e1e4a 100644 --- a/host/lib/usrp/usrp1/usrp1_impl.cpp +++ b/host/lib/usrp/usrp1/usrp1_impl.cpp @@ -20,6 +20,7 @@ #include <uhd/utils/safe_call.hpp> #include <uhd/transport/usb_control.hpp> #include <uhd/utils/msg.hpp> +#include <uhd/utils/cast.hpp> #include <uhd/exception.hpp> #include <uhd/utils/static.hpp> #include <uhd/utils/images.hpp> @@ -59,11 +60,11 @@ static device_addrs_t usrp1_find(const device_addr_t &hint) //since an address and resource is intended for a different, non-USB, device. if (hint.has_key("addr") || hint.has_key("resource")) return usrp1_addrs; - unsigned int vid, pid; + boost::uint16_t vid, pid; if(hint.has_key("vid") && hint.has_key("pid") && hint.has_key("type") && hint["type"] == "usrp1") { - sscanf(hint.get("vid").c_str(), "%x", &vid); - sscanf(hint.get("pid").c_str(), "%x", &pid); + vid = uhd::cast::hexstr_cast<boost::uint16_t>(hint.get("vid")); + pid = uhd::cast::hexstr_cast<boost::uint16_t>(hint.get("pid")); } else { vid = USRP1_VENDOR_ID; pid = USRP1_PRODUCT_ID; @@ -409,7 +410,7 @@ usrp1_impl::usrp1_impl(const device_addr_t &device_addr){ _tree->access<subdev_spec_t>(mb_path / "rx_subdev_spec").set(_rx_subdev_spec); if (_tree->list(mb_path / "tx_dsps").size() > 0) _tree->access<subdev_spec_t>(mb_path / "tx_subdev_spec").set(_tx_subdev_spec); - + _tree->create<double>(mb_path / "link_max_rate").set(USRP1_MAX_RATE_USB2); } usrp1_impl::~usrp1_impl(void){ diff --git a/host/lib/usrp/usrp1/usrp1_impl.hpp b/host/lib/usrp/usrp1/usrp1_impl.hpp index da9fe8b16..012bc0794 100644 --- a/host/lib/usrp/usrp1/usrp1_impl.hpp +++ b/host/lib/usrp/usrp1/usrp1_impl.hpp @@ -39,6 +39,7 @@ #define INCLUDED_USRP1_IMPL_HPP static const std::string USRP1_EEPROM_MAP_KEY = "B000"; +static const size_t USRP1_MAX_RATE_USB2 = 32000000; // bytes/s #define FR_RB_CAPS 3 #define FR_MODE 13 diff --git a/host/lib/usrp/usrp2/usrp2_impl.cpp b/host/lib/usrp/usrp2/usrp2_impl.cpp index 16d9b9a54..918f3e892 100644 --- a/host/lib/usrp/usrp2/usrp2_impl.cpp +++ b/host/lib/usrp/usrp2/usrp2_impl.cpp @@ -442,6 +442,7 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr){ _mbc[mb].spiface = _mbc[mb].iface; break; } + _tree->create<double>(mb_path / "link_max_rate").set(USRP2_LINK_RATE_BPS); //////////////////////////////////////////////////////////////// // setup the mboard eeprom @@ -655,12 +656,14 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr){ .subscribe(boost::bind(&time64_core_200::set_time_next_pps, _mbc[mb].time64, _1)); //setup time source props _tree->create<std::string>(mb_path / "time_source/value") - .subscribe(boost::bind(&time64_core_200::set_time_source, _mbc[mb].time64, _1)); + .subscribe(boost::bind(&time64_core_200::set_time_source, _mbc[mb].time64, _1)) + .set("none"); _tree->create<std::vector<std::string> >(mb_path / "time_source/options") .publish(boost::bind(&time64_core_200::get_time_sources, _mbc[mb].time64)); //setup reference source props _tree->create<std::string>(mb_path / "clock_source/value") - .subscribe(boost::bind(&usrp2_impl::update_clock_source, this, mb, _1)); + .subscribe(boost::bind(&usrp2_impl::update_clock_source, this, mb, _1)) + .set("internal"); std::vector<std::string> clock_sources = boost::assign::list_of("internal")("external")("mimo"); if (_mbc[mb].gps and _mbc[mb].gps->gps_detected()) clock_sources.push_back("gpsdo"); _tree->create<std::vector<std::string> >(mb_path / "clock_source/options").set(clock_sources); diff --git a/host/lib/usrp/x300/x300_impl.cpp b/host/lib/usrp/x300/x300_impl.cpp index e492b2238..f5e53678c 100644 --- a/host/lib/usrp/x300/x300_impl.cpp +++ b/host/lib/usrp/x300/x300_impl.cpp @@ -392,6 +392,8 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr) //Tell the quirks object which FIFOs carry TX stream data const uint32_t tx_data_fifos[2] = {X300_RADIO_DEST_PREFIX_TX, X300_RADIO_DEST_PREFIX_TX + 3}; mb.rio_fpga_interface->get_kernel_proxy().get_rio_quirks().register_tx_streams(tx_data_fifos); + + _tree->create<double>(mb_path / "link_max_rate").set(X300_MAX_RATE_PCIE); } BOOST_FOREACH(const std::string &key, dev_addr.keys()) @@ -456,6 +458,8 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr) << "UHD will use the auto-detected max frame size for this connection." << std::endl; } + + _tree->create<double>(mb_path / "link_max_rate").set(X300_MAX_RATE_10GIGE); } //create basic communication @@ -1133,11 +1137,14 @@ x300_impl::both_xports_t x300_impl::make_transport( if (mb.loaded_fpga_image == "HGS") { if (mb.router_dst_here == X300_XB_DST_E0) { eth_data_rec_frame_size = X300_1GE_DATA_FRAME_MAX_SIZE; + _tree->access<double>("/mboards/"+boost::lexical_cast<std::string>(mb_index) / "link_max_rate").set(X300_MAX_RATE_1GIGE); } else if (mb.router_dst_here == X300_XB_DST_E1) { eth_data_rec_frame_size = X300_10GE_DATA_FRAME_MAX_SIZE; + _tree->access<double>("/mboards/"+boost::lexical_cast<std::string>(mb_index) / "link_max_rate").set(X300_MAX_RATE_10GIGE); } } else if (mb.loaded_fpga_image == "XGS") { - eth_data_rec_frame_size = X300_10GE_DATA_FRAME_MAX_SIZE; + eth_data_rec_frame_size = X300_10GE_DATA_FRAME_MAX_SIZE; + _tree->access<double>("/mboards/"+boost::lexical_cast<std::string>(mb_index) / "link_max_rate").set(X300_MAX_RATE_10GIGE); } if (eth_data_rec_frame_size == 0) { diff --git a/host/lib/usrp/x300/x300_impl.hpp b/host/lib/usrp/x300/x300_impl.hpp index 692427f31..4b3efc845 100644 --- a/host/lib/usrp/x300/x300_impl.hpp +++ b/host/lib/usrp/x300/x300_impl.hpp @@ -85,6 +85,10 @@ static const size_t X300_RX_MAX_HDR_LEN = // bytes + sizeof(uhd::transport::vrt::if_packet_info_t().sid) // SID + sizeof(uhd::transport::vrt::if_packet_info_t().tsf); // Timestamp +static const size_t X300_MAX_RATE_PCIE = 800000000; // bytes/s +static const size_t X300_MAX_RATE_10GIGE = 800000000; // bytes/s +static const size_t X300_MAX_RATE_1GIGE = 100000000; // bytes/s + #define X300_RADIO_DEST_PREFIX_TX 0 #define X300_RADIO_DEST_PREFIX_CTRL 1 #define X300_RADIO_DEST_PREFIX_RX 2 diff --git a/host/lib/usrp/x300/x300_io_impl.cpp b/host/lib/usrp/x300/x300_io_impl.cpp index 09ed1d705..9263c9b44 100644 --- a/host/lib/usrp/x300/x300_io_impl.cpp +++ b/host/lib/usrp/x300/x300_io_impl.cpp @@ -242,6 +242,8 @@ struct x300_tx_fc_guts_t boost::shared_ptr<x300_impl::async_md_type> old_async_queue; }; +#define X300_ASYNC_EVENT_CODE_FLOW_CTRL 0 + static size_t get_tx_flow_control_window(size_t frame_size, const device_addr_t& tx_args) { double hw_buff_size = tx_args.cast<double>("send_buff_size", X300_TX_HW_BUFF_SIZE); @@ -283,23 +285,28 @@ static void handle_tx_async_msgs(boost::shared_ptr<x300_tx_fc_guts_t> guts, zero return; } - //catch the flow control packets and react - if (endian_conv(packet_buff[if_packet_info.num_header_words32+0]) == 0) - { - const size_t seq = endian_conv(packet_buff[if_packet_info.num_header_words32+1]); - guts->seq_queue.push_with_haste(seq); - return; - } - //fill in the async metadata async_metadata_t metadata; load_metadata_from_buff( endian_conv, metadata, if_packet_info, packet_buff, clock->get_master_clock_rate(), guts->stream_channel); - guts->async_queue->push_with_pop_on_full(metadata); - metadata.channel = guts->device_channel; - guts->old_async_queue->push_with_pop_on_full(metadata); - standard_async_msg_prints(metadata); + + //The FC response and the burst ack are two indicators that the radio + //consumed packets. Use them to update the FC metadata + if (metadata.event_code == X300_ASYNC_EVENT_CODE_FLOW_CTRL or + metadata.event_code == async_metadata_t::EVENT_CODE_BURST_ACK + ) { + const size_t seq = metadata.user_payload[0]; + guts->seq_queue.push_with_pop_on_full(seq); + } + + //FC responses don't propagate up to the user so filter them here + if (metadata.event_code != X300_ASYNC_EVENT_CODE_FLOW_CTRL) { + guts->async_queue->push_with_pop_on_full(metadata); + metadata.channel = guts->device_channel; + guts->old_async_queue->push_with_pop_on_full(metadata); + standard_async_msg_prints(metadata); + } } static managed_send_buffer::sptr get_tx_buff_with_flowctrl( @@ -319,7 +326,9 @@ static managed_send_buffer::sptr get_tx_buff_with_flowctrl( } managed_send_buffer::sptr buff = xport->get_send_buff(timeout); - if (buff) guts->last_seq_out++; //update seq, this will actually be a send + if (buff) { + guts->last_seq_out++; //update seq, this will actually be a send + } return buff; } diff --git a/host/tests/CMakeLists.txt b/host/tests/CMakeLists.txt index 2a40d0050..7c4815004 100644 --- a/host/tests/CMakeLists.txt +++ b/host/tests/CMakeLists.txt @@ -28,6 +28,7 @@ SET(test_sources buffer_test.cpp byteswap_test.cpp convert_test.cpp + cast_test.cpp dict_test.cpp error_test.cpp gain_group_test.cpp diff --git a/host/tests/cast_test.cpp b/host/tests/cast_test.cpp new file mode 100644 index 000000000..6b8a4c527 --- /dev/null +++ b/host/tests/cast_test.cpp @@ -0,0 +1,33 @@ +// +// Copyright 2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <iostream> +#include <boost/test/unit_test.hpp> +#include <boost/cstdint.hpp> +#include <uhd/utils/cast.hpp> + +BOOST_AUTO_TEST_CASE(test_mac_addr){ + std::string in = "0x0100"; + boost::uint16_t correct_result = 256; + boost::uint16_t x = uhd::cast::hexstr_cast<boost::uint16_t>(in); + //boost::uint16_t x = uhd::cast::hexstr_cast(in); + std::cout + << "Testing hex -> uint16_t conversion. " + << in << " == " << std::hex << x << "?" << std::endl; + BOOST_CHECK_EQUAL(x, correct_result); +} + diff --git a/host/utils/uhd_cal_rx_iq_balance.cpp b/host/utils/uhd_cal_rx_iq_balance.cpp index 551da7544..3188e02a0 100644 --- a/host/utils/uhd_cal_rx_iq_balance.cpp +++ b/host/utils/uhd_cal_rx_iq_balance.cpp @@ -121,37 +121,14 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ if (vm.count("help")){ std::cout << boost::format("USRP Generate RX IQ Balance Calibration Table %s") % desc << std::endl; std::cout << - "This application measures leakage between RX and TX on an XCVR daughterboard to self-calibrate.\n" + "This application measures leakage between RX and TX on a transceiver daughterboard to self-calibrate.\n" + "Note: Not all daughterboards support this feature. Refer to the UHD manual for details.\n" << std::endl; return EXIT_FAILURE; } - //create a usrp device - std::cout << std::endl; - std::cout << boost::format("Creating the usrp device with: %s...") % args << std::endl; - uhd::usrp::multi_usrp::sptr usrp = uhd::usrp::multi_usrp::make(args); - - // Configure subdev - if (vm.count("subdev")) { - usrp->set_tx_subdev_spec(subdev); - usrp->set_rx_subdev_spec(subdev); - } - UHD_MSG(status) << "Running calibration for " << usrp->get_tx_subdev_name(0) << std::endl; - serial = get_serial(usrp, "tx"); - UHD_MSG(status) << "Daughterboard serial: " << serial << std::endl; - - //set the antennas to cal - if (not uhd::has(usrp->get_rx_antennas(), "CAL") or not uhd::has(usrp->get_tx_antennas(), "CAL")){ - throw std::runtime_error("This board does not have the CAL antenna option, cannot self-calibrate."); - } - usrp->set_rx_antenna("CAL"); - usrp->set_tx_antenna("CAL"); - - //fail if daughterboard has no serial - check_for_empty_serial(usrp, "RX", "rx", args); - - //set optimum defaults - set_optimum_defaults(usrp); + // Create a USRP device + uhd::usrp::multi_usrp::sptr usrp = setup_usrp_for_cal(args, subdev, serial); //create a receive streamer uhd::stream_args_t stream_args("fc32"); //complex floats diff --git a/host/utils/uhd_cal_tx_dc_offset.cpp b/host/utils/uhd_cal_tx_dc_offset.cpp index eb82db826..b5c5293f0 100644 --- a/host/utils/uhd_cal_tx_dc_offset.cpp +++ b/host/utils/uhd_cal_tx_dc_offset.cpp @@ -123,37 +123,14 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ if (vm.count("help")){ std::cout << boost::format("USRP Generate TX DC Offset Calibration Table %s") % desc << std::endl; std::cout << - "This application measures leakage between RX and TX on an XCVR daughterboard to self-calibrate.\n" + "This application measures leakage between RX and TX on a transceiver daughterboard to self-calibrate.\n" + "Note: Not all daughterboards support this feature. Refer to the UHD manual for details.\n" << std::endl; return EXIT_FAILURE; } - //create a usrp device - std::cout << std::endl; - std::cout << boost::format("Creating the usrp device with: %s...") % args << std::endl; - uhd::usrp::multi_usrp::sptr usrp = uhd::usrp::multi_usrp::make(args); - - // Configure subdev - if (vm.count("subdev")) { - usrp->set_tx_subdev_spec(subdev); - usrp->set_rx_subdev_spec(subdev); - } - UHD_MSG(status) << "Running calibration for " << usrp->get_tx_subdev_name(0) << std::endl; - serial = get_serial(usrp, "tx"); - UHD_MSG(status) << "Daughterboard serial: " << serial << std::endl; - - //set the antennas to cal - if (not uhd::has(usrp->get_rx_antennas(), "CAL") or not uhd::has(usrp->get_tx_antennas(), "CAL")){ - throw std::runtime_error("This board does not have the CAL antenna option, cannot self-calibrate."); - } - usrp->set_rx_antenna("CAL"); - usrp->set_tx_antenna("CAL"); - - //fail if daughterboard has no serial - check_for_empty_serial(usrp, "TX", "tx", args); - - //set optimum defaults - set_optimum_defaults(usrp); + // Create a USRP device + uhd::usrp::multi_usrp::sptr usrp = setup_usrp_for_cal(args, subdev, serial); //create a receive streamer uhd::stream_args_t stream_args("fc32"); //complex floats diff --git a/host/utils/uhd_cal_tx_iq_balance.cpp b/host/utils/uhd_cal_tx_iq_balance.cpp index 786aac061..6461b3d71 100644 --- a/host/utils/uhd_cal_tx_iq_balance.cpp +++ b/host/utils/uhd_cal_tx_iq_balance.cpp @@ -18,12 +18,7 @@ #include "usrp_cal_utils.hpp" #include <uhd/utils/thread_priority.hpp> #include <uhd/utils/safe_main.hpp> -#include <uhd/utils/paths.hpp> -#include <uhd/utils/algorithm.hpp> -#include <uhd/utils/msg.hpp> -#include <uhd/usrp/multi_usrp.hpp> #include <boost/program_options.hpp> -#include <boost/format.hpp> #include <boost/thread/thread.hpp> #include <boost/math/special_functions/round.hpp> #include <iostream> @@ -124,37 +119,14 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ if (vm.count("help")){ std::cout << boost::format("USRP Generate TX IQ Balance Calibration Table %s") % desc << std::endl; std::cout << - "This application measures leakage between RX and TX on a daughterboard to self-calibrate.\n" + "This application measures leakage between RX and TX on a transceiver daughterboard to self-calibrate.\n" + "Note: Not all daughterboards support this feature. Refer to the UHD manual for details.\n" << std::endl; return EXIT_FAILURE; } - //create a usrp device - std::cout << std::endl; - std::cout << boost::format("Creating the usrp device with: %s...") % args << std::endl; - uhd::usrp::multi_usrp::sptr usrp = uhd::usrp::multi_usrp::make(args); - - // Configure subdev - if (vm.count("subdev")) { - usrp->set_tx_subdev_spec(subdev); - usrp->set_rx_subdev_spec(subdev); - } - UHD_MSG(status) << "Running calibration for " << usrp->get_tx_subdev_name(0) << std::endl; - serial = get_serial(usrp, "tx"); - UHD_MSG(status) << "Daughterboard serial: " << serial << std::endl; - - //set the antennas to cal - if (not uhd::has(usrp->get_rx_antennas(), "CAL") or not uhd::has(usrp->get_tx_antennas(), "CAL")){ - throw std::runtime_error("This board does not have the CAL antenna option, cannot self-calibrate."); - } - usrp->set_rx_antenna("CAL"); - usrp->set_tx_antenna("CAL"); - - //fail if daughterboard has no serial - check_for_empty_serial(usrp, "TX", "tx", args); - - //set optimum defaults - set_optimum_defaults(usrp); + // Create a USRP device + uhd::usrp::multi_usrp::sptr usrp = setup_usrp_for_cal(args, subdev, serial); //create a receive streamer uhd::stream_args_t stream_args("fc32"); //complex floats diff --git a/host/utils/usrp_burn_mb_eeprom.cpp b/host/utils/usrp_burn_mb_eeprom.cpp index ce0879c8e..c631c9c09 100644 --- a/host/utils/usrp_burn_mb_eeprom.cpp +++ b/host/utils/usrp_burn_mb_eeprom.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010,2013 Ettus Research LLC +// Copyright 2010,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 @@ -19,6 +19,7 @@ #include <uhd/device.hpp> #include <uhd/property_tree.hpp> #include <uhd/usrp/mboard_eeprom.hpp> +#include <uhd/types/device_addr.hpp> #include <boost/algorithm/string.hpp> #include <boost/program_options.hpp> #include <boost/format.hpp> @@ -28,14 +29,16 @@ namespace po = boost::program_options; int UHD_SAFE_MAIN(int argc, char *argv[]){ - std::string args, key, val; + std::string args, input_str, key, val; po::options_description desc("Allowed options"); desc.add_options() ("help", "help message") ("args", po::value<std::string>(&args)->default_value(""), "device address args [default = \"\"]") - ("key", po::value<std::string>(&key), "identifiers for new values in EEPROM, separate multiple by \",\"") - ("val", po::value<std::string>(&val), "the new values to set, omit for readback, separate multiple by \",\"") + ("values", po::value<std::string>(&input_str), "keys+values to read/write, separate multiple by \",\"") + ("key", po::value<std::string>(&key), "identifiers for new values in EEPROM, separate multiple by \",\" (DEPRECATED)") + ("val", po::value<std::string>(&val), "the new values to set, omit for readback, separate multiple by \",\" (DEPRECATED)") + ("read-all", "Read all motherboard EEPROM values without writing") ; po::variables_map vm; @@ -43,7 +46,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ po::notify(vm); //print the help message - if (vm.count("help") or not vm.count("key")){ + if (vm.count("help") or (not vm.count("key") and not vm.count("values") and not vm.count("read-all"))){ std::cout << boost::format("USRP Burn Motherboard EEPROM %s") % desc << std::endl; std::cout << boost::format( "Omit the value argument to perform a readback,\n" @@ -55,23 +58,33 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ std::cout << "Creating USRP device from address: " + args << std::endl; uhd::device::sptr dev = uhd::device::make(args); uhd::property_tree::sptr tree = dev->get_tree(); + uhd::usrp::mboard_eeprom_t mb_eeprom = tree->access<uhd::usrp::mboard_eeprom_t>("/mboards/0/eeprom").get(); std::cout << std::endl; - //remove whitespace, split arguments and values - boost::algorithm::erase_all(key, " "); - boost::algorithm::erase_all(val, " "); - std::vector<std::string> keys_vec, vals_vec; - boost::split(keys_vec, key, boost::is_any_of("\"',")); - boost::split(vals_vec, val, boost::is_any_of("\"',")); + if(vm.count("read-all")) keys_vec = mb_eeprom.keys(); //Leaving vals_vec empty will force utility to only read + else if(vm.count("values")){ + //uhd::device_addr_t properly parses input values + uhd::device_addr_t vals(input_str); + keys_vec = vals.keys(); + vals_vec = vals.vals(); + } + else{ + std::cout << "WARNING: Use of --key and --val is deprecated!" << std::endl; + //remove whitespace, split arguments and values + boost::algorithm::erase_all(key, " "); + boost::algorithm::erase_all(val, " "); + + boost::split(keys_vec, key, boost::is_any_of("\"',")); + boost::split(vals_vec, val, boost::is_any_of("\"',")); - if((keys_vec.size() != vals_vec.size()) and val != "") { - //If zero values are given, then user just wants values read to them - throw std::runtime_error("Number of keys must match number of values!"); + if((keys_vec.size() != vals_vec.size()) and val != "") { + //If zero values are given, then user just wants values read to them + throw std::runtime_error("Number of keys must match number of values!"); + } } std::cout << "Fetching current settings from EEPROM..." << std::endl; - uhd::usrp::mboard_eeprom_t mb_eeprom = tree->access<uhd::usrp::mboard_eeprom_t>("/mboards/0/eeprom").get(); for(size_t i = 0; i < keys_vec.size(); i++){ if (not mb_eeprom.has_key(keys_vec[i])){ std::cerr << boost::format("Cannot find value for EEPROM[%s]") % keys_vec[i] << std::endl; @@ -80,15 +93,15 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ std::cout << boost::format(" EEPROM [\"%s\"] is \"%s\"") % keys_vec[i] % mb_eeprom[keys_vec[i]] << std::endl; } std::cout << std::endl; - if (vm.count("val")){ - for(size_t i = 0; i < vals_vec.size(); i++){ + for(size_t i = 0; i < vals_vec.size(); i++){ + if(vals_vec[i] != ""){ uhd::usrp::mboard_eeprom_t mb_eeprom; mb_eeprom[keys_vec[i]] = vals_vec[i]; std::cout << boost::format("Setting EEPROM [\"%s\"] to \"%s\"...") % keys_vec[i] % vals_vec[i] << std::endl; tree->access<uhd::usrp::mboard_eeprom_t>("/mboards/0/eeprom").set(mb_eeprom); } - std::cout << "Power-cycle the USRP device for the changes to take effect." << std::endl; - std::cout << std::endl; } + std::cout << "Power-cycle the USRP device for the changes to take effect." << std::endl; + std::cout << std::endl; std::cout << "Done" << std::endl; return EXIT_SUCCESS; diff --git a/host/utils/usrp_cal_utils.hpp b/host/utils/usrp_cal_utils.hpp index 5aff5e22f..9e7f4c469 100644 --- a/host/utils/usrp_cal_utils.hpp +++ b/host/utils/usrp_cal_utils.hpp @@ -20,6 +20,8 @@ #include <uhd/usrp/multi_usrp.hpp> #include <uhd/usrp/dboard_eeprom.hpp> #include <uhd/utils/paths.hpp> +#include <uhd/utils/algorithm.hpp> +#include <uhd/utils/msg.hpp> #include <boost/filesystem.hpp> #include <boost/format.hpp> #include <iostream> @@ -50,6 +52,8 @@ static const size_t default_num_samps = 10000; **********************************************************************/ static inline void set_optimum_defaults(uhd::usrp::multi_usrp::sptr usrp){ uhd::property_tree::sptr tree = usrp->get_device()->get_tree(); + // Will work on 1st subdev, top-level must make sure it's the right one + uhd::usrp::subdev_spec_t subdev_spec = usrp->get_rx_subdev_spec(); const uhd::fs_path mb_path = "/mboards/0"; const std::string mb_name = tree->access<std::string>(mb_path / "name").get(); @@ -69,7 +73,7 @@ static inline void set_optimum_defaults(uhd::usrp::multi_usrp::sptr usrp){ throw std::runtime_error("self-calibration is not supported for this hardware"); } - const uhd::fs_path tx_fe_path = "/mboards/0/dboards/A/tx_frontends/0"; + const uhd::fs_path tx_fe_path = "/mboards/0/dboards/" + subdev_spec[0].db_name + "/tx_frontends/0"; const std::string tx_name = tree->access<std::string>(tx_fe_path / "name").get(); if (tx_name.find("WBX") != std::string::npos){ usrp->set_tx_gain(0); @@ -87,7 +91,7 @@ static inline void set_optimum_defaults(uhd::usrp::multi_usrp::sptr usrp){ throw std::runtime_error("self-calibration is not supported for this hardware"); } - const uhd::fs_path rx_fe_path = "/mboards/0/dboards/A/rx_frontends/0"; + const uhd::fs_path rx_fe_path = "/mboards/0/dboards/" + subdev_spec[0].db_name + "/rx_frontends/0"; const std::string rx_name = tree->access<std::string>(rx_fe_path / "name").get(); if (rx_name.find("WBX") != std::string::npos){ usrp->set_rx_gain(25); @@ -110,24 +114,19 @@ static inline void set_optimum_defaults(uhd::usrp::multi_usrp::sptr usrp){ /*********************************************************************** * Check for empty serial **********************************************************************/ - void check_for_empty_serial( - uhd::usrp::multi_usrp::sptr usrp, - std::string XX, - std::string xx, - std::string uhd_args + uhd::usrp::multi_usrp::sptr usrp ){ + // Will work on 1st subdev, top-level must make sure it's the right one + uhd::usrp::subdev_spec_t subdev_spec = usrp->get_rx_subdev_spec(); //extract eeprom uhd::property_tree::sptr tree = usrp->get_device()->get_tree(); - const uhd::fs_path db_path = "/mboards/0/dboards/A/" + xx + "_eeprom"; + // This only works with transceiver boards, so we can always check rx side + const uhd::fs_path db_path = "/mboards/0/dboards/" + subdev_spec[0].db_name + "/rx_eeprom"; const uhd::usrp::dboard_eeprom_t db_eeprom = tree->access<uhd::usrp::dboard_eeprom_t>(db_path).get(); - std::string args_str = ""; - if(uhd_args != "") args_str = str(boost::format(" --args=%s") % uhd_args); - - std::string error_string = str(boost::format("This %s dboard has no serial!\n\nPlease see the Calibration documentation for details on how to fix this.") % XX); - + std::string error_string = "This dboard has no serial!\n\nPlease see the Calibration documentation for details on how to fix this."; if (db_eeprom.serial.empty()) throw std::runtime_error(error_string); } @@ -188,6 +187,7 @@ static std::string get_serial( const std::string &tx_rx ){ uhd::property_tree::sptr tree = usrp->get_device()->get_tree(); + // Will work on 1st subdev, top-level must make sure it's the right one uhd::usrp::subdev_spec_t subdev_spec = usrp->get_rx_subdev_spec(); const uhd::fs_path db_path = "/mboards/0/dboards/" + subdev_spec[0].db_name + "/" + tx_rx + "_eeprom"; const uhd::usrp::dboard_eeprom_t db_eeprom = tree->access<uhd::usrp::dboard_eeprom_t>(db_path).get(); @@ -257,8 +257,8 @@ static void capture_samples( //validate the received data if (md.error_code != uhd::rx_metadata_t::ERROR_CODE_NONE){ throw std::runtime_error(str(boost::format( - "Unexpected error code 0x%x" - ) % md.error_code)); + "Receiver error: %s" + ) % md.strerror())); } //we can live if all the data didnt come in if (num_rx_samps > buff.size()/2){ @@ -270,3 +270,37 @@ static void capture_samples( } } +/*********************************************************************** + * Setup function + **********************************************************************/ +static uhd::usrp::multi_usrp::sptr setup_usrp_for_cal(std::string &args, std::string &subdev, std::string &serial) +{ + std::cout << std::endl; + std::cout << boost::format("Creating the usrp device with: %s...") % args << std::endl; + uhd::usrp::multi_usrp::sptr usrp = uhd::usrp::multi_usrp::make(args); + + // Configure subdev + if (!subdev.empty()) { + usrp->set_tx_subdev_spec(subdev); + usrp->set_rx_subdev_spec(subdev); + } + UHD_MSG(status) << "Running calibration for " << usrp->get_tx_subdev_name(0) << std::endl; + serial = get_serial(usrp, "tx"); + UHD_MSG(status) << "Daughterboard serial: " << serial << std::endl; + + //set the antennas to cal + if (not uhd::has(usrp->get_rx_antennas(), "CAL") or not uhd::has(usrp->get_tx_antennas(), "CAL")){ + throw std::runtime_error("This board does not have the CAL antenna option, cannot self-calibrate."); + } + usrp->set_rx_antenna("CAL"); + usrp->set_tx_antenna("CAL"); + + //fail if daughterboard has no serial + check_for_empty_serial(usrp); + + //set optimum defaults + set_optimum_defaults(usrp); + + return usrp; +} + diff --git a/host/utils/usrp_n2xx_simple_net_burner.cpp b/host/utils/usrp_n2xx_simple_net_burner.cpp index 277e807d9..cecac5588 100644 --- a/host/utils/usrp_n2xx_simple_net_burner.cpp +++ b/host/utils/usrp_n2xx_simple_net_burner.cpp @@ -17,13 +17,13 @@ #include <csignal> #include <iostream> -#include <map> #include <fstream> #include <time.h> #include <vector> #include <boost/foreach.hpp> #include <boost/asio.hpp> +#include <boost/filesystem.hpp> #include <boost/program_options.hpp> #include <boost/assign.hpp> #include <boost/assign/list_of.hpp> @@ -32,21 +32,97 @@ #include <boost/filesystem.hpp> #include <boost/thread/thread.hpp> -#include "usrp_simple_burner_utils.hpp" #include <uhd/exception.hpp> #include <uhd/property_tree.hpp> #include <uhd/transport/if_addrs.hpp> #include <uhd/transport/udp_simple.hpp> +#include <uhd/types/dict.hpp> #include <uhd/utils/byteswap.hpp> #include <uhd/utils/images.hpp> #include <uhd/utils/safe_main.hpp> #include <uhd/utils/safe_call.hpp> +namespace fs = boost::filesystem; namespace po = boost::program_options; using namespace boost::algorithm; using namespace uhd; using namespace uhd::transport; +#define UDP_FW_UPDATE_PORT 49154 +#define UDP_MAX_XFER_BYTES 1024 +#define UDP_TIMEOUT 3 +#define UDP_POLL_INTERVAL 0.10 //in seconds +#define USRP2_FW_PROTO_VERSION 7 //should be unused after r6 +#define USRP2_UDP_UPDATE_PORT 49154 +#define FLASH_DATA_PACKET_SIZE 256 +#define FPGA_IMAGE_SIZE_BYTES 1572864 +#define FW_IMAGE_SIZE_BYTES 31744 +#define PROD_FPGA_IMAGE_LOCATION_ADDR 0x00180000 +#define PROD_FW_IMAGE_LOCATION_ADDR 0x00300000 +#define SAFE_FPGA_IMAGE_LOCATION_ADDR 0x00000000 +#define SAFE_FW_IMAGE_LOCATION_ADDR 0x003F0000 + +typedef enum { + UNKNOWN = ' ', + + USRP2_QUERY = 'a', + USRP2_ACK = 'A', + + GET_FLASH_INFO_CMD = 'f', + GET_FLASH_INFO_ACK = 'F', + + ERASE_FLASH_CMD = 'e', + ERASE_FLASH_ACK = 'E', + + CHECK_ERASING_DONE_CMD = 'd', + DONE_ERASING_ACK = 'D', + NOT_DONE_ERASING_ACK = 'B', + + WRITE_FLASH_CMD = 'w', + WRITE_FLASH_ACK = 'W', + + READ_FLASH_CMD = 'r', + READ_FLASH_ACK = 'R', + + RESET_USRP_CMD = 's', + RESET_USRP_ACK = 'S', + + GET_HW_REV_CMD = 'v', + GET_HW_REV_ACK = 'V', + +} usrp2_fw_update_id_t; + +typedef struct { + uint32_t proto_ver; + uint32_t id; + uint32_t seq; + union { + uint32_t ip_addr; + uint32_t hw_rev; + struct { + uint32_t flash_addr; + uint32_t length; + uint8_t data[256]; + } flash_args; + struct { + uint32_t sector_size_bytes; + uint32_t memory_size_bytes; + } flash_info_args; + } data; +} usrp2_fw_update_data_t; + +//Mapping revision numbers to filenames +uhd::dict<boost::uint32_t, std::string> filename_map = boost::assign::map_list_of + (0xa, "n200_r3") + (0x100a, "n200_r4") + (0x10a, "n210_r3") + (0x110a, "n210_r4") +; + +boost::uint8_t usrp2_update_data_in_mem[udp_simple::mtu]; +boost::uint8_t fpga_image[FPGA_IMAGE_SIZE_BYTES]; +boost::uint8_t fw_image[FW_IMAGE_SIZE_BYTES]; + /*********************************************************************** * Signal handlers **********************************************************************/ @@ -66,59 +142,94 @@ void sig_int_handler(int){ } } -//Mapping revision numbers to filenames -std::map<boost::uint32_t, std::string> filename_map = boost::assign::map_list_of - (0xa, "n200_r3") - (0x100a, "n200_r4") - (0x10a, "n210_r3") - (0x110a, "n210_r4") -; +/*********************************************************************** + * List all connected USRP N2XX devices + **********************************************************************/ +void list_usrps(){ + udp_simple::sptr udp_bc_transport; + const usrp2_fw_update_data_t *update_data_in = reinterpret_cast<const usrp2_fw_update_data_t *>(usrp2_update_data_in_mem); + boost::uint32_t hw_rev; -//Images and image sizes, to be populated as necessary -boost::uint8_t fpga_image[FPGA_IMAGE_SIZE_BYTES]; -boost::uint8_t fw_image[FW_IMAGE_SIZE_BYTES]; -int fpga_image_size = 0; -int fw_image_size = 0; + usrp2_fw_update_data_t usrp2_ack_pkt = usrp2_fw_update_data_t(); + usrp2_ack_pkt.proto_ver = htonx<boost::uint32_t>(USRP2_FW_PROTO_VERSION); + usrp2_ack_pkt.id = htonx<boost::uint32_t>(USRP2_QUERY); + + std::cout << "Available USRP N2XX devices:" << std::endl; -//For non-standard images not covered by uhd::find_image_path() -bool does_image_exist(std::string image_filepath){ + //Send UDP packets to all broadcast addresses + BOOST_FOREACH(const if_addrs_t &if_addrs, get_if_addrs()){ + //Avoid the loopback device + if(if_addrs.inet == boost::asio::ip::address_v4::loopback().to_string()) continue; + udp_bc_transport = udp_simple::make_broadcast(if_addrs.bcast, BOOST_STRINGIZE(USRP2_UDP_UPDATE_PORT)); + udp_bc_transport->send(boost::asio::buffer(&usrp2_ack_pkt, sizeof(usrp2_ack_pkt))); - std::ifstream ifile((char*)image_filepath.c_str()); - return ifile; + size_t len = udp_bc_transport->recv(boost::asio::buffer(usrp2_update_data_in_mem), UDP_TIMEOUT); + if(len > offsetof(usrp2_fw_update_data_t, data) and ntohl(update_data_in->id) == USRP2_ACK){ + usrp2_ack_pkt.id = htonx<boost::uint32_t>(GET_HW_REV_CMD); + udp_bc_transport->send(boost::asio::buffer(&usrp2_ack_pkt, sizeof(usrp2_ack_pkt))); + + size_t len = udp_bc_transport->recv(boost::asio::buffer(usrp2_update_data_in_mem), UDP_TIMEOUT); + if(len > offsetof(usrp2_fw_update_data_t, data) and ntohl(update_data_in->id) == GET_HW_REV_ACK){ + hw_rev = ntohl(update_data_in->data.hw_rev); + } + + std::cout << boost::format(" * %s (%s)\n") % udp_bc_transport->get_recv_addr() % filename_map[hw_rev]; + } + } +} + +/*********************************************************************** + * Find USRP N2XX with specified IP address and return type + **********************************************************************/ +boost::uint32_t find_usrp(udp_simple::sptr udp_transport){ + boost::uint32_t hw_rev; + bool found_it = false; + + const usrp2_fw_update_data_t *update_data_in = reinterpret_cast<const usrp2_fw_update_data_t *>(usrp2_update_data_in_mem); + usrp2_fw_update_data_t hw_info_pkt = usrp2_fw_update_data_t(); + hw_info_pkt.proto_ver = htonx<boost::uint32_t>(USRP2_FW_PROTO_VERSION); + hw_info_pkt.id = htonx<boost::uint32_t>(GET_HW_REV_CMD); + udp_transport->send(boost::asio::buffer(&hw_info_pkt, sizeof(hw_info_pkt))); + + //Loop and receive until the timeout + size_t len = udp_transport->recv(boost::asio::buffer(usrp2_update_data_in_mem), UDP_TIMEOUT); + if(len > offsetof(usrp2_fw_update_data_t, data) and ntohl(update_data_in->id) == GET_HW_REV_ACK){ + hw_rev = ntohl(update_data_in->data.hw_rev); + if(filename_map.has_key(hw_rev)){ + std::cout << boost::format("Found %s.\n\n") % filename_map[hw_rev]; + found_it = true; + } + else throw std::runtime_error("Invalid revision found."); + } + if(not found_it) throw std::runtime_error("No USRP N2XX found."); + + return hw_rev; } /*********************************************************************** * Custom filename validation functions **********************************************************************/ -void validate_custom_fpga_file(std::string rev_str, std::string fpga_path){ +void validate_custom_fpga_file(std::string rev_str, std::string& fpga_path){ //Check for existence of file - if(!does_image_exist(fpga_path)) throw std::runtime_error(str(boost::format("No file at specified FPGA path: %s") % fpga_path)); + if(not fs::exists(fpga_path)) throw std::runtime_error(str(boost::format("No file at specified FPGA path: %s") % fpga_path)); //Check to find rev_str in filename uhd::fs_path custom_fpga_path(fpga_path); - if(custom_fpga_path.leaf().find("fw") != std::string::npos){ - throw std::runtime_error(str(boost::format("Invalid FPGA image filename at path: %s\nFilename indicates that this is a firmware image.") - % fpga_path)); - } if(custom_fpga_path.leaf().find(rev_str) == std::string::npos){ throw std::runtime_error(str(boost::format("Invalid FPGA image filename at path: %s\nFilename must contain '%s' to be considered valid for this model.") % fpga_path % rev_str)); } } -void validate_custom_fw_file(std::string rev_str, std::string fw_path){ +void validate_custom_fw_file(std::string rev_str, std::string& fw_path){ //Check for existence of file - if(!does_image_exist(fw_path)) throw std::runtime_error(str(boost::format("No file at specified firmware path: %s") % fw_path)); + if(not fs::exists(fw_path)) throw std::runtime_error(str(boost::format("No file at specified firmware path: %s") % fw_path)); //Check to find truncated rev_str in filename uhd::fs_path custom_fw_path(fw_path); - if(custom_fw_path.leaf().find("fpga") != std::string::npos){ - throw std::runtime_error(str(boost::format("Invalid firmware image filename at path: %s\nFilename indicates that this is an FPGA image.") - % fw_path)); - } if(custom_fw_path.leaf().find(erase_tail_copy(rev_str,3)) == std::string::npos){ throw std::runtime_error(str(boost::format("Invalid firmware image filename at path: %s\nFilename must contain '%s' to be considered valid for this model.") % fw_path % erase_tail_copy(rev_str,3))); @@ -126,89 +237,91 @@ void validate_custom_fw_file(std::string rev_str, std::string fw_path){ } /*********************************************************************** - * Grabbing and validating image binaries + * Reading and validating image binaries **********************************************************************/ -int grab_fpga_image(std::string fpga_path){ +int read_fpga_image(std::string& fpga_path){ - //Reading FPGA image from file - std::ifstream to_read_fpga((char*)fpga_path.c_str(), std::ios::binary); - to_read_fpga.seekg(0, std::ios::end); - fpga_image_size = to_read_fpga.tellg(); - to_read_fpga.seekg(0, std::ios::beg); - char fpga_read[FPGA_IMAGE_SIZE_BYTES]; - to_read_fpga.read(fpga_read,fpga_image_size); - to_read_fpga.close(); - for(int i = 0; i < fpga_image_size; i++) fpga_image[i] = (boost::uint8_t)fpga_read[i]; - - //Checking validity of image + //Check size of given image + std::ifstream fpga_file(fpga_path.c_str(), std::ios::binary); + fpga_file.seekg(0, std::ios::end); + int fpga_image_size = fpga_file.tellg(); if(fpga_image_size > FPGA_IMAGE_SIZE_BYTES){ - throw std::runtime_error(str(boost::format("FPGA image is too large. %d > %d") % fpga_image_size % FPGA_IMAGE_SIZE_BYTES)); + throw std::runtime_error(str(boost::format("FPGA image is too large. %d > %d") + % fpga_image_size % FPGA_IMAGE_SIZE_BYTES)); } - //Check sequence of bytes in image + //Check sequence of bytes in image before reading + boost::uint8_t fpga_test_bytes[63]; + fpga_file.seekg(0, std::ios::beg); + fpga_file.read((char*)fpga_test_bytes,63); bool is_good = false; for(int i = 0; i < 63; i++){ - if((boost::uint8_t)fpga_image[i] == 255) continue; - else if((boost::uint8_t)fpga_image[i] == 170 and - (boost::uint8_t)fpga_image[i+1] == 153){ + if(fpga_test_bytes[i] == 255) continue; + else if(fpga_test_bytes[i] == 170 and + fpga_test_bytes[i+1] == 153){ is_good = true; break; } } + if(not is_good) throw std::runtime_error("Not a valid FPGA image."); - if(!is_good) throw std::runtime_error("Not a valid FPGA image."); + //With image validated, read into utility + fpga_file.seekg(0, std::ios::beg); + fpga_file.read((char*)fpga_image,fpga_image_size); + fpga_file.close(); //Return image size return fpga_image_size; } -int grab_fw_image(std::string fw_path){ - - //Reading firmware image from file - std::ifstream to_read_fw((char*)fw_path.c_str(), std::ios::binary); - to_read_fw.seekg(0, std::ios::end); - fw_image_size = to_read_fw.tellg(); - to_read_fw.seekg(0, std::ios::beg); - char fw_read[FW_IMAGE_SIZE_BYTES]; - to_read_fw.read(fw_read,fw_image_size); - to_read_fw.close(); - for(int i = 0; i < fw_image_size; i++) fw_image[i] = (boost::uint8_t)fw_read[i]; +int read_fw_image(std::string& fw_path){ - //Checking validity of image + //Check size of given image + std::ifstream fw_file(fw_path.c_str(), std::ios::binary); + fw_file.seekg(0, std::ios::end); + int fw_image_size = fw_file.tellg(); if(fw_image_size > FW_IMAGE_SIZE_BYTES){ - throw std::runtime_error(str(boost::format("Firmware image is too large. %d > %d") % fw_image_size % FW_IMAGE_SIZE_BYTES)); + throw std::runtime_error(str(boost::format("Firmware image is too large. %d > %d") + % fw_image_size % FW_IMAGE_SIZE_BYTES)); } - //Check first four bytes of image - for(int i = 0; i < 4; i++) if((boost::uint8_t)fw_image[i] != 11) throw std::runtime_error("Not a valid firmware image."); + //Check sequence of bytes in image before reading + boost::uint8_t fw_test_bytes[4]; + fw_file.seekg(0, std::ios::beg); + fw_file.read((char*)fw_test_bytes,4); + for(int i = 0; i < 4; i++) if(fw_test_bytes[i] != 11) throw std::runtime_error("Not a valid firmware image."); + + //With image validated, read into utility + fw_file.seekg(0, std::ios::beg); + fw_file.read((char*)fw_image,fw_image_size); + fw_file.close(); - //Return image size return fw_image_size; } -boost::uint32_t* get_flash_info(std::string ip_addr){ +boost::uint32_t* get_flash_info(std::string& ip_addr){ boost::uint32_t *flash_info = new boost::uint32_t[2]; - boost::uint8_t usrp2_update_data_in_mem[udp_simple::mtu]; const usrp2_fw_update_data_t *update_data_in = reinterpret_cast<const usrp2_fw_update_data_t *>(usrp2_update_data_in_mem); udp_simple::sptr udp_transport = udp_simple::make_connected(ip_addr, BOOST_STRINGIZE(USRP2_UDP_UPDATE_PORT)); usrp2_fw_update_data_t get_flash_info_pkt = usrp2_fw_update_data_t(); get_flash_info_pkt.proto_ver = htonx<boost::uint32_t>(USRP2_FW_PROTO_VERSION); - get_flash_info_pkt.id = htonx<boost::uint32_t>(USRP2_FW_UPDATE_ID_WATS_TEH_FLASH_INFO_LOL); + get_flash_info_pkt.id = htonx<boost::uint32_t>(GET_FLASH_INFO_CMD); udp_transport->send(boost::asio::buffer(&get_flash_info_pkt, sizeof(get_flash_info_pkt))); //Loop and receive until the timeout size_t len = udp_transport->recv(boost::asio::buffer(usrp2_update_data_in_mem), UDP_TIMEOUT); - if(len > offsetof(usrp2_fw_update_data_t, data) and ntohl(update_data_in->id) == USRP2_FW_UPDATE_ID_HERES_TEH_FLASH_INFO_OMG){ + if(len > offsetof(usrp2_fw_update_data_t, data) and ntohl(update_data_in->id) == GET_FLASH_INFO_ACK){ flash_info[0] = ntohl(update_data_in->data.flash_info_args.sector_size_bytes); flash_info[1] = ntohl(update_data_in->data.flash_info_args.memory_size_bytes); } - else if(ntohl(update_data_in->id) != USRP2_FW_UPDATE_ID_HERES_TEH_FLASH_INFO_OMG){ - throw std::runtime_error(str(boost::format("Received invalid reply %d from device.\n") % ntohl(update_data_in->id))); + else if(ntohl(update_data_in->id) != GET_FLASH_INFO_ACK){ + throw std::runtime_error(str(boost::format("Received invalid reply %d from device.\n") + % ntohl(update_data_in->id))); } - + return flash_info; } @@ -218,102 +331,100 @@ boost::uint32_t* get_flash_info(std::string ip_addr){ void erase_image(udp_simple::sptr udp_transport, bool is_fw, boost::uint32_t memory_size){ + boost::uint32_t image_location_addr = is_fw ? PROD_FW_IMAGE_LOCATION_ADDR + : PROD_FPGA_IMAGE_LOCATION_ADDR; + boost::uint32_t image_size = is_fw ? FW_IMAGE_SIZE_BYTES + : FPGA_IMAGE_SIZE_BYTES; + //Making sure this won't attempt to erase past end of device - if(is_fw){ - if(PROD_FW_IMAGE_LOCATION_ADDR+FW_IMAGE_SIZE_BYTES > memory_size) throw std::runtime_error("Cannot erase past end of device."); - } - else{ - if(PROD_FPGA_IMAGE_LOCATION_ADDR+FPGA_IMAGE_SIZE_BYTES > memory_size) throw std::runtime_error("Cannot erase past end of device."); - } + if((image_location_addr+image_size) > memory_size) throw std::runtime_error("Cannot erase past end of device."); - //Setting up UDP transport - boost::uint8_t usrp2_update_data_in_mem[udp_simple::mtu]; + //UDP receive buffer const usrp2_fw_update_data_t *update_data_in = reinterpret_cast<const usrp2_fw_update_data_t *>(usrp2_update_data_in_mem); //Setting up UDP packet usrp2_fw_update_data_t erase_pkt = usrp2_fw_update_data_t(); - erase_pkt.id = htonx<boost::uint32_t>(USRP2_FW_UPDATE_ID_ERASE_TEH_FLASHES_LOL); + erase_pkt.id = htonx<boost::uint32_t>(ERASE_FLASH_CMD); erase_pkt.proto_ver = htonx<boost::uint32_t>(USRP2_FW_PROTO_VERSION); - if(is_fw){ - erase_pkt.data.flash_args.flash_addr = htonx<boost::uint32_t>(PROD_FW_IMAGE_LOCATION_ADDR); - erase_pkt.data.flash_args.length = htonx<boost::uint32_t>(FW_IMAGE_SIZE_BYTES); - } - else{ - erase_pkt.data.flash_args.flash_addr = htonx<boost::uint32_t>(PROD_FPGA_IMAGE_LOCATION_ADDR); - erase_pkt.data.flash_args.length = htonx<boost::uint32_t>(FPGA_IMAGE_SIZE_BYTES); - } + erase_pkt.data.flash_args.flash_addr = htonx<boost::uint32_t>(image_location_addr); + erase_pkt.data.flash_args.length = htonx<boost::uint32_t>(image_size); //Begin erasing udp_transport->send(boost::asio::buffer(&erase_pkt, sizeof(erase_pkt))); size_t len = udp_transport->recv(boost::asio::buffer(usrp2_update_data_in_mem), UDP_TIMEOUT); - if(len > offsetof(usrp2_fw_update_data_t, data) and ntohl(update_data_in->id) == USRP2_FW_UPDATE_ID_ERASING_TEH_FLASHES_OMG){ + if(len > offsetof(usrp2_fw_update_data_t, data) and ntohl(update_data_in->id) == ERASE_FLASH_ACK){ if(is_fw) std::cout << "Erasing firmware image." << std::endl; else std::cout << "Erasing FPGA image." << std::endl; } - else if(ntohl(update_data_in->id) != USRP2_FW_UPDATE_ID_ERASING_TEH_FLASHES_OMG){ - throw std::runtime_error(str(boost::format("Received invalid reply %d from device.\n") % ntohl(update_data_in->id))); + else if(ntohl(update_data_in->id) != ERASE_FLASH_ACK){ + throw std::runtime_error(str(boost::format("Received invalid reply %d from device.\n") + % ntohl(update_data_in->id))); } //Check for erase completion - erase_pkt.id = htonx<boost::uint32_t>(USRP2_FW_UPDATE_ID_R_U_DONE_ERASING_LOL); + erase_pkt.id = htonx<boost::uint32_t>(CHECK_ERASING_DONE_CMD); while(true){ udp_transport->send(boost::asio::buffer(&erase_pkt, sizeof(erase_pkt))); size_t len = udp_transport->recv(boost::asio::buffer(usrp2_update_data_in_mem), UDP_TIMEOUT); - if(len > offsetof(usrp2_fw_update_data_t, data) and ntohl(update_data_in->id) == USRP2_FW_UPDATE_ID_IM_DONE_ERASING_OMG){ - if(is_fw) std::cout << boost::format(" * Successfully erased %d bytes at %d.\n") % FW_IMAGE_SIZE_BYTES % PROD_FW_IMAGE_LOCATION_ADDR; - else std::cout << boost::format(" * Successfully erased %d bytes at %d.\n") % FPGA_IMAGE_SIZE_BYTES % PROD_FPGA_IMAGE_LOCATION_ADDR; + if(len > offsetof(usrp2_fw_update_data_t, data) and ntohl(update_data_in->id) == DONE_ERASING_ACK){ + std::cout << boost::format(" * Successfully erased %d bytes at %d.\n") + % image_size % image_location_addr; break; } - else if(ntohl(update_data_in->id) != USRP2_FW_UPDATE_ID_NOPE_NOT_DONE_ERASING_OMG){ - throw std::runtime_error(str(boost::format("Received invalid reply %d from device.\n") % ntohl(update_data_in->id))); + else if(ntohl(update_data_in->id) != NOT_DONE_ERASING_ACK){ + throw std::runtime_error(str(boost::format("Received invalid reply %d from device.\n") + % ntohl(update_data_in->id))); } } } void write_image(udp_simple::sptr udp_transport, bool is_fw, boost::uint8_t* image, boost::uint32_t memory_size, int image_size){ - boost::uint32_t current_addr; - if(is_fw) current_addr = PROD_FW_IMAGE_LOCATION_ADDR; - else current_addr = PROD_FPGA_IMAGE_LOCATION_ADDR; + boost::uint32_t begin_addr = is_fw ? PROD_FW_IMAGE_LOCATION_ADDR + : PROD_FPGA_IMAGE_LOCATION_ADDR; + boost::uint32_t current_addr = begin_addr; + std::string type = is_fw ? "firmware" : "FPGA"; //Making sure this won't attempt to write past end of device if(current_addr+image_size > memory_size) throw std::runtime_error("Cannot write past end of device."); - //Setting up UDP transport - boost::uint8_t usrp2_update_data_in_mem[udp_simple::mtu]; + //UDP receive buffer const usrp2_fw_update_data_t *update_data_in = reinterpret_cast<const usrp2_fw_update_data_t *>(usrp2_update_data_in_mem); //Setting up UDP packet usrp2_fw_update_data_t write_pkt = usrp2_fw_update_data_t(); - write_pkt.id = htonx<boost::uint32_t>(USRP2_FW_UPDATE_ID_WRITE_TEH_FLASHES_LOL); + write_pkt.id = htonx<boost::uint32_t>(WRITE_FLASH_CMD); write_pkt.proto_ver = htonx<boost::uint32_t>(USRP2_FW_PROTO_VERSION); write_pkt.data.flash_args.length = htonx<boost::uint32_t>(FLASH_DATA_PACKET_SIZE); - //Write image - if(is_fw) std::cout << "Writing firmware image." << std::endl; - else std::cout << "Writing FPGA image." << std::endl; - for(int i = 0; i < ((image_size/FLASH_DATA_PACKET_SIZE)+1); i++){ + //Print progress + std::cout << "\rWriting " << type << " image (" + << int((double(current_addr-begin_addr)/double(image_size))*100) << "%)." << std::flush; + write_pkt.data.flash_args.flash_addr = htonx<boost::uint32_t>(current_addr); std::copy(image+(i*FLASH_DATA_PACKET_SIZE), image+((i+1)*FLASH_DATA_PACKET_SIZE), write_pkt.data.flash_args.data); udp_transport->send(boost::asio::buffer(&write_pkt, sizeof(write_pkt))); size_t len = udp_transport->recv(boost::asio::buffer(usrp2_update_data_in_mem), UDP_TIMEOUT); - if(len > offsetof(usrp2_fw_update_data_t, data) and ntohl(update_data_in->id) != USRP2_FW_UPDATE_ID_WROTE_TEH_FLASHES_OMG){ - throw std::runtime_error(str(boost::format("Invalid reply %d from device.") % ntohl(update_data_in->id))); + if(len > offsetof(usrp2_fw_update_data_t, data) and ntohl(update_data_in->id) != WRITE_FLASH_ACK){ + throw std::runtime_error(str(boost::format("Invalid reply %d from device.") + % ntohl(update_data_in->id))); } current_addr += FLASH_DATA_PACKET_SIZE; } + std::cout << std::flush << "\rWriting " << type << " image (100%)." << std::endl; std::cout << boost::format(" * Successfully wrote %d bytes.\n") % image_size; } void verify_image(udp_simple::sptr udp_transport, bool is_fw, boost::uint8_t* image, boost::uint32_t memory_size, int image_size){ int current_index = 0; - boost::uint32_t current_addr; - if(is_fw) current_addr = PROD_FW_IMAGE_LOCATION_ADDR; - else current_addr = PROD_FPGA_IMAGE_LOCATION_ADDR; + boost::uint32_t begin_addr = is_fw ? PROD_FW_IMAGE_LOCATION_ADDR + : PROD_FPGA_IMAGE_LOCATION_ADDR; + boost::uint32_t current_addr = begin_addr; + std::string type = is_fw ? "firmware" : "FPGA"; //Array size needs to be known at runtime, this constant is guaranteed to be larger than any firmware or FPGA image boost::uint8_t from_usrp[FPGA_IMAGE_SIZE_BYTES]; @@ -321,27 +432,27 @@ void verify_image(udp_simple::sptr udp_transport, bool is_fw, boost::uint8_t* im //Making sure this won't attempt to read past end of device if(current_addr+image_size > memory_size) throw std::runtime_error("Cannot read past end of device."); - //Setting up UDP transport - boost::uint8_t usrp2_update_data_in_mem[udp_simple::mtu]; + //UDP receive buffer const usrp2_fw_update_data_t *update_data_in = reinterpret_cast<const usrp2_fw_update_data_t *>(usrp2_update_data_in_mem); //Setting up UDP packet usrp2_fw_update_data_t verify_pkt = usrp2_fw_update_data_t(); - verify_pkt.id = htonx<boost::uint32_t>(USRP2_FW_UPDATE_ID_READ_TEH_FLASHES_LOL); + verify_pkt.id = htonx<boost::uint32_t>(READ_FLASH_CMD); verify_pkt.proto_ver = htonx<boost::uint32_t>(USRP2_FW_PROTO_VERSION); verify_pkt.data.flash_args.length = htonx<boost::uint32_t>(FLASH_DATA_PACKET_SIZE); - //Verify image - if(is_fw) std::cout << "Verifying firmware image." << std::endl; - else std::cout << "Verifying FPGA image." << std::endl; - for(int i = 0; i < ((image_size/FLASH_DATA_PACKET_SIZE)+1); i++){ + //Print progress + std::cout << "\rVerifying " << type << " image (" + << int((double(current_addr-begin_addr)/double(image_size))*100) << "%)." << std::flush; + verify_pkt.data.flash_args.flash_addr = htonx<boost::uint32_t>(current_addr); udp_transport->send(boost::asio::buffer(&verify_pkt, sizeof(verify_pkt))); size_t len = udp_transport->recv(boost::asio::buffer(usrp2_update_data_in_mem), UDP_TIMEOUT); - if(len > offsetof(usrp2_fw_update_data_t, data) and ntohl(update_data_in->id) != USRP2_FW_UPDATE_ID_KK_READ_TEH_FLASHES_OMG){ - throw std::runtime_error(str(boost::format("Invalid reply %d from device.") % ntohl(update_data_in->id))); + if(len > offsetof(usrp2_fw_update_data_t, data) and ntohl(update_data_in->id) != READ_FLASH_ACK){ + throw std::runtime_error(str(boost::format("Invalid reply %d from device.") + % ntohl(update_data_in->id))); } for(int j = 0; j < FLASH_DATA_PACKET_SIZE; j++) from_usrp[current_index+j] = update_data_in->data.flash_args.data[j]; @@ -350,27 +461,27 @@ void verify_image(udp_simple::sptr udp_transport, bool is_fw, boost::uint8_t* im } for(int i = 0; i < image_size; i++) if(from_usrp[i] != image[i]) throw std::runtime_error("Image write failed."); + std::cout << std::flush << "\rVerifying " << type << " image (100%)." << std::endl; std::cout << " * Successful." << std::endl; } void reset_usrp(udp_simple::sptr udp_transport){ //Set up UDP transport - boost::uint8_t usrp2_update_data_in_mem[udp_simple::mtu]; const usrp2_fw_update_data_t *update_data_in = reinterpret_cast<const usrp2_fw_update_data_t *>(usrp2_update_data_in_mem); //Set up UDP packet usrp2_fw_update_data_t reset_pkt = usrp2_fw_update_data_t(); - reset_pkt.id = htonx<boost::uint32_t>(USRP2_FW_UPDATE_ID_RESET_MAH_COMPUTORZ_LOL); + reset_pkt.id = htonx<boost::uint32_t>(RESET_USRP_CMD); reset_pkt.proto_ver = htonx<boost::uint32_t>(USRP2_FW_PROTO_VERSION); //Reset USRP udp_transport->send(boost::asio::buffer(&reset_pkt, sizeof(reset_pkt))); size_t len = udp_transport->recv(boost::asio::buffer(usrp2_update_data_in_mem), UDP_TIMEOUT); - if(len > offsetof(usrp2_fw_update_data_t, data) and ntohl(update_data_in->id) == USRP2_FW_UPDATE_ID_RESETTIN_TEH_COMPUTORZ_OMG){ + if(len > offsetof(usrp2_fw_update_data_t, data) and ntohl(update_data_in->id) == RESET_USRP_ACK){ throw std::runtime_error("USRP reset failed."); //There should be no response to this UDP packet } - else std::cout << "Resetting USRP." << std::endl; + else std::cout << std::endl << "Resetting USRP." << std::endl; } int UHD_SAFE_MAIN(int argc, char *argv[]){ @@ -386,125 +497,88 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ ("addr", po::value<std::string>(&ip_addr)->default_value("192.168.10.2"), "Specify an IP address.") ("fw", po::value<std::string>(&fw_path), "Specify a filepath for a custom firmware image.") ("fpga", po::value<std::string>(&fpga_path), "Specify a filepath for a custom FPGA image.") - ("no_fw", "Do not burn a firmware image.") - ("no_fpga", "Do not burn an FPGA image.") - ("auto_reboot", "Automatically reboot N2XX without prompting.") + ("no-fw", "Do not burn a firmware image.") + ("no_fw", "Do not burn a firmware image (DEPRECATED).") + ("no-fpga", "Do not burn an FPGA image.") + ("no_fpga", "Do not burn an FPGA image (DEPRECATED).") + ("auto-reboot", "Automatically reboot N2XX without prompting.") + ("auto_reboot", "Automatically reboot N2XX without prompting (DEPRECATED).") ("list", "List available N2XX USRP devices.") ; po::variables_map vm; po::store(po::parse_command_line(argc, argv, desc), vm); po::notify(vm); - //Apply options + //Print help message if(vm.count("help") > 0){ std::cout << boost::format("N2XX Simple Net Burner\n"); std::cout << boost::format("Automatically detects and burns standard firmware and FPGA images onto USRP N2XX devices.\n"); std::cout << boost::format("Can optionally take user input for custom images.\n\n"); std::cout << desc << std::endl; - return EXIT_FAILURE; + return EXIT_SUCCESS; } - bool burn_fpga = (vm.count("no_fpga") == 0); - bool burn_fw = (vm.count("no_fw") == 0); + //List option + if(vm.count("list")){ + list_usrps(); + return EXIT_SUCCESS; + } + + //Process user options + bool burn_fpga = (vm.count("no-fpga") == 0) and (vm.count("no_fpga") == 0); + bool burn_fw = (vm.count("no-fw") == 0) and (vm.count("no_fw") == 0); bool use_custom_fpga = (vm.count("fpga") > 0); bool use_custom_fw = (vm.count("fw") > 0); - bool list_usrps = (vm.count("list") > 0); - bool auto_reboot = (vm.count("auto_reboot") > 0); + bool auto_reboot = (vm.count("auto-reboot") > 0) or (vm.count("auto_reboot") > 0); + int fpga_image_size = 0; + int fw_image_size = 0; - if(!burn_fpga && !burn_fw){ + if(not burn_fpga && not burn_fw){ std::cout << "No images will be burned." << std::endl; return EXIT_FAILURE; } - if(!burn_fw && use_custom_fw) std::cout << boost::format("Conflicting firmware options presented. Will not burn a firmware image.\n\n"); - if(!burn_fpga && use_custom_fpga) std::cout << boost::format("Conflicting FPGA options presented. Will not burn an FPGA image.\n\n"); - - //Variables not from options - boost::uint32_t hw_rev; - bool found_it = false; - boost::uint8_t usrp2_update_data_in_mem[udp_simple::mtu]; - const usrp2_fw_update_data_t *update_data_in = reinterpret_cast<const usrp2_fw_update_data_t *>(usrp2_update_data_in_mem); - - //List option - if(list_usrps){ - udp_simple::sptr udp_bc_transport; - usrp2_fw_update_data_t usrp2_ack_pkt = usrp2_fw_update_data_t(); - usrp2_ack_pkt.proto_ver = htonx<boost::uint32_t>(USRP2_FW_PROTO_VERSION); - usrp2_ack_pkt.id = htonx<boost::uint32_t>(USRP2_FW_UPDATE_ID_OHAI_LOL); - - std::cout << "Available USRP N2XX devices:" << std::endl; - - //Send UDP packets to all broadcast addresses - BOOST_FOREACH(const if_addrs_t &if_addrs, get_if_addrs()){ - //Avoid the loopback device - if(if_addrs.inet == boost::asio::ip::address_v4::loopback().to_string()) continue; - udp_bc_transport = udp_simple::make_broadcast(if_addrs.bcast, BOOST_STRINGIZE(USRP2_UDP_UPDATE_PORT)); - udp_bc_transport->send(boost::asio::buffer(&usrp2_ack_pkt, sizeof(usrp2_ack_pkt))); - - size_t len = udp_bc_transport->recv(boost::asio::buffer(usrp2_update_data_in_mem), UDP_TIMEOUT); - if(len > offsetof(usrp2_fw_update_data_t, data) and ntohl(update_data_in->id) == USRP2_FW_UPDATE_ID_OHAI_OMG){ - usrp2_ack_pkt.id = htonx<boost::uint32_t>(USRP2_FW_UPDATE_ID_I_CAN_HAS_HW_REV_LOL); - udp_bc_transport->send(boost::asio::buffer(&usrp2_ack_pkt, sizeof(usrp2_ack_pkt))); - - size_t len = udp_bc_transport->recv(boost::asio::buffer(usrp2_update_data_in_mem), UDP_TIMEOUT); - if(len > offsetof(usrp2_fw_update_data_t, data) and ntohl(update_data_in->id) == USRP2_FW_UPDATE_ID_HERES_TEH_HW_REV_OMG){ - hw_rev = ntohl(update_data_in->data.hw_rev); - } + //Print deprecation messages if necessary + if(vm.count("no_fpga") > 0) std::cout << "WARNING: --no_fpga option is deprecated! Use --no-fpga instead." << std::endl << std::endl; + if(vm.count("no_fw") > 0) std::cout << "WARNING: --no_fw option is deprecated! Use --no-fw instead." << std::endl << std::endl; + if(vm.count("auto_reboot") > 0) std::cout << "WARNING: --auto_reboot option is deprecated! Use --auto-reboot instead." << std::endl << std::endl; - std::cout << boost::format(" * %s (%s)\n") % udp_bc_transport->get_recv_addr() % filename_map[hw_rev]; - } - - } - return EXIT_FAILURE; - } + //Find USRP and establish connection std::cout << boost::format("Searching for USRP N2XX with IP address %s.\n") % ip_addr; - - //Address specified udp_simple::sptr udp_transport = udp_simple::make_connected(ip_addr, BOOST_STRINGIZE(USRP2_UDP_UPDATE_PORT)); - usrp2_fw_update_data_t hw_info_pkt = usrp2_fw_update_data_t(); - hw_info_pkt.proto_ver = htonx<boost::uint32_t>(USRP2_FW_PROTO_VERSION); - hw_info_pkt.id = htonx<boost::uint32_t>(USRP2_FW_UPDATE_ID_I_CAN_HAS_HW_REV_LOL); - udp_transport->send(boost::asio::buffer(&hw_info_pkt, sizeof(hw_info_pkt))); - - //Loop and receive until the timeout - size_t len = udp_transport->recv(boost::asio::buffer(usrp2_update_data_in_mem), UDP_TIMEOUT); - if(len > offsetof(usrp2_fw_update_data_t, data) and ntohl(update_data_in->id) == USRP2_FW_UPDATE_ID_HERES_TEH_HW_REV_OMG){ - hw_rev = ntohl(update_data_in->data.hw_rev); - if(filename_map.find(hw_rev) != filename_map.end()){ - std::cout << boost::format("Found %s.\n\n") % filename_map[hw_rev]; - found_it = true; - } - else throw std::runtime_error("Invalid revision found."); - } - if(!found_it) throw std::runtime_error("No USRP N2XX found."); - - //Determining default image filenames for validation - std::string default_fw_filename = str(boost::format("usrp_%s_fw.bin") % erase_tail_copy(filename_map[hw_rev],3)); - std::string default_fpga_filename = str(boost::format("usrp_%s_fpga.bin") % filename_map[hw_rev]); - std::string default_fw_filepath = ""; - std::string default_fpga_filepath = ""; + boost::uint32_t hw_rev = find_usrp(udp_transport); //Check validity of file locations and binaries before attempting burn std::cout << "Searching for specified images." << std::endl << std::endl; if(burn_fpga){ - if(!use_custom_fpga) fpga_path = find_image_path(default_fpga_filename); - else{ - //Replace ~ with home directory - if(fpga_path.find("~/") == 0) fpga_path.replace(0,1,getenv("HOME")); + if(use_custom_fpga){ + //Expand tilde usage if applicable + #ifndef UHD_PLATFORM_WIN32 + if(fpga_path.find("~/") == 0) fpga_path.replace(0,1,getenv("HOME")); + #endif validate_custom_fpga_file(filename_map[hw_rev], fpga_path); } + else{ + std::string default_fpga_filename = str(boost::format("usrp_%s_fpga.bin") % filename_map[hw_rev]); + fpga_path = find_image_path(default_fpga_filename); + } - grab_fpga_image(fpga_path); + fpga_image_size = read_fpga_image(fpga_path); } if(burn_fw){ - if(!use_custom_fw) fw_path = find_image_path(default_fw_filename); - else{ - //Replace ~ with home directory - if(fw_path.find("~/") == 0) fw_path.replace(0,1,getenv("HOME")); + if(use_custom_fw){ + //Expand tilde usage if applicable + #ifndef UHD_PLATFORM_WIN32 + if(fw_path.find("~/") == 0) fw_path.replace(0,1,getenv("HOME")); + #endif validate_custom_fw_file(filename_map[hw_rev], fw_path); } + else{ + std::string default_fw_filename = str(boost::format("usrp_%s_fw.bin") % erase_tail_copy(filename_map[hw_rev],3)); + fw_path = find_image_path(default_fw_filename); + } - grab_fw_image(fw_path); + fw_image_size = read_fw_image(fw_path); } std::cout << "Will burn the following images:" << std::endl; @@ -547,7 +621,6 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ std::cout << std::endl; //Formatting } if(reset) reset_usrp(udp_transport); - else return EXIT_SUCCESS; return EXIT_SUCCESS; } diff --git a/host/utils/usrp_simple_burner_utils.hpp b/host/utils/usrp_simple_burner_utils.hpp deleted file mode 100644 index f386c3620..000000000 --- a/host/utils/usrp_simple_burner_utils.hpp +++ /dev/null @@ -1,99 +0,0 @@ -// -// Copyright 2012 Ettus Research LLC -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see <http://www.gnu.org/licenses/>. -// - -#include <iostream> -#include <math.h> -#include <stdint.h> - -#include <boost/foreach.hpp> -#include <boost/asio.hpp> -#include <boost/filesystem.hpp> - -#include <uhd/exception.hpp> -#include <uhd/transport/if_addrs.hpp> -#include <uhd/transport/udp_simple.hpp> -#include <uhd/types/device_addr.hpp> -#include <uhd/utils/msg.hpp> - -#define UDP_FW_UPDATE_PORT 49154 -#define UDP_MAX_XFER_BYTES 1024 -#define UDP_TIMEOUT 3 -#define UDP_POLL_INTERVAL 0.10 //in seconds -#define USRP2_FW_PROTO_VERSION 7 //should be unused after r6 -#define USRP2_UDP_UPDATE_PORT 49154 -#define FLASH_DATA_PACKET_SIZE 256 -#define FPGA_IMAGE_SIZE_BYTES 1572864 -#define FW_IMAGE_SIZE_BYTES 31744 -#define PROD_FPGA_IMAGE_LOCATION_ADDR 0x00180000 -#define PROD_FW_IMAGE_LOCATION_ADDR 0x00300000 -#define SAFE_FPGA_IMAGE_LOCATION_ADDR 0x00000000 -#define SAFE_FW_IMAGE_LOCATION_ADDR 0x003F0000 - -using namespace uhd; -using namespace uhd::transport; -namespace asio = boost::asio; - -typedef enum { - USRP2_FW_UPDATE_ID_WAT = ' ', - - USRP2_FW_UPDATE_ID_OHAI_LOL = 'a', - USRP2_FW_UPDATE_ID_OHAI_OMG = 'A', - - USRP2_FW_UPDATE_ID_WATS_TEH_FLASH_INFO_LOL = 'f', - USRP2_FW_UPDATE_ID_HERES_TEH_FLASH_INFO_OMG = 'F', - - USRP2_FW_UPDATE_ID_ERASE_TEH_FLASHES_LOL = 'e', - USRP2_FW_UPDATE_ID_ERASING_TEH_FLASHES_OMG = 'E', - - USRP2_FW_UPDATE_ID_R_U_DONE_ERASING_LOL = 'd', - USRP2_FW_UPDATE_ID_IM_DONE_ERASING_OMG = 'D', - USRP2_FW_UPDATE_ID_NOPE_NOT_DONE_ERASING_OMG = 'B', - - USRP2_FW_UPDATE_ID_WRITE_TEH_FLASHES_LOL = 'w', - USRP2_FW_UPDATE_ID_WROTE_TEH_FLASHES_OMG = 'W', - - USRP2_FW_UPDATE_ID_READ_TEH_FLASHES_LOL = 'r', - USRP2_FW_UPDATE_ID_KK_READ_TEH_FLASHES_OMG = 'R', - - USRP2_FW_UPDATE_ID_RESET_MAH_COMPUTORZ_LOL = 's', - USRP2_FW_UPDATE_ID_RESETTIN_TEH_COMPUTORZ_OMG = 'S', - - USRP2_FW_UPDATE_ID_I_CAN_HAS_HW_REV_LOL = 'v', - USRP2_FW_UPDATE_ID_HERES_TEH_HW_REV_OMG = 'V', - - USRP2_FW_UPDATE_ID_KTHXBAI = '~' - -} usrp2_fw_update_id_t; - -typedef struct { - uint32_t proto_ver; - uint32_t id; - uint32_t seq; - union { - uint32_t ip_addr; - uint32_t hw_rev; - struct { - uint32_t flash_addr; - uint32_t length; - uint8_t data[256]; - } flash_args; - struct { - uint32_t sector_size_bytes; - uint32_t memory_size_bytes; - } flash_info_args; - } data; -} usrp2_fw_update_data_t; |