#!/usr/bin/env python # # Copyright 2010-2013,2015 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 name="gen_code(XE_MACRO, suffix)"> ######################################################################## /*********************************************************************** * internal 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}:<% num_header_words = 1 %><% flags = 0 %> ########## Stream ID ########## % if pred & sid_p: packet_buff[${num_header_words}] = ${XE_MACRO}(if_packet_info.sid);<% num_header_words += 1 %><% flags |= (0x1 << 28) %> % endif ########## Class ID ########## % if pred & cid_p: packet_buff[${num_header_words}] = 0; //not implemented<% num_header_words += 1 %> packet_buff[${num_header_words}] = 0; //not implemented<% num_header_words += 1 %><% flags |= (0x1 << 27) %> % endif ########## Integer Time ########## % if pred & tsi_p: packet_buff[${num_header_words}] = ${XE_MACRO}(if_packet_info.tsi);<% num_header_words += 1 %><% flags |= (0x3 << 22) %> % endif ########## Fractional Time ########## % if pred & tsf_p: packet_buff[${num_header_words}] = ${XE_MACRO}(boost::uint32_t(if_packet_info.tsf >> 32));<% num_header_words += 1 %> packet_buff[${num_header_words}] = ${XE_MACRO}(boost::uint32_t(if_packet_info.tsf >> 0));<% num_header_words += 1 %><% flags |= (0x1 << 20) %> % endif ########## Burst Flags ########## <% if pred & eob_p: flags |= (0x1 << 24) %><% if pred & sob_p: flags |= (0x1 << 25) %> ########## 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);<% flags |= (0x1 << 26) %><% num_trailer_words = 1 %> % else: <% num_trailer_words = 0 %> % endif ########## 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; % endfor } //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) ); } /*********************************************************************** * internal 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}:<% has_time_spec = False %><% 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}]);<% num_header_words += 1 %> % else: if_packet_info.has_sid = false; % endif ########## Class ID ########## % if pred & cid_p: if_packet_info.has_cid = true; if_packet_info.cid = 0; //not implemented<% num_header_words += 2 %> % else: if_packet_info.has_cid = false; % endif ########## Integer Time ########## % if pred & tsi_p: if_packet_info.has_tsi = true; if_packet_info.tsi = ${XE_MACRO}(packet_buff[${num_header_words}]); <% num_header_words += 1 %> % else: if_packet_info.has_tsi = false; % endif ########## 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;<% num_header_words += 1 %> if_packet_info.tsf |= ${XE_MACRO}(packet_buff[${num_header_words}]);<% num_header_words += 1 %> % else: if_packet_info.has_tsf = false; % endif ########## Burst Flags ########## % if pred & eob_p: if_packet_info.eob = true; % else: if_packet_info.eob = false; % endif % if pred & sob_p: if_packet_info.sob = true; % else: if_packet_info.sob = false; % endif ########## Trailer ########## % if pred & tlr_p: if_packet_info.has_tlr = true; if_packet_info.tlr = ${XE_MACRO}(packet_buff[packet_words32-1]);<% 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;<% num_trailer_words = 0 %> % endif ########## 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; % endfor } } /*********************************************************************** * 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; } } } ######################################################################## ######################################################################## ${gen_code("BE_MACRO", "be")} ${gen_code("LE_MACRO", "le")} """ def parse_tmpl(_tmpl_text, **kwargs): from mako.template import Template return Template(_tmpl_text).render(**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, ))