diff options
32 files changed, 462 insertions, 156 deletions
diff --git a/host/LICENSE b/host/LICENSE index 9aa03b39b..b91233b22 100644 --- a/host/LICENSE +++ b/host/LICENSE @@ -1,3 +1,7 @@ +This LICENSE file applies only to this directory and all subdirectories. Other +top-level directories in the UHD(tm) Software distribution are not necessarily +covered by this license. + 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 diff --git a/host/examples/benchmark_rate.cpp b/host/examples/benchmark_rate.cpp index ea49d48d9..9e9aa67e9 100644 --- a/host/examples/benchmark_rate.cpp +++ b/host/examples/benchmark_rate.cpp @@ -98,7 +98,7 @@ void benchmark_rx_rate(uhd::usrp::multi_usrp::sptr usrp, const std::string &rx_c break; default: - std::cerr << "Error code: " << md.error_code << std::endl; + std::cerr << "Receiver error: " << md.strerror() << std::endl; std::cerr << "Unexpected error on recv, continuing..." << std::endl; break; } diff --git a/host/examples/rx_multi_samples.cpp b/host/examples/rx_multi_samples.cpp index 9e5970978..a50b5f0e0 100644 --- a/host/examples/rx_multi_samples.cpp +++ b/host/examples/rx_multi_samples.cpp @@ -172,8 +172,8 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ if (md.error_code == uhd::rx_metadata_t::ERROR_CODE_TIMEOUT) break; if (md.error_code != uhd::rx_metadata_t::ERROR_CODE_NONE){ throw std::runtime_error(str(boost::format( - "Unexpected error code 0x%x" - ) % md.error_code)); + "Receiver error %s" + ) % md.strerror())); } if(verbose) std::cout << boost::format( diff --git a/host/examples/rx_samples_to_file.cpp b/host/examples/rx_samples_to_file.cpp index 0d42404d3..de3640794 100644 --- a/host/examples/rx_samples_to_file.cpp +++ b/host/examples/rx_samples_to_file.cpp @@ -101,18 +101,15 @@ template<typename samp_type> void recv_to_file( continue; } if (md.error_code != uhd::rx_metadata_t::ERROR_CODE_NONE){ - std::string error = str(boost::format( - "Unexpected error code 0x%x" - ) % md.error_code); - - if (continue_on_bad_packet){ - std::cerr << error << std::endl; - continue; - } - else - throw std::runtime_error(error); + std::string error = str(boost::format("Receiver error: %s") % md.strerror()); + if (continue_on_bad_packet){ + std::cerr << error << std::endl; + continue; + } + else + throw std::runtime_error(error); } - + if (enable_size_map){ SizeMap::iterator it = mapSizes.find(num_rx_samps); if (it == mapSizes.end()) diff --git a/host/examples/rx_timed_samples.cpp b/host/examples/rx_timed_samples.cpp index cc9216cb7..30535907f 100644 --- a/host/examples/rx_timed_samples.cpp +++ b/host/examples/rx_timed_samples.cpp @@ -130,8 +130,8 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ if (md.error_code == uhd::rx_metadata_t::ERROR_CODE_TIMEOUT) break; if (md.error_code != uhd::rx_metadata_t::ERROR_CODE_NONE){ throw std::runtime_error(str(boost::format( - "Unexpected error code 0x%x" - ) % md.error_code)); + "Receiver error %s" + ) % md.strerror())); } if(verbose) std::cout << boost::format( diff --git a/host/examples/test_timed_commands.cpp b/host/examples/test_timed_commands.cpp index 8c6011c68..3da4bc707 100644 --- a/host/examples/test_timed_commands.cpp +++ b/host/examples/test_timed_commands.cpp @@ -139,8 +139,8 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ const size_t num_rx_samps = rx_stream->recv(&buff.front(), buff.size(), md, 1.0); if (md.error_code != uhd::rx_metadata_t::ERROR_CODE_NONE){ throw std::runtime_error(str(boost::format( - "Unexpected error code 0x%x" - ) % md.error_code)); + "Receiver error %s" + ) % md.strerror())); } std::cout << boost::format( " Received packet: %u samples, %u full secs, %f frac secs" diff --git a/host/examples/transport_hammer.cpp b/host/examples/transport_hammer.cpp index 4b949e5bd..3f233b2a5 100644 --- a/host/examples/transport_hammer.cpp +++ b/host/examples/transport_hammer.cpp @@ -88,7 +88,7 @@ void rx_hammer(uhd::usrp::multi_usrp::sptr usrp, const std::string &rx_cpu, uhd: break; default: - std::cerr << "Error code: " << md.error_code << std::endl; + std::cerr << "Receiver error: " << md.strerror() << std::endl; std::cerr << "Unexpected error on recv, continuing..." << std::endl; break; } diff --git a/host/examples/txrx_loopback_to_file.cpp b/host/examples/txrx_loopback_to_file.cpp index 3d3cf1dfc..a62ccd7b2 100644 --- a/host/examples/txrx_loopback_to_file.cpp +++ b/host/examples/txrx_loopback_to_file.cpp @@ -181,8 +181,8 @@ template<typename samp_type> void recv_to_file( } if (md.error_code != uhd::rx_metadata_t::ERROR_CODE_NONE){ throw std::runtime_error(str(boost::format( - "Unexpected error code 0x%x" - ) % md.error_code)); + "Receiver error %s" + ) % md.strerror())); } num_total_samps += num_rx_samps; diff --git a/host/include/uhd/transport/nirio/nirio_fifo.h b/host/include/uhd/transport/nirio/nirio_fifo.h index f7abb396f..fc1de245d 100644 --- a/host/include/uhd/transport/nirio/nirio_fifo.h +++ b/host/include/uhd/transport/nirio/nirio_fifo.h @@ -104,16 +104,20 @@ public: uint32_t& num_remaining); private: //Methods - bool _is_initialized(); datatype_info_t _get_datatype_info(); nirio_status _get_transfer_count(uint64_t& transfer_count); nirio_status _ensure_transfer_completed(uint32_t timeout_ms); private: //Members + enum fifo_state_t { + UNMAPPED, MAPPED, STARTED + }; + std::string _name; fifo_direction_t _fifo_direction; uint32_t _fifo_channel; datatype_info_t _datatype_info; + fifo_state_t _state; size_t _acquired_pending; nirio_driver_iface::rio_mmap_t _mem_map; boost::recursive_mutex _mutex; diff --git a/host/include/uhd/transport/nirio/nirio_fifo.ipp b/host/include/uhd/transport/nirio/nirio_fifo.ipp index 80a0c2a89..437e3a1fc 100644 --- a/host/include/uhd/transport/nirio/nirio_fifo.ipp +++ b/host/include/uhd/transport/nirio/nirio_fifo.ipp @@ -31,6 +31,7 @@ nirio_fifo<data_t>::nirio_fifo( _fifo_direction(direction), _fifo_channel(fifo_instance), _datatype_info(_get_datatype_info()), + _state(UNMAPPED), _acquired_pending(0), _mem_map(), _riok_proxy_ptr(&riok_proxy), @@ -61,28 +62,37 @@ nirio_status nirio_fifo<data_t>::initialize( if (!_riok_proxy_ptr) return NiRio_Status_ResourceNotInitialized; boost::unique_lock<boost::recursive_mutex> lock(_mutex); - nirio_driver_iface::nirio_syncop_in_params_t in = {}; - nirio_driver_iface::nirio_syncop_out_params_t out = {}; + if (_state == UNMAPPED) { + nirio_driver_iface::nirio_syncop_in_params_t in = {}; + nirio_driver_iface::nirio_syncop_out_params_t out = {}; - //Forcefully stop the fifo if it is running - in.function = nirio_driver_iface::NIRIO_FUNC::FIFO; - in.subfunction = nirio_driver_iface::NIRIO_FIFO::STOP; - status = _riok_proxy_ptr->sync_operation(&in, sizeof(in), &out, sizeof(out)); + //Forcefully stop the fifo if it is running + in.function = nirio_driver_iface::NIRIO_FUNC::FIFO; + in.subfunction = nirio_driver_iface::NIRIO_FIFO::STOP; + in.params.fifo.channel = _fifo_channel; + _riok_proxy_ptr->sync_operation(&in, sizeof(in), &out, sizeof(out)); //Cleanup operation. Ignore status. - in.function = nirio_driver_iface::NIRIO_FUNC::FIFO; - in.subfunction = nirio_driver_iface::NIRIO_FIFO::CONFIGURE; + //Configure the FIFO now that we know it is stopped + in.function = nirio_driver_iface::NIRIO_FUNC::FIFO; + in.subfunction = nirio_driver_iface::NIRIO_FIFO::CONFIGURE; + in.params.fifo.channel = _fifo_channel; + in.params.fifo.op.config.requestedDepth = static_cast<uint32_t>(requested_depth); + in.params.fifo.op.config.requiresActuals = 1; + status = _riok_proxy_ptr->sync_operation(&in, sizeof(in), &out, sizeof(out)); - in.params.fifo.channel = _fifo_channel; - in.params.fifo.op.config.requestedDepth = static_cast<uint32_t>(requested_depth); - in.params.fifo.op.config.requiresActuals = 1; + if (nirio_status_fatal(status)) return status; - status = _riok_proxy_ptr->sync_operation(&in, sizeof(in), &out, sizeof(out)); - if (nirio_status_fatal(status)) return status; + actual_depth = out.params.fifo.op.config.actualDepth; + actual_size = out.params.fifo.op.config.actualSize; - actual_depth = out.params.fifo.op.config.actualDepth; - actual_size = out.params.fifo.op.config.actualSize; + status = _riok_proxy_ptr->map_fifo_memory(_fifo_channel, actual_size, _mem_map); - status = _riok_proxy_ptr->map_fifo_memory(_fifo_channel, actual_size, _mem_map); + if (nirio_status_not_fatal(status)) { + _state = MAPPED; + } + } else { + status = NiRio_Status_SoftwareFault; + } return status; } @@ -90,9 +100,13 @@ template <typename data_t> void nirio_fifo<data_t>::finalize() { boost::unique_lock<boost::recursive_mutex> lock(_mutex); - if (!_mem_map.is_null()) { - stop(); + + //If the FIFO is started, the stop will change the state to MAPPED. + stop(); + + if (_state == MAPPED) { _riok_proxy_ptr->unmap_fifo_memory(_mem_map); + _state = UNMAPPED; //Assume teardown succeeded } } @@ -104,16 +118,25 @@ nirio_status nirio_fifo<data_t>::start() boost::unique_lock<boost::recursive_mutex> lock(_mutex); - nirio_driver_iface::nirio_syncop_in_params_t in = {}; - nirio_driver_iface::nirio_syncop_out_params_t out = {}; + if (_state == STARTED) { + //Do nothing. Already started. + } else if (_state == MAPPED) { + nirio_driver_iface::nirio_syncop_in_params_t in = {}; + nirio_driver_iface::nirio_syncop_out_params_t out = {}; - in.function = nirio_driver_iface::NIRIO_FUNC::FIFO; - in.subfunction = nirio_driver_iface::NIRIO_FIFO::START; + in.function = nirio_driver_iface::NIRIO_FUNC::FIFO; + in.subfunction = nirio_driver_iface::NIRIO_FIFO::START; - status = _riok_proxy_ptr->sync_operation(&in, sizeof(in), &out, sizeof(out)); - if (nirio_status_not_fatal(status)) { - _acquired_pending = 0; - _expected_xfer_count = 0; + in.params.fifo.channel = _fifo_channel; + + status = _riok_proxy_ptr->sync_operation(&in, sizeof(in), &out, sizeof(out)); + if (nirio_status_not_fatal(status)) { + _state = STARTED; + _acquired_pending = 0; + _expected_xfer_count = 0; + } + } else { + status = NiRio_Status_ResourceNotInitialized; } return status; } @@ -125,15 +148,22 @@ nirio_status nirio_fifo<data_t>::stop() if (!_riok_proxy_ptr) return NiRio_Status_ResourceNotInitialized; boost::unique_lock<boost::recursive_mutex> lock(_mutex); - if (_acquired_pending > 0) release(_acquired_pending); - nirio_driver_iface::nirio_syncop_in_params_t in = {}; - nirio_driver_iface::nirio_syncop_out_params_t out = {}; + if (_state == STARTED) { + if (_acquired_pending > 0) release(_acquired_pending); + + nirio_driver_iface::nirio_syncop_in_params_t in = {}; + nirio_driver_iface::nirio_syncop_out_params_t out = {}; - in.function = nirio_driver_iface::NIRIO_FUNC::FIFO; - in.subfunction = nirio_driver_iface::NIRIO_FIFO::STOP; + in.function = nirio_driver_iface::NIRIO_FUNC::FIFO; + in.subfunction = nirio_driver_iface::NIRIO_FIFO::STOP; - status = _riok_proxy_ptr->sync_operation(&in, sizeof(in), &out, sizeof(out)); + in.params.fifo.channel = _fifo_channel; + + status = _riok_proxy_ptr->sync_operation(&in, sizeof(in), &out, sizeof(out)); + + _state = MAPPED; //Assume teardown succeeded + } return status; } @@ -151,36 +181,40 @@ nirio_status nirio_fifo<data_t>::acquire( boost::unique_lock<boost::recursive_mutex> lock(_mutex); - nirio_driver_iface::nirio_syncop_in_params_t in = {}; - uint32_t stuffed[2]; - nirio_driver_iface::nirio_syncop_out_params_t out = {}; - init_syncop_out_params(out, stuffed, sizeof(stuffed)); - - in.function = nirio_driver_iface::NIRIO_FUNC::FIFO; - in.subfunction = nirio_driver_iface::NIRIO_FIFO::WAIT; - - in.params.fifo.channel = _fifo_channel; - in.params.fifo.op.wait.elementsRequested = static_cast<uint32_t>(elements_requested); - in.params.fifo.op.wait.scalarType = static_cast<uint32_t>(_datatype_info.scalar_type); - in.params.fifo.op.wait.bitWidth = _datatype_info.width * 8; - in.params.fifo.op.wait.output = _fifo_direction == OUTPUT_FIFO; - in.params.fifo.op.wait.timeout = timeout; - - status = _riok_proxy_ptr->sync_operation(&in, sizeof(in), &out, sizeof(out)); - - if (nirio_status_not_fatal(status)) { - elements = static_cast<data_t*>(out.params.fifo.op.wait.elements.pointer); - elements_acquired = stuffed[0]; - elements_remaining = stuffed[1]; - _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 - ) { - _expected_xfer_count += static_cast<uint64_t>(elements_requested * sizeof(data_t)); - status = _ensure_transfer_completed(timeout); + if (_state == STARTED) { + nirio_driver_iface::nirio_syncop_in_params_t in = {}; + uint32_t stuffed[2]; + nirio_driver_iface::nirio_syncop_out_params_t out = {}; + init_syncop_out_params(out, stuffed, sizeof(stuffed)); + + in.function = nirio_driver_iface::NIRIO_FUNC::FIFO; + in.subfunction = nirio_driver_iface::NIRIO_FIFO::WAIT; + + in.params.fifo.channel = _fifo_channel; + in.params.fifo.op.wait.elementsRequested = static_cast<uint32_t>(elements_requested); + in.params.fifo.op.wait.scalarType = static_cast<uint32_t>(_datatype_info.scalar_type); + in.params.fifo.op.wait.bitWidth = _datatype_info.width * 8; + in.params.fifo.op.wait.output = _fifo_direction == OUTPUT_FIFO; + in.params.fifo.op.wait.timeout = timeout; + + status = _riok_proxy_ptr->sync_operation(&in, sizeof(in), &out, sizeof(out)); + + if (nirio_status_not_fatal(status)) { + elements = static_cast<data_t*>(out.params.fifo.op.wait.elements.pointer); + elements_acquired = stuffed[0]; + elements_remaining = stuffed[1]; + _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 + ) { + _expected_xfer_count += static_cast<uint64_t>(elements_requested * sizeof(data_t)); + status = _ensure_transfer_completed(timeout); + } } + } else { + status = NiRio_Status_ResourceNotInitialized; } return status; @@ -194,17 +228,21 @@ nirio_status nirio_fifo<data_t>::release(const size_t elements) boost::unique_lock<boost::recursive_mutex> lock(_mutex); - nirio_driver_iface::nirio_syncop_in_params_t in = {}; - nirio_driver_iface::nirio_syncop_out_params_t out = {}; + if (_state == STARTED) { + nirio_driver_iface::nirio_syncop_in_params_t in = {}; + nirio_driver_iface::nirio_syncop_out_params_t out = {}; - in.function = nirio_driver_iface::NIRIO_FUNC::FIFO; - in.subfunction = nirio_driver_iface::NIRIO_FIFO::GRANT; + in.function = nirio_driver_iface::NIRIO_FUNC::FIFO; + in.subfunction = nirio_driver_iface::NIRIO_FIFO::GRANT; - in.params.fifo.channel = _fifo_channel; - in.params.fifo.op.grant.elements = static_cast<uint32_t>(elements); + in.params.fifo.channel = _fifo_channel; + in.params.fifo.op.grant.elements = static_cast<uint32_t>(elements); - status = _riok_proxy_ptr->sync_operation(&in, sizeof(in), &out, sizeof(out)); - _acquired_pending = 0; + status = _riok_proxy_ptr->sync_operation(&in, sizeof(in), &out, sizeof(out)); + _acquired_pending = 0; + } else { + status = NiRio_Status_ResourceNotInitialized; + } return status; } @@ -222,23 +260,27 @@ nirio_status nirio_fifo<data_t>::read( boost::unique_lock<boost::recursive_mutex> lock(_mutex); - nirio_driver_iface::nirio_syncop_in_params_t in = {}; - nirio_driver_iface::nirio_syncop_out_params_t out = {}; - init_syncop_out_params(out, buf, num_elements * _datatype_info.width); + if (_state == STARTED) { + nirio_driver_iface::nirio_syncop_in_params_t in = {}; + nirio_driver_iface::nirio_syncop_out_params_t out = {}; + init_syncop_out_params(out, buf, num_elements * _datatype_info.width); - in.function = nirio_driver_iface::NIRIO_FUNC::FIFO; - in.subfunction = nirio_driver_iface::NIRIO_FIFO::READ; + in.function = nirio_driver_iface::NIRIO_FUNC::FIFO; + in.subfunction = nirio_driver_iface::NIRIO_FIFO::READ; - in.params.fifo.channel = _fifo_channel; - in.params.fifo.op.readWithDataType.timeout = timeout; - in.params.fifo.op.readWithDataType.scalarType = static_cast<uint32_t>(_datatype_info.scalar_type); - in.params.fifo.op.readWithDataType.bitWidth = _datatype_info.width * 8; + in.params.fifo.channel = _fifo_channel; + in.params.fifo.op.readWithDataType.timeout = timeout; + in.params.fifo.op.readWithDataType.scalarType = static_cast<uint32_t>(_datatype_info.scalar_type); + in.params.fifo.op.readWithDataType.bitWidth = _datatype_info.width * 8; - status = _riok_proxy_ptr->sync_operation(&in, sizeof(in), &out, sizeof(out)); + status = _riok_proxy_ptr->sync_operation(&in, sizeof(in), &out, sizeof(out)); - if (nirio_status_not_fatal(status) || status == NiRio_Status_FifoTimeout) { - num_read = out.params.fifo.op.read.numberRead; - num_remaining = out.params.fifo.op.read.numberRemaining; + if (nirio_status_not_fatal(status) || status == NiRio_Status_FifoTimeout) { + num_read = out.params.fifo.op.read.numberRead; + num_remaining = out.params.fifo.op.read.numberRemaining; + } + } else { + status = NiRio_Status_ResourceNotInitialized; } return status; @@ -256,22 +298,26 @@ nirio_status nirio_fifo<data_t>::write( boost::unique_lock<boost::recursive_mutex> lock(_mutex); - nirio_driver_iface::nirio_syncop_in_params_t in = {}; - init_syncop_in_params(in, buf, num_elements * _datatype_info.width); - nirio_driver_iface::nirio_syncop_out_params_t out = {}; + if (_state == STARTED) { + nirio_driver_iface::nirio_syncop_in_params_t in = {}; + init_syncop_in_params(in, buf, num_elements * _datatype_info.width); + nirio_driver_iface::nirio_syncop_out_params_t out = {}; - in.function = nirio_driver_iface::NIRIO_FUNC::FIFO; - in.subfunction = nirio_driver_iface::NIRIO_FIFO::WRITE; + in.function = nirio_driver_iface::NIRIO_FUNC::FIFO; + in.subfunction = nirio_driver_iface::NIRIO_FIFO::WRITE; - in.params.fifo.channel = _fifo_channel; - in.params.fifo.op.writeWithDataType.timeout = timeout; - in.params.fifo.op.readWithDataType.scalarType = static_cast<uint32_t>(_datatype_info.scalar_type); - in.params.fifo.op.readWithDataType.bitWidth = _datatype_info.width * 8; + in.params.fifo.channel = _fifo_channel; + in.params.fifo.op.writeWithDataType.timeout = timeout; + in.params.fifo.op.readWithDataType.scalarType = static_cast<uint32_t>(_datatype_info.scalar_type); + in.params.fifo.op.readWithDataType.bitWidth = _datatype_info.width * 8; - status = _riok_proxy_ptr->sync_operation(&in, sizeof(in), &out, sizeof(out)); + status = _riok_proxy_ptr->sync_operation(&in, sizeof(in), &out, sizeof(out)); - if (nirio_status_not_fatal(status) || status == NiRio_Status_FifoTimeout) { - num_remaining = out.params.fifo.op.write.numberRemaining; + if (nirio_status_not_fatal(status) || status == NiRio_Status_FifoTimeout) { + num_remaining = out.params.fifo.op.write.numberRemaining; + } + } else { + status = NiRio_Status_ResourceNotInitialized; } return status; diff --git a/host/include/uhd/transport/nirio/nirio_quirks.h b/host/include/uhd/transport/nirio/nirio_quirks.h index 326eeeb8c..ed4f72e7f 100644 --- a/host/include/uhd/transport/nirio/nirio_quirks.h +++ b/host/include/uhd/transport/nirio/nirio_quirks.h @@ -24,8 +24,8 @@ //Quirk#1: We need to verify RX zero-copy data transfers from the RIO // driver if we are in full duplex mode. -// This option allows disabling this quirk by compiling it out. -#define UHD_NIRIO_RX_FIFO_XFER_CHECK_EN 1 +// This option allows enabling this quirk. +#define UHD_NIRIO_RX_FIFO_XFER_CHECK_EN 0 namespace uhd { namespace niusrprio { diff --git a/host/include/uhd/types/metadata.hpp b/host/include/uhd/types/metadata.hpp index 6a79720d0..51a2b7c43 100644 --- a/host/include/uhd/types/metadata.hpp +++ b/host/include/uhd/types/metadata.hpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2012 Ettus Research LLC +// Copyright 2010-2012,2014 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -117,6 +117,20 @@ namespace uhd{ //! Out of sequence. The transport has either dropped a packet or received data out of order. bool out_of_sequence; + + /*! + * Convert a rx_metadata_t into a pretty print string. + * + * \param compact Set to false for a more verbose output. + * \return a printable string representing the metadata. + */ + std::string to_pp_string(bool compact=true) const; + + /*! + * Similar to C's strerror() function, creates a std::string describing the error code. + * \return a printable string representing the error. + */ + std::string strerror(void) const; }; /*! diff --git a/host/include/uhd/usrp/multi_usrp.hpp b/host/include/uhd/usrp/multi_usrp.hpp index aac40efe5..883e4da3d 100644 --- a/host/include/uhd/usrp/multi_usrp.hpp +++ b/host/include/uhd/usrp/multi_usrp.hpp @@ -119,14 +119,10 @@ public: virtual device::sptr get_device(void) = 0; //! Convenience method to get a RX streamer. See also uhd::device::get_rx_stream(). - rx_streamer::sptr get_rx_stream(const stream_args_t &args){ - return this->get_device()->get_rx_stream(args); - } + virtual rx_streamer::sptr get_rx_stream(const stream_args_t &args) = 0; //! Convenience method to get a TX streamer. See also uhd::device::get_rx_stream(). - tx_streamer::sptr get_tx_stream(const stream_args_t &args){ - return this->get_device()->get_tx_stream(args); - } + virtual tx_streamer::sptr get_tx_stream(const stream_args_t &args) = 0; /*! * Returns identifying information about this USRP's configuration. diff --git a/host/include/uhd/utils/CMakeLists.txt b/host/include/uhd/utils/CMakeLists.txt index c0991b3ce..e9633286f 100644 --- a/host/include/uhd/utils/CMakeLists.txt +++ b/host/include/uhd/utils/CMakeLists.txt @@ -22,6 +22,7 @@ UHD_INSTALL(FILES atomic.hpp byteswap.hpp byteswap.ipp + cast.hpp csv.hpp gain_group.hpp images.hpp diff --git a/host/include/uhd/utils/cast.hpp b/host/include/uhd/utils/cast.hpp new file mode 100644 index 000000000..9db92c526 --- /dev/null +++ b/host/include/uhd/utils/cast.hpp @@ -0,0 +1,43 @@ +// +// Copyright 2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_UHD_UTILS_CAST_HPP +#define INCLUDED_UHD_UTILS_CAST_HPP + +#include <uhd/config.hpp> +#include <string> +#include <sstream> + +namespace uhd{ namespace cast{ + //! Convert a hexadecimal string into a value. + // + // Example: + // boost::uint16_t x = hexstr_cast<boost::uint16_t>("0xDEADBEEF"); + // Uses stringstream. + template<typename T> inline T hexstr_cast(const std::string &in) + { + T x; + std::stringstream ss; + ss << std::hex << in; + ss >> x; + return x; + } + +}} //namespace uhd::cast + +#endif /* INCLUDED_UHD_UTILS_CAST_HPP */ + diff --git a/host/lib/convert/convert_impl.cpp b/host/lib/convert/convert_impl.cpp index dc7f8f9dc..c7907ed83 100644 --- a/host/lib/convert/convert_impl.cpp +++ b/host/lib/convert/convert_impl.cpp @@ -134,6 +134,7 @@ UHD_STATIC_BLOCK(convert_register_item_sizes){ convert::register_bytes_per_item("sc64", sizeof(std::complex<boost::int64_t>)); convert::register_bytes_per_item("sc32", sizeof(std::complex<boost::int32_t>)); convert::register_bytes_per_item("sc16", sizeof(std::complex<boost::int16_t>)); + convert::register_bytes_per_item("sc12", 3 * sizeof(std::complex<boost::int8_t>)); convert::register_bytes_per_item("sc8", sizeof(std::complex<boost::int8_t>)); //register standard real types diff --git a/host/lib/types/CMakeLists.txt b/host/lib/types/CMakeLists.txt index b69c8e487..7fc6bdd94 100644 --- a/host/lib/types/CMakeLists.txt +++ b/host/lib/types/CMakeLists.txt @@ -82,6 +82,7 @@ SET_SOURCE_FILES_PROPERTIES( LIBUHD_APPEND_SOURCES( ${CMAKE_CURRENT_SOURCE_DIR}/device_addr.cpp ${CMAKE_CURRENT_SOURCE_DIR}/mac_addr.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/metadata.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ranges.cpp ${CMAKE_CURRENT_SOURCE_DIR}/sensors.cpp ${CMAKE_CURRENT_SOURCE_DIR}/serial.cpp diff --git a/host/lib/types/metadata.cpp b/host/lib/types/metadata.cpp new file mode 100644 index 000000000..fec2ac564 --- /dev/null +++ b/host/lib/types/metadata.cpp @@ -0,0 +1,92 @@ +// +// Copyright 2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <string> +#include <sstream> +#include <boost/format.hpp> +#include <uhd/exception.hpp> +#include <uhd/types/metadata.hpp> +#include <uhd/types/time_spec.hpp> + +using namespace uhd; + +std::string rx_metadata_t::to_pp_string(bool compact) const +{ + std::stringstream ss; + + if (compact) { + if (has_time_spec) { + ss << "Time: " << time_spec.get_real_secs() << " s\n"; + } + if (more_fragments) { + ss << "Fragmentation offset: " << fragment_offset << "\n"; + } + if (start_of_burst) { + ss << "Start of burst.\n" << fragment_offset; + } + if (end_of_burst) { + ss << "End of burst.\n" << fragment_offset; + } + if (error_code != ERROR_CODE_NONE) { + ss << strerror() << "\n"; + } + } else { + ss << "Has timespec: " << (has_time_spec ? "Yes" : "No") + << "\tTime of first sample: " << time_spec.get_real_secs() + << "\nFragmented: " << (more_fragments ? "Yes" : "No") + << " Fragmentation offset: " << fragment_offset + << "\nStart of burst: " << (start_of_burst ? "Yes" : "No") + << "\tEnd of burst: " << (end_of_burst ? "Yes" : "No") + << "\nError Code: " << strerror() + << "\tOut of sequence: " << (out_of_sequence ? "Yes" : "No"); + } + + return ss.str(); +} + +std::string rx_metadata_t::strerror() const +{ + std::string errstr = ""; + switch(this->error_code) { + case ERROR_CODE_NONE: + errstr = "ERROR_CODE_NONE"; + break; + case ERROR_CODE_TIMEOUT: + errstr = "ERROR_CODE_TIMEOUT"; + break; + case ERROR_CODE_LATE_COMMAND: + errstr = "ERROR_CODE_LATE_COMMAND"; + break; + case ERROR_CODE_BROKEN_CHAIN: + errstr = "ERROR_CODE_BROKEN_CHAIN (Expected another stream command)"; + break; + case ERROR_CODE_OVERFLOW: + errstr = "ERROR_CODE_OVERFLOW "; + errstr += (this->out_of_sequence ? "(Out of sequence error)" : "(Overflow)"); + break; + case ERROR_CODE_ALIGNMENT: + errstr = "ERROR_CODE_ALIGNMENT (Multi-channel alignment failed)"; + break; + case ERROR_CODE_BAD_PACKET: + errstr = "ERROR_CODE_BAD_PACKET"; + break; + default: + errstr = std::string(str(boost::format("Unknown error code: 0x%x") % error_code)); + } + + return errstr; +} diff --git a/host/lib/usrp/b100/b100_impl.cpp b/host/lib/usrp/b100/b100_impl.cpp index a47856b07..baf2b6ae3 100644 --- a/host/lib/usrp/b100/b100_impl.cpp +++ b/host/lib/usrp/b100/b100_impl.cpp @@ -20,6 +20,7 @@ #include "b100_regs.hpp" #include <uhd/transport/usb_control.hpp> #include <uhd/utils/msg.hpp> +#include <uhd/utils/cast.hpp> #include <uhd/exception.hpp> #include <uhd/utils/static.hpp> #include <uhd/utils/images.hpp> @@ -56,11 +57,11 @@ static device_addrs_t b100_find(const device_addr_t &hint) //since an address and resource is intended for a different, non-USB, device. if (hint.has_key("addr") || hint.has_key("resource")) return b100_addrs; - unsigned int vid, pid; + boost::uint16_t vid, pid; if(hint.has_key("vid") && hint.has_key("pid") && hint.has_key("type") && hint["type"] == "b100") { - sscanf(hint.get("vid").c_str(), "%x", &vid); - sscanf(hint.get("pid").c_str(), "%x", &pid); + vid = uhd::cast::hexstr_cast<boost::uint16_t>(hint.get("vid")); + pid = uhd::cast::hexstr_cast<boost::uint16_t>(hint.get("pid")); } else { vid = B100_VENDOR_ID; pid = B100_PRODUCT_ID; @@ -515,6 +516,7 @@ b100_impl::b100_impl(const device_addr_t &device_addr){ _tree->access<subdev_spec_t>(mb_path / "tx_subdev_spec").set(subdev_spec_t("A:" + _tree->list(mb_path / "dboards/A/tx_frontends").at(0))); _tree->access<std::string>(mb_path / "clock_source/value").set("internal"); _tree->access<std::string>(mb_path / "time_source/value").set("none"); + _tree->create<double>(mb_path / "link_max_rate").set(B100_MAX_RATE_USB2); } b100_impl::~b100_impl(void){ diff --git a/host/lib/usrp/b100/b100_impl.hpp b/host/lib/usrp/b100/b100_impl.hpp index 7d71d5ec3..b6752681e 100644 --- a/host/lib/usrp/b100/b100_impl.hpp +++ b/host/lib/usrp/b100/b100_impl.hpp @@ -54,6 +54,7 @@ static const boost::uint32_t B100_CTRL_MSG_SID = 20; static const double B100_DEFAULT_TICK_RATE = 64e6; static const size_t B100_MAX_PKT_BYTE_LIMIT = 2048; static const std::string B100_EEPROM_MAP_KEY = "B100"; +static const size_t B100_MAX_RATE_USB2 = 32000000; // bytes/s #define I2C_ADDR_TX_A (I2C_DEV_EEPROM | 0x4) #define I2C_ADDR_RX_A (I2C_DEV_EEPROM | 0x5) diff --git a/host/lib/usrp/b200/b200_impl.cpp b/host/lib/usrp/b200/b200_impl.cpp index 9dd3a424d..98141dbaa 100644 --- a/host/lib/usrp/b200/b200_impl.cpp +++ b/host/lib/usrp/b200/b200_impl.cpp @@ -19,6 +19,7 @@ #include "b200_regs.hpp" #include <uhd/transport/usb_control.hpp> #include <uhd/utils/msg.hpp> +#include <uhd/utils/cast.hpp> #include <uhd/exception.hpp> #include <uhd/utils/static.hpp> #include <uhd/utils/images.hpp> @@ -58,11 +59,11 @@ static device_addrs_t b200_find(const device_addr_t &hint) //since an address and resource is intended for a different, non-USB, device. if (hint.has_key("addr") || hint.has_key("resource")) return b200_addrs; - unsigned int vid, pid; + boost::uint16_t vid, pid; if(hint.has_key("vid") && hint.has_key("pid") && hint.has_key("type") && hint["type"] == "b200") { - sscanf(hint.get("vid").c_str(), "%x", &vid); - sscanf(hint.get("pid").c_str(), "%x", &pid); + vid = uhd::cast::hexstr_cast<boost::uint16_t>(hint.get("vid")); + pid = uhd::cast::hexstr_cast<boost::uint16_t>(hint.get("pid")); } else { vid = B200_VENDOR_ID; pid = B200_PRODUCT_ID; @@ -160,9 +161,9 @@ b200_impl::b200_impl(const device_addr_t &device_addr) boost::uint16_t vid = B200_VENDOR_ID; boost::uint16_t pid = B200_PRODUCT_ID; if (device_addr.has_key("vid")) - sscanf(device_addr.get("vid").c_str(), "%hx", &vid); + vid = uhd::cast::hexstr_cast<boost::uint16_t>(device_addr.get("vid")); if (device_addr.has_key("pid")) - sscanf(device_addr.get("pid").c_str(), "%hx", &pid); + pid = uhd::cast::hexstr_cast<boost::uint16_t>(device_addr.get("pid")); std::vector<usb_device_handle::sptr> device_list = usb_device_handle::get_device_list(vid, pid); @@ -250,6 +251,7 @@ b200_impl::b200_impl(const device_addr_t &device_addr) ctrl_xport_args ); while (_ctrl_transport->get_recv_buff(0.0)){} //flush ctrl xport + _tree->create<double>(mb_path / "link_max_rate").set((usb_speed == 3) ? B200_MAX_RATE_USB3 : B200_MAX_RATE_USB2); //////////////////////////////////////////////////////////////////// // Async task structure diff --git a/host/lib/usrp/b200/b200_impl.hpp b/host/lib/usrp/b200/b200_impl.hpp index 7d98a8f8d..c3508c550 100644 --- a/host/lib/usrp/b200/b200_impl.hpp +++ b/host/lib/usrp/b200/b200_impl.hpp @@ -47,11 +47,13 @@ static const boost::uint8_t B200_FW_COMPAT_NUM_MAJOR = 0x04; static const boost::uint8_t B200_FW_COMPAT_NUM_MINOR = 0x00; static const boost::uint16_t B200_FPGA_COMPAT_NUM = 0x03; -static const double B200_LINK_RATE_BPS = (5e9)/8; //practical link rate (5 Gbps) static const double B200_BUS_CLOCK_RATE = 100e6; static const double B200_DEFAULT_TICK_RATE = 32e6; static const boost::uint32_t B200_GPSDO_ST_NONE = 0x83; +static const size_t B200_MAX_RATE_USB2 = 32000000; // bytes/s +static const size_t B200_MAX_RATE_USB3 = 500000000; // bytes/s + #define FLIP_SID(sid) (((sid)<<16)|((sid)>>16)) static const boost::uint32_t B200_CTRL0_MSG_SID = 0x00000010; diff --git a/host/lib/usrp/multi_usrp.cpp b/host/lib/usrp/multi_usrp.cpp index f08709669..4883b2410 100644 --- a/host/lib/usrp/multi_usrp.cpp +++ b/host/lib/usrp/multi_usrp.cpp @@ -24,6 +24,7 @@ #include <uhd/usrp/dboard_id.hpp> #include <uhd/usrp/mboard_eeprom.hpp> #include <uhd/usrp/dboard_eeprom.hpp> +#include <uhd/convert.hpp> #include <boost/assign/list_of.hpp> #include <boost/thread.hpp> #include <boost/foreach.hpp> @@ -103,6 +104,8 @@ static meta_range_t make_overall_tune_range( return range; } + + /*********************************************************************** * Gain helper functions **********************************************************************/ @@ -589,6 +592,11 @@ public: /******************************************************************* * RX methods ******************************************************************/ + rx_streamer::sptr get_rx_stream(const stream_args_t &args) { + _check_link_rate(args, false); + return this->get_device()->get_rx_stream(args); + } + void set_rx_subdev_spec(const subdev_spec_t &spec, size_t mboard){ if (mboard != ALL_MBOARDS){ _tree->access<subdev_spec_t>(mb_root(mboard) / "rx_subdev_spec").set(spec); @@ -770,6 +778,11 @@ public: /******************************************************************* * TX methods ******************************************************************/ + tx_streamer::sptr get_tx_stream(const stream_args_t &args) { + _check_link_rate(args, true); + return this->get_device()->get_tx_stream(args); + } + void set_tx_subdev_spec(const subdev_spec_t &spec, size_t mboard){ if (mboard != ALL_MBOARDS){ _tree->access<subdev_spec_t>(mb_root(mboard) / "tx_subdev_spec").set(spec); @@ -1178,6 +1191,34 @@ private: } return gg; } + + //! \param is_tx True for tx + // Assumption is that all mboards use the same link + bool _check_link_rate(const stream_args_t &args, bool is_tx) { + bool link_rate_is_ok = true; + size_t bytes_per_sample = convert::get_bytes_per_item(args.otw_format); + double max_link_rate = 0; + double sum_rate = 0; + BOOST_FOREACH(const size_t chan, args.channels) { + mboard_chan_pair mcp = is_tx ? tx_chan_to_mcp(chan) : rx_chan_to_mcp(chan); + if (_tree->exists(mb_root(mcp.mboard) / "link_max_rate")) { + max_link_rate = std::max( + max_link_rate, + _tree->access<double>(mb_root(mcp.mboard) / "link_max_rate").get() + ); + } + sum_rate += is_tx ? get_tx_rate(chan) : get_rx_rate(chan); + } + if (max_link_rate > 0 and (max_link_rate / bytes_per_sample) < sum_rate) { + UHD_MSG(warning) << boost::format( + "The total sum of rates (%f MSps on %u channels) exceeds the maximum capacity of the connection.\n" + "This can cause %s." + ) % (sum_rate/1e6) % args.channels.size() % (is_tx ? "underruns (U)" : "overflows (O)") << std::endl; + link_rate_is_ok = false; + } + + return link_rate_is_ok; + } }; /*********************************************************************** diff --git a/host/lib/usrp/usrp1/usrp1_impl.cpp b/host/lib/usrp/usrp1/usrp1_impl.cpp index 3b902b343..0ba2e1e4a 100644 --- a/host/lib/usrp/usrp1/usrp1_impl.cpp +++ b/host/lib/usrp/usrp1/usrp1_impl.cpp @@ -20,6 +20,7 @@ #include <uhd/utils/safe_call.hpp> #include <uhd/transport/usb_control.hpp> #include <uhd/utils/msg.hpp> +#include <uhd/utils/cast.hpp> #include <uhd/exception.hpp> #include <uhd/utils/static.hpp> #include <uhd/utils/images.hpp> @@ -59,11 +60,11 @@ static device_addrs_t usrp1_find(const device_addr_t &hint) //since an address and resource is intended for a different, non-USB, device. if (hint.has_key("addr") || hint.has_key("resource")) return usrp1_addrs; - unsigned int vid, pid; + boost::uint16_t vid, pid; if(hint.has_key("vid") && hint.has_key("pid") && hint.has_key("type") && hint["type"] == "usrp1") { - sscanf(hint.get("vid").c_str(), "%x", &vid); - sscanf(hint.get("pid").c_str(), "%x", &pid); + vid = uhd::cast::hexstr_cast<boost::uint16_t>(hint.get("vid")); + pid = uhd::cast::hexstr_cast<boost::uint16_t>(hint.get("pid")); } else { vid = USRP1_VENDOR_ID; pid = USRP1_PRODUCT_ID; @@ -409,7 +410,7 @@ usrp1_impl::usrp1_impl(const device_addr_t &device_addr){ _tree->access<subdev_spec_t>(mb_path / "rx_subdev_spec").set(_rx_subdev_spec); if (_tree->list(mb_path / "tx_dsps").size() > 0) _tree->access<subdev_spec_t>(mb_path / "tx_subdev_spec").set(_tx_subdev_spec); - + _tree->create<double>(mb_path / "link_max_rate").set(USRP1_MAX_RATE_USB2); } usrp1_impl::~usrp1_impl(void){ diff --git a/host/lib/usrp/usrp1/usrp1_impl.hpp b/host/lib/usrp/usrp1/usrp1_impl.hpp index da9fe8b16..012bc0794 100644 --- a/host/lib/usrp/usrp1/usrp1_impl.hpp +++ b/host/lib/usrp/usrp1/usrp1_impl.hpp @@ -39,6 +39,7 @@ #define INCLUDED_USRP1_IMPL_HPP static const std::string USRP1_EEPROM_MAP_KEY = "B000"; +static const size_t USRP1_MAX_RATE_USB2 = 32000000; // bytes/s #define FR_RB_CAPS 3 #define FR_MODE 13 diff --git a/host/lib/usrp/usrp2/usrp2_impl.cpp b/host/lib/usrp/usrp2/usrp2_impl.cpp index 16d9b9a54..918f3e892 100644 --- a/host/lib/usrp/usrp2/usrp2_impl.cpp +++ b/host/lib/usrp/usrp2/usrp2_impl.cpp @@ -442,6 +442,7 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr){ _mbc[mb].spiface = _mbc[mb].iface; break; } + _tree->create<double>(mb_path / "link_max_rate").set(USRP2_LINK_RATE_BPS); //////////////////////////////////////////////////////////////// // setup the mboard eeprom @@ -655,12 +656,14 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr){ .subscribe(boost::bind(&time64_core_200::set_time_next_pps, _mbc[mb].time64, _1)); //setup time source props _tree->create<std::string>(mb_path / "time_source/value") - .subscribe(boost::bind(&time64_core_200::set_time_source, _mbc[mb].time64, _1)); + .subscribe(boost::bind(&time64_core_200::set_time_source, _mbc[mb].time64, _1)) + .set("none"); _tree->create<std::vector<std::string> >(mb_path / "time_source/options") .publish(boost::bind(&time64_core_200::get_time_sources, _mbc[mb].time64)); //setup reference source props _tree->create<std::string>(mb_path / "clock_source/value") - .subscribe(boost::bind(&usrp2_impl::update_clock_source, this, mb, _1)); + .subscribe(boost::bind(&usrp2_impl::update_clock_source, this, mb, _1)) + .set("internal"); std::vector<std::string> clock_sources = boost::assign::list_of("internal")("external")("mimo"); if (_mbc[mb].gps and _mbc[mb].gps->gps_detected()) clock_sources.push_back("gpsdo"); _tree->create<std::vector<std::string> >(mb_path / "clock_source/options").set(clock_sources); diff --git a/host/lib/usrp/x300/x300_impl.cpp b/host/lib/usrp/x300/x300_impl.cpp index e492b2238..f5e53678c 100644 --- a/host/lib/usrp/x300/x300_impl.cpp +++ b/host/lib/usrp/x300/x300_impl.cpp @@ -392,6 +392,8 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr) //Tell the quirks object which FIFOs carry TX stream data const uint32_t tx_data_fifos[2] = {X300_RADIO_DEST_PREFIX_TX, X300_RADIO_DEST_PREFIX_TX + 3}; mb.rio_fpga_interface->get_kernel_proxy().get_rio_quirks().register_tx_streams(tx_data_fifos); + + _tree->create<double>(mb_path / "link_max_rate").set(X300_MAX_RATE_PCIE); } BOOST_FOREACH(const std::string &key, dev_addr.keys()) @@ -456,6 +458,8 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr) << "UHD will use the auto-detected max frame size for this connection." << std::endl; } + + _tree->create<double>(mb_path / "link_max_rate").set(X300_MAX_RATE_10GIGE); } //create basic communication @@ -1133,11 +1137,14 @@ x300_impl::both_xports_t x300_impl::make_transport( if (mb.loaded_fpga_image == "HGS") { if (mb.router_dst_here == X300_XB_DST_E0) { eth_data_rec_frame_size = X300_1GE_DATA_FRAME_MAX_SIZE; + _tree->access<double>("/mboards/"+boost::lexical_cast<std::string>(mb_index) / "link_max_rate").set(X300_MAX_RATE_1GIGE); } else if (mb.router_dst_here == X300_XB_DST_E1) { eth_data_rec_frame_size = X300_10GE_DATA_FRAME_MAX_SIZE; + _tree->access<double>("/mboards/"+boost::lexical_cast<std::string>(mb_index) / "link_max_rate").set(X300_MAX_RATE_10GIGE); } } else if (mb.loaded_fpga_image == "XGS") { - eth_data_rec_frame_size = X300_10GE_DATA_FRAME_MAX_SIZE; + eth_data_rec_frame_size = X300_10GE_DATA_FRAME_MAX_SIZE; + _tree->access<double>("/mboards/"+boost::lexical_cast<std::string>(mb_index) / "link_max_rate").set(X300_MAX_RATE_10GIGE); } if (eth_data_rec_frame_size == 0) { diff --git a/host/lib/usrp/x300/x300_impl.hpp b/host/lib/usrp/x300/x300_impl.hpp index 692427f31..4b3efc845 100644 --- a/host/lib/usrp/x300/x300_impl.hpp +++ b/host/lib/usrp/x300/x300_impl.hpp @@ -85,6 +85,10 @@ static const size_t X300_RX_MAX_HDR_LEN = // bytes + sizeof(uhd::transport::vrt::if_packet_info_t().sid) // SID + sizeof(uhd::transport::vrt::if_packet_info_t().tsf); // Timestamp +static const size_t X300_MAX_RATE_PCIE = 800000000; // bytes/s +static const size_t X300_MAX_RATE_10GIGE = 800000000; // bytes/s +static const size_t X300_MAX_RATE_1GIGE = 100000000; // bytes/s + #define X300_RADIO_DEST_PREFIX_TX 0 #define X300_RADIO_DEST_PREFIX_CTRL 1 #define X300_RADIO_DEST_PREFIX_RX 2 diff --git a/host/lib/usrp/x300/x300_io_impl.cpp b/host/lib/usrp/x300/x300_io_impl.cpp index 09ed1d705..9263c9b44 100644 --- a/host/lib/usrp/x300/x300_io_impl.cpp +++ b/host/lib/usrp/x300/x300_io_impl.cpp @@ -242,6 +242,8 @@ struct x300_tx_fc_guts_t boost::shared_ptr<x300_impl::async_md_type> old_async_queue; }; +#define X300_ASYNC_EVENT_CODE_FLOW_CTRL 0 + static size_t get_tx_flow_control_window(size_t frame_size, const device_addr_t& tx_args) { double hw_buff_size = tx_args.cast<double>("send_buff_size", X300_TX_HW_BUFF_SIZE); @@ -283,23 +285,28 @@ static void handle_tx_async_msgs(boost::shared_ptr<x300_tx_fc_guts_t> guts, zero return; } - //catch the flow control packets and react - if (endian_conv(packet_buff[if_packet_info.num_header_words32+0]) == 0) - { - const size_t seq = endian_conv(packet_buff[if_packet_info.num_header_words32+1]); - guts->seq_queue.push_with_haste(seq); - return; - } - //fill in the async metadata async_metadata_t metadata; load_metadata_from_buff( endian_conv, metadata, if_packet_info, packet_buff, clock->get_master_clock_rate(), guts->stream_channel); - guts->async_queue->push_with_pop_on_full(metadata); - metadata.channel = guts->device_channel; - guts->old_async_queue->push_with_pop_on_full(metadata); - standard_async_msg_prints(metadata); + + //The FC response and the burst ack are two indicators that the radio + //consumed packets. Use them to update the FC metadata + if (metadata.event_code == X300_ASYNC_EVENT_CODE_FLOW_CTRL or + metadata.event_code == async_metadata_t::EVENT_CODE_BURST_ACK + ) { + const size_t seq = metadata.user_payload[0]; + guts->seq_queue.push_with_pop_on_full(seq); + } + + //FC responses don't propagate up to the user so filter them here + if (metadata.event_code != X300_ASYNC_EVENT_CODE_FLOW_CTRL) { + guts->async_queue->push_with_pop_on_full(metadata); + metadata.channel = guts->device_channel; + guts->old_async_queue->push_with_pop_on_full(metadata); + standard_async_msg_prints(metadata); + } } static managed_send_buffer::sptr get_tx_buff_with_flowctrl( @@ -319,7 +326,9 @@ static managed_send_buffer::sptr get_tx_buff_with_flowctrl( } managed_send_buffer::sptr buff = xport->get_send_buff(timeout); - if (buff) guts->last_seq_out++; //update seq, this will actually be a send + if (buff) { + guts->last_seq_out++; //update seq, this will actually be a send + } return buff; } diff --git a/host/tests/CMakeLists.txt b/host/tests/CMakeLists.txt index 2a40d0050..7c4815004 100644 --- a/host/tests/CMakeLists.txt +++ b/host/tests/CMakeLists.txt @@ -28,6 +28,7 @@ SET(test_sources buffer_test.cpp byteswap_test.cpp convert_test.cpp + cast_test.cpp dict_test.cpp error_test.cpp gain_group_test.cpp diff --git a/host/tests/cast_test.cpp b/host/tests/cast_test.cpp new file mode 100644 index 000000000..6b8a4c527 --- /dev/null +++ b/host/tests/cast_test.cpp @@ -0,0 +1,33 @@ +// +// Copyright 2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <iostream> +#include <boost/test/unit_test.hpp> +#include <boost/cstdint.hpp> +#include <uhd/utils/cast.hpp> + +BOOST_AUTO_TEST_CASE(test_mac_addr){ + std::string in = "0x0100"; + boost::uint16_t correct_result = 256; + boost::uint16_t x = uhd::cast::hexstr_cast<boost::uint16_t>(in); + //boost::uint16_t x = uhd::cast::hexstr_cast(in); + std::cout + << "Testing hex -> uint16_t conversion. " + << in << " == " << std::hex << x << "?" << std::endl; + BOOST_CHECK_EQUAL(x, correct_result); +} + diff --git a/host/utils/usrp_cal_utils.hpp b/host/utils/usrp_cal_utils.hpp index 4f9c5224e..9e7f4c469 100644 --- a/host/utils/usrp_cal_utils.hpp +++ b/host/utils/usrp_cal_utils.hpp @@ -257,8 +257,8 @@ static void capture_samples( //validate the received data if (md.error_code != uhd::rx_metadata_t::ERROR_CODE_NONE){ throw std::runtime_error(str(boost::format( - "Unexpected error code 0x%x" - ) % md.error_code)); + "Receiver error: %s" + ) % md.strerror())); } //we can live if all the data didnt come in if (num_rx_samps > buff.size()/2){ |