summaryrefslogtreecommitdiffstats
path: root/host/lib
diff options
context:
space:
mode:
Diffstat (limited to 'host/lib')
-rw-r--r--host/lib/CMakeLists.txt3
-rw-r--r--host/lib/convert/CMakeLists.txt15
-rw-r--r--host/lib/convert/convert_common.hpp8
-rw-r--r--host/lib/convert/gen_convert_pred.py79
-rwxr-xr-xhost/lib/ic_reg_maps/gen_ad9522_regs.py8
-rw-r--r--host/lib/transport/CMakeLists.txt1
-rwxr-xr-xhost/lib/transport/gen_vrt_if_packet.py52
-rw-r--r--host/lib/transport/libusb1_zero_copy.cpp496
-rw-r--r--host/lib/transport/udp_zero_copy_asio.cpp307
-rw-r--r--host/lib/transport/vrt_packet_handler.hpp66
-rw-r--r--host/lib/transport/zero_copy.cpp108
-rw-r--r--host/lib/types/time_spec.cpp8
-rw-r--r--host/lib/types/types.cpp28
-rw-r--r--host/lib/usrp/multi_usrp.cpp8
-rw-r--r--host/lib/usrp/usrp1/io_impl.cpp143
-rw-r--r--host/lib/usrp/usrp1/soft_time_ctrl.cpp8
-rw-r--r--host/lib/usrp/usrp1/usrp1_impl.hpp4
-rw-r--r--host/lib/usrp/usrp2/io_impl.cpp139
-rw-r--r--host/lib/usrp/usrp2/usrp2_iface.cpp3
-rw-r--r--host/lib/usrp/usrp2/usrp2_impl.hpp4
-rw-r--r--host/lib/usrp/usrp_e100/CMakeLists.txt4
-rw-r--r--host/lib/usrp/usrp_e100/clock_ctrl.cpp326
-rw-r--r--host/lib/usrp/usrp_e100/clock_ctrl.hpp7
-rw-r--r--host/lib/usrp/usrp_e100/fpga_downloader.cpp (renamed from host/lib/usrp/usrp_e100/fpga-downloader.cc)42
-rw-r--r--host/lib/usrp/usrp_e100/io_impl.cpp90
-rw-r--r--host/lib/usrp/usrp_e100/mboard_impl.cpp8
-rw-r--r--host/lib/usrp/usrp_e100/usrp_e100_iface.cpp6
-rw-r--r--host/lib/usrp/usrp_e100/usrp_e100_impl.hpp8
-rw-r--r--host/lib/usrp/usrp_e100/usrp_e100_mmap_zero_copy.cpp165
-rw-r--r--host/lib/usrp/usrp_e100/usrp_e100_regs.hpp94
30 files changed, 1182 insertions, 1056 deletions
diff --git a/host/lib/CMakeLists.txt b/host/lib/CMakeLists.txt
index d2845ffda..c8a5dd51e 100644
--- a/host/lib/CMakeLists.txt
+++ b/host/lib/CMakeLists.txt
@@ -108,6 +108,9 @@ ADD_LIBRARY(uhd SHARED ${libuhd_sources})
TARGET_LINK_LIBRARIES(uhd ${Boost_LIBRARIES} ${libuhd_libs})
SET_TARGET_PROPERTIES(uhd PROPERTIES DEFINE_SYMBOL "UHD_DLL_EXPORTS")
SET_TARGET_PROPERTIES(uhd PROPERTIES SOVERSION ${UHD_VERSION_MAJOR})
+IF(DEFINED LIBUHD_OUTPUT_NAME)
+ SET_TARGET_PROPERTIES(uhd PROPERTIES OUTPUT_NAME ${LIBUHD_OUTPUT_NAME})
+ENDIF(DEFINED LIBUHD_OUTPUT_NAME)
INSTALL(TARGETS uhd
LIBRARY DESTINATION ${LIBRARY_DIR} # .so file
diff --git a/host/lib/convert/CMakeLists.txt b/host/lib/convert/CMakeLists.txt
index a9f977cdc..de9c660e1 100644
--- a/host/lib/convert/CMakeLists.txt
+++ b/host/lib/convert/CMakeLists.txt
@@ -22,15 +22,28 @@ INCLUDE(CheckIncludeFileCXX)
MESSAGE(STATUS "")
########################################################################
-# Check for SIMD headers
+# Check for SSE2 SIMD headers
########################################################################
+IF(CMAKE_COMPILER_IS_GNUCXX)
+ SET(EMMINTRIN_FLAGS -msse2)
+ELSEIF(MSVC)
+ SET(EMMINTRIN_FLAGS /arch:SSE2)
+ENDIF()
+
+SET(CMAKE_REQUIRED_FLAGS ${EMMINTRIN_FLAGS})
CHECK_INCLUDE_FILE_CXX(emmintrin.h HAVE_EMMINTRIN_H)
+UNSET(CMAKE_REQUIRED_FLAGS)
+
IF(HAVE_EMMINTRIN_H)
+ ADD_DEFINITIONS(${EMMINTRIN_FLAGS})
LIBUHD_APPEND_SOURCES(
${CMAKE_CURRENT_SOURCE_DIR}/convert_with_sse2.cpp
)
ENDIF(HAVE_EMMINTRIN_H)
+########################################################################
+# Check for NEON SIMD headers
+########################################################################
CHECK_INCLUDE_FILE_CXX(arm_neon.h HAVE_ARM_NEON_H)
IF(HAVE_ARM_NEON_H)
LIBUHD_APPEND_SOURCES(
diff --git a/host/lib/convert/convert_common.hpp b/host/lib/convert/convert_common.hpp
index c6ba1fcf9..c2ca233d9 100644
--- a/host/lib/convert/convert_common.hpp
+++ b/host/lib/convert/convert_common.hpp
@@ -25,16 +25,16 @@
#define DECLARE_CONVERTER(fcn, prio) \
static void fcn( \
- uhd::convert::input_type &inputs, \
- uhd::convert::output_type &outputs, \
+ const uhd::convert::input_type &inputs, \
+ const uhd::convert::output_type &outputs, \
size_t nsamps \
); \
UHD_STATIC_BLOCK(register_##fcn##_##prio){ \
uhd::convert::register_converter(#fcn, fcn, prio); \
} \
static void fcn( \
- uhd::convert::input_type &inputs, \
- uhd::convert::output_type &outputs, \
+ const uhd::convert::input_type &inputs, \
+ const uhd::convert::output_type &outputs, \
size_t nsamps \
)
diff --git a/host/lib/convert/gen_convert_pred.py b/host/lib/convert/gen_convert_pred.py
index fea7db4cc..d2f90bf41 100644
--- a/host/lib/convert/gen_convert_pred.py
+++ b/host/lib/convert/gen_convert_pred.py
@@ -21,8 +21,6 @@ TMPL_TEXT = """
/***********************************************************************
* This file was generated by $file on $time.strftime("%c")
**********************************************************************/
-typedef size_t pred_type;
-
\#include <boost/tokenizer.hpp>
\#include <boost/lexical_cast.hpp>
\#include <boost/detail/endian.hpp>
@@ -31,6 +29,9 @@ typedef size_t pred_type;
\#include <string>
\#include <vector>
+typedef size_t pred_type;
+typedef std::vector<pred_type> pred_vector_type;
+
enum dir_type{
DIR_OTW_TO_CPU = 0,
DIR_CPU_TO_OTW = 1
@@ -101,46 +102,60 @@ pred_type make_pred(const std::string &markup, dir_type &dir){
return pred;
}
+#define pred_table_wildcard pred_type(~0)
+#define pred_table_max_size size_t(128)
+#define pred_table_index(e) (pred_type(e) & 0x7f)
+
+static pred_vector_type get_pred_byte_order_table(void){
+ pred_vector_type table(pred_table_max_size, pred_table_wildcard);
+ \#ifdef BOOST_BIG_ENDIAN
+ table[pred_table_index(otw_type_t::BO_BIG_ENDIAN)] = $ph.nswap_p;
+ table[pred_table_index(otw_type_t::BO_LITTLE_ENDIAN)] = $ph.bswap_p;
+ \#else
+ table[pred_table_index(otw_type_t::BO_BIG_ENDIAN)] = $ph.bswap_p;
+ table[pred_table_index(otw_type_t::BO_LITTLE_ENDIAN)] = $ph.nswap_p;
+ \#endif
+ table[pred_table_index(otw_type_t::BO_NATIVE)] = $ph.nswap_p;
+ return table;
+}
+
+static pred_vector_type get_pred_io_type_table(void){
+ pred_vector_type table(pred_table_max_size, pred_table_wildcard);
+ table[pred_table_index(io_type_t::COMPLEX_FLOAT64)] = $ph.fc64_p;
+ table[pred_table_index(io_type_t::COMPLEX_FLOAT32)] = $ph.fc32_p;
+ table[pred_table_index(io_type_t::COMPLEX_INT16)] = $ph.sc16_p;
+ return table;
+}
+
+static pred_vector_type get_pred_num_io_table(void){
+ pred_vector_type table(pred_table_max_size, pred_table_wildcard);
+ table[1] = $ph.chan1_p;
+ table[2] = $ph.chan2_p;
+ table[3] = $ph.chan3_p;
+ table[4] = $ph.chan4_p;
+ return table;
+}
+
UHD_INLINE pred_type make_pred(
const io_type_t &io_type,
const otw_type_t &otw_type,
size_t num_inputs,
size_t num_outputs
){
- pred_type pred = 0;
+ pred_type pred = $ph.item32_p; //only item32 supported as of now
- switch(otw_type.byteorder){
- \#ifdef BOOST_BIG_ENDIAN
- case otw_type_t::BO_BIG_ENDIAN: pred |= $ph.nswap_p; break;
- case otw_type_t::BO_LITTLE_ENDIAN: pred |= $ph.bswap_p; break;
- \#else
- case otw_type_t::BO_BIG_ENDIAN: pred |= $ph.bswap_p; break;
- case otw_type_t::BO_LITTLE_ENDIAN: pred |= $ph.nswap_p; break;
- \#endif
- case otw_type_t::BO_NATIVE: pred |= $ph.nswap_p; break;
- default: throw pred_error("unhandled otw byteorder type");
- }
+ static const pred_vector_type pred_byte_order_table(get_pred_byte_order_table());
+ pred |= pred_byte_order_table[pred_table_index(otw_type.byteorder)];
- switch(otw_type.get_sample_size()){
- case sizeof(boost::uint32_t): pred |= $ph.item32_p; break;
- default: throw pred_error("unhandled otw sample size");
- }
+ static const pred_vector_type pred_io_type_table(get_pred_io_type_table());
+ pred |= pred_io_type_table[pred_table_index(io_type.tid)];
- switch(io_type.tid){
- case io_type_t::COMPLEX_FLOAT32: pred |= $ph.fc32_p; break;
- case io_type_t::COMPLEX_INT16: pred |= $ph.sc16_p; break;
- //case io_type_t::COMPLEX_INT8: pred |= $ph.sc8_p; break;
- case io_type_t::COMPLEX_FLOAT64: pred |= $ph.fc64_p; break;
- default: throw pred_error("unhandled io type id");
- }
+ static const pred_vector_type pred_num_io_table(get_pred_num_io_table());
+ pred |= pred_num_io_table[pred_table_index(num_inputs*num_outputs)];
- switch(num_inputs*num_outputs){ //FIXME treated as one value
- case 1: pred |= $ph.chan1_p; break;
- case 2: pred |= $ph.chan2_p; break;
- case 3: pred |= $ph.chan3_p; break;
- case 4: pred |= $ph.chan4_p; break;
- default: throw pred_error("unhandled number of channels");
- }
+ if (pred == pred_table_wildcard) throw pred_error(
+ "unhanded input combination for make_pred()"
+ );
return pred;
}
diff --git a/host/lib/ic_reg_maps/gen_ad9522_regs.py b/host/lib/ic_reg_maps/gen_ad9522_regs.py
index a5debe568..86605c34a 100755
--- a/host/lib/ic_reg_maps/gen_ad9522_regs.py
+++ b/host/lib/ic_reg_maps/gen_ad9522_regs.py
@@ -80,6 +80,14 @@ external_zero_delay_fcds 0x01E[4:3] 0
enable_external_zero_delay 0x01E[2] 0
enable_zero_delay 0x01E[1] 0
########################################################################
+vco_calibration_finished 0x01F[6] 0
+holdover_active 0x01F[5] 0
+ref2_selected 0x01F[4] 0
+vco_freq_gt_thresh 0x01F[3] 0
+ref2_freq_gt_thresh 0x01F[2] 0
+ref1_freq_gt_thresh 0x01F[1] 0
+digital_lock_detect 0x01F[0] 0
+########################################################################
#for $i in range(12)
#set $addr = ($i + 0x0F0)
out$(i)_format $(addr)[7] 0 lvds, cmos
diff --git a/host/lib/transport/CMakeLists.txt b/host/lib/transport/CMakeLists.txt
index 8765c6703..a66a58d32 100644
--- a/host/lib/transport/CMakeLists.txt
+++ b/host/lib/transport/CMakeLists.txt
@@ -77,5 +77,4 @@ LIBUHD_APPEND_SOURCES(
${CMAKE_CURRENT_SOURCE_DIR}/udp_simple.cpp
${CMAKE_CURRENT_SOURCE_DIR}/udp_zero_copy_asio.cpp
${CMAKE_CURRENT_SOURCE_DIR}/vrt_packet_handler.hpp
- ${CMAKE_CURRENT_SOURCE_DIR}/zero_copy.cpp
)
diff --git a/host/lib/transport/gen_vrt_if_packet.py b/host/lib/transport/gen_vrt_if_packet.py
index dbe026ba3..427217eb6 100755
--- a/host/lib/transport/gen_vrt_if_packet.py
+++ b/host/lib/transport/gen_vrt_if_packet.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python
#
-# Copyright 2010 Ettus Research LLC
+# Copyright 2010-2011 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
@@ -35,6 +35,7 @@ TMPL_TEXT = """
\#include <uhd/utils/byteswap.hpp>
\#include <boost/detail/endian.hpp>
\#include <stdexcept>
+\#include <vector>
//define the endian macros to convert integers
\#ifdef BOOST_BIG_ENDIAN
@@ -48,18 +49,28 @@ TMPL_TEXT = """
using namespace uhd;
using namespace uhd::transport;
-########################################################################
-#def gen_code($XE_MACRO, $suffix)
-########################################################################
+typedef size_t pred_type;
+typedef std::vector<pred_type> pred_table_type;
+#define pred_table_index(hdr) ((hdr >> 20) & 0x1ff)
+
+static pred_table_type get_pred_unpack_table(void){
+ pred_table_type table(1 << 9, 0); //only 9 bits useful here (20-28)
+ for (size_t i = 0; i < table.size(); i++){
+ boost::uint32_t vrt_hdr_word = i << 20;
+ if(vrt_hdr_word & $hex(0x1 << 28)) table[i] |= $hex($sid_p);
+ if(vrt_hdr_word & $hex(0x1 << 27)) table[i] |= $hex($cid_p);
+ if(vrt_hdr_word & $hex(0x3 << 22)) table[i] |= $hex($tsi_p);
+ if(vrt_hdr_word & $hex(0x3 << 20)) table[i] |= $hex($tsf_p);
+ if(vrt_hdr_word & $hex(0x1 << 26)) table[i] |= $hex($tlr_p);
+ }
+ return table;
+}
+
+static const pred_table_type pred_unpack_table(get_pred_unpack_table());
########################################################################
-## setup predicates
+#def gen_code($XE_MACRO, $suffix)
########################################################################
-#set $sid_p = 0b00001
-#set $cid_p = 0b00010
-#set $tsi_p = 0b00100
-#set $tsf_p = 0b01000
-#set $tlr_p = 0b10000
void vrt::if_hdr_pack_$(suffix)(
boost::uint32_t *packet_buff,
@@ -67,7 +78,7 @@ void vrt::if_hdr_pack_$(suffix)(
){
boost::uint32_t vrt_hdr_flags = 0;
- boost::uint8_t pred = 0;
+ pred_type pred = 0;
if (if_packet_info.has_sid) pred |= $hex($sid_p);
if (if_packet_info.has_cid) pred |= $hex($cid_p);
if (if_packet_info.has_tsi) pred |= $hex($tsi_p);
@@ -159,12 +170,7 @@ void vrt::if_hdr_unpack_$(suffix)(
//if_packet_info.sob = bool(vrt_hdr_word & $hex(0x1 << 25)); //not implemented
//if_packet_info.eob = bool(vrt_hdr_word & $hex(0x1 << 24)); //not implemented
- boost::uint8_t pred = 0;
- if(vrt_hdr_word & $hex(0x1 << 28)) pred |= $hex($sid_p);
- if(vrt_hdr_word & $hex(0x1 << 27)) pred |= $hex($cid_p);
- if(vrt_hdr_word & $hex(0x3 << 22)) pred |= $hex($tsi_p);
- if(vrt_hdr_word & $hex(0x3 << 20)) pred |= $hex($tsf_p);
- if(vrt_hdr_word & $hex(0x1 << 26)) pred |= $hex($tlr_p);
+ const pred_type pred = pred_unpack_table[pred_table_index(vrt_hdr_word)];
switch(pred){
#for $pred in range(2**5)
@@ -200,7 +206,7 @@ void vrt::if_hdr_unpack_$(suffix)(
if_packet_info.has_tsf = true;
if_packet_info.tsf = boost::uint64_t($(XE_MACRO)(packet_buff[$num_header_words])) << 32;
#set $num_header_words += 1
- if_packet_info.tsf |= boost::uint64_t($(XE_MACRO)(packet_buff[$num_header_words])) << 0;
+ if_packet_info.tsf |= $(XE_MACRO)(packet_buff[$num_header_words]);
#set $num_header_words += 1
#else
if_packet_info.has_tsf = false;
@@ -239,4 +245,12 @@ def parse_tmpl(_tmpl_text, **kwargs):
if __name__ == '__main__':
import sys
- open(sys.argv[1], 'w').write(parse_tmpl(TMPL_TEXT, file=__file__))
+ open(sys.argv[1], 'w').write(parse_tmpl(
+ TMPL_TEXT,
+ file=__file__,
+ sid_p = 0b00001,
+ cid_p = 0b00010,
+ tsi_p = 0b00100,
+ tsf_p = 0b01000,
+ tlr_p = 0b10000,
+ ))
diff --git a/host/lib/transport/libusb1_zero_copy.cpp b/host/lib/transport/libusb1_zero_copy.cpp
index 311a8953b..87adece45 100644
--- a/host/lib/transport/libusb1_zero_copy.cpp
+++ b/host/lib/transport/libusb1_zero_copy.cpp
@@ -21,266 +21,94 @@
#include <uhd/transport/buffer_pool.hpp>
#include <uhd/utils/thread_priority.hpp>
#include <uhd/utils/assert.hpp>
+#include <boost/function.hpp>
#include <boost/foreach.hpp>
-#include <boost/thread.hpp>
-#include <boost/enable_shared_from_this.hpp>
-#include <vector>
+#include <boost/thread/thread.hpp>
+#include <list>
#include <iostream>
using namespace uhd;
using namespace uhd::transport;
-static const double CLEANUP_TIMEOUT = 0.2; //seconds
static const size_t DEFAULT_NUM_XFERS = 16; //num xfers
static const size_t DEFAULT_XFER_SIZE = 32*512; //bytes
/***********************************************************************
- * Helper functions
- ***********************************************************************/
-/*
- * Print the values of a libusb_transfer struct
- * http://libusb.sourceforge.net/api-1.0/structlibusb__transfer.html
- */
-void pp_transfer(libusb_transfer *lut)
-{
- std::cout << "Libusb transfer" << std::endl;
- std::cout << " flags: 0x" << std::hex << (unsigned int) lut->flags << std::endl;
- std::cout << " endpoint: 0x" << std::hex << (unsigned int) lut->endpoint << std::endl;
- std::cout << " type: 0x" << std::hex << (unsigned int) lut->type << std::endl;
- std::cout << " timeout: " << std::dec << lut->timeout << std::endl;
- std::cout << " status: 0x" << std::hex << lut->status << std::endl;
- std::cout << " length: " << std::dec << lut->length << std::endl;
- std::cout << " actual_length: " << std::dec << lut->actual_length << std::endl;
-}
-
-/***********************************************************************
- * USB asynchronous zero_copy endpoint
- * This endpoint implementation provides asynchronous I/O to libusb-1.0
- * devices. Each endpoint is directional and two can be combined to
- * create a bidirectional interface. It is a zero copy implementation
- * with respect to libusb, however, each send and recv requires a copy
- * operation from kernel to userspace; this is due to the usbfs
- * interface provided by the kernel.
+ * Reusable managed receiver buffer:
+ * - Associated with a particular libusb transfer struct.
+ * - Submits the transfer to libusb in the release method.
**********************************************************************/
-class usb_endpoint {
+class libusb_zero_copy_mrb : public managed_recv_buffer{
public:
- typedef boost::shared_ptr<usb_endpoint> sptr;
-
- usb_endpoint(
- libusb::device_handle::sptr handle,
- int endpoint,
- bool input,
- size_t transfer_size,
- size_t num_transfers
- );
-
- ~usb_endpoint(void);
+ libusb_zero_copy_mrb(libusb_transfer *lut):
+ _lut(lut), _expired(true) { /* NOP */ }
- // Exposed interface for submitting / retrieving transfer buffers
-
- //! Submit a new transfer that was presumably just filled or emptied.
- void submit(libusb_transfer *lut);
-
- /*!
- * Get an available transfer:
- * For inputs, this is a just filled transfer.
- * For outputs, this is a just emptied transfer.
- * \param timeout the timeout to wait for a lut
- * \return the transfer pointer or NULL if timeout
- */
- libusb_transfer *get_lut_with_wait(double timeout);
+ void release(void){
+ if (_expired) return;
+ UHD_ASSERT_THROW(libusb_submit_transfer(_lut) == 0);
+ _expired = true;
+ }
- //Callback use only
- void callback_handle_transfer(libusb_transfer *lut);
+ sptr get_new(void){
+ _expired = false;
+ return sptr(this, &libusb_zero_copy_mrb::fake_deleter);
+ }
private:
- libusb::device_handle::sptr _handle;
- int _endpoint;
- bool _input;
-
- //! hold a bounded buffer of completed transfers
- typedef bounded_buffer<libusb_transfer *> lut_buff_type;
- lut_buff_type::sptr _completed_list;
-
- //! a list of all transfer structs we allocated
- std::vector<libusb_transfer *> _all_luts;
+ static void fake_deleter(void *obj){
+ static_cast<libusb_zero_copy_mrb *>(obj)->release();
+ }
- //! memory allocated for the transfer buffers
- buffer_pool::sptr _buffer_pool;
+ const void *get_buff(void) const{return _lut->buffer;}
+ size_t get_size(void) const{return _lut->actual_length;}
- // Calls for processing asynchronous I/O
- libusb_transfer *allocate_transfer(void *mem, size_t len);
- void print_transfer_status(libusb_transfer *lut);
+ libusb_transfer *_lut;
+ bool _expired;
};
-
-/*
- * Callback function called when submitted transfers complete.
- * The endpoint upon which the transfer is part of is recovered
- * and the transfer moved from pending to completed state.
- * Callbacks occur during the reaping calls where libusb_handle_events()
- * is used. The callback only modifies the transfer state by moving
- * it from the pending to completed status list.
- * \param lut pointer to libusb_transfer
- */
-static void callback(libusb_transfer *lut){
- usb_endpoint *endpoint = (usb_endpoint *) lut->user_data;
- endpoint->callback_handle_transfer(lut);
-}
-
-
-/*
- * Accessor call to allow list access from callback space
- * \param pointer to libusb_transfer
- */
-void usb_endpoint::callback_handle_transfer(libusb_transfer *lut){
- _completed_list->push_with_wait(lut);
-}
-
-
-/*
- * Constructor
- * Allocate libusb transfers and mark as free. For IN endpoints,
- * submit the transfers so that they're ready to return when
- * data is available.
- */
-usb_endpoint::usb_endpoint(
- libusb::device_handle::sptr handle,
- int endpoint,
- bool input,
- size_t transfer_size,
- size_t num_transfers
-):
- _handle(handle),
- _endpoint(endpoint),
- _input(input)
-{
- _completed_list = lut_buff_type::make(num_transfers);
- _buffer_pool = buffer_pool::make(num_transfers, transfer_size);
- for (size_t i = 0; i < num_transfers; i++){
- _all_luts.push_back(allocate_transfer(_buffer_pool->at(i), transfer_size));
-
- //input luts are immediately submitted to be filled
- //output luts go into the completed list as free buffers
- if (_input) this->submit(_all_luts.back());
- else _completed_list->push_with_wait(_all_luts.back());
+/***********************************************************************
+ * Reusable managed send buffer:
+ * - Associated with a particular libusb transfer struct.
+ * - Submits the transfer to libusb in the commit method.
+ **********************************************************************/
+class libusb_zero_copy_msb : public managed_send_buffer{
+public:
+ libusb_zero_copy_msb(libusb_transfer *lut):
+ _lut(lut), _expired(true) { /* NOP */ }
+
+ void commit(size_t len){
+ if (_expired) return;
+ _lut->length = len;
+ UHD_ASSERT_THROW(libusb_submit_transfer(_lut) == 0);
+ _expired = true;
}
-}
-
-/*
- * Destructor
- * Make sure all the memory is freed. Cancel any pending transfers.
- * When all completed transfers are moved to the free list, release
- * the transfers. Libusb will deallocate the data buffer held by
- * each transfer.
- */
-usb_endpoint::~usb_endpoint(void){
- //cancel all transfers
- BOOST_FOREACH(libusb_transfer *lut, _all_luts){
- libusb_cancel_transfer(lut);
+ sptr get_new(void){
+ _expired = false;
+ return sptr(this, &libusb_zero_copy_msb::fake_deleter);
}
- //collect canceled transfers (drain the queue)
- while (this->get_lut_with_wait(CLEANUP_TIMEOUT) != NULL){};
-
- //free all transfers
- BOOST_FOREACH(libusb_transfer *lut, _all_luts){
- libusb_free_transfer(lut);
+private:
+ static void fake_deleter(void *obj){
+ static_cast<libusb_zero_copy_msb *>(obj)->commit(0);
}
-}
-
-
-/*
- * Allocate a libusb transfer
- * The allocated transfer - and buffer it contains - is repeatedly
- * submitted, reaped, and reused and should not be freed until shutdown.
- * \param mem a pointer to the buffer memory
- * \param len size of the individual buffer
- * \return pointer to an allocated libusb_transfer
- */
-libusb_transfer *usb_endpoint::allocate_transfer(void *mem, size_t len){
- libusb_transfer *lut = libusb_alloc_transfer(0);
- UHD_ASSERT_THROW(lut != NULL);
-
- unsigned int endpoint = ((_endpoint & 0x7f) | (_input ? 0x80 : 0));
- unsigned char *buff = reinterpret_cast<unsigned char *>(mem);
- libusb_transfer_cb_fn lut_callback = libusb_transfer_cb_fn(&callback);
-
- libusb_fill_bulk_transfer(lut, // transfer
- _handle->get(), // dev_handle
- endpoint, // endpoint
- buff, // buffer
- len, // length
- lut_callback, // callback
- this, // user_data
- 0); // timeout
- return lut;
-}
+ void *get_buff(void) const{return _lut->buffer;}
+ size_t get_size(void) const{return _lut->length;}
-/*
- * Asynchonous transfer submission
- * Submit a libusb transfer to libusb add pending status
- * \param lut pointer to libusb_transfer
- * \return true on success or false on error
- */
-void usb_endpoint::submit(libusb_transfer *lut){
- UHD_ASSERT_THROW(libusb_submit_transfer(lut) == 0);
-}
-
-/*
- * Print status errors of a completed transfer
- * \param lut pointer to an libusb_transfer
- */
-void usb_endpoint::print_transfer_status(libusb_transfer *lut){
- std::cout << "here " << lut->status << std::endl;
- switch (lut->status) {
- case LIBUSB_TRANSFER_COMPLETED:
- if (lut->actual_length < lut->length) {
- std::cerr << "USB: transfer completed with short write,"
- << " length = " << lut->length
- << " actual = " << lut->actual_length << std::endl;
- }
-
- if ((lut->actual_length < 0) || (lut->length < 0)) {
- std::cerr << "USB: transfer completed with invalid response"
- << std::endl;
- }
- break;
- case LIBUSB_TRANSFER_CANCELLED:
- break;
- case LIBUSB_TRANSFER_NO_DEVICE:
- std::cerr << "USB: device was disconnected" << std::endl;
- break;
- case LIBUSB_TRANSFER_OVERFLOW:
- std::cerr << "USB: device sent more data than requested" << std::endl;
- break;
- case LIBUSB_TRANSFER_TIMED_OUT:
- std::cerr << "USB: transfer timed out" << std::endl;
- break;
- case LIBUSB_TRANSFER_STALL:
- std::cerr << "USB: halt condition detected (stalled)" << std::endl;
- break;
- case LIBUSB_TRANSFER_ERROR:
- std::cerr << "USB: transfer failed" << std::endl;
- break;
- default:
- std::cerr << "USB: received unknown transfer status" << std::endl;
- }
-}
+ libusb_transfer *_lut;
+ bool _expired;
+};
-libusb_transfer *usb_endpoint::get_lut_with_wait(double timeout){
- boost::this_thread::disable_interruption di; //disable because the wait can throw
- libusb_transfer *lut;
- if (_completed_list->pop_with_timed_wait(lut, timeout)) return lut;
- return NULL;
+//! helper function: handles all async callbacks
+static void libusb_async_cb(libusb_transfer *lut){
+ (*static_cast<boost::function<void()> *>(lut->user_data))();
}
/***********************************************************************
* USB zero_copy device class
**********************************************************************/
-class libusb_zero_copy_impl : public usb_zero_copy, public boost::enable_shared_from_this<libusb_zero_copy_impl> {
+class libusb_zero_copy_impl : public usb_zero_copy{
public:
libusb_zero_copy_impl(
@@ -288,16 +116,107 @@ public:
size_t recv_endpoint,
size_t send_endpoint,
const device_addr_t &hints
- );
+ ):
+ _handle(handle),
+ _recv_frame_size(size_t(hints.cast<double>("recv_frame_size", DEFAULT_XFER_SIZE))),
+ _num_recv_frames(size_t(hints.cast<double>("num_recv_frames", DEFAULT_NUM_XFERS))),
+ _send_frame_size(size_t(hints.cast<double>("send_frame_size", DEFAULT_XFER_SIZE))),
+ _num_send_frames(size_t(hints.cast<double>("num_send_frames", DEFAULT_NUM_XFERS))),
+ _recv_buffer_pool(buffer_pool::make(_num_recv_frames, _recv_frame_size)),
+ _send_buffer_pool(buffer_pool::make(_num_send_frames, _send_frame_size)),
+ _pending_recv_buffs(_num_recv_frames),
+ _pending_send_buffs(_num_send_frames)
+ {
+ _handle->claim_interface(2 /*in interface*/);
+ _handle->claim_interface(1 /*out interface*/);
+
+ //allocate libusb transfer structs and managed receive buffers
+ for (size_t i = 0; i < get_num_recv_frames(); i++){
+
+ libusb_transfer *lut = libusb_alloc_transfer(0);
+ UHD_ASSERT_THROW(lut != NULL);
+
+ _mrb_pool.push_back(libusb_zero_copy_mrb(lut));
+ _callbacks.push_back(boost::bind(
+ &libusb_zero_copy_impl::handle_recv, this, &_mrb_pool.back()
+ ));
+
+ libusb_fill_bulk_transfer(
+ lut, // transfer
+ _handle->get(), // dev_handle
+ (recv_endpoint & 0x7f) | 0x80, // endpoint
+ static_cast<unsigned char *>(_recv_buffer_pool->at(i)), // buffer
+ this->get_recv_frame_size(), // length
+ libusb_transfer_cb_fn(&libusb_async_cb), // callback
+ static_cast<void *>(&_callbacks.back()), // user_data
+ 0 // timeout
+ );
+
+ _all_luts.push_back(lut);
+ _mrb_pool.back().get_new();
+ }
+
+ //allocate libusb transfer structs and managed send buffers
+ for (size_t i = 0; i < get_num_send_frames(); i++){
+
+ libusb_transfer *lut = libusb_alloc_transfer(0);
+ UHD_ASSERT_THROW(lut != NULL);
+
+ _msb_pool.push_back(libusb_zero_copy_msb(lut));
+ _callbacks.push_back(boost::bind(
+ &libusb_zero_copy_impl::handle_send, this, &_msb_pool.back()
+ ));
+
+ libusb_fill_bulk_transfer(
+ lut, // transfer
+ _handle->get(), // dev_handle
+ (send_endpoint & 0x7f) | 0x00, // endpoint
+ static_cast<unsigned char *>(_send_buffer_pool->at(i)), // buffer
+ this->get_send_frame_size(), // length
+ libusb_transfer_cb_fn(&libusb_async_cb), // callback
+ static_cast<void *>(&_callbacks.back()), // user_data
+ 0 // timeout
+ );
+
+ _all_luts.push_back(lut);
+ libusb_async_cb(lut);
+ }
+
+ //spawn the event handler threads
+ size_t concurrency = hints.cast<size_t>("concurrency_hint", 1);
+ for (size_t i = 0; i < concurrency; i++) _thread_group.create_thread(
+ boost::bind(&libusb_zero_copy_impl::run_event_loop, this)
+ );
+ }
~libusb_zero_copy_impl(void){
+ //shutdown the threads
_threads_running = false;
_thread_group.interrupt_all();
_thread_group.join_all();
+
+ //cancel and free all transfers
+ BOOST_FOREACH(libusb_transfer *lut, _all_luts){
+ libusb_cancel_transfer(lut);
+ libusb_free_transfer(lut);
+ }
}
- managed_recv_buffer::sptr get_recv_buff(double);
- managed_send_buffer::sptr get_send_buff(double);
+ managed_recv_buffer::sptr get_recv_buff(double timeout){
+ libusb_zero_copy_mrb *mrb = NULL;
+ if (_pending_recv_buffs.pop_with_timed_wait(mrb, timeout)){
+ return mrb->get_new();
+ }
+ return managed_recv_buffer::sptr();
+ }
+
+ managed_send_buffer::sptr get_send_buff(double timeout){
+ libusb_zero_copy_msb *msb = NULL;
+ if (_pending_send_buffs.pop_with_timed_wait(msb, timeout)){
+ return msb->get_new();
+ }
+ return managed_send_buffer::sptr();
+ }
size_t get_num_recv_frames(void) const { return _num_recv_frames; }
size_t get_num_send_frames(void) const { return _num_send_frames; }
@@ -306,125 +225,50 @@ public:
size_t get_send_frame_size(void) const { return _send_frame_size; }
private:
- void release(libusb_transfer *lut){
- _recv_ep->submit(lut);
+ //! Handle a bound async callback for recv
+ void handle_recv(libusb_zero_copy_mrb *mrb){
+ _pending_recv_buffs.push_with_haste(mrb);
}
- void commit(libusb_transfer *lut, size_t num_bytes){
- lut->length = num_bytes;
- try{
- _send_ep->submit(lut);
- }
- catch(const std::exception &e){
- std::cerr << "Error in commit: " << e.what() << std::endl;
- }
+ //! Handle a bound async callback for send
+ void handle_send(libusb_zero_copy_msb *msb){
+ _pending_send_buffs.push_with_haste(msb);
}
libusb::device_handle::sptr _handle;
const size_t _recv_frame_size, _num_recv_frames;
const size_t _send_frame_size, _num_send_frames;
- usb_endpoint::sptr _recv_ep, _send_ep;
- //event handler threads
+ //! Storage for transfer related objects
+ buffer_pool::sptr _recv_buffer_pool, _send_buffer_pool;
+ bounded_buffer<libusb_zero_copy_mrb *> _pending_recv_buffs;
+ bounded_buffer<libusb_zero_copy_msb *> _pending_send_buffs;
+ std::list<libusb_zero_copy_mrb> _mrb_pool;
+ std::list<libusb_zero_copy_msb> _msb_pool;
+ std::list<boost::function<void()> > _callbacks;
+
+ //! a list of all transfer structs we allocated
+ std::list<libusb_transfer *> _all_luts;
+
+ //! event handler threads
boost::thread_group _thread_group;
bool _threads_running;
void run_event_loop(void){
set_thread_priority_safe();
- libusb::session::sptr session = libusb::session::get_global_session();
+ libusb_context *context = libusb::session::get_global_session()->get_context();
_threads_running = true;
try{
while(_threads_running){
timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 100000; //100ms
- libusb_handle_events_timeout(session->get_context(), &tv);
+ libusb_handle_events_timeout(context, &tv);
}
} catch(const boost::thread_interrupted &){}
}
-};
-
-/*
- * Constructor
- * Initializes libusb, opens devices, and sets up interfaces for I/O.
- * Finally, creates endpoints for asynchronous I/O.
- */
-libusb_zero_copy_impl::libusb_zero_copy_impl(
- libusb::device_handle::sptr handle,
- size_t recv_endpoint,
- size_t send_endpoint,
- const device_addr_t &hints
-):
- _handle(handle),
- _recv_frame_size(size_t(hints.cast<double>("recv_frame_size", DEFAULT_XFER_SIZE))),
- _num_recv_frames(size_t(hints.cast<double>("num_recv_frames", DEFAULT_NUM_XFERS))),
- _send_frame_size(size_t(hints.cast<double>("send_frame_size", DEFAULT_XFER_SIZE))),
- _num_send_frames(size_t(hints.cast<double>("num_send_frames", DEFAULT_NUM_XFERS)))
-{
- _handle->claim_interface(2 /*in interface*/);
- _handle->claim_interface(1 /*out interface*/);
-
- _recv_ep = usb_endpoint::sptr(new usb_endpoint(
- _handle, // libusb device_handle
- recv_endpoint, // USB endpoint number
- true, // IN endpoint
- this->get_recv_frame_size(), // buffer size per transfer
- this->get_num_recv_frames() // number of libusb transfers
- ));
-
- _send_ep = usb_endpoint::sptr(new usb_endpoint(
- _handle, // libusb device_handle
- send_endpoint, // USB endpoint number
- false, // OUT endpoint
- this->get_send_frame_size(), // buffer size per transfer
- this->get_num_send_frames() // number of libusb transfers
- ));
-
- //spawn the event handler threads
- size_t concurrency = hints.cast<size_t>("concurrency_hint", 1);
- for (size_t i = 0; i < concurrency; i++) _thread_group.create_thread(
- boost::bind(&libusb_zero_copy_impl::run_event_loop, this)
- );
-}
-
-/*
- * Construct a managed receive buffer from a completed libusb transfer
- * (happy with buffer full of data) obtained from the receive endpoint.
- * Return empty pointer if no transfer is available (timeout or error).
- * \return pointer to a managed receive buffer
- */
-managed_recv_buffer::sptr libusb_zero_copy_impl::get_recv_buff(double timeout){
- libusb_transfer *lut = _recv_ep->get_lut_with_wait(timeout);
- if (lut == NULL) {
- return managed_recv_buffer::sptr();
- }
- else {
- return managed_recv_buffer::make_safe(
- boost::asio::const_buffer(lut->buffer, lut->actual_length),
- boost::bind(&libusb_zero_copy_impl::release, shared_from_this(), lut)
- );
- }
-}
-
-/*
- * Construct a managed send buffer from a free libusb transfer (with
- * empty buffer). Return empty pointer of no transfer is available
- * (timeout or error).
- * \return pointer to a managed send buffer
- */
-managed_send_buffer::sptr libusb_zero_copy_impl::get_send_buff(double timeout){
- libusb_transfer *lut = _send_ep->get_lut_with_wait(timeout);
- if (lut == NULL) {
- return managed_send_buffer::sptr();
- }
- else {
- return managed_send_buffer::make_safe(
- boost::asio::mutable_buffer(lut->buffer, this->get_send_frame_size()),
- boost::bind(&libusb_zero_copy_impl::commit, shared_from_this(), lut, _1)
- );
- }
-}
+};
/***********************************************************************
* USB zero_copy make functions
diff --git a/host/lib/transport/udp_zero_copy_asio.cpp b/host/lib/transport/udp_zero_copy_asio.cpp
index a80de7b87..05352ffce 100644
--- a/host/lib/transport/udp_zero_copy_asio.cpp
+++ b/host/lib/transport/udp_zero_copy_asio.cpp
@@ -19,53 +19,102 @@
#include <uhd/transport/udp_simple.hpp> //mtu
#include <uhd/transport/bounded_buffer.hpp>
#include <uhd/transport/buffer_pool.hpp>
-#include <uhd/utils/thread_priority.hpp>
#include <uhd/utils/assert.hpp>
#include <uhd/utils/warning.hpp>
#include <boost/asio.hpp>
#include <boost/format.hpp>
-#include <boost/thread/thread.hpp>
-#include <boost/enable_shared_from_this.hpp>
#include <iostream>
+#include <list>
using namespace uhd;
using namespace uhd::transport;
namespace asio = boost::asio;
-//Define this to the the boost async io calls to perform receive.
-//Otherwise, get_recv_buff uses a blocking receive with timeout.
-#define USE_ASIO_ASYNC_RECV
-
-//Define this to the the boost async io calls to perform send.
-//Otherwise, the commit callback uses a blocking send.
-//#define USE_ASIO_ASYNC_SEND
-
-//The asio async receive implementation is broken for some macos.
-//Just disable for all macos since we don't know the problem.
-#if defined(UHD_PLATFORM_MACOS) && defined(USE_ASIO_ASYNC_RECV)
- #undef USE_ASIO_ASYNC_RECV
-#endif
-
-//The number of service threads to spawn for async ASIO:
-//A single concurrent thread for io_service seems to be the fastest.
-//Threads are disabled when no async implementations are enabled.
-#if defined(USE_ASIO_ASYNC_RECV) || defined(USE_ASIO_ASYNC_SEND)
-static const size_t CONCURRENCY_HINT = 1;
-#else
-static const size_t CONCURRENCY_HINT = 0;
-#endif
-
//A reasonable number of frames for send/recv and async/sync
static const size_t DEFAULT_NUM_FRAMES = 32;
/***********************************************************************
+ * Reusable managed receiver buffer:
+ * - Initialize with memory and a release callback.
+ * - Call get new with a length in bytes to re-use.
+ **********************************************************************/
+class udp_zero_copy_asio_mrb : public managed_recv_buffer{
+public:
+ typedef boost::function<void(udp_zero_copy_asio_mrb *)> release_cb_type;
+
+ udp_zero_copy_asio_mrb(void *mem, const release_cb_type &release_cb):
+ _mem(mem), _len(0), _release_cb(release_cb){/* NOP */}
+
+ void release(void){
+ if (_len == 0) return;
+ this->_release_cb(this);
+ _len = 0;
+ }
+
+ sptr get_new(size_t len){
+ _len = len;
+ return sptr(this, &udp_zero_copy_asio_mrb::fake_deleter);
+ }
+
+ template <class T> T cast(void) const{return static_cast<T>(_mem);}
+
+private:
+ static void fake_deleter(void *obj){
+ static_cast<udp_zero_copy_asio_mrb *>(obj)->release();
+ }
+
+ const void *get_buff(void) const{return _mem;}
+ size_t get_size(void) const{return _len;}
+
+ void *_mem;
+ size_t _len;
+ release_cb_type _release_cb;
+};
+
+/***********************************************************************
+ * Reusable managed send buffer:
+ * - Initialize with memory and a commit callback.
+ * - Call get new with a length in bytes to re-use.
+ **********************************************************************/
+class udp_zero_copy_asio_msb : public managed_send_buffer{
+public:
+ typedef boost::function<void(udp_zero_copy_asio_msb *, size_t)> commit_cb_type;
+
+ udp_zero_copy_asio_msb(void *mem, const commit_cb_type &commit_cb):
+ _mem(mem), _len(0), _commit_cb(commit_cb){/* NOP */}
+
+ void commit(size_t len){
+ if (_len == 0) return;
+ this->_commit_cb(this, len);
+ _len = 0;
+ }
+
+ sptr get_new(size_t len){
+ _len = len;
+ return sptr(this, &udp_zero_copy_asio_msb::fake_deleter);
+ }
+
+private:
+ static void fake_deleter(void *obj){
+ static_cast<udp_zero_copy_asio_msb *>(obj)->commit(0);
+ }
+
+ void *get_buff(void) const{return _mem;}
+ size_t get_size(void) const{return _len;}
+
+ void *_mem;
+ size_t _len;
+ commit_cb_type _commit_cb;
+};
+
+/***********************************************************************
* Zero Copy UDP 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 udp_zero_copy_asio_impl : public udp_zero_copy, public boost::enable_shared_from_this<udp_zero_copy_asio_impl> {
+class udp_zero_copy_asio_impl : public udp_zero_copy{
public:
typedef boost::shared_ptr<udp_zero_copy_asio_impl> sptr;
@@ -78,8 +127,10 @@ public:
_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))),
- _concurrency_hint(hints.cast<size_t>("concurrency_hint", CONCURRENCY_HINT)),
- _io_service(_concurrency_hint)
+ _recv_buffer_pool(buffer_pool::make(_num_recv_frames, _recv_frame_size)),
+ _send_buffer_pool(buffer_pool::make(_num_send_frames, _send_frame_size)),
+ _pending_recv_buffs(_num_recv_frames),
+ _pending_send_buffs(_num_send_frames)
{
//std::cout << boost::format("Creating udp transport for %s %s") % addr % port << std::endl;
@@ -93,39 +144,26 @@ public:
_socket->open(asio::ip::udp::v4());
_socket->connect(receiver_endpoint);
_sock_fd = _socket->native();
- }
-
- ~udp_zero_copy_asio_impl(void){
- delete _work; //allow io_service run to complete
- _thread_group.join_all(); //wait for service threads to exit
- delete _socket;
- }
- void init(void){
- //allocate all recv frames and release them to begin xfers
- _pending_recv_buffs = pending_buffs_type::make(_num_recv_frames);
- _recv_buffer_pool = buffer_pool::make(_num_recv_frames, _recv_frame_size);
- for (size_t i = 0; i < _num_recv_frames; i++){
- release(_recv_buffer_pool->at(i));
+ //allocate re-usable managed receive buffers
+ for (size_t i = 0; i < get_num_recv_frames(); i++){
+ _mrb_pool.push_back(udp_zero_copy_asio_mrb(_recv_buffer_pool->at(i),
+ boost::bind(&udp_zero_copy_asio_impl::release, this, _1))
+ );
+ handle_recv(&_mrb_pool.back());
}
- //allocate all send frames and push them into the fifo
- _pending_send_buffs = pending_buffs_type::make(_num_send_frames);
- _send_buffer_pool = buffer_pool::make(_num_send_frames, _send_frame_size);
- for (size_t i = 0; i < _num_send_frames; i++){
- handle_send(_send_buffer_pool->at(i));
+ //allocate re-usable managed send buffers
+ for (size_t i = 0; i < get_num_send_frames(); i++){
+ _msb_pool.push_back(udp_zero_copy_asio_msb(_send_buffer_pool->at(i),
+ boost::bind(&udp_zero_copy_asio_impl::commit, this, _1, _2))
+ );
+ handle_send(&_msb_pool.back());
}
-
- //spawn the service threads that will run the io service
- _work = new asio::io_service::work(_io_service); //new work to delete later
- for (size_t i = 0; i < _concurrency_hint; i++) _thread_group.create_thread(
- boost::bind(&udp_zero_copy_asio_impl::service, this)
- );
}
- void service(void){
- set_thread_priority_safe();
- _io_service.run();
+ ~udp_zero_copy_asio_impl(void){
+ delete _socket;
}
//get size for internal socket buffer
@@ -142,50 +180,15 @@ public:
return get_buff_size<Opt>();
}
- //! handle a recv callback -> push the filled memory into the fifo
- UHD_INLINE void handle_recv(void *mem, size_t len){
- boost::this_thread::disable_interruption di; //disable because the wait can throw
- _pending_recv_buffs->push_with_wait(boost::asio::buffer(mem, len));
- }
-
- ////////////////////////////////////////////////////////////////////
- #ifdef USE_ASIO_ASYNC_RECV
- ////////////////////////////////////////////////////////////////////
- //! pop a filled recv buffer off of the fifo and bind with the release callback
- managed_recv_buffer::sptr get_recv_buff(double timeout){
- boost::this_thread::disable_interruption di; //disable because the wait can throw
- asio::mutable_buffer buff;
- if (_pending_recv_buffs->pop_with_timed_wait(buff, timeout)){
- return managed_recv_buffer::make_safe(
- buff, boost::bind(
- &udp_zero_copy_asio_impl::release,
- shared_from_this(),
- asio::buffer_cast<void*>(buff)
- )
- );
- }
- return managed_recv_buffer::sptr();
- }
-
- //! release a recv buffer -> start an async recv on the buffer
- void release(void *mem){
- _socket->async_receive(
- boost::asio::buffer(mem, this->get_recv_frame_size()),
- boost::bind(
- &udp_zero_copy_asio_impl::handle_recv,
- shared_from_this(), mem,
- asio::placeholders::bytes_transferred
- )
- );
- }
-
- ////////////////////////////////////////////////////////////////////
- #else /*USE_ASIO_ASYNC_RECV*/
- ////////////////////////////////////////////////////////////////////
- managed_recv_buffer::sptr get_recv_buff(double timeout){
- boost::this_thread::disable_interruption di; //disable because the wait can throw
- asio::mutable_buffer buff;
-
+ /*******************************************************************
+ * Receive implementation:
+ *
+ * Use select to perform a blocking receive with timeout.
+ * Return the managed receive buffer with the new length.
+ * When the caller is finished with the managed buffer,
+ * the managed receive buffer is released back into the queue.
+ ******************************************************************/
+ UHD_INLINE bool is_recv_ready(double timeout){
//setup timeval for timeout
timeval tv;
tv.tv_sec = 0;
@@ -196,104 +199,70 @@ public:
FD_ZERO(&rset);
FD_SET(_sock_fd, &rset);
- //call select to perform timed wait and grab an available buffer with wait
- //if the condition is true, call receive and return the managed buffer
- if (
- ::select(_sock_fd+1, &rset, NULL, NULL, &tv) > 0 and
- _pending_recv_buffs->pop_with_timed_wait(buff, timeout)
- ){
- return managed_recv_buffer::make_safe(
- asio::buffer(
- boost::asio::buffer_cast<void *>(buff),
- _socket->receive(asio::buffer(buff))
- ),
- boost::bind(
- &udp_zero_copy_asio_impl::release,
- shared_from_this(),
- asio::buffer_cast<void*>(buff)
- )
- );
+ //call select with timeout on receive socket
+ return ::select(_sock_fd+1, &rset, NULL, NULL, &tv) > 0;
+ }
+
+ managed_recv_buffer::sptr get_recv_buff(double timeout){
+ udp_zero_copy_asio_mrb *mrb = NULL;
+ if (is_recv_ready(timeout) and _pending_recv_buffs.pop_with_timed_wait(mrb, timeout)){
+ return mrb->get_new(::recv(_sock_fd, mrb->cast<char *>(), _recv_frame_size, 0));
}
return managed_recv_buffer::sptr();
}
- void release(void *mem){
- boost::this_thread::disable_interruption di; //disable because the wait can throw
- handle_recv(mem, this->get_recv_frame_size());
+ UHD_INLINE void handle_recv(udp_zero_copy_asio_mrb *mrb){
+ _pending_recv_buffs.push_with_haste(mrb);
}
- ////////////////////////////////////////////////////////////////////
- #endif /*USE_ASIO_ASYNC_RECV*/
- ////////////////////////////////////////////////////////////////////
+ void release(udp_zero_copy_asio_mrb *mrb){
+ handle_recv(mrb);
+ }
size_t get_num_recv_frames(void) const {return _num_recv_frames;}
size_t get_recv_frame_size(void) const {return _recv_frame_size;}
- //! handle a send callback -> push the emptied memory into the fifo
- UHD_INLINE void handle_send(void *mem){
- boost::this_thread::disable_interruption di; //disable because the wait can throw
- _pending_send_buffs->push_with_wait(boost::asio::buffer(mem, this->get_send_frame_size()));
- }
-
- //! pop an empty send buffer off of the fifo and bind with the commit callback
+ /*******************************************************************
+ * Send implementation:
+ *
+ * Get a managed receive buffer immediately with max length set.
+ * The caller will fill the buffer and commit it when finished.
+ * The commit routine will perform a blocking send operation,
+ * and push the managed send buffer back into the queue.
+ ******************************************************************/
managed_send_buffer::sptr get_send_buff(double timeout){
- boost::this_thread::disable_interruption di; //disable because the wait can throw
- asio::mutable_buffer buff;
- if (_pending_send_buffs->pop_with_timed_wait(buff, timeout)){
- return managed_send_buffer::make_safe(
- buff, boost::bind(
- &udp_zero_copy_asio_impl::commit,
- shared_from_this(),
- asio::buffer_cast<void*>(buff), _1
- )
- );
+ udp_zero_copy_asio_msb *msb = NULL;
+ if (_pending_send_buffs.pop_with_timed_wait(msb, timeout)){
+ return msb->get_new(_send_frame_size);
}
return managed_send_buffer::sptr();
}
- ////////////////////////////////////////////////////////////////////
- #ifdef USE_ASIO_ASYNC_SEND
- ////////////////////////////////////////////////////////////////////
- //! commit a send buffer -> start an async send on the buffer
- void commit(void *mem, size_t len){
- _socket->async_send(
- boost::asio::buffer(mem, len),
- boost::bind(
- &udp_zero_copy_asio_impl::handle_send,
- shared_from_this(), mem
- )
- );
+ UHD_INLINE void handle_send(udp_zero_copy_asio_msb *msb){
+ _pending_send_buffs.push_with_haste(msb);
}
- ////////////////////////////////////////////////////////////////////
- #else /*USE_ASIO_ASYNC_SEND*/
- ////////////////////////////////////////////////////////////////////
- void commit(void *mem, size_t len){
- _socket->send(asio::buffer(mem, len));
- handle_send(mem);
+ void commit(udp_zero_copy_asio_msb *msb, size_t len){
+ ::send(_sock_fd, msb->cast<const char *>(), len, 0);
+ handle_send(msb);
}
- ////////////////////////////////////////////////////////////////////
- #endif /*USE_ASIO_ASYNC_SEND*/
- ////////////////////////////////////////////////////////////////////
-
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
- boost::thread_group _thread_group;
- buffer_pool::sptr _send_buffer_pool, _recv_buffer_pool;
- typedef bounded_buffer<asio::mutable_buffer> pending_buffs_type;
- pending_buffs_type::sptr _pending_recv_buffs, _pending_send_buffs;
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;
+ bounded_buffer<udp_zero_copy_asio_mrb *> _pending_recv_buffs;
+ bounded_buffer<udp_zero_copy_asio_msb *> _pending_send_buffs;
+ std::list<udp_zero_copy_asio_msb> _msb_pool;
+ std::list<udp_zero_copy_asio_mrb> _mrb_pool;
//asio guts -> socket and service
- size_t _concurrency_hint;
asio::io_service _io_service;
asio::ip::udp::socket *_socket;
- asio::io_service::work *_work;
int _sock_fd;
};
@@ -346,7 +315,5 @@ udp_zero_copy::sptr udp_zero_copy::make(
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");
- udp_trans->init(); //buffers resized -> call init() to use
-
return udp_trans;
}
diff --git a/host/lib/transport/vrt_packet_handler.hpp b/host/lib/transport/vrt_packet_handler.hpp
index c535edd04..6f3ac0421 100644
--- a/host/lib/transport/vrt_packet_handler.hpp
+++ b/host/lib/transport/vrt_packet_handler.hpp
@@ -67,13 +67,16 @@ template <typename T> UHD_INLINE T get_context_code(
std::vector<const boost::uint8_t *> copy_buffs;
size_t size_of_copy_buffs;
size_t fragment_offset_in_samps;
+ std::vector<void *> io_buffs;
+ std::vector<const void *> otw_buffs;
recv_state(size_t width = 1):
width(width),
managed_buffs(width),
copy_buffs(width, NULL),
size_of_copy_buffs(0),
- fragment_offset_in_samps(0)
+ fragment_offset_in_samps(0),
+ io_buffs(0) //resized later
{
/* NOP */
}
@@ -144,7 +147,7 @@ template <typename T> UHD_INLINE T get_context_code(
******************************************************************/
static UHD_INLINE size_t _recv1(
recv_state &state,
- const std::vector<void *> &buffs,
+ const uhd::device::recv_buffs_type &buffs,
size_t offset_bytes,
size_t total_samps,
uhd::rx_metadata_t &metadata,
@@ -192,17 +195,15 @@ template <typename T> UHD_INLINE T get_context_code(
size_t bytes_to_copy = nsamps_to_copy*bytes_per_item;
size_t nsamps_to_copy_per_io_buff = nsamps_to_copy/chans_per_otw_buff;
- std::vector<void *> io_buffs(chans_per_otw_buff);
- for (size_t i = 0; i < state.width; i+=chans_per_otw_buff){
+ for (size_t i = 0; i < buffs.size(); i+=chans_per_otw_buff){
//fill a vector with pointers to the io buffers
for (size_t j = 0; j < chans_per_otw_buff; j++){
- io_buffs[j] = reinterpret_cast<boost::uint8_t *>(buffs[i+j]) + offset_bytes;
+ state.io_buffs[j] = reinterpret_cast<boost::uint8_t *>(buffs[i+j]) + offset_bytes;
}
//copy-convert the samples from the recv buffer
- uhd::convert::input_type otw_buffs(1, state.copy_buffs[i]);
- converter(otw_buffs, io_buffs, nsamps_to_copy_per_io_buff);
+ converter(state.copy_buffs[i], state.io_buffs, nsamps_to_copy_per_io_buff);
//update the rx copy buffer to reflect the bytes copied
state.copy_buffs[i] += bytes_to_copy;
@@ -223,7 +224,7 @@ template <typename T> UHD_INLINE T get_context_code(
******************************************************************/
static UHD_INLINE size_t recv(
recv_state &state,
- const std::vector<void *> &buffs,
+ const uhd::device::recv_buffs_type &buffs,
const size_t total_num_samps,
uhd::rx_metadata_t &metadata,
uhd::device::recv_mode_t recv_mode,
@@ -236,6 +237,8 @@ template <typename T> UHD_INLINE T get_context_code(
size_t vrt_header_offset_words32 = 0,
size_t chans_per_otw_buff = 1
){
+ state.io_buffs.resize(chans_per_otw_buff);
+
uhd::convert::function_type converter(
uhd::convert::get_converter_otw_to_cpu(
io_type, otw_type, 1, chans_per_otw_buff
@@ -300,8 +303,18 @@ template <typename T> UHD_INLINE T get_context_code(
struct send_state{
//init the expected seq number
size_t next_packet_seq;
+ managed_send_buffs_t managed_buffs;
+ const boost::uint64_t zeros;
+ std::vector<const void *> zero_buffs;
+ std::vector<const void *> io_buffs;
- send_state(void) : next_packet_seq(0){
+ send_state(size_t width = 1):
+ next_packet_seq(0),
+ managed_buffs(width),
+ zeros(0),
+ zero_buffs(width, &zeros),
+ io_buffs(0) //resized later
+ {
/* NOP */
}
};
@@ -312,7 +325,7 @@ template <typename T> UHD_INLINE T get_context_code(
******************************************************************/
static UHD_INLINE size_t _send1(
send_state &state,
- const std::vector<const void *> &buffs,
+ const uhd::device::send_buffs_type &buffs,
const size_t offset_bytes,
const size_t num_samps,
uhd::transport::vrt::if_packet_info_t &if_packet_info,
@@ -326,29 +339,26 @@ template <typename T> UHD_INLINE T get_context_code(
if_packet_info.num_payload_words32 = (num_samps*chans_per_otw_buff*OTW_BYTES_PER_SAMP)/sizeof(boost::uint32_t);
if_packet_info.packet_count = state.next_packet_seq;
- //get send buffers for each channel
- managed_send_buffs_t send_buffs(buffs.size()/chans_per_otw_buff);
- if (not get_send_buffs(send_buffs)) return 0;
+ //get send buffers for each otw channel
+ if (not get_send_buffs(state.managed_buffs)) return 0;
- std::vector<const void *> io_buffs(chans_per_otw_buff);
for (size_t i = 0; i < buffs.size(); i+=chans_per_otw_buff){
//calculate pointers with offsets to io and otw memory
for (size_t j = 0; j < chans_per_otw_buff; j++){
- io_buffs[j] = reinterpret_cast<const boost::uint8_t *>(buffs[i+j]) + offset_bytes;
+ state.io_buffs[j] = reinterpret_cast<const boost::uint8_t *>(buffs[i+j]) + offset_bytes;
}
- boost::uint32_t *otw_mem = send_buffs[i]->cast<boost::uint32_t *>() + vrt_header_offset_words32;
+ boost::uint32_t *otw_mem = state.managed_buffs[i]->cast<boost::uint32_t *>() + vrt_header_offset_words32;
//pack metadata into a vrt header
vrt_packer(otw_mem, if_packet_info);
otw_mem += if_packet_info.num_header_words32;
//copy-convert the samples into the send buffer
- uhd::convert::output_type otw_buffs(1, otw_mem);
- converter(io_buffs, otw_buffs, num_samps);
+ converter(state.io_buffs, otw_mem, num_samps);
//commit the samples to the zero-copy interface
size_t num_bytes_total = (vrt_header_offset_words32+if_packet_info.num_packet_words32)*sizeof(boost::uint32_t);
- send_buffs[i]->commit(num_bytes_total);
+ state.managed_buffs[i]->commit(num_bytes_total);
}
state.next_packet_seq++; //increment sequence after commits
return num_samps;
@@ -359,7 +369,7 @@ template <typename T> UHD_INLINE T get_context_code(
******************************************************************/
static UHD_INLINE size_t send(
send_state &state,
- const std::vector<const void *> &buffs,
+ const uhd::device::send_buffs_type &buffs,
const size_t total_num_samps,
const uhd::tx_metadata_t &metadata,
uhd::device::send_mode_t send_mode,
@@ -372,6 +382,8 @@ template <typename T> UHD_INLINE T get_context_code(
size_t vrt_header_offset_words32 = 0,
size_t chans_per_otw_buff = 1
){
+ state.io_buffs.resize(chans_per_otw_buff);
+
uhd::convert::function_type converter(
uhd::convert::get_converter_cpu_to_otw(
io_type, otw_type, chans_per_otw_buff, 1
@@ -398,19 +410,11 @@ template <typename T> UHD_INLINE T get_context_code(
if_packet_info.sob = metadata.start_of_burst;
if_packet_info.eob = metadata.end_of_burst;
- //TODO remove this code when sample counts of zero are supported by hardware
- std::vector<const void *> buffs_(buffs);
- size_t total_num_samps_(total_num_samps);
- if (total_num_samps == 0){
- static const boost::uint64_t zeros = 0; //max size of a host sample
- buffs_ = std::vector<const void *>(buffs.size(), &zeros);
- total_num_samps_ = 1;
- }
-
return _send1(
state,
- buffs_, 0,
- std::min(total_num_samps_, max_samples_per_packet),
+ //TODO remove this code when sample counts of zero are supported by hardware
+ (total_num_samps)?buffs : state.zero_buffs, 0,
+ std::max<size_t>(1, std::min(total_num_samps, max_samples_per_packet)),
if_packet_info,
converter,
vrt_packer,
diff --git a/host/lib/transport/zero_copy.cpp b/host/lib/transport/zero_copy.cpp
deleted file mode 100644
index a5a864a04..000000000
--- a/host/lib/transport/zero_copy.cpp
+++ /dev/null
@@ -1,108 +0,0 @@
-//
-// Copyright 2010 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/zero_copy.hpp>
-
-using namespace uhd::transport;
-
-/***********************************************************************
- * Safe managed receive buffer
- **********************************************************************/
-static void release_nop(void){
- /* NOP */
-}
-
-class safe_managed_receive_buffer : public managed_recv_buffer{
-public:
- safe_managed_receive_buffer(
- const boost::asio::const_buffer &buff,
- const release_fcn_t &release_fcn
- ):
- _buff(buff), _release_fcn(release_fcn)
- {
- /* NOP */
- }
-
- ~safe_managed_receive_buffer(void){
- _release_fcn();
- }
-
- void release(void){
- release_fcn_t release_fcn = _release_fcn;
- _release_fcn = &release_nop;
- return release_fcn();
- }
-
-private:
- const boost::asio::const_buffer &get(void) const{
- return _buff;
- }
-
- const boost::asio::const_buffer _buff;
- release_fcn_t _release_fcn;
-};
-
-managed_recv_buffer::sptr managed_recv_buffer::make_safe(
- const boost::asio::const_buffer &buff,
- const release_fcn_t &release_fcn
-){
- return sptr(new safe_managed_receive_buffer(buff, release_fcn));
-}
-
-/***********************************************************************
- * Safe managed send buffer
- **********************************************************************/
-static void commit_nop(size_t){
- /* NOP */
-}
-
-class safe_managed_send_buffer : public managed_send_buffer{
-public:
- safe_managed_send_buffer(
- const boost::asio::mutable_buffer &buff,
- const commit_fcn_t &commit_fcn
- ):
- _buff(buff), _commit_fcn(commit_fcn)
- {
- /* NOP */
- }
-
- ~safe_managed_send_buffer(void){
- _commit_fcn(0);
- }
-
- void commit(size_t num_bytes){
- commit_fcn_t commit_fcn = _commit_fcn;
- _commit_fcn = &commit_nop;
- return commit_fcn(num_bytes);
- }
-
-private:
- const boost::asio::mutable_buffer &get(void) const{
- return _buff;
- }
-
- const boost::asio::mutable_buffer _buff;
- commit_fcn_t _commit_fcn;
-};
-
-safe_managed_send_buffer::sptr managed_send_buffer::make_safe(
- const boost::asio::mutable_buffer &buff,
- const commit_fcn_t &commit_fcn
-){
- return sptr(new safe_managed_send_buffer(buff, commit_fcn));
-}
diff --git a/host/lib/types/time_spec.cpp b/host/lib/types/time_spec.cpp
index ece3b92f3..4a41f0fb9 100644
--- a/host/lib/types/time_spec.cpp
+++ b/host/lib/types/time_spec.cpp
@@ -99,7 +99,7 @@ time_spec_t::time_spec_t(time_t full_secs, double frac_secs):
time_spec_t::time_spec_t(time_t full_secs, long tick_count, double tick_rate):
_full_secs(full_secs),
- _frac_secs(double(tick_count)/tick_rate)
+ _frac_secs(tick_count/tick_rate)
{
/* NOP */
}
@@ -116,13 +116,11 @@ double time_spec_t::get_real_secs(void) const{
}
time_t time_spec_t::get_full_secs(void) const{
- double intpart;
- std::modf(this->_frac_secs, &intpart);
- return this->_full_secs + time_t(intpart);
+ return this->_full_secs + time_t(this->_frac_secs);
}
double time_spec_t::get_frac_secs(void) const{
- return std::fmod(this->_frac_secs, 1.0);
+ return this->_frac_secs - time_t(this->_frac_secs);
}
/***********************************************************************
diff --git a/host/lib/types/types.cpp b/host/lib/types/types.cpp
index c1be2ff6d..7c65d2997 100644
--- a/host/lib/types/types.cpp
+++ b/host/lib/types/types.cpp
@@ -22,6 +22,7 @@
#include <boost/cstdint.hpp>
#include <stdexcept>
#include <complex>
+#include <vector>
using namespace uhd;
@@ -66,22 +67,25 @@ otw_type_t::otw_type_t(void):
/***********************************************************************
* io type
**********************************************************************/
-static size_t tid_to_size(io_type_t::tid_t tid){
- switch(tid){
- case io_type_t::COMPLEX_FLOAT64: return sizeof(std::complex<double>);
- case io_type_t::COMPLEX_FLOAT32: return sizeof(std::complex<float>);
- case io_type_t::COMPLEX_INT16: return sizeof(std::complex<boost::int16_t>);
- case io_type_t::COMPLEX_INT8: return sizeof(std::complex<boost::int8_t>);
- default: throw std::runtime_error("unknown io type tid");
- }
+static std::vector<size_t> get_tid_size_table(void){
+ std::vector<size_t> table(128, 0);
+ table[size_t(io_type_t::COMPLEX_FLOAT64)] = sizeof(std::complex<double>);
+ table[size_t(io_type_t::COMPLEX_FLOAT32)] = sizeof(std::complex<float>);
+ table[size_t(io_type_t::COMPLEX_INT16)] = sizeof(std::complex<boost::int16_t>);
+ table[size_t(io_type_t::COMPLEX_INT8)] = sizeof(std::complex<boost::int8_t>);
+ return table;
}
-io_type_t::io_type_t(tid_t tid)
-: size(tid_to_size(tid)), tid(tid){
+static const std::vector<size_t> tid_size_table(get_tid_size_table());
+
+io_type_t::io_type_t(tid_t tid):
+ size(tid_size_table[size_t(tid) & 0x7f]), tid(tid)
+{
/* NOP */
}
-io_type_t::io_type_t(size_t size)
-: size(size), tid(CUSTOM_TYPE){
+io_type_t::io_type_t(size_t size):
+ size(size), tid(CUSTOM_TYPE)
+{
/* NOP */
}
diff --git a/host/lib/usrp/multi_usrp.cpp b/host/lib/usrp/multi_usrp.cpp
index 817d7b085..4bdb2bf2e 100644
--- a/host/lib/usrp/multi_usrp.cpp
+++ b/host/lib/usrp/multi_usrp.cpp
@@ -128,12 +128,12 @@ public:
return _mboard(mboard)[MBOARD_PROP_NAME].as<std::string>();
}
- time_spec_t get_time_now(void){
- return _mboard(0)[MBOARD_PROP_TIME_NOW].as<time_spec_t>();
+ time_spec_t get_time_now(size_t mboard = 0){
+ return _mboard(mboard)[MBOARD_PROP_TIME_NOW].as<time_spec_t>();
}
- time_spec_t get_time_last_pps(void){
- return _mboard(0)[MBOARD_PROP_TIME_PPS].as<time_spec_t>();
+ time_spec_t get_time_last_pps(size_t mboard = 0){
+ return _mboard(mboard)[MBOARD_PROP_TIME_PPS].as<time_spec_t>();
}
void set_time_now(const time_spec_t &time_spec, size_t mboard){
diff --git a/host/lib/usrp/usrp1/io_impl.cpp b/host/lib/usrp/usrp1/io_impl.cpp
index 52a7c6650..8beeccf8f 100644
--- a/host/lib/usrp/usrp1/io_impl.cpp
+++ b/host/lib/usrp/usrp1/io_impl.cpp
@@ -37,21 +37,64 @@ static const size_t alignment_padding = 512;
/***********************************************************************
* Helper struct to associate an offset with a buffer
**********************************************************************/
-class offset_send_buffer{
-public:
- typedef boost::shared_ptr<offset_send_buffer> sptr;
+struct offset_send_buffer{
+ offset_send_buffer(void){
+ /* NOP */
+ }
- static sptr make(managed_send_buffer::sptr buff, size_t offset = 0){
- return sptr(new offset_send_buffer(buff, offset));
+ offset_send_buffer(managed_send_buffer::sptr buff, size_t offset = 0):
+ buff(buff), offset(offset)
+ {
+ /* NOP */
}
//member variables
managed_send_buffer::sptr buff;
size_t offset; /* in bytes */
+};
+
+/***********************************************************************
+ * Reusable managed send buffer to handle aligned commits
+ **********************************************************************/
+class offset_managed_send_buffer : public managed_send_buffer{
+public:
+ typedef boost::function<void(offset_send_buffer&, offset_send_buffer&, size_t)> commit_cb_type;
+ offset_managed_send_buffer(const commit_cb_type &commit_cb):
+ _expired(true), _commit_cb(commit_cb)
+ {
+ /* NOP */
+ }
+
+ bool expired(void){return _expired;}
+
+ void commit(size_t size){
+ if (_expired) return;
+ this->_commit_cb(_curr_buff, _next_buff, size);
+ _expired = true;
+ }
+
+ sptr get_new(
+ offset_send_buffer &curr_buff,
+ offset_send_buffer &next_buff
+ ){
+ _expired = false;
+ _curr_buff = curr_buff;
+ _next_buff = next_buff;
+ return sptr(this, &offset_managed_send_buffer::fake_deleter);
+ }
private:
- offset_send_buffer(managed_send_buffer::sptr buff, size_t offset):
- buff(buff), offset(offset){/* NOP */}
+ static void fake_deleter(void *){
+ //dont do anything and assume the bastard committed it
+ //static_cast<offset_managed_send_buffer *>(obj)->commit(0);
+ }
+
+ void *get_buff(void) const{return _curr_buff.buff->cast<char *>() + _curr_buff.offset;}
+ size_t get_size(void) const{return _curr_buff.buff->size() - _curr_buff.offset;}
+
+ bool _expired;
+ offset_send_buffer _curr_buff, _next_buff;
+ commit_cb_type _commit_cb;
};
/***********************************************************************
@@ -60,10 +103,12 @@ private:
struct usrp1_impl::io_impl{
io_impl(zero_copy_if::sptr data_transport):
data_transport(data_transport),
+ get_recv_buffs_fcn(boost::bind(&usrp1_impl::io_impl::get_recv_buffs, this, _1)),
+ get_send_buffs_fcn(boost::bind(&usrp1_impl::io_impl::get_send_buffs, this, _1)),
underflow_poll_samp_count(0),
overflow_poll_samp_count(0),
- curr_buff_committed(true),
- curr_buff(offset_send_buffer::make(data_transport->get_send_buff()))
+ curr_buff(offset_send_buffer(data_transport->get_send_buff())),
+ omsb(boost::bind(&usrp1_impl::io_impl::commit_send_buff, this, _1, _2, _3))
{
/* NOP */
}
@@ -74,6 +119,13 @@ struct usrp1_impl::io_impl{
zero_copy_if::sptr data_transport;
+ //timeouts set on calls to recv/send (passed into get buffs methods)
+ double recv_timeout, send_timeout;
+
+ //bound callbacks for get buffs (bound once here, not in fast-path)
+ vrt_packet_handler::get_recv_buffs_t get_recv_buffs_fcn;
+ vrt_packet_handler::get_send_buffs_t get_send_buffs_fcn;
+
//state management for the vrt packet handler code
vrt_packet_handler::recv_state packet_handler_recv_state;
vrt_packet_handler::send_state packet_handler_send_state;
@@ -86,11 +138,16 @@ struct usrp1_impl::io_impl{
//all of this to ensure only aligned lengths are committed
//NOTE: you must commit before getting a new buffer
//since the vrt packet handler obeys this, we are ok
- bool curr_buff_committed;
- offset_send_buffer::sptr curr_buff;
- void commit_send_buff(offset_send_buffer::sptr, offset_send_buffer::sptr, size_t);
+ offset_send_buffer curr_buff;
+ offset_managed_send_buffer omsb;
+ void commit_send_buff(offset_send_buffer&, offset_send_buffer&, size_t);
void flush_send_buff(void);
- bool get_send_buffs(vrt_packet_handler::managed_send_buffs_t &, double);
+ bool get_send_buffs(vrt_packet_handler::managed_send_buffs_t &);
+ bool get_recv_buffs(vrt_packet_handler::managed_recv_buffs_t &buffs){
+ UHD_ASSERT_THROW(buffs.size() == 1);
+ buffs[0] = data_transport->get_recv_buff(recv_timeout);
+ return buffs[0].get() != NULL;
+ }
};
/*!
@@ -99,12 +156,12 @@ struct usrp1_impl::io_impl{
* Commit the current buffer at multiples of alignment.
*/
void usrp1_impl::io_impl::commit_send_buff(
- offset_send_buffer::sptr curr,
- offset_send_buffer::sptr next,
+ offset_send_buffer &curr,
+ offset_send_buffer &next,
size_t num_bytes
){
//total number of bytes now in the current buffer
- size_t bytes_in_curr_buffer = curr->offset + num_bytes;
+ size_t bytes_in_curr_buffer = curr.offset + num_bytes;
//calculate how many to commit and remainder
size_t num_bytes_remaining = bytes_in_curr_buffer % alignment_padding;
@@ -112,17 +169,16 @@ void usrp1_impl::io_impl::commit_send_buff(
//copy the remainder into the next buffer
std::memcpy(
- next->buff->cast<char *>() + next->offset,
- curr->buff->cast<char *>() + num_bytes_to_commit,
+ next.buff->cast<char *>() + next.offset,
+ curr.buff->cast<char *>() + num_bytes_to_commit,
num_bytes_remaining
);
//update the offset into the next buffer
- next->offset += num_bytes_remaining;
+ next.offset += num_bytes_remaining;
//commit the current buffer
- curr->buff->commit(num_bytes_to_commit);
- curr_buff_committed = true;
+ curr.buff->commit(num_bytes_to_commit);
}
/*!
@@ -130,14 +186,14 @@ void usrp1_impl::io_impl::commit_send_buff(
*/
void usrp1_impl::io_impl::flush_send_buff(void){
//calculate the number of bytes to alignment
- size_t bytes_to_pad = (-1*curr_buff->offset)%alignment_padding;
+ size_t bytes_to_pad = (-1*curr_buff.offset)%alignment_padding;
//send at least alignment_padding to guarantee zeros are sent
if (bytes_to_pad == 0) bytes_to_pad = alignment_padding;
//get the buffer, clear, and commit (really current buffer)
vrt_packet_handler::managed_send_buffs_t buffs(1);
- if (this->get_send_buffs(buffs, 0.1)){
+ if (this->get_send_buffs(buffs)){
std::memset(buffs[0]->cast<void *>(), 0, bytes_to_pad);
buffs[0]->commit(bytes_to_pad);
}
@@ -148,27 +204,19 @@ void usrp1_impl::io_impl::flush_send_buff(void){
* Always grab the next send buffer so we can timeout here.
*/
bool usrp1_impl::io_impl::get_send_buffs(
- vrt_packet_handler::managed_send_buffs_t &buffs, double timeout
+ vrt_packet_handler::managed_send_buffs_t &buffs
){
- UHD_ASSERT_THROW(curr_buff_committed and buffs.size() == 1);
+ UHD_ASSERT_THROW(omsb.expired() and buffs.size() == 1);
//try to get a new managed buffer with timeout
- offset_send_buffer::sptr next_buff(offset_send_buffer::make(data_transport->get_send_buff(timeout)));
- if (not next_buff->buff.get()) return false; /* propagate timeout here */
-
- //calculate the buffer pointer and size given the offset
- //references to the buffers are held in the bound function
- buffs[0] = managed_send_buffer::make_safe(
- boost::asio::buffer(
- curr_buff->buff->cast<char *>() + curr_buff->offset,
- curr_buff->buff->size() - curr_buff->offset
- ),
- boost::bind(&usrp1_impl::io_impl::commit_send_buff, this, curr_buff, next_buff, _1)
- );
+ offset_send_buffer next_buff(data_transport->get_send_buff(send_timeout));
+ if (not next_buff.buff.get()) return false; /* propagate timeout here */
+
+ //make a new managed buffer with the offset buffs
+ buffs[0] = omsb.get_new(curr_buff, next_buff);
//store the next buffer for the next call
curr_buff = next_buff;
- curr_buff_committed = false;
return true;
}
@@ -222,12 +270,13 @@ size_t usrp1_impl::get_max_send_samps_per_packet(void) const {
}
size_t usrp1_impl::send(
- const std::vector<const void *> &buffs, size_t num_samps,
+ const send_buffs_type &buffs, size_t num_samps,
const tx_metadata_t &metadata, const io_type_t &io_type,
send_mode_t send_mode, double timeout
){
if (_soft_time_ctrl->send_pre(metadata, timeout)) return num_samps;
+ _io_impl->send_timeout = timeout;
size_t num_samps_sent = vrt_packet_handler::send(
_io_impl->packet_handler_send_state, //last state of the send handler
buffs, num_samps, //buffer to fill
@@ -235,7 +284,7 @@ size_t usrp1_impl::send(
io_type, _tx_otw_type, //input and output types to convert
_clock_ctrl->get_master_clock_freq(), //master clock tick rate
&usrp1_bs_vrt_packer,
- boost::bind(&usrp1_impl::io_impl::get_send_buffs, _io_impl.get(), _1, timeout),
+ _io_impl->get_send_buffs_fcn,
get_max_send_samps_per_packet(),
0, //vrt header offset
_tx_subdev_spec.size() //num channels
@@ -283,15 +332,6 @@ static void usrp1_bs_vrt_unpacker(
if_packet_info.has_tlr = false;
}
-static bool get_recv_buffs(
- zero_copy_if::sptr zc_if, double timeout,
- vrt_packet_handler::managed_recv_buffs_t &buffs
-){
- UHD_ASSERT_THROW(buffs.size() == 1);
- buffs[0] = zc_if->get_recv_buff(timeout);
- return buffs[0].get() != NULL;
-}
-
size_t usrp1_impl::get_max_recv_samps_per_packet(void) const {
return _data_transport->get_recv_frame_size()
/ _rx_otw_type.get_sample_size()
@@ -300,10 +340,11 @@ size_t usrp1_impl::get_max_recv_samps_per_packet(void) const {
}
size_t usrp1_impl::recv(
- const std::vector<void *> &buffs, size_t num_samps,
+ const recv_buffs_type &buffs, size_t num_samps,
rx_metadata_t &metadata, const io_type_t &io_type,
recv_mode_t recv_mode, double timeout
){
+ _io_impl->recv_timeout = timeout;
size_t num_samps_recvd = vrt_packet_handler::recv(
_io_impl->packet_handler_recv_state, //last state of the recv handler
buffs, num_samps, //buffer to fill
@@ -311,7 +352,7 @@ size_t usrp1_impl::recv(
io_type, _rx_otw_type, //input and output types to convert
_clock_ctrl->get_master_clock_freq(), //master clock tick rate
&usrp1_bs_vrt_unpacker,
- boost::bind(&get_recv_buffs, _data_transport, timeout, _1),
+ _io_impl->get_recv_buffs_fcn,
&vrt_packet_handler::handle_overflow_nop,
0, //vrt header offset
_rx_subdev_spec.size() //num channels
diff --git a/host/lib/usrp/usrp1/soft_time_ctrl.cpp b/host/lib/usrp/usrp1/soft_time_ctrl.cpp
index 246df93eb..e1b671811 100644
--- a/host/lib/usrp/usrp1/soft_time_ctrl.cpp
+++ b/host/lib/usrp/usrp1/soft_time_ctrl.cpp
@@ -39,7 +39,7 @@ public:
soft_time_ctrl_impl(const cb_fcn_type &stream_on_off):
_nsamps_remaining(0),
_stream_mode(stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS),
- _cmd_queue(bounded_buffer<boost::any>::make(2)),
+ _cmd_queue(2),
_stream_on_off(stream_on_off)
{
//synchronously spawn a new thread
@@ -112,7 +112,7 @@ public:
}
void issue_stream_cmd(const stream_cmd_t &cmd){
- _cmd_queue->push_with_wait(cmd);
+ _cmd_queue.push_with_wait(cmd);
}
void stream_on_off(bool enb){
@@ -180,7 +180,7 @@ public:
try{
boost::any cmd;
while (true){
- _cmd_queue->pop_with_wait(cmd);
+ _cmd_queue.pop_with_wait(cmd);
recv_cmd_handle_cmd(boost::any_cast<stream_cmd_t>(cmd));
}
} catch(const boost::thread_interrupted &){}
@@ -191,7 +191,7 @@ private:
size_t _nsamps_remaining;
stream_cmd_t::stream_mode_t _stream_mode;
time_spec_t _time_offset;
- bounded_buffer<boost::any>::sptr _cmd_queue;
+ bounded_buffer<boost::any> _cmd_queue;
const cb_fcn_type _stream_on_off;
boost::thread_group _thread_group;
};
diff --git a/host/lib/usrp/usrp1/usrp1_impl.hpp b/host/lib/usrp/usrp1/usrp1_impl.hpp
index 28199ebe3..1d9f6709f 100644
--- a/host/lib/usrp/usrp1/usrp1_impl.hpp
+++ b/host/lib/usrp/usrp1/usrp1_impl.hpp
@@ -80,13 +80,13 @@ public:
~usrp1_impl(void);
//the io interface
- size_t send(const std::vector<const void *> &,
+ size_t send(const send_buffs_type &,
size_t,
const uhd::tx_metadata_t &,
const uhd::io_type_t &,
send_mode_t, double);
- size_t recv(const std::vector<void *> &,
+ size_t recv(const recv_buffs_type &,
size_t, uhd::rx_metadata_t &,
const uhd::io_type_t &,
recv_mode_t, double);
diff --git a/host/lib/usrp/usrp2/io_impl.cpp b/host/lib/usrp/usrp2/io_impl.cpp
index 30eaecae2..b20b6652e 100644
--- a/host/lib/usrp/usrp2/io_impl.cpp
+++ b/host/lib/usrp/usrp2/io_impl.cpp
@@ -24,14 +24,24 @@
#include <boost/format.hpp>
#include <boost/bind.hpp>
#include <boost/thread.hpp>
-#include <boost/date_time/posix_time/posix_time_types.hpp>
#include <iostream>
-#include <list>
using namespace uhd;
using namespace uhd::usrp;
using namespace uhd::transport;
namespace asio = boost::asio;
+namespace pt = boost::posix_time;
+
+/***********************************************************************
+ * helpers
+ **********************************************************************/
+static UHD_INLINE pt::time_duration to_time_dur(double timeout){
+ return pt::microseconds(long(timeout*1e6));
+}
+
+static UHD_INLINE double from_time_dur(const pt::time_duration &time_dur){
+ return 1e-6*time_dur.total_microseconds();
+}
/***********************************************************************
* constants
@@ -61,6 +71,7 @@ public:
_last_seq_out = 0;
_last_seq_ack = 0;
_max_seqs_out = max_seqs_out;
+ _ready_fcn = boost::bind(&flow_control_monitor::ready, this);
}
/*!
@@ -73,11 +84,8 @@ public:
boost::this_thread::disable_interruption di; //disable because the wait can throw
boost::unique_lock<boost::mutex> lock(_fc_mutex);
_last_seq_out = seq;
- return _fc_cond.timed_wait(
- lock,
- boost::posix_time::microseconds(long(timeout*1e6)),
- boost::bind(&flow_control_monitor::ready, this)
- );
+ if (this->ready()) return true;
+ return _fc_cond.timed_wait(lock, to_time_dur(timeout), _ready_fcn);
}
/*!
@@ -99,6 +107,7 @@ private:
boost::mutex _fc_mutex;
boost::condition _fc_cond;
seq_type _last_seq_out, _last_seq_ack, _max_seqs_out;
+ boost::function<bool(void)> _ready_fcn;
};
/***********************************************************************
@@ -110,11 +119,15 @@ private:
**********************************************************************/
struct usrp2_impl::io_impl{
- io_impl(size_t send_frame_size, size_t width):
- packet_handler_recv_state(width),
- async_msg_fifo(bounded_buffer<async_metadata_t>::make(100/*messages deep*/))
+ io_impl(size_t send_frame_size, const std::vector<zero_copy_if::sptr> &xports):
+ xports(xports),
+ get_recv_buffs_fcn(boost::bind(&usrp2_impl::io_impl::get_recv_buffs, this, _1)),
+ get_send_buffs_fcn(boost::bind(&usrp2_impl::io_impl::get_send_buffs, this, _1)),
+ packet_handler_recv_state(xports.size()),
+ packet_handler_send_state(xports.size()),
+ async_msg_fifo(100/*messages deep*/)
{
- for (size_t i = 0; i < width; i++){
+ for (size_t i = 0; i < xports.size(); i++){
fc_mons.push_back(flow_control_monitor::sptr(
new flow_control_monitor(usrp2_impl::sram_bytes/send_frame_size)
));
@@ -135,31 +148,32 @@ struct usrp2_impl::io_impl{
recv_pirate_crew.join_all();
}
- bool get_send_buffs(
- const std::vector<zero_copy_if::sptr> &trans,
- vrt_packet_handler::managed_send_buffs_t &buffs,
- double timeout
- ){
- UHD_ASSERT_THROW(trans.size() == buffs.size());
+ bool get_send_buffs(vrt_packet_handler::managed_send_buffs_t &buffs){
+ UHD_ASSERT_THROW(xports.size() == buffs.size());
//calculate the flow control word
const boost::uint32_t fc_word32 = packet_handler_send_state.next_packet_seq;
//grab a managed buffer for each index
for (size_t i = 0; i < buffs.size(); i++){
- if (not fc_mons[i]->check_fc_condition(fc_word32, timeout)) return false;
- buffs[i] = trans[i]->get_send_buff(timeout);
+ if (not fc_mons[i]->check_fc_condition(fc_word32, send_timeout)) return false;
+ buffs[i] = xports[i]->get_send_buff(send_timeout);
if (not buffs[i].get()) return false;
buffs[i]->cast<boost::uint32_t *>()[0] = uhd::htonx(fc_word32);
}
return true;
}
- bool get_recv_buffs(
- const std::vector<zero_copy_if::sptr> &xports,
- vrt_packet_handler::managed_recv_buffs_t &buffs,
- double timeout
- );
+ bool get_recv_buffs(vrt_packet_handler::managed_recv_buffs_t &buffs);
+
+ const std::vector<zero_copy_if::sptr> &xports;
+
+ //timeouts set on calls to recv/send (passed into get buffs methods)
+ double recv_timeout, send_timeout;
+
+ //bound callbacks for get buffs (bound once here, not in fast-path)
+ vrt_packet_handler::get_recv_buffs_t get_recv_buffs_fcn;
+ vrt_packet_handler::get_send_buffs_t get_send_buffs_fcn;
//previous state for each buffer
std::vector<vrt::if_packet_info_t> prev_infos;
@@ -175,7 +189,7 @@ struct usrp2_impl::io_impl{
void recv_pirate_loop(zero_copy_if::sptr, usrp2_mboard_impl::sptr, size_t);
boost::thread_group recv_pirate_crew;
bool recv_pirate_crew_raiding;
- bounded_buffer<async_metadata_t>::sptr async_msg_fifo;
+ bounded_buffer<async_metadata_t> async_msg_fifo;
boost::mutex spawn_mutex;
};
@@ -228,7 +242,7 @@ void usrp2_impl::io_impl::recv_pirate_loop(
//print the famous U, and push the metadata into the message queue
if (metadata.event_code & underflow_flags) std::cerr << "U" << std::flush;
//else std::cout << "metadata.event_code " << metadata.event_code << std::endl;
- async_msg_fifo->push_with_pop_on_full(metadata);
+ async_msg_fifo.push_with_pop_on_full(metadata);
}
else{
//TODO unknown received packet, may want to print error...
@@ -248,7 +262,7 @@ void usrp2_impl::io_init(void){
const size_t send_frame_size = _data_transports.front()->get_send_frame_size();
//create new io impl
- _io_impl = UHD_PIMPL_MAKE(io_impl, (send_frame_size, _data_transports.size()));
+ _io_impl = UHD_PIMPL_MAKE(io_impl, (send_frame_size, _data_transports));
//create a new pirate thread for each zc if (yarr!!)
for (size_t i = 0; i < _data_transports.size(); i++){
@@ -274,7 +288,7 @@ bool usrp2_impl::recv_async_msg(
async_metadata_t &async_metadata, double timeout
){
boost::this_thread::disable_interruption di; //disable because the wait can throw
- return _io_impl->async_msg_fifo->pop_with_timed_wait(async_metadata, timeout);
+ return _io_impl->async_msg_fifo.pop_with_timed_wait(async_metadata, timeout);
}
/***********************************************************************
@@ -291,10 +305,11 @@ size_t usrp2_impl::get_max_send_samps_per_packet(void) const{
}
size_t usrp2_impl::send(
- const std::vector<const void *> &buffs, size_t num_samps,
+ const send_buffs_type &buffs, size_t num_samps,
const tx_metadata_t &metadata, const io_type_t &io_type,
send_mode_t send_mode, double timeout
){
+ _io_impl->send_timeout = timeout;
return vrt_packet_handler::send(
_io_impl->packet_handler_send_state, //last state of the send handler
buffs, num_samps, //buffer to fill
@@ -302,7 +317,7 @@ size_t usrp2_impl::send(
io_type, _tx_otw_type, //input and output types to convert
_mboards.front()->get_master_clock_freq(), //master clock tick rate
uhd::transport::vrt::if_hdr_pack_be,
- boost::bind(&usrp2_impl::io_impl::get_send_buffs, _io_impl.get(), _data_transports, _1, timeout),
+ _io_impl->get_send_buffs_fcn,
get_max_send_samps_per_packet(),
vrt_send_header_offset_words32
);
@@ -311,14 +326,6 @@ size_t usrp2_impl::send(
/***********************************************************************
* Alignment logic on receive
**********************************************************************/
-static UHD_INLINE boost::posix_time::time_duration to_time_dur(double timeout){
- return boost::posix_time::microseconds(long(timeout*1e6));
-}
-
-static UHD_INLINE double from_time_dur(const boost::posix_time::time_duration &time_dur){
- return 1e-6*time_dur.total_microseconds();
-}
-
static UHD_INLINE time_spec_t extract_time_spec(
const vrt::if_packet_info_t &packet_info
){
@@ -359,13 +366,24 @@ static UHD_INLINE bool handle_msg_packet(
return true;
}
+class alignment_indexes{
+public:
+ void reset(size_t len){_indexes = (1 << len) - 1;}
+ size_t front(void){ //TODO replace with look-up table
+ size_t index = 0;
+ while ((_indexes & (1 << index)) == 0) index++;
+ return index;
+ }
+ void remove(size_t index){_indexes &= ~(1 << index);}
+ bool empty(void){return _indexes == 0;}
+private: size_t _indexes;
+};
+
UHD_INLINE bool usrp2_impl::io_impl::get_recv_buffs(
- const std::vector<zero_copy_if::sptr> &xports,
- vrt_packet_handler::managed_recv_buffs_t &buffs,
- double timeout
+ vrt_packet_handler::managed_recv_buffs_t &buffs
){
if (buffs.size() == 1){
- buffs[0] = xports[0]->get_recv_buff(timeout);
+ buffs[0] = xports[0]->get_recv_buff(recv_timeout);
if (buffs[0].get() == NULL) return false;
bool clear, msg; time_spec_t time; //unused variables
//call extract_packet_info to handle printing the overflows
@@ -373,16 +391,15 @@ UHD_INLINE bool usrp2_impl::io_impl::get_recv_buffs(
return true;
}
//-------------------- begin alignment logic ---------------------//
- boost::system_time exit_time = boost::get_system_time() + to_time_dur(timeout);
+ boost::system_time exit_time = boost::get_system_time() + to_time_dur(recv_timeout);
managed_recv_buffer::sptr buff_tmp;
- std::list<size_t> _all_indexes, indexes_to_do;
- for (size_t i = 0; i < buffs.size(); i++) _all_indexes.push_back(i);
+ alignment_indexes indexes_to_do;
bool clear, msg;
time_spec_t expected_time;
//respond to a clear by starting from scratch
got_clear:
- indexes_to_do = _all_indexes;
+ indexes_to_do.reset(buffs.size());
clear = false;
//do an initial pop to load an initial sequence id
@@ -393,10 +410,10 @@ UHD_INLINE bool usrp2_impl::io_impl::get_recv_buffs(
if (clear) goto got_clear;
buffs[index] = buff_tmp;
if (msg) return handle_msg_packet(buffs, index);
- indexes_to_do.pop_front();
+ indexes_to_do.remove(index);
//get an aligned set of elements from the buffers:
- while(indexes_to_do.size() != 0){
+ while(not indexes_to_do.empty()){
//pop an element off for this index
index = indexes_to_do.front();
@@ -411,25 +428,22 @@ UHD_INLINE bool usrp2_impl::io_impl::get_recv_buffs(
//if the sequence id matches:
// remove this index from the list and continue
if (this_time == expected_time){
- indexes_to_do.pop_front();
- continue;
- }
-
- //if the sequence id is older:
- // continue with the same index to try again
- else if (this_time < expected_time){
- continue;
+ indexes_to_do.remove(index);
}
//if the sequence id is newer:
// use the new expected time for comparison
// add all other indexes back into the list
- else{
+ else if (this_time > expected_time){
expected_time = this_time;
- indexes_to_do = _all_indexes;
+ indexes_to_do.reset(buffs.size());
indexes_to_do.remove(index);
- continue;
}
+
+ //if the sequence id is older:
+ // continue with the same index to try again
+ //else if (this_time < expected_time)...
+
}
return true;
//-------------------- end alignment logic -----------------------//
@@ -454,10 +468,11 @@ static void handle_overflow(std::vector<usrp2_mboard_impl::sptr> &mboards, size_
}
size_t usrp2_impl::recv(
- const std::vector<void *> &buffs, size_t num_samps,
+ const recv_buffs_type &buffs, size_t num_samps,
rx_metadata_t &metadata, const io_type_t &io_type,
recv_mode_t recv_mode, double timeout
){
+ _io_impl->recv_timeout = timeout;
return vrt_packet_handler::recv(
_io_impl->packet_handler_recv_state, //last state of the recv handler
buffs, num_samps, //buffer to fill
@@ -465,7 +480,7 @@ size_t usrp2_impl::recv(
io_type, _rx_otw_type, //input and output types to convert
_mboards.front()->get_master_clock_freq(), //master clock tick rate
uhd::transport::vrt::if_hdr_unpack_be,
- boost::bind(&usrp2_impl::io_impl::get_recv_buffs, _io_impl.get(), _data_transports, _1, timeout),
- boost::bind(&handle_overflow, _mboards, _1)
+ _io_impl->get_recv_buffs_fcn,
+ boost::bind(&handle_overflow, boost::ref(_mboards), _1)
);
}
diff --git a/host/lib/usrp/usrp2/usrp2_iface.cpp b/host/lib/usrp/usrp2/usrp2_iface.cpp
index 149c5011f..4407a3011 100644
--- a/host/lib/usrp/usrp2/usrp2_iface.cpp
+++ b/host/lib/usrp/usrp2/usrp2_iface.cpp
@@ -1,5 +1,5 @@
//
-// Copyright 2010 Ettus Research LLC
+// Copyright 2010-2011 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
@@ -42,6 +42,7 @@ public:
**********************************************************************/
usrp2_iface_impl(udp_simple::sptr ctrl_transport){
_ctrl_transport = ctrl_transport;
+ _ctrl_seq_num = 0;
mb_eeprom = mboard_eeprom_t(*this, mboard_eeprom_t::MAP_N100);
switch(this->get_rev()){
diff --git a/host/lib/usrp/usrp2/usrp2_impl.hpp b/host/lib/usrp/usrp2/usrp2_impl.hpp
index ad95b2a4a..337f842d6 100644
--- a/host/lib/usrp/usrp2/usrp2_impl.hpp
+++ b/host/lib/usrp/usrp2/usrp2_impl.hpp
@@ -200,12 +200,12 @@ public:
//the io interface
size_t send(
- const std::vector<const void *> &, size_t,
+ const send_buffs_type &, size_t,
const uhd::tx_metadata_t &, const uhd::io_type_t &,
uhd::device::send_mode_t, double
);
size_t recv(
- const std::vector<void *> &, size_t,
+ const recv_buffs_type &, size_t,
uhd::rx_metadata_t &, const uhd::io_type_t &,
uhd::device::recv_mode_t, double
);
diff --git a/host/lib/usrp/usrp_e100/CMakeLists.txt b/host/lib/usrp/usrp_e100/CMakeLists.txt
index c32dd87f8..acbac177e 100644
--- a/host/lib/usrp/usrp_e100/CMakeLists.txt
+++ b/host/lib/usrp/usrp_e100/CMakeLists.txt
@@ -1,5 +1,5 @@
#
-# Copyright 2010 Ettus Research LLC
+# Copyright 2010-2011 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
@@ -40,7 +40,7 @@ IF(ENABLE_USRP_E100)
${CMAKE_CURRENT_SOURCE_DIR}/dboard_impl.cpp
${CMAKE_CURRENT_SOURCE_DIR}/dboard_iface.cpp
${CMAKE_CURRENT_SOURCE_DIR}/dsp_impl.cpp
- ${CMAKE_CURRENT_SOURCE_DIR}/fpga-downloader.cc
+ ${CMAKE_CURRENT_SOURCE_DIR}/fpga_downloader.cpp
${CMAKE_CURRENT_SOURCE_DIR}/io_impl.cpp
${CMAKE_CURRENT_SOURCE_DIR}/mboard_impl.cpp
${CMAKE_CURRENT_SOURCE_DIR}/usrp_e100_impl.cpp
diff --git a/host/lib/usrp/usrp_e100/clock_ctrl.cpp b/host/lib/usrp/usrp_e100/clock_ctrl.cpp
index 1fb1a7125..e29fe18ce 100644
--- a/host/lib/usrp/usrp_e100/clock_ctrl.cpp
+++ b/host/lib/usrp/usrp_e100/clock_ctrl.cpp
@@ -1,5 +1,5 @@
//
-// Copyright 2010 Ettus Research LLC
+// Copyright 2010-2011 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
@@ -22,11 +22,26 @@
#include "usrp_e100_regs.hpp" //spi slave constants
#include <boost/assign/list_of.hpp>
#include <boost/foreach.hpp>
+#include <boost/format.hpp>
+#include <boost/thread/thread.hpp>
+#include <boost/math/common_factor_rt.hpp> //gcd
+#include <algorithm>
#include <utility>
#include <iostream>
using namespace uhd;
+/***********************************************************************
+ * Constants
+ **********************************************************************/
+static const bool CLOCK_SETTINGS_DEBUG = false;
+static const bool ENABLE_THE_TEST_OUT = true;
+static const double REFERENCE_INPUT_RATE = 10e6;
+static const double DEFAULT_OUTPUT_RATE = 64e6;
+
+/***********************************************************************
+ * Helpers
+ **********************************************************************/
template <typename div_type, typename bypass_type> static void set_clock_divider(
size_t divider, div_type &low, div_type &high, bypass_type &bypass
){
@@ -36,24 +51,117 @@ template <typename div_type, typename bypass_type> static void set_clock_divider
}
/***********************************************************************
- * Constants
+ * Clock rate calculation stuff:
+ * Using the internal VCO between 1400 and 1800 MHz
**********************************************************************/
-static const bool enable_test_clock = false;
-static const size_t ref_clock_doubler = 2; //enabled below
-static const double ref_clock_rate = 10e6 * ref_clock_doubler;
+struct clock_settings_type{
+ size_t ref_clock_doubler, r_counter, a_counter, b_counter, prescaler, vco_divider, chan_divider;
+ size_t get_n_counter(void) const{return prescaler * b_counter + a_counter;}
+ double get_ref_rate(void) const{return REFERENCE_INPUT_RATE * ref_clock_doubler;}
+ double get_vco_rate(void) const{return get_ref_rate()/r_counter * get_n_counter();}
+ double get_chan_rate(void) const{return get_vco_rate()/vco_divider;}
+ double get_out_rate(void) const{return get_chan_rate()/chan_divider;}
+ std::string to_pp_string(void) const{
+ return str(boost::format(
+ " r_counter: %d\n"
+ " a_counter: %d\n"
+ " b_counter: %d\n"
+ " prescaler: %d\n"
+ " vco_divider: %d\n"
+ " chan_divider: %d\n"
+ " vco_rate: %fMHz\n"
+ " chan_rate: %fMHz\n"
+ " out_rate: %fMHz\n"
+ )
+ % r_counter
+ % a_counter
+ % b_counter
+ % prescaler
+ % vco_divider
+ % chan_divider
+ % (get_vco_rate()/1e6)
+ % (get_chan_rate()/1e6)
+ % (get_out_rate()/1e6)
+ );
+ }
+};
+
+//! gives the greatest divisor of num between 1 and max inclusive
+template<typename T> static inline T greatest_divisor(T num, T max){
+ for (T i = max; i > 1; i--) if (num%i == 0) return i; return 1;
+}
+
+//! gives the least divisor of num between min and num exclusive
+template<typename T> static inline T least_divisor(T num, T min){
+ for (T i = min; i < num; i++) if (num%i == 0) return i; return 1;
+}
+
+static clock_settings_type get_clock_settings(double rate){
+ clock_settings_type cs;
+ cs.ref_clock_doubler = 2; //always doubling
+ cs.prescaler = 8; //set to 8 when input is under 2400 MHz
-static const size_t r_counter = 1;
-static const size_t a_counter = 0;
-static const size_t b_counter = 20 / ref_clock_doubler;
-static const size_t prescaler = 8; //set below with enum, set to 8 when input is under 2400 MHz
-static const size_t vco_divider = 5; //set below with enum
+ //basic formulas used below:
+ //out_rate*X = ref_rate*Y
+ //X = i*ref_rate/gcd
+ //Y = i*out_rate/gcd
+ //X = chan_div * vco_div * R
+ //Y = P*B + A
-static const size_t n_counter = prescaler * b_counter + a_counter;
-static const size_t vco_clock_rate = ref_clock_rate/r_counter * n_counter; //between 1400 and 1800 MHz
-static const double master_clock_rate = vco_clock_rate/vco_divider;
+ const boost::uint64_t out_rate = boost::uint64_t(rate);
+ const boost::uint64_t ref_rate = boost::uint64_t(cs.get_ref_rate());
+ const size_t gcd = size_t(boost::math::gcd(ref_rate, out_rate));
-static const size_t fpga_clock_divider = size_t(master_clock_rate/64e6);
-static const size_t codec_clock_divider = size_t(master_clock_rate/64e6);
+ for (size_t i = 1; i <= 100; i++){
+ const size_t X = i*ref_rate/gcd;
+ const size_t Y = i*out_rate/gcd;
+
+ //determine A and B (P is fixed)
+ cs.b_counter = Y/cs.prescaler;
+ cs.a_counter = Y - cs.b_counter*cs.prescaler;
+
+ static const double vco_bound_pad = 100e6;
+ for ( //calculate an r divider that fits into the bounds of the vco
+ cs.r_counter = size_t(cs.get_n_counter()*cs.get_ref_rate()/(1800e6 - vco_bound_pad));
+ cs.r_counter <= size_t(cs.get_n_counter()*cs.get_ref_rate()/(1400e6 + vco_bound_pad))
+ and cs.r_counter > 0; cs.r_counter++
+ ){
+
+ //determine chan_div and vco_div
+ //and fill in that order of preference
+ cs.chan_divider = greatest_divisor<size_t>(X/cs.r_counter, 32);
+ cs.vco_divider = greatest_divisor<size_t>(X/cs.chan_divider/cs.r_counter, 6);
+
+ //avoid a vco divider of 1 (if possible)
+ if (cs.vco_divider == 1){
+ cs.vco_divider = least_divisor<size_t>(cs.chan_divider, 2);
+ cs.chan_divider /= cs.vco_divider;
+ }
+
+ if (CLOCK_SETTINGS_DEBUG){
+ std::cout << "gcd " << gcd << std::endl;
+ std::cout << "X " << X << std::endl;
+ std::cout << "Y " << Y << std::endl;
+ std::cout << cs.to_pp_string() << std::endl;
+ }
+
+ //filter limits on the counters
+ if (cs.vco_divider == 1) continue;
+ if (cs.r_counter >= (1<<14)) continue;
+ if (cs.b_counter == 2) continue;
+ if (cs.b_counter == 1 and cs.a_counter != 0) continue;
+ if (cs.b_counter >= (1<<13)) continue;
+ if (cs.a_counter >= (1<<6)) continue;
+
+ std::cout << "USRP-E100 clock control: " << i << std::endl << cs.to_pp_string() << std::endl;
+ return cs;
+ }
+ }
+
+ throw std::runtime_error(str(boost::format(
+ "USRP-E100 clock control: could not calculate settings for clock rate %fMHz"
+ ) % (rate/1e6)));
+}
/***********************************************************************
* Clock Control Implementation
@@ -62,35 +170,70 @@ class usrp_e100_clock_ctrl_impl : public usrp_e100_clock_ctrl{
public:
usrp_e100_clock_ctrl_impl(usrp_e100_iface::sptr iface){
_iface = iface;
+ _chan_rate = 0.0;
+ _out_rate = 0.0;
//init the clock gen registers
//Note: out0 should already be clocking the FPGA or this isnt going to work
_ad9522_regs.sdo_active = ad9522_regs_t::SDO_ACTIVE_SDO_SDIO;
- _ad9522_regs.enable_clock_doubler = 1; //enable ref clock doubler
_ad9522_regs.enb_stat_eeprom_at_stat_pin = 0; //use status pin
_ad9522_regs.status_pin_control = 0x1; //n divider
_ad9522_regs.ld_pin_control = 0x00; //dld
_ad9522_regs.refmon_pin_control = 0x12; //show ref2
+ _ad9522_regs.lock_detect_counter = ad9522_regs_t::LOCK_DETECT_COUNTER_16CYC;
- _ad9522_regs.enable_ref2 = 1;
- _ad9522_regs.enable_ref1 = 0;
- _ad9522_regs.select_ref = ad9522_regs_t::SELECT_REF_REF2;
+ this->use_internal_ref();
+
+ this->set_fpga_clock_rate(DEFAULT_OUTPUT_RATE); //initialize to something
+
+ this->enable_test_clock(ENABLE_THE_TEST_OUT);
+ this->enable_rx_dboard_clock(false);
+ this->enable_tx_dboard_clock(false);
+ }
+
+ ~usrp_e100_clock_ctrl_impl(void){
+ this->enable_test_clock(ENABLE_THE_TEST_OUT);
+ this->enable_rx_dboard_clock(false);
+ this->enable_tx_dboard_clock(false);
+ }
+
+ /***********************************************************************
+ * Clock rate control:
+ * - set clock rate w/ internal VCO
+ * - set clock rate w/ external VCXO
+ **********************************************************************/
+ void set_clock_settings_with_internal_vco(double rate){
+ const clock_settings_type cs = get_clock_settings(rate);
+
+ //set the rates to private variables so the implementation knows!
+ _chan_rate = cs.get_chan_rate();
+ _out_rate = cs.get_out_rate();
- _ad9522_regs.set_r_counter(r_counter);
- _ad9522_regs.a_counter = a_counter;
- _ad9522_regs.set_b_counter(b_counter);
+ _ad9522_regs.enable_clock_doubler = (cs.ref_clock_doubler == 2)? 1 : 0;
+
+ _ad9522_regs.set_r_counter(cs.r_counter);
+ _ad9522_regs.a_counter = cs.a_counter;
+ _ad9522_regs.set_b_counter(cs.b_counter);
+ UHD_ASSERT_THROW(cs.prescaler == 8); //assumes this below:
_ad9522_regs.prescaler_p = ad9522_regs_t::PRESCALER_P_DIV8_9;
_ad9522_regs.pll_power_down = ad9522_regs_t::PLL_POWER_DOWN_NORMAL;
_ad9522_regs.cp_current = ad9522_regs_t::CP_CURRENT_1_2MA;
- _ad9522_regs.vco_calibration_now = 1; //calibrate it!
- _ad9522_regs.vco_divider = ad9522_regs_t::VCO_DIVIDER_DIV5;
+ _ad9522_regs.bypass_vco_divider = 0;
+ switch(cs.vco_divider){
+ case 1: _ad9522_regs.vco_divider = ad9522_regs_t::VCO_DIVIDER_DIV1; break;
+ case 2: _ad9522_regs.vco_divider = ad9522_regs_t::VCO_DIVIDER_DIV2; break;
+ case 3: _ad9522_regs.vco_divider = ad9522_regs_t::VCO_DIVIDER_DIV3; break;
+ case 4: _ad9522_regs.vco_divider = ad9522_regs_t::VCO_DIVIDER_DIV4; break;
+ case 5: _ad9522_regs.vco_divider = ad9522_regs_t::VCO_DIVIDER_DIV5; break;
+ case 6: _ad9522_regs.vco_divider = ad9522_regs_t::VCO_DIVIDER_DIV6; break;
+ }
_ad9522_regs.select_vco_or_clock = ad9522_regs_t::SELECT_VCO_OR_CLOCK_VCO;
//setup fpga master clock
_ad9522_regs.out0_format = ad9522_regs_t::OUT0_FORMAT_LVDS;
- set_clock_divider(fpga_clock_divider,
+ set_clock_divider(cs.chan_divider,
_ad9522_regs.divider0_low_cycles,
_ad9522_regs.divider0_high_cycles,
_ad9522_regs.divider0_bypass
@@ -98,52 +241,69 @@ public:
//setup codec clock
_ad9522_regs.out3_format = ad9522_regs_t::OUT3_FORMAT_LVDS;
- set_clock_divider(codec_clock_divider,
+ set_clock_divider(cs.chan_divider,
_ad9522_regs.divider1_low_cycles,
_ad9522_regs.divider1_high_cycles,
_ad9522_regs.divider1_bypass
);
- //setup test clock (same divider as codec clock)
- _ad9522_regs.out4_format = ad9522_regs_t::OUT4_FORMAT_CMOS;
- _ad9522_regs.out4_cmos_configuration = (enable_test_clock)?
- ad9522_regs_t::OUT4_CMOS_CONFIGURATION_A_ON :
- ad9522_regs_t::OUT4_CMOS_CONFIGURATION_OFF;
+ this->send_all_regs();
+ calibrate_now();
+ }
- //setup a list of register ranges to write
- typedef std::pair<boost::uint16_t, boost::uint16_t> range_t;
- static const std::vector<range_t> ranges = boost::assign::list_of
- (range_t(0x000, 0x000)) (range_t(0x010, 0x01F))
- (range_t(0x0F0, 0x0FD)) (range_t(0x190, 0x19B))
- (range_t(0x1E0, 0x1E1)) (range_t(0x230, 0x230))
- ;
+ void set_clock_settings_with_external_vcxo(double rate){
+ //set the rates to private variables so the implementation knows!
+ _chan_rate = rate;
+ _out_rate = rate;
- //write initial register values and latch/update
- BOOST_FOREACH(const range_t &range, ranges){
- for(boost::uint16_t addr = range.first; addr <= range.second; addr++){
- this->send_reg(addr);
- }
- }
- this->latch_regs();
- //test read:
- //boost::uint32_t reg = _ad9522_regs.get_read_reg(0x01b);
- //boost::uint32_t result = _iface->transact_spi(
- // UE_SPI_SS_AD9522,
- // spi_config_t::EDGE_RISE,
- // reg, 24, true /*no*/
- //);
- //std::cout << "result " << std::hex << result << std::endl;
- this->enable_rx_dboard_clock(false);
- this->enable_tx_dboard_clock(false);
+ _ad9522_regs.enable_clock_doubler = 1; //doubler always on
+ const double ref_rate = REFERENCE_INPUT_RATE*2;
+
+ //bypass prescaler such that N = B
+ long gcd = boost::math::gcd(long(ref_rate), long(rate));
+ _ad9522_regs.set_r_counter(int(ref_rate/gcd));
+ _ad9522_regs.a_counter = 0;
+ _ad9522_regs.set_b_counter(int(rate/gcd));
+ _ad9522_regs.prescaler_p = ad9522_regs_t::PRESCALER_P_DIV1;
+
+ //setup external vcxo
+ _ad9522_regs.pll_power_down = ad9522_regs_t::PLL_POWER_DOWN_NORMAL;
+ _ad9522_regs.cp_current = ad9522_regs_t::CP_CURRENT_1_2MA;
+ _ad9522_regs.bypass_vco_divider = 1;
+ _ad9522_regs.select_vco_or_clock = ad9522_regs_t::SELECT_VCO_OR_CLOCK_EXTERNAL;
+
+ //setup fpga master clock
+ _ad9522_regs.out0_format = ad9522_regs_t::OUT0_FORMAT_LVDS;
+ _ad9522_regs.divider0_bypass = 1;
+
+ //setup codec clock
+ _ad9522_regs.out3_format = ad9522_regs_t::OUT3_FORMAT_LVDS;
+ _ad9522_regs.divider1_bypass = 1;
+
+ this->send_all_regs();
}
- ~usrp_e100_clock_ctrl_impl(void){
- this->enable_rx_dboard_clock(false);
- this->enable_tx_dboard_clock(false);
+ void set_fpga_clock_rate(double rate){
+ if (_out_rate == rate) return;
+ if (rate == 61.44e6) set_clock_settings_with_external_vcxo(rate);
+ else set_clock_settings_with_internal_vco(rate);
}
double get_fpga_clock_rate(void){
- return master_clock_rate/fpga_clock_divider;
+ return this->_out_rate;
+ }
+
+ /***********************************************************************
+ * Special test clock output
+ **********************************************************************/
+ void enable_test_clock(bool enb){
+ //setup test clock (same divider as codec clock)
+ _ad9522_regs.out4_format = ad9522_regs_t::OUT4_FORMAT_CMOS;
+ _ad9522_regs.out4_cmos_configuration = (enb)?
+ ad9522_regs_t::OUT4_CMOS_CONFIGURATION_A_ON :
+ ad9522_regs_t::OUT4_CMOS_CONFIGURATION_OFF;
+ this->send_reg(0x0F0);
+ this->latch_regs();
}
/***********************************************************************
@@ -161,13 +321,13 @@ public:
std::vector<double> get_rx_dboard_clock_rates(void){
std::vector<double> rates;
for(size_t div = 1; div <= 16+16; div++)
- rates.push_back(master_clock_rate/div);
+ rates.push_back(this->_chan_rate/div);
return rates;
}
void set_rx_dboard_clock_rate(double rate){
assert_has(get_rx_dboard_clock_rates(), rate, "rx dboard clock rate");
- size_t divider = size_t(master_clock_rate/rate);
+ size_t divider = size_t(this->_chan_rate/rate);
//set the divider registers
set_clock_divider(divider,
_ad9522_regs.divider3_low_cycles,
@@ -197,7 +357,7 @@ public:
void set_tx_dboard_clock_rate(double rate){
assert_has(get_tx_dboard_clock_rates(), rate, "tx dboard clock rate");
- size_t divider = size_t(master_clock_rate/rate);
+ size_t divider = size_t(this->_chan_rate/rate);
//set the divider registers
set_clock_divider(divider,
_ad9522_regs.divider2_low_cycles,
@@ -238,6 +398,8 @@ public:
private:
usrp_e100_iface::sptr _iface;
ad9522_regs_t _ad9522_regs;
+ double _out_rate; //rate at the fpga and codec
+ double _chan_rate; //rate before final dividers
void latch_regs(void){
_ad9522_regs.io_update = 1;
@@ -253,6 +415,46 @@ private:
reg, 24, false /*no rb*/
);
}
+
+ void calibrate_now(void){
+ //vco calibration routine:
+ _ad9522_regs.vco_calibration_now = 0;
+ this->send_reg(0x18);
+ this->latch_regs();
+ _ad9522_regs.vco_calibration_now = 1;
+ this->send_reg(0x18);
+ this->latch_regs();
+ //wait for calibration done:
+ static const boost::uint8_t addr = 0x01F;
+ for (size_t ms10 = 0; ms10 < 100; ms10++){
+ boost::uint32_t reg = _iface->transact_spi(
+ UE_SPI_SS_AD9522, spi_config_t::EDGE_RISE,
+ _ad9522_regs.get_read_reg(addr), 24, true /*rb*/
+ );
+ _ad9522_regs.set_reg(addr, reg);
+ if (_ad9522_regs.vco_calibration_finished) return;
+ boost::this_thread::sleep(boost::posix_time::milliseconds(10));
+ }
+ std::cerr << "USRP-E100 clock control: VCO calibration timeout" << std::endl;
+ }
+
+ void send_all_regs(void){
+ //setup a list of register ranges to write
+ typedef std::pair<boost::uint16_t, boost::uint16_t> range_t;
+ static const std::vector<range_t> ranges = boost::assign::list_of
+ (range_t(0x000, 0x000)) (range_t(0x010, 0x01F))
+ (range_t(0x0F0, 0x0FD)) (range_t(0x190, 0x19B))
+ (range_t(0x1E0, 0x1E1)) (range_t(0x230, 0x230))
+ ;
+
+ //write initial register values and latch/update
+ BOOST_FOREACH(const range_t &range, ranges){
+ for(boost::uint16_t addr = range.first; addr <= range.second; addr++){
+ this->send_reg(addr);
+ }
+ }
+ this->latch_regs();
+ }
};
/***********************************************************************
diff --git a/host/lib/usrp/usrp_e100/clock_ctrl.hpp b/host/lib/usrp/usrp_e100/clock_ctrl.hpp
index d613d1473..1f9960ce4 100644
--- a/host/lib/usrp/usrp_e100/clock_ctrl.hpp
+++ b/host/lib/usrp/usrp_e100/clock_ctrl.hpp
@@ -40,6 +40,13 @@ public:
static sptr make(usrp_e100_iface::sptr iface);
/*!
+ * Set the rate of the fpga clock line.
+ * Throws if rate is not valid.
+ * \param rate the new rate in Hz
+ */
+ virtual void set_fpga_clock_rate(double rate) = 0;
+
+ /*!
* Get the rate of the fpga clock line.
* \return the fpga clock rate in Hz
*/
diff --git a/host/lib/usrp/usrp_e100/fpga-downloader.cc b/host/lib/usrp/usrp_e100/fpga_downloader.cpp
index 4a3d3b9af..c0013fcbd 100644
--- a/host/lib/usrp/usrp_e100/fpga-downloader.cc
+++ b/host/lib/usrp/usrp_e100/fpga_downloader.cpp
@@ -1,5 +1,5 @@
//
-// Copyright 2010 Ettus Research LLC
+// Copyright 2010-2011 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
@@ -43,6 +43,8 @@
*
*/
+namespace usrp_e_fpga_downloader_utility{
+
const unsigned int PROG_B = 175;
const unsigned int DONE = 173;
const unsigned int INIT_B = 114;
@@ -209,11 +211,10 @@ static void send_file_to_fpga(const std::string &file_name, gpio &error, gpio &d
{
std::ifstream bitstream;
- std::cout << "File name - " << file_name.c_str() << std::endl;
-
bitstream.open(file_name.c_str(), std::ios::binary);
- if (!bitstream.is_open())
- std::cout << "File " << file_name << " not opened succesfully." << std::endl;
+ if (!bitstream.is_open()) throw std::runtime_error(
+ "Coult not open the file: " + file_name
+ );
spidev spi("/dev/spidev1.0");
char buf[BUF_SIZE];
@@ -232,35 +233,20 @@ static void send_file_to_fpga(const std::string &file_name, gpio &error, gpio &d
} while (bitstream.gcount() == BUF_SIZE);
}
-/*
-int main(int argc, char *argv[])
-{
-
- gpio gpio_prog_b(PROG_B, OUT);
- gpio gpio_init_b(INIT_B, IN);
- gpio gpio_done (DONE, IN);
-
- if (argc == 2)
- bit_file = argv[1];
-
- std::cout << "FPGA config file: " << bit_file << std::endl;
-
- prepare_fpga_for_configuration(gpio_prog_b, gpio_init_b);
-
- std::cout << "Done = " << gpio_done.get_value() << std::endl;
-
- send_file_to_fpga(bit_file, gpio_init_b, gpio_done);
-}
-*/
+}//namespace usrp_e_fpga_downloader_utility
void usrp_e100_load_fpga(const std::string &bin_file){
+ using namespace usrp_e_fpga_downloader_utility;
+
gpio gpio_prog_b(PROG_B, OUT);
gpio gpio_init_b(INIT_B, IN);
gpio gpio_done (DONE, IN);
std::cout << "Loading FPGA image: " << bin_file << "... " << std::flush;
- UHD_ASSERT_THROW(std::system("/sbin/rmmod usrp_e") == 0);
+ if(std::system("/sbin/rmmod usrp_e") != 0){
+ std::cerr << "USRP-E100 FPGA downloader: could not unload usrp_e module" << std::endl;
+ }
prepare_fpga_for_configuration(gpio_prog_b, gpio_init_b);
@@ -268,7 +254,9 @@ void usrp_e100_load_fpga(const std::string &bin_file){
send_file_to_fpga(bin_file, gpio_init_b, gpio_done);
- UHD_ASSERT_THROW(std::system("/sbin/modprobe usrp_e") == 0);
+ if(std::system("/sbin/modprobe usrp_e") != 0){
+ std::cerr << "USRP-E100 FPGA downloader: could not load usrp_e module" << std::endl;
+ }
}
diff --git a/host/lib/usrp/usrp_e100/io_impl.cpp b/host/lib/usrp/usrp_e100/io_impl.cpp
index 2388482c7..fc6aaeaee 100644
--- a/host/lib/usrp/usrp_e100/io_impl.cpp
+++ b/host/lib/usrp/usrp_e100/io_impl.cpp
@@ -1,5 +1,5 @@
//
-// Copyright 2010 Ettus Research LLC
+// Copyright 2010-2011 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
@@ -35,7 +35,8 @@ zero_copy_if::sptr usrp_e100_make_mmap_zero_copy(usrp_e100_iface::sptr iface);
/***********************************************************************
* Constants
**********************************************************************/
-static const size_t tx_async_report_sid = 1;
+static const size_t rx_data_inline_sid = 1;
+static const size_t tx_async_report_sid = 2;
static const int underflow_flags = async_metadata_t::EVENT_CODE_UNDERFLOW | async_metadata_t::EVENT_CODE_UNDERFLOW_IN_PACKET;
static const bool recv_debug = false;
@@ -47,15 +48,12 @@ static const bool recv_debug = false;
* - vrt packet handler states
**********************************************************************/
struct usrp_e100_impl::io_impl{
- //state management for the vrt packet handler code
- vrt_packet_handler::recv_state packet_handler_recv_state;
- vrt_packet_handler::send_state packet_handler_send_state;
- zero_copy_if::sptr data_xport;
- bool continuous_streaming;
io_impl(usrp_e100_iface::sptr iface):
data_xport(usrp_e100_make_mmap_zero_copy(iface)),
- recv_pirate_booty(recv_booty_type::make(data_xport->get_num_recv_frames())),
- async_msg_fifo(bounded_buffer<async_metadata_t>::make(100/*messages deep*/))
+ get_recv_buffs_fcn(boost::bind(&usrp_e100_impl::io_impl::get_recv_buffs, this, _1)),
+ get_send_buffs_fcn(boost::bind(&usrp_e100_impl::io_impl::get_send_buffs, this, _1)),
+ recv_pirate_booty(data_xport->get_num_recv_frames()),
+ async_msg_fifo(100/*messages deep*/)
{
/* NOP */
}
@@ -66,17 +64,38 @@ struct usrp_e100_impl::io_impl{
recv_pirate_crew.join_all();
}
- bool get_recv_buffs(vrt_packet_handler::managed_recv_buffs_t &buffs, double timeout){
+ bool get_recv_buffs(vrt_packet_handler::managed_recv_buffs_t &buffs){
UHD_ASSERT_THROW(buffs.size() == 1);
boost::this_thread::disable_interruption di; //disable because the wait can throw
- return recv_pirate_booty->pop_with_timed_wait(buffs.front(), timeout);
+ return recv_pirate_booty.pop_with_timed_wait(buffs.front(), recv_timeout);
+ }
+
+ bool get_send_buffs(vrt_packet_handler::managed_send_buffs_t &buffs){
+ UHD_ASSERT_THROW(buffs.size() == 1);
+ buffs[0] = data_xport->get_send_buff(send_timeout);
+ return buffs[0].get() != NULL;
}
+ //The data transport is listed first so that it is deconstructed last,
+ //which is after the states and booty which may hold managed buffers.
+ zero_copy_if::sptr data_xport;
+
+ //bound callbacks for get buffs (bound once here, not in fast-path)
+ vrt_packet_handler::get_recv_buffs_t get_recv_buffs_fcn;
+ vrt_packet_handler::get_send_buffs_t get_send_buffs_fcn;
+
+ //timeouts set on calls to recv/send (passed into get buffs methods)
+ double recv_timeout, send_timeout;
+
+ //state management for the vrt packet handler code
+ vrt_packet_handler::recv_state packet_handler_recv_state;
+ vrt_packet_handler::send_state packet_handler_send_state;
+ bool continuous_streaming;
+
//a pirate's life is the life for me!
void recv_pirate_loop(usrp_e100_clock_ctrl::sptr);
- typedef bounded_buffer<managed_recv_buffer::sptr> recv_booty_type;
- recv_booty_type::sptr recv_pirate_booty;
- bounded_buffer<async_metadata_t>::sptr async_msg_fifo;
+ bounded_buffer<managed_recv_buffer::sptr> recv_pirate_booty;
+ bounded_buffer<async_metadata_t> async_msg_fifo;
boost::thread_group recv_pirate_crew;
bool recv_pirate_crew_raiding;
};
@@ -110,8 +129,17 @@ void usrp_e100_impl::io_impl::recv_pirate_loop(usrp_e100_clock_ctrl::sptr clock_
const boost::uint32_t *vrt_hdr = buff->cast<const boost::uint32_t *>();
vrt::if_hdr_unpack_le(vrt_hdr, if_packet_info);
+ //handle an rx data packet or inline message
+ if (if_packet_info.sid == rx_data_inline_sid){
+ if (recv_debug) std::cout << "this is rx_data_inline_sid\n";
+ //same number of frames as the data transport -> always immediate
+ recv_pirate_booty.push_with_wait(buff);
+ continue;
+ }
+
//handle a tx async report message
if (if_packet_info.sid == tx_async_report_sid and if_packet_info.packet_type != vrt::if_packet_info_t::PACKET_TYPE_DATA){
+ if (recv_debug) std::cout << "this is tx_async_report_sid\n";
//fill in the async metadata
async_metadata_t metadata;
@@ -124,12 +152,11 @@ void usrp_e100_impl::io_impl::recv_pirate_loop(usrp_e100_clock_ctrl::sptr clock_
//print the famous U, and push the metadata into the message queue
if (metadata.event_code & underflow_flags) std::cerr << "U" << std::flush;
- async_msg_fifo->push_with_pop_on_full(metadata);
+ async_msg_fifo.push_with_pop_on_full(metadata);
continue;
}
- //same number of frames as the data transport -> always immediate
- recv_pirate_booty->push_with_wait(buff);
+ if (recv_debug) std::cout << "this is unknown packet\n";
}catch(const std::exception &e){
std::cerr << "Error (usrp-e recv pirate loop): " << e.what() << std::endl;
@@ -153,17 +180,20 @@ void usrp_e100_impl::io_init(void){
//setup before the registers (transport called to calculate max spp)
_io_impl = UHD_PIMPL_MAKE(io_impl, (_iface));
+ //clear state machines
+ _iface->poke32(UE_REG_CTRL_RX_CLEAR, 0);
+ _iface->poke32(UE_REG_CTRL_TX_CLEAR, 0);
+
//setup rx data path
_iface->poke32(UE_REG_CTRL_RX_NSAMPS_PER_PKT, get_max_recv_samps_per_packet());
_iface->poke32(UE_REG_CTRL_RX_NCHANNELS, 1);
- _iface->poke32(UE_REG_CTRL_RX_CLEAR_OVERRUN, 1); //reset
_iface->poke32(UE_REG_CTRL_RX_VRT_HEADER, 0
| (0x1 << 28) //if data with stream id
| (0x1 << 26) //has trailer
| (0x3 << 22) //integer time other
| (0x1 << 20) //fractional time sample count
);
- _iface->poke32(UE_REG_CTRL_RX_VRT_STREAM_ID, 0);
+ _iface->poke32(UE_REG_CTRL_RX_VRT_STREAM_ID, rx_data_inline_sid);
_iface->poke32(UE_REG_CTRL_RX_VRT_TRAILER, 0);
//setup the tx policy
@@ -185,7 +215,6 @@ void usrp_e100_impl::issue_stream_cmd(const stream_cmd_t &stream_cmd){
void usrp_e100_impl::handle_overrun(size_t){
std::cerr << "O"; //the famous OOOOOOOOOOO
- _iface->poke32(UE_REG_CTRL_RX_CLEAR_OVERRUN, 0);
if (_io_impl->continuous_streaming){
this->issue_stream_cmd(stream_cmd_t::STREAM_MODE_START_CONTINUOUS);
}
@@ -194,15 +223,6 @@ void usrp_e100_impl::handle_overrun(size_t){
/***********************************************************************
* Data Send
**********************************************************************/
-bool get_send_buffs(
- zero_copy_if::sptr trans, double timeout,
- vrt_packet_handler::managed_send_buffs_t &buffs
-){
- UHD_ASSERT_THROW(buffs.size() == 1);
- buffs[0] = trans->get_send_buff(timeout);
- return buffs[0].get() != NULL;
-}
-
size_t usrp_e100_impl::get_max_send_samps_per_packet(void) const{
static const size_t hdr_size = 0
+ vrt::max_if_hdr_words32*sizeof(boost::uint32_t)
@@ -213,10 +233,11 @@ size_t usrp_e100_impl::get_max_send_samps_per_packet(void) const{
}
size_t usrp_e100_impl::send(
- const std::vector<const void *> &buffs, size_t num_samps,
+ const send_buffs_type &buffs, size_t num_samps,
const tx_metadata_t &metadata, const io_type_t &io_type,
send_mode_t send_mode, double timeout
){
+ _io_impl->send_timeout = timeout;
return vrt_packet_handler::send(
_io_impl->packet_handler_send_state, //last state of the send handler
buffs, num_samps, //buffer to fill
@@ -224,7 +245,7 @@ size_t usrp_e100_impl::send(
io_type, _send_otw_type, //input and output types to convert
_clock_ctrl->get_fpga_clock_rate(), //master clock tick rate
uhd::transport::vrt::if_hdr_pack_le,
- boost::bind(&get_send_buffs, _io_impl->data_xport, timeout, _1),
+ _io_impl->get_send_buffs_fcn,
get_max_send_samps_per_packet()
);
}
@@ -243,10 +264,11 @@ size_t usrp_e100_impl::get_max_recv_samps_per_packet(void) const{
}
size_t usrp_e100_impl::recv(
- const std::vector<void *> &buffs, size_t num_samps,
+ const recv_buffs_type &buffs, size_t num_samps,
rx_metadata_t &metadata, const io_type_t &io_type,
recv_mode_t recv_mode, double timeout
){
+ _io_impl->recv_timeout = timeout;
return vrt_packet_handler::recv(
_io_impl->packet_handler_recv_state, //last state of the recv handler
buffs, num_samps, //buffer to fill
@@ -254,7 +276,7 @@ size_t usrp_e100_impl::recv(
io_type, _recv_otw_type, //input and output types to convert
_clock_ctrl->get_fpga_clock_rate(), //master clock tick rate
uhd::transport::vrt::if_hdr_unpack_le,
- boost::bind(&usrp_e100_impl::io_impl::get_recv_buffs, _io_impl.get(), _1, timeout),
+ _io_impl->get_recv_buffs_fcn,
boost::bind(&usrp_e100_impl::handle_overrun, this, _1)
);
}
@@ -266,5 +288,5 @@ bool usrp_e100_impl::recv_async_msg(
async_metadata_t &async_metadata, double timeout
){
boost::this_thread::disable_interruption di; //disable because the wait can throw
- return _io_impl->async_msg_fifo->pop_with_timed_wait(async_metadata, timeout);
+ return _io_impl->async_msg_fifo.pop_with_timed_wait(async_metadata, timeout);
}
diff --git a/host/lib/usrp/usrp_e100/mboard_impl.cpp b/host/lib/usrp/usrp_e100/mboard_impl.cpp
index f52d2e6fb..0e08cd435 100644
--- a/host/lib/usrp/usrp_e100/mboard_impl.cpp
+++ b/host/lib/usrp/usrp_e100/mboard_impl.cpp
@@ -152,6 +152,10 @@ void usrp_e100_impl::mboard_get(const wax::obj &key_, wax::obj &val){
return;
}
+ case MBOARD_PROP_CLOCK_RATE:
+ val = _clock_ctrl->get_fpga_clock_rate();
+ return;
+
default: UHD_THROW_PROP_GET_ERROR();
}
}
@@ -211,6 +215,10 @@ void usrp_e100_impl::mboard_set(const wax::obj &key, const wax::obj &val){
update_clock_config();
return;
+ case MBOARD_PROP_CLOCK_RATE:
+ _clock_ctrl->set_fpga_clock_rate(val.as<double>());
+ return;
+
default: UHD_THROW_PROP_SET_ERROR();
}
}
diff --git a/host/lib/usrp/usrp_e100/usrp_e100_iface.cpp b/host/lib/usrp/usrp_e100/usrp_e100_iface.cpp
index 40c7afabb..ad36dd97a 100644
--- a/host/lib/usrp/usrp_e100/usrp_e100_iface.cpp
+++ b/host/lib/usrp/usrp_e100/usrp_e100_iface.cpp
@@ -1,5 +1,5 @@
//
-// Copyright 2010 Ettus Research LLC
+// Copyright 2010-2011 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
@@ -16,6 +16,7 @@
//
#include "usrp_e100_iface.hpp"
+#include "usrp_e100_regs.hpp"
#include <uhd/utils/assert.hpp>
#include <sys/ioctl.h> //ioctl
#include <fcntl.h> //open, close
@@ -108,6 +109,9 @@ public:
throw std::runtime_error("Failed to open " + node);
}
+ //very first thing, reset all the wishbone, always do first!
+ this->poke32(UE_REG_CLEAR_GLOBAL, 0);
+
mb_eeprom = mboard_eeprom_t(get_i2c_dev_iface(), mboard_eeprom_t::MAP_E100);
}
diff --git a/host/lib/usrp/usrp_e100/usrp_e100_impl.hpp b/host/lib/usrp/usrp_e100/usrp_e100_impl.hpp
index df8e5dc9f..897616320 100644
--- a/host/lib/usrp/usrp_e100/usrp_e100_impl.hpp
+++ b/host/lib/usrp/usrp_e100/usrp_e100_impl.hpp
@@ -1,5 +1,5 @@
//
-// Copyright 2010 Ettus Research LLC
+// Copyright 2010-2011 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
@@ -30,7 +30,7 @@
#ifndef INCLUDED_USRP_E100_IMPL_HPP
#define INCLUDED_USRP_E100_IMPL_HPP
-static const boost::uint16_t USRP_E_COMPAT_NUM = 0x02; //make this 3 then the mainline fpga image gets fixed for embedded
+static const boost::uint16_t USRP_E_COMPAT_NUM = 0x03;
//! load an fpga image from a bin file into the usrp-e fpga
extern void usrp_e100_load_fpga(const std::string &bin_file);
@@ -83,8 +83,8 @@ public:
~usrp_e100_impl(void);
//the io interface
- size_t send(const std::vector<const void *> &, size_t, const uhd::tx_metadata_t &, const uhd::io_type_t &, send_mode_t, double);
- size_t recv(const std::vector<void *> &, size_t, uhd::rx_metadata_t &, const uhd::io_type_t &, recv_mode_t, double);
+ size_t send(const send_buffs_type &, size_t, const uhd::tx_metadata_t &, const uhd::io_type_t &, send_mode_t, double);
+ size_t recv(const recv_buffs_type &, size_t, uhd::rx_metadata_t &, const uhd::io_type_t &, recv_mode_t, double);
bool recv_async_msg(uhd::async_metadata_t &, double);
size_t get_max_send_samps_per_packet(void) const;
size_t get_max_recv_samps_per_packet(void) const;
diff --git a/host/lib/usrp/usrp_e100/usrp_e100_mmap_zero_copy.cpp b/host/lib/usrp/usrp_e100/usrp_e100_mmap_zero_copy.cpp
index bf378a9b1..c155d426a 100644
--- a/host/lib/usrp/usrp_e100/usrp_e100_mmap_zero_copy.cpp
+++ b/host/lib/usrp/usrp_e100/usrp_e100_mmap_zero_copy.cpp
@@ -1,5 +1,5 @@
//
-// Copyright 2010 Ettus Research LLC
+// Copyright 2010-2011 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
@@ -22,8 +22,7 @@
#include <sys/mman.h> //mmap
#include <unistd.h> //getpagesize
#include <poll.h> //poll
-#include <boost/bind.hpp>
-#include <boost/enable_shared_from_this.hpp>
+#include <vector>
#include <iostream>
using namespace uhd;
@@ -34,9 +33,85 @@ static const bool sp_verbose = false; //slow-path verbose
static const size_t poll_breakout = 10; //how many poll timeouts constitute a full timeout
/***********************************************************************
+ * Reusable managed receiver buffer:
+ * - The buffer knows how to claim and release a frame.
+ **********************************************************************/
+class usrp_e100_mmap_zero_copy_mrb : public managed_recv_buffer{
+public:
+ usrp_e100_mmap_zero_copy_mrb(void *mem, ring_buffer_info *info):
+ _mem(mem), _info(info) { /* NOP */ }
+
+ void release(void){
+ if (_info->flags != RB_USER_PROCESS) return;
+ if (fp_verbose) std::cout << "recv buff: release" << std::endl;
+ _info->flags = RB_KERNEL; //release the frame
+ }
+
+ bool ready(void){return _info->flags & RB_USER;}
+
+ sptr get_new(void){
+ if (fp_verbose) std::cout << " make_recv_buff: " << get_size() << std::endl;
+ _info->flags = RB_USER_PROCESS; //claim the frame
+ return sptr(this, &usrp_e100_mmap_zero_copy_mrb::fake_deleter);
+ }
+
+private:
+ static void fake_deleter(void *obj){
+ static_cast<usrp_e100_mmap_zero_copy_mrb *>(obj)->release();
+ }
+
+ const void *get_buff(void) const{return _mem;}
+ size_t get_size(void) const{return _info->len;}
+
+ void *_mem;
+ ring_buffer_info *_info;
+};
+
+/***********************************************************************
+ * Reusable managed send buffer:
+ * - The buffer knows how to claim and release a frame.
+ **********************************************************************/
+class usrp_e100_mmap_zero_copy_msb : public managed_send_buffer{
+public:
+ usrp_e100_mmap_zero_copy_msb(void *mem, ring_buffer_info *info, size_t len, int fd):
+ _mem(mem), _info(info), _len(len), _fd(fd) { /* NOP */ }
+
+ void commit(size_t len){
+ if (_info->flags != RB_USER_PROCESS) return;
+ if (fp_verbose) std::cout << "send buff: commit " << len << std::endl;
+ _info->len = len;
+ _info->flags = RB_USER; //release the frame
+ if (::write(_fd, NULL, 0) < 0){ //notifies the kernel
+ std::cerr << UHD_THROW_SITE_INFO("write error") << std::endl;
+ }
+ }
+
+ bool ready(void){return _info->flags & RB_KERNEL;}
+
+ sptr get_new(void){
+ if (fp_verbose) std::cout << " make_send_buff: " << get_size() << std::endl;
+ _info->flags = RB_USER_PROCESS; //claim the frame
+ return sptr(this, &usrp_e100_mmap_zero_copy_msb::fake_deleter);
+ }
+
+private:
+ static void fake_deleter(void *obj){
+ static_cast<usrp_e100_mmap_zero_copy_msb *>(obj)->commit(0);
+ }
+
+ void *get_buff(void) const{return _mem;}
+ size_t get_size(void) const{return _len;}
+
+ void *_mem;
+ ring_buffer_info *_info;
+ size_t _len;
+ int _fd;
+};
+
+/***********************************************************************
* The zero copy interface implementation
**********************************************************************/
-class usrp_e100_mmap_zero_copy_impl : public zero_copy_if, public boost::enable_shared_from_this<usrp_e100_mmap_zero_copy_impl> {
+class usrp_e100_mmap_zero_copy_impl : public zero_copy_if{
public:
usrp_e100_mmap_zero_copy_impl(usrp_e100_iface::sptr iface):
_fd(iface->get_file_descriptor()), _recv_index(0), _send_index(0)
@@ -82,13 +157,32 @@ public:
std::cout << "send_buff_off: " << send_buff_off << std::endl;
}
+ //pointers to sections in the mapped memory
+ ring_buffer_info (*recv_info)[], (*send_info)[];
+ char *recv_buff, *send_buff;
+
//set the internal pointers for info and buffers
typedef ring_buffer_info (*rbi_pta)[];
char *rb_ptr = reinterpret_cast<char *>(_mapped_mem);
- _recv_info = reinterpret_cast<rbi_pta>(rb_ptr + recv_info_off);
- _recv_buff = rb_ptr + recv_buff_off;
- _send_info = reinterpret_cast<rbi_pta>(rb_ptr + send_info_off);
- _send_buff = rb_ptr + send_buff_off;
+ recv_info = reinterpret_cast<rbi_pta>(rb_ptr + recv_info_off);
+ recv_buff = rb_ptr + recv_buff_off;
+ send_info = reinterpret_cast<rbi_pta>(rb_ptr + send_info_off);
+ send_buff = rb_ptr + send_buff_off;
+
+ //initialize the managed receive buffers
+ for (size_t i = 0; i < get_num_recv_frames(); i++){
+ _mrb_pool.push_back(usrp_e100_mmap_zero_copy_mrb(
+ recv_buff + get_recv_frame_size()*i, (*recv_info) + i
+ ));
+ }
+
+ //initialize the managed send buffers
+ for (size_t i = 0; i < get_num_recv_frames(); i++){
+ _msb_pool.push_back(usrp_e100_mmap_zero_copy_msb(
+ send_buff + get_send_frame_size()*i, (*send_info) + i,
+ get_send_frame_size(), _fd
+ ));
+ }
}
~usrp_e100_mmap_zero_copy_impl(void){
@@ -98,13 +192,10 @@ public:
managed_recv_buffer::sptr get_recv_buff(double timeout){
if (fp_verbose) std::cout << "get_recv_buff: " << _recv_index << std::endl;
-
- //grab pointers to the info and buffer
- ring_buffer_info *info = (*_recv_info) + _recv_index;
- void *mem = _recv_buff + _frame_size*_recv_index;
+ usrp_e100_mmap_zero_copy_mrb &mrb = _mrb_pool[_recv_index];
//poll/wait for a ready frame
- if (not (info->flags & RB_USER)){
+ if (not mrb.ready()){
for (size_t i = 0; i < poll_breakout; i++){
pollfd pfd;
pfd.fd = _fd;
@@ -116,18 +207,11 @@ public:
return managed_recv_buffer::sptr(); //timed-out for real
} found_user_frame:
- //the process has claimed the frame
- info->flags = RB_USER_PROCESS;
-
//increment the index for the next call
- if (++_recv_index == size_t(_rb_size.num_rx_frames)) _recv_index = 0;
+ if (++_recv_index == get_num_recv_frames()) _recv_index = 0;
//return the managed buffer for this frame
- if (fp_verbose) std::cout << " make_recv_buff: " << info->len << std::endl;
- return managed_recv_buffer::make_safe(
- boost::asio::const_buffer(mem, info->len),
- boost::bind(&usrp_e100_mmap_zero_copy_impl::release, shared_from_this(), info)
- );
+ return mrb.get_new();
}
size_t get_num_recv_frames(void) const{
@@ -140,13 +224,10 @@ public:
managed_send_buffer::sptr get_send_buff(double timeout){
if (fp_verbose) std::cout << "get_send_buff: " << _send_index << std::endl;
-
- //grab pointers to the info and buffer
- ring_buffer_info *info = (*_send_info) + _send_index;
- void *mem = _send_buff + _frame_size*_send_index;
+ usrp_e100_mmap_zero_copy_msb &msb = _msb_pool[_send_index];
//poll/wait for a ready frame
- if (not (info->flags & RB_KERNEL)){
+ if (not msb.ready()){
pollfd pfd;
pfd.fd = _fd;
pfd.events = POLLOUT;
@@ -156,14 +237,10 @@ public:
}
//increment the index for the next call
- if (++_send_index == size_t(_rb_size.num_tx_frames)) _send_index = 0;
+ if (++_send_index == get_num_send_frames()) _send_index = 0;
//return the managed buffer for this frame
- if (fp_verbose) std::cout << " make_send_buff: " << _frame_size << std::endl;
- return managed_send_buffer::make_safe(
- boost::asio::mutable_buffer(mem, _frame_size),
- boost::bind(&usrp_e100_mmap_zero_copy_impl::commit, shared_from_this(), info, _1)
- );
+ return msb.get_new();
}
size_t get_num_send_frames(void) const{
@@ -175,21 +252,7 @@ public:
}
private:
-
- void release(ring_buffer_info *info){
- if (fp_verbose) std::cout << "recv buff: release" << std::endl;
- info->flags = RB_KERNEL;
- }
-
- void commit(ring_buffer_info *info, size_t len){
- if (fp_verbose) std::cout << "send buff: commit " << len << std::endl;
- info->len = len;
- info->flags = RB_USER;
- if (::write(_fd, NULL, 0) < 0){
- std::cerr << UHD_THROW_SITE_INFO("write error") << std::endl;
- }
- }
-
+ //file descriptor for mmap
int _fd;
//the mapped memory itself
@@ -199,9 +262,9 @@ private:
usrp_e_ring_buffer_size_t _rb_size;
size_t _frame_size, _map_size;
- //pointers to sections in the mapped memory
- ring_buffer_info (*_recv_info)[], (*_send_info)[];
- char *_recv_buff, *_send_buff;
+ //re-usable managed buffers
+ std::vector<usrp_e100_mmap_zero_copy_mrb> _mrb_pool;
+ std::vector<usrp_e100_mmap_zero_copy_msb> _msb_pool;
//indexes into sub-sections of mapped memory
size_t _recv_index, _send_index;
diff --git a/host/lib/usrp/usrp_e100/usrp_e100_regs.hpp b/host/lib/usrp/usrp_e100/usrp_e100_regs.hpp
index a57fe5171..a030462d0 100644
--- a/host/lib/usrp/usrp_e100/usrp_e100_regs.hpp
+++ b/host/lib/usrp/usrp_e100/usrp_e100_regs.hpp
@@ -17,7 +17,6 @@
// Slave pointers
#define UE_REG_SLAVE(n) ((n)<<7)
-#define UE_REG_SR_ADDR(n) ((UE_REG_SLAVE(5)) + (4*(n)))
/////////////////////////////////////////////////////
// Slave 0 -- Misc Regs
@@ -89,16 +88,6 @@
#define GPIO_SEL_DEBUG_0 0 // if pin is an output, debug lines from FPGA fabric
#define GPIO_SEL_DEBUG_1 1 // if pin is an output, debug lines from FPGA fabric
-
-////////////////////////////////////////////////////
-// Slave 5 -- Settings Bus
-//
-// Output-only, no readback, 32 registers total
-// Each register must be written 32 bits at a time
-// First the address xxx_xx00 and then xxx_xx10
-
-#define UE_REG_SETTINGS_BASE UE_REG_SLAVE(5)
-
///////////////////////////////////////////////////
// Slave 6 -- ATR Controller
// 16 regs
@@ -123,48 +112,72 @@
#define UE_REG_RB_TIME_NOW_TICKS UE_REG_RB_MUX_32_BASE + 4
#define UE_REG_RB_TIME_PPS_SECS UE_REG_RB_MUX_32_BASE + 8
#define UE_REG_RB_TIME_PPS_TICKS UE_REG_RB_MUX_32_BASE + 12
+#define UE_REG_RB_MISC_TEST32 UE_REG_RB_MUX_32_BASE + 16
+
+////////////////////////////////////////////////////
+// Slave 8 -- Settings Bus
+//
+// Output-only, no readback, 64 registers total
+// Each register must be written 64 bits at a time
+// First the address xxx_xx00 and then xxx_xx10
+
+#define UE_REG_SETTINGS_BASE_ADDR(n) (UE_REG_SLAVE(8) + (4*(n)))
+
+#define UE_REG_SR_MISC_TEST32 UE_REG_SETTINGS_BASE_ADDR(52)
+
+/////////////////////////////////////////////////
+// Magic reset regs
+////////////////////////////////////////////////
+#define UE_REG_CLEAR_ADDR(n) (UE_REG_SETTINGS_BASE_ADDR(48) + (4*(n)))
+#define UE_REG_CLEAR_RX UE_REG_CLEAR_ADDR(0)
+#define UE_REG_CLEAR_TX UE_REG_CLEAR_ADDR(1)
+#define UE_REG_CLEAR_GLOBAL UE_REG_CLEAR_ADDR(2)
/////////////////////////////////////////////////
// DSP RX Regs
////////////////////////////////////////////////
-#define UE_REG_DSP_RX_FREQ UE_REG_SR_ADDR(0)
-#define UE_REG_DSP_RX_SCALE_IQ UE_REG_SR_ADDR(1) // {scale_i,scale_q}
-#define UE_REG_DSP_RX_DECIM_RATE UE_REG_SR_ADDR(2) // hb and decim rate
-#define UE_REG_DSP_RX_DCOFFSET_I UE_REG_SR_ADDR(3) // Bit 31 high sets fixed offset mode, using lower 14 bits, // otherwise it is automatic
-#define UE_REG_DSP_RX_DCOFFSET_Q UE_REG_SR_ADDR(4) // Bit 31 high sets fixed offset mode, using lower 14 bits
-#define UE_REG_DSP_RX_MUX UE_REG_SR_ADDR(5)
+#define UE_REG_DSP_RX_ADDR(n) (UE_REG_SETTINGS_BASE_ADDR(16) + (4*(n)))
+#define UE_REG_DSP_RX_FREQ UE_REG_DSP_RX_ADDR(0)
+#define UE_REG_DSP_RX_SCALE_IQ UE_REG_DSP_RX_ADDR(1) // {scale_i,scale_q}
+#define UE_REG_DSP_RX_DECIM_RATE UE_REG_DSP_RX_ADDR(2) // hb and decim rate
+#define UE_REG_DSP_RX_DCOFFSET_I UE_REG_DSP_RX_ADDR(3) // Bit 31 high sets fixed offset mode, using lower 14 bits, // otherwise it is automatic
+#define UE_REG_DSP_RX_DCOFFSET_Q UE_REG_DSP_RX_ADDR(4) // Bit 31 high sets fixed offset mode, using lower 14 bits
+#define UE_REG_DSP_RX_MUX UE_REG_DSP_RX_ADDR(5)
///////////////////////////////////////////////////
// VITA RX CTRL regs
///////////////////////////////////////////////////
+#define UE_REG_CTRL_RX_ADDR(n) (UE_REG_SETTINGS_BASE_ADDR(0) + (4*(n)))
// The following 3 are logically a single command register.
// They are clocked into the underlying fifo when time_ticks is written.
-#define UE_REG_CTRL_RX_STREAM_CMD UE_REG_SR_ADDR(8) // {now, chain, num_samples(30)
-#define UE_REG_CTRL_RX_TIME_SECS UE_REG_SR_ADDR(9)
-#define UE_REG_CTRL_RX_TIME_TICKS UE_REG_SR_ADDR(10)
-#define UE_REG_CTRL_RX_CLEAR_OVERRUN UE_REG_SR_ADDR(11) // write anything to clear overrun
-#define UE_REG_CTRL_RX_VRT_HEADER UE_REG_SR_ADDR(12) // word 0 of packet. FPGA fills in packet counter
-#define UE_REG_CTRL_RX_VRT_STREAM_ID UE_REG_SR_ADDR(13) // word 1 of packet.
-#define UE_REG_CTRL_RX_VRT_TRAILER UE_REG_SR_ADDR(14)
-#define UE_REG_CTRL_RX_NSAMPS_PER_PKT UE_REG_SR_ADDR(15)
-#define UE_REG_CTRL_RX_NCHANNELS UE_REG_SR_ADDR(16) // 1 in basic case, up to 4 for vector sources
+#define UE_REG_CTRL_RX_STREAM_CMD UE_REG_CTRL_RX_ADDR(0) // {now, chain, num_samples(30)
+#define UE_REG_CTRL_RX_TIME_SECS UE_REG_CTRL_RX_ADDR(1)
+#define UE_REG_CTRL_RX_TIME_TICKS UE_REG_CTRL_RX_ADDR(2)
+#define UE_REG_CTRL_RX_CLEAR UE_REG_CTRL_RX_ADDR(3) // write anything to clear
+#define UE_REG_CTRL_RX_VRT_HEADER UE_REG_CTRL_RX_ADDR(4) // word 0 of packet. FPGA fills in packet counter
+#define UE_REG_CTRL_RX_VRT_STREAM_ID UE_REG_CTRL_RX_ADDR(5) // word 1 of packet.
+#define UE_REG_CTRL_RX_VRT_TRAILER UE_REG_CTRL_RX_ADDR(6)
+#define UE_REG_CTRL_RX_NSAMPS_PER_PKT UE_REG_CTRL_RX_ADDR(7)
+#define UE_REG_CTRL_RX_NCHANNELS UE_REG_CTRL_RX_ADDR(8) // 1 in basic case, up to 4 for vector sources
/////////////////////////////////////////////////
// DSP TX Regs
////////////////////////////////////////////////
-#define UE_REG_DSP_TX_FREQ UE_REG_SR_ADDR(17)
-#define UE_REG_DSP_TX_SCALE_IQ UE_REG_SR_ADDR(18) // {scale_i,scale_q}
-#define UE_REG_DSP_TX_INTERP_RATE UE_REG_SR_ADDR(19)
-#define UE_REG_DSP_TX_UNUSED UE_REG_SR_ADDR(20)
-#define UE_REG_DSP_TX_MUX UE_REG_SR_ADDR(21)
+#define UE_REG_DSP_TX_ADDR(n) (UE_REG_SETTINGS_BASE_ADDR(32) + (4*(n)))
+#define UE_REG_DSP_TX_FREQ UE_REG_DSP_TX_ADDR(0)
+#define UE_REG_DSP_TX_SCALE_IQ UE_REG_DSP_TX_ADDR(1) // {scale_i,scale_q}
+#define UE_REG_DSP_TX_INTERP_RATE UE_REG_DSP_TX_ADDR(2)
+#define UE_REG_DSP_TX_UNUSED UE_REG_DSP_TX_ADDR(3)
+#define UE_REG_DSP_TX_MUX UE_REG_DSP_TX_ADDR(4)
/////////////////////////////////////////////////
// VITA TX CTRL regs
////////////////////////////////////////////////
-#define UE_REG_CTRL_TX_NCHANNELS UE_REG_SR_ADDR(24)
-#define UE_REG_CTRL_TX_CLEAR_UNDERRUN UE_REG_SR_ADDR(25)
-#define UE_REG_CTRL_TX_REPORT_SID UE_REG_SR_ADDR(26)
-#define UE_REG_CTRL_TX_POLICY UE_REG_SR_ADDR(27)
+#define UE_REG_CTRL_TX_ADDR(n) (UE_REG_SETTINGS_BASE_ADDR(24) + (4*(n)))
+#define UE_REG_CTRL_TX_NCHANNELS UE_REG_CTRL_TX_ADDR(0)
+#define UE_REG_CTRL_TX_CLEAR UE_REG_CTRL_TX_ADDR(1)
+#define UE_REG_CTRL_TX_REPORT_SID UE_REG_CTRL_TX_ADDR(2)
+#define UE_REG_CTRL_TX_POLICY UE_REG_CTRL_TX_ADDR(3)
#define UE_FLAG_CTRL_TX_POLICY_WAIT (0x1 << 0)
#define UE_FLAG_CTRL_TX_POLICY_NEXT_PACKET (0x1 << 1)
@@ -189,11 +202,12 @@
*
* </pre>
*/
-#define UE_REG_TIME64_SECS UE_REG_SR_ADDR(28) // value to set absolute secs to on next PPS
-#define UE_REG_TIME64_TICKS UE_REG_SR_ADDR(29) // value to set absolute ticks to on next PPS
-#define UE_REG_TIME64_FLAGS UE_REG_SR_ADDR(30) // flags - see chart above
-#define UE_REG_TIME64_IMM UE_REG_SR_ADDR(31) // set immediate (0=latch on next pps, 1=latch immediate, default=0)
-#define UE_REG_TIME64_TPS UE_REG_SR_ADDR(31) // clock ticks per second (counter rollover)
+#define UE_REG_TIME64_ADDR(n) (UE_REG_SETTINGS_BASE_ADDR(40) + (4*(n)))
+#define UE_REG_TIME64_SECS UE_REG_TIME64_ADDR(0) // value to set absolute secs to on next PPS
+#define UE_REG_TIME64_TICKS UE_REG_TIME64_ADDR(1) // value to set absolute ticks to on next PPS
+#define UE_REG_TIME64_FLAGS UE_REG_TIME64_ADDR(2) // flags - see chart above
+#define UE_REG_TIME64_IMM UE_REG_TIME64_ADDR(3) // set immediate (0=latch on next pps, 1=latch immediate, default=0)
+#define UE_REG_TIME64_TPS UE_REG_TIME64_ADDR(4) // clock ticks per second (counter rollover)
//pps flags (see above)
#define UE_FLAG_TIME64_PPS_NEGEDGE (0 << 0)