diff options
Diffstat (limited to 'host/lib')
63 files changed, 5607 insertions, 1008 deletions
diff --git a/host/lib/convert/CMakeLists.txt b/host/lib/convert/CMakeLists.txt index d189aa687..5f05b0cb8 100644 --- a/host/lib/convert/CMakeLists.txt +++ b/host/lib/convert/CMakeLists.txt @@ -26,6 +26,7 @@ MESSAGE(STATUS "")  ########################################################################  IF(CMAKE_COMPILER_IS_GNUCXX)      SET(EMMINTRIN_FLAGS -msse2) +    SET(NEON_FLAGS "-mfloat-abi=softfp -mfpu=neon")  ELSEIF(MSVC)      SET(EMMINTRIN_FLAGS /arch:SSE2)  ENDIF() @@ -47,12 +48,53 @@ ENDIF(HAVE_EMMINTRIN_H)  ########################################################################  # Check for NEON SIMD headers  ######################################################################## +SET(CMAKE_REQUIRED_FLAGS ${NEON_FLAGS})  CHECK_INCLUDE_FILE_CXX(arm_neon.h HAVE_ARM_NEON_H) -IF(HAVE_ARM_NEON_H) +UNSET(CMAKE_REQUIRED_FLAGS) +if(HAVE_ARM_NEON_H) +    MESSAGE(STATUS "Enabling NEON support") +    SET_SOURCE_FILES_PROPERTIES( +        ${CMAKE_CURRENT_SOURCE_DIR}/convert_with_neon.cpp +        PROPERTIES COMPILE_FLAGS "${NEON_FLAGS}" +    )      LIBUHD_APPEND_SOURCES(          ${CMAKE_CURRENT_SOURCE_DIR}/convert_with_neon.cpp      ) -ENDIF(HAVE_ARM_NEON_H) +else(HAVE_ARM_NEON_H) +    MESSAGE(STATUS "Disabling NEON support") +endif(HAVE_ARM_NEON_H) +######################################################################## +# Look for Orc support +######################################################################## +FIND_PACKAGE(PkgConfig) +IF(PKG_CONFIG_FOUND) +PKG_CHECK_MODULES(ORC "orc-0.4") +ENDIF(PKG_CONFIG_FOUND) + +FIND_PROGRAM(ORCC_EXECUTABLE orcc) + +IF(ORC_FOUND AND ORCC_EXECUTABLE) +    INCLUDE_DIRECTORIES(${ORC_INCLUDE_DIRS}) +    LINK_DIRECTORIES(${ORC_LIBRARY_DIRS}) +    ENABLE_LANGUAGE(C) + +    SET(orcc_src ${CMAKE_CURRENT_SOURCE_DIR}/convert_orc.orc) + +    GET_FILENAME_COMPONENT(orc_file_name_we ${orcc_src} NAME_WE) +    SET(orcc_gen ${CMAKE_CURRENT_BINARY_DIR}/${orc_file_name_we}.c) +    MESSAGE(STATUS "Orc found, enabling Orc support") +    ADD_CUSTOM_COMMAND( +        COMMAND ${ORCC_EXECUTABLE} --implementation -o ${orcc_gen} ${orcc_src} +        DEPENDS ${orcc_src} OUTPUT ${orcc_gen} +    ) +    LIBUHD_APPEND_SOURCES(${orcc_gen}) +    LIBUHD_APPEND_SOURCES( +        ${CMAKE_CURRENT_SOURCE_DIR}/convert_with_orc.cpp +    ) +    LIBUHD_APPEND_LIBS(${ORC_LIBRARIES}) +ELSE() +    MESSAGE(STATUS "Orc not found, disabling orc support...") +ENDIF(ORC_FOUND AND ORCC_EXECUTABLE)  ########################################################################  # Convert types generation diff --git a/host/lib/convert/convert_common.hpp b/host/lib/convert/convert_common.hpp index c2ca233d9..7f513b124 100644 --- a/host/lib/convert/convert_common.hpp +++ b/host/lib/convert/convert_common.hpp @@ -27,7 +27,7 @@      static void fcn( \          const uhd::convert::input_type &inputs, \          const uhd::convert::output_type &outputs, \ -        size_t nsamps \ +        size_t nsamps, double scale_factor \      ); \      UHD_STATIC_BLOCK(register_##fcn##_##prio){ \          uhd::convert::register_converter(#fcn, fcn, prio); \ @@ -35,7 +35,7 @@      static void fcn( \          const uhd::convert::input_type &inputs, \          const uhd::convert::output_type &outputs, \ -        size_t nsamps \ +        size_t nsamps, double scale_factor \      )  /*********************************************************************** @@ -50,7 +50,7 @@ typedef boost::uint32_t              item32_t;  /***********************************************************************   * Convert complex short buffer to items32   **********************************************************************/ -static UHD_INLINE item32_t sc16_to_item32(sc16_t num){ +static UHD_INLINE item32_t sc16_to_item32(sc16_t num, double){      boost::uint16_t real = num.real();      boost::uint16_t imag = num.imag();      return (item32_t(real) << 16) | (item32_t(imag) << 0); @@ -59,7 +59,7 @@ static UHD_INLINE item32_t sc16_to_item32(sc16_t num){  /***********************************************************************   * Convert items32 buffer to complex short   **********************************************************************/ -static UHD_INLINE sc16_t item32_to_sc16(item32_t item){ +static UHD_INLINE sc16_t item32_to_sc16(item32_t item, double){      return sc16_t(          boost::int16_t(item >> 16),          boost::int16_t(item >> 0) @@ -69,46 +69,38 @@ static UHD_INLINE sc16_t item32_to_sc16(item32_t item){  /***********************************************************************   * Convert complex float buffer to items32 (no swap)   **********************************************************************/ -static const float shorts_per_float = float(32767); - -static UHD_INLINE item32_t fc32_to_item32(fc32_t num){ -    boost::uint16_t real = boost::int16_t(num.real()*shorts_per_float); -    boost::uint16_t imag = boost::int16_t(num.imag()*shorts_per_float); +static UHD_INLINE item32_t fc32_to_item32(fc32_t num, float scale_factor){ +    boost::uint16_t real = boost::int16_t(num.real()*scale_factor); +    boost::uint16_t imag = boost::int16_t(num.imag()*scale_factor);      return (item32_t(real) << 16) | (item32_t(imag) << 0);  }  /***********************************************************************   * Convert items32 buffer to complex float   **********************************************************************/ -static const float floats_per_short = float(1.0/shorts_per_float); - -static UHD_INLINE fc32_t item32_to_fc32(item32_t item){ +static UHD_INLINE fc32_t item32_to_fc32(item32_t item, float scale_factor){      return fc32_t( -        float(boost::int16_t(item >> 16)*floats_per_short), -        float(boost::int16_t(item >> 0)*floats_per_short) +        float(boost::int16_t(item >> 16)*scale_factor), +        float(boost::int16_t(item >> 0)*scale_factor)      );  }  /***********************************************************************   * Convert complex double buffer to items32 (no swap)   **********************************************************************/ -static const double shorts_per_double = double(32767); - -static UHD_INLINE item32_t fc64_to_item32(fc64_t num){ -    boost::uint16_t real = boost::int16_t(num.real()*shorts_per_double); -    boost::uint16_t imag = boost::int16_t(num.imag()*shorts_per_double); +static UHD_INLINE item32_t fc64_to_item32(fc64_t num, double scale_factor){ +    boost::uint16_t real = boost::int16_t(num.real()*scale_factor); +    boost::uint16_t imag = boost::int16_t(num.imag()*scale_factor);      return (item32_t(real) << 16) | (item32_t(imag) << 0);  }  /***********************************************************************   * Convert items32 buffer to complex double   **********************************************************************/ -static const double doubles_per_short = double(1.0/shorts_per_double); - -static UHD_INLINE fc64_t item32_to_fc64(item32_t item){ +static UHD_INLINE fc64_t item32_to_fc64(item32_t item, double scale_factor){      return fc64_t( -        float(boost::int16_t(item >> 16)*doubles_per_short), -        float(boost::int16_t(item >> 0)*doubles_per_short) +        float(boost::int16_t(item >> 16)*scale_factor), +        float(boost::int16_t(item >> 0)*scale_factor)      );  } diff --git a/host/lib/convert/convert_orc.orc b/host/lib/convert/convert_orc.orc new file mode 100644 index 000000000..5450bf4db --- /dev/null +++ b/host/lib/convert/convert_orc.orc @@ -0,0 +1,63 @@ +.function _convert_fc32_1_to_item32_1_nswap_orc +.source 8 src +.dest 4 dst +.floatparam 4 scalar +.temp 8 scaled +.temp 8 converted +.temp 4 short +x2 mulf scaled, src, scalar +x2 convfl converted, scaled +x2 convlw short, converted +swapl short, short +x2 swapw dst, short + +.function _convert_fc32_1_to_item32_1_bswap_orc +.source 8 src +.dest 4 dst +.floatparam 4 scalar +.temp 8 scaled +.temp 8 converted +.temp 4 short +x2 mulf scaled, src, scalar +x2 convfl converted, scaled +x2 convlw short, converted +x2 swapw dst, short + +.function _convert_item32_1_to_fc32_1_nswap_orc +.source 4 src +.dest 8 dst +.floatparam 4 scalar +.temp 4 tmp1 +.temp 8 tmp2 +x2 swapw tmp1, src +swapl tmp1, tmp1 +x2 convswl tmp2, tmp1 +x2 convlf tmp2, tmp2 +x2 mulf dst, tmp2, scalar + +.function _convert_item32_1_to_fc32_1_bswap_orc +.source 4 src +.dest 8 dst +.floatparam 4 scalar +.temp 4 tmp1 +.temp 8 tmp2 +x2 swapw tmp1, src +x2 convswl tmp2, tmp1 +x2 convlf tmp2, tmp2 +x2 mulf dst, tmp2, scalar + +.function _convert_sc16_1_to_item32_1_nswap_orc +.source 4 src +.dest 4 dst +.temp 4 tmp +.floatparam 4 scalar +swapl tmp, src +x2 swapw dst, tmp + +.function _convert_item32_1_to_sc16_1_nswap_orc +.source 4 src +.dest 4 dst +.floatparam 4 scalar +.temp 4 tmp +x2 swapw tmp, src +swapl dst, tmp diff --git a/host/lib/convert/convert_with_neon.cpp b/host/lib/convert/convert_with_neon.cpp index 3d677db5b..e5f08cad9 100644 --- a/host/lib/convert/convert_with_neon.cpp +++ b/host/lib/convert/convert_with_neon.cpp @@ -26,7 +26,7 @@ DECLARE_CONVERTER(convert_fc32_1_to_item32_1_nswap, PRIORITY_CUSTOM){      size_t i; -    float32x4_t Q0 = vdupq_n_f32(shorts_per_float); +    float32x4_t Q0 = vdupq_n_f32(float(scale_factor));      for (i=0; i < (nsamps & ~0x03); i+=2) {          float32x4_t Q1 = vld1q_f32(reinterpret_cast<const float *>(&input[i]));          float32x4_t Q2 = vmulq_f32(Q1, Q0); @@ -37,7 +37,7 @@ DECLARE_CONVERTER(convert_fc32_1_to_item32_1_nswap, PRIORITY_CUSTOM){      }      for (; i < nsamps; i++) -        output[i] = fc32_to_item32(input[i]); +        output[i] = fc32_to_item32(input[i], float(scale_factor));  }  DECLARE_CONVERTER(convert_item32_1_to_fc32_1_nswap, PRIORITY_CUSTOM){ @@ -46,7 +46,7 @@ DECLARE_CONVERTER(convert_item32_1_to_fc32_1_nswap, PRIORITY_CUSTOM){      size_t i; -    float32x4_t Q1 = vdupq_n_f32(floats_per_short); +    float32x4_t Q1 = vdupq_n_f32(float(scale_factor));      for (i=0; i < (nsamps & ~0x03); i+=2) {          int16x4_t D0 = vld1_s16(reinterpret_cast<const int16_t *>(&input[i]));          int16x4_t D1 = vrev32_s16(D0); @@ -57,5 +57,5 @@ DECLARE_CONVERTER(convert_item32_1_to_fc32_1_nswap, PRIORITY_CUSTOM){      }      for (; i < nsamps; i++) -        output[i] = item32_to_fc32(input[i]); +        output[i] = item32_to_fc32(input[i], float(scale_factor));  } diff --git a/host/lib/convert/convert_with_orc.cpp b/host/lib/convert/convert_with_orc.cpp new file mode 100644 index 000000000..844c2595c --- /dev/null +++ b/host/lib/convert/convert_with_orc.cpp @@ -0,0 +1,54 @@ +// +// Copyright 2011 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// 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> + +using namespace uhd::convert; + +extern "C" { +extern void _convert_fc32_1_to_item32_1_nswap_orc(void *, const void *, float, int); +extern void _convert_fc32_1_to_item32_1_bswap_orc(void *, const void *, float, int); +extern void _convert_item32_1_to_fc32_1_nswap_orc(void *, const void *, float, int); +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); +} + +DECLARE_CONVERTER(convert_fc32_1_to_item32_1_nswap, PRIORITY_LIBORC){ +    _convert_fc32_1_to_item32_1_nswap_orc(outputs[0], inputs[0], scale_factor, nsamps); +} + +DECLARE_CONVERTER(convert_fc32_1_to_item32_1_bswap, PRIORITY_LIBORC){ +    _convert_fc32_1_to_item32_1_bswap_orc(outputs[0], inputs[0], scale_factor, nsamps); +} + +DECLARE_CONVERTER(convert_item32_1_to_fc32_1_nswap, PRIORITY_LIBORC){ +    _convert_item32_1_to_fc32_1_nswap_orc(outputs[0], inputs[0], scale_factor, nsamps); +} + +DECLARE_CONVERTER(convert_item32_1_to_fc32_1_bswap, PRIORITY_LIBORC){ +    _convert_item32_1_to_fc32_1_bswap_orc(outputs[0], inputs[0], scale_factor, nsamps); +} + +DECLARE_CONVERTER(convert_sc16_1_to_item32_1_nswap, PRIORITY_LIBORC){ +    _convert_sc16_1_to_item32_1_nswap_orc(outputs[0], inputs[0], scale_factor, nsamps); +} + +DECLARE_CONVERTER(convert_item32_1_to_sc16_1_nswap, PRIORITY_LIBORC){ +    _convert_item32_1_to_sc16_1_nswap_orc(outputs[0], inputs[0], scale_factor, nsamps); +} diff --git a/host/lib/convert/convert_with_sse2.cpp b/host/lib/convert/convert_with_sse2.cpp index 96ee9134c..52beea24a 100644 --- a/host/lib/convert/convert_with_sse2.cpp +++ b/host/lib/convert/convert_with_sse2.cpp @@ -25,7 +25,7 @@ DECLARE_CONVERTER(convert_fc32_1_to_item32_1_nswap, PRIORITY_CUSTOM){      const fc32_t *input = reinterpret_cast<const fc32_t *>(inputs[0]);      item32_t *output = reinterpret_cast<item32_t *>(outputs[0]); -    __m128 scalar = _mm_set_ps1(shorts_per_float); +    __m128 scalar = _mm_set_ps1(float(scale_factor));      //convert blocks of samples with intrinsics      size_t i = 0; for (; i < (nsamps & ~0x3); i+=4){ @@ -48,7 +48,7 @@ DECLARE_CONVERTER(convert_fc32_1_to_item32_1_nswap, PRIORITY_CUSTOM){      //convert remainder      for (; i < nsamps; i++){ -        output[i] = fc32_to_item32(input[i]); +        output[i] = fc32_to_item32(input[i], float(scale_factor));      }  } @@ -56,7 +56,7 @@ DECLARE_CONVERTER(convert_fc32_1_to_item32_1_bswap, PRIORITY_CUSTOM){      const fc32_t *input = reinterpret_cast<const fc32_t *>(inputs[0]);      item32_t *output = reinterpret_cast<item32_t *>(outputs[0]); -    __m128 scalar = _mm_set_ps1(shorts_per_float); +    __m128 scalar = _mm_set_ps1(float(scale_factor));      //convert blocks of samples with intrinsics      size_t i = 0; for (; i < (nsamps & ~0x3); i+=4){ @@ -78,7 +78,7 @@ DECLARE_CONVERTER(convert_fc32_1_to_item32_1_bswap, PRIORITY_CUSTOM){      //convert remainder      for (; i < nsamps; i++){ -        output[i] = uhd::byteswap(fc32_to_item32(input[i])); +        output[i] = uhd::byteswap(fc32_to_item32(input[i], float(scale_factor)));      }  } @@ -86,7 +86,7 @@ DECLARE_CONVERTER(convert_item32_1_to_fc32_1_nswap, PRIORITY_CUSTOM){      const item32_t *input = reinterpret_cast<const item32_t *>(inputs[0]);      fc32_t *output = reinterpret_cast<fc32_t *>(outputs[0]); -    __m128 scalar = _mm_set_ps1(floats_per_short/(1 << 16)); +    __m128 scalar = _mm_set_ps1(float(scale_factor)/(1 << 16));      __m128i zeroi = _mm_setzero_si128();      //convert blocks of samples with intrinsics @@ -111,7 +111,7 @@ DECLARE_CONVERTER(convert_item32_1_to_fc32_1_nswap, PRIORITY_CUSTOM){      //convert remainder      for (; i < nsamps; i++){ -        output[i] = item32_to_fc32(input[i]); +        output[i] = item32_to_fc32(input[i], float(scale_factor));      }  } @@ -119,7 +119,7 @@ DECLARE_CONVERTER(convert_item32_1_to_fc32_1_bswap, PRIORITY_CUSTOM){      const item32_t *input = reinterpret_cast<const item32_t *>(inputs[0]);      fc32_t *output = reinterpret_cast<fc32_t *>(outputs[0]); -    __m128 scalar = _mm_set_ps1(floats_per_short/(1 << 16)); +    __m128 scalar = _mm_set_ps1(float(scale_factor)/(1 << 16));      __m128i zeroi = _mm_setzero_si128();      //convert blocks of samples with intrinsics @@ -143,6 +143,6 @@ DECLARE_CONVERTER(convert_item32_1_to_fc32_1_bswap, PRIORITY_CUSTOM){      //convert remainder      for (; i < nsamps; i++){ -        output[i] = item32_to_fc32(uhd::byteswap(input[i])); +        output[i] = item32_to_fc32(uhd::byteswap(input[i]), float(scale_factor));      }  } diff --git a/host/lib/convert/gen_convert_general.py b/host/lib/convert/gen_convert_general.py index f03448047..8c3138bda 100644 --- a/host/lib/convert/gen_convert_general.py +++ b/host/lib/convert/gen_convert_general.py @@ -34,7 +34,7 @@ DECLARE_CONVERTER(convert_$(cpu_type)_1_to_item32_1_$(swap), PRIORITY_GENERAL){      item32_t *output = reinterpret_cast<item32_t *>(outputs[0]);      for (size_t i = 0; i < nsamps; i++){ -        output[i] = $(swap_fcn)($(cpu_type)_to_item32(input[i])); +        output[i] = $(swap_fcn)($(cpu_type)_to_item32(input[i], float(scale_factor)));      }  } @@ -43,7 +43,7 @@ DECLARE_CONVERTER(convert_item32_1_to_$(cpu_type)_1_$(swap), PRIORITY_GENERAL){      $(cpu_type)_t *output = reinterpret_cast<$(cpu_type)_t *>(outputs[0]);      for (size_t i = 0; i < nsamps; i++){ -        output[i] = item32_to_$(cpu_type)($(swap_fcn)(input[i])); +        output[i] = item32_to_$(cpu_type)($(swap_fcn)(input[i]), float(scale_factor));      }  }  """ @@ -56,7 +56,7 @@ DECLARE_CONVERTER(convert_$(cpu_type)_$(width)_to_item32_1_$(swap), PRIORITY_GEN      for (size_t i = 0, j = 0; i < nsamps; i++){          #for $w in range($width) -        output[j++] = $(swap_fcn)($(cpu_type)_to_item32(input$(w)[i])); +        output[j++] = $(swap_fcn)($(cpu_type)_to_item32(input$(w)[i], float(scale_factor)));          #end for      }  } @@ -69,7 +69,7 @@ DECLARE_CONVERTER(convert_item32_1_to_$(cpu_type)_$(width)_$(swap), PRIORITY_GEN      for (size_t i = 0, j = 0; i < nsamps; i++){          #for $w in range($width) -        output$(w)[i] = item32_to_$(cpu_type)($(swap_fcn)(input[j++])); +        output$(w)[i] = item32_to_$(cpu_type)($(swap_fcn)(input[j++]), float(scale_factor));          #end for      }  } diff --git a/host/lib/transport/CMakeLists.txt b/host/lib/transport/CMakeLists.txt index 90360977a..b1821956c 100644 --- a/host/lib/transport/CMakeLists.txt +++ b/host/lib/transport/CMakeLists.txt @@ -98,5 +98,5 @@ LIBUHD_APPEND_SOURCES(      ${CMAKE_CURRENT_SOURCE_DIR}/if_addrs.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/udp_simple.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/udp_zero_copy.cpp -    ${CMAKE_CURRENT_SOURCE_DIR}/vrt_packet_handler.hpp +    ${CMAKE_CURRENT_SOURCE_DIR}/usb_zero_copy_wrapper.cpp  ) diff --git a/host/lib/transport/gen_vrt_if_packet.py b/host/lib/transport/gen_vrt_if_packet.py index 7440def6a..5f048d8c7 100755 --- a/host/lib/transport/gen_vrt_if_packet.py +++ b/host/lib/transport/gen_vrt_if_packet.py @@ -62,6 +62,8 @@ static pred_table_type get_pred_unpack_table(void){          if(vrt_hdr_word & $hex(0x3 << 22)) table[i] |= $hex($tsi_p);          if(vrt_hdr_word & $hex(0x3 << 20)) table[i] |= $hex($tsf_p);          if(vrt_hdr_word & $hex(0x1 << 26)) table[i] |= $hex($tlr_p); +        if(vrt_hdr_word & $hex(0x1 << 24)) table[i] |= $hex($eob_p); +        if(vrt_hdr_word & $hex(0x1 << 25)) table[i] |= $hex($sob_p);      }      return table;  } @@ -84,9 +86,11 @@ void vrt::if_hdr_pack_$(suffix)(      if (if_packet_info.has_tsi) pred |= $hex($tsi_p);      if (if_packet_info.has_tsf) pred |= $hex($tsf_p);      if (if_packet_info.has_tlr) pred |= $hex($tlr_p); +    if (if_packet_info.eob)     pred |= $hex($eob_p); +    if (if_packet_info.sob)     pred |= $hex($sob_p);      switch(pred){ -    #for $pred in range(2**5) +    #for $pred in range(2**7)      case $pred:          #set $num_header_words = 1          #set $flags = 0 @@ -126,6 +130,13 @@ void vrt::if_hdr_pack_$(suffix)(          #else              #set $num_trailer_words = 0;          #end if +        ########## Burst Flags ########## +        #if $pred & $eob_p +            #set $flags |= (0x1 << 24); +        #end if +        #if $pred & $sob_p +            #set $flags |= (0x1 << 25); +        #end if          ########## Variables ##########              if_packet_info.num_header_words32 = $num_header_words;              if_packet_info.num_packet_words32 = $($num_header_words + $num_trailer_words) + if_packet_info.num_payload_words32; @@ -134,10 +145,6 @@ void vrt::if_hdr_pack_$(suffix)(      #end for      } -    //set the burst flags -    if (if_packet_info.sob) vrt_hdr_flags |= $hex(0x1 << 25); -    if (if_packet_info.eob) vrt_hdr_flags |= $hex(0x1 << 24); -      //fill in complete header word      packet_buff[0] = $(XE_MACRO)(boost::uint32_t(0          | (if_packet_info.packet_type << 29) @@ -162,13 +169,11 @@ void vrt::if_hdr_unpack_$(suffix)(      //extract fields from the header      if_packet_info.packet_type = if_packet_info_t::packet_type_t(vrt_hdr_word >> 29);      if_packet_info.packet_count = (vrt_hdr_word >> 16) & 0xf; -    //if_packet_info.sob = bool(vrt_hdr_word & $hex(0x1 << 25)); //not implemented -    //if_packet_info.eob = bool(vrt_hdr_word & $hex(0x1 << 24)); //not implemented      const pred_type pred = pred_unpack_table[pred_table_index(vrt_hdr_word)];      switch(pred){ -    #for $pred in range(2**5) +    #for $pred in range(2**7)      case $pred:          #set $has_time_spec = False          #set $num_header_words = 1 @@ -215,6 +220,17 @@ void vrt::if_hdr_unpack_$(suffix)(              if_packet_info.has_tlr = false;              #set $num_trailer_words = 0;          #end if +        ########## Burst Flags ########## +        #if $pred & $eob_p +            if_packet_info.eob = true; +        #else +            if_packet_info.eob = false; +        #end if +        #if $pred & $sob_p +            if_packet_info.sob = true; +        #else +            if_packet_info.sob = false; +        #end if          ########## Variables ##########              //another failure case              if (packet_words32 < $($num_header_words + $num_trailer_words)) @@ -243,9 +259,11 @@ if __name__ == '__main__':      open(sys.argv[1], 'w').write(parse_tmpl(          TMPL_TEXT,          file=__file__, -        sid_p = 0b00001, -        cid_p = 0b00010, -        tsi_p = 0b00100, -        tsf_p = 0b01000, -        tlr_p = 0b10000, +        sid_p = 0b0000001, +        cid_p = 0b0000010, +        tsi_p = 0b0000100, +        tsf_p = 0b0001000, +        tlr_p = 0b0010000, +        sob_p = 0b0100000, +        eob_p = 0b1000000,      )) diff --git a/host/lib/transport/libusb1_zero_copy.cpp b/host/lib/transport/libusb1_zero_copy.cpp index 28bea978b..19a7a3742 100644 --- a/host/lib/transport/libusb1_zero_copy.cpp +++ b/host/lib/transport/libusb1_zero_copy.cpp @@ -20,7 +20,7 @@  #include <uhd/transport/bounded_buffer.hpp>  #include <uhd/transport/buffer_pool.hpp>  #include <uhd/utils/thread_priority.hpp> -#include <uhd/utils/log.hpp> +#include <uhd/utils/msg.hpp>  #include <uhd/exception.hpp>  #include <boost/function.hpp>  #include <boost/foreach.hpp> @@ -44,15 +44,24 @@ static const size_t DEFAULT_XFER_SIZE = 32*512; //bytes   * to ensure that they are compiled with the same calling convention as libusb.   */ -//! helper function: handles all async callbacks -static void LIBUSB_CALL libusb_async_cb(libusb_transfer *lut){ +//! helper function: handles all rx async callbacks +static void LIBUSB_CALL libusb_async_rx_cb(libusb_transfer *lut){ +    if(lut->actual_length == 0) { +        UHD_ASSERT_THROW(libusb_submit_transfer(lut) == 0); //get out until you find some real data +        return; +    } +    (*static_cast<boost::function<void()> *>(lut->user_data))(); +} + +//! helper function: handles all tx async callbacks +static void LIBUSB_CALL libusb_async_tx_cb(libusb_transfer *lut) {      (*static_cast<boost::function<void()> *>(lut->user_data))();  }  //! callback to free transfer upon cancellation  static void LIBUSB_CALL cancel_transfer_cb(libusb_transfer *lut){ -    if (lut->status == LIBUSB_TRANSFER_CANCELLED) libusb_free_transfer(lut); -    else UHD_LOGV(rarely) << "libusb cancel_transfer unexpected status " << lut->status << std::endl; +    if (lut->status == LIBUSB_TRANSFER_CANCELLED || lut->status == LIBUSB_TRANSFER_TIMED_OUT) libusb_free_transfer(lut); +    else UHD_MSG(error) << "libusb cancel_transfer unexpected status " << lut->status << std::endl;  }  /*********************************************************************** @@ -97,7 +106,7 @@ public:      void commit(size_t len){          if (_expired) return;          _lut->length = len; -        if(len == 0) libusb_async_cb(_lut); +        if(len == 0) libusb_async_tx_cb(_lut);          else UHD_ASSERT_THROW(libusb_submit_transfer(_lut) == 0);          _expired = true;      } @@ -157,9 +166,9 @@ public:                  (recv_endpoint & 0x7f) | 0x80,                          // endpoint                  static_cast<unsigned char *>(_recv_buffer_pool->at(i)), // buffer                  this->get_recv_frame_size(),                            // length -                libusb_transfer_cb_fn(&libusb_async_cb),                // callback +                libusb_transfer_cb_fn(&libusb_async_rx_cb),             // callback                  static_cast<void *>(&_callbacks.back()),                // user_data -                0                                                       // timeout +                0                                                       // timeout (ms)              );              _all_luts.push_back(lut); @@ -183,13 +192,13 @@ public:                  (send_endpoint & 0x7f) | 0x00,                          // endpoint                  static_cast<unsigned char *>(_send_buffer_pool->at(i)), // buffer                  this->get_send_frame_size(),                            // length -                libusb_transfer_cb_fn(&libusb_async_cb),                // callback +                libusb_transfer_cb_fn(&libusb_async_tx_cb),             // callback                  static_cast<void *>(&_callbacks.back()),                // user_data                  0                                                       // timeout              );              _all_luts.push_back(lut); -            libusb_async_cb(lut); +            libusb_async_tx_cb(lut);          }          //spawn the event handler threads @@ -206,7 +215,9 @@ public:          BOOST_FOREACH(libusb_transfer *lut, _all_luts){              lut->callback = libusb_transfer_cb_fn(&cancel_transfer_cb);              libusb_cancel_transfer(lut); -            while(lut->status != LIBUSB_TRANSFER_CANCELLED && lut->status != LIBUSB_TRANSFER_COMPLETED) { +            while(lut->status != LIBUSB_TRANSFER_CANCELLED +               && lut->status != LIBUSB_TRANSFER_COMPLETED +               && lut->status != LIBUSB_TRANSFER_TIMED_OUT) {                  boost::this_thread::sleep(boost::posix_time::milliseconds(10));              }          } diff --git a/host/lib/transport/super_recv_packet_handler.hpp b/host/lib/transport/super_recv_packet_handler.hpp new file mode 100644 index 000000000..80ad17b6c --- /dev/null +++ b/host/lib/transport/super_recv_packet_handler.hpp @@ -0,0 +1,638 @@ +// +// Copyright 2011 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// 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_TRANSPORT_SUPER_RECV_PACKET_HANDLER_HPP +#define INCLUDED_LIBUHD_TRANSPORT_SUPER_RECV_PACKET_HANDLER_HPP + +#include <uhd/config.hpp> +#include <uhd/exception.hpp> +#include <uhd/convert.hpp> +#include <uhd/device.hpp> +#include <uhd/utils/msg.hpp> +#include <uhd/utils/byteswap.hpp> +#include <uhd/types/io_type.hpp> +#include <uhd/types/otw_type.hpp> +#include <uhd/types/metadata.hpp> +#include <uhd/transport/vrt_if_packet.hpp> +#include <uhd/transport/zero_copy.hpp> +#include <boost/thread/mutex.hpp> +#include <boost/foreach.hpp> +#include <boost/function.hpp> +#include <boost/format.hpp> +#include <iostream> +#include <vector> + +namespace uhd{ namespace transport{ namespace sph{ + +UHD_INLINE boost::uint32_t get_context_code( +    const boost::uint32_t *vrt_hdr, const vrt::if_packet_info_t &if_packet_info +){ +    //extract the context word (we dont know the endianness so mirror the bytes) +    boost::uint32_t word0 = vrt_hdr[if_packet_info.num_header_words32] | +              uhd::byteswap(vrt_hdr[if_packet_info.num_header_words32]); +    return word0 & 0xff; +} + +typedef boost::function<void(void)> handle_overflow_type; +static inline void handle_overflow_nop(void){} + +/*********************************************************************** + * Alignment indexes class: + *  - Access an integer set with very quick operations. + **********************************************************************/ +class alignment_indexes{ +public: +    typedef boost::uint16_t index_type; //16 buffers + +    alignment_indexes(void): +        _indexes(0), +        _sizes(256, 0), +        _fronts(256, ~0) +    { +        //fill the O(1) look up tables for a single byte +        for (size_t i = 0; i < 256; i++){ +            for (size_t j = 0; j < 8; j++){ +                if (i & (1 << j)){ +                    _sizes[i]++; +                    _fronts[i] = j; +                } +            } +        } +    } + +    UHD_INLINE void reset(size_t len){_indexes = (1 << len) - 1;} + +    UHD_INLINE size_t front(void){ +        //check one byte per iteration +        for (size_t i = 0; i < sizeof(_indexes)*8; i+=8){ +            size_t front = _fronts[(_indexes >> i) & 0xff]; +            if (front != size_t(~0)) return front + i; +        } +        if (empty()) throw uhd::runtime_error("cannot call front() when empty"); +        UHD_THROW_INVALID_CODE_PATH(); +    } + +    UHD_INLINE void remove(size_t index){_indexes &= ~(1 << index);} + +    UHD_INLINE bool empty(void){return _indexes == 0;} + +    UHD_INLINE size_t size(void){ +        size_t size = 0; +        //check one byte per iteration +        for (size_t i = 0; i < sizeof(_indexes)*8; i+=8){ +            size += _sizes[(_indexes >> i) & 0xff]; +        } +        return size; +    } + +private: +    index_type _indexes; +    std::vector<size_t> _sizes; +    std::vector<size_t> _fronts; +}; + +/*********************************************************************** + * Super receive packet handler + * + * A receive packet handler represents a group of channels. + * The channel group shares a common sample rate. + * All channels are received in unison in recv(). + **********************************************************************/ +class recv_packet_handler{ +public: +    typedef boost::function<managed_recv_buffer::sptr(double)> get_buff_type; +    typedef void(*vrt_unpacker_type)(const boost::uint32_t *, vrt::if_packet_info_t &); +    //typedef boost::function<void(const boost::uint32_t *, vrt::if_packet_info_t &)> vrt_unpacker_type; + +    /*! +     * Make a new packet handler for receive +     * \param size the number of transport channels +     */ +    recv_packet_handler(const size_t size = 1): +        _queue_error_for_next_call(false), +        _buffers_infos_index(0) +    { +        UHD_ASSERT_THROW(size <= sizeof(alignment_indexes::index_type)*8); +        this->resize(size); +        set_alignment_failure_threshold(1000); +    } + +    //! Resize the number of transport channels +    void resize(const size_t size){ +        if (this->size() == size) return; +        _props.resize(size); +        //re-initialize all buffers infos by re-creating the vector +        _buffers_infos = std::vector<buffers_info_type>(4, buffers_info_type(size)); +    } + +    //! Get the channel width of this handler +    size_t size(void) const{ +        return _props.size(); +    } + +    //! Setup the vrt unpacker function and offset +    void set_vrt_unpacker(const vrt_unpacker_type &vrt_unpacker, const size_t header_offset_words32 = 0){ +        _vrt_unpacker = vrt_unpacker; +        _header_offset_words32 = header_offset_words32; +    } + +    /*! +     * Set the threshold for alignment failure. +     * How many packets throw out before giving up? +     * \param threshold number of packets per channel +     */ +    void set_alignment_failure_threshold(const size_t threshold){ +        _alignment_faulure_threshold = threshold*this->size(); +    } + +    //! Set the rate of ticks per second +    void set_tick_rate(const double rate){ +        _tick_rate = rate; +    } + +    //! Set the rate of samples per second +    void set_samp_rate(const double rate){ +        _samp_rate = rate; +    } + +    /*! +     * Set the function to get a managed buffer. +     * \param xport_chan which transport channel +     * \param get_buff the getter function +     */ +    void set_xport_chan_get_buff(const size_t xport_chan, const get_buff_type &get_buff){ +        _props.at(xport_chan).get_buff = get_buff; +    } + +    /*! +     * Setup the conversion functions (homogeneous across transports). +     * Here, we load a table of converters for all possible io types. +     * This makes the converter look-up an O(1) operation. +     * \param otw_type the channel data type +     * \param width the streams per channel (usually 1) +     */ +    void set_converter(const uhd::otw_type_t &otw_type, const size_t width = 1){ +        _io_buffs.resize(width); +        _converters.resize(128); +        for (size_t io_type = 0; io_type < _converters.size(); io_type++){ +            try{ +                _converters[io_type] = uhd::convert::get_converter_otw_to_cpu( +                    io_type_t::tid_t(io_type), otw_type, 1, width +                ); +            }catch(const uhd::value_error &){} //we expect this, not all io_types valid... +        } +        _bytes_per_item = otw_type.get_sample_size(); +    } + +    //! Set the transport channel's overflow handler +    void set_overflow_handler(const size_t xport_chan, const handle_overflow_type &handle_overflow){ +        _props.at(xport_chan).handle_overflow = handle_overflow; +    } + +    //! Get a scoped lock object for this instance +    boost::mutex::scoped_lock get_scoped_lock(void){ +        return boost::mutex::scoped_lock(_mutex); +    } + +    /******************************************************************* +     * Receive: +     * The entry point for the fast-path receive calls. +     * Dispatch into combinations of single packet receive calls. +     ******************************************************************/ +    UHD_INLINE size_t recv( +        const uhd::device::recv_buffs_type &buffs, +        const size_t nsamps_per_buff, +        uhd::rx_metadata_t &metadata, +        const uhd::io_type_t &io_type, +        uhd::device::recv_mode_t recv_mode, +        double timeout +    ){ +        boost::mutex::scoped_lock lock(_mutex); + +        //handle metadata queued from a previous receive +        if (_queue_error_for_next_call){ +            _queue_error_for_next_call = false; +            metadata = _queue_metadata; +            //We want to allow a full buffer recv to be cut short by a timeout, +            //but do not want to generate an inline timeout message packet. +            if (_queue_metadata.error_code != rx_metadata_t::ERROR_CODE_TIMEOUT) return 0; +        } + +        switch(recv_mode){ + +        //////////////////////////////////////////////////////////////// +        case uhd::device::RECV_MODE_ONE_PACKET:{ +        //////////////////////////////////////////////////////////////// +            return recv_one_packet(buffs, nsamps_per_buff, metadata, io_type, timeout); +        } + +        //////////////////////////////////////////////////////////////// +        case uhd::device::RECV_MODE_FULL_BUFF:{ +        //////////////////////////////////////////////////////////////// +            size_t accum_num_samps = recv_one_packet( +                buffs, nsamps_per_buff, metadata, io_type, timeout +            ); + +            //first recv had an error code set, return immediately +            if (metadata.error_code != rx_metadata_t::ERROR_CODE_NONE) return accum_num_samps; + +            //loop until buffer is filled or error code +            while(accum_num_samps < nsamps_per_buff){ +                size_t num_samps = recv_one_packet( +                    buffs, nsamps_per_buff - accum_num_samps, _queue_metadata, +                    io_type, timeout, accum_num_samps*io_type.size +                ); + +                //metadata had an error code set, store for next call and return +                if (_queue_metadata.error_code != rx_metadata_t::ERROR_CODE_NONE){ +                    _queue_error_for_next_call = true; +                    break; +                } +                accum_num_samps += num_samps; +            } +            return accum_num_samps; +        } + +        default: throw uhd::value_error("unknown recv mode"); +        }//switch(recv_mode) +    } + +private: + +    boost::mutex _mutex; +    vrt_unpacker_type _vrt_unpacker; +    size_t _header_offset_words32; +    double _tick_rate, _samp_rate; +    bool _queue_error_for_next_call; +    size_t _alignment_faulure_threshold; +    rx_metadata_t _queue_metadata; +    struct xport_chan_props_type{ +        xport_chan_props_type(void): +            packet_count(0), +            handle_overflow(&handle_overflow_nop) +        {} +        get_buff_type get_buff; +        size_t packet_count; +        handle_overflow_type handle_overflow; +    }; +    std::vector<xport_chan_props_type> _props; +    std::vector<void *> _io_buffs; //used in conversion +    size_t _bytes_per_item; //used in conversion +    std::vector<uhd::convert::function_type> _converters; //used in conversion + +    //! information stored for a received buffer +    struct per_buffer_info_type{ +        managed_recv_buffer::sptr buff; +        const boost::uint32_t *vrt_hdr; +        vrt::if_packet_info_t ifpi; +        time_spec_t time; +        const char *copy_buff; +    }; + +    //!information stored for a set of aligned buffers +    struct buffers_info_type : std::vector<per_buffer_info_type> { +        buffers_info_type(const size_t size): +            std::vector<per_buffer_info_type>(size), +            alignment_time_valid(false), +            data_bytes_to_copy(0), +            fragment_offset_in_samps(0) +        { +            indexes_to_do.reset(size); +        } +        alignment_indexes indexes_to_do; //used in alignment logic +        time_spec_t alignment_time; //used in alignment logic +        bool alignment_time_valid; //used in alignment logic +        size_t data_bytes_to_copy; //keeps track of state +        size_t fragment_offset_in_samps; //keeps track of state +        rx_metadata_t metadata; //packet description +    }; + +    //! a circular queue of buffer infos +    std::vector<buffers_info_type> _buffers_infos; +    size_t _buffers_infos_index; +    buffers_info_type &get_curr_buffer_info(void){return _buffers_infos[_buffers_infos_index];} +    buffers_info_type &get_prev_buffer_info(void){return _buffers_infos[(_buffers_infos_index + 3)%4];} +    buffers_info_type &get_next_buffer_info(void){return _buffers_infos[(_buffers_infos_index + 1)%4];} +    void increment_buffer_info(void){_buffers_infos_index = (_buffers_infos_index + 1)%4;} + +    //! possible return options for the packet receiver +    enum packet_type{ +        PACKET_IF_DATA, +        PACKET_TIMESTAMP_ERROR, +        PACKET_INLINE_MESSAGE, +        PACKET_TIMEOUT_ERROR, +        PACKET_SEQUENCE_ERROR +    }; + +    /******************************************************************* +     * Get and process a single packet from the transport: +     * Receive a single packet at the given index. +     * Extract all the relevant info and store. +     * Check the info to determine the return code. +     ******************************************************************/ +    UHD_INLINE packet_type get_and_process_single_packet( +        const size_t index, +        buffers_info_type &prev_buffer_info, +        buffers_info_type &curr_buffer_info, +        double timeout +    ){ +        //get a single packet from the transport layer +        managed_recv_buffer::sptr &buff = curr_buffer_info[index].buff; +        buff = _props[index].get_buff(timeout); +        if (buff.get() == NULL) return PACKET_TIMEOUT_ERROR; + +        //bounds check before extract +        size_t num_packet_words32 = buff->size()/sizeof(boost::uint32_t); +        if (num_packet_words32 <= _header_offset_words32){ +            throw std::runtime_error("recv buffer smaller than vrt packet offset"); +        } + +        //extract packet info +        per_buffer_info_type &info = curr_buffer_info[index]; +        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.copy_buff = reinterpret_cast<const char *>(info.vrt_hdr + info.ifpi.num_header_words32); + +        //store the packet count for the next iteration +        #ifndef SRPH_DONT_CHECK_SEQUENCE +        const size_t expected_packet_count = _props[index].packet_count; +        _props[index].packet_count = (info.ifpi.packet_count + 1)%16; +        #endif + +        //-------------------------------------------------------------- +        //-- Determine return conditions: +        //-- The order of these checks is HOLY. +        //-------------------------------------------------------------- + +        //1) check for out of order timestamps +        if (info.ifpi.has_tsi and info.ifpi.has_tsf and prev_buffer_info[index].time > info.time){ +            return PACKET_TIMESTAMP_ERROR; +        } + +        //2) check for inline IF message packets +        if (info.ifpi.packet_type != vrt::if_packet_info_t::PACKET_TYPE_DATA){ +            return PACKET_INLINE_MESSAGE; +        } + +        //3) check for sequence errors +        #ifndef SRPH_DONT_CHECK_SEQUENCE +        if (expected_packet_count != info.ifpi.packet_count){ +            return PACKET_SEQUENCE_ERROR; +        } +        #endif + +        //4) otherwise the packet is normal! +        return PACKET_IF_DATA; +    } + +    /******************************************************************* +     * Alignment check: +     * Check the received packet for alignment and mark accordingly. +     ******************************************************************/ +    UHD_INLINE void alignment_check( +        const size_t index, buffers_info_type &info +    ){ +        //if alignment time was not valid or if the sequence id is newer: +        //  use this index's time as the alignment time +        //  reset the indexes list and remove this index +        if (not info.alignment_time_valid or info[index].time > info.alignment_time){ +            info.alignment_time_valid = true; +            info.alignment_time = info[index].time; +            info.indexes_to_do.reset(this->size()); +            info.indexes_to_do.remove(index); +            info.data_bytes_to_copy = info[index].ifpi.num_payload_words32*sizeof(boost::uint32_t); +        } + +        //if the sequence id matches: +        //  remove this index from the list and continue +        else if (info[index].time == info.alignment_time){ +            info.indexes_to_do.remove(index); +        } + +        //if the sequence id is older: +        //  continue with the same index to try again +        //else if (info[index].time < info.alignment_time)... +    } + +    /******************************************************************* +     * Get aligned buffers: +     * Iterate through each index and try to accumulate aligned buffers. +     * Handle all of the edge cases like inline messages and errors. +     * The logic will throw out older packets until it finds a match. +     ******************************************************************/ +    UHD_INLINE void get_aligned_buffs(double timeout){ + +        increment_buffer_info(); //increment to next buffer +        buffers_info_type &prev_info = get_prev_buffer_info(); +        buffers_info_type &curr_info = get_curr_buffer_info(); +        buffers_info_type &next_info = get_next_buffer_info(); + +        //Loop until we get a message of an aligned set of buffers: +        // - Receive a single packet and extract its info. +        // - Handle the packet type yielded by the receive. +        // - Check the timestamps for alignment conditions. +        size_t iterations = 0; +        while (not curr_info.indexes_to_do.empty()){ + +            //get the index to process for this iteration +            const size_t index = curr_info.indexes_to_do.front(); +            packet_type packet; + +            //receive a single packet from the transport +            try{ +                packet = get_and_process_single_packet( +                    index, prev_info, curr_info, timeout +                ); +            } + +            //handle the case when the get packet throws +            catch(const std::exception &e){ +                UHD_MSG(error) << boost::format( +                    "The receive packet handler caught an exception.\n%s" +                ) % e.what() << std::endl; +                std::swap(curr_info, next_info); //save progress from curr -> next +                curr_info.metadata.has_time_spec = false; +                curr_info.metadata.time_spec = time_spec_t(0.0); +                curr_info.metadata.more_fragments = false; +                curr_info.metadata.fragment_offset = 0; +                curr_info.metadata.start_of_burst = false; +                curr_info.metadata.end_of_burst = false; +                curr_info.metadata.error_code = rx_metadata_t::ERROR_CODE_BAD_PACKET; +                return; +            } + +            switch(packet){ +            case PACKET_IF_DATA: +                alignment_check(index, curr_info); +                break; + +            case PACKET_TIMESTAMP_ERROR: +                //If the user changes the device time while streaming or without flushing, +                //we can receive a packet that comes before the previous packet in time. +                //This could cause the alignment logic to discard future received packets. +                //Therefore, when this occurs, we reset the info to restart from scratch. +                if (curr_info.alignment_time_valid and curr_info.alignment_time != curr_info[index].time){ +                    curr_info.alignment_time_valid = false; +                } +                alignment_check(index, curr_info); +                break; + +            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.time_spec = next_info[index].time; +                curr_info.metadata.more_fragments = false; +                curr_info.metadata.fragment_offset = 0; +                curr_info.metadata.start_of_burst = false; +                curr_info.metadata.end_of_burst = false; +                curr_info.metadata.error_code = rx_metadata_t::error_code_t(get_context_code(next_info[index].vrt_hdr, next_info[index].ifpi)); +                if (curr_info.metadata.error_code == rx_metadata_t::ERROR_CODE_OVERFLOW) _props[index].handle_overflow(); +                UHD_MSG(fastpath) << "O"; +                return; + +            case PACKET_TIMEOUT_ERROR: +                std::swap(curr_info, next_info); //save progress from curr -> next +                curr_info.metadata.has_time_spec = false; +                curr_info.metadata.time_spec = time_spec_t(0.0); +                curr_info.metadata.more_fragments = false; +                curr_info.metadata.fragment_offset = 0; +                curr_info.metadata.start_of_burst = false; +                curr_info.metadata.end_of_burst = false; +                curr_info.metadata.error_code = rx_metadata_t::ERROR_CODE_TIMEOUT; +                return; + +            case PACKET_SEQUENCE_ERROR: +                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, +                    prev_info[index].ifpi.num_payload_words32*sizeof(boost::uint32_t)/_bytes_per_item, _samp_rate); +                curr_info.metadata.more_fragments = false; +                curr_info.metadata.fragment_offset = 0; +                curr_info.metadata.start_of_burst = false; +                curr_info.metadata.end_of_burst = false; +                curr_info.metadata.error_code = rx_metadata_t::ERROR_CODE_OVERFLOW; +                UHD_MSG(fastpath) << "O"; +                return; + +            } + +            //too many iterations: detect alignment failure +            if (iterations++ > _alignment_faulure_threshold){ +                UHD_MSG(error) << boost::format( +                    "The receive packet handler failed to time-align packets.\n" +                    "%u received packets were processed by the handler.\n" +                    "However, a timestamp match could not be determined.\n" +                ) % iterations << std::endl; +                std::swap(curr_info, next_info); //save progress from curr -> next +                curr_info.metadata.has_time_spec = false; +                curr_info.metadata.time_spec = time_spec_t(0.0); +                curr_info.metadata.more_fragments = false; +                curr_info.metadata.fragment_offset = 0; +                curr_info.metadata.start_of_burst = false; +                curr_info.metadata.end_of_burst = false; +                curr_info.metadata.error_code = rx_metadata_t::ERROR_CODE_ALIGNMENT; +                return; +            } + +        } + +        //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.time_spec = curr_info[0].time; +        curr_info.metadata.more_fragments = false; +        curr_info.metadata.fragment_offset = 0; +        /* TODO SOB on RX not supported in hardware +        static const int tlr_sob_flags = (1 << 21) | (1 << 9); //enable and indicator bits +        curr_info.metadata.start_of_burst = curr_info[0].ifpi.has_tlr and (int(curr_info[0].ifpi.tlr & tlr_sob_flags) != 0); +        */ +        curr_info.metadata.start_of_burst = false; +        static const int tlr_eob_flags = (1 << 20) | (1 << 8); //enable and indicator bits +        curr_info.metadata.end_of_burst   = curr_info[0].ifpi.has_tlr and (int(curr_info[0].ifpi.tlr & tlr_eob_flags) != 0); +        curr_info.metadata.error_code = rx_metadata_t::ERROR_CODE_NONE; + +    } + +    /******************************************************************* +     * Receive a single packet: +     * Handles fragmentation, messages, errors, and copy-conversion. +     * When no fragments are available, call the get aligned buffers. +     * Then copy-convert available data into the user's IO buffers. +     ******************************************************************/ +    UHD_INLINE size_t recv_one_packet( +        const uhd::device::recv_buffs_type &buffs, +        const size_t nsamps_per_buff, +        uhd::rx_metadata_t &metadata, +        const uhd::io_type_t &io_type, +        double timeout, +        const size_t buffer_offset_bytes = 0 +    ){ +        //get the next buffer if the current one has expired +        if (get_curr_buffer_info().data_bytes_to_copy == 0){ + +            //reset current buffer info members for reuse +            get_curr_buffer_info().fragment_offset_in_samps = 0; +            get_curr_buffer_info().alignment_time_valid = false; +            get_curr_buffer_info().indexes_to_do.reset(this->size()); + +            //perform receive with alignment logic +            get_aligned_buffs(timeout); +        } + +        buffers_info_type &info = get_curr_buffer_info(); +        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); + +        //extract the number of samples available to copy +        const size_t nsamps_available = info.data_bytes_to_copy/_bytes_per_item; +        const size_t nsamps_to_copy = std::min(nsamps_per_buff*_io_buffs.size(), nsamps_available); +        const size_t bytes_to_copy = nsamps_to_copy*_bytes_per_item; +        const size_t nsamps_to_copy_per_io_buff = nsamps_to_copy/_io_buffs.size(); + +        size_t buff_index = 0; +        BOOST_FOREACH(per_buffer_info_type &buff_info, info){ + +            //fill a vector with pointers to the io buffers +            BOOST_FOREACH(void *&io_buff, _io_buffs){ +                io_buff = reinterpret_cast<char *>(buffs[buff_index++]) + buffer_offset_bytes; +            } + +            //copy-convert the samples from the recv buffer +            _converters[io_type.tid](buff_info.copy_buff, _io_buffs, nsamps_to_copy_per_io_buff, 1/32767.); + +            //update the rx copy buffer to reflect the bytes copied +            buff_info.copy_buff += bytes_to_copy; +        } +        //update the copy buffer's availability +        info.data_bytes_to_copy -= bytes_to_copy; + +        //setup the fragment flags and offset +        metadata.more_fragments = info.data_bytes_to_copy != 0; +        metadata.fragment_offset = info.fragment_offset_in_samps; +        info.fragment_offset_in_samps += nsamps_to_copy; //set for next call + +        return nsamps_to_copy_per_io_buff; +    } +}; + +}}} //namespace + +#endif /* INCLUDED_LIBUHD_TRANSPORT_SUPER_RECV_PACKET_HANDLER_HPP */ diff --git a/host/lib/transport/super_send_packet_handler.hpp b/host/lib/transport/super_send_packet_handler.hpp new file mode 100644 index 000000000..8ebc264ef --- /dev/null +++ b/host/lib/transport/super_send_packet_handler.hpp @@ -0,0 +1,287 @@ +// +// Copyright 2011 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// 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_TRANSPORT_SUPER_SEND_PACKET_HANDLER_HPP +#define INCLUDED_LIBUHD_TRANSPORT_SUPER_SEND_PACKET_HANDLER_HPP + +#include <uhd/config.hpp> +#include <uhd/exception.hpp> +#include <uhd/convert.hpp> +#include <uhd/device.hpp> +#include <uhd/utils/byteswap.hpp> +#include <uhd/types/io_type.hpp> +#include <uhd/types/otw_type.hpp> +#include <uhd/types/metadata.hpp> +#include <uhd/transport/vrt_if_packet.hpp> +#include <uhd/transport/zero_copy.hpp> +#include <boost/thread/thread_time.hpp> +#include <boost/thread/mutex.hpp> +#include <boost/foreach.hpp> +#include <boost/function.hpp> +#include <iostream> +#include <vector> + +namespace uhd{ namespace transport{ namespace sph{ + +/*********************************************************************** + * Super send packet handler + * + * A send packet handler represents a group of channels. + * The channel group shares a common sample rate. + * All channels are sent in unison in send(). + **********************************************************************/ +class send_packet_handler{ +public: +    typedef boost::function<managed_send_buffer::sptr(double)> get_buff_type; +    typedef void(*vrt_packer_type)(boost::uint32_t *, vrt::if_packet_info_t &); +    //typedef boost::function<void(boost::uint32_t *, vrt::if_packet_info_t &)> vrt_packer_type; + +    /*! +     * Make a new packet handler for send +     * \param size the number of transport channels +     */ +    send_packet_handler(const size_t size = 1): +        _next_packet_seq(0) +    { +        this->resize(size); +    } + +    //! Resize the number of transport channels +    void resize(const size_t size){ +        if (this->size() == size) return; +        _props.resize(size); +        static const boost::uint64_t zero = 0; +        _zero_buffs.resize(size, &zero); +    } + +    //! Get the channel width of this handler +    size_t size(void) const{ +        return _props.size(); +    } + +    //! Setup the vrt packer function and offset +    void set_vrt_packer(const vrt_packer_type &vrt_packer, const size_t header_offset_words32 = 0){ +        _vrt_packer = vrt_packer; +        _header_offset_words32 = header_offset_words32; +    } + +    //! Set the rate of ticks per second +    void set_tick_rate(const double rate){ +        _tick_rate = rate; +    } + +    //! Set the rate of samples per second +    void set_samp_rate(const double rate){ +        _samp_rate = rate; +    } + +    /*! +     * Set the function to get a managed buffer. +     * \param xport_chan which transport channel +     * \param get_buff the getter function +     */ +    void set_xport_chan_get_buff(const size_t xport_chan, const get_buff_type &get_buff){ +        _props.at(xport_chan).get_buff = get_buff; +    } + +    /*! +     * Setup the conversion functions (homogeneous across transports). +     * Here, we load a table of converters for all possible io types. +     * This makes the converter look-up an O(1) operation. +     * \param otw_type the channel data type +     * \param width the streams per channel (usually 1) +     */ +    void set_converter(const uhd::otw_type_t &otw_type, const size_t width = 1){ +        _io_buffs.resize(width); +        _converters.resize(128); +        for (size_t io_type = 0; io_type < _converters.size(); io_type++){ +            try{ +                _converters[io_type] = uhd::convert::get_converter_cpu_to_otw( +                    io_type_t::tid_t(io_type), otw_type, 1, width +                ); +            }catch(const uhd::value_error &){} //we expect this, not all io_types valid... +        } +        _bytes_per_item = otw_type.get_sample_size(); +    } + +    /*! +     * Set the maximum number of samples per host packet. +     * Ex: A USRP1 in dual channel mode would be half. +     * \param num_samps the maximum samples in a packet +     */ +    void set_max_samples_per_packet(const size_t num_samps){ +        _max_samples_per_packet = num_samps; +    } + +    //! Get a scoped lock object for this instance +    boost::mutex::scoped_lock get_scoped_lock(void){ +        return boost::mutex::scoped_lock(_mutex); +    } + +    /******************************************************************* +     * Send: +     * The entry point for the fast-path send calls. +     * Dispatch into combinations of single packet send calls. +     ******************************************************************/ +    UHD_INLINE size_t send( +        const uhd::device::send_buffs_type &buffs, +        const size_t nsamps_per_buff, +        const uhd::tx_metadata_t &metadata, +        const uhd::io_type_t &io_type, +        uhd::device::send_mode_t send_mode, +        double timeout +    ){ +        boost::mutex::scoped_lock lock(_mutex); + +        //translate the metadata to vrt if packet info +        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_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.sob     = metadata.start_of_burst; +        if_packet_info.eob     = metadata.end_of_burst; + +        if (nsamps_per_buff <= _max_samples_per_packet) send_mode = uhd::device::SEND_MODE_ONE_PACKET; +        switch(send_mode){ + +        //////////////////////////////////////////////////////////////// +        case uhd::device::SEND_MODE_ONE_PACKET:{ +        //////////////////////////////////////////////////////////////// + +            //TODO remove this code when sample counts of zero are supported by hardware +            if (nsamps_per_buff == 0) return send_one_packet( +                _zero_buffs, 1, if_packet_info, io_type, timeout +            ); + +            return send_one_packet( +                buffs, +                std::min(nsamps_per_buff, _max_samples_per_packet), +                if_packet_info, io_type, timeout +            ); +        } + +        //////////////////////////////////////////////////////////////// +        case uhd::device::SEND_MODE_FULL_BUFF:{ +        //////////////////////////////////////////////////////////////// +            size_t total_num_samps_sent = 0; + +            //false until final fragment +            if_packet_info.eob = false; + +            const size_t num_fragments = (nsamps_per_buff-1)/_max_samples_per_packet; +            const size_t final_length = ((nsamps_per_buff-1)%_max_samples_per_packet)+1; + +            //loop through the following fragment indexes +            for (size_t i = 0; i < num_fragments; i++){ + +                //send a fragment with the helper function +                const size_t num_samps_sent = send_one_packet( +                    buffs, _max_samples_per_packet, +                    if_packet_info, io_type, timeout, +                    total_num_samps_sent*io_type.size +                ); +                total_num_samps_sent += num_samps_sent; +                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)); +                if_packet_info.sob = false; + +            } + +            //send the final fragment with the helper function +            if_packet_info.eob = metadata.end_of_burst; +            return total_num_samps_sent + send_one_packet( +                buffs, final_length, +                if_packet_info, io_type, timeout, +                total_num_samps_sent*io_type.size +            ); +        } + +        default: throw uhd::value_error("unknown send mode"); +        }//switch(send_mode) +    } + +private: + +    boost::mutex _mutex; +    vrt_packer_type _vrt_packer; +    size_t _header_offset_words32; +    double _tick_rate, _samp_rate; +    struct xport_chan_props_type{ +        get_buff_type get_buff; +    }; +    std::vector<xport_chan_props_type> _props; +    std::vector<const void *> _io_buffs; //used in conversion +    size_t _bytes_per_item; //used in conversion +    std::vector<uhd::convert::function_type> _converters; //used in conversion +    size_t _max_samples_per_packet; +    std::vector<const void *> _zero_buffs; +    size_t _next_packet_seq; + +    /******************************************************************* +     * Send a single packet: +     ******************************************************************/ +    size_t send_one_packet( +        const uhd::device::send_buffs_type &buffs, +        const size_t nsamps_per_buff, +        vrt::if_packet_info_t &if_packet_info, +        const uhd::io_type_t &io_type, +        double timeout, +        const size_t buffer_offset_bytes = 0 +    ){ +        //load the rest of the if_packet_info in here +        if_packet_info.num_payload_words32 = (nsamps_per_buff*_io_buffs.size()*_bytes_per_item)/sizeof(boost::uint32_t); +        if_packet_info.packet_count = _next_packet_seq; + +        size_t buff_index = 0; +        BOOST_FOREACH(xport_chan_props_type &props, _props){ +            managed_send_buffer::sptr buff = props.get_buff(timeout); +            if (buff.get() == NULL) return 0; //timeout + +            //fill a vector with pointers to the io buffers +            BOOST_FOREACH(const void *&io_buff, _io_buffs){ +                io_buff = reinterpret_cast<const char *>(buffs[buff_index++]) + buffer_offset_bytes; +            } +            boost::uint32_t *otw_mem = buff->cast<boost::uint32_t *>() + _header_offset_words32; + +            //pack metadata into a vrt header +            _vrt_packer(otw_mem, if_packet_info); +            otw_mem += if_packet_info.num_header_words32; + +            //copy-convert the samples into the send buffer +            _converters[io_type.tid](_io_buffs, otw_mem, nsamps_per_buff, 32767.); + +            //commit the samples to the zero-copy interface +            size_t num_bytes_total = (_header_offset_words32+if_packet_info.num_packet_words32)*sizeof(boost::uint32_t); +            buff->commit(num_bytes_total); + +        } +        _next_packet_seq++; //increment sequence after commits +        return nsamps_per_buff; +    } +}; + +}}} //namespace + +#endif /* INCLUDED_LIBUHD_TRANSPORT_SUPER_SEND_PACKET_HANDLER_HPP */ diff --git a/host/lib/transport/udp_zero_copy.cpp b/host/lib/transport/udp_zero_copy.cpp index 8c97e1e99..bc73b96a8 100644 --- a/host/lib/transport/udp_zero_copy.cpp +++ b/host/lib/transport/udp_zero_copy.cpp @@ -39,14 +39,12 @@ static const size_t DEFAULT_NUM_FRAMES = 32;   **********************************************************************/  class udp_zero_copy_asio_mrb : public managed_recv_buffer{  public: -    typedef boost::function<void(udp_zero_copy_asio_mrb *)> release_cb_type; - -    udp_zero_copy_asio_mrb(void *mem, const release_cb_type &release_cb): -        _mem(mem), _len(0), _release_cb(release_cb){/* NOP */} +    udp_zero_copy_asio_mrb(void *mem, bounded_buffer<udp_zero_copy_asio_mrb *> &pending): +        _mem(mem), _len(0), _pending(pending){/* NOP */}      void release(void){          if (_len == 0) return; -        this->_release_cb(this); +        _pending.push_with_haste(this);          _len = 0;      } @@ -63,7 +61,7 @@ private:      void *_mem;      size_t _len; -    release_cb_type _release_cb; +    bounded_buffer<udp_zero_copy_asio_mrb *> &_pending;  };  /*********************************************************************** @@ -73,14 +71,13 @@ private:   **********************************************************************/  class udp_zero_copy_asio_msb : public managed_send_buffer{  public: -    typedef boost::function<void(udp_zero_copy_asio_msb *, size_t)> commit_cb_type; - -    udp_zero_copy_asio_msb(void *mem, const commit_cb_type &commit_cb): -        _mem(mem), _len(0), _commit_cb(commit_cb){/* NOP */} +    udp_zero_copy_asio_msb(void *mem, bounded_buffer<udp_zero_copy_asio_msb *> &pending, int sock_fd): +        _mem(mem), _len(0), _pending(pending), _sock_fd(sock_fd){/* NOP */}      void commit(size_t len){          if (_len == 0) return; -        this->_commit_cb(this, len); +        ::send(_sock_fd, this->cast<const char *>(), len, 0); +        _pending.push_with_haste(this);          _len = 0;      } @@ -95,7 +92,8 @@ private:      void *_mem;      size_t _len; -    commit_cb_type _commit_cb; +    bounded_buffer<udp_zero_copy_asio_msb *> &_pending; +    int _sock_fd;  };  /*********************************************************************** @@ -138,18 +136,18 @@ public:          //allocate re-usable managed receive buffers          for (size_t i = 0; i < get_num_recv_frames(); i++){ -            _mrb_pool.push_back(udp_zero_copy_asio_mrb(_recv_buffer_pool->at(i), -                boost::bind(&udp_zero_copy_asio_impl::release, this, _1)) -            ); -            handle_recv(&_mrb_pool.back()); +            _mrb_pool.push_back(udp_zero_copy_asio_mrb( +                _recv_buffer_pool->at(i), _pending_recv_buffs +            )); +            _pending_recv_buffs.push_with_haste(&_mrb_pool.back());          }          //allocate re-usable managed send buffers          for (size_t i = 0; i < get_num_send_frames(); i++){ -            _msb_pool.push_back(udp_zero_copy_asio_msb(_send_buffer_pool->at(i), -                boost::bind(&udp_zero_copy_asio_impl::commit, this, _1, _2)) -            ); -            handle_send(&_msb_pool.back()); +            _msb_pool.push_back(udp_zero_copy_asio_msb( +                _send_buffer_pool->at(i), _pending_send_buffs, _sock_fd +            )); +            _pending_send_buffs.push_with_haste(&_msb_pool.back());          }      } @@ -189,19 +187,11 @@ public:                  ::recv(_sock_fd, mrb->cast<char *>(), _recv_frame_size, 0)              ); -            this->handle_recv(mrb); //timeout: return the managed buffer to the queue +            _pending_recv_buffs.push_with_haste(mrb); //timeout: return the managed buffer to the queue          }          return managed_recv_buffer::sptr();      } -    UHD_INLINE void handle_recv(udp_zero_copy_asio_mrb *mrb){ -        _pending_recv_buffs.push_with_haste(mrb); -    } - -    void release(udp_zero_copy_asio_mrb *mrb){ -        handle_recv(mrb); -    } -      size_t get_num_recv_frames(void) const {return _num_recv_frames;}      size_t get_recv_frame_size(void) const {return _recv_frame_size;} @@ -221,15 +211,6 @@ public:          return managed_send_buffer::sptr();      } -    UHD_INLINE void handle_send(udp_zero_copy_asio_msb *msb){ -        _pending_send_buffs.push_with_haste(msb); -    } - -    void commit(udp_zero_copy_asio_msb *msb, size_t len){ -        ::send(_sock_fd, msb->cast<const char *>(), len, 0); -        handle_send(msb); -    } -      size_t get_num_send_frames(void) const {return _num_send_frames;}      size_t get_send_frame_size(void) const {return _send_frame_size;} diff --git a/host/lib/transport/usb_zero_copy_wrapper.cpp b/host/lib/transport/usb_zero_copy_wrapper.cpp new file mode 100644 index 000000000..227c4b392 --- /dev/null +++ b/host/lib/transport/usb_zero_copy_wrapper.cpp @@ -0,0 +1,202 @@ +// +// Copyright 2011 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/transport/usb_zero_copy.hpp> +#include <uhd/transport/bounded_buffer.hpp> +#include <uhd/transport/buffer_pool.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 + **********************************************************************/ +class usb_zero_copy_wrapper_mrb : public managed_recv_buffer{ +public: +    usb_zero_copy_wrapper_mrb(bounded_buffer<usb_zero_copy_wrapper_mrb *> &queue): +        _queue(queue){/*NOP*/} + +    void release(void){ +        if (_mrb.get() == NULL) return; +        _mrb->release(); +        _queue.push_with_haste(this); +        _mrb.reset(); +    } + +    sptr get_new(managed_recv_buffer::sptr mrb, const void *mem, size_t len){ +        _mrb = mrb; +        _mem = mem; +        _len = len; +        return make_managed_buffer(this); +    } + +private: +    const void *get_buff(void) const{return _mem;} +    size_t get_size(void) const{return _len;} + +    bounded_buffer<usb_zero_copy_wrapper_mrb *> &_queue; +    const void *_mem; +    size_t _len; +    managed_recv_buffer::sptr _mrb; +}; + +/*********************************************************************** + * USB zero copy wrapper - managed send buffer + **********************************************************************/ +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*/} + +    void commit(size_t len){ +        if (_msb.get() == NULL) return; +        _msb->commit(next_boundary(len, _boundary)); +        _queue.push_with_haste(this); +        _msb.reset(); +    } + +    sptr get_new(managed_send_buffer::sptr msb){ +        _msb = msb; +        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; +}; + +/*********************************************************************** + * USB zero copy wrapper implementation + **********************************************************************/ +class usb_zero_copy_wrapper : public usb_zero_copy{ +public: +    usb_zero_copy_wrapper( +        sptr usb_zc, size_t usb_frame_boundary +    ): +        _internal_zc(usb_zc), +        _usb_frame_boundary(usb_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)) +    { +        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){ +        //attempt to get a managed recv buffer +        if (not _last_recv_buff.get()){ +            _last_recv_buff = _internal_zc->get_recv_buff(timeout); +            _last_recv_offset = 0; +        } + +        //attempt to get a wrapper for a managed recv buffer +        usb_zero_copy_wrapper_mrb *wmrb = NULL; +        if (_last_recv_buff.get() and _available_recv_buffs.pop_with_timed_wait(wmrb, timeout)){ +            //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) +             +            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); +             +            //check if this receive buffer has been exhausted +            if (_last_recv_offset >= _last_recv_buff->size()) { +                _last_recv_buff.reset(); +            } +             +            return recv_buff; +        } + +        //otherwise return a null sptr for failure +        return managed_recv_buffer::sptr(); +    } + +    size_t get_num_recv_frames(void) const{ +        return _internal_zc->get_num_recv_frames(); +    } + +    size_t get_recv_frame_size(void) const{ +        return _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(); +    } + +    size_t get_num_send_frames(void) const{ +        return _internal_zc->get_num_send_frames(); +    } + +    size_t get_send_frame_size(void) const{ +        return _internal_zc->get_send_frame_size(); +    } + +private: +    sptr _internal_zc; +    size_t _usb_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; +     +    //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; +}; + +/*********************************************************************** + * USB zero copy wrapper factory function + **********************************************************************/ +usb_zero_copy::sptr usb_zero_copy::make_wrapper( +    sptr usb_zc, size_t usb_frame_boundary +){ +    return sptr(new usb_zero_copy_wrapper(usb_zc, usb_frame_boundary)); +} diff --git a/host/lib/transport/vrt_packet_handler.hpp b/host/lib/transport/vrt_packet_handler.hpp deleted file mode 100644 index d74b2c13c..000000000 --- a/host/lib/transport/vrt_packet_handler.hpp +++ /dev/null @@ -1,470 +0,0 @@ -// -// Copyright 2010-2011 Ettus Research LLC -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// 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_TRANSPORT_VRT_PACKET_HANDLER_HPP -#define INCLUDED_LIBUHD_TRANSPORT_VRT_PACKET_HANDLER_HPP - -#include <uhd/config.hpp> -#include <uhd/device.hpp> -#include <uhd/exception.hpp> -#include <uhd/utils/byteswap.hpp> -#include <uhd/types/io_type.hpp> -#include <uhd/types/otw_type.hpp> -#include <uhd/types/metadata.hpp> -#include <uhd/transport/vrt_if_packet.hpp> -#include <uhd/convert.hpp> -#include <uhd/transport/zero_copy.hpp> -#include <boost/function.hpp> -#include <stdexcept> -#include <iostream> -#include <vector> - -namespace vrt_packet_handler{ - -//this may change in the future but its a constant for now -static const size_t OTW_BYTES_PER_SAMP = sizeof(boost::uint32_t); - -template <typename T> UHD_INLINE T get_context_code( -    const boost::uint32_t *vrt_hdr, -    const uhd::transport::vrt::if_packet_info_t &if_packet_info -){ -    //extract the context word (we dont know the endianness so mirror the bytes) -    boost::uint32_t word0 = vrt_hdr[if_packet_info.num_header_words32] | -              uhd::byteswap(vrt_hdr[if_packet_info.num_header_words32]); -    return T(word0 & 0xff); -} - -/*********************************************************************** - * vrt packet handler for recv - **********************************************************************/ -    typedef std::vector<uhd::transport::managed_recv_buffer::sptr> managed_recv_buffs_t; -    typedef boost::function<bool(managed_recv_buffs_t &)> get_recv_buffs_t; -    typedef boost::function<void(size_t /*which channel*/)> handle_overflow_t; -    typedef boost::function<void(const boost::uint32_t *, uhd::transport::vrt::if_packet_info_t &)> vrt_unpacker_t; - -    static inline void handle_overflow_nop(size_t){} - -    struct recv_state{ -        //width of the receiver in channels -        size_t width; - -        //state variables to handle fragments -        managed_recv_buffs_t managed_buffs; -        std::vector<const boost::uint8_t *> copy_buffs; -        size_t size_of_copy_buffs; -        size_t fragment_offset_in_samps; -        std::vector<void *> io_buffs; -        std::vector<const void *> otw_buffs; - -        recv_state(size_t width = 1): -            width(width), -            managed_buffs(width), -            copy_buffs(width, NULL), -            size_of_copy_buffs(0), -            fragment_offset_in_samps(0), -            io_buffs(0) //resized later -        { -            /* NOP */ -        } -    }; - -    /******************************************************************* -     * Unpack a received vrt header and set the copy buffer. -     *  - helper function for vrt_packet_handler::_recv1 -     ******************************************************************/ -    static UHD_INLINE void _recv1_helper( -        recv_state &state, -        uhd::rx_metadata_t &metadata, -        double tick_rate, -        const vrt_unpacker_t &vrt_unpacker, -        const handle_overflow_t &handle_overflow, -        size_t vrt_header_offset_words32 -    ){ -        //vrt unpack each managed buffer -        uhd::transport::vrt::if_packet_info_t if_packet_info; -        for (size_t i = 0; i < state.width; i++){ -            if (state.managed_buffs[i].get() == NULL) continue; //better have a message packet coming up... - -            //extract packet words and check thats its enough to move on -            size_t num_packet_words32 = state.managed_buffs[i]->size()/sizeof(boost::uint32_t); -            if (num_packet_words32 <= vrt_header_offset_words32){ -                throw std::runtime_error("recv buffer smaller than vrt packet offset"); -            } - -            //unpack the vrt header into the info struct -            const boost::uint32_t *vrt_hdr = state.managed_buffs[i]->cast<const boost::uint32_t *>() + vrt_header_offset_words32; -            if_packet_info.num_packet_words32 = num_packet_words32 - vrt_header_offset_words32; -            vrt_unpacker(vrt_hdr, if_packet_info); - -            //handle the non-data packet case and parse its contents -            if (if_packet_info.packet_type != uhd::transport::vrt::if_packet_info_t::PACKET_TYPE_DATA){ - -                metadata.error_code = get_context_code<uhd::rx_metadata_t::error_code_t>(vrt_hdr, if_packet_info); -                if (metadata.error_code == uhd::rx_metadata_t::ERROR_CODE_OVERFLOW) handle_overflow(i); - -                //break to exit loop and store metadata below -                state.size_of_copy_buffs = 0; break; -            } - -            //setup the buffer to point to the data -            state.copy_buffs[i] = reinterpret_cast<const boost::uint8_t *>(vrt_hdr + if_packet_info.num_header_words32); - -            //store the minimum payload length into the copy buffer length -            size_t num_payload_bytes = if_packet_info.num_payload_words32*sizeof(boost::uint32_t); -            if (i == 0 or state.size_of_copy_buffs > num_payload_bytes){ -                state.size_of_copy_buffs = num_payload_bytes; -            } -        } - -        //store the last vrt info into the metadata -        metadata.has_time_spec = if_packet_info.has_tsi and if_packet_info.has_tsf; -        metadata.time_spec = uhd::time_spec_t( -            time_t(if_packet_info.tsi), size_t(if_packet_info.tsf), tick_rate -        ); -        static const int tlr_sob_flags = (1 << 21) | (1 << 9); //enable and indicator bits -        metadata.start_of_burst = if_packet_info.has_tlr and (int(if_packet_info.tlr & tlr_sob_flags) == tlr_sob_flags); -        static const int tlr_eob_flags = (1 << 20) | (1 << 8); //enable and indicator bits -        metadata.end_of_burst   = if_packet_info.has_tlr and (int(if_packet_info.tlr & tlr_eob_flags) == tlr_eob_flags); -    } - -    /******************************************************************* -     * Recv data, unpack a vrt header, and copy-convert the data. -     *  - helper function for vrt_packet_handler::recv -     ******************************************************************/ -    static UHD_INLINE size_t _recv1( -        recv_state &state, -        const uhd::device::recv_buffs_type &buffs, -        size_t offset_bytes, -        size_t total_samps, -        uhd::rx_metadata_t &metadata, -        uhd::convert::function_type &converter, -        double tick_rate, -        const vrt_unpacker_t &vrt_unpacker, -        const get_recv_buffs_t &get_recv_buffs, -        const handle_overflow_t &handle_overflow, -        size_t vrt_header_offset_words32, -        size_t chans_per_otw_buff -    ){ -        metadata.error_code = uhd::rx_metadata_t::ERROR_CODE_NONE; - -        //perform a receive if no rx data is waiting to be copied -        if (state.size_of_copy_buffs == 0){ -            state.fragment_offset_in_samps = 0; -            if (not get_recv_buffs(state.managed_buffs)){ -                metadata.error_code = uhd::rx_metadata_t::ERROR_CODE_TIMEOUT; -                return 0; -            } -            try{ -                _recv1_helper( -                    state, metadata, tick_rate, -                    vrt_unpacker, handle_overflow, -                    vrt_header_offset_words32 -                ); -            }catch(const std::exception &e){ -                state.size_of_copy_buffs = 0; //reset copy buffs size -                std::cerr << "Error (recv): " << e.what() << std::endl; -                metadata.error_code = uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET; -                return 0; -            } -        } -        //defaults for the metadata when this is a fragment -        else{ -            metadata.has_time_spec = false; -            metadata.start_of_burst = false; -            metadata.end_of_burst = false; -        } - -        //extract the number of samples available to copy -        size_t bytes_per_item = OTW_BYTES_PER_SAMP; -        size_t nsamps_available = state.size_of_copy_buffs/bytes_per_item; -        size_t nsamps_to_copy = std::min(total_samps*chans_per_otw_buff, nsamps_available); -        size_t bytes_to_copy = nsamps_to_copy*bytes_per_item; -        size_t nsamps_to_copy_per_io_buff = nsamps_to_copy/chans_per_otw_buff; - -        for (size_t i = 0; i < buffs.size(); i+=chans_per_otw_buff){ - -            //fill a vector with pointers to the io buffers -            for (size_t j = 0; j < chans_per_otw_buff; j++){ -                state.io_buffs[j] = reinterpret_cast<boost::uint8_t *>(buffs[i+j]) + offset_bytes; -            } - -            //copy-convert the samples from the recv buffer -            converter(state.copy_buffs[i], state.io_buffs, nsamps_to_copy_per_io_buff); - -            //update the rx copy buffer to reflect the bytes copied -            state.copy_buffs[i] += bytes_to_copy; -        } -        //update the copy buffer's availability -        state.size_of_copy_buffs -= bytes_to_copy; - -        //setup the fragment flags and offset -        metadata.more_fragments = state.size_of_copy_buffs != 0; -        metadata.fragment_offset = state.fragment_offset_in_samps; -        state.fragment_offset_in_samps += nsamps_to_copy; //set for next call - -        return nsamps_to_copy_per_io_buff; -    } - -    /******************************************************************* -     * Recv vrt packets and copy convert the samples into the buffer. -     ******************************************************************/ -    static UHD_INLINE size_t recv( -        recv_state &state, -        const uhd::device::recv_buffs_type &buffs, -        const size_t total_num_samps, -        uhd::rx_metadata_t &metadata, -        uhd::device::recv_mode_t recv_mode, -        const uhd::io_type_t &io_type, -        const uhd::otw_type_t &otw_type, -        double tick_rate, -        const vrt_unpacker_t &vrt_unpacker, -        const get_recv_buffs_t &get_recv_buffs, -        const handle_overflow_t &handle_overflow = &handle_overflow_nop, -        size_t vrt_header_offset_words32 = 0, -        size_t chans_per_otw_buff = 1 -    ){ -        state.io_buffs.resize(chans_per_otw_buff); - -        uhd::convert::function_type converter( -            uhd::convert::get_converter_otw_to_cpu( -                io_type, otw_type, 1, chans_per_otw_buff -        )); - -        switch(recv_mode){ - -        //////////////////////////////////////////////////////////////// -        case uhd::device::RECV_MODE_ONE_PACKET:{ -        //////////////////////////////////////////////////////////////// -            return _recv1( -                state, -                buffs, 0, -                total_num_samps, -                metadata, -                converter, -                tick_rate, -                vrt_unpacker, -                get_recv_buffs, -                handle_overflow, -                vrt_header_offset_words32, -                chans_per_otw_buff -            ); -        } - -        //////////////////////////////////////////////////////////////// -        case uhd::device::RECV_MODE_FULL_BUFF:{ -        //////////////////////////////////////////////////////////////// -            size_t accum_num_samps = 0; -            uhd::rx_metadata_t tmp_md; -            while(accum_num_samps < total_num_samps){ -                size_t num_samps = _recv1( -                    state, -                    buffs, accum_num_samps*io_type.size, -                    total_num_samps - accum_num_samps, -                    (accum_num_samps == 0)? metadata : tmp_md, //only the first metadata gets kept -                    converter, -                    tick_rate, -                    vrt_unpacker, -                    get_recv_buffs, -                    handle_overflow, -                    vrt_header_offset_words32, -                    chans_per_otw_buff -                ); -                if (num_samps == 0) break; //had a recv timeout or error, break loop -                accum_num_samps += num_samps; -            } -            return accum_num_samps; -        } - -        default: throw std::runtime_error("unknown recv mode"); -        }//switch(recv_mode) -    } - -/*********************************************************************** - * vrt packet handler for send - **********************************************************************/ -    typedef std::vector<uhd::transport::managed_send_buffer::sptr> managed_send_buffs_t; -    typedef boost::function<bool(managed_send_buffs_t &)> get_send_buffs_t; -    typedef boost::function<void(boost::uint32_t *, uhd::transport::vrt::if_packet_info_t &)> vrt_packer_t; - -    static const boost::uint64_t zeros = 0; - -    struct send_state{ -        //init the expected seq number -        size_t next_packet_seq; -        managed_send_buffs_t managed_buffs; -        std::vector<const void *> zero_buffs; -        std::vector<const void *> io_buffs; - -        send_state(size_t width = 1): -            next_packet_seq(0), -            managed_buffs(width), -            zero_buffs(width, &zeros), -            io_buffs(0) //resized later -        { -            /* NOP */ -        } -    }; - -    /******************************************************************* -     * Pack a vrt header, copy-convert the data, and send it. -     *  - helper function for vrt_packet_handler::send -     ******************************************************************/ -    static UHD_INLINE size_t _send1( -        send_state &state, -        const uhd::device::send_buffs_type &buffs, -        const size_t offset_bytes, -        const size_t num_samps, -        uhd::transport::vrt::if_packet_info_t &if_packet_info, -        uhd::convert::function_type &converter, -        const vrt_packer_t &vrt_packer, -        const get_send_buffs_t &get_send_buffs, -        const size_t vrt_header_offset_words32, -        const size_t chans_per_otw_buff -    ){ -        //load the rest of the if_packet_info in here -        if_packet_info.num_payload_words32 = (num_samps*chans_per_otw_buff*OTW_BYTES_PER_SAMP)/sizeof(boost::uint32_t); -        if_packet_info.packet_count = state.next_packet_seq; - -        //get send buffers for each otw channel -        if (not get_send_buffs(state.managed_buffs)) return 0; - -        for (size_t i = 0; i < buffs.size(); i+=chans_per_otw_buff){ -            //calculate pointers with offsets to io and otw memory -            for (size_t j = 0; j < chans_per_otw_buff; j++){ -                state.io_buffs[j] = reinterpret_cast<const boost::uint8_t *>(buffs[i+j]) + offset_bytes; -            } -            boost::uint32_t *otw_mem = state.managed_buffs[i]->cast<boost::uint32_t *>() + vrt_header_offset_words32; - -            //pack metadata into a vrt header -            vrt_packer(otw_mem, if_packet_info); -            otw_mem += if_packet_info.num_header_words32; - -            //copy-convert the samples into the send buffer -            converter(state.io_buffs, otw_mem, num_samps); - -            //commit the samples to the zero-copy interface -            size_t num_bytes_total = (vrt_header_offset_words32+if_packet_info.num_packet_words32)*sizeof(boost::uint32_t); -            state.managed_buffs[i]->commit(num_bytes_total); -        } -        state.next_packet_seq++; //increment sequence after commits -        return num_samps; -    } - -    /******************************************************************* -     * Send vrt packets and copy convert the samples into the buffer. -     ******************************************************************/ -    static UHD_INLINE size_t send( -        send_state &state, -        const uhd::device::send_buffs_type &buffs, -        const size_t total_num_samps, -        const uhd::tx_metadata_t &metadata, -        uhd::device::send_mode_t send_mode, -        const uhd::io_type_t &io_type, -        const uhd::otw_type_t &otw_type, -        double tick_rate, -        const vrt_packer_t &vrt_packer, -        const get_send_buffs_t &get_send_buffs, -        size_t max_samples_per_packet, -        size_t vrt_header_offset_words32 = 0, -        size_t chans_per_otw_buff = 1 -    ){ -        state.io_buffs.resize(chans_per_otw_buff); - -        uhd::convert::function_type converter( -            uhd::convert::get_converter_cpu_to_otw( -                io_type, otw_type, chans_per_otw_buff, 1 -        )); - -        //translate the metadata to vrt if packet info -        uhd::transport::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.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 (total_num_samps <= max_samples_per_packet) send_mode = uhd::device::SEND_MODE_ONE_PACKET; -        switch(send_mode){ - -        //////////////////////////////////////////////////////////////// -        case uhd::device::SEND_MODE_ONE_PACKET:{ -        //////////////////////////////////////////////////////////////// - -            //fill in parts of the packet info overwrote in full buff mode -            if_packet_info.has_tsi = metadata.has_time_spec; -            if_packet_info.has_tsf = metadata.has_time_spec; -            if_packet_info.sob = metadata.start_of_burst; -            if_packet_info.eob = metadata.end_of_burst; - -            return _send1( -                state, -                //TODO remove this code when sample counts of zero are supported by hardware -                (total_num_samps)?buffs : state.zero_buffs, 0, -                std::max<size_t>(1, std::min(total_num_samps, max_samples_per_packet)), -                if_packet_info, -                converter, -                vrt_packer, -                get_send_buffs, -                vrt_header_offset_words32, -                chans_per_otw_buff -            ); -        } - -        //////////////////////////////////////////////////////////////// -        case uhd::device::SEND_MODE_FULL_BUFF:{ -        //////////////////////////////////////////////////////////////// -            size_t total_num_samps_sent = 0; - -            //loop through the following fragment indexes -            while(total_num_samps_sent < total_num_samps){ - -                //calculate per-loop-iteration variables -                const size_t total_num_samps_unsent = total_num_samps - total_num_samps_sent; -                const bool first_fragment = (total_num_samps_sent == 0); -                const bool final_fragment = (total_num_samps_unsent <= max_samples_per_packet); - -                //calculate new flags for the fragments -                if_packet_info.has_tsi = metadata.has_time_spec  and first_fragment; -                if_packet_info.has_tsf = if_packet_info.has_tsi; -                if_packet_info.sob     = metadata.start_of_burst and first_fragment; -                if_packet_info.eob     = metadata.end_of_burst   and final_fragment; - -                //send the fragment with the helper function -                const size_t num_samps_sent = _send1( -                    state, -                    buffs, total_num_samps_sent*io_type.size, -                    std::min(total_num_samps_unsent, max_samples_per_packet), -                    if_packet_info, -                    converter, -                    vrt_packer, -                    get_send_buffs, -                    vrt_header_offset_words32, -                    chans_per_otw_buff -                ); -                total_num_samps_sent += num_samps_sent; -                if (num_samps_sent == 0) return total_num_samps_sent; -            } -            return total_num_samps_sent; -        } - -        default: throw std::runtime_error("unknown send mode"); -        }//switch(send_mode) -    } - -} //namespace vrt_packet_handler - -#endif /* INCLUDED_LIBUHD_TRANSPORT_VRT_PACKET_HANDLER_HPP */ diff --git a/host/lib/usrp/CMakeLists.txt b/host/lib/usrp/CMakeLists.txt index 018beb417..a30a45977 100644 --- a/host/lib/usrp/CMakeLists.txt +++ b/host/lib/usrp/CMakeLists.txt @@ -34,6 +34,8 @@ LIBUHD_APPEND_SOURCES(  )  INCLUDE_SUBDIRECTORY(dboard) +INCLUDE_SUBDIRECTORY(fx2)  INCLUDE_SUBDIRECTORY(usrp1)  INCLUDE_SUBDIRECTORY(usrp2) +INCLUDE_SUBDIRECTORY(usrp_b100)  INCLUDE_SUBDIRECTORY(usrp_e100) diff --git a/host/lib/usrp/dboard/db_basic_and_lf.cpp b/host/lib/usrp/dboard/db_basic_and_lf.cpp index 6f8de9a7b..566e24d97 100644 --- a/host/lib/usrp/dboard/db_basic_and_lf.cpp +++ b/host/lib/usrp/dboard/db_basic_and_lf.cpp @@ -111,6 +111,7 @@ basic_rx::basic_rx(ctor_args_t args, double max_freq) : rx_dboard_base(args){      this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, 0x0000);      this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, 0xFFFF);      this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, 0x0000); +    this->get_iface()->set_clock_enabled(dboard_iface::UNIT_RX, true);  }  basic_rx::~basic_rx(void){ @@ -216,6 +217,7 @@ void basic_rx::rx_set(const wax::obj &key_, const wax::obj &val){   **********************************************************************/  basic_tx::basic_tx(ctor_args_t args, double max_freq) : tx_dboard_base(args){      _max_freq = max_freq; +    this->get_iface()->set_clock_enabled(dboard_iface::UNIT_TX, true);  }  basic_tx::~basic_tx(void){ diff --git a/host/lib/usrp/dboard/db_tvrx2.cpp b/host/lib/usrp/dboard/db_tvrx2.cpp index fe07a70a5..39ff90d79 100644 --- a/host/lib/usrp/dboard/db_tvrx2.cpp +++ b/host/lib/usrp/dboard/db_tvrx2.cpp @@ -53,6 +53,7 @@  #include <uhd/utils/static.hpp>  #include <uhd/utils/log.hpp>  #include <uhd/utils/msg.hpp> +#include <uhd/utils/safe_call.hpp>  #include <uhd/utils/assert_has.hpp>  #include <uhd/utils/algorithm.hpp>  #include <uhd/types/ranges.hpp> @@ -1039,7 +1040,7 @@ tvrx2::~tvrx2(void){      UHD_LOGV(often) << boost::format(          "TVRX2 (%s): Called Destructor"      ) % (get_subdev_name()) << std::endl; -    if (_enabled) set_disabled(); +    UHD_SAFE_CALL(if (_enabled) set_disabled();)  }  void tvrx2::set_disabled(void){ diff --git a/host/lib/usrp/dboard/db_xcvr2450.cpp b/host/lib/usrp/dboard/db_xcvr2450.cpp index c775eae38..fcd05ea04 100644 --- a/host/lib/usrp/dboard/db_xcvr2450.cpp +++ b/host/lib/usrp/dboard/db_xcvr2450.cpp @@ -50,6 +50,7 @@  #include "max2829_regs.hpp"  #include <uhd/utils/log.hpp>  #include <uhd/utils/static.hpp> +#include <uhd/utils/safe_call.hpp>  #include <uhd/utils/assert_has.hpp>  #include <uhd/utils/algorithm.hpp>  #include <uhd/types/ranges.hpp> @@ -234,7 +235,7 @@ xcvr2450::xcvr2450(ctor_args_t args) : xcvr_dboard_base(args){  }  xcvr2450::~xcvr2450(void){ -    spi_reset(); +    UHD_SAFE_CALL(spi_reset();)  }  void xcvr2450::spi_reset(void){ diff --git a/host/lib/usrp/dboard_manager.cpp b/host/lib/usrp/dboard_manager.cpp index 11b72b9fa..3cc2ca3c0 100644 --- a/host/lib/usrp/dboard_manager.cpp +++ b/host/lib/usrp/dboard_manager.cpp @@ -20,6 +20,7 @@  #include <uhd/usrp/subdev_props.hpp>  #include <uhd/utils/msg.hpp>  #include <uhd/utils/log.hpp> +#include <uhd/utils/safe_call.hpp>  #include <uhd/utils/static.hpp>  #include <uhd/exception.hpp>  #include <uhd/types/dict.hpp> @@ -350,9 +351,9 @@ void dboard_manager_impl::init(      }  } -dboard_manager_impl::~dboard_manager_impl(void){ +dboard_manager_impl::~dboard_manager_impl(void){UHD_SAFE_CALL(      set_nice_dboard_if(); -} +)}  prop_names_t dboard_manager_impl::get_rx_subdev_names(void){      return _rx_dboards.keys(); diff --git a/host/lib/usrp/fx2/CMakeLists.txt b/host/lib/usrp/fx2/CMakeLists.txt new file mode 100644 index 000000000..109eea6b7 --- /dev/null +++ b/host/lib/usrp/fx2/CMakeLists.txt @@ -0,0 +1,27 @@ +# +# Copyright 2011 Ettus Research LLC +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# 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/>. +# + +######################################################################## +IF(ENABLE_USRP1) +    INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/../firmware/fx2/common) +    INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) + +    LIBUHD_APPEND_SOURCES( +        ${CMAKE_CURRENT_SOURCE_DIR}/fx2_ctrl.cpp +        ${CMAKE_CURRENT_SOURCE_DIR}/fx2_ctrl.hpp +    ) +ENDIF(ENABLE_USRP1) diff --git a/host/lib/usrp/usrp1/usrp1_ctrl.cpp b/host/lib/usrp/fx2/fx2_ctrl.cpp index 2e6f6e014..06ca51c25 100644 --- a/host/lib/usrp/usrp1/usrp1_ctrl.cpp +++ b/host/lib/usrp/fx2/fx2_ctrl.cpp @@ -15,7 +15,7 @@  // along with this program.  If not, see <http://www.gnu.org/licenses/>.  // -#include "usrp1_ctrl.hpp" +#include "fx2_ctrl.hpp"  #include "usrp_commands.h"  #include <uhd/utils/msg.hpp>  #include <uhd/exception.hpp> @@ -29,6 +29,7 @@  #include <cstring>  using namespace uhd; +using namespace uhd::usrp;  #define FX2_FIRMWARE_LOAD 0xa0 @@ -128,9 +129,9 @@ bool parse_record(std::string *record, unsigned int &len,  /*!   * USRP control implementation for device discovery and configuration   */ -class usrp_ctrl_impl : public usrp_ctrl { +class fx2_ctrl_impl : public fx2_ctrl {  public: -    usrp_ctrl_impl(uhd::transport::usb_control::sptr ctrl_transport) +    fx2_ctrl_impl(uhd::transport::usb_control::sptr ctrl_transport)      {          _ctrl_transport = ctrl_transport;      } @@ -240,6 +241,7 @@ public:          while (not file.eof()) {              file.read((char *)buf, sizeof(buf));              size_t n = file.gcount(); +            if(n == 0) continue;              int ret = usrp_control_write(VRQ_FPGA_LOAD, 0, FL_XFER, buf, n);              if (ret < 0 or size_t(ret) != n) {                  throw uhd::io_error("usrp_load_fpga: fpga load error"); @@ -414,9 +416,9 @@ private:  };  /*********************************************************************** - * Public make function for usrp_ctrl interface + * Public make function for fx2_ctrl interface   **********************************************************************/ -usrp_ctrl::sptr usrp_ctrl::make(uhd::transport::usb_control::sptr ctrl_transport){ -    return sptr(new usrp_ctrl_impl(ctrl_transport)); +fx2_ctrl::sptr fx2_ctrl::make(uhd::transport::usb_control::sptr ctrl_transport){ +    return sptr(new fx2_ctrl_impl(ctrl_transport));  } diff --git a/host/lib/usrp/usrp1/usrp1_ctrl.hpp b/host/lib/usrp/fx2/fx2_ctrl.hpp index a6e4ffba7..37fa09605 100644 --- a/host/lib/usrp/usrp1/usrp1_ctrl.hpp +++ b/host/lib/usrp/fx2/fx2_ctrl.hpp @@ -22,9 +22,11 @@  #include <boost/shared_ptr.hpp>  #include <boost/utility.hpp> -class usrp_ctrl : boost::noncopyable{ +namespace uhd{ namespace usrp{ + +class fx2_ctrl : boost::noncopyable{  public: -    typedef boost::shared_ptr<usrp_ctrl> sptr; +    typedef boost::shared_ptr<fx2_ctrl> sptr;      /*!       * Make a usrp control object from a control transport @@ -55,7 +57,7 @@ public:       * \param filename name of EEPROM image       */      virtual void usrp_load_eeprom(std::string filestring) = 0; - +          /*!       * Submit an IN transfer        * \param request device specific request  @@ -115,4 +117,6 @@ public:      virtual void usrp_tx_enable(bool on) = 0;  }; +}} //namespace uhd::usrp +  #endif /* INCLUDED_USRP_CTRL_HPP */ diff --git a/host/lib/usrp/usrp1/CMakeLists.txt b/host/lib/usrp/usrp1/CMakeLists.txt index 9e50f5728..c208cfe8c 100644 --- a/host/lib/usrp/usrp1/CMakeLists.txt +++ b/host/lib/usrp/usrp1/CMakeLists.txt @@ -25,8 +25,6 @@  LIBUHD_REGISTER_COMPONENT("USRP1" ENABLE_USRP1 ON "ENABLE_LIBUHD;ENABLE_USB" OFF)  IF(ENABLE_USRP1) -    INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/../firmware/fx2/common) -      LIBUHD_APPEND_SOURCES(          ${CMAKE_CURRENT_SOURCE_DIR}/clock_ctrl.cpp          ${CMAKE_CURRENT_SOURCE_DIR}/clock_ctrl.hpp @@ -43,7 +41,5 @@ IF(ENABLE_USRP1)          ${CMAKE_CURRENT_SOURCE_DIR}/usrp1_iface.hpp          ${CMAKE_CURRENT_SOURCE_DIR}/usrp1_impl.cpp          ${CMAKE_CURRENT_SOURCE_DIR}/usrp1_impl.hpp -        ${CMAKE_CURRENT_SOURCE_DIR}/usrp1_ctrl.cpp -        ${CMAKE_CURRENT_SOURCE_DIR}/usrp1_ctrl.hpp      )  ENDIF(ENABLE_USRP1) diff --git a/host/lib/usrp/usrp1/codec_ctrl.cpp b/host/lib/usrp/usrp1/codec_ctrl.cpp index 64a93ede5..448135185 100644 --- a/host/lib/usrp/usrp1/codec_ctrl.cpp +++ b/host/lib/usrp/usrp1/codec_ctrl.cpp @@ -20,6 +20,7 @@  #include "clock_ctrl.hpp"  #include "ad9862_regs.hpp"  #include <uhd/utils/log.hpp> +#include <uhd/utils/safe_call.hpp>  #include <uhd/types/dict.hpp>  #include <uhd/exception.hpp>  #include <uhd/utils/algorithm.hpp> @@ -140,8 +141,7 @@ usrp1_codec_ctrl_impl::usrp1_codec_ctrl_impl(usrp1_iface::sptr iface,      this->send_reg(34);  } -usrp1_codec_ctrl_impl::~usrp1_codec_ctrl_impl(void) -{ +usrp1_codec_ctrl_impl::~usrp1_codec_ctrl_impl(void){UHD_SAFE_CALL(      //set aux dacs to zero      this->write_aux_dac(AUX_DAC_A, 0);      this->write_aux_dac(AUX_DAC_B, 0); @@ -154,7 +154,7 @@ usrp1_codec_ctrl_impl::~usrp1_codec_ctrl_impl(void)      _ad9862_regs.tx_digital_pd = 1;      _ad9862_regs.tx_analog_pd = ad9862_regs_t::TX_ANALOG_PD_BOTH;      this->send_reg(8); -} +)}  /***********************************************************************   * Codec Control Gain Control Methods diff --git a/host/lib/usrp/usrp1/dsp_impl.cpp b/host/lib/usrp/usrp1/dsp_impl.cpp index 66b11b989..0bddc49f0 100644 --- a/host/lib/usrp/usrp1/dsp_impl.cpp +++ b/host/lib/usrp/usrp1/dsp_impl.cpp @@ -114,6 +114,7 @@ void usrp1_impl::rx_dsp_set(const wax::obj &key_, const wax::obj &val, size_t wh              _iface->poke32(FR_DECIM_RATE, _rx_dsp_decim/2 - 1);              this->restore_rx(s);          } +        this->update_xport_channel_mapping(); //rate changed -> update          return;      case DSP_PROP_STREAM_CMD: @@ -211,8 +212,10 @@ void usrp1_impl::tx_dsp_set(const wax::obj &key_, const wax::obj &val, size_t wh              bool s = this->disable_tx();              _iface->poke32(FR_INTERP_RATE, _tx_dsp_interp/2 - 1);              this->restore_tx(s); -            return;          } +        this->update_xport_channel_mapping(); //rate changed -> update +        return; +      default: UHD_THROW_PROP_SET_ERROR();      } diff --git a/host/lib/usrp/usrp1/io_impl.cpp b/host/lib/usrp/usrp1/io_impl.cpp index 8ac2696eb..90ed17cd8 100644 --- a/host/lib/usrp/usrp1/io_impl.cpp +++ b/host/lib/usrp/usrp1/io_impl.cpp @@ -15,12 +15,15 @@  // along with this program.  If not, see <http://www.gnu.org/licenses/>.  // -#include "../../transport/vrt_packet_handler.hpp" +#define SRPH_DONT_CHECK_SEQUENCE +#include "../../transport/super_recv_packet_handler.hpp" +#include "../../transport/super_send_packet_handler.hpp"  #include "usrp_commands.h"  #include "usrp1_impl.hpp"  #include <uhd/utils/msg.hpp>  #include <uhd/utils/safe_call.hpp>  #include <uhd/utils/thread_priority.hpp> +#include <uhd/usrp/dsp_props.hpp>  #include <uhd/transport/bounded_buffer.hpp>  #include <boost/bind.hpp>  #include <boost/format.hpp> @@ -88,13 +91,39 @@ private:  };  /*********************************************************************** + * BS VRT packer/unpacker functions (since samples don't have headers) + **********************************************************************/ +static void usrp1_bs_vrt_packer( +    boost::uint32_t *, +    vrt::if_packet_info_t &if_packet_info +){ +    if_packet_info.num_header_words32 = 0; +    if_packet_info.num_packet_words32 = if_packet_info.num_payload_words32; +} + +static void usrp1_bs_vrt_unpacker( +    const boost::uint32_t *, +    vrt::if_packet_info_t &if_packet_info +){ +    if_packet_info.packet_type = vrt::if_packet_info_t::PACKET_TYPE_DATA; +    if_packet_info.num_payload_words32 = if_packet_info.num_packet_words32; +    if_packet_info.num_header_words32 = 0; +    if_packet_info.packet_count = 0; +    if_packet_info.sob = false; +    if_packet_info.eob = false; +    if_packet_info.has_sid = false; +    if_packet_info.has_cid = false; +    if_packet_info.has_tsi = false; +    if_packet_info.has_tsf = false; +    if_packet_info.has_tlr = false; +} + +/***********************************************************************   * IO Implementation Details   **********************************************************************/  struct usrp1_impl::io_impl{      io_impl(zero_copy_if::sptr data_transport):          data_transport(data_transport), -        get_recv_buffs_fcn(boost::bind(&usrp1_impl::io_impl::get_recv_buffs, this, _1)), -        get_send_buffs_fcn(boost::bind(&usrp1_impl::io_impl::get_send_buffs, this, _1)),          underflow_poll_samp_count(0),          overflow_poll_samp_count(0),          curr_buff(offset_send_buffer(data_transport->get_send_buff())), @@ -109,16 +138,9 @@ struct usrp1_impl::io_impl{      zero_copy_if::sptr data_transport; -    //timeouts set on calls to recv/send (passed into get buffs methods) -    double recv_timeout, send_timeout; - -    //bound callbacks for get buffs (bound once here, not in fast-path) -    vrt_packet_handler::get_recv_buffs_t get_recv_buffs_fcn; -    vrt_packet_handler::get_send_buffs_t get_send_buffs_fcn; -      //state management for the vrt packet handler code -    vrt_packet_handler::recv_state packet_handler_recv_state; -    vrt_packet_handler::send_state packet_handler_send_state; +    sph::recv_packet_handler recv_handler; +    sph::send_packet_handler send_handler;      //state management for overflow and underflow      size_t underflow_poll_samp_count; @@ -132,11 +154,13 @@ struct usrp1_impl::io_impl{      offset_managed_send_buffer omsb;      void commit_send_buff(offset_send_buffer&, offset_send_buffer&, size_t);      void flush_send_buff(void); -    bool get_send_buffs(vrt_packet_handler::managed_send_buffs_t &); -    bool get_recv_buffs(vrt_packet_handler::managed_recv_buffs_t &buffs){ -        UHD_ASSERT_THROW(buffs.size() == 1); -        buffs[0] = data_transport->get_recv_buff(recv_timeout); -        return buffs[0].get() != NULL; +    managed_send_buffer::sptr get_send_buff(double timeout){ +        //try to get a new managed buffer with timeout +        offset_send_buffer next_buff(data_transport->get_send_buff(timeout)); +        if (not next_buff.buff.get()) return managed_send_buffer::sptr(); /* propagate timeout here */ + +        //make a new managed buffer with the offset buffs +        return omsb.get_new(curr_buff, next_buff);      }  }; @@ -185,32 +209,13 @@ void usrp1_impl::io_impl::flush_send_buff(void){      if (bytes_to_pad == 0) bytes_to_pad = alignment_padding;      //get the buffer, clear, and commit (really current buffer) -    vrt_packet_handler::managed_send_buffs_t buffs(1); -    if (this->get_send_buffs(buffs)){ -        std::memset(buffs[0]->cast<void *>(), 0, bytes_to_pad); -        buffs[0]->commit(bytes_to_pad); +    managed_send_buffer::sptr buff = this->get_send_buff(.1); +    if (buff.get() != NULL){ +        std::memset(buff->cast<void *>(), 0, bytes_to_pad); +        buff->commit(bytes_to_pad);      }  } -/*! - * Get a managed send buffer with the alignment padding: - * Always grab the next send buffer so we can timeout here. - */ -bool usrp1_impl::io_impl::get_send_buffs( -    vrt_packet_handler::managed_send_buffs_t &buffs -){ -    UHD_ASSERT_THROW(buffs.size() == 1); - -    //try to get a new managed buffer with timeout -    offset_send_buffer next_buff(data_transport->get_send_buff(send_timeout)); -    if (not next_buff.buff.get()) return false; /* propagate timeout here */ - -    //make a new managed buffer with the offset buffs -    buffs[0] = omsb.get_new(curr_buff, next_buff); - -    return true; -} -  /***********************************************************************   * Initialize internals within this file   **********************************************************************/ @@ -232,6 +237,34 @@ void usrp1_impl::io_init(void){      this->enable_tx(true); //always enabled      rx_stream_on_off(false);      _io_impl->flush_send_buff(); + +    //update mapping here since it didnt b4 when io init not called first +    update_xport_channel_mapping(); +} + +void usrp1_impl::update_xport_channel_mapping(void){ +    if (_io_impl.get() == NULL) return; //not inited yet + +    //set all of the relevant properties on the handler +    boost::mutex::scoped_lock recv_lock = _io_impl->recv_handler.get_scoped_lock(); +    _io_impl->recv_handler.set_vrt_unpacker(&usrp1_bs_vrt_unpacker); +    _io_impl->recv_handler.set_tick_rate(_clock_ctrl->get_master_clock_freq()); +    _io_impl->recv_handler.set_samp_rate(_rx_dsp_proxies[_rx_dsp_proxies.keys().at(0)]->get_link()[DSP_PROP_HOST_RATE].as<double>()); +    _io_impl->recv_handler.set_xport_chan_get_buff(0, boost::bind( +        &uhd::transport::zero_copy_if::get_recv_buff, _io_impl->data_transport, _1 +    )); +    _io_impl->recv_handler.set_converter(_rx_otw_type, _rx_subdev_spec.size()); + +    //set all of the relevant properties on the handler +    boost::mutex::scoped_lock send_lock = _io_impl->send_handler.get_scoped_lock(); +    _io_impl->send_handler.set_vrt_packer(&usrp1_bs_vrt_packer); +    _io_impl->send_handler.set_tick_rate(_clock_ctrl->get_master_clock_freq()); +    _io_impl->send_handler.set_samp_rate(_tx_dsp_proxies[_tx_dsp_proxies.keys().at(0)]->get_link()[DSP_PROP_HOST_RATE].as<double>()); +    _io_impl->send_handler.set_xport_chan_get_buff(0, boost::bind( +        &usrp1_impl::io_impl::get_send_buff, _io_impl.get(), _1 +    )); +    _io_impl->send_handler.set_converter(_tx_otw_type, _tx_subdev_spec.size()); +    _io_impl->send_handler.set_max_samples_per_packet(get_max_send_samps_per_packet());  }  void usrp1_impl::rx_stream_on_off(bool enb){ @@ -245,14 +278,6 @@ void usrp1_impl::rx_stream_on_off(bool enb){  /***********************************************************************   * Data send + helper functions   **********************************************************************/ -static void usrp1_bs_vrt_packer( -    boost::uint32_t *, -    vrt::if_packet_info_t &if_packet_info -){ -    if_packet_info.num_header_words32 = 0; -    if_packet_info.num_packet_words32 = if_packet_info.num_payload_words32; -} -  size_t usrp1_impl::get_max_send_samps_per_packet(void) const {      return (_data_transport->get_send_frame_size() - alignment_padding)          / _tx_otw_type.get_sample_size() @@ -261,29 +286,21 @@ size_t usrp1_impl::get_max_send_samps_per_packet(void) const {  }  size_t usrp1_impl::send( -    const send_buffs_type &buffs, size_t num_samps, +    const send_buffs_type &buffs, size_t nsamps_per_buff,      const tx_metadata_t &metadata, const io_type_t &io_type,      send_mode_t send_mode, double timeout  ){ -    if (_soft_time_ctrl->send_pre(metadata, timeout)) return num_samps; - -    _io_impl->send_timeout = timeout; -    size_t num_samps_sent = vrt_packet_handler::send( -        _io_impl->packet_handler_send_state,       //last state of the send handler -        buffs, num_samps,                          //buffer to fill -        metadata, send_mode,                       //samples metadata -        io_type, _tx_otw_type,                     //input and output types to convert -        _clock_ctrl->get_master_clock_freq(),      //master clock tick rate -        &usrp1_bs_vrt_packer, -        _io_impl->get_send_buffs_fcn, -        get_max_send_samps_per_packet(), -        0,                                         //vrt header offset -        _tx_subdev_spec.size()                     //num channels +    if (_soft_time_ctrl->send_pre(metadata, timeout)) return 0; + +    size_t num_samps_sent = _io_impl->send_handler.send( +        buffs, nsamps_per_buff, +        metadata, io_type, +        send_mode, timeout      );      //handle eob flag (commit the buffer, disable the DACs)      //check num samps sent to avoid flush on incomplete/timeout -    if (metadata.end_of_burst and num_samps_sent == num_samps){ +    if (metadata.end_of_burst and num_samps_sent == nsamps_per_buff){          _io_impl->flush_send_buff();      } @@ -306,23 +323,6 @@ size_t usrp1_impl::send(  /***********************************************************************   * Data recv + helper functions   **********************************************************************/ -static void usrp1_bs_vrt_unpacker( -    const boost::uint32_t *, -    vrt::if_packet_info_t &if_packet_info -){ -    if_packet_info.packet_type = vrt::if_packet_info_t::PACKET_TYPE_DATA; -    if_packet_info.num_payload_words32 = if_packet_info.num_packet_words32; -    if_packet_info.num_header_words32 = 0; -    if_packet_info.packet_count = 0; -    if_packet_info.sob = false; -    if_packet_info.eob = false; -    if_packet_info.has_sid = false; -    if_packet_info.has_cid = false; -    if_packet_info.has_tsi = false; -    if_packet_info.has_tsf = false; -    if_packet_info.has_tlr = false; -} -  size_t usrp1_impl::get_max_recv_samps_per_packet(void) const {      return _data_transport->get_recv_frame_size()          / _rx_otw_type.get_sample_size() @@ -331,22 +331,14 @@ size_t usrp1_impl::get_max_recv_samps_per_packet(void) const {  }  size_t usrp1_impl::recv( -    const recv_buffs_type &buffs, size_t num_samps, +    const recv_buffs_type &buffs, size_t nsamps_per_buff,      rx_metadata_t &metadata, const io_type_t &io_type,      recv_mode_t recv_mode, double timeout  ){ -    _io_impl->recv_timeout = timeout; -    size_t num_samps_recvd = vrt_packet_handler::recv( -        _io_impl->packet_handler_recv_state,       //last state of the recv handler -        buffs, num_samps,                          //buffer to fill -        metadata, recv_mode,                       //samples metadata -        io_type, _rx_otw_type,                     //input and output types to convert -        _clock_ctrl->get_master_clock_freq(),      //master clock tick rate -        &usrp1_bs_vrt_unpacker, -        _io_impl->get_recv_buffs_fcn, -        &vrt_packet_handler::handle_overflow_nop, -        0,                                         //vrt header offset -        _rx_subdev_spec.size()                     //num channels +    size_t num_samps_recvd = _io_impl->recv_handler.recv( +        buffs, nsamps_per_buff, +        metadata, io_type, +        recv_mode, timeout      );      _soft_time_ctrl->recv_post(metadata, num_samps_recvd); diff --git a/host/lib/usrp/usrp1/mboard_impl.cpp b/host/lib/usrp/usrp1/mboard_impl.cpp index f699c8e12..a265a5089 100644 --- a/host/lib/usrp/usrp1/mboard_impl.cpp +++ b/host/lib/usrp/usrp1/mboard_impl.cpp @@ -352,6 +352,7 @@ void usrp1_impl::mboard_set(const wax::obj &key, const wax::obj &val)          bool s = this->disable_rx();          _iface->poke32(FR_RX_MUX, calc_rx_mux(_rx_subdev_spec, _mboard_proxy->get_link()));          this->restore_rx(s); +        this->update_xport_channel_mapping();      }return;      case MBOARD_PROP_TX_SUBDEV_SPEC:{ @@ -367,6 +368,7 @@ void usrp1_impl::mboard_set(const wax::obj &key, const wax::obj &val)          bool s = this->disable_tx();          _iface->poke32(FR_TX_MUX, calc_tx_mux(_tx_subdev_spec, _mboard_proxy->get_link()));          this->restore_tx(s); +        this->update_xport_channel_mapping();      }return;      case MBOARD_PROP_EEPROM_MAP: @@ -387,6 +389,7 @@ void usrp1_impl::mboard_set(const wax::obj &key, const wax::obj &val)              << "See the application notes for USRP1 for further instructions.\n"          ;          _clock_ctrl->set_master_clock_freq(val.as<double>()); +        this->update_xport_channel_mapping();          return;      case MBOARD_PROP_CLOCK_CONFIG:{ diff --git a/host/lib/usrp/usrp1/usrp1_iface.cpp b/host/lib/usrp/usrp1/usrp1_iface.cpp index 0942e2613..8877f19db 100644 --- a/host/lib/usrp/usrp1/usrp1_iface.cpp +++ b/host/lib/usrp/usrp1/usrp1_iface.cpp @@ -33,7 +33,7 @@ public:      /*******************************************************************       * Structors       ******************************************************************/ -    usrp1_iface_impl(usrp_ctrl::sptr ctrl_transport) +    usrp1_iface_impl(uhd::usrp::fx2_ctrl::sptr ctrl_transport)      {          _ctrl_transport = ctrl_transport;          mb_eeprom = mboard_eeprom_t(*this, mboard_eeprom_t::MAP_B000); @@ -287,13 +287,13 @@ public:      }  private: -    usrp_ctrl::sptr _ctrl_transport; +    uhd::usrp::fx2_ctrl::sptr _ctrl_transport;  };  /***********************************************************************   * Public Make Function   **********************************************************************/ -usrp1_iface::sptr usrp1_iface::make(usrp_ctrl::sptr ctrl_transport) +usrp1_iface::sptr usrp1_iface::make(uhd::usrp::fx2_ctrl::sptr ctrl_transport)  {      return sptr(new usrp1_iface_impl(ctrl_transport));  } diff --git a/host/lib/usrp/usrp1/usrp1_iface.hpp b/host/lib/usrp/usrp1/usrp1_iface.hpp index 2ebcdbacc..c374149fa 100644 --- a/host/lib/usrp/usrp1/usrp1_iface.hpp +++ b/host/lib/usrp/usrp1/usrp1_iface.hpp @@ -21,7 +21,7 @@  #include <uhd/usrp/mboard_iface.hpp>  #include <boost/shared_ptr.hpp>  #include <boost/utility.hpp> -#include "usrp1_ctrl.hpp" +#include "fx2_ctrl.hpp"  /*!   * The usrp1 interface class: @@ -40,7 +40,7 @@ public:       * \param ctrl_transport the usrp controller object       * \return a new usrp1 interface object       */ -    static sptr make(usrp_ctrl::sptr ctrl_transport); +    static sptr make(uhd::usrp::fx2_ctrl::sptr ctrl_transport);      /*!       * Perform a general USB firmware OUT operation diff --git a/host/lib/usrp/usrp1/usrp1_impl.cpp b/host/lib/usrp/usrp1/usrp1_impl.cpp index b1fa986d1..a3d502038 100644 --- a/host/lib/usrp/usrp1/usrp1_impl.cpp +++ b/host/lib/usrp/usrp1/usrp1_impl.cpp @@ -16,7 +16,6 @@  //  #include "usrp1_impl.hpp" -#include "usrp1_ctrl.hpp"  #include "fpga_regs_standard.h"  #include "usrp_spi_defs.h"  #include <uhd/utils/log.hpp> @@ -90,7 +89,7 @@ static device_addrs_t usrp1_find(const device_addr_t &hint)          try{control = usb_control::make(handle);}          catch(const uhd::exception &){continue;} //ignore claimed -        usrp_ctrl::make(control)->usrp_load_firmware(usrp1_fw_image); +        fx2_ctrl::make(control)->usrp_load_firmware(usrp1_fw_image);      }      //get descriptors again with serial number, but using the initialized VID/PID now since we have firmware @@ -102,7 +101,7 @@ static device_addrs_t usrp1_find(const device_addr_t &hint)          try{control = usb_control::make(handle);}          catch(const uhd::exception &){continue;} //ignore claimed -        usrp1_iface::sptr iface = usrp1_iface::make(usrp_ctrl::make(control)); +        usrp1_iface::sptr iface = usrp1_iface::make(fx2_ctrl::make(control));          device_addr_t new_addr;          new_addr["type"] = "usrp1";          new_addr["name"] = iface->mb_eeprom["name"]; @@ -147,7 +146,7 @@ static device::sptr usrp1_make(const device_addr_t &device_addr){      //create control objects and a data transport      usb_control::sptr ctrl_transport = usb_control::make(handle); -    usrp_ctrl::sptr usrp_ctrl = usrp_ctrl::make(ctrl_transport); +    fx2_ctrl::sptr usrp_ctrl = fx2_ctrl::make(ctrl_transport);      usrp_ctrl->usrp_load_fpga(usrp1_fpga_image);      usrp_ctrl->usrp_init();      usb_zero_copy::sptr data_transport = usb_zero_copy::make( @@ -169,7 +168,7 @@ UHD_STATIC_BLOCK(register_usrp1_device){   * Structors   **********************************************************************/  usrp1_impl::usrp1_impl(uhd::transport::usb_zero_copy::sptr data_transport, -                       usrp_ctrl::sptr ctrl_transport) +                       uhd::usrp::fx2_ctrl::sptr ctrl_transport)   : _data_transport(data_transport), _ctrl_transport(ctrl_transport)  {      _iface = usrp1_iface::make(ctrl_transport); @@ -200,29 +199,19 @@ usrp1_impl::usrp1_impl(uhd::transport::usb_zero_copy::sptr data_transport,      //initialize the dsps      tx_dsp_init(); -    //initialize the send/recv -    io_init(); -      //init the subdev specs      this->mboard_set(MBOARD_PROP_RX_SUBDEV_SPEC, subdev_spec_t());      this->mboard_set(MBOARD_PROP_TX_SUBDEV_SPEC, subdev_spec_t()); -} -usrp1_impl::~usrp1_impl(void){ -    UHD_SAFE_CALL(this->enable_rx(false);) -    UHD_SAFE_CALL(this->enable_tx(false);) -    //Safely destruct all RAII objects in a device. -    //This prevents the mboard deconstructor from throwing, -    //which allows the device to be safely deconstructed. -    BOOST_FOREACH(dboard_slot_t slot, _dboard_slots){ -        UHD_SAFE_CALL(_dboard_managers[slot].reset();) -        UHD_SAFE_CALL(_dboard_ifaces[slot].reset();) -        UHD_SAFE_CALL(_codec_ctrls[slot].reset();) -    } -    UHD_SAFE_CALL(_clock_ctrl.reset();) -    UHD_SAFE_CALL(_io_impl.reset();) +    //initialize the send/recv +    io_init();  } +usrp1_impl::~usrp1_impl(void){UHD_SAFE_CALL( +    this->enable_rx(false); +    this->enable_tx(false); +)} +  bool usrp1_impl::recv_async_msg(uhd::async_metadata_t &, double timeout){      //dummy fill-in for the recv_async_msg      boost::this_thread::sleep(boost::posix_time::microseconds(long(timeout*1e6))); diff --git a/host/lib/usrp/usrp1/usrp1_impl.hpp b/host/lib/usrp/usrp1/usrp1_impl.hpp index a4d40a54d..bea1dbe3b 100644 --- a/host/lib/usrp/usrp1/usrp1_impl.hpp +++ b/host/lib/usrp/usrp1/usrp1_impl.hpp @@ -16,7 +16,6 @@  //  #include "usrp1_iface.hpp" -#include "usrp1_ctrl.hpp"  #include "clock_ctrl.hpp"  #include "codec_ctrl.hpp"  #include "soft_time_ctrl.hpp" @@ -75,7 +74,7 @@ public:      //structors      usrp1_impl(uhd::transport::usb_zero_copy::sptr data_transport, -               usrp_ctrl::sptr ctrl_transport); +               uhd::usrp::fx2_ctrl::sptr ctrl_transport);      ~usrp1_impl(void); @@ -115,6 +114,9 @@ private:          const uhd::usrp::dboard_id_t &rx_dboard_id      ); +    //!call when the channel mapping is changed +    void update_xport_channel_mapping(void); +      //soft time control emulation      uhd::usrp::soft_time_ctrl::sptr _soft_time_ctrl; @@ -197,7 +199,7 @@ private:      //transports      uhd::transport::usb_zero_copy::sptr _data_transport; -    usrp_ctrl::sptr _ctrl_transport; +    uhd::usrp::fx2_ctrl::sptr _ctrl_transport;      //capabilities      size_t get_num_ducs(void); diff --git a/host/lib/usrp/usrp2/clock_ctrl.cpp b/host/lib/usrp/usrp2/clock_ctrl.cpp index f1fdfb7cf..66c7a6c28 100644 --- a/host/lib/usrp/usrp2/clock_ctrl.cpp +++ b/host/lib/usrp/usrp2/clock_ctrl.cpp @@ -19,6 +19,7 @@  #include "ad9510_regs.hpp"  #include "usrp2_regs.hpp" //spi slave constants  #include "usrp2_clk_regs.hpp" +#include <uhd/utils/safe_call.hpp>  #include <uhd/utils/assert_has.hpp>  #include <boost/cstdint.hpp>  #include <boost/lexical_cast.hpp> @@ -77,7 +78,7 @@ public:          this->enable_test_clock(enb_test_clk);      } -    ~usrp2_clock_ctrl_impl(void){ +    ~usrp2_clock_ctrl_impl(void){UHD_SAFE_CALL(          //power down clock outputs          this->enable_external_ref(false);          this->enable_rx_dboard_clock(false); @@ -86,7 +87,7 @@ public:          this->enable_adc_clock(false);          this->enable_mimo_clock_out(false);          this->enable_test_clock(false); -    } +    )}      void enable_mimo_clock_out(bool enb){          //calculate the low and high dividers diff --git a/host/lib/usrp/usrp2/codec_ctrl.cpp b/host/lib/usrp/usrp2/codec_ctrl.cpp index 047195390..ee0ef9ceb 100644 --- a/host/lib/usrp/usrp2/codec_ctrl.cpp +++ b/host/lib/usrp/usrp2/codec_ctrl.cpp @@ -20,6 +20,7 @@  #include "ads62p44_regs.hpp"  #include "usrp2_regs.hpp"  #include <uhd/utils/log.hpp> +#include <uhd/utils/safe_call.hpp>  #include <uhd/exception.hpp>  #include <boost/cstdint.hpp>  #include <boost/foreach.hpp> @@ -95,7 +96,7 @@ public:          }      } -    ~usrp2_codec_ctrl_impl(void){ +    ~usrp2_codec_ctrl_impl(void){UHD_SAFE_CALL(          //power-down dac          _ad9777_regs.power_down_mode = 1;          this->send_ad9777_reg(0); @@ -118,7 +119,7 @@ public:          case usrp2_iface::USRP_NXXX: break;          } -    } +    )}      void set_tx_mod_mode(int mod_mode){          //set the sign of the frequency shift diff --git a/host/lib/usrp/usrp2/dsp_impl.cpp b/host/lib/usrp/usrp2/dsp_impl.cpp index 292659f36..03cdeae42 100644 --- a/host/lib/usrp/usrp2/dsp_impl.cpp +++ b/host/lib/usrp/usrp2/dsp_impl.cpp @@ -118,12 +118,6 @@ void usrp2_mboard_impl::issue_ddc_stream_cmd(const stream_cmd_t &stream_cmd, siz      _iface->poke32(U2_REG_RX_CTRL_TIME_TICKS(which_dsp), stream_cmd.time_spec.get_tick_count(get_master_clock_freq()));  } -void usrp2_mboard_impl::handle_overflow(size_t which_dsp){ -    if (_dsp_impl->continuous_streaming[which_dsp]){ //re-issue the stream command if already continuous -        this->issue_ddc_stream_cmd(stream_cmd_t::STREAM_MODE_START_CONTINUOUS, which_dsp); -    } -} -  /***********************************************************************   * DDC Properties   **********************************************************************/ @@ -186,6 +180,7 @@ void usrp2_mboard_impl::ddc_set(const wax::obj &key_, const wax::obj &val, size_                  dsp_type1::calc_iq_scale_word(default_rx_scale_iq, default_rx_scale_iq)              );          } +        _device.update_xport_channel_mapping(); //rate changed -> update          return;      default: UHD_THROW_PROP_SET_ERROR(); @@ -259,6 +254,7 @@ void usrp2_mboard_impl::duc_set(const wax::obj &key_, const wax::obj &val, size_              //set the scaling              _iface->poke32(U2_REG_DSP_TX_SCALE_IQ, dsp_type1::calc_iq_scale_word(_dsp_impl->duc_interp[which_dsp]));          } +        _device.update_xport_channel_mapping(); //rate changed -> update          return;      default: UHD_THROW_PROP_SET_ERROR(); diff --git a/host/lib/usrp/usrp2/io_impl.cpp b/host/lib/usrp/usrp2/io_impl.cpp index 33f249599..ffe9a88e7 100644 --- a/host/lib/usrp/usrp2/io_impl.cpp +++ b/host/lib/usrp/usrp2/io_impl.cpp @@ -15,18 +15,21 @@  // along with this program.  If not, see <http://www.gnu.org/licenses/>.  // -#include "../../transport/vrt_packet_handler.hpp" +#include "../../transport/super_recv_packet_handler.hpp" +#include "../../transport/super_send_packet_handler.hpp"  #include "usrp2_impl.hpp"  #include "usrp2_regs.hpp"  #include <uhd/utils/log.hpp>  #include <uhd/utils/msg.hpp>  #include <uhd/exception.hpp>  #include <uhd/usrp/mboard_props.hpp> +#include <uhd/usrp/dsp_props.hpp>  #include <uhd/utils/byteswap.hpp>  #include <uhd/utils/thread_priority.hpp>  #include <uhd/transport/bounded_buffer.hpp>  #include <boost/format.hpp>  #include <boost/bind.hpp> +#include <boost/thread/mutex.hpp>  #include <boost/thread/thread.hpp>  #include <boost/thread/barrier.hpp>  #include <iostream> @@ -80,14 +83,21 @@ public:      }      /*! +     * Gets the current sequence number to go out. +     * Increments the sequence for the next call +     * \return the sequence to be sent to the dsp +     */ +    UHD_INLINE seq_type get_curr_seq_out(void){ +        return _last_seq_out++; +    } + +    /*!       * Check the flow control condition. -     * \param seq the sequence to go out       * \param timeout the timeout in seconds       * \return false on timeout       */ -    UHD_INLINE bool check_fc_condition(seq_type seq, double timeout){ -        boost::unique_lock<boost::mutex> lock(_fc_mutex); -        _last_seq_out = seq; +    UHD_INLINE bool check_fc_condition(double timeout){ +        boost::mutex::scoped_lock lock(_fc_mutex);          if (this->ready()) return true;          boost::this_thread::disable_interruption di; //disable because the wait can throw          return _fc_cond.timed_wait(lock, to_time_dur(timeout), _ready_fcn); @@ -98,7 +108,7 @@ public:       * \param seq the last sequence number to be ACK'd       */      UHD_INLINE void update_fc_condition(seq_type seq){ -        boost::unique_lock<boost::mutex> lock(_fc_mutex); +        boost::mutex::scoped_lock lock(_fc_mutex);          _last_seq_ack = seq;          lock.unlock();          _fc_cond.notify_one(); @@ -116,23 +126,6 @@ private:  };  /*********************************************************************** - * Alignment indexes class: keeps track of indexes - **********************************************************************/ -class alignment_indexes{ -public: -    alignment_indexes(void){_indexes = 0;} -    void reset(size_t len){_indexes = (1 << len) - 1;} -    size_t front(void){ //TODO replace with look-up table -        size_t index = 0; -        while ((_indexes & (1 << index)) == 0) index++; -        return index; -    } -    void remove(size_t index){_indexes &= ~(1 << index);} -    bool empty(void){return _indexes == 0;} -private: size_t _indexes; -}; - -/***********************************************************************   * io impl details (internal to this file)   * - pirate crew   * - alignment buffer @@ -143,24 +136,13 @@ struct usrp2_impl::io_impl{      io_impl(std::vector<zero_copy_if::sptr> &dsp_xports):          dsp_xports(dsp_xports), //the assumption is that all data transports should be identical -        get_recv_buffs_fcn(boost::bind(&usrp2_impl::io_impl::get_recv_buffs, this, _1)), -        get_send_buffs_fcn(boost::bind(&usrp2_impl::io_impl::get_send_buffs, this, _1)),          async_msg_fifo(100/*messages deep*/)      {          for (size_t i = 0; i < dsp_xports.size(); i++){              fc_mons.push_back(flow_control_monitor::sptr(new flow_control_monitor(                  usrp2_impl::sram_bytes/dsp_xports.front()->get_send_frame_size() -            )));; +            )));          } - -        //init empty packet infos -        vrt::if_packet_info_t packet_info = vrt::if_packet_info_t(); -        packet_info.packet_count = 0xf; -        packet_info.has_tsi = true; -        packet_info.tsi = 0; -        packet_info.has_tsf = true; -        packet_info.tsf = 0; -        prev_infos.resize(dsp_xports.size(), packet_info);      }      ~io_impl(void){ @@ -169,38 +151,27 @@ struct usrp2_impl::io_impl{          recv_pirate_crew.join_all();      } -    bool get_send_buffs(vrt_packet_handler::managed_send_buffs_t &buffs){ -        UHD_ASSERT_THROW(send_map.size() == buffs.size()); +    managed_send_buffer::sptr get_send_buff(size_t chan, double timeout){ +        const size_t index = send_map[chan]; +        flow_control_monitor &fc_mon = *fc_mons[index]; -        //calculate the flow control word -        const boost::uint32_t fc_word32 = packet_handler_send_state.next_packet_seq; +        //wait on flow control w/ timeout +        if (not fc_mon.check_fc_condition(timeout)) return managed_send_buffer::sptr(); -        //grab a managed buffer for each index -        for (size_t i = 0; i < buffs.size(); i++){ -            if (not fc_mons[send_map[i]]->check_fc_condition(fc_word32, send_timeout)) return false; -            buffs[i] = dsp_xports[send_map[i]]->get_send_buff(send_timeout); -            if (not buffs[i].get()) return false; -            buffs[i]->cast<boost::uint32_t *>()[0] = uhd::htonx(fc_word32); -        } -        return true; -    } +        //get a buffer from the transport w/ timeout +        managed_send_buffer::sptr buff = dsp_xports[index]->get_send_buff(timeout); + +        //write the flow control word into the buffer +        if (buff.get()) buff->cast<boost::uint32_t *>()[0] = uhd::htonx(fc_mon.get_curr_seq_out()); -    alignment_indexes indexes_to_do; //used in alignment logic -    time_spec_t expected_time; //used in alignment logic -    bool get_recv_buffs(vrt_packet_handler::managed_recv_buffs_t &buffs); +        return buff; +    }      std::vector<zero_copy_if::sptr> &dsp_xports;      //mappings from channel index to dsp xport      std::vector<size_t> send_map, recv_map; -    //timeouts set on calls to recv/send (passed into get buffs methods) -    double recv_timeout, send_timeout; - -    //bound callbacks for get buffs (bound once here, not in fast-path) -    vrt_packet_handler::get_recv_buffs_t get_recv_buffs_fcn; -    vrt_packet_handler::get_send_buffs_t get_send_buffs_fcn; -      //previous state for each buffer      std::vector<vrt::if_packet_info_t> prev_infos; @@ -208,8 +179,8 @@ struct usrp2_impl::io_impl{      std::vector<flow_control_monitor::sptr> fc_mons;      //state management for the vrt packet handler code -    vrt_packet_handler::recv_state packet_handler_recv_state; -    vrt_packet_handler::send_state packet_handler_send_state; +    sph::recv_packet_handler recv_handler; +    sph::send_packet_handler send_handler;      //methods and variables for the pirate crew      void recv_pirate_loop(boost::barrier &, usrp2_mboard_impl::sptr, zero_copy_if::sptr, size_t); @@ -258,7 +229,7 @@ void usrp2_impl::io_impl::recv_pirate_loop(                  metadata.time_spec = time_spec_t(                      time_t(if_packet_info.tsi), size_t(if_packet_info.tsf), mboard->get_master_clock_freq()                  ); -                metadata.event_code = vrt_packet_handler::get_context_code<async_metadata_t::event_code_t>(vrt_hdr, if_packet_info); +                metadata.event_code = async_metadata_t::event_code_t(sph::get_context_code(vrt_hdr, if_packet_info));                  //catch the flow control packets and react                  if (metadata.event_code == 0){ @@ -327,8 +298,39 @@ void usrp2_impl::update_xport_channel_mapping(void){      } -    _io_impl->packet_handler_recv_state = vrt_packet_handler::recv_state(_io_impl->recv_map.size()); -    _io_impl->packet_handler_send_state = vrt_packet_handler::send_state(_io_impl->send_map.size()); +    //set all of the relevant properties on the handler +    boost::mutex::scoped_lock recv_lock = _io_impl->recv_handler.get_scoped_lock(); +    _io_impl->recv_handler.resize(_io_impl->recv_map.size()); +    _io_impl->recv_handler.set_vrt_unpacker(&vrt::if_hdr_unpack_be); +    _io_impl->recv_handler.set_tick_rate(_mboards.front()->get_master_clock_freq()); +    //TODO temporarily use the first dsp rate until we support non-homo rates +    const std::string rx_dsp_name = _mboards.at(0)->get_link()[MBOARD_PROP_RX_DSP_NAMES].as<prop_names_t>().at(0); +    const double rx_host_rate = _mboards.at(0)->get_link()[named_prop_t(MBOARD_PROP_RX_DSP, rx_dsp_name)][DSP_PROP_HOST_RATE].as<double>(); +    _io_impl->recv_handler.set_samp_rate(rx_host_rate); +    for (size_t chan = 0; chan < _io_impl->recv_handler.size(); chan++){ +        _io_impl->recv_handler.set_xport_chan_get_buff(chan, boost::bind( +            &uhd::transport::zero_copy_if::get_recv_buff, +            _io_impl->dsp_xports[_io_impl->recv_map[chan]], _1 +        )); +    } +    _io_impl->recv_handler.set_converter(_rx_otw_type); + +    //set all of the relevant properties on the handler +    boost::mutex::scoped_lock send_lock = _io_impl->send_handler.get_scoped_lock(); +    _io_impl->send_handler.resize(_io_impl->send_map.size()); +    _io_impl->send_handler.set_vrt_packer(&vrt::if_hdr_pack_be, vrt_send_header_offset_words32); +    _io_impl->send_handler.set_tick_rate(_mboards.front()->get_master_clock_freq()); +    //TODO temporarily use the first dsp rate until we support non-homo rates +    const std::string tx_dsp_name = _mboards.at(0)->get_link()[MBOARD_PROP_TX_DSP_NAMES].as<prop_names_t>().at(0); +    const double tx_host_rate = _mboards.at(0)->get_link()[named_prop_t(MBOARD_PROP_TX_DSP, tx_dsp_name)][DSP_PROP_HOST_RATE].as<double>(); +    _io_impl->send_handler.set_samp_rate(tx_host_rate); +    for (size_t chan = 0; chan < _io_impl->send_handler.size(); chan++){ +        _io_impl->send_handler.set_xport_chan_get_buff(chan, boost::bind( +            &usrp2_impl::io_impl::get_send_buff, _io_impl.get(), chan, _1 +        )); +    } +    _io_impl->send_handler.set_converter(_tx_otw_type); +    _io_impl->send_handler.set_max_samples_per_packet(get_max_send_samps_per_packet());  }  /*********************************************************************** @@ -355,143 +357,17 @@ size_t usrp2_impl::get_max_send_samps_per_packet(void) const{  }  size_t usrp2_impl::send( -    const send_buffs_type &buffs, size_t num_samps, +    const send_buffs_type &buffs, size_t nsamps_per_buff,      const tx_metadata_t &metadata, const io_type_t &io_type,      send_mode_t send_mode, double timeout  ){ -    _io_impl->send_timeout = timeout; -    return vrt_packet_handler::send( -        _io_impl->packet_handler_send_state,       //last state of the send handler -        buffs, num_samps,                          //buffer to fill -        metadata, send_mode,                       //samples metadata -        io_type, _tx_otw_type,                     //input and output types to convert -        _mboards.front()->get_master_clock_freq(), //master clock tick rate -        uhd::transport::vrt::if_hdr_pack_be, -        _io_impl->get_send_buffs_fcn, -        get_max_send_samps_per_packet(), -        vrt_send_header_offset_words32 -    ); -} - -/*********************************************************************** - * Alignment logic on receive - **********************************************************************/ -static UHD_INLINE time_spec_t extract_time_spec( -    const vrt::if_packet_info_t &packet_info -){ -    return time_spec_t( //assumes has_tsi and has_tsf are true -        time_t(packet_info.tsi), size_t(packet_info.tsf), -        100e6 //tick rate does not have to be correct for comparison purposes +    return _io_impl->send_handler.send( +        buffs, nsamps_per_buff, +        metadata, io_type, +        send_mode, timeout      );  } -static UHD_INLINE void extract_packet_info( -    managed_recv_buffer::sptr &buff, -    vrt::if_packet_info_t &prev_info, -    time_spec_t &time, bool &clear, bool &msg -){ -    //extract packet info -    vrt::if_packet_info_t next_info; -    next_info.num_packet_words32 = buff->size()/sizeof(boost::uint32_t); -    vrt::if_hdr_unpack_be(buff->cast<const boost::uint32_t *>(), next_info); - -    //handle the packet count / sequence number -    if ((prev_info.packet_count+1)%16 != next_info.packet_count){ -        UHD_MSG(fastpath) << "O"; //report overflow (drops in the kernel) -    } - -    time = extract_time_spec(next_info); -    clear = extract_time_spec(prev_info) > time; -    msg = next_info.packet_type != vrt::if_packet_info_t::PACKET_TYPE_DATA; -    prev_info = next_info; -} - -static UHD_INLINE bool handle_msg_packet( -    vrt_packet_handler::managed_recv_buffs_t &buffs, size_t index -){ -    for (size_t i = 0; i < buffs.size(); i++){ -        if (i == index) continue; -        buffs[i].reset(); //set NULL -    } -    return true; -} - -UHD_INLINE bool usrp2_impl::io_impl::get_recv_buffs( -    vrt_packet_handler::managed_recv_buffs_t &buffs -){ -    if (buffs.size() == 1){ -        buffs[0] = dsp_xports[recv_map[0]]->get_recv_buff(recv_timeout); -        if (buffs[0].get() == NULL) return false; -        bool clear, msg; time_spec_t time; //unused variables -        //call extract_packet_info to handle printing the overflows -        extract_packet_info(buffs[0], this->prev_infos[recv_map[0]], time, clear, msg); -        return true; -    } -    //-------------------- begin alignment logic ---------------------// -    UHD_ASSERT_THROW(recv_map.size() == buffs.size()); -    boost::system_time exit_time = boost::get_system_time() + to_time_dur(recv_timeout); -    managed_recv_buffer::sptr buff_tmp; -    bool clear, msg; -    size_t index; - -    //If we did not enter this routine with an empty indexes set, -    //jump to after the clear so we can preserve the previous state. -    //This saves buffers from being lost when using non-blocking recv. -    if (not indexes_to_do.empty()) goto skip_pop_initial; - -    //respond to a clear by starting from scratch -    got_clear: -    indexes_to_do.reset(buffs.size()); -    clear = false; - -    //do an initial pop to load an initial sequence id -    index = indexes_to_do.front(); -    buff_tmp = dsp_xports[recv_map[index]]->get_recv_buff(from_time_dur(exit_time - boost::get_system_time())); -    if (buff_tmp.get() == NULL) return false; -    extract_packet_info(buff_tmp, this->prev_infos[recv_map[index]], expected_time, clear, msg); -    if (clear) goto got_clear; -    buffs[index] = buff_tmp; -    if (msg) return handle_msg_packet(buffs, index); -    indexes_to_do.remove(index); -    skip_pop_initial: - -    //get an aligned set of elements from the buffers: -    while(not indexes_to_do.empty()){ - -        //pop an element off for this index -        index = indexes_to_do.front(); -        buff_tmp = dsp_xports[recv_map[index]]->get_recv_buff(from_time_dur(exit_time - boost::get_system_time())); -        if (buff_tmp.get() == NULL) return false; -        time_spec_t this_time; -        extract_packet_info(buff_tmp, this->prev_infos[recv_map[index]], this_time, clear, msg); -        if (clear) goto got_clear; -        buffs[index] = buff_tmp; -        if (msg) return handle_msg_packet(buffs, index); - -        //if the sequence id matches: -        //  remove this index from the list and continue -        if (this_time == expected_time){ -            indexes_to_do.remove(index); -        } - -        //if the sequence id is newer: -        //  use the new expected time for comparison -        //  add all other indexes back into the list -        else if (this_time > expected_time){ -            expected_time = this_time; -            indexes_to_do.reset(buffs.size()); -            indexes_to_do.remove(index); -        } - -        //if the sequence id is older: -        //  continue with the same index to try again -        //else if (this_time < expected_time)... - -    } -    return true; -    //-------------------- end alignment logic -----------------------// -} -  /***********************************************************************   * Receive Data   **********************************************************************/ @@ -505,26 +381,14 @@ size_t usrp2_impl::get_max_recv_samps_per_packet(void) const{      return bpp/_rx_otw_type.get_sample_size();  } -void usrp2_impl::handle_overflow(size_t chan){ -    UHD_MSG(fastpath) << "O"; -    ldiv_t indexes = ldiv(chan, usrp2_mboard_impl::NUM_RX_DSPS); -    _mboards.at(indexes.quot)->handle_overflow(indexes.rem); -} -  size_t usrp2_impl::recv( -    const recv_buffs_type &buffs, size_t num_samps, +    const recv_buffs_type &buffs, size_t nsamps_per_buff,      rx_metadata_t &metadata, const io_type_t &io_type,      recv_mode_t recv_mode, double timeout  ){ -    _io_impl->recv_timeout = timeout; -    return vrt_packet_handler::recv( -        _io_impl->packet_handler_recv_state,       //last state of the recv handler -        buffs, num_samps,                          //buffer to fill -        metadata, recv_mode,                       //samples metadata -        io_type, _rx_otw_type,                     //input and output types to convert -        _mboards.front()->get_master_clock_freq(), //master clock tick rate -        uhd::transport::vrt::if_hdr_unpack_be, -        _io_impl->get_recv_buffs_fcn, -        boost::bind(&usrp2_impl::handle_overflow, this, _1) +    return _io_impl->recv_handler.recv( +        buffs, nsamps_per_buff, +        metadata, io_type, +        recv_mode, timeout      );  } diff --git a/host/lib/usrp/usrp2/mboard_impl.cpp b/host/lib/usrp/usrp2/mboard_impl.cpp index bf1fd5cce..6bf412a3e 100644 --- a/host/lib/usrp/usrp2/mboard_impl.cpp +++ b/host/lib/usrp/usrp2/mboard_impl.cpp @@ -176,18 +176,10 @@ usrp2_mboard_impl::usrp2_mboard_impl(      //------------------------------------------------------------------  } -usrp2_mboard_impl::~usrp2_mboard_impl(void){ -    //Safely destruct all RAII objects in an mboard. -    //This prevents the mboard deconstructor from throwing, -    //which allows the device to be safely deconstructed. -    UHD_SAFE_CALL(_iface->poke32(U2_REG_TX_CTRL_CYCLES_PER_UP, 0);) -    UHD_SAFE_CALL(_iface->poke32(U2_REG_TX_CTRL_PACKETS_PER_UP, 0);) -    UHD_SAFE_CALL(_dboard_manager.reset();) -    UHD_SAFE_CALL(_dboard_iface.reset();) -    UHD_SAFE_CALL(_codec_ctrl.reset();) -    UHD_SAFE_CALL(_clock_ctrl.reset();) -    UHD_SAFE_CALL(_gps_ctrl.reset();) -} +usrp2_mboard_impl::~usrp2_mboard_impl(void){UHD_SAFE_CALL( +    _iface->poke32(U2_REG_TX_CTRL_CYCLES_PER_UP, 0); +    _iface->poke32(U2_REG_TX_CTRL_PACKETS_PER_UP, 0); +)}  /***********************************************************************   * Helper Methods @@ -474,6 +466,7 @@ void usrp2_mboard_impl::set(const wax::obj &key, const wax::obj &val){      case MBOARD_PROP_CLOCK_RATE:          UHD_ASSERT_THROW(val.as<double>() == this->get_master_clock_freq()); +        _device.update_xport_channel_mapping();          return;      default: UHD_THROW_PROP_SET_ERROR(); diff --git a/host/lib/usrp/usrp2/usrp2_iface.cpp b/host/lib/usrp/usrp2/usrp2_iface.cpp index 85858929d..ec1a2e94c 100644 --- a/host/lib/usrp/usrp2/usrp2_iface.cpp +++ b/host/lib/usrp/usrp2/usrp2_iface.cpp @@ -270,7 +270,7 @@ public:   **********************************************************************/      void write_uart(boost::uint8_t dev, const std::string &buf){        //first tokenize the string into 20-byte substrings -      boost::offset_separator f(20, 1, true, true); +      boost::offset_separator f(20, 20, true, true);        boost::tokenizer<boost::offset_separator> tok(buf, f);        std::vector<std::string> queue(tok.begin(), tok.end()); diff --git a/host/lib/usrp/usrp2/usrp2_impl.hpp b/host/lib/usrp/usrp2/usrp2_impl.hpp index ccaf0c9a8..4d19863b1 100644 --- a/host/lib/usrp/usrp2/usrp2_impl.hpp +++ b/host/lib/usrp/usrp2/usrp2_impl.hpp @@ -220,7 +220,6 @@ private:      uhd::otw_type_t _rx_otw_type, _tx_otw_type;      UHD_PIMPL_DECL(io_impl) _io_impl;      void io_init(void); -    void handle_overflow(size_t);  };  #endif /* INCLUDED_USRP2_IMPL_HPP */ diff --git a/host/lib/usrp/usrp_b100/CMakeLists.txt b/host/lib/usrp/usrp_b100/CMakeLists.txt new file mode 100644 index 000000000..f83a10935 --- /dev/null +++ b/host/lib/usrp/usrp_b100/CMakeLists.txt @@ -0,0 +1,47 @@ +# +# Copyright 2010-2011 Ettus Research LLC +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# 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/>. +# + +######################################################################## +# This file included, use CMake directory variables +######################################################################## + +######################################################################## +# Conditionally configure the B100 support +######################################################################## +LIBUHD_REGISTER_COMPONENT("B100" ENABLE_B100 ON "ENABLE_LIBUHD;ENABLE_USB" OFF) + +IF(ENABLE_B100) +    LIBUHD_APPEND_SOURCES( +        ${CMAKE_SOURCE_DIR}/lib/usrp/usrp_b100/clock_ctrl.cpp +        ${CMAKE_SOURCE_DIR}/lib/usrp/usrp_b100/clock_ctrl.hpp +        ${CMAKE_SOURCE_DIR}/lib/usrp/usrp_b100/codec_ctrl.cpp +        ${CMAKE_SOURCE_DIR}/lib/usrp/usrp_b100/codec_ctrl.hpp +        ${CMAKE_SOURCE_DIR}/lib/usrp/usrp_b100/codec_impl.cpp +        ${CMAKE_SOURCE_DIR}/lib/usrp/usrp_b100/ctrl_packet.hpp +        ${CMAKE_SOURCE_DIR}/lib/usrp/usrp_b100/dboard_impl.cpp +        ${CMAKE_SOURCE_DIR}/lib/usrp/usrp_b100/dboard_iface.cpp +        ${CMAKE_SOURCE_DIR}/lib/usrp/usrp_b100/dsp_impl.cpp +        ${CMAKE_SOURCE_DIR}/lib/usrp/usrp_b100/io_impl.cpp +        ${CMAKE_SOURCE_DIR}/lib/usrp/usrp_b100/mboard_impl.cpp +        ${CMAKE_SOURCE_DIR}/lib/usrp/usrp_b100/b100_ctrl.cpp +        ${CMAKE_SOURCE_DIR}/lib/usrp/usrp_b100/b100_ctrl.hpp +        ${CMAKE_SOURCE_DIR}/lib/usrp/usrp_b100/b100_iface.cpp +        ${CMAKE_SOURCE_DIR}/lib/usrp/usrp_b100/b100_iface.hpp +        ${CMAKE_SOURCE_DIR}/lib/usrp/usrp_b100/b100_impl.cpp +        ${CMAKE_SOURCE_DIR}/lib/usrp/usrp_b100/b100_impl.hpp +    ) +ENDIF(ENABLE_B100) diff --git a/host/lib/usrp/usrp_b100/b100_ctrl.cpp b/host/lib/usrp/usrp_b100/b100_ctrl.cpp new file mode 100644 index 000000000..4d4520e1e --- /dev/null +++ b/host/lib/usrp/usrp_b100/b100_ctrl.cpp @@ -0,0 +1,245 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#include "../../transport/super_recv_packet_handler.hpp" +#include "b100_ctrl.hpp" +#include "b100_impl.hpp" +#include <uhd/transport/usb_zero_copy.hpp> +#include <uhd/transport/zero_copy.hpp> +#include <uhd/transport/vrt_if_packet.hpp> +#include <uhd/utils/thread_priority.hpp> +#include <uhd/utils/msg.hpp> +#include <uhd/types/metadata.hpp> +#include <uhd/types/serial.hpp> +#include "ctrl_packet.hpp" +#include <boost/thread.hpp> +#include <uhd/exception.hpp> + +using namespace uhd::transport; +using namespace uhd; + +bool b100_ctrl_debug = false; + +class b100_ctrl_impl : public b100_ctrl { +public: +    b100_ctrl_impl(uhd::transport::usb_zero_copy::sptr ctrl_transport) :  +        sync_ctrl_fifo(2), +        async_msg_fifo(100), +        _ctrl_transport(ctrl_transport), +        _seq(0) +    { +        boost::barrier spawn_barrier(2); +        viking_marauders.create_thread(boost::bind(&b100_ctrl_impl::viking_marauder_loop, this, boost::ref(spawn_barrier))); +        spawn_barrier.wait(); +    } +     +    int write(boost::uint32_t addr, const ctrl_data_t &data); +    ctrl_data_t read(boost::uint32_t addr, size_t len); +     +    ~b100_ctrl_impl(void) { +        bbl_out_marauding = false; +        viking_marauders.interrupt_all(); +        viking_marauders.join_all(); +    } +     +    bool get_ctrl_data(ctrl_data_t &pkt_data, double timeout); +    bool recv_async_msg(uhd::async_metadata_t &async_metadata, double timeout); +     +private: +    int send_pkt(boost::uint16_t *cmd); +     +    //änd hërë wë gö ä-Vïkïng för äsynchronous control packets +    void viking_marauder_loop(boost::barrier &); +    bounded_buffer<ctrl_data_t> sync_ctrl_fifo; +    bounded_buffer<async_metadata_t> async_msg_fifo; +    boost::thread_group viking_marauders; +    bool bbl_out_marauding; +     +    uhd::transport::usb_zero_copy::sptr _ctrl_transport; +    boost::uint8_t _seq; +}; + +/*********************************************************************** + * helper functions for packing/unpacking control packets + **********************************************************************/ +void pack_ctrl_pkt(boost::uint16_t *pkt_buff, +                          const ctrl_pkt_t &pkt){ +    //first two bits are OP +    //next six bits are CALLBACKS +    //next 8 bits are SEQUENCE +    //next 16 bits are LENGTH (16-bit word) +    //next 32 bits are ADDRESS (16-bit word LSW) +    //then DATA (28 16-bit words) +    pkt_buff[0] = (boost::uint16_t(pkt.pkt_meta.op) << 14) | (boost::uint16_t(pkt.pkt_meta.callbacks) << 8) | pkt.pkt_meta.seq; +    pkt_buff[1] = pkt.pkt_meta.len; +    pkt_buff[2] = (pkt.pkt_meta.addr & 0x00000FFF); +    pkt_buff[3] = 0x0000; //address high bits always 0 on this device +     +    for(size_t i = 0; i < pkt.data.size(); i++) { +        pkt_buff[4+i] = pkt.data[i]; +    } +} + +void unpack_ctrl_pkt(const boost::uint16_t *pkt_buff, +                            ctrl_pkt_t &pkt){ +    pkt.pkt_meta.seq = pkt_buff[0] & 0xFF; +    pkt.pkt_meta.op = CTRL_PKT_OP_READ; //really this is useless +    pkt.pkt_meta.len = pkt_buff[1]; +    pkt.pkt_meta.callbacks = 0; //callbacks aren't implemented yet +    pkt.pkt_meta.addr = pkt_buff[2] | boost::uint32_t(pkt_buff[3] << 16); +     +    //let's check this so we don't go pushing 64K of crap onto the pkt +    if(pkt.pkt_meta.len > CTRL_PACKET_DATA_LENGTH) { +        throw uhd::runtime_error("Received control packet too long"); +    } + +    for(int i = 4; i < 4+pkt.pkt_meta.len; i++) pkt.data.push_back(pkt_buff[i]); +} + +int b100_ctrl_impl::send_pkt(boost::uint16_t *cmd) { +    managed_send_buffer::sptr sbuf = _ctrl_transport->get_send_buff(); +    if(!sbuf.get()) { +        throw uhd::runtime_error("Control channel send error"); +    } +         +    //FIXME there's a better way to do this +    for(size_t i = 0; i < (CTRL_PACKET_LENGTH / sizeof(boost::uint16_t)); i++) { +        sbuf->cast<boost::uint16_t *>()[i] = cmd[i]; +    } +    sbuf->commit(CTRL_PACKET_LENGTH); //fixed size transaction +    return 0; +} + +int b100_ctrl_impl::write(boost::uint32_t addr, const ctrl_data_t &data) { +    UHD_ASSERT_THROW(data.size() <= (CTRL_PACKET_DATA_LENGTH / sizeof(boost::uint16_t))); +    ctrl_pkt_t pkt; +    pkt.data = data; +    pkt.pkt_meta.op = CTRL_PKT_OP_WRITE; +    pkt.pkt_meta.callbacks = 0; +    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)]; +         +    pack_ctrl_pkt(pkt_buff, pkt); +    size_t result = send_pkt(pkt_buff); +    return result; +} + +ctrl_data_t b100_ctrl_impl::read(boost::uint32_t addr, size_t len) { +    UHD_ASSERT_THROW(len <= (CTRL_PACKET_DATA_LENGTH / sizeof(boost::uint16_t))); + +    ctrl_pkt_t pkt; +    pkt.pkt_meta.op = CTRL_PKT_OP_READ; +    pkt.pkt_meta.callbacks = 0; +    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)]; + +    pack_ctrl_pkt(pkt_buff, pkt); +    send_pkt(pkt_buff); +     +    //loop around waiting for the response to appear +    while(!get_ctrl_data(pkt.data, 0.05)); + +    return pkt.data; +} + +/*********************************************************************** + * Viking marauders go pillaging for asynchronous control packets in the + * control response endpoint. Sync packets go in sync_ctrl_fifo, + * async TX error messages go in async_msg_fifo. sync_ctrl_fifo should + * never have more than 1 message in it, since it's expected that we'll + * wait for a control operation to finish before starting another one. + **********************************************************************/ +void b100_ctrl_impl::viking_marauder_loop(boost::barrier &spawn_barrier) { +    bbl_out_marauding = true; +    spawn_barrier.wait(); +    set_thread_priority_safe(); +     +    while(bbl_out_marauding){ +        managed_recv_buffer::sptr rbuf = _ctrl_transport->get_recv_buff(); +        if(!rbuf.get()) continue; //that's ok, there are plenty of villages to pillage! +        const boost::uint16_t *pkt_buf = rbuf->cast<const boost::uint16_t *>(); +         +        if(pkt_buf[0] >> 8 == CTRL_PACKET_HEADER_MAGIC) { +            //so it's got a control packet header, let's parse it. +            ctrl_pkt_t pkt; +            unpack_ctrl_pkt(pkt_buf, pkt); +         +            if(pkt.pkt_meta.seq != boost::uint8_t(_seq - 1)) { +                throw uhd::runtime_error("Sequence error on control channel"); +            } +            if(pkt.pkt_meta.len > (CTRL_PACKET_LENGTH - CTRL_PACKET_HEADER_LENGTH)) { +                throw uhd::runtime_error("Control channel packet length too long"); +            } +         +            //push it onto the queue +            sync_ctrl_fifo.push_with_wait(pkt.data); +        } else { //it's an async status pkt +            //extract the vrt header packet info +            vrt::if_packet_info_t if_packet_info; +            if_packet_info.num_packet_words32 = rbuf->size()/sizeof(boost::uint32_t); +            const boost::uint32_t *vrt_hdr = rbuf->cast<const boost::uint32_t *>(); +            vrt::if_hdr_unpack_le(vrt_hdr, if_packet_info); +             +            if(    if_packet_info.sid == 0  +               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), 64e6 //FIXME get from clock_ctrl +                ); +                metadata.event_code = async_metadata_t::event_code_t(sph::get_context_code(vrt_hdr, if_packet_info)); +                //print the famous U, and push the metadata into the message queue +                if (metadata.event_code &  +                    ( async_metadata_t::EVENT_CODE_UNDERFLOW  +                    | async_metadata_t::EVENT_CODE_UNDERFLOW_IN_PACKET) ) +                    UHD_MSG(fastpath) << "U"; +                     +                if (metadata.event_code &  +                    ( async_metadata_t::EVENT_CODE_SEQ_ERROR +                    | async_metadata_t::EVENT_CODE_SEQ_ERROR_IN_BURST) ) +                    UHD_MSG(fastpath) << "S"; +                     +                async_msg_fifo.push_with_pop_on_full(metadata); +                continue; +            } +            throw uhd::runtime_error("Control: unknown async response"); +        } +    } +} + +bool b100_ctrl_impl::get_ctrl_data(ctrl_data_t &pkt_data, double timeout){ +    boost::this_thread::disable_interruption di; //disable because the wait can throw +    return sync_ctrl_fifo.pop_with_timed_wait(pkt_data, timeout); +} + +bool b100_ctrl_impl::recv_async_msg(uhd::async_metadata_t &async_metadata, double timeout) { +    boost::this_thread::disable_interruption di; //disable because the wait can throw +    return async_msg_fifo.pop_with_timed_wait(async_metadata, timeout); +} + +/*********************************************************************** + * Public make function for b100_ctrl interface + **********************************************************************/ +b100_ctrl::sptr b100_ctrl::make(uhd::transport::usb_zero_copy::sptr ctrl_transport){ +    return sptr(new b100_ctrl_impl(ctrl_transport)); +} diff --git a/host/lib/usrp/usrp_b100/b100_ctrl.hpp b/host/lib/usrp/usrp_b100/b100_ctrl.hpp new file mode 100644 index 000000000..ae706dbb4 --- /dev/null +++ b/host/lib/usrp/usrp_b100/b100_ctrl.hpp @@ -0,0 +1,69 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_B100_CTRL_HPP +#define INCLUDED_B100_CTRL_HPP + +#include <uhd/transport/bounded_buffer.hpp> +#include <uhd/transport/usb_zero_copy.hpp> +#include <uhd/types/metadata.hpp> +#include <uhd/types/serial.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/utility.hpp> +#include "ctrl_packet.hpp" +#include <boost/thread.hpp> + +class b100_ctrl : boost::noncopyable{ +public: +    typedef boost::shared_ptr<b100_ctrl> sptr; + +    /*! +     * Make a USRP control object from a data transport +     * \param ctrl_transport a USB data transport +     * \return a new b100 control object +     */ +    static sptr make(uhd::transport::usb_zero_copy::sptr ctrl_transport); + +    /*! +     * Write a byte vector to an FPGA register +     * \param addr the FPGA register address +     * \param bytes the data to write +     * \return 0 on success, error code on failure +     */ +    virtual int write(boost::uint32_t addr, const ctrl_data_t &data) = 0; + +    /*! +     * Read a byte vector from an FPGA register (blocking read) +     * \param addr the FPGA register address +     * \param len the length of the read +     * \return a vector of bytes from the register(s) in question +     */ +    virtual ctrl_data_t read(boost::uint32_t addr, size_t len) = 0; + +    /*! +     * Get a sync ctrl packet (blocking) +     * \param the packet data buffer +     * \param the timeout value +     * \return true if it got something +     */ +    virtual bool get_ctrl_data(ctrl_data_t &pkt_data, double timeout) = 0; +     +    virtual bool recv_async_msg(uhd::async_metadata_t &async_metadata, double timeout) = 0; +     +}; + +#endif /* INCLUDED_B100_CTRL_HPP */ diff --git a/host/lib/usrp/usrp_b100/b100_iface.cpp b/host/lib/usrp/usrp_b100/b100_iface.cpp new file mode 100644 index 000000000..17ea2e6ad --- /dev/null +++ b/host/lib/usrp/usrp_b100/b100_iface.cpp @@ -0,0 +1,336 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#include "b100_iface.hpp" +#include "usrp_commands.h" +#include <uhd/exception.hpp> +#include <uhd/utils/byteswap.hpp> +#include <uhd/utils/safe_call.hpp> +#include <uhd/utils/msg.hpp> +#include <boost/format.hpp> +#include <iomanip> +#include <iostream> + +//FOR TESTING ONLY +#include "b100_regs.hpp" +#include <boost/thread/thread.hpp> +#include "usrp_i2c_addr.h" + +using namespace uhd; +using namespace uhd::usrp; +using namespace uhd::transport; + +/*********************************************************************** + * Constants + **********************************************************************/ +static const bool iface_debug = true; +static const boost::uint16_t USRP_B_FW_COMPAT_NUM = 0x02; +static const boost::uint16_t USRP_B_FPGA_COMPAT_NUM = 0x03; + +/*********************************************************************** + * I2C + FX2 implementation wrapper + **********************************************************************/ +class b100_i2c_fx2_iface : public i2c_iface{ +public: +    b100_i2c_fx2_iface(uhd::usrp::fx2_ctrl::sptr fx2_ctrl){ +        _fx2_ctrl = fx2_ctrl; +    } + +    void write_i2c(boost::uint8_t addr, const byte_vector_t &bytes) +    { +        UHD_ASSERT_THROW(bytes.size() < max_i2c_data_bytes); + +        unsigned char buff[max_i2c_data_bytes]; +        std::copy(bytes.begin(), bytes.end(), buff); + +        int ret = _fx2_ctrl->usrp_i2c_write(addr & 0xff, +                                             buff, +                                             bytes.size()); + +        if (iface_debug && (ret < 0)) +            uhd::runtime_error("USRP: failed i2c write"); +    } + +    byte_vector_t read_i2c(boost::uint8_t addr, size_t num_bytes) +    { +      UHD_ASSERT_THROW(num_bytes < max_i2c_data_bytes); + +      unsigned char buff[max_i2c_data_bytes]; +      int ret = _fx2_ctrl->usrp_i2c_read(addr & 0xff, +                                            buff, +                                            num_bytes); + +      if (iface_debug && ((ret < 0) || (unsigned)ret < (num_bytes))) +          uhd::runtime_error("USRP: failed i2c read"); + +      byte_vector_t out_bytes; +      for (size_t i = 0; i < num_bytes; i++) +          out_bytes.push_back(buff[i]); + +      return out_bytes; +    } + +private: +    static const size_t max_i2c_data_bytes = 64; +    uhd::usrp::fx2_ctrl::sptr _fx2_ctrl; +}; + +/*********************************************************************** + * USRP-E100 interface implementation + **********************************************************************/ +class b100_iface_impl : public b100_iface{ +public: +    /******************************************************************* +     * Structors +     ******************************************************************/ +    b100_iface_impl(uhd::usrp::fx2_ctrl::sptr fx2_ctrl, +                      b100_ctrl::sptr fpga_ctrl) : +                      _fx2_i2c_iface(fx2_ctrl), +                      _fx2_ctrl(fx2_ctrl), +                      _fpga_ctrl(fpga_ctrl) +    { +        this->check_fw_compat(); +        if (fpga_ctrl.get() != NULL){ +            enable_gpif(1); +            i2c_init(); +            this->check_fpga_compat(); +        } +        mb_eeprom = mboard_eeprom_t(get_fx2_i2c_iface(), mboard_eeprom_t::MAP_B000); +    } + +    void check_fw_compat(void){ +        unsigned char data[4]; //useless data buffer +        const boost::uint16_t fw_compat_num = _fx2_ctrl->usrp_control_read( +            VRQ_FW_COMPAT, 0, 0, data, sizeof(data) +        ); +        if (fw_compat_num != USRP_B_FW_COMPAT_NUM){ +            throw uhd::runtime_error(str(boost::format( +                "Expected firmware compatibility number 0x%x, but got 0x%x:\n" +                "The firmware build is not compatible with the host code build." +            ) % USRP_B_FW_COMPAT_NUM % fw_compat_num)); +        } +    } + +    void check_fpga_compat(void){ +        const boost::uint16_t fpga_compat_num = this->peek16(B100_REG_MISC_COMPAT); +        if (fpga_compat_num != USRP_B_FPGA_COMPAT_NUM){ +            throw uhd::runtime_error(str(boost::format( +                "Expected FPGA compatibility number 0x%x, but got 0x%x:\n" +                "The FPGA build is not compatible with the host code build." +            ) % USRP_B_FPGA_COMPAT_NUM % fpga_compat_num)); +        } +    } + +    ~b100_iface_impl(void) +    { +        /* NOP */ +    } + +    /******************************************************************* +     * Peek and Poke +     ******************************************************************/ + +    void poke(boost::uint32_t addr, const ctrl_data_t &data) { +        boost::mutex::scoped_lock lock(_ctrl_mutex); +        _fpga_ctrl->write(addr, data); +    } + +    ctrl_data_t peek(boost::uint32_t addr, size_t len) { +        boost::mutex::scoped_lock lock(_ctrl_mutex); +        return _fpga_ctrl->read(addr, len); +    } + +    void poke16(boost::uint32_t addr, boost::uint16_t value) +    { +        ctrl_data_t words(1); +        words[0] = value; +        poke(addr, words); +    } + +    void poke32(boost::uint32_t addr, boost::uint32_t value) +    { +        //just a subset of poke() to maintain compatibility +        ctrl_data_t words(2); +        words[0] = value & 0x0000FFFF; +        words[1] = value >> 16; +        poke(addr, words); +    } + +    boost::uint32_t peek32(boost::uint32_t addr) +    { +        ctrl_data_t words = peek(addr, 2); +        return boost::uint32_t((boost::uint32_t(words[1]) << 16) | words[0]); +    } + +    boost::uint16_t peek16(boost::uint32_t addr) +    { +        ctrl_data_t words = peek(addr, 1); +        return boost::uint16_t(words[0]); +    } + +    /******************************************************************* +     * I2C +     ******************************************************************/ +    static const boost::uint32_t i2c_datarate = 400000; +    static const boost::uint32_t wishbone_clk = 64000000; //FIXME should go somewhere else + +    void i2c_init(void) { +        //init I2C FPGA interface. +        poke16(B100_REG_I2C_CTRL, 0x0000); +        //set prescalers to operate at 400kHz: WB_CLK is 64MHz... +        boost::uint16_t prescaler = wishbone_clk / (i2c_datarate*5) - 1; +        poke16(B100_REG_I2C_PRESCALER_LO, prescaler & 0xFF); +        poke16(B100_REG_I2C_PRESCALER_HI, (prescaler >> 8) & 0xFF); +        poke16(B100_REG_I2C_CTRL, I2C_CTRL_EN); //enable I2C core +    } + +    static const size_t max_i2c_data_bytes = 64; + +    void i2c_wait_for_xfer(void) +    { +        while(this->peek16(B100_REG_I2C_CMD_STATUS) & I2C_ST_TIP) +            boost::this_thread::sleep(boost::posix_time::milliseconds(10)); +    } + +    bool wait_chk_ack(void) { +        i2c_wait_for_xfer(); +        return (this->peek16(B100_REG_I2C_CMD_STATUS) & I2C_ST_RXACK) == 0; +    } + +    void write_i2c(boost::uint8_t addr, const byte_vector_t &bytes) +    { +        poke16(B100_REG_I2C_DATA, (addr << 1) | 0); //addr and read bit (0) +        poke16(B100_REG_I2C_CMD_STATUS, I2C_CMD_WR | I2C_CMD_START | (bytes.size() == 0 ? I2C_CMD_STOP : 0)); + +        //wait for previous transfer to complete +        if(!wait_chk_ack()) { +            poke16(B100_REG_I2C_CMD_STATUS, I2C_CMD_STOP); +            return; +        } + +        for(size_t i = 0; i < bytes.size(); i++) { +            poke16(B100_REG_I2C_DATA, bytes[i]); +            poke16(B100_REG_I2C_CMD_STATUS, I2C_CMD_WR | ((i == (bytes.size() - 1)) ? I2C_CMD_STOP : 0)); +            if(!wait_chk_ack()) { +                poke16(B100_REG_I2C_CMD_STATUS, I2C_CMD_STOP); +                return; +            } +        } +    } + +    byte_vector_t read_i2c(boost::uint8_t addr, size_t num_bytes) +    { +        byte_vector_t bytes; +        if(num_bytes == 0) return bytes; + +        while (peek16(B100_REG_I2C_CMD_STATUS) & I2C_ST_BUSY); + +        poke16(B100_REG_I2C_DATA, (addr << 1) | 1); //addr and read bit (1) +        poke16(B100_REG_I2C_CMD_STATUS, I2C_CMD_WR | I2C_CMD_START); +        //wait for previous transfer to complete +        if(!wait_chk_ack()) { +            poke16(B100_REG_I2C_CMD_STATUS, I2C_CMD_STOP); +        } + +        for(; num_bytes > 0; num_bytes--) { +            poke16(B100_REG_I2C_CMD_STATUS, I2C_CMD_RD | ((num_bytes == 1) ? (I2C_CMD_STOP | I2C_CMD_NACK) : 0)); +            i2c_wait_for_xfer(); +            boost::uint8_t readback = peek16(B100_REG_I2C_DATA) & 0xFF; +            bytes.push_back(readback); +        } +        return bytes; +    } + +    i2c_iface &get_fx2_i2c_iface(void){ +        return _fx2_i2c_iface; +    } + +    /******************************************************************* +     * SPI interface +     * Eventually this will be replaced with a control-channel system +     * to let the firmware do the actual write/readback cycles. +     * This keeps the bandwidth on the control channel down. +     ******************************************************************/ + +    void spi_wait(void) { +        while(peek32(B100_REG_SPI_CTRL) & SPI_CTRL_GO_BSY); +    } + +    boost::uint32_t transact_spi(int which_slave, +                                 const spi_config_t &config, +                                 boost::uint32_t bits, +                                 size_t num_bits, +                                 bool readback) +    { +        UHD_ASSERT_THROW((num_bits <= 32) && !(num_bits % 8)); + +        int edge_flags = ((config.miso_edge==spi_config_t::EDGE_FALL) ? SPI_CTRL_RXNEG : 0) | +                         ((config.mosi_edge==spi_config_t::EDGE_FALL) ? 0 : SPI_CTRL_TXNEG) +                         ; + +        boost::uint16_t ctrl = SPI_CTRL_ASS | (SPI_CTRL_CHAR_LEN_MASK & num_bits) | edge_flags; + +        poke16(B100_REG_SPI_DIV, 0x0001); // = fpga_clk / 4 +        poke32(B100_REG_SPI_SS, which_slave & 0xFFFF); +        poke32(B100_REG_SPI_TXRX0, bits); +        poke16(B100_REG_SPI_CTRL, ctrl); + +        poke16(B100_REG_SPI_CTRL, ctrl | SPI_CTRL_GO_BSY); +        if(readback) { +            spi_wait(); +            return peek32(B100_REG_SPI_TXRX0); +        } +        else { +            return 0; +        } +    } + +    void reset_gpif(boost::uint16_t ep) { +        _fx2_ctrl->usrp_control_write(VRQ_RESET_GPIF, ep, ep, 0, 0); +    } + +    void enable_gpif(bool en) { +        _fx2_ctrl->usrp_control_write(VRQ_ENABLE_GPIF, en ? 1 : 0, 0, 0, 0); +    } + +    void clear_fpga_fifo(void) { +        _fx2_ctrl->usrp_control_write(VRQ_CLEAR_FPGA_FIFO, 0, 0, 0, 0); +    } + +    void write_uart(boost::uint8_t, const std::string &) { +        throw uhd::not_implemented_error("Unhandled command write_uart()"); +    } +     +    std::string read_uart(boost::uint8_t) { +        throw uhd::not_implemented_error("Unhandled command read_uart()"); +    } + +private: +    b100_i2c_fx2_iface _fx2_i2c_iface; +    uhd::usrp::fx2_ctrl::sptr _fx2_ctrl; +    b100_ctrl::sptr _fpga_ctrl; +    boost::mutex _ctrl_mutex; +}; + +/*********************************************************************** + * Public Make Function + **********************************************************************/ +b100_iface::sptr b100_iface::make(uhd::usrp::fx2_ctrl::sptr fx2_ctrl, +                                      b100_ctrl::sptr fpga_ctrl) +{ +    return b100_iface::sptr(new b100_iface_impl(fx2_ctrl, fpga_ctrl)); +} diff --git a/host/lib/usrp/usrp_b100/b100_iface.hpp b/host/lib/usrp/usrp_b100/b100_iface.hpp new file mode 100644 index 000000000..57ed6a45c --- /dev/null +++ b/host/lib/usrp/usrp_b100/b100_iface.hpp @@ -0,0 +1,73 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_B100_IFACE_HPP +#define INCLUDED_B100_IFACE_HPP + +#include <uhd/usrp/mboard_iface.hpp> +#include <uhd/usrp/mboard_eeprom.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/utility.hpp> +#include <uhd/transport/usb_zero_copy.hpp> +#include "../fx2/fx2_ctrl.hpp" +#include "b100_ctrl.hpp" + +/*! + * The usrp1 interface class: + * Provides a set of functions to implementation layer. + * Including spi, peek, poke, control... + */ +class b100_iface : boost::noncopyable, public uhd::usrp::mboard_iface{ +public: +    typedef boost::shared_ptr<b100_iface> sptr; + +    /*! +     * Make a new b100 interface with the control transport. +     * \param fx2_ctrl the usrp control object +     * \param fpga_ctrl the FPGA interface control object +     * \return a new usrp1 interface object +     */ +    static sptr make(uhd::usrp::fx2_ctrl::sptr fx2_ctrl, +                     b100_ctrl::sptr fpga_ctrl = b100_ctrl::sptr() +    ); + +    /*! +     * Reset the GPIF interface on the FX2 +     * \param which endpoint to reset +     * \return +     */ +    virtual void reset_gpif(boost::uint16_t ep) = 0; + +    /*! +     * Clear the GPIF FIFOs on the FPGA +     * \return +     */ +    virtual void clear_fpga_fifo(void) = 0; + +    /*! +     * Enable/disable the GPIF interfaces on the FX2 +     * \return +     */ +    virtual void enable_gpif(bool en) = 0; + +    //! Get access to the FX2 I2C interface +    virtual uhd::i2c_iface &get_fx2_i2c_iface(void) = 0; + +    uhd::usrp::mboard_eeprom_t mb_eeprom; +}; + +#endif /* INCLUDED_USRP1_IFACE_HPP */ diff --git a/host/lib/usrp/usrp_b100/b100_impl.cpp b/host/lib/usrp/usrp_b100/b100_impl.cpp new file mode 100644 index 000000000..772da8f42 --- /dev/null +++ b/host/lib/usrp/usrp_b100/b100_impl.cpp @@ -0,0 +1,277 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#include "b100_impl.hpp" +#include "b100_ctrl.hpp" +#include "fpga_regs_standard.h" +#include "usrp_spi_defs.h" +#include <uhd/transport/usb_control.hpp> +#include "ctrl_packet.hpp" +#include <uhd/usrp/device_props.hpp> +#include <uhd/usrp/mboard_props.hpp> +#include <uhd/utils/msg.hpp> +#include <uhd/exception.hpp> +#include <uhd/utils/static.hpp> +#include <uhd/utils/images.hpp> +#include <uhd/utils/safe_call.hpp> +#include <boost/format.hpp> +#include <boost/assign/list_of.hpp> +#include <boost/filesystem.hpp> +#include <boost/thread/thread.hpp> +#include <boost/lexical_cast.hpp> +#include "b100_regs.hpp" + +using namespace uhd; +using namespace uhd::usrp; +using namespace uhd::transport; + +const boost::uint16_t B100_VENDOR_ID  = 0x2500; +const boost::uint16_t B100_PRODUCT_ID = 0x0001; +const boost::uint16_t FX2_VENDOR_ID    = 0x04b4; +const boost::uint16_t FX2_PRODUCT_ID   = 0x8613; + +/*********************************************************************** + * Discovery + **********************************************************************/ +static device_addrs_t b100_find(const device_addr_t &hint) +{ +    device_addrs_t b100_addrs; + +    //return an empty list of addresses when type is set to non-b100 +    if (hint.has_key("type") and hint["type"] != "b100") return b100_addrs; + +    //extract the firmware path for the b100 +    std::string b100_fw_image; +    try{ +        b100_fw_image = find_image_path( +            hint.has_key("fw")? hint["fw"] : "usrp_b100_fw.ihx" +        ); +    } +    catch(...){ +        UHD_MSG(warning) << boost::format( +            "Could not locate B100 firmware.\n" +            "Please install the images package.\n" +        ); +        return b100_addrs; +    } + +    boost::uint16_t vid = hint.has_key("uninit") ? FX2_VENDOR_ID : B100_VENDOR_ID; +    boost::uint16_t pid = hint.has_key("uninit") ? FX2_PRODUCT_ID : B100_PRODUCT_ID; + +    // Important note: +    // The get device list calls are nested inside the for loop. +    // This allows the usb guts to decontruct when not in use, +    // so that re-enumeration after fw load can occur successfully. +    // This requirement is a courtesy of libusb1.0 on windows. + +    //find the usrps and load firmware +    BOOST_FOREACH(usb_device_handle::sptr handle, usb_device_handle::get_device_list(vid, pid)) { +        try { +            fx2_ctrl::make(usb_control::make(handle))->usrp_load_firmware(b100_fw_image); +        } catch (...) { +            UHD_MSG(status) << "Interface claimed, ignoring device" << std::endl; +        } +    } + +    //get descriptors again with serial number, but using the initialized VID/PID now since we have firmware +    vid = B100_VENDOR_ID; +    pid = B100_PRODUCT_ID; + +    BOOST_FOREACH(usb_device_handle::sptr handle, usb_device_handle::get_device_list(vid, pid)) { +        device_addr_t new_addr; +        new_addr["type"] = "b100"; + +        //Attempt to read the name from the EEPROM and perform filtering. +        //This operation can throw due to compatibility mismatch. +        try{ +            usb_control::sptr control = usb_control::make(handle); +            b100_iface::sptr iface = b100_iface::make(fx2_ctrl::make(control)); +            new_addr["name"] = iface->mb_eeprom["name"]; +            new_addr["serial"] = handle->get_serial(); +        } +        catch(const uhd::exception &){ +            //set these values as empty string so the device may still be found +            //and the filter's below can still operate on the discovered device +            new_addr["name"] = ""; +            new_addr["serial"] = ""; +        } + +        //this is a found b100 when the hint serial and name match or blank +        if ( +            (not hint.has_key("name")   or hint["name"]   == new_addr["name"]) and +            (not hint.has_key("serial") or hint["serial"] == new_addr["serial"]) +        ){ +            b100_addrs.push_back(new_addr); +        } +    } + +    return b100_addrs; +} + +/*********************************************************************** + * Make + **********************************************************************/ +static device::sptr b100_make(const device_addr_t &device_addr){ + +    //extract the FPGA path for the B100 +    std::string b100_fpga_image = find_image_path( +        device_addr.has_key("fpga")? device_addr["fpga"] : "usrp_b100_fpga.bin" +    ); + +    //try to match the given device address with something on the USB bus +    std::vector<usb_device_handle::sptr> device_list = +        usb_device_handle::get_device_list(B100_VENDOR_ID, B100_PRODUCT_ID); + +    //locate the matching handle in the device list +    usb_device_handle::sptr handle; +    BOOST_FOREACH(usb_device_handle::sptr dev_handle, device_list) { +        if (dev_handle->get_serial() == device_addr["serial"]){ +            handle = dev_handle; +            break; +        } +    } +    UHD_ASSERT_THROW(handle.get() != NULL); //better be found + +    //create control objects and a data transport +    usb_control::sptr fx2_transport = usb_control::make(handle); +    fx2_ctrl::sptr fx2_ctrl = fx2_ctrl::make(fx2_transport); +    fx2_ctrl->usrp_load_fpga(b100_fpga_image); + +    device_addr_t data_xport_args; +    data_xport_args["recv_frame_size"] = device_addr.get("recv_frame_size", "16384"); +    data_xport_args["num_recv_frames"] = device_addr.get("num_recv_frames", "16"); +    data_xport_args["send_frame_size"] = device_addr.get("send_frame_size", "16384"); +    data_xport_args["num_send_frames"] = device_addr.get("num_send_frames", "16"); + +    usb_zero_copy::sptr data_transport = usb_zero_copy::make_wrapper( +        usb_zero_copy::make( +            handle,        // identifier +            6,             // IN endpoint +            2,             // OUT endpoint +            data_xport_args    // param hints +        ) +    ); + +    //create the control transport +    device_addr_t ctrl_xport_args; +    ctrl_xport_args["recv_frame_size"] = boost::lexical_cast<std::string>(CTRL_PACKET_LENGTH); +    ctrl_xport_args["num_recv_frames"] = "16"; +    ctrl_xport_args["send_frame_size"] = boost::lexical_cast<std::string>(CTRL_PACKET_LENGTH); +    ctrl_xport_args["num_send_frames"] = "4"; + +    usb_zero_copy::sptr ctrl_transport = usb_zero_copy::make( +        handle, +        8, +        4, +        ctrl_xport_args +    ); + +    const double master_clock_rate = device_addr.cast<double>("master_clock_rate", 64e6); + + +    //create the b100 implementation guts +    return device::sptr(new b100_impl(data_transport, ctrl_transport, fx2_ctrl, master_clock_rate)); +} + +UHD_STATIC_BLOCK(register_b100_device){ +    device::register_device(&b100_find, &b100_make); +} + +/*********************************************************************** + * Structors + **********************************************************************/ +b100_impl::b100_impl(uhd::transport::usb_zero_copy::sptr data_transport, +                         uhd::transport::usb_zero_copy::sptr ctrl_transport, +                         uhd::usrp::fx2_ctrl::sptr fx2_ctrl, +                         const double master_clock_rate) + : _data_transport(data_transport), _fx2_ctrl(fx2_ctrl) +{ +    //this is the handler object for FPGA control packets +    _fpga_ctrl = b100_ctrl::make(ctrl_transport); + +    _iface = b100_iface::make(_fx2_ctrl, _fpga_ctrl); + +    //create clock interface +    _clock_ctrl = b100_clock_ctrl::make(_iface, master_clock_rate); + +    //create codec interface +    _codec_ctrl = b100_codec_ctrl::make(_iface); + +    //initialize the codecs +    codec_init(); + +    //initialize the mboard +    mboard_init(); + +    //initialize the dboards +    dboard_init(); + +    //initialize the dsps +    rx_ddc_init(); + +    //initialize the dsps +    tx_duc_init(); + +    //init the subdev specs +    this->mboard_set(MBOARD_PROP_RX_SUBDEV_SPEC, subdev_spec_t()); +    this->mboard_set(MBOARD_PROP_TX_SUBDEV_SPEC, subdev_spec_t()); + +    //initialize the send/recv buffs +    io_init(); +} + +b100_impl::~b100_impl(void){ +    /* NOP */ +} + +bool b100_impl::recv_async_msg(uhd::async_metadata_t &md, double timeout){ +    return _fpga_ctrl->recv_async_msg(md, timeout); +} + +/*********************************************************************** + * Device Get + **********************************************************************/ +void b100_impl::get(const wax::obj &key_, wax::obj &val) +{ +    named_prop_t key = named_prop_t::extract(key_); + +    //handle the get request conditioned on the key +    switch(key.as<device_prop_t>()){ +    case DEVICE_PROP_NAME: +        val = std::string("USRP-B100 device"); +        return; + +    case DEVICE_PROP_MBOARD: +        UHD_ASSERT_THROW(key.name == ""); +        val = _mboard_proxy->get_link(); +        return; + +    case DEVICE_PROP_MBOARD_NAMES: +        val = prop_names_t(1, ""); //vector of size 1 with empty string +        return; + +    default: UHD_THROW_PROP_GET_ERROR(); +    } +} + +/*********************************************************************** + * Device Set + **********************************************************************/ +void b100_impl::set(const wax::obj &, const wax::obj &) +{ +    UHD_THROW_PROP_SET_ERROR(); +} diff --git a/host/lib/usrp/usrp_b100/b100_impl.hpp b/host/lib/usrp/usrp_b100/b100_impl.hpp new file mode 100644 index 000000000..daec70bca --- /dev/null +++ b/host/lib/usrp/usrp_b100/b100_impl.hpp @@ -0,0 +1,204 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#include "b100_iface.hpp" +#include "b100_ctrl.hpp" +#include "clock_ctrl.hpp" +#include "codec_ctrl.hpp" +#include <uhd/device.hpp> +#include <uhd/utils/pimpl.hpp> +#include <uhd/types/dict.hpp> +#include <uhd/types/otw_type.hpp> +#include <uhd/types/clock_config.hpp> +#include <uhd/types/stream_cmd.hpp> +#include <uhd/usrp/dboard_id.hpp> +#include <uhd/usrp/subdev_spec.hpp> +#include <uhd/usrp/dboard_eeprom.hpp> +#include <uhd/usrp/dboard_manager.hpp> +#include <uhd/transport/usb_zero_copy.hpp> + +#ifndef INCLUDED_B100_IMPL_HPP +#define INCLUDED_B100_IMPL_HPP + +/*! + * Make a b100 dboard interface. + * \param iface the b100 interface object + * \param clock the clock control interface + * \param codec the codec control interface + * \return a sptr to a new dboard interface + */ +uhd::usrp::dboard_iface::sptr make_b100_dboard_iface( +    b100_iface::sptr iface, +    b100_clock_ctrl::sptr clock, +    b100_codec_ctrl::sptr codec +); + +/*! + * Simple wax obj proxy class: + * Provides a wax obj interface for a set and a get function. + * This allows us to create nested properties structures + * while maintaining flattened code within the implementation. + */ +class wax_obj_proxy : public wax::obj { +public: +    typedef boost::function<void(const wax::obj &, wax::obj &)>       get_t; +    typedef boost::function<void(const wax::obj &, const wax::obj &)> set_t; +    typedef boost::shared_ptr<wax_obj_proxy> sptr; + +    static sptr make(const get_t &get, const set_t &set){ +        return sptr(new wax_obj_proxy(get, set)); +    } + +private: +    get_t _get; set_t _set; +    wax_obj_proxy(const get_t &get, const set_t &set): _get(get), _set(set) {}; +    void get(const wax::obj &key, wax::obj &val) {return _get(key, val);} +    void set(const wax::obj &key, const wax::obj &val) {return _set(key, val);} +}; + +/*! + * USRP1 implementation guts: + * The implementation details are encapsulated here. + * Handles properties on the mboard, dboard, dsps... + */ +class b100_impl : public uhd::device { +public: +    //structors +    b100_impl(uhd::transport::usb_zero_copy::sptr data_transport, +                uhd::transport::usb_zero_copy::sptr ctrl_transport, +                uhd::usrp::fx2_ctrl::sptr fx2_ctrl, +                double master_clock_rate); + +    ~b100_impl(void); + +    //the io interface +    size_t send(const send_buffs_type &, +                size_t, +                const uhd::tx_metadata_t &, +                const uhd::io_type_t &, +                send_mode_t, double); + +    size_t recv(const recv_buffs_type &, +                size_t, uhd::rx_metadata_t &, +                const uhd::io_type_t &, +                recv_mode_t, double); + +    size_t get_max_send_samps_per_packet(void) const; + +    size_t get_max_recv_samps_per_packet(void) const; + +    bool recv_async_msg(uhd::async_metadata_t &, double); + +private: +    //clock control +    b100_clock_ctrl::sptr _clock_ctrl; + +    //interface to ioctls and file descriptor +    b100_iface::sptr _iface; + +    //handle io stuff +    uhd::transport::zero_copy_if::sptr _data_transport; +    UHD_PIMPL_DECL(io_impl) _io_impl; +    void update_transport_channel_mapping(void); +    void io_init(void); +    void issue_stream_cmd(const uhd::stream_cmd_t &stream_cmd); +    void handle_overrun(size_t); + +    //otw types +    uhd::otw_type_t _recv_otw_type; +    uhd::otw_type_t _send_otw_type; + +    //configuration shadows +    uhd::clock_config_t _clock_config; +    uhd::usrp::subdev_spec_t _rx_subdev_spec, _tx_subdev_spec; + +    //ad9862 codec control interface +    b100_codec_ctrl::sptr _codec_ctrl; + +    //codec properties interfaces +    void codec_init(void); +    void rx_codec_get(const wax::obj &, wax::obj &); +    void rx_codec_set(const wax::obj &, const wax::obj &); +    void tx_codec_get(const wax::obj &, wax::obj &); +    void tx_codec_set(const wax::obj &, const wax::obj &); +    wax_obj_proxy::sptr _rx_codec_proxy, _tx_codec_proxy; + +    //device functions and settings +    void get(const wax::obj &, wax::obj &); +    void set(const wax::obj &, const wax::obj &); + +    //mboard functions and settings +    void mboard_init(void); +    void mboard_get(const wax::obj &, wax::obj &); +    void mboard_set(const wax::obj &, const wax::obj &); +    void update_clock_config(void); +    wax_obj_proxy::sptr _mboard_proxy; +     +    /*! +     * Make a usrp1 dboard interface. +     * \param iface the usrp1 interface object +     * \param clock the clock control interface +     * \param codec the codec control interface +     * \param dboard_slot the slot identifier +     * \param rx_dboard_id the db id for the rx board (used for evil dbsrx purposes) +     * \return a sptr to a new dboard interface +     */ +    static uhd::usrp::dboard_iface::sptr make_dboard_iface( +        b100_iface::sptr iface, +        b100_clock_ctrl::sptr clock, +        b100_codec_ctrl::sptr codec, +        const uhd::usrp::dboard_id_t &rx_dboard_id +    ); + +    //xx dboard functions and settings +    void dboard_init(void); +    uhd::usrp::dboard_manager::sptr _dboard_manager; +    uhd::usrp::dboard_iface::sptr _dboard_iface; + +    //rx dboard functions and settings +    uhd::usrp::dboard_eeprom_t _rx_db_eeprom; +    void rx_dboard_get(const wax::obj &, wax::obj &); +    void rx_dboard_set(const wax::obj &, const wax::obj &); +    wax_obj_proxy::sptr _rx_dboard_proxy; + +    //tx dboard functions and settings +    uhd::usrp::dboard_eeprom_t _tx_db_eeprom, _gdb_eeprom; +    void tx_dboard_get(const wax::obj &, wax::obj &); +    void tx_dboard_set(const wax::obj &, const wax::obj &); +    wax_obj_proxy::sptr _tx_dboard_proxy; + +    //rx ddc functions and settings +    void rx_ddc_init(void); +    void rx_ddc_get(const wax::obj &, wax::obj &); +    void rx_ddc_set(const wax::obj &, const wax::obj &); +    double _ddc_freq; size_t _ddc_decim; +    wax_obj_proxy::sptr _rx_ddc_proxy; + +    //tx duc functions and settings +    void tx_duc_init(void); +    void tx_duc_get(const wax::obj &, wax::obj &); +    void tx_duc_set(const wax::obj &, const wax::obj &); +    double _duc_freq; size_t _duc_interp; +    wax_obj_proxy::sptr _tx_duc_proxy; + +    //transports +    b100_ctrl::sptr _fpga_ctrl; +    uhd::usrp::fx2_ctrl::sptr _fx2_ctrl; + +}; + +#endif /* INCLUDED_b100_IMPL_HPP */ diff --git a/host/lib/usrp/usrp_b100/b100_regs.hpp b/host/lib/usrp/usrp_b100/b100_regs.hpp new file mode 100644 index 000000000..010df283e --- /dev/null +++ b/host/lib/usrp/usrp_b100/b100_regs.hpp @@ -0,0 +1,264 @@ + + +//////////////////////////////////////////////////////////////// +// +//         Memory map for wishbone bus +// +//////////////////////////////////////////////////////////////// + +// All addresses are byte addresses.  All accesses are word (16-bit) accesses. +//  This means that address bit 0 is usually 0. +//  There are 11 bits of address for the control. + +#ifndef __B100_REGS_H +#define __B100_REGS_H + +///////////////////////////////////////////////////// +// Slave pointers + +#define B100_REG_SLAVE(n) ((n)<<7) +#define B100_REG_SR_ADDR(n) ((B100_REG_SETTINGS_BASE) + (4*(n))) + +///////////////////////////////////////////////////// +// Slave 0 -- Misc Regs + +#define B100_REG_MISC_BASE B100_REG_SLAVE(0) + +#define B100_REG_MISC_LED        B100_REG_MISC_BASE + 0 +#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 +#define B100_REG_MISC_COMPAT     B100_REG_MISC_BASE + 16 + +///////////////////////////////////////////////////// +// Slave 1 -- UART +//   CLKDIV is 16 bits, others are only 8 + +#define B100_REG_UART_BASE B100_REG_SLAVE(1) + +#define B100_REG_UART_CLKDIV  B100_REG_UART_BASE + 0 +#define B100_REG_UART_TXLEVEL B100_REG_UART_BASE + 2 +#define B100_REG_UART_RXLEVEL B100_REG_UART_BASE + 4 +#define B100_REG_UART_TXCHAR  B100_REG_UART_BASE + 6 +#define B100_REG_UART_RXCHAR  B100_REG_UART_BASE + 8 + +///////////////////////////////////////////////////// +// Slave 2 -- SPI Core +//these are 32-bit registers mapped onto the 16-bit Wishbone bus.  +//Using peek32/poke32 should allow transparent use of these registers. +#define B100_REG_SPI_BASE B100_REG_SLAVE(2) +#define B100_REG_SPI_TXRX0 B100_REG_SPI_BASE + 0 +#define B100_REG_SPI_TXRX1 B100_REG_SPI_BASE + 4 +#define B100_REG_SPI_TXRX2 B100_REG_SPI_BASE + 8 +#define B100_REG_SPI_TXRX3 B100_REG_SPI_BASE + 12 +#define B100_REG_SPI_CTRL  B100_REG_SPI_BASE + 16 +#define B100_REG_SPI_DIV   B100_REG_SPI_BASE + 20 +#define B100_REG_SPI_SS    B100_REG_SPI_BASE + 24 + +//spi slave constants +#define B100_SPI_SS_AD9862    (1 << 2) +#define B100_SPI_SS_TX_DB     (1 << 1) +#define B100_SPI_SS_RX_DB     (1 << 0) + +//spi ctrl register bit definitions +#define SPI_CTRL_ASS      (1<<13) +#define SPI_CTRL_IE       (1<<12) +#define SPI_CTRL_LSB      (1<<11) +#define SPI_CTRL_TXNEG    (1<<10) //mosi edge, push on falling edge when 1 +#define SPI_CTRL_RXNEG    (1<< 9) //miso edge, latch on falling edge when 1 +#define SPI_CTRL_GO_BSY   (1<< 8) +#define SPI_CTRL_CHAR_LEN_MASK 0x7F + +//////////////////////////////////////////////// +// Slave 3 -- I2C Core + +#define B100_REG_I2C_BASE B100_REG_SLAVE(3) +#define B100_REG_I2C_PRESCALER_LO B100_REG_I2C_BASE + 0 +#define B100_REG_I2C_PRESCALER_HI B100_REG_I2C_BASE + 2 +#define B100_REG_I2C_CTRL         B100_REG_I2C_BASE + 4 +#define B100_REG_I2C_DATA         B100_REG_I2C_BASE + 6 +#define B100_REG_I2C_CMD_STATUS   B100_REG_I2C_BASE + 8 + +//and while we're here... + +// +// STA, STO, RD, WR, and IACK bits are cleared automatically +// + +#define	I2C_CTRL_EN	(1 << 7)	// core enable +#define	I2C_CTRL_IE	(1 << 6)	// interrupt enable + +#define	I2C_CMD_START	(1 << 7)	// generate (repeated) start condition +#define I2C_CMD_STOP	(1 << 6)	// generate stop condition +#define	I2C_CMD_RD	(1 << 5)	// read from slave +#define I2C_CMD_WR	(1 << 4)	// write to slave +#define	I2C_CMD_NACK	(1 << 3)	// when a rcvr, send ACK (ACK=0) or NACK (ACK=1) +#define I2C_CMD_RSVD_2	(1 << 2)	// reserved +#define	I2C_CMD_RSVD_1	(1 << 1)	// reserved +#define I2C_CMD_IACK	(1 << 0)	// set to clear pending interrupt + +#define I2C_ST_RXACK	(1 << 7)	// Received acknowledgement from slave (1 = NAK, 0 = ACK) +#define	I2C_ST_BUSY	(1 << 6)	// 1 after START signal detected; 0 after STOP signal detected +#define	I2C_ST_AL	(1 << 5)	// Arbitration lost.  1 when core lost arbitration +#define	I2C_ST_RSVD_4	(1 << 4)	// reserved +#define	I2C_ST_RSVD_3	(1 << 3)	// reserved +#define	I2C_ST_RSVD_2	(1 << 2)	// reserved +#define I2C_ST_TIP	(1 << 1)	// Transfer-in-progress +#define	I2C_ST_IP	(1 << 0)	// Interrupt pending + +//////////////////////////////////////////////// +// Slave 4 -- GPIO + +#define B100_REG_GPIO_BASE B100_REG_SLAVE(4) + +#define B100_REG_GPIO_RX_IO      B100_REG_GPIO_BASE + 0 +#define B100_REG_GPIO_TX_IO      B100_REG_GPIO_BASE + 2 +#define B100_REG_GPIO_RX_DDR     B100_REG_GPIO_BASE + 4 +#define B100_REG_GPIO_TX_DDR     B100_REG_GPIO_BASE + 6 +#define B100_REG_GPIO_RX_SEL     B100_REG_GPIO_BASE + 8 +#define B100_REG_GPIO_TX_SEL     B100_REG_GPIO_BASE + 10 +#define B100_REG_GPIO_RX_DBG     B100_REG_GPIO_BASE + 12 +#define B100_REG_GPIO_TX_DBG     B100_REG_GPIO_BASE + 14 + +//possible bit values for sel when dbg is 0: +#define GPIO_SEL_SW    0 // if pin is an output, set by software in the io reg +#define GPIO_SEL_ATR   1 // if pin is an output, set by ATR logic + +//possible bit values for sel when dbg is 1: +#define GPIO_SEL_DEBUG_0   0 // if pin is an output, debug lines from FPGA fabric +#define GPIO_SEL_DEBUG_1   1 // if pin is an output, debug lines from FPGA fabric + +/////////////////////////////////////////////////// +// Slave 6 -- ATR Controller +//   16 regs + +#define B100_REG_ATR_BASE  B100_REG_SLAVE(6) + +#define	B100_REG_ATR_IDLE_RXSIDE  B100_REG_ATR_BASE + 0 +#define	B100_REG_ATR_IDLE_TXSIDE  B100_REG_ATR_BASE + 2 +#define B100_REG_ATR_INTX_RXSIDE  B100_REG_ATR_BASE + 4 +#define B100_REG_ATR_INTX_TXSIDE  B100_REG_ATR_BASE + 6 +#define	B100_REG_ATR_INRX_RXSIDE  B100_REG_ATR_BASE + 8 +#define	B100_REG_ATR_INRX_TXSIDE  B100_REG_ATR_BASE + 10 +#define	B100_REG_ATR_FULL_RXSIDE  B100_REG_ATR_BASE + 12 +#define	B100_REG_ATR_FULL_TXSIDE  B100_REG_ATR_BASE + 14 + +/////////////////////////////////////////////////// +// Slave 7 -- Readback Mux 32 + +#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_MISC_TEST32     B100_REG_RB_MUX_32_BASE + 16 + +//////////////////////////////////////////////////// +// Slaves 8 & 9 -- Settings Bus +// +// Output-only, no readback, 64 registers total +//  Each register must be written 32 bits at a time +//  First the address xxx_xx00 and then xxx_xx10 + +#define B100_REG_SETTINGS_BASE_ADDR(n) (B100_REG_SLAVE(8) + (4*(n))) + +#define B100_REG_SR_MISC_TEST32        B100_REG_SETTINGS_BASE_ADDR(52) + +///////////////////////////////////////////////// +// DSP RX Regs +//////////////////////////////////////////////// +#define B100_REG_DSP_RX_ADDR(n)      (B100_REG_SETTINGS_BASE_ADDR(16) + (4*(n))) +#define B100_REG_DSP_RX_FREQ         B100_REG_DSP_RX_ADDR(0) +#define B100_REG_DSP_RX_SCALE_IQ     B100_REG_DSP_RX_ADDR(1)  // {scale_i,scale_q} +#define B100_REG_DSP_RX_DECIM_RATE   B100_REG_DSP_RX_ADDR(2)  // hb and decim rate +#define B100_REG_DSP_RX_DCOFFSET_I   B100_REG_DSP_RX_ADDR(3) // Bit 31 high sets fixed offset mode, using lower 14 bits, // otherwise it is automatic +#define B100_REG_DSP_RX_DCOFFSET_Q   B100_REG_DSP_RX_ADDR(4) // Bit 31 high sets fixed offset mode, using lower 14 bits +#define B100_REG_DSP_RX_MUX          B100_REG_DSP_RX_ADDR(5) + +/////////////////////////////////////////////////// +// VITA RX CTRL regs +/////////////////////////////////////////////////// +// The following 3 are logically a single command register. +// They are clocked into the underlying fifo when time_ticks is written. +#define B100_REG_CTRL_RX_ADDR(n)           (B100_REG_SETTINGS_BASE_ADDR(0) + (4*(n))) +#define B100_REG_CTRL_RX_STREAM_CMD        B100_REG_CTRL_RX_ADDR(0) // {now, chain, num_samples(30) +#define B100_REG_CTRL_RX_TIME_SECS         B100_REG_CTRL_RX_ADDR(1) +#define B100_REG_CTRL_RX_TIME_TICKS        B100_REG_CTRL_RX_ADDR(2) +#define B100_REG_CTRL_RX_CLEAR_OVERRUN     B100_REG_CTRL_RX_ADDR(3) // write anything to clear overrun +#define B100_REG_CTRL_RX_VRT_HEADER        B100_REG_CTRL_RX_ADDR(4) // word 0 of packet.  FPGA fills in packet counter +#define B100_REG_CTRL_RX_VRT_STREAM_ID     B100_REG_CTRL_RX_ADDR(5) // word 1 of packet. +#define B100_REG_CTRL_RX_VRT_TRAILER       B100_REG_CTRL_RX_ADDR(6) +#define B100_REG_CTRL_RX_NSAMPS_PER_PKT    B100_REG_CTRL_RX_ADDR(7) +#define B100_REG_CTRL_RX_NCHANNELS         B100_REG_CTRL_RX_ADDR(8) // 1 in basic case, up to 4 for vector sources + +///////////////////////////////////////////////// +// DSP TX Regs +//////////////////////////////////////////////// +#define B100_REG_DSP_TX_ADDR(n)      (B100_REG_SETTINGS_BASE_ADDR(32) + (4*(n))) +#define B100_REG_DSP_TX_FREQ         B100_REG_DSP_TX_ADDR(0) +#define B100_REG_DSP_TX_SCALE_IQ     B100_REG_DSP_TX_ADDR(1) // {scale_i,scale_q} +#define B100_REG_DSP_TX_INTERP_RATE  B100_REG_DSP_TX_ADDR(2) +#define B100_REG_DSP_TX_UNUSED       B100_REG_DSP_TX_ADDR(3) +#define B100_REG_DSP_TX_MUX          B100_REG_DSP_TX_ADDR(4) + +///////////////////////////////////////////////// +// VITA TX CTRL regs +//////////////////////////////////////////////// +#define B100_REG_CTRL_TX_ADDR(n)           (B100_REG_SETTINGS_BASE_ADDR(24) + (4*(n))) +#define B100_REG_CTRL_TX_NCHANNELS         B100_REG_CTRL_TX_ADDR(0) +#define B100_REG_CTRL_TX_CLEAR_UNDERRUN    B100_REG_CTRL_TX_ADDR(1) +#define B100_REG_CTRL_TX_REPORT_SID        B100_REG_CTRL_TX_ADDR(2) +#define B100_REG_CTRL_TX_POLICY            B100_REG_CTRL_TX_ADDR(3) + +#define B100_FLAG_CTRL_TX_POLICY_WAIT          (0x1 << 0) +#define B100_FLAG_CTRL_TX_POLICY_NEXT_PACKET   (0x1 << 1) +#define B100_FLAG_CTRL_TX_POLICY_NEXT_BURST    (0x1 << 2) + +///////////////////////////////////////////////// +// VITA49 64 bit time (write only) +//////////////////////////////////////////////// +  /*! +   * \brief Time 64 flags +   * +   * <pre> +   * +   *    3                   2                   1 +   *  1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 +   * +-----------------------------------------------------------+-+-+ +   * |                                                           |S|P| +   * +-----------------------------------------------------------+-+-+ +   * +   * P - PPS edge selection (0=negedge, 1=posedge, default=0) +   * S - Source (0=sma, 1=mimo, 0=default) +   * +   * </pre> +   */ +#define B100_REG_TIME64_ADDR(n)     (B100_REG_SETTINGS_BASE_ADDR(40) + (4*(n))) +#define B100_REG_TIME64_SECS  B100_REG_TIME64_ADDR(0)  // value to set absolute secs to on next PPS +#define B100_REG_TIME64_TICKS B100_REG_TIME64_ADDR(1)  // value to set absolute ticks to on next PPS +#define B100_REG_TIME64_FLAGS B100_REG_TIME64_ADDR(2)  // flags - see chart above +#define B100_REG_TIME64_IMM   B100_REG_TIME64_ADDR(3)  // set immediate (0=latch on next pps, 1=latch immediate, default=0) +#define B100_REG_TIME64_TPS   B100_REG_TIME64_ADDR(4)  // clock ticks per second (counter rollover) + +//pps flags (see above) +#define B100_FLAG_TIME64_PPS_NEGEDGE (0 << 0) +#define B100_FLAG_TIME64_PPS_POSEDGE (1 << 0) +#define B100_FLAG_TIME64_PPS_SMA     (0 << 1) +#define B100_FLAG_TIME64_PPS_MIMO    (1 << 1) + +#define B100_FLAG_TIME64_LATCH_NOW 1 +#define B100_FLAG_TIME64_LATCH_NEXT_PPS 0 + +#define B100_REG_CLEAR_RX_FIFO     B100_REG_SETTINGS_BASE_ADDR(48) +#define B100_REG_CLEAR_TX_FIFO     B100_REG_SETTINGS_BASE_ADDR(49) + +#define B100_REG_GLOBAL_RESET      B100_REG_SETTINGS_BASE_ADDR(50) +#define B100_REG_TEST32            B100_REG_SETTINGS_BASE_ADDR(52) + +#endif + diff --git a/host/lib/usrp/usrp_b100/clock_ctrl.cpp b/host/lib/usrp/usrp_b100/clock_ctrl.cpp new file mode 100644 index 000000000..e138242d1 --- /dev/null +++ b/host/lib/usrp/usrp_b100/clock_ctrl.cpp @@ -0,0 +1,525 @@ +// +// Copyright 2010-2011 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// 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 "clock_ctrl.hpp" +#include "ad9522_regs.hpp" +#include <uhd/utils/log.hpp> +#include <uhd/utils/msg.hpp> +#include <uhd/exception.hpp> +#include <uhd/utils/assert_has.hpp> +#include <uhd/utils/safe_call.hpp> +#include <boost/cstdint.hpp> +#include "b100_regs.hpp" //spi slave constants +#include <boost/assign/list_of.hpp> +#include <boost/foreach.hpp> +#include <boost/format.hpp> +#include <boost/thread/thread.hpp> +#include <boost/math/common_factor_rt.hpp> //gcd +#include <algorithm> +#include <utility> + +using namespace uhd; + +/*********************************************************************** + * Constants + **********************************************************************/ +static const bool ENABLE_THE_TEST_OUT = true; +static const double REFERENCE_INPUT_RATE = 10e6; + +/*********************************************************************** + * Helpers + **********************************************************************/ +template <typename div_type, typename bypass_type> static void set_clock_divider( +    size_t divider, div_type &low, div_type &high, bypass_type &bypass +){ +    high = divider/2 - 1; +    low = divider - high - 2; +    bypass = (divider == 1)? 1 : 0; +} + +/*********************************************************************** + * Clock rate calculation stuff: + *   Using the internal VCO between 1400 and 1800 MHz + **********************************************************************/ +struct clock_settings_type{ +    size_t ref_clock_doubler, r_counter, a_counter, b_counter, prescaler, vco_divider, chan_divider; +    size_t get_n_counter(void) const{return prescaler * b_counter + a_counter;} +    double get_ref_rate(void) const{return REFERENCE_INPUT_RATE * ref_clock_doubler;} +    double get_vco_rate(void) const{return get_ref_rate()/r_counter * get_n_counter();} +    double get_chan_rate(void) const{return get_vco_rate()/vco_divider;} +    double get_out_rate(void) const{return get_chan_rate()/chan_divider;} +    std::string to_pp_string(void) const{ +        return str(boost::format( +            "  r_counter: %d\n" +            "  a_counter: %d\n" +            "  b_counter: %d\n" +            "  prescaler: %d\n" +            "  vco_divider: %d\n" +            "  chan_divider: %d\n" +            "  vco_rate: %fMHz\n" +            "  chan_rate: %fMHz\n" +            "  out_rate: %fMHz\n" +            ) +            % r_counter +            % a_counter +            % b_counter +            % prescaler +            % vco_divider +            % chan_divider +            % (get_vco_rate()/1e6) +            % (get_chan_rate()/1e6) +            % (get_out_rate()/1e6) +        ); +    } +}; + +//! gives the greatest divisor of num between 1 and max inclusive +template<typename T> static inline T greatest_divisor(T num, T max){ +    for (T i = max; i > 1; i--) if (num%i == 0) return i; return 1; +} + +//! gives the least divisor of num between min and num exclusive +template<typename T> static inline T least_divisor(T num, T min){ +    for (T i = min; i < num; i++) if (num%i == 0) return i; return 1; +} + +static clock_settings_type get_clock_settings(double rate){ +    clock_settings_type cs; +    cs.ref_clock_doubler = 2; //always doubling +    cs.prescaler = 8; //set to 8 when input is under 2400 MHz + +    //basic formulas used below: +    //out_rate*X = ref_rate*Y +    //X = i*ref_rate/gcd +    //Y = i*out_rate/gcd +    //X = chan_div * vco_div * R +    //Y = P*B + A + +    const boost::uint64_t out_rate = boost::uint64_t(rate); +    const boost::uint64_t ref_rate = boost::uint64_t(cs.get_ref_rate()); +    const size_t gcd = size_t(boost::math::gcd(ref_rate, out_rate)); + +    for (size_t i = 1; i <= 100; i++){ +        const size_t X = i*ref_rate/gcd; +        const size_t Y = i*out_rate/gcd; + +        //determine A and B (P is fixed) +        cs.b_counter = Y/cs.prescaler; +        cs.a_counter = Y - cs.b_counter*cs.prescaler; + +        static const double vco_bound_pad = 100e6; +        for ( //calculate an r divider that fits into the bounds of the vco +            cs.r_counter  = size_t(cs.get_n_counter()*cs.get_ref_rate()/(1800e6 - vco_bound_pad)); +            cs.r_counter <= size_t(cs.get_n_counter()*cs.get_ref_rate()/(1400e6 + vco_bound_pad)) +            and cs.r_counter > 0; cs.r_counter++ +        ){ + +            //determine chan_div and vco_div +            //and fill in that order of preference +            cs.chan_divider = greatest_divisor<size_t>(X/cs.r_counter, 32); +            cs.vco_divider = greatest_divisor<size_t>(X/cs.chan_divider/cs.r_counter, 6); + +            //avoid a vco divider of 1 (if possible) +            if (cs.vco_divider == 1){ +                cs.vco_divider = least_divisor<size_t>(cs.chan_divider, 2); +                cs.chan_divider /= cs.vco_divider; +            } + +            UHD_LOGV(always) +                << "gcd " << gcd << std::endl +                << "X " << X << std::endl +                << "Y " << Y << std::endl +                << cs.to_pp_string() << std::endl +            ; + +            //filter limits on the counters +            if (cs.vco_divider == 1) continue; +            if (cs.r_counter >= (1<<14)) continue; +            if (cs.b_counter == 2) continue; +            if (cs.b_counter == 1 and cs.a_counter != 0) continue; +            if (cs.b_counter >= (1<<13)) continue; +            if (cs.a_counter >= (1<<6)) continue; + +            UHD_MSG(status) << "USRP-B100 clock control: " << i << std::endl << cs.to_pp_string() << std::endl; +            return cs; +        } +    } + +    throw uhd::runtime_error(str(boost::format( +        "USRP-B100 clock control: could not calculate settings for clock rate %fMHz" +    ) % (rate/1e6))); +} + +/*********************************************************************** + * Clock Control Implementation + **********************************************************************/ +class b100_clock_ctrl_impl : public b100_clock_ctrl{ +public: +    b100_clock_ctrl_impl(b100_iface::sptr iface, double master_clock_rate){ +        _iface = iface; +        _chan_rate = 0.0; +        _out_rate = 0.0; + +        //init the clock gen registers +        _ad9522_regs.sdo_active = ad9522_regs_t::SDO_ACTIVE_SDO_SDIO; +        _ad9522_regs.enb_stat_eeprom_at_stat_pin = 0; //use status pin +        _ad9522_regs.status_pin_control = 0x1; //n divider +        _ad9522_regs.ld_pin_control = 0x00; //dld +        _ad9522_regs.refmon_pin_control = 0x12; //show ref2 +        _ad9522_regs.lock_detect_counter = ad9522_regs_t::LOCK_DETECT_COUNTER_16CYC; + +        this->use_internal_ref(); + +        this->set_fpga_clock_rate(master_clock_rate); //initialize to something + +        this->enable_fpga_clock(true); +        this->enable_test_clock(ENABLE_THE_TEST_OUT); +        this->enable_rx_dboard_clock(false); +        this->enable_tx_dboard_clock(false); +    } + +    ~b100_clock_ctrl_impl(void){ +        UHD_SAFE_CALL( +            this->enable_test_clock(ENABLE_THE_TEST_OUT); +            this->enable_rx_dboard_clock(false); +            this->enable_tx_dboard_clock(false); +            //this->enable_fpga_clock(false); //FIXME +        ) +    } + +    /*********************************************************************** +     * Clock rate control: +     *  - set clock rate w/ internal VCO +     *  - set clock rate w/ external VCXO +     **********************************************************************/ +    void set_clock_settings_with_internal_vco(double rate){ +        const clock_settings_type cs = get_clock_settings(rate); + +        //set the rates to private variables so the implementation knows! +        _chan_rate = cs.get_chan_rate(); +        _out_rate = cs.get_out_rate(); + +        _ad9522_regs.enable_clock_doubler = (cs.ref_clock_doubler == 2)? 1 : 0; + +        _ad9522_regs.set_r_counter(cs.r_counter); +        _ad9522_regs.a_counter = cs.a_counter; +        _ad9522_regs.set_b_counter(cs.b_counter); +        UHD_ASSERT_THROW(cs.prescaler == 8); //assumes this below: +        _ad9522_regs.prescaler_p = ad9522_regs_t::PRESCALER_P_DIV8_9; + +        _ad9522_regs.pll_power_down = ad9522_regs_t::PLL_POWER_DOWN_NORMAL; +        _ad9522_regs.cp_current = ad9522_regs_t::CP_CURRENT_1_2MA; + +        _ad9522_regs.bypass_vco_divider = 0; +        switch(cs.vco_divider){ +        case 1: _ad9522_regs.vco_divider = ad9522_regs_t::VCO_DIVIDER_DIV1; break; +        case 2: _ad9522_regs.vco_divider = ad9522_regs_t::VCO_DIVIDER_DIV2; break; +        case 3: _ad9522_regs.vco_divider = ad9522_regs_t::VCO_DIVIDER_DIV3; break; +        case 4: _ad9522_regs.vco_divider = ad9522_regs_t::VCO_DIVIDER_DIV4; break; +        case 5: _ad9522_regs.vco_divider = ad9522_regs_t::VCO_DIVIDER_DIV5; break; +        case 6: _ad9522_regs.vco_divider = ad9522_regs_t::VCO_DIVIDER_DIV6; break; +        } +        _ad9522_regs.select_vco_or_clock = ad9522_regs_t::SELECT_VCO_OR_CLOCK_VCO; + +        //setup fpga master clock +        _ad9522_regs.out0_format = ad9522_regs_t::OUT0_FORMAT_LVDS; +        set_clock_divider(cs.chan_divider, +            _ad9522_regs.divider0_low_cycles, +            _ad9522_regs.divider0_high_cycles, +            _ad9522_regs.divider0_bypass +        ); + +        //setup codec clock +        _ad9522_regs.out3_format = ad9522_regs_t::OUT3_FORMAT_LVDS; +        set_clock_divider(cs.chan_divider, +            _ad9522_regs.divider1_low_cycles, +            _ad9522_regs.divider1_high_cycles, +            _ad9522_regs.divider1_bypass +        ); + +        this->send_all_regs(); +        calibrate_now(); +    } + +    void set_clock_settings_with_external_vcxo(double rate){ +        //set the rates to private variables so the implementation knows! +        _chan_rate = rate; +        _out_rate = rate; + +        _ad9522_regs.enable_clock_doubler = 1; //doubler always on +        const double ref_rate = REFERENCE_INPUT_RATE*2; + +        //bypass prescaler such that N = B +        long gcd = boost::math::gcd(long(ref_rate), long(rate)); +        _ad9522_regs.set_r_counter(int(ref_rate/gcd)); +        _ad9522_regs.a_counter = 0; +        _ad9522_regs.set_b_counter(int(rate/gcd)); +        _ad9522_regs.prescaler_p = ad9522_regs_t::PRESCALER_P_DIV1; + +        //setup external vcxo +        _ad9522_regs.pll_power_down = ad9522_regs_t::PLL_POWER_DOWN_NORMAL; +        _ad9522_regs.cp_current = ad9522_regs_t::CP_CURRENT_1_2MA; +        _ad9522_regs.bypass_vco_divider = 1; +        _ad9522_regs.select_vco_or_clock = ad9522_regs_t::SELECT_VCO_OR_CLOCK_EXTERNAL; + +        //setup fpga master clock +        _ad9522_regs.out0_format = ad9522_regs_t::OUT0_FORMAT_LVDS; +        _ad9522_regs.divider0_bypass = 1; + +        //setup codec clock +        _ad9522_regs.out3_format = ad9522_regs_t::OUT3_FORMAT_LVDS; +        _ad9522_regs.divider1_bypass = 1; + +        this->send_all_regs(); +    } + +    void set_fpga_clock_rate(double rate){ +        if (_out_rate == rate) return; +        if (rate == 61.44e6) set_clock_settings_with_external_vcxo(rate); +        else                 set_clock_settings_with_internal_vco(rate); +        //clock rate changed! update dboard clocks and FPGA ticks per second +        set_rx_dboard_clock_rate(rate); +        set_tx_dboard_clock_rate(rate); +        _iface->poke32(B100_REG_TIME64_TPS, boost::uint32_t(get_fpga_clock_rate())); +    } + +    double get_fpga_clock_rate(void){ +        return this->_out_rate; +    } + +    /*********************************************************************** +     * FPGA clock enable +     **********************************************************************/ +    void enable_fpga_clock(bool enb){ +        _ad9522_regs.out0_format = ad9522_regs_t::OUT0_FORMAT_LVDS; +        _ad9522_regs.out0_lvds_power_down = !enb; +        this->send_reg(0x0F0); +        this->latch_regs(); +    } + +    /*********************************************************************** +     * Special test clock output +     **********************************************************************/ +    void enable_test_clock(bool enb){ +        //setup test clock (same divider as codec clock) +        _ad9522_regs.out4_format = ad9522_regs_t::OUT4_FORMAT_CMOS; +        _ad9522_regs.out4_cmos_configuration = (enb)? +            ad9522_regs_t::OUT4_CMOS_CONFIGURATION_A_ON : +            ad9522_regs_t::OUT4_CMOS_CONFIGURATION_OFF; +        this->send_reg(0x0F4); +        this->latch_regs(); +    } + +    /*********************************************************************** +     * RX Dboard Clock Control (output 9, divider 3) +     **********************************************************************/ +    void enable_rx_dboard_clock(bool enb){ +        _ad9522_regs.out9_format = ad9522_regs_t::OUT9_FORMAT_CMOS; +        _ad9522_regs.out9_cmos_configuration = (enb)? +            ad9522_regs_t::OUT9_CMOS_CONFIGURATION_B_ON : +            ad9522_regs_t::OUT9_CMOS_CONFIGURATION_OFF; +        this->send_reg(0x0F9); +        this->latch_regs(); +    } + +    std::vector<double> get_rx_dboard_clock_rates(void){ +        std::vector<double> rates; +        for(size_t div = 1; div <= 16+16; div++) +            rates.push_back(this->_chan_rate/div); +        return rates; +    } + +    void set_rx_dboard_clock_rate(double rate){ +        assert_has(get_rx_dboard_clock_rates(), rate, "rx dboard clock rate"); +        _rx_clock_rate = rate; +        size_t divider = size_t(this->_chan_rate/rate); +        //set the divider registers +        set_clock_divider(divider, +            _ad9522_regs.divider3_low_cycles, +            _ad9522_regs.divider3_high_cycles, +            _ad9522_regs.divider3_bypass +        ); +        this->send_reg(0x199); +        this->send_reg(0x19a); +        this->soft_sync(); +    } + +    double get_rx_clock_rate(void){ +        return _rx_clock_rate; +    } + +    /*********************************************************************** +     * TX Dboard Clock Control (output 6, divider 2) +     **********************************************************************/ +    void enable_tx_dboard_clock(bool enb){ +        _ad9522_regs.out6_format = ad9522_regs_t::OUT6_FORMAT_CMOS; +        _ad9522_regs.out6_cmos_configuration = (enb)? +            ad9522_regs_t::OUT6_CMOS_CONFIGURATION_B_ON : +            ad9522_regs_t::OUT6_CMOS_CONFIGURATION_OFF; +        this->send_reg(0x0F6); +        this->latch_regs(); +    } + +    std::vector<double> get_tx_dboard_clock_rates(void){ +        return get_rx_dboard_clock_rates(); //same master clock, same dividers... +    } + +    void set_tx_dboard_clock_rate(double rate){ +        assert_has(get_tx_dboard_clock_rates(), rate, "tx dboard clock rate"); +        _tx_clock_rate = rate; +        size_t divider = size_t(this->_chan_rate/rate); +        //set the divider registers +        set_clock_divider(divider, +            _ad9522_regs.divider2_low_cycles, +            _ad9522_regs.divider2_high_cycles, +            _ad9522_regs.divider2_bypass +        ); +        this->send_reg(0x196); +        this->send_reg(0x197); +        this->soft_sync(); +    } + +    double get_tx_clock_rate(void){ +        return _tx_clock_rate; +    } + +    /*********************************************************************** +     * Clock reference control +     **********************************************************************/ +    void use_internal_ref(void) { +        _ad9522_regs.enable_ref2 = 1; +        _ad9522_regs.enable_ref1 = 0; +        _ad9522_regs.select_ref = ad9522_regs_t::SELECT_REF_REF2; +        _ad9522_regs.enb_auto_ref_switchover = ad9522_regs_t::ENB_AUTO_REF_SWITCHOVER_MANUAL; +        this->send_reg(0x01C); +        this->latch_regs(); +    } + +    void use_external_ref(void) { +        _ad9522_regs.enable_ref2 = 0; +        _ad9522_regs.enable_ref1 = 1; +        _ad9522_regs.select_ref = ad9522_regs_t::SELECT_REF_REF1; +        _ad9522_regs.enb_auto_ref_switchover = ad9522_regs_t::ENB_AUTO_REF_SWITCHOVER_MANUAL; +        this->send_reg(0x01C); +        this->latch_regs(); +    } + +    void use_auto_ref(void) { +        _ad9522_regs.enable_ref2 = 1; +        _ad9522_regs.enable_ref1 = 1; +        _ad9522_regs.select_ref = ad9522_regs_t::SELECT_REF_REF1; +        _ad9522_regs.enb_auto_ref_switchover = ad9522_regs_t::ENB_AUTO_REF_SWITCHOVER_AUTO; +        this->send_reg(0x01C); +        this->latch_regs(); +    } + +private: +    b100_iface::sptr _iface; +    ad9522_regs_t _ad9522_regs; +    double _out_rate; //rate at the fpga and codec +    double _chan_rate; //rate before final dividers +    double _rx_clock_rate, _tx_clock_rate; + +    void latch_regs(void){ +        _ad9522_regs.io_update = 1; +        this->send_reg(0x232); +    } + +    void send_reg(boost::uint16_t addr){ +        boost::uint32_t reg = _ad9522_regs.get_write_reg(addr); +        UHD_LOGV(often) << "clock control write reg: " << std::hex << reg << std::endl; +        byte_vector_t buf; +        buf.push_back(boost::uint8_t(reg >> 16)); +        buf.push_back(boost::uint8_t(reg >> 8)); +        buf.push_back(boost::uint8_t(reg & 0xff)); + +        _iface->get_fx2_i2c_iface().write_i2c(0x5C, buf); +    } + +    boost::uint8_t read_reg(boost::uint16_t addr){ +        byte_vector_t buf; +        buf.push_back(boost::uint8_t(addr >> 8)); +        buf.push_back(boost::uint8_t(addr & 0xff)); +        _iface->get_fx2_i2c_iface().write_i2c(0x5C, buf); + +        buf = _iface->get_fx2_i2c_iface().read_i2c(0x5C, 1); + +        return boost::uint32_t(buf[0] & 0xFF); +    } + +    void calibrate_now(void){ +        //vco calibration routine: +        _ad9522_regs.vco_calibration_now = 0; +        this->send_reg(0x18); +        this->latch_regs(); +        _ad9522_regs.vco_calibration_now = 1; +        this->send_reg(0x18); +        this->latch_regs(); +        //wait for calibration done: +        static const boost::uint8_t addr = 0x01F; +        for (size_t ms10 = 0; ms10 < 100; ms10++){ +            boost::this_thread::sleep(boost::posix_time::milliseconds(10)); +            boost::uint32_t reg = read_reg(addr); +            _ad9522_regs.set_reg(addr, reg); +            if (_ad9522_regs.vco_calibration_finished) goto wait_for_ld; +        } +        UHD_MSG(error) << "USRP-B100 clock control: VCO calibration timeout" << std::endl; +        wait_for_ld: +        //wait for digital lock detect: +        for (size_t ms10 = 0; ms10 < 100; ms10++){ +            boost::this_thread::sleep(boost::posix_time::milliseconds(10)); +            boost::uint32_t reg = read_reg(addr); +            _ad9522_regs.set_reg(addr, reg); +            if (_ad9522_regs.digital_lock_detect) return; +        } +        UHD_MSG(error) << "USRP-B100 clock control: lock detection timeout" << std::endl; +    } + +    void soft_sync(void){ +        _ad9522_regs.soft_sync = 1; +        this->send_reg(0x230); +        this->latch_regs(); +        _ad9522_regs.soft_sync = 0; +        this->send_reg(0x230); +        this->latch_regs(); +    } + +    void send_all_regs(void){ +        //setup a list of register ranges to write +        typedef std::pair<boost::uint16_t, boost::uint16_t> range_t; +        static const std::vector<range_t> ranges = boost::assign::list_of +            (range_t(0x000, 0x000)) (range_t(0x010, 0x01F)) +            (range_t(0x0F0, 0x0FD)) (range_t(0x190, 0x19B)) +            (range_t(0x1E0, 0x1E1)) (range_t(0x230, 0x230)) +        ; + +        //write initial register values and latch/update +        BOOST_FOREACH(const range_t &range, ranges){ +            for(boost::uint16_t addr = range.first; addr <= range.second; addr++){ +                this->send_reg(addr); +            } +        } +        this->latch_regs(); +    } +}; + +/*********************************************************************** + * Clock Control Make + **********************************************************************/ +b100_clock_ctrl::sptr b100_clock_ctrl::make(b100_iface::sptr iface, double master_clock_rate){ +    return sptr(new b100_clock_ctrl_impl(iface, master_clock_rate)); +} diff --git a/host/lib/usrp/usrp_b100/clock_ctrl.hpp b/host/lib/usrp/usrp_b100/clock_ctrl.hpp new file mode 100644 index 000000000..2a2e74024 --- /dev/null +++ b/host/lib/usrp/usrp_b100/clock_ctrl.hpp @@ -0,0 +1,118 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_B100_CLOCK_CTRL_HPP +#define INCLUDED_B100_CLOCK_CTRL_HPP + +#include "b100_iface.hpp" +#include <boost/shared_ptr.hpp> +#include <boost/utility.hpp> +#include <vector> + +/*! + * The usrp-e clock control: + * - Setup system clocks. + * - Disable/enable clock lines. + */ +class b100_clock_ctrl : boost::noncopyable{ +public: +    typedef boost::shared_ptr<b100_clock_ctrl> sptr; + +    /*! +     * Make a new clock control object. +     * \param iface the b100 iface object +     * \param master_clock_rate the master FPGA/sample clock rate +     * \return the clock control object +     */ +    static sptr make(b100_iface::sptr iface, double master_clock_rate); + +    /*! +     * Set the rate of the fpga clock line. +     * Throws if rate is not valid. +     * \param rate the new rate in Hz +     */ +    virtual void set_fpga_clock_rate(double rate) = 0; + +    /*! +     * Get the rate of the fpga clock line. +     * \return the fpga clock rate in Hz +     */ +    virtual double get_fpga_clock_rate(void) = 0; + +    /*! +     * Get the possible rates of the rx dboard clock. +     * \return a vector of clock rates in Hz +     */ +    virtual std::vector<double> get_rx_dboard_clock_rates(void) = 0; + +    /*! +     * Get the possible rates of the tx dboard clock. +     * \return a vector of clock rates in Hz +     */ +    virtual std::vector<double> get_tx_dboard_clock_rates(void) = 0; + +    /*! +     * Set the rx dboard clock rate to a possible rate. +     * \param rate the new clock rate in Hz +     * \throw exception when rate cannot be achieved +     */ +    virtual void set_rx_dboard_clock_rate(double rate) = 0; + +    /*! +     * Set the tx dboard clock rate to a possible rate. +     * \param rate the new clock rate in Hz +     * \throw exception when rate cannot be achieved +     */ +    virtual void set_tx_dboard_clock_rate(double rate) = 0; +     +    /*! +     * Enable/disable the FPGA clock. +     * \param enb true to enable +     */ +     +    virtual void enable_fpga_clock(bool enb) = 0; + +    /*! +     * Enable/disable the rx dboard clock. +     * \param enb true to enable +     */ +    virtual void enable_rx_dboard_clock(bool enb) = 0; + +    /*! +     * Enable/disable the tx dboard clock. +     * \param enb true to enable +     */ +    virtual void enable_tx_dboard_clock(bool enb) = 0; +     +    /*! +     * Use the internal TCXO reference +     */ +    virtual void use_internal_ref(void) = 0; +     +    /*! +     * Use the external SMA reference +     */ +    virtual void use_external_ref(void) = 0; +     +    /*! +     * Use external if available, internal otherwise +     */ +    virtual void use_auto_ref(void) = 0; + +}; + +#endif /* INCLUDED_B100_CLOCK_CTRL_HPP */ diff --git a/host/lib/usrp/usrp_b100/codec_ctrl.cpp b/host/lib/usrp/usrp_b100/codec_ctrl.cpp new file mode 100644 index 000000000..4d118b68b --- /dev/null +++ b/host/lib/usrp/usrp_b100/codec_ctrl.cpp @@ -0,0 +1,283 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#include "codec_ctrl.hpp" +#include "ad9862_regs.hpp" +#include <uhd/types/dict.hpp> +#include <uhd/exception.hpp> +#include <uhd/utils/algorithm.hpp> +#include <uhd/utils/log.hpp> +#include <uhd/utils/safe_call.hpp> +#include <boost/cstdint.hpp> +#include <boost/tuple/tuple.hpp> +#include <boost/math/special_functions/round.hpp> +#include "b100_regs.hpp" //spi slave constants +#include <boost/assign/list_of.hpp> + +using namespace uhd; + +const gain_range_t b100_codec_ctrl::tx_pga_gain_range(-20, 0, double(0.1)); +const gain_range_t b100_codec_ctrl::rx_pga_gain_range(0, 20, 1); + +/*********************************************************************** + * Codec Control Implementation + **********************************************************************/ +class b100_codec_ctrl_impl : public b100_codec_ctrl{ +public: +    //structors +    b100_codec_ctrl_impl(b100_iface::sptr iface); +    ~b100_codec_ctrl_impl(void); + +    //aux adc and dac control +    double read_aux_adc(aux_adc_t which); +    void write_aux_dac(aux_dac_t which, double volts); + +    //pga gain control +    void set_tx_pga_gain(double); +    double get_tx_pga_gain(void); +    void set_rx_pga_gain(double, char); +    double get_rx_pga_gain(char); + +private: +    b100_iface::sptr _iface; +    ad9862_regs_t _ad9862_regs; +    void send_reg(boost::uint8_t addr); +    void recv_reg(boost::uint8_t addr); +}; + +/*********************************************************************** + * Codec Control Structors + **********************************************************************/ +b100_codec_ctrl_impl::b100_codec_ctrl_impl(b100_iface::sptr iface){ +    _iface = iface; + +    //soft reset +    _ad9862_regs.soft_reset = 1; +    this->send_reg(0); + +    //initialize the codec register settings +    _ad9862_regs.sdio_bidir = ad9862_regs_t::SDIO_BIDIR_SDIO_SDO; +    _ad9862_regs.lsb_first = ad9862_regs_t::LSB_FIRST_MSB; +    _ad9862_regs.soft_reset = 0; + +    //setup rx side of codec +    _ad9862_regs.byp_buffer_a = 1; +    _ad9862_regs.byp_buffer_b = 1; +    _ad9862_regs.buffer_a_pd = 1; +    _ad9862_regs.buffer_b_pd = 1; +    _ad9862_regs.mux_out = ad9862_regs_t::MUX_OUT_RX_MUX_MODE; //B100 uses interleaved RX->FPGA +    _ad9862_regs.rx_pga_a = 0;//0x1f;  //TODO bring under api control +    _ad9862_regs.rx_pga_b = 0;//0x1f;  //TODO bring under api control +    _ad9862_regs.rx_twos_comp = 1; +    _ad9862_regs.rx_hilbert = ad9862_regs_t::RX_HILBERT_DIS; + +    //setup tx side of codec +    _ad9862_regs.two_data_paths = ad9862_regs_t::TWO_DATA_PATHS_BOTH; +    _ad9862_regs.interleaved = ad9862_regs_t::INTERLEAVED_INTERLEAVED; +    _ad9862_regs.tx_retime = ad9862_regs_t::TX_RETIME_CLKOUT2; +    _ad9862_regs.tx_pga_gain = 199; //TODO bring under api control +    _ad9862_regs.tx_hilbert = ad9862_regs_t::TX_HILBERT_DIS; +    _ad9862_regs.interp = ad9862_regs_t::INTERP_2; +    _ad9862_regs.tx_twos_comp = 1; +    _ad9862_regs.fine_mode = ad9862_regs_t::FINE_MODE_BYPASS; +    _ad9862_regs.coarse_mod = ad9862_regs_t::COARSE_MOD_BYPASS; +    _ad9862_regs.dac_a_coarse_gain = 0x3; +    _ad9862_regs.dac_b_coarse_gain = 0x3; +    _ad9862_regs.edges = ad9862_regs_t::EDGES_NORMAL; + +    //setup the dll +    _ad9862_regs.input_clk_ctrl = ad9862_regs_t::INPUT_CLK_CTRL_EXTERNAL; +    _ad9862_regs.dll_mult = ad9862_regs_t::DLL_MULT_2; +    _ad9862_regs.dll_mode = ad9862_regs_t::DLL_MODE_FAST; + +    //write the register settings to the codec +    for (uint8_t addr = 0; addr <= 25; addr++){ +        this->send_reg(addr); +    } + +    //always start conversions for aux ADC +    _ad9862_regs.start_a = 1; +    _ad9862_regs.start_b = 1; + +    //aux adc clock +    _ad9862_regs.clk_4 = ad9862_regs_t::CLK_4_1_4; +    this->send_reg(34); +} + +b100_codec_ctrl_impl::~b100_codec_ctrl_impl(void){ +    UHD_SAFE_CALL( +        //set aux dacs to zero +        this->write_aux_dac(AUX_DAC_A, 0); +        this->write_aux_dac(AUX_DAC_B, 0); +        this->write_aux_dac(AUX_DAC_C, 0); +        this->write_aux_dac(AUX_DAC_D, 0); + +        //power down +        _ad9862_regs.all_rx_pd = 1; +        this->send_reg(1); +        _ad9862_regs.tx_digital_pd = 1; +        _ad9862_regs.tx_analog_pd = ad9862_regs_t::TX_ANALOG_PD_BOTH; +        this->send_reg(8); +    ) +} + +/*********************************************************************** + * Codec Control Gain Control Methods + **********************************************************************/ +static const int mtpgw = 255; //maximum tx pga gain word + +void b100_codec_ctrl_impl::set_tx_pga_gain(double gain){ +    int gain_word = int(mtpgw*(gain - tx_pga_gain_range.start())/(tx_pga_gain_range.stop() - tx_pga_gain_range.start())); +    _ad9862_regs.tx_pga_gain = uhd::clip(gain_word, 0, mtpgw); +    this->send_reg(16); +} + +double b100_codec_ctrl_impl::get_tx_pga_gain(void){ +    return (_ad9862_regs.tx_pga_gain*(tx_pga_gain_range.stop() - tx_pga_gain_range.start())/mtpgw) + tx_pga_gain_range.start(); +} + +static const int mrpgw = 0x14; //maximum rx pga gain word + +void b100_codec_ctrl_impl::set_rx_pga_gain(double gain, char which){ +    int gain_word = int(mrpgw*(gain - rx_pga_gain_range.start())/(rx_pga_gain_range.stop() - rx_pga_gain_range.start())); +    gain_word = uhd::clip(gain_word, 0, mrpgw); +    switch(which){ +    case 'A': +        _ad9862_regs.rx_pga_a = gain_word; +        this->send_reg(2); +        return; +    case 'B': +        _ad9862_regs.rx_pga_b = gain_word; +        this->send_reg(3); +        return; +    default: UHD_THROW_INVALID_CODE_PATH(); +    } +} + +double b100_codec_ctrl_impl::get_rx_pga_gain(char which){ +    int gain_word; +    switch(which){ +    case 'A': gain_word = _ad9862_regs.rx_pga_a; break; +    case 'B': gain_word = _ad9862_regs.rx_pga_b; break; +    default: UHD_THROW_INVALID_CODE_PATH(); +    } +    return (gain_word*(rx_pga_gain_range.stop() - rx_pga_gain_range.start())/mrpgw) + rx_pga_gain_range.start(); +} + +/*********************************************************************** + * Codec Control AUX ADC Methods + **********************************************************************/ +static double aux_adc_to_volts(boost::uint8_t high, boost::uint8_t low){ +    return double((boost::uint16_t(high) << 2) | low)*3.3/0x3ff; +} + +double b100_codec_ctrl_impl::read_aux_adc(aux_adc_t which){ +    switch(which){ + +    case AUX_ADC_A1: +        _ad9862_regs.select_a = ad9862_regs_t::SELECT_A_AUX_ADC1; +        this->send_reg(34); //start conversion and select mux +        this->recv_reg(28); //read the value (2 bytes, 2 reads) +        this->recv_reg(29); +        return aux_adc_to_volts(_ad9862_regs.aux_adc_a1_9_2, _ad9862_regs.aux_adc_a1_1_0); +    case AUX_ADC_A2: +        _ad9862_regs.select_a = ad9862_regs_t::SELECT_A_AUX_ADC2; +        this->send_reg(34); //start conversion and select mux +        this->recv_reg(26); //read the value (2 bytes, 2 reads) +        this->recv_reg(27); +        return aux_adc_to_volts(_ad9862_regs.aux_adc_a2_9_2, _ad9862_regs.aux_adc_a2_1_0); + +    case AUX_ADC_B1: +        _ad9862_regs.select_b = ad9862_regs_t::SELECT_B_AUX_ADC1; +        this->send_reg(34); //start conversion and select mux +        this->recv_reg(32); //read the value (2 bytes, 2 reads) +        this->recv_reg(33); +        return aux_adc_to_volts(_ad9862_regs.aux_adc_b1_9_2, _ad9862_regs.aux_adc_b1_1_0); +    case AUX_ADC_B2: +        _ad9862_regs.select_b = ad9862_regs_t::SELECT_B_AUX_ADC2; +        this->send_reg(34); //start conversion and select mux +        this->recv_reg(30); //read the value (2 bytes, 2 reads) +        this->recv_reg(31); +        return aux_adc_to_volts(_ad9862_regs.aux_adc_b2_9_2, _ad9862_regs.aux_adc_b2_1_0); +    } +    UHD_THROW_INVALID_CODE_PATH(); +} + +/*********************************************************************** + * Codec Control AUX DAC Methods + **********************************************************************/ +void b100_codec_ctrl_impl::write_aux_dac(aux_dac_t which, double volts){ +    //special case for aux dac d (aka sigma delta word) +    if (which == AUX_DAC_D){ +        boost::uint16_t dac_word = uhd::clip(boost::math::iround(volts*0xfff/3.3), 0, 0xfff); +        _ad9862_regs.sig_delt_11_4 = boost::uint8_t(dac_word >> 4); +        _ad9862_regs.sig_delt_3_0 = boost::uint8_t(dac_word & 0xf); +        this->send_reg(42); +        this->send_reg(43); +        return; +    } + +    //calculate the dac word for aux dac a, b, c +    boost::uint8_t dac_word = uhd::clip(boost::math::iround(volts*0xff/3.3), 0, 0xff); + +    //setup a lookup table for the aux dac params (reg ref, reg addr) +    typedef boost::tuple<boost::uint8_t*, boost::uint8_t> dac_params_t; +    uhd::dict<aux_dac_t, dac_params_t> aux_dac_to_params = boost::assign::map_list_of +        (AUX_DAC_A, dac_params_t(&_ad9862_regs.aux_dac_a, 36)) +        (AUX_DAC_B, dac_params_t(&_ad9862_regs.aux_dac_b, 37)) +        (AUX_DAC_C, dac_params_t(&_ad9862_regs.aux_dac_c, 38)) +    ; + +    //set the aux dac register +    UHD_ASSERT_THROW(aux_dac_to_params.has_key(which)); +    boost::uint8_t *reg_ref, reg_addr; +    boost::tie(reg_ref, reg_addr) = aux_dac_to_params[which]; +    *reg_ref = dac_word; +    this->send_reg(reg_addr); +} + +/*********************************************************************** + * Codec Control SPI Methods + **********************************************************************/ +void b100_codec_ctrl_impl::send_reg(boost::uint8_t addr){ +    boost::uint32_t reg = _ad9862_regs.get_write_reg(addr); +    UHD_LOGV(rarely) << "codec control write reg: " << std::hex << reg << std::endl; +    _iface->transact_spi( +        B100_SPI_SS_AD9862, +        spi_config_t::EDGE_RISE, +        reg, 16, false /*no rb*/ +    ); +} + +void b100_codec_ctrl_impl::recv_reg(boost::uint8_t addr){ +    boost::uint32_t reg = _ad9862_regs.get_read_reg(addr); +    UHD_LOGV(rarely) << "codec control read reg: " << std::hex << reg << std::endl; +    boost::uint32_t ret = _iface->transact_spi( +        B100_SPI_SS_AD9862, +        spi_config_t::EDGE_RISE, +        reg, 16, true /*rb*/ +    ); +    UHD_LOGV(rarely) << "codec control read ret: " << std::hex << boost::uint16_t(ret & 0xFF) << std::endl; +    _ad9862_regs.set_reg(addr, boost::uint8_t(ret&0xff)); +} + +/*********************************************************************** + * Codec Control Make + **********************************************************************/ +b100_codec_ctrl::sptr b100_codec_ctrl::make(b100_iface::sptr iface){ +    return sptr(new b100_codec_ctrl_impl(iface)); +} diff --git a/host/lib/usrp/usrp_b100/codec_ctrl.hpp b/host/lib/usrp/usrp_b100/codec_ctrl.hpp new file mode 100644 index 000000000..1bd579190 --- /dev/null +++ b/host/lib/usrp/usrp_b100/codec_ctrl.hpp @@ -0,0 +1,90 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_B100_CODEC_CTRL_HPP +#define INCLUDED_B100_CODEC_CTRL_HPP + +#include "b100_iface.hpp" +#include <uhd/types/ranges.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/utility.hpp> + +/*! + * The usrp-e codec control: + * - Init/power down codec. + * - Read aux adc, write aux dac. + */ +class b100_codec_ctrl : boost::noncopyable{ +public: +    typedef boost::shared_ptr<b100_codec_ctrl> sptr; + +    static const uhd::gain_range_t tx_pga_gain_range; +    static const uhd::gain_range_t rx_pga_gain_range; + +    /*! +     * Make a new codec control object. +     * \param iface the usrp_e iface object +     * \return the codec control object +     */ +    static sptr make(b100_iface::sptr iface); + +    //! aux adc identifier constants +    enum aux_adc_t{ +        AUX_ADC_A2 = 0xA2, +        AUX_ADC_A1 = 0xA1, +        AUX_ADC_B2 = 0xB2, +        AUX_ADC_B1 = 0xB1 +    }; + +    /*! +     * Read an auxiliary adc: +     * The internals remember which aux adc was read last. +     * Therefore, the aux adc switch is only changed as needed. +     * \param which which of the 4 adcs +     * \return a value in volts +     */ +    virtual double read_aux_adc(aux_adc_t which) = 0; + +    //! aux dac identifier constants +    enum aux_dac_t{ +        AUX_DAC_A = 0xA, +        AUX_DAC_B = 0xB, +        AUX_DAC_C = 0xC, +        AUX_DAC_D = 0xD //really the sigma delta output +    }; + +    /*! +     * Write an auxiliary dac. +     * \param which which of the 4 dacs +     * \param volts the level in in volts +     */ +    virtual void write_aux_dac(aux_dac_t which, double volts) = 0; + +    //! Set the TX PGA gain +    virtual void set_tx_pga_gain(double gain) = 0; + +    //! Get the TX PGA gain +    virtual double get_tx_pga_gain(void) = 0; + +    //! Set the RX PGA gain ('A' or 'B') +    virtual void set_rx_pga_gain(double gain, char which) = 0; + +    //! Get the RX PGA gain ('A' or 'B') +    virtual double get_rx_pga_gain(char which) = 0; +}; + +#endif /* INCLUDED_B100_CODEC_CTRL_HPP */ diff --git a/host/lib/usrp/usrp_b100/codec_impl.cpp b/host/lib/usrp/usrp_b100/codec_impl.cpp new file mode 100644 index 000000000..de3ca3a66 --- /dev/null +++ b/host/lib/usrp/usrp_b100/codec_impl.cpp @@ -0,0 +1,149 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#include "b100_impl.hpp" +#include <uhd/exception.hpp> +#include <uhd/usrp/codec_props.hpp> +#include <boost/bind.hpp> + +using namespace uhd; +using namespace uhd::usrp; + +/*********************************************************************** + * Helper Methods + **********************************************************************/ +void b100_impl::codec_init(void){ +    //make proxies +    _rx_codec_proxy = wax_obj_proxy::make( +        boost::bind(&b100_impl::rx_codec_get, this, _1, _2), +        boost::bind(&b100_impl::rx_codec_set, this, _1, _2) +    ); +    _tx_codec_proxy = wax_obj_proxy::make( +        boost::bind(&b100_impl::tx_codec_get, this, _1, _2), +        boost::bind(&b100_impl::tx_codec_set, this, _1, _2) +    ); +} + +/*********************************************************************** + * RX Codec Properties + **********************************************************************/ +static const std::string ad9862_pga_gain_name = "ad9862 pga"; + +void b100_impl::rx_codec_get(const wax::obj &key_, wax::obj &val){ +    named_prop_t key = named_prop_t::extract(key_); + +    //handle the get request conditioned on the key +    switch(key.as<codec_prop_t>()){ +    case CODEC_PROP_NAME: +        val = std::string("b100 adc - ad9522"); +        return; + +    case CODEC_PROP_OTHERS: +        val = prop_names_t(); +        return; + +    case CODEC_PROP_GAIN_NAMES: +        val = prop_names_t(1, ad9862_pga_gain_name); +        return; + +    case CODEC_PROP_GAIN_RANGE: +        UHD_ASSERT_THROW(key.name == ad9862_pga_gain_name); +        val = b100_codec_ctrl::rx_pga_gain_range; +        return; + +    case CODEC_PROP_GAIN_I: +        UHD_ASSERT_THROW(key.name == ad9862_pga_gain_name); +        val = _codec_ctrl->get_rx_pga_gain('A'); +        return; + +    case CODEC_PROP_GAIN_Q: +        UHD_ASSERT_THROW(key.name == ad9862_pga_gain_name); +        val = _codec_ctrl->get_rx_pga_gain('B'); +        return; + +    default: UHD_THROW_PROP_GET_ERROR(); +    } +} + +void b100_impl::rx_codec_set(const wax::obj &key_, const wax::obj &val){ +    named_prop_t key = named_prop_t::extract(key_); + +    //handle the set request conditioned on the key +    switch(key.as<codec_prop_t>()){ +    case CODEC_PROP_GAIN_I: +        UHD_ASSERT_THROW(key.name == ad9862_pga_gain_name); +        _codec_ctrl->set_rx_pga_gain(val.as<double>(), 'A'); +        return; + +    case CODEC_PROP_GAIN_Q: +        UHD_ASSERT_THROW(key.name == ad9862_pga_gain_name); +        _codec_ctrl->set_rx_pga_gain(val.as<double>(), 'B'); +        return; + +    default: UHD_THROW_PROP_SET_ERROR(); +    } +} + +/*********************************************************************** + * TX Codec Properties + **********************************************************************/ +void b100_impl::tx_codec_get(const wax::obj &key_, wax::obj &val){ +    named_prop_t key = named_prop_t::extract(key_); + +    //handle the get request conditioned on the key +    switch(key.as<codec_prop_t>()){ +    case CODEC_PROP_NAME: +        val = std::string("b100 dac - ad9522"); +        return; + +    case CODEC_PROP_OTHERS: +        val = prop_names_t(); +        return; + +    case CODEC_PROP_GAIN_NAMES: +        val = prop_names_t(1, ad9862_pga_gain_name); +        return; + +    case CODEC_PROP_GAIN_RANGE: +        UHD_ASSERT_THROW(key.name == ad9862_pga_gain_name); +        val = b100_codec_ctrl::tx_pga_gain_range; +        return; + +    case CODEC_PROP_GAIN_I: //only one gain for I and Q +    case CODEC_PROP_GAIN_Q: +        UHD_ASSERT_THROW(key.name == ad9862_pga_gain_name); +        val = _codec_ctrl->get_tx_pga_gain(); +        return; + +    default: UHD_THROW_PROP_GET_ERROR(); +    } +} + +void b100_impl::tx_codec_set(const wax::obj &key_, const wax::obj &val){ +    named_prop_t key = named_prop_t::extract(key_); + +    //handle the set request conditioned on the key +    switch(key.as<codec_prop_t>()){ +    case CODEC_PROP_GAIN_I: //only one gain for I and Q +    case CODEC_PROP_GAIN_Q: +        UHD_ASSERT_THROW(key.name == ad9862_pga_gain_name); +        _codec_ctrl->set_tx_pga_gain(val.as<double>()); +        return; + +    default: UHD_THROW_PROP_SET_ERROR(); +    } +} diff --git a/host/lib/usrp/usrp_b100/ctrl_packet.hpp b/host/lib/usrp/usrp_b100/ctrl_packet.hpp new file mode 100644 index 000000000..f504fc5aa --- /dev/null +++ b/host/lib/usrp/usrp_b100/ctrl_packet.hpp @@ -0,0 +1,75 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_CTRL_PACKET_HPP +#define INCLUDED_CTRL_PACKET_HPP + +#include <uhd/config.hpp> +#include <boost/cstdint.hpp> +#include <uhd/types/serial.hpp> + +typedef std::vector<boost::uint16_t> ctrl_data_t; + +/*! + * Control packet operation type + */ +enum ctrl_pkt_op_t {  +    CTRL_PKT_OP_WRITE = 1, +    CTRL_PKT_OP_READ = 2, +    CTRL_PKT_OP_READBACK = 3 +}; + +/*! + * Control packet transaction length + */ +const size_t CTRL_PACKET_LENGTH = 32; +const size_t CTRL_PACKET_HEADER_LENGTH = 8; +const size_t CTRL_PACKET_DATA_LENGTH = 24; //=length-header + +/*! + * Control packet header magic value + */ +const boost::uint8_t CTRL_PACKET_HEADER_MAGIC = 0xAA; + +/*!  + * Callback triggers for readback operation + */ +//FIXME: these are not real numbers, callbacks aren't implemented yet +const boost::uint16_t CTRL_PACKET_CALLBACK_SPI = 0x0001; +const boost::uint16_t CTRL_PACKET_CALLBACK_I2C = 0x0002; +//and so on + +/*! + * Metadata structure to describe a control packet + */ +struct UHD_API ctrl_pkt_meta_t { +    ctrl_pkt_op_t op; +    boost::uint8_t callbacks; +    boost::uint8_t seq; +    boost::uint16_t len; +    boost::uint32_t addr; +}; + +/*!  + * Full control packet structure + */ +struct UHD_API ctrl_pkt_t { +    ctrl_pkt_meta_t pkt_meta; +    ctrl_data_t data; +}; + +#endif /* INCLUDED_CTRL_PACKET_HPP */ diff --git a/host/lib/usrp/usrp_b100/dboard_iface.cpp b/host/lib/usrp/usrp_b100/dboard_iface.cpp new file mode 100644 index 000000000..ec3da6220 --- /dev/null +++ b/host/lib/usrp/usrp_b100/dboard_iface.cpp @@ -0,0 +1,298 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#include "b100_iface.hpp" +#include "b100_regs.hpp" +#include "clock_ctrl.hpp" +#include "codec_ctrl.hpp" +#include <uhd/usrp/dboard_iface.hpp> +#include <uhd/types/dict.hpp> +#include <uhd/exception.hpp> +#include <boost/assign/list_of.hpp> + + +using namespace uhd; +using namespace uhd::usrp; +using namespace boost::assign; + +class b100_dboard_iface : public dboard_iface{ +public: + +    b100_dboard_iface( +        b100_iface::sptr iface, +        b100_clock_ctrl::sptr clock, +        b100_codec_ctrl::sptr codec +    ){ +        _iface = iface; +        _clock = clock; +        _codec = codec; + +        //init the clock rate shadows +        this->set_clock_rate(UNIT_RX, _clock->get_fpga_clock_rate()); +        this->set_clock_rate(UNIT_TX, _clock->get_fpga_clock_rate()); + +        _iface->poke16(B100_REG_GPIO_RX_DBG, 0); +        _iface->poke16(B100_REG_GPIO_TX_DBG, 0); +    } + +    ~b100_dboard_iface(void){ +        /* NOP */ +    } + +    special_props_t get_special_props(void){ +        special_props_t props; +        props.soft_clock_divider = false; +        props.mangle_i2c_addrs = false; +        return props; +    } + +    void write_aux_dac(unit_t, aux_dac_t, double); +    double read_aux_adc(unit_t, aux_adc_t); + +    void _set_pin_ctrl(unit_t, boost::uint16_t); +    void _set_atr_reg(unit_t, atr_reg_t, boost::uint16_t); +    void _set_gpio_ddr(unit_t, boost::uint16_t); +    void _set_gpio_out(unit_t, boost::uint16_t); +    void set_gpio_debug(unit_t, int); +    boost::uint16_t read_gpio(unit_t); + +    void write_i2c(boost::uint8_t, const byte_vector_t &); +    byte_vector_t read_i2c(boost::uint8_t, size_t); + +    void write_spi( +        unit_t unit, +        const spi_config_t &config, +        boost::uint32_t data, +        size_t num_bits +    ); + +    boost::uint32_t read_write_spi( +        unit_t unit, +        const spi_config_t &config, +        boost::uint32_t data, +        size_t num_bits +    ); + +    void set_clock_rate(unit_t, double); +    std::vector<double> get_clock_rates(unit_t); +    double get_clock_rate(unit_t); +    void set_clock_enabled(unit_t, bool); +    double get_codec_rate(unit_t); + +private: +    b100_iface::sptr _iface; +    b100_clock_ctrl::sptr _clock; +    b100_codec_ctrl::sptr _codec; +    uhd::dict<unit_t, double> _clock_rates; +}; + +/*********************************************************************** + * Make Function + **********************************************************************/ +dboard_iface::sptr make_b100_dboard_iface( +    b100_iface::sptr iface, +    b100_clock_ctrl::sptr clock, +    b100_codec_ctrl::sptr codec +){ +    return dboard_iface::sptr(new b100_dboard_iface(iface, clock, codec)); +} + +/*********************************************************************** + * Clock Rates + **********************************************************************/ +void b100_dboard_iface::set_clock_rate(unit_t unit, double rate){ +    _clock_rates[unit] = rate; +    switch(unit){ +    case UNIT_RX: return _clock->set_rx_dboard_clock_rate(rate); +    case UNIT_TX: return _clock->set_tx_dboard_clock_rate(rate); +    } +} + +std::vector<double> b100_dboard_iface::get_clock_rates(unit_t unit){ +    switch(unit){ +    case UNIT_RX: return _clock->get_rx_dboard_clock_rates(); +    case UNIT_TX: return _clock->get_tx_dboard_clock_rates(); +    default: UHD_THROW_INVALID_CODE_PATH(); +    } +} + +double b100_dboard_iface::get_clock_rate(unit_t unit){ +    return _clock_rates[unit]; +} + +void b100_dboard_iface::set_clock_enabled(unit_t unit, bool enb){ +    switch(unit){ +    case UNIT_RX: return _clock->enable_rx_dboard_clock(enb); +    case UNIT_TX: return _clock->enable_tx_dboard_clock(enb); +    } +} + +double b100_dboard_iface::get_codec_rate(unit_t){ +    return _clock->get_fpga_clock_rate(); +} + +/*********************************************************************** + * GPIO + **********************************************************************/ +void b100_dboard_iface::_set_pin_ctrl(unit_t unit, boost::uint16_t value){ +    UHD_ASSERT_THROW(GPIO_SEL_ATR == 1); //make this assumption +    switch(unit){ +    case UNIT_RX: _iface->poke16(B100_REG_GPIO_RX_SEL, value); return; +    case UNIT_TX: _iface->poke16(B100_REG_GPIO_TX_SEL, value); return; +    } +} + +void b100_dboard_iface::_set_gpio_ddr(unit_t unit, boost::uint16_t value){ +    switch(unit){ +    case UNIT_RX: _iface->poke16(B100_REG_GPIO_RX_DDR, value); return; +    case UNIT_TX: _iface->poke16(B100_REG_GPIO_TX_DDR, value); return; +    } +} + +void b100_dboard_iface::_set_gpio_out(unit_t unit, boost::uint16_t value){ +    switch(unit){ +    case UNIT_RX: _iface->poke16(B100_REG_GPIO_RX_IO, value); return; +    case UNIT_TX: _iface->poke16(B100_REG_GPIO_TX_IO, value); return; +    } +} + +boost::uint16_t b100_dboard_iface::read_gpio(unit_t unit){ +    switch(unit){ +    case UNIT_RX: return _iface->peek16(B100_REG_GPIO_RX_IO); +    case UNIT_TX: return _iface->peek16(B100_REG_GPIO_TX_IO); +    default: UHD_THROW_INVALID_CODE_PATH(); +    } +} + +void b100_dboard_iface::_set_atr_reg(unit_t unit, atr_reg_t atr, boost::uint16_t value){ +    //define mapping of unit to atr regs to register address +    static const uhd::dict< +        unit_t, uhd::dict<atr_reg_t, boost::uint32_t> +    > unit_to_atr_to_addr = map_list_of +        (UNIT_RX, map_list_of +            (ATR_REG_IDLE,        B100_REG_ATR_IDLE_RXSIDE) +            (ATR_REG_TX_ONLY,     B100_REG_ATR_INTX_RXSIDE) +            (ATR_REG_RX_ONLY,     B100_REG_ATR_INRX_RXSIDE) +            (ATR_REG_FULL_DUPLEX, B100_REG_ATR_FULL_RXSIDE) +        ) +        (UNIT_TX, map_list_of +            (ATR_REG_IDLE,        B100_REG_ATR_IDLE_TXSIDE) +            (ATR_REG_TX_ONLY,     B100_REG_ATR_INTX_TXSIDE) +            (ATR_REG_RX_ONLY,     B100_REG_ATR_INRX_TXSIDE) +            (ATR_REG_FULL_DUPLEX, B100_REG_ATR_FULL_TXSIDE) +        ) +    ; +    _iface->poke16(unit_to_atr_to_addr[unit][atr], value); +} + +void b100_dboard_iface::set_gpio_debug(unit_t unit, int which){ +    //set this unit to all outputs +    this->set_gpio_ddr(unit, 0xffff); + +    //calculate the debug selections +    boost::uint32_t dbg_sels = 0x0; +    int sel = (which == 0)? GPIO_SEL_DEBUG_0 : GPIO_SEL_DEBUG_1; +    for(size_t i = 0; i < 16; i++) dbg_sels |= sel << i; + +    //set the debug on and which debug selection +    switch(unit){ +    case UNIT_RX: +        _iface->poke16(B100_REG_GPIO_RX_DBG, 0xffff); +        _iface->poke16(B100_REG_GPIO_RX_SEL, dbg_sels); +        return; + +    case UNIT_TX: +        _iface->poke16(B100_REG_GPIO_TX_DBG, 0xffff); +        _iface->poke16(B100_REG_GPIO_TX_SEL, dbg_sels); +        return; +    } +} + +/*********************************************************************** + * SPI + **********************************************************************/ +/*! + * Static function to convert a unit type to a spi slave device number. + * \param unit the dboard interface unit type enum + * \return the slave device number + */ +static boost::uint32_t unit_to_otw_spi_dev(dboard_iface::unit_t unit){ +    switch(unit){ +    case dboard_iface::UNIT_TX: return B100_SPI_SS_TX_DB; +    case dboard_iface::UNIT_RX: return B100_SPI_SS_RX_DB; +    } +    throw std::invalid_argument("unknown unit type"); +} + +void b100_dboard_iface::write_spi( +    unit_t unit, +    const spi_config_t &config, +    boost::uint32_t data, +    size_t num_bits +){ +    _iface->transact_spi(unit_to_otw_spi_dev(unit), config, data, num_bits, false /*no rb*/); +} + +boost::uint32_t b100_dboard_iface::read_write_spi( +    unit_t unit, +    const spi_config_t &config, +    boost::uint32_t data, +    size_t num_bits +){ +    return _iface->transact_spi(unit_to_otw_spi_dev(unit), config, data, num_bits, true /*rb*/); +} + +/*********************************************************************** + * I2C + **********************************************************************/ +void b100_dboard_iface::write_i2c(boost::uint8_t addr, const byte_vector_t &bytes){ +    return _iface->write_i2c(addr, bytes); +} + +byte_vector_t b100_dboard_iface::read_i2c(boost::uint8_t addr, size_t num_bytes){ +    return _iface->read_i2c(addr, num_bytes); +} + +/*********************************************************************** + * Aux DAX/ADC + **********************************************************************/ +void b100_dboard_iface::write_aux_dac(dboard_iface::unit_t, aux_dac_t which, double value){ +    //same aux dacs for each unit +    static const uhd::dict<aux_dac_t, b100_codec_ctrl::aux_dac_t> which_to_aux_dac = map_list_of +        (AUX_DAC_A, b100_codec_ctrl::AUX_DAC_A) +        (AUX_DAC_B, b100_codec_ctrl::AUX_DAC_B) +        (AUX_DAC_C, b100_codec_ctrl::AUX_DAC_C) +        (AUX_DAC_D, b100_codec_ctrl::AUX_DAC_D) +    ; +    _codec->write_aux_dac(which_to_aux_dac[which], value); +} + +double b100_dboard_iface::read_aux_adc(dboard_iface::unit_t unit, aux_adc_t which){ +    static const uhd::dict< +        unit_t, uhd::dict<aux_adc_t, b100_codec_ctrl::aux_adc_t> +    > unit_to_which_to_aux_adc = map_list_of +        (UNIT_RX, map_list_of +            (AUX_ADC_A, b100_codec_ctrl::AUX_ADC_A1) +            (AUX_ADC_B, b100_codec_ctrl::AUX_ADC_B1) +        ) +        (UNIT_TX, map_list_of +            (AUX_ADC_A, b100_codec_ctrl::AUX_ADC_A2) +            (AUX_ADC_B, b100_codec_ctrl::AUX_ADC_B2) +        ) +    ; +    return _codec->read_aux_adc(unit_to_which_to_aux_adc[unit][which]); +} diff --git a/host/lib/usrp/usrp_b100/dboard_impl.cpp b/host/lib/usrp/usrp_b100/dboard_impl.cpp new file mode 100644 index 000000000..ba3776728 --- /dev/null +++ b/host/lib/usrp/usrp_b100/dboard_impl.cpp @@ -0,0 +1,185 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#include "b100_impl.hpp" +#include "b100_regs.hpp" +#include <uhd/exception.hpp> +#include <uhd/usrp/dboard_props.hpp> +#include <uhd/usrp/subdev_props.hpp> +#include <uhd/usrp/misc_utils.hpp> +#include <boost/bind.hpp> +#include "usrp_i2c_addr.h" + +using namespace uhd; +using namespace uhd::usrp; + +/*********************************************************************** + * Dboard Initialization + **********************************************************************/ +void b100_impl::dboard_init(void){ +    //read the tx and rx dboard eeproms +    _rx_db_eeprom.load(*_iface, I2C_ADDR_RX_A); +    _tx_db_eeprom.load(*_iface, I2C_ADDR_TX_A); +    _gdb_eeprom.load(*_iface, I2C_ADDR_TX_A ^ 5); + +    //create a new dboard interface and manager +    _dboard_iface = make_b100_dboard_iface( +        _iface, _clock_ctrl, _codec_ctrl +    ); +    _dboard_manager = dboard_manager::make( +        _rx_db_eeprom.id, +        ((_gdb_eeprom.id == dboard_id_t::none())? _tx_db_eeprom : _gdb_eeprom).id, +        _dboard_iface +    ); + +    //setup the dboard proxies +    _rx_dboard_proxy = wax_obj_proxy::make( +        boost::bind(&b100_impl::rx_dboard_get, this, _1, _2), +        boost::bind(&b100_impl::rx_dboard_set, this, _1, _2) +    ); +    _tx_dboard_proxy = wax_obj_proxy::make( +        boost::bind(&b100_impl::tx_dboard_get, this, _1, _2), +        boost::bind(&b100_impl::tx_dboard_set, this, _1, _2) +    ); +} + +/*********************************************************************** + * RX Dboard Get + **********************************************************************/ +void b100_impl::rx_dboard_get(const wax::obj &key_, wax::obj &val){ +    named_prop_t key = named_prop_t::extract(key_); + +    //handle the get request conditioned on the key +    switch(key.as<dboard_prop_t>()){ +    case DBOARD_PROP_NAME: +        val = std::string("b100 dboard (rx unit)"); +        return; + +    case DBOARD_PROP_SUBDEV: +        val = _dboard_manager->get_rx_subdev(key.name); +        return; + +    case DBOARD_PROP_SUBDEV_NAMES: +        val = _dboard_manager->get_rx_subdev_names(); +        return; + +    case DBOARD_PROP_DBOARD_EEPROM: +        val = _rx_db_eeprom; +        return; + +    case DBOARD_PROP_DBOARD_IFACE: +        val = _dboard_iface; +        return; + +    case DBOARD_PROP_CODEC: +        val = _rx_codec_proxy->get_link(); +        return; + +    case DBOARD_PROP_GAIN_GROUP: +        val = make_gain_group( +            _rx_db_eeprom.id, +            _dboard_manager->get_rx_subdev(key.name), +            _rx_codec_proxy->get_link(), +            GAIN_GROUP_POLICY_RX +        ); +        return; + +    default: UHD_THROW_PROP_GET_ERROR(); +    } +} + +/*********************************************************************** + * RX Dboard Set + **********************************************************************/ +void b100_impl::rx_dboard_set(const wax::obj &key, const wax::obj &val){ +    switch(key.as<dboard_prop_t>()){ +    case DBOARD_PROP_DBOARD_EEPROM: +        _rx_db_eeprom = val.as<dboard_eeprom_t>(); +        _rx_db_eeprom.store(*_iface, I2C_ADDR_RX_A); +        return; + +    default: UHD_THROW_PROP_SET_ERROR(); +    } +} + +/*********************************************************************** + * TX Dboard Get + **********************************************************************/ +void b100_impl::tx_dboard_get(const wax::obj &key_, wax::obj &val){ +    named_prop_t key = named_prop_t::extract(key_); + +    //handle the get request conditioned on the key +    switch(key.as<dboard_prop_t>()){ +    case DBOARD_PROP_NAME: +        val = std::string("b100 dboard (tx unit)"); +        return; + +    case DBOARD_PROP_SUBDEV: +        val = _dboard_manager->get_tx_subdev(key.name); +        return; + +    case DBOARD_PROP_SUBDEV_NAMES: +        val = _dboard_manager->get_tx_subdev_names(); +        return; + +    case DBOARD_PROP_DBOARD_EEPROM: +        val = _tx_db_eeprom; +        return; + +    case DBOARD_PROP_GBOARD_EEPROM: +        val = _gdb_eeprom; +        return; + +    case DBOARD_PROP_DBOARD_IFACE: +        val = _dboard_iface; +        return; + +    case DBOARD_PROP_CODEC: +        val = _tx_codec_proxy->get_link(); +        return; + +    case DBOARD_PROP_GAIN_GROUP: +        val = make_gain_group( +            _tx_db_eeprom.id, +            _dboard_manager->get_tx_subdev(key.name), +            _tx_codec_proxy->get_link(), +            GAIN_GROUP_POLICY_TX +        ); +        return; + +    default: UHD_THROW_PROP_GET_ERROR(); +    } +} + +/*********************************************************************** + * TX Dboard Set + **********************************************************************/ +void b100_impl::tx_dboard_set(const wax::obj &key, const wax::obj &val){ +    switch(key.as<dboard_prop_t>()){ +    case DBOARD_PROP_DBOARD_EEPROM: +        _tx_db_eeprom = val.as<dboard_eeprom_t>(); +        _tx_db_eeprom.store(*_iface, I2C_ADDR_TX_A); +        return; + +    case DBOARD_PROP_GBOARD_EEPROM: +        _gdb_eeprom = val.as<dboard_eeprom_t>(); +        _gdb_eeprom.store(*_iface, I2C_ADDR_TX_A ^ 5); +        return; + +    default: UHD_THROW_PROP_SET_ERROR(); +    } +} diff --git a/host/lib/usrp/usrp_b100/dsp_impl.cpp b/host/lib/usrp/usrp_b100/dsp_impl.cpp new file mode 100644 index 000000000..c1bf6bedd --- /dev/null +++ b/host/lib/usrp/usrp_b100/dsp_impl.cpp @@ -0,0 +1,189 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#include "b100_impl.hpp" +#include "b100_regs.hpp" +#include <uhd/usrp/dsp_utils.hpp> +#include <uhd/usrp/dsp_props.hpp> +#include <boost/math/special_functions/round.hpp> +#include <boost/bind.hpp> + +#define rint boost::math::iround + +using namespace uhd; +using namespace uhd::usrp; + +static const double MASTER_CLOCK_RATE = 64e6; //TODO get from clock control + +/*********************************************************************** + * RX DDC Initialization + **********************************************************************/ +void b100_impl::rx_ddc_init(void){ +    _rx_ddc_proxy = wax_obj_proxy::make( +        boost::bind(&b100_impl::rx_ddc_get, this, _1, _2), +        boost::bind(&b100_impl::rx_ddc_set, this, _1, _2) +    ); + +    //initial config and update +    rx_ddc_set(DSP_PROP_FREQ_SHIFT, double(0)); +    rx_ddc_set(DSP_PROP_HOST_RATE, double(16e6)); +} + +/*********************************************************************** + * RX DDC Get + **********************************************************************/ +void b100_impl::rx_ddc_get(const wax::obj &key_, wax::obj &val){ +    named_prop_t key = named_prop_t::extract(key_); + +    switch(key.as<dsp_prop_t>()){ +    case DSP_PROP_NAME: +        val = std::string("USRP-B100 RX DSP"); +        return; + +    case DSP_PROP_OTHERS: +        val = prop_names_t(); //empty +        return; + +    case DSP_PROP_FREQ_SHIFT: +        val = _ddc_freq; +        return; + +    case DSP_PROP_CODEC_RATE: +        val = _clock_ctrl->get_fpga_clock_rate(); +        return; + +    case DSP_PROP_HOST_RATE: +        val = _clock_ctrl->get_fpga_clock_rate()/_ddc_decim; +        return; + +    default: UHD_THROW_PROP_GET_ERROR(); +    } +} + +/*********************************************************************** + * RX DDC Set + **********************************************************************/ +void b100_impl::rx_ddc_set(const wax::obj &key_, const wax::obj &val){ +    named_prop_t key = named_prop_t::extract(key_); + +    switch(key.as<dsp_prop_t>()){ +    case DSP_PROP_STREAM_CMD: +        issue_stream_cmd(val.as<stream_cmd_t>()); +        return; + +    case DSP_PROP_FREQ_SHIFT:{ +            double new_freq = val.as<double>(); +            _iface->poke32(B100_REG_DSP_RX_FREQ, +                dsp_type1::calc_cordic_word_and_update(new_freq, _clock_ctrl->get_fpga_clock_rate()) +            ); +            _ddc_freq = new_freq; //shadow +        } +        return; + +    case DSP_PROP_HOST_RATE:{ +            //set the decimation +            _ddc_decim = rint(_clock_ctrl->get_fpga_clock_rate()/val.as<double>()); +            _iface->poke32(B100_REG_DSP_RX_DECIM_RATE, dsp_type1::calc_cic_filter_word(_ddc_decim)); + +            //set the scaling +            static const boost::int16_t default_rx_scale_iq = 1024; +            _iface->poke32(B100_REG_DSP_RX_SCALE_IQ, +                dsp_type1::calc_iq_scale_word(default_rx_scale_iq, default_rx_scale_iq) +            ); +        } +        return; + +    default: UHD_THROW_PROP_SET_ERROR(); +    } +} + +/*********************************************************************** + * TX DUC Initialization + **********************************************************************/ +void b100_impl::tx_duc_init(void){ +    _tx_duc_proxy = wax_obj_proxy::make( +        boost::bind(&b100_impl::tx_duc_get, this, _1, _2), +        boost::bind(&b100_impl::tx_duc_set, this, _1, _2) +    ); + +    //initial config and update +    tx_duc_set(DSP_PROP_FREQ_SHIFT, double(0)); +    tx_duc_set(DSP_PROP_HOST_RATE, double(16e6)); +} + +/*********************************************************************** + * TX DUC Get + **********************************************************************/ +void b100_impl::tx_duc_get(const wax::obj &key_, wax::obj &val){ +    named_prop_t key = named_prop_t::extract(key_); + +    switch(key.as<dsp_prop_t>()){ +    case DSP_PROP_NAME: +        val = std::string("USRP-B100 TX DSP"); +        return; + +    case DSP_PROP_OTHERS: +        val = prop_names_t(); //empty +        return; + +    case DSP_PROP_FREQ_SHIFT: +        val = _duc_freq; +        return; + +    case DSP_PROP_CODEC_RATE: +        val = _clock_ctrl->get_fpga_clock_rate(); +        return; + +    case DSP_PROP_HOST_RATE: +        val = _clock_ctrl->get_fpga_clock_rate()/_duc_interp; +        return; + +    default: UHD_THROW_PROP_GET_ERROR(); +    } +} + +/*********************************************************************** + * TX DUC Set + **********************************************************************/ +void b100_impl::tx_duc_set(const wax::obj &key_, const wax::obj &val){ +    named_prop_t key = named_prop_t::extract(key_); + +    switch(key.as<dsp_prop_t>()){ + +    case DSP_PROP_FREQ_SHIFT:{ +            double new_freq = val.as<double>(); +            _iface->poke32(B100_REG_DSP_TX_FREQ, +                dsp_type1::calc_cordic_word_and_update(new_freq, _clock_ctrl->get_fpga_clock_rate()) +            ); +            _duc_freq = new_freq; //shadow +        } +        return; + +    case DSP_PROP_HOST_RATE:{ +            _duc_interp = rint(_clock_ctrl->get_fpga_clock_rate()/val.as<double>()); + +            //set the interpolation +            _iface->poke32(B100_REG_DSP_TX_INTERP_RATE, dsp_type1::calc_cic_filter_word(_duc_interp)); + +            //set the scaling +            _iface->poke32(B100_REG_DSP_TX_SCALE_IQ, dsp_type1::calc_iq_scale_word(_duc_interp)); +        } +        return; + +    default: UHD_THROW_PROP_SET_ERROR(); +    } +} diff --git a/host/lib/usrp/usrp_b100/io_impl.cpp b/host/lib/usrp/usrp_b100/io_impl.cpp new file mode 100644 index 000000000..3978bea75 --- /dev/null +++ b/host/lib/usrp/usrp_b100/io_impl.cpp @@ -0,0 +1,210 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#include "../../transport/super_recv_packet_handler.hpp" +#include "../../transport/super_send_packet_handler.hpp" +#include "usrp_commands.h" +#include "b100_impl.hpp" +#include "b100_regs.hpp" +#include <uhd/usrp/dsp_utils.hpp> +#include <uhd/usrp/dsp_props.hpp> +#include <uhd/utils/thread_priority.hpp> +#include <uhd/transport/bounded_buffer.hpp> +#include <boost/bind.hpp> +#include <boost/format.hpp> +#include <boost/asio.hpp> +#include <boost/bind.hpp> +#include <boost/thread.hpp> +#include <uhd/utils/msg.hpp> +#include <uhd/utils/log.hpp> +#include <iostream> + +using namespace uhd; +using namespace uhd::usrp; +using namespace uhd::transport; +namespace asio = boost::asio; + +/*********************************************************************** + * IO Implementation Details + **********************************************************************/ +struct b100_impl::io_impl{ +    io_impl(zero_copy_if::sptr data_transport): +        data_transport(data_transport) +    { +        /* NOP */ +    } + +    ~io_impl(void){ +        //drain the rx buffs +        //while(data_transport->get_recv_buff().get() != NULL){ +                /* NOP */ +        //} +    } + +    zero_copy_if::sptr &data_transport; + +    sph::recv_packet_handler recv_handler; +    sph::send_packet_handler send_handler; +    bool continuous_streaming; +}; + +/*********************************************************************** + * Initialize internals within this file + **********************************************************************/ +void b100_impl::io_init(void){ +    _recv_otw_type.width = 16; +    _recv_otw_type.shift = 0; +    _recv_otw_type.byteorder = otw_type_t::BO_LITTLE_ENDIAN; + +    _send_otw_type.width = 16; +    _send_otw_type.shift = 0; +    _send_otw_type.byteorder = otw_type_t::BO_LITTLE_ENDIAN; +     +    _iface->reset_gpif(6); + +    //reset state machines +    _iface->poke32(B100_REG_CTRL_TX_CLEAR_UNDERRUN, 0); +    _iface->poke32(B100_REG_CTRL_RX_CLEAR_OVERRUN, 0); + +    _io_impl = UHD_PIMPL_MAKE(io_impl, (_data_transport)); +     +    //setup rx data path +    _iface->poke32(B100_REG_CTRL_RX_NSAMPS_PER_PKT, get_max_recv_samps_per_packet()); +    UHD_LOGV(always) << "IO: Using " << get_max_recv_samps_per_packet() << " samples per packet" << std::endl; +    _iface->poke32(B100_REG_CTRL_RX_NCHANNELS, 1); +    _iface->poke32(B100_REG_CTRL_RX_VRT_HEADER, 0 +        | (0x1 << 28) //if data with stream id +        | (0x1 << 26) //has trailer +        | (0x3 << 22) //integer time other +        | (0x1 << 20) //fractional time sample count +    ); +    _iface->poke32(B100_REG_CTRL_RX_VRT_TRAILER, 0); + +    //set the streamid to reset the seq num +    _iface->poke32(B100_REG_CTRL_TX_REPORT_SID, 0); +    //setup the tx policy +    _iface->poke32(B100_REG_CTRL_TX_POLICY, B100_FLAG_CTRL_TX_POLICY_NEXT_PACKET); +     +    //set the expected packet size in USB frames +    _iface->poke32(B100_REG_MISC_RX_LEN, 4); + +    update_transport_channel_mapping(); +} + +void b100_impl::update_transport_channel_mapping(void){ +    if (_io_impl.get() == NULL) return; //not inited yet + +    //set all of the relevant properties on the handler +    boost::mutex::scoped_lock recv_lock = _io_impl->recv_handler.get_scoped_lock(); +    _io_impl->recv_handler.resize(_rx_subdev_spec.size()); +    _io_impl->recv_handler.set_vrt_unpacker(&vrt::if_hdr_unpack_le); +    _io_impl->recv_handler.set_tick_rate(_clock_ctrl->get_fpga_clock_rate()); +    _io_impl->recv_handler.set_samp_rate(_rx_ddc_proxy->get_link()[DSP_PROP_HOST_RATE].as<double>()); +    for (size_t chan = 0; chan < _io_impl->recv_handler.size(); chan++){ +        _io_impl->recv_handler.set_xport_chan_get_buff(chan, boost::bind( +            &uhd::transport::zero_copy_if::get_recv_buff, _io_impl->data_transport, _1 +        )); +        _io_impl->recv_handler.set_overflow_handler(chan, boost::bind( +            &b100_impl::handle_overrun, this, chan +        )); +    } +    _io_impl->recv_handler.set_converter(_recv_otw_type); + +    //set all of the relevant properties on the handler +    boost::mutex::scoped_lock send_lock = _io_impl->send_handler.get_scoped_lock(); +    _io_impl->send_handler.resize(_tx_subdev_spec.size()); +    _io_impl->send_handler.set_vrt_packer(&vrt::if_hdr_pack_le); +    _io_impl->send_handler.set_tick_rate(_clock_ctrl->get_fpga_clock_rate()); +    _io_impl->send_handler.set_samp_rate(_tx_duc_proxy->get_link()[DSP_PROP_HOST_RATE].as<double>()); +    for (size_t chan = 0; chan < _io_impl->send_handler.size(); chan++){ +        _io_impl->send_handler.set_xport_chan_get_buff(chan, boost::bind( +            &uhd::transport::zero_copy_if::get_send_buff, _io_impl->data_transport, _1 +        )); +    } +    _io_impl->send_handler.set_converter(_send_otw_type); +    _io_impl->send_handler.set_max_samples_per_packet(get_max_send_samps_per_packet()); +} + +/*********************************************************************** + * Data send + helper functions + **********************************************************************/ +size_t b100_impl::get_max_send_samps_per_packet(void) const { +    static const size_t hdr_size = 0 +        + vrt::max_if_hdr_words32*sizeof(boost::uint32_t) +        - sizeof(vrt::if_packet_info_t().cid) //no class id ever used +    ; +    static const size_t bpp = 2048 - hdr_size; +    return bpp / _send_otw_type.get_sample_size(); +} + +size_t b100_impl::send( +    const send_buffs_type &buffs, size_t nsamps_per_buff, +    const tx_metadata_t &metadata, const io_type_t &io_type, +    send_mode_t send_mode, double timeout +){     +    return _io_impl->send_handler.send( +        buffs, nsamps_per_buff, +        metadata, io_type, +        send_mode, timeout +    ); +} + +/*********************************************************************** + * Data recv + helper functions + **********************************************************************/ + +size_t b100_impl::get_max_recv_samps_per_packet(void) const { +    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 +    ; +    size_t bpp = 2048 - hdr_size; //limited by FPGA pkt buffer size +    return bpp/_recv_otw_type.get_sample_size(); +} + +size_t b100_impl::recv( +    const recv_buffs_type &buffs, size_t nsamps_per_buff, +    rx_metadata_t &metadata, const io_type_t &io_type, +    recv_mode_t recv_mode, double timeout +){ +    return _io_impl->recv_handler.recv( +        buffs, nsamps_per_buff, +        metadata, io_type, +        recv_mode, timeout +    ); +} + +void b100_impl::issue_stream_cmd(const stream_cmd_t &stream_cmd) +{ +    _io_impl->continuous_streaming = (stream_cmd.stream_mode == stream_cmd_t::STREAM_MODE_START_CONTINUOUS); +    _iface->poke32(B100_REG_CTRL_RX_STREAM_CMD, dsp_type1::calc_stream_cmd_word(stream_cmd)); +    _iface->poke32(B100_REG_CTRL_RX_TIME_SECS,  boost::uint32_t(stream_cmd.time_spec.get_full_secs())); +    _iface->poke32(B100_REG_CTRL_RX_TIME_TICKS, stream_cmd.time_spec.get_tick_count(_clock_ctrl->get_fpga_clock_rate())); +     +    if (stream_cmd.stream_mode == stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS) { +        while(_io_impl->data_transport->get_recv_buff().get() != NULL){ +            /* NOP */ +        } +    } +} + +void b100_impl::handle_overrun(size_t){ +    if (_io_impl->continuous_streaming){ +        this->issue_stream_cmd(stream_cmd_t::STREAM_MODE_START_CONTINUOUS); +    } +} diff --git a/host/lib/usrp/usrp_b100/mboard_impl.cpp b/host/lib/usrp/usrp_b100/mboard_impl.cpp new file mode 100644 index 000000000..c651ff2a2 --- /dev/null +++ b/host/lib/usrp/usrp_b100/mboard_impl.cpp @@ -0,0 +1,246 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#include "b100_impl.hpp" +#include "usrp_commands.h" +#include "fpga_regs_standard.h" +#include "fpga_regs_common.h" +#include "b100_regs.hpp" +#include "usrp_i2c_addr.h" +#include <uhd/usrp/misc_utils.hpp> +#include <uhd/usrp/mboard_props.hpp> +#include <uhd/usrp/dboard_props.hpp> +#include <uhd/usrp/subdev_props.hpp> +#include <uhd/utils/msg.hpp> +#include <uhd/exception.hpp> +#include <uhd/utils/images.hpp> +#include <boost/assign/list_of.hpp> +#include <boost/foreach.hpp> +#include <boost/bind.hpp> +#include <boost/thread/thread.hpp> +#include <uhd/usrp/dsp_utils.hpp> + +using namespace uhd; +using namespace uhd::usrp; + +static const bool b100_mboard_verbose = true; + +/*********************************************************************** + * Mboard Initialization + **********************************************************************/ +void b100_impl::mboard_init(void) +{ +    _mboard_proxy = wax_obj_proxy::make( +                     boost::bind(&b100_impl::mboard_get, this, _1, _2), +                     boost::bind(&b100_impl::mboard_set, this, _1, _2)); + +    //set the ticks per seconds into the vita time control +    _iface->poke32(B100_REG_TIME64_TPS, +        boost::uint32_t(_clock_ctrl->get_fpga_clock_rate()) +    ); +     +    //init the clock config +    _clock_config = clock_config_t::internal(); +    update_clock_config(); +} + +void b100_impl::update_clock_config(void){ +    boost::uint32_t pps_flags = 0; + +    //translate pps polarity enums +    switch(_clock_config.pps_polarity){ +    case clock_config_t::PPS_POS: pps_flags |= B100_FLAG_TIME64_PPS_POSEDGE; break; +    case clock_config_t::PPS_NEG: pps_flags |= B100_FLAG_TIME64_PPS_NEGEDGE; break; +    default: throw uhd::runtime_error("unhandled clock configuration pps polarity"); +    } + +    //set the pps flags +    _iface->poke32(B100_REG_TIME64_FLAGS, pps_flags); + +    //clock source ref 10mhz +    switch(_clock_config.ref_source){ +    case clock_config_t::REF_AUTO: _clock_ctrl->use_auto_ref(); break; +    case clock_config_t::REF_INT: _clock_ctrl->use_internal_ref(); break; +    case clock_config_t::REF_SMA: _clock_ctrl->use_auto_ref(); break; +    default: throw uhd::runtime_error("unhandled clock configuration ref source"); +    } +} + +/*********************************************************************** + * Mboard Get + **********************************************************************/ +void b100_impl::mboard_get(const wax::obj &key_, wax::obj &val){ +    named_prop_t key = named_prop_t::extract(key_); + +    //handle the get request conditioned on the key +    switch(key.as<mboard_prop_t>()){ +    case MBOARD_PROP_NAME: +        val = std::string("USRP-B100 mboard"); +        return; + +    case MBOARD_PROP_OTHERS: +        val = prop_names_t(); +        return; + +    case MBOARD_PROP_RX_DBOARD: +        UHD_ASSERT_THROW(key.name == ""); +        val = _rx_dboard_proxy->get_link(); +        return; + +    case MBOARD_PROP_RX_DBOARD_NAMES: +        val = prop_names_t(1, ""); //vector of size 1 with empty string +        return; + +    case MBOARD_PROP_TX_DBOARD: +        UHD_ASSERT_THROW(key.name == ""); +        val = _tx_dboard_proxy->get_link(); +        return; + +    case MBOARD_PROP_TX_DBOARD_NAMES: +        val = prop_names_t(1, ""); //vector of size 1 with empty string +        return; + +    case MBOARD_PROP_RX_DSP: +        UHD_ASSERT_THROW(key.name == ""); +        val = _rx_ddc_proxy->get_link(); +        return; + +    case MBOARD_PROP_RX_DSP_NAMES: +        val = prop_names_t(1, ""); +        return; + +    case MBOARD_PROP_TX_DSP: +        UHD_ASSERT_THROW(key.name == ""); +        val = _tx_duc_proxy->get_link(); +        return; + +    case MBOARD_PROP_TX_DSP_NAMES: +        val = prop_names_t(1, ""); +        return; + +    case MBOARD_PROP_CLOCK_CONFIG: +        val = _clock_config; +        return; + +    case MBOARD_PROP_RX_SUBDEV_SPEC: +        val = _rx_subdev_spec; +        return; + +    case MBOARD_PROP_TX_SUBDEV_SPEC: +        val = _tx_subdev_spec; +        return; +         +    case MBOARD_PROP_EEPROM_MAP: +        val = _iface->mb_eeprom; +        return; + +    case MBOARD_PROP_TIME_NOW:while(true){ +        uint32_t secs = _iface->peek32(B100_REG_RB_TIME_NOW_SECS); +        uint32_t ticks = _iface->peek32(B100_REG_RB_TIME_NOW_TICKS); +        if (secs != _iface->peek32(B100_REG_RB_TIME_NOW_SECS)) continue; +        val = time_spec_t(secs, ticks, _clock_ctrl->get_fpga_clock_rate()); +        return; +    } +     +    case MBOARD_PROP_TIME_PPS: while(true){ +        uint32_t secs = _iface->peek32(B100_REG_RB_TIME_PPS_SECS); +        uint32_t ticks = _iface->peek32(B100_REG_RB_TIME_PPS_TICKS); +        if (secs != _iface->peek32(B100_REG_RB_TIME_PPS_SECS)) continue; +        val = time_spec_t(secs, ticks, _clock_ctrl->get_fpga_clock_rate()); +        return; +    } + +    case MBOARD_PROP_CLOCK_RATE: +        val = _clock_ctrl->get_fpga_clock_rate(); +        return; + +    default: UHD_THROW_PROP_GET_ERROR(); +    } +} + +/*********************************************************************** + * Mboard Set + **********************************************************************/ +void b100_impl::mboard_set(const wax::obj &key, const wax::obj &val) +{ +    if(key.type() == typeid(std::string)) { +      if(key.as<std::string>() == "load_eeprom") { +        std::string b100_eeprom_image = val.as<std::string>(); +        UHD_MSG(status) << "B100 EEPROM image: " << b100_eeprom_image << std::endl; +        _fx2_ctrl->usrp_load_eeprom(val.as<std::string>()); +      } +      return; +   	} + +    //handle the get request conditioned on the key +    switch(key.as<mboard_prop_t>()){ +    case MBOARD_PROP_TIME_NOW: +    case MBOARD_PROP_TIME_PPS:{ +            time_spec_t time_spec = val.as<time_spec_t>(); +            _iface->poke32(B100_REG_TIME64_TICKS, time_spec.get_tick_count(_clock_ctrl->get_fpga_clock_rate())); +            boost::uint32_t imm_flags = (key.as<mboard_prop_t>() == MBOARD_PROP_TIME_NOW)? 1 : 0; +            _iface->poke32(B100_REG_TIME64_IMM, imm_flags); +            _iface->poke32(B100_REG_TIME64_SECS, boost::uint32_t(time_spec.get_full_secs())); +        } +        return; + +    case MBOARD_PROP_RX_SUBDEV_SPEC: +        _rx_subdev_spec = val.as<subdev_spec_t>(); +        verify_rx_subdev_spec(_rx_subdev_spec, _mboard_proxy->get_link()); +        UHD_ASSERT_THROW(_rx_subdev_spec.size() == 1); +        //set the mux +        _iface->poke32(B100_REG_DSP_RX_MUX, dsp_type1::calc_rx_mux_word( +            _dboard_manager->get_rx_subdev(_rx_subdev_spec.front().sd_name)[SUBDEV_PROP_CONNECTION].as<subdev_conn_t>() +        )); +        return; + +    case MBOARD_PROP_TX_SUBDEV_SPEC: +        _tx_subdev_spec = val.as<subdev_spec_t>(); +        verify_tx_subdev_spec(_tx_subdev_spec, _mboard_proxy->get_link()); +        UHD_ASSERT_THROW(_tx_subdev_spec.size() == 1); +        //set the mux and set the number of tx channels +        _iface->poke32(B100_REG_DSP_TX_MUX, dsp_type1::calc_tx_mux_word( +            _dboard_manager->get_tx_subdev(_tx_subdev_spec.front().sd_name)[SUBDEV_PROP_CONNECTION].as<subdev_conn_t>() +        )); +        return; + +    case MBOARD_PROP_EEPROM_MAP: +        // Step1: commit the map, writing only those values set. +        // Step2: readback the entire eeprom map into the iface. +        val.as<mboard_eeprom_t>().commit(*_iface, mboard_eeprom_t::MAP_B000); +        _iface->mb_eeprom = mboard_eeprom_t(*_iface, mboard_eeprom_t::MAP_B000); +        return; + +    case MBOARD_PROP_CLOCK_CONFIG: +        _clock_config = val.as<clock_config_t>(); +        update_clock_config(); +        return; + +    case MBOARD_PROP_CLOCK_RATE: +        UHD_MSG(warning) +            << "You are setting the master clock rate from the API.\n" +            << "You may want to pass this into the device address as master_clock_rate=<rate>.\n" +            << "This way, the clock rate is guaranteed to be initialized first.\n" +            << "See the application notes for USRP-B100 for further instructions.\n" +        ; +        _clock_ctrl->set_fpga_clock_rate(val.as<double>()); +        update_transport_channel_mapping(); +        return; + +    default: UHD_THROW_PROP_SET_ERROR(); +    } +} diff --git a/host/lib/usrp/usrp_e100/dsp_impl.cpp b/host/lib/usrp/usrp_e100/dsp_impl.cpp index 8d084f066..93034b5dc 100644 --- a/host/lib/usrp/usrp_e100/dsp_impl.cpp +++ b/host/lib/usrp/usrp_e100/dsp_impl.cpp @@ -104,6 +104,7 @@ void usrp_e100_impl::rx_ddc_set(const wax::obj &key_, const wax::obj &val){                  dsp_type1::calc_iq_scale_word(default_rx_scale_iq, default_rx_scale_iq)              );          } +        this->update_xport_channel_mapping(); //rate changed -> update          return;      default: UHD_THROW_PROP_SET_ERROR(); @@ -181,6 +182,7 @@ void usrp_e100_impl::tx_duc_set(const wax::obj &key_, const wax::obj &val){              //set the scaling              _iface->poke32(UE_REG_DSP_TX_SCALE_IQ, dsp_type1::calc_iq_scale_word(_duc_interp));          } +        this->update_xport_channel_mapping(); //rate changed -> update          return;      default: UHD_THROW_PROP_SET_ERROR(); diff --git a/host/lib/usrp/usrp_e100/io_impl.cpp b/host/lib/usrp/usrp_e100/io_impl.cpp index aa6e7c485..91b129276 100644 --- a/host/lib/usrp/usrp_e100/io_impl.cpp +++ b/host/lib/usrp/usrp_e100/io_impl.cpp @@ -15,14 +15,16 @@  // along with this program.  If not, see <http://www.gnu.org/licenses/>.  // +#include "../../transport/super_recv_packet_handler.hpp" +#include "../../transport/super_send_packet_handler.hpp"  #include "usrp_e100_impl.hpp"  #include "usrp_e100_regs.hpp"  #include <uhd/utils/msg.hpp>  #include <uhd/utils/log.hpp>  #include <uhd/usrp/dsp_utils.hpp> +#include <uhd/usrp/dsp_props.hpp>  #include <uhd/utils/thread_priority.hpp>  #include <uhd/transport/bounded_buffer.hpp> -#include "../../transport/vrt_packet_handler.hpp"  #include <boost/bind.hpp>  #include <boost/format.hpp>  #include <boost/thread/thread.hpp> @@ -51,8 +53,6 @@ static const int underflow_flags = async_metadata_t::EVENT_CODE_UNDERFLOW | asyn  struct usrp_e100_impl::io_impl{      io_impl(zero_copy_if::sptr &xport):          data_xport(xport), -        get_recv_buffs_fcn(boost::bind(&usrp_e100_impl::io_impl::get_recv_buffs, this, _1)), -        get_send_buffs_fcn(boost::bind(&usrp_e100_impl::io_impl::get_send_buffs, this, _1)),          recv_pirate_booty(data_xport->get_num_recv_frames()),          async_msg_fifo(100/*messages deep*/)      { @@ -65,16 +65,11 @@ struct usrp_e100_impl::io_impl{          recv_pirate_crew.join_all();      } -    bool get_recv_buffs(vrt_packet_handler::managed_recv_buffs_t &buffs){ -        UHD_ASSERT_THROW(buffs.size() == 1); +    managed_recv_buffer::sptr get_recv_buff(double timeout){          boost::this_thread::disable_interruption di; //disable because the wait can throw -        return recv_pirate_booty.pop_with_timed_wait(buffs.front(), recv_timeout); -    } - -    bool get_send_buffs(vrt_packet_handler::managed_send_buffs_t &buffs){ -        UHD_ASSERT_THROW(buffs.size() == 1); -        buffs[0] = data_xport->get_send_buff(send_timeout); -        return buffs[0].get() != NULL; +        managed_recv_buffer::sptr buff; +        recv_pirate_booty.pop_with_timed_wait(buff, timeout); +        return buff; //ASSUME buff == NULL when pop times-out      }      //The data transport is listed first so that it is deconstructed last, @@ -82,16 +77,9 @@ struct usrp_e100_impl::io_impl{      //This comment is invalid because its now a reference and not stored here.      zero_copy_if::sptr &data_xport; -    //bound callbacks for get buffs (bound once here, not in fast-path) -    vrt_packet_handler::get_recv_buffs_t get_recv_buffs_fcn; -    vrt_packet_handler::get_send_buffs_t get_send_buffs_fcn; - -    //timeouts set on calls to recv/send (passed into get buffs methods) -    double recv_timeout, send_timeout; -      //state management for the vrt packet handler code -    vrt_packet_handler::recv_state packet_handler_recv_state; -    vrt_packet_handler::send_state packet_handler_send_state; +    sph::recv_packet_handler recv_handler; +    sph::send_packet_handler send_handler;      bool continuous_streaming;      //a pirate's life is the life for me! @@ -133,16 +121,18 @@ void usrp_e100_impl::io_impl::recv_pirate_loop(              vrt::if_packet_info_t if_packet_info;              if_packet_info.num_packet_words32 = buff->size()/sizeof(boost::uint32_t);              const boost::uint32_t *vrt_hdr = buff->cast<const boost::uint32_t *>(); -            vrt::if_hdr_unpack_le(vrt_hdr, if_packet_info);              //handle an rx data packet or inline message -            if (if_packet_info.sid == rx_data_inline_sid){ +            if (uhd::ntohx(vrt_hdr[1]) == rx_data_inline_sid){ //ASSUME has_sid                  if (fp_recv_debug) UHD_LOGV(always) << "this is rx_data_inline_sid\n";                  //same number of frames as the data transport -> always immediate                  recv_pirate_booty.push_with_wait(buff);                  continue;              } +            //unpack the vrt header and process below... +            vrt::if_hdr_unpack_le(vrt_hdr, if_packet_info); +              //handle a tx async report message              if (if_packet_info.sid == tx_async_report_sid and if_packet_info.packet_type != vrt::if_packet_info_t::PACKET_TYPE_DATA){                  if (fp_recv_debug) UHD_LOGV(always) << "this is tx_async_report_sid\n"; @@ -154,7 +144,7 @@ void usrp_e100_impl::io_impl::recv_pirate_loop(                  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 = vrt_packet_handler::get_context_code<async_metadata_t::event_code_t>(vrt_hdr, if_packet_info); +                metadata.event_code = async_metadata_t::event_code_t(sph::get_context_code(vrt_hdr, if_packet_info));                  //print the famous U, and push the metadata into the message queue                  if (metadata.event_code & underflow_flags) UHD_MSG(fastpath) << "U"; @@ -162,6 +152,7 @@ void usrp_e100_impl::io_impl::recv_pirate_loop(                  continue;              } +            //TODO replace this below with a UHD_MSG(error)              if (fp_recv_debug) UHD_LOGV(always) << "this is unknown packet\n";          }catch(const std::exception &e){ @@ -213,6 +204,42 @@ void usrp_e100_impl::io_init(void){          boost::ref(spawn_barrier), _clock_ctrl      ));      spawn_barrier.wait(); +    //update mapping here since it didnt b4 when io init not called first +    update_xport_channel_mapping(); +} + +void usrp_e100_impl::update_xport_channel_mapping(void){ +    if (_io_impl.get() == NULL) return; //not inited yet + +    //set all of the relevant properties on the handler +    boost::mutex::scoped_lock recv_lock = _io_impl->recv_handler.get_scoped_lock(); +    _io_impl->recv_handler.resize(_rx_subdev_spec.size()); +    _io_impl->recv_handler.set_vrt_unpacker(&vrt::if_hdr_unpack_le); +    _io_impl->recv_handler.set_tick_rate(_clock_ctrl->get_fpga_clock_rate()); +    _io_impl->recv_handler.set_samp_rate(_rx_ddc_proxy->get_link()[DSP_PROP_HOST_RATE].as<double>()); +    for (size_t chan = 0; chan < _io_impl->recv_handler.size(); chan++){ +        _io_impl->recv_handler.set_xport_chan_get_buff(chan, boost::bind( +            &usrp_e100_impl::io_impl::get_recv_buff, _io_impl.get(), _1 +        )); +        _io_impl->recv_handler.set_overflow_handler(chan, boost::bind( +            &usrp_e100_impl::handle_overrun, this, chan +        )); +    } +    _io_impl->recv_handler.set_converter(_recv_otw_type); + +    //set all of the relevant properties on the handler +    boost::mutex::scoped_lock send_lock = _io_impl->send_handler.get_scoped_lock(); +    _io_impl->send_handler.resize(_tx_subdev_spec.size()); +    _io_impl->send_handler.set_vrt_packer(&vrt::if_hdr_pack_le); +    _io_impl->send_handler.set_tick_rate(_clock_ctrl->get_fpga_clock_rate()); +    _io_impl->send_handler.set_samp_rate(_tx_duc_proxy->get_link()[DSP_PROP_HOST_RATE].as<double>()); +    for (size_t chan = 0; chan < _io_impl->send_handler.size(); chan++){ +        _io_impl->send_handler.set_xport_chan_get_buff(chan, boost::bind( +            &uhd::transport::zero_copy_if::get_send_buff, _io_impl->data_xport, _1 +        )); +    } +    _io_impl->send_handler.set_converter(_send_otw_type); +    _io_impl->send_handler.set_max_samples_per_packet(get_max_send_samps_per_packet());  }  void usrp_e100_impl::issue_stream_cmd(const stream_cmd_t &stream_cmd){ @@ -222,8 +249,7 @@ void usrp_e100_impl::issue_stream_cmd(const stream_cmd_t &stream_cmd){      _iface->poke32(UE_REG_CTRL_RX_TIME_TICKS, stream_cmd.time_spec.get_tick_count(_clock_ctrl->get_fpga_clock_rate()));  } -void usrp_e100_impl::handle_overrun(size_t){ -    UHD_MSG(fastpath) << "O"; //the famous OOOOOOOOOOO +void usrp_e100_impl::handle_overrun(size_t /*chan*/){      if (_io_impl->continuous_streaming){          this->issue_stream_cmd(stream_cmd_t::STREAM_MODE_START_CONTINUOUS);      } @@ -242,20 +268,14 @@ size_t usrp_e100_impl::get_max_send_samps_per_packet(void) const{  }  size_t usrp_e100_impl::send( -    const send_buffs_type &buffs, size_t num_samps, +    const send_buffs_type &buffs, size_t nsamps_per_buff,      const tx_metadata_t &metadata, const io_type_t &io_type,      send_mode_t send_mode, double timeout  ){ -    _io_impl->send_timeout = timeout; -    return vrt_packet_handler::send( -        _io_impl->packet_handler_send_state,       //last state of the send handler -        buffs, num_samps,                          //buffer to fill -        metadata, send_mode,                       //samples metadata -        io_type, _send_otw_type,                   //input and output types to convert -        _clock_ctrl->get_fpga_clock_rate(),        //master clock tick rate -        uhd::transport::vrt::if_hdr_pack_le, -        _io_impl->get_send_buffs_fcn, -        get_max_send_samps_per_packet() +    return _io_impl->send_handler.send( +        buffs, nsamps_per_buff, +        metadata, io_type, +        send_mode, timeout      );  } @@ -273,20 +293,14 @@ size_t usrp_e100_impl::get_max_recv_samps_per_packet(void) const{  }  size_t usrp_e100_impl::recv( -    const recv_buffs_type &buffs, size_t num_samps, +    const recv_buffs_type &buffs, size_t nsamps_per_buff,      rx_metadata_t &metadata, const io_type_t &io_type,      recv_mode_t recv_mode, double timeout  ){ -    _io_impl->recv_timeout = timeout; -    return vrt_packet_handler::recv( -        _io_impl->packet_handler_recv_state,       //last state of the recv handler -        buffs, num_samps,                          //buffer to fill -        metadata, recv_mode,                       //samples metadata -        io_type, _recv_otw_type,                   //input and output types to convert -        _clock_ctrl->get_fpga_clock_rate(),        //master clock tick rate -        uhd::transport::vrt::if_hdr_unpack_le, -        _io_impl->get_recv_buffs_fcn, -        boost::bind(&usrp_e100_impl::handle_overrun, this, _1) +    return _io_impl->recv_handler.recv( +        buffs, nsamps_per_buff, +        metadata, io_type, +        recv_mode, timeout      );  } diff --git a/host/lib/usrp/usrp_e100/mboard_impl.cpp b/host/lib/usrp/usrp_e100/mboard_impl.cpp index b2533e7ee..f4b8d79f6 100644 --- a/host/lib/usrp/usrp_e100/mboard_impl.cpp +++ b/host/lib/usrp/usrp_e100/mboard_impl.cpp @@ -209,11 +209,12 @@ void usrp_e100_impl::mboard_set(const wax::obj &key, const wax::obj &val){      case MBOARD_PROP_CLOCK_RATE:          UHD_MSG(warning)              << "I see that you are setting the master clock rate from the API.\n" -            << "You may want to pass this into the device address as mcr=<rate>.\n" +            << "You may pass this into the device address as master_clock_rate=<rate>.\n"              << "This way, the clock rate is guaranteed to be initialized first.\n"              << "See the application notes for USRP-E1XX for further instructions.\n"          ;          _clock_ctrl->set_fpga_clock_rate(val.as<double>()); +        this->update_xport_channel_mapping();          return;      default: UHD_THROW_PROP_SET_ERROR(); diff --git a/host/lib/usrp/usrp_e100/usrp_e100_impl.cpp b/host/lib/usrp/usrp_e100/usrp_e100_impl.cpp index e9e9b6e20..5b039aafc 100644 --- a/host/lib/usrp/usrp_e100/usrp_e100_impl.cpp +++ b/host/lib/usrp/usrp_e100/usrp_e100_impl.cpp @@ -39,7 +39,7 @@ static device_addrs_t usrp_e100_find(const device_addr_t &hint){      device_addrs_t usrp_e100_addrs;      //return an empty list of addresses when type is set to non-usrp-e -    if (hint.has_key("type") and hint["type"] != "usrp-e") return usrp_e100_addrs; +    if (hint.has_key("type") and hint["type"] != "e100") return usrp_e100_addrs;      //device node not provided, assume its 0      if (not hint.has_key("node")){ @@ -171,12 +171,13 @@ usrp_e100_impl::usrp_e100_impl(      //init the codec properties      codec_init(); -    //init the io send/recv -    io_init(); -      //set default subdev specs      this->mboard_set(MBOARD_PROP_RX_SUBDEV_SPEC, subdev_spec_t());      this->mboard_set(MBOARD_PROP_TX_SUBDEV_SPEC, subdev_spec_t()); + +    //init the io send/recv +    io_init(); +  }  usrp_e100_impl::~usrp_e100_impl(void){ diff --git a/host/lib/usrp/usrp_e100/usrp_e100_impl.hpp b/host/lib/usrp/usrp_e100/usrp_e100_impl.hpp index 318a75191..1c17863fb 100644 --- a/host/lib/usrp/usrp_e100/usrp_e100_impl.hpp +++ b/host/lib/usrp/usrp_e100/usrp_e100_impl.hpp @@ -104,6 +104,7 @@ private:      void io_init(void);      void issue_stream_cmd(const uhd::stream_cmd_t &stream_cmd);      void handle_overrun(size_t); +    void update_xport_channel_mapping(void);      //configuration shadows      uhd::clock_config_t _clock_config;  | 
