aboutsummaryrefslogtreecommitdiffstats
path: root/host
diff options
context:
space:
mode:
Diffstat (limited to 'host')
-rw-r--r--host/docs/CMakeLists.txt1
-rw-r--r--host/docs/general.rst54
-rw-r--r--host/docs/index.rst1
-rw-r--r--host/docs/usrp_b1xx.rst76
-rw-r--r--host/include/uhd/convert.hpp2
-rw-r--r--host/include/uhd/transport/usb_zero_copy.hpp21
-rw-r--r--host/lib/convert/CMakeLists.txt46
-rw-r--r--host/lib/convert/convert_common.hpp40
-rw-r--r--host/lib/convert/convert_orc.orc63
-rw-r--r--host/lib/convert/convert_with_neon.cpp8
-rw-r--r--host/lib/convert/convert_with_orc.cpp54
-rw-r--r--host/lib/convert/convert_with_sse2.cpp16
-rw-r--r--host/lib/convert/gen_convert_general.py8
-rw-r--r--host/lib/transport/CMakeLists.txt2
-rwxr-xr-xhost/lib/transport/gen_vrt_if_packet.py44
-rw-r--r--host/lib/transport/libusb1_zero_copy.cpp33
-rw-r--r--host/lib/transport/super_recv_packet_handler.hpp638
-rw-r--r--host/lib/transport/super_send_packet_handler.hpp287
-rw-r--r--host/lib/transport/usb_zero_copy_wrapper.cpp202
-rw-r--r--host/lib/transport/vrt_packet_handler.hpp470
-rw-r--r--host/lib/usrp/CMakeLists.txt1
-rw-r--r--host/lib/usrp/b100/CMakeLists.txt47
-rw-r--r--host/lib/usrp/b100/b100_ctrl.cpp245
-rw-r--r--host/lib/usrp/b100/b100_ctrl.hpp69
-rw-r--r--host/lib/usrp/b100/b100_iface.cpp336
-rw-r--r--host/lib/usrp/b100/b100_iface.hpp73
-rw-r--r--host/lib/usrp/b100/b100_impl.cpp280
-rw-r--r--host/lib/usrp/b100/b100_impl.hpp204
-rw-r--r--host/lib/usrp/b100/b100_regs.hpp264
-rw-r--r--host/lib/usrp/b100/clock_ctrl.cpp525
-rw-r--r--host/lib/usrp/b100/clock_ctrl.hpp118
-rw-r--r--host/lib/usrp/b100/codec_ctrl.cpp283
-rw-r--r--host/lib/usrp/b100/codec_ctrl.hpp90
-rw-r--r--host/lib/usrp/b100/codec_impl.cpp149
-rw-r--r--host/lib/usrp/b100/ctrl_packet.hpp75
-rw-r--r--host/lib/usrp/b100/dboard_iface.cpp298
-rw-r--r--host/lib/usrp/b100/dboard_impl.cpp185
-rw-r--r--host/lib/usrp/b100/dsp_impl.cpp189
-rw-r--r--host/lib/usrp/b100/io_impl.cpp210
-rw-r--r--host/lib/usrp/b100/mboard_impl.cpp246
-rw-r--r--host/lib/usrp/dboard/db_basic_and_lf.cpp2
-rw-r--r--host/lib/usrp/fx2/fx2_ctrl.hpp2
-rw-r--r--host/lib/usrp/usrp1/dsp_impl.cpp5
-rw-r--r--host/lib/usrp/usrp1/io_impl.cpp180
-rw-r--r--host/lib/usrp/usrp1/mboard_impl.cpp3
-rw-r--r--host/lib/usrp/usrp1/usrp1_impl.cpp6
-rw-r--r--host/lib/usrp/usrp1/usrp1_impl.hpp3
-rw-r--r--host/lib/usrp/usrp2/dsp_impl.cpp8
-rw-r--r--host/lib/usrp/usrp2/io_impl.cpp286
-rw-r--r--host/lib/usrp/usrp2/mboard_impl.cpp1
-rw-r--r--host/lib/usrp/usrp2/usrp2_impl.hpp1
-rw-r--r--host/lib/usrp/usrp_e100/dsp_impl.cpp2
-rw-r--r--host/lib/usrp/usrp_e100/io_impl.cpp110
-rw-r--r--host/lib/usrp/usrp_e100/mboard_impl.cpp1
-rw-r--r--host/lib/usrp/usrp_e100/usrp_e100_impl.cpp9
-rw-r--r--host/lib/usrp/usrp_e100/usrp_e100_impl.hpp1
-rw-r--r--host/tests/CMakeLists.txt2
-rw-r--r--host/tests/convert_test.cpp14
-rw-r--r--host/tests/sph_recv_test.cpp715
-rw-r--r--host/tests/sph_send_test.cpp204
-rw-r--r--host/usrp_e_utils/usrp-e-loopback.c27
-rw-r--r--host/usrp_e_utils/usrp-e-timed.c2
62 files changed, 6617 insertions, 920 deletions
diff --git a/host/docs/CMakeLists.txt b/host/docs/CMakeLists.txt
index 5926e062e..88afb9c98 100644
--- a/host/docs/CMakeLists.txt
+++ b/host/docs/CMakeLists.txt
@@ -30,6 +30,7 @@ SET(manual_sources
transport.rst
usrp1.rst
usrp2.rst
+ usrp_b1xx.rst
usrp_e1xx.rst
)
diff --git a/host/docs/general.rst b/host/docs/general.rst
index ff85fb0f9..cc00fc0f9 100644
--- a/host/docs/general.rst
+++ b/host/docs/general.rst
@@ -61,6 +61,42 @@ Pseudo-code for dealing with settling time after tuning on receive:
usrp->issue_stream_command(...);
------------------------------------------------------------------------
+Overflow/Underflow notes
+------------------------------------------------------------------------
+**Note:** The following overflow/underflow notes do not apply to USRP1,
+which does not support the advanced features available in newer products.
+
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Overflow notes
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+When receiving, the device produces samples at a constant rate.
+Overflows occurs when the host does not consume data fast enough.
+When UHD detects the overflow, it prints an "O" to stdout,
+and pushes an inline message packet into the receive stream.
+
+**Network-based devices**:
+The host does not back-pressure the receive stream.
+When the kernel's socket buffer becomes full, it will drop subsequent packets.
+UHD detects the overflow as a discontinuity in the packet's sequence numbers,
+and muxes an inline message packet into the receive stream.
+
+**Other devices**:
+The host back-pressures the receive stream.
+Therefore, overflows always occur in the device itself.
+When the device's internal buffers become full, streaming is shutoff,
+and an inline message packet is sent to the host.
+If the device was in continuous streaming mode,
+the UHD will automatically restart streaming.
+
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Underflow notes
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+When transmitting, the device consumes samples at a constant rate.
+Underflow occurs when the host does not produce data fast enough.
+When the UHD detects underflow, it prints an "U" to stdout,
+and pushes a message packet into the async message stream.
+
+------------------------------------------------------------------------
Threading notes
------------------------------------------------------------------------
@@ -114,3 +150,21 @@ For a module to be loaded at runtime, it must be:
* found in the UHD_MODULE_PATH environment variable,
* installed into the <install-path>/share/uhd/modules directory,
* or installed into /usr/share/uhd/modules directory (unix only).
+
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Disabling or redirecting prints to stdout
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+The user can disable the UHD library from printing directly to stdout by registering a custom message handler.
+The handler will intercept all messages, which can be dropped or redirected.
+Only one handler can be registered at a time.
+Make "register_handler" your first call into UHD:
+
+::
+
+ #include <uhd/utils/msg.hpp>
+
+ void my_handler(uhd::msg::type_t type, const std::string &msg){
+ //handle the message...
+ }
+
+ uhd::msg::register_handler(&my_handler);
diff --git a/host/docs/index.rst b/host/docs/index.rst
index 40fe64599..b4ebdad6c 100644
--- a/host/docs/index.rst
+++ b/host/docs/index.rst
@@ -24,6 +24,7 @@ Application Notes
* `USRP1 Application Notes <./usrp1.html>`_
* `USRP2 Application Notes <./usrp2.html>`_
* `USRP-N2XX Series Application Notes <./usrp2.html>`_
+* `USRP-B1XX Series Application Notes <./usrp_b1xx.html>`_
* `USRP-E1XX Series Application Notes <./usrp_e1xx.html>`_
* `Daughterboard Application Notes <./dboards.html>`_
* `Transport Application Notes <./transport.html>`_
diff --git a/host/docs/usrp_b1xx.rst b/host/docs/usrp_b1xx.rst
new file mode 100644
index 000000000..2f7385070
--- /dev/null
+++ b/host/docs/usrp_b1xx.rst
@@ -0,0 +1,76 @@
+========================================================================
+UHD - USRP-B1XX Series Application Notes
+========================================================================
+
+.. contents:: Table of Contents
+
+------------------------------------------------------------------------
+Specify a non-standard image
+------------------------------------------------------------------------
+The UHD will automatically select the USRP B-Series images from the installed images package.
+The image selection can be overridden with the "fpga" and "fw" device address parameters.
+
+Example device address string representations to specify non-standard images:
+
+::
+
+ fpga=usrp_b100_fpga_firmware.bin
+
+ -- OR --
+
+ fw=usrp_b100_fw_firmware.ihx
+
+------------------------------------------------------------------------
+Changing the master clock rate
+------------------------------------------------------------------------
+The master clock rate of the USRP embedded feeds both the FPGA DSP and the codec chip.
+Hundreds of rates between 32MHz and 64MHz are available.
+A few notable rates are:
+
+* 64MHz - maximum rate of the codec chip
+* 61.44MHz - good for UMTS/WCDMA applications
+* 52Mhz - good for GSM applications
+
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Set 61.44MHz - uses external VCXO
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+To use the 61.44MHz clock rate, the USRP embedded will require two jumpers to be moved.
+
+* J16 is a two pin header, remove the jumper (or leave it on pin1 only)
+* J15 is a three pin header, move the jumper to (pin1, pin2)
+
+**Note:** See instructions below to communicate the desired clock rate into the UHD.
+
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Set other rates - uses internal VCO
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+To use other clock rates, the jumpers will need to be in the default position.
+
+* J16 is a two pin header, move the jumper to (pin1, pin2)
+* J15 is a three pin header, move the jumper to (pin2, pin3)
+
+To communicate the desired clock rate into the UHD,
+specify the a special device address argument,
+where the key is "master_clock_rate" and the value is a rate in Hz.
+Example:
+::
+
+ uhd_usrp_probe --args="master_clock_rate=52e6"
+
+------------------------------------------------------------------------
+OS specific notes
+------------------------------------------------------------------------
+
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Linux - setup udev
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+On Linux, udev handles USB plug and unplug events.
+The following commands create a udev rule for the B100
+so that non-root users may access the device:
+
+::
+
+ echo 'ACTION=="add", BUS=="usb", SYSFS{idVendor}=="2500", SYSFS{idProduct}=="0001", MODE:="0666"' > tmpfile
+ sudo chown root.root tmpfile
+ sudo mv tmpfile /etc/udev/rules.d/10-usrp_b100.rules
+ sudo udevadm control --reload-rules
diff --git a/host/include/uhd/convert.hpp b/host/include/uhd/convert.hpp
index 8fc2f38db..99f1860ae 100644
--- a/host/include/uhd/convert.hpp
+++ b/host/include/uhd/convert.hpp
@@ -29,7 +29,7 @@ namespace uhd{ namespace convert{
typedef uhd::ref_vector<void *> output_type;
typedef uhd::ref_vector<const void *> input_type;
- typedef boost::function<void(const input_type&, const output_type&, size_t)> function_type;
+ typedef boost::function<void(const input_type&, const output_type&, size_t, double)> function_type;
/*!
* Describe the priority of a converter function.
diff --git a/host/include/uhd/transport/usb_zero_copy.hpp b/host/include/uhd/transport/usb_zero_copy.hpp
index b39171fba..dc344ad8b 100644
--- a/host/include/uhd/transport/usb_zero_copy.hpp
+++ b/host/include/uhd/transport/usb_zero_copy.hpp
@@ -1,5 +1,5 @@
//
-// Copyright 2010 Ettus Research LLC
+// Copyright 2010-2011 Ettus Research LLC
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
@@ -49,6 +49,7 @@ public:
* \param recv_endpoint an integer specifiying an IN endpoint number
* \param send_endpoint an integer specifiying an OUT endpoint number
* \param hints optional parameters to pass to the underlying transport
+ * \return a new zero copy usb object
*/
static sptr make(
usb_device_handle::sptr handle,
@@ -56,6 +57,24 @@ public:
size_t send_endpoint,
const device_addr_t &hints = device_addr_t()
);
+
+ /*!
+ * Make a wrapper around a zero copy implementation.
+ * The wrapper performs the following functions:
+ * - Pad commits to the frame boundary
+ * - Extract multiple packets on recv
+ *
+ * When enable multiple receive packets is set to true,
+ * the implementation inspects the vita length on transfers,
+ * and may split a single transfer into multiple managed buffers.
+ *
+ * \param usb_zc a usb zero copy interface object
+ * \param usb_frame_boundary bytes per frame
+ * \return a new zero copy wrapper object
+ */
+ static sptr make_wrapper(
+ sptr usb_zc, size_t usb_frame_boundary = 512
+ );
};
}} //namespace
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/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 281910735..80f4bf45e 100644
--- a/host/lib/usrp/CMakeLists.txt
+++ b/host/lib/usrp/CMakeLists.txt
@@ -37,4 +37,5 @@ INCLUDE_SUBDIRECTORY(dboard)
INCLUDE_SUBDIRECTORY(fx2)
INCLUDE_SUBDIRECTORY(usrp1)
INCLUDE_SUBDIRECTORY(usrp2)
+INCLUDE_SUBDIRECTORY(b100)
INCLUDE_SUBDIRECTORY(usrp_e100)
diff --git a/host/lib/usrp/b100/CMakeLists.txt b/host/lib/usrp/b100/CMakeLists.txt
new file mode 100644
index 000000000..e1618a49c
--- /dev/null
+++ b/host/lib/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_CURRENT_SOURCE_DIR}/clock_ctrl.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/clock_ctrl.hpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/codec_ctrl.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/codec_ctrl.hpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/codec_impl.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/ctrl_packet.hpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/dboard_impl.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/dboard_iface.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/dsp_impl.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/io_impl.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/mboard_impl.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/b100_ctrl.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/b100_ctrl.hpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/b100_iface.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/b100_iface.hpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/b100_impl.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/b100_impl.hpp
+ )
+ENDIF(ENABLE_B100)
diff --git a/host/lib/usrp/b100/b100_ctrl.cpp b/host/lib/usrp/b100/b100_ctrl.cpp
new file mode 100644
index 000000000..4d4520e1e
--- /dev/null
+++ b/host/lib/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/b100/b100_ctrl.hpp b/host/lib/usrp/b100/b100_ctrl.hpp
new file mode 100644
index 000000000..ae706dbb4
--- /dev/null
+++ b/host/lib/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/b100/b100_iface.cpp b/host/lib/usrp/b100/b100_iface.cpp
new file mode 100644
index 000000000..17ea2e6ad
--- /dev/null
+++ b/host/lib/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/b100/b100_iface.hpp b/host/lib/usrp/b100/b100_iface.hpp
new file mode 100644
index 000000000..57ed6a45c
--- /dev/null
+++ b/host/lib/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/b100/b100_impl.cpp b/host/lib/usrp/b100/b100_impl.cpp
new file mode 100644
index 000000000..7e733ddd9
--- /dev/null
+++ b/host/lib/usrp/b100/b100_impl.cpp
@@ -0,0 +1,280 @@
+//
+// 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;
+
+ //Return an empty list of addresses when an address is specified,
+ //since an address is intended for a different, non-USB, device.
+ if (hint.has_key("addr")) 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)) {
+ //extract the firmware path for the b100
+ std::string b100_fw_image;
+ try{
+ b100_fw_image = find_image_path(hint.get("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;
+ }
+ UHD_LOG << "the firmware image: " << b100_fw_image << std::endl;
+
+ usb_control::sptr control;
+ try{control = usb_control::make(handle);}
+ catch(const uhd::exception &){continue;} //ignore claimed
+
+ fx2_ctrl::make(control)->usrp_load_firmware(b100_fw_image);
+ }
+
+ //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/b100/b100_impl.hpp b/host/lib/usrp/b100/b100_impl.hpp
new file mode 100644
index 000000000..daec70bca
--- /dev/null
+++ b/host/lib/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/b100/b100_regs.hpp b/host/lib/usrp/b100/b100_regs.hpp
new file mode 100644
index 000000000..010df283e
--- /dev/null
+++ b/host/lib/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/b100/clock_ctrl.cpp b/host/lib/usrp/b100/clock_ctrl.cpp
new file mode 100644
index 000000000..e138242d1
--- /dev/null
+++ b/host/lib/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/b100/clock_ctrl.hpp b/host/lib/usrp/b100/clock_ctrl.hpp
new file mode 100644
index 000000000..2a2e74024
--- /dev/null
+++ b/host/lib/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/b100/codec_ctrl.cpp b/host/lib/usrp/b100/codec_ctrl.cpp
new file mode 100644
index 000000000..4d118b68b
--- /dev/null
+++ b/host/lib/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/b100/codec_ctrl.hpp b/host/lib/usrp/b100/codec_ctrl.hpp
new file mode 100644
index 000000000..1bd579190
--- /dev/null
+++ b/host/lib/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/b100/codec_impl.cpp b/host/lib/usrp/b100/codec_impl.cpp
new file mode 100644
index 000000000..de3ca3a66
--- /dev/null
+++ b/host/lib/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/b100/ctrl_packet.hpp b/host/lib/usrp/b100/ctrl_packet.hpp
new file mode 100644
index 000000000..f504fc5aa
--- /dev/null
+++ b/host/lib/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/b100/dboard_iface.cpp b/host/lib/usrp/b100/dboard_iface.cpp
new file mode 100644
index 000000000..ec3da6220
--- /dev/null
+++ b/host/lib/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/b100/dboard_impl.cpp b/host/lib/usrp/b100/dboard_impl.cpp
new file mode 100644
index 000000000..ba3776728
--- /dev/null
+++ b/host/lib/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/b100/dsp_impl.cpp b/host/lib/usrp/b100/dsp_impl.cpp
new file mode 100644
index 000000000..c1bf6bedd
--- /dev/null
+++ b/host/lib/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/b100/io_impl.cpp b/host/lib/usrp/b100/io_impl.cpp
new file mode 100644
index 000000000..3978bea75
--- /dev/null
+++ b/host/lib/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/b100/mboard_impl.cpp b/host/lib/usrp/b100/mboard_impl.cpp
new file mode 100644
index 000000000..c651ff2a2
--- /dev/null
+++ b/host/lib/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/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/fx2/fx2_ctrl.hpp b/host/lib/usrp/fx2/fx2_ctrl.hpp
index 98c98bb8c..37fa09605 100644
--- a/host/lib/usrp/fx2/fx2_ctrl.hpp
+++ b/host/lib/usrp/fx2/fx2_ctrl.hpp
@@ -57,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
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_impl.cpp b/host/lib/usrp/usrp1/usrp1_impl.cpp
index 37955d628..a3d502038 100644
--- a/host/lib/usrp/usrp1/usrp1_impl.cpp
+++ b/host/lib/usrp/usrp1/usrp1_impl.cpp
@@ -199,12 +199,12 @@ 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());
+
+ //initialize the send/recv
+ io_init();
}
usrp1_impl::~usrp1_impl(void){UHD_SAFE_CALL(
diff --git a/host/lib/usrp/usrp1/usrp1_impl.hpp b/host/lib/usrp/usrp1/usrp1_impl.hpp
index fbae2a3b9..bea1dbe3b 100644
--- a/host/lib/usrp/usrp1/usrp1_impl.hpp
+++ b/host/lib/usrp/usrp1/usrp1_impl.hpp
@@ -114,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;
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 4a28ea9c4..6bf412a3e 100644
--- a/host/lib/usrp/usrp2/mboard_impl.cpp
+++ b/host/lib/usrp/usrp2/mboard_impl.cpp
@@ -466,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_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_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 483286e47..f4b8d79f6 100644
--- a/host/lib/usrp/usrp_e100/mboard_impl.cpp
+++ b/host/lib/usrp/usrp_e100/mboard_impl.cpp
@@ -214,6 +214,7 @@ void usrp_e100_impl::mboard_set(const wax::obj &key, const wax::obj &val){
<< "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;
diff --git a/host/tests/CMakeLists.txt b/host/tests/CMakeLists.txt
index b38afccf0..b7bcfb7d5 100644
--- a/host/tests/CMakeLists.txt
+++ b/host/tests/CMakeLists.txt
@@ -28,6 +28,8 @@ SET(test_sources
gain_group_test.cpp
msg_test.cpp
ranges_test.cpp
+ sph_recv_test.cpp
+ sph_send_test.cpp
subdev_spec_test.cpp
time_spec_test.cpp
tune_helper_test.cpp
diff --git a/host/tests/convert_test.cpp b/host/tests/convert_test.cpp
index d1c2b7625..d828ed64a 100644
--- a/host/tests/convert_test.cpp
+++ b/host/tests/convert_test.cpp
@@ -31,7 +31,7 @@ typedef std::complex<boost::int16_t> sc16_t;
typedef std::complex<float> fc32_t;
typedef std::complex<double> fc64_t;
-#define MY_CHECK_CLOSE(a, b, f) if ((std::abs(a) > (f) and std::abs(b) > (f))) \
+#define MY_CHECK_CLOSE(a, b, f) if ((std::abs(a) > (f))) \
BOOST_CHECK_CLOSE_FRACTION(a, b, f)
/***********************************************************************
@@ -55,12 +55,12 @@ template <typename Range> static void loopback(
//convert to intermediate type
convert::get_converter_cpu_to_otw(
io_type, otw_type, input0.size(), output0.size()
- )(input0, output0, nsamps);
+ )(input0, output0, nsamps, 32767.);
//convert back to host type
convert::get_converter_otw_to_cpu(
io_type, otw_type, input1.size(), output1.size()
- )(input1, output1, nsamps);
+ )(input1, output1, nsamps, 1/32767.);
}
/***********************************************************************
@@ -207,12 +207,12 @@ BOOST_AUTO_TEST_CASE(test_convert_types_fc32_to_sc16){
//convert float to intermediate
convert::get_converter_cpu_to_otw(
io_type_in, otw_type, input0.size(), output0.size()
- )(input0, output0, nsamps);
+ )(input0, output0, nsamps, 32767.);
//convert intermediate to short
convert::get_converter_otw_to_cpu(
io_type_out, otw_type, input1.size(), output1.size()
- )(input1, output1, nsamps);
+ )(input1, output1, nsamps, 1/32767.);
//test that the inputs and outputs match
for (size_t i = 0; i < nsamps; i++){
@@ -247,12 +247,12 @@ BOOST_AUTO_TEST_CASE(test_convert_types_sc16_to_fc32){
//convert short to intermediate
convert::get_converter_cpu_to_otw(
io_type_in, otw_type, input0.size(), output0.size()
- )(input0, output0, nsamps);
+ )(input0, output0, nsamps, 32767.);
//convert intermediate to float
convert::get_converter_otw_to_cpu(
io_type_out, otw_type, input1.size(), output1.size()
- )(input1, output1, nsamps);
+ )(input1, output1, nsamps, 1/32767.);
//test that the inputs and outputs match
for (size_t i = 0; i < nsamps; i++){
diff --git a/host/tests/sph_recv_test.cpp b/host/tests/sph_recv_test.cpp
new file mode 100644
index 000000000..6bb5d1175
--- /dev/null
+++ b/host/tests/sph_recv_test.cpp
@@ -0,0 +1,715 @@
+//
+// 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 <boost/test/unit_test.hpp>
+#include "../lib/transport/super_recv_packet_handler.hpp"
+#include <boost/shared_array.hpp>
+#include <boost/bind.hpp>
+#include <complex>
+#include <vector>
+#include <list>
+
+#define BOOST_CHECK_TS_CLOSE(a, b) \
+ BOOST_CHECK_CLOSE((a).get_real_secs(), (b).get_real_secs(), 0.001)
+
+/***********************************************************************
+ * A dummy managed receive buffer for testing
+ **********************************************************************/
+class dummy_mrb : public uhd::transport::managed_recv_buffer{
+public:
+ void release(void){
+ //NOP
+ }
+
+ sptr get_new(boost::shared_array<char> mem, size_t len){
+ _mem = mem;
+ _len = len;
+ return make_managed_buffer(this);
+ }
+
+private:
+ const void *get_buff(void) const{return _mem.get();}
+ size_t get_size(void) const{return _len;}
+
+ boost::shared_array<char> _mem;
+ size_t _len;
+};
+
+/***********************************************************************
+ * A dummy transport class to fill with fake data
+ **********************************************************************/
+class dummy_recv_xport_class{
+public:
+ dummy_recv_xport_class(const uhd::otw_type_t &otw_type){
+ _otw_type = otw_type;
+ }
+
+ void push_back_packet(
+ uhd::transport::vrt::if_packet_info_t &ifpi,
+ const boost::uint32_t optional_msg_word = 0
+ ){
+ const size_t max_pkt_len = (ifpi.num_payload_words32 + uhd::transport::vrt::max_if_hdr_words32 + 1/*tlr*/)*sizeof(boost::uint32_t);
+ _mems.push_back(boost::shared_array<char>(new char[max_pkt_len]));
+ if (_otw_type.byteorder == uhd::otw_type_t::BO_BIG_ENDIAN){
+ uhd::transport::vrt::if_hdr_pack_be(reinterpret_cast<boost::uint32_t *>(_mems.back().get()), ifpi);
+ }
+ if (_otw_type.byteorder == uhd::otw_type_t::BO_LITTLE_ENDIAN){
+ uhd::transport::vrt::if_hdr_pack_le(reinterpret_cast<boost::uint32_t *>(_mems.back().get()), ifpi);
+ }
+ (reinterpret_cast<boost::uint32_t *>(_mems.back().get()) + ifpi.num_header_words32)[0] = optional_msg_word | uhd::byteswap(optional_msg_word);
+ _lens.push_back(ifpi.num_packet_words32*sizeof(boost::uint32_t));
+ }
+
+ uhd::transport::managed_recv_buffer::sptr get_recv_buff(double){
+ if (_mems.empty()) return uhd::transport::managed_recv_buffer::sptr(); //timeout
+ _mrbs.push_back(dummy_mrb());
+ uhd::transport::managed_recv_buffer::sptr mrb = _mrbs.back().get_new(_mems.front(), _lens.front());
+ _mems.pop_front();
+ _lens.pop_front();
+ return mrb;
+ }
+
+private:
+ std::list<boost::shared_array<char> > _mems;
+ std::list<size_t> _lens;
+ std::list<dummy_mrb> _mrbs; //list means no-realloc
+ uhd::otw_type_t _otw_type;
+};
+
+////////////////////////////////////////////////////////////////////////
+BOOST_AUTO_TEST_CASE(test_sph_recv_one_channel_normal){
+////////////////////////////////////////////////////////////////////////
+ uhd::otw_type_t otw_type;
+ otw_type.width = 16;
+ otw_type.shift = 0;
+ otw_type.byteorder = uhd::otw_type_t::BO_BIG_ENDIAN;
+
+ dummy_recv_xport_class dummy_recv_xport(otw_type);
+ uhd::transport::vrt::if_packet_info_t ifpi;
+ ifpi.packet_type = uhd::transport::vrt::if_packet_info_t::PACKET_TYPE_DATA;
+ ifpi.num_payload_words32 = 0;
+ ifpi.packet_count = 0;
+ ifpi.sob = true;
+ ifpi.eob = false;
+ ifpi.has_sid = false;
+ ifpi.has_cid = false;
+ ifpi.has_tsi = true;
+ ifpi.has_tsf = true;
+ ifpi.tsi = 0;
+ ifpi.tsf = 0;
+ ifpi.has_tlr = false;
+
+ static const double TICK_RATE = 100e6;
+ static const double SAMP_RATE = 10e6;
+ static const size_t NUM_PKTS_TO_TEST = 30;
+
+ //generate a bunch of packets
+ for (size_t i = 0; i < NUM_PKTS_TO_TEST; i++){
+ ifpi.num_payload_words32 = 10 + i%10;
+ dummy_recv_xport.push_back_packet(ifpi);
+ ifpi.packet_count++;
+ ifpi.tsf += ifpi.num_payload_words32*size_t(TICK_RATE/SAMP_RATE);
+ }
+
+ //create the super receive packet handler
+ uhd::transport::sph::recv_packet_handler handler(1);
+ handler.set_vrt_unpacker(&uhd::transport::vrt::if_hdr_unpack_be);
+ handler.set_tick_rate(TICK_RATE);
+ handler.set_samp_rate(SAMP_RATE);
+ handler.set_xport_chan_get_buff(0, boost::bind(&dummy_recv_xport_class::get_recv_buff, &dummy_recv_xport, _1));
+ handler.set_converter(otw_type);
+
+ //check the received packets
+ size_t num_accum_samps = 0;
+ std::vector<std::complex<float> > buff(20);
+ uhd::rx_metadata_t metadata;
+ for (size_t i = 0; i < NUM_PKTS_TO_TEST; i++){
+ std::cout << "data check " << i << std::endl;
+ size_t num_samps_ret = handler.recv(
+ &buff.front(), buff.size(), metadata,
+ uhd::io_type_t::COMPLEX_FLOAT32,
+ uhd::device::RECV_MODE_ONE_PACKET, 1.0
+ );
+ BOOST_CHECK_EQUAL(metadata.error_code, uhd::rx_metadata_t::ERROR_CODE_NONE);
+ BOOST_CHECK(not metadata.more_fragments);
+ BOOST_CHECK(metadata.has_time_spec);
+ BOOST_CHECK_TS_CLOSE(metadata.time_spec, uhd::time_spec_t(0, num_accum_samps, SAMP_RATE));
+ BOOST_CHECK_EQUAL(num_samps_ret, 10 + i%10);
+ num_accum_samps += num_samps_ret;
+ }
+
+ //subsequent receives should be a timeout
+ for (size_t i = 0; i < 3; i++){
+ std::cout << "timeout check " << i << std::endl;
+ handler.recv(
+ &buff.front(), buff.size(), metadata,
+ uhd::io_type_t::COMPLEX_FLOAT32,
+ uhd::device::RECV_MODE_ONE_PACKET, 1.0
+ );
+ BOOST_CHECK_EQUAL(metadata.error_code, uhd::rx_metadata_t::ERROR_CODE_TIMEOUT);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////
+BOOST_AUTO_TEST_CASE(test_sph_recv_one_channel_sequence_error){
+////////////////////////////////////////////////////////////////////////
+ uhd::otw_type_t otw_type;
+ otw_type.width = 16;
+ otw_type.shift = 0;
+ otw_type.byteorder = uhd::otw_type_t::BO_BIG_ENDIAN;
+
+ dummy_recv_xport_class dummy_recv_xport(otw_type);
+ uhd::transport::vrt::if_packet_info_t ifpi;
+ ifpi.packet_type = uhd::transport::vrt::if_packet_info_t::PACKET_TYPE_DATA;
+ ifpi.num_payload_words32 = 0;
+ ifpi.packet_count = 0;
+ ifpi.sob = true;
+ ifpi.eob = false;
+ ifpi.has_sid = false;
+ ifpi.has_cid = false;
+ ifpi.has_tsi = true;
+ ifpi.has_tsf = true;
+ ifpi.tsi = 0;
+ ifpi.tsf = 0;
+ ifpi.has_tlr = false;
+
+ static const double TICK_RATE = 100e6;
+ static const double SAMP_RATE = 10e6;
+ static const size_t NUM_PKTS_TO_TEST = 30;
+
+ //generate a bunch of packets
+ for (size_t i = 0; i < NUM_PKTS_TO_TEST; i++){
+ ifpi.num_payload_words32 = 10 + i%10;
+ if (i != NUM_PKTS_TO_TEST/2){ //simulate a lost packet
+ dummy_recv_xport.push_back_packet(ifpi);
+ }
+ ifpi.packet_count++;
+ ifpi.tsf += ifpi.num_payload_words32*size_t(TICK_RATE/SAMP_RATE);
+ }
+
+ //create the super receive packet handler
+ uhd::transport::sph::recv_packet_handler handler(1);
+ handler.set_vrt_unpacker(&uhd::transport::vrt::if_hdr_unpack_be);
+ handler.set_tick_rate(TICK_RATE);
+ handler.set_samp_rate(SAMP_RATE);
+ handler.set_xport_chan_get_buff(0, boost::bind(&dummy_recv_xport_class::get_recv_buff, &dummy_recv_xport, _1));
+ handler.set_converter(otw_type);
+
+ //check the received packets
+ size_t num_accum_samps = 0;
+ std::vector<std::complex<float> > buff(20);
+ uhd::rx_metadata_t metadata;
+ for (size_t i = 0; i < NUM_PKTS_TO_TEST; i++){
+ std::cout << "data check " << i << std::endl;
+ size_t num_samps_ret = handler.recv(
+ &buff.front(), buff.size(), metadata,
+ uhd::io_type_t::COMPLEX_FLOAT32,
+ uhd::device::RECV_MODE_ONE_PACKET, 1.0
+ );
+ if (i == NUM_PKTS_TO_TEST/2){
+ //must get the soft overflow here
+ BOOST_REQUIRE(metadata.error_code == uhd::rx_metadata_t::ERROR_CODE_OVERFLOW);
+ BOOST_CHECK_TS_CLOSE(metadata.time_spec, uhd::time_spec_t(0, num_accum_samps, SAMP_RATE));
+ num_accum_samps += 10 + i%10;
+ }
+ else{
+ BOOST_CHECK_EQUAL(metadata.error_code, uhd::rx_metadata_t::ERROR_CODE_NONE);
+ BOOST_CHECK(not metadata.more_fragments);
+ BOOST_CHECK(metadata.has_time_spec);
+ BOOST_CHECK_TS_CLOSE(metadata.time_spec, uhd::time_spec_t(0, num_accum_samps, SAMP_RATE));
+ BOOST_CHECK_EQUAL(num_samps_ret, 10 + i%10);
+ num_accum_samps += num_samps_ret;
+ }
+ }
+
+ //subsequent receives should be a timeout
+ for (size_t i = 0; i < 3; i++){
+ std::cout << "timeout check " << i << std::endl;
+ handler.recv(
+ &buff.front(), buff.size(), metadata,
+ uhd::io_type_t::COMPLEX_FLOAT32,
+ uhd::device::RECV_MODE_ONE_PACKET, 1.0
+ );
+ BOOST_CHECK_EQUAL(metadata.error_code, uhd::rx_metadata_t::ERROR_CODE_TIMEOUT);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////
+BOOST_AUTO_TEST_CASE(test_sph_recv_one_channel_inline_message){
+////////////////////////////////////////////////////////////////////////
+ uhd::otw_type_t otw_type;
+ otw_type.width = 16;
+ otw_type.shift = 0;
+ otw_type.byteorder = uhd::otw_type_t::BO_BIG_ENDIAN;
+
+ dummy_recv_xport_class dummy_recv_xport(otw_type);
+ uhd::transport::vrt::if_packet_info_t ifpi;
+ ifpi.packet_type = uhd::transport::vrt::if_packet_info_t::PACKET_TYPE_DATA;
+ ifpi.num_payload_words32 = 0;
+ ifpi.packet_count = 0;
+ ifpi.sob = true;
+ ifpi.eob = false;
+ ifpi.has_sid = false;
+ ifpi.has_cid = false;
+ ifpi.has_tsi = true;
+ ifpi.has_tsf = true;
+ ifpi.tsi = 0;
+ ifpi.tsf = 0;
+ ifpi.has_tlr = false;
+
+ static const double TICK_RATE = 100e6;
+ static const double SAMP_RATE = 10e6;
+ static const size_t NUM_PKTS_TO_TEST = 30;
+
+ //generate a bunch of packets
+ for (size_t i = 0; i < NUM_PKTS_TO_TEST; i++){
+ ifpi.num_payload_words32 = 10 + i%10;
+ dummy_recv_xport.push_back_packet(ifpi);
+ ifpi.packet_count++;
+ ifpi.tsf += ifpi.num_payload_words32*size_t(TICK_RATE/SAMP_RATE);
+
+ //simulate overflow
+ if (i == NUM_PKTS_TO_TEST/2){
+ ifpi.packet_type = uhd::transport::vrt::if_packet_info_t::PACKET_TYPE_EXTENSION;
+ ifpi.num_payload_words32 = 1;
+ dummy_recv_xport.push_back_packet(ifpi, uhd::rx_metadata_t::ERROR_CODE_OVERFLOW);
+ ifpi.packet_count++;
+ ifpi.packet_type = uhd::transport::vrt::if_packet_info_t::PACKET_TYPE_DATA;
+ }
+ }
+
+ //create the super receive packet handler
+ uhd::transport::sph::recv_packet_handler handler(1);
+ handler.set_vrt_unpacker(&uhd::transport::vrt::if_hdr_unpack_be);
+ handler.set_tick_rate(TICK_RATE);
+ handler.set_samp_rate(SAMP_RATE);
+ handler.set_xport_chan_get_buff(0, boost::bind(&dummy_recv_xport_class::get_recv_buff, &dummy_recv_xport, _1));
+ handler.set_converter(otw_type);
+
+ //check the received packets
+ size_t num_accum_samps = 0;
+ std::vector<std::complex<float> > buff(20);
+ uhd::rx_metadata_t metadata;
+ for (size_t i = 0; i < NUM_PKTS_TO_TEST; i++){
+ std::cout << "data check " << i << std::endl;
+ size_t num_samps_ret = handler.recv(
+ &buff.front(), buff.size(), metadata,
+ uhd::io_type_t::COMPLEX_FLOAT32,
+ uhd::device::RECV_MODE_ONE_PACKET, 1.0
+ );
+ BOOST_CHECK_EQUAL(metadata.error_code, uhd::rx_metadata_t::ERROR_CODE_NONE);
+ BOOST_CHECK(not metadata.more_fragments);
+ BOOST_CHECK(metadata.has_time_spec);
+ BOOST_CHECK_TS_CLOSE(metadata.time_spec, uhd::time_spec_t(0, num_accum_samps, SAMP_RATE));
+ BOOST_CHECK_EQUAL(num_samps_ret, 10 + i%10);
+ num_accum_samps += num_samps_ret;
+ if (i == NUM_PKTS_TO_TEST/2){
+ handler.recv(
+ &buff.front(), buff.size(), metadata,
+ uhd::io_type_t::COMPLEX_FLOAT32,
+ uhd::device::RECV_MODE_ONE_PACKET, 1.0
+ );
+ std::cout << "metadata.error_code " << metadata.error_code << std::endl;
+ BOOST_REQUIRE(metadata.error_code == uhd::rx_metadata_t::ERROR_CODE_OVERFLOW);
+ BOOST_CHECK_TS_CLOSE(metadata.time_spec, uhd::time_spec_t(0, num_accum_samps, SAMP_RATE));
+ }
+ }
+
+ //subsequent receives should be a timeout
+ for (size_t i = 0; i < 3; i++){
+ std::cout << "timeout check " << i << std::endl;
+ handler.recv(
+ &buff.front(), buff.size(), metadata,
+ uhd::io_type_t::COMPLEX_FLOAT32,
+ uhd::device::RECV_MODE_ONE_PACKET, 1.0
+ );
+ BOOST_CHECK_EQUAL(metadata.error_code, uhd::rx_metadata_t::ERROR_CODE_TIMEOUT);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////
+BOOST_AUTO_TEST_CASE(test_sph_recv_multi_channel_normal){
+////////////////////////////////////////////////////////////////////////
+ uhd::otw_type_t otw_type;
+ otw_type.width = 16;
+ otw_type.shift = 0;
+ otw_type.byteorder = uhd::otw_type_t::BO_BIG_ENDIAN;
+
+ uhd::transport::vrt::if_packet_info_t ifpi;
+ ifpi.packet_type = uhd::transport::vrt::if_packet_info_t::PACKET_TYPE_DATA;
+ ifpi.num_payload_words32 = 0;
+ ifpi.packet_count = 0;
+ ifpi.sob = true;
+ ifpi.eob = false;
+ ifpi.has_sid = false;
+ ifpi.has_cid = false;
+ ifpi.has_tsi = true;
+ ifpi.has_tsf = true;
+ ifpi.tsi = 0;
+ ifpi.tsf = 0;
+ ifpi.has_tlr = false;
+
+ static const double TICK_RATE = 100e6;
+ static const double SAMP_RATE = 10e6;
+ static const size_t NUM_PKTS_TO_TEST = 30;
+ static const size_t NUM_SAMPS_PER_BUFF = 20;
+ static const size_t NCHANNELS = 4;
+
+ std::vector<dummy_recv_xport_class> dummy_recv_xports(NCHANNELS, dummy_recv_xport_class(otw_type));
+
+ //generate a bunch of packets
+ for (size_t i = 0; i < NUM_PKTS_TO_TEST; i++){
+ ifpi.num_payload_words32 = 10 + i%10;
+ for (size_t ch = 0; ch < NCHANNELS; ch++){
+ dummy_recv_xports[ch].push_back_packet(ifpi);
+ }
+ ifpi.packet_count++;
+ ifpi.tsf += ifpi.num_payload_words32*size_t(TICK_RATE/SAMP_RATE);
+ }
+
+ //create the super receive packet handler
+ uhd::transport::sph::recv_packet_handler handler(NCHANNELS);
+ handler.set_vrt_unpacker(&uhd::transport::vrt::if_hdr_unpack_be);
+ handler.set_tick_rate(TICK_RATE);
+ handler.set_samp_rate(SAMP_RATE);
+ for (size_t ch = 0; ch < NCHANNELS; ch++){
+ handler.set_xport_chan_get_buff(ch, boost::bind(&dummy_recv_xport_class::get_recv_buff, &dummy_recv_xports[ch], _1));
+ }
+ handler.set_converter(otw_type);
+
+ //check the received packets
+ size_t num_accum_samps = 0;
+ std::vector<std::complex<float> > mem(NUM_SAMPS_PER_BUFF*NCHANNELS);
+ std::vector<std::complex<float> *> buffs(NCHANNELS);
+ for (size_t ch = 0; ch < NCHANNELS; ch++){
+ buffs[ch] = &mem[ch*NUM_SAMPS_PER_BUFF];
+ }
+ uhd::rx_metadata_t metadata;
+ for (size_t i = 0; i < NUM_PKTS_TO_TEST; i++){
+ std::cout << "data check " << i << std::endl;
+ size_t num_samps_ret = handler.recv(
+ buffs, NUM_SAMPS_PER_BUFF, metadata,
+ uhd::io_type_t::COMPLEX_FLOAT32,
+ uhd::device::RECV_MODE_ONE_PACKET, 1.0
+ );
+ BOOST_CHECK_EQUAL(metadata.error_code, uhd::rx_metadata_t::ERROR_CODE_NONE);
+ BOOST_CHECK(not metadata.more_fragments);
+ BOOST_CHECK(metadata.has_time_spec);
+ BOOST_CHECK_TS_CLOSE(metadata.time_spec, uhd::time_spec_t(0, num_accum_samps, SAMP_RATE));
+ BOOST_CHECK_EQUAL(num_samps_ret, 10 + i%10);
+ num_accum_samps += num_samps_ret;
+ }
+
+ //subsequent receives should be a timeout
+ for (size_t i = 0; i < 3; i++){
+ std::cout << "timeout check " << i << std::endl;
+ handler.recv(
+ buffs, NUM_SAMPS_PER_BUFF, metadata,
+ uhd::io_type_t::COMPLEX_FLOAT32,
+ uhd::device::RECV_MODE_ONE_PACKET, 1.0
+ );
+ BOOST_CHECK_EQUAL(metadata.error_code, uhd::rx_metadata_t::ERROR_CODE_TIMEOUT);
+ }
+
+}
+
+////////////////////////////////////////////////////////////////////////
+BOOST_AUTO_TEST_CASE(test_sph_recv_multi_channel_sequence_error){
+////////////////////////////////////////////////////////////////////////
+ uhd::otw_type_t otw_type;
+ otw_type.width = 16;
+ otw_type.shift = 0;
+ otw_type.byteorder = uhd::otw_type_t::BO_BIG_ENDIAN;
+
+ uhd::transport::vrt::if_packet_info_t ifpi;
+ ifpi.packet_type = uhd::transport::vrt::if_packet_info_t::PACKET_TYPE_DATA;
+ ifpi.num_payload_words32 = 0;
+ ifpi.packet_count = 0;
+ ifpi.sob = true;
+ ifpi.eob = false;
+ ifpi.has_sid = false;
+ ifpi.has_cid = false;
+ ifpi.has_tsi = true;
+ ifpi.has_tsf = true;
+ ifpi.tsi = 0;
+ ifpi.tsf = 0;
+ ifpi.has_tlr = false;
+
+ static const double TICK_RATE = 100e6;
+ static const double SAMP_RATE = 10e6;
+ static const size_t NUM_PKTS_TO_TEST = 30;
+ static const size_t NUM_SAMPS_PER_BUFF = 20;
+ static const size_t NCHANNELS = 4;
+
+ std::vector<dummy_recv_xport_class> dummy_recv_xports(NCHANNELS, dummy_recv_xport_class(otw_type));
+
+ //generate a bunch of packets
+ for (size_t i = 0; i < NUM_PKTS_TO_TEST; i++){
+ ifpi.num_payload_words32 = 10 + i%10;
+ for (size_t ch = 0; ch < NCHANNELS; ch++){
+ if (i == NUM_PKTS_TO_TEST/2 and ch == 2){
+ continue; //simulates a lost packet
+ }
+ dummy_recv_xports[ch].push_back_packet(ifpi);
+ }
+ ifpi.packet_count++;
+ ifpi.tsf += ifpi.num_payload_words32*size_t(TICK_RATE/SAMP_RATE);
+ }
+
+ //create the super receive packet handler
+ uhd::transport::sph::recv_packet_handler handler(NCHANNELS);
+ handler.set_vrt_unpacker(&uhd::transport::vrt::if_hdr_unpack_be);
+ handler.set_tick_rate(TICK_RATE);
+ handler.set_samp_rate(SAMP_RATE);
+ for (size_t ch = 0; ch < NCHANNELS; ch++){
+ handler.set_xport_chan_get_buff(ch, boost::bind(&dummy_recv_xport_class::get_recv_buff, &dummy_recv_xports[ch], _1));
+ }
+ handler.set_converter(otw_type);
+
+ //check the received packets
+ size_t num_accum_samps = 0;
+ std::vector<std::complex<float> > mem(NUM_SAMPS_PER_BUFF*NCHANNELS);
+ std::vector<std::complex<float> *> buffs(NCHANNELS);
+ for (size_t ch = 0; ch < NCHANNELS; ch++){
+ buffs[ch] = &mem[ch*NUM_SAMPS_PER_BUFF];
+ }
+ uhd::rx_metadata_t metadata;
+ for (size_t i = 0; i < NUM_PKTS_TO_TEST; i++){
+ std::cout << "data check " << i << std::endl;
+ size_t num_samps_ret = handler.recv(
+ buffs, NUM_SAMPS_PER_BUFF, metadata,
+ uhd::io_type_t::COMPLEX_FLOAT32,
+ uhd::device::RECV_MODE_ONE_PACKET, 1.0
+ );
+ if (i == NUM_PKTS_TO_TEST/2){
+ //must get the soft overflow here
+ BOOST_REQUIRE(metadata.error_code == uhd::rx_metadata_t::ERROR_CODE_OVERFLOW);
+ BOOST_CHECK_TS_CLOSE(metadata.time_spec, uhd::time_spec_t(0, num_accum_samps, SAMP_RATE));
+ num_accum_samps += 10 + i%10;
+ }
+ else{
+ BOOST_CHECK_EQUAL(metadata.error_code, uhd::rx_metadata_t::ERROR_CODE_NONE);
+ BOOST_CHECK(not metadata.more_fragments);
+ BOOST_CHECK(metadata.has_time_spec);
+ BOOST_CHECK_TS_CLOSE(metadata.time_spec, uhd::time_spec_t(0, num_accum_samps, SAMP_RATE));
+ BOOST_CHECK_EQUAL(num_samps_ret, 10 + i%10);
+ num_accum_samps += num_samps_ret;
+ }
+ }
+
+ //subsequent receives should be a timeout
+ for (size_t i = 0; i < 3; i++){
+ std::cout << "timeout check " << i << std::endl;
+ handler.recv(
+ buffs, NUM_SAMPS_PER_BUFF, metadata,
+ uhd::io_type_t::COMPLEX_FLOAT32,
+ uhd::device::RECV_MODE_ONE_PACKET, 1.0
+ );
+ BOOST_CHECK_EQUAL(metadata.error_code, uhd::rx_metadata_t::ERROR_CODE_TIMEOUT);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////
+BOOST_AUTO_TEST_CASE(test_sph_recv_multi_channel_time_error){
+////////////////////////////////////////////////////////////////////////
+ uhd::otw_type_t otw_type;
+ otw_type.width = 16;
+ otw_type.shift = 0;
+ otw_type.byteorder = uhd::otw_type_t::BO_BIG_ENDIAN;
+
+ uhd::transport::vrt::if_packet_info_t ifpi;
+ ifpi.packet_type = uhd::transport::vrt::if_packet_info_t::PACKET_TYPE_DATA;
+ ifpi.num_payload_words32 = 0;
+ ifpi.packet_count = 0;
+ ifpi.sob = true;
+ ifpi.eob = false;
+ ifpi.has_sid = false;
+ ifpi.has_cid = false;
+ ifpi.has_tsi = true;
+ ifpi.has_tsf = true;
+ ifpi.tsi = 0;
+ ifpi.tsf = 0;
+ ifpi.has_tlr = false;
+
+ static const double TICK_RATE = 100e6;
+ static const double SAMP_RATE = 10e6;
+ static const size_t NUM_PKTS_TO_TEST = 30;
+ static const size_t NUM_SAMPS_PER_BUFF = 20;
+ static const size_t NCHANNELS = 4;
+
+ std::vector<dummy_recv_xport_class> dummy_recv_xports(NCHANNELS, dummy_recv_xport_class(otw_type));
+
+ //generate a bunch of packets
+ for (size_t i = 0; i < NUM_PKTS_TO_TEST; i++){
+ ifpi.num_payload_words32 = 10 + i%10;
+ for (size_t ch = 0; ch < NCHANNELS; ch++){
+ dummy_recv_xports[ch].push_back_packet(ifpi);
+ }
+ ifpi.packet_count++;
+ ifpi.tsf += ifpi.num_payload_words32*size_t(TICK_RATE/SAMP_RATE);
+ if (i == NUM_PKTS_TO_TEST/2){
+ ifpi.tsf = 0; //simulate the user changing the time
+ }
+ }
+
+ //create the super receive packet handler
+ uhd::transport::sph::recv_packet_handler handler(NCHANNELS);
+ handler.set_vrt_unpacker(&uhd::transport::vrt::if_hdr_unpack_be);
+ handler.set_tick_rate(TICK_RATE);
+ handler.set_samp_rate(SAMP_RATE);
+ for (size_t ch = 0; ch < NCHANNELS; ch++){
+ handler.set_xport_chan_get_buff(ch, boost::bind(&dummy_recv_xport_class::get_recv_buff, &dummy_recv_xports[ch], _1));
+ }
+ handler.set_converter(otw_type);
+
+ //check the received packets
+ size_t num_accum_samps = 0;
+ std::vector<std::complex<float> > mem(NUM_SAMPS_PER_BUFF*NCHANNELS);
+ std::vector<std::complex<float> *> buffs(NCHANNELS);
+ for (size_t ch = 0; ch < NCHANNELS; ch++){
+ buffs[ch] = &mem[ch*NUM_SAMPS_PER_BUFF];
+ }
+ uhd::rx_metadata_t metadata;
+ for (size_t i = 0; i < NUM_PKTS_TO_TEST; i++){
+ std::cout << "data check " << i << std::endl;
+ size_t num_samps_ret = handler.recv(
+ buffs, NUM_SAMPS_PER_BUFF, metadata,
+ uhd::io_type_t::COMPLEX_FLOAT32,
+ uhd::device::RECV_MODE_ONE_PACKET, 1.0
+ );
+ BOOST_CHECK_EQUAL(metadata.error_code, uhd::rx_metadata_t::ERROR_CODE_NONE);
+ BOOST_CHECK(not metadata.more_fragments);
+ BOOST_CHECK(metadata.has_time_spec);
+ BOOST_CHECK_TS_CLOSE(metadata.time_spec, uhd::time_spec_t(0, num_accum_samps, SAMP_RATE));
+ BOOST_CHECK_EQUAL(num_samps_ret, 10 + i%10);
+ num_accum_samps += num_samps_ret;
+ if (i == NUM_PKTS_TO_TEST/2){
+ num_accum_samps = 0; //simulate the user changing the time
+ }
+ }
+
+ //subsequent receives should be a timeout
+ for (size_t i = 0; i < 3; i++){
+ std::cout << "timeout check " << i << std::endl;
+ handler.recv(
+ buffs, NUM_SAMPS_PER_BUFF, metadata,
+ uhd::io_type_t::COMPLEX_FLOAT32,
+ uhd::device::RECV_MODE_ONE_PACKET, 1.0
+ );
+ BOOST_CHECK_EQUAL(metadata.error_code, uhd::rx_metadata_t::ERROR_CODE_TIMEOUT);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////
+BOOST_AUTO_TEST_CASE(test_sph_recv_multi_channel_fragment){
+////////////////////////////////////////////////////////////////////////
+ uhd::otw_type_t otw_type;
+ otw_type.width = 16;
+ otw_type.shift = 0;
+ otw_type.byteorder = uhd::otw_type_t::BO_BIG_ENDIAN;
+
+ uhd::transport::vrt::if_packet_info_t ifpi;
+ ifpi.packet_type = uhd::transport::vrt::if_packet_info_t::PACKET_TYPE_DATA;
+ ifpi.num_payload_words32 = 0;
+ ifpi.packet_count = 0;
+ ifpi.sob = true;
+ ifpi.eob = false;
+ ifpi.has_sid = false;
+ ifpi.has_cid = false;
+ ifpi.has_tsi = true;
+ ifpi.has_tsf = true;
+ ifpi.tsi = 0;
+ ifpi.tsf = 0;
+ ifpi.has_tlr = false;
+
+ static const double TICK_RATE = 100e6;
+ static const double SAMP_RATE = 10e6;
+ static const size_t NUM_PKTS_TO_TEST = 30;
+ static const size_t NUM_SAMPS_PER_BUFF = 10;
+ static const size_t NCHANNELS = 4;
+
+ std::vector<dummy_recv_xport_class> dummy_recv_xports(NCHANNELS, dummy_recv_xport_class(otw_type));
+
+ //generate a bunch of packets
+ for (size_t i = 0; i < NUM_PKTS_TO_TEST; i++){
+ ifpi.num_payload_words32 = 10 + i%10;
+ for (size_t ch = 0; ch < NCHANNELS; ch++){
+ dummy_recv_xports[ch].push_back_packet(ifpi);
+ }
+ ifpi.packet_count++;
+ ifpi.tsf += ifpi.num_payload_words32*size_t(TICK_RATE/SAMP_RATE);
+ }
+
+ //create the super receive packet handler
+ uhd::transport::sph::recv_packet_handler handler(NCHANNELS);
+ handler.set_vrt_unpacker(&uhd::transport::vrt::if_hdr_unpack_be);
+ handler.set_tick_rate(TICK_RATE);
+ handler.set_samp_rate(SAMP_RATE);
+ for (size_t ch = 0; ch < NCHANNELS; ch++){
+ handler.set_xport_chan_get_buff(ch, boost::bind(&dummy_recv_xport_class::get_recv_buff, &dummy_recv_xports[ch], _1));
+ }
+ handler.set_converter(otw_type);
+
+ //check the received packets
+ size_t num_accum_samps = 0;
+ std::vector<std::complex<float> > mem(NUM_SAMPS_PER_BUFF*NCHANNELS);
+ std::vector<std::complex<float> *> buffs(NCHANNELS);
+ for (size_t ch = 0; ch < NCHANNELS; ch++){
+ buffs[ch] = &mem[ch*NUM_SAMPS_PER_BUFF];
+ }
+ uhd::rx_metadata_t metadata;
+ for (size_t i = 0; i < NUM_PKTS_TO_TEST; i++){
+ std::cout << "data check " << i << std::endl;
+ size_t num_samps_ret = handler.recv(
+ buffs, NUM_SAMPS_PER_BUFF, metadata,
+ uhd::io_type_t::COMPLEX_FLOAT32,
+ uhd::device::RECV_MODE_ONE_PACKET, 1.0
+ );
+ BOOST_CHECK_EQUAL(metadata.error_code, uhd::rx_metadata_t::ERROR_CODE_NONE);
+ BOOST_CHECK(metadata.has_time_spec);
+ BOOST_CHECK_TS_CLOSE(metadata.time_spec, uhd::time_spec_t(0, num_accum_samps, SAMP_RATE));
+ BOOST_CHECK_EQUAL(num_samps_ret, 10);
+ num_accum_samps += num_samps_ret;
+
+ if (not metadata.more_fragments) continue;
+
+ num_samps_ret = handler.recv(
+ buffs, NUM_SAMPS_PER_BUFF, metadata,
+ uhd::io_type_t::COMPLEX_FLOAT32,
+ uhd::device::RECV_MODE_ONE_PACKET, 1.0
+ );
+ BOOST_CHECK_EQUAL(metadata.error_code, uhd::rx_metadata_t::ERROR_CODE_NONE);
+ BOOST_CHECK(not metadata.more_fragments);
+ BOOST_CHECK_EQUAL(metadata.fragment_offset, 10);
+ BOOST_CHECK(metadata.has_time_spec);
+ BOOST_CHECK_TS_CLOSE(metadata.time_spec, uhd::time_spec_t(0, num_accum_samps, SAMP_RATE));
+ BOOST_CHECK_EQUAL(num_samps_ret, i%10);
+ num_accum_samps += num_samps_ret;
+ }
+
+ //subsequent receives should be a timeout
+ for (size_t i = 0; i < 3; i++){
+ std::cout << "timeout check " << i << std::endl;
+ handler.recv(
+ buffs, NUM_SAMPS_PER_BUFF, metadata,
+ uhd::io_type_t::COMPLEX_FLOAT32,
+ uhd::device::RECV_MODE_ONE_PACKET, 1.0
+ );
+ BOOST_CHECK_EQUAL(metadata.error_code, uhd::rx_metadata_t::ERROR_CODE_TIMEOUT);
+ }
+
+}
diff --git a/host/tests/sph_send_test.cpp b/host/tests/sph_send_test.cpp
new file mode 100644
index 000000000..ed2f54371
--- /dev/null
+++ b/host/tests/sph_send_test.cpp
@@ -0,0 +1,204 @@
+//
+// 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 <boost/test/unit_test.hpp>
+#include "../lib/transport/super_send_packet_handler.hpp"
+#include <boost/shared_array.hpp>
+#include <boost/bind.hpp>
+#include <complex>
+#include <vector>
+#include <list>
+
+#define BOOST_CHECK_TS_CLOSE(a, b) \
+ BOOST_CHECK_CLOSE((a).get_real_secs(), (b).get_real_secs(), 0.001)
+
+/***********************************************************************
+ * A dummy managed send buffer for testing
+ **********************************************************************/
+class dummy_msb : public uhd::transport::managed_send_buffer{
+public:
+ void commit(size_t len){
+ if (len == 0) return;
+ *_len = len;
+ }
+
+ sptr get_new(boost::shared_array<char> mem, size_t *len){
+ _mem = mem;
+ _len = len;
+ return make_managed_buffer(this);
+ }
+
+private:
+ void *get_buff(void) const{return _mem.get();}
+ size_t get_size(void) const{return *_len;}
+
+ boost::shared_array<char> _mem;
+ size_t *_len;
+};
+
+/***********************************************************************
+ * A dummy transport class to fill with fake data
+ **********************************************************************/
+class dummy_send_xport_class{
+public:
+ dummy_send_xport_class(const uhd::otw_type_t &otw_type){
+ _otw_type = otw_type;
+ }
+
+ void pop_front_packet(
+ uhd::transport::vrt::if_packet_info_t &ifpi
+ ){
+ ifpi.num_packet_words32 = _lens.front()/sizeof(boost::uint32_t);
+ if (_otw_type.byteorder == uhd::otw_type_t::BO_BIG_ENDIAN){
+ uhd::transport::vrt::if_hdr_unpack_be(reinterpret_cast<boost::uint32_t *>(_mems.front().get()), ifpi);
+ }
+ if (_otw_type.byteorder == uhd::otw_type_t::BO_LITTLE_ENDIAN){
+ uhd::transport::vrt::if_hdr_unpack_le(reinterpret_cast<boost::uint32_t *>(_mems.front().get()), ifpi);
+ }
+ _mems.pop_front();
+ _lens.pop_front();
+ }
+
+ uhd::transport::managed_send_buffer::sptr get_send_buff(double){
+ _msbs.push_back(dummy_msb());
+ _mems.push_back(boost::shared_array<char>(new char[1000]));
+ _lens.push_back(1000);
+ uhd::transport::managed_send_buffer::sptr mrb = _msbs.back().get_new(_mems.back(), &_lens.back());
+ return mrb;
+ }
+
+private:
+ std::list<boost::shared_array<char> > _mems;
+ std::list<size_t> _lens;
+ std::list<dummy_msb> _msbs; //list means no-realloc
+ uhd::otw_type_t _otw_type;
+};
+
+////////////////////////////////////////////////////////////////////////
+BOOST_AUTO_TEST_CASE(test_sph_send_one_channel_one_packet_mode){
+////////////////////////////////////////////////////////////////////////
+ uhd::otw_type_t otw_type;
+ otw_type.width = 16;
+ otw_type.shift = 0;
+ otw_type.byteorder = uhd::otw_type_t::BO_BIG_ENDIAN;
+
+ dummy_send_xport_class dummy_send_xport(otw_type);
+
+ static const double TICK_RATE = 100e6;
+ static const double SAMP_RATE = 10e6;
+ static const size_t NUM_PKTS_TO_TEST = 30;
+
+ //create the super send packet handler
+ uhd::transport::sph::send_packet_handler handler(1);
+ handler.set_vrt_packer(&uhd::transport::vrt::if_hdr_pack_be);
+ handler.set_tick_rate(TICK_RATE);
+ handler.set_samp_rate(SAMP_RATE);
+ handler.set_xport_chan_get_buff(0, boost::bind(&dummy_send_xport_class::get_send_buff, &dummy_send_xport, _1));
+ handler.set_converter(otw_type);
+ handler.set_max_samples_per_packet(20);
+
+ //allocate metadata and buffer
+ std::vector<std::complex<float> > buff(20);
+ uhd::tx_metadata_t metadata;
+ metadata.has_time_spec = true;
+ metadata.time_spec = uhd::time_spec_t(0.0);
+
+ //generate the test data
+ for (size_t i = 0; i < NUM_PKTS_TO_TEST; i++){
+ metadata.start_of_burst = (i == 0);
+ metadata.end_of_burst = (i == NUM_PKTS_TO_TEST-1);
+ const size_t num_sent = handler.send(
+ &buff.front(), 10 + i%10, metadata,
+ uhd::io_type_t::COMPLEX_FLOAT32,
+ uhd::device::SEND_MODE_ONE_PACKET, 1.0
+ );
+ BOOST_CHECK_EQUAL(num_sent, 10 + i%10);
+ metadata.time_spec += uhd::time_spec_t(0, num_sent, SAMP_RATE);
+ }
+
+ //check the sent packets
+ size_t num_accum_samps = 0;
+ uhd::transport::vrt::if_packet_info_t ifpi;
+ for (size_t i = 0; i < NUM_PKTS_TO_TEST; i++){
+ std::cout << "data check " << i << std::endl;
+ dummy_send_xport.pop_front_packet(ifpi);
+ BOOST_CHECK_EQUAL(ifpi.num_payload_words32, 10+i%10);
+ BOOST_CHECK(ifpi.has_tsi);
+ BOOST_CHECK(ifpi.has_tsf);
+ BOOST_CHECK_EQUAL(ifpi.tsi, 0);
+ BOOST_CHECK_EQUAL(ifpi.tsf, num_accum_samps*TICK_RATE/SAMP_RATE);
+ BOOST_CHECK_EQUAL(ifpi.sob, i == 0);
+ BOOST_CHECK_EQUAL(ifpi.eob, i == NUM_PKTS_TO_TEST-1);
+ num_accum_samps += ifpi.num_payload_words32;
+ }
+}
+
+////////////////////////////////////////////////////////////////////////
+BOOST_AUTO_TEST_CASE(test_sph_send_one_channel_full_buffer_mode){
+////////////////////////////////////////////////////////////////////////
+ uhd::otw_type_t otw_type;
+ otw_type.width = 16;
+ otw_type.shift = 0;
+ otw_type.byteorder = uhd::otw_type_t::BO_BIG_ENDIAN;
+
+ dummy_send_xport_class dummy_send_xport(otw_type);
+
+ static const double TICK_RATE = 100e6;
+ static const double SAMP_RATE = 10e6;
+ static const size_t NUM_PKTS_TO_TEST = 30;
+
+ //create the super send packet handler
+ uhd::transport::sph::send_packet_handler handler(1);
+ handler.set_vrt_packer(&uhd::transport::vrt::if_hdr_pack_be);
+ handler.set_tick_rate(TICK_RATE);
+ handler.set_samp_rate(SAMP_RATE);
+ handler.set_xport_chan_get_buff(0, boost::bind(&dummy_send_xport_class::get_send_buff, &dummy_send_xport, _1));
+ handler.set_converter(otw_type);
+ handler.set_max_samples_per_packet(20);
+
+ //allocate metadata and buffer
+ std::vector<std::complex<float> > buff(20*NUM_PKTS_TO_TEST);
+ uhd::tx_metadata_t metadata;
+ metadata.start_of_burst = true;
+ metadata.end_of_burst = true;
+ metadata.has_time_spec = true;
+ metadata.time_spec = uhd::time_spec_t(0.0);
+
+ //generate the test data
+ const size_t num_sent = handler.send(
+ &buff.front(), buff.size(), metadata,
+ uhd::io_type_t::COMPLEX_FLOAT32,
+ uhd::device::SEND_MODE_FULL_BUFF, 1.0
+ );
+ BOOST_CHECK_EQUAL(num_sent, buff.size());
+
+ //check the sent packets
+ size_t num_accum_samps = 0;
+ uhd::transport::vrt::if_packet_info_t ifpi;
+ for (size_t i = 0; i < NUM_PKTS_TO_TEST; i++){
+ std::cout << "data check " << i << std::endl;
+ dummy_send_xport.pop_front_packet(ifpi);
+ BOOST_CHECK_EQUAL(ifpi.num_payload_words32, 20);
+ BOOST_CHECK(ifpi.has_tsi);
+ BOOST_CHECK(ifpi.has_tsf);
+ BOOST_CHECK_EQUAL(ifpi.tsi, 0);
+ BOOST_CHECK_EQUAL(ifpi.tsf, num_accum_samps*TICK_RATE/SAMP_RATE);
+ BOOST_CHECK_EQUAL(ifpi.sob, i == 0);
+ BOOST_CHECK_EQUAL(ifpi.eob, i == NUM_PKTS_TO_TEST-1);
+ num_accum_samps += ifpi.num_payload_words32;
+ }
+}
diff --git a/host/usrp_e_utils/usrp-e-loopback.c b/host/usrp_e_utils/usrp-e-loopback.c
index 0bd3d3100..762e55ac8 100644
--- a/host/usrp_e_utils/usrp-e-loopback.c
+++ b/host/usrp_e_utils/usrp-e-loopback.c
@@ -51,12 +51,27 @@ static int calc_checksum(struct pkt *p)
return sum;
}
+static struct timeval delta_time(struct timeval f, struct timeval s)
+{
+ struct timeval d;
+
+ if (f.tv_usec > s.tv_usec) {
+ d.tv_usec = f.tv_usec - s.tv_usec;
+ d.tv_sec = f.tv_sec - s.tv_sec;
+ } else {
+ d.tv_usec = f.tv_usec - s.tv_usec + 1e6;
+ d.tv_sec = f.tv_sec - s.tv_sec - 1;
+ }
+
+ return d;
+}
+
static void *read_thread(void *threadid)
{
int cnt, prev_seq_num, pkt_count, seq_num_failure;
struct pkt *p;
- unsigned long bytes_transfered, elapsed_seconds;
- struct timeval start_time, finish_time;
+ unsigned long bytes_transfered;
+ struct timeval start_time;
int rb_read;
printf("Greetings from the reading thread!\n");
@@ -125,11 +140,15 @@ static void *read_thread(void *threadid)
bytes_transfered += cnt;
if (bytes_transfered > (100 * 1000000)) {
+ struct timeval finish_time, d_time;
+ float elapsed_seconds;
+
gettimeofday(&finish_time, NULL);
- elapsed_seconds = finish_time.tv_sec - start_time.tv_sec;
+ d_time = delta_time(finish_time, start_time);
+ elapsed_seconds = (float)d_time.tv_sec + ((float)d_time.tv_usec * 1e-6f);
printf("RX data transfer rate = %f K Samples/second\n",
- (float) bytes_transfered / (float) elapsed_seconds / 4000);
+ (float) bytes_transfered / elapsed_seconds / 4000.0f);
start_time = finish_time;
diff --git a/host/usrp_e_utils/usrp-e-timed.c b/host/usrp_e_utils/usrp-e-timed.c
index 06dfdf512..407564429 100644
--- a/host/usrp_e_utils/usrp-e-timed.c
+++ b/host/usrp_e_utils/usrp-e-timed.c
@@ -273,7 +273,7 @@ static void *write_thread(void *threadid)
d_time = delta_time(finish_time, start_time);
- elapsed_seconds = (float)d_time.tv_sec - ((float)d_time.tv_usec * 1e-6f);
+ elapsed_seconds = (float)d_time.tv_sec + ((float)d_time.tv_usec * 1e-6f);
printf("Bytes transfered = %ld, elapsed seconds = %f\n", bytes_transfered, elapsed_seconds);
printf("TX data transfer rate = %f K Samples/second\n",