diff options
| -rw-r--r-- | host/docs/uhd.dox | 1 | ||||
| -rw-r--r-- | host/docs/vrt_chdr.dox | 83 | ||||
| -rw-r--r-- | host/include/uhd/transport/CMakeLists.txt | 4 | ||||
| -rw-r--r-- | host/include/uhd/transport/chdr.hpp | 113 | ||||
| -rw-r--r-- | host/include/uhd/transport/vrt_if_packet.hpp | 74 | ||||
| -rw-r--r-- | host/lib/transport/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | host/lib/transport/chdr.cpp | 182 | ||||
| -rw-r--r-- | host/tests/CMakeLists.txt | 3 | ||||
| -rw-r--r-- | host/tests/chdr_test.cpp | 141 | 
9 files changed, 598 insertions, 4 deletions
diff --git a/host/docs/uhd.dox b/host/docs/uhd.dox index 5b0738969..5cbe51a90 100644 --- a/host/docs/uhd.dox +++ b/host/docs/uhd.dox @@ -11,6 +11,7 @@ Some additional pages on developing UHD are also available here:  \li \subpage page_coding  \li \subpage page_stream +\li \subpage page_rtp  */  // vim:ft=doxygen: diff --git a/host/docs/vrt_chdr.dox b/host/docs/vrt_chdr.dox new file mode 100644 index 000000000..8ab177b21 --- /dev/null +++ b/host/docs/vrt_chdr.dox @@ -0,0 +1,83 @@ +/*! \page page_rtp Radio Transport Protocols + +\tableofcontents + +Radio transport protocols are used to exchange samples (or other items) between host and devices. +If one were to sniff Ethernet traffic between a USRP and a PC, the packets would conform to a +radio transport protocol. + +For USRP devices, two radio transport protocols are relevent: VRT (the VITA Radio Transport protocol) +and CVITA (compressed VITA), also known as CHDR. Generation-3 devices and the B200 use CHDR, the rest +use VRT. + +\section rtp_vrt VRT + +VRT is an open protocol defined by the VITA-49 standard. It was designed for interoperability, +and to allow different device types to work with different software stacks. + +VRT is a very verbose standard, and only a subset is implemented in UHD/USRPs. +The full standard is available from the VITA website: http://www.vita.com . + + +\section rtp_chdr CVITA (CHDR) + +For the third generation of Ettus devices, a new type transport protocol was designed. +It reduces the complexity of the original standard and uses a fixed-length 64-Bit header +for everything except the timestamp. Because this is a "compressed" form of VITA, it +was dubbed "Compressed VITA" (CVITA). The compressed header is called CHDR, which is why +the protocol is often called CHDR itself (pronounced like the cheese "cheddar"). + +By compressing all information into a 64-bit line, the header can efficiently be parsed +in newer FPGAs, where the common streaming protocol is 64-Bit AXI. The first line in a +packet already provides all necessary information to proceed. + +Some CHDR-specific functions can be found in: uhd::transport::vrt::chdr. + +The form of a CVITA packet is the following: + +Address (Bytes) | Length (Bytes) | Payload +----------------|----------------|---------------------------- +0               | 8              | Compressed Header (CHDR) +8               | 8              | Fractional Time (Optional!) +8/16            | -              | Data + +If there is no timestamp present, the data starts at address 8, otherwise, it starts at 16. + +The 64 Bits in the compressed header have the following meaning: + +Bits   | Meaning +-------|-------------------------------------------------- +63:62  | Packet Type +61     | Has fractional time stamp (1: Yes) +60     | End-of-burst or error flag +59:48  | 12-bit sequence number +47:32  | Total packet length in Bytes +31:0   | Stream ID (SID) + + +The packet type is determined mainly by the first two bits, although +the EOB or error flag are also taken into consideration: + +Bit 63 | Bit 62 | Bit 60 | Packet Type +-------|--------|--------|-------------- +0      | 0      | 0      | Data +0      | 0      | 1      | Data (End-of-burst) +0      | 1      | 0      | Flow Control +1      | 0      | 0      | Command Packet +1      | 1      | 0      | Command Response +1      | 1      | 1      | Command Response (Error) + +\section vrt_tools Tools + +For CHDR, we provide a Wireshark dissector under tools/chdr_dissector. It can be used +for Ethernet links as well as USB (e.g., for the B210). + +\section vrt_code Code + +Relevent code sections for the radio transport layer are: +* uhd::transport::vrt - Namespace for radio transport protocol related functions and definitions +* uhd::transport::vrt::chdr - Sub-namespace specifically for CVITA/CHDR +* uhd::sid_t - Datatype to represent SIDs + +*/ +// vim:ft=doxygen: diff --git a/host/include/uhd/transport/CMakeLists.txt b/host/include/uhd/transport/CMakeLists.txt index 2118674c6..623c179e9 100644 --- a/host/include/uhd/transport/CMakeLists.txt +++ b/host/include/uhd/transport/CMakeLists.txt @@ -1,5 +1,5 @@  # -# Copyright 2010-2013 Ettus Research LLC +# Copyright 2010-2014 Ettus Research LLC  #  # This program is free software: you can redistribute it and/or modify  # it under the terms of the GNU General Public License as published by @@ -15,11 +15,11 @@  # along with this program.  If not, see <http://www.gnu.org/licenses/>.  # -  UHD_INSTALL(FILES      bounded_buffer.hpp      bounded_buffer.ipp      buffer_pool.hpp +    chdr.hpp      if_addrs.hpp      udp_constants.hpp      udp_simple.hpp diff --git a/host/include/uhd/transport/chdr.hpp b/host/include/uhd/transport/chdr.hpp new file mode 100644 index 000000000..5e8cd58a9 --- /dev/null +++ b/host/include/uhd/transport/chdr.hpp @@ -0,0 +1,113 @@ +// +// Copyright 2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_UHD_TRANSPORT_CHDR_HPP +#define INCLUDED_UHD_TRANSPORT_CHDR_HPP + +#include <uhd/transport/vrt_if_packet.hpp> + +namespace uhd{ namespace transport{ namespace vrt{ + +/*! \brief CVITA/CHDR related function + * + * See \ref rtp_chdr for details on the CVITA/CHDR protocol. + * + * All packers take the host format into account. Choose the _le functions + * if the transport uses little endian format (e.g. PCIe) and the _be + * functions if the transport uses big endian format (e.g. Ethernet). + * + * Note 1: All packers assume there to be enough space at the address + * provided by \p packet_buff. See also \ref vrt_pack_contract. + * + * Note 2: All these packers assume the following options without checking them: + * - `if_packet_info.link_type == LINK_TYPE_CHDR` + * - `if_packet_info.has_cid == false` + * - `if_packet_info.has_sid == true` + * - `if_packet_info.has_tsi == false` + * - `if_packet_info.has_tlr == false` + * This relaxes some of \ref vrt_pack_contract, but adds the additional + * constraint that the input data must be CHDR. + * + * In the unpacker, these values will be set accordingly. + */ +namespace chdr{ + +    //! The maximum number of 64-bit words in a CVITA header +    static const size_t max_if_hdr_words64 = 2; // CHDR + tsf (fractional timestamp) + +    /*! +     * Pack a CHDR header from metadata (big endian format). +     * +     * See \ref vrt_pack_contract, but `link_type` is assumed to be +     * `LINK_TYPE_CHDR`. +     * +     * \param packet_buff memory to write the packed vrt header +     * \param if_packet_info the if packet info (read/write) +     */ +    UHD_API void if_hdr_pack_be( +        boost::uint32_t *packet_buff, +        if_packet_info_t &if_packet_info +    ); + +    /*! +     * Unpack a CHDR header to metadata (big endian format). +     * +     * See \ref vrt_unpack_contract, but `link_type` is assumed to be +     * `LINK_TYPE_CHDR`. +     * +     * \param packet_buff memory to read the packed vrt header +     * \param if_packet_info the if packet info (read/write) +     */ +    UHD_API void if_hdr_unpack_be( +        const boost::uint32_t *packet_buff, +        if_packet_info_t &if_packet_info +    ); + +    /*! +     * Pack a CHDR header from metadata (little endian format). +     * +     * See \ref vrt_pack_contract, but `link_type` is assumed to be +     * `LINK_TYPE_CHDR`. +     * +     * \param packet_buff memory to write the packed vrt header +     * \param if_packet_info the if packet info (read/write) +     */ +    UHD_API void if_hdr_pack_le( +        boost::uint32_t *packet_buff, +        if_packet_info_t &if_packet_info +    ); + +    /*! +     * Unpack a CHDR header to metadata (little endian format). +     * +     * See \ref vrt_unpack_contract, but `link_type` is assumed to be +     * `LINK_TYPE_CHDR`. +     * +     * \param packet_buff memory to read the packed vrt header +     * \param if_packet_info the if packet info (read/write) +     */ +    UHD_API void if_hdr_unpack_le( +        const boost::uint32_t *packet_buff, +        if_packet_info_t &if_packet_info +    ); + +} //namespace chdr + +}}} //namespace uhd::transport::vrt + +#endif /* INCLUDED_UHD_TRANSPORT_CHDR_HPP */ + diff --git a/host/include/uhd/transport/vrt_if_packet.hpp b/host/include/uhd/transport/vrt_if_packet.hpp index d16892281..8bc65cdf1 100644 --- a/host/include/uhd/transport/vrt_if_packet.hpp +++ b/host/include/uhd/transport/vrt_if_packet.hpp @@ -1,5 +1,5 @@  // -// Copyright 2010-2013 Ettus Research LLC +// Copyright 2010-2014 Ettus Research LLC  //  // This program is free software: you can redistribute it and/or modify  // it under the terms of the GNU General Public License as published by @@ -52,9 +52,18 @@ namespace vrt{          //packet type          enum packet_type_t          { +            // VRT language:              PACKET_TYPE_DATA      = 0x0,              PACKET_TYPE_IF_EXT    = 0x1,              PACKET_TYPE_CONTEXT   = 0x2, //extension context: has_sid = true + +            // CVITA language: +            //PACKET_TYPE_DATA      = 0x0, // Data +            PACKET_TYPE_FC        = 0x1, // Flow control +            PACKET_TYPE_ACK       = 0x1, // Flow control (ack) +            PACKET_TYPE_CMD       = 0x2, // Command +            PACKET_TYPE_RESP      = 0x3, // Command response +            PACKET_TYPE_ERROR     = 0x3, // Command response: Error (the EOB bit is raised in this case)          } packet_type;          //size fields @@ -65,18 +74,46 @@ namespace vrt{          //header fields          size_t packet_count; +        //! Asserted for start- or end-of-burst          bool sob, eob; +        //! This is asserted for command responses that are errors (CHDR only) +        bool error;          //optional fields +        //! Stream ID (SID). See uhd::sid_t          bool has_sid; boost::uint32_t sid; +        //! Class ID.          bool has_cid; boost::uint64_t cid; +        //! Integer timestamp          bool has_tsi; boost::uint32_t tsi; +        //! Fractional timestamp          bool has_tsf; boost::uint64_t tsf; +        //! Trailer          bool has_tlr; boost::uint32_t tlr;      };      /*!       * Pack a vrt header from metadata (big endian format). +     * +     * \section vrt_pack_contract Packing contract +     * +     * \subsection Requirements: +     * - packet_buff points to a valid address space with enough space to write +     *   the entire buffer, regardless of its length. At the very least, it must +     *   be able to hold an entire header. +     * - `if_packet_info` has the following members set to correct values: +     *   - `has_*` members all set accordingly +     *   - For every true `has_*` member, the corresponding variable holds a valid +     *     value (e.g. if `has_sid` is true, `sid` contains a valid SID) +     *   - `num_payload_bytes` and `num_payload_words32` are both set to the correct values +     * +     * \subsection Result: +     * - `packet_buff` now points to a valid header that can be sent over the transport +     *   without further modification +     * - The following members on `if_packet_info` are set: +     *   - `num_header_words32` +     *   - `num_packet_words32` +     *       * \param packet_buff memory to write the packed vrt header       * \param if_packet_info the if packet info (read/write)       */ @@ -87,6 +124,34 @@ namespace vrt{      /*!       * Unpack a vrt header to metadata (big endian format). +     * +     * \section vrt_unpack_contract Unpacking contract +     * +     * \subsection Requirements +     * - `packet_buff` points to a readable address space with a +     *   CHDR packet, starting at the header. `packet_buff[0]` *must* always +     *   point to a valid first word of the header. This implies that num_packet_words32 +     *   must be at least 1. +     * - `if_packet_info` has the following members set to correct values: +     *   - `num_packet_words32`. This means all values `packet_buff[0]` +     *     through `packet_buff[if_packet_info.num_packet_words32-1]` are +     *     readable words from this packet. +     *   - `link_type` +     * +     * \subsection Result +     * - `if_packet_info` now has the following values set to correct values: +     *   - `packet_type` +     *   - `num_payload_bytes` +     *   - `num_payload_words32` +     *   - `num_header_words32` +     *   - `has_*` +     *   - `sob`, `eob`, `error`, `cid`, `sid` (if applicable) +     *   - `tsf`, `tsi` (if applicable) +     * +     * \subsection Exceptions +     * - If the header is invalid, but the requirements are still met, +     *   will throw a uhd::value_error. +     *       * \param packet_buff memory to read the packed vrt header       * \param if_packet_info the if packet info (read/write)       */ @@ -97,6 +162,9 @@ namespace vrt{      /*!       * Pack a vrt header from metadata (little endian format). +     * +     * See \ref vrt_pack_contract. +     *       * \param packet_buff memory to write the packed vrt header       * \param if_packet_info the if packet info (read/write)       */ @@ -107,6 +175,9 @@ namespace vrt{      /*!       * Unpack a vrt header to metadata (little endian format). +     * +     * See \ref vrt_unpack_contract. +     *       * \param packet_buff memory to read the packed vrt header       * \param if_packet_info the if packet info (read/write)       */ @@ -124,6 +195,7 @@ namespace vrt{          num_packet_words32(0),          packet_count(0),          sob(false), eob(false), +        error(false),          has_sid(false), sid(0),          has_cid(false), cid(0),          has_tsi(false), tsi(0), diff --git a/host/lib/transport/CMakeLists.txt b/host/lib/transport/CMakeLists.txt index 5920f3d78..9ec8a5c0b 100644 --- a/host/lib/transport/CMakeLists.txt +++ b/host/lib/transport/CMakeLists.txt @@ -129,6 +129,7 @@ LIBUHD_APPEND_SOURCES(      ${CMAKE_CURRENT_SOURCE_DIR}/if_addrs.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/udp_simple.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/nirio_zero_copy.cpp +    ${CMAKE_CURRENT_SOURCE_DIR}/chdr.cpp  )  # Verbose Debug output for send/recv diff --git a/host/lib/transport/chdr.cpp b/host/lib/transport/chdr.cpp new file mode 100644 index 000000000..47ac961b9 --- /dev/null +++ b/host/lib/transport/chdr.cpp @@ -0,0 +1,182 @@ +// +// Copyright 2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/transport/chdr.hpp> +#include <uhd/utils/byteswap.hpp> +#include <uhd/exception.hpp> + +//define the endian macros to convert integers +#ifdef BOOST_BIG_ENDIAN +    #define BE_MACRO(x) (x) +    #define LE_MACRO(x) uhd::byteswap(x) +#else +    #define BE_MACRO(x) uhd::byteswap(x) +    #define LE_MACRO(x) (x) +#endif + +using namespace uhd::transport::vrt; + +static const boost::uint32_t HDR_FLAG_TSF = (1 << 29); +static const boost::uint32_t HDR_FLAG_EOB = (1 << 28); +static const boost::uint32_t HDR_FLAG_ERROR = (1 << 28); + +/***************************************************************************/ +/* Packing                                                                 */ +/***************************************************************************/ +/*! Translate the contents of \p if_packet_info into a 32-Bit word and return it. + */ +UHD_INLINE boost::uint32_t _hdr_pack_chdr( +        if_packet_info_t &if_packet_info +) { +    // Set fields in if_packet_info +    if_packet_info.num_header_words32 = 2 + (if_packet_info.has_tsf ? 2 : 0); +    if_packet_info.num_packet_words32 = +            if_packet_info.num_header_words32 + +            if_packet_info.num_payload_words32; + +    boost::uint16_t pkt_length = +        if_packet_info.num_payload_bytes + (4 * if_packet_info.num_header_words32); +    boost::uint32_t chdr = 0 +        // 2 Bits: Packet type +        | (if_packet_info.packet_type << 30) +        // 1 Bit: Has time +        | (if_packet_info.has_tsf ? HDR_FLAG_TSF : 0) +        // 1 Bit: EOB or Error +        | ((if_packet_info.eob or if_packet_info.error) ? HDR_FLAG_EOB : 0) +        // 12 Bits: Sequence number +        | ((if_packet_info.packet_count & 0xFFF) << 16) +        // 16 Bits: Total packet length +        | pkt_length; +    return chdr; +} + +void chdr::if_hdr_pack_be( +        boost::uint32_t *packet_buff, +        if_packet_info_t &if_packet_info +) { +    // Write header and update if_packet_info +    packet_buff[0] = BE_MACRO(_hdr_pack_chdr(if_packet_info)); + +    // Write SID +    packet_buff[1] = BE_MACRO(if_packet_info.sid); + +    // Write time +    if (if_packet_info.has_tsf) { +        packet_buff[2] = BE_MACRO(boost::uint32_t(if_packet_info.tsf >> 32)); +        packet_buff[3] = BE_MACRO(boost::uint32_t(if_packet_info.tsf >> 0)); +    } +} + +void chdr::if_hdr_pack_le( +        boost::uint32_t *packet_buff, +        if_packet_info_t &if_packet_info +) { +    // Write header and update if_packet_info +    packet_buff[0] = LE_MACRO(_hdr_pack_chdr(if_packet_info)); + +    // Write SID +    packet_buff[1] = LE_MACRO(if_packet_info.sid); + +    // Write time +    if (if_packet_info.has_tsf) { +        packet_buff[2] = LE_MACRO(boost::uint32_t(if_packet_info.tsf >> 32)); +        packet_buff[3] = LE_MACRO(boost::uint32_t(if_packet_info.tsf >> 0)); +    } +} + + +/***************************************************************************/ +/* Unpacking                                                               */ +/***************************************************************************/ +UHD_INLINE void _hdr_unpack_chdr( +        const boost::uint32_t chdr, +        if_packet_info_t &if_packet_info +) { +    // Set constant members +    if_packet_info.link_type = if_packet_info_t::LINK_TYPE_CHDR; +    if_packet_info.has_cid = false; +    if_packet_info.has_sid = true; +    if_packet_info.has_tsi = false; +    if_packet_info.has_tlr = false; +    if_packet_info.sob = false; + +    // Set configurable members +    if_packet_info.has_tsf = bool(chdr & HDR_FLAG_TSF); +    if_packet_info.packet_type = if_packet_info_t::packet_type_t((chdr >> 30) & 0x3); +    if_packet_info.eob = (if_packet_info.packet_type == if_packet_info_t::PACKET_TYPE_DATA) +                         && bool(chdr & HDR_FLAG_EOB); +    if_packet_info.error = (if_packet_info.packet_type == if_packet_info_t::PACKET_TYPE_RESP) +                         && bool(chdr & HDR_FLAG_ERROR); +    if_packet_info.packet_count = (chdr >> 16) & 0xFFF; + +    // Set packet length variables +    if (if_packet_info.has_tsf) { +        if_packet_info.num_header_words32 = 4; +    } else { +        if_packet_info.num_header_words32 = 2; +    } +    size_t pkt_size_bytes = (chdr & 0xFFFF); +    size_t pkt_size_word32 = (pkt_size_bytes / 4) + ((pkt_size_bytes % 4) ? 1 : 0); +    // Check lengths match: +    if (pkt_size_word32 < if_packet_info.num_header_words32) { +        throw uhd::value_error("Bad CHDR or invalid packet length"); +    } +    if (if_packet_info.num_packet_words32 < pkt_size_word32) { +        throw uhd::value_error("Bad CHDR or packet fragment"); +    } +    if_packet_info.num_payload_bytes = pkt_size_bytes - (4 * if_packet_info.num_header_words32); +    if_packet_info.num_payload_words32 = pkt_size_word32 - if_packet_info.num_header_words32; +} + +void chdr::if_hdr_unpack_be( +        const boost::uint32_t *packet_buff, +        if_packet_info_t &if_packet_info +) { +    // Read header and update if_packet_info +    boost::uint32_t chdr = BE_MACRO(packet_buff[0]); +    _hdr_unpack_chdr(chdr, if_packet_info); + +    // Read SID +    if_packet_info.sid = BE_MACRO(packet_buff[1]); + +    // Read time (has_tsf was updated earlier) +    if (if_packet_info.has_tsf) { +        if_packet_info.tsf = 0 +            | boost::uint64_t(BE_MACRO(packet_buff[2])) << 32 +            | BE_MACRO(packet_buff[3]); +    } +} + +void chdr::if_hdr_unpack_le( +        const boost::uint32_t *packet_buff, +        if_packet_info_t &if_packet_info +) { +    // Read header and update if_packet_info +    boost::uint32_t chdr = LE_MACRO(packet_buff[0]); +    _hdr_unpack_chdr(chdr, if_packet_info); + +    // Read SID +    if_packet_info.sid = LE_MACRO(packet_buff[1]); + +    // Read time (has_tsf was updated earlier) +    if (if_packet_info.has_tsf) { +        if_packet_info.tsf = 0 +            | boost::uint64_t(LE_MACRO(packet_buff[2])) << 32 +            | LE_MACRO(packet_buff[3]); +    } +} + diff --git a/host/tests/CMakeLists.txt b/host/tests/CMakeLists.txt index 579fd46ca..829fb8e94 100644 --- a/host/tests/CMakeLists.txt +++ b/host/tests/CMakeLists.txt @@ -27,8 +27,9 @@ SET(test_sources      addr_test.cpp      buffer_test.cpp      byteswap_test.cpp -    convert_test.cpp      cast_test.cpp +    chdr_test.cpp +    convert_test.cpp      dict_test.cpp      error_test.cpp      fp_compare_delta_test.cpp diff --git a/host/tests/chdr_test.cpp b/host/tests/chdr_test.cpp new file mode 100644 index 000000000..8893e12e5 --- /dev/null +++ b/host/tests/chdr_test.cpp @@ -0,0 +1,141 @@ +// +// Copyright 2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/transport/chdr.hpp> +#include <uhd/utils/byteswap.hpp> +#include <boost/test/unit_test.hpp> +#include <boost/format.hpp> +#include <cstdlib> +#include <iostream> + +using namespace uhd::transport::vrt; + +static void pack_and_unpack( +    if_packet_info_t &if_packet_info_in +){ +    // Temp buffer for packed packet +    boost::uint32_t packet_buff[2048] = {0}; + +    // Check input (must not be lazy) +    BOOST_REQUIRE( +        (if_packet_info_in.num_payload_words32 == 0 and if_packet_info_in.num_payload_bytes == 0) +        or +        (if_packet_info_in.num_payload_words32 != 0 and if_packet_info_in.num_payload_bytes != 0) +    ); +    if (if_packet_info_in.num_payload_words32) { +        BOOST_REQUIRE(if_packet_info_in.num_payload_bytes <= 4 * if_packet_info_in.num_payload_words32); +        BOOST_REQUIRE(if_packet_info_in.num_payload_bytes > 4*(if_packet_info_in.num_payload_words32-1)); +    } + +    //pack metadata into a vrt header +    chdr::if_hdr_pack_be( +        packet_buff, if_packet_info_in +    ); +    std::cout << std::endl; +    boost::uint32_t header_bits = (uhd::ntohx(packet_buff[0]) >> 28); +    std::cout << boost::format("header bits = 0b%d%d%d%d") % bool(header_bits & 8) %  bool(header_bits & 4) % bool(header_bits & 2) % bool(header_bits & 1) << std::endl; +    for (size_t i = 0; i < 5; i++) +    { +        std::cout << boost::format("packet_buff[%u] = 0x%08x") % i % uhd::ntohx(packet_buff[i]) << std::endl; +    } + +    if_packet_info_t if_packet_info_out; +    // Must be set a-priori as per contract +    if_packet_info_out.num_packet_words32 = if_packet_info_in.num_packet_words32; + +    //unpack the vrt header back into metadata +    chdr::if_hdr_unpack_be( +        packet_buff, if_packet_info_out +    ); + +    //check the the unpacked metadata is the same +    BOOST_CHECK_EQUAL(if_packet_info_in.packet_count, if_packet_info_out.packet_count); +    BOOST_CHECK_EQUAL(if_packet_info_in.num_header_words32, if_packet_info_out.num_header_words32); +    BOOST_CHECK_EQUAL(if_packet_info_in.num_payload_words32, if_packet_info_out.num_payload_words32); +    BOOST_CHECK(if_packet_info_out.has_sid); +    BOOST_CHECK_EQUAL(if_packet_info_in.sid, if_packet_info_out.sid); +    BOOST_CHECK(if_packet_info_out.has_sid); +    BOOST_CHECK_EQUAL(if_packet_info_in.has_tsf, if_packet_info_out.has_tsf); +    if (if_packet_info_in.has_tsf and if_packet_info_out.has_tsf){ +        BOOST_CHECK_EQUAL(if_packet_info_in.tsf, if_packet_info_out.tsf); +    } +} + +BOOST_AUTO_TEST_CASE(test_with_chdr){ +    if_packet_info_t if_packet_info; +    if_packet_info.packet_type = if_packet_info_t::PACKET_TYPE_DATA; +    if_packet_info.eob = false; +    if_packet_info.packet_count = 7; +    if_packet_info.has_tsf = true; +    if_packet_info.tsf = 0x1234567890ABCDEF; +    if_packet_info.sid = 0xAABBCCDD; +    if_packet_info.num_payload_words32 = 24; +    if_packet_info.num_payload_bytes = 95; +    pack_and_unpack(if_packet_info); +} + +BOOST_AUTO_TEST_CASE(test_with_chdr_fc){ +    if_packet_info_t if_packet_info; +    if_packet_info.packet_type = if_packet_info_t::PACKET_TYPE_FC; +    if_packet_info.eob = false; +    if_packet_info.packet_count = 19; +    if_packet_info.has_tsf = false; +    if_packet_info.tsf = 0x1234567890ABCDEF; +    if_packet_info.sid = 0xAABBCCDD; +    if_packet_info.num_payload_words32 = 4; +    if_packet_info.num_payload_bytes = 16; +    pack_and_unpack(if_packet_info); +} + +BOOST_AUTO_TEST_CASE(test_with_chdr_cmd){ +    if_packet_info_t if_packet_info; +    if_packet_info.packet_type = if_packet_info_t::PACKET_TYPE_CMD; +    if_packet_info.packet_count = 19; +    if_packet_info.has_tsf = true; +    if_packet_info.tsf = 0x1234567890ABCDEF; +    if_packet_info.sid = 0xAABBCCDD; +    if_packet_info.num_payload_words32 = 4; +    if_packet_info.num_payload_bytes = 16; +    pack_and_unpack(if_packet_info); +} + +BOOST_AUTO_TEST_CASE(test_with_chdr_resp){ +    if_packet_info_t if_packet_info; +    if_packet_info.packet_type = if_packet_info_t::PACKET_TYPE_RESP; +    if_packet_info.packet_count = 123; +    if_packet_info.has_tsf = false; +    if_packet_info.tsf = 0x1234567890ABCDEF; +    if_packet_info.sid = 0xAABBCCDD; +    if_packet_info.num_payload_words32 = 4; +    if_packet_info.num_payload_bytes = 16; +    pack_and_unpack(if_packet_info); +} + +BOOST_AUTO_TEST_CASE(test_with_chdr_err){ +    if_packet_info_t if_packet_info; +    if_packet_info.packet_type = if_packet_info_t::PACKET_TYPE_ERROR; +    if_packet_info.packet_count = 1928; +    if_packet_info.eob = false; +    if_packet_info.error = false; // Needs to be set explicitly +    if_packet_info.has_tsf = false; +    if_packet_info.tsf = 0x1234567890ABCDEF; +    if_packet_info.sid = 0xAABBCCDD; +    if_packet_info.num_payload_words32 = 4; +    if_packet_info.num_payload_bytes = 16; +    pack_and_unpack(if_packet_info); +} +  | 
