From 08fad28f209a2f6c79d939ad54ca3a1d4e270b0b Mon Sep 17 00:00:00 2001
From: Josh Blum <josh@joshknows.com>
Date: Tue, 29 Jun 2010 22:55:45 -0700
Subject: uhd: work vectorizing the vrt packet handler, reworked vrt packet
 stuff, needs testing

---
 host/include/uhd/device.hpp               |  51 ++++--
 host/include/uhd/transport/vrt.hpp        | 110 ++++++-------
 host/include/uhd/types/metadata.hpp       |  14 --
 host/lib/transport/gen_vrt.py             | 117 +++++++-------
 host/lib/transport/vrt_packet_handler.hpp | 252 ++++++++++++++++--------------
 host/lib/types.cpp                        |   4 -
 host/lib/usrp/usrp2/io_impl.cpp           |  32 ++--
 host/lib/usrp/usrp2/usrp2_impl.hpp        |   9 +-
 host/test/vrt_test.cpp                    | 147 ++++++++++-------
 9 files changed, 401 insertions(+), 335 deletions(-)

(limited to 'host')

diff --git a/host/include/uhd/device.hpp b/host/include/uhd/device.hpp
index d3e9015c4..f04a5b4fb 100644
--- a/host/include/uhd/device.hpp
+++ b/host/include/uhd/device.hpp
@@ -27,6 +27,7 @@
 #include <boost/shared_ptr.hpp>
 #include <boost/function.hpp>
 #include <boost/asio/buffer.hpp>
+#include <vector>
 
 namespace uhd{
 
@@ -96,8 +97,22 @@ public:
         RECV_MODE_ONE_PACKET = 1
     };
 
+    //! wrapper call for single buffer recv //TODO put somewhere
+    size_t send(
+        const boost::asio::const_buffer &buff,
+        const tx_metadata_t &metadata,
+        const io_type_t &io_type,
+        send_mode_t send_mode
+    ){
+        return send(
+            std::vector<const void *>(1, boost::asio::buffer_cast<const void *>(buff)),
+            boost::asio::buffer_size(buff)/io_type.size,
+            metadata, io_type, send_mode
+        );
+    }
+
     /*!
-     * Send a buffer containing IF data with its metadata.
+     * Send buffers containing IF data described by the metadata.
      *
      * Send handles fragmentation as follows:
      * If the buffer has more samples than the maximum per packet,
@@ -108,23 +123,39 @@ public:
      * Fragmentation only applies in the full buffer send mode.
      *
      * This is a blocking call and will not return until the number
-     * of samples returned have been read out of the buffer.
+     * of samples returned have been read out of each buffer.
      *
-     * \param buff a buffer pointing to some read-only memory
+     * \param buffs a vector of read-only memory containing IF data
+     * \param nsamps_per_buff the number of samples to send, per buffer
      * \param metadata data describing the buffer's contents
      * \param io_type the type of data loaded in the buffer
      * \param send_mode tells send how to unload the buffer
      * \return the number of samples sent
      */
     virtual size_t send(
-        const boost::asio::const_buffer &buff,
+        const std::vector<const void *> &buffs,
+        size_t nsamps_per_buff,
         const tx_metadata_t &metadata,
         const io_type_t &io_type,
         send_mode_t send_mode
     ) = 0;
 
+    //! wrapper call for single buffer recv //TODO put somewhere
+    size_t recv(
+        const boost::asio::mutable_buffer &buff,
+        rx_metadata_t &metadata,
+        const io_type_t &io_type,
+        recv_mode_t recv_mode
+    ){
+        return recv(
+            std::vector<void *>(1, boost::asio::buffer_cast<void *>(buff)),
+            boost::asio::buffer_size(buff)/io_type.size,
+            metadata, io_type, recv_mode
+        );
+    }
+
     /*!
-     * Receive a buffer containing IF data and its metadata.
+     * Receive buffers containing IF data described by the metadata.
      *
      * Receive handles fragmentation as follows:
      * If the buffer has insufficient space to hold all samples
@@ -138,7 +169,7 @@ public:
      * See the rx metadata fragment flags and offset fields for details.
      *
      * This is a blocking call and will not return until the number
-     * of samples returned have been written into the buffer.
+     * of samples returned have been written into each buffer.
      * However, a call to receive may timeout and return zero samples.
      * The timeout duration is decided by the underlying transport layer.
      * The caller should assume that the call to receive will not return
@@ -146,17 +177,19 @@ public:
      * and that the timeout duration is reasonably tuned for performance.
      *
      * When using the full buffer recv mode, the metadata only applies
-     * to the first packet received and written into the recv buffer.
+     * to the first packet received and written into the recv buffers.
      * Use the one packet recv mode to get per packet metadata.
      *
-     * \param buff the buffer to fill with IF data
+     * \param buffs a vector of writable memory to fill with IF data
+     * \param nsamps_per_buff the size of each buffer in number of samples
      * \param metadata data to fill describing the buffer
      * \param io_type the type of data to fill into the buffer
      * \param recv_mode tells recv how to load the buffer
      * \return the number of samples received
      */
     virtual size_t recv(
-        const boost::asio::mutable_buffer &buff,
+        const std::vector<void *> &buffs,
+        size_t nsamps_per_buff,
         rx_metadata_t &metadata,
         const io_type_t &io_type,
         recv_mode_t recv_mode
diff --git a/host/include/uhd/transport/vrt.hpp b/host/include/uhd/transport/vrt.hpp
index fb6efc99c..17da2d540 100644
--- a/host/include/uhd/transport/vrt.hpp
+++ b/host/include/uhd/transport/vrt.hpp
@@ -19,93 +19,77 @@
 #define INCLUDED_UHD_TRANSPORT_VRT_HPP
 
 #include <uhd/config.hpp>
-#include <uhd/types/metadata.hpp>
-#include <cstddef>
+#include <boost/cstdint.hpp>
+#include <cstddef> //size_t
 
 namespace uhd{ namespace transport{
 
 namespace vrt{
 
-    static const size_t max_header_words32 = 5; //hdr+sid+tsi+tsf (no class id supported)
+    //! The maximum number of 32-bit words in a vrt if packet header
+    static const size_t max_if_hdr_words32 = 7; //hdr+sid+cid+tsi+tsf
+
+    /*!
+     * Definition for fields that can be packed into a vrt if header.
+     * The size fields are used for input and output depending upon
+     * the operation used (ie the pack or unpack function call).
+     */
+    struct UHD_API if_packet_info_t{
+        //size fields
+        size_t num_payload_words32; //required in pack, derived in unpack
+        size_t num_header_words32;  //derived in pack, derived in unpack
+        size_t num_packet_words32;  //derived in pack, required in unpack
+
+        //header fields
+        size_t packet_count;
+        bool sob, eob;
+
+        //optional fields
+        bool has_sid; boost::uint32_t sid;
+        bool has_cid; boost::uint64_t cid;
+        bool has_tsi; boost::uint32_t tsi;
+        bool has_tsf; boost::uint64_t tsf;
+        bool has_tlr; boost::uint32_t tlr;
+    };
 
     /*!
      * Pack a vrt header from metadata (big endian format).
-     * \param metadata the tx metadata with flags and timestamps
-     * \param header_buff memory to write the packed vrt header
-     * \param num_header_words32 number of words in the vrt header
-     * \param num_payload_words32 the length of the payload
-     * \param num_packet_words32 the length of the packet
-     * \param packet_count the packet count sequence number
-     * \param tick_rate ticks per second used in time conversion
+     * \param packet_buff memory to write the packed vrt header
+     * \param if_packet_info the if packet info (read/write)
      */
-    UHD_API void pack_be(
-        const tx_metadata_t &metadata, //input
-        boost::uint32_t *header_buff,  //output
-        size_t &num_header_words32,    //output
-        size_t num_payload_words32,    //input
-        size_t &num_packet_words32,    //output
-        size_t packet_count,           //input
-        double tick_rate               //input
+    UHD_API void if_hdr_pack_be(
+        boost::uint32_t *packet_buff,
+        if_packet_info_t &if_packet_info
     );
 
     /*!
      * Unpack a vrt header to metadata (big endian format).
-     * \param metadata the rx metadata with flags and timestamps
-     * \param header_buff memory to read the packed vrt header
-     * \param num_header_words32 number of words in the vrt header
-     * \param num_payload_words32 the length of the payload
-     * \param num_packet_words32 the length of the packet
-     * \param packet_count the packet count sequence number
-     * \param tick_rate ticks per second used in time conversion
+     * \param packet_buff memory to read the packed vrt header
+     * \param if_packet_info the if packet info (read/write)
      */
-    UHD_API void unpack_be(
-        rx_metadata_t &metadata,            //output
-        const boost::uint32_t *header_buff, //input
-        size_t &num_header_words32,         //output
-        size_t &num_payload_words32,        //output
-        size_t num_packet_words32,          //input
-        size_t &packet_count,               //output
-        double tick_rate                    //input
+    UHD_API void if_hdr_unpack_be(
+        const boost::uint32_t *packet_buff,
+        if_packet_info_t &if_packet_info
     );
 
     /*!
      * Pack a vrt header from metadata (little endian format).
-     * \param metadata the tx metadata with flags and timestamps
-     * \param header_buff memory to write the packed vrt header
-     * \param num_header_words32 number of words in the vrt header
-     * \param num_payload_words32 the length of the payload
-     * \param num_packet_words32 the length of the packet
-     * \param packet_count the packet count sequence number
-     * \param tick_rate ticks per second used in time conversion
+     * \param packet_buff memory to write the packed vrt header
+     * \param if_packet_info the if packet info (read/write)
      */
-    UHD_API void pack_le(
-        const tx_metadata_t &metadata, //input
-        boost::uint32_t *header_buff,  //output
-        size_t &num_header_words32,    //output
-        size_t num_payload_words32,    //input
-        size_t &num_packet_words32,    //output
-        size_t packet_count,           //input
-        double tick_rate               //input
+    UHD_API void if_hdr_pack_le(
+        boost::uint32_t *packet_buff,
+        if_packet_info_t &if_packet_info
     );
 
     /*!
      * Unpack a vrt header to metadata (little endian format).
-     * \param metadata the rx metadata with flags and timestamps
-     * \param header_buff memory to read the packed vrt header
-     * \param num_header_words32 number of words in the vrt header
-     * \param num_payload_words32 the length of the payload
-     * \param num_packet_words32 the length of the packet
-     * \param packet_count the packet count sequence number
-     * \param tick_rate ticks per second used in time conversion
+     * \param packet_buff memory to read the packed vrt header
+     * \param if_packet_info the if packet info (read/write)
      */
-    UHD_API void unpack_le(
-        rx_metadata_t &metadata,            //output
-        const boost::uint32_t *header_buff, //input
-        size_t &num_header_words32,         //output
-        size_t &num_payload_words32,        //output
-        size_t num_packet_words32,          //input
-        size_t &packet_count,               //output
-        double tick_rate                    //input
+    UHD_API void if_hdr_unpack_le(
+        const boost::uint32_t *packet_buff,
+        if_packet_info_t &if_packet_info
     );
 
 } //namespace vrt
diff --git a/host/include/uhd/types/metadata.hpp b/host/include/uhd/types/metadata.hpp
index 55add71cc..f4c962ff7 100644
--- a/host/include/uhd/types/metadata.hpp
+++ b/host/include/uhd/types/metadata.hpp
@@ -30,13 +30,6 @@ namespace uhd{
      * The receive routines will convert IF data headers into metadata.
      */
     struct UHD_API rx_metadata_t{
-        /*!
-         * Stream IDs may be used to identify source DSP units.
-         * --Not currently used in any known device implementation.--
-         */
-        bool has_stream_id;
-        boost::uint32_t stream_id;
-
         /*!
          * Time specification:
          * Set from timestamps on incoming data when provided.
@@ -83,13 +76,6 @@ namespace uhd{
      * The send routines will convert the metadata to IF data headers.
      */
     struct UHD_API tx_metadata_t{
-        /*!
-         * Stream IDs may be used to identify destination DSP units.
-         * --Not currently used in any known device implementation.--
-         */
-        bool has_stream_id;
-        boost::uint32_t stream_id;
-
         /*!
          * Time specification:
          * Set has time spec to false to perform a send "now".
diff --git a/host/lib/transport/gen_vrt.py b/host/lib/transport/gen_vrt.py
index 8e0fce9ff..ad87b9972 100755
--- a/host/lib/transport/gen_vrt.py
+++ b/host/lib/transport/gen_vrt.py
@@ -61,20 +61,22 @@ using namespace uhd::transport;
 #set $tsf_p = 0b01000
 #set $tlr_p = 0b10000
 
-void vrt::pack_$(suffix)(
-    const tx_metadata_t &metadata, //input
-    boost::uint32_t *header_buff,  //output
-    size_t &num_header_words32,    //output
-    size_t num_payload_words32,    //input
-    size_t &num_packet_words32,    //output
-    size_t packet_count,           //input
-    double tick_rate               //input
+static UHD_INLINE void pack_uint64_$(suffix)(boost::uint64_t num, boost::uint32_t *mem){
+    *(reinterpret_cast<boost::uint64_t *>(mem)) = $(XE_MACRO)(num);
+}
+
+void vrt::if_hdr_pack_$(suffix)(
+    boost::uint32_t *packet_buff,
+    if_packet_info_t &if_packet_info
 ){
     boost::uint32_t vrt_hdr_flags = 0;
 
     boost::uint8_t pred = 0;
-    if (metadata.has_stream_id) pred |= $hex($sid_p);
-    if (metadata.has_time_spec) pred |= $hex($tsi_p | $tsf_p);
+    if (if_packet_info.has_sid) pred |= $hex($sid_p);
+    if (if_packet_info.has_cid) pred |= $hex($cid_p);
+    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);
 
     switch(pred){
     #for $pred in range(2**5)
@@ -83,79 +85,71 @@ void vrt::pack_$(suffix)(
         #set $flags = 0
         ########## Stream ID ##########
         #if $pred & $sid_p
-            header_buff[$num_header_words] = $(XE_MACRO)(metadata.stream_id);
+            packet_buff[$num_header_words] = $(XE_MACRO)(if_packet_info.sid);
             #set $num_header_words += 1
             #set $flags |= (0x1 << 28);
         #end if
         ########## Class ID ##########
         #if $pred & $cid_p
-            header_buff[$num_header_words] = 0;
-            #set $num_header_words += 1
-            header_buff[$num_header_words] = 0;
-            #set $num_header_words += 1
+            pack_uint64_$(suffix)(if_packet_info.cid, packet_buff+$num_header_words);
+            #set $num_header_words += 2
             #set $flags |= (0x1 << 27);
         #end if
         ########## Integer Time ##########
         #if $pred & $tsi_p
-            header_buff[$num_header_words] = $(XE_MACRO)(boost::uint32_t(metadata.time_spec.get_full_secs()));
+            packet_buff[$num_header_words] = $(XE_MACRO)(if_packet_info.tsi);
             #set $num_header_words += 1
             #set $flags |= (0x3 << 22);
         #end if
         ########## Fractional Time ##########
         #if $pred & $tsf_p
-            header_buff[$num_header_words] = 0;
-            #set $num_header_words += 1
-            header_buff[$num_header_words] = $(XE_MACRO)(boost::uint32_t(metadata.time_spec.get_tick_count(tick_rate)));
-            #set $num_header_words += 1
+            pack_uint64_$(suffix)(if_packet_info.tsf, packet_buff+$num_header_words);
+            #set $num_header_words += 2
             #set $flags |= (0x1 << 20);
         #end if
         ########## Trailer ##########
         #if $pred & $tlr_p
+            packet_buff[$num_header_words+if_packet_info.num_payload_words32] = $(XE_MACRO)(if_packet_info.tlr);
             #set $flags |= (0x1 << 26);
             #set $num_trailer_words = 1;
         #else
             #set $num_trailer_words = 0;
         #end if
         ########## Variables ##########
-            num_header_words32 = $num_header_words;
-            num_packet_words32 = $($num_header_words + $num_trailer_words) + num_payload_words32;
+            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;
             vrt_hdr_flags = $hex($flags);
         break;
     #end for
     }
 
     //set the burst flags
-    if (metadata.start_of_burst) vrt_hdr_flags |= $hex(0x1 << 25);
-    if (metadata.end_of_burst)   vrt_hdr_flags |= $hex(0x1 << 24);
+    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
-    header_buff[0] = $(XE_MACRO)(boost::uint32_t(0
+    packet_buff[0] = $(XE_MACRO)(boost::uint32_t(0
         | vrt_hdr_flags
-        | ((packet_count & 0xf) << 16)
-        | (num_packet_words32 & 0xffff)
+        | ((if_packet_info.packet_count & 0xf) << 16)
+        | (if_packet_info.num_packet_words32 & 0xffff)
     ));
 }
 
-void vrt::unpack_$(suffix)(
-    rx_metadata_t &metadata,            //output
-    const boost::uint32_t *header_buff, //input
-    size_t &num_header_words32,         //output
-    size_t &num_payload_words32,        //output
-    size_t num_packet_words32,          //input
-    size_t &packet_count,               //output
-    double tick_rate                    //input
-){
-    //clear the metadata
-    metadata = rx_metadata_t();
-    boost::uint32_t secs = 0, ticks = 0;
+static UHD_INLINE void unpack_uint64_$(suffix)(boost::uint64_t &num, const boost::uint32_t *mem){
+    num = $(XE_MACRO)(*reinterpret_cast<const boost::uint64_t *>(mem));
+}
 
+void vrt::if_hdr_unpack_$(suffix)(
+    const boost::uint32_t *packet_buff,
+    if_packet_info_t &if_packet_info
+){
     //extract vrt header
-    boost::uint32_t vrt_hdr_word = $(XE_MACRO)(header_buff[0]);
+    boost::uint32_t vrt_hdr_word = $(XE_MACRO)(packet_buff[0]);
     size_t packet_words32 = vrt_hdr_word & 0xffff;
-    packet_count = (vrt_hdr_word >> 16) & 0xf;
+    if_packet_info.packet_count = (vrt_hdr_word >> 16) & 0xf;
 
     //failure cases
-    if (packet_words32 == 0 or num_packet_words32 < packet_words32)
+    if (packet_words32 == 0 or if_packet_info.num_packet_words32 < packet_words32)
         throw std::runtime_error("bad vrt header or packet fragment");
     if (vrt_hdr_word & (0x7 << 29))
         throw std::runtime_error("unsupported vrt packet type");
@@ -174,41 +168,48 @@ void vrt::unpack_$(suffix)(
         #set $num_header_words = 1
         ########## Stream ID ##########
         #if $pred & $sid_p
-            metadata.has_stream_id = true;
-            metadata.stream_id = $(XE_MACRO)(header_buff[$num_header_words]);
+            if_packet_info.has_sid = true;
+            if_packet_info.sid = $(XE_MACRO)(packet_buff[$num_header_words]);
             #set $num_header_words += 1
+        #else
+            if_packet_info.has_sid = false;
         #end if
         ########## Class ID ##########
         #if $pred & $cid_p
-            #set $num_header_words += 1
-            #set $num_header_words += 1
+            if_packet_info.has_cid = true;
+            unpack_uint64_$(suffix)(if_packet_info.cid, packet_buff+$num_header_words);
+            #set $num_header_words += 2
+        #else
+            if_packet_info.has_cid = false;
         #end if
         ########## Integer Time ##########
         #if $pred & $tsi_p
-            #set $has_time_spec = True
-            secs = $(XE_MACRO)(header_buff[$num_header_words]);
+            if_packet_info.has_tsi = true;
+            if_packet_info.tsi = $(XE_MACRO)(packet_buff[$num_header_words]);
             #set $num_header_words += 1
+        #else
+            if_packet_info.has_tsi = false;
         #end if
         ########## Fractional Time ##########
         #if $pred & $tsf_p
-            #set $has_time_spec = True
-            #set $num_header_words += 1
-            ticks = $(XE_MACRO)(header_buff[$num_header_words]);
-            #set $num_header_words += 1
-        #end if
-        #if $has_time_spec
-            metadata.has_time_spec = true;
-            metadata.time_spec = time_spec_t(secs, ticks, tick_rate);
+            if_packet_info.has_tsf = true;
+            unpack_uint64_$(suffix)(if_packet_info.tsf, packet_buff+$num_header_words);
+            #set $num_header_words += 2
+        #else
+            if_packet_info.has_tsf = false;
         #end if
         ########## Trailer ##########
         #if $pred & $tlr_p
+            if_packet_info.has_tlr = true;
+            if_packet_info.tlr = $(XE_MACRO)(packet_buff[$num_header_words+packet_words32]);
             #set $num_trailer_words = 1;
         #else
+            if_packet_info.has_tlr = false;
             #set $num_trailer_words = 0;
         #end if
         ########## Variables ##########
-            num_header_words32 = $num_header_words;
-            num_payload_words32 = packet_words32 - $($num_header_words + $num_trailer_words);
+            if_packet_info.num_header_words32 = $num_header_words;
+            if_packet_info.num_payload_words32 = packet_words32 - $($num_header_words + $num_trailer_words);
         break;
     #end for
     }
diff --git a/host/lib/transport/vrt_packet_handler.hpp b/host/lib/transport/vrt_packet_handler.hpp
index 8dfc7b3d0..01cccd81e 100644
--- a/host/lib/transport/vrt_packet_handler.hpp
+++ b/host/lib/transport/vrt_packet_handler.hpp
@@ -20,6 +20,7 @@
 
 #include <uhd/config.hpp>
 #include <uhd/device.hpp>
+#include <uhd/utils/assert.hpp>
 #include <uhd/types/io_type.hpp>
 #include <uhd/types/otw_type.hpp>
 #include <uhd/types/metadata.hpp>
@@ -30,37 +31,43 @@
 #include <boost/function.hpp>
 #include <stdexcept>
 #include <iostream>
+#include <vector>
 
 namespace vrt_packet_handler{
 
 /***********************************************************************
  * vrt packet handler for recv
  **********************************************************************/
+    typedef std::vector<uhd::transport::managed_recv_buffer::sptr> managed_recv_buffs_t;
+
     struct recv_state{
+        //width of the receiver in channels
+        size_t width;
+
         //init the expected seq number
-        size_t next_packet_seq;
+        std::vector<size_t> next_packet_seq;
 
         //state variables to handle fragments
-        uhd::transport::managed_recv_buffer::sptr managed_buff;
-        boost::asio::const_buffer copy_buff;
+        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;
 
-        recv_state(void){
-            //first expected seq is zero
-            next_packet_seq = 0;
-
-            //initially empty copy buffer
-            copy_buff = boost::asio::buffer("", 0);
+        recv_state(size_t width):
+            width(width),
+            next_packet_seq(width, 0),
+            managed_buffs(width),
+            copy_buffs(width, NULL),
+            size_of_copy_buffs(0),
+            fragment_offset_in_samps(0)
+        {
+            /* NOP */
         }
     };
 
-    typedef boost::function<uhd::transport::managed_recv_buffer::sptr(void)> get_recv_buff_t;
-
-    typedef boost::function<void(uhd::transport::managed_recv_buffer::sptr)> recv_cb_t;
+    typedef boost::function<bool(managed_recv_buffs_t &)> get_recv_buffs_t;
 
-    static UHD_INLINE void recv_cb_nop(uhd::transport::managed_recv_buffer::sptr){
-        /* NOP */
-    }
+    typedef boost::function<void(managed_recv_buffs_t &)> recv_cb_t;
 
     /*******************************************************************
      * Unpack a received vrt header and set the copy buffer.
@@ -74,33 +81,41 @@ namespace vrt_packet_handler{
         vrt_unpacker_type vrt_unpacker,
         size_t vrt_header_offset_words32
     ){
-        size_t num_packet_words32 = state.managed_buff->size()/sizeof(boost::uint32_t);
+        size_t num_packet_words32 = state.managed_buffs[0]->size()/sizeof(boost::uint32_t);
         if (num_packet_words32 <= vrt_header_offset_words32){
-            state.copy_buff = boost::asio::buffer("", 0);
+            state.size_of_copy_buffs = 0;
             return; //must exit here after setting the buffer
         }
-        const boost::uint32_t *vrt_hdr = state.managed_buff->cast<const boost::uint32_t *>() + vrt_header_offset_words32;
-        size_t num_header_words32_out, num_payload_words32_out, packet_count_out;
-        vrt_unpacker(
-            metadata,                //output
-            vrt_hdr,                 //input
-            num_header_words32_out,  //output
-            num_payload_words32_out, //output
-            num_packet_words32,      //input
-            packet_count_out,        //output
-            tick_rate
-        );
 
-        //handle the packet count / sequence number
-        if (packet_count_out != state.next_packet_seq){
-            std::cerr << "S" << (packet_count_out - state.next_packet_seq)%16;
+        //vrt unpack each managed buffer
+        uhd::transport::vrt::if_packet_info_t if_packet_info;
+        for (size_t i = 0; i < state.width; i++){
+            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_unpacker(vrt_hdr, if_packet_info);
+
+            //handle the packet count / sequence number
+            if (if_packet_info.packet_count != state.next_packet_seq[i]){
+                std::cerr << "S" << (if_packet_info.packet_count - state.next_packet_seq[i])%16;
+            }
+            state.next_packet_seq[i] = (if_packet_info.packet_count+1)%16;
+
+            //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;
+            }
         }
-        state.next_packet_seq = (packet_count_out+1)%16;
 
-        //setup the buffer to point to the data
-        state.copy_buff = boost::asio::buffer(
-            vrt_hdr + num_header_words32_out,
-            num_payload_words32_out*sizeof(boost::uint32_t)
+        //store the last vrt info into the metadata
+        metadata.has_time_spec = if_packet_info.has_tsi or if_packet_info.has_tsf;
+        metadata.time_spec = uhd::time_spec_t(
+            time_t((if_packet_info.has_tsi)? if_packet_info.tsi : 0),
+            size_t((if_packet_info.has_tsf)? if_packet_info.tsf : 0),
+            tick_rate
         );
     }
 
@@ -111,24 +126,22 @@ namespace vrt_packet_handler{
     template<typename vrt_unpacker_type>
     static UHD_INLINE size_t _recv1(
         recv_state &state,
-        void *recv_mem,
+        const std::vector<void *> &buffs,
+        size_t offset_bytes,
         size_t total_samps,
         uhd::rx_metadata_t &metadata,
         const uhd::io_type_t &io_type,
         const uhd::otw_type_t &otw_type,
         double tick_rate,
         vrt_unpacker_type vrt_unpacker,
-        const get_recv_buff_t &get_recv_buff,
+        const get_recv_buffs_t &get_recv_buffs,
         //use these two params to handle a layer above vrt
-        size_t vrt_header_offset_words32,
-        const recv_cb_t &recv_cb
+        size_t vrt_header_offset_words32
     ){
         //perform a receive if no rx data is waiting to be copied
-        if (boost::asio::buffer_size(state.copy_buff) == 0){
+        if (state.size_of_copy_buffs == 0){
             state.fragment_offset_in_samps = 0;
-            state.managed_buff = get_recv_buff();
-            if (state.managed_buff.get() == NULL) return 0;
-            recv_cb(state.managed_buff); //callback before vrt unpack
+            if (not get_recv_buffs(state.managed_buffs)) return 0;
             try{
                 _recv1_helper(
                     state, metadata, tick_rate, vrt_unpacker, vrt_header_offset_words32
@@ -141,26 +154,28 @@ namespace vrt_packet_handler{
 
         //extract the number of samples available to copy
         size_t bytes_per_item = otw_type.get_sample_size();
-        size_t bytes_available = boost::asio::buffer_size(state.copy_buff);
+        size_t bytes_available = state.size_of_copy_buffs;
         size_t num_samps = std::min(total_samps, bytes_available/bytes_per_item);
+        size_t bytes_to_copy = num_samps*bytes_per_item;
 
         //setup the fragment flags and offset
         metadata.more_fragments = total_samps < num_samps;
         metadata.fragment_offset = state.fragment_offset_in_samps;
         state.fragment_offset_in_samps += num_samps; //set for next call
 
-        //copy-convert the samples from the recv buffer
-        uhd::transport::convert_otw_type_to_io_type(
-            boost::asio::buffer_cast<const void*>(state.copy_buff), otw_type,
-            recv_mem, io_type, num_samps
-        );
+        for (size_t i = 0; i < state.width; i++){
+            //copy-convert the samples from the recv buffer
+            uhd::transport::convert_otw_type_to_io_type(
+                state.copy_buffs[i], otw_type,
+                reinterpret_cast<boost::uint8_t *>(buffs[i]) + offset_bytes,
+                io_type, num_samps
+            );
 
-        //update the rx copy buffer to reflect the bytes copied
-        size_t bytes_copied = num_samps*bytes_per_item;
-        state.copy_buff = boost::asio::buffer(
-            boost::asio::buffer_cast<const boost::uint8_t*>(state.copy_buff) + bytes_copied,
-            bytes_available - bytes_copied
-        );
+            //update the rx copy buffer to reflect the bytes copied
+            state.copy_buffs[i] = reinterpret_cast<const boost::uint8_t *>(state.copy_buffs[i]) + bytes_to_copy;
+        }
+        //update the copy buffer's availability
+        state.size_of_copy_buffs -= bytes_to_copy;
 
         return num_samps;
     }
@@ -171,20 +186,19 @@ namespace vrt_packet_handler{
     template<typename vrt_unpacker_type>
     static UHD_INLINE size_t recv(
         recv_state &state,
-        const boost::asio::mutable_buffer &buff,
+        const std::vector<void *> &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,
         vrt_unpacker_type vrt_unpacker,
-        const get_recv_buff_t &get_recv_buff,
+        const get_recv_buffs_t &get_recv_buffs,
         //use these two params to handle a layer above vrt
-        size_t vrt_header_offset_words32 = 0,
-        const recv_cb_t& recv_cb = &recv_cb_nop
+        size_t vrt_header_offset_words32 = 0
     ){
         metadata = uhd::rx_metadata_t(); //init the metadata
-        const size_t total_num_samps = boost::asio::buffer_size(buff)/io_type.size;
 
         switch(recv_mode){
 
@@ -193,15 +207,14 @@ namespace vrt_packet_handler{
         ////////////////////////////////////////////////////////////////
             return _recv1(
                 state,
-                boost::asio::buffer_cast<void *>(buff),
+                buffs, 0,
                 total_num_samps,
                 metadata,
                 io_type, otw_type,
                 tick_rate,
                 vrt_unpacker,
-                get_recv_buff,
-                vrt_header_offset_words32,
-                recv_cb
+                get_recv_buffs,
+                vrt_header_offset_words32
             );
         }
 
@@ -213,15 +226,14 @@ namespace vrt_packet_handler{
             while(accum_num_samps < total_num_samps){
                 size_t num_samps = _recv1(
                     state,
-                    boost::asio::buffer_cast<boost::uint8_t *>(buff) + (accum_num_samps*io_type.size),
+                    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
                     io_type, otw_type,
                     tick_rate,
                     vrt_unpacker,
-                    get_recv_buff,
-                    vrt_header_offset_words32,
-                    recv_cb
+                    get_recv_buffs,
+                    vrt_header_offset_words32
                 );
                 if (num_samps == 0) break; //had a recv timeout or error, break loop
                 accum_num_samps += num_samps;
@@ -236,6 +248,8 @@ namespace vrt_packet_handler{
 /***********************************************************************
  * vrt packet handler for send
  **********************************************************************/
+    typedef std::vector<uhd::transport::managed_send_buffer::sptr> managed_send_buffs_t;
+
     struct send_state{
         //init the expected seq number
         size_t next_packet_seq;
@@ -245,13 +259,9 @@ namespace vrt_packet_handler{
         }
     };
 
-    typedef boost::function<uhd::transport::managed_send_buffer::sptr(void)> get_send_buff_t;
-
-    typedef boost::function<void(uhd::transport::managed_send_buffer::sptr)> send_cb_t;
+    typedef boost::function<bool(managed_send_buffs_t &)> get_send_buffs_t;
 
-    static UHD_INLINE void send_cb_nop(uhd::transport::managed_send_buffer::sptr){
-        /* NOP */
-    }
+    typedef boost::function<void(managed_send_buffs_t &)> send_cb_t;
 
     /*******************************************************************
      * Pack a vrt header, copy-convert the data, and send it.
@@ -260,46 +270,51 @@ namespace vrt_packet_handler{
     template<typename vrt_packer_type>
     static UHD_INLINE void _send1(
         send_state &state,
-        const void *send_mem,
+        const std::vector<const void *> &buffs,
+        size_t offset_bytes,
         size_t num_samps,
         const uhd::tx_metadata_t &metadata,
         const uhd::io_type_t &io_type,
         const uhd::otw_type_t &otw_type,
         double tick_rate,
         vrt_packer_type vrt_packer,
-        const get_send_buff_t &get_send_buff,
-        size_t vrt_header_offset_words32,
-        const send_cb_t& send_cb
+        const get_send_buffs_t &get_send_buffs,
+        size_t vrt_header_offset_words32
     ){
-        //get a new managed send buffer
-        uhd::transport::managed_send_buffer::sptr send_buff = get_send_buff();
-        boost::uint32_t *tx_mem = send_buff->cast<boost::uint32_t *>() + vrt_header_offset_words32;
-
-        size_t num_header_words32, num_packet_words32;
-        size_t packet_count = state.next_packet_seq++;
-
-        //pack metadata into a vrt header
-        vrt_packer(
-            metadata,            //input
-            tx_mem,              //output
-            num_header_words32,  //output
-            num_samps,           //input
-            num_packet_words32,  //output
-            packet_count,        //input
-            tick_rate
-        );
-
-        //copy-convert the samples into the send buffer
-        uhd::transport::convert_io_type_to_otw_type(
-            send_mem, io_type,
-            tx_mem + num_header_words32, otw_type,
-            num_samps
-        );
-
-        send_cb(send_buff); //callback after memory filled
+        //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_tsi = metadata.has_time_spec;
+        if_packet_info.tsi = boost::uint32_t(metadata.time_spec.get_full_secs());
+        if_packet_info.has_tsf = metadata.has_time_spec;
+        if_packet_info.tsf = boost::uint64_t(metadata.time_spec.get_tick_count(tick_rate));
+        if_packet_info.has_tlr = false;
+        if_packet_info.num_payload_words32 = (num_samps*io_type.size)/sizeof(boost::uint32_t);
+        if_packet_info.packet_count = state.next_packet_seq++;
+
+        //get send buffers for each channel
+        managed_send_buffs_t send_buffs(buffs.size());
+        UHD_ASSERT_THROW(get_send_buffs(send_buffs));
+
+        for (size_t i = 0; i < buffs.size(); i++){
+            //calculate pointers with offsets to io and otw memory
+            const boost::uint8_t *io_mem = reinterpret_cast<const boost::uint8_t *>(buffs[i]) + offset_bytes;
+            boost::uint32_t *otw_mem = send_buffs[i]->cast<boost::uint32_t *>() + vrt_header_offset_words32;
+
+            //pack metadata into a vrt header
+            vrt_packer(otw_mem, if_packet_info);
+
+            //copy-convert the samples into the send buffer
+            uhd::transport::convert_io_type_to_otw_type(
+                io_mem, io_type,
+                otw_mem + if_packet_info.num_header_words32, otw_type,
+                num_samps
+            );
 
-        //commit the samples to the zero-copy interface
-        send_buff->commit(num_packet_words32*sizeof(boost::uint32_t));
+            //commit the samples to the zero-copy interface
+            send_buffs[i]->commit(if_packet_info.num_packet_words32*sizeof(boost::uint32_t));
+        }
     }
 
     /*******************************************************************
@@ -308,20 +323,19 @@ namespace vrt_packet_handler{
     template<typename vrt_packer_type>
     static UHD_INLINE size_t send(
         send_state &state,
-        const boost::asio::const_buffer &buff,
+        const std::vector<const void *> &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,
         vrt_packer_type vrt_packer,
-        const get_send_buff_t &get_send_buff,
+        const get_send_buffs_t &get_send_buffs,
         size_t max_samples_per_packet,
         //use these two params to handle a layer above vrt
-        size_t vrt_header_offset_words32 = 0,
-        const send_cb_t& send_cb = &send_cb_nop
+        size_t vrt_header_offset_words32 = 0
     ){
-        const size_t total_num_samps = boost::asio::buffer_size(buff)/io_type.size;
         if (total_num_samps <= max_samples_per_packet) send_mode = uhd::device::SEND_MODE_ONE_PACKET;
         switch(send_mode){
 
@@ -331,15 +345,14 @@ namespace vrt_packet_handler{
             size_t num_samps = std::min(total_num_samps, max_samples_per_packet);
             _send1(
                 state,
-                boost::asio::buffer_cast<const void *>(buff),
+                buffs, 0,
                 num_samps,
                 metadata,
                 io_type, otw_type,
                 tick_rate,
                 vrt_packer,
-                get_send_buff,
-                vrt_header_offset_words32,
-                send_cb
+                get_send_buffs,
+                vrt_header_offset_words32
             );
             return num_samps;
         }
@@ -366,15 +379,14 @@ namespace vrt_packet_handler{
                 //send the fragment with the helper function
                 _send1(
                     state,
-                    boost::asio::buffer_cast<const boost::uint8_t *>(buff) + (n*max_samples_per_packet*io_type.size),
+                    buffs, n*max_samples_per_packet*io_type.size,
                     (n == final_fragment_index)?(total_num_samps%max_samples_per_packet):max_samples_per_packet,
                     md,
                     io_type, otw_type,
                     tick_rate,
                     vrt_packer,
-                    get_send_buff,
-                    vrt_header_offset_words32,
-                    send_cb
+                    get_send_buffs,
+                    vrt_header_offset_words32
                 );
             }
             return total_num_samps;
diff --git a/host/lib/types.cpp b/host/lib/types.cpp
index 6a9fcf5b5..9cf2a2220 100644
--- a/host/lib/types.cpp
+++ b/host/lib/types.cpp
@@ -96,8 +96,6 @@ stream_cmd_t::stream_cmd_t(const stream_mode_t &stream_mode):
  * metadata
  **********************************************************************/
 rx_metadata_t::rx_metadata_t(void):
-    has_stream_id(false),
-    stream_id(0),
     has_time_spec(false),
     time_spec(time_spec_t()),
     more_fragments(false),
@@ -107,8 +105,6 @@ rx_metadata_t::rx_metadata_t(void):
 }
 
 tx_metadata_t::tx_metadata_t(void):
-    has_stream_id(false),
-    stream_id(0),
     has_time_spec(false),
     time_spec(time_spec_t()),
     start_of_burst(false),
diff --git a/host/lib/usrp/usrp2/io_impl.cpp b/host/lib/usrp/usrp2/io_impl.cpp
index aa0f63321..e3a3a3b17 100644
--- a/host/lib/usrp/usrp2/io_impl.cpp
+++ b/host/lib/usrp/usrp2/io_impl.cpp
@@ -52,7 +52,7 @@ struct usrp2_impl::io_impl{
     bounded_buffer<managed_recv_buffer::sptr>::sptr recv_pirate_booty;
 };
 
-usrp2_impl::io_impl::io_impl(zero_copy_if::sptr zc_if){
+usrp2_impl::io_impl::io_impl(zero_copy_if::sptr zc_if): packet_handler_recv_state(1){
     //create a large enough booty
     size_t num_frames = zc_if->get_num_recv_frames();
     std::cout << "Recv pirate num frames: " << num_frames << std::endl;
@@ -130,19 +130,26 @@ void usrp2_impl::io_init(void){
 /***********************************************************************
  * Send Data
  **********************************************************************/
+bool tmp_todo_fixme_remove_get_send_buffs(vrt_packet_handler::managed_send_buffs_t &buffs, const zero_copy_if::sptr &zc_if){
+    buffs[0] = zc_if->get_send_buff();
+    return buffs[0].get() != NULL;
+}
+
 size_t usrp2_impl::send(
-    const asio::const_buffer &buff,
+    const std::vector<const void *> &buffs,
+    size_t nsamps_per_buff,
     const tx_metadata_t &metadata,
     const io_type_t &io_type,
     send_mode_t send_mode
 ){
     return vrt_packet_handler::send(
         _io_impl->packet_handler_send_state, //last state of the send handler
-        buff, metadata, send_mode,  //buffer to empty and samples metadata
+        buffs, nsamps_per_buff,     //buffer to empty
+        metadata, send_mode,        //samples metadata
         io_type, _tx_otw_type,      //input and output types to convert
         get_master_clock_freq(),    //master clock tick rate
-        uhd::transport::vrt::pack_be,
-        boost::bind(&zero_copy_if::get_send_buff, _data_transport),
+        uhd::transport::vrt::if_hdr_pack_be,
+        boost::bind(&tmp_todo_fixme_remove_get_send_buffs, _1, _data_transport),
         get_max_send_samps_per_packet()
     );
 }
@@ -150,18 +157,25 @@ size_t usrp2_impl::send(
 /***********************************************************************
  * Receive Data
  **********************************************************************/
+bool tmp_todo_fixme_remove_get_recv_buffs(vrt_packet_handler::managed_recv_buffs_t &buffs, boost::shared_ptr<usrp2_impl::io_impl> impl){
+    buffs[0] = impl->get_recv_buff();
+    return buffs[0].get() != NULL;
+}
+
 size_t usrp2_impl::recv(
-    const asio::mutable_buffer &buff,
+    const std::vector<void *> &buffs,
+    size_t nsamps_per_buff,
     rx_metadata_t &metadata,
     const io_type_t &io_type,
     recv_mode_t recv_mode
 ){
     return vrt_packet_handler::recv(
         _io_impl->packet_handler_recv_state, //last state of the recv handler
-        buff, metadata, recv_mode,  //buffer to fill and samples metadata
+        buffs, nsamps_per_buff,     //buffer to empty
+        metadata, recv_mode,        //samples metadata
         io_type, _rx_otw_type,      //input and output types to convert
         get_master_clock_freq(),    //master clock tick rate
-        uhd::transport::vrt::unpack_be,
-        boost::bind(&usrp2_impl::io_impl::get_recv_buff, _io_impl)
+        uhd::transport::vrt::if_hdr_unpack_be,
+        boost::bind(&tmp_todo_fixme_remove_get_recv_buffs, _1, _io_impl)
     );
 }
diff --git a/host/lib/usrp/usrp2/usrp2_impl.hpp b/host/lib/usrp/usrp2/usrp2_impl.hpp
index 2126b9565..70735173e 100644
--- a/host/lib/usrp/usrp2/usrp2_impl.hpp
+++ b/host/lib/usrp/usrp2/usrp2_impl.hpp
@@ -108,7 +108,7 @@ public:
         return _max_tx_bytes_per_packet/_tx_otw_type.get_sample_size();
     }
     size_t send(
-        const boost::asio::const_buffer &,
+        const std::vector<const void *> &, size_t,
         const uhd::tx_metadata_t &,
         const uhd::io_type_t &,
         uhd::device::send_mode_t
@@ -117,12 +117,14 @@ public:
         return _max_rx_bytes_per_packet/_rx_otw_type.get_sample_size();
     }
     size_t recv(
-        const boost::asio::mutable_buffer &,
+        const std::vector<void *> &, size_t,
         uhd::rx_metadata_t &,
         const uhd::io_type_t &,
         uhd::device::recv_mode_t
     );
 
+    UHD_PIMPL_DECL(io_impl) _io_impl;
+
 private:
     inline double get_master_clock_freq(void){
         return _clock_ctrl->get_master_clock_rate();
@@ -148,11 +150,10 @@ private:
     ;
     static const size_t _max_tx_bytes_per_packet =
         USRP2_UDP_BYTES -
-        uhd::transport::vrt::max_header_words32*sizeof(boost::uint32_t)
+        uhd::transport::vrt::max_if_hdr_words32*sizeof(boost::uint32_t)
     ;
 
     uhd::otw_type_t _rx_otw_type, _tx_otw_type;
-    UHD_PIMPL_DECL(io_impl) _io_impl;
     void io_init(void);
 
     //udp transports for control and data
diff --git a/host/test/vrt_test.cpp b/host/test/vrt_test.cpp
index 3776e33aa..3f74a836e 100644
--- a/host/test/vrt_test.cpp
+++ b/host/test/vrt_test.cpp
@@ -17,84 +17,123 @@
 
 #include <boost/test/unit_test.hpp>
 #include <uhd/transport/vrt.hpp>
+#include <cstdlib>
 
 using namespace uhd::transport;
 
 static void pack_and_unpack(
-    const uhd::tx_metadata_t &metadata,
-    size_t num_payload_words32,
-    size_t packet_count
+    vrt::if_packet_info_t &if_packet_info_in
 ){
-    boost::uint32_t header_buff[vrt::max_header_words32];
-    size_t num_header_words32;
-    size_t num_packet_words32;
+    boost::uint32_t header_buff[vrt::max_if_hdr_words32];
 
     //pack metadata into a vrt header
-    vrt::pack_be(
-        metadata,            //input
-        header_buff,         //output
-        num_header_words32,  //output
-        num_payload_words32, //input
-        num_packet_words32,  //output
-        packet_count,        //input
-        100e6
+    vrt::if_hdr_pack_be(
+        header_buff, if_packet_info_in
     );
 
-    uhd::rx_metadata_t metadata_out;
-    size_t num_header_words32_out;
-    size_t num_payload_words32_out;
-    size_t packet_count_out;
+    vrt::if_packet_info_t if_packet_info_out;
+    if_packet_info_out.num_packet_words32 = if_packet_info_in.num_packet_words32;
 
     //unpack the vrt header back into metadata
-    vrt::unpack_be(
-        metadata_out,            //output
-        header_buff,             //input
-        num_header_words32_out,  //output
-        num_payload_words32_out, //output
-        num_packet_words32,      //input
-        packet_count_out,        //output
-        100e6
+    vrt::if_hdr_unpack_be(
+        header_buff, if_packet_info_out
     );
 
     //check the the unpacked metadata is the same
-    BOOST_CHECK_EQUAL(packet_count, packet_count_out);
-    BOOST_CHECK_EQUAL(num_header_words32, num_header_words32_out);
-    BOOST_CHECK_EQUAL(num_payload_words32, num_payload_words32_out);
-    BOOST_CHECK_EQUAL(metadata.has_stream_id, metadata_out.has_stream_id);
-    if (metadata.has_stream_id and metadata_out.has_stream_id){
-        BOOST_CHECK_EQUAL(metadata.stream_id, metadata_out.stream_id);
+    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_EQUAL(if_packet_info_in.has_sid, if_packet_info_out.has_sid);
+    if (if_packet_info_in.has_sid and if_packet_info_out.has_sid){
+        BOOST_CHECK_EQUAL(if_packet_info_in.sid, if_packet_info_out.sid);
     }
-    BOOST_CHECK_EQUAL(metadata.has_time_spec, metadata_out.has_time_spec);
-    if (metadata.has_time_spec and metadata_out.has_time_spec){
-        BOOST_CHECK_EQUAL(metadata.time_spec.get_full_secs(), metadata_out.time_spec.get_full_secs());
-        BOOST_CHECK_EQUAL(metadata.time_spec.get_frac_secs(), metadata_out.time_spec.get_frac_secs());
+    BOOST_CHECK_EQUAL(if_packet_info_in.has_cid, if_packet_info_out.has_cid);
+    if (if_packet_info_in.has_cid and if_packet_info_out.has_cid){
+        BOOST_CHECK_EQUAL(if_packet_info_in.cid, if_packet_info_out.cid);
+    }
+    BOOST_CHECK_EQUAL(if_packet_info_in.has_tsi, if_packet_info_out.has_tsi);
+    if (if_packet_info_in.has_tsi and if_packet_info_out.has_tsi){
+        BOOST_CHECK_EQUAL(if_packet_info_in.tsi, if_packet_info_out.tsi);
+    }
+    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_CHECK_EQUAL(if_packet_info_in.has_tlr, if_packet_info_out.has_tlr);
+    if (if_packet_info_in.has_tlr and if_packet_info_out.has_tlr){
+        BOOST_CHECK_EQUAL(if_packet_info_in.tlr, if_packet_info_out.tlr);
     }
 }
 
+/***********************************************************************
+ * Loopback test the vrt packer/unpacker with various packet info combos
+ * The trailer is not tested as it is not convenient to do so.
+ **********************************************************************/
+
 BOOST_AUTO_TEST_CASE(test_with_none){
-    uhd::tx_metadata_t metadata;
-    pack_and_unpack(metadata, 300, 1);
+    vrt::if_packet_info_t if_packet_info;
+    if_packet_info.packet_count = 0;
+    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;
+    if_packet_info.num_payload_words32 = 0;
+    pack_and_unpack(if_packet_info);
 }
 
 BOOST_AUTO_TEST_CASE(test_with_sid){
-    uhd::tx_metadata_t metadata;
-    metadata.has_stream_id = true;
-    metadata.stream_id = 6;
-    pack_and_unpack(metadata, 400, 2);
+    vrt::if_packet_info_t if_packet_info;
+    if_packet_info.packet_count = 1;
+    if_packet_info.has_sid = true;
+    if_packet_info.has_cid = false;
+    if_packet_info.has_tsi = false;
+    if_packet_info.has_tsf = false;
+    if_packet_info.has_tlr = false;
+    if_packet_info.sid = std::rand();
+    if_packet_info.num_payload_words32 = 1111;
+    pack_and_unpack(if_packet_info);
+}
+
+BOOST_AUTO_TEST_CASE(test_with_cid){
+    vrt::if_packet_info_t if_packet_info;
+    if_packet_info.packet_count = 2;
+    if_packet_info.has_sid = false;
+    if_packet_info.has_cid = true;
+    if_packet_info.has_tsi = false;
+    if_packet_info.has_tsf = false;
+    if_packet_info.has_tlr = false;
+    if_packet_info.cid = std::rand();
+    if_packet_info.num_payload_words32 = 2222;
+    pack_and_unpack(if_packet_info);
 }
 
-BOOST_AUTO_TEST_CASE(test_with_time_spec){
-    uhd::tx_metadata_t metadata;
-    metadata.has_time_spec = true;
-    metadata.time_spec = uhd::time_spec_t(7, 0.2);
-    pack_and_unpack(metadata, 500, 3);
+BOOST_AUTO_TEST_CASE(test_with_time){
+    vrt::if_packet_info_t if_packet_info;
+    if_packet_info.packet_count = 3;
+    if_packet_info.has_sid = false;
+    if_packet_info.has_cid = false;
+    if_packet_info.has_tsi = true;
+    if_packet_info.has_tsf = true;
+    if_packet_info.has_tlr = false;
+    if_packet_info.tsi = std::rand();
+    if_packet_info.tsf = std::rand();
+    if_packet_info.num_payload_words32 = 33333;
+    pack_and_unpack(if_packet_info);
 }
 
-BOOST_AUTO_TEST_CASE(test_with_sid_and_time_spec){
-    uhd::tx_metadata_t metadata;
-    metadata.has_stream_id = true;
-    metadata.stream_id = 2;
-    metadata.has_time_spec = true;
-    metadata.time_spec = uhd::time_spec_t(5, 0.1);
-    pack_and_unpack(metadata, 600, 4);
+BOOST_AUTO_TEST_CASE(test_with_all){
+    vrt::if_packet_info_t if_packet_info;
+    if_packet_info.packet_count = 4;
+    if_packet_info.has_sid = true;
+    if_packet_info.has_cid = true;
+    if_packet_info.has_tsi = true;
+    if_packet_info.has_tsf = true;
+    if_packet_info.has_tlr = false;
+    if_packet_info.sid = std::rand();
+    if_packet_info.cid = std::rand();
+    if_packet_info.tsi = std::rand();
+    if_packet_info.tsf = std::rand();
+    if_packet_info.num_payload_words32 = 44444;
+    pack_and_unpack(if_packet_info);
 }
-- 
cgit v1.2.3