summaryrefslogtreecommitdiffstats
path: root/host/lib/transport
diff options
context:
space:
mode:
authorBen Hilburn <ben.hilburn@ettus.com>2014-02-04 11:04:07 -0800
committerBen Hilburn <ben.hilburn@ettus.com>2014-02-04 11:04:07 -0800
commit178ac3f1c9950d383c8f64b3df464c0f943c4a23 (patch)
tree318ed621a7b59b7d34d4ce6e4a92f73f0bcef509 /host/lib/transport
parent2718ac110fa931cc29daf7cb3dc5ab6230ee02ab (diff)
downloaduhd-178ac3f1c9950d383c8f64b3df464c0f943c4a23.tar.gz
uhd-178ac3f1c9950d383c8f64b3df464c0f943c4a23.tar.bz2
uhd-178ac3f1c9950d383c8f64b3df464c0f943c4a23.zip
Merging USRP X300 and X310 support!!
Diffstat (limited to 'host/lib/transport')
-rw-r--r--host/lib/transport/CMakeLists.txt16
-rw-r--r--host/lib/transport/buffer_pool.cpp10
-rw-r--r--host/lib/transport/libusb1_zero_copy.cpp45
-rw-r--r--host/lib/transport/nirio/CMakeLists.txt48
-rw-r--r--host/lib/transport/nirio/lvbitx/CMakeLists.txt65
-rwxr-xr-xhost/lib/transport/nirio/lvbitx/process-lvbitx.py185
-rw-r--r--host/lib/transport/nirio/lvbitx/template_lvbitx.cpp86
-rw-r--r--host/lib/transport/nirio/lvbitx/template_lvbitx.hpp48
-rwxr-xr-xhost/lib/transport/nirio/lvbitx/x300.lvbitx_base469
-rwxr-xr-xhost/lib/transport/nirio/lvbitx/x310.lvbitx_base469
-rw-r--r--host/lib/transport/nirio/nifpga_lvbitx.cpp138
-rw-r--r--host/lib/transport/nirio/nirio_driver_iface_linux.cpp111
-rw-r--r--host/lib/transport/nirio/nirio_driver_iface_macos.cpp63
-rw-r--r--host/lib/transport/nirio/nirio_driver_iface_win.cpp148
-rw-r--r--host/lib/transport/nirio/nirio_resource_manager.cpp120
-rw-r--r--host/lib/transport/nirio/niriok_proxy.cpp300
-rw-r--r--host/lib/transport/nirio/niusrprio_session.cpp199
-rw-r--r--host/lib/transport/nirio/rpc/CMakeLists.txt29
-rw-r--r--host/lib/transport/nirio/rpc/rpc_client.cpp201
-rw-r--r--host/lib/transport/nirio/rpc/usrprio_rpc_client.cpp229
-rw-r--r--host/lib/transport/nirio/status.cpp55
-rw-r--r--host/lib/transport/nirio_zero_copy.cpp352
-rw-r--r--host/lib/transport/super_recv_packet_handler.hpp180
-rw-r--r--host/lib/transport/super_send_packet_handler.hpp121
-rw-r--r--host/lib/transport/tcp_zero_copy.cpp225
-rw-r--r--host/lib/transport/udp_wsa_zero_copy.cpp25
-rw-r--r--host/lib/transport/udp_zero_copy.cpp63
-rw-r--r--host/lib/transport/xport_benchmarker.cpp155
-rw-r--r--host/lib/transport/xport_benchmarker.hpp77
29 files changed, 4155 insertions, 77 deletions
diff --git a/host/lib/transport/CMakeLists.txt b/host/lib/transport/CMakeLists.txt
index 963edcf85..5920f3d78 100644
--- a/host/lib/transport/CMakeLists.txt
+++ b/host/lib/transport/CMakeLists.txt
@@ -20,6 +20,11 @@
########################################################################
########################################################################
+# Include subdirectories (different than add)
+########################################################################
+INCLUDE_SUBDIRECTORY(nirio)
+
+########################################################################
# Setup libusb
########################################################################
MESSAGE(STATUS "")
@@ -119,7 +124,18 @@ LIBUHD_PYTHON_GEN_SOURCE(
)
LIBUHD_APPEND_SOURCES(
+ ${CMAKE_CURRENT_SOURCE_DIR}/tcp_zero_copy.cpp
${CMAKE_CURRENT_SOURCE_DIR}/buffer_pool.cpp
${CMAKE_CURRENT_SOURCE_DIR}/if_addrs.cpp
${CMAKE_CURRENT_SOURCE_DIR}/udp_simple.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/nirio_zero_copy.cpp
)
+
+# Verbose Debug output for send/recv
+SET( UHD_TXRX_DEBUG_PRINTS OFF CACHE BOOL "Use verbose debug output for send/recv" )
+OPTION( UHD_TXRX_DEBUG_PRINTS "Use verbose debug output for send/recv" "" )
+IF(UHD_TXRX_DEBUG_PRINTS)
+ MESSAGE(STATUS "Using verbose debug output for send/recv")
+ ADD_DEFINITIONS(-DUHD_TXRX_DEBUG_PRINTS)
+ENDIF()
+
diff --git a/host/lib/transport/buffer_pool.cpp b/host/lib/transport/buffer_pool.cpp
index 971bbb75a..0fcebecb1 100644
--- a/host/lib/transport/buffer_pool.cpp
+++ b/host/lib/transport/buffer_pool.cpp
@@ -16,11 +16,21 @@
//
#include <uhd/transport/buffer_pool.hpp>
+#include <uhd/transport/zero_copy.hpp>
#include <boost/shared_array.hpp>
#include <vector>
using namespace uhd::transport;
+#ifdef UHD_TXRX_DEBUG_PRINTS
+/*
+ * This is the implementation for the static variable 's_buffer_count'
+ * located in uhd/transport/zero_copy.hpp.
+ * It is used in the managed_buffer class.
+ */
+boost::detail::atomic_count managed_buffer::s_buffer_count(0);
+#endif // UHD_TXRX_DEBUG_PRINTS
+
//! pad the byte count to a multiple of alignment
static size_t pad_to_boundary(const size_t bytes, const size_t alignment){
return bytes + (alignment - bytes)%alignment;
diff --git a/host/lib/transport/libusb1_zero_copy.cpp b/host/lib/transport/libusb1_zero_copy.cpp
index 2d18e1623..4ea2032e3 100644
--- a/host/lib/transport/libusb1_zero_copy.cpp
+++ b/host/lib/transport/libusb1_zero_copy.cpp
@@ -31,6 +31,12 @@
#include <boost/thread/condition_variable.hpp>
#include <list>
+#ifdef UHD_TXRX_DEBUG_PRINTS
+#include <vector>
+#include <fstream>
+#include <boost/format.hpp>
+#endif
+
using namespace uhd;
using namespace uhd::transport;
@@ -69,12 +75,23 @@ struct lut_result_t
completed = 0;
status = LIBUSB_TRANSFER_COMPLETED;
actual_length = 0;
+#ifdef UHD_TXRX_DEBUG_PRINTS
+ start_time = 0;
+ buff_num = -1;
+#endif
}
int completed;
libusb_transfer_status status;
int actual_length;
boost::mutex mut;
boost::condition_variable usb_transfer_complete;
+
+#ifdef UHD_TXRX_DEBUG_PRINTS
+ // These are fore debugging
+ long start_time;
+ int buff_num;
+ bool is_recv;
+#endif
};
// Created to be used as an argument to boost::condition_variable::timed_wait() function
@@ -84,6 +101,14 @@ struct lut_result_completed {
bool operator()() const {return (_result.completed ? true : false);}
};
+#ifdef UHD_TXRX_DEBUG_PRINTS
+static std::string dbg_prefix("libusb1_zero_copy,");
+static void libusb1_zerocopy_dbg_print_err(std::string msg){
+ msg = dbg_prefix + msg;
+ fprintf(stderr, "%s\n", msg.c_str());
+}
+#endif
+
/*!
* All libusb callback functions should be marked with the LIBUSB_CALL macro
* to ensure that they are compiled with the same calling convention as libusb.
@@ -98,6 +123,10 @@ static void LIBUSB_CALL libusb_async_cb(libusb_transfer *lut)
r->actual_length = lut->actual_length;
r->completed = 1;
r->usb_transfer_complete.notify_one(); // wake up thread waiting in wait_for_completion() member function below
+#ifdef UHD_TXRX_DEBUG_PRINTS
+ long end_time = boost::get_system_time().time_of_day().total_microseconds();
+ libusb1_zerocopy_dbg_print_err( (boost::format("libusb_async_cb,%s,%i,%i,%i,%ld,%ld") % (r->is_recv ? "rx":"tx") % r->buff_num % r->actual_length % r->status % end_time % r->start_time).str() );
+#endif
}
/***********************************************************************
@@ -113,11 +142,18 @@ public:
_ctx(libusb::session::get_global_session()->get_context()),
_lut(lut), _frame_size(frame_size) { /* NOP */ }
- void release(void){_release_cb(this);}
+ void release(void){
+ _release_cb(this);
+ }
UHD_INLINE void submit(void)
{
- _lut->length = (_is_recv)? _frame_size : size(); //always set length
+ _lut->length = (_is_recv)? _frame_size : size(); //always set length
+#ifdef UHD_TXRX_DEBUG_PRINTS
+ result.start_time = boost::get_system_time().time_of_day().total_microseconds();
+ result.buff_num = num();
+ result.is_recv = _is_recv;
+#endif
const int ret = libusb_submit_transfer(_lut);
if (ret != 0) throw uhd::runtime_error(str(boost::format(
"usb %s submit failed: %s") % _name % libusb_error_name(ret)));
@@ -160,7 +196,6 @@ public:
}
private:
-
boost::function<void(libusb_zero_copy_mb *)> _release_cb;
const bool _is_recv;
const std::string _name;
@@ -214,7 +249,7 @@ public:
UHD_ASSERT_THROW(lut != NULL);
_mb_pool.push_back(boost::make_shared<libusb_zero_copy_mb>(
- lut, this->get_frame_size(), boost::bind(&libusb_zero_copy_single::enqueue_damn_buffer, this, _1), is_recv, name
+ lut, this->get_frame_size(), boost::bind(&libusb_zero_copy_single::enqueue_buffer, this, _1), is_recv, name
));
libusb_fill_bulk_transfer(
@@ -304,7 +339,7 @@ private:
//! why 2 queues? there is room in the future to have > N buffers but only N in flight
boost::circular_buffer<libusb_zero_copy_mb *> _enqueued, _released;
- void enqueue_damn_buffer(libusb_zero_copy_mb *mb)
+ void enqueue_buffer(libusb_zero_copy_mb *mb)
{
boost::mutex::scoped_lock l(_mutex);
_released.push_back(mb);
diff --git a/host/lib/transport/nirio/CMakeLists.txt b/host/lib/transport/nirio/CMakeLists.txt
new file mode 100644
index 000000000..6a33da6c5
--- /dev/null
+++ b/host/lib/transport/nirio/CMakeLists.txt
@@ -0,0 +1,48 @@
+#
+# Copyright 2013-2014 Ettus Research LLC
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+########################################################################
+# This file included, use CMake directory variables
+########################################################################
+
+########################################################################
+# Include subdirectories (different than add)
+########################################################################
+INCLUDE_SUBDIRECTORY(lvbitx)
+INCLUDE_SUBDIRECTORY(rpc)
+
+########################################################################
+# Append to the list of sources for lib uhd
+########################################################################
+
+LIBUHD_APPEND_SOURCES(
+ ${CMAKE_CURRENT_SOURCE_DIR}/nifpga_lvbitx.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/niusrprio_session.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/niriok_proxy.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/nirio_resource_manager.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/status.cpp
+)
+
+IF(WIN32)
+ LIBUHD_APPEND_SOURCES(${CMAKE_CURRENT_SOURCE_DIR}/nirio_driver_iface_win.cpp)
+ELSE(WIN32)
+ IF(APPLE)
+ LIBUHD_APPEND_SOURCES(${CMAKE_CURRENT_SOURCE_DIR}/nirio_driver_iface_macos.cpp)
+ ELSE(APPLE)
+ LIBUHD_APPEND_SOURCES(${CMAKE_CURRENT_SOURCE_DIR}/nirio_driver_iface_linux.cpp)
+ ENDIF(APPLE)
+ENDIF(WIN32)
diff --git a/host/lib/transport/nirio/lvbitx/CMakeLists.txt b/host/lib/transport/nirio/lvbitx/CMakeLists.txt
new file mode 100644
index 000000000..35cfaa456
--- /dev/null
+++ b/host/lib/transport/nirio/lvbitx/CMakeLists.txt
@@ -0,0 +1,65 @@
+#
+# Copyright 2013 Ettus Research LLC
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+MACRO(LIBUHD_LVBITX_GEN_SOURCE_AND_BITSTREAM lvbitx binfile)
+ GET_FILENAME_COMPONENT(lvbitxprefix ${lvbitx} NAME_WE)
+
+ IF( ${binfile} STREQUAL "OFF" )
+ SET( GEN_OPTIONS )
+ MESSAGE( STATUS " Using ${lvbitx} for codegen" )
+ ELSE( ${binfile} STREQUAL "OFF" )
+ SET( GEN_OPTIONS --merge-bin=${CMAKE_SOURCE_DIR}/../binaries/${binfile} --output-lvbitx-path=${CMAKE_SOURCE_DIR}/../binaries )
+ MESSAGE( STATUS " Merging ${lvbitx} with ${binfile} for codegen" )
+ ENDIF( ${binfile} STREQUAL "OFF" )
+
+ SET(OUTPUT_PATH_OPT --output-src-path=${CMAKE_CURRENT_BINARY_DIR})
+ SET(IMAGES_PATH_OPT --uhd-images-path=${FPGA_IMAGES_DIR})
+
+ ADD_CUSTOM_COMMAND(
+ OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${lvbitxprefix}_lvbitx.hpp
+ OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${lvbitxprefix}_lvbitx.cpp
+ DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/process-lvbitx.py
+ DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/template_lvbitx.hpp
+ DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/template_lvbitx.cpp
+ DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${lvbitx}
+ COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/process-lvbitx.py ${OUTPUT_PATH_OPT} ${IMAGES_PATH_OPT} ${GEN_OPTIONS} ${CMAKE_CURRENT_SOURCE_DIR}/${lvbitx}
+ COMMENT "Generating ${CMAKE_CURRENT_BINARY_DIR}/${lvbitxprefix}_lvbitx.cpp"
+ )
+
+ #make libuhd depend on the output file
+ LIBUHD_APPEND_SOURCES(${CMAKE_CURRENT_BINARY_DIR}/${lvbitxprefix}_lvbitx.cpp)
+ENDMACRO(LIBUHD_LVBITX_GEN_SOURCE_AND_BITSTREAM)
+
+INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR})
+
+########################################################################
+# Generation code
+########################################################################
+
+MESSAGE(STATUS "")
+MESSAGE(STATUS "Processing NI-RIO FPGA LVBITX Bitstreams...")
+
+FILE(TO_NATIVE_PATH ${CMAKE_INSTALL_PREFIX}/share/uhd/images default_images_dir)
+SET( FPGA_IMAGES_DIR ${default_images_dir} CACHE STRING "Path to installed FPGA image files." )
+OPTION( FPGA_IMAGES_DIR "Path to installed FPGA image files." "" )
+MESSAGE( STATUS " LVBITX install directory: ${FPGA_IMAGES_DIR}" )
+
+# X300 Stuff
+LIBUHD_LVBITX_GEN_SOURCE_AND_BITSTREAM(x300.lvbitx_base OFF)
+
+# X310 Stuff
+LIBUHD_LVBITX_GEN_SOURCE_AND_BITSTREAM(x310.lvbitx_base OFF)
diff --git a/host/lib/transport/nirio/lvbitx/process-lvbitx.py b/host/lib/transport/nirio/lvbitx/process-lvbitx.py
new file mode 100755
index 000000000..a3d88d0bb
--- /dev/null
+++ b/host/lib/transport/nirio/lvbitx/process-lvbitx.py
@@ -0,0 +1,185 @@
+#!/usr/bin/python
+#
+# Copyright 2013-2014 Ettus Research LLC
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+from xml.etree import ElementTree
+from collections import namedtuple
+import optparse
+import base64
+import hashlib
+import os
+import sys
+
+# Parse options
+parser = optparse.OptionParser()
+parser.add_option("--uhd-images-path", type="string", dest="uhd_images_path", help="Install location for UHD images", default='')
+parser.add_option("--merge-bin", type="string", dest="merge_bin", help="Path to bin file that needs to be merged with the LVBITX before exporting", default=None)
+parser.add_option("--output-bin", action="store_true", dest="output_bin", help="Generate a binary FPGA programming bitstream file", default=False)
+parser.add_option("--output-lvbitx-path", type="string", dest="output_lvbitx_path", help="Output path for autogenerated LVBITX file", default=None)
+parser.add_option("--output-src-path", type="string", dest="output_src_path", help="Output path for autogenerated src file", default=None)
+(options, args) = parser.parse_args()
+
+# Args
+if (len(args) < 1):
+ print 'ERROR: Please specify the input LVBITX file name'
+ sys.exit(1)
+
+lvbitx_filename = args[0]
+input_filename = os.path.abspath(lvbitx_filename)
+autogen_src_path = os.path.abspath(options.output_src_path) if (options.output_src_path is not None) else os.path.dirname(input_filename)
+class_name = os.path.splitext(os.path.basename(input_filename))[0]
+
+if (not os.path.isfile(input_filename)):
+ print 'ERROR: FPGA File ' + input_filename + ' could not be accessed or is not a file.'
+ sys.exit(1)
+if (options.merge_bin is not None and not os.path.isfile(os.path.abspath(options.merge_bin))):
+ print 'ERROR: FPGA Bin File ' + options.merge_bin + ' could not be accessed or is not a file.'
+ sys.exit(1)
+if (not os.path.exists(autogen_src_path)):
+ print 'ERROR: Output path ' + autogen_src_path + ' could not be accessed.'
+ sys.exit(1)
+if (options.output_lvbitx_path is not None and input_filename == os.path.join(autogen_src_path, class_name + '.lvbitx')):
+ print 'ERROR: Input and output LVBITX files were the same. Choose a difference input file or output path.'
+ sys.exit(1)
+
+# Get XML Tree Node
+tree = ElementTree.parse(input_filename)
+root = tree.getroot()
+codegen_transform = {}
+
+# General info
+codegen_transform['autogen_msg'] = '// Auto-generated file: DO NOT EDIT!\n// Generated from a LabVIEW FPGA LVBITX image using "process-lvbitx.py"'
+codegen_transform['lvbitx_search_paths'] = options.uhd_images_path.replace('\\', '\\\\')
+codegen_transform['lvbitx_classname'] = class_name
+codegen_transform['lvbitx_classname_u'] = class_name.upper()
+bitstream_version = root.find('BitstreamVersion').text
+
+# Enumerate registers (controls and indicators)
+register_list = root.find('VI').find('RegisterList')
+
+reg_init_seq = ''
+control_list = ''
+indicator_list = ''
+control_idx = 0
+indicator_idx = 0
+for register in register_list.findall('Register'):
+ reg_type = 'INDICATOR' if (register.find('Indicator').text.lower() == 'true') else 'CONTROL'
+ reg_name = '\"' + register.find('Name').text + '\"'
+
+ if (reg_type == 'INDICATOR'):
+ indicator_list += '\n ' + reg_name + ','
+ idx = indicator_idx
+ indicator_idx += 1
+ else:
+ control_list += '\n ' + reg_name + ','
+ idx = control_idx
+ control_idx += 1
+
+ reg_init_seq += '\n vtr.push_back(nirio_register_info_t('
+ reg_init_seq += hex(int(register.find('Offset').text)) + ', '
+ reg_init_seq += reg_type + 'S[' + str(idx) + '], '
+ reg_init_seq += reg_type
+ reg_init_seq += ')); //' + reg_name
+
+
+codegen_transform['register_init'] = reg_init_seq
+codegen_transform['control_list'] = control_list
+codegen_transform['indicator_list'] = indicator_list
+
+# Enumerate FIFOs
+nifpga_metadata = root.find('Project').find('CompilationResultsTree').find('CompilationResults').find('NiFpga')
+dma_channel_list = nifpga_metadata.find('DmaChannelAllocationList')
+reg_block_list = nifpga_metadata.find('RegisterBlockList')
+
+fifo_init_seq = ''
+out_fifo_list = ''
+in_fifo_list = ''
+out_fifo_idx = 0
+in_fifo_idx = 0
+for dma_channel in dma_channel_list:
+ fifo_name = '\"' + dma_channel.attrib['name'] + '\"'
+ direction = 'OUTPUT_FIFO' if (dma_channel.find('Direction').text == 'HostToTarget') else 'INPUT_FIFO'
+ for reg_block in reg_block_list.findall('RegisterBlock'):
+ if (reg_block.attrib['name'] == dma_channel.find('BaseAddressTag').text):
+ base_addr = reg_block.find('Offset').text
+ break
+
+ if (direction == 'OUTPUT_FIFO'):
+ out_fifo_list += '\n ' + fifo_name + ','
+ idx = out_fifo_idx
+ out_fifo_idx += 1
+ else:
+ in_fifo_list += '\n ' + fifo_name + ','
+ idx = in_fifo_idx
+ in_fifo_idx += 1
+
+ fifo_init_seq += '\n vtr.push_back(nirio_fifo_info_t('
+ fifo_init_seq += dma_channel.find('Number').text + ', '
+ fifo_init_seq += direction + 'S[' + str(idx) + '], '
+ fifo_init_seq += direction + ', '
+ fifo_init_seq += str.lower(base_addr) + ', '
+ fifo_init_seq += dma_channel.find('NumberOfElements').text + ', '
+ fifo_init_seq += 'SCALAR_' + dma_channel.find('DataType').find('SubType').text + ', '
+ fifo_init_seq += dma_channel.find('DataType').find('WordLength').text + ', '
+ fifo_init_seq += bitstream_version
+ fifo_init_seq += ')); //' + fifo_name
+
+
+codegen_transform['fifo_init'] = fifo_init_seq
+codegen_transform['out_fifo_list'] = out_fifo_list
+codegen_transform['in_fifo_list'] = in_fifo_list
+
+# Merge bitstream into LVBITX
+if (options.merge_bin is not None):
+ with open(os.path.abspath(options.merge_bin), 'rb') as bin_file:
+ bitstream = bin_file.read()
+ bitstream_md5 = hashlib.md5(bitstream).hexdigest()
+ bitstream_b64 = base64.b64encode(bitstream)
+ bitstream_b64_lb = ''
+ for i in range(0, len(bitstream_b64), 76):
+ bitstream_b64_lb += bitstream_b64[i:i+76] + '\n'
+
+ root.find('Bitstream').text = bitstream_b64_lb
+ root.find('BitstreamMD5').text = bitstream_md5
+
+codegen_transform['lvbitx_signature'] = str.upper(root.find('SignatureRegister').text)
+
+# Write BIN file
+bitstream = base64.b64decode(root.find('Bitstream').text)
+if (options.output_lvbitx_path is not None and hashlib.md5(bitstream).hexdigest() != root.find('BitstreamMD5').text):
+ print 'ERROR: The MD5 sum for the output LVBITX was incorrect. Make sure that the bitstream in the input LVBITX or BIN file is valid.'
+ sys.exit(1)
+if (options.output_bin):
+ fpga_bin_file = open(os.path.join(options.output_lvbitx_path, class_name + '.bin'), 'w')
+ fpga_bin_file.write(bitstream)
+ fpga_bin_file.close()
+
+# Save LVBITX
+if (options.output_lvbitx_path is not None):
+ tree.write(os.path.join(options.output_lvbitx_path, class_name + '_fpga.lvbitx'), encoding="utf-8", xml_declaration=True, default_namespace=None, method="xml")
+
+# Save HPP and CPP
+with open(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'template_lvbitx.hpp'), 'r') as template_file:
+ template_string = template_file.read()
+with open(os.path.join(autogen_src_path, class_name + '_lvbitx.hpp'), 'w') as source_file:
+ source_file.write(template_string.format(**codegen_transform))
+
+with open(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'template_lvbitx.cpp'), 'r') as template_file:
+ template_string = template_file.read()
+with open(os.path.join(autogen_src_path, class_name + '_lvbitx.cpp'), 'w') as source_file:
+ source_file.write(template_string.format(**codegen_transform))
+
diff --git a/host/lib/transport/nirio/lvbitx/template_lvbitx.cpp b/host/lib/transport/nirio/lvbitx/template_lvbitx.cpp
new file mode 100644
index 000000000..a1899c771
--- /dev/null
+++ b/host/lib/transport/nirio/lvbitx/template_lvbitx.cpp
@@ -0,0 +1,86 @@
+{autogen_msg}
+
+#include "{lvbitx_classname}_lvbitx.hpp"
+#include <string>
+#include <iostream>
+#include <fstream>
+#include <streambuf>
+#include <boost/filesystem/path.hpp>
+#include <boost/algorithm/string.hpp>
+#include <boost/regex.hpp>
+
+namespace uhd {{ namespace niusrprio {{
+
+#define SEARCH_PATHS "{lvbitx_search_paths}"
+
+const char* {lvbitx_classname}_lvbitx::CONTROLS[] = {{{control_list}
+}};
+
+const char* {lvbitx_classname}_lvbitx::INDICATORS[] = {{{indicator_list}
+}};
+
+const char* {lvbitx_classname}_lvbitx::OUTPUT_FIFOS[] = {{{out_fifo_list}
+}};
+
+const char* {lvbitx_classname}_lvbitx::INPUT_FIFOS[] = {{{in_fifo_list}
+}};
+
+{lvbitx_classname}_lvbitx::{lvbitx_classname}_lvbitx(const std::string& option)
+{{
+ boost::filesystem::path fpga_path(_get_fpga_images_dir(SEARCH_PATHS));
+ fpga_path /= "usrp_{lvbitx_classname}_fpga_" + option + ".lvbitx";
+ _fpga_file_name = fpga_path.string();
+ _bitstream_checksum = _get_bitstream_checksum(_fpga_file_name);
+}}
+
+const char* {lvbitx_classname}_lvbitx::get_bitfile_path() {{
+ return _fpga_file_name.c_str();
+}}
+
+const char* {lvbitx_classname}_lvbitx::get_signature() {{
+ return "{lvbitx_signature}";
+}}
+
+const char* {lvbitx_classname}_lvbitx::get_bitstream_checksum() {{
+ return _bitstream_checksum.c_str();
+}}
+
+size_t {lvbitx_classname}_lvbitx::get_input_fifo_count() {{
+ return sizeof(INPUT_FIFOS)/sizeof(*INPUT_FIFOS);
+}}
+
+const char** {lvbitx_classname}_lvbitx::get_input_fifo_names() {{
+ return INPUT_FIFOS;
+}}
+
+size_t {lvbitx_classname}_lvbitx::get_output_fifo_count() {{
+ return sizeof(OUTPUT_FIFOS)/sizeof(*OUTPUT_FIFOS);
+}}
+
+const char** {lvbitx_classname}_lvbitx::get_output_fifo_names() {{
+ return OUTPUT_FIFOS;
+}}
+
+size_t {lvbitx_classname}_lvbitx::get_control_count() {{
+ return sizeof(CONTROLS)/sizeof(*CONTROLS);
+}}
+
+const char** {lvbitx_classname}_lvbitx::get_control_names() {{
+ return CONTROLS;
+}}
+
+size_t {lvbitx_classname}_lvbitx::get_indicator_count() {{
+ return sizeof(INDICATORS)/sizeof(*INDICATORS);
+}}
+
+const char** {lvbitx_classname}_lvbitx::get_indicator_names() {{
+ return INDICATORS;
+}}
+
+void {lvbitx_classname}_lvbitx::init_register_info(nirio_register_info_vtr& vtr) {{ {register_init}
+}}
+
+void {lvbitx_classname}_lvbitx::init_fifo_info(nirio_fifo_info_vtr& vtr) {{ {fifo_init}
+}}
+
+}}}}
diff --git a/host/lib/transport/nirio/lvbitx/template_lvbitx.hpp b/host/lib/transport/nirio/lvbitx/template_lvbitx.hpp
new file mode 100644
index 000000000..d8872e15c
--- /dev/null
+++ b/host/lib/transport/nirio/lvbitx/template_lvbitx.hpp
@@ -0,0 +1,48 @@
+{autogen_msg}
+
+#ifndef INCLUDED_{lvbitx_classname_u}_LVBITX_HPP
+#define INCLUDED_{lvbitx_classname_u}_LVBITX_HPP
+
+#include <uhd/transport/nirio/nifpga_lvbitx.h>
+
+namespace uhd {{ namespace niusrprio {{
+
+class {lvbitx_classname}_lvbitx : public nifpga_lvbitx {{
+public:
+ {lvbitx_classname}_lvbitx(const std::string& option);
+
+ virtual ~{lvbitx_classname}_lvbitx() {{}};
+
+ virtual const char* get_bitfile_path();
+ virtual const char* get_signature();
+ virtual const char* get_bitstream_checksum();
+
+ virtual size_t get_input_fifo_count();
+ virtual const char** get_input_fifo_names();
+
+ virtual size_t get_output_fifo_count();
+ virtual const char** get_output_fifo_names();
+
+ virtual size_t get_control_count();
+ virtual const char** get_control_names();
+
+ virtual size_t get_indicator_count();
+ virtual const char** get_indicator_names();
+
+ virtual void init_register_info(nirio_register_info_vtr& vtr);
+ virtual void init_fifo_info(nirio_fifo_info_vtr& vtr);
+
+ static const char* CONTROLS[];
+ static const char* INDICATORS[];
+ static const char* OUTPUT_FIFOS[];
+ static const char* INPUT_FIFOS[];
+
+private:
+ std::string _fpga_file_name;
+ std::string _bitstream_checksum;
+}};
+
+}}}}
+
+#endif /* INCLUDED_{lvbitx_classname_u}_LVBITX_HPP */
+
diff --git a/host/lib/transport/nirio/lvbitx/x300.lvbitx_base b/host/lib/transport/nirio/lvbitx/x300.lvbitx_base
new file mode 100755
index 000000000..c264e7157
--- /dev/null
+++ b/host/lib/transport/nirio/lvbitx/x300.lvbitx_base
@@ -0,0 +1,469 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Bitfile>
+ <BitfileVersion>4.0</BitfileVersion>
+ <Documentation>
+ <BuildSpecVersion/>
+ <BuildSpecDescription/>
+ </Documentation>
+ <SignatureRegister>97C6D9F4F4829001B83378F93CAB0C94</SignatureRegister>
+ <SignatureGuids>7BAD6AEB9741248079F13147B3F8AD94</SignatureGuids>
+ <SignatureNames>AE54C47F787D92DB46F7DC973338D786</SignatureNames>
+ <TimeStamp/>
+ <CompilationStatus/>
+ <BitstreamVersion>2</BitstreamVersion>
+ <VI>
+ <Name>USRP_X3x0_Top.vi</Name>
+ <RegisterList>
+ <Register>
+ <Name>ViSignature</Name>
+ <Hidden>true</Hidden>
+ <Indicator>true</Indicator>
+ <Datatype>
+ <Array>
+ <Name/>
+ <Size>4</Size>
+ <Type>
+ <U32>
+ <Name/>
+ </U32>
+ </Type>
+ </Array>
+ </Datatype>
+ <FlattenedType/>
+ <Grouping/>
+ <Offset>262132</Offset>
+ <SizeInBits>128</SizeInBits>
+ <Class>0</Class>
+ <Internal>true</Internal>
+ <TypedefPath/>
+ <TypedefRelativePath/>
+ <ID>0</ID>
+ <Bidirectional>false</Bidirectional>
+ <Synchronous>false</Synchronous>
+ <MechanicalAction>Switch When Pressed</MechanicalAction>
+ <AccessMayTimeout>false</AccessMayTimeout>
+ <RegisterNode>false</RegisterNode>
+ <SubControlList/>
+ </Register>
+ <Register>
+ <Name>DiagramReset</Name>
+ <Hidden>false</Hidden>
+ <Indicator>false</Indicator>
+ <Datatype>
+ <U32>
+ <Name/>
+ </U32>
+ </Datatype>
+ <FlattenedType/>
+ <Grouping/>
+ <Offset>262140</Offset>
+ <SizeInBits>32</SizeInBits>
+ <Class>0</Class>
+ <Internal>true</Internal>
+ <TypedefPath/>
+ <TypedefRelativePath/>
+ <ID>0</ID>
+ <Bidirectional>true</Bidirectional>
+ <Synchronous>false</Synchronous>
+ <MechanicalAction>Switch When Pressed</MechanicalAction>
+ <AccessMayTimeout>false</AccessMayTimeout>
+ <RegisterNode>false</RegisterNode>
+ <SubControlList/>
+ </Register>
+ <Register>
+ <Name>ViControl</Name>
+ <Hidden>false</Hidden>
+ <Indicator>false</Indicator>
+ <Datatype>
+ <U32>
+ <Name/>
+ </U32>
+ </Datatype>
+ <FlattenedType/>
+ <Grouping/>
+ <Offset>262136</Offset>
+ <SizeInBits>32</SizeInBits>
+ <Class>0</Class>
+ <Internal>true</Internal>
+ <TypedefPath/>
+ <TypedefRelativePath/>
+ <ID>0</ID>
+ <Bidirectional>true</Bidirectional>
+ <Synchronous>false</Synchronous>
+ <MechanicalAction>Switch When Pressed</MechanicalAction>
+ <AccessMayTimeout>false</AccessMayTimeout>
+ <RegisterNode>false</RegisterNode>
+ <SubControlList/>
+ </Register>
+ <Register>
+ <Name>InterruptEnable</Name>
+ <Hidden>false</Hidden>
+ <Indicator>false</Indicator>
+ <Datatype>
+ <U32>
+ <Name/>
+ </U32>
+ </Datatype>
+ <FlattenedType/>
+ <Grouping/>
+ <Offset>262116</Offset>
+ <SizeInBits>32</SizeInBits>
+ <Class>0</Class>
+ <Internal>true</Internal>
+ <TypedefPath/>
+ <TypedefRelativePath/>
+ <ID>0</ID>
+ <Bidirectional>true</Bidirectional>
+ <Synchronous>false</Synchronous>
+ <MechanicalAction>Switch When Pressed</MechanicalAction>
+ <AccessMayTimeout>false</AccessMayTimeout>
+ <RegisterNode>false</RegisterNode>
+ <SubControlList/>
+ </Register>
+ <Register>
+ <Name>InterruptMask</Name>
+ <Hidden>false</Hidden>
+ <Indicator>false</Indicator>
+ <Datatype>
+ <U32>
+ <Name/>
+ </U32>
+ </Datatype>
+ <FlattenedType/>
+ <Grouping/>
+ <Offset>262124</Offset>
+ <SizeInBits>32</SizeInBits>
+ <Class>0</Class>
+ <Internal>true</Internal>
+ <TypedefPath/>
+ <TypedefRelativePath/>
+ <ID>0</ID>
+ <Bidirectional>true</Bidirectional>
+ <Synchronous>false</Synchronous>
+ <MechanicalAction>Switch When Pressed</MechanicalAction>
+ <AccessMayTimeout>false</AccessMayTimeout>
+ <RegisterNode>false</RegisterNode>
+ <SubControlList/>
+ </Register>
+ <Register>
+ <Name>InterruptStatus</Name>
+ <Hidden>false</Hidden>
+ <Indicator>false</Indicator>
+ <Datatype>
+ <U32>
+ <Name/>
+ </U32>
+ </Datatype>
+ <FlattenedType/>
+ <Grouping/>
+ <Offset>262128</Offset>
+ <SizeInBits>32</SizeInBits>
+ <Class>0</Class>
+ <Internal>true</Internal>
+ <TypedefPath/>
+ <TypedefRelativePath/>
+ <ID>0</ID>
+ <Bidirectional>true</Bidirectional>
+ <Synchronous>false</Synchronous>
+ <MechanicalAction>Switch When Pressed</MechanicalAction>
+ <AccessMayTimeout>false</AccessMayTimeout>
+ <RegisterNode>false</RegisterNode>
+ <SubControlList/>
+ </Register>
+ </RegisterList>
+ <Icon>
+ <ImageType>0</ImageType>
+ <ImageDepth>8</ImageDepth>
+ <Image>////////////////////////////////////////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//wAA/////////////////////////wAAAAAAAAAAAP//AAD/+fn5+fn5+fn5+fn59/ks+SzgAAAAAAAAAAAA//8AAP/5///////////////3+Sz5LP8AAAAAAAAAAAD//wAA//n/6OTo////6OTo//f8K/ws/wAAAAAAAAAAAP//AAD/+f/k/+T////k/+T/9ywsLCzgAAAAAAAAAAAA//8AAP/56OT/5Oj/6OT/5Oj3K/wrLP8AAAAAAAAAAAD//wAA//nk6P/o5P/k6P/o5Pf8CPws/wAAAAAAAAAAAP//AAD/+eT////k/+T////k9/wI/Cz/AAAAAAAAAAAA//8AAP/5/////+jk6P/////3K/wrLP8AAAAAAAAAAAD//wAA//n///////////////csLCws/wAAAAAAAAAAAP//AAD/9/f39/f39/f39/f39ywsg4P/AAAAAAAAAAAA//8AAP8sLCwsLCwsLCwsLCwsLCyDBYODAAAAAAAAAAD//wAA/yz8LCwsLCz8LCwsLCMjI4MFBQWDgwAAAAAAAP//AAD//PD8LCws/CP8LCMjLCwsgwUF/wUFg4MAAAAA//8AAP8s7ywsLCwjLCwjLCwsLCyDBf///wUFBYMjIwD//wAA///w////I///I////////4MFBf8FBYODAAAAAP//AAAAAO8AAAAjAAAjAADw7+/wgwUFBYODAAAAAAAA//8AAAAAAPAAAAAjIwAA7wAAAACDBYODAAAAAAAAAAD//wAAAAAAAO/vAAAAAPAAAAAAAIODAAAAAAAAAAAAAP//AAAAAAAAAADw7/DvAAAAAAAAAAAAAAAAAP8AAAAA//8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//wAAAAD//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAAAAP//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8AAAAA//8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wAAAAD//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAAAAP//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8AAAAA//8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///////////////////////////////////////////w==</Image>
+ <Mask>//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////8=</Mask>
+ <Colors>AP///wD//8wA//+ZAP//ZgD//zMA//8AAP/M/wD/zMwA/8yZAP/MZgD/zDMA/8wAAP+Z/wD/mcwA/5mZAP+ZZgD/mTMA/5kAAP9m/wD/ZswA/2aZAP9mZgD/ZjMA/2YAAP8z/wD/M8wA/zOZAP8zZgD/MzMA/zMAAP8A/wD/AMwA/wCZAP8AZgD/ADMA/wAAAMz//wDM/8wAzP+ZAMz/ZgDM/zMAzP8AAMzM/wDMzMwAzMyZAMzMZgDMzDMAzMwAAMyZ/wDMmcwAzJmZAMyZZgDMmTMAzJkAAMxm/wDMZswAzGaZAMxmZgDMZjMAzGYAAMwz/wDMM8wAzDOZAMwzZgDMMzMAzDMAAMwA/wDMAMwAzACZAMwAZgDMADMAzAAAAJn//wCZ/8wAmf+ZAJn/ZgCZ/zMAmf8AAJnM/wCZzMwAmcyZAJnMZgCZzDMAmcwAAJmZ/wCZmcwAmZmZAJmZZgCZmTMAmZkAAJlm/wCZZswAmWaZAJlmZgCZZjMAmWYAAJkz/wCZM8wAmTOZAJkzZgCZMzMAmTMAAJkA/wCZAMwAmQCZAJkAZgCZADMAmQAAAGb//wBm/8wAZv+ZAGb/ZgBm/zMAZv8AAGbM/wBmzMwAZsyZAGbMZgBmzDMAZswAAGaZ/wBmmcwAZpmZAGaZZgBmmTMAZpkAAGZm/wBmZswAZmaZAGZmZgBmZjMAZmYAAGYz/wBmM8wAZjOZAGYzZgBmMzMAZjMAAGYA/wBmAMwAZgCZAGYAZgBmADMAZgAAADP//wAz/8wAM/+ZADP/ZgAz/zMAM/8AADPM/wAzzMwAM8yZADPMZgAzzDMAM8wAADOZ/wAzmcwAM5mZADOZZgAzmTMAM5kAADNm/wAzZswAM2aZADNmZgAzZjMAM2YAADMz/wAzM8wAMzOZADMzZgAzMzMAMzMAADMA/wAzAMwAMwCZADMAZgAzADMAMwAAAAD//wAA/8wAAP+ZAAD/ZgAA/zMAAP8AAADM/wAAzMwAAMyZAADMZgAAzDMAAMwAAACZ/wAAmcwAAJmZAACZZgAAmTMAAJkAAABm/wAAZswAAGaZAABmZgAAZjMAAGYAAAAz/wAAM8wAADOZAAAzZgAAMzMAADMAAAAA/wAAAMwAAACZAAAAZgAAADMA7gAAAN0AAAC7AAAAqgAAAIgAAAB3AAAAVQAAAEQAAAAiAAAAEQAAAADuAAAA3QAAALsAAACqAAAAiAAAAHcAAABVAAAARAAAACIAAAARAAAAAO4AAADdAAAAuwAAAKoAAACIAAAAdwAAAFUAAABEAAAAIgAAABEA7u7uAN3d3QC7u7sAqqqqAIiIiAB3d3cAVVVVAERERAAiIiIAERERAAAAAA==</Colors>
+ <Rectangle>
+ <Left>0</Left>
+ <Top>0</Top>
+ <Right>32</Right>
+ <Bottom>32</Bottom>
+ </Rectangle>
+ </Icon>
+ </VI>
+ <Project>
+ <TargetClass>294XR; 295XR</TargetClass>
+ <AutoRunWhenDownloaded>false</AutoRunWhenDownloaded>
+ <CompilationResultsTree>
+ <CompilationResults>
+ <NiFpga>
+ <BaseAddressOnDevice>0</BaseAddressOnDevice>
+ <DmaChannelAllocationList>
+ <Channel name="RX FIFO 0">
+ <BaseAddressTag>NiLvFpgaFIFO 0</BaseAddressTag>
+ <ControlSet>0</ControlSet>
+ <DataType>
+ <Delta>1.000000000000000000000000000000000000000000000000000000</Delta>
+ <IntegerWordLength>64</IntegerWordLength>
+ <Maximum>18446744073709551600.00000000000000000000000000000000000</Maximum>
+ <Minimum>0.000000000000000000000000000000000000000000000000000000</Minimum>
+ <Signed>false</Signed>
+ <SubType>U64</SubType>
+ <WordLength>64</WordLength>
+ </DataType>
+ <Direction>TargetToHost</Direction>
+ <Implementation>niFpgaTargetToHost</Implementation>
+ <Number>0</Number>
+ <NumberOfElements>1023</NumberOfElements>
+ <UserVisible>true</UserVisible>
+ </Channel>
+ <Channel name="RX FIFO 1">
+ <BaseAddressTag>NiLvFpgaFIFO 1</BaseAddressTag>
+ <ControlSet>1</ControlSet>
+ <DataType>
+ <Delta>1.000000000000000000000000000000000000000000000000000000</Delta>
+ <IntegerWordLength>64</IntegerWordLength>
+ <Maximum>18446744073709551600.00000000000000000000000000000000000</Maximum>
+ <Minimum>0.000000000000000000000000000000000000000000000000000000</Minimum>
+ <Signed>false</Signed>
+ <SubType>U64</SubType>
+ <WordLength>64</WordLength>
+ </DataType>
+ <Direction>TargetToHost</Direction>
+ <Implementation>niFpgaTargetToHost</Implementation>
+ <Number>1</Number>
+ <NumberOfElements>1023</NumberOfElements>
+ <UserVisible>true</UserVisible>
+ </Channel>
+ <Channel name="RX FIFO 2">
+ <BaseAddressTag>NiLvFpgaFIFO 2</BaseAddressTag>
+ <ControlSet>2</ControlSet>
+ <DataType>
+ <Delta>1.000000000000000000000000000000000000000000000000000000</Delta>
+ <IntegerWordLength>64</IntegerWordLength>
+ <Maximum>18446744073709551600.00000000000000000000000000000000000</Maximum>
+ <Minimum>0.000000000000000000000000000000000000000000000000000000</Minimum>
+ <Signed>false</Signed>
+ <SubType>U64</SubType>
+ <WordLength>64</WordLength>
+ </DataType>
+ <Direction>TargetToHost</Direction>
+ <Implementation>niFpgaTargetToHost</Implementation>
+ <Number>2</Number>
+ <NumberOfElements>1023</NumberOfElements>
+ <UserVisible>true</UserVisible>
+ </Channel>
+ <Channel name="RX FIFO 3">
+ <BaseAddressTag>NiLvFpgaFIFO 3</BaseAddressTag>
+ <ControlSet>3</ControlSet>
+ <DataType>
+ <Delta>1.000000000000000000000000000000000000000000000000000000</Delta>
+ <IntegerWordLength>64</IntegerWordLength>
+ <Maximum>18446744073709551600.00000000000000000000000000000000000</Maximum>
+ <Minimum>0.000000000000000000000000000000000000000000000000000000</Minimum>
+ <Signed>false</Signed>
+ <SubType>U64</SubType>
+ <WordLength>64</WordLength>
+ </DataType>
+ <Direction>TargetToHost</Direction>
+ <Implementation>niFpgaTargetToHost</Implementation>
+ <Number>3</Number>
+ <NumberOfElements>1023</NumberOfElements>
+ <UserVisible>true</UserVisible>
+ </Channel>
+ <Channel name="RX FIFO 4">
+ <BaseAddressTag>NiLvFpgaFIFO 4</BaseAddressTag>
+ <ControlSet>4</ControlSet>
+ <DataType>
+ <Delta>1.000000000000000000000000000000000000000000000000000000</Delta>
+ <IntegerWordLength>64</IntegerWordLength>
+ <Maximum>18446744073709551600.00000000000000000000000000000000000</Maximum>
+ <Minimum>0.000000000000000000000000000000000000000000000000000000</Minimum>
+ <Signed>false</Signed>
+ <SubType>U64</SubType>
+ <WordLength>64</WordLength>
+ </DataType>
+ <Direction>TargetToHost</Direction>
+ <Implementation>niFpgaTargetToHost</Implementation>
+ <Number>4</Number>
+ <NumberOfElements>1023</NumberOfElements>
+ <UserVisible>true</UserVisible>
+ </Channel>
+ <Channel name="RX FIFO 5">
+ <BaseAddressTag>NiLvFpgaFIFO 5</BaseAddressTag>
+ <ControlSet>5</ControlSet>
+ <DataType>
+ <Delta>1.000000000000000000000000000000000000000000000000000000</Delta>
+ <IntegerWordLength>64</IntegerWordLength>
+ <Maximum>18446744073709551600.00000000000000000000000000000000000</Maximum>
+ <Minimum>0.000000000000000000000000000000000000000000000000000000</Minimum>
+ <Signed>false</Signed>
+ <SubType>U64</SubType>
+ <WordLength>64</WordLength>
+ </DataType>
+ <Direction>TargetToHost</Direction>
+ <Implementation>niFpgaTargetToHost</Implementation>
+ <Number>5</Number>
+ <NumberOfElements>1023</NumberOfElements>
+ <UserVisible>true</UserVisible>
+ </Channel>
+ <Channel name="TX FIFO 0">
+ <BaseAddressTag>NiLvFpgaFIFO 6</BaseAddressTag>
+ <ControlSet>6</ControlSet>
+ <DataType>
+ <Delta>1.000000000000000000000000000000000000000000000000000000</Delta>
+ <IntegerWordLength>64</IntegerWordLength>
+ <Maximum>18446744073709551600.00000000000000000000000000000000000</Maximum>
+ <Minimum>0.000000000000000000000000000000000000000000000000000000</Minimum>
+ <Signed>false</Signed>
+ <SubType>U64</SubType>
+ <WordLength>64</WordLength>
+ </DataType>
+ <Direction>HostToTarget</Direction>
+ <Implementation>niFpgaHostToTarget</Implementation>
+ <Number>6</Number>
+ <NumberOfElements>1029</NumberOfElements>
+ <UserVisible>true</UserVisible>
+ </Channel>
+ <Channel name="TX FIFO 1">
+ <BaseAddressTag>NiLvFpgaFIFO 7</BaseAddressTag>
+ <ControlSet>7</ControlSet>
+ <DataType>
+ <Delta>1.000000000000000000000000000000000000000000000000000000</Delta>
+ <IntegerWordLength>64</IntegerWordLength>
+ <Maximum>18446744073709551600.00000000000000000000000000000000000</Maximum>
+ <Minimum>0.000000000000000000000000000000000000000000000000000000</Minimum>
+ <Signed>false</Signed>
+ <SubType>U64</SubType>
+ <WordLength>64</WordLength>
+ </DataType>
+ <Direction>HostToTarget</Direction>
+ <Implementation>niFpgaHostToTarget</Implementation>
+ <Number>7</Number>
+ <NumberOfElements>1029</NumberOfElements>
+ <UserVisible>true</UserVisible>
+ </Channel>
+ <Channel name="TX FIFO 2">
+ <BaseAddressTag>NiLvFpgaFIFO 8</BaseAddressTag>
+ <ControlSet>8</ControlSet>
+ <DataType>
+ <Delta>1.000000000000000000000000000000000000000000000000000000</Delta>
+ <IntegerWordLength>64</IntegerWordLength>
+ <Maximum>18446744073709551600.00000000000000000000000000000000000</Maximum>
+ <Minimum>0.000000000000000000000000000000000000000000000000000000</Minimum>
+ <Signed>false</Signed>
+ <SubType>U64</SubType>
+ <WordLength>64</WordLength>
+ </DataType>
+ <Direction>HostToTarget</Direction>
+ <Implementation>niFpgaHostToTarget</Implementation>
+ <Number>8</Number>
+ <NumberOfElements>1029</NumberOfElements>
+ <UserVisible>true</UserVisible>
+ </Channel>
+ <Channel name="TX FIFO 3">
+ <BaseAddressTag>NiLvFpgaFIFO 9</BaseAddressTag>
+ <ControlSet>9</ControlSet>
+ <DataType>
+ <Delta>1.000000000000000000000000000000000000000000000000000000</Delta>
+ <IntegerWordLength>64</IntegerWordLength>
+ <Maximum>18446744073709551600.00000000000000000000000000000000000</Maximum>
+ <Minimum>0.000000000000000000000000000000000000000000000000000000</Minimum>
+ <Signed>false</Signed>
+ <SubType>U64</SubType>
+ <WordLength>64</WordLength>
+ </DataType>
+ <Direction>HostToTarget</Direction>
+ <Implementation>niFpgaHostToTarget</Implementation>
+ <Number>9</Number>
+ <NumberOfElements>1029</NumberOfElements>
+ <UserVisible>true</UserVisible>
+ </Channel>
+ <Channel name="TX FIFO 4">
+ <BaseAddressTag>NiLvFpgaFIFO 10</BaseAddressTag>
+ <ControlSet>10</ControlSet>
+ <DataType>
+ <Delta>1.000000000000000000000000000000000000000000000000000000</Delta>
+ <IntegerWordLength>64</IntegerWordLength>
+ <Maximum>18446744073709551600.00000000000000000000000000000000000</Maximum>
+ <Minimum>0.000000000000000000000000000000000000000000000000000000</Minimum>
+ <Signed>false</Signed>
+ <SubType>U64</SubType>
+ <WordLength>64</WordLength>
+ </DataType>
+ <Direction>HostToTarget</Direction>
+ <Implementation>niFpgaHostToTarget</Implementation>
+ <Number>10</Number>
+ <NumberOfElements>1029</NumberOfElements>
+ <UserVisible>true</UserVisible>
+ </Channel>
+ <Channel name="TX FIFO 5">
+ <BaseAddressTag>NiLvFpgaFIFO 11</BaseAddressTag>
+ <ControlSet>11</ControlSet>
+ <DataType>
+ <Delta>1.000000000000000000000000000000000000000000000000000000</Delta>
+ <IntegerWordLength>64</IntegerWordLength>
+ <Maximum>18446744073709551600.00000000000000000000000000000000000</Maximum>
+ <Minimum>0.000000000000000000000000000000000000000000000000000000</Minimum>
+ <Signed>false</Signed>
+ <SubType>U64</SubType>
+ <WordLength>64</WordLength>
+ </DataType>
+ <Direction>HostToTarget</Direction>
+ <Implementation>niFpgaHostToTarget</Implementation>
+ <Number>11</Number>
+ <NumberOfElements>1029</NumberOfElements>
+ <UserVisible>true</UserVisible>
+ </Channel>
+ </DmaChannelAllocationList>
+ <RegisterBlockList>
+ <RegisterBlock name="NiLvFpgaFIFO 0">
+ <Offset>0xFF80</Offset>
+ </RegisterBlock>
+ <RegisterBlock name="NiLvFpgaFIFO 1">
+ <Offset>0xFF40</Offset>
+ </RegisterBlock>
+ <RegisterBlock name="NiLvFpgaFIFO 2">
+ <Offset>0xFF00</Offset>
+ </RegisterBlock>
+ <RegisterBlock name="NiLvFpgaFIFO 3">
+ <Offset>0xFEC0</Offset>
+ </RegisterBlock>
+ <RegisterBlock name="NiLvFpgaFIFO 4">
+ <Offset>0xFE80</Offset>
+ </RegisterBlock>
+ <RegisterBlock name="NiLvFpgaFIFO 5">
+ <Offset>0xFE40</Offset>
+ </RegisterBlock>
+ <RegisterBlock name="NiLvFpgaFIFO 6">
+ <Offset>0xFE00</Offset>
+ </RegisterBlock>
+ <RegisterBlock name="NiLvFpgaFIFO 7">
+ <Offset>0xFDC0</Offset>
+ </RegisterBlock>
+ <RegisterBlock name="NiLvFpgaFIFO 8">
+ <Offset>0xFD80</Offset>
+ </RegisterBlock>
+ <RegisterBlock name="NiLvFpgaFIFO 9">
+ <Offset>0xFD40</Offset>
+ </RegisterBlock>
+ <RegisterBlock name="NiLvFpgaFIFO 10">
+ <Offset>0xFD00</Offset>
+ </RegisterBlock>
+ <RegisterBlock name="NiLvFpgaFIFO 11">
+ <Offset>0xFCC0</Offset>
+ </RegisterBlock>
+ </RegisterBlockList>
+ <UsedBaseClockList>
+ <BaseClock name="ReliableClkIn">
+ </BaseClock>
+ <BaseClock name="ChinchClk">
+ </BaseClock>
+ <BaseClock name="40 MHz Onboard Clock">
+ </BaseClock>
+ </UsedBaseClockList>
+ <version>1</version>
+ </NiFpga>
+ </CompilationResults>
+ </CompilationResultsTree>
+ <MultipleUserClocks>false</MultipleUserClocks>
+ <AllowImplicitEnableRemoval>false</AllowImplicitEnableRemoval>
+ </Project>
+ <ClientData/>
+ <BitstreamMD5>a72ba1716893a0bd02f88dac3ab28e1b</BitstreamMD5>
+ <Bitstream>a72ba1716893a0bd02f88dac3ab28e1b</Bitstream>
+</Bitfile>
diff --git a/host/lib/transport/nirio/lvbitx/x310.lvbitx_base b/host/lib/transport/nirio/lvbitx/x310.lvbitx_base
new file mode 100755
index 000000000..c264e7157
--- /dev/null
+++ b/host/lib/transport/nirio/lvbitx/x310.lvbitx_base
@@ -0,0 +1,469 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Bitfile>
+ <BitfileVersion>4.0</BitfileVersion>
+ <Documentation>
+ <BuildSpecVersion/>
+ <BuildSpecDescription/>
+ </Documentation>
+ <SignatureRegister>97C6D9F4F4829001B83378F93CAB0C94</SignatureRegister>
+ <SignatureGuids>7BAD6AEB9741248079F13147B3F8AD94</SignatureGuids>
+ <SignatureNames>AE54C47F787D92DB46F7DC973338D786</SignatureNames>
+ <TimeStamp/>
+ <CompilationStatus/>
+ <BitstreamVersion>2</BitstreamVersion>
+ <VI>
+ <Name>USRP_X3x0_Top.vi</Name>
+ <RegisterList>
+ <Register>
+ <Name>ViSignature</Name>
+ <Hidden>true</Hidden>
+ <Indicator>true</Indicator>
+ <Datatype>
+ <Array>
+ <Name/>
+ <Size>4</Size>
+ <Type>
+ <U32>
+ <Name/>
+ </U32>
+ </Type>
+ </Array>
+ </Datatype>
+ <FlattenedType/>
+ <Grouping/>
+ <Offset>262132</Offset>
+ <SizeInBits>128</SizeInBits>
+ <Class>0</Class>
+ <Internal>true</Internal>
+ <TypedefPath/>
+ <TypedefRelativePath/>
+ <ID>0</ID>
+ <Bidirectional>false</Bidirectional>
+ <Synchronous>false</Synchronous>
+ <MechanicalAction>Switch When Pressed</MechanicalAction>
+ <AccessMayTimeout>false</AccessMayTimeout>
+ <RegisterNode>false</RegisterNode>
+ <SubControlList/>
+ </Register>
+ <Register>
+ <Name>DiagramReset</Name>
+ <Hidden>false</Hidden>
+ <Indicator>false</Indicator>
+ <Datatype>
+ <U32>
+ <Name/>
+ </U32>
+ </Datatype>
+ <FlattenedType/>
+ <Grouping/>
+ <Offset>262140</Offset>
+ <SizeInBits>32</SizeInBits>
+ <Class>0</Class>
+ <Internal>true</Internal>
+ <TypedefPath/>
+ <TypedefRelativePath/>
+ <ID>0</ID>
+ <Bidirectional>true</Bidirectional>
+ <Synchronous>false</Synchronous>
+ <MechanicalAction>Switch When Pressed</MechanicalAction>
+ <AccessMayTimeout>false</AccessMayTimeout>
+ <RegisterNode>false</RegisterNode>
+ <SubControlList/>
+ </Register>
+ <Register>
+ <Name>ViControl</Name>
+ <Hidden>false</Hidden>
+ <Indicator>false</Indicator>
+ <Datatype>
+ <U32>
+ <Name/>
+ </U32>
+ </Datatype>
+ <FlattenedType/>
+ <Grouping/>
+ <Offset>262136</Offset>
+ <SizeInBits>32</SizeInBits>
+ <Class>0</Class>
+ <Internal>true</Internal>
+ <TypedefPath/>
+ <TypedefRelativePath/>
+ <ID>0</ID>
+ <Bidirectional>true</Bidirectional>
+ <Synchronous>false</Synchronous>
+ <MechanicalAction>Switch When Pressed</MechanicalAction>
+ <AccessMayTimeout>false</AccessMayTimeout>
+ <RegisterNode>false</RegisterNode>
+ <SubControlList/>
+ </Register>
+ <Register>
+ <Name>InterruptEnable</Name>
+ <Hidden>false</Hidden>
+ <Indicator>false</Indicator>
+ <Datatype>
+ <U32>
+ <Name/>
+ </U32>
+ </Datatype>
+ <FlattenedType/>
+ <Grouping/>
+ <Offset>262116</Offset>
+ <SizeInBits>32</SizeInBits>
+ <Class>0</Class>
+ <Internal>true</Internal>
+ <TypedefPath/>
+ <TypedefRelativePath/>
+ <ID>0</ID>
+ <Bidirectional>true</Bidirectional>
+ <Synchronous>false</Synchronous>
+ <MechanicalAction>Switch When Pressed</MechanicalAction>
+ <AccessMayTimeout>false</AccessMayTimeout>
+ <RegisterNode>false</RegisterNode>
+ <SubControlList/>
+ </Register>
+ <Register>
+ <Name>InterruptMask</Name>
+ <Hidden>false</Hidden>
+ <Indicator>false</Indicator>
+ <Datatype>
+ <U32>
+ <Name/>
+ </U32>
+ </Datatype>
+ <FlattenedType/>
+ <Grouping/>
+ <Offset>262124</Offset>
+ <SizeInBits>32</SizeInBits>
+ <Class>0</Class>
+ <Internal>true</Internal>
+ <TypedefPath/>
+ <TypedefRelativePath/>
+ <ID>0</ID>
+ <Bidirectional>true</Bidirectional>
+ <Synchronous>false</Synchronous>
+ <MechanicalAction>Switch When Pressed</MechanicalAction>
+ <AccessMayTimeout>false</AccessMayTimeout>
+ <RegisterNode>false</RegisterNode>
+ <SubControlList/>
+ </Register>
+ <Register>
+ <Name>InterruptStatus</Name>
+ <Hidden>false</Hidden>
+ <Indicator>false</Indicator>
+ <Datatype>
+ <U32>
+ <Name/>
+ </U32>
+ </Datatype>
+ <FlattenedType/>
+ <Grouping/>
+ <Offset>262128</Offset>
+ <SizeInBits>32</SizeInBits>
+ <Class>0</Class>
+ <Internal>true</Internal>
+ <TypedefPath/>
+ <TypedefRelativePath/>
+ <ID>0</ID>
+ <Bidirectional>true</Bidirectional>
+ <Synchronous>false</Synchronous>
+ <MechanicalAction>Switch When Pressed</MechanicalAction>
+ <AccessMayTimeout>false</AccessMayTimeout>
+ <RegisterNode>false</RegisterNode>
+ <SubControlList/>
+ </Register>
+ </RegisterList>
+ <Icon>
+ <ImageType>0</ImageType>
+ <ImageDepth>8</ImageDepth>
+ <Image>////////////////////////////////////////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//wAA/////////////////////////wAAAAAAAAAAAP//AAD/+fn5+fn5+fn5+fn59/ks+SzgAAAAAAAAAAAA//8AAP/5///////////////3+Sz5LP8AAAAAAAAAAAD//wAA//n/6OTo////6OTo//f8K/ws/wAAAAAAAAAAAP//AAD/+f/k/+T////k/+T/9ywsLCzgAAAAAAAAAAAA//8AAP/56OT/5Oj/6OT/5Oj3K/wrLP8AAAAAAAAAAAD//wAA//nk6P/o5P/k6P/o5Pf8CPws/wAAAAAAAAAAAP//AAD/+eT////k/+T////k9/wI/Cz/AAAAAAAAAAAA//8AAP/5/////+jk6P/////3K/wrLP8AAAAAAAAAAAD//wAA//n///////////////csLCws/wAAAAAAAAAAAP//AAD/9/f39/f39/f39/f39ywsg4P/AAAAAAAAAAAA//8AAP8sLCwsLCwsLCwsLCwsLCyDBYODAAAAAAAAAAD//wAA/yz8LCwsLCz8LCwsLCMjI4MFBQWDgwAAAAAAAP//AAD//PD8LCws/CP8LCMjLCwsgwUF/wUFg4MAAAAA//8AAP8s7ywsLCwjLCwjLCwsLCyDBf///wUFBYMjIwD//wAA///w////I///I////////4MFBf8FBYODAAAAAP//AAAAAO8AAAAjAAAjAADw7+/wgwUFBYODAAAAAAAA//8AAAAAAPAAAAAjIwAA7wAAAACDBYODAAAAAAAAAAD//wAAAAAAAO/vAAAAAPAAAAAAAIODAAAAAAAAAAAAAP//AAAAAAAAAADw7/DvAAAAAAAAAAAAAAAAAP8AAAAA//8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//wAAAAD//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAAAAP//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8AAAAA//8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wAAAAD//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAAAAP//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8AAAAA//8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///////////////////////////////////////////w==</Image>
+ <Mask>//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////8=</Mask>
+ <Colors>AP///wD//8wA//+ZAP//ZgD//zMA//8AAP/M/wD/zMwA/8yZAP/MZgD/zDMA/8wAAP+Z/wD/mcwA/5mZAP+ZZgD/mTMA/5kAAP9m/wD/ZswA/2aZAP9mZgD/ZjMA/2YAAP8z/wD/M8wA/zOZAP8zZgD/MzMA/zMAAP8A/wD/AMwA/wCZAP8AZgD/ADMA/wAAAMz//wDM/8wAzP+ZAMz/ZgDM/zMAzP8AAMzM/wDMzMwAzMyZAMzMZgDMzDMAzMwAAMyZ/wDMmcwAzJmZAMyZZgDMmTMAzJkAAMxm/wDMZswAzGaZAMxmZgDMZjMAzGYAAMwz/wDMM8wAzDOZAMwzZgDMMzMAzDMAAMwA/wDMAMwAzACZAMwAZgDMADMAzAAAAJn//wCZ/8wAmf+ZAJn/ZgCZ/zMAmf8AAJnM/wCZzMwAmcyZAJnMZgCZzDMAmcwAAJmZ/wCZmcwAmZmZAJmZZgCZmTMAmZkAAJlm/wCZZswAmWaZAJlmZgCZZjMAmWYAAJkz/wCZM8wAmTOZAJkzZgCZMzMAmTMAAJkA/wCZAMwAmQCZAJkAZgCZADMAmQAAAGb//wBm/8wAZv+ZAGb/ZgBm/zMAZv8AAGbM/wBmzMwAZsyZAGbMZgBmzDMAZswAAGaZ/wBmmcwAZpmZAGaZZgBmmTMAZpkAAGZm/wBmZswAZmaZAGZmZgBmZjMAZmYAAGYz/wBmM8wAZjOZAGYzZgBmMzMAZjMAAGYA/wBmAMwAZgCZAGYAZgBmADMAZgAAADP//wAz/8wAM/+ZADP/ZgAz/zMAM/8AADPM/wAzzMwAM8yZADPMZgAzzDMAM8wAADOZ/wAzmcwAM5mZADOZZgAzmTMAM5kAADNm/wAzZswAM2aZADNmZgAzZjMAM2YAADMz/wAzM8wAMzOZADMzZgAzMzMAMzMAADMA/wAzAMwAMwCZADMAZgAzADMAMwAAAAD//wAA/8wAAP+ZAAD/ZgAA/zMAAP8AAADM/wAAzMwAAMyZAADMZgAAzDMAAMwAAACZ/wAAmcwAAJmZAACZZgAAmTMAAJkAAABm/wAAZswAAGaZAABmZgAAZjMAAGYAAAAz/wAAM8wAADOZAAAzZgAAMzMAADMAAAAA/wAAAMwAAACZAAAAZgAAADMA7gAAAN0AAAC7AAAAqgAAAIgAAAB3AAAAVQAAAEQAAAAiAAAAEQAAAADuAAAA3QAAALsAAACqAAAAiAAAAHcAAABVAAAARAAAACIAAAARAAAAAO4AAADdAAAAuwAAAKoAAACIAAAAdwAAAFUAAABEAAAAIgAAABEA7u7uAN3d3QC7u7sAqqqqAIiIiAB3d3cAVVVVAERERAAiIiIAERERAAAAAA==</Colors>
+ <Rectangle>
+ <Left>0</Left>
+ <Top>0</Top>
+ <Right>32</Right>
+ <Bottom>32</Bottom>
+ </Rectangle>
+ </Icon>
+ </VI>
+ <Project>
+ <TargetClass>294XR; 295XR</TargetClass>
+ <AutoRunWhenDownloaded>false</AutoRunWhenDownloaded>
+ <CompilationResultsTree>
+ <CompilationResults>
+ <NiFpga>
+ <BaseAddressOnDevice>0</BaseAddressOnDevice>
+ <DmaChannelAllocationList>
+ <Channel name="RX FIFO 0">
+ <BaseAddressTag>NiLvFpgaFIFO 0</BaseAddressTag>
+ <ControlSet>0</ControlSet>
+ <DataType>
+ <Delta>1.000000000000000000000000000000000000000000000000000000</Delta>
+ <IntegerWordLength>64</IntegerWordLength>
+ <Maximum>18446744073709551600.00000000000000000000000000000000000</Maximum>
+ <Minimum>0.000000000000000000000000000000000000000000000000000000</Minimum>
+ <Signed>false</Signed>
+ <SubType>U64</SubType>
+ <WordLength>64</WordLength>
+ </DataType>
+ <Direction>TargetToHost</Direction>
+ <Implementation>niFpgaTargetToHost</Implementation>
+ <Number>0</Number>
+ <NumberOfElements>1023</NumberOfElements>
+ <UserVisible>true</UserVisible>
+ </Channel>
+ <Channel name="RX FIFO 1">
+ <BaseAddressTag>NiLvFpgaFIFO 1</BaseAddressTag>
+ <ControlSet>1</ControlSet>
+ <DataType>
+ <Delta>1.000000000000000000000000000000000000000000000000000000</Delta>
+ <IntegerWordLength>64</IntegerWordLength>
+ <Maximum>18446744073709551600.00000000000000000000000000000000000</Maximum>
+ <Minimum>0.000000000000000000000000000000000000000000000000000000</Minimum>
+ <Signed>false</Signed>
+ <SubType>U64</SubType>
+ <WordLength>64</WordLength>
+ </DataType>
+ <Direction>TargetToHost</Direction>
+ <Implementation>niFpgaTargetToHost</Implementation>
+ <Number>1</Number>
+ <NumberOfElements>1023</NumberOfElements>
+ <UserVisible>true</UserVisible>
+ </Channel>
+ <Channel name="RX FIFO 2">
+ <BaseAddressTag>NiLvFpgaFIFO 2</BaseAddressTag>
+ <ControlSet>2</ControlSet>
+ <DataType>
+ <Delta>1.000000000000000000000000000000000000000000000000000000</Delta>
+ <IntegerWordLength>64</IntegerWordLength>
+ <Maximum>18446744073709551600.00000000000000000000000000000000000</Maximum>
+ <Minimum>0.000000000000000000000000000000000000000000000000000000</Minimum>
+ <Signed>false</Signed>
+ <SubType>U64</SubType>
+ <WordLength>64</WordLength>
+ </DataType>
+ <Direction>TargetToHost</Direction>
+ <Implementation>niFpgaTargetToHost</Implementation>
+ <Number>2</Number>
+ <NumberOfElements>1023</NumberOfElements>
+ <UserVisible>true</UserVisible>
+ </Channel>
+ <Channel name="RX FIFO 3">
+ <BaseAddressTag>NiLvFpgaFIFO 3</BaseAddressTag>
+ <ControlSet>3</ControlSet>
+ <DataType>
+ <Delta>1.000000000000000000000000000000000000000000000000000000</Delta>
+ <IntegerWordLength>64</IntegerWordLength>
+ <Maximum>18446744073709551600.00000000000000000000000000000000000</Maximum>
+ <Minimum>0.000000000000000000000000000000000000000000000000000000</Minimum>
+ <Signed>false</Signed>
+ <SubType>U64</SubType>
+ <WordLength>64</WordLength>
+ </DataType>
+ <Direction>TargetToHost</Direction>
+ <Implementation>niFpgaTargetToHost</Implementation>
+ <Number>3</Number>
+ <NumberOfElements>1023</NumberOfElements>
+ <UserVisible>true</UserVisible>
+ </Channel>
+ <Channel name="RX FIFO 4">
+ <BaseAddressTag>NiLvFpgaFIFO 4</BaseAddressTag>
+ <ControlSet>4</ControlSet>
+ <DataType>
+ <Delta>1.000000000000000000000000000000000000000000000000000000</Delta>
+ <IntegerWordLength>64</IntegerWordLength>
+ <Maximum>18446744073709551600.00000000000000000000000000000000000</Maximum>
+ <Minimum>0.000000000000000000000000000000000000000000000000000000</Minimum>
+ <Signed>false</Signed>
+ <SubType>U64</SubType>
+ <WordLength>64</WordLength>
+ </DataType>
+ <Direction>TargetToHost</Direction>
+ <Implementation>niFpgaTargetToHost</Implementation>
+ <Number>4</Number>
+ <NumberOfElements>1023</NumberOfElements>
+ <UserVisible>true</UserVisible>
+ </Channel>
+ <Channel name="RX FIFO 5">
+ <BaseAddressTag>NiLvFpgaFIFO 5</BaseAddressTag>
+ <ControlSet>5</ControlSet>
+ <DataType>
+ <Delta>1.000000000000000000000000000000000000000000000000000000</Delta>
+ <IntegerWordLength>64</IntegerWordLength>
+ <Maximum>18446744073709551600.00000000000000000000000000000000000</Maximum>
+ <Minimum>0.000000000000000000000000000000000000000000000000000000</Minimum>
+ <Signed>false</Signed>
+ <SubType>U64</SubType>
+ <WordLength>64</WordLength>
+ </DataType>
+ <Direction>TargetToHost</Direction>
+ <Implementation>niFpgaTargetToHost</Implementation>
+ <Number>5</Number>
+ <NumberOfElements>1023</NumberOfElements>
+ <UserVisible>true</UserVisible>
+ </Channel>
+ <Channel name="TX FIFO 0">
+ <BaseAddressTag>NiLvFpgaFIFO 6</BaseAddressTag>
+ <ControlSet>6</ControlSet>
+ <DataType>
+ <Delta>1.000000000000000000000000000000000000000000000000000000</Delta>
+ <IntegerWordLength>64</IntegerWordLength>
+ <Maximum>18446744073709551600.00000000000000000000000000000000000</Maximum>
+ <Minimum>0.000000000000000000000000000000000000000000000000000000</Minimum>
+ <Signed>false</Signed>
+ <SubType>U64</SubType>
+ <WordLength>64</WordLength>
+ </DataType>
+ <Direction>HostToTarget</Direction>
+ <Implementation>niFpgaHostToTarget</Implementation>
+ <Number>6</Number>
+ <NumberOfElements>1029</NumberOfElements>
+ <UserVisible>true</UserVisible>
+ </Channel>
+ <Channel name="TX FIFO 1">
+ <BaseAddressTag>NiLvFpgaFIFO 7</BaseAddressTag>
+ <ControlSet>7</ControlSet>
+ <DataType>
+ <Delta>1.000000000000000000000000000000000000000000000000000000</Delta>
+ <IntegerWordLength>64</IntegerWordLength>
+ <Maximum>18446744073709551600.00000000000000000000000000000000000</Maximum>
+ <Minimum>0.000000000000000000000000000000000000000000000000000000</Minimum>
+ <Signed>false</Signed>
+ <SubType>U64</SubType>
+ <WordLength>64</WordLength>
+ </DataType>
+ <Direction>HostToTarget</Direction>
+ <Implementation>niFpgaHostToTarget</Implementation>
+ <Number>7</Number>
+ <NumberOfElements>1029</NumberOfElements>
+ <UserVisible>true</UserVisible>
+ </Channel>
+ <Channel name="TX FIFO 2">
+ <BaseAddressTag>NiLvFpgaFIFO 8</BaseAddressTag>
+ <ControlSet>8</ControlSet>
+ <DataType>
+ <Delta>1.000000000000000000000000000000000000000000000000000000</Delta>
+ <IntegerWordLength>64</IntegerWordLength>
+ <Maximum>18446744073709551600.00000000000000000000000000000000000</Maximum>
+ <Minimum>0.000000000000000000000000000000000000000000000000000000</Minimum>
+ <Signed>false</Signed>
+ <SubType>U64</SubType>
+ <WordLength>64</WordLength>
+ </DataType>
+ <Direction>HostToTarget</Direction>
+ <Implementation>niFpgaHostToTarget</Implementation>
+ <Number>8</Number>
+ <NumberOfElements>1029</NumberOfElements>
+ <UserVisible>true</UserVisible>
+ </Channel>
+ <Channel name="TX FIFO 3">
+ <BaseAddressTag>NiLvFpgaFIFO 9</BaseAddressTag>
+ <ControlSet>9</ControlSet>
+ <DataType>
+ <Delta>1.000000000000000000000000000000000000000000000000000000</Delta>
+ <IntegerWordLength>64</IntegerWordLength>
+ <Maximum>18446744073709551600.00000000000000000000000000000000000</Maximum>
+ <Minimum>0.000000000000000000000000000000000000000000000000000000</Minimum>
+ <Signed>false</Signed>
+ <SubType>U64</SubType>
+ <WordLength>64</WordLength>
+ </DataType>
+ <Direction>HostToTarget</Direction>
+ <Implementation>niFpgaHostToTarget</Implementation>
+ <Number>9</Number>
+ <NumberOfElements>1029</NumberOfElements>
+ <UserVisible>true</UserVisible>
+ </Channel>
+ <Channel name="TX FIFO 4">
+ <BaseAddressTag>NiLvFpgaFIFO 10</BaseAddressTag>
+ <ControlSet>10</ControlSet>
+ <DataType>
+ <Delta>1.000000000000000000000000000000000000000000000000000000</Delta>
+ <IntegerWordLength>64</IntegerWordLength>
+ <Maximum>18446744073709551600.00000000000000000000000000000000000</Maximum>
+ <Minimum>0.000000000000000000000000000000000000000000000000000000</Minimum>
+ <Signed>false</Signed>
+ <SubType>U64</SubType>
+ <WordLength>64</WordLength>
+ </DataType>
+ <Direction>HostToTarget</Direction>
+ <Implementation>niFpgaHostToTarget</Implementation>
+ <Number>10</Number>
+ <NumberOfElements>1029</NumberOfElements>
+ <UserVisible>true</UserVisible>
+ </Channel>
+ <Channel name="TX FIFO 5">
+ <BaseAddressTag>NiLvFpgaFIFO 11</BaseAddressTag>
+ <ControlSet>11</ControlSet>
+ <DataType>
+ <Delta>1.000000000000000000000000000000000000000000000000000000</Delta>
+ <IntegerWordLength>64</IntegerWordLength>
+ <Maximum>18446744073709551600.00000000000000000000000000000000000</Maximum>
+ <Minimum>0.000000000000000000000000000000000000000000000000000000</Minimum>
+ <Signed>false</Signed>
+ <SubType>U64</SubType>
+ <WordLength>64</WordLength>
+ </DataType>
+ <Direction>HostToTarget</Direction>
+ <Implementation>niFpgaHostToTarget</Implementation>
+ <Number>11</Number>
+ <NumberOfElements>1029</NumberOfElements>
+ <UserVisible>true</UserVisible>
+ </Channel>
+ </DmaChannelAllocationList>
+ <RegisterBlockList>
+ <RegisterBlock name="NiLvFpgaFIFO 0">
+ <Offset>0xFF80</Offset>
+ </RegisterBlock>
+ <RegisterBlock name="NiLvFpgaFIFO 1">
+ <Offset>0xFF40</Offset>
+ </RegisterBlock>
+ <RegisterBlock name="NiLvFpgaFIFO 2">
+ <Offset>0xFF00</Offset>
+ </RegisterBlock>
+ <RegisterBlock name="NiLvFpgaFIFO 3">
+ <Offset>0xFEC0</Offset>
+ </RegisterBlock>
+ <RegisterBlock name="NiLvFpgaFIFO 4">
+ <Offset>0xFE80</Offset>
+ </RegisterBlock>
+ <RegisterBlock name="NiLvFpgaFIFO 5">
+ <Offset>0xFE40</Offset>
+ </RegisterBlock>
+ <RegisterBlock name="NiLvFpgaFIFO 6">
+ <Offset>0xFE00</Offset>
+ </RegisterBlock>
+ <RegisterBlock name="NiLvFpgaFIFO 7">
+ <Offset>0xFDC0</Offset>
+ </RegisterBlock>
+ <RegisterBlock name="NiLvFpgaFIFO 8">
+ <Offset>0xFD80</Offset>
+ </RegisterBlock>
+ <RegisterBlock name="NiLvFpgaFIFO 9">
+ <Offset>0xFD40</Offset>
+ </RegisterBlock>
+ <RegisterBlock name="NiLvFpgaFIFO 10">
+ <Offset>0xFD00</Offset>
+ </RegisterBlock>
+ <RegisterBlock name="NiLvFpgaFIFO 11">
+ <Offset>0xFCC0</Offset>
+ </RegisterBlock>
+ </RegisterBlockList>
+ <UsedBaseClockList>
+ <BaseClock name="ReliableClkIn">
+ </BaseClock>
+ <BaseClock name="ChinchClk">
+ </BaseClock>
+ <BaseClock name="40 MHz Onboard Clock">
+ </BaseClock>
+ </UsedBaseClockList>
+ <version>1</version>
+ </NiFpga>
+ </CompilationResults>
+ </CompilationResultsTree>
+ <MultipleUserClocks>false</MultipleUserClocks>
+ <AllowImplicitEnableRemoval>false</AllowImplicitEnableRemoval>
+ </Project>
+ <ClientData/>
+ <BitstreamMD5>a72ba1716893a0bd02f88dac3ab28e1b</BitstreamMD5>
+ <Bitstream>a72ba1716893a0bd02f88dac3ab28e1b</Bitstream>
+</Bitfile>
diff --git a/host/lib/transport/nirio/nifpga_lvbitx.cpp b/host/lib/transport/nirio/nifpga_lvbitx.cpp
new file mode 100644
index 000000000..289a44d4a
--- /dev/null
+++ b/host/lib/transport/nirio/nifpga_lvbitx.cpp
@@ -0,0 +1,138 @@
+//
+// Copyright 2013-2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <uhd/transport/nirio/nifpga_lvbitx.h>
+#include <string>
+#include <iostream>
+#include <fstream>
+#include <streambuf>
+#include <boost/foreach.hpp>
+#include <boost/format.hpp>
+#include <boost/filesystem.hpp>
+#include <boost/algorithm/string.hpp>
+#include <boost/regex.hpp>
+
+namespace uhd { namespace niusrprio {
+
+std::string nifpga_lvbitx::_get_bitstream_checksum(const std::string& file_path)
+{
+ std::string checksum;
+ std::ifstream lvbitx_stream(file_path.c_str());
+ if (lvbitx_stream.is_open()) {
+ std::string lvbitx_contents;
+ lvbitx_stream.seekg(0, std::ios::end);
+ lvbitx_contents.reserve(static_cast<size_t>(lvbitx_stream.tellg()));
+ lvbitx_stream.seekg(0, std::ios::beg);
+ lvbitx_contents.assign((std::istreambuf_iterator<char>(lvbitx_stream)), std::istreambuf_iterator<char>());
+ try {
+ boost::smatch md5_match;
+ if (boost::regex_search(lvbitx_contents, md5_match, boost::regex("<BitstreamMD5>([a-zA-Z0-9]{32})<\\/BitstreamMD5>", boost::regex::icase))) {
+ checksum = std::string(md5_match[1].first, md5_match[1].second);
+ }
+ } catch (boost::exception&) {
+ checksum = "";
+ }
+ }
+ boost::to_upper(checksum);
+ return checksum;
+}
+
+#ifdef UHD_PLATFORM_WIN32
+#include <windows.h>
+
+std::string _get_path_from_registry(const std::string& registry_key_path)
+{
+ boost::smatch reg_key_match;
+ //If a substring in the search path is enclosed in [] (square brackets) then it is interpreted as a registry path
+ if (not boost::regex_search(registry_key_path, reg_key_match, boost::regex("\\[(.+)\\](.*)", boost::regex::icase)))
+ return std::string();
+ std::string reg_key_path = std::string(reg_key_match[1].first, reg_key_match[1].second);
+ std::string path_suffix = std::string(reg_key_match[2].first, reg_key_match[2].second);
+
+ //Split the registry path into parent, key-path and value.
+ boost::smatch reg_parent_match;
+ if (not boost::regex_search(reg_key_path, reg_parent_match, boost::regex("^(.+?)\\\\(.+)\\\\(.+)$", boost::regex::icase)))
+ return std::string();
+ std::string reg_parent = std::string(reg_parent_match[1].first, reg_parent_match[1].second);
+ std::string reg_path = std::string(reg_parent_match[2].first, reg_parent_match[2].second);
+ std::string reg_val_name = std::string(reg_parent_match[3].first, reg_parent_match[3].second);
+
+ HKEY hkey_parent = HKEY_LOCAL_MACHINE;
+ if (reg_parent == "HKEY_LOCAL_MACHINE") hkey_parent = HKEY_LOCAL_MACHINE;
+ else if (reg_parent == "HKEY_CURRENT_USER") hkey_parent = HKEY_CURRENT_USER;
+ else if (reg_parent == "HKEY_CLASSES_ROOT") hkey_parent = HKEY_CLASSES_ROOT;
+ else if (reg_parent == "HKEY_CURRENT_CONFIG") hkey_parent = HKEY_CURRENT_CONFIG;
+ else if (reg_parent == "HKEY_USERS") hkey_parent = HKEY_CURRENT_USER;
+
+ TCHAR value_buff[1024];
+ DWORD value_buff_size = 1024*sizeof(TCHAR);
+
+ //Get a handle to the key location
+ HKEY hkey_location;
+ if (RegOpenKeyExA(hkey_parent, reg_path.c_str(), NULL, KEY_QUERY_VALUE, &hkey_location) != ERROR_SUCCESS)
+ return std::string();
+
+ //Query key value
+ DWORD dw_type = REG_SZ;
+ if(RegQueryValueExA(hkey_location, reg_val_name.c_str(), NULL, &dw_type, (LPBYTE)value_buff, &value_buff_size) == ERROR_SUCCESS) {
+ RegCloseKey(hkey_location);
+ if (value_buff_size >= 1024*sizeof(TCHAR)) {
+ return std::string();
+ } else {
+ std::string return_value(value_buff, value_buff_size-1); //value_buff_size includes the null terminator
+ return_value += path_suffix;
+ return return_value;
+ }
+ } else {
+ return std::string();
+ }
+}
+
+#endif /*UHD_PLATFORM_WIN32*/
+
+std::string nifpga_lvbitx::_get_fpga_images_dir(const std::string search_paths)
+{
+ std::vector<std::string> search_path_vtr;
+ boost::split(search_path_vtr, search_paths, boost::is_any_of(","));
+
+ std::string lvbitx_dir;
+ //Traverse through the list of search paths. Priority: lexical
+ BOOST_FOREACH(std::string& search_path, search_path_vtr) {
+ boost::algorithm::trim(search_path);
+ if (search_path.empty()) continue;
+
+#ifdef UHD_PLATFORM_WIN32
+ lvbitx_dir = _get_path_from_registry(search_path);
+ if (lvbitx_dir.empty()) {
+ //Could not read from the registry due to missing key, invalid values, etc
+ //Just use the search path. The is_directory check will fail if this is a
+ //registry path and we will move on to the next item in the list.
+ lvbitx_dir = search_path;
+ }
+#else
+ lvbitx_dir = search_path;
+#endif
+
+ //If the current directory exists then stop traversing the search path list.
+ if (boost::filesystem::is_directory(lvbitx_dir)) break;
+ }
+
+ return lvbitx_dir;
+}
+
+
+}}
diff --git a/host/lib/transport/nirio/nirio_driver_iface_linux.cpp b/host/lib/transport/nirio/nirio_driver_iface_linux.cpp
new file mode 100644
index 000000000..b1f4bb49f
--- /dev/null
+++ b/host/lib/transport/nirio/nirio_driver_iface_linux.cpp
@@ -0,0 +1,111 @@
+//
+// Copyright 2013 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <uhd/transport/nirio/nirio_driver_iface.h>
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+
+namespace nirio_driver_iface {
+
+nirio_status rio_open(
+ const std::string& device_path,
+ rio_dev_handle_t& device_handle)
+{
+ device_handle = ::open(device_path.c_str(), O_RDWR | O_CLOEXEC);
+ return (device_handle < 0) ? NiRio_Status_InvalidParameter : NiRio_Status_Success;
+}
+
+void rio_close(rio_dev_handle_t& device_handle)
+{
+ ::close(device_handle);
+ device_handle = -1;
+}
+
+bool rio_isopen(rio_dev_handle_t device_handle)
+{
+ return (device_handle >= 0);
+}
+
+nirio_status rio_ioctl(
+ rio_dev_handle_t device_handle,
+ uint32_t ioctl_code,
+ const void *write_buf,
+ size_t write_buf_len,
+ void *read_buf,
+ size_t read_buf_len)
+{
+ nirio_ioctl_block_t ioctl_block = {0,0,0,0,0,0};
+
+ // two-casts necessary to prevent pointer sign-extension
+ ioctl_block.inBuf = static_cast<uint64_t>(reinterpret_cast<uintptr_t>(write_buf));
+ ioctl_block.inBufLength = write_buf_len;
+ ioctl_block.outBuf = static_cast<uint64_t>(reinterpret_cast<uintptr_t>(read_buf));
+ ioctl_block.outBufLength = read_buf_len;
+
+ int status = ::ioctl(device_handle, ioctl_code, &ioctl_block);
+ if (status == -1) {
+ switch (errno) {
+ case EINVAL: return NiRio_Status_InvalidParameter;
+ case EFAULT: return NiRio_Status_MemoryFull;
+ default: return NiRio_Status_SoftwareFault;
+ }
+ } else {
+ return NiRio_Status_Success;
+ }
+}
+
+nirio_status rio_mmap(
+ rio_dev_handle_t device_handle,
+ uint16_t memory_type,
+ size_t size,
+ bool writable,
+ rio_mmap_t &map)
+{
+ int access_mode = PROT_READ; //Write-only mode not supported
+ if (writable) access_mode |= PROT_WRITE;
+ map.addr = ::mmap(NULL, size, access_mode, MAP_SHARED, device_handle, (off_t) memory_type * sysconf(_SC_PAGESIZE));
+ map.size = size;
+
+ if (map.addr == MAP_FAILED) {
+ map.addr = NULL;
+ map.size = 0;
+ switch (errno) {
+ case EINVAL: return NiRio_Status_InvalidParameter;
+ case EFAULT: return NiRio_Status_MemoryFull;
+ default: return NiRio_Status_SoftwareFault;
+ }
+ }
+ return NiRio_Status_Success;
+}
+
+nirio_status rio_munmap(rio_mmap_t &map)
+{
+ nirio_status status = 0;
+ if (map.addr != NULL) {
+ status = ::munmap(map.addr, map.size);
+
+ map.addr = NULL;
+ map.size = 0;
+ }
+ return status;
+}
+
+}
diff --git a/host/lib/transport/nirio/nirio_driver_iface_macos.cpp b/host/lib/transport/nirio/nirio_driver_iface_macos.cpp
new file mode 100644
index 000000000..1a1142525
--- /dev/null
+++ b/host/lib/transport/nirio/nirio_driver_iface_macos.cpp
@@ -0,0 +1,63 @@
+//
+// Copyright 2013 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+#include <uhd/transport/nirio/nirio_driver_iface.h>
+
+namespace nirio_driver_iface {
+
+nirio_status rio_open(
+ const std::string& device_path,
+ rio_dev_handle_t& device_handle)
+{
+ return NiRio_Status_FeatureNotSupported;
+}
+
+void rio_close(rio_dev_handle_t& device_handle)
+{
+}
+
+bool rio_isopen(rio_dev_handle_t device_handle)
+{
+ return false;
+}
+
+nirio_status rio_ioctl(
+ rio_dev_handle_t device_handle,
+ uint32_t ioctl_code,
+ const void *write_buf,
+ size_t write_buf_len,
+ void *read_buf,
+ size_t read_buf_len)
+{
+ return NiRio_Status_FeatureNotSupported;
+}
+
+nirio_status rio_mmap(
+ rio_dev_handle_t device_handle,
+ uint16_t memory_type,
+ size_t size,
+ bool writable,
+ rio_mmap_t &map)
+{
+ return NiRio_Status_FeatureNotSupported;
+}
+
+nirio_status rio_munmap(rio_mmap_t &map)
+{
+ return NiRio_Status_FeatureNotSupported;
+}
+
+}
diff --git a/host/lib/transport/nirio/nirio_driver_iface_win.cpp b/host/lib/transport/nirio/nirio_driver_iface_win.cpp
new file mode 100644
index 000000000..b47c6ce85
--- /dev/null
+++ b/host/lib/transport/nirio/nirio_driver_iface_win.cpp
@@ -0,0 +1,148 @@
+//
+// Copyright 2013 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <uhd/transport/nirio/nirio_driver_iface.h>
+#include <process.h>
+
+#define NIRIO_IOCTL_MAP_MEMORY CTL_CODE(FILE_DEVICE_UNKNOWN, 0xF00, METHOD_BUFFERED, (FILE_READ_ACCESS | FILE_WRITE_ACCESS))
+#define NIRIO_IOCTL_UNMAP_MEMORY CTL_CODE(FILE_DEVICE_UNKNOWN, 0xF01, METHOD_BUFFERED, (FILE_READ_ACCESS | FILE_WRITE_ACCESS))
+
+namespace nirio_driver_iface {
+
+nirio_status rio_open(
+ const std::string& device_path,
+ rio_dev_handle_t& device_handle)
+{
+ device_handle = CreateFileA(device_path.c_str(), GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, /* default security */
+ OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL /* template file */);
+
+ return (device_handle == INVALID_HANDLE_VALUE) ? NiRio_Status_InvalidParameter : NiRio_Status_Success;
+}
+
+void rio_close(rio_dev_handle_t& device_handle)
+{
+ ::CloseHandle(device_handle);
+ device_handle = INVALID_HANDLE_VALUE;
+}
+
+bool rio_isopen(rio_dev_handle_t device_handle)
+{
+ return (device_handle != INVALID_HANDLE_VALUE);
+}
+
+nirio_status rio_ioctl(
+ rio_dev_handle_t device_handle,
+ uint32_t ioctl_code,
+ const void *write_buf,
+ size_t write_buf_len,
+ void *read_buf,
+ size_t read_buf_len)
+{
+ if (!rio_isopen(device_handle)) return NiRio_Status_ResourceNotInitialized;
+
+ /* Note, if the file handle was opened with the OVERLAPPED flag, you must
+ * supply an OVERLAPPED structure to ReadFile, WriteFile, and
+ * DeviceIoControl, even when doing synchronous IO. */
+ OVERLAPPED zeroedOverlapped = {0};
+ DWORD outLen = 0;
+ int_fast32_t lastError = 0;
+
+ if (!(DeviceIoControl(device_handle, ioctl_code,
+ const_cast<void*>(write_buf), static_cast<DWORD>(write_buf_len),
+ read_buf, static_cast<DWORD>(read_buf_len),
+ &outLen, &zeroedOverlapped )))
+ {
+ lastError = GetLastError();
+ return NiRio_Status_SoftwareFault;
+ }
+
+ return NiRio_Status_Success;
+}
+
+unsigned int __stdcall memory_map_thread_routine(void *context)
+{
+ rio_mmap_threadargs_t *args = (rio_mmap_threadargs_t*)context;
+ args->status = rio_ioctl(args->device_handle, NIRIO_IOCTL_MAP_MEMORY, &(args->params), sizeof(args->params), NULL, 0);
+ if (nirio_status_fatal(args->status))
+ {
+ SetEvent(reinterpret_cast<HANDLE>(args->params.map_ready_event_handle));
+ }
+ return 0;
+}
+
+nirio_status rio_mmap(
+ rio_dev_handle_t device_handle,
+ uint16_t memory_type,
+ size_t size,
+ bool writable,
+ rio_mmap_t &map)
+{
+ if (!rio_isopen(device_handle)) return NiRio_Status_ResourceNotInitialized;
+
+ access_mode_t access_mode = writable ? ACCESS_MODE_WRITE : ACCESS_MODE_READ;
+
+ uint64_t mapped_addr = 0;
+ map.map_thread_args.device_handle = device_handle;
+ map.map_thread_args.status = NiRio_Status_Success;
+ map.map_thread_args.params.memoryType = memory_type;
+ map.map_thread_args.params.size = (uint32_t)size;
+ map.map_thread_args.params.mapped_va_ptr = reinterpret_cast<uintptr_t>(&mapped_addr);
+ map.map_thread_args.params.access_mode = (uint8_t)access_mode;
+ HANDLE map_ready_event_handle = CreateEventA(NULL, TRUE, FALSE, NULL);
+ if (map_ready_event_handle == NULL) {
+ map.addr = NULL;
+ return NiRio_Status_SoftwareFault;
+ }
+ map.map_thread_args.params.map_ready_event_handle = reinterpret_cast<uint64_t>(map_ready_event_handle);
+ map.map_thread_handle = (HANDLE) _beginthreadex(NULL, 0, memory_map_thread_routine, &(map.map_thread_args), 0, NULL);
+
+ nirio_status status = NiRio_Status_Success;
+ if (map.map_thread_handle == NULL) {
+ map.addr = NULL;
+ return NiRio_Status_SoftwareFault;
+ } else {
+ WaitForSingleObject(map_ready_event_handle, INFINITE);
+ map.addr = reinterpret_cast<void*>(mapped_addr);
+ if (map.addr == NULL) {
+ WaitForSingleObject(map.map_thread_handle, INFINITE);
+ CloseHandle(map.map_thread_handle);
+ nirio_status_chain(map.map_thread_args.status, status);
+ }
+ }
+ CloseHandle(map_ready_event_handle);
+ return status;
+}
+
+nirio_status rio_munmap(rio_mmap_t &map)
+{
+ if (!rio_isopen(map.map_thread_args.device_handle)) return NiRio_Status_ResourceNotInitialized;
+
+ nirio_status status = NiRio_Status_Success;
+ if (map.addr != NULL) {
+ uint64_t mapped_addr = reinterpret_cast<uintptr_t>(map.addr);
+ status = rio_ioctl(map.map_thread_args.device_handle, NIRIO_IOCTL_UNMAP_MEMORY, &mapped_addr, sizeof(mapped_addr), NULL, 0);
+ if (nirio_status_not_fatal(status)) {
+ WaitForSingleObject(map.map_thread_handle, INFINITE);
+ }
+ CloseHandle(map.map_thread_handle);
+ map.addr = NULL;
+ }
+ return status;
+}
+
+}
diff --git a/host/lib/transport/nirio/nirio_resource_manager.cpp b/host/lib/transport/nirio/nirio_resource_manager.cpp
new file mode 100644
index 000000000..85e789087
--- /dev/null
+++ b/host/lib/transport/nirio/nirio_resource_manager.cpp
@@ -0,0 +1,120 @@
+//
+// Copyright 2013 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+
+#include <uhd/transport/nirio/nirio_resource_manager.h>
+
+#ifdef __clang__
+ #pragma GCC diagnostic push ignored "-Wmissing-field-initializers"
+#elif defined(__GNUC__)
+ #pragma GCC diagnostic ignored "-Wmissing-field-initializers"
+#endif
+
+namespace uhd { namespace niusrprio
+{
+
+nirio_resource_manager::nirio_resource_manager(
+ niriok_proxy& proxy) : _kernel_proxy(proxy), _fifo_info_map(), _reg_info_map()
+{
+}
+
+nirio_resource_manager::~nirio_resource_manager()
+{
+ finalize();
+}
+
+nirio_status nirio_resource_manager::initialize(
+ const nirio_register_info_vtr& reg_info_vtr,
+ const nirio_fifo_info_vtr& fifo_info_vtr)
+{
+ nirio_status status = 0;
+ for (nirio_fifo_info_vtr::const_iterator it = fifo_info_vtr.begin(); it != fifo_info_vtr.end(); it++) {
+ const nirio_fifo_info_t& fifo_info = *it;
+ status = _add_fifo_resource(fifo_info);
+ if (nirio_status_fatal(status)) return status;
+
+ _fifo_info_map.insert(fifo_info_map_t::value_type(fifo_info.name, fifo_info));
+ }
+ for (nirio_register_info_vtr::const_iterator it = reg_info_vtr.begin(); it != reg_info_vtr.end(); it++) {
+ const nirio_register_info_t& reg_info = *it;
+
+ _reg_info_map.insert(register_info_map_t::value_type(reg_info.name, reg_info));
+ }
+ return _set_driver_config();
+}
+
+void nirio_resource_manager::finalize()
+{
+ _fifo_info_map.clear();
+}
+
+nirio_status nirio_resource_manager::get_register_offset(
+ const char* register_name,
+ uint32_t& offset)
+{
+ register_info_map_t::const_iterator it = _reg_info_map.find(fifo_info_map_t::key_type(register_name));
+ if (it == _reg_info_map.end()) return NiRio_Status_InvalidParameter;
+
+ offset = (*it).second.offset;
+
+ return NiRio_Status_Success;
+}
+
+
+nirio_status nirio_resource_manager::_add_fifo_resource(
+ const nirio_fifo_info_t& fifo_info)
+{
+ nirio_driver_iface::nirio_syncop_in_params_t in = {};
+ nirio_driver_iface::nirio_syncop_out_params_t out = {};
+
+ in.function = nirio_driver_iface::NIRIO_FUNC::ADD_RESOURCE;
+ in.subfunction = (fifo_info.direction == OUTPUT_FIFO) ?
+ nirio_driver_iface::NIRIO_RESOURCE::OUTPUT_FIFO :
+ nirio_driver_iface::NIRIO_RESOURCE::INPUT_FIFO;
+
+ in.params.add.fifoWithDataType.channel = fifo_info.channel;
+ in.params.add.fifoWithDataType.baseAddress = fifo_info.base_addr;
+ in.params.add.fifoWithDataType.depthInSamples = fifo_info.depth;
+ in.params.add.fifoWithDataType.scalarType = fifo_info.scalar_type;
+ in.params.add.fifoWithDataType.bitWidth = fifo_info.width;
+ in.params.add.fifoWithDataType.version = fifo_info.version;
+
+ return _kernel_proxy.sync_operation(&in, sizeof(in), &out, sizeof(out));
+}
+
+nirio_status nirio_resource_manager::_set_driver_config()
+{
+ nirio_driver_iface::nirio_syncop_in_params_t in = {};
+ nirio_driver_iface::nirio_syncop_out_params_t out = {};
+ in.function = nirio_driver_iface::NIRIO_FUNC::SET_DRIVER_CONFIG;
+ in.subfunction = 0;
+
+ return _kernel_proxy.sync_operation(&in, sizeof(in), &out, sizeof(out));
+}
+
+nirio_fifo_info_t* nirio_resource_manager::_lookup_fifo_info(const char* fifo_name) {
+ fifo_info_map_t::iterator it = _fifo_info_map.find(fifo_info_map_t::key_type(fifo_name));
+ if (it == _fifo_info_map.end()) return NULL;
+
+ return &((*it).second);
+}
+
+}}
+
+#ifdef __GNUC__
+ #pragma GCC diagnostic pop
+#endif
diff --git a/host/lib/transport/nirio/niriok_proxy.cpp b/host/lib/transport/nirio/niriok_proxy.cpp
new file mode 100644
index 000000000..031623c9a
--- /dev/null
+++ b/host/lib/transport/nirio/niriok_proxy.cpp
@@ -0,0 +1,300 @@
+//
+// Copyright 2013-2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+
+#include <uhd/transport/nirio/niriok_proxy.h>
+#include <cstring>
+
+#define NI_VENDOR_NUM 0x1093
+
+#define VERSION_BUILD_SHIFT 0
+#define VERSION_PHASE_SHIFT 14
+#define VERSION_MAINT_SHIFT 16
+#define VERSION_UPGRD_SHIFT 20
+#define VERSION_MAJOR_SHIFT 24
+#define VERSION_BUILD_MASK 0x00003FFF
+#define VERSION_PHASE_MASK 0x0000C000
+#define VERSION_MAINT_MASK 0x000F0000
+#define VERSION_UPGRD_MASK 0x00F00000
+#define VERSION_MAJOR_MASK 0xFF000000
+
+#define GET_FIFO_MEMORY_TYPE(fifo_inst) (static_cast<uint16_t>(0x0100 | static_cast<uint16_t>(fifo_inst)))
+
+#ifdef __clang__
+ #pragma GCC diagnostic push ignored "-Wmissing-field-initializers"
+#elif defined(__GNUC__)
+ #pragma GCC diagnostic ignored "-Wmissing-field-initializers"
+#endif
+
+namespace uhd { namespace niusrprio
+{
+ //-------------------------------------------------------
+ // niriok_proxy
+ //-------------------------------------------------------
+ niriok_proxy::niriok_proxy(): _device_handle(nirio_driver_iface::INVALID_RIO_HANDLE)
+ {
+ }
+
+ niriok_proxy::~niriok_proxy()
+ {
+ close();
+ }
+
+ nirio_status niriok_proxy::open(const std::string& interface_path)
+ {
+ if (interface_path.empty()) return NiRio_Status_ResourceNotFound;
+
+ //close if already open.
+ close();
+
+ nirio_status status = NiRio_Status_Success;
+ nirio_status_chain(nirio_driver_iface::rio_open(
+ interface_path, _device_handle), status);
+ if (nirio_status_not_fatal(status)) {
+ nirio_status_chain(nirio_driver_iface::rio_ioctl(_device_handle,
+ nirio_driver_iface::NIRIO_IOCTL_POST_OPEN,
+ NULL, 0, NULL, 0), status);
+ nirio_driver_iface::nirio_ioctl_packet_t out(&_interface_num, sizeof(_interface_num), 0);
+ nirio_status_chain(nirio_driver_iface::rio_ioctl(_device_handle,
+ nirio_driver_iface::NIRIO_IOCTL_GET_IFACE_NUM,
+ NULL, 0,
+ &out, sizeof(out)), status);
+
+ if (nirio_status_fatal(status)) close();
+ }
+ return status;
+ }
+
+ void niriok_proxy::close(void)
+ {
+ if(nirio_driver_iface::rio_isopen(_device_handle))
+ {
+ nirio_driver_iface::rio_ioctl(
+ _device_handle, nirio_driver_iface::NIRIO_IOCTL_PRE_CLOSE, NULL, 0, NULL, 0);
+ nirio_driver_iface::rio_close(_device_handle);
+ }
+ }
+
+ nirio_status niriok_proxy::reset()
+ {
+ nirio_driver_iface::nirio_syncop_in_params_t in = {};
+ nirio_driver_iface::nirio_syncop_out_params_t out = {};
+
+ in.function = nirio_driver_iface::NIRIO_FUNC::RESET;
+
+ return sync_operation(&in, sizeof(in), &out, sizeof(out));
+ }
+
+ nirio_status niriok_proxy::get_cached_session(
+ uint32_t& session)
+ {
+ nirio_driver_iface::nirio_ioctl_packet_t out(&session, sizeof(session), 0);
+ return nirio_driver_iface::rio_ioctl(_device_handle,
+ nirio_driver_iface::NIRIO_IOCTL_GET_SESSION,
+ NULL, 0,
+ &out, sizeof(out));
+ }
+
+ nirio_status niriok_proxy::get_version(
+ nirio_version_t type,
+ uint32_t& major,
+ uint32_t& upgrade,
+ uint32_t& maintenance,
+ char& phase,
+ uint32_t& build)
+ {
+ nirio_device_attr_32_t version_attr = (type==CURRENT)?CURRENT_VERSION:OLDEST_COMPATIBLE_VERSION;
+ uint32_t raw_version = 0;
+ nirio_status status = get_attribute(version_attr, raw_version);
+
+ major = (raw_version & VERSION_MAJOR_MASK) >> VERSION_MAJOR_SHIFT;
+ upgrade = (raw_version & VERSION_UPGRD_MASK) >> VERSION_UPGRD_SHIFT;
+ maintenance = (raw_version & VERSION_MAINT_MASK) >> VERSION_MAINT_SHIFT;
+ build = (raw_version & VERSION_BUILD_MASK) >> VERSION_BUILD_SHIFT;
+
+ uint32_t phase_num = (raw_version & VERSION_PHASE_MASK) >> VERSION_PHASE_SHIFT;
+ switch (phase_num) {
+ case 0: phase = 'd'; break;
+ case 1: phase = 'a'; break;
+ case 2: phase = 'b'; break;
+ case 3: phase = 'f'; break;
+ }
+
+ return status;
+ }
+
+ nirio_status niriok_proxy::sync_operation(
+ const void *writeBuffer,
+ size_t writeBufferLength,
+ void *readBuffer,
+ size_t readBufferLength)
+ {
+ nirio_driver_iface::nirio_ioctl_packet_t out(readBuffer, readBufferLength, 0);
+ nirio_status ioctl_status = nirio_driver_iface::rio_ioctl(_device_handle,
+ nirio_driver_iface::NIRIO_IOCTL_SYNCOP,
+ writeBuffer, writeBufferLength,
+ &out, sizeof(out));
+ if (nirio_status_fatal(ioctl_status)) return ioctl_status;
+
+ return out.statusCode;
+ }
+
+ nirio_status niriok_proxy::get_attribute(
+ const nirio_device_attr_32_t attribute,
+ uint32_t& attrValue)
+ {
+ nirio_driver_iface::nirio_syncop_in_params_t in = {};
+ nirio_driver_iface::nirio_syncop_out_params_t out = {};
+
+ in.function = nirio_driver_iface::NIRIO_FUNC::GET32;
+ in.params.attribute32.attribute = attribute;
+
+ nirio_status status = sync_operation(&in, sizeof(in), &out, sizeof(out));
+
+ attrValue = out.params.attribute32.value;
+ return status;
+ }
+
+ nirio_status niriok_proxy::get_attribute(
+ const nirio_device_attr_str_t attribute,
+ char *buf,
+ const uint32_t bufLen,
+ uint32_t& stringLen)
+ {
+ nirio_driver_iface::nirio_syncop_in_params_t in = {};
+ nirio_driver_iface::nirio_syncop_out_params_t out = {};
+ nirio_driver_iface::init_syncop_out_params(out, buf, bufLen);
+
+ in.function = nirio_driver_iface::NIRIO_FUNC::GET_STRING;
+ in.params.attributeStr.attribute = attribute;
+
+ nirio_status status = sync_operation(&in, sizeof(in), &out, sizeof(out));
+
+ stringLen = out.params.stringLength;
+ return status;
+ }
+
+ nirio_status niriok_proxy::set_attribute(
+ const nirio_device_attr_32_t attribute,
+ const uint32_t value)
+ {
+ nirio_driver_iface::nirio_syncop_in_params_t in = {};
+ nirio_driver_iface::nirio_syncop_out_params_t out = {};
+
+ in.function = nirio_driver_iface::NIRIO_FUNC::SET32;
+ in.params.attribute32.attribute = attribute;
+ in.params.attribute32.value = value;
+
+ return sync_operation(&in, sizeof(in), &out, sizeof(out));
+ }
+
+ nirio_status niriok_proxy::set_attribute(
+ const nirio_device_attr_str_t attribute,
+ const char* const buffer)
+ {
+ nirio_driver_iface::nirio_syncop_in_params_t in = {};
+ nirio_driver_iface::init_syncop_in_params(in, buffer, strlen(buffer) + 1);
+ nirio_driver_iface::nirio_syncop_out_params_t out = {};
+
+ in.function = nirio_driver_iface::NIRIO_FUNC::SET_STRING;
+ in.params.attributeStr.attribute = attribute;
+
+ return sync_operation(&in, sizeof(in), &out, sizeof(out));
+ }
+
+ nirio_status niriok_proxy::peek(uint32_t offset, uint32_t& value)
+ {
+ if (offset % 4 != 0) return NiRio_Status_MisalignedAccess;
+
+ nirio_driver_iface::nirio_syncop_in_params_t in = {};
+ nirio_driver_iface::nirio_syncop_out_params_t out = {};
+
+ in.function = nirio_driver_iface::NIRIO_FUNC::IO;
+ in.subfunction = nirio_driver_iface::NIRIO_IO::PEEK32;
+ in.params.io.offset = offset;
+
+ nirio_status status = sync_operation(&in, sizeof(in), &out, sizeof(out));
+ value = out.params.io.value.value32;
+ return status;
+ }
+
+ nirio_status niriok_proxy::peek(uint32_t offset, uint64_t& value)
+ {
+ if (offset % 8 != 0) return NiRio_Status_MisalignedAccess;
+
+ nirio_driver_iface::nirio_syncop_in_params_t in = {};
+ nirio_driver_iface::nirio_syncop_out_params_t out = {};
+
+ in.function = nirio_driver_iface::NIRIO_FUNC::IO;
+ in.subfunction = nirio_driver_iface::NIRIO_IO::PEEK64;
+ in.params.io.offset = offset;
+
+ nirio_status status = sync_operation(&in, sizeof(in), &out, sizeof(out));
+ value = out.params.io.value.value64;
+ return status;
+ }
+
+ nirio_status niriok_proxy::poke(uint32_t offset, const uint32_t& value)
+ {
+ if (offset % 4 != 0) return NiRio_Status_MisalignedAccess;
+
+ nirio_driver_iface::nirio_syncop_in_params_t in = {};
+ nirio_driver_iface::nirio_syncop_out_params_t out = {};
+
+ in.function = nirio_driver_iface::NIRIO_FUNC::IO;
+ in.subfunction = nirio_driver_iface::NIRIO_IO::POKE32;
+ in.params.io.offset = offset;
+ in.params.io.value.value32 = value;
+
+ return sync_operation(&in, sizeof(in), &out, sizeof(out));
+ }
+
+ nirio_status niriok_proxy::poke(uint32_t offset, const uint64_t& value)
+ {
+ if (offset % 8 != 0) return NiRio_Status_MisalignedAccess;
+
+ nirio_driver_iface::nirio_syncop_in_params_t in = {};
+ nirio_driver_iface::nirio_syncop_out_params_t out = {};
+
+ in.function = nirio_driver_iface::NIRIO_FUNC::IO;
+ in.subfunction = nirio_driver_iface::NIRIO_IO::POKE64;
+ in.params.io.offset = offset;
+ in.params.io.value.value64 = value;
+
+ return sync_operation(&in, sizeof(in), &out, sizeof(out));
+ }
+
+ nirio_status niriok_proxy::map_fifo_memory(
+ uint32_t fifo_instance,
+ size_t size,
+ nirio_driver_iface::rio_mmap_t& map)
+ {
+ return nirio_driver_iface::rio_mmap(_device_handle,
+ GET_FIFO_MEMORY_TYPE(fifo_instance),
+ size, true, map);
+ }
+
+ nirio_status niriok_proxy::unmap_fifo_memory(
+ nirio_driver_iface::rio_mmap_t& map)
+ {
+ return nirio_driver_iface::rio_munmap(map);
+ }
+}}
+
+#ifdef __GNUC__
+ #pragma GCC diagnostic pop
+#endif
diff --git a/host/lib/transport/nirio/niusrprio_session.cpp b/host/lib/transport/nirio/niusrprio_session.cpp
new file mode 100644
index 000000000..a07bc4fdf
--- /dev/null
+++ b/host/lib/transport/nirio/niusrprio_session.cpp
@@ -0,0 +1,199 @@
+//
+// Copyright 2013 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+
+#include <uhd/transport/nirio/niusrprio_session.h>
+#include <uhd/transport/nirio/nirio_fifo.h>
+#include <uhd/transport/nirio/status.h>
+#include <boost/format.hpp>
+#include <boost/algorithm/string.hpp>
+#include <boost/lexical_cast.hpp>
+#include <stdio.h>
+#include <fstream>
+//@TODO: Move the register defs required by the class to a common location
+#include "../../usrp/x300/x300_regs.hpp"
+
+namespace uhd { namespace niusrprio {
+
+niusrprio_session::niusrprio_session(const std::string& resource_name, const std::string& rpc_port_name) :
+ _resource_name(resource_name),
+ _session_open(false),
+ _resource_manager(_riok_proxy),
+ _rpc_client("localhost", rpc_port_name)
+{
+}
+
+niusrprio_session::~niusrprio_session()
+{
+ close();
+}
+
+nirio_status niusrprio_session::enumerate(const std::string& rpc_port_name, device_info_vtr& device_info_vtr)
+{
+ usrprio_rpc::usrprio_rpc_client temp_rpc_client("localhost", rpc_port_name);
+ nirio_status status = temp_rpc_client.get_ctor_status();
+ nirio_status_chain(temp_rpc_client.niusrprio_enumerate(device_info_vtr), status);
+ return status;
+}
+
+nirio_status niusrprio_session::open(
+ nifpga_lvbitx::sptr lvbitx,
+ bool force_download)
+{
+ boost::unique_lock<boost::recursive_mutex> lock(_session_mutex);
+
+ _lvbitx = lvbitx;
+
+ nirio_status status = NiRio_Status_Success;
+ std::string bitfile_path(_lvbitx->get_bitfile_path());
+ std::string signature(_lvbitx->get_signature());
+
+ //Make sure that the RPC client connected to the server properly
+ nirio_status_chain(_rpc_client.get_ctor_status(), status);
+ //Get a handle to the kernel driver
+ nirio_status_chain(_rpc_client.niusrprio_get_interface_path(_resource_name, _interface_path), status);
+ nirio_status_chain(_riok_proxy.open(_interface_path), status);
+
+ if (nirio_status_not_fatal(status)) {
+ //Bitfile build for a particular LVFPGA interface will have the same signature
+ //because the API of the bitfile does not change. Two files with the same signature
+ //can however have different bitstreams because of non-LVFPGA code differences.
+ //That is why we need another identifier to qualify the signature. The BIN
+ //checksum is a good candidate.
+ std::string lvbitx_checksum(_lvbitx->get_bitstream_checksum());
+ boost::uint16_t download_fpga = (force_download || (_read_bitstream_checksum() != lvbitx_checksum)) ? 1 : 0;
+
+ nirio_status_chain(_rpc_client.niusrprio_open_session(
+ _resource_name, bitfile_path, signature, download_fpga), status);
+ _session_open = nirio_status_not_fatal(status);
+
+ if (nirio_status_not_fatal(status)) {
+ nirio_register_info_vtr reg_vtr;
+ nirio_fifo_info_vtr fifo_vtr;
+ _lvbitx->init_register_info(reg_vtr);
+ _lvbitx->init_fifo_info(fifo_vtr);
+ _resource_manager.initialize(reg_vtr, fifo_vtr);
+
+ nirio_status_chain(_verify_signature(), status);
+ nirio_status_chain(_write_bitstream_checksum(lvbitx_checksum), status);
+ }
+ }
+
+ return status;
+}
+
+void niusrprio_session::close(bool skip_reset)
+{
+ boost::unique_lock<boost::recursive_mutex> lock(_session_mutex);
+
+ if (_session_open) {
+ nirio_status status = NiRio_Status_Success;
+ if (!skip_reset) reset();
+ nirio_status_chain(_rpc_client.niusrprio_close_session(_resource_name), status);
+ _session_open = false;
+ }
+}
+
+nirio_status niusrprio_session::reset()
+{
+ boost::unique_lock<boost::recursive_mutex> lock(_session_mutex);
+ return _rpc_client.niusrprio_reset_device(_resource_name);
+}
+
+nirio_status niusrprio_session::download_bitstream_to_flash(const std::string& bitstream_path)
+{
+ boost::unique_lock<boost::recursive_mutex> lock(_session_mutex);
+ return _rpc_client.niusrprio_download_fpga_to_flash(_resource_name, bitstream_path);
+}
+
+niriok_proxy::sptr niusrprio_session::create_kernel_proxy(
+ const std::string& resource_name,
+ const std::string& rpc_port_name)
+{
+ usrprio_rpc::usrprio_rpc_client temp_rpc_client("localhost", rpc_port_name);
+ nirio_status status = temp_rpc_client.get_ctor_status();
+
+ std::string interface_path;
+ nirio_status_chain(temp_rpc_client.niusrprio_get_interface_path(resource_name, interface_path), status);
+
+ niriok_proxy::sptr proxy;
+ if (nirio_status_not_fatal(status)) {
+ proxy.reset(new niriok_proxy());
+ if (proxy) nirio_status_chain(proxy->open(interface_path), status);
+ }
+
+ return proxy;
+}
+
+nirio_status niusrprio_session::_verify_signature()
+{
+ //Validate the signature using the kernel proxy
+ nirio_status status = NiRio_Status_Success;
+ boost::uint32_t sig_offset = 0;
+ nirio_status_chain(_riok_proxy.get_attribute(DEFAULT_FPGA_SIGNATURE_OFFSET, sig_offset), status);
+ niriok_scoped_addr_space(_riok_proxy, FPGA, status);
+ std::string signature;
+ for (boost::uint32_t i = 0; i < 8; i++) {
+ boost::uint32_t quarter_sig;
+ nirio_status_chain(_riok_proxy.peek(sig_offset, quarter_sig), status);
+ signature += boost::str(boost::format("%08x") % quarter_sig);
+ }
+
+ std::string expected_signature(_lvbitx->get_signature());
+ boost::to_upper(signature);
+ boost::to_upper(expected_signature);
+ if (signature.find(expected_signature) == std::string::npos) {
+ nirio_status_chain(NiRio_Status_SignatureMismatch, status);
+ }
+
+ return status;
+}
+
+std::string niusrprio_session::_read_bitstream_checksum()
+{
+ nirio_status status = NiRio_Status_Success;
+ niriok_scoped_addr_space(_riok_proxy, BUS_INTERFACE, status);
+ std::string usr_signature;
+ for (boost::uint32_t i = 0; i < FPGA_USR_SIG_REG_SIZE; i+=4) {
+ boost::uint32_t quarter_sig;
+ nirio_status_chain(_riok_proxy.peek(FPGA_USR_SIG_REG_BASE + i, quarter_sig), status);
+ usr_signature += boost::str(boost::format("%08x") % quarter_sig);
+ }
+ boost::to_upper(usr_signature);
+
+ return usr_signature;
+}
+
+nirio_status niusrprio_session::_write_bitstream_checksum(const std::string& checksum)
+{
+ nirio_status status = NiRio_Status_Success;
+ niriok_scoped_addr_space(_riok_proxy, BUS_INTERFACE, status);
+ for (boost::uint32_t i = 0; i < FPGA_USR_SIG_REG_SIZE; i+=4) {
+ boost::uint32_t quarter_sig;
+ try {
+ std::stringstream ss;
+ ss << std::hex << checksum.substr(i*2,8);
+ ss >> quarter_sig;
+ } catch (std::exception&) {
+ quarter_sig = 0;
+ }
+ nirio_status_chain(_riok_proxy.poke(FPGA_USR_SIG_REG_BASE + i, quarter_sig), status);
+ }
+ return status;
+}
+
+}}
diff --git a/host/lib/transport/nirio/rpc/CMakeLists.txt b/host/lib/transport/nirio/rpc/CMakeLists.txt
new file mode 100644
index 000000000..02c16d2ff
--- /dev/null
+++ b/host/lib/transport/nirio/rpc/CMakeLists.txt
@@ -0,0 +1,29 @@
+#
+# Copyright 2013 Ettus Research LLC
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+########################################################################
+# This file included, use CMake directory variables
+########################################################################
+
+########################################################################
+# Append to the list of sources for lib uhd
+########################################################################
+
+LIBUHD_APPEND_SOURCES(
+ ${CMAKE_CURRENT_SOURCE_DIR}/rpc_client.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/usrprio_rpc_client.cpp
+)
diff --git a/host/lib/transport/nirio/rpc/rpc_client.cpp b/host/lib/transport/nirio/rpc/rpc_client.cpp
new file mode 100644
index 000000000..a5f8cf412
--- /dev/null
+++ b/host/lib/transport/nirio/rpc/rpc_client.cpp
@@ -0,0 +1,201 @@
+///
+// Copyright 2013 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <uhd/transport/nirio/rpc/rpc_client.hpp>
+#include <boost/bind.hpp>
+#include <boost/format.hpp>
+
+#define CHAIN_BLOCKING_XFER(func, exp, status) \
+ if (status) { \
+ status = (static_cast<size_t>((func)) == exp); \
+ } else { \
+ UHD_LOG << "rpc_client operation skipped: " #func "\n"; \
+ } \
+
+namespace uhd { namespace usrprio_rpc {
+
+using boost::asio::ip::tcp;
+
+rpc_client::rpc_client (
+ const std::string& server,
+ const std::string& port,
+ boost::uint32_t process_id,
+ boost::uint32_t host_id
+) : _socket(_io_service)
+{
+ //Fill in handshake info
+ _hshake_args_client.version = CURRENT_VERSION;
+ _hshake_args_client.oldest_comp_version = OLDEST_COMPATIBLE_VERSION;
+ _hshake_args_client.client_id = build_client_id(host_id, process_id);
+ _hshake_args_client.boost_archive_version = boost_serialization_archive_utils::get_version();
+
+ try {
+ //Synchronous resolve + connect
+ tcp::resolver resolver(_io_service);
+ tcp::resolver::query query(tcp::v4(), server, port);
+ tcp::resolver::iterator iterator = resolver.resolve(query);
+ boost::asio::connect(_socket, iterator);
+ UHD_LOG << "rpc_client connected to server." << std::endl;
+
+ try {
+ //Perform handshake
+ bool status = true;
+ CHAIN_BLOCKING_XFER(
+ boost::asio::write(_socket, boost::asio::buffer(&_hshake_args_client, sizeof(_hshake_args_client))),
+ sizeof(_hshake_args_client), status);
+ CHAIN_BLOCKING_XFER(
+ boost::asio::read(_socket, boost::asio::buffer(&_hshake_args_server, sizeof(_hshake_args_server))),
+ sizeof(_hshake_args_server), status);
+
+ _request.header.client_id = _hshake_args_server.client_id;
+
+ if (_hshake_args_server.version >= _hshake_args_client.oldest_comp_version &&
+ _hshake_args_client.version >= _hshake_args_server.oldest_comp_version &&
+ status)
+ {
+ UHD_LOG << "rpc_client bound to server." << std::endl;
+ _wait_for_next_response_header();
+
+ //Spawn a thread for the io_service callback handler. This thread will run until rpc_client is destroyed.
+ _io_service_thread.reset(new boost::thread(boost::bind(&boost::asio::io_service::run, &_io_service)));
+ } else {
+ UHD_LOG << "rpc_client handshake failed." << std::endl;
+ _exec_err.assign(boost::asio::error::connection_refused, boost::system::system_category());
+ }
+ UHD_LOG << boost::format("rpc_client archive = %d, rpc_server archive = %d\n.") %
+ _hshake_args_client.boost_archive_version %
+ _hshake_args_server.boost_archive_version;
+ } catch (boost::exception&) {
+ UHD_LOG << "rpc_client handshake aborted." << std::endl;
+ _exec_err.assign(boost::asio::error::connection_refused, boost::system::system_category());
+ }
+ } catch (boost::exception&) {
+ UHD_LOG << "rpc_client connection request cancelled/aborted." << std::endl;
+ _exec_err.assign(boost::asio::error::connection_aborted, boost::system::system_category());
+ }
+}
+
+rpc_client::~rpc_client () {
+ _stop_io_service();
+}
+
+const boost::system::error_code& rpc_client::call(
+ func_id_t func_id,
+ const func_args_writer_t& in_args,
+ func_args_reader_t &out_args,
+ boost::posix_time::milliseconds timeout
+)
+{
+ boost::mutex::scoped_lock lock(_mutex);
+
+ if (_io_service_thread.get()) {
+ _request.header.func_id = func_id;
+ in_args.store(_request.data);
+ _request.header.func_args_size = _request.data.size();
+
+ _exec_err.clear();
+
+ //Send function call header and args
+ bool status = true;
+ try {
+ CHAIN_BLOCKING_XFER(
+ boost::asio::write(_socket, boost::asio::buffer(&_request.header, sizeof(_request.header))),
+ sizeof(_request.header), status);
+ CHAIN_BLOCKING_XFER(
+ boost::asio::write(_socket, boost::asio::buffer(&(*_request.data.begin()), _request.data.size())),
+ _request.data.size(), status);
+ } catch (boost::exception&) {
+ status = false;
+ }
+
+ //Wait for response using condition variable
+ if (status) {
+ if (!_exec_gate.timed_wait(lock, timeout)) {
+ UHD_LOG << "rpc_client function timed out." << std::endl;
+ _exec_err.assign(boost::asio::error::timed_out, boost::system::system_category());
+ }
+ } else {
+ UHD_LOG << "rpc_client connection dropped." << std::endl;
+ _exec_err.assign(boost::asio::error::connection_aborted, boost::system::system_category());
+ _stop_io_service();
+ }
+
+ //Verify that we are talking to the correct endpoint
+ if ((_request.header.client_id != _response.header.client_id) && !_exec_err) {
+ UHD_LOG << "rpc_client confused about who its talking to." << std::endl;
+ _exec_err.assign(boost::asio::error::operation_aborted, boost::system::system_category());
+ }
+
+ if (!_exec_err) out_args.load(_response.data);
+ }
+
+ return _exec_err;
+}
+
+void rpc_client::_handle_response_hdr(const boost::system::error_code& err, size_t transferred, size_t expected)
+{
+ boost::mutex::scoped_lock lock(_mutex);
+ _exec_err = err;
+ if (!_exec_err && (transferred == expected)) {
+ //Response header received. Verify that it is expected
+ if (func_args_header_t::match_function(_request.header, _response.header)) {
+ _response.data.resize(_response.header.func_args_size);
+
+ //Wait for response data
+ boost::asio::async_read(_socket,
+ boost::asio::buffer(&(*_response.data.begin()), _response.data.size()),
+ boost::bind(&rpc_client::_handle_response_data, this,
+ boost::asio::placeholders::error,
+ boost::asio::placeholders::bytes_transferred,
+ _response.data.size()));
+ } else {
+ //Unexpected response. Ignore it.
+ UHD_LOG << "rpc_client received garbage responses." << std::endl;
+ _exec_err.assign(boost::asio::error::operation_aborted, boost::system::system_category());
+
+ _wait_for_next_response_header();
+ }
+ }
+
+ if (_exec_err) _exec_gate.notify_all();
+}
+
+void rpc_client::_handle_response_data(const boost::system::error_code& err, size_t transferred, size_t expected)
+{
+ boost::mutex::scoped_lock lock(_mutex);
+ _exec_err = err;
+ if (transferred != expected) {
+ _exec_err.assign(boost::asio::error::operation_aborted, boost::system::system_category());
+ }
+
+ _exec_gate.notify_all();
+
+ _wait_for_next_response_header();
+}
+
+void rpc_client::_wait_for_next_response_header() {
+ //_mutex must be locked when this call is made
+ boost::asio::async_read(
+ _socket,
+ boost::asio::buffer(&_response.header, sizeof(_response.header)),
+ boost::bind(&rpc_client::_handle_response_hdr, this,
+ boost::asio::placeholders::error,
+ boost::asio::placeholders::bytes_transferred,
+ sizeof(_response.header)));
+}
+
+}}
diff --git a/host/lib/transport/nirio/rpc/usrprio_rpc_client.cpp b/host/lib/transport/nirio/rpc/usrprio_rpc_client.cpp
new file mode 100644
index 000000000..1a1f1cd21
--- /dev/null
+++ b/host/lib/transport/nirio/rpc/usrprio_rpc_client.cpp
@@ -0,0 +1,229 @@
+//
+// Copyright 2013 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <uhd/transport/nirio/rpc/usrprio_rpc_client.hpp>
+#include <uhd/utils/platform.hpp>
+
+namespace uhd { namespace usrprio_rpc {
+
+usrprio_rpc_client::usrprio_rpc_client(
+ std::string server,
+ std::string port
+) : _rpc_client(server, port, uhd::get_process_id(), uhd::get_host_id()),
+ _timeout(boost::posix_time::milliseconds(DEFAULT_TIMEOUT_IN_MS))
+{
+ _ctor_status = _rpc_client.status() ? NiRio_Status_RpcConnectionError : NiRio_Status_Success;
+}
+
+usrprio_rpc_client::~usrprio_rpc_client()
+{
+}
+
+nirio_status usrprio_rpc_client::niusrprio_enumerate(NIUSRPRIO_ENUMERATE_ARGS)
+/*
+#define NIUSRPRIO_ENUMERATE_ARGS \
+ usrprio_device_info_vtr& device_info_vtr
+*/
+{
+ usrprio_rpc::func_args_writer_t in_args;
+ usrprio_rpc::func_args_reader_t out_args;
+ nirio_status status = NiRio_Status_Success;
+ boost::uint32_t vtr_size = 0;
+
+ status = _boost_error_to_nirio_status(
+ _rpc_client.call(NIUSRPRIO_ENUMERATE, in_args, out_args, _timeout));
+
+ if (nirio_status_not_fatal(status)) {
+ out_args >> status;
+ out_args >> vtr_size;
+ }
+ if (nirio_status_not_fatal(status) && vtr_size > 0) {
+ device_info_vtr.resize(vtr_size);
+ for (size_t i = 0; i < (size_t)vtr_size; i++) {
+ usrprio_device_info info;
+ out_args >> info;
+ device_info_vtr[i] = info;
+ }
+ }
+ return status;
+}
+
+nirio_status usrprio_rpc_client::niusrprio_open_session(NIUSRPRIO_OPEN_SESSION_ARGS)
+/*
+#define NIUSRPRIO_OPEN_SESSION_ARGS \
+ const std::string& resource, \
+ const std::string& path, \
+ const std::string& signature, \
+ const boost::uint16_t& download_fpga
+*/
+{
+ usrprio_rpc::func_args_writer_t in_args;
+ usrprio_rpc::func_args_reader_t out_args;
+ nirio_status status = NiRio_Status_Success;
+
+ in_args << resource;
+ in_args << path;
+ in_args << signature;
+ in_args << download_fpga;
+
+ //Open needs a longer timeout because the FPGA download can take upto 6 secs and the NiFpga libload can take 4.
+ static const boost::uint32_t OPEN_TIMEOUT = 15000;
+ status = _boost_error_to_nirio_status(
+ _rpc_client.call(NIUSRPRIO_OPEN_SESSION, in_args, out_args, boost::posix_time::milliseconds(OPEN_TIMEOUT)));
+
+ if (nirio_status_not_fatal(status)) {
+ out_args >> status;
+ }
+
+ return status;
+}
+
+nirio_status usrprio_rpc_client::niusrprio_close_session(NIUSRPRIO_CLOSE_SESSION_ARGS)
+/*
+#define NIUSRPRIO_CLOSE_SESSION_ARGS \
+ const std::string& resource
+*/
+{
+ usrprio_rpc::func_args_writer_t in_args;
+ usrprio_rpc::func_args_reader_t out_args;
+ nirio_status status = NiRio_Status_Success;
+
+ in_args << resource;
+
+ status = _boost_error_to_nirio_status(
+ _rpc_client.call(NIUSRPRIO_CLOSE_SESSION, in_args, out_args, _timeout));
+
+ if (nirio_status_not_fatal(status)) {
+ out_args >> status;
+ }
+
+ return status;
+}
+
+nirio_status usrprio_rpc_client::niusrprio_reset_device(NIUSRPRIO_RESET_SESSION_ARGS)
+/*
+#define NIUSRPRIO_RESET_SESSION_ARGS \
+ const std::string& resource
+*/
+{
+ usrprio_rpc::func_args_writer_t in_args;
+ usrprio_rpc::func_args_reader_t out_args;
+ nirio_status status = NiRio_Status_Success;
+
+ in_args << resource;
+
+ status = _boost_error_to_nirio_status(
+ _rpc_client.call(NIUSRPRIO_RESET_SESSION, in_args, out_args, _timeout));
+
+ if (nirio_status_not_fatal(status)) {
+ out_args >> status;
+ }
+
+ return status;
+}
+
+nirio_status usrprio_rpc_client::niusrprio_get_interface_path(NIUSRPRIO_GET_INTERFACE_PATH_ARGS)
+/*
+#define NIUSRPRIO_GET_INTERFACE_PATH_ARGS \
+ const std::string& resource, \
+ std::string& interface_path
+*/
+{
+ usrprio_rpc::func_args_writer_t in_args;
+ usrprio_rpc::func_args_reader_t out_args;
+ nirio_status status = NiRio_Status_Success;
+
+ in_args << resource;
+
+ status = _boost_error_to_nirio_status(
+ _rpc_client.call(NIUSRPRIO_GET_INTERFACE_PATH, in_args, out_args, _timeout));
+
+ if (nirio_status_not_fatal(status)) {
+ out_args >> status;
+ out_args >> interface_path;
+ }
+
+ return status;
+}
+
+nirio_status usrprio_rpc_client::niusrprio_download_fpga_to_flash(NIUSRPRIO_DOWNLOAD_FPGA_TO_FLASH_ARGS)
+/*
+#define NIUSRPRIO_DOWNLOAD_FPGA_TO_FLASH_ARGS \
+ const boost::uint32_t& interface_num, \
+ const std::string& bitstream_path
+*/
+{
+ usrprio_rpc::func_args_writer_t in_args;
+ usrprio_rpc::func_args_reader_t out_args;
+ nirio_status status = NiRio_Status_Success;
+
+ in_args << resource;
+ in_args << bitstream_path;
+
+ static const boost::uint32_t DOWNLOAD_FPGA_TIMEOUT = 1200000;
+ status = _boost_error_to_nirio_status(
+ _rpc_client.call(NIUSRPRIO_DOWNLOAD_FPGA_TO_FLASH, in_args, out_args,
+ boost::posix_time::milliseconds(DOWNLOAD_FPGA_TIMEOUT)));
+
+ if (nirio_status_not_fatal(status)) {
+ out_args >> status;
+ }
+
+ return status;
+}
+
+nirio_status usrprio_rpc_client::niusrprio_download_bitstream_to_fpga(NIUSRPRIO_DOWNLOAD_BITSTREAM_TO_FPGA_ARGS)
+/*
+#define NIUSRPRIO_DOWNLOAD_BITSTREAM_TO_FPGA_ARGS \
+ const std::string& resource
+*/
+{
+ usrprio_rpc::func_args_writer_t in_args;
+ usrprio_rpc::func_args_reader_t out_args;
+ nirio_status status = NiRio_Status_Success;
+
+ in_args << resource;
+
+ status = _boost_error_to_nirio_status(
+ _rpc_client.call(NIUSRPRIO_DOWNLOAD_BITSTREAM_TO_FPGA, in_args, out_args, _timeout));
+
+ if (nirio_status_not_fatal(status)) {
+ out_args >> status;
+ }
+
+ return status;
+}
+
+nirio_status usrprio_rpc_client::_boost_error_to_nirio_status(const boost::system::error_code& err) {
+ if (err) {
+ switch (err.value()) {
+ case boost::asio::error::connection_aborted:
+ case boost::asio::error::connection_refused:
+ case boost::asio::error::eof:
+ return NiRio_Status_RpcSessionError;
+ case boost::asio::error::timed_out:
+ case boost::asio::error::operation_aborted:
+ return NiRio_Status_RpcOperationError;
+ default:
+ return NiRio_Status_SoftwareFault;
+ }
+ } else {
+ return NiRio_Status_Success;
+ }
+}
+
+}}
diff --git a/host/lib/transport/nirio/status.cpp b/host/lib/transport/nirio/status.cpp
new file mode 100644
index 000000000..f3f8d4cd1
--- /dev/null
+++ b/host/lib/transport/nirio/status.cpp
@@ -0,0 +1,55 @@
+//
+// Copyright 2013 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+
+#include <uhd/transport/nirio/status.h>
+#include <boost/format.hpp>
+
+namespace uhd { namespace niusrprio {
+
+#define NIRIO_ERR_INFO(CONST_NAME, ERR_CODE, ERR_MSG) \
+ nirio_err_info(ERR_CODE, ERR_MSG),
+
+const nirio_err_info nirio_err_info::NIRIO_ERROR_TABLE[] = {
+ #include "../../../include/uhd/transport/nirio/nirio_err_template.h"
+};
+
+#undef NIRIO_ERR_INFO
+
+const size_t nirio_err_info::NIRIO_ERROR_TABLE_SIZE = sizeof(NIRIO_ERROR_TABLE)/sizeof(*NIRIO_ERROR_TABLE);
+
+const std::string lookup_err_msg(nirio_status code) {
+ std::string error_msg = (boost::format("Unknown error. (Error code %d)") % code).str();
+ for (size_t i = 0; i < nirio_err_info::NIRIO_ERROR_TABLE_SIZE; i++) {
+ if (nirio_err_info::NIRIO_ERROR_TABLE[i].code == code) {
+ error_msg = (boost::format("%s (Error code %d)") % nirio_err_info::NIRIO_ERROR_TABLE[i].msg % code).str();
+ break;
+ }
+ }
+ return error_msg;
+}
+
+void nirio_status_to_exception(const nirio_status& status, const std::string& message) {
+ if (nirio_status_fatal(status)) {
+ throw uhd::runtime_error((boost::format("%s %s") % message % lookup_err_msg(status)).str());
+ }
+}
+
+}}
+
+
+
diff --git a/host/lib/transport/nirio_zero_copy.cpp b/host/lib/transport/nirio_zero_copy.cpp
new file mode 100644
index 000000000..7b1e32fe0
--- /dev/null
+++ b/host/lib/transport/nirio_zero_copy.cpp
@@ -0,0 +1,352 @@
+//
+// Copyright 2013-2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <uhd/transport/nirio_zero_copy.hpp>
+#include <stdio.h>
+#include <uhd/transport/nirio/nirio_fifo.h>
+#include <uhd/transport/nirio/nirio_fifo.h>
+#include <uhd/transport/buffer_pool.hpp>
+#include <uhd/utils/msg.hpp>
+#include <uhd/utils/log.hpp>
+#include <uhd/utils/atomic.hpp>
+#include <boost/format.hpp>
+#include <boost/make_shared.hpp>
+#include <boost/thread/thread.hpp> //sleep
+#include <vector>
+#include <algorithm> // std::max
+//@TODO: Move the register defs required by the class to a common location
+#include "../usrp/x300/x300_regs.hpp"
+
+using namespace uhd;
+using namespace uhd::transport;
+using namespace uhd::niusrprio;
+
+typedef uint64_t fifo_data_t;
+
+class nirio_zero_copy_mrb : public managed_recv_buffer
+{
+public:
+ nirio_zero_copy_mrb(nirio_fifo<fifo_data_t>& fifo, const size_t frame_size):
+ _fifo(fifo), _frame_size(frame_size) { }
+
+ void release(void)
+ {
+ _fifo.release(_frame_size / sizeof(fifo_data_t));
+ }
+
+ UHD_INLINE sptr get_new(const double timeout, size_t &index)
+ {
+ nirio_status status = 0;
+ size_t elems_acquired, elems_remaining;
+ nirio_status_chain(_fifo.acquire(
+ _typed_buffer, _frame_size / sizeof(fifo_data_t),
+ static_cast<uint32_t>(timeout*1000),
+ elems_acquired, elems_remaining), status);
+ _length = elems_acquired * sizeof(fifo_data_t);
+ _buffer = static_cast<void*>(_typed_buffer);
+
+ if (nirio_status_not_fatal(status)) {
+ index++; //Advances the caller's buffer
+ return make(this, _buffer, _length);
+ } else if (status == NiRio_Status_CommunicationTimeout) {
+ nirio_status_to_exception(status, "NI-RIO PCIe data transfer failed.");
+ return sptr();
+ } else {
+ return sptr(); //NULL for timeout or error.
+ }
+ }
+
+private:
+ nirio_fifo<fifo_data_t>& _fifo;
+ fifo_data_t* _typed_buffer;
+ const size_t _frame_size;
+ size_t _num_frames;
+};
+
+class nirio_zero_copy_msb : public managed_send_buffer
+{
+public:
+ nirio_zero_copy_msb(nirio_fifo<fifo_data_t>& fifo, const size_t frame_size):
+ _fifo(fifo), _frame_size(frame_size) { }
+
+ void release(void)
+ {
+ _fifo.release(_frame_size / sizeof(fifo_data_t));
+ }
+
+ UHD_INLINE sptr get_new(const double timeout, size_t &index)
+ {
+ nirio_status status = 0;
+ size_t elems_acquired, elems_remaining;
+ nirio_status_chain(_fifo.acquire(
+ _typed_buffer, _frame_size / sizeof(fifo_data_t),
+ static_cast<uint32_t>(timeout*1000),
+ elems_acquired, elems_remaining), status);
+ _length = elems_acquired * sizeof(fifo_data_t);
+ _buffer = static_cast<void*>(_typed_buffer);
+
+ if (nirio_status_not_fatal(status)) {
+ index++; //Advances the caller's buffer
+ return make(this, _buffer, _length);
+ } else if (status == NiRio_Status_CommunicationTimeout) {
+ nirio_status_to_exception(status, "NI-RIO PCIe data transfer failed.");
+ return sptr();
+ } else {
+ return sptr(); //NULL for timeout or error.
+ }
+ }
+
+private:
+ nirio_fifo<fifo_data_t>& _fifo;
+ fifo_data_t* _typed_buffer;
+ const size_t _frame_size;
+ size_t _num_frames;
+};
+
+class nirio_zero_copy_impl : public nirio_zero_copy {
+public:
+ typedef boost::shared_ptr<nirio_zero_copy_impl> sptr;
+
+ nirio_zero_copy_impl(
+ uhd::niusrprio::niusrprio_session::sptr fpga_session,
+ uint32_t instance,
+ const zero_copy_xport_params& xport_params
+ ):
+ _fpga_session(fpga_session),
+ _fifo_instance(instance),
+ _xport_params(xport_params),
+ _next_recv_buff_index(0), _next_send_buff_index(0)
+ {
+ UHD_LOG << boost::format("Creating PCIe transport for channel %d") % instance << std::endl;
+ UHD_LOG << boost::format("nirio zero-copy RX transport configured with frame size = %u, #frames = %u, buffer size = %u\n")
+ % _xport_params.recv_frame_size % _xport_params.num_recv_frames %
+ (_xport_params.recv_frame_size * _xport_params.num_recv_frames);
+ UHD_LOG << boost::format("nirio zero-copy TX transport configured with frame size = %u, #frames = %u, buffer size = %u\n")
+ % _xport_params.send_frame_size % _xport_params.num_send_frames % (_xport_params.send_frame_size * _xport_params.num_send_frames);
+
+ _recv_buffer_pool = buffer_pool::make(_xport_params.num_recv_frames, _xport_params.recv_frame_size);
+ _send_buffer_pool = buffer_pool::make(_xport_params.num_send_frames, _xport_params.send_frame_size);
+
+ nirio_status status = 0;
+ size_t actual_depth = 0, actual_size = 0;
+
+ //Configure frame width
+ nirio_status_chain(
+ _proxy().poke(PCIE_TX_DMA_REG(DMA_FRAME_SIZE_REG, _fifo_instance),
+ static_cast<uint32_t>(_xport_params.send_frame_size/sizeof(fifo_data_t))),
+ status);
+ nirio_status_chain(
+ _proxy().poke(PCIE_RX_DMA_REG(DMA_FRAME_SIZE_REG, _fifo_instance),
+ static_cast<uint32_t>(_xport_params.recv_frame_size/sizeof(fifo_data_t))),
+ status);
+ //Config 32-bit word flipping and Reset DMA streams
+ nirio_status_chain(
+ _proxy().poke(PCIE_TX_DMA_REG(DMA_CTRL_STATUS_REG, _fifo_instance),
+ DMA_CTRL_SW_BUF_U32 | DMA_CTRL_RESET),
+ status);
+ nirio_status_chain(
+ _proxy().poke(PCIE_RX_DMA_REG(DMA_CTRL_STATUS_REG, _fifo_instance),
+ DMA_CTRL_SW_BUF_U32 | DMA_CTRL_RESET),
+ status);
+
+ //Create FIFOs
+ nirio_status_chain(
+ _fpga_session->create_rx_fifo(_fifo_instance, _recv_fifo),
+ status);
+ nirio_status_chain(
+ _fpga_session->create_tx_fifo(_fifo_instance, _send_fifo),
+ status);
+
+ if ((_recv_fifo.get() != NULL) && (_send_fifo.get() != NULL)) {
+ //Initialize FIFOs
+ nirio_status_chain(
+ _recv_fifo->initialize(
+ (_xport_params.recv_frame_size*_xport_params.num_recv_frames)/sizeof(fifo_data_t),
+ actual_depth, actual_size),
+ status);
+ nirio_status_chain(
+ _send_fifo->initialize(
+ (_xport_params.send_frame_size*_xport_params.num_send_frames)/sizeof(fifo_data_t),
+ actual_depth, actual_size),
+ status);
+
+ _proxy().get_rio_quirks().add_tx_fifo(_fifo_instance);
+
+ nirio_status_chain(_recv_fifo->start(), status);
+ nirio_status_chain(_send_fifo->start(), status);
+
+ if (nirio_status_not_fatal(status)) {
+ //Flush RX kernel buffers in case some cruft was
+ //left behind from the last run
+ _flush_rx_buff();
+
+ //allocate re-usable managed receive buffers
+ for (size_t i = 0; i < get_num_recv_frames(); i++){
+ _mrb_pool.push_back(boost::shared_ptr<nirio_zero_copy_mrb>(new nirio_zero_copy_mrb(
+ *_recv_fifo, get_recv_frame_size())));
+ }
+
+ //allocate re-usable managed send buffers
+ for (size_t i = 0; i < get_num_send_frames(); i++){
+ _msb_pool.push_back(boost::shared_ptr<nirio_zero_copy_msb>(new nirio_zero_copy_msb(
+ *_send_fifo, get_send_frame_size())));
+ }
+ }
+ } else {
+ nirio_status_chain(NiRio_Status_ResourceNotInitialized, status);
+ }
+
+ nirio_status_to_exception(status, "Could not create nirio_zero_copy transport.");
+ }
+
+ virtual ~nirio_zero_copy_impl()
+ {
+ _proxy().get_rio_quirks().remove_tx_fifo(_fifo_instance);
+
+ //Reset DMA streams (Teardown, so don't status chain)
+ _proxy().poke(PCIE_TX_DMA_REG(DMA_CTRL_STATUS_REG, _fifo_instance), DMA_CTRL_RESET);
+ _proxy().poke(PCIE_RX_DMA_REG(DMA_CTRL_STATUS_REG, _fifo_instance), DMA_CTRL_RESET);
+
+ _flush_rx_buff();
+
+ //Stop DMA channels. Stop is called in the fifo dtor but
+ //it doesn't hurt to do it here.
+ _send_fifo->stop();
+ _recv_fifo->stop();
+ }
+
+ /*******************************************************************
+ * Receive implementation:
+ * Block on the managed buffer's get call and advance the index.
+ ******************************************************************/
+ managed_recv_buffer::sptr get_recv_buff(double timeout)
+ {
+ if (_next_recv_buff_index == _xport_params.num_recv_frames) _next_recv_buff_index = 0;
+ return _mrb_pool[_next_recv_buff_index]->get_new(timeout, _next_recv_buff_index);
+ }
+
+ size_t get_num_recv_frames(void) const {return _xport_params.num_recv_frames;}
+ size_t get_recv_frame_size(void) const {return _xport_params.recv_frame_size;}
+
+ /*******************************************************************
+ * Send implementation:
+ * Block on the managed buffer's get call and advance the index.
+ ******************************************************************/
+ managed_send_buffer::sptr get_send_buff(double timeout)
+ {
+ if (_next_send_buff_index == _xport_params.num_send_frames) _next_send_buff_index = 0;
+ return _msb_pool[_next_send_buff_index]->get_new(timeout, _next_send_buff_index);
+ }
+
+ size_t get_num_send_frames(void) const {return _xport_params.num_send_frames;}
+ size_t get_send_frame_size(void) const {return _xport_params.send_frame_size;}
+
+private:
+
+ UHD_INLINE niriok_proxy& _proxy() { return _fpga_session->get_kernel_proxy(); }
+
+ UHD_INLINE void _flush_rx_buff()
+ {
+ nirio_status flush_status = 0;
+ while (nirio_status_not_fatal(flush_status)) {
+ static const size_t NUM_ELEMS_TO_FLUSH = 1;
+ static const uint32_t FLUSH_TIMEOUT_IN_MS = 0;
+
+ fifo_data_t* flush_data_ptr = NULL;
+ size_t flush_elems_acquired = 0, flush_elems_remaining = 0;
+ flush_status = _recv_fifo->acquire(
+ flush_data_ptr, NUM_ELEMS_TO_FLUSH, FLUSH_TIMEOUT_IN_MS,
+ flush_elems_acquired, flush_elems_remaining);
+ if (nirio_status_not_fatal(flush_status)) {
+ _recv_fifo->release(flush_elems_acquired);
+ }
+ }
+ }
+
+ //memory management -> buffers and fifos
+ niusrprio::niusrprio_session::sptr _fpga_session;
+ uint32_t _fifo_instance;
+ nirio_fifo<fifo_data_t>::sptr _recv_fifo, _send_fifo;
+ const zero_copy_xport_params _xport_params;
+ buffer_pool::sptr _recv_buffer_pool, _send_buffer_pool;
+ std::vector<boost::shared_ptr<nirio_zero_copy_msb> > _msb_pool;
+ std::vector<boost::shared_ptr<nirio_zero_copy_mrb> > _mrb_pool;
+ size_t _next_recv_buff_index, _next_send_buff_index;
+};
+
+
+nirio_zero_copy::sptr nirio_zero_copy::make(
+ uhd::niusrprio::niusrprio_session::sptr fpga_session,
+ const uint32_t instance,
+ const zero_copy_xport_params& default_buff_args,
+ const device_addr_t &hints
+){
+ //Initialize xport_params
+ zero_copy_xport_params xport_params = default_buff_args;
+
+ //The kernel buffer for this transport must be (num_frames * frame_size) big. Unlike ethernet,
+ //where the kernel buffer size is independent of the circular buffer size for the transport,
+ //it is possible for users to over constrain the system when they set the num_frames and the buff_size
+ //So we give buff_size priority over num_frames and throw an error if they conflict.
+
+ //RX
+ xport_params.recv_frame_size = size_t(hints.cast<double>("recv_frame_size", default_buff_args.recv_frame_size));
+
+ size_t usr_num_recv_frames = static_cast<size_t>(
+ hints.cast<double>("num_recv_frames", default_buff_args.num_recv_frames));
+ size_t usr_recv_buff_size = static_cast<size_t>(
+ hints.cast<double>("recv_buff_size", default_buff_args.num_recv_frames));
+
+ if (hints.has_key("num_recv_frames") and hints.has_key("recv_buff_size")) {
+ if (usr_recv_buff_size < xport_params.recv_frame_size)
+ throw uhd::value_error("recv_buff_size must be equal to or greater than (num_recv_frames * recv_frame_size)");
+
+ if ((usr_recv_buff_size/xport_params.recv_frame_size) != usr_num_recv_frames)
+ throw uhd::value_error("Conflicting values for recv_buff_size and num_recv_frames");
+ }
+
+ if (hints.has_key("recv_buff_size")) {
+ xport_params.num_recv_frames = std::max<size_t>(1, usr_recv_buff_size/xport_params.recv_frame_size); //Round down
+ } else if (hints.has_key("num_recv_frames")) {
+ xport_params.num_recv_frames = usr_num_recv_frames;
+ }
+
+ //TX
+ xport_params.send_frame_size = size_t(hints.cast<double>("send_frame_size", default_buff_args.send_frame_size));
+
+ size_t usr_num_send_frames = static_cast<size_t>(
+ hints.cast<double>("num_send_frames", default_buff_args.num_send_frames));
+ size_t usr_send_buff_size = static_cast<size_t>(
+ hints.cast<double>("send_buff_size", default_buff_args.num_send_frames));
+
+ if (hints.has_key("num_send_frames") and hints.has_key("send_buff_size")) {
+ if (usr_send_buff_size < xport_params.send_frame_size)
+ throw uhd::value_error("send_buff_size must be equal to or greater than (num_send_frames * send_frame_size)");
+
+ if ((usr_send_buff_size/xport_params.send_frame_size) != usr_num_send_frames)
+ throw uhd::value_error("Conflicting values for send_buff_size and num_send_frames");
+ }
+
+ if (hints.has_key("send_buff_size")) {
+ xport_params.num_send_frames = std::max<size_t>(1, usr_send_buff_size/xport_params.send_frame_size); //Round down
+ } else if (hints.has_key("num_send_frames")) {
+ xport_params.num_send_frames = usr_num_send_frames;
+ }
+
+ return nirio_zero_copy::sptr(new nirio_zero_copy_impl(fpga_session, instance, xport_params));
+}
+
diff --git a/host/lib/transport/super_recv_packet_handler.hpp b/host/lib/transport/super_recv_packet_handler.hpp
index 5080182d6..5fdf2594d 100644
--- a/host/lib/transport/super_recv_packet_handler.hpp
+++ b/host/lib/transport/super_recv_packet_handler.hpp
@@ -39,6 +39,13 @@
#include <iostream>
#include <vector>
+// Included for debugging
+#ifdef UHD_TXRX_DEBUG_PRINTS
+#include <boost/format.hpp>
+#include <boost/thread/thread.hpp>
+#include "boost/date_time/posix_time/posix_time.hpp"
+#endif
+
namespace uhd{ namespace transport{ namespace sph{
UHD_INLINE boost::uint32_t get_context_code(
@@ -76,6 +83,10 @@ public:
_queue_error_for_next_call(false),
_buffers_infos_index(0)
{
+ #ifdef ERROR_INJECT_DROPPED_PACKETS
+ recvd_packets = 0;
+ #endif
+
this->resize(size);
set_alignment_failure_threshold(1000);
}
@@ -142,6 +153,32 @@ public:
}
/*!
+ * Flush all transports in the streamer:
+ * This calls into get_and_process_single_packet(),
+ * so the sequence and flow control are handled.
+ * However, the packet payload is discarded.
+ */
+ void flush_all(const double timeout = 0.0)
+ {
+ increment_buffer_info(); //increment to next buffer
+
+ for (size_t i = 0; i < _props.size(); i++)
+ {
+ while (true) //while (_props.at(i).get_buff(timeout));
+ {
+ //receive a single packet from the transport
+ try
+ {
+ if (get_and_process_single_packet(i,
+ get_prev_buffer_info(),
+ get_curr_buffer_info(),
+ timeout) == PACKET_TIMEOUT_ERROR) break;
+ }catch(...){}
+ }
+ }
+ }
+
+ /*!
* Set the function to handle flow control
* \param xport_chan which transport channel
* \param handle_flowctrl the callback function
@@ -213,7 +250,12 @@ public:
buffs, nsamps_per_buff, metadata, timeout
);
- if (one_packet) return accum_num_samps;
+ if (one_packet){
+#ifdef UHD_TXRX_DEBUG_PRINTS
+ dbg_gather_data(nsamps_per_buff, accum_num_samps, metadata, timeout, one_packet);
+#endif
+ return accum_num_samps;
+ }
//first recv had an error code set, return immediately
if (metadata.error_code != rx_metadata_t::ERROR_CODE_NONE) return accum_num_samps;
@@ -232,11 +274,13 @@ public:
}
accum_num_samps += num_samps;
}
+#ifdef UHD_TXRX_DEBUG_PRINTS
+ dbg_gather_data(nsamps_per_buff, accum_num_samps, metadata, timeout, one_packet);
+#endif
return accum_num_samps;
}
private:
-
vrt_unpacker_type _vrt_unpacker;
size_t _header_offset_words32;
double _tick_rate, _samp_rate;
@@ -264,6 +308,13 @@ private:
//! information stored for a received buffer
struct per_buffer_info_type{
+ void reset()
+ {
+ buff.reset();
+ vrt_hdr = NULL;
+ time = time_spec_t(0.0);
+ copy_buff = NULL;
+ }
managed_recv_buffer::sptr buff;
const boost::uint32_t *vrt_hdr;
vrt::if_packet_info_t ifpi;
@@ -280,6 +331,17 @@ private:
data_bytes_to_copy(0),
fragment_offset_in_samps(0)
{/* NOP */}
+ void reset()
+ {
+ indexes_todo.set();
+ alignment_time = time_spec_t(0.0);
+ alignment_time_valid = false;
+ data_bytes_to_copy = 0;
+ fragment_offset_in_samps = 0;
+ metadata.reset();
+ for (size_t i = 0; i < size(); i++)
+ at(i).reset();
+ }
boost::dynamic_bitset<> indexes_todo; //used in alignment logic
time_spec_t alignment_time; //used in alignment logic
bool alignment_time_valid; //used in alignment logic
@@ -305,6 +367,10 @@ private:
PACKET_SEQUENCE_ERROR
};
+ #ifdef ERROR_INJECT_DROPPED_PACKETS
+ int recvd_packets;
+ #endif
+
/*******************************************************************
* Get and process a single packet from the transport:
* Receive a single packet at the given index.
@@ -322,6 +388,16 @@ private:
buff = _props[index].get_buff(timeout);
if (buff.get() == NULL) return PACKET_TIMEOUT_ERROR;
+ #ifdef ERROR_INJECT_DROPPED_PACKETS
+ if (++recvd_packets > 1000)
+ {
+ recvd_packets = 0;
+ buff.reset();
+ buff = _props[index].get_buff(timeout);
+ if (buff.get() == NULL) return PACKET_TIMEOUT_ERROR;
+ }
+ #endif
+
//bounds check before extract
size_t num_packet_words32 = buff->size()/sizeof(boost::uint32_t);
if (num_packet_words32 <= _header_offset_words32){
@@ -339,7 +415,7 @@ private:
//handle flow control
if (_props[index].handle_flowctrl)
{
- if ((info.ifpi.packet_count % _props[index].fc_update_window/2) == 0)
+ if ((info.ifpi.packet_count % _props[index].fc_update_window) == 0)
{
_props[index].handle_flowctrl(info.ifpi.packet_count);
}
@@ -411,7 +487,10 @@ private:
******************************************************************/
UHD_INLINE void get_aligned_buffs(double timeout){
+ get_prev_buffer_info().reset(); // no longer need the previous info - reset it for future use
+
increment_buffer_info(); //increment to next buffer
+
buffers_info_type &prev_info = get_prev_buffer_info();
buffers_info_type &curr_info = get_curr_buffer_info();
buffers_info_type &next_info = get_next_buffer_info();
@@ -440,12 +519,6 @@ private:
"The receive packet handler caught an exception.\n%s"
) % e.what() << std::endl;
std::swap(curr_info, next_info); //save progress from curr -> next
- curr_info.metadata.has_time_spec = false;
- curr_info.metadata.time_spec = time_spec_t(0.0);
- curr_info.metadata.more_fragments = false;
- curr_info.metadata.fragment_offset = 0;
- curr_info.metadata.start_of_burst = false;
- curr_info.metadata.end_of_burst = false;
curr_info.metadata.error_code = rx_metadata_t::ERROR_CODE_BAD_PACKET;
return;
}
@@ -470,10 +543,6 @@ private:
std::swap(curr_info, next_info); //save progress from curr -> next
curr_info.metadata.has_time_spec = next_info[index].ifpi.has_tsf;
curr_info.metadata.time_spec = next_info[index].time;
- curr_info.metadata.more_fragments = false;
- curr_info.metadata.fragment_offset = 0;
- curr_info.metadata.start_of_burst = false;
- curr_info.metadata.end_of_burst = false;
curr_info.metadata.error_code = rx_metadata_t::error_code_t(get_context_code(next_info[index].vrt_hdr, next_info[index].ifpi));
if (curr_info.metadata.error_code == rx_metadata_t::ERROR_CODE_OVERFLOW){
_props[index].handle_overflow();
@@ -483,12 +552,6 @@ private:
case PACKET_TIMEOUT_ERROR:
std::swap(curr_info, next_info); //save progress from curr -> next
- curr_info.metadata.has_time_spec = false;
- curr_info.metadata.time_spec = time_spec_t(0.0);
- curr_info.metadata.more_fragments = false;
- curr_info.metadata.fragment_offset = 0;
- curr_info.metadata.start_of_burst = false;
- curr_info.metadata.end_of_burst = false;
curr_info.metadata.error_code = rx_metadata_t::ERROR_CODE_TIMEOUT;
return;
@@ -498,10 +561,7 @@ private:
curr_info.metadata.has_time_spec = prev_info.metadata.has_time_spec;
curr_info.metadata.time_spec = prev_info.metadata.time_spec + time_spec_t::from_ticks(
prev_info[index].ifpi.num_payload_words32*sizeof(boost::uint32_t)/_bytes_per_otw_item, _samp_rate);
- curr_info.metadata.more_fragments = false;
- curr_info.metadata.fragment_offset = 0;
- curr_info.metadata.start_of_burst = false;
- curr_info.metadata.end_of_burst = false;
+ curr_info.metadata.out_of_sequence = true;
curr_info.metadata.error_code = rx_metadata_t::ERROR_CODE_OVERFLOW;
UHD_MSG(fastpath) << "D";
return;
@@ -516,12 +576,6 @@ private:
"However, a timestamp match could not be determined.\n"
) % iterations << std::endl;
std::swap(curr_info, next_info); //save progress from curr -> next
- curr_info.metadata.has_time_spec = false;
- curr_info.metadata.time_spec = time_spec_t(0.0);
- curr_info.metadata.more_fragments = false;
- curr_info.metadata.fragment_offset = 0;
- curr_info.metadata.start_of_burst = false;
- curr_info.metadata.end_of_burst = false;
curr_info.metadata.error_code = rx_metadata_t::ERROR_CODE_ALIGNMENT;
_props[index].handle_overflow();
return;
@@ -554,13 +608,8 @@ private:
const size_t buffer_offset_bytes = 0
){
//get the next buffer if the current one has expired
- if (get_curr_buffer_info().data_bytes_to_copy == 0){
-
- //reset current buffer info members for reuse
- get_curr_buffer_info().fragment_offset_in_samps = 0;
- get_curr_buffer_info().alignment_time_valid = false;
- get_curr_buffer_info().indexes_todo.set();
-
+ if (get_curr_buffer_info().data_bytes_to_copy == 0)
+ {
//perform receive with alignment logic
get_aligned_buffs(timeout);
}
@@ -641,6 +690,65 @@ private:
size_t _convert_buffer_offset_bytes;
size_t _convert_bytes_to_copy;
+ /*
+ * This last section is only for debugging purposes.
+ * It causes a lot of prints to stderr which can be piped to a file.
+ * Gathered data can be used to post process it with external tools.
+ */
+#ifdef UHD_TXRX_DEBUG_PRINTS
+ struct dbg_recv_stat_t {
+ dbg_recv_stat_t(long wc, size_t nspb, size_t nsr, uhd::rx_metadata_t md, double to, bool op, double rate):
+ wallclock(wc), nsamps_per_buff(nspb), nsamps_recv(nsr), metadata(md), timeout(to), one_packet(op), samp_rate(rate)
+ {}
+ long wallclock;
+ size_t nsamps_per_buff;
+ size_t nsamps_recv;
+ uhd::rx_metadata_t metadata;
+ double timeout;
+ bool one_packet;
+ double samp_rate;
+ // Create a formatted print line for all the info gathered in this struct.
+ std::string print_line() {
+ boost::format fmt("recv,%ld,%f,%i,%i,%s,%i,%s,%s,%s,%i,%s,%ld");
+ fmt % wallclock;
+ fmt % timeout % (int)nsamps_per_buff % (int) nsamps_recv;
+ fmt % (one_packet ? "true":"false");
+ fmt % metadata.error_code;
+ fmt % (metadata.start_of_burst ? "true":"false") % (metadata.end_of_burst ? "true":"false");
+ fmt % (metadata.more_fragments ? "true":"false") % (int)metadata.fragment_offset;
+ fmt % (metadata.has_time_spec ? "true":"false") % metadata.time_spec.to_ticks(samp_rate);
+ return fmt.str();
+ }
+ };
+
+ void dbg_gather_data(const size_t nsamps_per_buff, const size_t nsamps_recv,
+ uhd::rx_metadata_t &metadata, const double timeout,
+ const bool one_packet,
+ bool dbg_print_directly = true
+ )
+ {
+ // Initialize a struct with all available data. It can return a formatted string with all infos if wanted.
+ dbg_recv_stat_t data(boost::get_system_time().time_of_day().total_microseconds(),
+ nsamps_per_buff,
+ nsamps_recv,
+ metadata,
+ timeout,
+ one_packet,
+ _samp_rate
+ );
+ if(dbg_print_directly) {
+ dbg_print_err(data.print_line());
+ }
+ }
+
+
+
+ void dbg_print_err(std::string msg) {
+ std::string dbg_prefix("super_recv_packet_handler,");
+ msg = dbg_prefix + msg;
+ fprintf(stderr, "%s\n", msg.c_str());
+ }
+#endif
};
class recv_packet_streamer : public recv_packet_handler, public rx_streamer{
diff --git a/host/lib/transport/super_send_packet_handler.hpp b/host/lib/transport/super_send_packet_handler.hpp
index ae483d1f3..c2810842e 100644
--- a/host/lib/transport/super_send_packet_handler.hpp
+++ b/host/lib/transport/super_send_packet_handler.hpp
@@ -35,7 +35,18 @@
#include <iostream>
#include <vector>
-namespace uhd{ namespace transport{ namespace sph{
+#ifdef UHD_TXRX_DEBUG_PRINTS
+// Included for debugging
+#include <boost/format.hpp>
+#include <boost/thread/thread.hpp>
+#include "boost/date_time/posix_time/posix_time.hpp"
+#include <map>
+#include <fstream>
+#endif
+
+namespace uhd {
+namespace transport {
+namespace sph {
/***********************************************************************
* Super send packet handler
@@ -56,7 +67,7 @@ public:
* \param size the number of transport channels
*/
send_packet_handler(const size_t size = 1):
- _next_packet_seq(0)
+ _next_packet_seq(0), _cached_metadata(false)
{
this->set_enable_trailer(true);
this->resize(size);
@@ -183,6 +194,23 @@ public:
if_packet_info.sob = metadata.start_of_burst;
if_packet_info.eob = metadata.end_of_burst;
+ /*
+ * Metadata is cached when we get a send requesting a start of burst with no samples.
+ * It is applied here on the next call to send() that actually has samples to send.
+ */
+ if (_cached_metadata && nsamps_per_buff != 0)
+ {
+ // If the new metada has a time_spec, do not use the cached time_spec.
+ if (!metadata.has_time_spec)
+ {
+ if_packet_info.has_tsf = _metadata_cache.has_time_spec;
+ if_packet_info.tsf = _metadata_cache.time_spec.to_ticks(_tick_rate);
+ }
+ if_packet_info.sob = _metadata_cache.start_of_burst;
+ if_packet_info.eob = _metadata_cache.end_of_burst;
+ _cached_metadata = false;
+ }
+
if (nsamps_per_buff <= _max_samples_per_packet){
//TODO remove this code when sample counts of zero are supported by hardware
@@ -190,13 +218,27 @@ public:
static const boost::uint64_t zero = 0;
_zero_buffs.resize(buffs.size(), &zero);
- if (nsamps_per_buff == 0) return send_one_packet(
- _zero_buffs, 1, if_packet_info, timeout
- ) & 0x0;
+ if (nsamps_per_buff == 0)
+ {
+ // if this is a start of a burst and there are no samples
+ if (metadata.start_of_burst)
+ {
+ // cache metadata and apply on the next send()
+ _metadata_cache = metadata;
+ _cached_metadata = true;
+ return 0;
+ } else {
+ // send requests with no samples are handled here (such as end of burst)
+ return send_one_packet(_zero_buffs, 1, if_packet_info, timeout) & 0x0;
+ }
+ }
#endif
- return send_one_packet(buffs, nsamps_per_buff, if_packet_info, timeout);
- }
+ size_t nsamps_sent = send_one_packet(buffs, nsamps_per_buff, if_packet_info, timeout);
+#ifdef UHD_TXRX_DEBUG_PRINTS
+ dbg_print_send(nsamps_per_buff, nsamps_sent, metadata, timeout);
+#endif
+ return nsamps_sent; }
size_t total_num_samps_sent = 0;
//false until final fragment
@@ -226,11 +268,14 @@ public:
//send the final fragment with the helper function
if_packet_info.eob = metadata.end_of_burst;
- return total_num_samps_sent + send_one_packet(
- buffs, final_length,
- if_packet_info, timeout,
- total_num_samps_sent*_bytes_per_cpu_item
- );
+ size_t nsamps_sent = total_num_samps_sent
+ + send_one_packet(buffs, final_length, if_packet_info, timeout,
+ total_num_samps_sent * _bytes_per_cpu_item);
+#ifdef UHD_TXRX_DEBUG_PRINTS
+ dbg_print_send(nsamps_per_buff, nsamps_sent, metadata, timeout);
+
+#endif
+ return nsamps_sent;
}
private:
@@ -255,6 +300,53 @@ private:
size_t _next_packet_seq;
bool _has_tlr;
async_receiver_type _async_receiver;
+ bool _cached_metadata;
+ uhd::tx_metadata_t _metadata_cache;
+
+#ifdef UHD_TXRX_DEBUG_PRINTS
+ struct dbg_send_stat_t {
+ dbg_send_stat_t(long wc, size_t nspb, size_t nss, uhd::tx_metadata_t md, double to, double rate):
+ wallclock(wc), nsamps_per_buff(nspb), nsamps_sent(nss), metadata(md), timeout(to), samp_rate(rate)
+ {}
+ long wallclock;
+ size_t nsamps_per_buff;
+ size_t nsamps_sent;
+ uhd::tx_metadata_t metadata;
+ double timeout;
+ double samp_rate;
+ // Create a formatted print line for all the info gathered in this struct.
+ std::string print_line() {
+ boost::format fmt("send,%ld,%f,%i,%i,%s,%s,%s,%ld");
+ fmt % wallclock;
+ fmt % timeout % (int)nsamps_per_buff % (int) nsamps_sent;
+ fmt % (metadata.start_of_burst ? "true":"false") % (metadata.end_of_burst ? "true":"false");
+ fmt % (metadata.has_time_spec ? "true":"false") % metadata.time_spec.to_ticks(samp_rate);
+ return fmt.str();
+ }
+ };
+
+ void dbg_print_send(size_t nsamps_per_buff, size_t nsamps_sent,
+ const uhd::tx_metadata_t &metadata, const double timeout,
+ bool dbg_print_directly = true)
+ {
+ dbg_send_stat_t data(boost::get_system_time().time_of_day().total_microseconds(),
+ nsamps_per_buff,
+ nsamps_sent,
+ metadata,
+ timeout,
+ _samp_rate
+ );
+ if(dbg_print_directly){
+ dbg_print_err(data.print_line());
+ }
+ }
+ void dbg_print_err(std::string msg) {
+ msg = "super_send_packet_handler," + msg;
+ fprintf(stderr, "%s\n", msg.c_str());
+ }
+
+
+#endif
/*******************************************************************
* Send a single packet:
@@ -266,6 +358,7 @@ private:
const double timeout,
const size_t buffer_offset_bytes = 0
){
+
//load the rest of the if_packet_info in here
if_packet_info.num_payload_bytes = nsamps_per_buff*_num_inputs*_bytes_per_otw_item;
if_packet_info.num_payload_words32 = (if_packet_info.num_payload_bytes + 3/*round up*/)/sizeof(boost::uint32_t);
@@ -374,6 +467,8 @@ private:
size_t _max_num_samps;
};
-}}} //namespace
+} // namespace sph
+} // namespace transport
+} // namespace uhd
#endif /* INCLUDED_LIBUHD_TRANSPORT_SUPER_SEND_PACKET_HANDLER_HPP */
diff --git a/host/lib/transport/tcp_zero_copy.cpp b/host/lib/transport/tcp_zero_copy.cpp
new file mode 100644
index 000000000..402bda1e8
--- /dev/null
+++ b/host/lib/transport/tcp_zero_copy.cpp
@@ -0,0 +1,225 @@
+//
+// Copyright 2010-2013 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "udp_common.hpp"
+#include <uhd/transport/tcp_zero_copy.hpp>
+#include <uhd/transport/buffer_pool.hpp>
+#include <uhd/utils/msg.hpp>
+#include <uhd/utils/log.hpp>
+#include <uhd/utils/atomic.hpp>
+#include <boost/format.hpp>
+#include <boost/make_shared.hpp>
+#include <boost/thread/thread.hpp> //sleep
+#include <vector>
+
+using namespace uhd;
+using namespace uhd::transport;
+namespace asio = boost::asio;
+
+static const size_t DEFAULT_NUM_FRAMES = 32;
+static const size_t DEFAULT_FRAME_SIZE = 2048;
+
+/***********************************************************************
+ * Reusable managed receiver buffer:
+ * - get_new performs the recv operation
+ **********************************************************************/
+class tcp_zero_copy_asio_mrb : public managed_recv_buffer{
+public:
+ tcp_zero_copy_asio_mrb(void *mem, int sock_fd, const size_t frame_size):
+ _mem(mem), _sock_fd(sock_fd), _frame_size(frame_size) { /*NOP*/ }
+
+ void release(void){
+ _claimer.release();
+ }
+
+ UHD_INLINE sptr get_new(const double timeout, size_t &index){
+ if (not _claimer.claim_with_wait(timeout)) return sptr();
+
+ #ifdef MSG_DONTWAIT //try a non-blocking recv() if supported
+ _len = ::recv(_sock_fd, (char *)_mem, _frame_size, MSG_DONTWAIT);
+ if (_len > 0){
+ index++; //advances the caller's buffer
+ return make(this, _mem, size_t(_len));
+ }
+ #endif
+
+ if (wait_for_recv_ready(_sock_fd, timeout)){
+ _len = ::recv(_sock_fd, (char *)_mem, _frame_size, 0);
+ index++; //advances the caller's buffer
+ return make(this, _mem, size_t(_len));
+ }
+
+ _claimer.release(); //undo claim
+ return sptr(); //null for timeout
+ }
+
+private:
+ void *_mem;
+ int _sock_fd;
+ size_t _frame_size;
+ ssize_t _len;
+ simple_claimer _claimer;
+};
+
+/***********************************************************************
+ * Reusable managed send buffer:
+ * - commit performs the send operation
+ **********************************************************************/
+class tcp_zero_copy_asio_msb : public managed_send_buffer{
+public:
+ tcp_zero_copy_asio_msb(void *mem, int sock_fd, const size_t frame_size):
+ _mem(mem), _sock_fd(sock_fd), _frame_size(frame_size) { /*NOP*/ }
+
+ void release(void){
+ //Retry logic because send may fail with ENOBUFS.
+ //This is known to occur at least on some OSX systems.
+ //But it should be safe to always check for the error.
+ while (true)
+ {
+ this->commit(_frame_size); //always full size frames to avoid pkt coalescing
+ const ssize_t ret = ::send(_sock_fd, (const char *)_mem, size(), 0);
+ if (ret == ssize_t(size())) break;
+ if (ret == -1 and errno == ENOBUFS)
+ {
+ boost::this_thread::sleep(boost::posix_time::microseconds(1));
+ continue; //try to send again
+ }
+ UHD_ASSERT_THROW(ret == ssize_t(size()));
+ }
+ _claimer.release();
+ }
+
+ UHD_INLINE sptr get_new(const double timeout, size_t &index){
+ if (not _claimer.claim_with_wait(timeout)) return sptr();
+ index++; //advances the caller's buffer
+ return make(this, _mem, _frame_size);
+ }
+
+private:
+ void *_mem;
+ int _sock_fd;
+ size_t _frame_size;
+ simple_claimer _claimer;
+};
+
+/***********************************************************************
+ * Zero Copy TCP implementation with ASIO:
+ * This is the portable zero copy implementation for systems
+ * where a faster, platform specific solution is not available.
+ * However, it is not a true zero copy implementation as each
+ * send and recv requires a copy operation to/from userspace.
+ **********************************************************************/
+class tcp_zero_copy_asio_impl : public tcp_zero_copy{
+public:
+ typedef boost::shared_ptr<tcp_zero_copy_asio_impl> sptr;
+
+ tcp_zero_copy_asio_impl(
+ const std::string &addr,
+ const std::string &port,
+ const device_addr_t &hints
+ ):
+ _recv_frame_size(size_t(hints.cast<double>("recv_frame_size", DEFAULT_FRAME_SIZE))),
+ _num_recv_frames(size_t(hints.cast<double>("num_recv_frames", DEFAULT_NUM_FRAMES))),
+ _send_frame_size(size_t(hints.cast<double>("send_frame_size", DEFAULT_FRAME_SIZE))),
+ _num_send_frames(size_t(hints.cast<double>("num_send_frames", DEFAULT_NUM_FRAMES))),
+ _recv_buffer_pool(buffer_pool::make(_num_recv_frames, _recv_frame_size)),
+ _send_buffer_pool(buffer_pool::make(_num_send_frames, _send_frame_size)),
+ _next_recv_buff_index(0), _next_send_buff_index(0)
+ {
+ UHD_LOG << boost::format("Creating tcp transport for %s %s") % addr % port << std::endl;
+
+ //resolve the address
+ asio::ip::tcp::resolver resolver(_io_service);
+ asio::ip::tcp::resolver::query query(asio::ip::tcp::v4(), addr, port);
+ asio::ip::tcp::endpoint receiver_endpoint = *resolver.resolve(query);
+
+ //create, open, and connect the socket
+ _socket.reset(new asio::ip::tcp::socket(_io_service));
+ _socket->connect(receiver_endpoint);
+ _sock_fd = _socket->native();
+
+ //packets go out ASAP
+ asio::ip::tcp::no_delay option(true);
+ _socket->set_option(option);
+
+ //allocate re-usable managed receive buffers
+ for (size_t i = 0; i < get_num_recv_frames(); i++){
+ _mrb_pool.push_back(boost::make_shared<tcp_zero_copy_asio_mrb>(
+ _recv_buffer_pool->at(i), _sock_fd, get_recv_frame_size()
+ ));
+ }
+
+ //allocate re-usable managed send buffers
+ for (size_t i = 0; i < get_num_send_frames(); i++){
+ _msb_pool.push_back(boost::make_shared<tcp_zero_copy_asio_msb>(
+ _send_buffer_pool->at(i), _sock_fd, get_send_frame_size()
+ ));
+ }
+ }
+
+ /*******************************************************************
+ * Receive implementation:
+ * Block on the managed buffer's get call and advance the index.
+ ******************************************************************/
+ managed_recv_buffer::sptr get_recv_buff(double timeout){
+ if (_next_recv_buff_index == _num_recv_frames) _next_recv_buff_index = 0;
+ return _mrb_pool[_next_recv_buff_index]->get_new(timeout, _next_recv_buff_index);
+ }
+
+ size_t get_num_recv_frames(void) const {return _num_recv_frames;}
+ size_t get_recv_frame_size(void) const {return _recv_frame_size;}
+
+ /*******************************************************************
+ * Send implementation:
+ * Block on the managed buffer's get call and advance the index.
+ ******************************************************************/
+ managed_send_buffer::sptr get_send_buff(double timeout){
+ if (_next_send_buff_index == _num_send_frames) _next_send_buff_index = 0;
+ return _msb_pool[_next_send_buff_index]->get_new(timeout, _next_send_buff_index);
+ }
+
+ size_t get_num_send_frames(void) const {return _num_send_frames;}
+ size_t get_send_frame_size(void) const {return _send_frame_size;}
+
+private:
+ //memory management -> buffers and fifos
+ const size_t _recv_frame_size, _num_recv_frames;
+ const size_t _send_frame_size, _num_send_frames;
+ buffer_pool::sptr _recv_buffer_pool, _send_buffer_pool;
+ std::vector<boost::shared_ptr<tcp_zero_copy_asio_msb> > _msb_pool;
+ std::vector<boost::shared_ptr<tcp_zero_copy_asio_mrb> > _mrb_pool;
+ size_t _next_recv_buff_index, _next_send_buff_index;
+
+ //asio guts -> socket and service
+ asio::io_service _io_service;
+ boost::shared_ptr<asio::ip::tcp::socket> _socket;
+ int _sock_fd;
+};
+
+/***********************************************************************
+ * TCP zero copy make function
+ **********************************************************************/
+zero_copy_if::sptr tcp_zero_copy::make(
+ const std::string &addr,
+ const std::string &port,
+ const device_addr_t &hints
+){
+ zero_copy_if::sptr xport;
+ xport.reset(new tcp_zero_copy_asio_impl(addr, port, hints));
+ while (xport->get_recv_buff(0.0)){} //flush
+ return xport;
+}
diff --git a/host/lib/transport/udp_wsa_zero_copy.cpp b/host/lib/transport/udp_wsa_zero_copy.cpp
index 6fe4e3cad..031d26374 100644
--- a/host/lib/transport/udp_wsa_zero_copy.cpp
+++ b/host/lib/transport/udp_wsa_zero_copy.cpp
@@ -182,14 +182,15 @@ public:
udp_zero_copy_wsa_impl(
const std::string &addr,
const std::string &port,
+ zero_copy_xport_params& xport_params,
const device_addr_t &hints
):
- _recv_frame_size(size_t(hints.cast<double>("recv_frame_size", udp_simple::mtu))),
- _num_recv_frames(size_t(hints.cast<double>("num_recv_frames", DEFAULT_NUM_FRAMES))),
- _send_frame_size(size_t(hints.cast<double>("send_frame_size", udp_simple::mtu))),
- _num_send_frames(size_t(hints.cast<double>("num_send_frames", DEFAULT_NUM_FRAMES))),
- _recv_buffer_pool(buffer_pool::make(_num_recv_frames, _recv_frame_size)),
- _send_buffer_pool(buffer_pool::make(_num_send_frames, _send_frame_size)),
+ _recv_frame_size(xport_params.recv_frame_size),
+ _num_recv_frames(xport_params.num_recv_frames),
+ _send_frame_size(xport_params.send_frame_size),
+ _num_send_frames(xport_params.num_send_frames),
+ _recv_buffer_pool(buffer_pool::make(xport_params.num_recv_frames, xport_params.recv_frame_size)),
+ _send_buffer_pool(buffer_pool::make(xport_params.num_send_frames, xport_params.send_frame_size)),
_next_recv_buff_index(0), _next_send_buff_index(0)
{
#ifdef CHECK_REG_SEND_THRESH
@@ -294,7 +295,17 @@ private:
udp_zero_copy::sptr udp_zero_copy::make(
const std::string &addr,
const std::string &port,
+ const zero_copy_xport_params &default_buff_args,
+ udp_zero_copy::buff_params& buff_params_out,
const device_addr_t &hints
){
- return sptr(new udp_zero_copy_wsa_impl(addr, port, hints));
+ //Initialize xport_params
+ zero_copy_xport_params xport_params = default_buff_args;
+
+ xport_params.recv_frame_size = size_t(hints.cast<double>("recv_frame_size", default_buff_args.recv_frame_size));
+ xport_params.num_recv_frames = size_t(hints.cast<double>("num_recv_frames", default_buff_args.num_recv_frames));
+ xport_params.send_frame_size = size_t(hints.cast<double>("send_frame_size", default_buff_args.send_frame_size));
+ xport_params.num_send_frames = size_t(hints.cast<double>("num_send_frames", default_buff_args.num_send_frames));
+
+ return sptr(new udp_zero_copy_wsa_impl(addr, port, xport_params, hints));
}
diff --git a/host/lib/transport/udp_zero_copy.cpp b/host/lib/transport/udp_zero_copy.cpp
index 7b6a476f5..adc7d5585 100644
--- a/host/lib/transport/udp_zero_copy.cpp
+++ b/host/lib/transport/udp_zero_copy.cpp
@@ -158,14 +158,14 @@ public:
udp_zero_copy_asio_impl(
const std::string &addr,
const std::string &port,
- const device_addr_t &hints
+ const zero_copy_xport_params& xport_params
):
- _recv_frame_size(size_t(hints.cast<double>("recv_frame_size", udp_simple::mtu))),
- _num_recv_frames(size_t(hints.cast<double>("num_recv_frames", DEFAULT_NUM_FRAMES))),
- _send_frame_size(size_t(hints.cast<double>("send_frame_size", udp_simple::mtu))),
- _num_send_frames(size_t(hints.cast<double>("num_send_frames", DEFAULT_NUM_FRAMES))),
- _recv_buffer_pool(buffer_pool::make(_num_recv_frames, _recv_frame_size)),
- _send_buffer_pool(buffer_pool::make(_num_send_frames, _send_frame_size)),
+ _recv_frame_size(xport_params.recv_frame_size),
+ _num_recv_frames(xport_params.num_recv_frames),
+ _send_frame_size(xport_params.send_frame_size),
+ _num_send_frames(xport_params.num_send_frames),
+ _recv_buffer_pool(buffer_pool::make(xport_params.num_recv_frames, xport_params.recv_frame_size)),
+ _send_buffer_pool(buffer_pool::make(xport_params.num_send_frames, xport_params.send_frame_size)),
_next_recv_buff_index(0), _next_send_buff_index(0)
{
UHD_LOG << boost::format("Creating udp transport for %s %s") % addr % port << std::endl;
@@ -256,11 +256,12 @@ private:
/***********************************************************************
* UDP zero copy make function
**********************************************************************/
-template<typename Opt> static void resize_buff_helper(
+template<typename Opt> static size_t resize_buff_helper(
udp_zero_copy_asio_impl::sptr udp_trans,
const size_t target_size,
const std::string &name
){
+ size_t actual_size = 0;
std::string help_message;
#if defined(UHD_PLATFORM_LINUX)
help_message = str(boost::format(
@@ -270,7 +271,7 @@ template<typename Opt> static void resize_buff_helper(
//resize the buffer if size was provided
if (target_size > 0){
- size_t actual_size = udp_trans->resize_buff<Opt>(target_size);
+ actual_size = udp_trans->resize_buff<Opt>(target_size);
UHD_LOG << boost::format(
"Target %s sock buff size: %d bytes\n"
"Actual %s sock buff size: %d bytes"
@@ -282,24 +283,54 @@ template<typename Opt> static void resize_buff_helper(
"See the transport application notes on buffer resizing.\n%s"
) % name % target_size % actual_size % help_message;
}
+
+ return actual_size;
}
udp_zero_copy::sptr udp_zero_copy::make(
const std::string &addr,
const std::string &port,
+ const zero_copy_xport_params &default_buff_args,
+ udp_zero_copy::buff_params& buff_params_out,
const device_addr_t &hints
){
- udp_zero_copy_asio_impl::sptr udp_trans(
- new udp_zero_copy_asio_impl(addr, port, hints)
- );
+ //Initialize xport_params
+ zero_copy_xport_params xport_params = default_buff_args;
+
+ xport_params.recv_frame_size = size_t(hints.cast<double>("recv_frame_size", default_buff_args.recv_frame_size));
+ xport_params.num_recv_frames = size_t(hints.cast<double>("num_recv_frames", default_buff_args.num_recv_frames));
+ xport_params.send_frame_size = size_t(hints.cast<double>("send_frame_size", default_buff_args.send_frame_size));
+ xport_params.num_send_frames = size_t(hints.cast<double>("num_send_frames", default_buff_args.num_send_frames));
//extract buffer size hints from the device addr
- size_t recv_buff_size = size_t(hints.cast<double>("recv_buff_size", 0.0));
- size_t send_buff_size = size_t(hints.cast<double>("send_buff_size", 0.0));
+ size_t usr_recv_buff_size = size_t(hints.cast<double>("recv_buff_size", 0.0));
+ size_t usr_send_buff_size = size_t(hints.cast<double>("send_buff_size", 0.0));
+
+ if (hints.has_key("recv_buff_size")) {
+ if (usr_recv_buff_size < xport_params.recv_frame_size * xport_params.num_recv_frames) {
+ throw uhd::value_error((boost::format(
+ "recv_buff_size must be equal to or greater than (num_recv_frames * recv_frame_size) where num_recv_frames=%d, recv_frame_size=%d")
+ % xport_params.num_recv_frames % xport_params.recv_frame_size).str());
+ }
+ }
+
+ if (hints.has_key("send_buff_size")) {
+ if (usr_send_buff_size < xport_params.send_frame_size * xport_params.num_send_frames) {
+ throw uhd::value_error((boost::format(
+ "send_buff_size must be equal to or greater than (num_send_frames * send_frame_size) where num_send_frames=%d, send_frame_size=%d")
+ % xport_params.num_send_frames % xport_params.send_frame_size).str());
+ }
+ }
+
+ udp_zero_copy_asio_impl::sptr udp_trans(
+ new udp_zero_copy_asio_impl(addr, port, xport_params)
+ );
//call the helper to resize send and recv buffers
- resize_buff_helper<asio::socket_base::receive_buffer_size>(udp_trans, recv_buff_size, "recv");
- resize_buff_helper<asio::socket_base::send_buffer_size> (udp_trans, send_buff_size, "send");
+ buff_params_out.recv_buff_size =
+ resize_buff_helper<asio::socket_base::receive_buffer_size>(udp_trans, usr_recv_buff_size, "recv");
+ buff_params_out.send_buff_size =
+ resize_buff_helper<asio::socket_base::send_buffer_size> (udp_trans, usr_send_buff_size, "send");
return udp_trans;
}
diff --git a/host/lib/transport/xport_benchmarker.cpp b/host/lib/transport/xport_benchmarker.cpp
new file mode 100644
index 000000000..d58dbea47
--- /dev/null
+++ b/host/lib/transport/xport_benchmarker.cpp
@@ -0,0 +1,155 @@
+//
+// Copyright 2010-2013 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "xport_benchmarker.hpp"
+
+namespace uhd { namespace transport {
+
+const device_addr_t& xport_benchmarker::benchmark_throughput_chdr
+(
+ zero_copy_if::sptr tx_transport,
+ zero_copy_if::sptr rx_transport,
+ boost::uint32_t sid,
+ bool big_endian,
+ boost::uint32_t duration_ms)
+{
+ vrt::if_packet_info_t pkt_info;
+ _initialize_chdr(tx_transport, rx_transport, sid, pkt_info);
+ _reset_counters();
+ boost::posix_time::ptime start_time(boost::posix_time::microsec_clock::local_time());
+
+ _tx_thread.reset(new boost::thread(boost::bind(&xport_benchmarker::_stream_tx, this, tx_transport.get(), &pkt_info, big_endian)));
+ _rx_thread.reset(new boost::thread(boost::bind(&xport_benchmarker::_stream_rx, this, rx_transport.get(), &pkt_info, big_endian)));
+
+ boost::this_thread::sleep(boost::posix_time::milliseconds(duration_ms));
+
+ _tx_thread->interrupt();
+ _rx_thread->interrupt();
+ _tx_thread->join();
+ _rx_thread->join();
+
+ boost::posix_time::ptime stop_time(boost::posix_time::microsec_clock::local_time());
+ double duration_s = ((double)(stop_time-start_time).total_microseconds())/1e6;
+
+ boost::uint64_t tx_bytes = pkt_info.num_payload_words32*sizeof(uint32_t)*_num_tx_packets;
+ boost::uint64_t rx_bytes = pkt_info.num_payload_words32*sizeof(uint32_t)*_num_rx_packets;
+ double tx_rate = (((double)tx_bytes)/duration_s);
+ double rx_rate = (((double)rx_bytes)/duration_s);
+
+ _results["TX-Bytes"] = (boost::format("%.2fMB") % (tx_bytes/(1024*1024))).str();
+ _results["RX-Bytes"] = (boost::format("%.2fMB") % (rx_bytes/(1024*1024))).str();
+ _results["TX-Throughput"] = (boost::format("%.2fMB/s") % (tx_rate/(1024*1024))).str();
+ _results["RX-Throughput"] = (boost::format("%.2fMB/s") % (rx_rate/(1024*1024))).str();
+ _results["TX-Timeouts"] = boost::lexical_cast<std::string>(_num_tx_timeouts);
+ _results["RX-Timeouts"] = boost::lexical_cast<std::string>(_num_rx_timeouts);
+ _results["Data-Errors"] = boost::lexical_cast<std::string>(_num_data_errors);
+
+ return _results;
+}
+
+void xport_benchmarker::_stream_tx(zero_copy_if* transport, vrt::if_packet_info_t* pkt_info, bool big_endian)
+{
+ while (not boost::this_thread::interruption_requested()) {
+ managed_send_buffer::sptr buff = transport->get_send_buff(_tx_timeout);
+ if (buff) {
+ boost::uint32_t *packet_buff = buff->cast<boost::uint32_t *>();
+ //Populate packet
+ if (big_endian) {
+ vrt::if_hdr_pack_be(packet_buff, *pkt_info);
+ } else {
+ vrt::if_hdr_pack_le(packet_buff, *pkt_info);
+ }
+ //send the buffer over the interface
+ buff->commit(sizeof(boost::uint32_t)*(pkt_info->num_packet_words32));
+ _num_tx_packets++;
+ } else {
+ _num_tx_timeouts++;
+ }
+ }
+}
+
+void xport_benchmarker::_stream_rx(zero_copy_if* transport, const vrt::if_packet_info_t* exp_pkt_info, bool big_endian)
+{
+ while (not boost::this_thread::interruption_requested()) {
+ managed_recv_buffer::sptr buff = transport->get_recv_buff(_rx_timeout);
+ if (buff) {
+ //Extract packet info
+ vrt::if_packet_info_t pkt_info;
+ pkt_info.link_type = exp_pkt_info->link_type;
+ pkt_info.num_packet_words32 = buff->size()/sizeof(boost::uint32_t);
+ const boost::uint32_t *packet_buff = buff->cast<const boost::uint32_t *>();
+
+ _num_rx_packets++;
+
+ //unpacking can fail
+ try {
+ if (big_endian) {
+ vrt::if_hdr_unpack_be(packet_buff, pkt_info);
+ } else {
+ vrt::if_hdr_unpack_le(packet_buff, pkt_info);
+ }
+
+ if (exp_pkt_info->packet_type != pkt_info.packet_type ||
+ exp_pkt_info->num_payload_bytes != pkt_info.num_payload_bytes) {
+ _num_data_errors++;
+ }
+ } catch(const std::exception &ex) {
+ _num_data_errors++;
+ }
+ } else {
+ _num_rx_timeouts++;
+ }
+ }
+}
+
+void xport_benchmarker::_reset_counters(void)
+{
+ _num_tx_packets = 0;
+ _num_rx_packets = 0;
+ _num_tx_timeouts = 0;
+ _num_rx_timeouts = 0;
+ _num_data_errors = 0;
+}
+
+void xport_benchmarker::_initialize_chdr(
+ zero_copy_if::sptr tx_transport,
+ zero_copy_if::sptr rx_transport,
+ boost::uint32_t sid,
+ vrt::if_packet_info_t& pkt_info)
+{
+ _tx_timeout = 0.5;
+ _rx_timeout = 0.5;
+
+ size_t frame_size = std::min(tx_transport->get_send_frame_size(), rx_transport->get_recv_frame_size());
+
+ pkt_info.link_type = vrt::if_packet_info_t::LINK_TYPE_CHDR;
+ pkt_info.packet_type = vrt::if_packet_info_t::PACKET_TYPE_DATA;
+ pkt_info.num_packet_words32 = (frame_size/sizeof(boost::uint32_t));
+ pkt_info.num_payload_words32 = pkt_info.num_packet_words32 - 2;
+ pkt_info.num_payload_bytes = pkt_info.num_payload_words32*sizeof(boost::uint32_t);
+ pkt_info.packet_count = 0;
+ pkt_info.sob = false;
+ pkt_info.eob = false;
+ pkt_info.sid = sid;
+ pkt_info.has_sid = true;
+ pkt_info.has_cid = false;
+ pkt_info.has_tsi = false;
+ pkt_info.has_tsf = false;
+ pkt_info.has_tlr = false;
+}
+
+}}
diff --git a/host/lib/transport/xport_benchmarker.hpp b/host/lib/transport/xport_benchmarker.hpp
new file mode 100644
index 000000000..9fca8d1fb
--- /dev/null
+++ b/host/lib/transport/xport_benchmarker.hpp
@@ -0,0 +1,77 @@
+//
+// Copyright 2010-2013 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_LIBUHD_XPORT_BENCHMARKER_HPP
+#define INCLUDED_LIBUHD_XPORT_BENCHMARKER_HPP
+
+#include <uhd/transport/zero_copy.hpp>
+#include <uhd/types/device_addr.hpp>
+#include <uhd/utils/msg.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/thread/thread.hpp>
+#include <uhd/transport/vrt_if_packet.hpp>
+
+namespace uhd { namespace transport {
+
+//Test class to benchmark a low-level transport object with a VITA/C-VITA data stream
+class xport_benchmarker : boost::noncopyable {
+public:
+ const device_addr_t& benchmark_throughput_chdr(
+ zero_copy_if::sptr tx_transport,
+ zero_copy_if::sptr rx_transport,
+ boost::uint32_t sid,
+ bool big_endian,
+ boost::uint32_t duration_ms);
+
+private:
+ void _stream_tx(
+ zero_copy_if* transport,
+ vrt::if_packet_info_t* pkt_info,
+ bool big_endian);
+
+ void _stream_rx(
+ zero_copy_if* transport,
+ const vrt::if_packet_info_t* exp_pkt_info,
+ bool big_endian);
+
+ void _initialize_chdr(
+ zero_copy_if::sptr tx_transport,
+ zero_copy_if::sptr rx_transport,
+ boost::uint32_t sid,
+ vrt::if_packet_info_t& pkt_info);
+
+ void _reset_counters(void);
+
+ boost::shared_ptr<boost::thread> _tx_thread;
+ boost::shared_ptr<boost::thread> _rx_thread;
+
+ boost::uint64_t _num_tx_packets;
+ boost::uint64_t _num_rx_packets;
+ boost::uint64_t _num_tx_timeouts;
+ boost::uint64_t _num_rx_timeouts;
+ boost::uint64_t _num_data_errors;
+
+ double _tx_timeout;
+ double _rx_timeout;
+
+ device_addr_t _results;
+};
+
+
+}} //namespace
+
+#endif /* INCLUDED_LIBUHD_XPORT_BENCHMARKER_HPP */