#!/usr/bin/env python
#
# Copyright 2010-2013 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 .
#
"""
The vrt packer/unpacker code generator:
This script will generate the pack and unpack routines that convert
metatdata into vrt headers and vrt headers into metadata.
The generated code infers jump tables to speed-up the parsing time.
"""
TMPL_TEXT = """
#import time
/***********************************************************************
* This file was generated by $file on $time.strftime("%c")
**********************************************************************/
\#include
\#include
\#include
\#include
\#include
//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;
using namespace uhd::transport;
using namespace uhd::transport::vrt;
typedef size_t pred_type;
typedef std::vector pred_table_type;
#define pred_table_index(hdr) ((hdr >> 20) & 0x1ff)
static pred_table_type get_pred_unpack_table(void)
{
pred_table_type table(1 << 9, 0); //only 9 bits useful here (20-28)
for (size_t i = 0; i < table.size(); i++){
boost::uint32_t vrt_hdr_word = i << 20;
if(vrt_hdr_word & $hex(0x1 << 28)) table[i] |= $hex($sid_p);
if(vrt_hdr_word & $hex(0x1 << 27)) table[i] |= $hex($cid_p);
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;
}
static const pred_table_type pred_unpack_table(get_pred_unpack_table());
//maps trailer bits to num empty bytes
//maps num empty bytes to trailer bits
static const size_t occ_table[] = {0, 2, 1, 3};
const boost::uint32_t VRLP = ('V' << 24) | ('R' << 16) | ('L' << 8) | ('P' << 0);
const boost::uint32_t VEND = ('V' << 24) | ('E' << 16) | ('N' << 8) | ('D' << 0);
UHD_INLINE static boost::uint32_t chdr_to_vrt(const boost::uint32_t chdr, if_packet_info_t &info)
{
const boost::uint32_t bytes = chdr & 0xffff;
boost::uint32_t vrt = (bytes + 3)/4;
info.packet_count = (chdr >> 16) & 0xfff;
vrt |= ((chdr >> 31) & 0x1) << 30; //context packet
vrt |= ((chdr >> 29) & 0x1) << 20; //has tsf
vrt |= ((chdr >> 28) & 0x1) << 24; //has eob
vrt |= (0x1) << 28; //has sid (always)
return vrt;
}
UHD_INLINE static boost::uint32_t vrt_to_chdr(const boost::uint32_t vrt, const if_packet_info_t &info)
{
const boost::uint32_t words32 = vrt & 0xffff;
int bytes_rem = info.num_payload_bytes % 4;
if (bytes_rem != 0) bytes_rem -= 4; //adjust for round up
boost::uint32_t chdr = (words32 * 4) + bytes_rem;
chdr |= (info.packet_count & 0xfff) << 16;
chdr |= ((vrt >> 30) & 0x1) << 31; //context packet
chdr |= ((vrt >> 20) & 0x1) << 29; //has tsf
chdr |= ((vrt >> 24) & 0x1) << 28; //has eob
return chdr;
}
########################################################################
#def gen_code($XE_MACRO, $suffix)
########################################################################
/***********************************************************************
* interal impl of packing VRT IF header only
**********************************************************************/
UHD_INLINE void __if_hdr_pack_$(suffix)(
boost::uint32_t *packet_buff,
if_packet_info_t &if_packet_info,
boost::uint32_t &vrt_hdr_word32
){
boost::uint32_t vrt_hdr_flags = 0;
pred_type pred = 0;
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);
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**7)
case $pred:
#set $num_header_words = 1
#set $flags = 0
########## Stream ID ##########
#if $pred & $sid_p
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
packet_buff[$num_header_words] = 0; //not implemented
#set $num_header_words += 1
packet_buff[$num_header_words] = 0; //not implemented
#set $num_header_words += 1
#set $flags |= (0x1 << 27);
#end if
########## Integer Time ##########
#if $pred & $tsi_p
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
packet_buff[$num_header_words] = $(XE_MACRO)(boost::uint32_t(if_packet_info.tsf >> 32));
#set $num_header_words += 1
packet_buff[$num_header_words] = $(XE_MACRO)(boost::uint32_t(if_packet_info.tsf >> 0));
#set $num_header_words += 1
#set $flags |= (0x1 << 20);
#end if
########## Burst Flags ##########
#if $pred & $eob_p
#set $flags |= (0x1 << 24);
#end if
#if $pred & $sob_p
#set $flags |= (0x1 << 25);
#end if
########## Trailer ##########
#if $pred & $tlr_p
{
const size_t empty_bytes = if_packet_info.num_payload_words32*sizeof(boost::uint32_t) - if_packet_info.num_payload_bytes;
if_packet_info.tlr = (0x3 << 22) | (occ_table[empty_bytes & 0x3] << 10);
}
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 ##########
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
}
//fill in complete header word
vrt_hdr_word32 = boost::uint32_t(0
| (if_packet_info.packet_type << 29)
| vrt_hdr_flags
| ((if_packet_info.packet_count & 0xf) << 16)
| (if_packet_info.num_packet_words32 & 0xffff)
);
}
/***********************************************************************
* interal impl of unpacking VRT IF header only
**********************************************************************/
UHD_INLINE void __if_hdr_unpack_$(suffix)(
const boost::uint32_t *packet_buff,
if_packet_info_t &if_packet_info,
const boost::uint32_t vrt_hdr_word32
){
const size_t packet_words32 = vrt_hdr_word32 & 0xffff;
//failure case
if (if_packet_info.num_packet_words32 < packet_words32)
throw uhd::value_error("bad vrt header or packet fragment");
//extract fields from the header
if_packet_info.packet_type = if_packet_info_t::packet_type_t(vrt_hdr_word32 >> 29);
if_packet_info.packet_count = (vrt_hdr_word32 >> 16) & 0xf;
const pred_type pred = pred_unpack_table[pred_table_index(vrt_hdr_word32)];
size_t empty_bytes = 0;
switch(pred){
#for $pred in range(2**7)
case $pred:
#set $has_time_spec = False
#set $num_header_words = 1
########## Stream ID ##########
#if $pred & $sid_p
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
if_packet_info.has_cid = true;
if_packet_info.cid = 0; //not implemented
#set $num_header_words += 2
#else
if_packet_info.has_cid = false;
#end if
########## Integer Time ##########
#if $pred & $tsi_p
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
if_packet_info.has_tsf = true;
if_packet_info.tsf = boost::uint64_t($(XE_MACRO)(packet_buff[$num_header_words])) << 32;
#set $num_header_words += 1
if_packet_info.tsf |= $(XE_MACRO)(packet_buff[$num_header_words]);
#set $num_header_words += 1
#else
if_packet_info.has_tsf = false;
#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
########## Trailer ##########
#if $pred & $tlr_p
if_packet_info.has_tlr = true;
if_packet_info.tlr = $(XE_MACRO)(packet_buff[packet_words32-1]);
#set $num_trailer_words = 1;
{
const int indicators = (if_packet_info.tlr >> 20) & (if_packet_info.tlr >> 8);
if ((indicators & (1 << 0)) != 0) if_packet_info.eob = true;
if ((indicators & (1 << 1)) != 0) if_packet_info.sob = true;
empty_bytes = occ_table[(indicators >> 2) & 0x3];
}
#else
if_packet_info.has_tlr = false;
#set $num_trailer_words = 0;
#end if
########## Variables ##########
//another failure case
if (packet_words32 < $($num_header_words + $num_trailer_words))
throw uhd::value_error("bad vrt header or invalid packet length");
if_packet_info.num_header_words32 = $num_header_words;
if_packet_info.num_payload_words32 = packet_words32 - $($num_header_words + $num_trailer_words);
if_packet_info.num_payload_bytes = if_packet_info.num_payload_words32*sizeof(boost::uint32_t) - empty_bytes;
break;
#end for
}
}
/***********************************************************************
* link layer + VRT IF packing
**********************************************************************/
void vrt::if_hdr_pack_$(suffix)(
boost::uint32_t *packet_buff,
if_packet_info_t &if_packet_info
){
boost::uint32_t vrt_hdr_word32 = 0;
switch (if_packet_info.link_type)
{
case if_packet_info_t::LINK_TYPE_NONE:
__if_hdr_pack_$(suffix)(packet_buff, if_packet_info, vrt_hdr_word32);
packet_buff[0] = $(XE_MACRO)(vrt_hdr_word32);
break;
case if_packet_info_t::LINK_TYPE_CHDR:
{
__if_hdr_pack_$(suffix)(packet_buff, if_packet_info, vrt_hdr_word32);
const boost::uint32_t chdr = vrt_to_chdr(vrt_hdr_word32, if_packet_info);
packet_buff[0] = $(XE_MACRO)(chdr);
break;
}
case if_packet_info_t::LINK_TYPE_VRLP:
__if_hdr_pack_$(suffix)(packet_buff+2, if_packet_info, vrt_hdr_word32);
if_packet_info.num_header_words32 += 2;
if_packet_info.num_packet_words32 += 3;
packet_buff[0] = $(XE_MACRO)(VRLP);
packet_buff[1] = $(XE_MACRO)(boost::uint32_t(
(if_packet_info.num_packet_words32 & 0xfffff) |
((if_packet_info.packet_count & 0xfff) << 20)
));
packet_buff[2] = $(XE_MACRO)(vrt_hdr_word32);
packet_buff[if_packet_info.num_packet_words32-1] = $(XE_MACRO)(VEND);
break;
}
}
/***********************************************************************
* link layer + VRT IF unpacking
**********************************************************************/
void vrt::if_hdr_unpack_$(suffix)(
const boost::uint32_t *packet_buff,
if_packet_info_t &if_packet_info
){
boost::uint32_t vrt_hdr_word32 = 0;
switch (if_packet_info.link_type)
{
case if_packet_info_t::LINK_TYPE_NONE:
vrt_hdr_word32 = $(XE_MACRO)(packet_buff[0]);
__if_hdr_unpack_$(suffix)(packet_buff, if_packet_info, vrt_hdr_word32);
break;
case if_packet_info_t::LINK_TYPE_CHDR:
{
const boost::uint32_t chdr = $(XE_MACRO)(packet_buff[0]);
vrt_hdr_word32 = chdr_to_vrt(chdr, if_packet_info);
size_t packet_count = if_packet_info.packet_count;
__if_hdr_unpack_$(suffix)(packet_buff, if_packet_info, vrt_hdr_word32);
if_packet_info.num_payload_bytes -= (~chdr + 1) & 0x3;
if_packet_info.packet_count = packet_count;
break;
}
case if_packet_info_t::LINK_TYPE_VRLP:
{
if ($(XE_MACRO)(packet_buff[0]) != VRLP) throw uhd::value_error("bad vrl header VRLP");
const boost::uint32_t vrl_hdr = $(XE_MACRO)(packet_buff[1]);
vrt_hdr_word32 = $(XE_MACRO)(packet_buff[2]);
if (if_packet_info.num_packet_words32 < (vrl_hdr & 0xfffff)) throw uhd::value_error("bad vrl header or packet fragment");
if ($(XE_MACRO)(packet_buff[(vrl_hdr & 0xfffff)-1]) != VEND) throw uhd::value_error("bad vrl trailer VEND");
__if_hdr_unpack_$(suffix)(packet_buff+2, if_packet_info, vrt_hdr_word32);
if_packet_info.num_header_words32 += 2; //add vrl header
if_packet_info.packet_count = (vrl_hdr >> 20) & 0xfff;
break;
}
}
}
########################################################################
#end def
########################################################################
$gen_code("BE_MACRO", "be")
$gen_code("LE_MACRO", "le")
"""
def parse_tmpl(_tmpl_text, **kwargs):
from Cheetah.Template import Template
return str(Template(_tmpl_text, kwargs))
if __name__ == '__main__':
import sys
open(sys.argv[1], 'w').write(parse_tmpl(
TMPL_TEXT,
file=__file__,
sid_p = 0b0000001,
cid_p = 0b0000010,
tsi_p = 0b0000100,
tsf_p = 0b0001000,
tlr_p = 0b0010000,
sob_p = 0b0100000,
eob_p = 0b1000000,
))