aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMark Meserve <mark.meserve@ni.com>2019-04-11 16:37:24 -0500
committermichael-west <michael.west@ettus.com>2019-05-21 16:11:35 -0700
commit45e2f0e116d2b14532e8e06304e2489b1ab0d9cf (patch)
tree9bab2f1e0632018ebaf958ff8bd749f7ae326981
parent4f57ecab13e37f132c99ec797d412def3f1e2a66 (diff)
downloaduhd-45e2f0e116d2b14532e8e06304e2489b1ab0d9cf.tar.gz
uhd-45e2f0e116d2b14532e8e06304e2489b1ab0d9cf.tar.bz2
uhd-45e2f0e116d2b14532e8e06304e2489b1ab0d9cf.zip
b200: enable usage of custom bootloader
- Update MB EEPROM - Add bootloader load command to fx3 util
-rw-r--r--host/lib/usrp/b200/b200_iface.cpp170
-rw-r--r--host/lib/usrp/b200/b200_iface.hpp6
-rw-r--r--host/lib/usrp/b200/b200_mb_eeprom.cpp195
-rw-r--r--host/utils/b2xx_fx3_utils.cpp134
4 files changed, 403 insertions, 102 deletions
diff --git a/host/lib/usrp/b200/b200_iface.cpp b/host/lib/usrp/b200/b200_iface.cpp
index 432201429..082be071c 100644
--- a/host/lib/usrp/b200/b200_iface.cpp
+++ b/host/lib/usrp/b200/b200_iface.cpp
@@ -1,6 +1,6 @@
//
// Copyright 2012-2013 Ettus Research LLC
-// Copyright 2018 Ettus Research, a National Instruments Company
+// Copyright 2018-2019 Ettus Research, a National Instruments Brand
//
// SPDX-License-Identifier: GPL-3.0-or-later
//
@@ -15,6 +15,7 @@
#include <boost/functional/hash.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/format.hpp>
+#include <boost/filesystem.hpp>
#include <libusb.h>
#include <fstream>
#include <string>
@@ -37,6 +38,10 @@ using namespace uhd::transport;
static const bool load_img_msg = true;
const static uint8_t FX3_FIRMWARE_LOAD = 0xA0;
+
+// 32 KB - 256 bytes for EEPROM storage
+constexpr size_t BOOTLOADER_MAX_SIZE = 32512;
+
const static uint8_t VRT_VENDOR_OUT = (LIBUSB_REQUEST_TYPE_VENDOR
| LIBUSB_ENDPOINT_OUT);
const static uint8_t VRT_VENDOR_IN = (LIBUSB_REQUEST_TYPE_VENDOR
@@ -365,6 +370,29 @@ public:
throw uhd::io_error((boost::format("Short write on set FPGA hash (expecting: %d, returned: %d)") % bytes_to_send % ret).str());
}
+ // Establish default largest possible control request transfer size based on operating
+ // USB speed
+ int _get_transfer_size()
+ {
+ switch (get_usb_speed())
+ {
+ case 2:
+ return VREQ_DEFAULT_SIZE;
+ case 3:
+ return VREQ_MAX_SIZE_USB3;
+ default:
+ throw uhd::io_error(
+ "load_fpga: get_usb_speed returned invalid USB speed (not 2 or 3).");
+ }
+ }
+
+ size_t _get_file_size(const char * filename)
+ {
+ boost::filesystem::path path(filename);
+ auto filesize = boost::filesystem::file_size(path);
+ return static_cast<size_t>(filesize);
+ }
+
uint32_t load_fpga(const std::string filestring, bool force) {
uint8_t fx3_state = 0;
@@ -378,33 +406,20 @@ public:
hash_type loaded_hash; usrp_get_fpga_hash(loaded_hash);
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;
- int current_usb_speed = get_usb_speed();
- if (current_usb_speed == 3)
- transfer_size = VREQ_MAX_SIZE_USB3;
- else if (current_usb_speed != 2)
- throw uhd::io_error("load_fpga: get_usb_speed returned invalid USB speed (not 2 or 3).");
+ const int transfer_size = _get_transfer_size();
UHD_ASSERT_THROW(transfer_size <= VREQ_MAX_SIZE);
unsigned char out_buff[VREQ_MAX_SIZE];
// Request loopback read, which will indicate the firmware's current control request buffer size
- // Make sure that if operating as USB2, requested length is within spec
- int ntoread = std::min(transfer_size, (int)sizeof(out_buff));
- int nread = fx3_control_read(B200_VREQ_LOOP, 0, 0, out_buff, ntoread, 1000);
+ int nread = fx3_control_read(B200_VREQ_LOOP, 0, 0, out_buff, transfer_size, 1000);
if (nread < 0)
throw uhd::io_error((boost::format("load_fpga: unable to complete firmware loopback request (%d: %s)") % nread % libusb_error_name(nread)).str());
- else if (nread != ntoread)
- throw uhd::io_error((boost::format("load_fpga: short read on firmware loopback request (expecting: %d, returned: %d)") % ntoread % nread).str());
- transfer_size = std::min(transfer_size, nread); // Select the smaller value
+ else if (nread != transfer_size)
+ throw uhd::io_error((boost::format("load_fpga: short read on firmware loopback request (expecting: %d, returned: %d)") % transfer_size % nread).str());
- size_t file_size = 0;
- {
- std::ifstream file(filename, std::ios::in | std::ios::binary | std::ios::ate);
- file_size = size_t(file.tellg());
- }
+ const size_t file_size = _get_file_size(filename);
std::ifstream file;
file.open(filename, std::ios::in | std::ios::binary);
@@ -481,7 +496,7 @@ public:
const size_t LOG_GRANULARITY = 10; // %. Keep this an integer divisor of 100.
if (load_img_msg)
{
- if (bytes_sent == 0) UHD_LOGGER_DEBUG("B200") << " 0%" << std::flush;
+ if (bytes_sent == 0) UHD_LOGGER_DEBUG("B200") << "FPGA load: 0%" << std::flush;
const size_t percent_before =
size_t((bytes_sent*100)/file_size) -
(size_t((bytes_sent*100)/file_size) % LOG_GRANULARITY);
@@ -491,7 +506,8 @@ public:
(size_t((bytes_sent*100)/file_size) % LOG_GRANULARITY);
if (percent_before != percent_after)
{
- UHD_LOGGER_DEBUG("B200") << std::setw(3) << percent_after << "%";
+ UHD_LOGGER_DEBUG("B200")
+ << "FPGA load: " << std::setw(3) << percent_after << "%";
}
}
}
@@ -520,6 +536,118 @@ public:
return 0;
}
+ uint32_t load_bootloader(const std::string filestring)
+ {
+ // open bootloader file
+ const char* filename = filestring.c_str();
+
+ const size_t file_size = _get_file_size(filename);
+
+ if (file_size > BOOTLOADER_MAX_SIZE) {
+ throw uhd::runtime_error(
+ (boost::format("Bootloader img file is too large for EEPROM! (expecting: "
+ "less than %d actual: %d")
+ % BOOTLOADER_MAX_SIZE % file_size)
+ .str());
+ }
+ std::ifstream file;
+ file.open(filename, std::ios::in | std::ios::binary);
+
+ if (!file.good()) {
+ throw uhd::io_error("load_bootloader: cannot open bootloader input file.");
+ }
+
+ // allocate buffer
+ const int transfer_size = _get_transfer_size();
+ UHD_ASSERT_THROW(transfer_size <= VREQ_MAX_SIZE);
+ std::vector<uint8_t> out_buff(transfer_size);
+
+ // Request loopback read, which will indicate the firmware's current control
+ // request buffer size
+ int nread =
+ fx3_control_read(B200_VREQ_LOOP, 0, 0, out_buff.data(), transfer_size, 1000);
+ if (nread < 0) {
+ throw uhd::io_error((boost::format("load_bootloader: unable to complete "
+ "firmware loopback request (%d: %s)")
+ % nread % libusb_error_name(nread))
+ .str());
+ } else if (nread != transfer_size) {
+ throw uhd::io_error(
+ (boost::format("load_bootloader: short read on firmware loopback request "
+ "(expecting: %d, returned: %d)")
+ % transfer_size % nread)
+ .str());
+ }
+ // ensure FX3 is in non-error state
+ {
+ uint8_t fx3_state = get_fx3_status();
+
+ if (fx3_state == FX3_STATE_ERROR or fx3_state == FX3_STATE_UNDEFINED) {
+ return fx3_state;
+ }
+ }
+
+ UHD_LOGGER_INFO("B200") << "Loading bootloader image: " << filestring << "...";
+
+ size_t bytes_sent = 0;
+ while (!file.eof()) {
+ file.read((char*)&out_buff[0], transfer_size);
+ const std::streamsize n = file.gcount();
+ if (n == 0)
+ continue;
+
+ uint16_t transfer_count = uint16_t(n);
+
+ // Send the data to the device
+ int nwritten = fx3_control_write(
+ B200_VREQ_EEPROM_WRITE, 0, bytes_sent, out_buff.data(), transfer_count, 5000);
+ if (nwritten < 0) {
+ throw uhd::io_error(
+ (boost::format(
+ "load_bootloader: cannot write bitstream to FX3 (%d: %s)")
+ % nwritten % libusb_error_name(nwritten))
+ .str());
+ } else if (nwritten != transfer_count) {
+ throw uhd::io_error(
+ (boost::format(
+ "load_bootloader: short write while transferring bitstream "
+ "to FX3 (expecting: %d, returned: %d)")
+ % transfer_count % nwritten)
+ .str());
+ }
+
+ const size_t LOG_GRANULARITY = 10; // %. Keep this an integer divisor of 100.
+
+ if (bytes_sent == 0)
+ UHD_LOGGER_DEBUG("B200") << "Bootloader load: 0%" << std::flush;
+ const size_t percent_before =
+ size_t((bytes_sent * 100) / file_size)
+ - (size_t((bytes_sent * 100) / file_size) % LOG_GRANULARITY);
+ bytes_sent += transfer_count;
+ const size_t percent_after =
+ size_t((bytes_sent * 100) / file_size)
+ - (size_t((bytes_sent * 100) / file_size) % LOG_GRANULARITY);
+ if (percent_before != percent_after) {
+ UHD_LOGGER_DEBUG("B200") << "Bootloader load: " << std::setw(3) << percent_after << "%";
+ }
+ }
+
+ file.close();
+
+ // ensure FX3 is in non-error state
+ {
+ uint8_t fx3_state = get_fx3_status();
+
+ if (fx3_state == FX3_STATE_ERROR or fx3_state == FX3_STATE_UNDEFINED) {
+ return fx3_state;
+ }
+ }
+
+ UHD_LOGGER_DEBUG("B200") << "Bootloader image loaded!";
+
+ return 0;
+ }
+
private:
usb_control::sptr _usb_ctrl;
};
diff --git a/host/lib/usrp/b200/b200_iface.hpp b/host/lib/usrp/b200/b200_iface.hpp
index 86307bc21..a61e03075 100644
--- a/host/lib/usrp/b200/b200_iface.hpp
+++ b/host/lib/usrp/b200/b200_iface.hpp
@@ -1,6 +1,6 @@
//
// Copyright 2012-2013 Ettus Research LLC
-// Copyright 2018 Ettus Research, a National Instruments Company
+// Copyright 2018-2019 Ettus Research, a National Instruments Brand
//
// SPDX-License-Identifier: GPL-3.0-or-later
//
@@ -45,6 +45,7 @@ static const uhd::dict<uint16_t, b200_product_t> B2XX_PID_TO_PRODUCT = boost::as
;
static const std::string B200_FW_FILE_NAME = "usrp_b200_fw.hex";
+static const std::string B200_BL_FILE_NAME = "usrp_b200_bl.img";
//! Map the EEPROM product ID codes to the product
static const uhd::dict<uint16_t, b200_product_t> B2XX_PRODUCT_ID = boost::assign::map_list_of
@@ -111,6 +112,9 @@ public:
//! load an FPGA image
virtual uint32_t load_fpga(const std::string filestring, bool force=false) = 0;
+ //! load a bootloader image onto device EEPROM
+ virtual uint32_t load_bootloader(const std::string filestring) = 0;
+
virtual void write_eeprom(uint16_t addr, uint16_t offset, const uhd::byte_vector_t &bytes) = 0;
virtual uhd::byte_vector_t read_eeprom(uint16_t addr, uint16_t offset, size_t num_bytes) = 0;
diff --git a/host/lib/usrp/b200/b200_mb_eeprom.cpp b/host/lib/usrp/b200/b200_mb_eeprom.cpp
index 2be014fd5..5a37cc9c1 100644
--- a/host/lib/usrp/b200/b200_mb_eeprom.cpp
+++ b/host/lib/usrp/b200/b200_mb_eeprom.cpp
@@ -1,82 +1,163 @@
//
-// Copyright 2017 Ettus Research (National Instruments Corp.)
+// Copyright 2017-2019 Ettus Research, a National Instruments Brand
//
// SPDX-License-Identifier: GPL-3.0-or-later
//
#include "b200_impl.hpp"
-#include <uhdlib/utils/eeprom_utils.hpp>
#include <uhd/usrp/mboard_eeprom.hpp>
+#include <uhdlib/utils/eeprom_utils.hpp>
-namespace {
- /* On the B200, this field indicates the slave address. From the FX3, this
- * address is always 0. */
- static const uint8_t B200_EEPROM_SLAVE_ADDR = 0x04;
-
- //use char array so we dont need to attribute packed
- struct b200_eeprom_map{
- unsigned char _r[220];
- unsigned char revision[2];
- unsigned char product[2];
- unsigned char name[NAME_MAX_LEN];
- unsigned char serial[SERIAL_LEN];
- };
-}
+#include <unordered_map>
using namespace uhd;
using uhd::usrp::mboard_eeprom_t;
+namespace {
+
+constexpr auto LOG_ID = "B2xx_EEPROM";
+
+struct eeprom_field_t
+{
+ size_t offset;
+ size_t length;
+};
+
+// EEPROM map information is duplicated in common_const.h for the
+// firmware and bootloader code.
+// EEPROM map information is duplicated in b2xx_fx3_utils.cpp
+
+constexpr uint16_t SIGNATURE_ADDR = 0x0000;
+constexpr size_t SIGNATURE_LENGTH = 4;
+
+constexpr auto EXPECTED_MAGIC = "45568"; // 0xB200
+constexpr auto EXPECTED_COMPAT = "1";
+
+constexpr uint32_t REV1_SIGNATURE = 0xB01A5943;
+constexpr uint16_t REV1_BASE_ADDR = 0x7F00;
+constexpr size_t REV1_LENGTH = 46;
+
+const std::unordered_map<std::string, eeprom_field_t> B200_REV1_MAP = {
+ {"magic", {0, 2}},
+ {"eeprom_revision", {2, 2}},
+ {"eeprom_compat", {4, 2}},
+ {"vendor_id", {6, 2}},
+ {"product_id", {8, 2}},
+ {"revision", {10, 2}},
+ {"product", {12, 2}},
+ {"name", {14, NAME_MAX_LEN}},
+ {"serial", {14 + NAME_MAX_LEN, SERIAL_LEN}},
+ // pad of 210 bytes
+};
+
+constexpr uint32_t REV0_SIGNATURE = 0xB2145943;
+constexpr uint16_t REV0_BASE_ADDR = 0x04DC;
+constexpr size_t REV0_LENGTH = 36;
+
+const std::unordered_map<std::string, eeprom_field_t> B200_REV0_MAP = {
+ // front pad of 220 bytes
+ {"revision", {0, 2}},
+ {"product", {2, 2}},
+ {"name", {4, NAME_MAX_LEN}},
+ {"serial", {4 + NAME_MAX_LEN, SERIAL_LEN}},
+};
+
+constexpr int UNKNOWN_REV = -1;
+
+int _get_rev(uhd::i2c_iface::sptr iface)
+{
+ auto bytes =
+ iface->read_eeprom(SIGNATURE_ADDR >> 8, SIGNATURE_ADDR & 0xFF, SIGNATURE_LENGTH);
+ uint32_t signature = bytes[3] << 24 | bytes[2] << 16 | bytes[1] << 8 | bytes[0];
+ if (signature == REV0_SIGNATURE) {
+ return 0;
+ } else if (signature == REV1_SIGNATURE) {
+ return 1;
+ } else {
+ UHD_LOG_WARNING(LOG_ID, "Unknown signature! 0x" << std::hex << signature);
+ return UNKNOWN_REV;
+ }
+}
+
+byte_vector_t _get_eeprom(uhd::i2c_iface::sptr iface)
+{
+ const auto rev = _get_rev(iface);
+ if (rev == UNKNOWN_REV)
+ {
+ return byte_vector_t();
+ }
+
+ const uint16_t addr = (rev == 0) ? REV0_BASE_ADDR : REV1_BASE_ADDR;
+ const size_t length = (rev == 0) ? REV0_LENGTH : REV1_LENGTH;
+
+ return iface->read_eeprom(addr >> 8, addr & 0xFF, length);
+}
+
+}
+
mboard_eeprom_t b200_impl::get_mb_eeprom(uhd::i2c_iface::sptr iface)
{
+ auto rev = _get_rev(iface);
+ auto bytes = _get_eeprom(iface);
mboard_eeprom_t mb_eeprom;
+ if (rev == UNKNOWN_REV or bytes.empty()) {
+ return mb_eeprom;
+ }
- //extract the revision number
- mb_eeprom["revision"] = uint16_bytes_to_string(
- iface->read_eeprom(B200_EEPROM_SLAVE_ADDR, offsetof(b200_eeprom_map, revision), 2)
- );
+ auto eeprom_map = (rev == 0) ? B200_REV0_MAP : B200_REV1_MAP;
- //extract the product code
- mb_eeprom["product"] = uint16_bytes_to_string(
- iface->read_eeprom(B200_EEPROM_SLAVE_ADDR, offsetof(b200_eeprom_map, product), 2)
- );
+ for (const auto &element : eeprom_map)
+ {
+ // There is an assumption here that fields of length 2 are uint16's and
+ // lengths other than 2 are strings. Update this code if that
+ // assumption changes.
+ byte_vector_t element_bytes = byte_vector_t(bytes.begin() + element.second.offset,
+ bytes.begin() + element.second.offset + element.second.length);
- //extract the serial
- mb_eeprom["serial"] = bytes_to_string(iface->read_eeprom(
- B200_EEPROM_SLAVE_ADDR, offsetof(b200_eeprom_map, serial), SERIAL_LEN
- ));
+ mb_eeprom[element.first] = (element.second.length == 2)
+ ? uint16_bytes_to_string(element_bytes)
+ : bytes_to_string(element_bytes);
+ }
- //extract the name
- mb_eeprom["name"] = bytes_to_string(iface->read_eeprom(
- B200_EEPROM_SLAVE_ADDR, offsetof(b200_eeprom_map, name), NAME_MAX_LEN
- ));
+ if (rev > 0) {
+ if (mb_eeprom["magic"] != EXPECTED_MAGIC)
+ {
+ throw uhd::runtime_error(
+ str(boost::format("EEPROM magic value mismatch. Device returns %s, but "
+ "should have been %s.")
+ % mb_eeprom["magic"] % EXPECTED_MAGIC));
+ }
+ if (mb_eeprom["eeprom_compat"] != EXPECTED_COMPAT) {
+ throw uhd::runtime_error(
+ str(boost::format("EEPROM compat value mismatch. Device returns %s, but "
+ "should have been %s.")
+ % mb_eeprom["eeprom_compat"] % EXPECTED_COMPAT));
+ }
+ }
return mb_eeprom;
}
-void b200_impl::set_mb_eeprom(const mboard_eeprom_t &mb_eeprom)
+void b200_impl::set_mb_eeprom(const mboard_eeprom_t& mb_eeprom)
{
- //parse the revision number
- if (mb_eeprom.has_key("revision")) _iface->write_eeprom(
- B200_EEPROM_SLAVE_ADDR, offsetof(b200_eeprom_map, revision),
- string_to_uint16_bytes(mb_eeprom["revision"])
- );
-
- //parse the product code
- if (mb_eeprom.has_key("product")) _iface->write_eeprom(
- B200_EEPROM_SLAVE_ADDR, offsetof(b200_eeprom_map, product),
- string_to_uint16_bytes(mb_eeprom["product"])
- );
-
- //store the serial
- if (mb_eeprom.has_key("serial")) _iface->write_eeprom(
- B200_EEPROM_SLAVE_ADDR, offsetof(b200_eeprom_map, serial),
- string_to_bytes(mb_eeprom["serial"], SERIAL_LEN)
- );
-
- //store the name
- if (mb_eeprom.has_key("name")) _iface->write_eeprom(
- B200_EEPROM_SLAVE_ADDR, offsetof(b200_eeprom_map, name),
- string_to_bytes(mb_eeprom["name"], NAME_MAX_LEN)
- );
-}
+ const auto rev = _get_rev(_iface);
+ auto eeprom_map = (rev == 0) ? B200_REV0_MAP : B200_REV1_MAP;
+ auto base_addr = (rev == 0) ? REV0_BASE_ADDR : REV1_BASE_ADDR;
+ for (const auto& key : mb_eeprom.keys())
+ {
+ if (eeprom_map.find(key) == eeprom_map.end())
+ {
+ UHD_LOG_WARNING(
+ LOG_ID, "Unknown key in mb_eeprom during set_mb_eeprom: " << key);
+ continue;
+ }
+ // There is an assumption here that fields of length 2 are uint16's and
+ // lengths other than 2 are strings. Update this code if that
+ // assumption changes.
+ auto field = eeprom_map.at(key);
+ auto bytes = (field.length == 2) ? string_to_uint16_bytes(mb_eeprom[key])
+ : string_to_bytes(mb_eeprom[key], field.length);
+ _iface->write_eeprom(base_addr >> 8, (base_addr & 0xFF) + field.offset, bytes);
+ }
+}
diff --git a/host/utils/b2xx_fx3_utils.cpp b/host/utils/b2xx_fx3_utils.cpp
index e723b904a..c94badc9d 100644
--- a/host/utils/b2xx_fx3_utils.cpp
+++ b/host/utils/b2xx_fx3_utils.cpp
@@ -1,6 +1,6 @@
//
// Copyright 2010-2014 Ettus Research LLC
-// Copyright 2018 Ettus Research, a National Instruments Company
+// Copyright 2018-2019 Ettus Research, a National Instruments Brand
//
// SPDX-License-Identifier: GPL-3.0-or-later
//
@@ -32,36 +32,52 @@
namespace po = boost::program_options;
namespace fs = boost::filesystem;
+namespace {
struct vid_pid_t
{
uint16_t vid;
uint16_t pid;
};
-const static vid_pid_t known_vid_pids[] = {{FX3_VID, FX3_DEFAULT_PID},
+const vid_pid_t known_vid_pids[] = {{FX3_VID, FX3_DEFAULT_PID},
{FX3_VID, FX3_REENUM_PID},
{B200_VENDOR_ID, B200_PRODUCT_ID},
{B200_VENDOR_ID, B200MINI_PRODUCT_ID},
{B200_VENDOR_ID, B205MINI_PRODUCT_ID},
{B200_VENDOR_NI_ID, B200_PRODUCT_NI_ID},
{B200_VENDOR_NI_ID, B210_PRODUCT_NI_ID}};
-const static std::vector<vid_pid_t> known_vid_pid_vector(known_vid_pids,
+const std::vector<vid_pid_t> known_vid_pid_vector(known_vid_pids,
known_vid_pids + (sizeof(known_vid_pids) / sizeof(known_vid_pids[0])));
-static const size_t EEPROM_INIT_VALUE_VECTOR_SIZE = 8;
-static uhd::byte_vector_t construct_eeprom_init_value_vector(uint16_t vid, uint16_t pid)
+const uhd::byte_vector_t OLD_EEPROM_SIGNATURE = {0x43, 0x59, 0x14, 0xB2};
+const uhd::byte_vector_t NEW_EEPROM_SIGNATURE = {0x43, 0x59, 0x1A, 0xB0};
+
+const size_t EEPROM_INIT_VALUE_VECTOR_SIZE = 8;
+uhd::byte_vector_t construct_eeprom_init_value_vector(uint16_t vid, uint16_t pid)
{
- uhd::byte_vector_t init_values(EEPROM_INIT_VALUE_VECTOR_SIZE);
- init_values.at(0) = 0x43;
- init_values.at(1) = 0x59;
- init_values.at(2) = 0x14;
- init_values.at(3) = 0xB2;
- init_values.at(4) = static_cast<uint8_t>(pid & 0xff);
- init_values.at(5) = static_cast<uint8_t>(pid >> 8);
- init_values.at(6) = static_cast<uint8_t>(vid & 0xff);
- init_values.at(7) = static_cast<uint8_t>(vid >> 8);
+ uhd::byte_vector_t init_values(OLD_EEPROM_SIGNATURE);
+ init_values.push_back(static_cast<uint8_t>(pid & 0xff));
+ init_values.push_back(static_cast<uint8_t>(pid >> 8));
+ init_values.push_back(static_cast<uint8_t>(vid & 0xff));
+ init_values.push_back(static_cast<uint8_t>(vid >> 8));
return init_values;
}
+constexpr uint8_t EEPROM_DATA_ADDR_HIGH_BYTE = 0x7F;
+constexpr uint8_t EEPROM_DATA_HEADER_ADDR = 0x00;
+constexpr uint8_t EEPROM_DATA_VID_PID_ADDR = 0x06;
+constexpr uint8_t EEPROM_DATA_OLD_DATA_ADDR = 0x0A;
+
+const uhd::byte_vector_t EEPROM_DATA_HEADER = {
+ 0x00,
+ 0xB2, // magic
+ 0x01,
+ 0x00, // eeprom_revision
+ 0x01,
+ 0x00 // eeprom_compat
+};
+
+} // namespace
+
//! used with lexical cast to parse a hex string
template <class T> struct to_hex
{
@@ -300,29 +316,36 @@ int erase_eeprom(b200_iface::sptr& b200)
int32_t main(int32_t argc, char* argv[])
{
uint16_t vid, pid;
- std::string pid_str, vid_str, fw_file, fpga_file, writevid_str, writepid_str;
+ std::string pid_str, vid_str, fw_file, fpga_file, bl_file, writevid_str, writepid_str;
bool user_supplied_vid_pid = false;
+ // clang-format off
po::options_description visible("Allowed options");
- visible.add_options()("help,h", "help message")(
+ visible.add_options()(
+ "help,h", "help message")(
"vid,v", po::value<std::string>(&vid_str), "Specify VID of device to use.")(
"pid,p", po::value<std::string>(&pid_str), "Specify PID of device to use.")(
"speed,S", "Read back the USB mode currently in use.")(
"reset-device,D", "Reset the B2xx Device.")(
"reset-fpga,F", "Reset the FPGA (does not require re-programming.")(
- "reset-usb,U", "Reset the USB subsystem on your host computer.")("load-fw,W",
- po::value<std::string>(&fw_file),
- "Load a firmware (hex) file into the FX3.")("load-fpga,L",
- po::value<std::string>(&fpga_file),
- "Load a FPGA (bin) file into the FPGA.");
+ "reset-usb,U", "Reset the USB subsystem on your host computer.")(
+ "load-fw,W", po::value<std::string>(&fw_file),
+ "Load a firmware (hex) file into the FX3.")(
+ "load-fpga,L", po::value<std::string>(&fpga_file),
+ "Load a FPGA (bin) file into the FPGA.")(
+ "load-bootloader,B", po::value<std::string>(&bl_file),
+ "Load a bootloader (img) file into the EEPROM");
// Hidden options provided for testing - use at your own risk!
po::options_description hidden("Hidden options");
- hidden.add_options()("init-device,I", "Initialize a B2xx device.")("uninit-device",
- "Uninitialize a B2xx device.")("read-eeprom,R", "Read first 8 bytes of EEPROM")(
+ hidden.add_options()(
+ "init-device,I", "Initialize a B2xx device.")(
+ "uninit-device", "Uninitialize a B2xx device.")(
+ "read-eeprom,R", "Read first 8 bytes of EEPROM")(
"erase-eeprom,E", "Erase first 8 bytes of EEPROM")(
"write-vid", po::value<std::string>(&writevid_str), "Write VID field of EEPROM")(
"write-pid", po::value<std::string>(&writepid_str), "Write PID field of EEPROM");
+ // clang-format on
po::options_description desc;
desc.add(visible);
@@ -578,6 +601,71 @@ int32_t main(int32_t argc, char* argv[])
}
std::cout << "FPGA load complete, releasing USB interface..." << std::endl;
+ } else if (vm.count("load-bootloader")) {
+ if (bl_file.empty())
+ bl_file = uhd::find_image_path(B200_BL_FILE_NAME);
+
+ if (bl_file.empty()) {
+ std::cerr << "Bootloader image not found!" << std::endl;
+ return -1;
+ }
+
+ if (!(fs::exists(bl_file))) {
+ std::cerr << "Invalid filepath: " << bl_file << std::endl;
+ return -1;
+ }
+
+ std::cout << "Loading Bootloader image (" << bl_file << ")" << std::endl;
+
+ // In the upgrade case, we need to migrate the EEPROM data to a new
+ // location before loading the bootloader
+
+ // Use the signature to detect the old EEPROM layout
+ auto signature = b200->read_eeprom(0x0, 0x0, 4);
+ if (signature == OLD_EEPROM_SIGNATURE) {
+ std::cout << "Old EEPROM detected. Upgrading EEPROM image to latest revision."
+ << std::endl;
+
+ // Read values that will be clobbered by the bootloader
+ auto pidvid = b200->read_eeprom(0x00, 0x04, 4);
+ uhd::byte_vector_t vidpid = {pidvid[2], pidvid[3], pidvid[0], pidvid[1]};
+ auto eeprom_data = b200->read_eeprom(0x04, 0xDC, 36);
+
+ // Write in default header
+ b200->write_eeprom(
+ EEPROM_DATA_ADDR_HIGH_BYTE, EEPROM_DATA_HEADER_ADDR, EEPROM_DATA_HEADER);
+ // Write back data to the device
+ b200->write_eeprom(
+ EEPROM_DATA_ADDR_HIGH_BYTE, EEPROM_DATA_VID_PID_ADDR, vidpid);
+ b200->write_eeprom(
+ EEPROM_DATA_ADDR_HIGH_BYTE, EEPROM_DATA_OLD_DATA_ADDR, eeprom_data);
+ }
+
+ uint32_t fx3_state;
+ try {
+ fx3_state = b200->load_bootloader(bl_file);
+ } // returns 0 on success, or FX3 state on error
+ catch (uhd::exception& e) {
+ std::cerr << "Exception while loading bootloader: " << e.what() << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ if (fx3_state != 0) {
+ std::cerr << std::flush << "Error loading bootloader. FX3 state ("
+ << fx3_state << "): " << b200_iface::fx3_state_string(fx3_state)
+ << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ std::cout << "Bootloader load complete, resetting device..." << std::endl;
+
+ // reset the device
+ try {
+ b200->reset_fx3();
+ } catch (uhd::exception& e) {
+ std::cerr << "Exception while resetting FX3: " << e.what() << std::endl;
+ return EXIT_FAILURE;
+ }
}
std::cout << "Operation complete! I did it! I did it!" << std::endl;