diff options
-rw-r--r-- | host/include/uhd/transport/nirio/nirio_fifo.h | 136 | ||||
-rw-r--r-- | host/include/uhd/transport/nirio/nirio_fifo.ipp | 189 | ||||
-rw-r--r-- | host/lib/transport/nirio/niriok_proxy_impl_v2.cpp | 14 | ||||
-rw-r--r-- | host/lib/transport/nirio_zero_copy.cpp | 2 |
4 files changed, 295 insertions, 46 deletions
diff --git a/host/include/uhd/transport/nirio/nirio_fifo.h b/host/include/uhd/transport/nirio/nirio_fifo.h index a0cd7149f..f6eb97ce2 100644 --- a/host/include/uhd/transport/nirio/nirio_fifo.h +++ b/host/include/uhd/transport/nirio/nirio_fifo.h @@ -28,6 +28,7 @@ #include <string> #include <boost/thread/recursive_mutex.hpp> #include <boost/thread/thread.hpp> +#include <boost/atomic/atomic.hpp> namespace uhd { namespace niusrprio { @@ -43,29 +44,91 @@ class nirio_fifo : private boost::noncopyable public: typedef boost::shared_ptr< nirio_fifo<data_t> > sptr; + typedef enum { + MINIMIZE_LATENCY, + MAXIMIZE_THROUGHPUT + } fifo_optimization_option_t; + + /*! + * Initializing constructor + * \param riok_proxy Proxy to the kernel driver + * \param direction Direction of the fifo (INPUT_FIFO [from device] or OUTPUT_FIFO [to device]) + * \param name Name of the fifo + * \param fifo_instance Instance of the fifo as defined by RIO FPGA target + */ nirio_fifo( niriok_proxy::sptr riok_proxy, - fifo_direction_t direction, + const fifo_direction_t direction, const std::string& name, - uint32_t fifo_instance); + const uint32_t fifo_instance); virtual ~nirio_fifo(); + /*! + * Configures the characterists of this DMA FIFO, allocates DMA buffer and maps it to user mode + * \param requested_depth Desired size of DMA FIFO buffer on host (in elements) + * \param frame_size_in_elements Size of a single frame (smallest transfer block) for this FIFO + * \param actual_depth Receives the size (in elements) of the allocated DMA buffer + * \param actual_size Receives the size (in bytes) of the allocated DMA buffer + * \param fifo_optimization_option FIFO acquire policy (MINIMIZE_LATENCY [fetch what was there last time] or MAXIMIZE_THROUGHPUT [fetch all available]) + * \return status + */ nirio_status initialize( const size_t requested_depth, + const size_t frame_size_in_elements, size_t& actual_depth, - size_t& actual_size); + size_t& actual_size, + const fifo_optimization_option_t fifo_optimization_option = MINIMIZE_LATENCY); + /*! + * Stops FIFO if started, releases any acquired blocks, and unmaps the DMA buffer + */ void finalize(); + /*! + * Name accessor + * \return FIFO name + */ inline const std::string& get_name() const { return _name; } + + /*! + * Channel/Instance accessor + * \return FIFO channel (instance) + */ inline uint32_t get_channel() const { return _fifo_channel; } + + /*! + * Direction accessor + * \return FIFO direction + */ inline fifo_direction_t get_direction() const { return _fifo_direction; } + + /*! + * Type accessor + * \return FIFO element type + */ inline nirio_scalar_type_t get_scalar_type() const { return _datatype_info.scalar_type; } + /*! + * Starts the DMA transfer between host and device, pre-acquires any available blocks + * \return status + */ nirio_status start(); + /*! + * Stops the DMA transfer between host and device, releases any acquired blocks + * \return status + */ nirio_status stop(); + /*! + * Acquires space in the DMA buffer so it can be written by the host (output) or read by the host (input) + * \param elements Receives the address of the acquired block (pointer to mapped zero-copy DMA buffer) + * \param elements_requested Size (in elements) of the block to acquire + * \param timeout The amount of time (in ms) to wait for the elements to become available in the DMA buffer + * \param elements_acquired Receives the number of DMA buffer elements actually acquired + * \param elements_remaining Receives the number of DMA buffer elements available to be acquired by the host + * \return status + */ nirio_status acquire( data_t*& elements, const size_t elements_requested, @@ -73,26 +136,80 @@ public: size_t& elements_acquired, size_t& elements_remaining); + /*! + * Releases space in the DMA buffer so it can be read by the device (output) or written by the device (input) + * \param elements Size (in elements) of the block to release. + * \return status + */ nirio_status release(const size_t elements); + /*! + * Reads data from the DMA FIFO into the provided buffer + * \param buf The buffer into which to read data from the DMA FIFO + * \param num_elements Size (in elements) of the data to read + * \param timeout The amount of time (in ms) to wait for the elements to become available in the DMA buffer + * \param num_read Receives the number of DMA buffer elements actually read + * \param num_remaining Receives the number of DMA buffer elements available be read by the host + * \return status + */ nirio_status read( data_t* buf, const uint32_t num_elements, - uint32_t timeout, + const uint32_t timeout, uint32_t& num_read, uint32_t& num_remaining); + /*! + * Writes data from the DMA FIFO + * \param buf The buffer containing data to be written to the DMA FIFO + * \param num_elements Size (in elements) of the data to write + * \param timeout The amount of time (in ms) to wait for the elements to become available in the DMA buffer + * \param num_remaining Receives the number of DMA buffer elements available be written by the host + * \return status + */ nirio_status write( const data_t* buf, const uint32_t num_elements, - uint32_t timeout, + const uint32_t timeout, uint32_t& num_remaining); private: //Methods + + /*! + * datatype info accessor + * \return datatype info + */ datatype_info_t _get_datatype_info(); + + /*! + * Queries the total transfer count so far between host and device + * \param transfer_count Receives the value from total transfer count register + * \return status + */ nirio_status _get_transfer_count(uint64_t& transfer_count); + + /*! + * Sleeps until expected transfer time has elapsed and checks total transfer count to see if transfer completed + * \param timeout_ms The amount of time (in ms) to wait for the elements to become available in the DMA buffer + * \return status + */ nirio_status _ensure_transfer_completed(uint32_t timeout_ms); + + /*! + * Conducts the low level operations to reserve DMA buffer space from RIO kernel driver + * \param elements_requested The minimum number of elements to acquire + * \param timeout_in_ms The amount of time (in ms) to wait for the elements to become available in the DMA buffer + * \param fifo_optimization_option FIFO acquire policy (MINIMIZE_LATENCY [fetch what was there last time] or MAXIMIZE_THROUGHPUT [fetch all available]) + * \param status status chaining variable + * \return Whether acquisition of requested elements (or more) was successful. + */ + bool _acquire_block_from_rio_buffer( + size_t elements_requested, + uint64_t timeout_in_ms, + const fifo_optimization_option_t fifo_optimization_option, + nirio_status& status); + private: //Members enum fifo_state_t { UNMAPPED, MAPPED, STARTED @@ -103,7 +220,8 @@ private: //Members uint32_t _fifo_channel; datatype_info_t _datatype_info; fifo_state_t _state; - size_t _acquired_pending; + size_t _remaining_in_claimed_block; + size_t _remaining_acquirable_elements; nirio_driver_iface::rio_mmap_t _mem_map; boost::recursive_mutex _mutex; niriok_proxy::sptr _riok_proxy_ptr; @@ -111,6 +229,12 @@ private: //Members uint64_t _expected_xfer_count; uint32_t _dma_base_addr; + data_t* _elements_buffer; + size_t _actual_depth_in_elements; + boost::atomic<size_t> _total_elements_acquired; + size_t _frame_size_in_elements; + fifo_optimization_option_t _fifo_optimization_option; + static const uint32_t FIFO_LOCK_TIMEOUT_IN_MS = 5000; }; diff --git a/host/include/uhd/transport/nirio/nirio_fifo.ipp b/host/include/uhd/transport/nirio/nirio_fifo.ipp index a7e2bb2c5..5669f259f 100644 --- a/host/include/uhd/transport/nirio/nirio_fifo.ipp +++ b/host/include/uhd/transport/nirio/nirio_fifo.ipp @@ -26,19 +26,25 @@ template <typename data_t> nirio_fifo<data_t>::nirio_fifo( niriok_proxy::sptr riok_proxy, - fifo_direction_t direction, + const fifo_direction_t direction, const std::string& name, - uint32_t fifo_instance) : + const uint32_t fifo_instance) : _name(name), _fifo_direction(direction), _fifo_channel(fifo_instance), _datatype_info(_get_datatype_info()), _state(UNMAPPED), - _acquired_pending(0), + _remaining_in_claimed_block(0), + _remaining_acquirable_elements(0), _mem_map(), _riok_proxy_ptr(riok_proxy), _expected_xfer_count(0), - _dma_base_addr(0) + _dma_base_addr(0), + _elements_buffer(NULL), + _actual_depth_in_elements(0), + _total_elements_acquired(0), + _frame_size_in_elements(0), + _fifo_optimization_option(MINIMIZE_LATENCY) { nirio_status status = 0; nirio_status_chain(_riok_proxy_ptr->set_attribute(RIO_ADDRESS_SPACE, BUS_INTERFACE), status); @@ -58,8 +64,10 @@ nirio_fifo<data_t>::~nirio_fifo() template <typename data_t> nirio_status nirio_fifo<data_t>::initialize( const size_t requested_depth, + const size_t frame_size_in_elements, size_t& actual_depth, - size_t& actual_size) + size_t& actual_size, + const fifo_optimization_option_t fifo_optimization_option) { nirio_status status = NiRio_Status_Success; if (!_riok_proxy_ptr) return NiRio_Status_ResourceNotInitialized; @@ -67,6 +75,9 @@ nirio_status nirio_fifo<data_t>::initialize( if (_state == UNMAPPED) { + _frame_size_in_elements = frame_size_in_elements; + _fifo_optimization_option = fifo_optimization_option; + uint32_t actual_depth_u32 = 0; uint32_t actual_size_u32 = 0; @@ -83,6 +94,7 @@ nirio_status nirio_fifo<data_t>::initialize( if (nirio_status_fatal(status)) return status; actual_depth = static_cast<size_t>(actual_depth_u32); + _actual_depth_in_elements = actual_depth; actual_size = static_cast<size_t>(actual_size_u32); status = _riok_proxy_ptr->map_fifo_memory(_fifo_channel, actual_size, _mem_map); @@ -110,6 +122,89 @@ void nirio_fifo<data_t>::finalize() } } + +template <typename data_t> +bool nirio_fifo<data_t>::_acquire_block_from_rio_buffer( + size_t elements_requested, + uint64_t timeout_in_ms, + const fifo_optimization_option_t fifo_optimization_option, + nirio_status& status) +{ + uint32_t elements_acquired_u32 = 0; + uint32_t elements_remaining_u32 = 0; + size_t elements_to_request = 0; + void* elements_buffer = NULL; + char context_buffer[64]; + + if (fifo_optimization_option == MAXIMIZE_THROUGHPUT) + { + // We'll maximize throughput by acquiring all the data that is available + // But this comes at the cost of an extra wait_on_fifo in which we query + // the total available by requesting 0. + + // first, see how many are available to acquire + // by trying to acquire 0 + nirio_status_chain(_riok_proxy_ptr->wait_on_fifo( + _fifo_channel, + 0, // elements requested + static_cast<uint32_t>(_datatype_info.scalar_type), + _datatype_info.width * 8, + 0, // timeout + _fifo_direction == OUTPUT_FIFO, + elements_buffer, + elements_acquired_u32, + elements_remaining_u32), + status); + + // acquire the maximum possible elements- all remaining + // yet limit to a multiple of the frame size + // (don't want to acquire partial frames) + elements_to_request = elements_remaining_u32 - (elements_remaining_u32 % _frame_size_in_elements); + + // the next call to wait_on_fifo can have a 0 timeout since we + // know there is at least as much data as we will request available + timeout_in_ms = 0; + } + else + { + // fifo_optimization_option == MINIMIZE_LATENCY + // acquire either the minimum amount (frame size) or the amount remaining from the last call + // (coerced to a multiple of frames) + elements_to_request = std::max( + elements_requested, + (_remaining_acquirable_elements - (_remaining_acquirable_elements % _frame_size_in_elements))); + } + + nirio_status_chain(_riok_proxy_ptr->wait_on_fifo( + _fifo_channel, + elements_to_request, + static_cast<uint32_t>(_datatype_info.scalar_type), + _datatype_info.width * 8, + timeout_in_ms, + _fifo_direction == OUTPUT_FIFO, + elements_buffer, + elements_acquired_u32, + elements_remaining_u32), + status); + + if (nirio_status_not_fatal(status)) + { + _remaining_acquirable_elements = static_cast<size_t>(elements_remaining_u32); + + if (elements_acquired_u32 > 0) + { + _total_elements_acquired += static_cast<size_t>(elements_acquired_u32); + _remaining_in_claimed_block = static_cast<size_t>(elements_acquired_u32); + _elements_buffer = static_cast<data_t*>(elements_buffer); + } + + return true; + } + + return false; +} + + template <typename data_t> nirio_status nirio_fifo<data_t>::start() { @@ -122,11 +217,24 @@ nirio_status nirio_fifo<data_t>::start() //Do nothing. Already started. } else if (_state == MAPPED) { + _total_elements_acquired = 0; + _remaining_in_claimed_block = 0; + _remaining_acquirable_elements = 0; + status = _riok_proxy_ptr->start_fifo(_fifo_channel); + if (nirio_status_not_fatal(status)) { _state = STARTED; - _acquired_pending = 0; _expected_xfer_count = 0; + + if (_fifo_direction == OUTPUT_FIFO) + { + // pre-acquire a block of data + // (should be entire DMA buffer at this point) + + // requesting 0 elements, but it will acquire all since MAXIMUM_THROUGHPUT + _acquire_block_from_rio_buffer(0, 1000, MAXIMIZE_THROUGHPUT, status); + } } } else { status = NiRio_Status_ResourceNotInitialized; @@ -143,7 +251,12 @@ nirio_status nirio_fifo<data_t>::stop() boost::unique_lock<boost::recursive_mutex> lock(_mutex); if (_state == STARTED) { - if (_acquired_pending > 0) release(_acquired_pending); + + // release any remaining acquired elements + if (_total_elements_acquired > 0) release(_total_elements_acquired); + _total_elements_acquired = 0; + _remaining_in_claimed_block = 0; + _remaining_acquirable_elements = 0; status = _riok_proxy_ptr->stop_fifo(_fifo_channel); @@ -167,34 +280,44 @@ nirio_status nirio_fifo<data_t>::acquire( boost::unique_lock<boost::recursive_mutex> lock(_mutex); if (_state == STARTED) { - uint32_t elements_acquired_u32 = 0; - uint32_t elements_remaining_u32 = 0; - void* elements_buffer = static_cast<void*>(elements); - status = _riok_proxy_ptr->wait_on_fifo( - _fifo_channel, - static_cast<uint32_t>(elements_requested), - static_cast<uint32_t>(_datatype_info.scalar_type), - _datatype_info.width * 8, - timeout, - _fifo_direction == OUTPUT_FIFO, - elements_buffer, - elements_acquired_u32, - elements_remaining_u32); - if (nirio_status_not_fatal(status)) { - elements = static_cast<data_t*>(elements_buffer); - elements_acquired = static_cast<size_t>(elements_acquired_u32); - elements_remaining = static_cast<size_t>(elements_remaining_u32); - _acquired_pending = elements_acquired; - - if (UHD_NIRIO_RX_FIFO_XFER_CHECK_EN && - _riok_proxy_ptr->get_rio_quirks().rx_fifo_xfer_check_en() && - get_direction() == INPUT_FIFO - ) { + if (_remaining_in_claimed_block == 0) + { + + // so acquire some now + if (!_acquire_block_from_rio_buffer( + elements_requested, + timeout, + _fifo_optimization_option, + status)) + { + elements_acquired = 0; + elements_remaining = _remaining_acquirable_elements; + return status; + } + + + if (get_direction() == INPUT_FIFO && + UHD_NIRIO_RX_FIFO_XFER_CHECK_EN && + _riok_proxy_ptr->get_rio_quirks().rx_fifo_xfer_check_en()) + { _expected_xfer_count += static_cast<uint64_t>(elements_requested * sizeof(data_t)); status = _ensure_transfer_completed(timeout); } } + + if (nirio_status_not_fatal(status)) + { + // Assign the request the proper area of the DMA FIFO buffer + elements = _elements_buffer; + elements_acquired = std::min(_remaining_in_claimed_block, elements_requested); + _remaining_in_claimed_block -= elements_acquired; + elements_remaining = _remaining_in_claimed_block + _remaining_acquirable_elements; + + // Advance the write pointer forward in the acquired elements buffer + _elements_buffer += elements_acquired; + } + } else { status = NiRio_Status_ResourceNotInitialized; } @@ -214,7 +337,7 @@ nirio_status nirio_fifo<data_t>::release(const size_t elements) status = _riok_proxy_ptr->grant_fifo( _fifo_channel, static_cast<uint32_t>(elements)); - _acquired_pending = 0; + _total_elements_acquired -= elements; } else { status = NiRio_Status_ResourceNotInitialized; } @@ -226,7 +349,7 @@ template <typename data_t> nirio_status nirio_fifo<data_t>::read( data_t* buf, const uint32_t num_elements, - uint32_t timeout, + const uint32_t timeout, uint32_t& num_read, uint32_t& num_remaining) { @@ -257,7 +380,7 @@ template <typename data_t> nirio_status nirio_fifo<data_t>::write( const data_t* buf, const uint32_t num_elements, - uint32_t timeout, + const uint32_t timeout, uint32_t& num_remaining) { nirio_status status = NiRio_Status_Success; diff --git a/host/lib/transport/nirio/niriok_proxy_impl_v2.cpp b/host/lib/transport/nirio/niriok_proxy_impl_v2.cpp index f75de01e1..4e88b8a6e 100644 --- a/host/lib/transport/nirio/niriok_proxy_impl_v2.cpp +++ b/host/lib/transport/nirio/niriok_proxy_impl_v2.cpp @@ -759,14 +759,14 @@ namespace uhd { namespace niusrprio in.timeout = timeout; in.status = NiRio_Status_Success; - ioctl_status = + ioctl_status = nirio_driver_iface::rio_ioctl( - _device_handle, - IOCTL_TRANSPORT_FIFO_WAIT, - &in, - sizeof(in), - &out, - sizeof(out)); + _device_handle, + IOCTL_TRANSPORT_FIFO_WAIT, + &in, + sizeof(in), + &out, + sizeof(out)); if (nirio_status_fatal(ioctl_status)) return ioctl_status; data_pointer = reinterpret_cast<void*>(out.elements); diff --git a/host/lib/transport/nirio_zero_copy.cpp b/host/lib/transport/nirio_zero_copy.cpp index 8bec49a5f..9ed02a6dc 100644 --- a/host/lib/transport/nirio_zero_copy.cpp +++ b/host/lib/transport/nirio_zero_copy.cpp @@ -182,11 +182,13 @@ public: nirio_status_chain( _recv_fifo->initialize( (_xport_params.recv_frame_size*_xport_params.num_recv_frames)/sizeof(fifo_data_t), + _xport_params.recv_frame_size / sizeof(fifo_data_t), actual_depth, actual_size), status); nirio_status_chain( _send_fifo->initialize( (_xport_params.send_frame_size*_xport_params.num_send_frames)/sizeof(fifo_data_t), + _xport_params.send_frame_size / sizeof(fifo_data_t), actual_depth, actual_size), status); |