aboutsummaryrefslogtreecommitdiffstats
path: root/host/lib/usrp/b200/b200_mb_eeprom.cpp
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 /host/lib/usrp/b200/b200_mb_eeprom.cpp
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
Diffstat (limited to 'host/lib/usrp/b200/b200_mb_eeprom.cpp')
-rw-r--r--host/lib/usrp/b200/b200_mb_eeprom.cpp195
1 files changed, 138 insertions, 57 deletions
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);
+ }
+}