diff options
Diffstat (limited to 'host/lib')
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) |