aboutsummaryrefslogtreecommitdiffstats
path: root/host
diff options
context:
space:
mode:
authorNicholas Corgan <nick.corgan@ettus.com>2015-07-15 09:32:18 -0700
committerNicholas Corgan <nick.corgan@ettus.com>2015-07-15 09:32:18 -0700
commit0595900eccfffee9e944dc53466337b44655caac (patch)
tree7db7ee8e908b604236b04cf49f3d8d978e386543 /host
parent012381d999c4a895593412aaf06e73432b458810 (diff)
downloaduhd-0595900eccfffee9e944dc53466337b44655caac.tar.gz
uhd-0595900eccfffee9e944dc53466337b44655caac.tar.bz2
uhd-0595900eccfffee9e944dc53466337b44655caac.zip
Added uhd::image_loader class and uhd_image_loader utility
* Single class for loading firmware/FPGA images onto devices instead of multiple utilities * Loading functions are registered for each device, corresponding to their --args="type=foo" name * Deprecation warnings added to all product-specific image loading utilities
Diffstat (limited to 'host')
-rw-r--r--host/docs/CMakeLists.txt3
-rw-r--r--host/docs/uhd_image_loader.1130
-rw-r--r--host/docs/usrp2.dox29
-rw-r--r--host/docs/usrp_x3x0.dox24
-rw-r--r--host/include/uhd/image_loader.hpp89
-rw-r--r--host/lib/CMakeLists.txt1
-rw-r--r--host/lib/image_loader.cpp87
-rw-r--r--host/lib/usrp/b200/CMakeLists.txt1
-rw-r--r--host/lib/usrp/b200/b200_iface.cpp4
-rw-r--r--host/lib/usrp/b200/b200_iface.hpp2
-rw-r--r--host/lib/usrp/b200/b200_image_loader.cpp125
-rw-r--r--host/lib/usrp/b200/b200_impl.cpp39
-rw-r--r--host/lib/usrp/b200/b200_impl.hpp16
-rw-r--r--host/lib/usrp/e100/e100_impl.cpp34
-rw-r--r--host/lib/usrp/e100/e100_impl.hpp4
-rw-r--r--host/lib/usrp/e100/fpga_downloader.cpp41
-rw-r--r--host/lib/usrp/e300/e300_common.cpp35
-rw-r--r--host/lib/usrp/e300/e300_impl.cpp63
-rw-r--r--host/lib/usrp/e300/e300_impl.hpp8
-rw-r--r--host/lib/usrp/usrp2/CMakeLists.txt15
-rw-r--r--host/lib/usrp/usrp2/n200_image_loader.cpp616
-rw-r--r--host/lib/usrp/usrp2/usrp2_iface.cpp10
-rw-r--r--host/lib/usrp/usrp2/usrp2_impl.cpp2
-rw-r--r--host/lib/usrp/usrp2/usrp2_impl.hpp3
-rw-r--r--host/lib/usrp/x300/CMakeLists.txt4
-rw-r--r--host/lib/usrp/x300/cdecode.c80
-rw-r--r--host/lib/usrp/x300/cdecode.h36
-rw-r--r--host/lib/usrp/x300/x300_image_loader.cpp402
-rw-r--r--host/lib/usrp/x300/x300_impl.cpp20
-rw-r--r--host/lib/usrp/x300/x300_impl.hpp4
-rw-r--r--host/lib/usrp_clock/octoclock/CMakeLists.txt8
-rw-r--r--host/lib/usrp_clock/octoclock/ihexcvt.cpp250
-rw-r--r--host/lib/usrp_clock/octoclock/ihexcvt.hpp22
-rw-r--r--host/lib/usrp_clock/octoclock/octoclock_image_loader.cpp340
-rw-r--r--host/lib/usrp_clock/octoclock/octoclock_impl.hpp2
-rw-r--r--host/utils/CMakeLists.txt3
-rw-r--r--host/utils/octoclock_firmware_burner.cpp24
-rw-r--r--host/utils/uhd_image_loader.cpp122
-rwxr-xr-xhost/utils/usrp_n2xx_net_burner.py51
-rw-r--r--host/utils/usrp_n2xx_simple_net_burner.cpp57
-rw-r--r--host/utils/usrp_x3xx_fpga_burner.cpp69
41 files changed, 2735 insertions, 140 deletions
diff --git a/host/docs/CMakeLists.txt b/host/docs/CMakeLists.txt
index d0f060ceb..79488e373 100644
--- a/host/docs/CMakeLists.txt
+++ b/host/docs/CMakeLists.txt
@@ -1,5 +1,5 @@
#
-# Copyright 2010-2013 Ettus Research LLC
+# Copyright 2010-2013,2015 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
@@ -120,6 +120,7 @@ SET(man_page_sources
uhd_cal_tx_dc_offset.1
uhd_cal_tx_iq_balance.1
uhd_find_devices.1
+ uhd_image_loader.1
uhd_images_downloader.1
uhd_usrp_probe.1
usrp_n2xx_simple_net_burner.1
diff --git a/host/docs/uhd_image_loader.1 b/host/docs/uhd_image_loader.1
new file mode 100644
index 000000000..5d5c88ec5
--- /dev/null
+++ b/host/docs/uhd_image_loader.1
@@ -0,0 +1,130 @@
+.TH "uhd_image_loader" 1 "3.9.0" UHD "User Commands"
+.SH NAME
+uhd_image_loader - UHD Image Loader
+
+.SH DESCRIPTION
+Burn firmware and FPGA images onto connected Ettus Research devices.
+
+.SH SYNOPSIS
+.B uhd_image_loader [OPTIONS]
+
+.SH OPTIONS
+.IP "List options:"
+--help
+.IP "Device and loader arguments:"
+--args=""
+.IP "Custom firmware filepath:"
+--fw-path=""
+.IP "Custom FPGA filepath:"
+--fpga-path=""
+.IP "Don't burn firmware:"
+--no-fw
+.IP "Don't burn FPGA:"
+--no-fpga
+
+.SH SPECIFYING A PARTICULAR DEVICE
+.sp
+Without any arguments given through the \fIargs=""\fR option, this utility will start a session
+with the first Ettus Research device it sees. The arguments shown below will narrow down the
+utility's search for a particular device:
+
+.SS All devices
+.sp
+The \fIname=\fR and \fIserial=\fR arguments can be used to specify any device except an
+uninitialized USB device or an OctoClock bootloader. The \fItype=\fR argument can be used
+to specify any device, as described below:
+
+"type=" argument
+
+Argument | Device
+
+type=usrp2 | USRP N200, USRP N210
+
+type=b200 | USRP B200, USRP B210
+
+type=e100 | USRP E100, USRP E110
+
+type=e3x0 | USRP E310
+
+type=octoclock | OctoClock
+
+.sp
+NOTE: The USRP1, USRP2, and USRP B100 are not supported.
+
+.SS Network devices
+.sp
+By default, this utility will check all network interfaces for network-based devices, but a specific
+IP address can be specified with the \fIaddr=\fR argument.
+
+.SS NI-RIO devices
+.sp
+An X-Series devices connected via PCIe can be specified by its NI-RIO resource through the \fIresource=\fR
+argument, and the RPC port through which to communicate with it can be specified with the \fIrpc-port=\fR
+argument. Using these options is not recommended, as their default values are almost always used.
+
+.SS OctoClock devices
+An OctoClock's name and serial are only exposed when the firmware is loaded, so if the device only has a
+bootloader, only the \fIaddr=\fR argument can be used to find it.
+
+.SH DEVICE-SPECIFIC LOADER OPTIONS
+.sp
+Certain devices have specific options for customizing their image loading process, and these can be passed
+in through the \fI--args=""\fR option. These arguments are specified below:
+
+.SS USRP N200, USRP N210
+.sp
+The \fIoverwrite-safe\fR option will overwrite the device's safe-mode firmware and FPGA images. This is
+NOT RECOMMENDED, as these images serve as the backup if the device's primary images are corrupted.
+
+.sp
+The \fIreset\fR option will automatically reset the device once the loading process finishes. When the
+device resets, it will have its new images loaded.
+
+.SS USRP X300, USRP X310 (Ethernet only)
+.sp
+The \fIconfigure\fR option will automatically reset the device once the loading process finishes. When
+the device resets, it will have its new FPGA image loaded.
+
+.sp
+The \fIverify\fR option will tell the device to internally verify the integrity of the image as it loads.
+This greatly increases the loading time.
+
+.SH EXAMPLES
+
+.SS Load only the default FPGA image onto a specific N2x0 device and reset
+.sp
+uhd_image_loader --args="type=usrp2,addr=192.168.10.2,reset" --no-fw
+.ft
+
+.SS Load a custom FPGA image onto a specific X3x0 device
+.sp
+uhd_image_loader --args="type=x300,addr=192.168.40.2" --fpga-path="/home/user/my_x300_fpga_image.bit"
+.ft
+
+.fi
+
+.SH SEE ALSO
+UHD documentation:
+.B http://files.ettus.com/manual/
+.LP
+GR-UHD documentation:
+.B http://gnuradio.org/doc/doxygen/page_uhd.html
+.LP
+Other UHD programs:
+.sp
+uhd_images_downloader(1) usrp2_card_burner(1)
+.SH AUTHOR
+This manual page was written by Nicholas Corgan
+for the Debian project (but may be used by others).
+.SH COPYRIGHT
+Copyright (c) 2015 Ettus Research LLC
+.LP
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+.LP
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
diff --git a/host/docs/usrp2.dox b/host/docs/usrp2.dox
index 7b6cb9ed0..3f85e45b5 100644
--- a/host/docs/usrp2.dox
+++ b/host/docs/usrp2.dox
@@ -62,32 +62,21 @@ 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.
-\subsection usrp2_loadflash_netburner Use the net burner tool
+\subsection usrp2_loadflash_imageloader Use the image loader
Use default images:
- usrp_n2xx_simple_net_burner --addr=<IP address>
+ uhd_image_loader --args="type=usrp2,addr=<IP address>"
Use custom-built images:
- usrp_n2xx_simple_net_burner --addr=<IP address> --fw=<firmware path> --fpga=<FPGA path>
+ uhd_image_loader --args="type=usrp2,addr=<IP address>" --fw-path="<firmware path>" --fpga-path="<FPGA path>"
<b>Note:</b> 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.
-
-\subsection usrp2_loadflash_gui Use the graphical net burner tool (Linux)
-
- <install-path>/lib/uhd/utils/usrp_n2xx_net_burner_gui.py
-
-\subsection usrp2_loadflash_guiwin Use the graphical net burner tool (Windows)
-
- <path_to_python.exe> <install-path>/lib/uhd/utils/usrp_n2xx_net_burner_gui.py
-
\subsection usrp2_loadflash_brick Device recovery and bricking
Its possible to put the device into an unusable state by loading bad
@@ -438,22 +427,20 @@ There is a sub-directory in the archive below the firmware/images called 'bit'.
The USRP should now be able to communicate on the network (you'll see some LEDs light up and network link be established). The next step is to flash the device and program the serial number. Both these steps can be done with UHD (the JTAG step is complete).
-To be sure, run `uhd_find_devices` and it should appear in the list - remember this IP address for the burner utility (should be 192.168.10.2 - make sure your network settings enable to you communicate with that subnet!).
+To be sure, run `uhd_find_devices` and it should appear in the list - remember this IP address for the image loader utility (should be 192.168.10.2 - make sure your network settings enable to you communicate with that subnet!).
-The first step is to flash the unit's safe-mode image, and then do a normal flash - both with the USRP N-series image burner utility.
+The first step is to flash the unit's safe-mode image, and then do a normal flash - both with the UHD Image Loader utility.
Make sure you have UHD installed, and the images from before, and follow the instructions in \ref usrp2_load.
-You can combine the `--fw` and `--fpga` arguments into the single invocation of the burner.
+You can combine the `--fw-path` and `--fpga-path` arguments into the single invocation of the image loader.
You will probably use "usrp_n210_fw.bin" for the firmware and "usrp_n210_r4_fpga.bin" for the FPGA image parameters (use the full/relative file path if your current directory is not that of the images).
- usrp_n2xx_net_burner.py --addr=192.168.10.2 --fw=usrp_n210_fw.bin --fpga=usrp_n210_r4_fpga.bin --overwrite-safe
+ uhd_image_loader --args="type=usrp2,addr=192.168.10.2,overwrite-safe" --fw-path=usrp_n210_fw.bin --fpga-path=usrp_n210_r4_fpga.bin
-Use `--overwrite-safe` the first time, and then repeat without it for the second time.
+Use the `overwrite-safe` option the first time, and then repeat without it for the second time.
Don't forget to power-cycle the device after it has been flashed.
-If you see a Python exception thrown (e.g. KeyError 65535) and something about if(check_rev)... use the --dont-check-rev option too (this is when the EEPROM has not yet been initialised, or has been blanked).
-
You can change the normal IP address by following the instructions in \ref usrp2_network_changeip.
If you run `uhd_usrp_probe`, you can see the EEPROM keys at the top. Example:
diff --git a/host/docs/usrp_x3x0.dox b/host/docs/usrp_x3x0.dox
index 98e21a4d0..d4dccaed5 100644
--- a/host/docs/usrp_x3x0.dox
+++ b/host/docs/usrp_x3x0.dox
@@ -89,13 +89,13 @@ number, you will have to update the FPGA image before you can start using your U
1. Download the current UHD images. You can use the `uhd_images_downloader` script provided
with UHD (see also \ref page_images).
-2. Use the `usrp_x3xx_fpga_burner` utility to update the FPGA image. On the command line, run:
+2. Use the `uhd_image_loader` utility to update the FPGA image. On the command line, run:
- usrp_x3xx_fpga_burner --addr=192.168.10.2 --type=HGS
+ uhd_image_loader --args="type=x300,addr=192.168.10.2,fpga=HGS"
If you have installed the images to a non-standard location, you might need to run (change the filename according to your device):
- usrp_x3xx_fpga_burner --addr=192.168.10.2 --fpga-path <path_to_images>/usrp_x310_fpga_HGS.bit
+ uhd_image_loader --args="type=x300,addr=192.168.10.2" --fpga-path="<path_to_images>/usrp_x310_fpga_HGS.bit"
The process of updating the FPGA image will take several minutes. Make sure the process of flashing the image does not get interrupted.
@@ -303,31 +303,31 @@ detect your device information, and you will need to use this number to
select which image to burn.
\b Note:
-The burner utility will default to using the appropriate BIT file if no custom
+The image loader utility will default to using the appropriate BIT file if no custom
FPGA image path is specified, but it is compatible with BIN, BIT, and LVBITX
images.
-\subsection x3x0_flash_burner_tool Use the burner tool over Ethernet
+\subsection uhd_image_loader_tool Use the image loader over Ethernet
Automatic FPGA path, detect image type:
- usrp_x3xx_fpga_burner --addr=<IP address>
+ uhd_image_loader --args="type=x300,addr=<IP address>"
Automatic FPGA path, select image type:
- usrp_x3xx_fpga_burner --addr=<IP address> --type=<HGS or XGS>
+ uhd_image_loader --args="type=x300,addr=<IP address>,fpga=<HGS or XGS>"
Manual FPGA path:
- usrp_x3xx_fpga_burner --addr=<IP address> --fpga-path=<path to FPGA image>
+ uhd_image_loader --args="type=x300,addr=<IP address>" --fpga-path="<path to FPGA image>"
-\subsection x3x0_flash_burner_tool_pcie Use the burner tool over PCI Express
+\subsection uhd_image_loader_tool_pcie Use the image loader over PCI Express
Automatic FPGA path, detect image type:
- usrp_x3xx_fpga_burner --resource=<NI-RIO resource>
+ uhd_image_loader --args="type=x300,resource=<NI-RIO resource>"
Automatic FPGA path, select image type:
- usrp_x3xx_fpga_burner --resource=<NI-RIO resource> --type=<HGS or XGS>
+ uhd_image_loader --args="type=x300,resource=<NI-RIO resource>,fpga=<HGS or XGS>"
Manual FPGA path:
- usrp_x3xx_fpga_burner --resource=<NI-RIO resource> --fpga-path=<path to FPGA image>
+ uhd_image_loader --args="type=x300,resource=<NI-RIO resource>" --fpga-path="<path to FPGA image>"
\subsection x3x0_flash_bricking Device recovery and bricking
It is possible to put the device into an unusable state by loading bad images ("bricking").
diff --git a/host/include/uhd/image_loader.hpp b/host/include/uhd/image_loader.hpp
new file mode 100644
index 000000000..8124e7dea
--- /dev/null
+++ b/host/include/uhd/image_loader.hpp
@@ -0,0 +1,89 @@
+//
+// Copyright 2014-2015 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_IMAGE_LOADER_HPP
+#define INCLUDED_UHD_IMAGE_LOADER_HPP
+
+#include <string>
+
+#include <boost/function.hpp>
+
+#include <uhd/config.hpp>
+#include <uhd/types/device_addr.hpp>
+
+namespace uhd{
+
+class UHD_API image_loader : boost::noncopyable{
+
+public:
+
+ typedef struct{
+ uhd::device_addr_t args;
+ bool load_firmware;
+ bool load_fpga;
+ std::string firmware_path;
+ std::string fpga_path;
+ } image_loader_args_t;
+
+ //! Signature of an image loading function
+ /*!
+ * This is the function signature for an image loading function.
+ * See the declaration of load() for the meaning of these arguments.
+ *
+ * This function must return true upon the end of a successful image load
+ * or false if no applicable device was found. It may only throw a runtime
+ * error under one of two conditions:
+ *
+ * * The function has already engaged with a specific device and
+ * something goes wrong.
+ * * The user gives arguments that unambiguously lead to a specific
+ * device and expect the default image(s) to be loaded, but the specific
+ * model of the device cannot be determined beyond a category.
+ */
+ typedef boost::function<bool(const image_loader_args_t &)> loader_fcn_t;
+
+ //! Register an image loader
+ /*!
+ * \param device_type the "type=foo" value given in an --args option
+ * \param loader_fcn the loader function for the given device
+ * \param recovery_instructions instructions on how to restore a device
+ */
+ static void register_image_loader(
+ const std::string &device_type,
+ const loader_fcn_t &loader_fcn,
+ const std::string &recovery_instructions
+ );
+
+ //! Load firmware and/or FPGA onto a device
+ /*!
+ * \param image_loader_args arguments to pass into image loading function
+ */
+ static bool load(const image_loader_args_t &image_loader_args);
+
+ //! Get the instructions on how to recovery a particular device
+ /*!
+ * These instructions should be queried if the user interrupts an image loading
+ * session, as this will likely leave the device in an unstable state.
+ * \param device_type the "type=foo" value given in an --args option
+ * \return recoverying instructions
+ */
+ static std::string get_recovery_instructions(const std::string &device_type);
+};
+
+}
+
+#endif /* INCLUDED_UHD_IMAGE_LOADER_HPP */
diff --git a/host/lib/CMakeLists.txt b/host/lib/CMakeLists.txt
index 3fa8ed22f..3d4ba8a68 100644
--- a/host/lib/CMakeLists.txt
+++ b/host/lib/CMakeLists.txt
@@ -89,6 +89,7 @@ CONFIGURE_FILE(
LIBUHD_APPEND_SOURCES(
${CMAKE_CURRENT_SOURCE_DIR}/deprecated.cpp
${CMAKE_CURRENT_SOURCE_DIR}/device.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/image_loader.cpp
${CMAKE_CURRENT_SOURCE_DIR}/stream.cpp
${CMAKE_CURRENT_SOURCE_DIR}/exception.cpp
${CMAKE_CURRENT_SOURCE_DIR}/property_tree.cpp
diff --git a/host/lib/image_loader.cpp b/host/lib/image_loader.cpp
new file mode 100644
index 000000000..91dd325dd
--- /dev/null
+++ b/host/lib/image_loader.cpp
@@ -0,0 +1,87 @@
+//
+// Copyright 2014-2015 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <iostream>
+#include <map>
+#include <utility>
+
+#include <boost/filesystem.hpp>
+#include <boost/foreach.hpp>
+#include <boost/format.hpp>
+
+#include <uhd/exception.hpp>
+#include <uhd/image_loader.hpp>
+#include <uhd/utils/log.hpp>
+#include <uhd/utils/msg.hpp>
+#include <uhd/utils/static.hpp>
+
+namespace fs = boost::filesystem;
+
+typedef std::map<std::string, uhd::image_loader::loader_fcn_t> loader_fcn_map_t;
+typedef std::map<std::string, std::string> string_map_t;
+
+// Nice typedefs for iterating over std::map
+typedef std::pair<std::string, uhd::image_loader::loader_fcn_t> loader_fcn_pair_t;
+typedef std::pair<std::string, std::string> string_pair_t;
+
+UHD_SINGLETON_FCN(loader_fcn_map_t, get_image_loaders);
+UHD_SINGLETON_FCN(string_map_t, get_recovery_strings);
+
+/*
+ * Registration
+ */
+void uhd::image_loader::register_image_loader(const std::string &device_type,
+ const loader_fcn_t &loader_fcn,
+ const std::string &recovery_instructions){
+ UHD_LOGV(always) << "Registering image loader and recovery instructions for "
+ << device_type << std::endl;
+
+ get_image_loaders().insert(loader_fcn_pair_t(device_type, loader_fcn));
+ get_recovery_strings().insert(string_pair_t(device_type, recovery_instructions));
+}
+
+/*
+ * Actual loading
+ */
+bool uhd::image_loader::load(const uhd::image_loader::image_loader_args_t &image_loader_args){
+
+ // If "type=foo" given in args, see if we have an image loader for that
+ if(image_loader_args.args.has_key("type")){
+ std::string type = image_loader_args.args.get("type");
+ if(get_image_loaders().find(type) == get_image_loaders().end()){
+ throw uhd::runtime_error(str(boost::format("There is no image loader registered for given type \"%s\".")
+ % type));
+ }
+ else return get_image_loaders().at(type)(image_loader_args);
+ }
+ else{
+ BOOST_FOREACH(const loader_fcn_pair_t &loader_fcn_pair, get_image_loaders()){
+ if(loader_fcn_pair.second(image_loader_args)) return true;
+ }
+ return false;
+ }
+}
+
+/*
+ * Get recovery instructions for particular device
+ */
+std::string uhd::image_loader::get_recovery_instructions(const std::string &device_type){
+ if(get_recovery_strings().count(device_type) == 0){
+ return "A firmware or FPGA loading process was interrupted by the user. This can leave your device in a non-working state.";
+ }
+ else return get_recovery_strings().at(device_type);
+}
diff --git a/host/lib/usrp/b200/CMakeLists.txt b/host/lib/usrp/b200/CMakeLists.txt
index ce89b5d80..cd8ebcba7 100644
--- a/host/lib/usrp/b200/CMakeLists.txt
+++ b/host/lib/usrp/b200/CMakeLists.txt
@@ -26,6 +26,7 @@ LIBUHD_REGISTER_COMPONENT("B200" ENABLE_B200 ON "ENABLE_LIBUHD;ENABLE_USB" OFF)
IF(ENABLE_B200)
LIBUHD_APPEND_SOURCES(
+ ${CMAKE_CURRENT_SOURCE_DIR}/b200_image_loader.cpp
${CMAKE_CURRENT_SOURCE_DIR}/b200_impl.cpp
${CMAKE_CURRENT_SOURCE_DIR}/b200_iface.cpp
${CMAKE_CURRENT_SOURCE_DIR}/b200_io_impl.cpp
diff --git a/host/lib/usrp/b200/b200_iface.cpp b/host/lib/usrp/b200/b200_iface.cpp
index 270d3bb4b..4754a6357 100644
--- a/host/lib/usrp/b200/b200_iface.cpp
+++ b/host/lib/usrp/b200/b200_iface.cpp
@@ -511,7 +511,7 @@ public:
throw uhd::io_error((boost::format("Short write on set FPGA hash (expecting: %d, returned: %d)") % bytes_to_send % ret).str());
}
- boost::uint32_t load_fpga(const std::string filestring) {
+ boost::uint32_t load_fpga(const std::string filestring, bool force) {
boost::uint8_t fx3_state = 0;
boost::uint32_t wait_count;
@@ -522,7 +522,7 @@ public:
hash_type hash = generate_hash(filename);
hash_type loaded_hash; usrp_get_fpga_hash(loaded_hash);
- if (hash == loaded_hash) return 0;
+ if (hash == loaded_hash and !force) return 0;
// Establish default largest possible control request transfer size based on operating USB speed
int transfer_size = VREQ_DEFAULT_SIZE;
diff --git a/host/lib/usrp/b200/b200_iface.hpp b/host/lib/usrp/b200/b200_iface.hpp
index 1d123439a..0c7ee6b9e 100644
--- a/host/lib/usrp/b200/b200_iface.hpp
+++ b/host/lib/usrp/b200/b200_iface.hpp
@@ -97,7 +97,7 @@ public:
virtual void set_fpga_reset_pin(const bool reset) = 0;
//! load an FPGA image
- virtual boost::uint32_t load_fpga(const std::string filestring) = 0;
+ virtual boost::uint32_t load_fpga(const std::string filestring, bool force=false) = 0;
virtual void write_eeprom(boost::uint16_t addr, boost::uint16_t offset, const uhd::byte_vector_t &bytes) = 0;
diff --git a/host/lib/usrp/b200/b200_image_loader.cpp b/host/lib/usrp/b200/b200_image_loader.cpp
new file mode 100644
index 000000000..87010244c
--- /dev/null
+++ b/host/lib/usrp/b200/b200_image_loader.cpp
@@ -0,0 +1,125 @@
+//
+// Copyright 2014-2015 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 <boost/assign.hpp>
+#include <boost/foreach.hpp>
+#include <boost/lexical_cast.hpp>
+
+#include <uhd/exception.hpp>
+#include <uhd/image_loader.hpp>
+#include <uhd/types/dict.hpp>
+#include <uhd/usrp/mboard_eeprom.hpp>
+#include <uhd/utils/paths.hpp>
+#include <uhd/utils/static.hpp>
+
+#include "b200_iface.hpp"
+#include "b200_impl.hpp"
+
+using namespace uhd;
+using namespace uhd::usrp;
+using namespace uhd::transport;
+
+namespace uhd{
+
+static b200_iface::sptr get_b200_iface(const image_loader::image_loader_args_t &image_loader_args,
+ mboard_eeprom_t &mb_eeprom,
+ bool user_specified){
+
+ std::vector<usb_device_handle::sptr> dev_handles = get_b200_device_handles(image_loader_args.args);
+ b200_iface::sptr iface;
+
+ if(dev_handles.size() > 0){
+ BOOST_FOREACH(usb_device_handle::sptr dev_handle, dev_handles){
+ if(dev_handle->firmware_loaded()){
+ iface = b200_iface::make(usb_control::make(dev_handle,0));
+ mb_eeprom = mboard_eeprom_t(*iface, "B200");
+ if(user_specified){
+ if(image_loader_args.args.has_key("serial") and
+ mb_eeprom.get("serial") != image_loader_args.args.get("serial")){
+ continue;
+ }
+ if(image_loader_args.args.has_key("name") and
+ mb_eeprom.get("name") != image_loader_args.args.get("name")){
+ continue;
+ }
+ return iface;
+ }
+ else return iface; // Just return first found
+ }
+ }
+ }
+
+ // No applicable devices found, return empty sptr so we can exit
+ iface.reset();
+ mb_eeprom = mboard_eeprom_t();
+ return iface;
+}
+
+static bool b200_image_loader(const image_loader::image_loader_args_t &image_loader_args){
+ if(!image_loader_args.load_fpga)
+ return false;
+
+ bool user_specified = (image_loader_args.args.has_key("serial") or
+ image_loader_args.args.has_key("name"));
+
+ // See if a B2x0 with the given args is found
+ mboard_eeprom_t mb_eeprom;
+ b200_iface::sptr iface = get_b200_iface(image_loader_args, mb_eeprom, user_specified);
+ if(!iface) return false; // No initialized B2x0 found
+
+ std::string fpga_path;
+ if(image_loader_args.fpga_path == ""){
+ /*
+ * Normally, we can auto-generate the FPGA filename from what's in the EEPROM,
+ * but if the applicable value is not in the EEPROM, the user must give a specific
+ * filename for us to use.
+ */
+ std::string product = mb_eeprom.get("product");
+ if(not B2X0_PRODUCT_ID.has_key(boost::lexical_cast<boost::uint16_t>(product))){
+ if(user_specified){
+ // The user specified a bad device but expects us to know what it is
+ throw uhd::runtime_error("Could not determine model. You must manually specify an FPGA image filename.");
+ }
+ else{
+ return false;
+ }
+ }
+ else{
+ fpga_path = find_image_path(B2X0_FPGA_FILE_NAME.get(get_b200_type(mb_eeprom)));
+ }
+ }
+ else fpga_path = image_loader_args.fpga_path;
+
+ std::cout << boost::format("Unit: USRP %s (%s)")
+ % B2X0_STR_NAMES.get(get_b200_type(mb_eeprom), "B2XX")
+ % mb_eeprom.get("serial")
+ << std::endl;
+
+ iface->load_fpga(fpga_path, true);
+
+ return true;
+}
+
+UHD_STATIC_BLOCK(register_b200_image_loader){
+ std::string recovery_instructions = "This device is likely in an unusable state. Power-cycle the\n"
+ "device, and the firmware/FPGA will be reloaded the next time\n"
+ "UHD uses the device.";
+
+ image_loader::register_image_loader("b200", b200_image_loader, recovery_instructions);
+}
+
+} /* namespace uhd */
diff --git a/host/lib/usrp/b200/b200_impl.cpp b/host/lib/usrp/b200/b200_impl.cpp
index 7c672fd46..17086de02 100644
--- a/host/lib/usrp/b200/b200_impl.cpp
+++ b/host/lib/usrp/b200/b200_impl.cpp
@@ -76,7 +76,7 @@ public:
//! Look up the type of B-Series device we're currently running.
// If the product ID stored in mb_eeprom is invalid, throws a
// uhd::runtime_error.
-static b200_type_t get_b200_type(const mboard_eeprom_t &mb_eeprom)
+b200_type_t get_b200_type(const mboard_eeprom_t &mb_eeprom)
{
if (mb_eeprom["product"].empty()) {
throw uhd::runtime_error("B200: Missing product ID on EEPROM.");
@@ -91,6 +91,21 @@ static b200_type_t get_b200_type(const mboard_eeprom_t &mb_eeprom)
return B2X0_PRODUCT_ID[product_id];
}
+std::vector<usb_device_handle::sptr> get_b200_device_handles(const device_addr_t &hint)
+{
+ std::vector<usb_device_handle::vid_pid_pair_t> vid_pid_pair_list;
+
+ if(hint.has_key("vid") && hint.has_key("pid") && hint.has_key("type") && hint["type"] == "b200") {
+ vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(uhd::cast::hexstr_cast<boost::uint16_t>(hint.get("vid")),
+ uhd::cast::hexstr_cast<boost::uint16_t>(hint.get("pid"))));
+ } else {
+ vid_pid_pair_list = b200_vid_pid_pairs;
+ }
+
+ //find the usrps and load firmware
+ return usb_device_handle::get_device_list(vid_pid_pair_list);
+}
+
static device_addrs_t b200_find(const device_addr_t &hint)
{
device_addrs_t b200_addrs;
@@ -104,28 +119,14 @@ static device_addrs_t b200_find(const device_addr_t &hint)
if (hint_i.has_key("addr") || hint_i.has_key("resource")) return b200_addrs;
}
- size_t found = 0;
- std::vector<usb_device_handle::vid_pid_pair_t> vid_pid_pair_list;//vid pid pair search list for devices.
-
- if(hint.has_key("vid") && hint.has_key("pid") && hint.has_key("type") && hint["type"] == "b200") {
- vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(uhd::cast::hexstr_cast<boost::uint16_t>(hint.get("vid")),
- uhd::cast::hexstr_cast<boost::uint16_t>(hint.get("pid"))));
- } else {
- vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(B200_VENDOR_ID, B200_PRODUCT_ID));
- vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(B200_VENDOR_NI_ID, B200_PRODUCT_NI_ID));
- vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(B200_VENDOR_NI_ID, B210_PRODUCT_NI_ID));
- }
-
// Important note:
// The get device list calls are nested inside the for loop.
// This allows the usb guts to decontruct when not in use,
// so that re-enumeration after fw load can occur successfully.
// This requirement is a courtesy of libusb1.0 on windows.
-
- //find the usrps and load firmware
- std::vector<usb_device_handle::sptr> uhd_usb_device_vector = usb_device_handle::get_device_list(vid_pid_pair_list);
-
- BOOST_FOREACH(usb_device_handle::sptr handle, uhd_usb_device_vector) {
+ size_t found = 0;
+ std::vector<usb_device_handle::sptr> b200_device_handles = get_b200_device_handles(hint);
+ BOOST_FOREACH(usb_device_handle::sptr handle, b200_device_handles) {
//extract the firmware path for the b200
std::string b200_fw_image;
try{
@@ -156,7 +157,7 @@ static device_addrs_t b200_find(const device_addr_t &hint)
//search for the device until found or timeout
while (boost::get_system_time() < timeout_time and b200_addrs.empty() and found != 0)
{
- BOOST_FOREACH(usb_device_handle::sptr handle, usb_device_handle::get_device_list(vid_pid_pair_list))
+ BOOST_FOREACH(usb_device_handle::sptr handle, b200_device_handles)
{
usb_control::sptr control;
try{control = usb_control::make(handle, 0);}
diff --git a/host/lib/usrp/b200/b200_impl.hpp b/host/lib/usrp/b200/b200_impl.hpp
index 11c3da0d9..a3c93e22e 100644
--- a/host/lib/usrp/b200/b200_impl.hpp
+++ b/host/lib/usrp/b200/b200_impl.hpp
@@ -43,6 +43,7 @@
#include <uhd/usrp/gps_ctrl.hpp>
#include <uhd/transport/usb_zero_copy.hpp>
#include <uhd/transport/bounded_buffer.hpp>
+#include <boost/assign.hpp>
#include <boost/weak_ptr.hpp>
#include "recv_packet_demuxer_3000.hpp"
static const boost::uint8_t B200_FW_COMPAT_NUM_MAJOR = 8;
@@ -82,9 +83,18 @@ static const boost::uint32_t B200_RX_GPS_UART_SID = FLIP_SID(B200_TX_GPS_UART_SI
static const boost::uint32_t B200_LOCAL_CTRL_SID = 0x00000040;
static const boost::uint32_t B200_LOCAL_RESP_SID = FLIP_SID(B200_LOCAL_CTRL_SID);
-/***********************************************************************
- * The B200 Capability Constants
- **********************************************************************/
+/*
+ * VID/PID pairs for all B2xx products
+ */
+static std::vector<uhd::transport::usb_device_handle::vid_pid_pair_t> b200_vid_pid_pairs =
+ boost::assign::list_of
+ (uhd::transport::usb_device_handle::vid_pid_pair_t(B200_VENDOR_ID, B200_PRODUCT_ID))
+ (uhd::transport::usb_device_handle::vid_pid_pair_t(B200_VENDOR_NI_ID, B200_PRODUCT_NI_ID))
+ (uhd::transport::usb_device_handle::vid_pid_pair_t(B200_VENDOR_NI_ID, B210_PRODUCT_NI_ID))
+ ;
+
+b200_type_t get_b200_type(const uhd::usrp::mboard_eeprom_t &mb_eeprom);
+std::vector<uhd::transport::usb_device_handle::sptr> get_b200_device_handles(const uhd::device_addr_t &hint);
//! Implementation guts
class b200_impl : public uhd::device
diff --git a/host/lib/usrp/e100/e100_impl.cpp b/host/lib/usrp/e100/e100_impl.cpp
index ac419e0e0..6d3c08534 100644
--- a/host/lib/usrp/e100/e100_impl.cpp
+++ b/host/lib/usrp/e100/e100_impl.cpp
@@ -1,5 +1,5 @@
//
-// Copyright 2010-2012,2014 Ettus Research LLC
+// Copyright 2010-2012,2014-2015 Ettus Research LLC
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
@@ -28,6 +28,7 @@
#include <boost/functional/hash.hpp>
#include <boost/assign/list_of.hpp>
#include <fstream>
+#include <iostream>
#include <ctime>
using namespace uhd;
@@ -45,7 +46,7 @@ namespace fs = boost::filesystem;
/***********************************************************************
* Discovery
**********************************************************************/
-static device_addrs_t e100_find(const device_addr_t &hint){
+device_addrs_t e100_find(const device_addr_t &hint){
device_addrs_t e100_addrs;
//return an empty list of addresses when type is set to non-usrp-e
@@ -104,17 +105,10 @@ static const uhd::dict<std::string, std::string> model_to_fpga_file_name = boost
("E110", "usrp_e110_fpga.bin")
;
-/***********************************************************************
- * Structors
- **********************************************************************/
-e100_impl::e100_impl(const uhd::device_addr_t &device_addr){
- _tree = property_tree::make();
- _type = device::USRP;
- _ignore_cal_file = device_addr.has_key("ignore-cal-file");
-
+std::string get_default_e1x0_fpga_image(const uhd::device_addr_t &device_addr){
//read the eeprom so we can determine the hardware
- _dev_i2c_iface = e100_ctrl::make_dev_i2c_iface(E100_I2C_DEV_NODE);
- const mboard_eeprom_t mb_eeprom(*_dev_i2c_iface, E100_EEPROM_MAP_KEY);
+ uhd::i2c_iface::sptr dev_i2c_iface = e100_ctrl::make_dev_i2c_iface(E100_I2C_DEV_NODE);
+ const mboard_eeprom_t mb_eeprom(*dev_i2c_iface, E100_EEPROM_MAP_KEY);
//determine the model string for this device
const std::string model = device_addr.get("model", mb_eeprom.get("model", ""));
@@ -126,7 +120,21 @@ e100_impl::e100_impl(const uhd::device_addr_t &device_addr){
) % model));
//extract the fpga path and compute hash
- const std::string default_fpga_file_name = model_to_fpga_file_name[model];
+ return model_to_fpga_file_name[model];
+}
+
+/***********************************************************************
+ * Structors
+ **********************************************************************/
+e100_impl::e100_impl(const uhd::device_addr_t &device_addr){
+ _tree = property_tree::make();
+ _type = device::USRP;
+ _ignore_cal_file = device_addr.has_key("ignore-cal-file");
+
+ _dev_i2c_iface = e100_ctrl::make_dev_i2c_iface(E100_I2C_DEV_NODE);
+ const mboard_eeprom_t mb_eeprom(*_dev_i2c_iface, E100_EEPROM_MAP_KEY);
+ const std::string default_fpga_file_name = get_default_e1x0_fpga_image(device_addr);
+ const std::string model = device_addr["model"];
std::string e100_fpga_image;
try{
e100_fpga_image = find_image_path(device_addr.get("fpga", default_fpga_file_name));
diff --git a/host/lib/usrp/e100/e100_impl.hpp b/host/lib/usrp/e100/e100_impl.hpp
index 4efc21427..d00668224 100644
--- a/host/lib/usrp/e100/e100_impl.hpp
+++ b/host/lib/usrp/e100/e100_impl.hpp
@@ -29,6 +29,7 @@
#include "recv_packet_demuxer.hpp"
#include <uhd/device.hpp>
#include <uhd/property_tree.hpp>
+#include <uhd/types/device_addr.hpp>
#include <uhd/usrp/subdev_spec.hpp>
#include <uhd/usrp/dboard_eeprom.hpp>
#include <uhd/usrp/mboard_eeprom.hpp>
@@ -68,6 +69,9 @@ uhd::usrp::dboard_iface::sptr make_e100_dboard_iface(
e100_codec_ctrl::sptr codec
);
+uhd::device_addrs_t e100_find(const uhd::device_addr_t &hint);
+std::string get_default_e1x0_fpga_image(const uhd::device_addr_t &device_addr);
+
/*!
* USRP-E100 implementation guts:
* The implementation details are encapsulated here.
diff --git a/host/lib/usrp/e100/fpga_downloader.cpp b/host/lib/usrp/e100/fpga_downloader.cpp
index c9d77f560..9abde32f7 100644
--- a/host/lib/usrp/e100/fpga_downloader.cpp
+++ b/host/lib/usrp/e100/fpga_downloader.cpp
@@ -1,5 +1,5 @@
//
-// Copyright 2010-2011,2014 Ettus Research LLC
+// Copyright 2010-2011,2014-2015 Ettus Research LLC
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
@@ -17,8 +17,16 @@
#include <uhd/config.hpp>
#ifdef UHD_DLL_EXPORTS
+#include <boost/filesystem.hpp>
+#include <boost/format.hpp>
#include <uhd/exception.hpp>
+#include <uhd/device.hpp>
+#include <uhd/image_loader.hpp>
+#include <uhd/types/device_addr.hpp>
#include <uhd/utils/msg.hpp>
+#include <uhd/utils/paths.hpp>
+#include <uhd/utils/static.hpp>
+#include "e100_impl.hpp"
#else //special case when this file is externally included
#include <stdexcept>
#include <iostream>
@@ -270,3 +278,34 @@ void e100_load_fpga(const std::string &bin_file){
}
+#ifdef UHD_DLL_EXPORTS
+namespace fs = boost::filesystem;
+
+static bool e100_image_loader(const uhd::image_loader::image_loader_args_t &image_loader_args){
+ // Make sure this is an E1x0
+ uhd::device_addrs_t devs = e100_find(uhd::device_addr_t());
+ if(devs.size() == 0 or !image_loader_args.load_fpga) return false;
+
+ std::string fpga_filename;
+ if(image_loader_args.fpga_path == ""){
+ fpga_filename = uhd::find_image_path(get_default_e1x0_fpga_image(devs[0]));
+ }
+ else{
+ if(not fs::exists(image_loader_args.fpga_path)){
+ throw uhd::runtime_error(str(boost::format("The path \"%s\" does not exist.")
+ % image_loader_args.fpga_path));
+ }
+ else fpga_filename = image_loader_args.fpga_path;
+ }
+
+ e100_load_fpga(fpga_filename);
+ return true;
+}
+
+UHD_STATIC_BLOCK(register_e100_image_loader){
+ std::string recovery_instructions = "The default FPGA image will be loaded the next time "
+ "UHD uses this device.";
+
+ uhd::image_loader::register_image_loader("e100", e100_image_loader, recovery_instructions);
+}
+#endif /* UHD_DLL_EXPORTS */
diff --git a/host/lib/usrp/e300/e300_common.cpp b/host/lib/usrp/e300/e300_common.cpp
index db5b37055..29117e21f 100644
--- a/host/lib/usrp/e300/e300_common.cpp
+++ b/host/lib/usrp/e300/e300_common.cpp
@@ -1,5 +1,5 @@
//
-// Copyright 2014 Ettus Research LLC
+// Copyright 2014-2015 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
@@ -14,8 +14,12 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
+#include <uhd/image_loader.hpp>
#include <uhd/utils/msg.hpp>
+#include <uhd/utils/paths.hpp>
+#include <uhd/utils/static.hpp>
+#include "e300_impl.hpp"
#include "e300_fifo_config.hpp"
#include "e300_fifo_config.hpp"
@@ -23,6 +27,7 @@
#include <boost/filesystem.hpp>
#include <fstream>
+#include <string>
namespace uhd { namespace usrp { namespace e300 {
@@ -54,6 +59,34 @@ void load_fpga_image(const std::string &path)
UHD_MSG(status) << " done" << std::endl;
}
+static bool e300_image_loader(const image_loader::image_loader_args_t &image_loader_args) {
+ // Make sure this is an E3x0 and we don't want to use anything connected
+ uhd::device_addrs_t devs = e300_find(image_loader_args.args);
+ if(devs.size() == 0 or !image_loader_args.load_fpga) return false;
+
+ std::string fpga_filename, idle_image; // idle_image never used, just needed for function
+ if(image_loader_args.fpga_path == "") {
+ get_e3x0_fpga_images(devs[0], fpga_filename, idle_image);
+ }
+ else {
+ if(not boost::filesystem::exists(image_loader_args.fpga_path)) {
+ throw uhd::runtime_error(str(boost::format("The path \"%s\" does not exist.")
+ % image_loader_args.fpga_path));
+ }
+ else fpga_filename = image_loader_args.fpga_path;
+ }
+
+ load_fpga_image(fpga_filename);
+ return true;
+}
+
+UHD_STATIC_BLOCK(register_e300_image_loader) {
+ std::string recovery_instructions = "The default FPGA image will be loaded the next "
+ "time UHD uses this device.";
+
+ image_loader::register_image_loader("e3x0", e300_image_loader, recovery_instructions);
+}
+
}
}}}
diff --git a/host/lib/usrp/e300/e300_impl.cpp b/host/lib/usrp/e300/e300_impl.cpp
index de2357100..5e3f4c575 100644
--- a/host/lib/usrp/e300/e300_impl.cpp
+++ b/host/lib/usrp/e300/e300_impl.cpp
@@ -1,5 +1,5 @@
//
-// Copyright 2013-2014 Ettus Research LLC
+// Copyright 2013-2015 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
@@ -121,7 +121,7 @@ static bool is_loopback(const if_addrs_t &if_addrs)
return if_addrs.inet == asio::ip::address_v4::loopback().to_string();
}
-static device_addrs_t e300_find(const device_addr_t &multi_dev_hint)
+device_addrs_t e300_find(const device_addr_t &multi_dev_hint)
{
// handle multi device discovery
device_addrs_t hints = separate_device_addr(multi_dev_hint);
@@ -268,6 +268,36 @@ static device::sptr e300_make(const device_addr_t &device_addr)
return device::sptr(new e300_impl(device_addr));
}
+// Common code used by e300_impl and e300_image_loader
+void get_e3x0_fpga_images(const uhd::device_addr_t &device_addr,
+ std::string &fpga_image,
+ std::string &idle_image){
+ const boost::uint16_t pid = boost::lexical_cast<boost::uint16_t>(
+ device_addr["product"]);
+
+ //extract the FPGA path for the e300
+ switch(e300_eeprom_manager::get_mb_type(pid)) {
+ case e300_eeprom_manager::USRP_E310_MB:
+ fpga_image = device_addr.cast<std::string>("fpga",
+ find_image_path(E310_FPGA_FILE_NAME));
+ idle_image = find_image_path(E310_FPGA_IDLE_FILE_NAME);
+ break;
+ case e300_eeprom_manager::USRP_E300_MB:
+ fpga_image = device_addr.cast<std::string>("fpga",
+ find_image_path(E300_FPGA_FILE_NAME));
+ idle_image = find_image_path(E300_FPGA_IDLE_FILE_NAME);
+ break;
+ case e300_eeprom_manager::UNKNOWN:
+ default:
+ UHD_MSG(warning) << "Unknown motherboard type, loading e300 image."
+ << std::endl;
+ fpga_image = device_addr.cast<std::string>("fpga",
+ find_image_path(E300_FPGA_FILE_NAME));
+ idle_image = find_image_path(E300_FPGA_IDLE_FILE_NAME);
+ break;
+ }
+}
+
/***********************************************************************
* Structors
**********************************************************************/
@@ -286,33 +316,10 @@ e300_impl::e300_impl(const uhd::device_addr_t &device_addr)
if (_xport_path == AXI) {
_do_not_reload = device_addr.has_key("no_reload_fpga");
if (not _do_not_reload) {
- // Load FPGA image if provided via args
- const boost::uint16_t pid = boost::lexical_cast<boost::uint16_t>(
- device_addr["product"]);
-
std::string fpga_image;
-
- //extract the FPGA path for the e300
- switch(e300_eeprom_manager::get_mb_type(pid)) {
- case e300_eeprom_manager::USRP_E310_MB:
- fpga_image = device_addr.cast<std::string>("fpga",
- find_image_path(E310_FPGA_FILE_NAME));
- _idle_image = find_image_path(E310_FPGA_IDLE_FILE_NAME);
- break;
- case e300_eeprom_manager::USRP_E300_MB:
- fpga_image = device_addr.cast<std::string>("fpga",
- find_image_path(E300_FPGA_FILE_NAME));
- _idle_image = find_image_path(E300_FPGA_IDLE_FILE_NAME);
- break;
- case e300_eeprom_manager::UNKNOWN:
- default:
- UHD_MSG(warning) << "Unknown motherboard type, loading e300 image."
- << std::endl;
- fpga_image = device_addr.cast<std::string>("fpga",
- find_image_path(E300_FPGA_FILE_NAME));
- _idle_image = find_image_path(E300_FPGA_IDLE_FILE_NAME);
- break;
- }
+ get_e3x0_fpga_images(device_addr,
+ fpga_image,
+ _idle_image);
common::load_fpga_image(fpga_image);
}
}
diff --git a/host/lib/usrp/e300/e300_impl.hpp b/host/lib/usrp/e300/e300_impl.hpp
index c530a5d72..3ed133489 100644
--- a/host/lib/usrp/e300/e300_impl.hpp
+++ b/host/lib/usrp/e300/e300_impl.hpp
@@ -1,5 +1,5 @@
//
-// Copyright 2013-2014 Ettus Research LLC
+// Copyright 2013-2015 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
@@ -20,6 +20,7 @@
#include <uhd/device.hpp>
#include <uhd/property_tree.hpp>
+#include <uhd/types/device_addr.hpp>
#include <uhd/usrp/subdev_spec.hpp>
#include <uhd/usrp/mboard_eeprom.hpp>
#include <uhd/usrp/dboard_eeprom.hpp>
@@ -28,6 +29,7 @@
#include <uhd/types/sensors.hpp>
#include <boost/weak_ptr.hpp>
#include <boost/thread/mutex.hpp>
+#include <string>
#include "e300_fifo_config.hpp"
#include "radio_ctrl_core_3000.hpp"
#include "rx_frontend_core_200.hpp"
@@ -98,6 +100,10 @@ static const size_t E300_R1_CTRL_STREAM = (1 << 2) | E300_RADIO_DEST_PREFIX_C
static const size_t E300_R1_TX_DATA_STREAM = (1 << 2) | E300_RADIO_DEST_PREFIX_TX;
static const size_t E300_R1_RX_DATA_STREAM = (1 << 2) | E300_RADIO_DEST_PREFIX_RX;
+uhd::device_addrs_t e300_find(const uhd::device_addr_t &multi_dev_hint);
+void get_e3x0_fpga_images(const uhd::device_addr_t &device_args,
+ std::string &fpga_image,
+ std::string &idle_image);
/*!
* USRP-E300 implementation guts:
diff --git a/host/lib/usrp/usrp2/CMakeLists.txt b/host/lib/usrp/usrp2/CMakeLists.txt
index c6257c7fe..bd302895b 100644
--- a/host/lib/usrp/usrp2/CMakeLists.txt
+++ b/host/lib/usrp/usrp2/CMakeLists.txt
@@ -1,5 +1,5 @@
#
-# Copyright 2011-2012,2014 Ettus Research LLC
+# Copyright 2011-2012,2014-2015 Ettus Research LLC
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -25,18 +25,6 @@
LIBUHD_REGISTER_COMPONENT("USRP2" ENABLE_USRP2 ON "ENABLE_LIBUHD" OFF)
IF(ENABLE_USRP2)
- ########################################################################
- # Define UHD_PKG_DATA_PATH for usrp2_iface.cpp
- ########################################################################
- FILE(TO_NATIVE_PATH ${CMAKE_INSTALL_PREFIX} UHD_PKG_PATH)
- STRING(REPLACE "\\" "\\\\" UHD_PKG_PATH ${UHD_PKG_PATH})
-
- SET_SOURCE_FILES_PROPERTIES(
- ${CMAKE_CURRENT_SOURCE_DIR}/usrp2_iface.cpp
- PROPERTIES COMPILE_DEFINITIONS
- "UHD_LIB_DIR=\"lib${LIB_SUFFIX}\""
- )
-
LIBUHD_APPEND_SOURCES(
${CMAKE_CURRENT_SOURCE_DIR}/clock_ctrl.cpp
${CMAKE_CURRENT_SOURCE_DIR}/codec_ctrl.cpp
@@ -45,5 +33,6 @@ IF(ENABLE_USRP2)
${CMAKE_CURRENT_SOURCE_DIR}/usrp2_iface.cpp
${CMAKE_CURRENT_SOURCE_DIR}/usrp2_impl.cpp
${CMAKE_CURRENT_SOURCE_DIR}/usrp2_fifo_ctrl.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/n200_image_loader.cpp
)
ENDIF(ENABLE_USRP2)
diff --git a/host/lib/usrp/usrp2/n200_image_loader.cpp b/host/lib/usrp/usrp2/n200_image_loader.cpp
new file mode 100644
index 000000000..ce956c22c
--- /dev/null
+++ b/host/lib/usrp/usrp2/n200_image_loader.cpp
@@ -0,0 +1,616 @@
+//
+// Copyright 2015 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 <cstring>
+#include <iostream>
+#include <fstream>
+
+#include <boost/asio/ip/address_v4.hpp>
+#include <boost/assign.hpp>
+#include <boost/filesystem.hpp>
+#include <boost/format.hpp>
+#include <boost/thread.hpp>
+#include <boost/algorithm/string/erase.hpp>
+
+#include <uhd/config.hpp>
+#include <uhd/image_loader.hpp>
+#include <uhd/exception.hpp>
+#include <uhd/transport/if_addrs.hpp>
+#include <uhd/transport/udp_simple.hpp>
+#include <uhd/utils/byteswap.hpp>
+#include <uhd/utils/paths.hpp>
+#include <uhd/utils/static.hpp>
+#include <uhd/types/dict.hpp>
+
+#include "fw_common.h"
+#include "usrp2_iface.hpp"
+#include "usrp2_impl.hpp"
+
+typedef boost::asio::ip::address_v4 ip_v4;
+
+namespace fs = boost::filesystem;
+using namespace boost::algorithm;
+
+using namespace uhd;
+using namespace uhd::usrp;
+using namespace uhd::transport;
+
+/*
+ * Constants
+ */
+
+#define N200_FLASH_DATA_PACKET_SIZE 256
+#define N200_UDP_FW_UPDATE_PORT 49154
+#define UDP_TIMEOUT 0.5
+
+#define N200_FW_MAX_SIZE_BYTES 31744
+#define N200_PROD_FW_IMAGE_ADDR 0x00300000
+#define N200_SAFE_FW_IMAGE_ADDR 0x003F0000
+
+#define N200_FPGA_MAX_SIZE_BYTES 1572864
+#define N200_PROD_FPGA_IMAGE_ADDR 0x00180000
+#define N200_SAFE_FPGA_IMAGE_ADDR 0x00000000
+
+/*
+ * Packet codes
+ */
+typedef enum {
+ UNKNOWN = ' ',
+
+ N200_QUERY = 'a',
+ N200_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_CMD = 's',
+ RESET_ACK = 'S',
+
+ GET_HW_REV_CMD = 'v',
+ GET_HW_REV_ACK = 'V',
+} n200_fw_update_id_t;
+
+/*
+ * Mapping revision numbers to names
+ */
+static const uhd::dict<boost::uint32_t, std::string> n200_filename_map = boost::assign::map_list_of
+ (0, "n2xx") // Is an N-Series, but the EEPROM value is invalid
+ (0xa, "n200_r3")
+ (0x100a, "n200_r4")
+ (0x10a, "n210_r3")
+ (0x110a, "n210_r4")
+;
+
+/*
+ * Packet structure
+ */
+typedef struct {
+ boost::uint32_t proto_ver;
+ boost::uint32_t id;
+ boost::uint32_t seq;
+ union {
+ boost::uint32_t ip_addr;
+ boost::uint32_t hw_rev;
+ struct {
+ boost::uint32_t flash_addr;
+ boost::uint32_t length;
+ boost::uint8_t data[256];
+ } flash_args;
+ struct {
+ boost::uint32_t sector_size_bytes;
+ boost::uint32_t memory_size_bytes;
+ } flash_info_args;
+ } data;
+} n200_fw_update_data_t;
+
+/*
+ * N-Series burn session
+ */
+typedef struct {
+ bool fw;
+ bool overwrite_safe;
+ bool reset;
+ uhd::device_addr_t dev_addr;
+ std::string burn_type;
+ std::string filepath;
+ boost::uint8_t data_in[udp_simple::mtu];
+ boost::uint32_t size;
+ boost::uint32_t max_size;
+ boost::uint32_t flash_addr;
+ udp_simple::sptr xport;
+} n200_session_t;
+
+/***********************************************************************
+ * uhd::image_loader functionality
+ **********************************************************************/
+
+static void print_usrp2_error(const image_loader::image_loader_args_t &image_loader_args){
+ #ifdef UHD_PLATFORM_WIN32
+ std::string usrp2_card_burner_gui = "\"";
+ const std::string nl = " ^\n ";
+ #else
+ std::string usrp2_card_burner_gui = "sudo \"";
+ const std::string nl = " \\\n ";
+ #endif
+
+ usrp2_card_burner_gui += find_utility("usrp2_card_burner_gui.py");
+ usrp2_card_burner_gui += "\"";
+
+ if(image_loader_args.load_firmware){
+ usrp2_card_burner_gui += str(boost::format("%s--fw=\"%s\"")
+ % nl
+ % ((image_loader_args.firmware_path == "")
+ ? find_image_path("usrp2_fw.bin")
+ : image_loader_args.firmware_path));
+ }
+ if(image_loader_args.load_fpga){
+ usrp2_card_burner_gui += str(boost::format("%s--fpga=\"%s\"")
+ % nl
+ % ((image_loader_args.fpga_path == "")
+ ? find_image_path("usrp2_fpga.bin")
+ : image_loader_args.fpga_path));
+ }
+
+ throw uhd::runtime_error(str(boost::format("The specified device is a USRP2, which is not supported by this utility.\n"
+ "Instead, plug the device's SD card into your machine and run this command:\n\n"
+ "%s"
+ ) % usrp2_card_burner_gui));
+}
+
+/*
+ * Ethernet communication functions
+ */
+static UHD_INLINE size_t n200_send_and_recv(udp_simple::sptr xport,
+ n200_fw_update_id_t pkt_code,
+ n200_fw_update_data_t *pkt_out,
+ boost::uint8_t* data){
+ pkt_out->proto_ver = htonx<boost::uint32_t>(USRP2_FW_COMPAT_NUM);
+ pkt_out->id = htonx<boost::uint32_t>(pkt_code);
+ xport->send(boost::asio::buffer(pkt_out, sizeof(*pkt_out)));
+ return xport->recv(boost::asio::buffer(data, udp_simple::mtu), UDP_TIMEOUT);
+}
+
+static UHD_INLINE bool n200_response_matches(const n200_fw_update_data_t *pkt_in,
+ n200_fw_update_id_t pkt_code,
+ size_t len){
+ return (len > offsetof(n200_fw_update_data_t, data) and
+ ntohl(pkt_in->id) == pkt_code);
+}
+
+static uhd::device_addr_t n200_find(const image_loader::image_loader_args_t &image_loader_args){
+ bool user_specified = image_loader_args.args.has_key("addr") or
+ image_loader_args.args.has_key("serial") or
+ image_loader_args.args.has_key("name");
+
+ uhd::device_addrs_t found = usrp2_find(image_loader_args.args);
+ if(found.size() > 0){
+ uhd::device_addr_t ret = found[0];
+
+ /*
+ * Make sure the device found is an N-Series and not a USRP2. A USRP2
+ * will not respond to this query. If the user supplied specific
+ * arguments that led to a USRP2, throw an error.
+ */
+ udp_simple::sptr rev_xport = udp_simple::make_connected(
+ ret["addr"],
+ BOOST_STRINGIZE(N200_UDP_FW_UPDATE_PORT)
+ );
+
+ n200_fw_update_data_t pkt_out;
+ boost::uint8_t data_in[udp_simple::mtu];
+ const n200_fw_update_data_t *pkt_in = reinterpret_cast<const n200_fw_update_data_t*>(data_in);
+
+ size_t len = n200_send_and_recv(rev_xport, GET_HW_REV_CMD, &pkt_out, data_in);
+ if(n200_response_matches(pkt_in, GET_HW_REV_ACK, len)){
+ boost::uint32_t rev = ntohl(pkt_in->data.hw_rev);
+ ret["hw_rev"] = n200_filename_map.get(rev, "n2xx");
+ return ret;
+ }
+ else if(len > offsetof(n200_fw_update_data_t, data) and ntohl(pkt_in->id) != GET_HW_REV_ACK){
+ throw uhd::runtime_error(str(boost::format("Received invalid reply %d from device.")
+ % ntohl(pkt_in->id)));
+ }
+ else if(user_specified){
+ // At this point, we haven't received any response, so assume it's a USRP2
+ print_usrp2_error(image_loader_args);
+ }
+ }
+
+ return uhd::device_addr_t();
+}
+
+/*
+ * Validate and read firmware image
+ */
+static void n200_validate_firmware_image(n200_session_t &session){
+ if(not fs::exists(session.filepath)){
+ throw uhd::runtime_error(str(boost::format("Could not find image at path \"%s\".")
+ % session.filepath));
+ }
+
+ session.size = fs::file_size(session.filepath);
+ session.max_size = N200_FW_MAX_SIZE_BYTES;
+
+ if(session.size > session.max_size){
+ throw uhd::runtime_error(str(boost::format("The specified FPGA image is too large: %d vs. %d")
+ % session.size % session.max_size));
+ }
+
+ // File must have proper header
+ std::ifstream image_file(session.filepath.c_str(), std::ios::binary);
+ boost::uint8_t test_bytes[4];
+ image_file.seekg(0, std::ios::beg);
+ image_file.read((char*)test_bytes,4);
+ image_file.close();
+ for(int i = 0; i < 4; i++) if(test_bytes[i] != 11){
+ throw uhd::runtime_error(str(boost::format("The file at path \"%s\" is not a valid firmware image.")
+ % session.filepath));
+ }
+}
+
+/*
+ * Validate and validate FPGA image
+ */
+static void n200_validate_fpga_image(n200_session_t &session){
+ if(not fs::exists(session.filepath)){
+ throw uhd::runtime_error(str(boost::format("Could not find image at path \"%s\".")
+ % session.filepath));
+ }
+
+ session.size = fs::file_size(session.filepath);
+ session.max_size = N200_FPGA_MAX_SIZE_BYTES;
+
+ if(session.size > session.max_size){
+ throw uhd::runtime_error(str(boost::format("The specified FPGA image is too large: %d vs. %d")
+ % session.size % session.max_size));
+ }
+
+ // File must have proper header
+ std::ifstream image_file(session.filepath.c_str(), std::ios::binary);
+ boost::uint8_t test_bytes[63];
+ image_file.seekg(0, std::ios::beg);
+ image_file.read((char*)test_bytes, 63);
+ bool is_good = false;
+ for(int i = 0; i < 63; i++){
+ if(test_bytes[i] == 255) continue;
+ else if(test_bytes[i] == 170 and
+ test_bytes[i+1] == 153){
+ is_good = true;
+ break;
+ }
+ }
+ image_file.close();
+ if(not is_good){
+ throw uhd::runtime_error(str(boost::format("The file at path \"%s\" is not a valid FPGA image.")
+ % session.filepath));
+ }
+}
+
+/*
+ * Set up a session for burning an N-Series image. This session info
+ * will be passed into the erase, burn, and verify functions.
+ */
+static void n200_setup_session(n200_session_t &session,
+ const image_loader::image_loader_args_t &image_loader_args,
+ bool fw){
+
+
+ session.fw = fw;
+ session.reset = image_loader_args.args.has_key("reset");
+
+ /*
+ * If no filepath is given, attempt to determine the default image by
+ * querying the device for its revision. If the device has a corrupt
+ * EEPROM or is otherwise unable to provide its revision, this is
+ * impossible, and the user must manually provide a firmware file.
+ */
+ if((session.fw and image_loader_args.firmware_path == "") or
+ image_loader_args.fpga_path == ""){
+ if(session.dev_addr["hw_rev"] == "n2xx"){
+ throw uhd::runtime_error("This device's revision cannot be determined. "
+ "You must manually specify a filepath.");
+ }
+ else{
+ session.filepath = session.fw ? find_image_path(str(boost::format("usrp_%s_fw.bin")
+ % erase_tail_copy(session.dev_addr["hw_rev"],3)))
+ : find_image_path(str(boost::format("usrp_%s_fpga.bin")
+ % session.dev_addr["hw_rev"]));
+ }
+ }
+ else{
+ session.filepath = session.fw ? image_loader_args.firmware_path
+ : image_loader_args.fpga_path;
+ }
+ if(session.fw) n200_validate_firmware_image(session);
+ else n200_validate_fpga_image(session);
+
+ session.overwrite_safe = image_loader_args.args.has_key("overwrite-safe");
+ if(session.overwrite_safe){
+ session.flash_addr = session.fw ? N200_SAFE_FW_IMAGE_ADDR
+ : N200_SAFE_FPGA_IMAGE_ADDR;
+ session.burn_type = session.fw ? "firmware safe"
+ : "FPGA safe";
+ }
+ else{
+ session.flash_addr = session.fw ? N200_PROD_FW_IMAGE_ADDR
+ : N200_PROD_FPGA_IMAGE_ADDR;
+ session.burn_type = session.fw ? "firmware"
+ : "FPGA";
+ }
+
+ session.xport = udp_simple::make_connected(session.dev_addr["addr"],
+ BOOST_STRINGIZE(N200_UDP_FW_UPDATE_PORT));
+}
+
+static void n200_erase_image(n200_session_t &session){
+
+ // UDP receive buffer
+ n200_fw_update_data_t pkt_out;
+ const n200_fw_update_data_t *pkt_in = reinterpret_cast<const n200_fw_update_data_t*>(session.data_in);
+
+ // Setting up UDP packet
+ pkt_out.data.flash_args.flash_addr = htonx<boost::uint32_t>(session.flash_addr);
+ pkt_out.data.flash_args.length = htonx<boost::uint32_t>(session.size);
+
+ // Begin erasing
+ size_t len = n200_send_and_recv(session.xport, ERASE_FLASH_CMD, &pkt_out, session.data_in);
+ if(n200_response_matches(pkt_in, ERASE_FLASH_ACK, len)){
+ std::cout << boost::format("-- Erasing %s image...") % session.burn_type << std::flush;
+ }
+ else if(len < offsetof(n200_fw_update_data_t, data)){
+ std::cout << "failed." << std::endl;
+ throw uhd::runtime_error("Timed out waiting for reply from device.");
+ }
+ else if(ntohl(pkt_in->id) != ERASE_FLASH_ACK){
+ std::cout << "failed." << std::endl;
+ throw uhd::runtime_error(str(boost::format("Received invalid reply %d from device.\n")
+ % ntohl(pkt_in->id)));
+ }
+ else{
+ std::cout << "failed." << std::endl;
+ throw uhd::runtime_error("Did not receive response from device.");
+ }
+
+ // Check for erase completion
+ while(true){
+ len = n200_send_and_recv(session.xport, CHECK_ERASING_DONE_CMD, &pkt_out, session.data_in);
+ if(n200_response_matches(pkt_in, DONE_ERASING_ACK, len)){
+ std::cout << "successful." << std::endl;
+ break;
+ }
+ else if(len < offsetof(n200_fw_update_data_t, data)){
+ std::cout << "failed." << std::endl;
+ throw uhd::runtime_error("Timed out waiting for reply from device.");
+ }
+ else if(ntohl(pkt_in->id) != NOT_DONE_ERASING_ACK){
+ std::cout << "failed." << std::endl;
+ throw uhd::runtime_error(str(boost::format("Received invalid reply %d from device.\n")
+ % ntohl(pkt_in->id)));
+ }
+ }
+}
+
+static void n200_write_image(n200_session_t &session){
+
+ // UDP receive buffer
+ n200_fw_update_data_t pkt_out;
+ const n200_fw_update_data_t *pkt_in = reinterpret_cast<const n200_fw_update_data_t*>(session.data_in);
+ size_t len = 0;
+
+ // Write image
+ std::ifstream image(session.filepath.c_str(), std::ios::binary);
+ boost::uint32_t current_addr = session.flash_addr;
+ pkt_out.data.flash_args.length = htonx<boost::uint32_t>(N200_FLASH_DATA_PACKET_SIZE);
+ for(size_t i = 0; i < ((session.size/N200_FLASH_DATA_PACKET_SIZE)+1); i++){
+ pkt_out.data.flash_args.flash_addr = htonx<boost::uint32_t>(current_addr);
+ memset(pkt_out.data.flash_args.data, 0x0, N200_FLASH_DATA_PACKET_SIZE);
+ image.read((char*)pkt_out.data.flash_args.data, N200_FLASH_DATA_PACKET_SIZE);
+
+ len = n200_send_and_recv(session.xport, WRITE_FLASH_CMD, &pkt_out, session.data_in);
+ if(n200_response_matches(pkt_in, WRITE_FLASH_ACK, len)){
+ std::cout << boost::format("\r-- Writing %s image (%d%%)")
+ % session.burn_type
+ % int((double(current_addr-session.flash_addr)/double(session.size))*100)
+ << std::flush;
+ }
+ else if(len < offsetof(n200_fw_update_data_t, data)){
+ image.close();
+ std::cout << boost::format("\r--Writing %s image..failed at %d%%.")
+ % session.burn_type
+ % int((double(current_addr-session.flash_addr)/double(session.size))*100)
+ << std::endl;
+ throw uhd::runtime_error("Timed out waiting for reply from device.");
+ }
+ else if(ntohl(pkt_in->id) != WRITE_FLASH_ACK){
+ image.close();
+ std::cout << boost::format("\r--Writing %s image..failed at %d%%.")
+ % session.burn_type
+ % int((double(current_addr-session.flash_addr)/double(session.size))*100)
+ << std::endl;
+ throw uhd::runtime_error(str(boost::format("Received invalid reply %d from device.\n")
+ % ntohl(pkt_in->id)));
+ }
+
+ current_addr += N200_FLASH_DATA_PACKET_SIZE;
+ }
+ std::cout << boost::format("\r-- Writing %s image...successful.")
+ % session.burn_type
+ << std::endl;
+
+ image.close();
+}
+
+static void n200_verify_image(n200_session_t &session){
+
+ // UDP receive buffer
+ n200_fw_update_data_t pkt_out;
+ const n200_fw_update_data_t *pkt_in = reinterpret_cast<const n200_fw_update_data_t*>(session.data_in);
+ size_t len = 0;
+
+ // Read and verify image
+ std::ifstream image(session.filepath.c_str(), std::ios::binary);
+ boost::uint8_t image_part[N200_FLASH_DATA_PACKET_SIZE];
+ boost::uint32_t current_addr = session.flash_addr;
+ pkt_out.data.flash_args.length = htonx<boost::uint32_t>(N200_FLASH_DATA_PACKET_SIZE);
+ boost::uint16_t cmp_len = 0;
+ for(size_t i = 0; i < ((session.size/N200_FLASH_DATA_PACKET_SIZE)+1); i++){
+ memset(image_part, 0x0, N200_FLASH_DATA_PACKET_SIZE);
+ memset((void*)pkt_in->data.flash_args.data, 0x0, N200_FLASH_DATA_PACKET_SIZE);
+
+ pkt_out.data.flash_args.flash_addr = htonx<boost::uint32_t>(current_addr);
+ image.read((char*)image_part, N200_FLASH_DATA_PACKET_SIZE);
+ cmp_len = image.gcount();
+
+ len = n200_send_and_recv(session.xport, READ_FLASH_CMD, &pkt_out, session.data_in);
+ if(n200_response_matches(pkt_in, READ_FLASH_ACK, len)){
+ std::cout << boost::format("\r-- Verifying %s image (%d%%)")
+ % session.burn_type
+ % int((double(current_addr-session.flash_addr)/double(session.size))*100)
+ << std::flush;
+
+ if(memcmp(image_part, pkt_in->data.flash_args.data, cmp_len)){
+ std::cout << boost::format("\r-- Verifying %s image...failed at %d%%.")
+ % session.burn_type
+ % int((double(current_addr-session.flash_addr)/double(session.size))*100)
+ << std::endl;
+ throw uhd::runtime_error(str(boost::format("Failed to verify %s image.")
+ % session.burn_type));
+ }
+ }
+ else if(len < offsetof(n200_fw_update_data_t, data)){
+ image.close();
+ std::cout << boost::format("\r-- Verifying %s image...failed at %d%%.")
+ % session.burn_type
+ % int((double(current_addr-session.flash_addr)/double(session.size))*100)
+ << std::endl;
+ throw uhd::runtime_error("Timed out waiting for reply from device.");
+ }
+ else if(ntohl(pkt_in->id) != READ_FLASH_ACK){
+ image.close();
+ std::cout << boost::format("\r-- Verifying %s image...failed at %d%%.")
+ % session.burn_type
+ % int((double(current_addr-session.flash_addr)/double(session.size))*100)
+ << std::endl;
+ throw uhd::runtime_error(str(boost::format("Received invalid reply %d from device.\n")
+ % ntohl(pkt_in->id)));
+ }
+
+ current_addr += N200_FLASH_DATA_PACKET_SIZE;
+ }
+ std::cout << boost::format("\r-- Verifying %s image...successful.") % session.burn_type
+ << std::endl;
+
+ image.close();
+}
+
+static void n200_reset(n200_session_t &session){
+
+ // UDP receive buffer
+ n200_fw_update_data_t pkt_out;
+
+ // There should be no response
+ std::cout << "-- Resetting device..." << std::flush;
+ size_t len = n200_send_and_recv(session.xport, RESET_CMD, &pkt_out, session.data_in);
+ if(len > 0){
+ std::cout << "failed." << std::endl;
+ throw uhd::runtime_error("Failed to reset N200.");
+ }
+ std::cout << "successful." << std::endl;
+}
+
+// n210_r4 -> N210 r4
+static std::string nice_name(const std::string &fw_rev){
+ std::string ret = fw_rev;
+ ret[0] = ::toupper(ret[0]);
+
+ size_t pos = 0;
+ if((pos = fw_rev.find("_")) != std::string::npos){
+ ret[pos] = ' ';
+ }
+
+ return ret;
+}
+
+static bool n200_image_loader(const image_loader::image_loader_args_t &image_loader_args){
+ // See if any N2x0 with the given args is found
+ // This will throw if specific args lead to a USRP2
+ n200_session_t session;
+ session.dev_addr = n200_find(image_loader_args);
+ if(session.dev_addr.size() == 0 or (!image_loader_args.load_firmware and !image_loader_args.load_fpga)){
+ return false;
+ }
+
+ std::cout << boost::format("Unit: USRP %s (%s, %s)")
+ % nice_name(session.dev_addr.get("hw_rev"))
+ % session.dev_addr.get("serial")
+ % session.dev_addr.get("addr")
+ << std::endl;
+
+ if(image_loader_args.load_firmware){
+ n200_setup_session(session,
+ image_loader_args,
+ true
+ );
+
+ std::cout << "Firmware image: " << session.filepath << std::endl;
+
+ n200_erase_image(session);
+ n200_write_image(session);
+ n200_verify_image(session);
+ if(session.reset and !image_loader_args.load_fpga){
+ n200_reset(session);
+ }
+ }
+ if(image_loader_args.load_fpga){
+ n200_setup_session(session,
+ image_loader_args,
+ false
+ );
+
+ std::cout << "FPGA image: " << session.filepath << std::endl;
+
+ n200_erase_image(session);
+ n200_write_image(session);
+ n200_verify_image(session);
+ if(session.reset){
+ n200_reset(session);
+ }
+ }
+
+ return true;
+}
+
+UHD_STATIC_BLOCK(register_n200_image_loader){
+ std::string recovery_instructions = "Aborting. Your USRP-N Series unit will likely be unusable.\n"
+ "Refer to http://files.ettus.com/manual/page_usrp2.html#usrp2_loadflash_brick\n"
+ "for details on restoring your device.";
+
+ image_loader::register_image_loader("usrp2", n200_image_loader, recovery_instructions);
+}
diff --git a/host/lib/usrp/usrp2/usrp2_iface.cpp b/host/lib/usrp/usrp2/usrp2_iface.cpp
index 1d41173f8..2b382ae38 100644
--- a/host/lib/usrp/usrp2/usrp2_iface.cpp
+++ b/host/lib/usrp/usrp2/usrp2_iface.cpp
@@ -387,15 +387,15 @@ public:
//create the burner commands
if (this->get_rev() == USRP2_REV3 or this->get_rev() == USRP2_REV4){
- const std::string card_burner = (fs::path(uhd::get_pkg_path()) / UHD_LIB_DIR / "uhd" / "utils" / "usrp2_card_burner_gui.py").string();
- const std::string card_burner_cmd = str(boost::format("\"%s%s\" %s--fpga=\"%s\" %s--fw=\"%s\"") % sudo % card_burner % ml % fpga_image_path % ml % fw_image_path);
+ const std::string card_burner = uhd::find_utility("usrp2_card_burner_gui.py");
+ const std::string card_burner_cmd = str(boost::format(" %s\"%s\" %s--fpga=\"%s\" %s--fw=\"%s\"") % sudo % card_burner % ml % fpga_image_path % ml % fw_image_path);
return str(boost::format("%s\n%s") % print_utility_error("uhd_images_downloader.py") % card_burner_cmd);
}
else{
const std::string addr = _ctrl_transport->get_recv_addr();
- const std::string net_burner_path = (fs::path(uhd::get_pkg_path()) / UHD_LIB_DIR / "uhd" / "utils" / "usrp_n2xx_simple_net_burner").string();
- const std::string net_burner_cmd = str(boost::format("\"%s\" %s--addr=\"%s\"") % net_burner_path % ml % addr);
- return str(boost::format("%s\n%s") % print_utility_error("uhd_images_downloader.py") % net_burner_cmd);
+ const std::string image_loader_path = (fs::path(uhd::get_pkg_path()) / "bin" / "uhd_image_loader").string();
+ const std::string image_loader_cmd = str(boost::format(" \"%s\" %s--args=\"type=usrp2,addr=%s\"") % image_loader_path % ml % addr);
+ return str(boost::format("%s\n%s") % print_utility_error("uhd_images_downloader.py") % image_loader_cmd);
}
}
diff --git a/host/lib/usrp/usrp2/usrp2_impl.cpp b/host/lib/usrp/usrp2/usrp2_impl.cpp
index 1acc1dad3..6073ec1c0 100644
--- a/host/lib/usrp/usrp2/usrp2_impl.cpp
+++ b/host/lib/usrp/usrp2/usrp2_impl.cpp
@@ -48,7 +48,7 @@ static const size_t DEFAULT_NUM_FRAMES = 32;
/***********************************************************************
* Discovery over the udp transport
**********************************************************************/
-static device_addrs_t usrp2_find(const device_addr_t &hint_){
+device_addrs_t usrp2_find(const device_addr_t &hint_){
//handle the multi-device discovery
device_addrs_t hints = separate_device_addr(hint_);
if (hints.size() > 1){
diff --git a/host/lib/usrp/usrp2/usrp2_impl.hpp b/host/lib/usrp/usrp2/usrp2_impl.hpp
index 701403029..07cd98b4c 100644
--- a/host/lib/usrp/usrp2/usrp2_impl.hpp
+++ b/host/lib/usrp/usrp2/usrp2_impl.hpp
@@ -42,6 +42,7 @@
#include <uhd/transport/vrt_if_packet.hpp>
#include <uhd/transport/udp_simple.hpp>
#include <uhd/transport/udp_zero_copy.hpp>
+#include <uhd/types/device_addr.hpp>
#include <uhd/usrp/dboard_manager.hpp>
#include <uhd/usrp/subdev_spec.hpp>
#include <boost/weak_ptr.hpp>
@@ -55,6 +56,8 @@ static const boost::uint32_t USRP2_TX_ASYNC_SID = 2;
static const boost::uint32_t USRP2_RX_SID_BASE = 3;
static const std::string USRP2_EEPROM_MAP_KEY = "N100";
+uhd::device_addrs_t usrp2_find(const uhd::device_addr_t &hint_);
+
//! Make a usrp2 dboard interface.
uhd::usrp::dboard_iface::sptr make_usrp2_dboard_iface(
uhd::timed_wb_iface::sptr wb_iface,
diff --git a/host/lib/usrp/x300/CMakeLists.txt b/host/lib/usrp/x300/CMakeLists.txt
index a588f901b..15af44721 100644
--- a/host/lib/usrp/x300/CMakeLists.txt
+++ b/host/lib/usrp/x300/CMakeLists.txt
@@ -1,5 +1,5 @@
#
-# Copyright 2013 Ettus Research LLC
+# Copyright 2013,2015 Ettus Research LLC
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -34,5 +34,7 @@ IF(ENABLE_X300)
${CMAKE_CURRENT_SOURCE_DIR}/x300_io_impl.cpp
${CMAKE_CURRENT_SOURCE_DIR}/x300_dboard_iface.cpp
${CMAKE_CURRENT_SOURCE_DIR}/x300_clock_ctrl.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/x300_image_loader.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/cdecode.c
)
ENDIF(ENABLE_X300)
diff --git a/host/lib/usrp/x300/cdecode.c b/host/lib/usrp/x300/cdecode.c
new file mode 100644
index 000000000..1d09cbe22
--- /dev/null
+++ b/host/lib/usrp/x300/cdecode.c
@@ -0,0 +1,80 @@
+/*
+cdecoder.c - c source to a base64 decoding algorithm implementation
+
+This is part of the libb64 project, and has been placed in the public domain.
+For details, see http://sourceforge.net/projects/libb64
+*/
+
+#include "cdecode.h"
+
+int base64_decode_value(char value_in){
+ static const char decoding[] = {62,-1,-1,-1,63,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-2,-1,-1,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51};
+ static const char decoding_size = sizeof(decoding);
+ value_in -= 43;
+ if ((signed char)value_in < 0 || value_in > decoding_size) return -1;
+ return decoding[(int)value_in];
+}
+
+void base64_init_decodestate(base64_decodestate* state_in){
+ state_in->step = step_a;
+ state_in->plainchar = 0;
+}
+
+size_t base64_decode_block(const char* code_in, const size_t length_in, char* plaintext_out, base64_decodestate* state_in){
+ const char* codechar = code_in;
+ char* plainchar = plaintext_out;
+ char fragment;
+
+ *plainchar = state_in->plainchar;
+
+ switch (state_in->step){
+ while (1){
+ case step_a:
+ do{
+ if (codechar == code_in+length_in){
+ state_in->step = step_a;
+ state_in->plainchar = *plainchar;
+ return plainchar - plaintext_out;
+ }
+ fragment = (char)base64_decode_value(*codechar++);
+ } while ((signed char)fragment < 0);
+ *plainchar = (fragment & 0x03f) << 2;
+
+ case step_b:
+ do{
+ if (codechar == code_in+length_in){
+ state_in->step = step_b;
+ state_in->plainchar = *plainchar;
+ return plainchar - plaintext_out;
+ }
+ fragment = (char)base64_decode_value(*codechar++);
+ } while ((signed char)fragment < 0);
+ *plainchar++ |= (fragment & 0x030) >> 4;
+ *plainchar = (fragment & 0x00f) << 4;
+ case step_c:
+ do{
+ if (codechar == code_in+length_in)
+ {
+ state_in->step = step_c;
+ state_in->plainchar = *plainchar;
+ return plainchar - plaintext_out;
+ }
+ fragment = (char)base64_decode_value(*codechar++);
+ } while ((signed char)fragment < 0);
+ *plainchar++ |= (fragment & 0x03c) >> 2;
+ *plainchar = (fragment & 0x003) << 6;
+ case step_d:
+ do{
+ if (codechar == code_in+length_in){
+ state_in->step = step_d;
+ state_in->plainchar = *plainchar;
+ return plainchar - plaintext_out;
+ }
+ fragment = (char)base64_decode_value(*codechar++);
+ } while ((signed char)fragment < 0);
+ *plainchar++ |= (fragment & 0x03f);
+ }
+ }
+ /* control should not reach here */
+ return plainchar - plaintext_out;
+}
diff --git a/host/lib/usrp/x300/cdecode.h b/host/lib/usrp/x300/cdecode.h
new file mode 100644
index 000000000..b8da55aa1
--- /dev/null
+++ b/host/lib/usrp/x300/cdecode.h
@@ -0,0 +1,36 @@
+/*
+cdecode.h - c header for a base64 decoding algorithm
+
+This is part of the libb64 project, and has been placed in the public domain.
+For details, see http://sourceforge.net/projects/libb64
+*/
+
+#ifndef BASE64_CDECODE_H
+#define BASE64_CDECODE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stddef.h>
+
+typedef enum{
+ step_a, step_b, step_c, step_d
+} base64_decodestep;
+
+typedef struct{
+ base64_decodestep step;
+ char plainchar;
+} base64_decodestate;
+
+void base64_init_decodestate(base64_decodestate* state_in);
+
+int base64_decode_value(char value_in);
+
+size_t base64_decode_block(const char* code_in, const size_t length_in, char* plaintext_out, base64_decodestate* state_in);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* BASE64_CDECODE_H */
diff --git a/host/lib/usrp/x300/x300_image_loader.cpp b/host/lib/usrp/x300/x300_image_loader.cpp
new file mode 100644
index 000000000..9d92e7932
--- /dev/null
+++ b/host/lib/usrp/x300/x300_image_loader.cpp
@@ -0,0 +1,402 @@
+//
+// Copyright 2015 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 <fstream>
+#include <vector>
+
+#include <boost/algorithm/string.hpp>
+#include <boost/filesystem.hpp>
+#include <boost/property_tree/ptree.hpp>
+#include <boost/property_tree/xml_parser.hpp>
+
+#include <uhd/config.hpp>
+#include <uhd/device.hpp>
+#include <uhd/image_loader.hpp>
+#include <uhd/exception.hpp>
+#include <uhd/transport/udp_simple.hpp>
+#include <uhd/transport/nirio/niusrprio_session.h>
+#include <uhd/transport/nirio/status.h>
+#include <uhd/utils/byteswap.hpp>
+#include <uhd/utils/paths.hpp>
+#include <uhd/utils/static.hpp>
+
+#include "x300_impl.hpp"
+#include "x300_fw_common.h"
+#include "cdecode.h"
+
+namespace fs = boost::filesystem;
+
+using namespace boost::algorithm;
+using namespace uhd;
+using namespace uhd::transport;
+
+/*
+ * Constants
+ */
+#define X300_FPGA_BIN_SIZE_BYTES 15877916
+#define X300_FPGA_BIT_SIZE_BYTES 15878022
+#define X300_FPGA_PROG_UDP_PORT 49157
+#define X300_FLASH_SECTOR_SIZE 131072
+#define X300_PACKET_SIZE_BYTES 256
+#define X300_FPGA_SECTOR_START 32
+#define X300_MAX_RESPONSE_BYTES 128
+#define UDP_TIMEOUT 3
+#define FPGA_LOAD_TIMEOUT 15
+
+/*
+ * Packet structure
+ */
+typedef struct {
+ boost::uint32_t flags;
+ boost::uint32_t sector;
+ boost::uint32_t index;
+ boost::uint32_t size;
+ union {
+ boost::uint8_t data8[X300_PACKET_SIZE_BYTES];
+ boost::uint16_t data16[X300_PACKET_SIZE_BYTES/2];
+ };
+} x300_fpga_update_data_t;
+
+/*
+ * X-Series burn session
+ */
+typedef struct {
+ bool found;
+ bool ethernet;
+ bool configure; // Reload FPGA after burning to flash (Ethernet only)
+ bool verify; // Device will verify the download along the way (Ethernet only)
+ bool lvbitx;
+ uhd::device_addr_t dev_addr;
+ std::string ip_addr;
+ std::string fpga_type;
+ std::string resource;
+ std::string filepath;
+ std::string rpc_port;
+ boost::uint32_t size;
+ udp_simple::sptr xport;
+ std::vector<char> bitstream; // .bin image extracted from .lvbitx file
+ boost::uint8_t data_in[udp_simple::mtu];
+} x300_session_t;
+
+/*
+ * Extract the .bin image from the given LVBITX file.
+ */
+static void extract_from_lvbitx(x300_session_t &session){
+ boost::property_tree::ptree pt;
+ boost::property_tree::xml_parser::read_xml(session.filepath.c_str(), pt,
+ boost::property_tree::xml_parser::no_comments |
+ boost::property_tree::xml_parser::trim_whitespace);
+ const std::string encoded_bitstream(pt.get<std::string>("Bitfile.Bitstream"));
+ std::vector<char> decoded_bitstream(encoded_bitstream.size());
+
+ base64_decodestate decode_state;
+ base64_init_decodestate(&decode_state);
+ const size_t decoded_size = base64_decode_block(encoded_bitstream.c_str(),
+ encoded_bitstream.size(), &decoded_bitstream.front(), &decode_state);
+ decoded_bitstream.resize(decoded_size);
+ session.bitstream.swap(decoded_bitstream);
+
+ session.size = session.bitstream.size();
+}
+
+/*
+ * Validate X300 image and extract if LVBITX.
+ */
+static void x300_validate_image(x300_session_t &session){
+ if(not fs::exists(session.filepath)){
+ throw uhd::runtime_error(str(boost::format("Could not find image at path \"%s\".")
+ % session.filepath));
+ }
+
+ std::string extension = fs::extension(session.filepath);
+ session.lvbitx = (extension == ".lvbitx");
+
+ if(session.lvbitx){
+ extract_from_lvbitx(session);
+ if(session.size > X300_FPGA_BIN_SIZE_BYTES){
+ throw uhd::runtime_error(str(boost::format("The specified FPGA image is too large: %d vs. %d")
+ % session.size % X300_FPGA_BIN_SIZE_BYTES));
+ }
+
+ /*
+ * PCIe burning just takes a filepath, even for a .lvbitx file,
+ * so just extract it to validate the size.
+ */
+ if(!session.ethernet) session.bitstream.clear();
+ }
+ else if(extension == ".bin" or extension == ".bit"){
+ boost::uint32_t max_size = (extension == ".bin") ? X300_FPGA_BIN_SIZE_BYTES
+ : X300_FPGA_BIT_SIZE_BYTES;
+
+ session.size = fs::file_size(session.filepath);
+ if(session.size > max_size){
+ throw uhd::runtime_error(str(boost::format("The specified FPGA image is too large: %d vs. %d")
+ % session.size % max_size));
+ return;
+ }
+ }
+ else{
+ throw uhd::runtime_error(str(boost::format("Invalid extension \"%s\". Extension must be .bin, .bit, or .lvbitx.")
+ % extension));
+ }
+}
+
+static void x300_setup_session(x300_session_t &session,
+ const device_addr_t &args,
+ const std::string &filepath){
+ device_addr_t find_args;
+ find_args["type"] = "x300";
+ if(args.has_key("name")) find_args["name"] = args["name"];
+ if(args.has_key("serial")) find_args["serial"] = args["serial"];
+ if(args.has_key("ip-addr")) find_args["addr"] = args["ip-addr"];
+ else if(args.has_key("resource")) find_args["resource"] = args["resource"];
+
+ device_addrs_t devs = x300_find(args);
+ session.found = (devs.size() > 0);
+ if(!session.found) return;
+
+ session.dev_addr = devs[0];
+ session.ethernet = session.dev_addr.has_key("addr");
+ if(session.ethernet){
+ session.ip_addr = session.dev_addr["addr"];
+ session.configure = args.has_key("configure");
+ session.xport = udp_simple::make_connected(session.ip_addr,
+ BOOST_STRINGIZE(X300_FPGA_PROG_UDP_PORT));
+ session.verify = args.has_key("verify");
+ }
+ else{
+ session.resource = session.dev_addr["resource"];
+ session.rpc_port = args.get("rpc-port", "5444");
+ }
+
+ /*
+ * The user can specify an FPGA type (1G, HGS, XGS), rather than a filename. If the user
+ * does not specify one, this will default to the type currently on the device. If this
+ * cannot be determined, then the user is forced to specify a filename.
+ */
+ session.fpga_type = args.get("fpga", session.dev_addr.get("fpga", ""));
+ if(filepath == ""){
+ if(!session.dev_addr.has_key("product") or session.fpga_type == ""){
+ throw uhd::runtime_error("Found a device but could not auto-generate an image filename.");
+ }
+ else session.filepath = find_image_path(str(boost::format("usrp_%s_fpga_%s.bit")
+ % (to_lower_copy(session.dev_addr["product"]))
+ % session.fpga_type));
+ }
+ else session.filepath = filepath;
+
+ // Validate image
+ x300_validate_image(session);
+}
+
+/*
+ * Ethernet communication functions
+ */
+static UHD_INLINE size_t x300_send_and_recv(udp_simple::sptr xport,
+ boost::uint32_t pkt_code,
+ x300_fpga_update_data_t *pkt_out,
+ boost::uint8_t* data){
+ pkt_out->flags = uhd::htonx<boost::uint32_t>(pkt_code);
+ xport->send(boost::asio::buffer(pkt_out, sizeof(*pkt_out)));
+ return xport->recv(boost::asio::buffer(data, udp_simple::mtu), UDP_TIMEOUT);
+}
+
+static UHD_INLINE bool x300_recv_ok(const x300_fpga_update_data_t *pkt_in,
+ size_t len){
+ return (len > 0 and
+ ((ntohl(pkt_in->flags) & X300_FPGA_PROG_FLAGS_ERROR) != X300_FPGA_PROG_FLAGS_ERROR));
+}
+
+// Image data needs to be bitswapped
+static UHD_INLINE void x300_bitswap(boost::uint8_t *num){
+ *num = ((*num & 0xF0) >> 4) | ((*num & 0x0F) << 4);
+ *num = ((*num & 0xCC) >> 2) | ((*num & 0x33) << 2);
+ *num = ((*num & 0xAA) >> 1) | ((*num & 0x55) << 1);
+}
+
+static void x300_ethernet_load(x300_session_t &session){
+
+ // UDP receive buffer
+ x300_fpga_update_data_t pkt_out;
+ const x300_fpga_update_data_t *pkt_in = reinterpret_cast<const x300_fpga_update_data_t*>(session.data_in);
+
+ // Initialize write session
+ boost::uint32_t flags = X300_FPGA_PROG_FLAGS_ACK | X300_FPGA_PROG_FLAGS_INIT;
+ size_t len = x300_send_and_recv(session.xport, flags, &pkt_out, session.data_in);
+ if(x300_recv_ok(pkt_in, len)){
+ std::cout << "-- Initializing FPGA loading..." << std::flush;
+ }
+ else if(len == 0){
+ std::cout << "failed." << std::endl;
+ throw uhd::runtime_error("Timed out waiting for reply from device.");
+ }
+ else{
+ std::cout << "failed." << std::endl;
+ throw uhd::runtime_error("Device reported an error during initialization.");
+ }
+
+ std::cout << "successful." << std::endl;
+ if(session.verify){
+ std::cout << "-- NOTE: Device is verifying the image it is receiving, increasing the loading time." << std::endl;
+ }
+
+ size_t current_pos = 0;
+ size_t sectors = (session.size / X300_FLASH_SECTOR_SIZE);
+ std::ifstream image(session.filepath.c_str(), std::ios::binary);
+
+ // Each sector
+ for(size_t i = 0; i < session.size; i += X300_FLASH_SECTOR_SIZE){
+
+ // Print progress percentage at beginning of each sector
+ std::cout << boost::format("\r-- Loading %s FPGA image: %d%% (%d/%d sectors)")
+ % session.fpga_type
+ % (int(double(i) / double(session.size) * 100.0))
+ % (i / X300_FLASH_SECTOR_SIZE)
+ % sectors
+ << std::flush;
+
+ // Each packet
+ for(size_t j = i; (j < session.size and j < (i+X300_FLASH_SECTOR_SIZE)); j += X300_PACKET_SIZE_BYTES){
+ flags = X300_FPGA_PROG_FLAGS_ACK;
+ if(j == i) flags |= X300_FPGA_PROG_FLAGS_ERASE; // Erase at beginning of sector
+ if(session.verify) flags |= X300_FPGA_PROG_FLAGS_VERIFY;
+
+ // Set burn location
+ pkt_out.sector = htonx<boost::uint32_t>(X300_FPGA_SECTOR_START + (i/X300_FLASH_SECTOR_SIZE));
+ pkt_out.index = htonx<boost::uint32_t>((j % X300_FLASH_SECTOR_SIZE) / 2);
+ pkt_out.size = htonx<boost::uint32_t>(X300_PACKET_SIZE_BYTES / 2);
+
+ // Read next piece of image
+ memset(pkt_out.data8, 0, X300_PACKET_SIZE_BYTES);
+ if(session.lvbitx){
+ memcpy(pkt_out.data8, &session.bitstream[current_pos], X300_PACKET_SIZE_BYTES);
+ current_pos += X300_PACKET_SIZE_BYTES;
+ }
+ else{
+ image.read((char*)pkt_out.data8, X300_PACKET_SIZE_BYTES);
+ }
+
+ // Data must be bitswapped and byteswapped
+ for(size_t k = 0; k < X300_PACKET_SIZE_BYTES; k++){
+ x300_bitswap(&pkt_out.data8[k]);
+ }
+ for(size_t k = 0; k < (X300_PACKET_SIZE_BYTES/2); k++){
+ pkt_out.data16[k] = htonx<boost::uint16_t>(pkt_out.data16[k]);
+ }
+
+ len = x300_send_and_recv(session.xport, flags, &pkt_out, session.data_in);
+ if(len == 0){
+ if(!session.lvbitx) image.close();
+ throw uhd::runtime_error("Timed out waiting for reply from device.");
+ }
+ else if((ntohl(pkt_in->flags) & X300_FPGA_PROG_FLAGS_ERROR)){
+ if(!session.lvbitx) image.close();
+ throw uhd::runtime_error("Device reported an error.");
+ }
+ }
+ }
+ if(!session.lvbitx){
+ image.close();
+ }
+
+ std::cout << boost::format("\r-- Loading %s FPGA image: 100%% (%d/%d sectors)")
+ % session.fpga_type
+ % sectors
+ % sectors
+ << std::endl;
+
+ // Cleanup
+ if(!session.lvbitx) image.close();
+ flags = (X300_FPGA_PROG_FLAGS_CLEANUP | X300_FPGA_PROG_FLAGS_ACK);
+ pkt_out.sector = pkt_out.index = pkt_out.size = 0;
+ memset(pkt_out.data8, 0, X300_PACKET_SIZE_BYTES);
+ std::cout << "-- Finalizing image load..." << std::flush;
+ len = x300_send_and_recv(session.xport, flags, &pkt_out, session.data_in);
+ if(len == 0){
+ std::cout << "failed." << std::endl;
+ throw uhd::runtime_error("Timed out waiting for reply from device.");
+ }
+ else if((ntohl(pkt_in->flags) & X300_FPGA_PROG_FLAGS_ERROR)){
+ std::cout << "failed." << std::endl;
+ throw uhd::runtime_error("Device reported an error during cleanup.");
+ }
+ else std::cout << "successful." << std::endl;
+
+ // Save new FPGA image (if option set)
+ if(session.configure){
+ flags = (X300_FPGA_PROG_CONFIGURE | X300_FPGA_PROG_FLAGS_ACK);
+ x300_send_and_recv(session.xport, flags, &pkt_out, session.data_in);
+ std::cout << "-- Saving image onto device..." << std::flush;
+ if(len == 0){
+ std::cout << "failed." << std::endl;
+ throw uhd::runtime_error("Timed out waiting for reply from device.");
+ }
+ else if((ntohl(pkt_in->flags) & X300_FPGA_PROG_FLAGS_ERROR)){
+ std::cout << "failed." << std::endl;
+ throw uhd::runtime_error("Device reported an error while saving the image.");
+ }
+ else std::cout << "successful." << std::endl;
+ }
+}
+
+static void x300_pcie_load(x300_session_t &session){
+
+ std::cout << boost::format("\r-- Loading %s FPGA image (this will take 5-10 minutes)...")
+ % session.fpga_type
+ << std::flush;
+
+ nirio_status status = NiRio_Status_Success;
+ niusrprio::niusrprio_session fpga_session(session.resource, session.rpc_port);
+ nirio_status_chain(fpga_session.download_bitstream_to_flash(session.filepath), status);
+
+ if(nirio_status_fatal(status)){
+ std::cout << "failed." << std::endl;
+ niusrprio::nirio_status_to_exception(status, "NI-RIO reported the following error:");
+ }
+ else std::cout << "successful." << std::endl;
+}
+
+static bool x300_image_loader(const image_loader::image_loader_args_t &image_loader_args){
+ // See if any X3x0 with the given args is found
+ device_addrs_t devs = x300_find(image_loader_args.args);
+ if(devs.size() == 0 or !image_loader_args.load_fpga) return false;
+
+ x300_session_t session;
+ x300_setup_session(session,
+ image_loader_args.args,
+ image_loader_args.fpga_path
+ );
+ if(!session.found) return false;
+
+ std::cout << boost::format("Unit: USRP %s (%s, %s)\nFPGA Image: %s\n")
+ % session.dev_addr["product"]
+ % session.dev_addr["serial"]
+ % session.dev_addr[session.ethernet ? "addr" : "resource"]
+ % session.filepath;
+
+ if(session.ethernet) x300_ethernet_load(session);
+ else x300_pcie_load(session);
+ return true;
+}
+
+UHD_STATIC_BLOCK(register_x300_image_loader){
+ std::string recovery_instructions = "Aborting. Your USRP X-Series device will likely be unusable. Visit\n"
+ "http://files.ettus.com/manual/page_usrp_x3x0.html#x3x0_load_fpga_imgs_jtag\n"
+ "for details on restoring your device.";
+
+ image_loader::register_image_loader("x300", x300_image_loader, recovery_instructions);
+}
diff --git a/host/lib/usrp/x300/x300_impl.cpp b/host/lib/usrp/x300/x300_impl.cpp
index 91e6ded26..8c565a252 100644
--- a/host/lib/usrp/x300/x300_impl.cpp
+++ b/host/lib/usrp/x300/x300_impl.cpp
@@ -235,7 +235,7 @@ static device_addrs_t x300_find_pcie(const device_addr_t &hint, bool explicit_qu
return addrs;
}
-static device_addrs_t x300_find(const device_addr_t &hint_)
+device_addrs_t x300_find(const device_addr_t &hint_)
{
//handle the multi-device discovery
device_addrs_t hints = separate_device_addr(hint_);
@@ -509,7 +509,7 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr)
//check compat numbers
//check fpga compat before fw compat because the fw is a subset of the fpga image
- this->check_fpga_compat(mb_path, mb.zpu_ctrl);
+ this->check_fpga_compat(mb_path, mb);
this->check_fw_compat(mb_path, mb.zpu_ctrl);
//store which FPGA image is loaded
@@ -1696,25 +1696,33 @@ void x300_impl::check_fw_compat(const fs_path &mb_path, wb_iface::sptr iface)
% compat_major % compat_minor));
}
-void x300_impl::check_fpga_compat(const fs_path &mb_path, wb_iface::sptr iface)
+void x300_impl::check_fpga_compat(const fs_path &mb_path, const mboard_members_t &members)
{
- boost::uint32_t compat_num = iface->peek32(SR_ADDR(SET0_BASE, ZPU_RB_COMPAT_NUM));
+ boost::uint32_t compat_num = members.zpu_ctrl->peek32(SR_ADDR(SET0_BASE, ZPU_RB_COMPAT_NUM));
boost::uint32_t compat_major = (compat_num >> 16);
boost::uint32_t compat_minor = (compat_num & 0xffff);
if (compat_major != X300_FPGA_COMPAT_MAJOR)
{
+ std::string image_loader_path = (fs::path(uhd::get_pkg_path()) / "bin" / "uhd_image_loader").string();
+ std::string image_loader_cmd = str(boost::format("\"%s\" --args=\"type=x300,%s=%s\"")
+ % image_loader_path
+ % (members.xport_path == "eth" ? "addr"
+ : "resource")
+ % members.addr);
+
throw uhd::runtime_error(str(boost::format(
"Expected FPGA compatibility number %d, but got %d:\n"
"The FPGA image on your device is not compatible with this host code build.\n"
"Download the appropriate FPGA images for this version of UHD.\n"
"%s\n\n"
"Then burn a new image to the on-board flash storage of your\n"
- "USRP X3xx device using the burner utility. %s\n\n"
+ "USRP X3xx device using the image loader utility. Use this command:\n\n%s\n\n"
"For more information, refer to the UHD manual:\n\n"
" http://files.ettus.com/manual/page_usrp_x3x0.html#x3x0_flash"
) % int(X300_FPGA_COMPAT_MAJOR) % compat_major
- % print_utility_error("uhd_images_downloader.py") % print_utility_error("usrp_x3xx_fpga_burner")));
+ % print_utility_error("uhd_images_downloader.py")
+ % image_loader_cmd));
}
_tree->create<std::string>(mb_path / "fpga_version").set(str(boost::format("%u.%u")
% compat_major % compat_minor));
diff --git a/host/lib/usrp/x300/x300_impl.hpp b/host/lib/usrp/x300/x300_impl.hpp
index dca6360b8..64f14cae6 100644
--- a/host/lib/usrp/x300/x300_impl.hpp
+++ b/host/lib/usrp/x300/x300_impl.hpp
@@ -142,6 +142,8 @@ uhd::uart_iface::sptr x300_make_uart_iface(uhd::wb_iface::sptr iface);
uhd::wb_iface::sptr x300_make_ctrl_iface_enet(uhd::transport::udp_simple::sptr udp);
uhd::wb_iface::sptr x300_make_ctrl_iface_pcie(uhd::niusrprio::niriok_proxy::sptr drv_proxy);
+uhd::device_addrs_t x300_find(const uhd::device_addr_t &hint_);
+
class x300_impl : public uhd::device
{
public:
@@ -398,7 +400,7 @@ private:
void set_mb_eeprom(uhd::i2c_iface::sptr i2c, const uhd::usrp::mboard_eeprom_t &);
void check_fw_compat(const uhd::fs_path &mb_path, uhd::wb_iface::sptr iface);
- void check_fpga_compat(const uhd::fs_path &mb_path, uhd::wb_iface::sptr iface);
+ void check_fpga_compat(const uhd::fs_path &mb_path, const mboard_members_t &members);
void update_atr_leds(gpio_core_200_32wo::sptr, const std::string &ant);
boost::uint32_t get_fp_gpio(gpio_core_200::sptr);
diff --git a/host/lib/usrp_clock/octoclock/CMakeLists.txt b/host/lib/usrp_clock/octoclock/CMakeLists.txt
index e363bb9da..c489657e2 100644
--- a/host/lib/usrp_clock/octoclock/CMakeLists.txt
+++ b/host/lib/usrp_clock/octoclock/CMakeLists.txt
@@ -1,5 +1,5 @@
#
-# Copyright 2011-2014 Ettus Research LLC
+# Copyright 2013-2015 Ettus Research LLC
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -16,17 +16,15 @@
#
########################################################################
-# This file included, use CMake directory variables
-########################################################################
-
-########################################################################
# Conditionally configure the OctoClock support
########################################################################
LIBUHD_REGISTER_COMPONENT("OctoClock" ENABLE_OCTOCLOCK ON "ENABLE_LIBUHD" OFF)
IF(ENABLE_OCTOCLOCK)
LIBUHD_APPEND_SOURCES(
+ ${CMAKE_CURRENT_SOURCE_DIR}/ihexcvt.cpp
${CMAKE_CURRENT_SOURCE_DIR}/octoclock_eeprom.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/octoclock_image_loader.cpp
${CMAKE_CURRENT_SOURCE_DIR}/octoclock_impl.cpp
${CMAKE_CURRENT_SOURCE_DIR}/octoclock_uart.cpp
)
diff --git a/host/lib/usrp_clock/octoclock/ihexcvt.cpp b/host/lib/usrp_clock/octoclock/ihexcvt.cpp
new file mode 100644
index 000000000..0605ee61c
--- /dev/null
+++ b/host/lib/usrp_clock/octoclock/ihexcvt.cpp
@@ -0,0 +1,250 @@
+/* IHexCvt - Intel HEX File <=> Binary Converter (C++)
+ Copyright (C) 2014 Ali Nakisaee
+
+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 2 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, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.*/
+
+//Include needed stuff from C++
+#include <iostream>
+#include <fstream>
+#include <string>
+
+//... and also from C
+#include <stdio.h>
+
+#include <boost/filesystem.hpp>
+
+#include <uhd/exception.hpp>
+#include "ihexcvt.hpp"
+
+//Avoid repeating 'std::':
+using namespace std;
+
+//The following function reads a hexadecimal number from a text file.
+template <class T>
+static bool ReadValueFromHex(ifstream& InputFile, T& outCh, unsigned char* ApplyChecksum)
+{
+ char V, L;
+ T X = 0;
+ outCh = 0;
+
+ //Get the characters one by one.
+ //Remember: These values are big-endian.
+ //Remember: Every two hex characters (0-9/A-F) indicate ONE byte.
+ for (size_t i = 0; i < 2 * sizeof(T); i++)
+ {
+ InputFile.get( V );
+ if (InputFile.fail())
+ return false;
+
+ X <<= 4;
+ if (V >= '0' && V <= '9')
+ L = (V - '0');
+ else if (V >= 'a' && V <= 'f')
+ L = (V - 'a' + 10);
+ else if (V >= 'A' && V <= 'F')
+ L = (V - 'A' + 10);
+ else
+ return false;
+ X |= L;
+
+ //Apply this character to the checksum
+ if (ApplyChecksum && i % 2 == 1) *ApplyChecksum += X & 0xFF;
+ }
+
+ //Return...
+ outCh = X;
+ return true;
+}
+
+//The following function writes a hexadecimal number from a text file.
+template <class T>
+static bool WriteHexValue(ofstream& OutFile, T Value, unsigned char* CalcChecksum)
+{
+ unsigned char V0 = 0;
+ char C;
+
+ //Remember: These values are big-endian.
+ for (size_t i = 0; i < sizeof(T); i++)
+ {
+ //Get byte #i from the value.
+ V0 = (Value >> ((sizeof(T) - i - 1) * 8)) & 0xFF;
+
+ //Extract the high nibble (4-bits)
+ if ((V0 & 0xF0) <= 0x90)
+ C = (V0 >> 4) + '0';
+ else
+ C = (V0 >> 4) + ('A' - 10);
+ OutFile.put( C );
+
+ //Extract the low nibble (4-bits)
+ if ((V0 & 0xF) <= 0x9)
+ C = (V0 & 0xF) + '0';
+ else
+ C = (V0 & 0xF) + ('A' - 10);
+ OutFile.put( C );
+
+ //Calculate the checksum
+ if (CalcChecksum) *CalcChecksum += V0;
+ }
+ return true;
+}
+
+//Skip any incoming whitespaces
+static void SkipWhitespace(ifstream& InputFile)
+{
+ for (;;)
+ {
+ char C;
+ InputFile.get(C);
+ if (InputFile.eof() || InputFile.fail()) break;
+ if (!(C == '\n' || C == '\r' || C == ' ' || C == '\t' || C == '\v'))
+ {
+ InputFile.putback(C);
+ break;
+ }
+ }
+}
+
+//The function responsible for conversion from HEX files to BINary.
+void Hex2Bin(const char* SrcName, const char* DstName, bool IgnoreChecksum)
+{
+ ifstream Src(SrcName);
+ if (Src.bad())
+ {
+ throw uhd::runtime_error("Could not convert Intel .hex file to binary.");
+ }
+
+ ofstream Dst(DstName, ios_base::binary);
+ if (Dst.bad())
+ {
+ throw uhd::runtime_error("Could not convert Intel .hex file to binary.");
+ }
+
+ char Ch;
+ int LineIdx = 1;
+
+ unsigned char ByteCount;
+ unsigned short AddressLow;
+ unsigned short Extra;
+ unsigned long ExtraL;
+ unsigned long AddressOffset = 0;
+ unsigned char RecordType;
+ unsigned char Data[255];
+ unsigned char CurChecksum;
+ unsigned char FileChecksum;
+ bool EOFMarker = false;
+ bool EOFWarn = false;
+
+ for ( ;; )
+ {
+ Src.get(Ch);
+ if (Src.eof())
+ break;
+ if (EOFMarker && !EOFWarn)
+ {
+ throw uhd::runtime_error("Could not convert Intel .hex file to binary.");
+ }
+ if (Ch != ':') goto genericErr;
+
+ CurChecksum = 0;
+ if (!ReadValueFromHex( Src, ByteCount, &CurChecksum )) goto genericErr;
+ if (!ReadValueFromHex( Src, AddressLow, &CurChecksum )) goto genericErr;
+ if (!ReadValueFromHex( Src, RecordType, &CurChecksum )) goto genericErr;
+
+ switch (RecordType)
+ {
+ case 0x00: //Data record
+ for (int i = 0; i < ByteCount; i++)
+ if (!ReadValueFromHex( Src, Data[i], &CurChecksum )) goto genericErr;
+ break;
+ case 0x01: //End Marker
+ if ( ByteCount != 0 )
+ {
+ goto onErrExit;
+ }
+ EOFMarker = true;
+ break;
+ case 0x02: //Extended Segment Address
+ if ( ByteCount != 2 || AddressLow != 0 )
+ {
+ goto onErrExit;
+ }
+ if (!ReadValueFromHex( Src, Extra, &CurChecksum )) goto genericErr;
+ AddressOffset = (unsigned long)Extra << 4;
+ break;
+ case 0x03: //Start Segment Address
+ if ( ByteCount != 4 || AddressLow != 0 )
+ {
+ goto onErrExit;
+ }
+ if (!ReadValueFromHex( Src, ExtraL, &CurChecksum )) goto genericErr;
+ break;
+ case 0x04: //Extended Linear Address
+ if ( ByteCount != 2 || AddressLow != 0 )
+ {
+ goto onErrExit;
+ }
+ if (!ReadValueFromHex( Src, Extra, &CurChecksum )) goto genericErr;
+ AddressOffset = (unsigned long)Extra << 16;
+ break;
+ case 0x05: //Start Linear Address
+ if ( ByteCount != 4 || AddressLow != 0 )
+ {
+ goto onErrExit;
+ }
+ if (!ReadValueFromHex( Src, ExtraL, &CurChecksum )) goto genericErr;
+ break;
+ }
+
+ //Verify checksum
+ CurChecksum = (~(CurChecksum & 0xFF) + 1) & 0xFF;
+ if (!ReadValueFromHex( Src, FileChecksum, NULL )) goto genericErr;
+ if (CurChecksum != FileChecksum)
+ {
+ if (!IgnoreChecksum) goto onErrExit;
+ }
+
+ //Put Data
+ if (RecordType == 0x00)
+ {
+ Dst.seekp( AddressLow + AddressOffset );
+ for (int i = 0; i < ByteCount; i++)
+ {
+ Dst.put( Data[i] );
+ }
+ }
+
+ //Skip any white space
+ SkipWhitespace( Src );
+
+ LineIdx++;
+ }
+
+ Dst << flush;
+ Dst.close();
+
+ return;
+
+genericErr:
+ throw uhd::runtime_error("Invalid Intel .hex file detected.");
+
+onErrExit:
+ Dst.close();
+ Src.close();
+ boost::filesystem::remove(DstName);
+ throw uhd::runtime_error("Could not convert Intel .hex file to binary.");
+}
diff --git a/host/lib/usrp_clock/octoclock/ihexcvt.hpp b/host/lib/usrp_clock/octoclock/ihexcvt.hpp
new file mode 100644
index 000000000..d577ece1f
--- /dev/null
+++ b/host/lib/usrp_clock/octoclock/ihexcvt.hpp
@@ -0,0 +1,22 @@
+//
+// 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 _IHEXCVT_HPP_
+#define _IHEXCVT_HPP_
+
+void Hex2Bin(const char* SrcName, const char* DstName, bool IgnoreChecksum);
+
+#endif /* _IHEXCVT_HPP_ */
diff --git a/host/lib/usrp_clock/octoclock/octoclock_image_loader.cpp b/host/lib/usrp_clock/octoclock/octoclock_image_loader.cpp
new file mode 100644
index 000000000..c88177f0f
--- /dev/null
+++ b/host/lib/usrp_clock/octoclock/octoclock_image_loader.cpp
@@ -0,0 +1,340 @@
+//
+// Copyright 2015 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 <cstring>
+#include <fstream>
+#include <iostream>
+#include <string>
+
+#include <boost/cstdint.hpp>
+#include <boost/filesystem.hpp>
+#include <boost/format.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/thread.hpp>
+
+#include <uhd/device.hpp>
+#include <uhd/image_loader.hpp>
+#include <uhd/transport/udp_simple.hpp>
+#include <uhd/types/device_addr.hpp>
+#include <uhd/types/time_spec.hpp>
+#include <uhd/utils/byteswap.hpp>
+#include <uhd/utils/paths.hpp>
+#include <uhd/utils/static.hpp>
+
+#include "octoclock_impl.hpp"
+#include "common.h"
+#include "ihexcvt.hpp"
+
+namespace fs = boost::filesystem;
+using namespace uhd;
+using namespace uhd::usrp_clock;
+using namespace uhd::transport;
+
+#define OCTOCLOCK_FIRMWARE_MAX_SIZE_BYTES (1024*120) // Last 8 MB are for bootloader
+#define OCTOCLOCK_BLOCK_SIZE 256
+
+/*
+ * OctoClock burn session
+ */
+typedef struct {
+ bool valid;
+ uhd::device_addr_t dev_addr;
+ std::string given_filepath;
+ std::string actual_filepath; // If using a .hex, this is the converted .bin
+ bool from_hex;
+ boost::uint32_t size;
+ boost::uint16_t crc;
+ boost::uint16_t num_blocks;
+ udp_simple::sptr ctrl_xport;
+ udp_simple::sptr fw_xport;
+ boost::uint8_t data_in[udp_simple::mtu];
+} octoclock_session_t;
+
+static void octoclock_calculate_crc(octoclock_session_t &session){
+ std::ifstream ifile(session.actual_filepath.c_str());
+ boost::uint8_t temp_image[OCTOCLOCK_FIRMWARE_MAX_SIZE_BYTES];
+ ifile.read((char*)temp_image, session.size);
+
+ session.crc = 0xFFFF;
+ for(size_t i = 0; i < session.size; i++){
+ session.crc ^= temp_image[i];
+ for(boost::uint8_t j = 0; j < 8; ++j){
+ if(session.crc & 1) session.crc = (session.crc >> 1) ^ 0xA001;
+ else session.crc = (session.crc >> 1);
+ }
+ }
+
+ ifile.close();
+}
+
+static void octoclock_validate_firmware_image(octoclock_session_t &session){
+ if(not fs::exists(session.given_filepath)){
+ throw uhd::runtime_error(str(boost::format("Could not find image at path \"%s\"")
+ % session.given_filepath));
+ }
+
+ std::string extension = fs::extension(session.given_filepath);
+ if(extension == ".bin"){
+ session.actual_filepath = session.given_filepath;
+ session.from_hex = false;
+ }
+ else if(extension == ".hex"){
+ session.actual_filepath = fs::path(fs::path(uhd::get_tmp_path()) /
+ str(boost::format("octoclock_fw_%d.bin")
+ % time_spec_t::get_system_time().get_full_secs())
+ ).string();
+
+ Hex2Bin(session.given_filepath.c_str(), session.actual_filepath.c_str(), false);
+ session.from_hex = true;
+ }
+ else throw uhd::runtime_error(str(boost::format("Invalid extension \"%s\". Extension must be .hex or .bin.")));
+
+ session.size = fs::file_size(session.actual_filepath);
+ if(session.size > OCTOCLOCK_FIRMWARE_MAX_SIZE_BYTES){
+ throw uhd::runtime_error(str(boost::format("The specified firmware image is too large: %d vs. %d")
+ % session.size % OCTOCLOCK_FIRMWARE_MAX_SIZE_BYTES));
+ }
+
+ session.num_blocks = (session.size % OCTOCLOCK_BLOCK_SIZE) ? ((session.size / OCTOCLOCK_BLOCK_SIZE) + 1)
+ : (session.size / OCTOCLOCK_BLOCK_SIZE);
+
+ octoclock_calculate_crc(session);
+ session.valid = true;
+}
+
+static void octoclock_setup_session(octoclock_session_t &session,
+ const std::string &filepath){
+
+ // If no filepath is given, use the default
+ if(filepath == ""){
+ session.given_filepath = find_image_path(str(boost::format("octoclock_r%d_fw.hex")
+ % boost::lexical_cast<std::string>(
+ session.dev_addr.get("revision","4")
+ )));
+ }
+ else session.given_filepath = filepath;
+
+ octoclock_validate_firmware_image(session);
+
+ session.ctrl_xport = udp_simple::make_connected(session.dev_addr["addr"],
+ BOOST_STRINGIZE(OCTOCLOCK_UDP_CTRL_PORT));
+ session.fw_xport = udp_simple::make_connected(session.dev_addr["addr"],
+ BOOST_STRINGIZE(OCTOCLOCK_UDP_FW_PORT));
+}
+
+static void octoclock_reset_into_bootloader(octoclock_session_t &session){
+
+ // Already in bootloader
+ if(session.dev_addr["type"] == "octoclock-bootloader")
+ return;
+
+ octoclock_packet_t pkt_out;
+ pkt_out.sequence = uhd::htonx<boost::uint32_t>(std::rand());
+ const octoclock_packet_t* pkt_in = reinterpret_cast<const octoclock_packet_t*>(session.data_in);
+ size_t len;
+
+ std::cout << " -- Resetting into bootloader..." << std::flush;
+ UHD_OCTOCLOCK_SEND_AND_RECV(session.ctrl_xport, RESET_CMD, pkt_out, len, session.data_in);
+ if(UHD_OCTOCLOCK_PACKET_MATCHES(RESET_ACK, pkt_out, pkt_in, len)){
+
+ // Make sure this device is now in its bootloader
+ boost::this_thread::sleep(boost::posix_time::milliseconds(500));
+ uhd::device_addrs_t octoclocks = uhd::device::find(
+ uhd::device_addr_t(str(boost::format("addr=%s")
+ % session.dev_addr["addr"]
+ )));
+ if(octoclocks.size() == 0){
+ std::cout << "failed." << std::endl;
+ throw uhd::runtime_error("Failed to reset OctoClock.");
+ }
+ else if(octoclocks[0]["type"] != "octoclock-bootloader"){
+ std::cout << "failed." << std::endl;
+ throw uhd::runtime_error("Failed to reset OctoClock.");
+ }
+ else{
+ std::cout << "successful." << std::endl;
+ session.dev_addr = octoclocks[0];
+ }
+ }
+ else{
+ std::cout << "failed." << std::endl;
+ throw uhd::runtime_error("Failed to reset OctoClock.");
+ }
+}
+
+static void octoclock_burn(octoclock_session_t &session){
+
+ // Make sure we're in the bootloader for this
+ octoclock_reset_into_bootloader(session);
+
+ octoclock_packet_t pkt_out;
+ pkt_out.sequence = htonx<boost::uint32_t>(std::rand());
+ const octoclock_packet_t* pkt_in = reinterpret_cast<const octoclock_packet_t*>(session.data_in);
+
+ // Tell OctoClock to prepare for burn
+ pkt_out.len = htonx<boost::uint16_t>(session.size);
+ size_t len = 0;
+ std::cout << " -- Preparing OctoClock for firmware load..." << std::flush;
+ pkt_out.len = session.size;
+ pkt_out.crc = session.crc;
+ UHD_OCTOCLOCK_SEND_AND_RECV(session.fw_xport, PREPARE_FW_BURN_CMD, pkt_out, len, session.data_in);
+ if(UHD_OCTOCLOCK_PACKET_MATCHES(FW_BURN_READY_ACK, pkt_out, pkt_in, len)){
+ std::cout << "successful." << std::endl;
+ }
+ else{
+ std::cout << "failed." << std::endl;
+ if(session.from_hex){
+ fs::remove(session.actual_filepath);
+ }
+ throw uhd::runtime_error("Failed to prepare OctoClock for firmware load.");
+ }
+
+ // Start burning
+ std::ifstream image(session.actual_filepath.c_str(), std::ios::binary);
+ for(size_t i = 0; i < session.num_blocks; i++){
+ pkt_out.sequence++;
+ pkt_out.addr = i * OCTOCLOCK_BLOCK_SIZE;
+
+ std::cout << str(boost::format("\r -- Loading firmware: %d%% (%d/%d blocks)")
+ % int((double(i)/double(session.num_blocks))*100)
+ % i % session.num_blocks)
+ << std::flush;
+
+ memset(pkt_out.data, 0, OCTOCLOCK_BLOCK_SIZE);
+ image.read((char*)pkt_out.data, OCTOCLOCK_BLOCK_SIZE);
+ UHD_OCTOCLOCK_SEND_AND_RECV(session.fw_xport, FILE_TRANSFER_CMD, pkt_out, len, session.data_in);
+ if(not UHD_OCTOCLOCK_PACKET_MATCHES(FILE_TRANSFER_ACK, pkt_out, pkt_in, len)){
+ image.close();
+ std::cout << std::endl;
+ if(session.from_hex){
+ fs::remove(session.actual_filepath);
+ }
+ throw uhd::runtime_error("Failed to load firmware.");
+ }
+ }
+
+ std::cout << str(boost::format("\r -- Loading firmware: 100%% (%d/%d blocks)")
+ % session.num_blocks % session.num_blocks)
+ << std::endl;
+ image.close();
+}
+
+static void octoclock_verify(octoclock_session_t &session){
+
+ octoclock_packet_t pkt_out;
+ pkt_out.sequence = htonx<boost::uint32_t>(std::rand());
+ const octoclock_packet_t* pkt_in = reinterpret_cast<const octoclock_packet_t*>(session.data_in);
+ size_t len = 0;
+
+ std::ifstream image(session.actual_filepath.c_str(), std::ios::binary);
+ boost::uint8_t image_part[OCTOCLOCK_BLOCK_SIZE];
+ boost::uint16_t cmp_len = 0;
+ for(size_t i = 0; i < session.num_blocks; i++){
+ pkt_out.sequence++;
+ pkt_out.addr = i * OCTOCLOCK_BLOCK_SIZE;
+
+ std::cout << str(boost::format("\r -- Verifying firmware load: %d%% (%d/%d blocks)")
+ % int((double(i)/double(session.num_blocks))*100)
+ % i % session.num_blocks)
+ << std::flush;
+
+ memset(image_part, 0, OCTOCLOCK_BLOCK_SIZE);
+ image.read((char*)image_part, OCTOCLOCK_BLOCK_SIZE);
+ cmp_len = image.gcount();
+
+ UHD_OCTOCLOCK_SEND_AND_RECV(session.fw_xport, READ_FW_CMD, pkt_out, len, session.data_in);
+ if(UHD_OCTOCLOCK_PACKET_MATCHES(READ_FW_ACK, pkt_out, pkt_in, len)){
+ if(memcmp(pkt_in->data, image_part, cmp_len)){
+ std::cout << std::endl;
+ image.close();
+ if(session.from_hex){
+ fs::remove(session.actual_filepath);
+ }
+ throw uhd::runtime_error("Failed to verify OctoClock firmware.");
+ }
+ }
+ else{
+ std::cout << std::endl;
+ image.close();
+ if(session.from_hex){
+ fs::remove(session.actual_filepath);
+ }
+ throw uhd::runtime_error("Failed to verify OctoClock firmware.");
+ }
+ }
+
+ image.close();
+ if(session.from_hex){
+ fs::remove(session.actual_filepath);
+ }
+ std::cout << str(boost::format("\r -- Verifying firmware load: 100%% (%d/%d blocks)")
+ % session.num_blocks % session.num_blocks)
+ << std::endl;
+}
+
+static void octoclock_finalize(octoclock_session_t &session){
+
+ octoclock_packet_t pkt_out;
+ pkt_out.sequence = htonx<boost::uint32_t>(std::rand());
+ const octoclock_packet_t* pkt_in = reinterpret_cast<const octoclock_packet_t*>(session.data_in);
+ size_t len = 0;
+
+ std::cout << " -- Finalizing firmware load..." << std::flush;
+ UHD_OCTOCLOCK_SEND_AND_RECV(session.fw_xport, FINALIZE_BURNING_CMD, pkt_out, len, session.data_in);
+ if(UHD_OCTOCLOCK_PACKET_MATCHES(FINALIZE_BURNING_ACK, pkt_out, pkt_in, len)){
+ std::cout << "successful." << std::endl;
+ }
+ else{
+ std::cout << "failed." << std::endl;
+ throw uhd::runtime_error("Failed to finalize OctoClock firmware load.");
+ }
+}
+
+bool octoclock_image_loader(const image_loader::image_loader_args_t &image_loader_args){
+ // See if we can find an OctoClock with the given args
+ device_addrs_t devs = octoclock_find(image_loader_args.args);
+ if(devs.size() == 0 or !image_loader_args.load_firmware) return false;
+
+ octoclock_session_t session;
+ session.dev_addr = devs[0];
+ octoclock_setup_session(session,
+ image_loader_args.firmware_path
+ );
+
+ std::cout << boost::format("Unit: OctoClock (%s)")
+ % session.dev_addr["addr"]
+ << std::endl;
+ std::cout << "Firmware: " << session.given_filepath << std::endl;
+
+ octoclock_burn(session);
+ octoclock_verify(session);
+ octoclock_finalize(session);
+
+ return true;
+}
+
+UHD_STATIC_BLOCK(register_octoclock_image_loader){
+ std::string recovery_instructions = "Aborting. Your OctoClock firmware is now corrupt. The bootloader\n"
+ "is functional, but the device will not have functional clock distribution."
+ "Run this utility again to restore functionality or refer to:\n\n"
+ "http://files.ettus.com/manual/page_octoclock.html\n\n"
+ "for alternative setups.";
+
+ image_loader::register_image_loader("octoclock",
+ octoclock_image_loader,
+ recovery_instructions);
+}
diff --git a/host/lib/usrp_clock/octoclock/octoclock_impl.hpp b/host/lib/usrp_clock/octoclock/octoclock_impl.hpp
index ab45cd5f0..453e75ec5 100644
--- a/host/lib/usrp_clock/octoclock/octoclock_impl.hpp
+++ b/host/lib/usrp_clock/octoclock/octoclock_impl.hpp
@@ -31,6 +31,8 @@
#include "common.h"
+uhd::device_addrs_t octoclock_find(const uhd::device_addr_t &hint);
+
/*!
* OctoClock implementation guts
*/
diff --git a/host/utils/CMakeLists.txt b/host/utils/CMakeLists.txt
index e24b417c1..2f61c3c2e 100644
--- a/host/utils/CMakeLists.txt
+++ b/host/utils/CMakeLists.txt
@@ -1,5 +1,5 @@
#
-# Copyright 2010-2014 Ettus Research LLC
+# Copyright 2010-2015 Ettus Research LLC
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -21,6 +21,7 @@
SET(util_runtime_sources
uhd_find_devices.cpp
uhd_usrp_probe.cpp
+ uhd_image_loader.cpp
uhd_cal_rx_iq_balance.cpp
uhd_cal_tx_dc_offset.cpp
uhd_cal_tx_iq_balance.cpp
diff --git a/host/utils/octoclock_firmware_burner.cpp b/host/utils/octoclock_firmware_burner.cpp
index eb8198a2b..1ec77d0c8 100644
--- a/host/utils/octoclock_firmware_burner.cpp
+++ b/host/utils/octoclock_firmware_burner.cpp
@@ -104,6 +104,28 @@ void list_octoclocks(){
}
}
+void print_image_loader_warning(const std::string &fw_path, const po::variables_map &vm){
+ // Newline + indent
+ #ifdef UHD_PLATFORM_WIN32
+ const std::string nl = " ^\n ";
+ #else
+ const std::string nl = " \\\n ";
+ #endif
+
+ std::string uhd_image_loader = str(boost::format("uhd_image_loader --args=\"type=octoclock,addr=%s\""
+ "%s --fw-path=%s")
+ % vm["addr"].as<std::string>() % nl % fw_path);
+
+ std::cout << "************************************************************************************************" << std::endl
+ << "WARNING: This utility will be removed in an upcoming version of UHD. In the future, use" << std::endl
+ << " this command:" << std::endl
+ << std::endl
+ << uhd_image_loader << std::endl
+ << std::endl
+ << "************************************************************************************************" << std::endl
+ << std::endl;
+}
+
/*
* Manually find bootloader. This sends multiple packets in order to increase chances of getting
* bootloader before it switches to the application.
@@ -363,6 +385,8 @@ int UHD_SAFE_MAIN(UHD_UNUSED(int argc), UHD_UNUSED(char *argv[])){
read_firmware();
+ print_image_loader_warning(firmware_path, vm);
+
std::signal(SIGINT, &sig_int_handler);
burn_firmware(udp_transport);
diff --git a/host/utils/uhd_image_loader.cpp b/host/utils/uhd_image_loader.cpp
new file mode 100644
index 000000000..39efc8f1e
--- /dev/null
+++ b/host/utils/uhd_image_loader.cpp
@@ -0,0 +1,122 @@
+//
+// Copyright 2015 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 <csignal>
+#include <cstdlib>
+#include <iostream>
+
+#include <boost/assign.hpp>
+#include <boost/filesystem.hpp>
+#include <boost/format.hpp>
+
+#include <uhd/config.hpp>
+#include <uhd/image_loader.hpp>
+#include <uhd/types/device_addr.hpp>
+#include <uhd/utils/safe_main.hpp>
+#include <boost/program_options.hpp>
+
+namespace fs = boost::filesystem;
+namespace po = boost::program_options;
+
+static std::string device_type = "";
+static int num_ctrl_c = 0;
+
+/*
+ * If the user presses Ctrl+C, warn them that they may corrupt their device.
+ * If they press it again, provide instructions on restoring the device
+ * (if applicable) and exit.
+ */
+void sigint_handler(int){
+ num_ctrl_c++;
+ if(num_ctrl_c == 1){
+ std::cout << std::endl
+ << "Are you sure you want to abort? If you do, your device will likely" << std::endl
+ << "be in an unstable or unusable state." << std::endl
+ << "Press Ctrl+C again to abort." << std::endl << std::endl;
+ }
+ else{
+ std::cout << std::endl << uhd::image_loader::get_recovery_instructions(device_type) << std::endl;
+ exit(EXIT_FAILURE);
+ }
+}
+
+int UHD_SAFE_MAIN(int argc, char *argv[]){
+
+ std::string fw_path = "";
+ std::string fpga_path = "";
+
+ po::options_description desc("Allowed options");
+ desc.add_options()
+ ("help", "help message")
+ ("args", po::value<std::string>()->default_value(""), "Device args, optional loader args")
+ ("fw-path", po::value<std::string>(&fw_path)->default_value(""), "Firmware path (uses default if none specified)")
+ ("fpga-path", po::value<std::string>(&fpga_path)->default_value(""), "FPGA path (uses default if none specified)")
+ ("no-fw", "Don't burn firmware")
+ ("no-fpga", "Don't burn FPGA")
+ ;
+
+ po::variables_map vm;
+ po::store(po::parse_command_line(argc, argv, desc), vm);
+ po::notify(vm);
+
+ // Help message
+ if (vm.count("help")){
+ std::cout << "UHD Image Loader" << std::endl
+ << std::endl
+ << "Load firmware and/or FPGA images onto an Ettus Research device." << std::endl
+ << std::endl
+ << desc << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ // Convert user options
+ uhd::image_loader::image_loader_args_t image_loader_args;
+ image_loader_args.args = vm["args"].as<std::string>();
+ image_loader_args.load_firmware = (vm.count("no-fw") == 0);
+ image_loader_args.load_fpga = (vm.count("no-fpga") == 0);
+ image_loader_args.firmware_path = vm["fw-path"].as<std::string>();
+ image_loader_args.fpga_path = vm["fpga-path"].as<std::string>();
+
+ // Clean up paths, if given
+ if(image_loader_args.firmware_path != ""){
+ #ifndef UHD_PLATFORM_WIN32
+ if(image_loader_args.firmware_path.find("~") == 0){
+ image_loader_args.firmware_path.replace(0,1,getenv("HOME"));
+ }
+ #endif /* UHD_PLATFORM_WIN32 */
+ image_loader_args.firmware_path = fs::absolute(image_loader_args.firmware_path).string();
+ }
+ if(image_loader_args.fpga_path != ""){
+ #ifndef UHD_PLATFORM_WIN32
+ if(image_loader_args.fpga_path.find("~") == 0){
+ image_loader_args.fpga_path.replace(0,1,getenv("HOME"));
+ }
+ #endif /* UHD_PLATFORM_WIN32 */
+ image_loader_args.fpga_path = fs::absolute(image_loader_args.fpga_path).string();
+ }
+
+ // Detect which type of device we're working with
+ device_type = image_loader_args.args.get("type","");
+
+ std::signal(SIGINT, &sigint_handler);
+ if(not uhd::image_loader::load(image_loader_args)){
+ std::cerr << "No applicable UHD devices found" << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/host/utils/usrp_n2xx_net_burner.py b/host/utils/usrp_n2xx_net_burner.py
index 8f16de501..5605b0028 100755
--- a/host/utils/usrp_n2xx_net_burner.py
+++ b/host/utils/usrp_n2xx_net_burner.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python
#
-# Copyright 2010-2011 Ettus Research LLC
+# Copyright 2010-2011,2015 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
@@ -91,6 +91,45 @@ def seq():
return _seq
########################################################################
+# print equivalent uhd_image_loader command
+########################################################################
+def print_image_loader_warning(fw, fpga, reset, safe, addr):
+
+ # Newline + indent
+ if platform.system() == "Windows":
+ nl = " ^\n "
+ else:
+ nl = " \\\n "
+
+ # Generate uhd_image_loader command based on given arguments
+ uhd_image_loader = "uhd_image_loader --args=\"type=usrp2,addr={0}".format(addr)
+ if reset:
+ uhd_image_loader += ",reset"
+ if safe:
+ uhd_image_loader += ",overwrite-safe"
+ uhd_image_loader += "\""
+
+ if fw:
+ uhd_image_loader += "{0}--fw-path=\"{1}\"".format(nl, fw)
+ else:
+ uhd_image_loader += "{0}--no-fw".format(nl)
+
+ if fpga:
+ uhd_image_loader += "{0}--fpga-path=\"{1}\"".format(nl, fpga)
+ else:
+ uhd_image_loader += "{0}--no-fpga".format(nl)
+
+ print("")
+ print("************************************************************************************************")
+ print("WARNING: This utility will be removed in an upcoming version of UHD. In the future, use")
+ print(" this command:")
+ print("")
+ print(uhd_image_loader)
+ print("")
+ print("************************************************************************************************")
+ print("")
+
+########################################################################
# helper functions
########################################################################
def unpack_flash_args_fmt(s):
@@ -234,6 +273,7 @@ def enumerate_devices():
class burner_socket(object):
def __init__(self, addr, quiet):
self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ self._addr = addr
self._quiet = quiet
self._sock.settimeout(UDP_TIMEOUT)
self._sock.connect((addr, UDP_FW_UPDATE_PORT))
@@ -284,6 +324,8 @@ class burner_socket(object):
return (self.memory_size_bytes, self.sector_size_bytes)
def burn_fw(self, fw, fpga, reset, safe, check_rev=True):
+ print_image_loader_warning(fw, fpga, reset, safe, self._addr)
+
(flash_size, sector_size) = self.get_flash_info()
hw_rev = self.get_hw_rev()
@@ -501,7 +543,12 @@ if __name__=='__main__':
if options.overwrite_safe and not options.read:
print("Are you REALLY, REALLY sure you want to overwrite the safe image? This is ALMOST ALWAYS a terrible idea.")
print("If your image is faulty, your USRP2+ will become a brick until reprogrammed via JTAG.")
- response = raw_input("""Type "yes" to continue, or anything else to quit: """)
+
+ python_major_version = int(platform.python_version_tuple()[0])
+ if python_major_version > 2:
+ response = input("""Type "yes" to continue, or anything else to quit: """)
+ else:
+ response = raw_input("""Type "yes" to continue, or anything else to quit: """)
if response != "yes": sys.exit(0)
burner = burner_socket(addr=options.addr,quiet=False)
diff --git a/host/utils/usrp_n2xx_simple_net_burner.cpp b/host/utils/usrp_n2xx_simple_net_burner.cpp
index 642e9a407..b105e9cb6 100644
--- a/host/utils/usrp_n2xx_simple_net_burner.cpp
+++ b/host/utils/usrp_n2xx_simple_net_burner.cpp
@@ -1,5 +1,5 @@
//
-// Copyright 2012-2014 Ettus Research LLC
+// Copyright 2012-2015 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
@@ -184,6 +184,59 @@ void list_usrps(){
/***********************************************************************
* Find USRP N2XX with specified IP address and return type
**********************************************************************/
+void print_image_loader_warning(const std::string &fw_path,
+ const std::string &fpga_path,
+ const po::variables_map &vm){
+
+ // Newline + indent
+ #ifdef UHD_PLATFORM_WIN32
+ const std::string nl = " ^\n ";
+ #else
+ const std::string nl = " \\\n ";
+ #endif
+
+ std::string uhd_image_loader = str(boost::format("uhd_image_loader --args=\"type=usrp2,addr=%s")
+ % vm["addr"].as<std::string>());
+ if(vm.count("auto-reboot") > 0)
+ uhd_image_loader += ",reset";
+ if(vm.count("overwrite-safe") > 0)
+ uhd_image_loader += ",overwrite-safe";
+ if(vm.count("dont-check-rev") > 0)
+ uhd_image_loader += ",dont-check-rev";
+
+ uhd_image_loader += "\"";
+
+ if(vm.count("no-fw") == 0){
+ uhd_image_loader += str(boost::format("%s--fw-path=\"%s\"")
+ % nl % fw_path);
+ }
+ else{
+ uhd_image_loader += str(boost::format("%s--no-fw")
+ % nl);
+ }
+
+ if(vm.count("no-fpga") == 0){
+ uhd_image_loader += str(boost::format("%s--fpga-path=\"%s\"")
+ % nl % fpga_path);
+ }
+ else{
+ uhd_image_loader += str(boost::format("%s--no-fpga")
+ % nl);
+ }
+
+ std::cout << "************************************************************************************************" << std::endl
+ << "WARNING: This utility will be removed in an upcoming version of UHD. In the future, use" << std::endl
+ << " this command:" << std::endl
+ << std::endl
+ << uhd_image_loader << std::endl
+ << std::endl
+ << "************************************************************************************************" << std::endl
+ << std::endl;
+}
+
+/***********************************************************************
+ * Find USRP N2XX with specified IP address and return type
+ **********************************************************************/
boost::uint32_t find_usrp(udp_simple::sptr udp_transport, bool check_rev){
boost::uint32_t hw_rev;
bool found_it = false;
@@ -627,6 +680,8 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){
fw_image_size = read_fw_image(fw_path);
}
+ print_image_loader_warning(fw_path, fpga_path, vm);
+
std::cout << "Will burn the following images:" << std::endl;
if(burn_fw) std::cout << boost::format(" * Firmware: %s\n") % fw_path;
if(burn_fpga) std::cout << boost::format(" * FPGA: %s\n") % fpga_path;
diff --git a/host/utils/usrp_x3xx_fpga_burner.cpp b/host/utils/usrp_x3xx_fpga_burner.cpp
index 3c6225531..0b3d2da27 100644
--- a/host/utils/usrp_x3xx_fpga_burner.cpp
+++ b/host/utils/usrp_x3xx_fpga_burner.cpp
@@ -1,5 +1,5 @@
//
-// Copyright 2013-2014 Ettus Research LLC
+// Copyright 2013-2015 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
@@ -195,6 +195,71 @@ void extract_from_lvbitx(std::string lvbitx_path, std::vector<char> &bitstream){
bitstream.swap(decoded_bitstream);
}
+void print_image_loader_warning(const std::string &fpga_path, const po::variables_map &vm){
+
+ // Newline + indent
+ #ifdef UHD_PLATFORM_WIN32
+ const std::string nl = " ^\n ";
+ #else
+ const std::string nl = " \\\n ";
+ #endif
+
+ // Generate equivalent uhd_image_loader command
+ std::string uhd_image_loader = "uhd_image_loader --args=\"type=x300";
+
+ if(vm.count("addr") > 0){
+ uhd_image_loader += str(boost::format(",addr=%s")
+ % vm["addr"].as<std::string>());
+
+ if(vm.count("configure") > 0){
+ uhd_image_loader += ",configure";
+ }
+
+ if(vm.count("verify") > 0){
+ uhd_image_loader += ",verify";
+ }
+ }
+ else{
+ uhd_image_loader += str(boost::format(",resource=%s")
+ % vm["resource"].as<std::string>());
+
+ /*
+ * Since we have a default value, vm.count("rpc-port") will
+ * always be > 0, so only add the option if a different port
+ * is given.
+ */
+ if(vm["rpc-port"].as<std::string>() != "5444"){
+ uhd_image_loader += str(boost::format(",rpc-port=%s")
+ % vm["rpc-port"].as<std::string>());
+ }
+ }
+
+ if(vm.count("type") > 0){
+ uhd_image_loader += str(boost::format(",fpga=%s")
+ % vm["type"].as<std::string>());
+ }
+
+ uhd_image_loader += "\"";
+
+ /*
+ * The --type option overrides any given path, so only add an FPGA path
+ * if there was no --type argument.
+ */
+ if(vm.count("type") == 0){
+ uhd_image_loader += str(boost::format("%s--fpga-path=\"%s\"")
+ % nl % fpga_path);
+ }
+
+ std::cout << "************************************************************************************************" << std::endl
+ << "WARNING: This utility will be removed in an upcoming version of UHD. In the future, use" << std::endl
+ << " this command:" << std::endl
+ << std::endl
+ << uhd_image_loader << std::endl
+ << std::endl
+ << "************************************************************************************************" << std::endl
+ << std::endl;
+}
+
void ethernet_burn(udp_simple::sptr udp_transport, std::string fpga_path, bool verify){
boost::uint32_t max_size;
std::vector<char> bitstream;
@@ -479,6 +544,8 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){
throw std::runtime_error("The image filename must end in .bin, .bit, or .lvbitx.");
}
+ print_image_loader_warning(fpga_path, vm);
+
std::signal(SIGINT, &sig_int_handler);
if(vm.count("addr")){
udp_simple::sptr udp_transport = udp_simple::make_connected(ip_addr, BOOST_STRINGIZE(X300_FPGA_PROG_UDP_PORT));