diff options
Diffstat (limited to 'host')
| -rw-r--r-- | host/include/uhd/rfnoc/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | host/include/uhd/rfnoc/chdr/CMakeLists.txt | 12 | ||||
| -rw-r--r-- | host/include/uhd/rfnoc/chdr/chdr_header.hpp | 238 | ||||
| -rw-r--r-- | host/include/uhd/rfnoc/chdr/chdr_packet.hpp | 160 | ||||
| -rw-r--r-- | host/tests/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | host/tests/rfnoc_chdr_test.cpp | 95 | 
6 files changed, 507 insertions, 1 deletions
diff --git a/host/include/uhd/rfnoc/CMakeLists.txt b/host/include/uhd/rfnoc/CMakeLists.txt index b515bfe8c..7b5b69f4b 100644 --- a/host/include/uhd/rfnoc/CMakeLists.txt +++ b/host/include/uhd/rfnoc/CMakeLists.txt @@ -39,7 +39,7 @@ if(ENABLE_RFNOC)          COMPONENT headers      )  endif(ENABLE_RFNOC) - +add_subdirectory(chdr)  add_subdirectory(blocks)  #add_subdirectory(components) diff --git a/host/include/uhd/rfnoc/chdr/CMakeLists.txt b/host/include/uhd/rfnoc/chdr/CMakeLists.txt new file mode 100644 index 000000000..3ef4d3b64 --- /dev/null +++ b/host/include/uhd/rfnoc/chdr/CMakeLists.txt @@ -0,0 +1,12 @@ +# +# Copyright 2019 Ettus Research, a National Instruments Brand +# +# SPDX-License-Identifier: GPL-3.0-or-later +# + +UHD_INSTALL(FILES +    chdr_header.hpp +    chdr_packet.hpp +    DESTINATION ${PKG_DATA_DIR}/rfnoc/chdr +    COMPONENT headers # TODO: Different component +) diff --git a/host/include/uhd/rfnoc/chdr/chdr_header.hpp b/host/include/uhd/rfnoc/chdr/chdr_header.hpp new file mode 100644 index 000000000..bd6d71fd1 --- /dev/null +++ b/host/include/uhd/rfnoc/chdr/chdr_header.hpp @@ -0,0 +1,238 @@ +// +// Copyright 2019 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#ifndef INCLUDED_RFNOC_CHDR_HEADER_HPP +#define INCLUDED_RFNOC_CHDR_HEADER_HPP + +#include <uhd/types/endianness.hpp> +#include <uhd/utils/byteswap.hpp> +#include <cassert> + +namespace uhd { namespace rfnoc { namespace chdr { + +static constexpr size_t FLAGS_OFFSET        = 58; +static constexpr size_t PKT_TYPE_OFFSET     = 55; +static constexpr size_t NUM_METADATA_OFFSET = 48; +static constexpr size_t SEQ_NUM_OFFSET      = 32; +static constexpr size_t LENGTH_OFFSET       = 16; +static constexpr size_t DST_EPID_OFFSET     = 0; +static constexpr uint64_t FLAGS_MASK        = ((uint64_t)0x3F << FLAGS_OFFSET); +static constexpr uint64_t PKT_TYPE_MASK     = ((uint64_t)0x7 << PKT_TYPE_OFFSET); +static constexpr uint64_t NUM_METADATA_MASK = ((uint64_t)0x7F << NUM_METADATA_OFFSET); +static constexpr uint64_t SEQ_NUM_MASK      = ((uint64_t)0xFFFF << SEQ_NUM_OFFSET); +static constexpr uint64_t LENGTH_MASK       = ((uint64_t)0xFFFF << LENGTH_OFFSET); +static constexpr uint64_t DST_EPID_MASK     = ((uint64_t)0xFFFF << DST_EPID_OFFSET); + +enum class packet_type_t { +    PACKET_TYPE_MGMT         = 0x0, // Management packet +    PACKET_TYPE_STS          = 0x1, // Stream status +    PACKET_TYPE_STC          = 0x2, // Stream Command +    PACKET_TYPE_CTRL         = 0x4, // Control Transaction +    PACKET_TYPE_DATA_NO_TS   = 0x6, // Data Packet without TimeStamp +    PACKET_TYPE_DATA_WITH_TS = 0x7, // Data Packet with TimeStamp +}; + +/*! Header information of a data packet + * This is the first 64-bit + */ +template <size_t chdr_w> class chdr_header +{ +public: + +    ~chdr_header() = default; + +    chdr_header(packet_type_t packet_type) +    { +        _set_pkt_type(packet_type); +    } + +    chdr_header(uint64_t header_val) : _header_val(header_val) +    { +    } + +    /*! Return the word size in bytes +     */ +    constexpr size_t get_word_size() const +    { +        return chdr_w / 8; +    } + +    /*! Returns the 64 header bits in native byte order +     */ +    uint64_t get() const +    { +        return _header_val; +    } + +    /*! Get flags field of this CHDR packet +     */ +    inline uint8_t get_flags() const +    { +        return _get_value<uint8_t>(_header_val, FLAGS_MASK, FLAGS_OFFSET); +    }; + +    /*! Get packet type field of this CHDR packet +     */ +    inline packet_type_t get_pkt_type() const +    { +        return _get_value<packet_type_t>(_header_val, PKT_TYPE_MASK, PKT_TYPE_OFFSET); +    } + +    /*! Get number length of metadata of this CHDR packet in words +     */ +    inline uint8_t get_num_metadata() const +    { +        return _get_value<uint8_t>(_header_val, NUM_METADATA_MASK, NUM_METADATA_OFFSET); +    } + +    /*! Get number length of metadata of this CHDR packet in bytes +     */ +    inline uint8_t get_num_bytes_metadata() const +    { +        return get_num_metadata() * get_word_size(); +    } + +    /*! Get sequence number field of this CHDR packet +     */ +    inline uint16_t get_seq_num() const +    { +        return _get_value<uint16_t>(_header_val, SEQ_NUM_MASK, SEQ_NUM_OFFSET); +    } + +    /*! Get length (in bytes) of this CHDR packet +     */ +    inline uint16_t get_length() const +    { +        return _get_value<uint16_t>(_header_val, LENGTH_MASK, LENGTH_OFFSET); +    } + +    /*! Get dst_epid field of this CHDR packet +     */ +    inline uint16_t get_dst_epid() const +    { +        return _get_value<uint16_t>(_header_val, DST_EPID_MASK, DST_EPID_OFFSET); +    } + +    /*! Return number of payload in the packet (in CHDR word unit) +     */ +    inline size_t get_num_words_payload() const +    { +        return (get_length() / get_word_size()) - get_num_metadata() - 1 +               - _has_timestamp(get_pkt_type()); +    } + +    /*! Return number of payload in the packet (in CHDR word unit) +     */ +    inline size_t get_num_bytes_payload() const +    { +        return get_length() - get_num_bytes_metadata() - get_word_size() +               - _has_timestamp(get_pkt_type()) * get_word_size(); +    } + +    /*! Return offset to payload this offset in bytes unit +     */ +    inline size_t get_metadata_offset() const +    { +        return (1 + _has_timestamp(get_pkt_type())) * get_word_size(); +    } + +    /*! Return offset to payload this offset in bytes unit +     */ +    inline size_t get_payload_offset() const +    { +        return get_metadata_offset() + get_num_metadata() * get_word_size(); +    } + + +    /*********** Setters *****************************************************/ + +    /*! Set flags field of this CHDR packet +     * \param flags value to set to flags field of this CHDR packet +     */ +    inline void set_flags(uint8_t flags) +    { +        _set_value(_header_val, flags, FLAGS_OFFSET, FLAGS_MASK); +    } + +    /*! Set sequence number field of this CHDR packet +     *\param seq_num value to set to sequence number field of this CHDR packet +     */ +    inline void set_seq_num(uint16_t seq_num) +    { +        _set_value(_header_val, seq_num, SEQ_NUM_OFFSET, SEQ_NUM_MASK); +    } + +    /*! Set dst_epid field of this CHDR packet +     * \param dst_epid value to set to dst_epid field of CHDR packet +     */ +    inline void set_dst_epid(uint16_t dst_epid) +    { +        _set_value(_header_val, dst_epid, DST_EPID_OFFSET, DST_EPID_MASK); +    } + +    /*! Set the size of payload and metadata in word unit +     *\param num_payload number of payload in word +     *\param num_metadata number of metadata in word +     */ +    inline void set_packet_size(size_t num_payload, size_t num_metadata = 0) +    { +        _set_num_metadata(num_metadata); +        _update_length(num_metadata, num_payload); +    } + +private: +    /*! The actual header bits, stored in native (host) byte order +     */ +    uint64_t _header_val = 0; + +    template <typename value_t, typename data_t> +    inline value_t _get_value(const data_t data, data_t mask, size_t offset) const +    { +        return static_cast<value_t>((data & mask) >> offset); +    } + +    template <typename data_t, typename value_t> +    inline void _set_value(data_t& data, value_t value, size_t offset, data_t mask) +    { +        data = (data & ~mask) | (static_cast<data_t>(value) << offset); +    } + +    inline uint8_t _has_timestamp(packet_type_t pkt_type) const +    { +        return pkt_type == packet_type_t::PACKET_TYPE_DATA_WITH_TS and chdr_w == 64 ? 1 +                                                                                    : 0; +    } + +    inline void _update_length(uint8_t num_metadata, size_t num_payload) +    { +        _set_length(static_cast<uint16_t>( +            (num_metadata + num_payload + 1 + _has_timestamp(get_pkt_type())) * chdr_w +            / 8)); +    } + +    inline void _set_length(uint16_t length) +    { +        _set_value(_header_val, length, LENGTH_OFFSET, LENGTH_MASK); +    } + +    inline void _set_pkt_type(packet_type_t pkt_type) +    { +        _set_value(_header_val, pkt_type, PKT_TYPE_OFFSET, PKT_TYPE_MASK); +    } + +    /*! Set number of metadata (in word) of this CHDR packet +     * This function also update the total length field of this CHDR packet +     * \param num_metadata number of metadata (in word) of this CHDR packet +     */ +    inline void _set_num_metadata(uint8_t num_metadata) +    { +        _set_value(_header_val, num_metadata, NUM_METADATA_OFFSET, NUM_METADATA_MASK); +    } +}; + +}}} // namespace uhd::rfnoc::chdr + +#endif /* INCLUDED_RFNOC_CHDR_HEADER_HPP */ diff --git a/host/include/uhd/rfnoc/chdr/chdr_packet.hpp b/host/include/uhd/rfnoc/chdr/chdr_packet.hpp new file mode 100644 index 000000000..89716679a --- /dev/null +++ b/host/include/uhd/rfnoc/chdr/chdr_packet.hpp @@ -0,0 +1,160 @@ +// +// Copyright 2019 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#ifndef INCLUDED_RFNOC_CHDR_DATA_PACKET_HPP +#define INCLUDED_RFNOC_CHDR_DATA_PACKET_HPP + +#include <uhd/rfnoc/chdr/chdr_header.hpp> +#include <cassert> + +namespace uhd { namespace rfnoc { namespace chdr { + +/*! CHDR Packet Abstraction Class + * + * This class wraps a pointer to a buffer containing a CHDR packet. It also + * provides access to the packet header in native byte ordering. The byte + * ordering of the metadata and payload needs to be handled elsewhere, but it + * needs to be also passed into this class as a template argument \p endianness. + * The byte ordering refers to the byte order on the transport. + * + * No size checking is performed in this class. The size of the buffer needs to + * be tracked separately in order to avoid buffer overflows. + * + * NOTE: This assumes the host is little-endian. + */ +template <size_t chdr_w, endianness_t endianness> +class chdr_packet +{ +public: +    typedef chdr_header<chdr_w> header_t; + +    ~chdr_packet() = default; + +    /*! This constructor is used to parse an incoming CHDR packet. +     * +     * Note: If the header declares the size of the packet to be larger than the +     * valid memory readable at \p pkt_buff, then there is the possibility of a +     * buffer overflow ocurring. +     * +     * \param pkt_buff a pointer to the packet buffer. Its byte ordering has to be +     *                 provided as the template argument \p endianness +     */ +    chdr_packet(void* pkt_buff) +    { +        assert(pkt_buff != NULL); +        _pkt_buff = static_cast<uint64_t*>(pkt_buff); +        if (endianness == endianness_t::ENDIANNESS_BIG) { +            _header = header_t(ntohx<uint64_t>(*_pkt_buff)); +        } else { +            _header = header_t(*_pkt_buff); +        } +    } + +    /*! This constructor is used to construct an outgoing CHDR packet with given header. +     * +     * \param header CHDR header (in host-native byte ordering) +     * \param pkt_buff a pointer to packet buffer. Its byte ordering has to be provided +     *                 as the template argument \p endianness +     */ +    chdr_packet(header_t header, void* pkt_buff) : _header(header) +    { +        assert(pkt_buff != NULL); +        _pkt_buff = static_cast<uint64_t*>(pkt_buff); +        if (endianness == endianness_t::ENDIANNESS_BIG) { +            *_pkt_buff = htonx<uint64_t>(header.get()); +        } else { +            *_pkt_buff = header.get(); +        } +    } + +    /*! Get a copy of the header data (the first 64-bit of the CHDR packet) +     * +     * This copy will be in native byte order. +    */ +    inline header_t get_header() const +    { +        return _header; +    } + +    /*! Return the pointer to the first byte of metadata of this CHDR packet +     * +     * Note: This class does no bounds checking. It is up to the user to only +     * to the metadata portion of the the CHDR buffer. +     * +     * ~~~{.cpp} +     * // Assume chdrpkt is of type chdr_packet: +     * uint8_t* metadata_ptr = chdrpkt.metadata_ptr_of_type<uint8_t>(); +     * const size_t metadata_size = chdrpkt.get_header().get_num_bytes_metadata(); +     * // The following expression is fine if metadata_size > 0: +     * metadata_ptr[0] = 0xF0; +     * // The following expression is never fine, but the code will not throw an +     * // exception (most likely, it won't segfault either, unless the payload +     * // size is zero): +     * metadata_ptr[metadata_size] = 0xF0; +     * ~~~ +    */ +    template <typename data_t> inline data_t* metadata_ptr_of_type() const +    { +        return reinterpret_cast<data_t*>( +                reinterpret_cast<char*>(_pkt_buff) + _header.get_metadata_offset()); +    } + +    /*! Get 64-bit timestamp of this CHDR packet in native byte order +     * +     * This will byteswap if necessary. +     * +     */ +    inline uint64_t get_timestamp() const +    { +        assert(_header.get_pkt_type() == packet_type_t::PACKET_TYPE_DATA_WITH_TS); +        return endianness_t::ENDIANNESS_BIG ? ntohx<uint64_t>(*(_pkt_buff + 1)) +            : *(_pkt_buff + 1); +    } + +    /*! Set 64-bit timestamp of this CHDR packet. +     * +     * If this packet is not a data packet with timestamp, this operation will +     * overwrite the metadata or payload memory. +     * +     * \param timestamp 64-bit value of timestamp of this CHDR packet +     */ +    inline void set_timestamp(uint64_t timestamp) +    { +        assert(_header.get_pkt_type() == packet_type_t::PACKET_TYPE_DATA_WITH_TS); +        *(_pkt_buff + 1) = endianness_t::ENDIANNESS_BIG ? htonx<uint64_t>(timestamp) +            : timestamp; +    } + +    /*! Return the pointer to the first payload byte of this CHDR packet +     * +     * Note: This class does no bounds checking. It is up to the user to only +     * to the metadata portion of the the CHDR buffer. +     * +     * ~~~{.cpp} +     * // Assume chdrpkt is of type chdr_packet: +     * uint8_t* payload_ptr = chdrpkt.metadata_ptr_of_type<uint8_t>(); +     * const size_t payload_size = chdrpkt.get_header().get_num_bytes_payload(); +     * // The following expression is fine if payload_size > 0: +     * payload_ptr[0] = 0xF0; +     * // The following expression is never fine, but the code will not throw an +     * // exception. Instead, it could even segfault: +     * payload_ptr[payload_size] = 0xF0; +     * ~~~ +    */ +    template <typename payload_t> inline payload_t* payload_ptr_of_type() const +    { +        return reinterpret_cast<payload_t*>( +                reinterpret_cast<char*>(_pkt_buff) + _header.get_payload_offset()); +    } + +private: +    uint64_t* _pkt_buff; +    header_t _header = header_t(0); +}; + +}}} // namespace uhd::rfnoc::chdr + +#endif /* INCLUDED_RFNOC_CHDR_DATA_PACKET_HPP */ diff --git a/host/tests/CMakeLists.txt b/host/tests/CMakeLists.txt index e476eb61b..e4255196b 100644 --- a/host/tests/CMakeLists.txt +++ b/host/tests/CMakeLists.txt @@ -52,6 +52,7 @@ set(test_sources      vrt_test.cpp      expert_test.cpp      fe_conn_test.cpp +    rfnoc_chdr_test.cpp  )  set(benchmark_sources diff --git a/host/tests/rfnoc_chdr_test.cpp b/host/tests/rfnoc_chdr_test.cpp new file mode 100644 index 000000000..1fb2df8cf --- /dev/null +++ b/host/tests/rfnoc_chdr_test.cpp @@ -0,0 +1,95 @@ +// +// Copyright 2019 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#include <uhd/rfnoc/chdr/chdr_packet.hpp> +#include <uhd/types/endianness.hpp> +#include <uhd/utils/byteswap.hpp> +#include <boost/format.hpp> +#include <boost/test/unit_test.hpp> +#include <cstring> +#include <cstdint> + +using namespace uhd::rfnoc::chdr; + +namespace { +    constexpr size_t MAX_BUF_SIZE = 8192; + +    // Poorman's send +    void send(const void* from, void* to, size_t len) +    { +        std::memcpy(to, from, len); +    } +} + +uint8_t send_packet_buff[MAX_BUF_SIZE]; +uint8_t recv_packet_buff[MAX_BUF_SIZE]; + + +template <size_t chdr_w, uhd::endianness_t endianness> +void test_loopback(packet_type_t pkt_type, +    uint16_t dst_epid, +    uint8_t flags, +    uint16_t num_md, +    size_t num_payload) +{ +    // Clear buffers +    std::memset(send_packet_buff, 0, MAX_BUF_SIZE); +    std::memset(recv_packet_buff, 0, MAX_BUF_SIZE); + +    auto send_header = chdr_header<chdr_w>(pkt_type); +    send_header.set_dst_epid(dst_epid); +    send_header.set_flags(flags); +    send_header.set_packet_size(num_payload, num_md); +    chdr_packet<chdr_w, endianness> send_chdr_packet(send_header, send_packet_buff); +    //chdr_packet<64, uhd::endianness_t::ENDIANNESS_LITTLE> send_chdr_packet(send_header, send_packet_buff); +    uint8_t* md_buff = send_chdr_packet.template metadata_ptr_of_type<uint8_t>(); +    //* start filling in the meta data +    for (size_t i = 0; i < send_chdr_packet.get_header().get_num_bytes_metadata(); i++) { +        md_buff[i] = i + 1; +    } +    auto* pay_load = send_chdr_packet.template payload_ptr_of_type<uint8_t>(); +    //* start filling in the pay load +    for (size_t i = 0; i < send_chdr_packet.get_header().get_num_bytes_payload(); i++) { +        pay_load[i] = 2 * (i + 1); +    } +    send(send_packet_buff, recv_packet_buff, MAX_BUF_SIZE); + +    const chdr_packet<chdr_w, endianness> recv_chdr_packet(recv_packet_buff); +    BOOST_CHECK_EQUAL((num_md + num_payload + 1 +                          + (pkt_type == packet_type_t::PACKET_TYPE_DATA_WITH_TS ? 1 : 0)) +                          * (chdr_w / 8), +        recv_chdr_packet.get_header().get_length()); +    BOOST_CHECK_EQUAL(flags, recv_chdr_packet.get_header().get_flags()); +    BOOST_CHECK_EQUAL(dst_epid, recv_chdr_packet.get_header().get_dst_epid()); +    BOOST_CHECK_EQUAL(num_md, recv_chdr_packet.get_header().get_num_metadata()); +    BOOST_CHECK_EQUAL(num_payload, recv_chdr_packet.get_header().get_num_words_payload()); +    BOOST_CHECK(pkt_type == recv_chdr_packet.get_header().get_pkt_type()); +    const auto* out_md_buff = recv_chdr_packet.template metadata_ptr_of_type<uint8_t>(); +    for (size_t i = 0; i < recv_chdr_packet.get_header().get_num_bytes_metadata(); i++) { +        BOOST_CHECK_EQUAL(md_buff[i], out_md_buff[i]); +    } +    const auto* out_payload = recv_chdr_packet.template payload_ptr_of_type<uint8_t>(); +    for (size_t i = 0; i < recv_chdr_packet.get_header().get_num_bytes_payload(); i++) { +        BOOST_CHECK_EQUAL(pay_load[i], out_payload[i]); +    } +} + +BOOST_AUTO_TEST_CASE(simple_read_if_chdr_pkt) +{ +    constexpr size_t num_payload           = 5; // in words +    constexpr packet_type_t pkt_type       = packet_type_t::PACKET_TYPE_DATA_NO_TS; +    constexpr uint16_t num_md              = 0x5A; // 90 +    constexpr uint16_t dst_epid            = 0xCAFE; +    constexpr uint8_t flags                = 0x3A; + +    test_loopback<64, uhd::endianness_t::ENDIANNESS_LITTLE>(pkt_type, dst_epid, flags, num_md, num_payload); +    test_loopback<128, uhd::endianness_t::ENDIANNESS_LITTLE>(pkt_type, dst_epid, flags, num_md, num_payload); +    test_loopback<256, uhd::endianness_t::ENDIANNESS_LITTLE>(pkt_type, dst_epid, flags, num_md, num_payload); + +    test_loopback<64, uhd::endianness_t::ENDIANNESS_BIG>(pkt_type, dst_epid, flags, num_md, num_payload); +    test_loopback<128, uhd::endianness_t::ENDIANNESS_BIG>(pkt_type, dst_epid, flags, num_md, num_payload); +    test_loopback<256, uhd::endianness_t::ENDIANNESS_BIG>(pkt_type, dst_epid, flags, num_md, num_payload); +}  | 
