aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Braun <martin.braun@ettus.com>2022-07-07 17:03:50 +0200
committermichael-west <michael.west@ettus.com>2023-01-10 14:19:00 -0800
commite315f0dbc69b1939abd5fffb7a17ff2f3f9aeb81 (patch)
tree26dade33fa4107356d9284bf3b0f6277ec8e32cf
parentx300: Respect X300_FW_COMMS_FLAGS_ARP_FAIL flag (diff)
downloaduhd-e315f0dbc69b1939abd5fffb7a17ff2f3f9aeb81.tar.xz
uhd-e315f0dbc69b1939abd5fffb7a17ff2f3f9aeb81.zip
usrp: Add xport_adapter_ctrl core
This allows accessing the advanced transport adapter capabilities on X300, where there is no MPM to aid accessing it. It is functionally equivalent to xport_adapter_ctrl.py. Includes unit tests. Co-authored-by: Virendra Kakade <virendra.kakade@ni.com>
-rw-r--r--host/lib/include/uhdlib/usrp/cores/xport_adapter_ctrl.hpp96
-rw-r--r--host/lib/usrp/cores/CMakeLists.txt6
-rw-r--r--host/lib/usrp/cores/xport_adapter_ctrl.cpp183
-rw-r--r--host/tests/CMakeLists.txt6
-rw-r--r--host/tests/xport_adapter_ctrl_test.cpp188
5 files changed, 479 insertions, 0 deletions
diff --git a/host/lib/include/uhdlib/usrp/cores/xport_adapter_ctrl.hpp b/host/lib/include/uhdlib/usrp/cores/xport_adapter_ctrl.hpp
new file mode 100644
index 000000000..80a16c5ac
--- /dev/null
+++ b/host/lib/include/uhdlib/usrp/cores/xport_adapter_ctrl.hpp
@@ -0,0 +1,96 @@
+//
+// Copyright 2022 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#pragma once
+
+#include <uhd/types/device_addr.hpp>
+#include <uhdlib/rfnoc/rfnoc_common.hpp>
+#include <uhdlib/utils/compat_check.hpp>
+#include <cstdlib>
+#include <functional>
+#include <string>
+
+namespace uhd { namespace usrp {
+
+/*! Transport adapter control
+ *
+ * This is a C++ version of xport_adapter_ctrl.py and xport_adapter_mgr.py
+ * rolled into one class.
+ */
+class xport_adapter_ctrl
+{
+public:
+ // Register offsets
+ static constexpr uint32_t XPORT_ADAPTER_COMPAT_NUM =
+ 0x0000; // 8 bits major, 8 bits minor
+ static constexpr uint32_t XPORT_ADAPTER_INFO = 0x0004;
+ static constexpr uint32_t XPORT_ADAPTER_NODE_INST = 0x0008; // read-only
+ static constexpr uint32_t KV_MAC_LO = 0x000C;
+ static constexpr uint32_t KV_MAC_HI = 0x0010;
+ static constexpr uint32_t KV_IPV4 = 0x0014;
+ static constexpr uint32_t KV_UDP_PORT = 0x0018;
+ static constexpr uint32_t KV_CFG = 0x001C;
+ static constexpr uint32_t KV_IPV4_W_ARP = 0x0020;
+ // The last entry has no equivalent in the FPGA, it will be mapped back to
+ // KV_IPV4, but will force a MAC address lookup on the device firmware.
+
+ // Use these as the stream_mode argument in add_remote_ep_route()
+ static const char STREAM_MODE_RAW_PAYLOAD[];
+ static const char STREAM_MODE_FULL_PACKET[];
+
+
+ using poke_fn_type = std::function<void(const uint32_t, const uint32_t)>;
+ using peek_fn_type = std::function<uint32_t(const uint32_t)>;
+
+ xport_adapter_ctrl(poke_fn_type&& poke_fn,
+ peek_fn_type&& peek_fn,
+ const bool has_arp,
+ const std::string& log_id);
+
+ ~xport_adapter_ctrl(void) = default;
+
+ uhd::rfnoc::sep_inst_t get_xport_adapter_inst() const
+ {
+ return _ta_inst;
+ }
+
+ uhd::compat_num16 get_compat_num() const
+ {
+ return _compat_num;
+ }
+
+ uhd::device_addr_t get_capabilities() const
+ {
+ return _capabilities;
+ }
+
+ void add_remote_ep_route(const rfnoc::sep_inst_t epid,
+ const std::string ipv4,
+ const std::string port,
+ const std::string mac_addr,
+ const std::string stream_mode);
+
+private:
+ //! Poker object
+ poke_fn_type _poke32;
+
+ //! Peeker object
+ peek_fn_type _peek32;
+
+ //! Log ID (prefix)
+ const std::string _log_id;
+
+ //! Compat numbers
+ const uhd::compat_num16 _compat_num;
+
+ //! Transport adapter instance
+ const rfnoc::sep_inst_t _ta_inst;
+
+ //! Dictionary of available capabilities (rx_routing, rx_hdr_removal, ...)
+ uhd::device_addr_t _capabilities;
+};
+
+}} // namespace usrp::zbx
diff --git a/host/lib/usrp/cores/CMakeLists.txt b/host/lib/usrp/cores/CMakeLists.txt
index 807eec685..2e2132ad3 100644
--- a/host/lib/usrp/cores/CMakeLists.txt
+++ b/host/lib/usrp/cores/CMakeLists.txt
@@ -20,6 +20,12 @@ if(ENABLE_B100 OR ENABLE_USRP2)
)
endif(ENABLE_B100 OR ENABLE_USRP2)
+if(ENABLE_X300)
+ LIBUHD_APPEND_SOURCES(
+ ${CMAKE_CURRENT_SOURCE_DIR}/xport_adapter_ctrl.cpp
+ )
+endif()
+
LIBUHD_APPEND_SOURCES(
${CMAKE_CURRENT_SOURCE_DIR}/dma_fifo_core_3000.cpp
${CMAKE_CURRENT_SOURCE_DIR}/dsp_core_utils.cpp
diff --git a/host/lib/usrp/cores/xport_adapter_ctrl.cpp b/host/lib/usrp/cores/xport_adapter_ctrl.cpp
new file mode 100644
index 000000000..3a07b767a
--- /dev/null
+++ b/host/lib/usrp/cores/xport_adapter_ctrl.cpp
@@ -0,0 +1,183 @@
+//
+// Copyright 2022 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#include <uhd/exception.hpp>
+#include <uhd/utils/log.hpp>
+#include <uhdlib/usrp/cores/xport_adapter_ctrl.hpp>
+#include <unordered_map>
+#include <boost/algorithm/string/case_conv.hpp>
+#include <boost/asio.hpp>
+#include <cstdio>
+#include <chrono>
+#include <thread>
+
+using namespace uhd::usrp;
+
+namespace {
+
+const uhd::compat_num16 MIN_COMPAT_REMOTE_STRM{1, 0};
+
+const std::unordered_map<std::string, uint32_t> STREAM_MODES{
+ {xport_adapter_ctrl::STREAM_MODE_FULL_PACKET, 0},
+ {xport_adapter_ctrl::STREAM_MODE_RAW_PAYLOAD, 1}};
+
+std::pair<uint32_t, uint32_t> cast_ipv4_and_port(
+ const std::string& ipv4, const std::string& port)
+{
+ using namespace boost::asio;
+ io_service io_service;
+ ip::udp::resolver resolver(io_service);
+ try {
+ ip::udp::resolver::query query(ip::udp::v4(), ipv4, port);
+ ip::udp::endpoint endpoint = *resolver.resolve(query);
+ return {uint32_t(endpoint.address().to_v4().to_ulong()),
+ uint32_t(endpoint.port())};
+ } catch (const std::exception&) {
+ throw uhd::value_error("Invalid UDP address: " + ipv4 + ":" + port);
+ }
+}
+
+std::pair<uint32_t, uint32_t> cast_mac(const std::string& mac_addr)
+{
+ unsigned char mac[8] = {0}; // 8 bytes for conversion to uint64_t
+ int ret = std::sscanf(mac_addr.c_str(),
+ "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
+ &mac[5],
+ &mac[4],
+ &mac[3],
+ &mac[2],
+ &mac[1],
+ &mac[0]);
+ if (ret != 6) {
+ throw uhd::value_error("Invalid MAC address: " + mac_addr);
+ }
+ const uint32_t mac_msb = *(reinterpret_cast<uint32_t*>(mac + 0));
+ const uint32_t mac_lsb = *(reinterpret_cast<uint32_t*>(mac + 4));
+ return {mac_msb, mac_lsb};
+}
+
+
+} // namespace
+
+constexpr const char xport_adapter_ctrl::STREAM_MODE_RAW_PAYLOAD[] = "RAW_PAYLOAD";
+constexpr const char xport_adapter_ctrl::STREAM_MODE_FULL_PACKET[] = "FULL_PACKET";
+
+xport_adapter_ctrl::xport_adapter_ctrl(poke_fn_type&& poke_fn,
+ peek_fn_type&& peek_fn,
+ const bool has_arp,
+ const std::string& log_id)
+ : _poke32(std::move(poke_fn))
+ , _peek32(std::move(peek_fn))
+ , _log_id(log_id)
+ , _compat_num(_peek32(XPORT_ADAPTER_COMPAT_NUM))
+ , _ta_inst(
+ _compat_num >= MIN_COMPAT_REMOTE_STRM ? _peek32(XPORT_ADAPTER_NODE_INST) : 0)
+{
+ if (_compat_num >= MIN_COMPAT_REMOTE_STRM) {
+ const uint32_t capabilities = _peek32(XPORT_ADAPTER_INFO);
+ if (capabilities & (1 << 0)) {
+ _capabilities["rx_routing"] = "1";
+ }
+ if (capabilities & (1 << 1)) {
+ _capabilities["rx_hdr_removal"] = "1";
+ }
+ if (has_arp) {
+ _capabilities["arp"] = "1";
+ }
+ _capabilities["ta_inst"] = std::to_string(_ta_inst);
+ }
+ UHD_LOG_TRACE(
+ _log_id, "Remote streaming capabilities: " << _capabilities.to_string());
+}
+
+
+void xport_adapter_ctrl::add_remote_ep_route(const uhd::rfnoc::sep_inst_t epid,
+ const std::string ipv4,
+ const std::string port,
+ const std::string mac_addr,
+ const std::string stream_mode)
+{
+ const std::string stream_mode_upper = boost::to_upper_copy(stream_mode);
+ UHD_ASSERT_THROW(STREAM_MODES.count(stream_mode_upper));
+ if (!_capabilities.has_key("rx_routing")) {
+ throw uhd::runtime_error(
+ "This transport adapter does not support routing to remote "
+ "destinations!");
+ }
+ if (stream_mode_upper == STREAM_MODE_RAW_PAYLOAD
+ && !_capabilities.has_key("rx_hdr_removal")) {
+ throw uhd::runtime_error(
+ "Requesting to remove CHDR headers, but feature not enabled!");
+ }
+
+ if (ipv4.empty()) {
+ throw uhd::value_error("Must provide valid IPv4 address!");
+ }
+ if (port.empty()) {
+ throw uhd::value_error("Must provide valid port value!");
+ }
+ if (mac_addr.empty() && !_capabilities.has_key("arp")) {
+ throw uhd::value_error(
+ "Device has no ARP capabilities -- must provide MAC address!");
+ }
+ const bool request_arp = mac_addr.empty();
+ const auto ipv4_and_port = cast_ipv4_and_port(ipv4, port);
+ const uint32_t stream_mode_int = STREAM_MODES.at(stream_mode_upper);
+ const uint32_t cfg_word = epid | (stream_mode_int << 16);
+
+ // Check TA is ready by polling BUSY flag
+ using namespace std::chrono_literals;
+ const auto timeout = std::chrono::steady_clock::now() + 500ms;
+ while (bool(_peek32(KV_CFG) & (1 << 31))) {
+ if (std::chrono::steady_clock::now() > timeout) {
+ UHD_LOG_THROW(uhd::runtime_error,
+ _log_id,
+ "Timeout while polling BUSY flag on transport adapter!");
+ }
+ std::this_thread::sleep_for(100ms);
+ }
+
+ // Now write settings to TA
+ UHD_LOG_DEBUG(_log_id,
+ "On transport adapter " << _ta_inst << ": Adding route from EPID " << epid
+ << " to destination " << ipv4 << ":" << port
+ << " (MAC Address: " << (request_arp ? "AUTO" : mac_addr)
+ << "), stream mode " << stream_mode_upper << " ("
+ << stream_mode_int << ")");
+ if (!request_arp) {
+ const auto mac_int = cast_mac(mac_addr);
+ _poke32(KV_MAC_LO, mac_int.first);
+ _poke32(KV_MAC_HI, mac_int.second);
+ _poke32(KV_IPV4, ipv4_and_port.first);
+ } else {
+ // If the user didn't specify MAC, then the device firmware can try and
+ // look it up.
+ constexpr int num_arp_tries = 3;
+ constexpr auto retry_interval = 300ms;
+ bool arp_successful = false;
+ for (int i = 0; i < num_arp_tries; i++) {
+ try {
+ _poke32(KV_IPV4_W_ARP, ipv4_and_port.first);
+ arp_successful = true;
+ break;
+ } catch (const uhd::lookup_error&) {
+ UHD_LOG_TRACE(_log_id, "ARP lookup failed for IP address " << ipv4);
+ std::this_thread::sleep_for(retry_interval);
+ }
+ }
+ if (!arp_successful) {
+ UHD_LOG_THROW(uhd::lookup_error,
+ _log_id,
+ "Device was unable to look up Ethernet (MAC) address for IP "
+ "address "
+ << ipv4
+ << ". Make sure device is correctly connected, "
+ "or provide MAC address manually.");
+ }
+ }
+ _poke32(KV_UDP_PORT, ipv4_and_port.second);
+ _poke32(KV_CFG, cfg_word);
+}
diff --git a/host/tests/CMakeLists.txt b/host/tests/CMakeLists.txt
index b8a07d341..f40c252ad 100644
--- a/host/tests/CMakeLists.txt
+++ b/host/tests/CMakeLists.txt
@@ -453,6 +453,12 @@ UHD_ADD_NONAPI_TEST(
${UHD_SOURCE_DIR}/lib/utils/compat_check.cpp
)
+UHD_ADD_NONAPI_TEST(
+ TARGET "xport_adapter_ctrl_test.cpp"
+ EXTRA_SOURCES
+ ${UHD_SOURCE_DIR}/lib/usrp/cores/xport_adapter_ctrl.cpp
+)
+
########################################################################
# demo of a loadable module
########################################################################
diff --git a/host/tests/xport_adapter_ctrl_test.cpp b/host/tests/xport_adapter_ctrl_test.cpp
new file mode 100644
index 000000000..9fd2ddfd6
--- /dev/null
+++ b/host/tests/xport_adapter_ctrl_test.cpp
@@ -0,0 +1,188 @@
+//
+// Copyright 2022 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+
+#include <uhd/utils/log.hpp>
+#include <uhdlib/usrp/cores/xport_adapter_ctrl.hpp>
+#include <boost/test/unit_test.hpp>
+#include <iostream>
+#include <map>
+
+using namespace uhd::usrp;
+
+
+struct mock_xport_adapter_regs
+{
+ mock_xport_adapter_regs(uhd::rfnoc::sep_inst_t ta_inst,
+ const uhd::compat_num16 compat,
+ const uint32_t cap_flags)
+ {
+ mem[uint32_t(xport_adapter_ctrl::XPORT_ADAPTER_COMPAT_NUM)] =
+ (static_cast<uint32_t>(compat.get_major()) << 8) | compat.get_minor();
+ mem[uint32_t(xport_adapter_ctrl::XPORT_ADAPTER_NODE_INST)] =
+ static_cast<uint32_t>(ta_inst);
+ mem[uint32_t(xport_adapter_ctrl::XPORT_ADAPTER_INFO)] = cap_flags;
+ mem[uint32_t(xport_adapter_ctrl::KV_CFG)] = 0; // not busy
+ }
+
+ void poke32(const uint32_t addr, const uint32_t data)
+ {
+ mem[addr] = data;
+ }
+
+ uint32_t peek32(const uint32_t addr) const
+ {
+ return mem.count(addr) ? mem.at(addr) : 0xFFFFFFFF;
+ }
+
+ std::map<uint32_t, uint32_t> mem;
+};
+
+BOOST_AUTO_TEST_CASE(test_xport_adapter_init)
+{
+ constexpr uhd::rfnoc::sep_inst_t ta_inst = 1;
+ uhd::compat_num16 compat(1, 0);
+ mock_xport_adapter_regs regs(ta_inst, compat, 0x3);
+
+ xport_adapter_ctrl ta_ctl(
+ [&](const uint32_t addr, const uint32_t data) { regs.poke32(addr, data); },
+ [&](const uint32_t addr) { return regs.peek32(addr); },
+ false,
+ "TEST_TA_CTL");
+
+ BOOST_CHECK(ta_ctl.get_compat_num() == compat);
+ BOOST_CHECK_EQUAL(ta_ctl.get_xport_adapter_inst(), ta_inst);
+ const auto caps = ta_ctl.get_capabilities();
+ BOOST_CHECK(caps.has_key("rx_routing"));
+ BOOST_CHECK(caps.has_key("rx_hdr_removal"));
+ BOOST_CHECK(!caps.has_key("arp"));
+
+ xport_adapter_ctrl ta_ctl_w_arp(
+ [&](const uint32_t addr, const uint32_t data) { regs.poke32(addr, data); },
+ [&](const uint32_t addr) { return regs.peek32(addr); },
+ true,
+ "TEST_TA_CTL");
+ BOOST_CHECK(ta_ctl_w_arp.get_capabilities().has_key("arp"));
+}
+
+BOOST_AUTO_TEST_CASE(test_xport_adapter_sanity_check)
+{
+ constexpr uhd::rfnoc::sep_inst_t ta_inst = 1;
+ uhd::compat_num16 compat(1, 0);
+ mock_xport_adapter_regs regs(ta_inst, compat, 0x0);
+ xport_adapter_ctrl ta_ctl_nostream(
+ [&](const uint32_t addr, const uint32_t data) { regs.poke32(addr, data); },
+ [&](const uint32_t addr) { return regs.peek32(addr); },
+ false,
+ "TEST_TA_CTL");
+
+ BOOST_REQUIRE_THROW(
+ ta_ctl_nostream.add_remote_ep_route(0, "1.2.3.4", "5678", "", "INVALID_MODE"),
+ uhd::assertion_error);
+ BOOST_REQUIRE_THROW(
+ ta_ctl_nostream.add_remote_ep_route(
+ 0, "1.2.3.4", "5678", "", xport_adapter_ctrl::STREAM_MODE_RAW_PAYLOAD),
+ uhd::runtime_error);
+
+ regs.mem[uint32_t(xport_adapter_ctrl::XPORT_ADAPTER_INFO)] = 0x1;
+ xport_adapter_ctrl ta_ctl_noraw(
+ [&](const uint32_t addr, const uint32_t data) { regs.poke32(addr, data); },
+ [&](const uint32_t addr) { return regs.peek32(addr); },
+ false,
+ "TEST_TA_CTL");
+ BOOST_REQUIRE_THROW(
+ ta_ctl_nostream.add_remote_ep_route(
+ 0, "1.2.3.4", "5678", "", xport_adapter_ctrl::STREAM_MODE_RAW_PAYLOAD),
+ uhd::runtime_error);
+
+ regs.mem[uint32_t(xport_adapter_ctrl::XPORT_ADAPTER_INFO)] = 0x3;
+ xport_adapter_ctrl ta_ctl(
+ [&](const uint32_t addr, const uint32_t data) { regs.poke32(addr, data); },
+ [&](const uint32_t addr) { return regs.peek32(addr); },
+ false,
+ "TEST_TA_CTL");
+
+ ta_ctl.add_remote_ep_route(23,
+ "192.168.40.1",
+ "5678",
+ "AA:BB:CC:DD:EE:FF",
+ xport_adapter_ctrl::STREAM_MODE_RAW_PAYLOAD);
+ BOOST_CHECK_EQUAL(regs.mem[uint32_t(xport_adapter_ctrl::KV_MAC_LO)], 0xCCDDEEFF);
+ BOOST_CHECK_EQUAL(regs.mem[uint32_t(xport_adapter_ctrl::KV_MAC_HI)], 0x0000AABB);
+ BOOST_CHECK_EQUAL(regs.mem[uint32_t(xport_adapter_ctrl::KV_IPV4)], 0xc0a82801);
+ BOOST_CHECK_EQUAL(regs.mem[uint32_t(xport_adapter_ctrl::KV_UDP_PORT)], 5678);
+ BOOST_CHECK_EQUAL(regs.mem[uint32_t(xport_adapter_ctrl::KV_CFG)] & 0xFFFF, 23);
+ BOOST_CHECK_EQUAL((regs.mem[uint32_t(xport_adapter_ctrl::KV_CFG)] >> 16), 1);
+}
+
+BOOST_AUTO_TEST_CASE(test_xport_adapter_busy_flag)
+{
+ constexpr uhd::rfnoc::sep_inst_t ta_inst = 1;
+ uhd::compat_num16 compat(1, 0);
+ mock_xport_adapter_regs regs(ta_inst, compat, 0x3);
+ // We make it permanently busy
+ regs.mem[uint32_t(xport_adapter_ctrl::KV_CFG)] = 1 << 31;
+ xport_adapter_ctrl ta_ctl(
+ [&](const uint32_t addr, const uint32_t data) { regs.poke32(addr, data); },
+ [&](const uint32_t addr) { return regs.peek32(addr); },
+ false,
+ "TEST_TA_CTL");
+
+ UHD_LOG_INFO("TEST", "Expecting error here VVV");
+ BOOST_CHECK_THROW(ta_ctl.add_remote_ep_route(23,
+ "192.168.40.1",
+ "5678",
+ "AA:BB:CC:DD:EE:FF",
+ xport_adapter_ctrl::STREAM_MODE_RAW_PAYLOAD),
+ uhd::runtime_error);
+ UHD_LOG_INFO("TEST", "Expecting error here ^^^");
+
+ // Now we fake out BUSY flag going low after 2 peeks
+ int peek_count = 0;
+ xport_adapter_ctrl ta_ctl2(
+ [&](const uint32_t addr, const uint32_t data) { regs.poke32(addr, data); },
+ [&](const uint32_t addr) {
+ if (addr == uint32_t(xport_adapter_ctrl::KV_CFG)) {
+ UHD_LOG_INFO("TEST", "Detecting peek to KV_CFG...");
+ if (++peek_count == 2) {
+ return uint32_t(0);
+ }
+ }
+ return regs.peek32(addr);
+ },
+ false,
+ "TEST_TA_CTL");
+
+ BOOST_CHECK_NO_THROW(ta_ctl2.add_remote_ep_route(23,
+ "192.168.40.1",
+ "5678",
+ "AA:BB:CC:DD:EE:FF",
+ xport_adapter_ctrl::STREAM_MODE_RAW_PAYLOAD));
+}
+
+BOOST_AUTO_TEST_CASE(test_xport_adapter_arp)
+{
+ constexpr uhd::rfnoc::sep_inst_t ta_inst = 2;
+ uhd::compat_num16 compat(1, 0);
+ mock_xport_adapter_regs regs(ta_inst, compat, 0x3);
+ xport_adapter_ctrl ta_ctl(
+ [&](const uint32_t addr, const uint32_t data) { regs.poke32(addr, data); },
+ [&](const uint32_t addr) { return regs.peek32(addr); },
+ true,
+ "TEST_TA_CTL");
+
+ BOOST_CHECK_NO_THROW(ta_ctl.add_remote_ep_route(
+ 42, "192.168.30.1", "5678", "", xport_adapter_ctrl::STREAM_MODE_RAW_PAYLOAD));
+ BOOST_CHECK(!regs.mem.count(uint32_t(xport_adapter_ctrl::KV_MAC_LO)));
+ BOOST_CHECK(!regs.mem.count(uint32_t(xport_adapter_ctrl::KV_MAC_HI)));
+ BOOST_CHECK(!regs.mem.count(uint32_t(xport_adapter_ctrl::KV_IPV4)));
+ BOOST_CHECK_EQUAL(regs.mem[uint32_t(xport_adapter_ctrl::KV_UDP_PORT)], 5678);
+ BOOST_CHECK_EQUAL(regs.mem[uint32_t(xport_adapter_ctrl::KV_CFG)] & 0xFFFF, 42);
+ BOOST_CHECK_EQUAL((regs.mem[uint32_t(xport_adapter_ctrl::KV_CFG)] >> 16), 1);
+ BOOST_CHECK_EQUAL(regs.mem[uint32_t(xport_adapter_ctrl::KV_IPV4_W_ARP)], 0xc0a81e01);
+}
+
+