aboutsummaryrefslogtreecommitdiffstats
path: root/host
diff options
context:
space:
mode:
authorMartin Braun <martin.braun@ettus.com>2019-07-25 10:41:25 -0700
committerMartin Braun <martin.braun@ettus.com>2019-08-02 11:03:11 -0700
commit173531521970ed823b2f300180b8cacda94ec841 (patch)
treedf23a21876e4430ae2b232499af5626189b819a3 /host
parente2ce13e35d915c2d8b1300fd88745610c17638d1 (diff)
downloaduhd-173531521970ed823b2f300180b8cacda94ec841.tar.gz
uhd-173531521970ed823b2f300180b8cacda94ec841.tar.bz2
uhd-173531521970ed823b2f300180b8cacda94ec841.zip
x300: Refactor heavily
This pulls out a lot of code from x300_impl and puts it into its own compilation units: - EEPROM code goes to x300_mb_eeprom.* - Claim code goes to x300_claim.* - PCIe code goes to uhd::usrp::x300::pcie_manager - Ethernet code goes to uhd::usrp::x300::eth_manager
Diffstat (limited to 'host')
-rw-r--r--host/lib/usrp/x300/CMakeLists.txt4
-rw-r--r--host/lib/usrp/x300/x300_claim.cpp89
-rw-r--r--host/lib/usrp/x300/x300_claim.hpp25
-rw-r--r--host/lib/usrp/x300/x300_defaults.hpp56
-rw-r--r--host/lib/usrp/x300/x300_device_args.hpp40
-rw-r--r--host/lib/usrp/x300/x300_eth_mgr.cpp724
-rw-r--r--host/lib/usrp/x300/x300_eth_mgr.hpp126
-rw-r--r--host/lib/usrp/x300/x300_impl.cpp1218
-rw-r--r--host/lib/usrp/x300/x300_impl.hpp147
-rw-r--r--host/lib/usrp/x300/x300_mb_eeprom.cpp9
-rw-r--r--host/lib/usrp/x300/x300_mb_eeprom.hpp24
-rw-r--r--host/lib/usrp/x300/x300_mb_eeprom_iface.cpp23
-rw-r--r--host/lib/usrp/x300/x300_mboard_type.hpp2
-rw-r--r--host/lib/usrp/x300/x300_pcie_mgr.cpp390
-rw-r--r--host/lib/usrp/x300/x300_pcie_mgr.hpp82
15 files changed, 1607 insertions, 1352 deletions
diff --git a/host/lib/usrp/x300/CMakeLists.txt b/host/lib/usrp/x300/CMakeLists.txt
index 76a9e6b54..062db3f16 100644
--- a/host/lib/usrp/x300/CMakeLists.txt
+++ b/host/lib/usrp/x300/CMakeLists.txt
@@ -1,6 +1,7 @@
#
# Copyright 2013,2015 Ettus Research LLC
# Copyright 2018 Ettus Research, a National Instruments Company
+# Copyright 2019 Ettus Research, a National Instruments Brand
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
@@ -14,12 +15,14 @@
########################################################################
if(ENABLE_X300)
LIBUHD_APPEND_SOURCES(
+ ${CMAKE_CURRENT_SOURCE_DIR}/x300_claim.cpp
${CMAKE_CURRENT_SOURCE_DIR}/x300_radio_ctrl_impl.cpp
${CMAKE_CURRENT_SOURCE_DIR}/x300_impl.cpp
${CMAKE_CURRENT_SOURCE_DIR}/x300_fw_ctrl.cpp
${CMAKE_CURRENT_SOURCE_DIR}/x300_fw_uart.cpp
${CMAKE_CURRENT_SOURCE_DIR}/x300_adc_ctrl.cpp
${CMAKE_CURRENT_SOURCE_DIR}/x300_dac_ctrl.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/x300_eth_mgr.cpp
${CMAKE_CURRENT_SOURCE_DIR}/x300_io_impl.cpp
${CMAKE_CURRENT_SOURCE_DIR}/x300_dboard_iface.cpp
${CMAKE_CURRENT_SOURCE_DIR}/x300_clock_ctrl.cpp
@@ -28,6 +31,7 @@ if(ENABLE_X300)
${CMAKE_CURRENT_SOURCE_DIR}/x300_mb_eeprom.cpp
${CMAKE_CURRENT_SOURCE_DIR}/x300_mboard_type.hpp
${CMAKE_CURRENT_SOURCE_DIR}/x300_mboard_type.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/x300_pcie_mgr.cpp
${CMAKE_CURRENT_SOURCE_DIR}/cdecode.c
)
endif(ENABLE_X300)
diff --git a/host/lib/usrp/x300/x300_claim.cpp b/host/lib/usrp/x300/x300_claim.cpp
new file mode 100644
index 000000000..8a8de037d
--- /dev/null
+++ b/host/lib/usrp/x300/x300_claim.cpp
@@ -0,0 +1,89 @@
+//
+// Copyright 2019 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#include "x300_claim.hpp"
+#include "x300_fw_common.h"
+#include <uhd/utils/platform.hpp>
+#include <chrono>
+#include <thread>
+
+using namespace uhd;
+using namespace uhd::usrp::x300;
+
+/***********************************************************************
+ * claimer logic
+ **********************************************************************/
+
+void uhd::usrp::x300::claimer_loop(wb_iface::sptr iface)
+{
+ claim(iface);
+ std::this_thread::sleep_for(std::chrono::seconds(1));
+}
+
+claim_status_t uhd::usrp::x300::claim_status(wb_iface::sptr iface)
+{
+ claim_status_t claim_status = CLAIMED_BY_OTHER; // Default to most restrictive
+ auto timeout_time = std::chrono::steady_clock::now() + std::chrono::seconds(1);
+ while (std::chrono::steady_clock::now() < timeout_time) {
+ // If timed out, then device is definitely unclaimed
+ if (iface->peek32(X300_FW_SHMEM_ADDR(X300_FW_SHMEM_CLAIM_STATUS)) == 0) {
+ claim_status = UNCLAIMED;
+ break;
+ }
+
+ // otherwise check claim src to determine if another thread with the same src has
+ // claimed the device
+ uint32_t hash = iface->peek32(X300_FW_SHMEM_ADDR(X300_FW_SHMEM_CLAIM_SRC));
+ if (hash == 0) {
+ // A non-zero claim status and an empty hash means the claim might
+ // be in the process of being released. This is possible because
+ // older firmware takes a long time to update the status. Wait and
+ // check status again.
+ std::this_thread::sleep_for(std::chrono::milliseconds(5));
+ continue;
+ }
+ claim_status = (hash == get_process_hash() ? CLAIMED_BY_US : CLAIMED_BY_OTHER);
+ break;
+ }
+ return claim_status;
+}
+
+void uhd::usrp::x300::claim(wb_iface::sptr iface)
+{
+ iface->poke32(X300_FW_SHMEM_ADDR(X300_FW_SHMEM_CLAIM_TIME), uint32_t(time(NULL)));
+ iface->poke32(X300_FW_SHMEM_ADDR(X300_FW_SHMEM_CLAIM_SRC), get_process_hash());
+}
+
+bool uhd::usrp::x300::try_to_claim(wb_iface::sptr iface, long timeout_ms)
+{
+ const auto timeout_time =
+ std::chrono::steady_clock::now() + std::chrono::milliseconds(timeout_ms);
+ while (1) {
+ claim_status_t status = claim_status(iface);
+ if (status == UNCLAIMED) {
+ claim(iface);
+ // It takes the claimer 10ms to update status, so wait 20ms before verifying
+ // claim
+ std::this_thread::sleep_for(std::chrono::milliseconds(20));
+ continue;
+ }
+ if (status == CLAIMED_BY_US) {
+ break;
+ }
+ if (std::chrono::steady_clock::now() > timeout_time) {
+ // Another process owns the device - give up
+ return false;
+ }
+ std::this_thread::sleep_for(std::chrono::milliseconds(100));
+ }
+ return true;
+}
+
+void uhd::usrp::x300::release(wb_iface::sptr iface)
+{
+ iface->poke32(X300_FW_SHMEM_ADDR(X300_FW_SHMEM_CLAIM_TIME), 0);
+ iface->poke32(X300_FW_SHMEM_ADDR(X300_FW_SHMEM_CLAIM_SRC), 0);
+}
diff --git a/host/lib/usrp/x300/x300_claim.hpp b/host/lib/usrp/x300/x300_claim.hpp
new file mode 100644
index 000000000..bd222f55e
--- /dev/null
+++ b/host/lib/usrp/x300/x300_claim.hpp
@@ -0,0 +1,25 @@
+//
+// Copyright 2019 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#ifndef INCLUDED_X300_CLAIM_HPP
+#define INCLUDED_X300_CLAIM_HPP
+
+#include <uhd/types/wb_iface.hpp>
+
+namespace uhd { namespace usrp { namespace x300 {
+
+// device claim functions
+enum claim_status_t { UNCLAIMED, CLAIMED_BY_US, CLAIMED_BY_OTHER };
+
+claim_status_t claim_status(uhd::wb_iface::sptr iface);
+void claimer_loop(uhd::wb_iface::sptr iface);
+void claim(uhd::wb_iface::sptr iface);
+bool try_to_claim(uhd::wb_iface::sptr iface, long timeout = 2000);
+void release(uhd::wb_iface::sptr iface);
+
+}}} // namespace uhd::usrp::x300
+
+#endif /* INCLUDED_X300_CLAIM_HPP */
diff --git a/host/lib/usrp/x300/x300_defaults.hpp b/host/lib/usrp/x300/x300_defaults.hpp
index c5e9f6004..752298aff 100644
--- a/host/lib/usrp/x300/x300_defaults.hpp
+++ b/host/lib/usrp/x300/x300_defaults.hpp
@@ -8,15 +8,13 @@
#define INCLUDED_X300_DEFAULTS_HPP
#include "../device3/device3_impl.hpp"
-#include <uhd/transport/udp_simple.hpp> //mtu
#include <string>
-
+#include <vector>
namespace uhd { namespace usrp { namespace x300 {
static constexpr size_t NIUSRPRIO_DEFAULT_RPC_PORT = 5444;
-static constexpr uint32_t RADIO_DEST_PREFIX_TX = 0;
static constexpr size_t XB_DST_E0 = 0;
static constexpr size_t XB_DST_E1 = 1;
static constexpr size_t XB_DST_PCI = 2;
@@ -32,6 +30,7 @@ static constexpr double DEFAULT_TICK_RATE = 200e6; // Hz
static constexpr double MAX_TICK_RATE = 200e6; // Hz
static constexpr double MIN_TICK_RATE = 184.32e6; // Hz
static constexpr double BUS_CLOCK_RATE = 187.5e6; // Hz
+static constexpr double DEFAULT_SYSREF_RATE = 10e6;
static const std::string FW_FILE_NAME = "usrp_x300_fw.bin";
@@ -47,61 +46,14 @@ static const std::vector<std::string> TIME_SOURCE_OPTIONS{
static const std::vector<double> EXTERNAL_FREQ_OPTIONS{
10e6, 11.52e6, 23.04e6, 30.72e6};
-static constexpr size_t RX_SW_BUFF_SIZE_ETH =
- 0x2000000; // 32MiB For an ~8k frame size any size >32MiB is just wasted buffer
- // space
-static constexpr size_t RX_SW_BUFF_SIZE_ETH_MACOS = 0x100000; // 1Mib
-
-// The FIFO closest to the DMA controller is 1023 elements deep for RX and 1029 elements
-// deep for TX where an element is 8 bytes. The buffers (number of frames * frame size)
-// must be aligned to the memory page size. For the control, we are getting lucky because
-// 64 frames * 256 bytes each aligns with the typical page size of 4096 bytes. Since most
-// page sizes are 4096 bytes or some multiple of that, keep the number of frames * frame
-// size aligned to it.
-static constexpr size_t PCIE_RX_DATA_FRAME_SIZE = 4096; // bytes
-static constexpr size_t PCIE_RX_DATA_NUM_FRAMES = 4096;
-static constexpr size_t PCIE_TX_DATA_FRAME_SIZE = 4096; // bytes
-static constexpr size_t PCIE_TX_DATA_NUM_FRAMES = 4096;
-static constexpr size_t PCIE_MSG_FRAME_SIZE = 256; // bytes
-static constexpr size_t PCIE_MSG_NUM_FRAMES = 64;
-static constexpr size_t PCIE_MAX_CHANNELS = 6;
-static constexpr size_t PCIE_MAX_MUXED_CTRL_XPORTS = 32;
-static constexpr size_t PCIE_MAX_MUXED_ASYNC_XPORTS = 4;
+// Limit the number of initialization threads
+static const size_t MAX_INIT_THREADS = 10;
static const size_t DATA_FRAME_MAX_SIZE = 8000; // CHDR packet size in bytes
-static const size_t XGE_DATA_FRAME_SEND_SIZE =
- 4000; // Reduced to make sure flow control packets are not blocked for too long at
- // high rates
-static const size_t XGE_DATA_FRAME_RECV_SIZE = 8000;
-static const size_t GE_DATA_FRAME_SEND_SIZE = 1472;
-static const size_t GE_DATA_FRAME_RECV_SIZE = 1472;
-
-static const size_t ETH_MSG_FRAME_SIZE = uhd::transport::udp_simple::mtu; // bytes
-// MTU throttling for ethernet/TX (see above):
-static constexpr size_t ETH_DATA_FRAME_MAX_TX_SIZE = 8000;
static constexpr double RECV_OFFLOAD_BUFFER_TIMEOUT = 0.1; // seconds
static constexpr double THREAD_BUFFER_TIMEOUT = 0.1; // Time in seconds
-static constexpr size_t ETH_MSG_NUM_FRAMES = 64;
-static constexpr size_t ETH_DATA_NUM_FRAMES = 32;
-static constexpr double DEFAULT_SYSREF_RATE = 10e6;
-
-// Limit the number of initialization threads
-static const size_t MAX_INIT_THREADS = 10;
-
-static const size_t MAX_RATE_PCIE = 800000000; // bytes/s
-static const size_t MAX_RATE_10GIGE = (size_t)( // bytes/s
- 10e9 / 8 * // wire speed multiplied by percentage of packets that is sample data
- (float(DATA_FRAME_MAX_SIZE - uhd::usrp::DEVICE3_TX_MAX_HDR_LEN)
- / float(DATA_FRAME_MAX_SIZE
- + 8 /* UDP header */ + 20 /* Ethernet header length */)));
-static const size_t MAX_RATE_1GIGE = (size_t)( // bytes/s
- 10e9 / 8 * // wire speed multiplied by percentage of packets that is sample data
- (float(GE_DATA_FRAME_RECV_SIZE - uhd::usrp::DEVICE3_TX_MAX_HDR_LEN)
- / float(GE_DATA_FRAME_RECV_SIZE
- + 8 /* UDP header */ + 20 /* Ethernet header length */)));
-
static constexpr double DEFAULT_EXT_ADC_SELF_TEST_DURATION = 30.0;
}}} /* namespace uhd::usrp::x300 */
diff --git a/host/lib/usrp/x300/x300_device_args.hpp b/host/lib/usrp/x300/x300_device_args.hpp
index 1b153184c..9e9e328a9 100644
--- a/host/lib/usrp/x300/x300_device_args.hpp
+++ b/host/lib/usrp/x300/x300_device_args.hpp
@@ -8,7 +8,7 @@
#define INCLUDED_X300_DEV_ARGS_HPP
#include "x300_defaults.hpp"
-#include "x300_impl.hpp"
+#include <uhd/utils/log.hpp>
#include <uhdlib/usrp/constrained_device_args.hpp>
namespace uhd { namespace usrp { namespace x300 {
@@ -37,6 +37,10 @@ public:
, _blank_eeprom("blank_eeprom", false)
, _enable_tx_dual_eth("enable_tx_dual_eth", false)
, _use_dpdk("use_dpdk", false)
+ , _fpga_option("fpga", "")
+ , _download_fpga("download-fpga", false)
+ , _recv_frame_size("recv_frame_size", DATA_FRAME_MAX_SIZE)
+ , _send_frame_size("send_frame_size", DATA_FRAME_MAX_SIZE)
{
// nop
}
@@ -121,6 +125,26 @@ public:
{
return _use_dpdk.get();
}
+ std::string get_fpga_option() const
+ {
+ return _fpga_option.get();
+ }
+ bool get_download_fpga() const
+ {
+ return _download_fpga.get();
+ }
+ size_t get_recv_frame_size() const
+ {
+ return _recv_frame_size.get();
+ }
+ size_t get_send_frame_size() const
+ {
+ return _send_frame_size.get();
+ }
+ device_addr_t get_orig_args() const
+ {
+ return _orig_args;
+ }
inline virtual std::string to_string() const
{
@@ -156,12 +180,14 @@ public:
+ (_has_fw_file.get() ? _fw_file.to_string() + ", " : "")
+ (_enable_tx_dual_eth.get() ? (_enable_tx_dual_eth.to_string() + ", ")
: "")
- ;
+ + (_fpga_option.get().empty() ? "" : _fpga_option.to_string() + ", ")
+ + (_download_fpga.get() ? _download_fpga.to_string() + ", " : "");
}
private:
virtual void _parse(const device_addr_t& dev_args)
{
+ _orig_args = dev_args;
// Extract parameters from dev_args
#define PARSE_DEFAULT(arg) parse_arg_default(dev_args, arg);
PARSE_DEFAULT(_master_clock_rate)
@@ -188,6 +214,8 @@ private:
PARSE_DEFAULT(_first_addr)
PARSE_DEFAULT(_second_addr)
PARSE_DEFAULT(_resource)
+ PARSE_DEFAULT(_fpga_option)
+ PARSE_DEFAULT(_download_fpga)
if (_first_addr.get().empty() && !_second_addr.get().empty()) {
UHD_LOG_WARNING("X300",
"Specifying `second_addr' without `addr'is inconsistent and has "
@@ -233,6 +261,8 @@ private:
"Detected use_dpdk argument, but DPDK support not built in.");
#endif
}
+ PARSE_DEFAULT(_recv_frame_size)
+ PARSE_DEFAULT(_send_frame_size)
// Sanity check params
_enforce_range(_master_clock_rate, MIN_TICK_RATE, MAX_TICK_RATE);
@@ -261,6 +291,12 @@ private:
constrained_device_args_t::bool_arg _blank_eeprom;
constrained_device_args_t::bool_arg _enable_tx_dual_eth;
constrained_device_args_t::bool_arg _use_dpdk;
+ constrained_device_args_t::str_arg<true> _fpga_option;
+ constrained_device_args_t::bool_arg _download_fpga;
+ constrained_device_args_t::num_arg<size_t> _recv_frame_size;
+ constrained_device_args_t::num_arg<size_t> _send_frame_size;
+
+ device_addr_t _orig_args;
};
}}} // namespace uhd::usrp::x300
diff --git a/host/lib/usrp/x300/x300_eth_mgr.cpp b/host/lib/usrp/x300/x300_eth_mgr.cpp
new file mode 100644
index 000000000..adaf101d4
--- /dev/null
+++ b/host/lib/usrp/x300/x300_eth_mgr.cpp
@@ -0,0 +1,724 @@
+//
+// Copyright 2019 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#include "x300_eth_mgr.hpp"
+#include "x300_claim.hpp"
+#include "x300_fw_common.h"
+#include "x300_mb_eeprom.hpp"
+#include "x300_mb_eeprom_iface.hpp"
+#include "x300_regs.hpp"
+#include <uhd/exception.hpp>
+#include <uhd/transport/if_addrs.hpp>
+#include <uhd/transport/udp_constants.hpp>
+#include <uhd/transport/udp_simple.hpp>
+#include <uhd/transport/udp_zero_copy.hpp>
+#include <uhd/transport/zero_copy_recv_offload.hpp>
+#ifdef HAVE_DPDK
+# include <uhdlib/transport/dpdk_simple.hpp>
+# include <uhdlib/transport/dpdk_zero_copy.hpp>
+#endif
+#include <uhdlib/usrp/cores/i2c_core_100_wb32.hpp>
+#include <boost/asio.hpp>
+#include <string>
+
+uhd::wb_iface::sptr x300_make_ctrl_iface_enet(
+ uhd::transport::udp_simple::sptr udp, bool enable_errors = true);
+
+using namespace uhd;
+using namespace uhd::usrp;
+using namespace uhd::transport;
+using namespace uhd::usrp::x300;
+namespace asio = boost::asio;
+
+namespace {
+
+constexpr unsigned int X300_UDP_RESERVED_FRAME_SIZE = 64;
+// Reduced to make sure flow control packets are not blocked for too long at
+// high rates:
+constexpr size_t XGE_DATA_FRAME_SEND_SIZE = 4000;
+constexpr size_t XGE_DATA_FRAME_RECV_SIZE = 8000;
+constexpr size_t GE_DATA_FRAME_SEND_SIZE = 1472;
+constexpr size_t GE_DATA_FRAME_RECV_SIZE = 1472;
+constexpr size_t ETH_MSG_NUM_FRAMES = 64;
+constexpr size_t ETH_DATA_NUM_FRAMES = 32;
+constexpr size_t ETH_MSG_FRAME_SIZE = uhd::transport::udp_simple::mtu; // bytes
+constexpr size_t MAX_RATE_10GIGE = (size_t)( // bytes/s
+ 10e9 / 8 * // wire speed multiplied by percentage of packets that is sample data
+ (float(x300::DATA_FRAME_MAX_SIZE - uhd::usrp::DEVICE3_TX_MAX_HDR_LEN)
+ / float(x300::DATA_FRAME_MAX_SIZE
+ + 8 /* UDP header */ + 20 /* Ethernet header length */)));
+constexpr size_t MAX_RATE_1GIGE = (size_t)( // bytes/s
+ 10e9 / 8 * // wire speed multiplied by percentage of packets that is sample data
+ (float(GE_DATA_FRAME_RECV_SIZE - uhd::usrp::DEVICE3_TX_MAX_HDR_LEN)
+ / float(GE_DATA_FRAME_RECV_SIZE
+ + 8 /* UDP header */ + 20 /* Ethernet header length */)));
+
+
+} // namespace
+
+/******************************************************************************
+ * Static Methods
+ *****************************************************************************/
+eth_manager::udp_simple_factory_t eth_manager::x300_get_udp_factory(
+ const device_addr_t& args)
+{
+ udp_simple_factory_t udp_make_connected = udp_simple::make_connected;
+ if (args.has_key("use_dpdk")) {
+#ifdef HAVE_DPDK
+ udp_make_connected = [](const std::string& addr, const std::string& port) {
+ auto& ctx = uhd::transport::uhd_dpdk_ctx::get();
+ return dpdk_simple::make_connected(ctx, addr, port);
+ };
+#else
+ UHD_LOG_WARNING(
+ "DPDK", "Detected use_dpdk argument, but DPDK support not built in.");
+#endif
+ }
+ return udp_make_connected;
+}
+
+device_addrs_t eth_manager::find(const device_addr_t& hint)
+{
+ udp_simple_factory_t udp_make_broadcast = udp_simple::make_broadcast;
+ udp_simple_factory_t udp_make_connected = x300_get_udp_factory(hint);
+#ifdef HAVE_DPDK
+ if (hint.has_key("use_dpdk")) {
+ auto& dpdk_ctx = uhd::transport::uhd_dpdk_ctx::get();
+ if (not dpdk_ctx.is_init_done()) {
+ dpdk_ctx.init(hint);
+ }
+ udp_make_broadcast = [](const std::string& addr, const std::string& port) {
+ auto& ctx = uhd::transport::uhd_dpdk_ctx::get();
+ return dpdk_simple::make_broadcast(ctx, addr, port);
+ };
+ }
+#endif
+ udp_simple::sptr comm =
+ udp_make_broadcast(hint["addr"], BOOST_STRINGIZE(X300_FW_COMMS_UDP_PORT));
+
+ // load request struct
+ x300_fw_comms_t request = x300_fw_comms_t();
+ request.flags = uhd::htonx<uint32_t>(X300_FW_COMMS_FLAGS_ACK);
+ request.sequence = uhd::htonx<uint32_t>(std::rand());
+
+ // send request
+ comm->send(asio::buffer(&request, sizeof(request)));
+
+ // loop for replies until timeout
+ device_addrs_t addrs;
+ while (true) {
+ char buff[X300_FW_COMMS_MTU] = {};
+ const size_t nbytes = comm->recv(asio::buffer(buff), 0.050);
+ if (nbytes == 0)
+ break;
+ const x300_fw_comms_t* reply = (const x300_fw_comms_t*)buff;
+ if (request.flags != reply->flags)
+ continue;
+ if (request.sequence != reply->sequence)
+ continue;
+ device_addr_t new_addr;
+ new_addr["type"] = "x300";
+ new_addr["addr"] = comm->get_recv_addr();
+
+ // Attempt to read the name from the EEPROM and perform filtering.
+ // This operation can throw due to compatibility mismatch.
+ try {
+ wb_iface::sptr zpu_ctrl = x300_make_ctrl_iface_enet(
+ udp_make_connected(
+ new_addr["addr"], BOOST_STRINGIZE(X300_FW_COMMS_UDP_PORT)),
+ false /* Suppress timeout errors */
+ );
+
+ new_addr["fpga"] = get_fpga_option(zpu_ctrl);
+
+ i2c_core_100_wb32::sptr zpu_i2c =
+ i2c_core_100_wb32::make(zpu_ctrl, I2C1_BASE);
+ x300_mb_eeprom_iface::sptr eeprom_iface =
+ x300_mb_eeprom_iface::make(zpu_ctrl, zpu_i2c);
+ const mboard_eeprom_t mb_eeprom = get_mb_eeprom(eeprom_iface);
+ if (mb_eeprom.size() == 0 or claim_status(zpu_ctrl) == CLAIMED_BY_OTHER) {
+ // Skip device claimed by another process
+ continue;
+ }
+ new_addr["name"] = mb_eeprom["name"];
+ new_addr["serial"] = mb_eeprom["serial"];
+ const std::string product_name =
+ map_mb_type_to_product_name(get_mb_type_from_eeprom(mb_eeprom));
+ if (!product_name.empty()) {
+ new_addr["product"] = product_name;
+ }
+ } catch (const std::exception&) {
+ // set these values as empty string so the device may still be found
+ // and the filter's below can still operate on the discovered device
+ new_addr["name"] = "";
+ new_addr["serial"] = "";
+ }
+ // filter the discovered device below by matching optional keys
+ if ((not hint.has_key("name") or hint["name"] == new_addr["name"])
+ and (not hint.has_key("serial") or hint["serial"] == new_addr["serial"])
+ and (not hint.has_key("product") or hint["product"] == new_addr["product"])) {
+ addrs.push_back(new_addr);
+ }
+ }
+
+ return addrs;
+}
+
+/******************************************************************************
+ * Structors
+ *****************************************************************************/
+eth_manager::eth_manager(const x300_device_args_t& args,
+ uhd::property_tree::sptr tree,
+ const uhd::fs_path& root_path)
+ : _args(args)
+{
+ UHD_ASSERT_THROW(!args.get_first_addr().empty());
+
+ auto dev_addr = args.get_orig_args();
+ for (const std::string& key : dev_addr.keys()) {
+ if (key.find("recv") != std::string::npos)
+ recv_args[key] = dev_addr[key];
+ if (key.find("send") != std::string::npos)
+ send_args[key] = dev_addr[key];
+ }
+
+ // Initially store only the first address provided to setup communication
+ // Once we read the EEPROM, we use it to map IP to its interface
+ // In discover_eth(), we'll check and enable the other IP address, if given
+ x300_eth_conn_t init;
+ init.addr = args.get_first_addr();
+ eth_conns.push_back(init);
+
+ _x300_make_udp_connected = x300_get_udp_factory(dev_addr);
+
+ tree->create<double>(root_path / "link_max_rate").set(10e9);
+ _tree = tree->subtree(root_path);
+}
+
+both_xports_t eth_manager::make_transport(both_xports_t xports,
+ const uhd::usrp::device3_impl::xport_type_t xport_type,
+ const uhd::device_addr_t& args,
+ const size_t send_mtu,
+ const size_t recv_mtu,
+ std::function<uhd::sid_t(uint32_t, uint32_t)>&& allocate_sid)
+{
+ zero_copy_xport_params default_buff_args;
+ xports.endianness = ENDIANNESS_BIG;
+ xports.lossless = false;
+ xports.recv = nullptr;
+
+ size_t& next_src_addr = xport_type == uhd::usrp::device3_impl::TX_DATA
+ ? _next_tx_src_addr
+ : xport_type == uhd::usrp::device3_impl::RX_DATA
+ ? _next_rx_src_addr
+ : _next_src_addr;
+
+ // Decide on the IP/Interface pair based on the endpoint index
+ x300_eth_conn_t conn = eth_conns[next_src_addr];
+ const uint32_t xbar_src_addr = next_src_addr == 0 ? x300::SRC_ADDR0 : x300::SRC_ADDR1;
+ const uint32_t xbar_src_dst = conn.type == X300_IFACE_ETH0 ? x300::XB_DST_E0
+ : x300::XB_DST_E1;
+
+ // Do not increment src addr for tx_data by default, using dual ethernet
+ // with the DMA FIFO causes sequence errors to DMA FIFO bandwidth
+ // limitations.
+ if (xport_type != uhd::usrp::device3_impl::TX_DATA
+ || _args.get_enable_tx_dual_eth()) {
+ next_src_addr = (next_src_addr + 1) % eth_conns.size();
+ }
+
+ xports.send_sid = allocate_sid(xbar_src_addr, xbar_src_dst);
+ xports.recv_sid = xports.send_sid.reversed();
+ // Set size and number of frames
+ default_buff_args.send_frame_size = std::min(send_mtu, ETH_MSG_FRAME_SIZE);
+ default_buff_args.recv_frame_size = std::min(recv_mtu, ETH_MSG_FRAME_SIZE);
+
+ if (_args.get_use_dpdk()) {
+#ifdef HAVE_DPDK
+ auto& dpdk_ctx = uhd::transport::uhd_dpdk_ctx::get();
+
+ default_buff_args.num_recv_frames = ETH_MSG_NUM_FRAMES;
+ default_buff_args.num_send_frames = ETH_MSG_NUM_FRAMES;
+ if (xport_type == uhd::usrp::device3_impl::CTRL) {
+ // Increasing number of recv frames here because ctrl_iface uses it
+ // to determine how many control packets can be in flight before it
+ // must wait for an ACK
+ default_buff_args.num_recv_frames =
+ uhd::rfnoc::CMD_FIFO_SIZE / uhd::rfnoc::MAX_CMD_PKT_SIZE;
+ } else if (xport_type == uhd::usrp::device3_impl::TX_DATA) {
+ size_t default_frame_size = conn.link_rate == MAX_RATE_1GIGE
+ ? GE_DATA_FRAME_SEND_SIZE
+ : XGE_DATA_FRAME_SEND_SIZE;
+ default_buff_args.send_frame_size = args.cast<size_t>(
+ "send_frame_size", std::min(default_frame_size, send_mtu));
+ default_buff_args.num_send_frames =
+ args.cast<size_t>("num_send_frames", default_buff_args.num_send_frames);
+ default_buff_args.send_buff_size = args.cast<size_t>("send_buff_size", 0);
+ } else if (xport_type == uhd::usrp::device3_impl::RX_DATA) {
+ size_t default_frame_size = conn.link_rate == MAX_RATE_1GIGE
+ ? GE_DATA_FRAME_RECV_SIZE
+ : XGE_DATA_FRAME_RECV_SIZE;
+ default_buff_args.recv_frame_size = args.cast<size_t>(
+ "recv_frame_size", std::min(default_frame_size, recv_mtu));
+ default_buff_args.num_recv_frames =
+ args.cast<size_t>("num_recv_frames", default_buff_args.num_recv_frames);
+ default_buff_args.recv_buff_size = args.cast<size_t>("recv_buff_size", 0);
+ }
+
+ int dpdk_port_id = dpdk_ctx.get_route(conn.addr);
+ if (dpdk_port_id < 0) {
+ throw uhd::runtime_error(
+ "Could not find a DPDK port with route to " + conn.addr);
+ }
+ auto recv = transport::dpdk_zero_copy::make(dpdk_ctx,
+ (const unsigned int)dpdk_port_id,
+ conn.addr,
+ BOOST_STRINGIZE(X300_VITA_UDP_PORT),
+ "0",
+ default_buff_args,
+ uhd::device_addr_t());
+
+ xports.recv = recv; // Note: This is a type cast!
+ xports.send = xports.recv;
+ xports.recv_buff_size =
+ (default_buff_args.recv_frame_size - X300_UDP_RESERVED_FRAME_SIZE)
+ * default_buff_args.num_recv_frames;
+ xports.send_buff_size =
+ (default_buff_args.send_frame_size - X300_UDP_RESERVED_FRAME_SIZE)
+ * default_buff_args.num_send_frames;
+ UHD_LOG_TRACE("BUFF",
+ "num_recv_frames="
+ << default_buff_args.num_recv_frames
+ << ", num_send_frames=" << default_buff_args.num_send_frames
+ << ", recv_frame_size=" << default_buff_args.recv_frame_size
+ << ", send_frame_size=" << default_buff_args.send_frame_size);
+
+#else
+ UHD_LOG_WARNING("X300", "Cannot create DPDK transport, falling back to UDP");
+#endif
+ }
+ if (!xports.recv) {
+ // Buffering is done in the socket buffers, so size them relative to
+ // the link rate
+ default_buff_args.send_buff_size = conn.link_rate / 50; // 20ms
+ default_buff_args.recv_buff_size = std::max(conn.link_rate / 50,
+ ETH_MSG_NUM_FRAMES * ETH_MSG_FRAME_SIZE); // enough to hold greater of 20ms or
+ // number of msg frames
+ // There is no need for more than 1 send and recv frame since the
+ // buffering is done in the socket buffers
+ default_buff_args.num_send_frames = 1;
+ default_buff_args.num_recv_frames = 1;
+ if (xport_type == uhd::usrp::device3_impl::CTRL) {
+ // Increasing number of recv frames here because ctrl_iface uses it
+ // to determine how many control packets can be in flight before it
+ // must wait for an ACK
+ default_buff_args.num_recv_frames =
+ uhd::rfnoc::CMD_FIFO_SIZE / uhd::rfnoc::MAX_CMD_PKT_SIZE;
+ } else if (xport_type == uhd::usrp::device3_impl::TX_DATA) {
+ size_t default_frame_size = conn.link_rate == MAX_RATE_1GIGE
+ ? GE_DATA_FRAME_SEND_SIZE
+ : XGE_DATA_FRAME_SEND_SIZE;
+ default_buff_args.send_frame_size = args.cast<size_t>(
+ "send_frame_size", std::min(default_frame_size, send_mtu));
+ default_buff_args.num_send_frames =
+ args.cast<size_t>("num_send_frames", default_buff_args.num_send_frames);
+ default_buff_args.send_buff_size =
+ args.cast<size_t>("send_buff_size", default_buff_args.send_buff_size);
+ } else if (xport_type == uhd::usrp::device3_impl::RX_DATA) {
+ size_t default_frame_size = conn.link_rate == MAX_RATE_1GIGE
+ ? GE_DATA_FRAME_RECV_SIZE
+ : XGE_DATA_FRAME_RECV_SIZE;
+ default_buff_args.recv_frame_size = args.cast<size_t>(
+ "recv_frame_size", std::min(default_frame_size, recv_mtu));
+ // set some buffers so the offload thread actually offloads the
+ // socket I/O
+ default_buff_args.num_recv_frames = args.cast<size_t>("num_recv_frames", 2);
+ default_buff_args.recv_buff_size =
+ args.cast<size_t>("recv_buff_size", default_buff_args.recv_buff_size);
+ }
+
+ // make a new transport - fpga has no idea how to talk to us on this yet
+ udp_zero_copy::buff_params buff_params;
+ xports.recv = udp_zero_copy::make(conn.addr,
+ BOOST_STRINGIZE(X300_VITA_UDP_PORT),
+ default_buff_args,
+ buff_params);
+
+ // Create a threaded transport for the receive chain only
+ if (xport_type == uhd::usrp::device3_impl::RX_DATA) {
+ xports.recv = zero_copy_recv_offload::make(
+ xports.recv, x300::RECV_OFFLOAD_BUFFER_TIMEOUT);
+ }
+
+ xports.send = xports.recv;
+
+ // For the UDP transport the buffer size is the size of the socket buffer
+ // in the kernel
+ xports.recv_buff_size = buff_params.recv_buff_size;
+ xports.send_buff_size = buff_params.send_buff_size;
+ }
+
+ // send a mini packet with SID into the ZPU
+ // ZPU will reprogram the ethernet framer
+ UHD_LOGGER_DEBUG("X300") << "programming packet for new xport on " << conn.addr
+ << " sid " << xports.send_sid;
+ // YES, get a __send__ buffer from the __recv__ socket
+ //-- this is the only way to program the framer for recv:
+ managed_send_buffer::sptr buff = xports.recv->get_send_buff();
+ buff->cast<uint32_t*>()[0] = 0; // eth dispatch looks for != 0
+ buff->cast<uint32_t*>()[1] = uhd::htonx(xports.send_sid.get());
+ buff->commit(8);
+ buff.reset();
+
+ return xports;
+}
+
+/******************************************************************************
+ * API
+ *****************************************************************************/
+wb_iface::sptr eth_manager::get_ctrl_iface()
+{
+ return x300_make_ctrl_iface_enet(_x300_make_udp_connected(
+ get_pri_eth().addr, BOOST_STRINGIZE(X300_FW_COMMS_UDP_PORT)));
+}
+
+void eth_manager::init_link(
+ const mboard_eeprom_t& mb_eeprom, const std::string& loaded_fpga_image)
+{
+ double link_max_rate = 0.0;
+
+ // Discover ethernet interfaces
+ discover_eth(mb_eeprom, loaded_fpga_image);
+
+ /* This is an ETH connection. Figure out what the maximum supported frame
+ * size is for the transport in the up and down directions. The frame size
+ * depends on the host PC's NIC's MTU settings. To determine the frame size,
+ * we test for support up to an expected "ceiling". If the user
+ * specified a frame size, we use that frame size as the ceiling. If no
+ * frame size was specified, we use the maximum UHD frame size.
+ *
+ * To optimize performance, the frame size should be greater than or equal
+ * to the frame size that UHD uses so that frames don't get split across
+ * multiple transmission units - this is why the limits passed into the
+ * 'determine_max_frame_size' function are actually frame sizes. */
+ frame_size_t req_max_frame_size;
+ req_max_frame_size.recv_frame_size =
+ (recv_args.has_key("recv_frame_size"))
+ ? boost::lexical_cast<size_t>(recv_args["recv_frame_size"])
+ : x300::DATA_FRAME_MAX_SIZE;
+ req_max_frame_size.send_frame_size =
+ (send_args.has_key("send_frame_size"))
+ ? boost::lexical_cast<size_t>(send_args["send_frame_size"])
+ : x300::DATA_FRAME_MAX_SIZE;
+
+#if defined UHD_PLATFORM_LINUX
+ const std::string mtu_tool("ip link");
+#elif defined UHD_PLATFORM_WIN32
+ const std::string mtu_tool("netsh");
+#else
+ const std::string mtu_tool("ifconfig");
+#endif
+
+ // Detect the frame size on the path to the USRP
+ try {
+ frame_size_t pri_frame_sizes =
+ determine_max_frame_size(get_pri_eth().addr, req_max_frame_size);
+
+ _max_frame_sizes = pri_frame_sizes;
+ if (eth_conns.size() > 1) {
+ frame_size_t sec_frame_sizes =
+ determine_max_frame_size(eth_conns.at(1).addr, req_max_frame_size);
+
+ // Choose the minimum of the max frame sizes
+ // to ensure we don't exceed any one of the links' MTU
+ _max_frame_sizes.recv_frame_size = std::min(
+ pri_frame_sizes.recv_frame_size, sec_frame_sizes.recv_frame_size);
+
+ _max_frame_sizes.send_frame_size = std::min(
+ pri_frame_sizes.send_frame_size, sec_frame_sizes.send_frame_size);
+ }
+ } catch (std::exception& e) {
+ UHD_LOGGER_ERROR("X300") << e.what();
+ }
+
+ if ((recv_args.has_key("recv_frame_size"))
+ && (req_max_frame_size.recv_frame_size > _max_frame_sizes.recv_frame_size)) {
+ UHD_LOGGER_WARNING("X300")
+ << boost::format("You requested a receive frame size of (%lu) but your "
+ "NIC's max frame size is (%lu).")
+ % req_max_frame_size.recv_frame_size % _max_frame_sizes.recv_frame_size
+ << boost::format("Please verify your NIC's MTU setting using '%s' or set "
+ "the recv_frame_size argument appropriately.")
+ % mtu_tool
+ << "UHD will use the auto-detected max frame size for this connection.";
+ }
+
+ if ((send_args.has_key("send_frame_size"))
+ && (req_max_frame_size.send_frame_size > _max_frame_sizes.send_frame_size)) {
+ UHD_LOGGER_WARNING("X300")
+ << boost::format("You requested a send frame size of (%lu) but your "
+ "NIC's max frame size is (%lu).")
+ % req_max_frame_size.send_frame_size % _max_frame_sizes.send_frame_size
+ << boost::format("Please verify your NIC's MTU setting using '%s' or set "
+ "the send_frame_size argument appropriately.")
+ % mtu_tool
+ << "UHD will use the auto-detected max frame size for this connection.";
+ }
+
+ // Check frame sizes
+ for (auto conn : eth_conns) {
+ link_max_rate += conn.link_rate;
+
+ size_t rec_send_frame_size = conn.link_rate == MAX_RATE_1GIGE
+ ? GE_DATA_FRAME_SEND_SIZE
+ : XGE_DATA_FRAME_SEND_SIZE;
+ size_t rec_recv_frame_size = conn.link_rate == MAX_RATE_1GIGE
+ ? GE_DATA_FRAME_RECV_SIZE
+ : XGE_DATA_FRAME_RECV_SIZE;
+
+ if (_max_frame_sizes.send_frame_size < rec_send_frame_size) {
+ UHD_LOGGER_WARNING("X300")
+ << boost::format("For the %s connection, UHD recommends a send frame "
+ "size of at least %lu for best\nperformance, but "
+ "your configuration will only allow %lu.")
+ % conn.addr % rec_send_frame_size
+ % _max_frame_sizes.send_frame_size
+ << "This may negatively impact your maximum achievable sample "
+ "rate.\nCheck the MTU on the interface and/or the send_frame_size "
+ "argument.";
+ }
+
+ if (_max_frame_sizes.recv_frame_size < rec_recv_frame_size) {
+ UHD_LOGGER_WARNING("X300")
+ << boost::format("For the %s connection, UHD recommends a receive "
+ "frame size of at least %lu for best\nperformance, "
+ "but your configuration will only allow %lu.")
+ % conn.addr % rec_recv_frame_size
+ % _max_frame_sizes.recv_frame_size
+ << "This may negatively impact your maximum achievable sample "
+ "rate.\nCheck the MTU on the interface and/or the recv_frame_size "
+ "argument.";
+ }
+ }
+
+ _tree->create<size_t>("mtu/recv").set(_max_frame_sizes.recv_frame_size);
+ _tree->create<size_t>("mtu/send").set(_max_frame_sizes.send_frame_size);
+ _tree->access<double>("link_max_rate").set(link_max_rate);
+}
+
+size_t eth_manager::get_mtu(uhd::direction_t dir)
+{
+ return dir == uhd::RX_DIRECTION ? _max_frame_sizes.recv_frame_size
+ : _max_frame_sizes.send_frame_size;
+}
+
+
+void eth_manager::discover_eth(
+ const mboard_eeprom_t mb_eeprom, const std::string& loaded_fpga_image)
+{
+ udp_simple_factory_t udp_make_connected = x300_get_udp_factory(send_args);
+ // Load all valid, non-duplicate IP addrs
+ std::vector<std::string> ip_addrs{_args.get_first_addr()};
+ if (not _args.get_second_addr().empty()
+ && (_args.get_first_addr() != _args.get_second_addr())) {
+ ip_addrs.push_back(_args.get_second_addr());
+ }
+
+ // Clear any previous addresses added
+ eth_conns.clear();
+
+ // Index the MB EEPROM addresses
+ std::vector<std::string> mb_eeprom_addrs;
+ const size_t num_mb_eeprom_addrs = 4;
+ for (size_t i = 0; i < num_mb_eeprom_addrs; i++) {
+ const std::string key = "ip-addr" + boost::to_string(i);
+
+ // Show a warning if there exists duplicate addresses in the mboard eeprom
+ if (std::find(mb_eeprom_addrs.begin(), mb_eeprom_addrs.end(), mb_eeprom[key])
+ != mb_eeprom_addrs.end()) {
+ UHD_LOGGER_WARNING("X300") << str(
+ boost::format(
+ "Duplicate IP address %s found in mboard EEPROM. "
+ "Device may not function properly. View and reprogram the values "
+ "using the usrp_burn_mb_eeprom utility.")
+ % mb_eeprom[key]);
+ }
+ mb_eeprom_addrs.push_back(mb_eeprom[key]);
+ }
+
+ for (const std::string& addr : ip_addrs) {
+ x300_eth_conn_t conn_iface;
+ conn_iface.addr = addr;
+ conn_iface.type = X300_IFACE_NONE;
+
+ // Decide from the mboard eeprom what IP corresponds
+ // to an interface
+ for (size_t i = 0; i < mb_eeprom_addrs.size(); i++) {
+ if (addr == mb_eeprom_addrs[i]) {
+ // Choose the interface based on the index parity
+ if (i % 2 == 0) {
+ conn_iface.type = X300_IFACE_ETH0;
+ conn_iface.link_rate = loaded_fpga_image == "HG" ? MAX_RATE_1GIGE
+ : MAX_RATE_10GIGE;
+ } else {
+ conn_iface.type = X300_IFACE_ETH1;
+ conn_iface.link_rate = MAX_RATE_10GIGE;
+ }
+ break;
+ }
+ }
+
+ // Check default IP addresses if we couldn't
+ // determine the IP from the mboard eeprom
+ if (conn_iface.type == X300_IFACE_NONE) {
+ UHD_LOGGER_WARNING("X300") << str(
+ boost::format(
+ "Address %s not found in mboard EEPROM. Address may be wrong or "
+ "the EEPROM may be corrupt. Attempting to continue with default "
+ "IP addresses.")
+ % conn_iface.addr);
+
+ if (addr
+ == boost::asio::ip::address_v4(uint32_t(X300_DEFAULT_IP_ETH0_1G))
+ .to_string()) {
+ conn_iface.type = X300_IFACE_ETH0;
+ conn_iface.link_rate = MAX_RATE_1GIGE;
+ } else if (addr
+ == boost::asio::ip::address_v4(uint32_t(X300_DEFAULT_IP_ETH1_1G))
+ .to_string()) {
+ conn_iface.type = X300_IFACE_ETH1;
+ conn_iface.link_rate = MAX_RATE_1GIGE;
+ } else if (addr
+ == boost::asio::ip::address_v4(uint32_t(X300_DEFAULT_IP_ETH0_10G))
+ .to_string()) {
+ conn_iface.type = X300_IFACE_ETH0;
+ conn_iface.link_rate = MAX_RATE_10GIGE;
+ } else if (addr
+ == boost::asio::ip::address_v4(uint32_t(X300_DEFAULT_IP_ETH1_10G))
+ .to_string()) {
+ conn_iface.type = X300_IFACE_ETH1;
+ conn_iface.link_rate = MAX_RATE_10GIGE;
+ } else {
+ throw uhd::assertion_error(
+ str(boost::format(
+ "X300 Initialization Error: Failed to match address %s with "
+ "any addresses for the device. Please check the address.")
+ % conn_iface.addr));
+ }
+ }
+
+ // Save to a vector of connections
+ if (conn_iface.type != X300_IFACE_NONE) {
+ // Check the address before we add it
+ try {
+ wb_iface::sptr zpu_ctrl = x300_make_ctrl_iface_enet(
+ udp_make_connected(
+ conn_iface.addr, BOOST_STRINGIZE(X300_FW_COMMS_UDP_PORT)),
+ false /* Suppress timeout errors */
+ );
+
+ // Peek the ZPU ctrl to make sure this connection works
+ zpu_ctrl->peek32(0);
+ }
+
+ // If the address does not work, throw an error
+ catch (std::exception&) {
+ throw uhd::io_error(
+ str(boost::format("X300 Initialization Error: Invalid address %s")
+ % conn_iface.addr));
+ }
+ eth_conns.push_back(conn_iface);
+ }
+ }
+
+ if (eth_conns.size() == 0)
+ throw uhd::assertion_error(
+ "X300 Initialization Error: No ethernet interfaces specified.");
+}
+
+eth_manager::frame_size_t eth_manager::determine_max_frame_size(
+ const std::string& addr, const frame_size_t& user_frame_size)
+{
+ auto udp = _x300_make_udp_connected(addr, BOOST_STRINGIZE(X300_MTU_DETECT_UDP_PORT));
+
+ std::vector<uint8_t> buffer(
+ std::max(user_frame_size.recv_frame_size, user_frame_size.send_frame_size));
+ x300_mtu_t* request = reinterpret_cast<x300_mtu_t*>(&buffer.front());
+ constexpr double echo_timeout = 0.020; // 20 ms
+
+ // test holler - check if its supported in this fw version
+ request->flags = uhd::htonx<uint32_t>(X300_MTU_DETECT_ECHO_REQUEST);
+ request->size = uhd::htonx<uint32_t>(sizeof(x300_mtu_t));
+ udp->send(boost::asio::buffer(buffer, sizeof(x300_mtu_t)));
+ udp->recv(boost::asio::buffer(buffer), echo_timeout);
+ if (!(uhd::ntohx<uint32_t>(request->flags) & X300_MTU_DETECT_ECHO_REPLY)) {
+ throw uhd::not_implemented_error("Holler protocol not implemented");
+ }
+
+ // Reducing range of (min,max) by setting max value to 10gig max_frame_size as larger
+ // sizes are not supported
+ size_t min_recv_frame_size = sizeof(x300_mtu_t);
+ size_t max_recv_frame_size =
+ std::min(user_frame_size.recv_frame_size, x300::DATA_FRAME_MAX_SIZE) & size_t(~3);
+ size_t min_send_frame_size = sizeof(x300_mtu_t);
+ size_t max_send_frame_size =
+ std::min(user_frame_size.send_frame_size, x300::DATA_FRAME_MAX_SIZE) & size_t(~3);
+
+ UHD_LOGGER_DEBUG("X300") << "Determining maximum frame size... ";
+ while (min_recv_frame_size < max_recv_frame_size) {
+ size_t test_frame_size = (max_recv_frame_size / 2 + min_recv_frame_size / 2 + 3)
+ & ~3;
+
+ request->flags = uhd::htonx<uint32_t>(X300_MTU_DETECT_ECHO_REQUEST);
+ request->size = uhd::htonx<uint32_t>(test_frame_size);
+ udp->send(boost::asio::buffer(buffer, sizeof(x300_mtu_t)));
+
+ size_t len = udp->recv(boost::asio::buffer(buffer), echo_timeout);
+
+ if (len >= test_frame_size)
+ min_recv_frame_size = test_frame_size;
+ else
+ max_recv_frame_size = test_frame_size - 4;
+ }
+
+ if (min_recv_frame_size < IP_PROTOCOL_MIN_MTU_SIZE - IP_PROTOCOL_UDP_PLUS_IP_HEADER) {
+ throw uhd::runtime_error("System receive MTU size is less than the minimum "
+ "required by the IP protocol.");
+ }
+
+ while (min_send_frame_size < max_send_frame_size) {
+ size_t test_frame_size = (max_send_frame_size / 2 + min_send_frame_size / 2 + 3)
+ & ~3;
+
+ request->flags = uhd::htonx<uint32_t>(X300_MTU_DETECT_ECHO_REQUEST);
+ request->size = uhd::htonx<uint32_t>(sizeof(x300_mtu_t));
+ udp->send(boost::asio::buffer(buffer, test_frame_size));
+
+ size_t len = udp->recv(boost::asio::buffer(buffer), echo_timeout);
+ if (len >= sizeof(x300_mtu_t))
+ len = uhd::ntohx<uint32_t>(request->size);
+
+ if (len >= test_frame_size)
+ min_send_frame_size = test_frame_size;
+ else
+ max_send_frame_size = test_frame_size - 4;
+ }
+
+ if (min_send_frame_size < IP_PROTOCOL_MIN_MTU_SIZE - IP_PROTOCOL_UDP_PLUS_IP_HEADER) {
+ throw uhd::runtime_error(
+ "System send MTU size is less than the minimum required by the IP protocol.");
+ }
+
+ frame_size_t frame_size;
+ // There are cases when NICs accept oversized packets, in which case we'd falsely
+ // detect a larger-than-possible frame size. A safe and sensible value is the minimum
+ // of the recv and send frame sizes.
+ frame_size.recv_frame_size = std::min(min_recv_frame_size, min_send_frame_size);
+ frame_size.send_frame_size = std::min(min_recv_frame_size, min_send_frame_size);
+ UHD_LOGGER_INFO("X300") << "Maximum frame size: " << frame_size.send_frame_size
+ << " bytes.";
+ return frame_size;
+}
diff --git a/host/lib/usrp/x300/x300_eth_mgr.hpp b/host/lib/usrp/x300/x300_eth_mgr.hpp
new file mode 100644
index 000000000..85b3eabd0
--- /dev/null
+++ b/host/lib/usrp/x300/x300_eth_mgr.hpp
@@ -0,0 +1,126 @@
+//
+// Copyright 2019 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#ifndef INCLUDED_X300_ETH_MGR_HPP
+#define INCLUDED_X300_ETH_MGR_HPP
+
+#include "x300_device_args.hpp"
+#include "x300_mboard_type.hpp"
+#include <uhd/transport/if_addrs.hpp>
+#include <uhd/transport/udp_constants.hpp>
+#include <uhd/transport/udp_simple.hpp> //mtu
+#include <uhd/transport/udp_zero_copy.hpp>
+#include <uhd/types/device_addr.hpp>
+#include <uhdlib/rfnoc/xports.hpp>
+#include <functional>
+#include <vector>
+
+namespace uhd { namespace usrp { namespace x300 {
+
+/*! Helper class to manage the eth connections
+ */
+class eth_manager
+{
+public:
+ eth_manager(const x300_device_args_t& args,
+ uhd::property_tree::sptr tree,
+ const uhd::fs_path& root_path);
+
+ //! Return the motherboard type using eth
+ static x300_mboard_t get_mb_type_from_eth(
+ const std::string& resource, const std::string& rpc_port);
+
+ static uhd::device_addrs_t find(const uhd::device_addr_t& hint);
+
+ /*! Return a reference to a ZPU ctrl interface object
+ */
+ uhd::wb_iface::sptr get_ctrl_iface();
+
+ void init_link(
+ const mboard_eeprom_t& mb_eeprom, const std::string& loaded_fpga_image);
+
+ size_t get_mtu(uhd::direction_t dir);
+
+ /*! Safely release a ZPU control object
+ *
+ * This embeds the release call (provided by \p release_fn) within a safe
+ * context to avoid multiple accesses to the eth bus.
+ */
+ void release_ctrl_iface(std::function<void(void)>&& release_fn);
+
+ both_xports_t make_transport(both_xports_t xports,
+ const uhd::usrp::device3_impl::xport_type_t xport_type,
+ const uhd::device_addr_t& args,
+ const size_t send_mtu,
+ const size_t recv_mtu,
+ std::function<uhd::sid_t(uint32_t, uint32_t)>&& allocate_sid);
+
+private:
+ //! Function to create a udp_simple::sptr (kernel-based or DPDK-based)
+ using udp_simple_factory_t = std::function<uhd::transport::udp_simple::sptr(
+ const std::string&, const std::string&)>;
+
+ // Ethernet ports
+ enum x300_eth_iface_t {
+ X300_IFACE_NONE = 0,
+ X300_IFACE_ETH0 = 1,
+ X300_IFACE_ETH1 = 2,
+ };
+
+ struct x300_eth_conn_t
+ {
+ std::string addr;
+ x300_eth_iface_t type;
+ size_t link_rate;
+ };
+
+ struct frame_size_t
+ {
+ size_t recv_frame_size;
+ size_t send_frame_size;
+ };
+
+ // Get the primary ethernet connection
+ inline const x300_eth_conn_t& get_pri_eth() const
+ {
+ return eth_conns[0];
+ }
+
+ static udp_simple_factory_t x300_get_udp_factory(const device_addr_t& args);
+
+ /*!
+ * Automatically determine the maximum frame size available by sending a UDP packet
+ * to the device and see which packet sizes actually work. This way, we can take
+ * switches etc. into account which might live between the device and the host.
+ */
+ frame_size_t determine_max_frame_size(
+ const std::string& addr, const frame_size_t& user_mtu);
+
+ // Discover the ethernet connections per motherboard
+ void discover_eth(
+ const uhd::usrp::mboard_eeprom_t mb_eeprom, const std::string& loaded_fpga_image);
+
+
+ const x300_device_args_t _args;
+
+ uhd::property_tree::sptr _tree;
+
+ udp_simple_factory_t _x300_make_udp_connected;
+
+ std::vector<x300_eth_conn_t> eth_conns;
+ size_t _next_src_addr = 0;
+ size_t _next_tx_src_addr = 0;
+ size_t _next_rx_src_addr = 0;
+
+ frame_size_t _max_frame_sizes;
+
+ uhd::device_addr_t recv_args;
+ uhd::device_addr_t send_args;
+};
+
+}}} // namespace uhd::usrp::x300
+
+#endif /* INCLUDED_X300_ETH_MGR_HPP */
diff --git a/host/lib/usrp/x300/x300_impl.cpp b/host/lib/usrp/x300/x300_impl.cpp
index 8ad1433d3..59ba1153b 100644
--- a/host/lib/usrp/x300/x300_impl.cpp
+++ b/host/lib/usrp/x300/x300_impl.cpp
@@ -1,278 +1,42 @@
//
// Copyright 2013-2016 Ettus Research LLC
// Copyright 2018 Ettus Research, a National Instruments Company
+// Copyright 2019 Ettus Research, a National Instruments Brand
//
// SPDX-License-Identifier: GPL-3.0-or-later
//
#include "x300_impl.hpp"
-#include "x300_lvbitx.hpp"
+#include "x300_claim.hpp"
+#include "x300_mb_eeprom.hpp"
#include "x300_mb_eeprom_iface.hpp"
#include "x300_mboard_type.hpp"
-#include "x310_lvbitx.hpp"
#include <uhd/transport/if_addrs.hpp>
-#include <uhd/transport/nirio/niusrprio_session.h>
-#include <uhd/transport/nirio_zero_copy.hpp>
-#include <uhd/transport/udp_constants.hpp>
-#include <uhd/transport/udp_zero_copy.hpp>
-#include <uhd/transport/zero_copy_recv_offload.hpp>
#include <uhd/types/sid.hpp>
#include <uhd/usrp/subdev_spec.hpp>
#include <uhd/utils/log.hpp>
#include <uhd/utils/math.hpp>
#include <uhd/utils/paths.hpp>
-#include <uhd/utils/platform.hpp>
#include <uhd/utils/safe_call.hpp>
#include <uhd/utils/static.hpp>
-#include <uhdlib/usrp/common/apply_corrections.hpp>
-#ifdef HAVE_DPDK
-# include <uhdlib/transport/dpdk_simple.hpp>
-# include <uhdlib/transport/dpdk_zero_copy.hpp>
-#endif
#include <boost/algorithm/string.hpp>
-#include <boost/asio.hpp>
#include <boost/make_shared.hpp>
#include <chrono>
#include <fstream>
#include <thread>
+uhd::uart_iface::sptr x300_make_uart_iface(uhd::wb_iface::sptr iface);
+
using namespace uhd;
using namespace uhd::usrp;
using namespace uhd::rfnoc;
-using namespace uhd::transport;
-using namespace uhd::niusrprio;
-using namespace uhd::usrp::gpio_atr;
using namespace uhd::usrp::x300;
namespace asio = boost::asio;
-/******************************************************************************
- * Helpers
- *****************************************************************************/
-namespace {
-
-constexpr unsigned int X300_UDP_RESERVED_FRAME_SIZE = 64;
-
-} // namespace
-
-static x300_impl::udp_simple_factory_t x300_get_udp_factory(const device_addr_t& args)
-{
- x300_impl::udp_simple_factory_t udp_make_connected = udp_simple::make_connected;
- if (args.has_key("use_dpdk")) {
-#ifdef HAVE_DPDK
- udp_make_connected = [](const std::string& addr, const std::string& port) {
- auto& ctx = uhd::transport::uhd_dpdk_ctx::get();
- return dpdk_simple::make_connected(ctx, addr, port);
- };
-#else
- UHD_LOG_WARNING(
- "DPDK", "Detected use_dpdk argument, but DPDK support not built in.");
-#endif
- }
- return udp_make_connected;
-}
-
/***********************************************************************
* Discovery over the udp and pcie transport
**********************************************************************/
-//@TODO: Refactor the find functions to collapse common code for ethernet and PCIe
-static device_addrs_t x300_find_with_addr(const device_addr_t& hint)
-{
- x300_impl::udp_simple_factory_t udp_make_broadcast = udp_simple::make_broadcast;
- x300_impl::udp_simple_factory_t udp_make_connected = x300_get_udp_factory(hint);
-#ifdef HAVE_DPDK
- if (hint.has_key("use_dpdk")) {
- auto& dpdk_ctx = uhd::transport::uhd_dpdk_ctx::get();
- if (not dpdk_ctx.is_init_done()) {
- dpdk_ctx.init(hint);
- }
- udp_make_broadcast = [](const std::string& addr, const std::string& port) {
- auto& ctx = uhd::transport::uhd_dpdk_ctx::get();
- return dpdk_simple::make_broadcast(ctx, addr, port);
- };
- }
-#endif
- udp_simple::sptr comm =
- udp_make_broadcast(hint["addr"], BOOST_STRINGIZE(X300_FW_COMMS_UDP_PORT));
-
- // load request struct
- x300_fw_comms_t request = x300_fw_comms_t();
- request.flags = uhd::htonx<uint32_t>(X300_FW_COMMS_FLAGS_ACK);
- request.sequence = uhd::htonx<uint32_t>(std::rand());
-
- // send request
- comm->send(asio::buffer(&request, sizeof(request)));
-
- // loop for replies until timeout
- device_addrs_t addrs;
- while (true) {
- char buff[X300_FW_COMMS_MTU] = {};
- const size_t nbytes = comm->recv(asio::buffer(buff), 0.050);
- if (nbytes == 0)
- break;
- const x300_fw_comms_t* reply = (const x300_fw_comms_t*)buff;
- if (request.flags != reply->flags)
- continue;
- if (request.sequence != reply->sequence)
- continue;
- device_addr_t new_addr;
- new_addr["type"] = "x300";
- new_addr["addr"] = comm->get_recv_addr();
-
- // Attempt to read the name from the EEPROM and perform filtering.
- // This operation can throw due to compatibility mismatch.
- try {
- wb_iface::sptr zpu_ctrl = x300_make_ctrl_iface_enet(
- udp_make_connected(
- new_addr["addr"], BOOST_STRINGIZE(X300_FW_COMMS_UDP_PORT)),
- false /* Suppress timeout errors */
- );
-
- new_addr["fpga"] = get_fpga_option(zpu_ctrl);
-
- i2c_core_100_wb32::sptr zpu_i2c =
- i2c_core_100_wb32::make(zpu_ctrl, I2C1_BASE);
- x300_mb_eeprom_iface::sptr eeprom_iface =
- x300_mb_eeprom_iface::make(zpu_ctrl, zpu_i2c);
- const mboard_eeprom_t mb_eeprom = x300_impl::get_mb_eeprom(eeprom_iface);
- if (mb_eeprom.size() == 0
- or x300_impl::claim_status(zpu_ctrl) == x300_impl::CLAIMED_BY_OTHER) {
- // Skip device claimed by another process
- continue;
- }
- new_addr["name"] = mb_eeprom["name"];
- new_addr["serial"] = mb_eeprom["serial"];
- const std::string product_name =
- map_mb_type_to_product_name(get_mb_type_from_eeprom(mb_eeprom));
- if (!product_name.empty()) {
- new_addr["product"] = product_name;
- }
- } catch (const std::exception&) {
- // set these values as empty string so the device may still be found
- // and the filter's below can still operate on the discovered device
- new_addr["name"] = "";
- new_addr["serial"] = "";
- }
- // filter the discovered device below by matching optional keys
- if ((not hint.has_key("name") or hint["name"] == new_addr["name"])
- and (not hint.has_key("serial") or hint["serial"] == new_addr["serial"])
- and (not hint.has_key("product") or hint["product"] == new_addr["product"])) {
- addrs.push_back(new_addr);
- }
- }
-
- return addrs;
-}
-
-
-// We need a zpu xport registry to ensure synchronization between the static finder method
-// and the instances of the x300_impl class.
-typedef uhd::dict<std::string, boost::weak_ptr<wb_iface>> pcie_zpu_iface_registry_t;
-UHD_SINGLETON_FCN(pcie_zpu_iface_registry_t, get_pcie_zpu_iface_registry)
-static boost::mutex pcie_zpu_iface_registry_mutex;
-
-static device_addrs_t x300_find_pcie(const device_addr_t& hint, bool explicit_query)
-{
- std::string rpc_port_name(std::to_string(NIUSRPRIO_DEFAULT_RPC_PORT));
- if (hint.has_key("niusrpriorpc_port")) {
- rpc_port_name = hint["niusrpriorpc_port"];
- }
-
- device_addrs_t addrs;
- niusrprio_session::device_info_vtr dev_info_vtr;
- nirio_status status = niusrprio_session::enumerate(rpc_port_name, dev_info_vtr);
- if (explicit_query)
- nirio_status_to_exception(
- status, "x300_find_pcie: Error enumerating NI-RIO devices.");
-
- for (niusrprio_session::device_info& dev_info : dev_info_vtr) {
- device_addr_t new_addr;
- new_addr["type"] = "x300";
- new_addr["resource"] = dev_info.resource_name;
- std::string resource_d(dev_info.resource_name);
- boost::to_upper(resource_d);
-
- const std::string product_name = map_mb_type_to_product_name(
- x300_impl::get_mb_type_from_pcie(resource_d, rpc_port_name));
- if (product_name.empty()) {
- continue;
- } else {
- new_addr["product"] = product_name;
- }
-
- niriok_proxy::sptr kernel_proxy =
- niriok_proxy::make_and_open(dev_info.interface_path);
-
- // Attempt to read the name from the EEPROM and perform filtering.
- // This operation can throw due to compatibility mismatch.
- try {
- // This block could throw an exception if the user is switching to using UHD
- // after LabVIEW FPGA. In that case, skip reading the name and serial and pick
- // a default FPGA flavor. During make, a new image will be loaded and
- // everything will be OK
-
- wb_iface::sptr zpu_ctrl;
-
- // Hold on to the registry mutex as long as zpu_ctrl is alive
- // to prevent any use by different threads while enumerating
- boost::mutex::scoped_lock lock(pcie_zpu_iface_registry_mutex);
-
- if (get_pcie_zpu_iface_registry().has_key(resource_d)) {
- zpu_ctrl = get_pcie_zpu_iface_registry()[resource_d].lock();
- if (!zpu_ctrl) {
- get_pcie_zpu_iface_registry().pop(resource_d);
- }
- }
-
- // if the registry didn't have a key OR that key was an orphaned weak_ptr
- if (!zpu_ctrl) {
- zpu_ctrl = x300_make_ctrl_iface_pcie(
- kernel_proxy, false /* suppress timeout errors */);
- // We don't put this zpu_ctrl in the registry because we need
- // a persistent niriok_proxy associated with the object
- }
-
- // Attempt to autodetect the FPGA type
- if (not hint.has_key("fpga")) {
- new_addr["fpga"] = get_fpga_option(zpu_ctrl);
- }
-
- i2c_core_100_wb32::sptr zpu_i2c =
- i2c_core_100_wb32::make(zpu_ctrl, I2C1_BASE);
- x300_mb_eeprom_iface::sptr eeprom_iface =
- x300_mb_eeprom_iface::make(zpu_ctrl, zpu_i2c);
- const mboard_eeprom_t mb_eeprom = x300_impl::get_mb_eeprom(eeprom_iface);
- if (mb_eeprom.size() == 0
- or x300_impl::claim_status(zpu_ctrl) == x300_impl::CLAIMED_BY_OTHER) {
- // Skip device claimed by another process
- continue;
- }
- new_addr["name"] = mb_eeprom["name"];
- new_addr["serial"] = mb_eeprom["serial"];
- } catch (const std::exception&) {
- // set these values as empty string so the device may still be found
- // and the filter's below can still operate on the discovered device
- if (not hint.has_key("fpga")) {
- new_addr["fpga"] = "HG";
- }
- new_addr["name"] = "";
- new_addr["serial"] = "";
- }
-
- // filter the discovered device below by matching optional keys
- std::string resource_i = hint.has_key("resource") ? hint["resource"] : "";
- boost::to_upper(resource_i);
-
- if ((not hint.has_key("resource") or resource_i == resource_d)
- and (not hint.has_key("name") or hint["name"] == new_addr["name"])
- and (not hint.has_key("serial") or hint["serial"] == new_addr["serial"])
- and (not hint.has_key("product") or hint["product"] == new_addr["product"])) {
- addrs.push_back(new_addr);
- }
- }
- return addrs;
-}
-
device_addrs_t x300_find(const device_addr_t& hint_)
{
// handle the multi-device discovery
@@ -303,15 +67,15 @@ device_addrs_t x300_find(const device_addr_t& hint_)
hints.resize(1); // in case it was empty
device_addr_t hint = hints[0];
device_addrs_t addrs;
- if (hint.has_key("type") and hint["type"] != "x300")
+ if (hint.has_key("type") and hint["type"] != "x300") {
return addrs;
-
+ }
// use the address given
if (hint.has_key("addr")) {
device_addrs_t reply_addrs;
try {
- reply_addrs = x300_find_with_addr(hint);
+ reply_addrs = eth_manager::find(hint);
} catch (const std::exception& ex) {
UHD_LOGGER_ERROR("X300") << "X300 Network discovery error " << ex.what();
} catch (...) {
@@ -322,7 +86,7 @@ device_addrs_t x300_find(const device_addr_t& hint_)
if (!hint.has_key("resource")) {
// otherwise, no address was specified, send a broadcast on each interface
- for (const if_addrs_t& if_addrs : get_if_addrs()) {
+ for (const transport::if_addrs_t& if_addrs : transport::get_if_addrs()) {
// avoid the loopback device
if (if_addrs.inet == asio::ip::address_v4::loopback().to_string())
continue;
@@ -355,9 +119,10 @@ device_addrs_t x300_find(const device_addr_t& hint_)
}
}
- device_addrs_t pcie_addrs = x300_find_pcie(hint, hint.has_key("resource"));
- if (not pcie_addrs.empty())
+ device_addrs_t pcie_addrs = pcie_manager::find(hint, hint.has_key("resource"));
+ if (not pcie_addrs.empty()) {
addrs.insert(addrs.end(), pcie_addrs.begin(), pcie_addrs.end());
+ }
return addrs;
}
@@ -406,8 +171,6 @@ x300_impl::x300_impl(const uhd::device_addr_t& dev_addr) : device3_impl(), _sid_
UHD_LOGGER_INFO("X300") << "X300 initialization sequence...";
_tree->create<std::string>("/name").set("X-Series Device");
- _x300_make_udp_connected = x300_get_udp_factory(dev_addr);
-
const device_addrs_t device_args = separate_device_addr(dev_addr);
_mb.resize(device_args.size());
@@ -437,208 +200,13 @@ x300_impl::x300_impl(const uhd::device_addr_t& dev_addr) : device3_impl(), _sid_
}
}
-void x300_impl::mboard_members_t::discover_eth(
- const mboard_eeprom_t mb_eeprom, const std::vector<std::string>& ip_addrs)
-{
- x300_impl::udp_simple_factory_t udp_make_connected = x300_get_udp_factory(send_args);
-
- // Clear any previous addresses added
- eth_conns.clear();
-
- // Index the MB EEPROM addresses
- std::vector<std::string> mb_eeprom_addrs;
- const size_t num_mb_eeprom_addrs = 4;
- for (size_t i = 0; i < num_mb_eeprom_addrs; i++) {
- const std::string key = "ip-addr" + boost::to_string(i);
-
- // Show a warning if there exists duplicate addresses in the mboard eeprom
- if (std::find(mb_eeprom_addrs.begin(), mb_eeprom_addrs.end(), mb_eeprom[key])
- != mb_eeprom_addrs.end()) {
- UHD_LOGGER_WARNING("X300") << str(
- boost::format(
- "Duplicate IP address %s found in mboard EEPROM. "
- "Device may not function properly. View and reprogram the values "
- "using the usrp_burn_mb_eeprom utility.")
- % mb_eeprom[key]);
- }
- mb_eeprom_addrs.push_back(mb_eeprom[key]);
- }
-
- for (const std::string& addr : ip_addrs) {
- x300_eth_conn_t conn_iface;
- conn_iface.addr = addr;
- conn_iface.type = X300_IFACE_NONE;
-
- // Decide from the mboard eeprom what IP corresponds
- // to an interface
- for (size_t i = 0; i < mb_eeprom_addrs.size(); i++) {
- if (addr == mb_eeprom_addrs[i]) {
- // Choose the interface based on the index parity
- if (i % 2 == 0) {
- conn_iface.type = X300_IFACE_ETH0;
- conn_iface.link_rate = loaded_fpga_image == "HG"
- ? x300::MAX_RATE_1GIGE
- : x300::MAX_RATE_10GIGE;
- } else {
- conn_iface.type = X300_IFACE_ETH1;
- conn_iface.link_rate = x300::MAX_RATE_10GIGE;
- }
- break;
- }
- }
-
- // Check default IP addresses if we couldn't
- // determine the IP from the mboard eeprom
- if (conn_iface.type == X300_IFACE_NONE) {
- UHD_LOGGER_WARNING("X300") << str(
- boost::format(
- "Address %s not found in mboard EEPROM. Address may be wrong or "
- "the EEPROM may be corrupt. Attempting to continue with default "
- "IP addresses.")
- % conn_iface.addr);
-
- if (addr
- == boost::asio::ip::address_v4(uint32_t(X300_DEFAULT_IP_ETH0_1G))
- .to_string()) {
- conn_iface.type = X300_IFACE_ETH0;
- conn_iface.link_rate = x300::MAX_RATE_1GIGE;
- } else if (addr
- == boost::asio::ip::address_v4(uint32_t(X300_DEFAULT_IP_ETH1_1G))
- .to_string()) {
- conn_iface.type = X300_IFACE_ETH1;
- conn_iface.link_rate = x300::MAX_RATE_1GIGE;
- } else if (addr
- == boost::asio::ip::address_v4(uint32_t(X300_DEFAULT_IP_ETH0_10G))
- .to_string()) {
- conn_iface.type = X300_IFACE_ETH0;
- conn_iface.link_rate = x300::MAX_RATE_10GIGE;
- } else if (addr
- == boost::asio::ip::address_v4(uint32_t(X300_DEFAULT_IP_ETH1_10G))
- .to_string()) {
- conn_iface.type = X300_IFACE_ETH1;
- conn_iface.link_rate = x300::MAX_RATE_10GIGE;
- } else {
- throw uhd::assertion_error(
- str(boost::format(
- "X300 Initialization Error: Failed to match address %s with "
- "any addresses for the device. Please check the address.")
- % conn_iface.addr));
- }
- }
-
- // Save to a vector of connections
- if (conn_iface.type != X300_IFACE_NONE) {
- // Check the address before we add it
- try {
- wb_iface::sptr zpu_ctrl = x300_make_ctrl_iface_enet(
- udp_make_connected(
- conn_iface.addr, BOOST_STRINGIZE(X300_FW_COMMS_UDP_PORT)),
- false /* Suppress timeout errors */
- );
-
- // Peek the ZPU ctrl to make sure this connection works
- zpu_ctrl->peek32(0);
- }
-
- // If the address does not work, throw an error
- catch (std::exception&) {
- throw uhd::io_error(
- str(boost::format("X300 Initialization Error: Invalid address %s")
- % conn_iface.addr));
- }
- eth_conns.push_back(conn_iface);
- }
- }
-
- if (eth_conns.size() == 0)
- throw uhd::assertion_error(
- "X300 Initialization Error: No ethernet interfaces specified.");
-}
-
void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t& dev_addr)
{
const fs_path mb_path = fs_path("/mboards") / mb_i;
mboard_members_t& mb = _mb[mb_i];
- mb.initialization_done = false;
-
- const std::string thread_id(
- boost::lexical_cast<std::string>(boost::this_thread::get_id()));
- const std::string thread_msg(
- "Thread ID " + thread_id + " for motherboard " + std::to_string(mb_i));
-
mb.args.parse(dev_addr);
-
- std::vector<std::string> eth_addrs;
- // Not choosing eth0 based on resource might cause user issues
- std::string eth0_addr = dev_addr.has_key("resource") ? dev_addr["resource"]
- : dev_addr["addr"];
- eth_addrs.push_back(eth0_addr);
-
- mb.next_src_addr = 0; // Host source address for blocks
- mb.next_tx_src_addr = 0;
- mb.next_rx_src_addr = 0;
- if (not mb.args.get_second_addr().empty()) {
- std::string eth1_addr = mb.args.get_second_addr();
-
- // Ensure we do not have duplicate addresses
- if (eth1_addr != eth0_addr)
- eth_addrs.push_back(eth1_addr);
- }
-
- // Initially store the first address provided to setup communication
- // Once we read the eeprom, we use it to map IP to its interface
- x300_eth_conn_t init;
- init.addr = eth_addrs[0];
- mb.eth_conns.push_back(init);
-
- mb.xport_path = dev_addr.has_key("resource") ? "nirio" : "eth";
- mb.if_pkt_is_big_endian = mb.xport_path != "nirio";
-
- if (mb.xport_path == "nirio") {
- nirio_status status = 0;
-
- const std::string rpc_port_name = mb.args.get_niusrprio_rpc_port();
- UHD_LOGGER_INFO("X300")
- << boost::format("Connecting to niusrpriorpc at localhost:%s...")
- % rpc_port_name;
-
- // Instantiate the correct lvbitx object
- nifpga_lvbitx::sptr lvbitx;
- switch (get_mb_type_from_pcie(mb.args.get_resource(), rpc_port_name)) {
- case USRP_X300_MB:
- lvbitx.reset(new x300_lvbitx(dev_addr["fpga"]));
- break;
- case USRP_X310_MB:
- case USRP_X310_MB_NI_2974:
- lvbitx.reset(new x310_lvbitx(dev_addr["fpga"]));
- break;
- default:
- nirio_status_to_exception(
- status, "Motherboard detection error. Please ensure that you \
- have a valid USRP X3x0, NI USRP-294xR, NI USRP-295xR or NI USRP-2974 device and that all the device \
- drivers have loaded successfully.");
- }
- // Load the lvbitx onto the device
- UHD_LOGGER_INFO("X300")
- << boost::format("Using LVBITX bitfile %s...") % lvbitx->get_bitfile_path();
- mb.rio_fpga_interface.reset(
- new niusrprio_session(dev_addr["resource"], rpc_port_name));
- nirio_status_chain(
- mb.rio_fpga_interface->open(lvbitx, dev_addr.has_key("download-fpga")),
- status);
- nirio_status_to_exception(status, "x300_impl: Could not initialize RIO session.");
-
- // Tell the quirks object which FIFOs carry TX stream data
- const uint32_t tx_data_fifos[2] = {
- x300::RADIO_DEST_PREFIX_TX, x300::RADIO_DEST_PREFIX_TX + 3};
- mb.rio_fpga_interface->get_kernel_proxy()->get_rio_quirks().register_tx_streams(
- tx_data_fifos, 2);
-
- _tree->create<size_t>(mb_path / "mtu/recv").set(x300::PCIE_RX_DATA_FRAME_SIZE);
- _tree->create<size_t>(mb_path / "mtu/send").set(x300::PCIE_TX_DATA_FRAME_SIZE);
- _tree->create<double>(mb_path / "link_max_rate").set(x300::MAX_RATE_PCIE);
- }
-
+ mb.xport_path = dev_addr.has_key("resource") ? xport_path_t::NIRIO
+ : xport_path_t::ETH;
for (const std::string& key : dev_addr.keys()) {
if (key.find("recv") != std::string::npos)
mb.recv_args[key] = dev_addr[key];
@@ -646,37 +214,23 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t& dev_addr)
mb.send_args[key] = dev_addr[key];
}
-#ifdef HAVE_DPDK
- if (dev_addr.has_key("use_dpdk")) {
- mb.recv_args["use_dpdk"] = dev_addr["use_dpdk"];
- mb.send_args["use_dpdk"] = dev_addr["use_dpdk"];
- }
-#endif
-
- // create basic communication
UHD_LOGGER_DEBUG("X300") << "Setting up basic communication...";
- if (mb.xport_path == "nirio") {
- boost::mutex::scoped_lock lock(pcie_zpu_iface_registry_mutex);
- if (get_pcie_zpu_iface_registry().has_key(mb.get_pri_eth().addr)) {
- throw uhd::assertion_error(
- "Someone else has a ZPU transport to the device open. Internal error!");
- } else {
- mb.zpu_ctrl =
- x300_make_ctrl_iface_pcie(mb.rio_fpga_interface->get_kernel_proxy());
- get_pcie_zpu_iface_registry()[mb.get_pri_eth().addr] =
- boost::weak_ptr<wb_iface>(mb.zpu_ctrl);
- }
+ if (mb.xport_path == xport_path_t::NIRIO) {
+ mb.pcie_mgr =
+ std::unique_ptr<pcie_manager>(new pcie_manager(mb.args, _tree, mb_path));
+ mb.zpu_ctrl = mb.pcie_mgr->get_ctrl_iface();
} else {
- mb.zpu_ctrl = x300_make_ctrl_iface_enet(_x300_make_udp_connected(
- mb.get_pri_eth().addr, BOOST_STRINGIZE(X300_FW_COMMS_UDP_PORT)));
+ mb.eth_mgr =
+ std::unique_ptr<eth_manager>(new eth_manager(mb.args, _tree, mb_path));
+ mb.zpu_ctrl = mb.eth_mgr->get_ctrl_iface();
}
// Claim device
if (not try_to_claim(mb.zpu_ctrl)) {
throw uhd::runtime_error("Failed to claim device");
}
- mb.claimer_task = uhd::task::make(
- [this, mb]() { this->claimer_loop(mb.zpu_ctrl); }, "x300_claimer");
+ mb.claimer_task =
+ uhd::task::make([&mb]() { claimer_loop(mb.zpu_ctrl); }, "x300_claimer");
// extract the FW path for the X300
// and live load fw over ethernet link
@@ -741,8 +295,8 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t& dev_addr)
// Initialize the property with a current copy of the EEPROM contents
.set(mb_eeprom)
// Whenever this property is written, update the chip
- .add_coerced_subscriber([this, eeprom16](const mboard_eeprom_t& mb_eeprom) {
- this->set_mb_eeprom(eeprom16, mb_eeprom);
+ .add_coerced_subscriber([eeprom16](const mboard_eeprom_t& mb_eeprom) {
+ set_mb_eeprom(eeprom16, mb_eeprom);
});
if (mb.args.get_recover_mb_eeprom()) {
@@ -775,131 +329,10 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t& dev_addr)
////////////////////////////////////////////////////////////////////
// discover interfaces, frame sizes, and link rates
////////////////////////////////////////////////////////////////////
- if (mb.xport_path == "nirio") {
- _max_frame_sizes.recv_frame_size = PCIE_RX_DATA_FRAME_SIZE;
- _max_frame_sizes.send_frame_size = PCIE_TX_DATA_FRAME_SIZE;
- } else if (mb.xport_path == "eth") {
- double link_max_rate = 0.0;
-
- // Discover ethernet interfaces
- mb.discover_eth(mb_eeprom, eth_addrs);
-
- /* This is an ETH connection. Figure out what the maximum supported frame
- * size is for the transport in the up and down directions. The frame size
- * depends on the host PC's NIC's MTU settings. To determine the frame size,
- * we test for support up to an expected "ceiling". If the user
- * specified a frame size, we use that frame size as the ceiling. If no
- * frame size was specified, we use the maximum UHD frame size.
- *
- * To optimize performance, the frame size should be greater than or equal
- * to the frame size that UHD uses so that frames don't get split across
- * multiple transmission units - this is why the limits passed into the
- * 'determine_max_frame_size' function are actually frame sizes. */
- frame_size_t req_max_frame_size;
- req_max_frame_size.recv_frame_size =
- (mb.recv_args.has_key("recv_frame_size"))
- ? boost::lexical_cast<size_t>(mb.recv_args["recv_frame_size"])
- : x300::DATA_FRAME_MAX_SIZE;
- req_max_frame_size.send_frame_size =
- (mb.send_args.has_key("send_frame_size"))
- ? boost::lexical_cast<size_t>(mb.send_args["send_frame_size"])
- : x300::DATA_FRAME_MAX_SIZE;
-
-#if defined UHD_PLATFORM_LINUX
- const std::string mtu_tool("ip link");
-#elif defined UHD_PLATFORM_WIN32
- const std::string mtu_tool("netsh");
-#else
- const std::string mtu_tool("ifconfig");
-#endif
-
- // Detect the frame size on the path to the USRP
- try {
- frame_size_t pri_frame_sizes =
- determine_max_frame_size(eth_addrs.at(0), req_max_frame_size);
-
- _max_frame_sizes = pri_frame_sizes;
- if (eth_addrs.size() > 1) {
- frame_size_t sec_frame_sizes =
- determine_max_frame_size(eth_addrs.at(1), req_max_frame_size);
-
- // Choose the minimum of the max frame sizes
- // to ensure we don't exceed any one of the links' MTU
- _max_frame_sizes.recv_frame_size = std::min(
- pri_frame_sizes.recv_frame_size, sec_frame_sizes.recv_frame_size);
-
- _max_frame_sizes.send_frame_size = std::min(
- pri_frame_sizes.send_frame_size, sec_frame_sizes.send_frame_size);
- }
- } catch (std::exception& e) {
- UHD_LOGGER_ERROR("X300") << e.what();
- }
-
- if ((mb.recv_args.has_key("recv_frame_size"))
- && (req_max_frame_size.recv_frame_size > _max_frame_sizes.recv_frame_size)) {
- UHD_LOGGER_WARNING("X300")
- << boost::format("You requested a receive frame size of (%lu) but your "
- "NIC's max frame size is (%lu).")
- % req_max_frame_size.recv_frame_size
- % _max_frame_sizes.recv_frame_size
- << boost::format("Please verify your NIC's MTU setting using '%s' or set "
- "the recv_frame_size argument appropriately.")
- % mtu_tool
- << "UHD will use the auto-detected max frame size for this connection.";
- }
-
- if ((mb.send_args.has_key("send_frame_size"))
- && (req_max_frame_size.send_frame_size > _max_frame_sizes.send_frame_size)) {
- UHD_LOGGER_WARNING("X300")
- << boost::format("You requested a send frame size of (%lu) but your "
- "NIC's max frame size is (%lu).")
- % req_max_frame_size.send_frame_size
- % _max_frame_sizes.send_frame_size
- << boost::format("Please verify your NIC's MTU setting using '%s' or set "
- "the send_frame_size argument appropriately.")
- % mtu_tool
- << "UHD will use the auto-detected max frame size for this connection.";
- }
-
- // Check frame sizes
- for (auto conn : mb.eth_conns) {
- link_max_rate += conn.link_rate;
-
- size_t rec_send_frame_size = conn.link_rate == x300::MAX_RATE_1GIGE
- ? x300::GE_DATA_FRAME_SEND_SIZE
- : x300::XGE_DATA_FRAME_SEND_SIZE;
- size_t rec_recv_frame_size = conn.link_rate == x300::MAX_RATE_1GIGE
- ? x300::GE_DATA_FRAME_RECV_SIZE
- : x300::XGE_DATA_FRAME_RECV_SIZE;
-
- if (_max_frame_sizes.send_frame_size < rec_send_frame_size) {
- UHD_LOGGER_WARNING("X300")
- << boost::format("For the %s connection, UHD recommends a send frame "
- "size of at least %lu for best\nperformance, but "
- "your configuration will only allow %lu.")
- % conn.addr % rec_send_frame_size
- % _max_frame_sizes.send_frame_size
- << "This may negatively impact your maximum achievable sample "
- "rate.\nCheck the MTU on the interface and/or the send_frame_size "
- "argument.";
- }
-
- if (_max_frame_sizes.recv_frame_size < rec_recv_frame_size) {
- UHD_LOGGER_WARNING("X300")
- << boost::format("For the %s connection, UHD recommends a receive "
- "frame size of at least %lu for best\nperformance, "
- "but your configuration will only allow %lu.")
- % conn.addr % rec_recv_frame_size
- % _max_frame_sizes.recv_frame_size
- << "This may negatively impact your maximum achievable sample "
- "rate.\nCheck the MTU on the interface and/or the recv_frame_size "
- "argument.";
- }
- }
-
- _tree->create<size_t>(mb_path / "mtu/recv").set(_max_frame_sizes.recv_frame_size);
- _tree->create<size_t>(mb_path / "mtu/send").set(_max_frame_sizes.send_frame_size);
- _tree->create<double>(mb_path / "link_max_rate").set(link_max_rate);
+ if (mb.xport_path == xport_path_t::NIRIO) {
+ mb.pcie_mgr->init_link();
+ } else if (mb.xport_path == xport_path_t::ETH) {
+ mb.eth_mgr->init_link(mb_eeprom, mb.loaded_fpga_image);
}
////////////////////////////////////////////////////////////////////
@@ -930,7 +363,7 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t& dev_addr)
////////////////////////////////////////////////////////////////////
// create clock properties
////////////////////////////////////////////////////////////////////
- _tree->create<double>(mb_path / "master_clock_rate").set_publisher([mb]() {
+ _tree->create<double>(mb_path / "master_clock_rate").set_publisher([&mb]() {
return mb.clock->get_master_clock_rate();
});
@@ -940,7 +373,7 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t& dev_addr)
////////////////////////////////////////////////////////////////////
// Create the GPSDO control
////////////////////////////////////////////////////////////////////
- static const uint32_t dont_look_for_gpsdo = 0x1234abcdul;
+ static constexpr uint32_t dont_look_for_gpsdo = 0x1234abcdul;
// otherwise if not disabled, look for the internal GPSDO
if (mb.zpu_ctrl->peek32(SR_ADDR(X300_FW_SHMEM_BASE, X300_FW_SHMEM_GPSDO_STATUS))
@@ -972,10 +405,8 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t& dev_addr)
.add_coerced_subscriber([this, &mb](const std::string& time_source) {
this->update_time_source(mb, time_source);
});
- static const std::vector<std::string> time_sources = {
- "internal", "external", "gpsdo"};
_tree->create<std::vector<std::string>>(mb_path / "time_source" / "options")
- .set(time_sources);
+ .set(TIME_SOURCE_OPTIONS);
// setup the time output, default to ON
_tree->create<bool>(mb_path / "time_source" / "output")
@@ -992,10 +423,8 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t& dev_addr)
.add_coerced_subscriber([this, &mb](const std::string& clock_source) {
this->update_clock_source(mb, clock_source);
});
- static const std::vector<std::string> clock_source_options = {
- "internal", "external", "gpsdo"};
_tree->create<std::vector<std::string>>(mb_path / "clock_source" / "options")
- .set(clock_source_options);
+ .set(CLOCK_SOURCE_OPTIONS);
// setup external reference options. default to 10 MHz input reference
_tree->create<std::string>(mb_path / "clock_source" / "external");
@@ -1107,14 +536,10 @@ x300_impl::~x300_impl(void)
for (mboard_members_t& mb : _mb) {
// kill the claimer task and unclaim the device
mb.claimer_task.reset();
- { // Critical section
- boost::mutex::scoped_lock lock(pcie_zpu_iface_registry_mutex);
+ if (mb.xport_path == xport_path_t::NIRIO) {
+ mb.pcie_mgr->release_ctrl_iface([&mb]() { release(mb.zpu_ctrl); });
+ } else {
release(mb.zpu_ctrl);
- // If the process is killed, the entire registry will disappear so we
- // don't need to worry about unclean shutdowns here.
- if (get_pcie_zpu_iface_registry().has_key(mb.get_pri_eth().addr)) {
- get_pcie_zpu_iface_registry().pop(mb.get_pri_eth().addr);
- }
}
}
} catch (...) {
@@ -1122,67 +547,13 @@ x300_impl::~x300_impl(void)
}
}
-uint32_t x300_impl::mboard_members_t::allocate_pcie_dma_chan(
- const uhd::sid_t& tx_sid, const xport_type_t xport_type)
-{
- static const uint32_t CTRL_CHANNEL = 0;
- static const uint32_t ASYNC_MSG_CHANNEL = 1;
- static const uint32_t FIRST_DATA_CHANNEL = 2;
- if (xport_type == CTRL) {
- return CTRL_CHANNEL;
- } else if (xport_type == ASYNC_MSG) {
- return ASYNC_MSG_CHANNEL;
- } else {
- // sid_t has no comparison defined, so we need to convert it uint32_t
- uint32_t raw_sid = tx_sid.get();
-
- if (_dma_chan_pool.count(raw_sid) == 0) {
- size_t channel = _dma_chan_pool.size() + FIRST_DATA_CHANNEL;
- if (channel > x300::PCIE_MAX_CHANNELS) {
- throw uhd::runtime_error(
- "Trying to allocate more DMA channels than are available");
- }
- _dma_chan_pool[raw_sid] = channel;
- UHD_LOGGER_DEBUG("X300")
- << "Assigning PCIe DMA channel " << _dma_chan_pool[raw_sid] << " to SID "
- << tx_sid.to_pp_string_hex();
- }
-
- return _dma_chan_pool[raw_sid];
- }
-}
-
-static uint32_t extract_sid_from_pkt(void* pkt, size_t)
-{
- return uhd::sid_t(uhd::wtohx(static_cast<const uint32_t*>(pkt)[1])).get_dst();
-}
-
-static uhd::transport::muxed_zero_copy_if::sptr make_muxed_pcie_msg_xport(
- uhd::niusrprio::niusrprio_session::sptr rio_fpga_interface,
- uint32_t dma_channel_num,
- size_t max_muxed_ports)
-{
- zero_copy_xport_params buff_args;
- buff_args.send_frame_size = x300::PCIE_MSG_FRAME_SIZE;
- buff_args.recv_frame_size = x300::PCIE_MSG_FRAME_SIZE;
- buff_args.num_send_frames = x300::PCIE_MSG_NUM_FRAMES;
- buff_args.num_recv_frames = x300::PCIE_MSG_NUM_FRAMES;
-
- zero_copy_if::sptr base_xport = nirio_zero_copy::make(
- rio_fpga_interface, dma_channel_num, buff_args, uhd::device_addr_t());
- return muxed_zero_copy_if::make(base_xport, extract_sid_from_pkt, max_muxed_ports);
-}
-
uhd::both_xports_t x300_impl::make_transport(const uhd::sid_t& address,
const xport_type_t xport_type,
const uhd::device_addr_t& args)
{
- const size_t mb_index = address.get_dst_addr() - x300::DST_ADDR;
- mboard_members_t& mb = _mb[mb_index];
- zero_copy_xport_params default_buff_args;
-
+ const size_t mb_index = address.get_dst_addr() - x300::DST_ADDR;
+ mboard_members_t& mb = _mb[mb_index];
both_xports_t xports;
- xports.endianness = mb.if_pkt_is_big_endian ? ENDIANNESS_BIG : ENDIANNESS_LITTLE;
// Calculate MTU based on MTU in args and device limitations
const size_t send_mtu = args.cast<size_t>("mtu",
@@ -1190,183 +561,24 @@ uhd::both_xports_t x300_impl::make_transport(const uhd::sid_t& address,
const size_t recv_mtu = args.cast<size_t>("mtu",
get_mtu(mb_index, uhd::RX_DIRECTION));
- if (mb.xport_path == "nirio") {
- xports.lossless = true;
+ if (mb.xport_path == xport_path_t::NIRIO) {
xports.send_sid =
this->allocate_sid(mb, address, x300::SRC_ADDR0, x300::XB_DST_PCI);
xports.recv_sid = xports.send_sid.reversed();
-
- uint32_t dma_channel_num = mb.allocate_pcie_dma_chan(xports.send_sid, xport_type);
- if (xport_type == CTRL) {
- // Transport for control stream
- if (not mb.ctrl_dma_xport) {
- // One underlying DMA channel will handle
- // all control traffic
- mb.ctrl_dma_xport = make_muxed_pcie_msg_xport(mb.rio_fpga_interface,
- dma_channel_num,
- x300::PCIE_MAX_MUXED_CTRL_XPORTS);
- }
- // Create a virtual control transport
- xports.recv = mb.ctrl_dma_xport->make_stream(xports.recv_sid.get_dst());
- } else if (xport_type == ASYNC_MSG) {
- // Transport for async message stream
- if (not mb.async_msg_dma_xport) {
- // One underlying DMA channel will handle
- // all async message traffic
- mb.async_msg_dma_xport = make_muxed_pcie_msg_xport(mb.rio_fpga_interface,
- dma_channel_num,
- x300::PCIE_MAX_MUXED_ASYNC_XPORTS);
- }
- // Create a virtual async message transport
- xports.recv = mb.async_msg_dma_xport->make_stream(xports.recv_sid.get_dst());
- } else if (xport_type == TX_DATA) {
- default_buff_args.send_frame_size = args.cast<size_t>(
- "send_frame_size", std::min(send_mtu,
- x300::PCIE_TX_DATA_FRAME_SIZE));
- default_buff_args.num_send_frames = args.cast<size_t>(
- "num_send_frames", x300::PCIE_TX_DATA_NUM_FRAMES);
- default_buff_args.send_buff_size = args.cast<size_t>(
- "send_buff_size", 0);
- default_buff_args.recv_frame_size = x300::PCIE_MSG_FRAME_SIZE;
- default_buff_args.num_recv_frames = x300::PCIE_MSG_NUM_FRAMES;
- xports.recv = nirio_zero_copy::make(
- mb.rio_fpga_interface, dma_channel_num, default_buff_args);
- } else if (xport_type == RX_DATA) {
- default_buff_args.send_frame_size = x300::PCIE_MSG_FRAME_SIZE;
- default_buff_args.num_send_frames = x300::PCIE_MSG_NUM_FRAMES;
- default_buff_args.recv_frame_size = args.cast<size_t>(
- "recv_frame_size", std::min(recv_mtu,
- x300::PCIE_RX_DATA_FRAME_SIZE));
- default_buff_args.num_recv_frames = args.cast<size_t>(
- "num_recv_frames", x300::PCIE_RX_DATA_NUM_FRAMES);
- default_buff_args.recv_buff_size = args.cast<size_t>(
- "recv_buff_size", 0);
- xports.recv = nirio_zero_copy::make(
- mb.rio_fpga_interface, dma_channel_num, default_buff_args);
- }
-
- xports.send = xports.recv;
-
- // Router config word is:
- // - Upper 16 bits: Destination address (e.g. 0.0)
- // - Lower 16 bits: DMA channel
- uint32_t router_config_word = (xports.recv_sid.get_dst() << 16) | dma_channel_num;
- mb.rio_fpga_interface->get_kernel_proxy()->poke(
- PCIE_ROUTER_REG(0), router_config_word);
-
- // For the nirio transport, buffer size is depends on the frame size and num
- // frames
- xports.recv_buff_size =
- xports.recv->get_num_recv_frames() * xports.recv->get_recv_frame_size();
- xports.send_buff_size =
- xports.send->get_num_send_frames() * xports.send->get_send_frame_size();
-
-#ifdef HAVE_DPDK
- } else if (mb.xport_path == "eth" and mb.recv_args.has_key("use_dpdk")) {
- auto& dpdk_ctx = uhd::transport::uhd_dpdk_ctx::get();
- // Decide on the IP/Interface pair based on the endpoint index
- size_t& next_src_addr = xport_type == TX_DATA
- ? mb.next_tx_src_addr
- : xport_type == RX_DATA ? mb.next_rx_src_addr
- : mb.next_src_addr;
- x300_eth_conn_t conn = mb.eth_conns[next_src_addr];
- const uint32_t xbar_src_addr = next_src_addr == 0 ? x300::SRC_ADDR0
- : x300::SRC_ADDR1;
- const uint32_t xbar_src_dst = conn.type == X300_IFACE_ETH0 ? x300::XB_DST_E0
- : x300::XB_DST_E1;
-
- // Do not increment src addr for tx_data by default, using dual ethernet
- // with the DMA FIFO causes sequence errors to DMA FIFO bandwidth
- // limitations.
- if (xport_type != TX_DATA || mb.args.get_enable_tx_dual_eth()) {
- next_src_addr = (next_src_addr + 1) % mb.eth_conns.size();
- }
-
- xports.send_sid = this->allocate_sid(mb, address, xbar_src_addr, xbar_src_dst);
- xports.recv_sid = xports.send_sid.reversed();
-
- // Set size and number of frames
- default_buff_args.send_frame_size = std::min(send_mtu,
- x300::ETH_MSG_FRAME_SIZE);
- default_buff_args.recv_frame_size = std::min(recv_mtu,
- x300::ETH_MSG_FRAME_SIZE);
- default_buff_args.num_recv_frames = x300::ETH_MSG_NUM_FRAMES;
- default_buff_args.num_send_frames = x300::ETH_MSG_NUM_FRAMES;
- if (xport_type == CTRL) {
- // Increasing number of recv frames here because ctrl_iface uses it
- // to determine how many control packets can be in flight before it
- // must wait for an ACK
- default_buff_args.num_recv_frames =
- uhd::rfnoc::CMD_FIFO_SIZE / uhd::rfnoc::MAX_CMD_PKT_SIZE;
- } else if (xport_type == TX_DATA) {
- size_t default_frame_size = conn.link_rate == x300::MAX_RATE_1GIGE
- ? x300::GE_DATA_FRAME_SEND_SIZE
- : x300::XGE_DATA_FRAME_SEND_SIZE;
- default_buff_args.send_frame_size =
- args.cast<size_t>("send_frame_size",
- std::min(default_frame_size, send_mtu));
- default_buff_args.num_send_frames =
- args.cast<size_t>("num_send_frames",
- default_buff_args.num_send_frames);
- default_buff_args.send_buff_size =
- args.cast<size_t>("send_buff_size", 0);
- } else if (xport_type == RX_DATA) {
- size_t default_frame_size = conn.link_rate == x300::MAX_RATE_1GIGE
- ? x300::GE_DATA_FRAME_RECV_SIZE
- : x300::XGE_DATA_FRAME_RECV_SIZE;
- default_buff_args.recv_frame_size =
- args.cast<size_t>("recv_frame_size",
- std::min(default_frame_size, recv_mtu));
- default_buff_args.num_recv_frames =
- args.cast<size_t>("num_recv_frames",
- default_buff_args.num_recv_frames);
- default_buff_args.recv_buff_size =
- args.cast<size_t>("recv_buff_size", 0);
- }
-
- int dpdk_port_id = dpdk_ctx.get_route(conn.addr);
- if (dpdk_port_id < 0) {
- throw uhd::runtime_error(
- "Could not find a DPDK port with route to " + conn.addr);
- }
- auto recv = transport::dpdk_zero_copy::make(
- dpdk_ctx,
- (const unsigned int) dpdk_port_id,
- conn.addr,
- BOOST_STRINGIZE(X300_VITA_UDP_PORT),
- "0",
- default_buff_args,
- uhd::device_addr_t()
- );
-
- xports.recv = recv; // Note: This is a type cast!
- xports.send = xports.recv;
- xports.recv_buff_size =
- (default_buff_args.recv_frame_size - X300_UDP_RESERVED_FRAME_SIZE)
- * default_buff_args.num_recv_frames;
- xports.send_buff_size =
- (default_buff_args.send_frame_size - X300_UDP_RESERVED_FRAME_SIZE)
- * default_buff_args.num_send_frames;
- UHD_LOG_TRACE("BUFF",
- "num_recv_frames="
- << default_buff_args.num_recv_frames
- << ", num_send_frames=" << default_buff_args.num_send_frames
- << ", recv_frame_size=" << default_buff_args.recv_frame_size
- << ", send_frame_size=" << default_buff_args.send_frame_size);
- // send a mini packet with SID into the ZPU
- // ZPU will reprogram the ethernet framer
- UHD_LOGGER_DEBUG("X300") << "programming packet for new xport on " << conn.addr
- << " sid " << xports.send_sid;
- // YES, get a __send__ buffer from the __recv__ socket
- //-- this is the only way to program the framer for recv:
- managed_send_buffer::sptr buff = xports.recv->get_send_buff();
- buff->cast<uint32_t*>()[0] = 0; // eth dispatch looks for != 0
- buff->cast<uint32_t*>()[1] = uhd::htonx(xports.send_sid.get());
- buff->commit(8);
- buff.reset();
+ return mb.pcie_mgr->make_transport(xports, xport_type, args, send_mtu, recv_mtu);
+ } else if (mb.xport_path == xport_path_t::ETH) {
+ xports = mb.eth_mgr->make_transport(xports,
+ xport_type,
+ args,
+ send_mtu,
+ recv_mtu,
+ [this, &mb, address](const uint32_t src_addr, const uint32_t src_dst) {
+ return this->allocate_sid(mb, address, src_addr, src_dst);
+ });
// reprogram the ethernet dispatcher's udp port (should be safe to always set)
- UHD_LOGGER_TRACE("X300") << "reprogram the ethernet dispatcher's udp port";
+ UHD_LOGGER_TRACE("X300")
+ << "reprogram the ethernet dispatcher's udp port to " << X300_VITA_UDP_PORT;
mb.zpu_ctrl->poke32(
SR_ADDR(SET0_BASE, (ZPU_SR_ETHINT0 + 8 + 3)), X300_VITA_UDP_PORT);
mb.zpu_ctrl->poke32(
@@ -1375,128 +587,10 @@ uhd::both_xports_t x300_impl::make_transport(const uhd::sid_t& address,
// Do a peek to an arbitrary address to guarantee that the
// ethernet framer has been programmed before we return.
mb.zpu_ctrl->peek32(0);
-#endif
- } else if (mb.xport_path == "eth") {
- // Decide on the IP/Interface pair based on the endpoint index
- size_t& next_src_addr = xport_type == TX_DATA
- ? mb.next_tx_src_addr
- : xport_type == RX_DATA ? mb.next_rx_src_addr
- : mb.next_src_addr;
- x300_eth_conn_t conn = mb.eth_conns[next_src_addr];
- const uint32_t xbar_src_addr = next_src_addr == 0 ? x300::SRC_ADDR0
- : x300::SRC_ADDR1;
- const uint32_t xbar_src_dst = conn.type == X300_IFACE_ETH0 ? x300::XB_DST_E0
- : x300::XB_DST_E1;
-
- // Do not increment src addr for tx_data by default, using dual ethernet
- // with the DMA FIFO causes sequence errors to DMA FIFO bandwidth
- // limitations.
- if (xport_type != TX_DATA || mb.args.get_enable_tx_dual_eth()) {
- next_src_addr = (next_src_addr + 1) % mb.eth_conns.size();
- }
-
- xports.send_sid = this->allocate_sid(mb, address, xbar_src_addr, xbar_src_dst);
- xports.recv_sid = xports.send_sid.reversed();
-
- // Set size and number of frames
- default_buff_args.send_frame_size = std::min(send_mtu,
- x300::ETH_MSG_FRAME_SIZE);
- default_buff_args.recv_frame_size = std::min(recv_mtu,
- x300::ETH_MSG_FRAME_SIZE);
- // Buffering is done in the socket buffers, so size them relative to
- // the link rate
- default_buff_args.send_buff_size = conn.link_rate / 50; // 20ms
- default_buff_args.recv_buff_size = std::max(conn.link_rate / 50,
- x300::ETH_MSG_NUM_FRAMES
- * x300::ETH_MSG_FRAME_SIZE); // enough to hold greater of 20ms or number
- // of msg frames
- // There is no need for more than 1 send and recv frame since the
- // buffering is done in the socket buffers
- default_buff_args.num_send_frames = 1;
- default_buff_args.num_recv_frames = 1;
- if (xport_type == CTRL) {
- // Increasing number of recv frames here because ctrl_iface uses it
- // to determine how many control packets can be in flight before it
- // must wait for an ACK
- default_buff_args.num_recv_frames =
- uhd::rfnoc::CMD_FIFO_SIZE / uhd::rfnoc::MAX_CMD_PKT_SIZE;
- } else if (xport_type == TX_DATA) {
- size_t default_frame_size = conn.link_rate == x300::MAX_RATE_1GIGE
- ? x300::GE_DATA_FRAME_SEND_SIZE
- : x300::XGE_DATA_FRAME_SEND_SIZE;
- default_buff_args.send_frame_size =
- args.cast<size_t>("send_frame_size",
- std::min(default_frame_size, send_mtu));
- default_buff_args.num_send_frames =
- args.cast<size_t>("num_send_frames",
- default_buff_args.num_send_frames);
- default_buff_args.send_buff_size =
- args.cast<size_t>("send_buff_size",
- default_buff_args.send_buff_size);
- } else if (xport_type == RX_DATA) {
- size_t default_frame_size = conn.link_rate == x300::MAX_RATE_1GIGE
- ? x300::GE_DATA_FRAME_RECV_SIZE
- : x300::XGE_DATA_FRAME_RECV_SIZE;
- default_buff_args.recv_frame_size =
- args.cast<size_t>("recv_frame_size",
- std::min(default_frame_size, recv_mtu));
- // set some buffers so the offload thread actually offloads the
- // socket I/O
- default_buff_args.num_recv_frames =
- args.cast<size_t>("num_recv_frames", 2);
- default_buff_args.recv_buff_size =
- args.cast<size_t>("recv_buff_size",
- default_buff_args.recv_buff_size);
- }
- // make a new transport - fpga has no idea how to talk to us on this yet
- udp_zero_copy::buff_params buff_params;
- xports.recv = udp_zero_copy::make(conn.addr,
- BOOST_STRINGIZE(X300_VITA_UDP_PORT),
- default_buff_args,
- buff_params);
-
- // Create a threaded transport for the receive chain only
- // Note that this shouldn't affect PCIe
- if (xport_type == RX_DATA) {
- xports.recv = zero_copy_recv_offload::make(
- xports.recv, x300::RECV_OFFLOAD_BUFFER_TIMEOUT);
- }
- xports.send = xports.recv;
-
- // For the UDP transport the buffer size is the size of the socket buffer
- // in the kernel
- xports.recv_buff_size = buff_params.recv_buff_size;
- xports.send_buff_size = buff_params.send_buff_size;
-
- // clear the ethernet dispatcher's udp port
- // NOT clearing this, the dispatcher is now intelligent
- //_zpu_ctrl->poke32(SR_ADDR(SET0_BASE, (ZPU_SR_ETHINT0+8+3)), 0);
-
- // send a mini packet with SID into the ZPU
- // ZPU will reprogram the ethernet framer
- UHD_LOGGER_DEBUG("X300") << "programming packet for new xport on " << conn.addr
- << " sid " << xports.send_sid;
- // YES, get a __send__ buffer from the __recv__ socket
- //-- this is the only way to program the framer for recv:
- managed_send_buffer::sptr buff = xports.recv->get_send_buff();
- buff->cast<uint32_t*>()[0] = 0; // eth dispatch looks for != 0
- buff->cast<uint32_t*>()[1] = uhd::htonx(xports.send_sid.get());
- buff->commit(8);
- buff.reset();
-
- // reprogram the ethernet dispatcher's udp port (should be safe to always set)
- UHD_LOGGER_TRACE("X300") << "reprogram the ethernet dispatcher's udp port";
- mb.zpu_ctrl->poke32(
- SR_ADDR(SET0_BASE, (ZPU_SR_ETHINT0 + 8 + 3)), X300_VITA_UDP_PORT);
- mb.zpu_ctrl->poke32(
- SR_ADDR(SET0_BASE, (ZPU_SR_ETHINT1 + 8 + 3)), X300_VITA_UDP_PORT);
-
- // Do a peek to an arbitrary address to guarantee that the
- // ethernet framer has been programmed before we return.
- mb.zpu_ctrl->peek32(0);
+ return xports;
}
- return xports;
+ UHD_THROW_INVALID_CODE_PATH();
}
@@ -1719,177 +813,20 @@ bool x300_impl::is_pps_present(mboard_members_t& mb)
}
/***********************************************************************
- * claimer logic
- **********************************************************************/
-
-void x300_impl::claimer_loop(wb_iface::sptr iface)
-{
- claim(iface);
- std::this_thread::sleep_for(std::chrono::seconds(1));
-}
-
-x300_impl::claim_status_t x300_impl::claim_status(wb_iface::sptr iface)
-{
- claim_status_t claim_status = CLAIMED_BY_OTHER; // Default to most restrictive
- auto timeout_time = std::chrono::steady_clock::now() + std::chrono::seconds(1);
- while (std::chrono::steady_clock::now() < timeout_time) {
- // If timed out, then device is definitely unclaimed
- if (iface->peek32(X300_FW_SHMEM_ADDR(X300_FW_SHMEM_CLAIM_STATUS)) == 0) {
- claim_status = UNCLAIMED;
- break;
- }
-
- // otherwise check claim src to determine if another thread with the same src has
- // claimed the device
- uint32_t hash = iface->peek32(X300_FW_SHMEM_ADDR(X300_FW_SHMEM_CLAIM_SRC));
- if (hash == 0) {
- // A non-zero claim status and an empty hash means the claim might
- // be in the process of being released. This is possible because
- // older firmware takes a long time to update the status. Wait and
- // check status again.
- std::this_thread::sleep_for(std::chrono::milliseconds(5));
- continue;
- }
- claim_status = (hash == get_process_hash() ? CLAIMED_BY_US : CLAIMED_BY_OTHER);
- break;
- }
- return claim_status;
-}
-
-void x300_impl::claim(wb_iface::sptr iface)
-{
- iface->poke32(X300_FW_SHMEM_ADDR(X300_FW_SHMEM_CLAIM_TIME), uint32_t(time(NULL)));
- iface->poke32(X300_FW_SHMEM_ADDR(X300_FW_SHMEM_CLAIM_SRC), get_process_hash());
-}
-
-bool x300_impl::try_to_claim(wb_iface::sptr iface, long timeout_ms)
-{
- const auto timeout_time =
- std::chrono::steady_clock::now() + std::chrono::milliseconds(timeout_ms);
- while (1) {
- claim_status_t status = claim_status(iface);
- if (status == UNCLAIMED) {
- claim(iface);
- // It takes the claimer 10ms to update status, so wait 20ms before verifying
- // claim
- std::this_thread::sleep_for(std::chrono::milliseconds(20));
- continue;
- }
- if (status == CLAIMED_BY_US) {
- break;
- }
- if (std::chrono::steady_clock::now() > timeout_time) {
- // Another process owns the device - give up
- return false;
- }
- std::this_thread::sleep_for(std::chrono::milliseconds(100));
- }
- return true;
-}
-
-void x300_impl::release(wb_iface::sptr iface)
-{
- iface->poke32(X300_FW_SHMEM_ADDR(X300_FW_SHMEM_CLAIM_TIME), 0);
- iface->poke32(X300_FW_SHMEM_ADDR(X300_FW_SHMEM_CLAIM_SRC), 0);
-}
-
-/***********************************************************************
* Frame size detection
**********************************************************************/
-x300_impl::frame_size_t x300_impl::determine_max_frame_size(
- const std::string& addr, const frame_size_t& user_frame_size)
+size_t x300_impl::get_mtu(const size_t mb_index, const uhd::direction_t dir)
{
- udp_simple::sptr udp =
- _x300_make_udp_connected(addr, BOOST_STRINGIZE(X300_MTU_DETECT_UDP_PORT));
-
- std::vector<uint8_t> buffer(
- std::max(user_frame_size.recv_frame_size, user_frame_size.send_frame_size));
- x300_mtu_t* request = reinterpret_cast<x300_mtu_t*>(&buffer.front());
- static const double echo_timeout = 0.020; // 20 ms
-
- // test holler - check if its supported in this fw version
- request->flags = uhd::htonx<uint32_t>(X300_MTU_DETECT_ECHO_REQUEST);
- request->size = uhd::htonx<uint32_t>(sizeof(x300_mtu_t));
- udp->send(boost::asio::buffer(buffer, sizeof(x300_mtu_t)));
- udp->recv(boost::asio::buffer(buffer), echo_timeout);
- if (!(uhd::ntohx<uint32_t>(request->flags) & X300_MTU_DETECT_ECHO_REPLY))
- throw uhd::not_implemented_error("Holler protocol not implemented");
-
- // Reducing range of (min,max) by setting max value to 10gig max_frame_size as larger
- // sizes are not supported
- size_t min_recv_frame_size = sizeof(x300_mtu_t);
- size_t max_recv_frame_size =
- std::min(user_frame_size.recv_frame_size, x300::DATA_FRAME_MAX_SIZE) & size_t(~3);
- size_t min_send_frame_size = sizeof(x300_mtu_t);
- size_t max_send_frame_size =
- std::min(user_frame_size.send_frame_size, x300::DATA_FRAME_MAX_SIZE) & size_t(~3);
-
- UHD_LOGGER_DEBUG("X300") << "Determining maximum frame size... ";
- while (min_recv_frame_size < max_recv_frame_size) {
- size_t test_frame_size = (max_recv_frame_size / 2 + min_recv_frame_size / 2 + 3)
- & ~3;
-
- request->flags = uhd::htonx<uint32_t>(X300_MTU_DETECT_ECHO_REQUEST);
- request->size = uhd::htonx<uint32_t>(test_frame_size);
- udp->send(boost::asio::buffer(buffer, sizeof(x300_mtu_t)));
-
- size_t len = udp->recv(boost::asio::buffer(buffer), echo_timeout);
-
- if (len >= test_frame_size)
- min_recv_frame_size = test_frame_size;
- else
- max_recv_frame_size = test_frame_size - 4;
- }
-
- if (min_recv_frame_size < IP_PROTOCOL_MIN_MTU_SIZE - IP_PROTOCOL_UDP_PLUS_IP_HEADER) {
- throw uhd::runtime_error("System receive MTU size is less than the minimum "
- "required by the IP protocol.");
- }
-
- while (min_send_frame_size < max_send_frame_size) {
- size_t test_frame_size = (max_send_frame_size / 2 + min_send_frame_size / 2 + 3)
- & ~3;
-
- request->flags = uhd::htonx<uint32_t>(X300_MTU_DETECT_ECHO_REQUEST);
- request->size = uhd::htonx<uint32_t>(sizeof(x300_mtu_t));
- udp->send(boost::asio::buffer(buffer, test_frame_size));
-
- size_t len = udp->recv(boost::asio::buffer(buffer), echo_timeout);
- if (len >= sizeof(x300_mtu_t))
- len = uhd::ntohx<uint32_t>(request->size);
-
- if (len >= test_frame_size)
- min_send_frame_size = test_frame_size;
- else
- max_send_frame_size = test_frame_size - 4;
- }
-
- if (min_send_frame_size < IP_PROTOCOL_MIN_MTU_SIZE - IP_PROTOCOL_UDP_PLUS_IP_HEADER) {
- throw uhd::runtime_error(
- "System send MTU size is less than the minimum required by the IP protocol.");
+ auto& mb = _mb.at(mb_index);
+ if (mb.xport_path == xport_path_t::NIRIO) {
+ return mb.pcie_mgr->get_mtu(dir);
}
-
- frame_size_t frame_size;
- // There are cases when NICs accept oversized packets, in which case we'd falsely
- // detect a larger-than-possible frame size. A safe and sensible value is the minimum
- // of the recv and send frame sizes.
- frame_size.recv_frame_size = std::min(min_recv_frame_size, min_send_frame_size);
- frame_size.send_frame_size = std::min(min_recv_frame_size, min_send_frame_size);
- UHD_LOGGER_INFO("X300") << "Maximum frame size: " << frame_size.send_frame_size
- << " bytes.";
- return frame_size;
-}
-
-size_t x300_impl::get_mtu(const size_t /*mb_index*/, const uhd::direction_t dir)
-{
- return (dir == RX_DIRECTION) ? _max_frame_sizes.recv_frame_size :
- _max_frame_sizes.send_frame_size;
+ return mb.eth_mgr->get_mtu(dir);
}
/***********************************************************************
* compat checks
**********************************************************************/
-
void x300_impl::check_fw_compat(const fs_path& mb_path, const mboard_members_t& members)
{
auto iface = members.zpu_ctrl;
@@ -1901,10 +838,11 @@ void x300_impl::check_fw_compat(const fs_path& mb_path, const mboard_members_t&
if (compat_major != X300_FW_COMPAT_MAJOR) {
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\" --args=\"type=x300,%s=%s\"") % image_loader_path
- % (members.xport_path == "eth" ? "addr" : "resource")
- % members.get_pri_eth().addr);
+ const std::string image_loader_cmd = str(
+ boost::format("\"%s\" --args=\"type=x300,%s=%s\"") % image_loader_path
+ % (members.xport_path == xport_path_t::ETH ? "addr" : "resource")
+ % (members.xport_path == xport_path_t::ETH ? members.args.get_first_addr()
+ : members.args.get_resource()));
throw uhd::runtime_error(
str(boost::format(
@@ -1934,10 +872,11 @@ void x300_impl::check_fpga_compat(const fs_path& mb_path, const mboard_members_t
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.get_pri_eth().addr);
+ std::string image_loader_cmd = str(
+ boost::format("\"%s\" --args=\"type=x300,%s=%s\"") % image_loader_path
+ % (members.xport_path == xport_path_t::ETH ? "addr" : "resource")
+ % (members.xport_path == xport_path_t::ETH ? members.args.get_first_addr()
+ : members.args.get_resource()));
throw uhd::runtime_error(
str(boost::format(
@@ -1967,24 +906,3 @@ void x300_impl::check_fpga_compat(const fs_path& mb_path, const mboard_members_t
<< " git hash: " << git_hash_str);
}
-x300_mboard_t x300_impl::get_mb_type_from_pcie(
- const std::string& resource, const std::string& rpc_port)
-{
- // Detect the PCIe product ID to distinguish between X300 and X310
- nirio_status status = NiRio_Status_Success;
- uint32_t pid;
- niriok_proxy::sptr discovery_proxy =
- niusrprio_session::create_kernel_proxy(resource, rpc_port);
- if (discovery_proxy) {
- nirio_status_chain(
- discovery_proxy->get_attribute(RIO_PRODUCT_NUMBER, pid), status);
- discovery_proxy->close();
- if (nirio_status_not_fatal(status)) {
- return map_pid_to_mb_type(pid);
- }
- }
-
- UHD_LOGGER_WARNING("X300") << "NI-RIO Error -- unable to determine motherboard type!";
- return UNKNOWN;
-}
-
diff --git a/host/lib/usrp/x300/x300_impl.hpp b/host/lib/usrp/x300/x300_impl.hpp
index d8bd23592..933006eab 100644
--- a/host/lib/usrp/x300/x300_impl.hpp
+++ b/host/lib/usrp/x300/x300_impl.hpp
@@ -1,6 +1,7 @@
//
// Copyright 2013-2016 Ettus Research LLC
// Copyright 2018 Ettus Research, a National Instruments Company
+// Copyright 2019 Ettus Research, a National Instruments Brand
//
// SPDX-License-Identifier: GPL-3.0-or-later
//
@@ -8,81 +9,33 @@
#ifndef INCLUDED_X300_IMPL_HPP
#define INCLUDED_X300_IMPL_HPP
-#include "../device3/device3_impl.hpp"
#include "x300_clock_ctrl.hpp"
#include "x300_defaults.hpp"
#include "x300_device_args.hpp"
+#include "x300_eth_mgr.hpp"
#include "x300_fw_common.h"
#include "x300_mboard_type.hpp"
+#include "x300_pcie_mgr.hpp"
#include "x300_radio_ctrl_impl.hpp"
#include "x300_regs.hpp"
-#include <uhd/property_tree.hpp>
-#include <uhd/transport/muxed_zero_copy_if.hpp>
-#include <uhd/transport/nirio/niusrprio_session.h>
-#include <uhd/transport/udp_simple.hpp> //mtu
-#include <uhd/transport/vrt_if_packet.hpp>
+#include <uhd/types/device_addr.hpp>
#include <uhd/types/sensors.hpp>
+#include <uhd/types/wb_iface.hpp>
#include <uhd/usrp/gps_ctrl.hpp>
-#include <uhd/usrp/mboard_eeprom.hpp>
#include <uhd/usrp/subdev_spec.hpp>
-///////////// RFNOC /////////////////////
-#include <uhd/rfnoc/block_ctrl.hpp>
-///////////// RFNOC /////////////////////
-
-#include <uhdlib/usrp/common/recv_packet_demuxer_3000.hpp>
#include <uhdlib/usrp/cores/i2c_core_100_wb32.hpp>
-#include <boost/dynamic_bitset.hpp>
-#include <boost/weak_ptr.hpp>
#include <atomic>
-#include <functional>
-
-// Ethernet ports
-enum x300_eth_iface_t {
- X300_IFACE_NONE = 0,
- X300_IFACE_ETH0 = 1,
- X300_IFACE_ETH1 = 2,
-};
-
-struct x300_eth_conn_t
-{
- std::string addr;
- x300_eth_iface_t type;
- size_t link_rate;
-};
-
-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, bool enable_errors = true);
-uhd::wb_iface::sptr x300_make_ctrl_iface_pcie(
- uhd::niusrprio::niriok_proxy::sptr drv_proxy, bool enable_errors = true);
+#include <memory>
uhd::device_addrs_t x300_find(const uhd::device_addr_t& hint_);
class x300_impl : public uhd::usrp::device3_impl
{
public:
- //! Function to create a udp_simple::sptr (kernel-based or DPDK-based)
- using udp_simple_factory_t =
- std::function<uhd::transport::udp_simple::sptr(const std::string&, const std::string&)>;
-
x300_impl(const uhd::device_addr_t&);
void setup_mb(const size_t which, const uhd::device_addr_t&);
~x300_impl(void);
- // device claim functions
- enum claim_status_t { UNCLAIMED, CLAIMED_BY_US, CLAIMED_BY_OTHER };
- static claim_status_t claim_status(uhd::wb_iface::sptr iface);
- static void claim(uhd::wb_iface::sptr iface);
- static bool try_to_claim(uhd::wb_iface::sptr iface, long timeout = 2000);
- static void release(uhd::wb_iface::sptr iface);
-
- static uhd::usrp::x300::x300_mboard_t get_mb_type_from_pcie(
- const std::string& resource, const std::string& rpc_port);
-
- //! Read out the on-board EEPROM, convert to dict, and return
- static uhd::usrp::mboard_eeprom_t get_mb_eeprom(uhd::i2c_iface::sptr i2c);
-
protected:
void subdev_to_blockid(const uhd::usrp::subdev_spec_pair_t& spec,
const size_t mb_i,
@@ -97,29 +50,11 @@ private:
{
uhd::usrp::x300::x300_device_args_t args;
- bool initialization_done;
+ bool initialization_done = false;
uhd::task::sptr claimer_task;
- std::string xport_path;
-
- std::vector<x300_eth_conn_t> eth_conns;
- size_t next_src_addr;
- size_t next_tx_src_addr;
- size_t next_rx_src_addr;
-
- // Discover the ethernet connections per motherboard
- void discover_eth(const uhd::usrp::mboard_eeprom_t mb_eeprom,
- const std::vector<std::string>& ip_addrs);
-
- // Get the primary ethernet connection
- inline const x300_eth_conn_t& get_pri_eth() const
- {
- return eth_conns[0];
- }
-
+ uhd::usrp::x300::xport_path_t xport_path;
uhd::device_addr_t send_args;
uhd::device_addr_t recv_args;
- bool if_pkt_is_big_endian;
- uhd::niusrprio::niusrprio_session::sptr rio_fpga_interface;
// perifs in the zpu
uhd::wb_iface::sptr zpu_ctrl;
@@ -140,27 +75,14 @@ private:
std::vector<uhd::rfnoc::x300_radio_ctrl_impl::sptr> radios;
- // PCIe specific components:
-
- //! Maps SID -> DMA channel
- std::map<uint32_t, uint32_t> _dma_chan_pool;
- //! Control transport for one PCIe connection
- uhd::transport::muxed_zero_copy_if::sptr ctrl_dma_xport;
- //! Async message transport
- uhd::transport::muxed_zero_copy_if::sptr async_msg_dma_xport;
-
- /*! Allocate or return a previously allocated PCIe channel pair
- *
- * Note the SID is always the transmit SID (i.e. from host to device).
- */
- uint32_t allocate_pcie_dma_chan(
- const uhd::sid_t& tx_sid, const xport_type_t xport_type);
+ // Ethernet-specific components:
+ std::unique_ptr<uhd::usrp::x300::eth_manager> eth_mgr;
+
+ // PCIe-specific components:
+ std::unique_ptr<uhd::usrp::x300::pcie_manager> pcie_mgr;
};
std::vector<mboard_members_t> _mb;
- // task for periodically reclaiming the device from others
- void claimer_loop(uhd::wb_iface::sptr);
-
std::atomic<size_t> _sid_framer;
uhd::sid_t allocate_sid(mboard_members_t& mb,
@@ -174,44 +96,6 @@ private:
//! get mtu
size_t get_mtu(const size_t, const uhd::direction_t);
- struct frame_size_t
- {
- size_t recv_frame_size;
- size_t send_frame_size;
- };
- frame_size_t _max_frame_sizes;
-
- /*!
- * Automatically determine the maximum frame size available by sending a UDP packet
- * to the device and see which packet sizes actually work. This way, we can take
- * switches etc. into account which might live between the device and the host.
- */
- frame_size_t determine_max_frame_size(
- const std::string& addr, const frame_size_t& user_mtu);
-
- ////////////////////////////////////////////////////////////////////
- //
- // Caching for transport interface re-use -- like sharing a DMA.
- // The cache is optionally used by make_transport by use-case.
- // The cache maps an ID string to a transport-ish object.
- // The ID string identifies a purpose for the transport.
- //
- // For recv, there is a demux cache, which maps a ID string
- // to a recv demux object. When a demux is used, the underlying transport
- // must never be used outside of the demux. Use demux->make_proxy(sid).
- //
- uhd::dict<std::string, uhd::usrp::recv_packet_demuxer_3000::sptr> _demux_cache;
- //
- // For send, there is a shared send xport, which maps an ID string
- // to a transport capable of sending buffers. Send transports
- // can be shared amongst multiple callers, unlike recv.
- //
- uhd::dict<std::string, uhd::transport::zero_copy_if::sptr> _send_cache;
- //
- ////////////////////////////////////////////////////////////////////
-
- uhd::dict<std::string, uhd::usrp::dboard_manager::sptr> _dboard_managers;
-
bool _ignore_cal_file;
void update_clock_control(mboard_members_t&);
@@ -225,9 +109,6 @@ private:
bool wait_for_clk_locked(mboard_members_t& mb, uint32_t which, double timeout);
bool is_pps_present(mboard_members_t& mb);
- //! Write the contents of an EEPROM dict to the on-board EEPROM
- 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, const mboard_members_t& members);
void check_fpga_compat(const uhd::fs_path& mb_path, const mboard_members_t& members);
@@ -236,8 +117,6 @@ private:
uhd::device_addr_t get_rx_hints(size_t mb_index);
void post_streamer_hooks(uhd::direction_t dir);
-
- udp_simple_factory_t _x300_make_udp_connected;
};
#endif /* INCLUDED_X300_IMPL_HPP */
diff --git a/host/lib/usrp/x300/x300_mb_eeprom.cpp b/host/lib/usrp/x300/x300_mb_eeprom.cpp
index 663f4c9db..54505a43d 100644
--- a/host/lib/usrp/x300/x300_mb_eeprom.cpp
+++ b/host/lib/usrp/x300/x300_mb_eeprom.cpp
@@ -4,7 +4,7 @@
// SPDX-License-Identifier: GPL-3.0-or-later
//
-#include "x300_impl.hpp"
+#include "x300_mb_eeprom.hpp"
#include <uhd/types/serial.hpp>
#include <uhd/usrp/mboard_eeprom.hpp>
#include <uhdlib/utils/eeprom_utils.hpp>
@@ -41,7 +41,7 @@ struct x300_eeprom_map
using namespace uhd;
using uhd::usrp::mboard_eeprom_t;
-mboard_eeprom_t x300_impl::get_mb_eeprom(uhd::i2c_iface::sptr iface)
+mboard_eeprom_t uhd::usrp::x300::get_mb_eeprom(uhd::i2c_iface::sptr iface)
{
byte_vector_t bytes =
iface->read_eeprom(X300_EEPROM_ADDR, 0, sizeof(struct x300_eeprom_map));
@@ -110,9 +110,10 @@ mboard_eeprom_t x300_impl::get_mb_eeprom(uhd::i2c_iface::sptr iface)
return mb_eeprom;
}
-void x300_impl::set_mb_eeprom(i2c_iface::sptr iface, const mboard_eeprom_t& mb_eeprom)
+void uhd::usrp::x300::set_mb_eeprom(
+ i2c_iface::sptr iface, const mboard_eeprom_t& mb_eeprom)
{
- const mboard_eeprom_t curr_eeprom = get_mb_eeprom(iface);
+ const mboard_eeprom_t curr_eeprom = uhd::usrp::x300::get_mb_eeprom(iface);
// Check for duplicate MAC and IP addresses
const std::vector<std::string> mac_keys{"mac-addr0", "mac-addr1"};
diff --git a/host/lib/usrp/x300/x300_mb_eeprom.hpp b/host/lib/usrp/x300/x300_mb_eeprom.hpp
new file mode 100644
index 000000000..74e78fda0
--- /dev/null
+++ b/host/lib/usrp/x300/x300_mb_eeprom.hpp
@@ -0,0 +1,24 @@
+//
+// Copyright 2019 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#ifndef INCLUDED_X300_EEPROM_HPP
+#define INCLUDED_X300_EEPROM_HPP
+
+#include <uhd/types/serial.hpp>
+#include <uhd/usrp/mboard_eeprom.hpp>
+
+namespace uhd { namespace usrp { namespace x300 {
+
+//! Read out the on-board EEPROM, convert to dict, and return
+uhd::usrp::mboard_eeprom_t get_mb_eeprom(uhd::i2c_iface::sptr i2c);
+
+//! Write the contents of an EEPROM dict to the on-board EEPROM
+void set_mb_eeprom(
+ uhd::i2c_iface::sptr iface, const uhd::usrp::mboard_eeprom_t& mb_eeprom);
+
+}}} // namespace uhd::usrp::x300
+
+#endif /* INCLUDED_X300_EEPROM_HPP */
diff --git a/host/lib/usrp/x300/x300_mb_eeprom_iface.cpp b/host/lib/usrp/x300/x300_mb_eeprom_iface.cpp
index 12022ec24..a54caa560 100644
--- a/host/lib/usrp/x300/x300_mb_eeprom_iface.cpp
+++ b/host/lib/usrp/x300/x300_mb_eeprom_iface.cpp
@@ -18,6 +18,7 @@
*/
#include "x300_mb_eeprom_iface.hpp"
+#include "x300_claim.hpp"
#include "x300_fw_common.h"
#include "x300_impl.hpp"
#include "x300_regs.hpp"
@@ -52,7 +53,7 @@ public:
void write_i2c(uint16_t addr, const byte_vector_t& buf)
{
UHD_ASSERT_THROW(addr == MBOARD_EEPROM_ADDR);
- if (x300_impl::claim_status(_wb) != x300_impl::CLAIMED_BY_US) {
+ if (uhd::usrp::x300::claim_status(_wb) != uhd::usrp::x300::CLAIMED_BY_US) {
throw uhd::io_error("Attempted to write MB EEPROM without claim to device.");
}
_i2c->write_i2c(addr, buf);
@@ -71,13 +72,14 @@ public:
if (_compat_num > X300_FW_SHMEM_IDENT_MIN_VERSION) {
bytes = read_eeprom(addr, 0, num_bytes);
} else {
- x300_impl::claim_status_t status = x300_impl::claim_status(_wb);
+ auto status = uhd::usrp::x300::claim_status(_wb);
// Claim device before driving the I2C bus
- if (status == x300_impl::CLAIMED_BY_US or x300_impl::try_to_claim(_wb)) {
+ if (status == uhd::usrp::x300::CLAIMED_BY_US
+ or uhd::usrp::x300::try_to_claim(_wb)) {
bytes = _i2c->read_i2c(addr, num_bytes);
- if (status != x300_impl::CLAIMED_BY_US) {
+ if (status != uhd::usrp::x300::CLAIMED_BY_US) {
// We didn't originally have the claim, so give it up
- x300_impl::release(_wb);
+ uhd::usrp::x300::release(_wb);
}
}
}
@@ -93,7 +95,7 @@ public:
void write_eeprom(uint16_t addr, uint16_t offset, const byte_vector_t& buf)
{
UHD_ASSERT_THROW(addr == MBOARD_EEPROM_ADDR);
- if (x300_impl::claim_status(_wb) != x300_impl::CLAIMED_BY_US) {
+ if (uhd::usrp::x300::claim_status(_wb) != uhd::usrp::x300::CLAIMED_BY_US) {
throw uhd::io_error("Attempted to write MB EEPROM without claim to device.");
}
_i2c->write_eeprom(addr, offset, buf);
@@ -110,7 +112,7 @@ public:
{
UHD_ASSERT_THROW(addr == MBOARD_EEPROM_ADDR);
byte_vector_t bytes;
- x300_impl::claim_status_t status = x300_impl::claim_status(_wb);
+ uhd::usrp::x300::claim_status_t status = uhd::usrp::x300::claim_status(_wb);
if (_compat_num >= X300_FW_SHMEM_IDENT_MIN_VERSION) {
// Get MB EEPROM data from firmware memory
if (num_bytes == 0)
@@ -128,11 +130,12 @@ public:
}
} else {
// Claim device before driving the I2C bus
- if (status == x300_impl::CLAIMED_BY_US or x300_impl::try_to_claim(_wb)) {
+ if (status == uhd::usrp::x300::CLAIMED_BY_US
+ or uhd::usrp::x300::try_to_claim(_wb)) {
bytes = _i2c->read_eeprom(addr, offset, num_bytes);
- if (status != x300_impl::CLAIMED_BY_US) {
+ if (status != uhd::usrp::x300::CLAIMED_BY_US) {
// We didn't originally have the claim, so give it up
- x300_impl::release(_wb);
+ uhd::usrp::x300::release(_wb);
}
}
}
diff --git a/host/lib/usrp/x300/x300_mboard_type.hpp b/host/lib/usrp/x300/x300_mboard_type.hpp
index 0a83aaf4d..d218a142f 100644
--- a/host/lib/usrp/x300/x300_mboard_type.hpp
+++ b/host/lib/usrp/x300/x300_mboard_type.hpp
@@ -13,6 +13,8 @@
namespace uhd { namespace usrp { namespace x300 {
+enum class xport_path_t { ETH, NIRIO };
+
enum x300_mboard_t { USRP_X300_MB, USRP_X310_MB, USRP_X310_MB_NI_2974, UNKNOWN };
/*! Return the correct motherboard type for a given product ID
diff --git a/host/lib/usrp/x300/x300_pcie_mgr.cpp b/host/lib/usrp/x300/x300_pcie_mgr.cpp
new file mode 100644
index 000000000..47095b370
--- /dev/null
+++ b/host/lib/usrp/x300/x300_pcie_mgr.cpp
@@ -0,0 +1,390 @@
+//
+// Copyright 2019 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#include "x300_pcie_mgr.hpp"
+#include "x300_claim.hpp"
+#include "x300_lvbitx.hpp"
+#include "x300_mb_eeprom.hpp"
+#include "x300_mb_eeprom_iface.hpp"
+#include "x300_mboard_type.hpp"
+#include "x300_regs.hpp"
+#include "x310_lvbitx.hpp"
+#include <uhd/transport/nirio_zero_copy.hpp>
+#include <uhd/transport/zero_copy.hpp>
+#include <uhd/types/device_addr.hpp>
+#include <uhd/utils/byteswap.hpp>
+#include <uhd/utils/log.hpp>
+#include <uhd/utils/static.hpp>
+#include <uhdlib/usrp/cores/i2c_core_100_wb32.hpp>
+#include <unordered_map>
+#include <mutex>
+
+namespace {
+
+uint32_t extract_sid_from_pkt(void* pkt, size_t)
+{
+ return uhd::sid_t(uhd::wtohx(static_cast<const uint32_t*>(pkt)[1])).get_dst();
+}
+
+constexpr uint32_t RADIO_DEST_PREFIX_TX = 0;
+
+// The FIFO closest to the DMA controller is 1023 elements deep for RX and 1029 elements
+// deep for TX where an element is 8 bytes. The buffers (number of frames * frame size)
+// must be aligned to the memory page size. For the control, we are getting lucky because
+// 64 frames * 256 bytes each aligns with the typical page size of 4096 bytes. Since most
+// page sizes are 4096 bytes or some multiple of that, keep the number of frames * frame
+// size aligned to it.
+constexpr size_t PCIE_RX_DATA_FRAME_SIZE = 4096; // bytes
+constexpr size_t PCIE_RX_DATA_NUM_FRAMES = 4096;
+constexpr size_t PCIE_TX_DATA_FRAME_SIZE = 4096; // bytes
+constexpr size_t PCIE_TX_DATA_NUM_FRAMES = 4096;
+constexpr size_t PCIE_MSG_FRAME_SIZE = 256; // bytes
+constexpr size_t PCIE_MSG_NUM_FRAMES = 64;
+constexpr size_t PCIE_MAX_MUXED_CTRL_XPORTS = 32;
+constexpr size_t PCIE_MAX_MUXED_ASYNC_XPORTS = 4;
+constexpr size_t PCIE_MAX_CHANNELS = 6;
+constexpr size_t MAX_RATE_PCIE = 800000000; // bytes/s
+}
+
+uhd::wb_iface::sptr x300_make_ctrl_iface_pcie(
+ uhd::niusrprio::niriok_proxy::sptr drv_proxy, bool enable_errors = true);
+
+using namespace uhd;
+using namespace uhd::transport;
+using namespace uhd::usrp::x300;
+using namespace uhd::niusrprio;
+
+// We need a zpu xport registry to ensure synchronization between the static
+// finder method and the instances of the x300_impl class.
+typedef std::unordered_map<std::string, boost::weak_ptr<uhd::wb_iface>>
+ pcie_zpu_iface_registry_t;
+UHD_SINGLETON_FCN(pcie_zpu_iface_registry_t, get_pcie_zpu_iface_registry)
+static std::mutex pcie_zpu_iface_registry_mutex;
+
+
+/******************************************************************************
+ * Static methods
+ *****************************************************************************/
+x300_mboard_t pcie_manager::get_mb_type_from_pcie(
+ const std::string& resource, const std::string& rpc_port)
+{
+ // Detect the PCIe product ID to distinguish between X300 and X310
+ nirio_status status = NiRio_Status_Success;
+ uint32_t pid;
+ niriok_proxy::sptr discovery_proxy =
+ niusrprio_session::create_kernel_proxy(resource, rpc_port);
+ if (discovery_proxy) {
+ nirio_status_chain(
+ discovery_proxy->get_attribute(RIO_PRODUCT_NUMBER, pid), status);
+ discovery_proxy->close();
+ if (nirio_status_not_fatal(status)) {
+ return map_pid_to_mb_type(pid);
+ }
+ }
+
+ UHD_LOG_WARNING("X300", "NI-RIO Error -- unable to determine motherboard type!");
+ return UNKNOWN;
+}
+
+/******************************************************************************
+ * Find
+ *****************************************************************************/
+device_addrs_t pcie_manager::find(const device_addr_t& hint, bool explicit_query)
+{
+ std::string rpc_port_name(std::to_string(NIUSRPRIO_DEFAULT_RPC_PORT));
+ if (hint.has_key("niusrpriorpc_port")) {
+ rpc_port_name = hint["niusrpriorpc_port"];
+ }
+
+ device_addrs_t addrs;
+ niusrprio_session::device_info_vtr dev_info_vtr;
+ nirio_status status = niusrprio_session::enumerate(rpc_port_name, dev_info_vtr);
+ if (explicit_query) {
+ nirio_status_to_exception(
+ status, "x300::pcie_manager::find: Error enumerating NI-RIO devices.");
+ }
+
+ for (niusrprio_session::device_info& dev_info : dev_info_vtr) {
+ device_addr_t new_addr;
+ new_addr["type"] = "x300";
+ new_addr["resource"] = dev_info.resource_name;
+ std::string resource_d(dev_info.resource_name);
+ boost::to_upper(resource_d);
+
+ const std::string product_name =
+ map_mb_type_to_product_name(get_mb_type_from_pcie(resource_d, rpc_port_name));
+ if (product_name.empty()) {
+ continue;
+ } else {
+ new_addr["product"] = product_name;
+ }
+
+ niriok_proxy::sptr kernel_proxy =
+ niriok_proxy::make_and_open(dev_info.interface_path);
+
+ // Attempt to read the name from the EEPROM and perform filtering.
+ // This operation can throw due to compatibility mismatch.
+ try {
+ // This block could throw an exception if the user is switching to using UHD
+ // after LabVIEW FPGA. In that case, skip reading the name and serial and pick
+ // a default FPGA flavor. During make, a new image will be loaded and
+ // everything will be OK
+
+ wb_iface::sptr zpu_ctrl;
+
+ // Hold on to the registry mutex as long as zpu_ctrl is alive
+ // to prevent any use by different threads while enumerating
+ std::lock_guard<std::mutex> lock(pcie_zpu_iface_registry_mutex);
+
+ if (get_pcie_zpu_iface_registry().count(resource_d)) {
+ zpu_ctrl = get_pcie_zpu_iface_registry()[resource_d].lock();
+ if (!zpu_ctrl) {
+ get_pcie_zpu_iface_registry().erase(resource_d);
+ }
+ }
+
+ // if the registry didn't have a key OR that key was an orphaned weak_ptr
+ if (!zpu_ctrl) {
+ zpu_ctrl = x300_make_ctrl_iface_pcie(
+ kernel_proxy, false /* suppress timeout errors */);
+ // We don't put this zpu_ctrl in the registry because we need
+ // a persistent niriok_proxy associated with the object
+ }
+
+ // Attempt to autodetect the FPGA type
+ if (not hint.has_key("fpga")) {
+ new_addr["fpga"] = get_fpga_option(zpu_ctrl);
+ }
+
+ i2c_core_100_wb32::sptr zpu_i2c =
+ i2c_core_100_wb32::make(zpu_ctrl, I2C1_BASE);
+ x300_mb_eeprom_iface::sptr eeprom_iface =
+ x300_mb_eeprom_iface::make(zpu_ctrl, zpu_i2c);
+ const mboard_eeprom_t mb_eeprom = get_mb_eeprom(eeprom_iface);
+ if (mb_eeprom.size() == 0 or claim_status(zpu_ctrl) == CLAIMED_BY_OTHER) {
+ // Skip device claimed by another process
+ continue;
+ }
+ new_addr["name"] = mb_eeprom["name"];
+ new_addr["serial"] = mb_eeprom["serial"];
+ } catch (const std::exception&) {
+ // set these values as empty string so the device may still be found
+ // and the filter's below can still operate on the discovered device
+ if (not hint.has_key("fpga")) {
+ new_addr["fpga"] = "HG";
+ }
+ new_addr["name"] = "";
+ new_addr["serial"] = "";
+ }
+
+ // filter the discovered device below by matching optional keys
+ std::string resource_i = hint.has_key("resource") ? hint["resource"] : "";
+ boost::to_upper(resource_i);
+
+ if ((not hint.has_key("resource") or resource_i == resource_d)
+ and (not hint.has_key("name") or hint["name"] == new_addr["name"])
+ and (not hint.has_key("serial") or hint["serial"] == new_addr["serial"])
+ and (not hint.has_key("product") or hint["product"] == new_addr["product"])) {
+ addrs.push_back(new_addr);
+ }
+ }
+ return addrs;
+}
+
+
+/******************************************************************************
+ * Structors
+ *****************************************************************************/
+pcie_manager::pcie_manager(const x300_device_args_t& args,
+ uhd::property_tree::sptr tree,
+ const uhd::fs_path& root_path)
+ : _args(args), _resource(args.get_resource())
+{
+ nirio_status status = 0;
+
+ const std::string rpc_port_name = args.get_niusrprio_rpc_port();
+ UHD_LOG_INFO(
+ "X300", "Connecting to niusrpriorpc at localhost:" << rpc_port_name << "...");
+
+ // Instantiate the correct lvbitx object
+ nifpga_lvbitx::sptr lvbitx;
+ switch (get_mb_type_from_pcie(args.get_resource(), rpc_port_name)) {
+ case USRP_X300_MB:
+ lvbitx.reset(new x300_lvbitx(args.get_fpga_option()));
+ break;
+ case USRP_X310_MB:
+ case USRP_X310_MB_NI_2974:
+ lvbitx.reset(new x310_lvbitx(args.get_fpga_option()));
+ break;
+ default:
+ nirio_status_to_exception(
+ status, "Motherboard detection error. Please ensure that you \
+ have a valid USRP X3x0, NI USRP-294xR, NI USRP-295xR or NI USRP-2974 device and that all the device \
+ drivers have loaded successfully.");
+ }
+ // Load the lvbitx onto the device
+ UHD_LOG_INFO("X300", "Using LVBITX bitfile " << lvbitx->get_bitfile_path());
+ _rio_fpga_interface.reset(new niusrprio_session(args.get_resource(), rpc_port_name));
+ nirio_status_chain(
+ _rio_fpga_interface->open(lvbitx, args.get_download_fpga()), status);
+ nirio_status_to_exception(status, "x300_impl: Could not initialize RIO session.");
+
+ // Tell the quirks object which FIFOs carry TX stream data
+ const uint32_t tx_data_fifos[2] = {RADIO_DEST_PREFIX_TX, RADIO_DEST_PREFIX_TX + 3};
+ _rio_fpga_interface->get_kernel_proxy()->get_rio_quirks().register_tx_streams(
+ tx_data_fifos, 2);
+
+ tree->create<size_t>(root_path / "mtu/recv").set(PCIE_RX_DATA_FRAME_SIZE);
+ tree->create<size_t>(root_path / "mtu/send").set(PCIE_TX_DATA_FRAME_SIZE);
+ tree->create<double>(root_path / "link_max_rate").set(MAX_RATE_PCIE);
+}
+
+/******************************************************************************
+ * API
+ *****************************************************************************/
+wb_iface::sptr pcie_manager::get_ctrl_iface()
+{
+ std::lock_guard<std::mutex> lock(pcie_zpu_iface_registry_mutex);
+ if (get_pcie_zpu_iface_registry().count(_resource)) {
+ throw uhd::assertion_error(
+ "Someone else has a ZPU transport to the device open. Internal error!");
+ }
+ auto zpu_ctrl = x300_make_ctrl_iface_pcie(_rio_fpga_interface->get_kernel_proxy());
+ get_pcie_zpu_iface_registry()[_resource] = boost::weak_ptr<wb_iface>(zpu_ctrl);
+ return zpu_ctrl;
+}
+
+void pcie_manager::init_link() {}
+
+size_t pcie_manager::get_mtu(uhd::direction_t dir)
+{
+ return dir == uhd::RX_DIRECTION ? PCIE_RX_DATA_FRAME_SIZE : PCIE_TX_DATA_FRAME_SIZE;
+}
+
+void pcie_manager::release_ctrl_iface(std::function<void(void)>&& release_fn)
+{
+ std::lock_guard<std::mutex> lock(pcie_zpu_iface_registry_mutex);
+ release_fn();
+ // If the process is killed, the entire registry will disappear so we
+ // don't need to worry about unclean shutdowns here.
+ if (get_pcie_zpu_iface_registry().count(_resource)) {
+ get_pcie_zpu_iface_registry().erase(_resource);
+ }
+}
+
+uint32_t pcie_manager::allocate_pcie_dma_chan(
+ const uhd::sid_t& tx_sid, const uhd::usrp::device3_impl::xport_type_t xport_type)
+{
+ constexpr uint32_t CTRL_CHANNEL = 0;
+ constexpr uint32_t ASYNC_MSG_CHANNEL = 1;
+ constexpr uint32_t FIRST_DATA_CHANNEL = 2;
+ if (xport_type == uhd::usrp::device3_impl::CTRL) {
+ return CTRL_CHANNEL;
+ } else if (xport_type == uhd::usrp::device3_impl::ASYNC_MSG) {
+ return ASYNC_MSG_CHANNEL;
+ } else {
+ // sid_t has no comparison defined, so we need to convert it uint32_t
+ uint32_t raw_sid = tx_sid.get();
+
+ if (_dma_chan_pool.count(raw_sid) == 0) {
+ size_t channel = _dma_chan_pool.size() + FIRST_DATA_CHANNEL;
+ if (channel > PCIE_MAX_CHANNELS) {
+ throw uhd::runtime_error(
+ "Trying to allocate more DMA channels than are available");
+ }
+ _dma_chan_pool[raw_sid] = channel;
+ UHD_LOGGER_DEBUG("X300")
+ << "Assigning PCIe DMA channel " << _dma_chan_pool[raw_sid] << " to SID "
+ << tx_sid.to_pp_string_hex();
+ }
+
+ return _dma_chan_pool[raw_sid];
+ }
+}
+
+muxed_zero_copy_if::sptr pcie_manager::make_muxed_pcie_msg_xport(
+ uint32_t dma_channel_num, size_t max_muxed_ports)
+{
+ zero_copy_xport_params buff_args;
+ buff_args.send_frame_size = PCIE_MSG_FRAME_SIZE;
+ buff_args.recv_frame_size = PCIE_MSG_FRAME_SIZE;
+ buff_args.num_send_frames = PCIE_MSG_NUM_FRAMES;
+ buff_args.num_recv_frames = PCIE_MSG_NUM_FRAMES;
+
+ zero_copy_if::sptr base_xport = nirio_zero_copy::make(
+ _rio_fpga_interface, dma_channel_num, buff_args, uhd::device_addr_t());
+ return muxed_zero_copy_if::make(base_xport, extract_sid_from_pkt, max_muxed_ports);
+}
+
+both_xports_t pcie_manager::make_transport(both_xports_t xports,
+ const uhd::usrp::device3_impl::xport_type_t xport_type,
+ const uhd::device_addr_t& args,
+ const size_t send_mtu,
+ const size_t recv_mtu)
+{
+ zero_copy_xport_params default_buff_args;
+ xports.endianness = ENDIANNESS_LITTLE;
+ xports.lossless = true;
+ const uint32_t dma_channel_num = allocate_pcie_dma_chan(xports.send_sid, xport_type);
+ if (xport_type == uhd::usrp::device3_impl::CTRL) {
+ // Transport for control stream
+ if (not _ctrl_dma_xport) {
+ // One underlying DMA channel will handle
+ // all control traffic
+ _ctrl_dma_xport =
+ make_muxed_pcie_msg_xport(dma_channel_num, PCIE_MAX_MUXED_CTRL_XPORTS);
+ }
+ // Create a virtual control transport
+ xports.recv = _ctrl_dma_xport->make_stream(xports.recv_sid.get_dst());
+ } else if (xport_type == uhd::usrp::device3_impl::ASYNC_MSG) {
+ // Transport for async message stream
+ if (not _async_msg_dma_xport) {
+ // One underlying DMA channel will handle
+ // all async message traffic
+ _async_msg_dma_xport =
+ make_muxed_pcie_msg_xport(dma_channel_num, PCIE_MAX_MUXED_ASYNC_XPORTS);
+ }
+ // Create a virtual async message transport
+ xports.recv = _async_msg_dma_xport->make_stream(xports.recv_sid.get_dst());
+ } else if (xport_type == uhd::usrp::device3_impl::TX_DATA) {
+ default_buff_args.send_frame_size = args.cast<size_t>(
+ "send_frame_size", std::min(send_mtu, PCIE_TX_DATA_FRAME_SIZE));
+ default_buff_args.num_send_frames =
+ args.cast<size_t>("num_send_frames", PCIE_TX_DATA_NUM_FRAMES);
+ default_buff_args.send_buff_size = args.cast<size_t>("send_buff_size", 0);
+ default_buff_args.recv_frame_size = PCIE_MSG_FRAME_SIZE;
+ default_buff_args.num_recv_frames = PCIE_MSG_NUM_FRAMES;
+ xports.recv = nirio_zero_copy::make(
+ _rio_fpga_interface, dma_channel_num, default_buff_args);
+ } else if (xport_type == uhd::usrp::device3_impl::RX_DATA) {
+ default_buff_args.send_frame_size = PCIE_MSG_FRAME_SIZE;
+ default_buff_args.num_send_frames = PCIE_MSG_NUM_FRAMES;
+ default_buff_args.recv_frame_size = args.cast<size_t>(
+ "recv_frame_size", std::min(recv_mtu, PCIE_RX_DATA_FRAME_SIZE));
+ default_buff_args.num_recv_frames =
+ args.cast<size_t>("num_recv_frames", PCIE_RX_DATA_NUM_FRAMES);
+ default_buff_args.recv_buff_size = args.cast<size_t>("recv_buff_size", 0);
+ xports.recv = nirio_zero_copy::make(
+ _rio_fpga_interface, dma_channel_num, default_buff_args);
+ }
+
+ xports.send = xports.recv;
+
+ // Router config word is:
+ // - Upper 16 bits: Destination address (e.g. 0.0)
+ // - Lower 16 bits: DMA channel
+ uint32_t router_config_word = (xports.recv_sid.get_dst() << 16) | dma_channel_num;
+ _rio_fpga_interface->get_kernel_proxy()->poke(PCIE_ROUTER_REG(0), router_config_word);
+
+ // For the nirio transport, buffer size is depends on the frame size and num
+ // frames
+ xports.recv_buff_size =
+ xports.recv->get_num_recv_frames() * xports.recv->get_recv_frame_size();
+ xports.send_buff_size =
+ xports.send->get_num_send_frames() * xports.send->get_send_frame_size();
+
+ return xports;
+}
diff --git a/host/lib/usrp/x300/x300_pcie_mgr.hpp b/host/lib/usrp/x300/x300_pcie_mgr.hpp
new file mode 100644
index 000000000..a9677e9bc
--- /dev/null
+++ b/host/lib/usrp/x300/x300_pcie_mgr.hpp
@@ -0,0 +1,82 @@
+//
+// Copyright 2019 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#ifndef INCLUDED_X300_PCI_MGR_HPP
+#define INCLUDED_X300_PCI_MGR_HPP
+
+#include "../device3/device3_impl.hpp"
+#include "x300_device_args.hpp"
+#include "x300_mboard_type.hpp"
+#include <uhd/transport/muxed_zero_copy_if.hpp>
+#include <uhd/transport/nirio/niusrprio_session.h>
+#include <uhdlib/rfnoc/xports.hpp>
+
+namespace uhd { namespace usrp { namespace x300 {
+
+/*! Helper class to manage the PCIe connections
+ */
+class pcie_manager
+{
+public:
+ pcie_manager(const x300_device_args_t& args,
+ uhd::property_tree::sptr tree,
+ const uhd::fs_path& root_path);
+
+ //! Return the motherboard type using PCIe
+ static x300_mboard_t get_mb_type_from_pcie(
+ const std::string& resource, const std::string& rpc_port);
+
+ static uhd::device_addrs_t find(const device_addr_t& hint, bool explicit_query);
+
+ /*! Return a reference to a ZPU ctrl interface object
+ */
+ uhd::wb_iface::sptr get_ctrl_iface();
+
+ void init_link();
+
+ size_t get_mtu(uhd::direction_t dir);
+
+ /*! Safely release a ZPU control object
+ *
+ * This embeds the release call (provided by \p release_fn) within a safe
+ * context to avoid multiple accesses to the PCIe bus.
+ */
+ void release_ctrl_iface(std::function<void(void)>&& release_fn);
+
+ both_xports_t make_transport(both_xports_t xports,
+ const uhd::usrp::device3_impl::xport_type_t xport_type,
+ const uhd::device_addr_t& args,
+ const size_t send_mtu,
+ const size_t recv_mtu);
+
+private:
+ /*! Allocate or return a previously allocated PCIe channel pair
+ *
+ * Note the SID is always the transmit SID (i.e. from host to device).
+ */
+ uint32_t allocate_pcie_dma_chan(
+ const uhd::sid_t& tx_sid, const uhd::usrp::device3_impl::xport_type_t xport_type);
+
+ uhd::transport::muxed_zero_copy_if::sptr make_muxed_pcie_msg_xport(
+ uint32_t dma_channel_num, size_t max_muxed_ports);
+
+ const x300_device_args_t _args;
+ const std::string _resource;
+
+ uhd::niusrprio::niusrprio_session::sptr _rio_fpga_interface;
+
+ //! Maps SID -> DMA channel
+ std::map<uint32_t, uint32_t> _dma_chan_pool;
+
+ //! Control transport for one PCIe connection
+ uhd::transport::muxed_zero_copy_if::sptr _ctrl_dma_xport;
+ //! Async message transport
+ uhd::transport::muxed_zero_copy_if::sptr _async_msg_dma_xport;
+};
+
+}}} // namespace uhd::usrp::x300
+
+#endif /* INCLUDED_X300_PCI_MGR_HPP */