diff options
Diffstat (limited to 'host')
59 files changed, 1268 insertions, 394 deletions
diff --git a/host/examples/benchmark_rate.cpp b/host/examples/benchmark_rate.cpp index fce184514..8f00e25de 100644 --- a/host/examples/benchmark_rate.cpp +++ b/host/examples/benchmark_rate.cpp @@ -40,11 +40,11 @@ unsigned long long num_seq_errors = 0; /*********************************************************************** * Benchmark RX Rate **********************************************************************/ -void benchmark_rx_rate(uhd::usrp::multi_usrp::sptr usrp){ +void benchmark_rx_rate(uhd::usrp::multi_usrp::sptr usrp, const std::string &rx_otw){ uhd::set_thread_priority_safe(); //create a receive streamer - uhd::stream_args_t stream_args("fc32"); //complex floats + uhd::stream_args_t stream_args("fc32", rx_otw); //complex floats uhd::rx_streamer::sptr rx_stream = usrp->get_rx_stream(stream_args); //print pre-test summary @@ -94,11 +94,11 @@ void benchmark_rx_rate(uhd::usrp::multi_usrp::sptr usrp){ /*********************************************************************** * Benchmark TX Rate **********************************************************************/ -void benchmark_tx_rate(uhd::usrp::multi_usrp::sptr usrp){ +void benchmark_tx_rate(uhd::usrp::multi_usrp::sptr usrp, const std::string &tx_otw){ uhd::set_thread_priority_safe(); //create a transmit streamer - uhd::stream_args_t stream_args("fc32"); //complex floats + uhd::stream_args_t stream_args("fc32", tx_otw); //complex floats uhd::tx_streamer::sptr tx_stream = usrp->get_tx_stream(stream_args); //print pre-test summary @@ -162,6 +162,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ std::string args; double duration; double rx_rate, tx_rate; + std::string rx_otw, tx_otw; //setup the program options po::options_description desc("Allowed options"); @@ -171,6 +172,8 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ ("duration", po::value<double>(&duration)->default_value(10.0), "duration for the test in seconds") ("rx_rate", po::value<double>(&rx_rate), "specify to perform a RX rate test (sps)") ("tx_rate", po::value<double>(&tx_rate), "specify to perform a TX rate test (sps)") + ("rx_otw", po::value<std::string>(&rx_otw)->default_value("sc16"), "specify the over-the-wire sample mode for RX") + ("tx_otw", po::value<std::string>(&tx_otw)->default_value("sc16"), "specify the over-the-wire sample mode for TX") ; po::variables_map vm; po::store(po::parse_command_line(argc, argv, desc), vm); @@ -203,13 +206,13 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ //spawn the receive test thread if (vm.count("rx_rate")){ usrp->set_rx_rate(rx_rate); - thread_group.create_thread(boost::bind(&benchmark_rx_rate, usrp)); + thread_group.create_thread(boost::bind(&benchmark_rx_rate, usrp, rx_otw)); } //spawn the transmit test thread if (vm.count("tx_rate")){ usrp->set_tx_rate(tx_rate); - thread_group.create_thread(boost::bind(&benchmark_tx_rate, usrp)); + thread_group.create_thread(boost::bind(&benchmark_tx_rate, usrp, tx_otw)); thread_group.create_thread(boost::bind(&benchmark_tx_rate_async_helper, usrp)); } diff --git a/host/examples/test_messages.cpp b/host/examples/test_messages.cpp index f24a172d1..afb092092 100644 --- a/host/examples/test_messages.cpp +++ b/host/examples/test_messages.cpp @@ -26,6 +26,7 @@ #include <boost/bind.hpp> #include <boost/format.hpp> #include <cstdlib> +#include <ctime> #include <complex> #include <iostream> @@ -326,7 +327,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ } //run the tests, pick at random - std::srand(uhd::time_spec_t::get_system_time().get_full_secs()); + std::srand((unsigned int) time(NULL)); for (size_t n = 0; n < ntests; n++){ std::string key = tests.keys()[std::rand() % tests.size()]; bool pass = tests[key](usrp, rx_stream, tx_stream); diff --git a/host/examples/tx_waveforms.cpp b/host/examples/tx_waveforms.cpp index 39f70aec6..6a377fdac 100644 --- a/host/examples/tx_waveforms.cpp +++ b/host/examples/tx_waveforms.cpp @@ -90,7 +90,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ uhd::set_thread_priority_safe(); //variables to be set by po - std::string args, wave_type, ant, subdev, ref; + std::string args, wave_type, ant, subdev, ref, otw; size_t spb; double rate, freq, gain, wave_freq, bw; float ampl; @@ -111,6 +111,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ ("wave-type", po::value<std::string>(&wave_type)->default_value("CONST"), "waveform type (CONST, SQUARE, RAMP, SINE)") ("wave-freq", po::value<double>(&wave_freq)->default_value(0), "waveform frequency in Hz") ("ref", po::value<std::string>(&ref)->default_value("internal"), "clock reference (internal, external, mimo)") + ("otw", po::value<std::string>(&otw)->default_value("sc16"), "specify the over-the-wire sample mode") ; po::variables_map vm; po::store(po::parse_command_line(argc, argv, desc), vm); @@ -193,7 +194,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ //create a transmit streamer //linearly map channels (index0 = channel0, index1 = channel1, ...) - uhd::stream_args_t stream_args("fc32"); + uhd::stream_args_t stream_args("fc32", otw); for (size_t chan = 0; chan < usrp->get_tx_num_channels(); chan++) stream_args.channels.push_back(chan); //linear mapping uhd::tx_streamer::sptr tx_stream = usrp->get_tx_stream(stream_args); diff --git a/host/include/uhd/stream.hpp b/host/include/uhd/stream.hpp index 352f63e4e..cec2eee79 100644 --- a/host/include/uhd/stream.hpp +++ b/host/include/uhd/stream.hpp @@ -79,9 +79,11 @@ struct UHD_API stream_args_t{ * The args parameter is used to pass arbitrary key/value pairs. * Possible keys used by args (depends on implementation): * - * - scalar: an integer scaling factor used with the sc8 wire format. - * The key/value pair scalar=1024 means that the sample in the DSP - * was multiplied by 1024 before its upper 8 bits were harvested. + * - peak: specifies a fractional sample level to calculate scaling with the sc8 wire format. + * When using sc8 samples over the wire, the device must scale samples + * (both on the host and in the device) to satisfy the dynamic range needs. + * The peak value specifies a fraction of the maximum sample level (1.0 = 100%). + * Set peak to max_sample_level/full_scale_level to ensure optimum dynamic range. * * - underflow_policy: how the TX DSP should recover from underflow. * Possible options are "next_burst" or "next_packet". diff --git a/host/include/uhd/types/metadata.hpp b/host/include/uhd/types/metadata.hpp index 269c77c7c..788999900 100644 --- a/host/include/uhd/types/metadata.hpp +++ b/host/include/uhd/types/metadata.hpp @@ -1,5 +1,5 @@ // -// Copyright 2010 Ettus Research LLC +// Copyright 2010-2012 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 @@ -20,6 +20,7 @@ #include <uhd/config.hpp> #include <uhd/types/time_spec.hpp> +#include <boost/cstdint.hpp> namespace uhd{ @@ -140,13 +141,21 @@ namespace uhd{ EVENT_CODE_UNDERFLOW = 0x2, //! Packet loss between host and device. EVENT_CODE_SEQ_ERROR = 0x4, - //! Packet had time that was late (or too early). + //! Packet had time that was late. EVENT_CODE_TIME_ERROR = 0x8, //! Underflow occurred inside a packet. EVENT_CODE_UNDERFLOW_IN_PACKET = 0x10, //! Packet loss within a burst. - EVENT_CODE_SEQ_ERROR_IN_BURST = 0x20 + EVENT_CODE_SEQ_ERROR_IN_BURST = 0x20, + //! Some kind of custom user payload + EVENT_CODE_USER_PAYLOAD = 0x40 } event_code; + + /*! + * A special payload populated by custom FPGA fabric. + */ + boost::uint32_t user_payload[4]; + }; } //namespace uhd diff --git a/host/include/uhd/types/time_spec.hpp b/host/include/uhd/types/time_spec.hpp index 02de20ea1..e7d7d5ab4 100644 --- a/host/include/uhd/types/time_spec.hpp +++ b/host/include/uhd/types/time_spec.hpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-2012 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 @@ -60,7 +60,7 @@ namespace uhd{ time_spec_t(time_t full_secs, double frac_secs = 0); /*! - * Create a time_spec_t from whole and fractional seconds. + * Create a time_spec_t from whole seconds and fractional ticks. * Translation from clock-domain specific units. * \param full_secs the whole/integer seconds count * \param tick_count the fractional seconds tick count @@ -69,6 +69,14 @@ namespace uhd{ time_spec_t(time_t full_secs, long tick_count, double tick_rate); /*! + * Create a time_spec_t from a 64-bit tick count. + * Translation from clock-domain specific units. + * \param ticks an integer count of ticks + * \param tick_rate the number of ticks per second + */ + static time_spec_t from_ticks(long long ticks, double tick_rate); + + /*! * Convert the fractional seconds to clock ticks. * Translation into clock-domain specific units. * \param tick_rate the number of ticks per second @@ -77,6 +85,14 @@ namespace uhd{ long get_tick_count(double tick_rate) const; /*! + * Convert the time spec into a 64-bit tick count. + * Translation into clock-domain specific units. + * \param tick_rate the number of ticks per second + * \return an integer number of ticks + */ + long long to_ticks(const double tick_rate) const; + + /*! * Get the time as a real-valued seconds count. * Note: If this time_spec_t represents an absolute time, * the precision of the fractional seconds may be lost. @@ -112,6 +128,14 @@ namespace uhd{ //! Implement less_than_comparable interface UHD_API bool operator<(const time_spec_t &, const time_spec_t &); + UHD_INLINE time_t time_spec_t::get_full_secs(void) const{ + return this->_full_secs; + } + + UHD_INLINE double time_spec_t::get_frac_secs(void) const{ + return this->_frac_secs; + } + } //namespace uhd #endif /* INCLUDED_UHD_TYPES_TIME_SPEC_HPP */ diff --git a/host/include/uhd/usrp/multi_usrp.hpp b/host/include/uhd/usrp/multi_usrp.hpp index 49354f1af..88affa40c 100644 --- a/host/include/uhd/usrp/multi_usrp.hpp +++ b/host/include/uhd/usrp/multi_usrp.hpp @@ -24,6 +24,7 @@ #define UHD_USRP_MULTI_USRP_FRONTEND_CAL_API #define UHD_USRP_MULTI_USRP_COMMAND_TIME_API #define UHD_USRP_MULTI_USRP_BW_RANGE_API +#define UHD_USRP_MULTI_USRP_USER_REGS_API #include <uhd/config.hpp> #include <uhd/device.hpp> @@ -338,6 +339,15 @@ public: */ virtual std::vector<std::string> get_mboard_sensor_names(size_t mboard = 0) = 0; + /*! + * Perform write on the user configuration register bus. These only exist if + * the user has implemented custom setting registers in the device FPGA. + * \param addr 8-bit register address + * \param data 32-bit register value + * \param mboard which motherboard to set the user register + */ + virtual void set_user_register(const boost::uint8_t addr, const boost::uint32_t data, size_t mboard = ALL_MBOARDS) = 0; + /******************************************************************* * RX methods ******************************************************************/ diff --git a/host/lib/convert/CMakeLists.txt b/host/lib/convert/CMakeLists.txt index 98907dc29..c42a0a434 100644 --- a/host/lib/convert/CMakeLists.txt +++ b/host/lib/convert/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2011 Ettus Research LLC +# Copyright 2011-2012 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 @@ -73,6 +73,8 @@ IF(HAVE_EMMINTRIN_H) SET(convert_with_sse2_sources ${CMAKE_CURRENT_SOURCE_DIR}/convert_fc32_with_sse2.cpp ${CMAKE_CURRENT_SOURCE_DIR}/convert_fc64_with_sse2.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/convert_fc32_to_sc8_with_sse2.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/convert_fc64_to_sc8_with_sse2.cpp ) SET_SOURCE_FILES_PROPERTIES( ${convert_with_sse2_sources} diff --git a/host/lib/convert/convert_common.hpp b/host/lib/convert/convert_common.hpp index 699d6301b..55bc2e99d 100644 --- a/host/lib/convert/convert_common.hpp +++ b/host/lib/convert/convert_common.hpp @@ -1,5 +1,5 @@ // -// Copyright 2011 Ettus Research LLC +// Copyright 2011-2012 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 @@ -190,4 +190,52 @@ static UHD_INLINE void item32_sc8_to_fc64(item32_t item, fc64_t &out0, fc64_t &o ); } +/*********************************************************************** + * Convert complex char to items32 sc8 buffer + **********************************************************************/ +static UHD_INLINE item32_t sc8_to_item32_sc8(sc8_t in0, sc8_t in1, double){ + return + (item32_t(boost::uint8_t(in0.real())) << 8) | + (item32_t(boost::uint8_t(in0.imag())) << 0) | + (item32_t(boost::uint8_t(in1.real())) << 24) | + (item32_t(boost::uint8_t(in1.imag())) << 16) + ; +} + +/*********************************************************************** + * Convert complex short to items32 sc8 buffer + **********************************************************************/ +static UHD_INLINE item32_t sc16_to_item32_sc8(sc16_t in0, sc16_t in1, double){ + return + (item32_t(boost::uint8_t(in0.real())) << 8) | + (item32_t(boost::uint8_t(in0.imag())) << 0) | + (item32_t(boost::uint8_t(in1.real())) << 24) | + (item32_t(boost::uint8_t(in1.imag())) << 16) + ; +} + +/*********************************************************************** + * Convert complex float to items32 sc8 buffer + **********************************************************************/ +static UHD_INLINE item32_t fc32_to_item32_sc8(fc32_t in0, fc32_t in1, double scale_factor){ + return + (item32_t(boost::uint8_t(in0.real()*float(scale_factor))) << 8) | + (item32_t(boost::uint8_t(in0.imag()*float(scale_factor))) << 0) | + (item32_t(boost::uint8_t(in1.real()*float(scale_factor))) << 24) | + (item32_t(boost::uint8_t(in1.imag()*float(scale_factor))) << 16) + ; +} + +/*********************************************************************** + * Convert complex double to items32 sc8 buffer + **********************************************************************/ +static UHD_INLINE item32_t fc64_to_item32_sc8(fc64_t in0, fc64_t in1, double scale_factor){ + return + (item32_t(boost::uint8_t(in0.real()*(scale_factor))) << 8) | + (item32_t(boost::uint8_t(in0.imag()*(scale_factor))) << 0) | + (item32_t(boost::uint8_t(in1.real()*(scale_factor))) << 24) | + (item32_t(boost::uint8_t(in1.imag()*(scale_factor))) << 16) + ; +} + #endif /* INCLUDED_LIBUHD_CONVERT_COMMON_HPP */ diff --git a/host/lib/convert/convert_fc32_to_sc8_with_sse2.cpp b/host/lib/convert/convert_fc32_to_sc8_with_sse2.cpp new file mode 100644 index 000000000..b633f487c --- /dev/null +++ b/host/lib/convert/convert_fc32_to_sc8_with_sse2.cpp @@ -0,0 +1,150 @@ +// +// Copyright 2012 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 "convert_common.hpp" +#include <uhd/utils/byteswap.hpp> +#include <emmintrin.h> + +using namespace uhd::convert; + +UHD_INLINE __m128i pack_sc32_4x_be( + const __m128 &in0, const __m128 &in1, + const __m128 &in2, const __m128 &in3, + const __m128 &scalar +){ + __m128i tmpi0 = _mm_cvtps_epi32(_mm_mul_ps(in0, scalar)); + tmpi0 = _mm_shuffle_epi32(tmpi0, _MM_SHUFFLE(1, 0, 3, 2)); + __m128i tmpi1 = _mm_cvtps_epi32(_mm_mul_ps(in1, scalar)); + tmpi1 = _mm_shuffle_epi32(tmpi1, _MM_SHUFFLE(1, 0, 3, 2)); + const __m128i lo = _mm_packs_epi32(tmpi0, tmpi1); + + __m128i tmpi2 = _mm_cvtps_epi32(_mm_mul_ps(in2, scalar)); + tmpi2 = _mm_shuffle_epi32(tmpi2, _MM_SHUFFLE(1, 0, 3, 2)); + __m128i tmpi3 = _mm_cvtps_epi32(_mm_mul_ps(in3, scalar)); + tmpi3 = _mm_shuffle_epi32(tmpi3, _MM_SHUFFLE(1, 0, 3, 2)); + const __m128i hi = _mm_packs_epi32(tmpi2, tmpi3); + + return _mm_packs_epi16(lo, hi); +} + +UHD_INLINE __m128i pack_sc32_4x_le( + const __m128 &in0, const __m128 &in1, + const __m128 &in2, const __m128 &in3, + const __m128 &scalar +){ + __m128i tmpi0 = _mm_cvtps_epi32(_mm_mul_ps(in0, scalar)); + tmpi0 = _mm_shuffle_epi32(tmpi0, _MM_SHUFFLE(2, 3, 0, 1)); + __m128i tmpi1 = _mm_cvtps_epi32(_mm_mul_ps(in1, scalar)); + tmpi1 = _mm_shuffle_epi32(tmpi1, _MM_SHUFFLE(2, 3, 0, 1)); + const __m128i lo = _mm_packs_epi32(tmpi0, tmpi1); + + __m128i tmpi2 = _mm_cvtps_epi32(_mm_mul_ps(in2, scalar)); + tmpi2 = _mm_shuffle_epi32(tmpi2, _MM_SHUFFLE(2, 3, 0, 1)); + __m128i tmpi3 = _mm_cvtps_epi32(_mm_mul_ps(in3, scalar)); + tmpi3 = _mm_shuffle_epi32(tmpi3, _MM_SHUFFLE(2, 3, 0, 1)); + const __m128i hi = _mm_packs_epi32(tmpi2, tmpi3); + + return _mm_packs_epi16(lo, hi); +} + +DECLARE_CONVERTER(fc32, 1, sc8_item32_be, 1, PRIORITY_SIMD){ + const fc32_t *input = reinterpret_cast<const fc32_t *>(inputs[0]); + item32_t *output = reinterpret_cast<item32_t *>(outputs[0]); + + const __m128 scalar = _mm_set_ps1(float(scale_factor)); + + #define convert_fc32_1_to_sc8_item32_1_bswap_guts(_al_) \ + for (size_t j = 0; i+7 < nsamps; i+=8, j+=4){ \ + /* load from input */ \ + __m128 tmp0 = _mm_load ## _al_ ## ps(reinterpret_cast<const float *>(input+i+0)); \ + __m128 tmp1 = _mm_load ## _al_ ## ps(reinterpret_cast<const float *>(input+i+2)); \ + __m128 tmp2 = _mm_load ## _al_ ## ps(reinterpret_cast<const float *>(input+i+4)); \ + __m128 tmp3 = _mm_load ## _al_ ## ps(reinterpret_cast<const float *>(input+i+6)); \ + \ + /* convert */ \ + const __m128i tmpi = pack_sc32_4x_be(tmp0, tmp1, tmp2, tmp3, scalar); \ + \ + /* store to output */ \ + _mm_storeu_si128(reinterpret_cast<__m128i *>(output+j), tmpi); \ + } \ + + size_t i = 0; + + //dispatch according to alignment + if ((size_t(input) & 0xf) == 0){ + convert_fc32_1_to_sc8_item32_1_bswap_guts(_) + } + else{ + convert_fc32_1_to_sc8_item32_1_bswap_guts(u_) + } + + //convert remainder + const size_t num_pairs = nsamps/2; + for (size_t j = i/2; j < num_pairs; j++, i+=2){ + const item32_t item = fc32_to_item32_sc8(input[i], input[i+1], scale_factor); + output[j] = uhd::byteswap(item); + } + + if (nsamps != num_pairs*2){ + const item32_t item = fc32_to_item32_sc8(input[nsamps-1], 0, scale_factor); + output[num_pairs] = uhd::byteswap(item); + } +} + +DECLARE_CONVERTER(fc32, 1, sc8_item32_le, 1, PRIORITY_SIMD){ + const fc32_t *input = reinterpret_cast<const fc32_t *>(inputs[0]); + item32_t *output = reinterpret_cast<item32_t *>(outputs[0]); + + const __m128 scalar = _mm_set_ps1(float(scale_factor)); + + #define convert_fc32_1_to_sc8_item32_1_nswap_guts(_al_) \ + for (size_t j = 0; i+7 < nsamps; i+=8, j+=4){ \ + /* load from input */ \ + __m128 tmp0 = _mm_load ## _al_ ## ps(reinterpret_cast<const float *>(input+i+0)); \ + __m128 tmp1 = _mm_load ## _al_ ## ps(reinterpret_cast<const float *>(input+i+2)); \ + __m128 tmp2 = _mm_load ## _al_ ## ps(reinterpret_cast<const float *>(input+i+4)); \ + __m128 tmp3 = _mm_load ## _al_ ## ps(reinterpret_cast<const float *>(input+i+6)); \ + \ + /* convert */ \ + const __m128i tmpi = pack_sc32_4x_le(tmp0, tmp1, tmp2, tmp3, scalar); \ + \ + /* store to output */ \ + _mm_storeu_si128(reinterpret_cast<__m128i *>(output+j), tmpi); \ + } \ + + size_t i = 0; + + //dispatch according to alignment + if ((size_t(input) & 0xf) == 0){ + convert_fc32_1_to_sc8_item32_1_nswap_guts(_) + } + else{ + convert_fc32_1_to_sc8_item32_1_nswap_guts(u_) + } + + //convert remainder + const size_t num_pairs = nsamps/2; + for (size_t j = i/2; j < num_pairs; j++, i+=2){ + const item32_t item = fc32_to_item32_sc8(input[i], input[i+1], scale_factor); + output[j] = (item); + } + + if (nsamps != num_pairs*2){ + const item32_t item = fc32_to_item32_sc8(input[nsamps-1], 0, scale_factor); + output[num_pairs] = (item); + } +} diff --git a/host/lib/convert/convert_fc32_with_sse2.cpp b/host/lib/convert/convert_fc32_with_sse2.cpp index 24a939d6c..97a3e8cdc 100644 --- a/host/lib/convert/convert_fc32_with_sse2.cpp +++ b/host/lib/convert/convert_fc32_with_sse2.cpp @@ -28,7 +28,7 @@ DECLARE_CONVERTER(fc32, 1, sc16_item32_le, 1, PRIORITY_SIMD){ const __m128 scalar = _mm_set_ps1(float(scale_factor)); #define convert_fc32_1_to_item32_1_nswap_guts(_al_) \ - for (; i+4 < nsamps; i+=4){ \ + for (; i+3 < nsamps; i+=4){ \ /* load from input */ \ __m128 tmplo = _mm_load ## _al_ ## ps(reinterpret_cast<const float *>(input+i+0)); \ __m128 tmphi = _mm_load ## _al_ ## ps(reinterpret_cast<const float *>(input+i+2)); \ @@ -71,7 +71,7 @@ DECLARE_CONVERTER(fc32, 1, sc16_item32_be, 1, PRIORITY_SIMD){ const __m128 scalar = _mm_set_ps1(float(scale_factor)); #define convert_fc32_1_to_item32_1_bswap_guts(_al_) \ - for (; i+4 < nsamps; i+=4){ \ + for (; i+3 < nsamps; i+=4){ \ /* load from input */ \ __m128 tmplo = _mm_load ## _al_ ## ps(reinterpret_cast<const float *>(input+i+0)); \ __m128 tmphi = _mm_load ## _al_ ## ps(reinterpret_cast<const float *>(input+i+2)); \ @@ -114,7 +114,7 @@ DECLARE_CONVERTER(sc16_item32_le, 1, fc32, 1, PRIORITY_SIMD){ const __m128i zeroi = _mm_setzero_si128(); #define convert_item32_1_to_fc32_1_nswap_guts(_al_) \ - for (; i+4 < nsamps; i+=4){ \ + for (; i+3 < nsamps; i+=4){ \ /* load from input */ \ __m128i tmpi = _mm_loadu_si128(reinterpret_cast<const __m128i *>(input+i)); \ \ @@ -159,7 +159,7 @@ DECLARE_CONVERTER(sc16_item32_be, 1, fc32, 1, PRIORITY_SIMD){ const __m128i zeroi = _mm_setzero_si128(); #define convert_item32_1_to_fc32_1_bswap_guts(_al_) \ - for (; i+4 < nsamps; i+=4){ \ + for (; i+3 < nsamps; i+=4){ \ /* load from input */ \ __m128i tmpi = _mm_loadu_si128(reinterpret_cast<const __m128i *>(input+i)); \ \ diff --git a/host/lib/convert/convert_fc64_to_sc8_with_sse2.cpp b/host/lib/convert/convert_fc64_to_sc8_with_sse2.cpp new file mode 100644 index 000000000..405850601 --- /dev/null +++ b/host/lib/convert/convert_fc64_to_sc8_with_sse2.cpp @@ -0,0 +1,156 @@ +// +// Copyright 2012 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 "convert_common.hpp" +#include <uhd/utils/byteswap.hpp> +#include <emmintrin.h> + +using namespace uhd::convert; + +UHD_INLINE __m128i pack_sc8_item32_4x( + const __m128i &in0, const __m128i &in1, + const __m128i &in2, const __m128i &in3 +){ + const __m128i lo = _mm_packs_epi32(in0, in1); + const __m128i hi = _mm_packs_epi32(in2, in3); + return _mm_packs_epi16(lo, hi); +} + +UHD_INLINE __m128i pack_sc32_4x_be( + const __m128d &lo, const __m128d &hi, + const __m128d &scalar +){ + const __m128i tmpi_lo = _mm_cvttpd_epi32(_mm_mul_pd(hi, scalar)); + const __m128i tmpi_hi = _mm_cvttpd_epi32(_mm_mul_pd(lo, scalar)); + return _mm_unpacklo_epi64(tmpi_lo, tmpi_hi); +} + +UHD_INLINE __m128i pack_sc32_4x_le( + const __m128d &lo, const __m128d &hi, + const __m128d &scalar +){ + const __m128i tmpi_lo = _mm_cvttpd_epi32(_mm_mul_pd(lo, scalar)); + const __m128i tmpi_hi = _mm_cvttpd_epi32(_mm_mul_pd(hi, scalar)); + const __m128i tmpi = _mm_unpacklo_epi64(tmpi_lo, tmpi_hi); + return _mm_shuffle_epi32(tmpi, _MM_SHUFFLE(2, 3, 0, 1)); +} + +DECLARE_CONVERTER(fc64, 1, sc8_item32_be, 1, PRIORITY_SIMD){ + const fc64_t *input = reinterpret_cast<const fc64_t *>(inputs[0]); + item32_t *output = reinterpret_cast<item32_t *>(outputs[0]); + + const __m128d scalar = _mm_set1_pd(scale_factor); + + #define convert_fc64_1_to_sc8_item32_1_bswap_guts(_al_) \ + for (size_t j = 0; i+7 < nsamps; i+=8, j+=4){ \ + /* load from input */ \ + __m128d tmp0 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+0)); \ + __m128d tmp1 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+1)); \ + __m128d tmp2 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+2)); \ + __m128d tmp3 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+3)); \ + __m128d tmp4 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+4)); \ + __m128d tmp5 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+5)); \ + __m128d tmp6 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+6)); \ + __m128d tmp7 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+7)); \ + \ + /* interleave */ \ + const __m128i tmpi = pack_sc8_item32_4x( \ + pack_sc32_4x_be(tmp0, tmp1, scalar), \ + pack_sc32_4x_be(tmp2, tmp3, scalar), \ + pack_sc32_4x_be(tmp4, tmp5, scalar), \ + pack_sc32_4x_be(tmp6, tmp7, scalar) \ + ); \ + \ + /* store to output */ \ + _mm_storeu_si128(reinterpret_cast<__m128i *>(output+j), tmpi); \ + } \ + + size_t i = 0; + + //dispatch according to alignment + if ((size_t(input) & 0xf) == 0){ + convert_fc64_1_to_sc8_item32_1_bswap_guts(_) + } + else{ + convert_fc64_1_to_sc8_item32_1_bswap_guts(u_) + } + + //convert remainder + const size_t num_pairs = nsamps/2; + for (size_t j = i/2; j < num_pairs; j++, i+=2){ + const item32_t item = fc64_to_item32_sc8(input[i], input[i+1], scale_factor); + output[j] = uhd::byteswap(item); + } + + if (nsamps != num_pairs*2){ + const item32_t item = fc64_to_item32_sc8(input[nsamps-1], 0, scale_factor); + output[num_pairs] = uhd::byteswap(item); + } +} + +DECLARE_CONVERTER(fc64, 1, sc8_item32_le, 1, PRIORITY_SIMD){ + const fc64_t *input = reinterpret_cast<const fc64_t *>(inputs[0]); + item32_t *output = reinterpret_cast<item32_t *>(outputs[0]); + + const __m128d scalar = _mm_set1_pd(scale_factor); + + #define convert_fc64_1_to_sc8_item32_1_nswap_guts(_al_) \ + for (size_t j = 0; i+7 < nsamps; i+=8, j+=4){ \ + /* load from input */ \ + __m128d tmp0 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+0)); \ + __m128d tmp1 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+1)); \ + __m128d tmp2 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+2)); \ + __m128d tmp3 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+3)); \ + __m128d tmp4 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+4)); \ + __m128d tmp5 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+5)); \ + __m128d tmp6 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+6)); \ + __m128d tmp7 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+7)); \ + \ + /* interleave */ \ + const __m128i tmpi = pack_sc8_item32_4x( \ + pack_sc32_4x_le(tmp0, tmp1, scalar), \ + pack_sc32_4x_le(tmp2, tmp3, scalar), \ + pack_sc32_4x_le(tmp4, tmp5, scalar), \ + pack_sc32_4x_le(tmp6, tmp7, scalar) \ + ); \ + \ + /* store to output */ \ + _mm_storeu_si128(reinterpret_cast<__m128i *>(output+j), tmpi); \ + } \ + + size_t i = 0; + + //dispatch according to alignment + if ((size_t(input) & 0xf) == 0){ + convert_fc64_1_to_sc8_item32_1_nswap_guts(_) + } + else{ + convert_fc64_1_to_sc8_item32_1_nswap_guts(u_) + } + + //convert remainder + const size_t num_pairs = nsamps/2; + for (size_t j = i/2; j < num_pairs; j++, i+=2){ + const item32_t item = fc64_to_item32_sc8(input[i], input[i+1], scale_factor); + output[j] = (item); + } + + if (nsamps != num_pairs*2){ + const item32_t item = fc64_to_item32_sc8(input[nsamps-1], 0, scale_factor); + output[num_pairs] = (item); + } +} diff --git a/host/lib/convert/convert_fc64_with_sse2.cpp b/host/lib/convert/convert_fc64_with_sse2.cpp index 837bb584e..6e097e380 100644 --- a/host/lib/convert/convert_fc64_with_sse2.cpp +++ b/host/lib/convert/convert_fc64_with_sse2.cpp @@ -28,7 +28,7 @@ DECLARE_CONVERTER(fc64, 1, sc16_item32_le, 1, PRIORITY_SIMD){ const __m128d scalar = _mm_set1_pd(scale_factor); #define convert_fc64_1_to_item32_1_nswap_guts(_al_) \ - for (; i+4 < nsamps; i+=4){ \ + for (; i+3 < nsamps; i+=4){ \ /* load from input */ \ __m128d tmp0 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+0)); \ __m128d tmp1 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+1)); \ @@ -75,7 +75,7 @@ DECLARE_CONVERTER(fc64, 1, sc16_item32_be, 1, PRIORITY_SIMD){ const __m128d scalar = _mm_set1_pd(scale_factor); #define convert_fc64_1_to_item32_1_bswap_guts(_al_) \ - for (; i+4 < nsamps; i+=4){ \ + for (; i+3 < nsamps; i+=4){ \ /* load from input */ \ __m128d tmp0 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+0)); \ __m128d tmp1 = _mm_load ## _al_ ## pd(reinterpret_cast<const double *>(input+i+1)); \ @@ -122,7 +122,7 @@ DECLARE_CONVERTER(sc16_item32_le, 1, fc64, 1, PRIORITY_SIMD){ const __m128i zeroi = _mm_setzero_si128(); #define convert_item32_1_to_fc64_1_nswap_guts(_al_) \ - for (; i+4 < nsamps; i+=4){ \ + for (; i+3 < nsamps; i+=4){ \ /* load from input */ \ __m128i tmpi = _mm_loadu_si128(reinterpret_cast<const __m128i *>(input+i)); \ \ @@ -171,7 +171,7 @@ DECLARE_CONVERTER(sc16_item32_be, 1, fc64, 1, PRIORITY_SIMD){ const __m128i zeroi = _mm_setzero_si128(); #define convert_item32_1_to_fc64_1_bswap_guts(_al_) \ - for (; i+4 < nsamps; i+=4){ \ + for (; i+3 < nsamps; i+=4){ \ /* load from input */ \ __m128i tmpi = _mm_loadu_si128(reinterpret_cast<const __m128i *>(input+i)); \ \ diff --git a/host/lib/convert/convert_orc.orc b/host/lib/convert/convert_orc.orc index 5450bf4db..f7075606e 100644 --- a/host/lib/convert/convert_orc.orc +++ b/host/lib/convert/convert_orc.orc @@ -61,3 +61,20 @@ x2 swapw dst, tmp .temp 4 tmp x2 swapw tmp, src swapl dst, tmp + +.function _convert_swap_byte_pairs_orc +.source 4 src +.dest 4 dst +swapl dst, src + +.function _convert_fc32_1_to_sc8_1_nswap_orc +.source 8 src +.dest 2 dst +.temp 8 tmp +.temp 4 tmp2 +.floatparam 4 scalar +x2 mulf tmp, src, scalar +x2 convfl tmp, tmp +swaplq tmp, tmp +x2 convlw tmp2, tmp +x2 convwb dst, tmp2 diff --git a/host/lib/convert/convert_with_orc.cpp b/host/lib/convert/convert_with_orc.cpp index 0c46bcf1e..e44c8ca73 100644 --- a/host/lib/convert/convert_with_orc.cpp +++ b/host/lib/convert/convert_with_orc.cpp @@ -27,6 +27,8 @@ extern void _convert_item32_1_to_fc32_1_nswap_orc(void *, const void *, float, i extern void _convert_item32_1_to_fc32_1_bswap_orc(void *, const void *, float, int); extern void _convert_sc16_1_to_item32_1_nswap_orc(void *, const void *, float, int); extern void _convert_item32_1_to_sc16_1_nswap_orc(void *, const void *, float, int); +extern void _convert_fc32_1_to_sc8_1_nswap_orc(void *, const void *, float, int); +extern void _convert_swap_byte_pairs_orc(void *, const void *, int); } DECLARE_CONVERTER(fc32, 1, sc16_item32_le, 1, PRIORITY_LIBORC){ @@ -52,3 +54,12 @@ DECLARE_CONVERTER(sc16, 1, sc16_item32_le, 1, PRIORITY_LIBORC){ DECLARE_CONVERTER(sc16_item32_le, 1, sc16, 1, PRIORITY_LIBORC){ _convert_item32_1_to_sc16_1_nswap_orc(outputs[0], inputs[0], scale_factor, nsamps); } + +DECLARE_CONVERTER(fc32, 1, sc8_item32_be, 1, PRIORITY_LIBORC){ + _convert_fc32_1_to_sc8_1_nswap_orc(outputs[0], inputs[0], scale_factor, nsamps); + _convert_swap_byte_pairs_orc(outputs[0], outputs[0], (nsamps + 1)/2); +} + +DECLARE_CONVERTER(fc32, 1, sc8_item32_le, 1, PRIORITY_LIBORC){ + _convert_fc32_1_to_sc8_1_nswap_orc(outputs[0], inputs[0], scale_factor, nsamps); +} diff --git a/host/lib/convert/convert_with_tables.cpp b/host/lib/convert/convert_with_tables.cpp index c45415d5d..4a3ce29b2 100644 --- a/host/lib/convert/convert_with_tables.cpp +++ b/host/lib/convert/convert_with_tables.cpp @@ -1,5 +1,5 @@ // -// Copyright 2011 Ettus Research LLC +// Copyright 2011-2012 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 @@ -17,6 +17,7 @@ #include "convert_common.hpp" #include <uhd/utils/byteswap.hpp> +#include <boost/math/special_functions/round.hpp> #include <vector> using namespace uhd::convert; @@ -26,6 +27,55 @@ static const size_t sc16_table_len = size_t(1 << 16); typedef boost::uint16_t (*tohost16_type)(boost::uint16_t); /*********************************************************************** + * Implementation for sc16 to sc8 lookup table + * - Lookup the real and imaginary parts individually + **********************************************************************/ +template <bool swap> +class convert_sc16_1_to_sc8_item32_1 : public converter{ +public: + convert_sc16_1_to_sc8_item32_1(void): _table(sc16_table_len){} + + void set_scalar(const double scalar){ + for (size_t i = 0; i < sc16_table_len; i++){ + const boost::int16_t val = boost::uint16_t(i); + _table[i] = boost::int8_t(boost::math::iround(val * scalar / 32767.)); + } + } + + void operator()(const input_type &inputs, const output_type &outputs, const size_t nsamps){ + const sc16_t *input = reinterpret_cast<const sc16_t *>(inputs[0]); + item32_t *output = reinterpret_cast<item32_t *>(outputs[0]); + + const size_t num_pairs = nsamps/2; + for (size_t i = 0, j = 0; i < num_pairs; i++, j+=2){ + output[i] = this->lookup(input[j], input[j+1]); + } + + if (nsamps != num_pairs*2){ + output[num_pairs] = this->lookup(input[nsamps-1], 0);; + } + } + + item32_t lookup(const sc16_t &in0, const sc16_t &in1){ + if (swap){ //hope this compiles out, its a template constant + return + (item32_t(_table[size_t(in0.real())]) << 16) | + (item32_t(_table[size_t(in0.imag())]) << 24) | + (item32_t(_table[size_t(in1.real())]) << 0) | + (item32_t(_table[size_t(in1.imag())]) << 8) ; + } + return + (item32_t(_table[size_t(in0.real())]) << 8) | + (item32_t(_table[size_t(in0.imag())]) << 0) | + (item32_t(_table[size_t(in1.real())]) << 24) | + (item32_t(_table[size_t(in1.imag())]) << 16) ; + } + +private: + std::vector<boost::uint8_t> _table; +}; + +/*********************************************************************** * Implementation for sc16 lookup table * - Lookup the real and imaginary parts individually **********************************************************************/ @@ -67,11 +117,19 @@ class convert_sc8_item32_1_to_fcxx_1 : public converter{ public: convert_sc8_item32_1_to_fcxx_1(void): _table(sc16_table_len){} + //special case for sc16 type, 32767 undoes float normalization + static type conv(const boost::int8_t &num, const double scalar){ + if (sizeof(type) == sizeof(s16_t)){ + return type(boost::math::iround(num*scalar*32767)); + } + return type(num*scalar); + } + void set_scalar(const double scalar){ for (size_t i = 0; i < sc16_table_len; i++){ const boost::uint16_t val = tohost(boost::uint16_t(i & 0xffff)); - const type real = type(boost::int8_t(val >> 8)*scalar); - const type imag = type(boost::int8_t(val >> 0)*scalar); + const type real = conv(boost::int8_t(val >> 8), scalar); + const type imag = conv(boost::int8_t(val >> 0), scalar); _table[i] = std::complex<type>(real, imag); } } @@ -112,9 +170,13 @@ private: #ifdef BOOST_BIG_ENDIAN # define SHIFT_PAIR0 16, 0 # define SHIFT_PAIR1 0, 16 +# define BE_SWAP false +# define LE_SWAP true #else # define SHIFT_PAIR0 0, 16 # define SHIFT_PAIR1 16, 0 +# define BE_SWAP true +# define LE_SWAP false #endif static converter::sptr make_convert_sc16_item32_be_1_to_fc32_1(void){ @@ -149,6 +211,22 @@ static converter::sptr make_convert_sc8_item32_le_1_to_fc64_1(void){ return converter::sptr(new convert_sc8_item32_1_to_fcxx_1<double, uhd::wtohx, SHIFT_PAIR0>()); } +static converter::sptr make_convert_sc8_item32_be_1_to_sc16_1(void){ + return converter::sptr(new convert_sc8_item32_1_to_fcxx_1<s16_t, uhd::ntohx, SHIFT_PAIR1>()); +} + +static converter::sptr make_convert_sc8_item32_le_1_to_sc16_1(void){ + return converter::sptr(new convert_sc8_item32_1_to_fcxx_1<s16_t, uhd::wtohx, SHIFT_PAIR0>()); +} + +static converter::sptr make_convert_sc16_1_to_sc8_item32_be_1(void){ + return converter::sptr(new convert_sc16_1_to_sc8_item32_1<BE_SWAP>()); +} + +static converter::sptr make_convert_sc16_1_to_sc8_item32_le_1(void){ + return converter::sptr(new convert_sc16_1_to_sc8_item32_1<LE_SWAP>()); +} + UHD_STATIC_BLOCK(register_convert_sc16_item32_1_to_fcxx_1){ uhd::convert::id_type id; id.num_inputs = 1; @@ -185,4 +263,20 @@ UHD_STATIC_BLOCK(register_convert_sc16_item32_1_to_fcxx_1){ id.output_format = "fc64"; id.input_format = "sc8_item32_le"; uhd::convert::register_converter(id, &make_convert_sc8_item32_le_1_to_fc64_1, PRIORITY_TABLE); + + id.output_format = "sc16"; + id.input_format = "sc8_item32_be"; + uhd::convert::register_converter(id, &make_convert_sc8_item32_be_1_to_sc16_1, PRIORITY_TABLE); + + id.output_format = "sc16"; + id.input_format = "sc8_item32_le"; + uhd::convert::register_converter(id, &make_convert_sc8_item32_le_1_to_sc16_1, PRIORITY_TABLE); + + id.output_format = "sc16"; + id.input_format = "sc8_item32_be"; + uhd::convert::register_converter(id, &make_convert_sc16_1_to_sc8_item32_be_1, PRIORITY_TABLE); + + id.output_format = "sc16"; + id.input_format = "sc8_item32_le"; + uhd::convert::register_converter(id, &make_convert_sc16_1_to_sc8_item32_le_1, PRIORITY_TABLE); } diff --git a/host/lib/convert/gen_convert_general.py b/host/lib/convert/gen_convert_general.py index 46a0c8185..364c4bd1a 100644 --- a/host/lib/convert/gen_convert_general.py +++ b/host/lib/convert/gen_convert_general.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -# Copyright 2011 Ettus Research LLC +# Copyright 2011-2012 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 @@ -92,6 +92,22 @@ DECLARE_CONVERTER(sc8_item32_$(end), 1, $(cpu_type), 1, PRIORITY_GENERAL){ item32_sc8_to_$(cpu_type)(item_n, output[num_samps-1], dummy, scale_factor); } } + +DECLARE_CONVERTER($(cpu_type), 1, sc8_item32_$(end), 1, PRIORITY_GENERAL){ + const $(cpu_type)_t *input = reinterpret_cast<const $(cpu_type)_t *>(inputs[0]); + item32_t *output = reinterpret_cast<item32_t *>(outputs[0]); + + const size_t num_pairs = nsamps/2; + for (size_t i = 0, j = 0; i < num_pairs; i++, j+=2){ + const item32_t item = $(cpu_type)_to_item32_sc8(input[j], input[j+1], scale_factor); + output[i] = $(to_wire)(item); + } + + if (nsamps != num_pairs*2){ + const item32_t item = $(cpu_type)_to_item32_sc8(input[nsamps-1], 0, scale_factor); + output[num_pairs] = $(to_wire)(item); + } +} """ TMPL_CONV_USRP1_COMPLEX = """ diff --git a/host/lib/transport/gen_vrt_if_packet.py b/host/lib/transport/gen_vrt_if_packet.py index 245a7ddbd..e28ce3aae 100755..100644 --- a/host/lib/transport/gen_vrt_if_packet.py +++ b/host/lib/transport/gen_vrt_if_packet.py @@ -137,7 +137,7 @@ void vrt::if_hdr_pack_$(suffix)( #if $pred & $tlr_p { const size_t empty_bytes = if_packet_info.num_payload_words32*sizeof(boost::uint32_t) - if_packet_info.num_payload_bytes; - if_packet_info.tlr |= (0x3 << 22) | (occ_table[empty_bytes & 0x3] << 10); + if_packet_info.tlr = (0x3 << 22) | (occ_table[empty_bytes & 0x3] << 10); } packet_buff[$num_header_words+if_packet_info.num_payload_words32] = $(XE_MACRO)(if_packet_info.tlr); #set $flags |= (0x1 << 26); diff --git a/host/lib/transport/libusb1_zero_copy.cpp b/host/lib/transport/libusb1_zero_copy.cpp index 28d6cdd5b..3e67264cd 100644 --- a/host/lib/transport/libusb1_zero_copy.cpp +++ b/host/lib/transport/libusb1_zero_copy.cpp @@ -80,13 +80,14 @@ UHD_INLINE bool wait_for_completion(libusb_context *ctx, const double timeout, b **********************************************************************/ class libusb_zero_copy_mrb : public managed_recv_buffer{ public: - libusb_zero_copy_mrb(libusb_transfer *lut): + libusb_zero_copy_mrb(libusb_transfer *lut, const size_t frame_size): _ctx(libusb::session::get_global_session()->get_context()), - _lut(lut), _expired(false) { /* NOP */ } + _lut(lut), _expired(false), _frame_size(frame_size) { /* NOP */ } void release(void){ if (_expired) return; completed = false; + _lut->length = _frame_size; //always reset length UHD_ASSERT_THROW(libusb_submit_transfer(_lut) == 0); _expired = true; } @@ -109,6 +110,7 @@ private: libusb_context *_ctx; libusb_transfer *_lut; bool _expired; + const size_t _frame_size; }; /*********************************************************************** @@ -118,9 +120,9 @@ private: **********************************************************************/ class libusb_zero_copy_msb : public managed_send_buffer{ public: - libusb_zero_copy_msb(libusb_transfer *lut): + libusb_zero_copy_msb(libusb_transfer *lut, const size_t frame_size): _ctx(libusb::session::get_global_session()->get_context()), - _lut(lut), _expired(false) { /* NOP */ } + _lut(lut), _expired(false), _frame_size(frame_size) { /* NOP */ } void commit(size_t len){ if (_expired) return; @@ -144,11 +146,12 @@ public: private: void *get_buff(void) const{return _lut->buffer;} - size_t get_size(void) const{return _lut->length;} + size_t get_size(void) const{return _frame_size;} libusb_context *_ctx; libusb_transfer *_lut; bool _expired; + const size_t _frame_size; }; /*********************************************************************** @@ -184,7 +187,7 @@ public: libusb_transfer *lut = libusb_alloc_transfer(0); UHD_ASSERT_THROW(lut != NULL); - _mrb_pool.push_back(boost::shared_ptr<libusb_zero_copy_mrb>(new libusb_zero_copy_mrb(lut))); + _mrb_pool.push_back(boost::shared_ptr<libusb_zero_copy_mrb>(new libusb_zero_copy_mrb(lut, this->get_recv_frame_size()))); libusb_fill_bulk_transfer( lut, // transfer @@ -207,7 +210,7 @@ public: libusb_transfer *lut = libusb_alloc_transfer(0); UHD_ASSERT_THROW(lut != NULL); - _msb_pool.push_back(boost::shared_ptr<libusb_zero_copy_msb>(new libusb_zero_copy_msb(lut))); + _msb_pool.push_back(boost::shared_ptr<libusb_zero_copy_msb>(new libusb_zero_copy_msb(lut, this->get_send_frame_size()))); libusb_fill_bulk_transfer( lut, // transfer diff --git a/host/lib/transport/super_recv_packet_handler.hpp b/host/lib/transport/super_recv_packet_handler.hpp index a5876c8bf..74fbe82fb 100644 --- a/host/lib/transport/super_recv_packet_handler.hpp +++ b/host/lib/transport/super_recv_packet_handler.hpp @@ -1,5 +1,5 @@ // -// Copyright 2011 Ettus Research LLC +// Copyright 2011-2012 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 @@ -283,7 +283,7 @@ private: info.ifpi.num_packet_words32 = num_packet_words32 - _header_offset_words32; info.vrt_hdr = buff->cast<const boost::uint32_t *>() + _header_offset_words32; _vrt_unpacker(info.vrt_hdr, info.ifpi); - info.time = time_spec_t(time_t(info.ifpi.tsi), size_t(info.ifpi.tsf), _tick_rate); //assumes has_tsi and has_tsf are true + info.time = time_spec_t::from_ticks(info.ifpi.tsf, _tick_rate); //assumes has_tsf is true info.copy_buff = reinterpret_cast<const char *>(info.vrt_hdr + info.ifpi.num_header_words32); //-------------------------------------------------------------- @@ -408,7 +408,7 @@ private: case PACKET_INLINE_MESSAGE: std::swap(curr_info, next_info); //save progress from curr -> next - curr_info.metadata.has_time_spec = next_info[index].ifpi.has_tsi and next_info[index].ifpi.has_tsf; + curr_info.metadata.has_time_spec = next_info[index].ifpi.has_tsf; curr_info.metadata.time_spec = next_info[index].time; curr_info.metadata.more_fragments = false; curr_info.metadata.fragment_offset = 0; @@ -436,7 +436,7 @@ private: alignment_check(index, curr_info); std::swap(curr_info, next_info); //save progress from curr -> next curr_info.metadata.has_time_spec = prev_info.metadata.has_time_spec; - curr_info.metadata.time_spec = prev_info.metadata.time_spec + time_spec_t(0, + curr_info.metadata.time_spec = prev_info.metadata.time_spec + time_spec_t::from_ticks( prev_info[index].ifpi.num_payload_words32*sizeof(boost::uint32_t)/_bytes_per_otw_item, _samp_rate); curr_info.metadata.more_fragments = false; curr_info.metadata.fragment_offset = 0; @@ -469,7 +469,7 @@ private: } //set the metadata from the buffer information at index zero - curr_info.metadata.has_time_spec = curr_info[0].ifpi.has_tsi and curr_info[0].ifpi.has_tsf; + curr_info.metadata.has_time_spec = curr_info[0].ifpi.has_tsf; curr_info.metadata.time_spec = curr_info[0].time; curr_info.metadata.more_fragments = false; curr_info.metadata.fragment_offset = 0; @@ -508,7 +508,7 @@ private: metadata = info.metadata; //interpolate the time spec (useful when this is a fragment) - metadata.time_spec += time_spec_t(0, info.fragment_offset_in_samps, _samp_rate); + metadata.time_spec += time_spec_t::from_ticks(info.fragment_offset_in_samps, _samp_rate); //extract the number of samples available to copy const size_t nsamps_available = info.data_bytes_to_copy/_bytes_per_otw_item; diff --git a/host/lib/transport/super_send_packet_handler.hpp b/host/lib/transport/super_send_packet_handler.hpp index 5ed8e0143..3d68507ed 100644 --- a/host/lib/transport/super_send_packet_handler.hpp +++ b/host/lib/transport/super_send_packet_handler.hpp @@ -1,5 +1,5 @@ // -// Copyright 2011 Ettus Research LLC +// Copyright 2011-2012 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 @@ -134,11 +134,10 @@ public: vrt::if_packet_info_t if_packet_info; if_packet_info.has_sid = false; if_packet_info.has_cid = false; - if_packet_info.has_tlr = false; - if_packet_info.has_tsi = metadata.has_time_spec; + if_packet_info.has_tlr = true; + if_packet_info.has_tsi = false; if_packet_info.has_tsf = metadata.has_time_spec; - if_packet_info.tsi = boost::uint32_t(metadata.time_spec.get_full_secs()); - if_packet_info.tsf = boost::uint64_t(metadata.time_spec.get_tick_count(_tick_rate)); + if_packet_info.tsf = metadata.time_spec.to_ticks(_tick_rate); if_packet_info.sob = metadata.start_of_burst; if_packet_info.eob = metadata.end_of_burst; @@ -174,9 +173,8 @@ public: if (num_samps_sent == 0) return total_num_samps_sent; //setup metadata for the next fragment - const time_spec_t time_spec = metadata.time_spec + time_spec_t(0, total_num_samps_sent, _samp_rate); - if_packet_info.tsi = boost::uint32_t(time_spec.get_full_secs()); - if_packet_info.tsf = boost::uint64_t(time_spec.get_tick_count(_tick_rate)); + const time_spec_t time_spec = metadata.time_spec + time_spec_t::from_ticks(total_num_samps_sent, _samp_rate); + if_packet_info.tsf = time_spec.to_ticks(_tick_rate); if_packet_info.sob = false; } diff --git a/host/lib/transport/usb_zero_copy_wrapper.cpp b/host/lib/transport/usb_zero_copy_wrapper.cpp index 227c4b392..690e5aaa2 100644 --- a/host/lib/transport/usb_zero_copy_wrapper.cpp +++ b/host/lib/transport/usb_zero_copy_wrapper.cpp @@ -1,5 +1,5 @@ // -// Copyright 2011 Ettus Research LLC +// Copyright 2011-2012 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 @@ -18,17 +18,13 @@ #include <uhd/transport/usb_zero_copy.hpp> #include <uhd/transport/bounded_buffer.hpp> #include <uhd/transport/buffer_pool.hpp> +#include <uhd/utils/byteswap.hpp> +#include <uhd/utils/msg.hpp> #include <boost/foreach.hpp> #include <vector> #include <iostream> using namespace uhd::transport; -bool debug = true; - -static inline size_t next_boundary(size_t length, size_t boundary){ - //pad to the boundary, assumes boundary is a power of 2 - return (length + (boundary-1)) & ~(boundary-1); -} /*********************************************************************** * USB zero copy wrapper - managed receive buffer @@ -45,7 +41,7 @@ public: _mrb.reset(); } - sptr get_new(managed_recv_buffer::sptr mrb, const void *mem, size_t len){ + UHD_INLINE sptr get_new(managed_recv_buffer::sptr mrb, const void *mem, size_t len){ _mrb = mrb; _mem = mem; _len = len; @@ -67,28 +63,46 @@ private: **********************************************************************/ class usb_zero_copy_wrapper_msb : public managed_send_buffer{ public: - usb_zero_copy_wrapper_msb(bounded_buffer<usb_zero_copy_wrapper_msb *> &queue, size_t boundary): - _queue(queue), _boundary(boundary){/*NOP*/} + usb_zero_copy_wrapper_msb(const usb_zero_copy::sptr internal, const size_t fragmentation_size): + _internal(internal), _fragmentation_size(fragmentation_size){/*NOP*/} void commit(size_t len){ - if (_msb.get() == NULL) return; - _msb->commit(next_boundary(len, _boundary)); - _queue.push_with_haste(this); - _msb.reset(); + if (len == 0) return; + + //get a reference to the VITA header before incrementing + const boost::uint32_t vita_header = reinterpret_cast<const boost::uint32_t *>(_mem_buffer_tip)[0]; + + _bytes_in_buffer += len; + _mem_buffer_tip += len; + + //extract VITA end of packet flag, we must force flush under eof conditions + const bool eop = (uhd::wtohx(vita_header) & (0x1 << 24)) != 0; + const bool full = _bytes_in_buffer >= (_last_send_buff->size() - _fragmentation_size); + if (eop or full){ + _last_send_buff->commit(_bytes_in_buffer); + _last_send_buff.reset(); + } } - sptr get_new(managed_send_buffer::sptr msb){ - _msb = msb; + UHD_INLINE sptr get_new(const double timeout){ + if (not _last_send_buff){ + _last_send_buff = _internal->get_send_buff(timeout); + if (not _last_send_buff) return sptr(); + _mem_buffer_tip = _last_send_buff->cast<char *>(); + _bytes_in_buffer = 0; + } return make_managed_buffer(this); } private: - void *get_buff(void) const{return _msb->cast<void *>();} - size_t get_size(void) const{return _msb->size();} - - bounded_buffer<usb_zero_copy_wrapper_msb *> &_queue; - size_t _boundary; - managed_send_buffer::sptr _msb; + void *get_buff(void) const{return reinterpret_cast<void *>(_mem_buffer_tip);} + size_t get_size(void) const{return _fragmentation_size;} + + usb_zero_copy::sptr _internal; + const size_t _fragmentation_size; + managed_send_buffer::sptr _last_send_buff; + size_t _bytes_in_buffer; + char *_mem_buffer_tip; }; /*********************************************************************** @@ -96,23 +110,16 @@ private: **********************************************************************/ class usb_zero_copy_wrapper : public usb_zero_copy{ public: - usb_zero_copy_wrapper( - sptr usb_zc, size_t usb_frame_boundary - ): + usb_zero_copy_wrapper(sptr usb_zc, const size_t frame_boundary): _internal_zc(usb_zc), - _usb_frame_boundary(usb_frame_boundary), + _frame_boundary(frame_boundary), _available_recv_buffs(this->get_num_recv_frames()), - _available_send_buffs(this->get_num_send_frames()), _mrb_pool(this->get_num_recv_frames(), usb_zero_copy_wrapper_mrb(_available_recv_buffs)), - _msb_pool(this->get_num_send_frames(), usb_zero_copy_wrapper_msb(_available_send_buffs, usb_frame_boundary)) + _the_only_msb(usb_zero_copy_wrapper_msb(usb_zc, frame_boundary)) { BOOST_FOREACH(usb_zero_copy_wrapper_mrb &mrb, _mrb_pool){ _available_recv_buffs.push_with_haste(&mrb); } - - BOOST_FOREACH(usb_zero_copy_wrapper_msb &msb, _msb_pool){ - _available_send_buffs.push_with_haste(&msb); - } } managed_recv_buffer::sptr get_recv_buff(double timeout){ @@ -128,18 +135,17 @@ public: //extract this packet's memory address and length in bytes const char *mem = _last_recv_buff->cast<const char *>() + _last_recv_offset; const boost::uint32_t *mem32 = reinterpret_cast<const boost::uint32_t *>(mem); - size_t len = (mem32[0] & 0xffff)*sizeof(boost::uint32_t); //length in bytes (from VRT header) - + const size_t len = (uhd::wtohx(mem32[0]) & 0xffff)*sizeof(boost::uint32_t); //length in bytes (from VRT header) + managed_recv_buffer::sptr recv_buff; //the buffer to be returned to the user - recv_buff = wmrb->get_new(_last_recv_buff, mem, len); - _last_recv_offset = next_boundary(_last_recv_offset + len, _usb_frame_boundary); - + _last_recv_offset += len; + //check if this receive buffer has been exhausted if (_last_recv_offset >= _last_recv_buff->size()) { _last_recv_buff.reset(); } - + return recv_buff; } @@ -152,20 +158,11 @@ public: } size_t get_recv_frame_size(void) const{ - return _internal_zc->get_recv_frame_size(); + return std::min(_frame_boundary, _internal_zc->get_recv_frame_size()); } managed_send_buffer::sptr get_send_buff(double timeout){ - managed_send_buffer::sptr send_buff = _internal_zc->get_send_buff(timeout); - - //attempt to get a wrapper for a managed send buffer - usb_zero_copy_wrapper_msb *wmsb = NULL; - if (send_buff.get() and _available_send_buffs.pop_with_haste(wmsb)){ - return wmsb->get_new(send_buff); - } - - //otherwise return a null sptr for failure - return managed_send_buffer::sptr(); + return _the_only_msb.get_new(timeout); } size_t get_num_send_frames(void) const{ @@ -173,20 +170,19 @@ public: } size_t get_send_frame_size(void) const{ - return _internal_zc->get_send_frame_size(); + return std::min(_frame_boundary, _internal_zc->get_send_frame_size()); } private: sptr _internal_zc; - size_t _usb_frame_boundary; + size_t _frame_boundary; bounded_buffer<usb_zero_copy_wrapper_mrb *> _available_recv_buffs; - bounded_buffer<usb_zero_copy_wrapper_msb *> _available_send_buffs; std::vector<usb_zero_copy_wrapper_mrb> _mrb_pool; - std::vector<usb_zero_copy_wrapper_msb> _msb_pool; - + usb_zero_copy_wrapper_msb _the_only_msb; + //buffer to store partially-received VRT packets in buffer_pool::sptr _fragment_mem; - + //state for last recv buffer to create multiple managed buffers managed_recv_buffer::sptr _last_recv_buff; size_t _last_recv_offset; diff --git a/host/lib/types/time_spec.cpp b/host/lib/types/time_spec.cpp index 8e540c14c..14b9c988a 100644 --- a/host/lib/types/time_spec.cpp +++ b/host/lib/types/time_spec.cpp @@ -1,5 +1,5 @@ // -// Copyright 2011 Ettus Research LLC +// Copyright 2011-2012 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 <uhd/types/time_spec.hpp> +#include <inttypes.h> //imaxdiv, intmax_t using namespace uhd; @@ -23,18 +24,6 @@ using namespace uhd; * Time spec system time **********************************************************************/ -/*! - * Creates a time spec from system counts: - * TODO make part of API as a static factory function - * The counts type is 64 bits and will overflow the ticks type of long. - * Therefore, divmod the counts into seconds + sub-second counts first. - */ -#include <inttypes.h> //imaxdiv, intmax_t -static UHD_INLINE time_spec_t time_spec_t_from_counts(intmax_t counts, intmax_t freq){ - imaxdiv_t divres = imaxdiv(counts, freq); - return time_spec_t(time_t(divres.quot), double(divres.rem)/freq); -} - #ifdef HAVE_CLOCK_GETTIME #include <time.h> time_spec_t time_spec_t::get_system_time(void){ @@ -49,7 +38,7 @@ time_spec_t time_spec_t::get_system_time(void){ time_spec_t time_spec_t::get_system_time(void){ mach_timebase_info_data_t info; mach_timebase_info(&info); intmax_t nanosecs = mach_absolute_time()*info.numer/info.denom; - return time_spec_t_from_counts(nanosecs, intmax_t(1e9)); + return time_spec_t::from_ticks(nanosecs, 1e9); } #endif /* HAVE_MACH_ABSOLUTE_TIME */ @@ -60,7 +49,7 @@ time_spec_t time_spec_t::get_system_time(void){ LARGE_INTEGER counts, freq; QueryPerformanceCounter(&counts); QueryPerformanceFrequency(&freq); - return time_spec_t_from_counts(counts.QuadPart, freq.QuadPart); + return time_spec_t::from_ticks(counts.QuadPart, freq.QuadPart); } #endif /* HAVE_QUERY_PERFORMANCE_COUNTER */ @@ -83,14 +72,21 @@ time_spec_t time_spec_t::get_system_time(void){ * Time spec constructors **********************************************************************/ #define time_spec_init(full, frac) { \ - _full_secs = full + time_t(frac); \ - _frac_secs = frac - time_t(frac); \ + const time_t _full = time_t(full); \ + const double _frac = double(frac); \ + const int _frac_int = int(_frac); \ + _full_secs = _full + _frac_int; \ + _frac_secs = _frac - _frac_int; \ if (_frac_secs < 0) {\ _full_secs -= 1; \ _frac_secs += 1; \ } \ } +UHD_INLINE long long fast_llround(const double x){ + return (long long)(x + 0.5); // assumption of non-negativity +} + time_spec_t::time_spec_t(double secs){ time_spec_init(0, secs); } @@ -104,23 +100,25 @@ time_spec_t::time_spec_t(time_t full_secs, long tick_count, double tick_rate){ time_spec_init(full_secs, frac_secs); } +time_spec_t time_spec_t::from_ticks(long long ticks, double tick_rate){ + const imaxdiv_t divres = imaxdiv(ticks, fast_llround(tick_rate)); + return time_spec_t(time_t(divres.quot), double(divres.rem)/tick_rate); +} + /*********************************************************************** * Time spec accessors **********************************************************************/ long time_spec_t::get_tick_count(double tick_rate) const{ - return long(this->get_frac_secs()*tick_rate + 0.5); -} - -double time_spec_t::get_real_secs(void) const{ - return this->_full_secs + this->_frac_secs; + return long(fast_llround(this->get_frac_secs()*tick_rate)); } -time_t time_spec_t::get_full_secs(void) const{ - return this->_full_secs; +long long time_spec_t::to_ticks(double tick_rate) const{ + return fast_llround(this->get_frac_secs()*tick_rate) + \ + (this->get_full_secs() * fast_llround(tick_rate)); } -double time_spec_t::get_frac_secs(void) const{ - return this->_frac_secs; +double time_spec_t::get_real_secs(void) const{ + return this->get_full_secs() + this->get_frac_secs(); } /*********************************************************************** @@ -128,16 +126,16 @@ double time_spec_t::get_frac_secs(void) const{ **********************************************************************/ time_spec_t &time_spec_t::operator+=(const time_spec_t &rhs){ time_spec_init( - this->_full_secs + rhs.get_full_secs(), - this->_frac_secs + rhs.get_frac_secs() + this->get_full_secs() + rhs.get_full_secs(), + this->get_frac_secs() + rhs.get_frac_secs() ); return *this; } time_spec_t &time_spec_t::operator-=(const time_spec_t &rhs){ time_spec_init( - this->_full_secs - rhs.get_full_secs(), - this->_frac_secs - rhs.get_frac_secs() + this->get_full_secs() - rhs.get_full_secs(), + this->get_frac_secs() - rhs.get_frac_secs() ); return *this; } diff --git a/host/lib/usrp/b100/b100_ctrl.cpp b/host/lib/usrp/b100/b100_ctrl.cpp index 7d40daa32..e6136c00e 100644 --- a/host/lib/usrp/b100/b100_ctrl.cpp +++ b/host/lib/usrp/b100/b100_ctrl.cpp @@ -165,7 +165,7 @@ int b100_ctrl_impl::write(boost::uint32_t addr, const ctrl_data_t &data) { pkt.pkt_meta.seq = _seq++; pkt.pkt_meta.len = pkt.data.size(); pkt.pkt_meta.addr = addr; - boost::uint16_t pkt_buff[CTRL_PACKET_LENGTH / sizeof(boost::uint16_t)]; + boost::uint16_t pkt_buff[CTRL_PACKET_LENGTH / sizeof(boost::uint16_t)] = {}; pack_ctrl_pkt(pkt_buff, pkt); size_t result = send_pkt(pkt_buff); @@ -181,7 +181,7 @@ ctrl_data_t b100_ctrl_impl::read(boost::uint32_t addr, size_t len) { pkt.pkt_meta.seq = _seq++; pkt.pkt_meta.len = len; pkt.pkt_meta.addr = addr; - boost::uint16_t pkt_buff[CTRL_PACKET_LENGTH / sizeof(boost::uint16_t)]; + boost::uint16_t pkt_buff[CTRL_PACKET_LENGTH / sizeof(boost::uint16_t)] = {}; //flush anything that might be in the queue while (get_ctrl_data(pkt.data, 0.0)){ diff --git a/host/lib/usrp/b100/b100_impl.cpp b/host/lib/usrp/b100/b100_impl.cpp index 61bc58bce..8b55494c5 100644 --- a/host/lib/usrp/b100/b100_impl.cpp +++ b/host/lib/usrp/b100/b100_impl.cpp @@ -1,5 +1,5 @@ // -// Copyright 2011 Ettus Research LLC +// Copyright 2012 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 @@ -150,6 +150,10 @@ UHD_STATIC_BLOCK(register_b100_device){ * Structors **********************************************************************/ b100_impl::b100_impl(const device_addr_t &device_addr){ + size_t initialization_count = 0; + b100_impl_constructor_begin: + initialization_count++; + _tree = property_tree::make(); //extract the FPGA path for the B100 @@ -181,6 +185,8 @@ b100_impl::b100_impl(const device_addr_t &device_addr){ //load FPGA image, gpif is disabled while loading this->enable_gpif(false); _fx2_ctrl->usrp_load_fpga(b100_fpga_image); + _fx2_ctrl->usrp_fpga_reset(false); //active low reset + _fx2_ctrl->usrp_fpga_reset(true); this->enable_gpif(true); //create the control transport @@ -196,13 +202,28 @@ b100_impl::b100_impl(const device_addr_t &device_addr){ 3, 4, //interface, endpoint ctrl_xport_args ); + while (_ctrl_transport->get_recv_buff(0.0)){} //flush ctrl xport //////////////////////////////////////////////////////////////////// // Initialize FPGA wishbone communication //////////////////////////////////////////////////////////////////// _fpga_ctrl = b100_ctrl::make(_ctrl_transport); - this->reset_gpif(6); //always reset first to ensure communication _fpga_ctrl->poke32(B100_REG_GLOBAL_RESET, 0); //global fpga reset + //perform a test peek operation + try{ + _fpga_ctrl->peek32(0); + } + //try reset once in the case of failure + catch(const uhd::exception &e){ + if (initialization_count > 1) throw; + UHD_MSG(warning) << + "The control endpoint was left in a bad state.\n" + "Attempting endpoint re-enumeration...\n" << std::endl; + _fpga_ctrl.reset(); + _ctrl_transport.reset(); + _fx2_ctrl->usrp_fx2_reset(); + goto b100_impl_constructor_begin; + } this->check_fpga_compat(); //check after reset and making control //////////////////////////////////////////////////////////////////// @@ -229,8 +250,10 @@ b100_impl::b100_impl(const device_addr_t &device_addr){ 2, 6, // IN interface, endpoint 1, 2, // OUT interface, endpoint data_xport_args // param hints - ) + ), + B100_MAX_PKT_BYTE_LIMIT ); + while (_data_transport->get_recv_buff(0.0)){} //flush data xport //////////////////////////////////////////////////////////////////// // Initialize the properties tree @@ -361,10 +384,10 @@ b100_impl::b100_impl(const device_addr_t &device_addr){ // create time control objects //////////////////////////////////////////////////////////////////// time64_core_200::readback_bases_type time64_rb_bases; - time64_rb_bases.rb_secs_now = B100_REG_RB_TIME_NOW_SECS; - time64_rb_bases.rb_ticks_now = B100_REG_RB_TIME_NOW_TICKS; - time64_rb_bases.rb_secs_pps = B100_REG_RB_TIME_PPS_SECS; - time64_rb_bases.rb_ticks_pps = B100_REG_RB_TIME_PPS_TICKS; + time64_rb_bases.rb_hi_now = B100_REG_RB_TIME_NOW_HI; + time64_rb_bases.rb_lo_now = B100_REG_RB_TIME_NOW_LO; + time64_rb_bases.rb_hi_pps = B100_REG_RB_TIME_PPS_HI; + time64_rb_bases.rb_lo_pps = B100_REG_RB_TIME_PPS_LO; _time64 = time64_core_200::make( _fpga_ctrl, B100_REG_SR_ADDR(B100_SR_TIME64), time64_rb_bases ); @@ -388,6 +411,13 @@ b100_impl::b100_impl(const device_addr_t &device_addr){ _tree->create<std::vector<std::string> >(mb_path / "clock_source/options").set(clock_sources); //////////////////////////////////////////////////////////////////// + // create user-defined control objects + //////////////////////////////////////////////////////////////////// + _user = user_settings_core_200::make(_fpga_ctrl, B100_REG_SR_ADDR(B100_SR_USER_REGS)); + _tree->create<user_settings_core_200::user_reg_t>(mb_path / "user/regs") + .subscribe(boost::bind(&user_settings_core_200::set_reg, _user, _1)); + + //////////////////////////////////////////////////////////////////// // create dboard control objects //////////////////////////////////////////////////////////////////// @@ -505,10 +535,6 @@ void b100_impl::update_clock_source(const std::string &source){ } ////////////////// some GPIF preparation related stuff ///////////////// -void b100_impl::reset_gpif(const boost::uint16_t ep) { - _fx2_ctrl->usrp_control_write(VRQ_RESET_GPIF, ep, ep, 0, 0); -} - void b100_impl::enable_gpif(const bool en) { _fx2_ctrl->usrp_control_write(VRQ_ENABLE_GPIF, en ? 1 : 0, 0, 0, 0); } diff --git a/host/lib/usrp/b100/b100_impl.hpp b/host/lib/usrp/b100/b100_impl.hpp index 96d90b14c..eab9c750b 100644 --- a/host/lib/usrp/b100/b100_impl.hpp +++ b/host/lib/usrp/b100/b100_impl.hpp @@ -1,5 +1,5 @@ // -// Copyright 2011 Ettus Research LLC +// Copyright 2011-2012 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 @@ -29,6 +29,7 @@ #include "rx_dsp_core_200.hpp" #include "tx_dsp_core_200.hpp" #include "time64_core_200.hpp" +#include "user_settings_core_200.hpp" #include <uhd/device.hpp> #include <uhd/property_tree.hpp> #include <uhd/utils/pimpl.hpp> @@ -43,14 +44,15 @@ #include <uhd/transport/usb_zero_copy.hpp> #include <boost/weak_ptr.hpp> -static const double B100_LINK_RATE_BPS = 256e6/8; //pratical link rate (< 480 Mbps) +static const double B100_LINK_RATE_BPS = 256e6/5; //pratical link rate (< 480 Mbps) static const std::string B100_FW_FILE_NAME = "usrp_b100_fw.ihx"; static const std::string B100_FPGA_FILE_NAME = "usrp_b100_fpga.bin"; -static const boost::uint16_t B100_FW_COMPAT_NUM = 0x02; -static const boost::uint16_t B100_FPGA_COMPAT_NUM = 0x08; +static const boost::uint16_t B100_FW_COMPAT_NUM = 0x03; +static const boost::uint16_t B100_FPGA_COMPAT_NUM = 0x09; static const boost::uint32_t B100_RX_SID_BASE = 2; static const boost::uint32_t B100_TX_ASYNC_SID = 1; static const double B100_DEFAULT_TICK_RATE = 64e6; +static const size_t B100_MAX_PKT_BYTE_LIMIT = 2048; //! Make a b100 dboard interface uhd::usrp::dboard_iface::sptr make_b100_dboard_iface( @@ -84,6 +86,7 @@ private: std::vector<rx_dsp_core_200::sptr> _rx_dsps; tx_dsp_core_200::sptr _tx_dsp; time64_core_200::sptr _time64; + user_settings_core_200::sptr _user; b100_clock_ctrl::sptr _clock_ctrl; b100_codec_ctrl::sptr _codec_ctrl; b100_ctrl::sptr _fpga_ctrl; @@ -120,7 +123,6 @@ private: void update_rx_subdev_spec(const uhd::usrp::subdev_spec_t &); void update_tx_subdev_spec(const uhd::usrp::subdev_spec_t &); void update_clock_source(const std::string &); - void reset_gpif(const boost::uint16_t); void enable_gpif(const bool); void clear_fpga_fifo(void); void handle_async_message(uhd::transport::managed_recv_buffer::sptr); diff --git a/host/lib/usrp/b100/b100_regs.hpp b/host/lib/usrp/b100/b100_regs.hpp index 491e16eef..987a09f03 100644 --- a/host/lib/usrp/b100/b100_regs.hpp +++ b/host/lib/usrp/b100/b100_regs.hpp @@ -1,4 +1,19 @@ - +// +// Copyright 2010-2012 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/>. +// //////////////////////////////////////////////////////////////// // @@ -27,10 +42,6 @@ #define B100_REG_MISC_SW B100_REG_MISC_BASE + 2 #define B100_REG_MISC_CGEN_CTRL B100_REG_MISC_BASE + 4 #define B100_REG_MISC_CGEN_ST B100_REG_MISC_BASE + 6 -#define B100_REG_MISC_TEST B100_REG_MISC_BASE + 8 -#define B100_REG_MISC_RX_LEN B100_REG_MISC_BASE + 10 -#define B100_REG_MISC_TX_LEN B100_REG_MISC_BASE + 12 -#define B100_REG_MISC_XFER_RATE B100_REG_MISC_BASE + 14 ///////////////////////////////////////////////////// // Slave 1 -- UART @@ -65,10 +76,10 @@ #define B100_REG_RB_MUX_32_BASE B100_REG_SLAVE(7) -#define B100_REG_RB_TIME_NOW_SECS B100_REG_RB_MUX_32_BASE + 0 -#define B100_REG_RB_TIME_NOW_TICKS B100_REG_RB_MUX_32_BASE + 4 -#define B100_REG_RB_TIME_PPS_SECS B100_REG_RB_MUX_32_BASE + 8 -#define B100_REG_RB_TIME_PPS_TICKS B100_REG_RB_MUX_32_BASE + 12 +#define B100_REG_RB_TIME_NOW_HI B100_REG_RB_MUX_32_BASE + 0 +#define B100_REG_RB_TIME_NOW_LO B100_REG_RB_MUX_32_BASE + 4 +#define B100_REG_RB_TIME_PPS_HI B100_REG_RB_MUX_32_BASE + 8 +#define B100_REG_RB_TIME_PPS_LO B100_REG_RB_MUX_32_BASE + 12 #define B100_REG_RB_MISC_TEST32 B100_REG_RB_MUX_32_BASE + 16 #define B100_REG_RB_COMPAT B100_REG_RB_MUX_32_BASE + 24 #define B100_REG_RB_GPIO B100_REG_RB_MUX_32_BASE + 28 @@ -92,9 +103,9 @@ #define B100_SR_TX_FRONT 54 // 5 regs (+0 to +4) #define B100_SR_REG_TEST32 60 // 1 reg -#define B100_SR_CLEAR_RX_FIFO 61 // 1 reg -#define B100_SR_CLEAR_TX_FIFO 62 // 1 reg +#define B100_SR_CLEAR_FIFO 61 // 1 reg #define B100_SR_GLOBAL_RESET 63 // 1 reg +#define B100_SR_USER_REGS 64 // 2 regs #define B100_SR_GPIO 128 @@ -105,8 +116,7 @@ ///////////////////////////////////////////////// // Magic reset regs //////////////////////////////////////////////// -#define B100_REG_CLEAR_RX B100_REG_SR_ADDR(B100_SR_CLEAR_RX_FIFO) -#define B100_REG_CLEAR_TX B100_REG_SR_ADDR(B100_SR_CLEAR_RX_FIFO) +#define B100_REG_CLEAR_FIFO B100_REG_SR_ADDR(B100_SR_CLEAR_FIFO) #define B100_REG_GLOBAL_RESET B100_REG_SR_ADDR(B100_SR_GLOBAL_RESET) #endif diff --git a/host/lib/usrp/b100/io_impl.cpp b/host/lib/usrp/b100/io_impl.cpp index 494d5d123..674380cca 100644 --- a/host/lib/usrp/b100/io_impl.cpp +++ b/host/lib/usrp/b100/io_impl.cpp @@ -1,5 +1,5 @@ // -// Copyright 2011 Ettus Research LLC +// Copyright 2011-2012 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 @@ -17,6 +17,7 @@ #include "recv_packet_demuxer.hpp" #include "validate_subdev_spec.hpp" +#include "async_packet_handler.hpp" #include "../../transport/super_recv_packet_handler.hpp" #include "../../transport/super_send_packet_handler.hpp" #include "usrp_commands.h" @@ -41,12 +42,13 @@ using namespace uhd::transport; **********************************************************************/ struct b100_impl::io_impl{ io_impl(void): - async_msg_fifo(100/*messages deep*/) + async_msg_fifo(1000/*messages deep*/) { /* NOP */ } zero_copy_if::sptr data_transport; bounded_buffer<async_metadata_t> async_msg_fifo; recv_packet_demuxer::sptr demuxer; + double tick_rate; }; /*********************************************************************** @@ -54,12 +56,8 @@ struct b100_impl::io_impl{ **********************************************************************/ void b100_impl::io_init(void){ - //clear state machines - _fpga_ctrl->poke32(B100_REG_CLEAR_RX, 0); - _fpga_ctrl->poke32(B100_REG_CLEAR_TX, 0); - - //set the expected packet size in USB frames - _fpga_ctrl->poke32(B100_REG_MISC_RX_LEN, 4); + //clear fifo state machines + _fpga_ctrl->poke32(B100_REG_CLEAR_FIFO, 0); //allocate streamer weak ptrs containers _rx_streamers.resize(_rx_dsps.size()); @@ -85,26 +83,16 @@ void b100_impl::handle_async_message(managed_recv_buffer::sptr rbuf){ } if (if_packet_info.sid == B100_TX_ASYNC_SID and if_packet_info.packet_type != vrt::if_packet_info_t::PACKET_TYPE_DATA){ + //fill in the async metadata async_metadata_t metadata; - metadata.channel = 0; - metadata.has_time_spec = if_packet_info.has_tsi and if_packet_info.has_tsf; - metadata.time_spec = time_spec_t( - time_t(if_packet_info.tsi), size_t(if_packet_info.tsf), _clock_ctrl->get_fpga_clock_rate() - ); - metadata.event_code = async_metadata_t::event_code_t(sph::get_context_code(vrt_hdr, if_packet_info)); + load_metadata_from_buff(uhd::wtohx<boost::uint32_t>, metadata, if_packet_info, vrt_hdr, _io_impl->tick_rate); + + //push the message onto the queue _io_impl->async_msg_fifo.push_with_pop_on_full(metadata); - if (metadata.event_code & - ( async_metadata_t::EVENT_CODE_UNDERFLOW - | async_metadata_t::EVENT_CODE_UNDERFLOW_IN_PACKET) - ) UHD_MSG(fastpath) << "U"; - else if (metadata.event_code & - ( async_metadata_t::EVENT_CODE_SEQ_ERROR - | async_metadata_t::EVENT_CODE_SEQ_ERROR_IN_BURST) - ) UHD_MSG(fastpath) << "S"; - else if (metadata.event_code & - async_metadata_t::EVENT_CODE_TIME_ERROR - ) UHD_MSG(fastpath) << "L"; + + //print some fastpath messages + standard_async_msg_prints(metadata); } else UHD_MSG(error) << "Unknown async packet" << std::endl; } @@ -123,6 +111,8 @@ void b100_impl::update_rates(void){ } void b100_impl::update_tick_rate(const double rate){ + _io_impl->tick_rate = rate; + //update the tick rate on all existing streamers -> thread safe for (size_t i = 0; i < _rx_streamers.size(); i++){ boost::shared_ptr<sph::recv_packet_streamer> my_streamer = @@ -154,6 +144,8 @@ void b100_impl::update_tx_samp_rate(const size_t dspno, const double rate){ if (my_streamer.get() == NULL) return; my_streamer->set_samp_rate(rate); + const double adj = _tx_dsp->get_scaling_adjustment(); + my_streamer->set_scale_factor(adj); } void b100_impl::update_rx_subdev_spec(const uhd::usrp::subdev_spec_t &spec){ @@ -202,15 +194,15 @@ rx_streamer::sptr b100_impl::get_rx_stream(const uhd::stream_args_t &args_){ //setup defaults for unspecified values args.otw_format = args.otw_format.empty()? "sc16" : args.otw_format; args.channels = args.channels.empty()? std::vector<size_t>(1, 0) : args.channels; - const unsigned sc8_scalar = unsigned(args.args.cast<double>("scalar", 0x400)); //calculate packet size static const size_t hdr_size = 0 + vrt::max_if_hdr_words32*sizeof(boost::uint32_t) + sizeof(vrt::if_packet_info_t().tlr) //forced to have trailer - sizeof(vrt::if_packet_info_t().cid) //no class id ever used + - sizeof(vrt::if_packet_info_t().tsi) //no int time ever used ; - const size_t bpp = 2048 - hdr_size; //limited by FPGA pkt buffer size + const size_t bpp = _data_transport->get_recv_frame_size() - hdr_size; const size_t bpi = convert::get_bytes_per_item(args.otw_format); const size_t spp = unsigned(args.args.cast<double>("spp", bpp/bpi)); @@ -233,8 +225,7 @@ rx_streamer::sptr b100_impl::get_rx_stream(const uhd::stream_args_t &args_){ for (size_t chan_i = 0; chan_i < args.channels.size(); chan_i++){ const size_t dsp = args.channels[chan_i]; _rx_dsps[dsp]->set_nsamps_per_packet(spp); //seems to be a good place to set this - if (not args.args.has_key("noclear")) _rx_dsps[dsp]->clear(); - _rx_dsps[dsp]->set_format(args.otw_format, sc8_scalar); + _rx_dsps[dsp]->setup(args); my_streamer->set_xport_chan_get_buff(chan_i, boost::bind( &recv_packet_demuxer::get_recv_buff, _io_impl->demuxer, dsp, _1 ), true /*flush*/); @@ -260,16 +251,15 @@ tx_streamer::sptr b100_impl::get_tx_stream(const uhd::stream_args_t &args_){ args.otw_format = args.otw_format.empty()? "sc16" : args.otw_format; args.channels = args.channels.empty()? std::vector<size_t>(1, 0) : args.channels; - if (args.otw_format != "sc16"){ - throw uhd::value_error("USRP TX cannot handle requested wire format: " + args.otw_format); - } - //calculate packet size static const size_t hdr_size = 0 + vrt::max_if_hdr_words32*sizeof(boost::uint32_t) + + sizeof(vrt::if_packet_info_t().tlr) //forced to have trailer + - sizeof(vrt::if_packet_info_t().sid) //no stream id ever used - sizeof(vrt::if_packet_info_t().cid) //no class id ever used + - sizeof(vrt::if_packet_info_t().tsi) //no int time ever used ; - static const size_t bpp = 2048 - hdr_size; + static const size_t bpp = _data_transport->get_send_frame_size() - hdr_size; const size_t spp = bpp/convert::get_bytes_per_item(args.otw_format); //make the new streamer given the samples per packet @@ -291,8 +281,7 @@ tx_streamer::sptr b100_impl::get_tx_stream(const uhd::stream_args_t &args_){ for (size_t chan_i = 0; chan_i < args.channels.size(); chan_i++){ const size_t dsp = args.channels[chan_i]; UHD_ASSERT_THROW(dsp == 0); //always 0 - if (not args.args.has_key("noclear")) _tx_dsp->clear(); - if (args.args.has_key("underflow_policy")) _tx_dsp->set_underflow_policy(args.args["underflow_policy"]); + _tx_dsp->setup(args); my_streamer->set_xport_chan_get_buff(chan_i, boost::bind( &zero_copy_if::get_send_buff, _data_transport, _1 )); diff --git a/host/lib/usrp/common/async_packet_handler.hpp b/host/lib/usrp/common/async_packet_handler.hpp new file mode 100644 index 000000000..fef03483f --- /dev/null +++ b/host/lib/usrp/common/async_packet_handler.hpp @@ -0,0 +1,71 @@ +// +// Copyright 2012 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_LIBUHD_USRP_COMMON_ASYNC_PACKET_HANDLER_HPP +#define INCLUDED_LIBUHD_USRP_COMMON_ASYNC_PACKET_HANDLER_HPP + +#include <uhd/config.hpp> +#include <uhd/transport/vrt_if_packet.hpp> +#include <uhd/types/metadata.hpp> +#include <uhd/utils/byteswap.hpp> +#include <uhd/utils/msg.hpp> + +namespace uhd{ namespace usrp{ + + template <typename to_host_type> + void load_metadata_from_buff( + const to_host_type &to_host, + async_metadata_t &metadata, + const transport::vrt::if_packet_info_t &if_packet_info, + const boost::uint32_t *vrt_hdr, + const double tick_rate, + const size_t channel = 0 + ){ + const boost::uint32_t *payload = vrt_hdr + if_packet_info.num_header_words32; + + //load into metadata + metadata.channel = channel; + metadata.has_time_spec = if_packet_info.has_tsf; + metadata.time_spec = time_spec_t::from_ticks(if_packet_info.tsf, tick_rate); + metadata.event_code = async_metadata_t::event_code_t(to_host(payload[0]) & 0xff); + + //load user payload + for (size_t i = 1; i < if_packet_info.num_payload_words32; i++){ + if (i-1 == 4) break; //limit of 4 words32 + metadata.user_payload[i-1] = to_host(payload[i]); + } + } + + UHD_INLINE void standard_async_msg_prints(const async_metadata_t &metadata) + { + if (metadata.event_code & + ( async_metadata_t::EVENT_CODE_UNDERFLOW + | async_metadata_t::EVENT_CODE_UNDERFLOW_IN_PACKET) + ) UHD_MSG(fastpath) << "U"; + else if (metadata.event_code & + ( async_metadata_t::EVENT_CODE_SEQ_ERROR + | async_metadata_t::EVENT_CODE_SEQ_ERROR_IN_BURST) + ) UHD_MSG(fastpath) << "S"; + else if (metadata.event_code & + async_metadata_t::EVENT_CODE_TIME_ERROR + ) UHD_MSG(fastpath) << "L"; + } + + +}} //namespace uhd::usrp + +#endif /* INCLUDED_LIBUHD_USRP_COMMON_ASYNC_PACKET_HANDLER_HPP */ diff --git a/host/lib/usrp/common/fx2_ctrl.cpp b/host/lib/usrp/common/fx2_ctrl.cpp index 3c6df7079..7b8920eb1 100644 --- a/host/lib/usrp/common/fx2_ctrl.cpp +++ b/host/lib/usrp/common/fx2_ctrl.cpp @@ -139,6 +139,15 @@ public: _ctrl_transport = ctrl_transport; } + void usrp_fx2_reset(void){ + unsigned char reset_y = 1; + unsigned char reset_n = 0; + usrp_control_write(FX2_FIRMWARE_LOAD, 0xe600, 0, &reset_y, 1); + usrp_control_write(FX2_FIRMWARE_LOAD, 0xe600, 0, &reset_n, 1); + //wait for things to settle + boost::this_thread::sleep(boost::posix_time::milliseconds(2000)); + } + void usrp_load_firmware(std::string filestring, bool force) { const char *filename = filestring.c_str(); @@ -419,7 +428,7 @@ public: { UHD_ASSERT_THROW(bytes.size() < max_i2c_data_bytes); - unsigned char buff[max_i2c_data_bytes]; + unsigned char buff[max_i2c_data_bytes] = {}; std::copy(bytes.begin(), bytes.end(), buff); int ret = this->usrp_i2c_write(addr & 0xff, @@ -434,7 +443,7 @@ public: { UHD_ASSERT_THROW(num_bytes < max_i2c_data_bytes); - unsigned char buff[max_i2c_data_bytes]; + unsigned char buff[max_i2c_data_bytes] = {}; int ret = this->usrp_i2c_read(addr & 0xff, buff, num_bytes); diff --git a/host/lib/usrp/common/fx2_ctrl.hpp b/host/lib/usrp/common/fx2_ctrl.hpp index 691d64275..f2e060862 100644 --- a/host/lib/usrp/common/fx2_ctrl.hpp +++ b/host/lib/usrp/common/fx2_ctrl.hpp @@ -39,6 +39,9 @@ public: //! Call init after the fpga is loaded virtual void usrp_init(void) = 0; + //! For emergency situations + virtual void usrp_fx2_reset(void) = 0; + /*! * Load firmware in Intel HEX Format onto device * \param filename name of firmware file @@ -116,6 +119,9 @@ public: //! enable/disable the tx path virtual void usrp_tx_enable(bool on) = 0; + + //! reset the fpga + virtual void usrp_fpga_reset(bool on) = 0; }; }} //namespace uhd::usrp diff --git a/host/lib/usrp/cores/CMakeLists.txt b/host/lib/usrp/cores/CMakeLists.txt index 2aa8f6b99..aa5f0bcbb 100644 --- a/host/lib/usrp/cores/CMakeLists.txt +++ b/host/lib/usrp/cores/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2011 Ettus Research LLC +# Copyright 2011-2012 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,4 +30,5 @@ LIBUHD_APPEND_SOURCES( ${CMAKE_CURRENT_SOURCE_DIR}/tx_dsp_core_200.cpp ${CMAKE_CURRENT_SOURCE_DIR}/rx_frontend_core_200.cpp ${CMAKE_CURRENT_SOURCE_DIR}/tx_frontend_core_200.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/user_settings_core_200.cpp ) diff --git a/host/lib/usrp/cores/rx_dsp_core_200.cpp b/host/lib/usrp/cores/rx_dsp_core_200.cpp index 2e21cc895..0996952ff 100644 --- a/host/lib/usrp/cores/rx_dsp_core_200.cpp +++ b/host/lib/usrp/cores/rx_dsp_core_200.cpp @@ -1,5 +1,5 @@ // -// Copyright 2011 Ettus Research LLC +// Copyright 2011-2012 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 @@ -18,6 +18,7 @@ #include "rx_dsp_core_200.hpp" #include <uhd/types/dict.hpp> #include <uhd/exception.hpp> +#include <uhd/utils/msg.hpp> #include <uhd/utils/algorithm.hpp> #include <boost/assign/list_of.hpp> #include <boost/thread/thread.hpp> //thread sleep @@ -27,7 +28,7 @@ #include <cmath> #define REG_DSP_RX_FREQ _dsp_base + 0 -//skip one right here +#define REG_DSP_RX_SCALE_IQ _dsp_base + 4 #define REG_DSP_RX_DECIM _dsp_base + 8 #define REG_DSP_RX_MUX _dsp_base + 12 @@ -35,15 +36,15 @@ #define FLAG_DSP_RX_MUX_REAL_MODE (1 << 1) #define REG_RX_CTRL_STREAM_CMD _ctrl_base + 0 -#define REG_RX_CTRL_TIME_SECS _ctrl_base + 4 -#define REG_RX_CTRL_TIME_TICKS _ctrl_base + 8 +#define REG_RX_CTRL_TIME_HI _ctrl_base + 4 +#define REG_RX_CTRL_TIME_LO _ctrl_base + 8 #define REG_RX_CTRL_CLEAR _ctrl_base + 12 #define REG_RX_CTRL_VRT_HDR _ctrl_base + 16 #define REG_RX_CTRL_VRT_SID _ctrl_base + 20 #define REG_RX_CTRL_VRT_TLR _ctrl_base + 24 #define REG_RX_CTRL_NSAMPS_PP _ctrl_base + 28 #define REG_RX_CTRL_NCHANNELS _ctrl_base + 32 -#define REG_RX_CTRL_FORMAT _ctrl_base + 36 +#define REG_RX_CTRL_FORMAT REG_RX_CTRL_CLEAR //re-use clear address template <class T> T ceil_log2(T num){ return std::ceil(std::log(num)/std::log(T(2))); @@ -60,6 +61,10 @@ public: ): _iface(iface), _dsp_base(dsp_base), _ctrl_base(ctrl_base), _sid(sid) { + //init to something so update method has reasonable defaults + _scaling_adjustment = 1.0; + _dsp_extra_scaling = 1.0; + //This is a hack/fix for the lingering packet problem. //The caller should also flush the recv transports if (lingering_packet){ @@ -78,7 +83,6 @@ public: _iface->poke32(REG_RX_CTRL_VRT_HDR, 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(REG_RX_CTRL_VRT_SID, _sid); @@ -117,8 +121,9 @@ public: //issue the stream command _iface->poke32(REG_RX_CTRL_STREAM_CMD, cmd_word); - _iface->poke32(REG_RX_CTRL_TIME_SECS, boost::uint32_t(stream_cmd.time_spec.get_full_secs())); - _iface->poke32(REG_RX_CTRL_TIME_TICKS, stream_cmd.time_spec.get_tick_count(_tick_rate)); //latches the command + const boost::uint64_t ticks = (stream_cmd.stream_now)? 0 : stream_cmd.time_spec.to_ticks(_tick_rate); + _iface->poke32(REG_RX_CTRL_TIME_HI, boost::uint32_t(ticks >> 32)); + _iface->poke32(REG_RX_CTRL_TIME_LO, boost::uint32_t(ticks >> 0)); //latches the command } void set_mux(const std::string &mode, const bool fe_swapped){ @@ -175,12 +180,20 @@ public: // Calculate closest multiplier constant to reverse gain absent scale multipliers const double rate_pow = std::pow(double(decim & 0xff), 4); _scaling_adjustment = std::pow(2, ceil_log2(rate_pow))/(1.65*rate_pow); + this->update_scalar(); return _tick_rate/decim_rate; } + void update_scalar(void){ + const double target_scalar = (1 << 16)*_scaling_adjustment/_dsp_extra_scaling; + const boost::int32_t actual_scalar = boost::math::iround(target_scalar); + _fxpt_scalar_correction = target_scalar/actual_scalar; //should be small + _iface->poke32(REG_DSP_RX_SCALE_IQ, actual_scalar); + } + double get_scaling_adjustment(void){ - return _scaling_adjustment/_fxpt_scale_adj; + return _fxpt_scalar_correction*_host_extra_scaling/32767.; } double set_freq(const double freq_){ @@ -210,22 +223,27 @@ public: if (_continuous_streaming) issue_stream_command(stream_cmd_t::STREAM_MODE_START_CONTINUOUS); } - void set_format(const std::string &format, const unsigned scale){ + void setup(const uhd::stream_args_t &stream_args){ + if (not stream_args.args.has_key("noclear")) this->clear(); + unsigned format_word = 0; - if (format == "sc16"){ + if (stream_args.otw_format == "sc16"){ format_word = 0; - _fxpt_scale_adj = 32767.; + _dsp_extra_scaling = 1.0; + _host_extra_scaling = 1.0; } - else if (format == "sc8"){ - format_word = (1 << 18); - _fxpt_scale_adj = 127. * scale; - _fxpt_scale_adj /= 256; //engine 16to8 drops lower 8 bits - _fxpt_scale_adj /= 4; //scale operation 2-bit pad + else if (stream_args.otw_format == "sc8"){ + format_word = (1 << 0); + double peak = stream_args.args.cast<double>("peak", 1.0); + peak = std::max(peak, 1.0/256); + _host_extra_scaling = peak*256; + _dsp_extra_scaling = peak*256; } - else throw uhd::value_error("USRP RX cannot handle requested wire format: " + format); + else throw uhd::value_error("USRP RX cannot handle requested wire format: " + stream_args.otw_format); + + this->update_scalar(); - const unsigned scale_word = scale & 0x3ffff; //18 bits; - _iface->poke32(REG_RX_CTRL_FORMAT, format_word | scale_word); + _iface->poke32(REG_RX_CTRL_FORMAT, format_word); } private: @@ -233,7 +251,7 @@ private: const size_t _dsp_base, _ctrl_base; double _tick_rate, _link_rate; bool _continuous_streaming; - double _scaling_adjustment, _fxpt_scale_adj; + double _scaling_adjustment, _dsp_extra_scaling, _host_extra_scaling, _fxpt_scalar_correction; const boost::uint32_t _sid; }; diff --git a/host/lib/usrp/cores/rx_dsp_core_200.hpp b/host/lib/usrp/cores/rx_dsp_core_200.hpp index 58be51eee..b01f751e9 100644 --- a/host/lib/usrp/cores/rx_dsp_core_200.hpp +++ b/host/lib/usrp/cores/rx_dsp_core_200.hpp @@ -19,6 +19,7 @@ #define INCLUDED_LIBUHD_USRP_RX_DSP_CORE_200_HPP #include <uhd/config.hpp> +#include <uhd/stream.hpp> #include <uhd/types/ranges.hpp> #include <boost/utility.hpp> #include <boost/shared_ptr.hpp> @@ -60,7 +61,7 @@ public: virtual void handle_overflow(void) = 0; - virtual void set_format(const std::string &format, const unsigned scale) = 0; + virtual void setup(const uhd::stream_args_t &stream_args) = 0; }; #endif /* INCLUDED_LIBUHD_USRP_RX_DSP_CORE_200_HPP */ diff --git a/host/lib/usrp/cores/time64_core_200.cpp b/host/lib/usrp/cores/time64_core_200.cpp index 23d1bdea2..e460d1106 100644 --- a/host/lib/usrp/cores/time64_core_200.cpp +++ b/host/lib/usrp/cores/time64_core_200.cpp @@ -20,11 +20,10 @@ #include <uhd/utils/assert_has.hpp> #include <boost/math/special_functions/round.hpp> -#define REG_TIME64_SECS _base + 0 -#define REG_TIME64_TICKS _base + 4 +#define REG_TIME64_TICKS_HI _base + 0 +#define REG_TIME64_TICKS_LO _base + 4 #define REG_TIME64_FLAGS _base + 8 #define REG_TIME64_IMM _base + 12 -#define REG_TIME64_TPS _base + 16 #define REG_TIME64_MIMO_SYNC _base + 20 //lower byte is delay cycles //pps flags (see above) @@ -59,39 +58,42 @@ public: void set_tick_rate(const double rate){ _tick_rate = rate; - _iface->poke32(REG_TIME64_TPS, boost::math::iround(rate)); } uhd::time_spec_t get_time_now(void){ for (size_t i = 0; i < 3; i++){ //special algorithm because we cant read 64 bits synchronously - const boost::uint32_t secs = _iface->peek32(_readback_bases.rb_secs_now); - const boost::uint32_t ticks = _iface->peek32(_readback_bases.rb_ticks_now); - if (secs != _iface->peek32(_readback_bases.rb_secs_now)) continue; - return time_spec_t(secs, ticks, _tick_rate); + const boost::uint32_t ticks_hi = _iface->peek32(_readback_bases.rb_hi_now); + const boost::uint32_t ticks_lo = _iface->peek32(_readback_bases.rb_lo_now); + if (ticks_hi != _iface->peek32(_readback_bases.rb_hi_now)) continue; + const boost::uint64_t ticks = (boost::uint64_t(ticks_hi) << 32) | ticks_lo; + return time_spec_t::from_ticks(ticks, _tick_rate); } throw uhd::runtime_error("time64_core_200: get time now timeout"); } uhd::time_spec_t get_time_last_pps(void){ for (size_t i = 0; i < 3; i++){ //special algorithm because we cant read 64 bits synchronously - const boost::uint32_t secs = _iface->peek32(_readback_bases.rb_secs_pps); - const boost::uint32_t ticks = _iface->peek32(_readback_bases.rb_ticks_pps); - if (secs != _iface->peek32(_readback_bases.rb_secs_pps)) continue; - return time_spec_t(secs, ticks, _tick_rate); + const boost::uint32_t ticks_hi = _iface->peek32(_readback_bases.rb_hi_pps); + const boost::uint32_t ticks_lo = _iface->peek32(_readback_bases.rb_lo_pps); + if (ticks_hi != _iface->peek32(_readback_bases.rb_hi_pps)) continue; + const boost::uint64_t ticks = (boost::uint64_t(ticks_hi) << 32) | ticks_lo; + return time_spec_t::from_ticks(ticks, _tick_rate); } throw uhd::runtime_error("time64_core_200: get time last pps timeout"); } void set_time_now(const uhd::time_spec_t &time){ - _iface->poke32(REG_TIME64_TICKS, time.get_tick_count(_tick_rate)); + const boost::uint64_t ticks = time.to_ticks(_tick_rate); + _iface->poke32(REG_TIME64_TICKS_LO, boost::uint32_t(ticks >> 0)); _iface->poke32(REG_TIME64_IMM, FLAG_TIME64_LATCH_NOW); - _iface->poke32(REG_TIME64_SECS, boost::uint32_t(time.get_full_secs())); //latches all 3 + _iface->poke32(REG_TIME64_TICKS_HI, boost::uint32_t(ticks >> 32)); //latches all 3 } void set_time_next_pps(const uhd::time_spec_t &time){ - _iface->poke32(REG_TIME64_TICKS, time.get_tick_count(_tick_rate)); + const boost::uint64_t ticks = time.to_ticks(_tick_rate); + _iface->poke32(REG_TIME64_TICKS_LO, boost::uint32_t(ticks >> 0)); _iface->poke32(REG_TIME64_IMM, FLAG_TIME64_LATCH_NEXT_PPS); - _iface->poke32(REG_TIME64_SECS, boost::uint32_t(time.get_full_secs())); //latches all 3 + _iface->poke32(REG_TIME64_TICKS_HI, boost::uint32_t(ticks >> 32)); //latches all 3 } void set_time_source(const std::string &source){ diff --git a/host/lib/usrp/cores/time64_core_200.hpp b/host/lib/usrp/cores/time64_core_200.hpp index ebd51a02f..7571573a5 100644 --- a/host/lib/usrp/cores/time64_core_200.hpp +++ b/host/lib/usrp/cores/time64_core_200.hpp @@ -31,8 +31,8 @@ public: typedef boost::shared_ptr<time64_core_200> sptr; struct readback_bases_type{ - size_t rb_secs_now, rb_ticks_now; - size_t rb_secs_pps, rb_ticks_pps; + size_t rb_hi_now, rb_lo_now; + size_t rb_hi_pps, rb_lo_pps; }; //! makes a new time64 core from iface and slave base diff --git a/host/lib/usrp/cores/tx_dsp_core_200.cpp b/host/lib/usrp/cores/tx_dsp_core_200.cpp index c5de4e361..7f02d59ca 100644 --- a/host/lib/usrp/cores/tx_dsp_core_200.cpp +++ b/host/lib/usrp/cores/tx_dsp_core_200.cpp @@ -1,5 +1,5 @@ // -// Copyright 2011 Ettus Research LLC +// Copyright 2011-2012 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 @@ -18,10 +18,12 @@ #include "tx_dsp_core_200.hpp" #include <uhd/types/dict.hpp> #include <uhd/exception.hpp> +#include <uhd/utils/msg.hpp> #include <uhd/utils/algorithm.hpp> #include <boost/assign/list_of.hpp> #include <boost/math/special_functions/round.hpp> #include <boost/math/special_functions/sign.hpp> +#include <boost/thread/thread.hpp> //sleep #include <algorithm> #include <cmath> @@ -29,8 +31,8 @@ #define REG_DSP_TX_SCALE_IQ _dsp_base + 4 #define REG_DSP_TX_INTERP _dsp_base + 8 -#define REG_TX_CTRL_NUM_CHAN _ctrl_base + 0 -#define REG_TX_CTRL_CLEAR_STATE _ctrl_base + 4 +#define REG_TX_CTRL_CLEAR _ctrl_base + 0 +#define REG_TX_CTRL_FORMAT _ctrl_base + 4 #define REG_TX_CTRL_REPORT_SID _ctrl_base + 8 #define REG_TX_CTRL_POLICY _ctrl_base + 12 #define REG_TX_CTRL_CYCLES_PER_UP _ctrl_base + 16 @@ -58,14 +60,19 @@ public: ): _iface(iface), _dsp_base(dsp_base), _ctrl_base(ctrl_base), _sid(sid) { + //init to something so update method has reasonable defaults + _scaling_adjustment = 1.0; + _dsp_extra_scaling = 1.0; + //init the tx control registers this->clear(); this->set_underflow_policy("next_packet"); } void clear(void){ - _iface->poke32(REG_TX_CTRL_CLEAR_STATE, 1); //reset - _iface->poke32(REG_TX_CTRL_NUM_CHAN, 0); //1 channel + _iface->poke32(REG_TX_CTRL_CLEAR, 1); //reset and flush technique + boost::this_thread::sleep(boost::posix_time::milliseconds(10)); + _iface->poke32(REG_TX_CTRL_CLEAR, 0); _iface->poke32(REG_TX_CTRL_REPORT_SID, _sid); } @@ -121,13 +128,24 @@ public: // Calculate CIC interpolation (i.e., without halfband interpolators) // Calculate closest multiplier constant to reverse gain absent scale multipliers - double rate_cubed = std::pow(double(interp & 0xff), 3); - const boost::int16_t scale = boost::math::iround((4096*std::pow(2, ceil_log2(rate_cubed)))/(1.65*rate_cubed)); - _iface->poke32(REG_DSP_TX_SCALE_IQ, (boost::uint32_t(scale) << 16) | (boost::uint32_t(scale) << 0)); + const double rate_pow = std::pow(double(interp & 0xff), 3); + _scaling_adjustment = std::pow(2, ceil_log2(rate_pow))/(1.65*rate_pow); + this->update_scalar(); return _tick_rate/interp_rate; } + void update_scalar(void){ + const double target_scalar = (1 << 17)*_scaling_adjustment/_dsp_extra_scaling; + const boost::int32_t actual_scalar = boost::math::iround(target_scalar); + _fxpt_scalar_correction = target_scalar/actual_scalar; //should be small + _iface->poke32(REG_DSP_TX_SCALE_IQ, actual_scalar); + } + + double get_scaling_adjustment(void){ + return _fxpt_scalar_correction*_host_extra_scaling*32767.; + } + double set_freq(const double freq_){ //correct for outside of rate (wrap around) double freq = std::fmod(freq_, _tick_rate); @@ -156,10 +174,38 @@ public: _iface->poke32(REG_TX_CTRL_PACKETS_PER_UP, (packets_per_up == 0)? 0 : (FLAG_TX_CTRL_UP_ENB | packets_per_up)); } + void setup(const uhd::stream_args_t &stream_args){ + if (not stream_args.args.has_key("noclear")) this->clear(); + + unsigned format_word = 0; + if (stream_args.otw_format == "sc16"){ + format_word = 0; + _dsp_extra_scaling = 1.0; + _host_extra_scaling = 1.0; + } + else if (stream_args.otw_format == "sc8"){ + format_word = (1 << 0); + double peak = stream_args.args.cast<double>("peak", 1.0); + peak = std::max(peak, 1.0/256); + _host_extra_scaling = 1.0/peak/256; + _dsp_extra_scaling = 1.0/peak; + } + else throw uhd::value_error("USRP TX cannot handle requested wire format: " + stream_args.otw_format); + + this->update_scalar(); + + _iface->poke32(REG_TX_CTRL_FORMAT, format_word); + + if (stream_args.args.has_key("underflow_policy")){ + this->set_underflow_policy(stream_args.args["underflow_policy"]); + } + } + private: wb_iface::sptr _iface; const size_t _dsp_base, _ctrl_base; double _tick_rate, _link_rate; + double _scaling_adjustment, _dsp_extra_scaling, _host_extra_scaling, _fxpt_scalar_correction; const boost::uint32_t _sid; }; diff --git a/host/lib/usrp/cores/tx_dsp_core_200.hpp b/host/lib/usrp/cores/tx_dsp_core_200.hpp index 4b39a5b07..0e1cfb6bc 100644 --- a/host/lib/usrp/cores/tx_dsp_core_200.hpp +++ b/host/lib/usrp/cores/tx_dsp_core_200.hpp @@ -19,6 +19,7 @@ #define INCLUDED_LIBUHD_USRP_TX_DSP_CORE_200_HPP #include <uhd/config.hpp> +#include <uhd/stream.hpp> #include <uhd/types/ranges.hpp> #include <boost/utility.hpp> #include <boost/shared_ptr.hpp> @@ -44,14 +45,15 @@ public: virtual uhd::meta_range_t get_host_rates(void) = 0; + virtual double get_scaling_adjustment(void) = 0; + virtual uhd::meta_range_t get_freq_range(void) = 0; virtual double set_freq(const double freq) = 0; virtual void set_updates(const size_t cycles_per_up, const size_t packets_per_up) = 0; - virtual void set_underflow_policy(const std::string &policy) = 0; - + virtual void setup(const uhd::stream_args_t &stream_args) = 0; }; #endif /* INCLUDED_LIBUHD_USRP_TX_DSP_CORE_200_HPP */ diff --git a/host/lib/usrp/cores/user_settings_core_200.cpp b/host/lib/usrp/cores/user_settings_core_200.cpp new file mode 100644 index 000000000..d262631b1 --- /dev/null +++ b/host/lib/usrp/cores/user_settings_core_200.cpp @@ -0,0 +1,43 @@ +// +// Copyright 2012 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 "user_settings_core_200.hpp" + +#define REG_USER_ADDR _base + 0 +#define REG_USER_DATA _base + 4 + +class user_settings_core_200_impl : public user_settings_core_200{ +public: + user_settings_core_200_impl(wb_iface::sptr iface, const size_t base): + _iface(iface), _base(base) + { + //NOP + } + + void set_reg(const user_reg_t ®){ + _iface->poke32(REG_USER_ADDR, reg.first); + _iface->poke32(REG_USER_DATA, reg.second); + } + +private: + wb_iface::sptr _iface; + const size_t _base; +}; + +user_settings_core_200::sptr user_settings_core_200::make(wb_iface::sptr iface, const size_t base){ + return sptr(new user_settings_core_200_impl(iface, base)); +} diff --git a/host/lib/usrp/cores/user_settings_core_200.hpp b/host/lib/usrp/cores/user_settings_core_200.hpp new file mode 100644 index 000000000..1f5d13de7 --- /dev/null +++ b/host/lib/usrp/cores/user_settings_core_200.hpp @@ -0,0 +1,36 @@ +// +// Copyright 2012 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_LIBUHD_USRP_USER_SETTINGS_CORE_200_HPP +#define INCLUDED_LIBUHD_USRP_USER_SETTINGS_CORE_200_HPP + +#include <uhd/config.hpp> +#include <boost/utility.hpp> +#include <boost/shared_ptr.hpp> +#include "wb_iface.hpp" + +class user_settings_core_200 : boost::noncopyable{ +public: + typedef boost::shared_ptr<user_settings_core_200> sptr; + typedef std::pair<boost::uint8_t, boost::uint32_t> user_reg_t; + + static sptr make(wb_iface::sptr iface, const size_t base); + + virtual void set_reg(const user_reg_t ®) = 0; +}; + +#endif /* INCLUDED_LIBUHD_USRP_USER_SETTINGS_CORE_200_HPP */ diff --git a/host/lib/usrp/e100/e100_impl.cpp b/host/lib/usrp/e100/e100_impl.cpp index f58138ae6..a01ce4a7b 100644 --- a/host/lib/usrp/e100/e100_impl.cpp +++ b/host/lib/usrp/e100/e100_impl.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-2012 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 @@ -326,10 +326,10 @@ e100_impl::e100_impl(const uhd::device_addr_t &device_addr){ // create time control objects //////////////////////////////////////////////////////////////////// time64_core_200::readback_bases_type time64_rb_bases; - time64_rb_bases.rb_secs_now = E100_REG_RB_TIME_NOW_SECS; - time64_rb_bases.rb_ticks_now = E100_REG_RB_TIME_NOW_TICKS; - time64_rb_bases.rb_secs_pps = E100_REG_RB_TIME_PPS_SECS; - time64_rb_bases.rb_ticks_pps = E100_REG_RB_TIME_PPS_TICKS; + time64_rb_bases.rb_hi_now = E100_REG_RB_TIME_NOW_HI; + time64_rb_bases.rb_lo_now = E100_REG_RB_TIME_NOW_LO; + time64_rb_bases.rb_hi_pps = E100_REG_RB_TIME_PPS_HI; + time64_rb_bases.rb_lo_pps = E100_REG_RB_TIME_PPS_LO; _time64 = time64_core_200::make( _fpga_ctrl, E100_REG_SR_ADDR(UE_SR_TIME64), time64_rb_bases ); @@ -353,6 +353,13 @@ e100_impl::e100_impl(const uhd::device_addr_t &device_addr){ _tree->create<std::vector<std::string> >(mb_path / "clock_source/options").set(clock_sources); //////////////////////////////////////////////////////////////////// + // create user-defined control objects + //////////////////////////////////////////////////////////////////// + _user = user_settings_core_200::make(_fpga_ctrl, E100_REG_SR_ADDR(UE_SR_USER_REGS)); + _tree->create<user_settings_core_200::user_reg_t>(mb_path / "user/regs") + .subscribe(boost::bind(&user_settings_core_200::set_reg, _user, _1)); + + //////////////////////////////////////////////////////////////////// // create dboard control objects //////////////////////////////////////////////////////////////////// diff --git a/host/lib/usrp/e100/e100_impl.hpp b/host/lib/usrp/e100/e100_impl.hpp index 2ea890375..1d36cb2ac 100644 --- a/host/lib/usrp/e100/e100_impl.hpp +++ b/host/lib/usrp/e100/e100_impl.hpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-2012 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 @@ -25,6 +25,7 @@ #include "rx_dsp_core_200.hpp" #include "tx_dsp_core_200.hpp" #include "time64_core_200.hpp" +#include "user_settings_core_200.hpp" #include <uhd/device.hpp> #include <uhd/property_tree.hpp> #include <uhd/utils/pimpl.hpp> @@ -48,7 +49,7 @@ static const double E100_RX_LINK_RATE_BPS = 166e6/3/2*2; static const double E100_TX_LINK_RATE_BPS = 166e6/3/1*2; static const std::string E100_I2C_DEV_NODE = "/dev/i2c-3"; static const std::string E100_UART_DEV_NODE = "/dev/ttyO0"; -static const boost::uint16_t E100_FPGA_COMPAT_NUM = 0x08; +static const boost::uint16_t E100_FPGA_COMPAT_NUM = 0x09; static const boost::uint32_t E100_RX_SID_BASE = 2; static const boost::uint32_t E100_TX_ASYNC_SID = 1; static const double E100_DEFAULT_CLOCK_RATE = 64e6; @@ -92,6 +93,7 @@ private: std::vector<rx_dsp_core_200::sptr> _rx_dsps; tx_dsp_core_200::sptr _tx_dsp; time64_core_200::sptr _time64; + user_settings_core_200::sptr _user; e100_clock_ctrl::sptr _clock_ctrl; e100_codec_ctrl::sptr _codec_ctrl; e100_ctrl::sptr _fpga_ctrl; diff --git a/host/lib/usrp/e100/e100_regs.hpp b/host/lib/usrp/e100/e100_regs.hpp index f24f5895b..75be2cfbe 100644 --- a/host/lib/usrp/e100/e100_regs.hpp +++ b/host/lib/usrp/e100/e100_regs.hpp @@ -1,4 +1,19 @@ - +// +// Copyright 2010-2012 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/>. +// //////////////////////////////////////////////////////////////// // @@ -71,10 +86,10 @@ #define E100_REG_RB_MUX_32_BASE E100_REG_SLAVE(7) -#define E100_REG_RB_TIME_NOW_SECS E100_REG_RB_MUX_32_BASE + 0 -#define E100_REG_RB_TIME_NOW_TICKS E100_REG_RB_MUX_32_BASE + 4 -#define E100_REG_RB_TIME_PPS_SECS E100_REG_RB_MUX_32_BASE + 8 -#define E100_REG_RB_TIME_PPS_TICKS E100_REG_RB_MUX_32_BASE + 12 +#define E100_REG_RB_TIME_NOW_HI E100_REG_RB_MUX_32_BASE + 0 +#define E100_REG_RB_TIME_NOW_LO E100_REG_RB_MUX_32_BASE + 4 +#define E100_REG_RB_TIME_PPS_HI E100_REG_RB_MUX_32_BASE + 8 +#define E100_REG_RB_TIME_PPS_LO E100_REG_RB_MUX_32_BASE + 12 #define E100_REG_RB_MISC_TEST32 E100_REG_RB_MUX_32_BASE + 16 #define E100_REG_RB_ERR_STATUS E100_REG_RB_MUX_32_BASE + 20 #define E100_REG_RB_COMPAT E100_REG_RB_MUX_32_BASE + 24 @@ -101,9 +116,9 @@ #define UE_SR_TX_FRONT 54 // 5 regs (+0 to +4) #define UE_SR_REG_TEST32 60 // 1 reg -#define UE_SR_CLEAR_RX_FIFO 61 // 1 reg -#define UE_SR_CLEAR_TX_FIFO 62 // 1 reg +#define UE_SR_CLEAR_FIFO 61 // 1 reg #define UE_SR_GLOBAL_RESET 63 // 1 reg +#define UE_SR_USER_REGS 64 // 2 regs #define UE_SR_GPIO 128 @@ -115,8 +130,7 @@ ///////////////////////////////////////////////// // Magic reset regs //////////////////////////////////////////////// -#define E100_REG_CLEAR_RX E100_REG_SR_ADDR(UE_SR_CLEAR_RX_FIFO) -#define E100_REG_CLEAR_TX E100_REG_SR_ADDR(UE_SR_CLEAR_RX_FIFO) +#define E100_REG_CLEAR_FIFO E100_REG_SR_ADDR(UE_SR_CLEAR_FIFO) #define E100_REG_GLOBAL_RESET E100_REG_SR_ADDR(UE_SR_GLOBAL_RESET) #endif diff --git a/host/lib/usrp/e100/io_impl.cpp b/host/lib/usrp/e100/io_impl.cpp index 441e32a8d..e9608125f 100644 --- a/host/lib/usrp/e100/io_impl.cpp +++ b/host/lib/usrp/e100/io_impl.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-2012 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 @@ -17,6 +17,7 @@ #include "recv_packet_demuxer.hpp" #include "validate_subdev_spec.hpp" +#include "async_packet_handler.hpp" #include "../../transport/super_recv_packet_handler.hpp" #include "../../transport/super_send_packet_handler.hpp" #include <linux/usrp_e.h> //ioctl structures and constants @@ -50,7 +51,7 @@ using namespace uhd::transport; **********************************************************************/ struct e100_impl::io_impl{ io_impl(void): - false_alarm(0), async_msg_fifo(100/*messages deep*/) + false_alarm(0), async_msg_fifo(1000/*messages deep*/) { /* NOP */ } double tick_rate; //set by update tick rate method @@ -123,28 +124,13 @@ void e100_impl::io_impl::handle_irq(void){ //fill in the async metadata async_metadata_t metadata; - metadata.channel = 0; - metadata.has_time_spec = if_packet_info.has_tsi and if_packet_info.has_tsf; - metadata.time_spec = time_spec_t( - time_t(if_packet_info.tsi), long(if_packet_info.tsf), tick_rate - ); - metadata.event_code = async_metadata_t::event_code_t(sph::get_context_code(data.buf, if_packet_info)); + load_metadata_from_buff(uhd::wtohx<boost::uint32_t>, metadata, if_packet_info, data.buf, tick_rate); //push the message onto the queue async_msg_fifo.push_with_pop_on_full(metadata); //print some fastpath messages - if (metadata.event_code & - ( async_metadata_t::EVENT_CODE_UNDERFLOW - | async_metadata_t::EVENT_CODE_UNDERFLOW_IN_PACKET) - ) UHD_MSG(fastpath) << "U"; - else if (metadata.event_code & - ( async_metadata_t::EVENT_CODE_SEQ_ERROR - | async_metadata_t::EVENT_CODE_SEQ_ERROR_IN_BURST) - ) UHD_MSG(fastpath) << "S"; - else if (metadata.event_code & - async_metadata_t::EVENT_CODE_TIME_ERROR - ) UHD_MSG(fastpath) << "L"; + standard_async_msg_prints(metadata); } //prepare for the next round @@ -164,9 +150,8 @@ void e100_impl::io_init(void){ _io_impl->demuxer = recv_packet_demuxer::make(_data_transport, _rx_dsps.size(), E100_RX_SID_BASE); _io_impl->iface = _fpga_ctrl; - //clear state machines - _fpga_ctrl->poke32(E100_REG_CLEAR_RX, 0); - _fpga_ctrl->poke32(E100_REG_CLEAR_TX, 0); + //clear fifo state machines + _fpga_ctrl->poke32(E100_REG_CLEAR_FIFO, 0); //allocate streamer weak ptrs containers _rx_streamers.resize(_rx_dsps.size()); @@ -217,6 +202,8 @@ void e100_impl::update_tx_samp_rate(const size_t dspno, const double rate){ if (my_streamer.get() == NULL) return; my_streamer->set_samp_rate(rate); + const double adj = _tx_dsp->get_scaling_adjustment(); + my_streamer->set_scale_factor(adj); } void e100_impl::update_rates(void){ @@ -278,13 +265,13 @@ rx_streamer::sptr e100_impl::get_rx_stream(const uhd::stream_args_t &args_){ //setup defaults for unspecified values args.otw_format = args.otw_format.empty()? "sc16" : args.otw_format; args.channels = args.channels.empty()? std::vector<size_t>(1, 0) : args.channels; - const unsigned sc8_scalar = unsigned(args.args.cast<double>("scalar", 0x400)); //calculate packet size static const size_t hdr_size = 0 + vrt::max_if_hdr_words32*sizeof(boost::uint32_t) + sizeof(vrt::if_packet_info_t().tlr) //forced to have trailer - sizeof(vrt::if_packet_info_t().cid) //no class id ever used + - sizeof(vrt::if_packet_info_t().tsi) //no int time ever used ; const size_t bpp = _data_transport->get_recv_frame_size() - hdr_size; const size_t bpi = convert::get_bytes_per_item(args.otw_format); @@ -309,8 +296,7 @@ rx_streamer::sptr e100_impl::get_rx_stream(const uhd::stream_args_t &args_){ for (size_t chan_i = 0; chan_i < args.channels.size(); chan_i++){ const size_t dsp = args.channels[chan_i]; _rx_dsps[dsp]->set_nsamps_per_packet(spp); //seems to be a good place to set this - if (not args.args.has_key("noclear")) _rx_dsps[dsp]->clear(); - _rx_dsps[dsp]->set_format(args.otw_format, sc8_scalar); + _rx_dsps[dsp]->setup(args); my_streamer->set_xport_chan_get_buff(chan_i, boost::bind( &recv_packet_demuxer::get_recv_buff, _io_impl->demuxer, dsp, _1 ), true /*flush*/); @@ -336,14 +322,13 @@ tx_streamer::sptr e100_impl::get_tx_stream(const uhd::stream_args_t &args_){ args.otw_format = args.otw_format.empty()? "sc16" : args.otw_format; args.channels = args.channels.empty()? std::vector<size_t>(1, 0) : args.channels; - if (args.otw_format != "sc16"){ - throw uhd::value_error("USRP TX cannot handle requested wire format: " + args.otw_format); - } - //calculate packet size static const size_t hdr_size = 0 + vrt::max_if_hdr_words32*sizeof(boost::uint32_t) + + sizeof(vrt::if_packet_info_t().tlr) //forced to have trailer + - sizeof(vrt::if_packet_info_t().sid) //no stream id ever used - sizeof(vrt::if_packet_info_t().cid) //no class id ever used + - sizeof(vrt::if_packet_info_t().tsi) //no int time ever used ; static const size_t bpp = _data_transport->get_send_frame_size() - hdr_size; const size_t spp = bpp/convert::get_bytes_per_item(args.otw_format); @@ -367,8 +352,7 @@ tx_streamer::sptr e100_impl::get_tx_stream(const uhd::stream_args_t &args_){ for (size_t chan_i = 0; chan_i < args.channels.size(); chan_i++){ const size_t dsp = args.channels[chan_i]; UHD_ASSERT_THROW(dsp == 0); //always 0 - if (not args.args.has_key("noclear")) _tx_dsp->clear(); - if (args.args.has_key("underflow_policy")) _tx_dsp->set_underflow_policy(args.args["underflow_policy"]); + _tx_dsp->setup(args); my_streamer->set_xport_chan_get_buff(chan_i, boost::bind( &zero_copy_if::get_send_buff, _data_transport, _1 )); diff --git a/host/lib/usrp/multi_usrp.cpp b/host/lib/usrp/multi_usrp.cpp index 6cd9f95bb..d9be19b83 100644 --- a/host/lib/usrp/multi_usrp.cpp +++ b/host/lib/usrp/multi_usrp.cpp @@ -445,6 +445,17 @@ public: return _tree->list(mb_root(mboard) / "sensors"); } + void set_user_register(const boost::uint8_t addr, const boost::uint32_t data, size_t mboard){ + if (mboard != ALL_MBOARDS){ + typedef std::pair<boost::uint8_t, boost::uint32_t> user_reg_t; + _tree->access<user_reg_t>(mb_root(mboard) / "user/reg").set(user_reg_t(addr, data)); + return; + } + for (size_t m = 0; m < get_num_mboards(); m++){ + set_user_register(addr, data, m); + } + } + /******************************************************************* * RX methods ******************************************************************/ diff --git a/host/lib/usrp/usrp1/io_impl.cpp b/host/lib/usrp/usrp1/io_impl.cpp index e9d9b65c2..f27135562 100644 --- a/host/lib/usrp/usrp1/io_impl.cpp +++ b/host/lib/usrp/usrp1/io_impl.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-2012 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 @@ -221,17 +221,17 @@ void usrp1_impl::io_init(void){ _io_impl = UHD_PIMPL_MAKE(io_impl, (_data_transport)); - //create a new vandal thread to poll xerflow conditions - _io_impl->vandal_task = task::make(boost::bind( - &usrp1_impl::vandal_conquest_loop, this - )); - //init as disabled, then call the real function (uses restore) this->enable_rx(false); this->enable_tx(false); rx_stream_on_off(false); tx_stream_on_off(false); _io_impl->flush_send_buff(); + + //create a new vandal thread to poll xerflow conditions + _io_impl->vandal_task = task::make(boost::bind( + &usrp1_impl::vandal_conquest_loop, this + )); } void usrp1_impl::rx_stream_on_off(bool enb){ diff --git a/host/lib/usrp/usrp1/soft_time_ctrl.cpp b/host/lib/usrp/usrp1/soft_time_ctrl.cpp index b8af8af06..90b3a92da 100644 --- a/host/lib/usrp/usrp1/soft_time_ctrl.cpp +++ b/host/lib/usrp/usrp1/soft_time_ctrl.cpp @@ -39,8 +39,8 @@ public: _nsamps_remaining(0), _stream_mode(stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS), _cmd_queue(2), - _async_msg_queue(100), - _inline_msg_queue(100), + _async_msg_queue(1000), + _inline_msg_queue(1000), _stream_on_off(stream_on_off) { //synchronously spawn a new thread diff --git a/host/lib/usrp/usrp1/usrp1_impl.cpp b/host/lib/usrp/usrp1/usrp1_impl.cpp index ef8ae950d..430ea59c8 100644 --- a/host/lib/usrp/usrp1/usrp1_impl.cpp +++ b/host/lib/usrp/usrp1/usrp1_impl.cpp @@ -210,6 +210,7 @@ usrp1_impl::usrp1_impl(const device_addr_t &device_addr){ //////////////////////////////////////////////////////////////////// // Initialize the properties tree //////////////////////////////////////////////////////////////////// + _rx_dc_offset_shadow = 0; _tree = property_tree::make(); _tree->create<std::string>("/name").set("USRP1 Device"); const fs_path mb_path = "/mboards/0"; diff --git a/host/lib/usrp/usrp2/fw_common.h b/host/lib/usrp/usrp2/fw_common.h index 1af1db860..0babf7445 100644 --- a/host/lib/usrp/usrp2/fw_common.h +++ b/host/lib/usrp/usrp2/fw_common.h @@ -30,9 +30,9 @@ extern "C" { #endif //fpga and firmware compatibility numbers -#define USRP2_FPGA_COMPAT_NUM 8 +#define USRP2_FPGA_COMPAT_NUM 9 #define USRP2_FW_COMPAT_NUM 11 -#define USRP2_FW_VER_MINOR 1 +#define USRP2_FW_VER_MINOR 2 //used to differentiate control packets over data port #define USRP2_INVALID_VRT_HEADER 0 diff --git a/host/lib/usrp/usrp2/io_impl.cpp b/host/lib/usrp/usrp2/io_impl.cpp index e3fa46920..d32ffb62c 100644 --- a/host/lib/usrp/usrp2/io_impl.cpp +++ b/host/lib/usrp/usrp2/io_impl.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-2012 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 "validate_subdev_spec.hpp" +#include "async_packet_handler.hpp" #include "../../transport/super_recv_packet_handler.hpp" #include "../../transport/super_send_packet_handler.hpp" #include "usrp2_impl.hpp" @@ -135,7 +136,8 @@ private: struct usrp2_impl::io_impl{ io_impl(void): - async_msg_fifo(100/*messages deep*/) + async_msg_fifo(1000/*messages deep*/), + tick_rate(1 /*non-zero default*/) { /* NOP */ } @@ -201,12 +203,7 @@ void usrp2_impl::io_impl::recv_pirate_loop( //fill in the async metadata async_metadata_t metadata; - metadata.channel = index; - metadata.has_time_spec = if_packet_info.has_tsi and if_packet_info.has_tsf; - metadata.time_spec = time_spec_t( - time_t(if_packet_info.tsi), size_t(if_packet_info.tsf), tick_rate - ); - metadata.event_code = async_metadata_t::event_code_t(sph::get_context_code(vrt_hdr, if_packet_info)); + load_metadata_from_buff(uhd::ntohx<boost::uint32_t>, metadata, if_packet_info, vrt_hdr, tick_rate, index); //catch the flow control packets and react if (metadata.event_code == 0){ @@ -217,17 +214,7 @@ void usrp2_impl::io_impl::recv_pirate_loop( //else UHD_MSG(often) << "metadata.event_code " << metadata.event_code << std::endl; async_msg_fifo.push_with_pop_on_full(metadata); - if (metadata.event_code & - ( async_metadata_t::EVENT_CODE_UNDERFLOW - | async_metadata_t::EVENT_CODE_UNDERFLOW_IN_PACKET) - ) UHD_MSG(fastpath) << "U"; - else if (metadata.event_code & - ( async_metadata_t::EVENT_CODE_SEQ_ERROR - | async_metadata_t::EVENT_CODE_SEQ_ERROR_IN_BURST) - ) UHD_MSG(fastpath) << "S"; - else if (metadata.event_code & - async_metadata_t::EVENT_CODE_TIME_ERROR - ) UHD_MSG(fastpath) << "L"; + standard_async_msg_prints(metadata); } else{ //TODO unknown received packet, may want to print error... @@ -307,6 +294,8 @@ void usrp2_impl::update_tx_samp_rate(const std::string &mb, const size_t dsp, co if (my_streamer.get() == NULL) return; my_streamer->set_samp_rate(rate); + const double adj = _mbc[mb].tx_dsp->get_scaling_adjustment(); + my_streamer->set_scale_factor(adj); } void usrp2_impl::update_rates(void){ @@ -380,13 +369,13 @@ rx_streamer::sptr usrp2_impl::get_rx_stream(const uhd::stream_args_t &args_){ //setup defaults for unspecified values args.otw_format = args.otw_format.empty()? "sc16" : args.otw_format; args.channels = args.channels.empty()? std::vector<size_t>(1, 0) : args.channels; - const unsigned sc8_scalar = unsigned(args.args.cast<double>("scalar", 0x400)); //calculate packet size static const size_t hdr_size = 0 + vrt::max_if_hdr_words32*sizeof(boost::uint32_t) + sizeof(vrt::if_packet_info_t().tlr) //forced to have trailer - sizeof(vrt::if_packet_info_t().cid) //no class id ever used + - sizeof(vrt::if_packet_info_t().tsi) //no int time ever used ; const size_t bpp = _mbc[_mbc.keys().front()].rx_dsp_xports[0]->get_recv_frame_size() - hdr_size; const size_t bpi = convert::get_bytes_per_item(args.otw_format); @@ -416,8 +405,7 @@ rx_streamer::sptr usrp2_impl::get_rx_stream(const uhd::stream_args_t &args_){ if (chan < num_chan_so_far){ const size_t dsp = chan + _mbc[mb].rx_chan_occ - num_chan_so_far; _mbc[mb].rx_dsps[dsp]->set_nsamps_per_packet(spp); //seems to be a good place to set this - if (not args.args.has_key("noclear")) _mbc[mb].rx_dsps[dsp]->clear(); - _mbc[mb].rx_dsps[dsp]->set_format(args.otw_format, sc8_scalar); + _mbc[mb].rx_dsps[dsp]->setup(args); my_streamer->set_xport_chan_get_buff(chan_i, boost::bind( &zero_copy_if::get_recv_buff, _mbc[mb].rx_dsp_xports[dsp], _1 ), true /*flush*/); @@ -447,15 +435,14 @@ tx_streamer::sptr usrp2_impl::get_tx_stream(const uhd::stream_args_t &args_){ args.otw_format = args.otw_format.empty()? "sc16" : args.otw_format; args.channels = args.channels.empty()? std::vector<size_t>(1, 0) : args.channels; - if (args.otw_format != "sc16"){ - throw uhd::value_error("USRP TX cannot handle requested wire format: " + args.otw_format); - } - //calculate packet size static const size_t hdr_size = 0 - + vrt::max_if_hdr_words32*sizeof(boost::uint32_t) + vrt_send_header_offset_words32*sizeof(boost::uint32_t) + + vrt::max_if_hdr_words32*sizeof(boost::uint32_t) + + sizeof(vrt::if_packet_info_t().tlr) //forced to have trailer - sizeof(vrt::if_packet_info_t().cid) //no class id ever used + - sizeof(vrt::if_packet_info_t().sid) //no stream id ever used + - sizeof(vrt::if_packet_info_t().tsi) //no int time ever used ; const size_t bpp = _mbc[_mbc.keys().front()].tx_dsp_xport->get_send_frame_size() - hdr_size; const size_t spp = bpp/convert::get_bytes_per_item(args.otw_format); @@ -485,10 +472,9 @@ tx_streamer::sptr usrp2_impl::get_tx_stream(const uhd::stream_args_t &args_){ if (chan < num_chan_so_far){ const size_t dsp = chan + _mbc[mb].tx_chan_occ - num_chan_so_far; if (not args.args.has_key("noclear")){ - _mbc[mb].tx_dsp->clear(); _io_impl->fc_mons[abs]->clear(); } - if (args.args.has_key("underflow_policy")) _mbc[mb].tx_dsp->set_underflow_policy(args.args["underflow_policy"]); + _mbc[mb].tx_dsp->setup(args); my_streamer->set_xport_chan_get_buff(chan_i, boost::bind( &usrp2_impl::io_impl::get_send_buff, _io_impl.get(), abs, _1 )); diff --git a/host/lib/usrp/usrp2/usrp2_iface.cpp b/host/lib/usrp/usrp2/usrp2_iface.cpp index 4830c10d9..eeba6756e 100644 --- a/host/lib/usrp/usrp2/usrp2_iface.cpp +++ b/host/lib/usrp/usrp2/usrp2_iface.cpp @@ -87,7 +87,7 @@ public: //Obtain the firmware's compat number. //Save the response compat number for communication. //TODO can choose to reject certain older compat numbers - usrp2_ctrl_data_t ctrl_data; + usrp2_ctrl_data_t ctrl_data = usrp2_ctrl_data_t(); ctrl_data.id = htonl(USRP2_CTRL_ID_WAZZUP_BRO); ctrl_data = ctrl_send_and_recv(ctrl_data, 0, ~0); if (ntohl(ctrl_data.id) != USRP2_CTRL_ID_WAZZUP_DUDE) @@ -126,10 +126,9 @@ public: bool is_device_locked(void){ boost::uint32_t lock_secs = this->get_reg<boost::uint32_t, USRP2_REG_ACTION_FW_PEEK32>(U2_FW_REG_LOCK_TIME); boost::uint32_t lock_gpid = this->get_reg<boost::uint32_t, USRP2_REG_ACTION_FW_PEEK32>(U2_FW_REG_LOCK_GPID); - boost::uint32_t curr_secs = this->peek32(U2_REG_TIME64_SECS_RB_IMM); //if the difference is larger, assume not locked anymore - if (curr_secs - lock_secs >= 3) return false; + if (this->get_curr_secs() - lock_secs >= 3) return false; //otherwise only lock if the device hash is different that ours return lock_gpid != boost::uint32_t(get_gpid()); @@ -137,12 +136,16 @@ public: void lock_task(void){ //re-lock in task - boost::uint32_t curr_secs = this->peek32(U2_REG_TIME64_SECS_RB_IMM); - this->get_reg<boost::uint32_t, USRP2_REG_ACTION_FW_POKE32>(U2_FW_REG_LOCK_TIME, curr_secs); + this->get_reg<boost::uint32_t, USRP2_REG_ACTION_FW_POKE32>(U2_FW_REG_LOCK_TIME, this->get_curr_secs()); //sleep for a bit boost::this_thread::sleep(boost::posix_time::milliseconds(1500)); } + boost::uint32_t get_curr_secs(void){ + //may not be the right tick rate, but this is ok for locking purposes + return boost::uint32_t(this->peek32(U2_REG_TIME64_LO_RB_IMM)/100e6); + } + /*********************************************************************** * Peek and Poke **********************************************************************/ diff --git a/host/lib/usrp/usrp2/usrp2_impl.cpp b/host/lib/usrp/usrp2/usrp2_impl.cpp index ef7151cee..2077ab009 100644 --- a/host/lib/usrp/usrp2/usrp2_impl.cpp +++ b/host/lib/usrp/usrp2/usrp2_impl.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-2012 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 @@ -546,10 +546,10 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr){ // create time control objects //////////////////////////////////////////////////////////////// time64_core_200::readback_bases_type time64_rb_bases; - time64_rb_bases.rb_secs_now = U2_REG_TIME64_SECS_RB_IMM; - time64_rb_bases.rb_ticks_now = U2_REG_TIME64_TICKS_RB_IMM; - time64_rb_bases.rb_secs_pps = U2_REG_TIME64_SECS_RB_PPS; - time64_rb_bases.rb_ticks_pps = U2_REG_TIME64_TICKS_RB_PPS; + time64_rb_bases.rb_hi_now = U2_REG_TIME64_HI_RB_IMM; + time64_rb_bases.rb_lo_now = U2_REG_TIME64_LO_RB_IMM; + time64_rb_bases.rb_hi_pps = U2_REG_TIME64_HI_RB_PPS; + time64_rb_bases.rb_lo_pps = U2_REG_TIME64_LO_RB_PPS; _mbc[mb].time64 = time64_core_200::make( _mbc[mb].iface, U2_REG_SR_ADDR(SR_TIME64), time64_rb_bases, mimo_clock_sync_delay_cycles ); @@ -572,6 +572,13 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr){ static const std::vector<std::string> clock_sources = boost::assign::list_of("internal")("external")("mimo"); _tree->create<std::vector<std::string> >(mb_path / "clock_source/options").set(clock_sources); + //////////////////////////////////////////////////////////////////// + // create user-defined control objects + //////////////////////////////////////////////////////////////////// + _mbc[mb].user = user_settings_core_200::make(_mbc[mb].iface, U2_REG_SR_ADDR(SR_USER_REGS)); + _tree->create<user_settings_core_200::user_reg_t>(mb_path / "user/regs") + .subscribe(boost::bind(&user_settings_core_200::set_reg, _mbc[mb].user, _1)); + //////////////////////////////////////////////////////////////// // create dboard control objects //////////////////////////////////////////////////////////////// diff --git a/host/lib/usrp/usrp2/usrp2_impl.hpp b/host/lib/usrp/usrp2/usrp2_impl.hpp index 278dc713e..882a61f80 100644 --- a/host/lib/usrp/usrp2/usrp2_impl.hpp +++ b/host/lib/usrp/usrp2/usrp2_impl.hpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-2012 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 @@ -26,6 +26,7 @@ #include "rx_dsp_core_200.hpp" #include "tx_dsp_core_200.hpp" #include "time64_core_200.hpp" +#include "user_settings_core_200.hpp" #include <uhd/property_tree.hpp> #include <uhd/usrp/gps_ctrl.hpp> #include <uhd/device.hpp> @@ -91,6 +92,7 @@ private: std::vector<boost::weak_ptr<uhd::tx_streamer> > tx_streamers; tx_dsp_core_200::sptr tx_dsp; time64_core_200::sptr time64; + user_settings_core_200::sptr user; std::vector<uhd::transport::zero_copy_if::sptr> rx_dsp_xports; uhd::transport::zero_copy_if::sptr tx_dsp_xport; uhd::usrp::dboard_manager::sptr dboard_manager; diff --git a/host/lib/usrp/usrp2/usrp2_regs.hpp b/host/lib/usrp/usrp2/usrp2_regs.hpp index 179a930c6..e14798ecb 100644 --- a/host/lib/usrp/usrp2/usrp2_regs.hpp +++ b/host/lib/usrp/usrp2/usrp2_regs.hpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-2012 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 @@ -39,7 +39,7 @@ #define SR_SIMTIMER 8 // 2 #define SR_TIME64 10 // 6 #define SR_BUF_POOL 16 // 4 - +#define SR_USER_REGS 20 // 2 #define SR_RX_FRONT 24 // 5 #define SR_RX_CTRL0 32 // 9 #define SR_RX_DSP0 48 // 7 @@ -97,11 +97,11 @@ //////////////////////////////////////////////// #define U2_REG_STATUS READBACK_BASE + 4*8 #define U2_REG_GPIO_RB READBACK_BASE + 4*9 -#define U2_REG_TIME64_SECS_RB_IMM READBACK_BASE + 4*10 -#define U2_REG_TIME64_TICKS_RB_IMM READBACK_BASE + 4*11 +#define U2_REG_TIME64_HI_RB_IMM READBACK_BASE + 4*10 +#define U2_REG_TIME64_LO_RB_IMM READBACK_BASE + 4*11 #define U2_REG_COMPAT_NUM_RB READBACK_BASE + 4*12 #define U2_REG_IRQ_RB READBACK_BASE + 4*13 -#define U2_REG_TIME64_SECS_RB_PPS READBACK_BASE + 4*14 -#define U2_REG_TIME64_TICKS_RB_PPS READBACK_BASE + 4*15 +#define U2_REG_TIME64_HI_RB_PPS READBACK_BASE + 4*14 +#define U2_REG_TIME64_LO_RB_PPS READBACK_BASE + 4*15 #endif /* INCLUDED_USRP2_REGS_HPP */ diff --git a/host/tests/convert_test.cpp b/host/tests/convert_test.cpp index 1a5d30080..6b0ae53a9 100644 --- a/host/tests/convert_test.cpp +++ b/host/tests/convert_test.cpp @@ -1,5 +1,5 @@ // -// Copyright 2011-2011 Ettus Research LLC +// Copyright 2011-2012 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 @@ -31,8 +31,9 @@ typedef std::complex<boost::int16_t> sc16_t; typedef std::complex<float> fc32_t; typedef std::complex<double> fc64_t; -#define MY_CHECK_CLOSE(a, b, f) if ((std::abs(a) > (f))) \ - BOOST_CHECK_CLOSE_FRACTION(a, b, f) +#define MY_CHECK_CLOSE(a, b, f) { \ + BOOST_CHECK_MESSAGE(std::abs((a)-(b)) < f, "\n\t" << #a << " (" << (a) << ") error " << #b << " (" << (b) << ")"); \ +} /*********************************************************************** * Loopback runner: @@ -67,13 +68,13 @@ template <typename Range> static void loopback( * Test short conversion **********************************************************************/ static void test_convert_types_sc16( - size_t nsamps, convert::id_type &id + size_t nsamps, convert::id_type &id, const int extra_div = 1 ){ //fill the input samples std::vector<sc16_t> input(nsamps), output(nsamps); BOOST_FOREACH(sc16_t &in, input) in = sc16_t( - std::rand()-(RAND_MAX/2), - std::rand()-(RAND_MAX/2) + short(((std::rand()/double(RAND_MAX/2)) - 1)*32767/extra_div), + short(((std::rand()/double(RAND_MAX/2)) - 1)*32767/extra_div) ); //run the loopback and test @@ -116,15 +117,15 @@ BOOST_AUTO_TEST_CASE(test_convert_types_le_sc16){ **********************************************************************/ template <typename data_type> static void test_convert_types_for_floats( - size_t nsamps, convert::id_type &id + size_t nsamps, convert::id_type &id, const double extra_scale = 1.0 ){ typedef typename data_type::value_type value_type; //fill the input samples std::vector<data_type> input(nsamps), output(nsamps); BOOST_FOREACH(data_type &in, input) in = data_type( - (std::rand()/value_type(RAND_MAX/2)) - 1, - (std::rand()/value_type(RAND_MAX/2)) - 1 + ((std::rand()/value_type(RAND_MAX/2)) - 1)*float(extra_scale), + ((std::rand()/value_type(RAND_MAX/2)) - 1)*float(extra_scale) ); //run the loopback and test @@ -134,8 +135,8 @@ static void test_convert_types_for_floats( std::swap(out_id.num_inputs, out_id.num_outputs); loopback(nsamps, in_id, out_id, input, output); for (size_t i = 0; i < nsamps; i++){ - MY_CHECK_CLOSE(input[i].real(), output[i].real(), value_type(0.01)); - MY_CHECK_CLOSE(input[i].imag(), output[i].imag(), value_type(0.01)); + MY_CHECK_CLOSE(input[i].real(), output[i].real(), value_type(1./32767)); + MY_CHECK_CLOSE(input[i].imag(), output[i].imag(), value_type(1./32767)); } } @@ -280,3 +281,63 @@ BOOST_AUTO_TEST_CASE(test_convert_types_sc16_to_fc32){ MY_CHECK_CLOSE(input[i].imag()/float(32767), output[i].imag(), float(0.01)); } } + +/*********************************************************************** + * Test sc8 conversions + **********************************************************************/ +BOOST_AUTO_TEST_CASE(test_convert_types_fc64_and_sc8){ + convert::id_type id; + id.input_format = "fc64"; + id.num_inputs = 1; + id.num_outputs = 1; + + //try various lengths to test edge cases + id.output_format = "sc8_item32_le"; + for (size_t nsamps = 1; nsamps < 16; nsamps++){ + test_convert_types_for_floats<fc64_t>(nsamps, id, 1./256); + } + + //try various lengths to test edge cases + id.output_format = "sc8_item32_be"; + for (size_t nsamps = 1; nsamps < 16; nsamps++){ + test_convert_types_for_floats<fc64_t>(nsamps, id, 1./256); + } +} + +BOOST_AUTO_TEST_CASE(test_convert_types_fc32_and_sc8){ + convert::id_type id; + id.input_format = "fc32"; + id.num_inputs = 1; + id.num_outputs = 1; + + //try various lengths to test edge cases + id.output_format = "sc8_item32_le"; + for (size_t nsamps = 1; nsamps < 16; nsamps++){ + test_convert_types_for_floats<fc32_t>(nsamps, id, 1./256); + } + + //try various lengths to test edge cases + id.output_format = "sc8_item32_be"; + for (size_t nsamps = 1; nsamps < 16; nsamps++){ + test_convert_types_for_floats<fc32_t>(nsamps, id, 1./256); + } +} + +BOOST_AUTO_TEST_CASE(test_convert_types_sc16_and_sc8){ + convert::id_type id; + id.input_format = "sc16"; + id.num_inputs = 1; + id.num_outputs = 1; + + //try various lengths to test edge cases + id.output_format = "sc8_item32_le"; + for (size_t nsamps = 1; nsamps < 16; nsamps++){ + test_convert_types_sc16(nsamps, id, 256); + } + + //try various lengths to test edge cases + id.output_format = "sc8_item32_be"; + for (size_t nsamps = 1; nsamps < 16; nsamps++){ + test_convert_types_sc16(nsamps, id, 256); + } +} diff --git a/host/tests/sph_recv_test.cpp b/host/tests/sph_recv_test.cpp index 85d06aa0d..9b45d7016 100644 --- a/host/tests/sph_recv_test.cpp +++ b/host/tests/sph_recv_test.cpp @@ -1,5 +1,5 @@ // -// Copyright 2011 Ettus Research LLC +// Copyright 2011-2012 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 @@ -159,7 +159,7 @@ BOOST_AUTO_TEST_CASE(test_sph_recv_one_channel_normal){ BOOST_CHECK_EQUAL(metadata.error_code, uhd::rx_metadata_t::ERROR_CODE_NONE); BOOST_CHECK(not metadata.more_fragments); BOOST_CHECK(metadata.has_time_spec); - BOOST_CHECK_TS_CLOSE(metadata.time_spec, uhd::time_spec_t(0, num_accum_samps, SAMP_RATE)); + BOOST_CHECK_TS_CLOSE(metadata.time_spec, uhd::time_spec_t::from_ticks(num_accum_samps, SAMP_RATE)); BOOST_CHECK_EQUAL(num_samps_ret, 10 + i%10); num_accum_samps += num_samps_ret; } @@ -232,14 +232,14 @@ BOOST_AUTO_TEST_CASE(test_sph_recv_one_channel_sequence_error){ if (i == NUM_PKTS_TO_TEST/2){ //must get the soft overflow here BOOST_REQUIRE(metadata.error_code == uhd::rx_metadata_t::ERROR_CODE_OVERFLOW); - BOOST_CHECK_TS_CLOSE(metadata.time_spec, uhd::time_spec_t(0, num_accum_samps, SAMP_RATE)); + BOOST_CHECK_TS_CLOSE(metadata.time_spec, uhd::time_spec_t::from_ticks(num_accum_samps, SAMP_RATE)); num_accum_samps += 10 + i%10; } else{ BOOST_CHECK_EQUAL(metadata.error_code, uhd::rx_metadata_t::ERROR_CODE_NONE); BOOST_CHECK(not metadata.more_fragments); BOOST_CHECK(metadata.has_time_spec); - BOOST_CHECK_TS_CLOSE(metadata.time_spec, uhd::time_spec_t(0, num_accum_samps, SAMP_RATE)); + BOOST_CHECK_TS_CLOSE(metadata.time_spec, uhd::time_spec_t::from_ticks(num_accum_samps, SAMP_RATE)); BOOST_CHECK_EQUAL(num_samps_ret, 10 + i%10); num_accum_samps += num_samps_ret; } @@ -323,7 +323,7 @@ BOOST_AUTO_TEST_CASE(test_sph_recv_one_channel_inline_message){ BOOST_CHECK_EQUAL(metadata.error_code, uhd::rx_metadata_t::ERROR_CODE_NONE); BOOST_CHECK(not metadata.more_fragments); BOOST_CHECK(metadata.has_time_spec); - BOOST_CHECK_TS_CLOSE(metadata.time_spec, uhd::time_spec_t(0, num_accum_samps, SAMP_RATE)); + BOOST_CHECK_TS_CLOSE(metadata.time_spec, uhd::time_spec_t::from_ticks(num_accum_samps, SAMP_RATE)); BOOST_CHECK_EQUAL(num_samps_ret, 10 + i%10); num_accum_samps += num_samps_ret; if (i == NUM_PKTS_TO_TEST/2){ @@ -332,7 +332,7 @@ BOOST_AUTO_TEST_CASE(test_sph_recv_one_channel_inline_message){ ); std::cout << "metadata.error_code " << metadata.error_code << std::endl; BOOST_REQUIRE(metadata.error_code == uhd::rx_metadata_t::ERROR_CODE_OVERFLOW); - BOOST_CHECK_TS_CLOSE(metadata.time_spec, uhd::time_spec_t(0, num_accum_samps, SAMP_RATE)); + BOOST_CHECK_TS_CLOSE(metadata.time_spec, uhd::time_spec_t::from_ticks(num_accum_samps, SAMP_RATE)); BOOST_CHECK_EQUAL(overflow_handler.num_overflow, size_t(1)); } } @@ -414,7 +414,7 @@ BOOST_AUTO_TEST_CASE(test_sph_recv_multi_channel_normal){ BOOST_CHECK_EQUAL(metadata.error_code, uhd::rx_metadata_t::ERROR_CODE_NONE); BOOST_CHECK(not metadata.more_fragments); BOOST_CHECK(metadata.has_time_spec); - BOOST_CHECK_TS_CLOSE(metadata.time_spec, uhd::time_spec_t(0, num_accum_samps, SAMP_RATE)); + BOOST_CHECK_TS_CLOSE(metadata.time_spec, uhd::time_spec_t::from_ticks(num_accum_samps, SAMP_RATE)); BOOST_CHECK_EQUAL(num_samps_ret, 10 + i%10); num_accum_samps += num_samps_ret; } @@ -500,14 +500,14 @@ BOOST_AUTO_TEST_CASE(test_sph_recv_multi_channel_sequence_error){ if (i == NUM_PKTS_TO_TEST/2){ //must get the soft overflow here BOOST_REQUIRE(metadata.error_code == uhd::rx_metadata_t::ERROR_CODE_OVERFLOW); - BOOST_CHECK_TS_CLOSE(metadata.time_spec, uhd::time_spec_t(0, num_accum_samps, SAMP_RATE)); + BOOST_CHECK_TS_CLOSE(metadata.time_spec, uhd::time_spec_t::from_ticks(num_accum_samps, SAMP_RATE)); num_accum_samps += 10 + i%10; } else{ BOOST_CHECK_EQUAL(metadata.error_code, uhd::rx_metadata_t::ERROR_CODE_NONE); BOOST_CHECK(not metadata.more_fragments); BOOST_CHECK(metadata.has_time_spec); - BOOST_CHECK_TS_CLOSE(metadata.time_spec, uhd::time_spec_t(0, num_accum_samps, SAMP_RATE)); + BOOST_CHECK_TS_CLOSE(metadata.time_spec, uhd::time_spec_t::from_ticks(num_accum_samps, SAMP_RATE)); BOOST_CHECK_EQUAL(num_samps_ret, 10 + i%10); num_accum_samps += num_samps_ret; } @@ -593,7 +593,7 @@ BOOST_AUTO_TEST_CASE(test_sph_recv_multi_channel_time_error){ BOOST_CHECK_EQUAL(metadata.error_code, uhd::rx_metadata_t::ERROR_CODE_NONE); BOOST_CHECK(not metadata.more_fragments); BOOST_CHECK(metadata.has_time_spec); - BOOST_CHECK_TS_CLOSE(metadata.time_spec, uhd::time_spec_t(0, num_accum_samps, SAMP_RATE)); + BOOST_CHECK_TS_CLOSE(metadata.time_spec, uhd::time_spec_t::from_ticks(num_accum_samps, SAMP_RATE)); BOOST_CHECK_EQUAL(num_samps_ret, 10 + i%10); num_accum_samps += num_samps_ret; if (i == NUM_PKTS_TO_TEST/2){ @@ -677,7 +677,7 @@ BOOST_AUTO_TEST_CASE(test_sph_recv_multi_channel_fragment){ ); BOOST_CHECK_EQUAL(metadata.error_code, uhd::rx_metadata_t::ERROR_CODE_NONE); BOOST_CHECK(metadata.has_time_spec); - BOOST_CHECK_TS_CLOSE(metadata.time_spec, uhd::time_spec_t(0, num_accum_samps, SAMP_RATE)); + BOOST_CHECK_TS_CLOSE(metadata.time_spec, uhd::time_spec_t::from_ticks(num_accum_samps, SAMP_RATE)); BOOST_CHECK_EQUAL(num_samps_ret, 10); num_accum_samps += num_samps_ret; @@ -690,7 +690,7 @@ BOOST_AUTO_TEST_CASE(test_sph_recv_multi_channel_fragment){ BOOST_CHECK(not metadata.more_fragments); BOOST_CHECK_EQUAL(metadata.fragment_offset, 10); BOOST_CHECK(metadata.has_time_spec); - BOOST_CHECK_TS_CLOSE(metadata.time_spec, uhd::time_spec_t(0, num_accum_samps, SAMP_RATE)); + BOOST_CHECK_TS_CLOSE(metadata.time_spec, uhd::time_spec_t::from_ticks(num_accum_samps, SAMP_RATE)); BOOST_CHECK_EQUAL(num_samps_ret, i%10); num_accum_samps += num_samps_ret; } diff --git a/host/tests/sph_send_test.cpp b/host/tests/sph_send_test.cpp index 25a3f97ee..c31399d12 100644 --- a/host/tests/sph_send_test.cpp +++ b/host/tests/sph_send_test.cpp @@ -1,5 +1,5 @@ // -// Copyright 2011 Ettus Research LLC +// Copyright 2011-2012 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 @@ -136,9 +136,7 @@ BOOST_AUTO_TEST_CASE(test_sph_send_one_channel_one_packet_mode){ std::cout << "data check " << i << std::endl; dummy_send_xport.pop_front_packet(ifpi); BOOST_CHECK_EQUAL(ifpi.num_payload_words32, 10+i%10); - BOOST_CHECK(ifpi.has_tsi); BOOST_CHECK(ifpi.has_tsf); - BOOST_CHECK_EQUAL(ifpi.tsi, 0); BOOST_CHECK_EQUAL(ifpi.tsf, num_accum_samps*TICK_RATE/SAMP_RATE); BOOST_CHECK_EQUAL(ifpi.sob, i == 0); BOOST_CHECK_EQUAL(ifpi.eob, i == NUM_PKTS_TO_TEST-1); @@ -191,9 +189,7 @@ BOOST_AUTO_TEST_CASE(test_sph_send_one_channel_full_buffer_mode){ std::cout << "data check " << i << std::endl; dummy_send_xport.pop_front_packet(ifpi); BOOST_CHECK_EQUAL(ifpi.num_payload_words32, 20); - BOOST_CHECK(ifpi.has_tsi); BOOST_CHECK(ifpi.has_tsf); - BOOST_CHECK_EQUAL(ifpi.tsi, 0); BOOST_CHECK_EQUAL(ifpi.tsf, num_accum_samps*TICK_RATE/SAMP_RATE); BOOST_CHECK_EQUAL(ifpi.sob, i == 0); BOOST_CHECK_EQUAL(ifpi.eob, i == NUM_PKTS_TO_TEST-1); diff --git a/host/tests/time_spec_test.cpp b/host/tests/time_spec_test.cpp index 467da5c18..102b7cda3 100644 --- a/host/tests/time_spec_test.cpp +++ b/host/tests/time_spec_test.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-2012 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 @@ -54,11 +54,11 @@ BOOST_AUTO_TEST_CASE(test_time_spec_parts){ BOOST_CHECK_EQUAL(uhd::time_spec_t(1.1).get_full_secs(), 1); BOOST_CHECK_CLOSE(uhd::time_spec_t(1.1).get_frac_secs(), 0.1, 0.001); - BOOST_CHECK_EQUAL(uhd::time_spec_t(1.1).get_tick_count(100), 10); + BOOST_CHECK_EQUAL(uhd::time_spec_t(1.1).to_ticks(100), 110); BOOST_CHECK_EQUAL(uhd::time_spec_t(-1.1).get_full_secs(), -2); BOOST_CHECK_CLOSE(uhd::time_spec_t(-1.1).get_frac_secs(), 0.9, 0.001); - BOOST_CHECK_EQUAL(uhd::time_spec_t(-1.1).get_tick_count(100), 90); + BOOST_CHECK_EQUAL(uhd::time_spec_t(-1.1).to_ticks(100), -110); } BOOST_AUTO_TEST_CASE(test_time_spec_get_system_time){ diff --git a/host/usrp_e_utils/usrp-e-loopback.cpp b/host/usrp_e_utils/usrp-e-loopback.cpp index 5b289c648..d4220adc3 100644 --- a/host/usrp_e_utils/usrp-e-loopback.cpp +++ b/host/usrp_e_utils/usrp-e-loopback.cpp @@ -261,8 +261,7 @@ int main(int argc, char *argv[]){ poke16(E100_REG_MISC_XFER_RATE, (1<<8) | (1<<9)); //clear FIFO state in FPGA and kernel - poke32(E100_REG_CLEAR_RX, 0); - poke32(E100_REG_CLEAR_TX, 0); + poke32(E100_REG_CLEAR_FIFO, 0); ::close(fp); if ((fp = ::open("/dev/usrp_e0", O_RDWR)) < 0){ std::cerr << "Open failed" << std::endl; |