aboutsummaryrefslogtreecommitdiffstats
path: root/host/lib/usrp/x300
diff options
context:
space:
mode:
Diffstat (limited to 'host/lib/usrp/x300')
-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
6 files changed, 538 insertions, 8 deletions
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);