aboutsummaryrefslogtreecommitdiffstats
path: root/host
diff options
context:
space:
mode:
authorAshish Chaudhari <ashish@ettus.com>2019-05-14 16:46:07 -0700
committerMartin Braun <martin.braun@ettus.com>2019-11-26 11:49:14 -0800
commita38845de27ccf51b60dca342101dff7a2ba552ec (patch)
tree4e5a8beae6483e2945020d3066ebeecade61ea42 /host
parentc97bdc6c94c98753215a90cf499af4bdf06db8e2 (diff)
downloaduhd-a38845de27ccf51b60dca342101dff7a2ba552ec.tar.gz
uhd-a38845de27ccf51b60dca342101dff7a2ba552ec.tar.bz2
uhd-a38845de27ccf51b60dca342101dff7a2ba552ec.zip
rfnoc: Refactored CHDR packet interfaces
- Moved packet interface code from public to private include - Split packet interface into two files: payload paring and packet iface - Added support for all CHDR packet types - Added more test cases to unit test
Diffstat (limited to 'host')
-rw-r--r--host/include/uhd/rfnoc/CMakeLists.txt1
-rw-r--r--host/include/uhd/rfnoc/chdr/CMakeLists.txt12
-rw-r--r--host/include/uhd/rfnoc/chdr/chdr_header.hpp285
-rw-r--r--host/include/uhd/rfnoc/chdr/chdr_packet.hpp294
-rw-r--r--host/lib/include/uhdlib/rfnoc/chdr/chdr_packet.hpp290
-rw-r--r--host/lib/include/uhdlib/rfnoc/chdr/chdr_types.hpp837
-rw-r--r--host/lib/rfnoc/CMakeLists.txt2
-rw-r--r--host/lib/rfnoc/chdr/CMakeLists.txt15
-rw-r--r--host/lib/rfnoc/chdr/chdr_packet.cpp213
-rw-r--r--host/lib/rfnoc/chdr/chdr_types.cpp426
-rw-r--r--host/tests/CMakeLists.txt14
-rw-r--r--host/tests/rfnoc_chdr_test.cpp263
12 files changed, 1992 insertions, 660 deletions
diff --git a/host/include/uhd/rfnoc/CMakeLists.txt b/host/include/uhd/rfnoc/CMakeLists.txt
index bc5d7f054..894d4fda4 100644
--- a/host/include/uhd/rfnoc/CMakeLists.txt
+++ b/host/include/uhd/rfnoc/CMakeLists.txt
@@ -42,7 +42,6 @@ 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
deleted file mode 100644
index 3ef4d3b64..000000000
--- a/host/include/uhd/rfnoc/chdr/CMakeLists.txt
+++ /dev/null
@@ -1,12 +0,0 @@
-#
-# 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
deleted file mode 100644
index 1fdbdc0b5..000000000
--- a/host/include/uhd/rfnoc/chdr/chdr_header.hpp
+++ /dev/null
@@ -1,285 +0,0 @@
-//
-// 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 <uhd/exception.hpp>
-#include <cassert>
-#include <memory>
-
-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
-};
-
-
-class chdr_header_iface
-{
-public:
- using uptr = std::unique_ptr<chdr_header_iface>;
-
- static uptr make(size_t chdr_w, uint64_t header_val);
- static uptr make(size_t chdr_w, packet_type_t pkt_type);
- virtual size_t get_word_size() const = 0;
- virtual uint64_t get() const = 0;
- virtual uint8_t get_flags() const = 0;
- virtual packet_type_t get_pkt_type() const = 0;
- virtual uint8_t get_num_metadata() const = 0;
- virtual uint8_t get_num_bytes_metadata() const = 0;
- virtual uint16_t get_seq_num() const = 0;
- virtual uint16_t get_length() const = 0;
- virtual uint16_t get_dst_epid() const = 0;
- virtual size_t get_num_words_payload() const = 0;
- virtual size_t get_num_bytes_payload() const = 0;
- virtual size_t get_metadata_offset() const = 0;
- virtual size_t get_payload_offset() const = 0;
- virtual void set_flags(uint8_t flags) = 0;
- virtual void set_seq_num(uint16_t seq_num) = 0;
- virtual void set_dst_epid(uint16_t dst_epid) = 0;
- virtual void set_packet_size(size_t num_payload, size_t num_metadata = 0) = 0;
-};
-
-/*! Header information of a data packet
- * This is the first 64-bit
- */
-template <size_t chdr_w>
-class chdr_header : public chdr_header_iface
-{
-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);
- }
-};
-
-chdr_header_iface::uptr chdr_header_iface::make(size_t chdr_w, uint64_t header_val)
-{
- if (chdr_w == 256)
- return uptr(new chdr_header<256>(header_val));
- else
- return uptr(new chdr_header<64>(header_val));
-}
-
-chdr_header_iface::uptr chdr_header_iface::make(size_t chdr_w, packet_type_t pkt_type)
-{
- switch (chdr_w) {
- case 256:
- return uptr(new chdr_header<256>(pkt_type));
- case 64:
- return uptr(new chdr_header<64>(pkt_type));
- default:
- UHD_THROW_INVALID_CODE_PATH();
- }
-}
-
-}}} // 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
deleted file mode 100644
index 0a2ae68f3..000000000
--- a/host/include/uhd/rfnoc/chdr/chdr_packet.hpp
+++ /dev/null
@@ -1,294 +0,0 @@
-//
-// 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 <uhd/exception.hpp>
-#include <cassert>
-#include <memory>
-
-namespace uhd { namespace rfnoc { namespace chdr {
-
-class chdr_packet_iface
-{
-public:
- typedef std::unique_ptr<chdr_packet_iface> uptr;
- static uptr make(void* pkt_buff);
- static uptr make(size_t chdr_w, endianness_t endianness, void* pkt_buff);
- static uptr make(
- size_t chdr_w, endianness_t endianness, uint64_t header_val, void* pkt_buff);
- virtual uint64_t get_header_val() const = 0;
- virtual void set_header_val(uint64_t header_val) = 0;
- virtual uint64_t get_timestamp() const = 0;
- virtual void set_timestamp(uint64_t timestamp) = 0;
- virtual uint64_t* metadata_ptr_of_type_uint64_t() const = 0;
- virtual uint32_t* metadata_ptr_of_type_uint32_t() const = 0;
- virtual uint64_t* payload_ptr_of_type_uint64_t() const = 0;
- virtual uint32_t* payload_ptr_of_type_uint32_t() const = 0;
- virtual uint16_t* payload_ptr_of_type_uint16_t() const = 0;
-};
-
-/*! 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 chdr_packet_iface
-{
-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;
- }
-
- /*! Get a copy of the 64-bit value of the packet header (the first 64-bit
- * of the CHDR packet)
- *
- * This copy will be in native byte order.
- */
- inline uint64_t get_header_val() const
- {
- return _header.get();
- }
-
- /*! Set 64-bit value of the packet header (the first 64-bit of the CHDR
- * packet)
- *
- * This set 64_bit value will swap byte to native byte order.
- */
- inline void set_header_val(uint64_t header_val)
- {
- if (endianness == endianness_t::ENDIANNESS_BIG) {
- _header = header_t(ntohx<uint64_t>(header_val));
- } else {
- _header = header_t(header_val);
- }
- }
-
- /*! 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());
- }
-
- inline uint64_t* payload_ptr_of_type_uint64_t() const
- {
- return reinterpret_cast<uint64_t*>(
- reinterpret_cast<char*>(_pkt_buff) + _header.get_payload_offset());
- }
-
- inline uint32_t* payload_ptr_of_type_uint32_t() const
- {
- return reinterpret_cast<uint32_t*>(
- reinterpret_cast<char*>(_pkt_buff) + _header.get_payload_offset());
- }
-
- inline uint16_t* payload_ptr_of_type_uint16_t() const
- {
- return reinterpret_cast<uint16_t*>(
- reinterpret_cast<char*>(_pkt_buff) + _header.get_payload_offset());
- }
-
- inline uint64_t* metadata_ptr_of_type_uint64_t() const
- {
- return reinterpret_cast<uint64_t*>(
- reinterpret_cast<char*>(_pkt_buff) + _header.get_metadata_offset());
- }
-
- inline uint32_t* metadata_ptr_of_type_uint32_t() const
- {
- return reinterpret_cast<uint32_t*>(
- reinterpret_cast<char*>(_pkt_buff) + _header.get_metadata_offset());
- }
-
-private:
- uint64_t* _pkt_buff;
- header_t _header = header_t(0);
-};
-
-chdr_packet_iface::uptr chdr_packet_iface::make(void* pkt_buff)
-{
- return uptr(new chdr_packet<64, ENDIANNESS_LITTLE>(pkt_buff));
-}
-
-chdr_packet_iface::uptr chdr_packet_iface::make(
- size_t chdr_w, endianness_t endianness, void* pkt_buff)
-{
- if (endianness == ENDIANNESS_BIG) {
- switch (chdr_w) {
- case 256:
- return uptr(new chdr_packet<256, ENDIANNESS_BIG>(pkt_buff));
- case 64:
- return uptr(new chdr_packet<64, ENDIANNESS_BIG>(pkt_buff));
- default:
- UHD_THROW_INVALID_CODE_PATH();
- }
- } else {
- switch (chdr_w) {
- case 256:
- return uptr(new chdr_packet<256, ENDIANNESS_LITTLE>(pkt_buff));
- case 64:
- return uptr(new chdr_packet<64, ENDIANNESS_LITTLE>(pkt_buff));
- default:
- UHD_THROW_INVALID_CODE_PATH();
- }
- }
-}
-
-chdr_packet_iface::uptr chdr_packet_iface::make(
- size_t chdr_w, endianness_t endianness, uint64_t header_val, void* pkt_buff)
-{
- if (endianness == ENDIANNESS_BIG) {
- switch (chdr_w) {
- case 256:
- return uptr(new chdr_packet<256, ENDIANNESS_BIG>(
- chdr_header<256>(header_val), pkt_buff));
- case 64:
- return uptr(new chdr_packet<64, ENDIANNESS_BIG>(
- chdr_header<64>(header_val), pkt_buff));
- default:
- UHD_THROW_INVALID_CODE_PATH();
- }
- } else {
- switch (chdr_w) {
- case 256:
- return uptr(new chdr_packet<256, ENDIANNESS_LITTLE>(
- chdr_header<256>(header_val), pkt_buff));
- case 64:
- return uptr(new chdr_packet<64, ENDIANNESS_LITTLE>(
- chdr_header<64>(header_val), pkt_buff));
- default:
- UHD_THROW_INVALID_CODE_PATH();
- }
- }
-}
-
-}}} // namespace uhd::rfnoc::chdr
-
-#endif /* INCLUDED_RFNOC_CHDR_DATA_PACKET_HPP */
diff --git a/host/lib/include/uhdlib/rfnoc/chdr/chdr_packet.hpp b/host/lib/include/uhdlib/rfnoc/chdr/chdr_packet.hpp
new file mode 100644
index 000000000..102a6e104
--- /dev/null
+++ b/host/lib/include/uhdlib/rfnoc/chdr/chdr_packet.hpp
@@ -0,0 +1,290 @@
+//
+// Copyright 2019 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#ifndef INCLUDED_RFNOC_CHDR_PACKET_HPP
+#define INCLUDED_RFNOC_CHDR_PACKET_HPP
+
+#include <uhd/types/endianness.hpp>
+#include <uhd/utils/byteswap.hpp>
+#include <uhdlib/rfnoc/chdr/chdr_types.hpp>
+#include <limits>
+
+namespace uhd { namespace rfnoc { namespace chdr {
+
+//----------------------------------------------------
+// Generic CHDR Packet Container
+//----------------------------------------------------
+
+//! A container class that wraps a generic buffer that contains a CHDR packet. The
+// container provides a high level API to read and write the header, metadata and payload
+// of the packet. The payload can be accessed as a generic buffer using this interface.
+//
+class chdr_packet
+{
+public:
+ //! A unique pointer to a const chdr_packet. Useful as a read-only interface.
+ typedef std::unique_ptr<const chdr_packet> cuptr;
+ //! A unique pointer to a non-const chdr_packet. Useful as a read-write interface.
+ typedef std::unique_ptr<chdr_packet> uptr;
+
+ virtual ~chdr_packet() = 0;
+
+ /*! Updates the underlying storage of this packet. This is a const method and is
+ * only useful for read-only (RX) access.
+ *
+ * \param pkt_buff Pointer to a buffer that contains the RX packet
+ */
+ virtual void refresh(const void* pkt_buff) const = 0;
+
+ /*! Updates the underlying storage of this packet, and populates it with the specified
+ * arguments. This is a non-const method and is useful for read-write (TX) access.
+ *
+ * \param pkt_buff Pointer to a buffer that should be populated with the TX packet
+ * \param header The CHDR header to fill into the TX packet
+ * \param timestamp The timestamp to fill into the TX packet (if requested)
+ */
+ virtual void refresh(void* pkt_buff, chdr_header& header, uint64_t timestamp = 0) = 0;
+
+ /*! Updates the CHDR header with the written payload size
+ *
+ * \param payload_size_bytes The payload size in bytes
+ */
+ virtual void update_payload_size(size_t payload_size_bytes) = 0;
+
+ /*! Returns a class that represents the contents of the CHDR header
+ *
+ * \return The CHDR header
+ */
+ virtual chdr_header get_chdr_header() const = 0;
+
+ /*! Returns the timestamp in the packet as an optional value
+ *
+ * \return A boost::optional which if initialized has the timestamp
+ */
+ virtual boost::optional<uint64_t> get_timestamp() const = 0;
+
+ /*! Returns the endianness of the metadata and payload buffers
+ *
+ * \return The byte order as a uhd::endianness_t
+ */
+ virtual endianness_t get_byte_order() const = 0;
+
+ /*! Returns the maximum transfer unit in bytes
+ *
+ * \return The maximum transfer unit in bytes
+ */
+ virtual size_t get_mtu_bytes() const = 0;
+
+ /*! Returns the metadata size in bytes
+ *
+ * \return The size in bytes
+ */
+ virtual size_t get_mdata_size() const = 0;
+
+ /*! Returns a const void pointer to the metadata section in the packet
+ *
+ * \return A pointer to the metadata
+ */
+ virtual const void* get_mdata_const_ptr() const = 0;
+
+ /*! Returns a non-const void pointer to the metadata section in the packet
+ *
+ * \return A pointer to the metadata
+ */
+ virtual void* get_mdata_ptr() = 0;
+
+ /*! Returns the payload size in bytes
+ *
+ * \return The size in bytes
+ */
+ virtual size_t get_payload_size() const = 0;
+
+ /*! Returns a const void pointer to the payload section in the packet
+ *
+ * \return A pointer to the payload
+ */
+ virtual const void* get_payload_const_ptr() const = 0;
+
+ /*! Returns a non-const void pointer to the payload section in the packet
+ *
+ * \return A pointer to the payload
+ */
+ virtual void* get_payload_ptr() = 0;
+
+ //! Shortcut to return the const metadata pointer cast as a specific type
+ template <typename data_t>
+ inline const data_t* get_mdata_const_ptr_as() const
+ {
+ return reinterpret_cast<const data_t*>(get_mdata_const_ptr());
+ }
+
+ //! Shortcut to return the non-const metadata pointer cast as a specific type
+ template <typename data_t>
+ inline data_t* get_mdata_ptr_as()
+ {
+ return reinterpret_cast<data_t*>(get_mdata_ptr());
+ }
+
+ //! Shortcut to return the const payload pointer cast as a specific type
+ template <typename data_t>
+ inline const data_t* get_payload_const_ptr_as() const
+ {
+ return reinterpret_cast<const data_t*>(get_payload_const_ptr());
+ }
+
+ //! Shortcut to return the non-const payload pointer cast as a specific type
+ template <typename data_t>
+ inline data_t* get_payload_ptr_as()
+ {
+ return reinterpret_cast<data_t*>(get_payload_ptr());
+ }
+
+ //! Return a function to convert a word of type data_t to host order
+ template <typename data_t>
+ const std::function<data_t(data_t)> conv_to_host() const
+ {
+ return (get_byte_order() == uhd::ENDIANNESS_BIG) ? uhd::ntohx<data_t>
+ : uhd::wtohx<data_t>;
+ }
+
+ //! Return a function to convert a word of type data_t from host order
+ template <typename data_t>
+ const std::function<data_t(data_t)> conv_from_host() const
+ {
+ return (get_byte_order() == uhd::ENDIANNESS_BIG) ? uhd::htonx<data_t>
+ : uhd::htowx<data_t>;
+ }
+};
+
+//----------------------------------------------------
+// Container for specific CHDR Packets
+//----------------------------------------------------
+
+//! A container class that wraps a generic buffer that contains a CHDR packet. The
+// container provides a high level API to read and write the header, metadata and payload
+// of the packet. The payload can be accessed as a specific type that will be serialized
+// and deserialized appropriately.
+//
+template <typename payload_t>
+class chdr_packet_specific
+{
+public:
+ //! A unique pointer to a const chdr_packet. Useful as a read-only interface.
+ typedef std::unique_ptr<const chdr_packet_specific<payload_t>> cuptr;
+ //! A unique pointer to a non-const chdr_packet. Useful as a read-write interface.
+ typedef std::unique_ptr<chdr_packet_specific<payload_t>> uptr;
+
+ chdr_packet_specific(chdr_packet::uptr chdr_pkt) : _chdr_pkt(std::move(chdr_pkt)) {}
+ ~chdr_packet_specific() = default;
+
+ //! Updates the underlying storage of this packet. This is a const method and is
+ // only useful for read-only access.
+ inline void refresh(const void* pkt_buff) const
+ {
+ _chdr_pkt->refresh(pkt_buff);
+ }
+
+ //! Updates the underlying storage of this packet, and populates it with the specified
+ // arguments. This is a non-const method and is useful for read-write access.
+ inline void refresh(void* pkt_buff, chdr_header& header, const payload_t& payload)
+ {
+ payload.populate_header(header);
+ _chdr_pkt->refresh(pkt_buff, header);
+ size_t bytes_copied = payload.serialize(_chdr_pkt->get_payload_ptr_as<uint64_t>(),
+ _chdr_pkt->get_mtu_bytes(),
+ _chdr_pkt->conv_from_host<uint64_t>());
+ _chdr_pkt->update_payload_size(bytes_copied);
+ header = _chdr_pkt->get_chdr_header();
+ }
+
+ //! Returns a class that represents the contents of the CHDR header
+ inline chdr_header get_chdr_header() const
+ {
+ return std::move(_chdr_pkt->get_chdr_header());
+ }
+
+ //! Returns a class that represents the contents of the CHDR payload
+ inline payload_t get_payload() const
+ {
+ payload_t payload;
+ payload.deserialize(_chdr_pkt->get_payload_const_ptr_as<uint64_t>(),
+ _chdr_pkt->get_payload_size() / sizeof(uint64_t),
+ _chdr_pkt->conv_to_host<uint64_t>());
+ return std::move(payload);
+ }
+
+ //! Fills the CHDR payload into the specified parameter
+ inline void fill_payload(payload_t& payload) const
+ {
+ payload.deserialize(_chdr_pkt->get_payload_const_ptr_as<uint64_t>(),
+ _chdr_pkt->get_payload_size() / sizeof(uint64_t),
+ _chdr_pkt->conv_to_host<uint64_t>());
+ }
+
+private:
+ chdr_packet::uptr _chdr_pkt;
+};
+
+//----------------------------------------------------
+// Specific CHDR packet types
+//----------------------------------------------------
+
+//! CHDR control packet
+typedef chdr_packet_specific<ctrl_payload> chdr_ctrl_packet;
+
+//! CHDR stream status packet
+typedef chdr_packet_specific<strs_payload> chdr_strs_packet;
+
+//! CHDR stream command packet
+typedef chdr_packet_specific<strc_payload> chdr_strc_packet;
+
+//! CHDR management packet
+typedef chdr_packet_specific<mgmt_payload> chdr_mgmt_packet;
+
+//----------------------------------------------------
+// CHDR packet factory
+//----------------------------------------------------
+
+//! A copyable and movable factory class that is capable of generating generic and
+//! specific CHDR packet containers.
+//
+class chdr_packet_factory
+{
+public:
+ //! A parametrized ctor that takes in all the info required to generate a CHDR packet
+ chdr_packet_factory(chdr_w_t chdr_w, endianness_t endianness);
+ chdr_packet_factory() = delete;
+ chdr_packet_factory(const chdr_packet_factory& rhs) = default;
+ chdr_packet_factory(chdr_packet_factory&& rhs) = default;
+
+ //! Makes a generic CHDR packet and transfers ownership to the client
+ chdr_packet::uptr make_generic(
+ size_t mtu_bytes = std::numeric_limits<size_t>::max()) const;
+
+ //! Makes a CHDR control packet and transfers ownership to the client
+ chdr_ctrl_packet::uptr make_ctrl(
+ size_t mtu_bytes = std::numeric_limits<size_t>::max()) const;
+
+ //! Makes a CHDR stream status packet and transfers ownership to the client
+ chdr_strs_packet::uptr make_strs(
+ size_t mtu_bytes = std::numeric_limits<size_t>::max()) const;
+
+ //! Makes a CHDR stream cmd packet and transfers ownership to the client
+ chdr_strc_packet::uptr make_strc(
+ size_t mtu_bytes = std::numeric_limits<size_t>::max()) const;
+
+ //! Makes a CHDR management packet and transfers ownership to the client
+ chdr_mgmt_packet::uptr make_mgmt(
+ size_t mtu_bytes = std::numeric_limits<size_t>::max()) const;
+
+private:
+ const chdr_w_t _chdr_w;
+ const endianness_t _endianness;
+};
+
+}}} // namespace uhd::rfnoc::chdr
+
+#endif /* INCLUDED_RFNOC_CHDR_PACKET_HPP */
diff --git a/host/lib/include/uhdlib/rfnoc/chdr/chdr_types.hpp b/host/lib/include/uhdlib/rfnoc/chdr/chdr_types.hpp
new file mode 100644
index 000000000..8363e1bcf
--- /dev/null
+++ b/host/lib/include/uhdlib/rfnoc/chdr/chdr_types.hpp
@@ -0,0 +1,837 @@
+//
+// Copyright 2019 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#ifndef INCLUDED_RFNOC_CHDR_TYPES_HPP
+#define INCLUDED_RFNOC_CHDR_TYPES_HPP
+
+#include <uhd/types/endianness.hpp>
+#include <uhd/utils/byteswap.hpp>
+#include <boost/optional.hpp>
+#include <list>
+#include <memory>
+#include <vector>
+
+namespace uhd { namespace rfnoc {
+
+enum chdr_w_t {
+ //! CHDR_W = 64 bits
+ CHDR_W_64 = 0,
+ //! CHDR_W = 128 bits
+ CHDR_W_128 = 1,
+ //! CHDR_W = 256 bits
+ CHDR_W_256 = 2,
+ //! CHDR_W = 512 bits
+ CHDR_W_512 = 3
+};
+
+using device_id_t = uint16_t;
+using sep_inst_t = uint16_t;
+using sep_addr_t = std::pair<device_id_t, sep_inst_t>;
+using sep_id_t = uint16_t;
+
+constexpr size_t chdr_w_to_bits(chdr_w_t chdr_w)
+{
+ switch (chdr_w) {
+ case CHDR_W_64:
+ return 64;
+ case CHDR_W_128:
+ return 128;
+ case CHDR_W_256:
+ return 256;
+ case CHDR_W_512:
+ return 512;
+ default:
+ return 0;
+ }
+}
+
+namespace chdr {
+
+enum packet_type_t {
+ PKT_TYPE_MGMT = 0x0, //! Management packet
+ PKT_TYPE_STRS = 0x1, //! Stream status
+ PKT_TYPE_STRC = 0x2, //! Stream Command
+ PKT_TYPE_CTRL = 0x4, //! Control Transaction
+ PKT_TYPE_DATA_NO_TS = 0x6, //! Data Packet without TimeStamp
+ PKT_TYPE_DATA_WITH_TS = 0x7, //! Data Packet with TimeStamp
+};
+
+//----------------------------------------------------
+// CHDR Header
+//----------------------------------------------------
+
+class chdr_header
+{
+public: // Functions
+ chdr_header() = default;
+ chdr_header(const chdr_header& rhs) = default;
+ chdr_header(chdr_header&& rhs) = default;
+
+ //! Unpack the header from a uint64_t
+ chdr_header(uint64_t flat_hdr) : _flat_hdr(flat_hdr) {}
+
+ //! Get the virtual channel field (6 bits)
+ inline uint8_t get_vc() const
+ {
+ return get_field<uint8_t>(_flat_hdr, VC_OFFSET, VC_WIDTH);
+ }
+
+ //! Set the virtual channel field (6 bits)
+ inline void set_vc(uint8_t vc)
+ {
+ _flat_hdr = set_field(_flat_hdr, vc, VC_OFFSET, VC_WIDTH);
+ }
+
+ //! Get the end-of-burst flag (1 bit)
+ inline bool get_eob() const
+ {
+ return get_field<bool>(_flat_hdr, EOB_OFFSET, EOB_WIDTH);
+ }
+
+ //! Set the end-of-burst flag (1 bit)
+ inline void set_eob(bool eob)
+ {
+ _flat_hdr = set_field(_flat_hdr, eob, EOB_OFFSET, EOB_WIDTH);
+ }
+
+ //! Get the end-of-vector flag (1 bit)
+ inline bool get_eov() const
+ {
+ return get_field<bool>(_flat_hdr, EOV_OFFSET, EOV_WIDTH);
+ }
+
+ //! Set the end-of-vector flag (1 bit)
+ inline void set_eov(bool eov)
+ {
+ _flat_hdr = set_field(_flat_hdr, eov, EOV_OFFSET, EOV_WIDTH);
+ }
+
+ //! Get the packet type field (3 bits)
+ inline packet_type_t get_pkt_type() const
+ {
+ return get_field<packet_type_t>(_flat_hdr, PKT_TYPE_OFFSET, PKT_TYPE_WIDTH);
+ }
+
+ //! Set the packet type field (3 bits)
+ inline void set_pkt_type(packet_type_t pkt_type)
+ {
+ _flat_hdr = set_field(_flat_hdr, pkt_type, PKT_TYPE_OFFSET, PKT_TYPE_WIDTH);
+ }
+
+ //! Get number of metadata words field (5 bits)
+ inline uint8_t get_num_mdata() const
+ {
+ return get_field<uint8_t>(_flat_hdr, NUM_MDATA_OFFSET, NUM_MDATA_WIDTH);
+ }
+
+ //! Set number of metadata words field (5 bits)
+ inline void set_num_mdata(uint8_t num_mdata)
+ {
+ _flat_hdr = set_field(_flat_hdr, num_mdata, NUM_MDATA_OFFSET, NUM_MDATA_WIDTH);
+ }
+
+ //! Get the sequence number field (16 bits)
+ inline uint16_t get_seq_num() const
+ {
+ return get_field<uint16_t>(_flat_hdr, SEQ_NUM_OFFSET, SEQ_NUM_WIDTH);
+ }
+
+ //! Set the sequence number field (16 bits)
+ inline void set_seq_num(uint16_t seq_num)
+ {
+ _flat_hdr = set_field(_flat_hdr, seq_num, SEQ_NUM_OFFSET, SEQ_NUM_WIDTH);
+ }
+
+ //! Get the packet length field (16 bits)
+ inline uint16_t get_length() const
+ {
+ return get_field<uint16_t>(_flat_hdr, LENGTH_OFFSET, LENGTH_WIDTH);
+ }
+
+ //! Set the packet length field (16 bits)
+ inline void set_length(uint16_t length)
+ {
+ _flat_hdr = set_field(_flat_hdr, length, LENGTH_OFFSET, LENGTH_WIDTH);
+ }
+
+ //! Get the destination EPID field (16 bits)
+ inline uint16_t get_dst_epid() const
+ {
+ return get_field<uint16_t>(_flat_hdr, DST_EPID_OFFSET, DST_EPID_WIDTH);
+ }
+
+ //! Set the destination EPID field (16 bits)
+ inline void set_dst_epid(uint16_t dst_epid)
+ {
+ _flat_hdr = set_field(_flat_hdr, dst_epid, DST_EPID_OFFSET, DST_EPID_WIDTH);
+ }
+
+ //! Pack the header into a uint64_t
+ inline uint64_t pack() const
+ {
+ return _flat_hdr;
+ }
+
+ //! Pack the header into a uint64_t as an implicit cast
+ inline operator uint64_t() const
+ {
+ return pack();
+ }
+
+ //! Comparison operator (==)
+ inline bool operator==(const chdr_header& rhs) const
+ {
+ return _flat_hdr == rhs._flat_hdr;
+ }
+
+ //! Comparison operator (!=)
+ inline bool operator!=(const chdr_header& rhs) const
+ {
+ return _flat_hdr != rhs._flat_hdr;
+ }
+
+ //! Assignment operator (=) from a chdr_header
+ inline const chdr_header& operator=(const chdr_header& rhs)
+ {
+ _flat_hdr = rhs._flat_hdr;
+ return *this;
+ }
+
+ //! Assignment operator (=) from a uint64_t
+ inline const chdr_header& operator=(const uint64_t& rhs)
+ {
+ _flat_hdr = rhs;
+ return *this;
+ }
+
+private:
+ // The flattened representation of the header stored in host order
+ uint64_t _flat_hdr = 0;
+
+ static constexpr size_t VC_WIDTH = 6;
+ static constexpr size_t EOB_WIDTH = 1;
+ static constexpr size_t EOV_WIDTH = 1;
+ static constexpr size_t PKT_TYPE_WIDTH = 3;
+ static constexpr size_t NUM_MDATA_WIDTH = 5;
+ static constexpr size_t SEQ_NUM_WIDTH = 16;
+ static constexpr size_t LENGTH_WIDTH = 16;
+ static constexpr size_t DST_EPID_WIDTH = 16;
+
+ static constexpr size_t VC_OFFSET = 58;
+ static constexpr size_t EOB_OFFSET = 57;
+ static constexpr size_t EOV_OFFSET = 56;
+ static constexpr size_t PKT_TYPE_OFFSET = 53;
+ static constexpr size_t NUM_MDATA_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 inline constexpr uint64_t mask(size_t width)
+ {
+ return ((uint64_t(1) << width) - 1);
+ }
+
+ template <typename field_t>
+ static inline constexpr field_t get_field(
+ uint64_t flat_hdr, size_t offset, size_t width)
+ {
+ return static_cast<field_t>((flat_hdr >> offset) & mask(width));
+ }
+
+ template <typename field_t>
+ static inline constexpr uint64_t set_field(
+ const uint64_t old_val, field_t field, size_t offset, size_t width)
+ {
+ return (old_val & ~(mask(width) << offset))
+ | ((static_cast<uint64_t>(field) & mask(width)) << offset);
+ }
+};
+
+
+//----------------------------------------------------
+// CHDR Control Packet Payload
+//----------------------------------------------------
+
+enum ctrl_status_t {
+ CMD_OKAY = 0x0, //! Transaction successful
+ CMD_CMDERR = 0x1, //! Slave asserted a command error
+ CMD_TSERR = 0x2, //! Slave asserted a time stamp error
+ CMD_WARNING = 0x3, //! Slave asserted non-critical error
+};
+
+enum ctrl_opcode_t {
+ OP_SLEEP = 0x0,
+ OP_WRITE = 0x1,
+ OP_READ = 0x2,
+ OP_READ_WRITE = 0x3,
+ OP_BLOCK_WRITE = 0x4,
+ OP_BLOCK_READ = 0x5,
+ OP_POLL = 0x6,
+ OP_USER1 = 0xA,
+ OP_USER2 = 0xB,
+ OP_USER3 = 0xC,
+ OP_USER4 = 0xD,
+ OP_USER5 = 0xE,
+ OP_USER6 = 0xF,
+};
+
+class ctrl_payload
+{
+public: // Members
+ //! Destination port for transaction (10 bits)
+ uint16_t dst_port = 0;
+ //! Source port for transaction (10 bits)
+ uint16_t src_port = 0;
+ //! Sequence number (6 bits)
+ uint8_t seq_num = 0;
+ //! Has Time Flag (1 bit) and timestamp (64 bits)
+ boost::optional<uint64_t> timestamp = boost::none;
+ //! Is Acknowledgment Flag (1 bit)
+ bool is_ack = false;
+ //! Source endpoint ID of transaction (16 bits)
+ uint16_t src_epid = 0;
+ //! Address for transaction (20 bits)
+ uint32_t address = 0;
+ //! Data for transaction (vector of 32 bits)
+ std::vector<uint32_t> data_vtr = {0};
+ //! Byte-enable mask for transaction (4 bits)
+ uint8_t byte_enable = 0xF;
+ //! Operation code (4 bits)
+ ctrl_opcode_t op_code = OP_SLEEP;
+ //! Transaction status (4 bits)
+ ctrl_status_t status = CMD_OKAY;
+
+public: // Functions
+ ctrl_payload() = default;
+ ctrl_payload(const ctrl_payload& rhs) = default;
+ ctrl_payload(ctrl_payload&& rhs) = default;
+
+ //! Populate the header for this type of packet
+ void populate_header(chdr_header& header) const;
+
+ //! Serialize the payload to a uint64_t buffer
+ size_t serialize(uint64_t* buff,
+ size_t max_size_bytes,
+ const std::function<uint64_t(uint64_t)>& conv_byte_order) const;
+
+ //! Serialize the payload to a uint64_t buffer (no conversion function)
+ template <endianness_t endianness>
+ size_t serialize(uint64_t* buff, size_t max_size_bytes) const
+ {
+ auto conv_byte_order = [](uint64_t x) -> uint64_t {
+ return (endianness == uhd::ENDIANNESS_BIG) ? uhd::htonx<uint64_t>(x)
+ : uhd::htowx<uint64_t>(x);
+ };
+ return serialize(buff, max_size_bytes, conv_byte_order);
+ }
+
+ //! Deserialize the payload from a uint64_t buffer
+ void deserialize(const uint64_t* buff,
+ size_t num_elems,
+ const std::function<uint64_t(uint64_t)>& conv_byte_order);
+
+ //! Deserialize the payload from a uint64_t buffer (no conversion function)
+ template <endianness_t endianness>
+ void deserialize(const uint64_t* buff, size_t num_elems)
+ {
+ auto conv_byte_order = [](uint64_t x) -> uint64_t {
+ return (endianness == uhd::ENDIANNESS_BIG) ? uhd::ntohx<uint64_t>(x)
+ : uhd::wtohx<uint64_t>(x);
+ };
+ deserialize(buff, num_elems, conv_byte_order);
+ }
+
+ //! Comparison operator (==)
+ bool operator==(const ctrl_payload& rhs) const;
+
+ //! Comparison operator (!=)
+ inline bool operator!=(const ctrl_payload& rhs) const
+ {
+ return !(*this == rhs);
+ }
+
+ //! Return a string representation of this object
+ const std::string to_string() const;
+
+private:
+ static constexpr size_t DST_PORT_WIDTH = 10;
+ static constexpr size_t SRC_PORT_WIDTH = 10;
+ static constexpr size_t NUM_DATA_WIDTH = 4;
+ static constexpr size_t SEQ_NUM_WIDTH = 6;
+ static constexpr size_t HAS_TIME_WIDTH = 1;
+ static constexpr size_t IS_ACK_WIDTH = 1;
+ static constexpr size_t SRC_EPID_WIDTH = 16;
+ static constexpr size_t ADDRESS_WIDTH = 20;
+ static constexpr size_t BYTE_ENABLE_WIDTH = 4;
+ static constexpr size_t OPCODE_WIDTH = 4;
+ static constexpr size_t STATUS_WIDTH = 2;
+
+ // Offsets assume 64-bit alignment
+ static constexpr size_t DST_PORT_OFFSET = 0;
+ static constexpr size_t SRC_PORT_OFFSET = 10;
+ static constexpr size_t NUM_DATA_OFFSET = 20;
+ static constexpr size_t SEQ_NUM_OFFSET = 24;
+ static constexpr size_t HAS_TIME_OFFSET = 30;
+ static constexpr size_t IS_ACK_OFFSET = 31;
+ static constexpr size_t SRC_EPID_OFFSET = 32;
+ static constexpr size_t ADDRESS_OFFSET = 0;
+ static constexpr size_t BYTE_ENABLE_OFFSET = 20;
+ static constexpr size_t OPCODE_OFFSET = 24;
+ static constexpr size_t STATUS_OFFSET = 30;
+ static constexpr size_t LO_DATA_OFFSET = 0;
+ static constexpr size_t HI_DATA_OFFSET = 32;
+};
+
+//----------------------------------------------------
+// CHDR Stream Status Packet Payload
+//----------------------------------------------------
+
+enum strs_status_t {
+ STRS_OKAY = 0x0, //! No error
+ STRS_CMDERR = 0x1, //! A stream command signalled an error
+ STRS_SEQERR = 0x2, //! Packet out of sequence (sequence error)
+ STRS_DATAERR = 0x3, //! Data integrity check failed
+ STRS_RTERR = 0x4, //! Unexpected destination (routing error)
+};
+
+class strs_payload
+{
+public: // Members
+ //! The source EPID for the stream (16 bits)
+ uint16_t src_epid = 0;
+ //! The status of the stream (4 bits)
+ strs_status_t status = STRS_OKAY;
+ //! Buffer capacity in bytes (40 bits)
+ uint64_t capacity_bytes = 0;
+ //! Buffer capacity in packets (24 bits)
+ uint32_t capacity_pkts = 0;
+ //! Transfer count in bytes (64 bits)
+ uint64_t xfer_count_bytes = 0;
+ //! Transfer count in packets (40 bits)
+ uint64_t xfer_count_pkts = 0;
+ //! Buffer info (16 bits)
+ uint16_t buff_info = 0;
+ //! Extended status info (48 bits)
+ uint64_t status_info = 0;
+
+public: // Functions
+ strs_payload() = default;
+ strs_payload(const strs_payload& rhs) = default;
+ strs_payload(strs_payload&& rhs) = default;
+
+ //! Populate the header for this type of packet
+ void populate_header(chdr_header& header) const;
+
+ //! Serialize the payload to a uint64_t buffer
+ size_t serialize(uint64_t* buff,
+ size_t max_size_bytes,
+ const std::function<uint64_t(uint64_t)>& conv_byte_order) const;
+
+ //! Serialize the payload to a uint64_t buffer (no conversion function)
+ template <endianness_t endianness>
+ size_t serialize(uint64_t* buff, size_t max_size_bytes) const
+ {
+ auto conv_byte_order = [](uint64_t x) -> uint64_t {
+ return (endianness == uhd::ENDIANNESS_BIG) ? uhd::htonx<uint64_t>(x)
+ : uhd::htowx<uint64_t>(x);
+ };
+ return serialize(buff, max_size_bytes, conv_byte_order);
+ }
+
+ //! Deserialize the payload from a uint64_t buffer
+ void deserialize(const uint64_t* buff,
+ size_t num_elems,
+ const std::function<uint64_t(uint64_t)>& conv_byte_order);
+
+ //! Deserialize the payload from a uint64_t buffer (no conversion function)
+ template <endianness_t endianness>
+ void deserialize(const uint64_t* buff, size_t num_elems)
+ {
+ auto conv_byte_order = [](uint64_t x) -> uint64_t {
+ return (endianness == uhd::ENDIANNESS_BIG) ? uhd::ntohx<uint64_t>(x)
+ : uhd::wtohx<uint64_t>(x);
+ };
+ deserialize(buff, num_elems, conv_byte_order);
+ }
+
+ //! Comparison operator (==)
+ bool operator==(const strs_payload& rhs) const;
+
+ //! Comparison operator (!=)
+ inline bool operator!=(const strs_payload& rhs) const
+ {
+ return !(*this == rhs);
+ }
+
+ //! Return a string representation of this object
+ const std::string to_string() const;
+
+private:
+ static constexpr size_t SRC_EPID_WIDTH = 16;
+ static constexpr size_t STATUS_WIDTH = 4;
+ static constexpr size_t CAPACITY_BYTES_WIDTH = 40;
+ static constexpr size_t CAPACITY_PKTS_WIDTH = 24;
+ static constexpr size_t XFER_COUNT_PKTS_WIDTH = 40;
+ static constexpr size_t BUFF_INFO_WIDTH = 16;
+ static constexpr size_t STATUS_INFO_WIDTH = 48;
+
+ // Offsets assume 64-bit alignment
+ static constexpr size_t SRC_EPID_OFFSET = 0;
+ static constexpr size_t STATUS_OFFSET = 16;
+ static constexpr size_t CAPACITY_BYTES_OFFSET = 24;
+ static constexpr size_t CAPACITY_PKTS_OFFSET = 0;
+ static constexpr size_t XFER_COUNT_PKTS_OFFSET = 24;
+ static constexpr size_t BUFF_INFO_OFFSET = 0;
+ static constexpr size_t STATUS_INFO_OFFSET = 16;
+};
+
+//----------------------------------------------------
+// CHDR Stream Command Packet Payload
+//----------------------------------------------------
+
+enum strc_op_code_t {
+ STRC_INIT = 0x0, //! Initialize stream
+ STRC_PING = 0x1, //! Trigger a stream status response
+ STRC_RESYNC = 0x2, //! Re-synchronize flow control
+};
+
+class strc_payload
+{
+public: // Members
+ //! The source EPID for the stream (16 bits)
+ uint16_t src_epid = 0;
+ //! Operation code for the command (4 bits)
+ strc_op_code_t op_code = STRC_INIT;
+ //! Data associated with the operation (4 bits)
+ uint8_t op_data = 0;
+ //! Number of packets to use for operation (40 bits)
+ uint64_t num_pkts = 0;
+ //! Number of bytes to use for operation (64 bits)
+ uint64_t num_bytes = 0;
+
+public: // Functions
+ strc_payload() = default;
+ strc_payload(const strc_payload& rhs) = default;
+ strc_payload(strc_payload&& rhs) = default;
+
+ //! Populate the header for this type of packet
+ void populate_header(chdr_header& header) const;
+
+ //! Serialize the payload to a uint64_t buffer
+ size_t serialize(uint64_t* buff,
+ size_t max_size_bytes,
+ const std::function<uint64_t(uint64_t)>& conv_byte_order) const;
+
+ //! Serialize the payload to a uint64_t buffer (no conversion function)
+ template <endianness_t endianness>
+ size_t serialize(uint64_t* buff, size_t max_size_bytes) const
+ {
+ auto conv_byte_order = [](uint64_t x) -> uint64_t {
+ return (endianness == uhd::ENDIANNESS_BIG) ? uhd::htonx<uint64_t>(x)
+ : uhd::htowx<uint64_t>(x);
+ };
+ return serialize(buff, max_size_bytes, conv_byte_order);
+ }
+
+ //! Deserialize the payload from a uint64_t buffer
+ void deserialize(const uint64_t* buff,
+ size_t num_elems,
+ const std::function<uint64_t(uint64_t)>& conv_byte_order);
+
+ //! Deserialize the payload from a uint64_t buffer (no conversion function)
+ template <endianness_t endianness>
+ void deserialize(const uint64_t* buff, size_t num_elems)
+ {
+ auto conv_byte_order = [](uint64_t x) -> uint64_t {
+ return (endianness == uhd::ENDIANNESS_BIG) ? uhd::ntohx<uint64_t>(x)
+ : uhd::wtohx<uint64_t>(x);
+ };
+ deserialize(buff, num_elems, conv_byte_order);
+ }
+
+ //! Comparison operator (==)
+ bool operator==(const strc_payload& rhs) const;
+
+ //! Comparison operator (!=)
+ inline bool operator!=(const strc_payload& rhs) const
+ {
+ return !(*this == rhs);
+ }
+
+ //! Return a string representation of this object
+ const std::string to_string() const;
+
+private:
+ static constexpr size_t SRC_EPID_WIDTH = 16;
+ static constexpr size_t OP_CODE_WIDTH = 4;
+ static constexpr size_t OP_DATA_WIDTH = 4;
+ static constexpr size_t NUM_PKTS_WIDTH = 40;
+
+ // Offsets assume 64-bit alignment
+ static constexpr size_t SRC_EPID_OFFSET = 0;
+ static constexpr size_t OP_CODE_OFFSET = 16;
+ static constexpr size_t OP_DATA_OFFSET = 20;
+ static constexpr size_t NUM_PKTS_OFFSET = 24;
+};
+
+//----------------------------------------------------
+// CHDR Management Packet Payload
+//----------------------------------------------------
+
+//! A class that represents a single management operation
+// An operation consists of an operation code and some
+// payload associated with that operation.
+class mgmt_op_t
+{
+public:
+ // Operation code
+ enum op_code_t {
+ //! Do nothing
+ MGMT_OP_NOP = 0,
+ //! Advertise this operation to the outside logic
+ MGMT_OP_ADVERTISE = 1,
+ //! Select the next destination for routing
+ MGMT_OP_SEL_DEST = 2,
+ //! Return the management packet back to its source
+ MGMT_OP_RETURN = 3,
+ //! Request information about the current node
+ MGMT_OP_INFO_REQ = 4,
+ //! A response to an information request
+ MGMT_OP_INFO_RESP = 5,
+ //! Perform a configuration write on the node
+ MGMT_OP_CFG_WR_REQ = 6,
+ //! Perform a configuration read on the node
+ MGMT_OP_CFG_RD_REQ = 7,
+ //! A response to a configuration read
+ MGMT_OP_CFG_RD_RESP = 8
+ };
+
+ //! The payload for an operation is 48 bits wide.
+ using payload_t = uint64_t;
+
+ //! An interpretation class for the payload for MGMT_OP_SEL_DEST
+ struct sel_dest_payload
+ {
+ const uint16_t dest;
+
+ sel_dest_payload(uint16_t dest_) : dest(dest_) {}
+ sel_dest_payload(payload_t payload_) : dest(static_cast<uint16_t>(payload_)) {}
+ operator payload_t() const
+ {
+ return static_cast<payload_t>(dest);
+ }
+ };
+
+ //! An interpretation class for the payload for MGMT_OP_CFG_WR_REQ,
+ //! MGMT_OP_CFG_RD_REQ and MGMT_OP_CFG_RD_RESP
+ struct cfg_payload
+ {
+ const uint16_t addr;
+ const uint32_t data;
+
+ cfg_payload(uint16_t addr_, uint32_t data_ = 0) : addr(addr_), data(data_) {}
+ cfg_payload(payload_t payload_)
+ : addr(static_cast<uint16_t>(payload_ >> 0))
+ , data(static_cast<uint32_t>(payload_ >> 16))
+ {
+ }
+ operator payload_t() const
+ {
+ return ((static_cast<payload_t>(data) << 16) | static_cast<payload_t>(addr));
+ }
+ };
+
+ //! An interpretation class for the payload for MGMT_OP_INFO_RESP
+ struct node_info_payload
+ {
+ const uint16_t device_id;
+ const uint8_t node_type;
+ const uint16_t node_inst;
+ const uint32_t ext_info;
+
+ node_info_payload(uint16_t device_id_,
+ uint8_t node_type_,
+ uint16_t node_inst_,
+ uint32_t ext_info_)
+ : device_id(device_id_)
+ , node_type(node_type_)
+ , node_inst(node_inst_)
+ , ext_info(ext_info_)
+ {
+ }
+ node_info_payload(payload_t payload_)
+ : device_id(static_cast<uint16_t>(payload_ >> 0))
+ , node_type(static_cast<uint8_t>((payload_ >> 16) & 0xF))
+ , node_inst(static_cast<uint16_t>((payload_ >> 20) & 0x3FF))
+ , ext_info(static_cast<uint32_t>((payload_ >> 30) & 0x3FFFF))
+ {
+ }
+ operator payload_t() const
+ {
+ return ((static_cast<payload_t>(device_id) << 0)
+ | (static_cast<payload_t>(node_type & 0xF) << 16)
+ | (static_cast<payload_t>(node_inst & 0x3FF) << 20)
+ | (static_cast<payload_t>(ext_info & 0x3FFFF) << 30));
+ }
+ };
+
+ mgmt_op_t(const op_code_t op_code, const payload_t op_payload = 0)
+ : _op_code(op_code), _op_payload(op_payload)
+ {
+ }
+ mgmt_op_t(const mgmt_op_t& rhs) = default;
+
+ //! Get the op-code for this transaction
+ inline op_code_t get_op_code() const
+ {
+ return _op_code;
+ }
+
+ //! Get the payload for this transaction
+ inline uint64_t get_op_payload() const
+ {
+ return _op_payload;
+ }
+
+private:
+ const op_code_t _op_code;
+ const payload_t _op_payload;
+};
+
+//! A class that represents a single management hop
+// A hop is a collection for management transactions for
+// a single node.
+class mgmt_hop_t
+{
+public:
+ mgmt_hop_t() = default;
+ mgmt_hop_t(const mgmt_hop_t& rhs) = default;
+
+ //! Add a management operation to this hop.
+ // Operations are added to the hop in FIFO order and executed in FIFO order.
+ inline void add_op(const mgmt_op_t& op)
+ {
+ _ops.push_back(op);
+ }
+
+ //! Get the number of management operations in this hop
+ inline size_t get_num_ops() const
+ {
+ return _ops.size();
+ }
+
+ //! Get the n'th operation in the hop
+ inline const mgmt_op_t& get_op(size_t i) const
+ {
+ return _ops.at(i);
+ }
+
+ //! Serialize the payload to a uint64_t buffer
+ size_t serialize(std::vector<uint64_t>& target,
+ const std::function<uint64_t(uint64_t)>& conv_byte_order) const;
+
+ //! Deserialize the payload from a uint64_t buffer
+ void deserialize(std::list<uint64_t>& src,
+ const std::function<uint64_t(uint64_t)>& conv_byte_order);
+
+private:
+ std::vector<mgmt_op_t> _ops;
+};
+
+//! A class that represents a complete multi-hop management transaction
+// A transaction is a collection of hops, where each hop is a collection
+// of management transactions.
+class mgmt_payload
+{
+public:
+ mgmt_payload() = default;
+ mgmt_payload(const mgmt_payload& rhs) = default;
+ mgmt_payload(mgmt_payload&& rhs) = default;
+
+ inline void set_header(sep_id_t src_epid, uint16_t protover, chdr_w_t chdr_w)
+ {
+ _src_epid = src_epid;
+ _chdr_w = chdr_w;
+ _protover = protover;
+ }
+
+ //! Add a management hop to this transaction
+ // Hops are added to the hop in FIFO order and executed in FIFO order.
+ inline void add_hop(const mgmt_hop_t& hop)
+ {
+ _hops.push_back(hop);
+ }
+
+ //! Get the number of management hops in this hop
+ inline size_t get_num_hops() const
+ {
+ return _hops.size();
+ }
+
+ //! Get the n'th hop in the transaction
+ inline const mgmt_hop_t& get_hop(size_t i) const
+ {
+ return _hops.at(i);
+ }
+
+ inline size_t get_size_bytes() const
+ {
+ size_t num_lines = 1; /* header */
+ for (const auto& hop : _hops) {
+ num_lines += hop.get_num_ops();
+ }
+ return num_lines * (chdr_w_to_bits(_chdr_w) / 8);
+ }
+
+ //! Populate the header for this type of packet
+ void populate_header(chdr_header& header) const;
+
+ //! Serialize the payload to a uint64_t buffer
+ size_t serialize(uint64_t* buff,
+ size_t max_size_bytes,
+ const std::function<uint64_t(uint64_t)>& conv_byte_order) const;
+
+ //! Serialize the payload to a uint64_t buffer (no conversion function)
+ template <endianness_t endianness>
+ size_t serialize(uint64_t* buff, size_t max_size_bytes) const
+ {
+ auto conv_byte_order = [](uint64_t x) -> uint64_t {
+ return (endianness == uhd::ENDIANNESS_BIG) ? uhd::htonx<uint64_t>(x)
+ : uhd::htowx<uint64_t>(x);
+ };
+ return serialize(buff, max_size_bytes, conv_byte_order);
+ }
+
+ //! Deserialize the payload from a uint64_t buffer
+ void deserialize(const uint64_t* buff,
+ size_t num_elems,
+ const std::function<uint64_t(uint64_t)>& conv_byte_order);
+
+ //! Deserialize the payload from a uint64_t buffer (no conversion function)
+ template <endianness_t endianness>
+ void deserialize(const uint64_t* buff, size_t num_elems)
+ {
+ auto conv_byte_order = [](uint64_t x) -> uint64_t {
+ return (endianness == uhd::ENDIANNESS_BIG) ? uhd::ntohx<uint64_t>(x)
+ : uhd::wtohx<uint64_t>(x);
+ };
+ deserialize(buff, num_elems, conv_byte_order);
+ }
+
+ //! Return a string representation of this object
+ const std::string to_string() const;
+
+private:
+ sep_id_t _src_epid = 0;
+ uint16_t _protover = 0;
+ chdr_w_t _chdr_w = CHDR_W_64;
+ std::vector<mgmt_hop_t> _hops;
+};
+
+} // namespace chdr
+}} // namespace uhd::rfnoc
+
+#endif /* INCLUDED_RFNOC_CHDR_TYPES_HPP */
diff --git a/host/lib/rfnoc/CMakeLists.txt b/host/lib/rfnoc/CMakeLists.txt
index 9b14f3456..fe8e8a564 100644
--- a/host/lib/rfnoc/CMakeLists.txt
+++ b/host/lib/rfnoc/CMakeLists.txt
@@ -32,6 +32,7 @@ LIBUHD_APPEND_SOURCES(
${CMAKE_CURRENT_SOURCE_DIR}/sink_node_ctrl.cpp
${CMAKE_CURRENT_SOURCE_DIR}/source_block_ctrl_base.cpp
${CMAKE_CURRENT_SOURCE_DIR}/source_node_ctrl.cpp
+# ${CMAKE_CURRENT_SOURCE_DIR}/mgmt_portal.cpp
${CMAKE_CURRENT_SOURCE_DIR}/stream_sig.cpp
${CMAKE_CURRENT_SOURCE_DIR}/tick_node_ctrl.cpp
${CMAKE_CURRENT_SOURCE_DIR}/tx_stream_terminator.cpp
@@ -48,4 +49,5 @@ LIBUHD_APPEND_SOURCES(
${CMAKE_CURRENT_SOURCE_DIR}/replay_block_ctrl_impl.cpp
)
+INCLUDE_SUBDIRECTORY(chdr)
INCLUDE_SUBDIRECTORY(nocscript)
diff --git a/host/lib/rfnoc/chdr/CMakeLists.txt b/host/lib/rfnoc/chdr/CMakeLists.txt
new file mode 100644
index 000000000..fca46fe4a
--- /dev/null
+++ b/host/lib/rfnoc/chdr/CMakeLists.txt
@@ -0,0 +1,15 @@
+#
+# Copyright 2014-2015,2017 Ettus Research LLC
+# Copyright 2018 Ettus Research, a National Instruments Company
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+
+########################################################################
+# This file included, use CMake directory variables
+########################################################################
+
+LIBUHD_APPEND_SOURCES(
+ ${CMAKE_CURRENT_SOURCE_DIR}/chdr_types.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/chdr_packet.cpp
+)
diff --git a/host/lib/rfnoc/chdr/chdr_packet.cpp b/host/lib/rfnoc/chdr/chdr_packet.cpp
new file mode 100644
index 000000000..b058115ab
--- /dev/null
+++ b/host/lib/rfnoc/chdr/chdr_packet.cpp
@@ -0,0 +1,213 @@
+//
+// Copyright 2019 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#include <uhdlib/rfnoc/chdr/chdr_packet.hpp>
+#include <cassert>
+#include <functional>
+#include <memory>
+
+using namespace uhd;
+using namespace uhd::rfnoc::chdr;
+
+chdr_packet::~chdr_packet() = default;
+
+//------------------------------------------------------------
+// chdr_packet
+//------------------------------------------------------------
+
+template <size_t chdr_w, endianness_t endianness>
+class chdr_packet_impl : public chdr_packet
+{
+public:
+ chdr_packet_impl() = delete;
+ chdr_packet_impl(size_t mtu_bytes) : _mtu_bytes(mtu_bytes) {}
+ ~chdr_packet_impl() = default;
+
+ virtual void refresh(const void* pkt_buff) const
+ {
+ assert(pkt_buff);
+ _pkt_buff = const_cast<uint64_t*>(reinterpret_cast<const uint64_t*>(pkt_buff));
+ _compute_mdata_offset();
+ }
+
+ virtual void refresh(void* pkt_buff, chdr_header& header, uint64_t timestamp = 0)
+ {
+ assert(pkt_buff);
+ _pkt_buff = reinterpret_cast<uint64_t*>(pkt_buff);
+ _pkt_buff[0] = u64_from_host(header);
+ if (_has_timestamp(header)) {
+ _pkt_buff[1] = timestamp;
+ }
+ _compute_mdata_offset();
+ }
+
+ virtual void update_payload_size(size_t payload_size_bytes)
+ {
+ chdr_header header = get_chdr_header();
+ header.set_length(((_mdata_offset + header.get_num_mdata()) * chdr_w_bytes)
+ + payload_size_bytes);
+ _pkt_buff[0] = u64_from_host(header);
+ }
+
+ virtual endianness_t get_byte_order() const
+ {
+ return endianness;
+ }
+
+ virtual size_t get_mtu_bytes() const
+ {
+ return _mtu_bytes;
+ }
+
+ virtual chdr_header get_chdr_header() const
+ {
+ assert(_pkt_buff);
+ return std::move(chdr_header(u64_to_host(_pkt_buff[0])));
+ }
+
+ virtual boost::optional<uint64_t> get_timestamp() const
+ {
+ if (_has_timestamp(get_chdr_header())) {
+ // In a unit64_t buffer, the timestamp is always immediately after the header
+ // regardless of chdr_w.
+ return u64_to_host(_pkt_buff[1]);
+ } else {
+ return boost::none;
+ }
+ }
+
+ virtual size_t get_mdata_size() const
+ {
+ return get_chdr_header().get_num_mdata() * chdr_w_bytes;
+ }
+
+ virtual const void* get_mdata_const_ptr() const
+ {
+ return const_cast<void*>(
+ const_cast<chdr_packet_impl<chdr_w, endianness>*>(this)->get_mdata_ptr());
+ }
+
+ virtual void* get_mdata_ptr()
+ {
+ return reinterpret_cast<void*>(_pkt_buff + (chdr_w_stride * _mdata_offset));
+ }
+
+ virtual size_t get_payload_size() const
+ {
+ return get_chdr_header().get_length() - get_mdata_size()
+ - (chdr_w_bytes * _mdata_offset);
+ }
+
+ virtual const void* get_payload_const_ptr() const
+ {
+ return const_cast<void*>(
+ const_cast<chdr_packet_impl<chdr_w, endianness>*>(this)->get_payload_ptr());
+ }
+
+ virtual void* get_payload_ptr()
+ {
+ return reinterpret_cast<void*>(
+ _pkt_buff
+ + (chdr_w_stride * (_mdata_offset + get_chdr_header().get_num_mdata())));
+ }
+
+private:
+ inline bool _has_timestamp(const chdr_header& header) const
+ {
+ return (header.get_pkt_type() == PKT_TYPE_DATA_WITH_TS);
+ }
+
+ inline void _compute_mdata_offset() const
+ {
+ // The metadata offset depends on the chdr_w and whether we have a timestamp
+ if (chdr_w == 64) {
+ _mdata_offset = _has_timestamp(get_chdr_header()) ? 2 : 1;
+ } else {
+ _mdata_offset = 1;
+ }
+ }
+
+ inline static uint64_t u64_to_host(uint64_t word)
+ {
+ return (endianness == ENDIANNESS_BIG) ? uhd::ntohx<uint64_t>(word)
+ : uhd::wtohx<uint64_t>(word);
+ }
+
+ inline static uint64_t u64_from_host(uint64_t word)
+ {
+ return (endianness == ENDIANNESS_BIG) ? uhd::htonx<uint64_t>(word)
+ : uhd::htowx<uint64_t>(word);
+ }
+
+ static const size_t chdr_w_bytes = (chdr_w / 8);
+ static const size_t chdr_w_stride = (chdr_w / 64);
+
+ // Packet state
+ const size_t _mtu_bytes = 0;
+ mutable uint64_t* _pkt_buff = nullptr;
+ mutable size_t _mdata_offset = 0;
+};
+
+chdr_packet_factory::chdr_packet_factory(chdr_w_t chdr_w, endianness_t endianness)
+ : _chdr_w(chdr_w), _endianness(endianness)
+{
+}
+
+chdr_packet::uptr chdr_packet_factory::make_generic(size_t mtu_bytes) const
+{
+ if (_endianness == ENDIANNESS_BIG) {
+ switch (_chdr_w) {
+ case CHDR_W_512:
+ return std::make_unique<chdr_packet_impl<512, ENDIANNESS_BIG>>(mtu_bytes);
+ case CHDR_W_256:
+ return std::make_unique<chdr_packet_impl<256, ENDIANNESS_BIG>>(mtu_bytes);
+ case CHDR_W_128:
+ return std::make_unique<chdr_packet_impl<128, ENDIANNESS_BIG>>(mtu_bytes);
+ case CHDR_W_64:
+ return std::make_unique<chdr_packet_impl<64, ENDIANNESS_BIG>>(mtu_bytes);
+ default:
+ assert(0);
+ }
+ } else {
+ switch (_chdr_w) {
+ case CHDR_W_512:
+ return std::make_unique<chdr_packet_impl<512, ENDIANNESS_LITTLE>>(
+ mtu_bytes);
+ case CHDR_W_256:
+ return std::make_unique<chdr_packet_impl<256, ENDIANNESS_LITTLE>>(
+ mtu_bytes);
+ case CHDR_W_128:
+ return std::make_unique<chdr_packet_impl<128, ENDIANNESS_LITTLE>>(
+ mtu_bytes);
+ case CHDR_W_64:
+ return std::make_unique<chdr_packet_impl<64, ENDIANNESS_LITTLE>>(
+ mtu_bytes);
+ default:
+ assert(0);
+ }
+ }
+ return chdr_packet::uptr();
+}
+
+chdr_ctrl_packet::uptr chdr_packet_factory::make_ctrl(size_t mtu_bytes) const
+{
+ return std::make_unique<chdr_ctrl_packet>(make_generic(mtu_bytes));
+}
+
+chdr_strs_packet::uptr chdr_packet_factory::make_strs(size_t mtu_bytes) const
+{
+ return std::make_unique<chdr_strs_packet>(make_generic(mtu_bytes));
+}
+
+chdr_strc_packet::uptr chdr_packet_factory::make_strc(size_t mtu_bytes) const
+{
+ return std::make_unique<chdr_strc_packet>(make_generic(mtu_bytes));
+}
+
+chdr_mgmt_packet::uptr chdr_packet_factory::make_mgmt(size_t mtu_bytes) const
+{
+ return std::make_unique<chdr_mgmt_packet>(make_generic(mtu_bytes));
+}
diff --git a/host/lib/rfnoc/chdr/chdr_types.cpp b/host/lib/rfnoc/chdr/chdr_types.cpp
new file mode 100644
index 000000000..8786b8193
--- /dev/null
+++ b/host/lib/rfnoc/chdr/chdr_types.cpp
@@ -0,0 +1,426 @@
+//
+// Copyright 2019 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#include <uhd/exception.hpp>
+#include <uhd/types/endianness.hpp>
+#include <uhdlib/rfnoc/chdr/chdr_types.hpp>
+#include <boost/format.hpp>
+#include <cassert>
+
+using namespace uhd;
+using namespace uhd::rfnoc::chdr;
+
+//----------------------------------------------------
+// Utility Functions
+//----------------------------------------------------
+
+static inline constexpr uint64_t mask_u64(size_t width)
+{
+ return ((uint64_t(1) << width) - 1);
+}
+
+template <typename field_t>
+static inline constexpr field_t get_field_u64(
+ uint64_t flat_hdr, size_t offset, size_t width)
+{
+ return static_cast<field_t>((flat_hdr >> offset) & mask_u64(width));
+}
+
+//----------------------------------------------------
+// CHDR Control Payload
+//----------------------------------------------------
+
+void ctrl_payload::populate_header(chdr_header& header) const
+{
+ header.set_pkt_type(PKT_TYPE_CTRL);
+ header.set_eob(false);
+ header.set_eov(false);
+ header.set_num_mdata(0);
+}
+
+size_t ctrl_payload::serialize(uint64_t* buff,
+ size_t max_size_bytes,
+ const std::function<uint64_t(uint64_t)>& conv_byte_order) const
+{
+ UHD_ASSERT_THROW((data_vtr.size() > 0 && data_vtr.size() < 16));
+ // We assume that buff has room to hold the entire packet
+ size_t ptr = 0;
+
+ // Populate control header
+ buff[ptr++] = conv_byte_order(
+ ((static_cast<uint64_t>(dst_port) & mask_u64(DST_PORT_WIDTH)) << DST_PORT_OFFSET)
+ | ((static_cast<uint64_t>(src_port) & mask_u64(SRC_PORT_WIDTH))
+ << SRC_PORT_OFFSET)
+ | ((static_cast<uint64_t>(data_vtr.size()) & mask_u64(NUM_DATA_WIDTH))
+ << NUM_DATA_OFFSET)
+ | ((static_cast<uint64_t>(seq_num) & mask_u64(SEQ_NUM_WIDTH)) << SEQ_NUM_OFFSET)
+ | ((static_cast<uint64_t>(timestamp ? 1 : 0) & mask_u64(HAS_TIME_WIDTH))
+ << HAS_TIME_OFFSET)
+ | ((static_cast<uint64_t>(is_ack) & mask_u64(IS_ACK_WIDTH)) << IS_ACK_OFFSET)
+ | ((static_cast<uint64_t>(src_epid) & mask_u64(SRC_EPID_WIDTH))
+ << SRC_EPID_OFFSET));
+
+ // Populate optional timestamp
+ if (timestamp.is_initialized()) {
+ buff[ptr++] = conv_byte_order(timestamp.get());
+ }
+
+ // Populate control operation word
+ buff[ptr++] = conv_byte_order(
+ ((static_cast<uint64_t>(address) & mask_u64(ADDRESS_WIDTH)) << ADDRESS_OFFSET)
+ | ((static_cast<uint64_t>(byte_enable) & mask_u64(BYTE_ENABLE_WIDTH))
+ << BYTE_ENABLE_OFFSET)
+ | ((static_cast<uint64_t>(op_code) & mask_u64(OPCODE_WIDTH)) << OPCODE_OFFSET)
+ | ((static_cast<uint64_t>(status) & mask_u64(STATUS_WIDTH)) << STATUS_OFFSET)
+ | (static_cast<uint64_t>(data_vtr[0]) << HI_DATA_OFFSET));
+
+ // Populate the rest of the data
+ for (size_t i = 1; i < data_vtr.size(); i += 2) {
+ const uint32_t hi_data =
+ (((i + 2) >= data_vtr.size()) && (data_vtr.size() % 2 == 0))
+ ? 0
+ : data_vtr[i + 1];
+ buff[ptr++] =
+ conv_byte_order(static_cast<uint64_t>(hi_data) << HI_DATA_OFFSET
+ | static_cast<uint64_t>(data_vtr[i]) << LO_DATA_OFFSET);
+ }
+
+ // FIXME: This UHD_ASSERT_THROW is a bit late because memory has already been
+ // corrupted
+ UHD_ASSERT_THROW(ptr <= max_size_bytes);
+ // Return bytes written
+ return (ptr * sizeof(uint64_t));
+}
+
+void ctrl_payload::deserialize(const uint64_t* buff,
+ size_t max_size_bytes,
+ const std::function<uint64_t(uint64_t)>& conv_byte_order)
+{
+ // We assume that buff has room to hold the entire packet
+ size_t ptr = 0;
+
+ // Read control header
+ uint64_t ctrl_header = conv_byte_order(buff[ptr++]);
+ data_vtr.resize(get_field_u64<size_t>(ctrl_header, NUM_DATA_OFFSET, NUM_DATA_WIDTH));
+ UHD_ASSERT_THROW((data_vtr.size() > 0 && data_vtr.size() < 16));
+ dst_port = get_field_u64<uint16_t>(ctrl_header, DST_PORT_OFFSET, DST_PORT_WIDTH);
+ src_port = get_field_u64<uint16_t>(ctrl_header, SRC_PORT_OFFSET, SRC_PORT_WIDTH);
+ seq_num = get_field_u64<uint8_t>(ctrl_header, SEQ_NUM_OFFSET, SEQ_NUM_WIDTH);
+ is_ack = get_field_u64<bool>(ctrl_header, IS_ACK_OFFSET, IS_ACK_WIDTH);
+ src_epid = get_field_u64<uint16_t>(ctrl_header, SRC_EPID_OFFSET, SRC_EPID_WIDTH);
+
+ // Read optional timestamp
+ if (get_field_u64<bool>(ctrl_header, HAS_TIME_OFFSET, HAS_TIME_WIDTH)) {
+ timestamp = conv_byte_order(buff[ptr++]);
+ } else {
+ timestamp = boost::none;
+ }
+
+ // Read control operation word
+ uint64_t op_word = conv_byte_order(buff[ptr++]);
+ if (data_vtr.size() > 0) {
+ data_vtr[0] = get_field_u64<uint32_t>(op_word, HI_DATA_OFFSET, 32);
+ }
+ address = get_field_u64<uint32_t>(op_word, ADDRESS_OFFSET, ADDRESS_WIDTH);
+ byte_enable = get_field_u64<uint8_t>(op_word, BYTE_ENABLE_OFFSET, BYTE_ENABLE_WIDTH);
+ op_code = get_field_u64<ctrl_opcode_t>(op_word, OPCODE_OFFSET, OPCODE_WIDTH);
+ status = get_field_u64<ctrl_status_t>(op_word, STATUS_OFFSET, STATUS_WIDTH);
+
+ // Read the rest of the data
+ for (size_t i = 1; i < data_vtr.size(); i += 2) {
+ uint64_t data_word = conv_byte_order(buff[ptr++]);
+ if (((i + 2) < data_vtr.size()) || (data_vtr.size() % 2 != 0)) {
+ data_vtr[i + 1] = get_field_u64<uint32_t>(data_word, HI_DATA_OFFSET, 32);
+ }
+ data_vtr[i] = get_field_u64<uint32_t>(data_word, LO_DATA_OFFSET, 32);
+ }
+ UHD_ASSERT_THROW(ptr <= max_size_bytes);
+}
+
+bool ctrl_payload::operator==(const ctrl_payload& rhs) const
+{
+ return (dst_port == rhs.dst_port) && (src_port == rhs.src_port)
+ && (seq_num == rhs.seq_num)
+ && (timestamp.is_initialized() == rhs.timestamp.is_initialized())
+ && ((!timestamp.is_initialized()) || (timestamp.get() == rhs.timestamp.get()))
+ && (is_ack == rhs.is_ack) && (src_epid == rhs.src_epid)
+ && (address == rhs.address) && (data_vtr == rhs.data_vtr)
+ && (byte_enable == rhs.byte_enable) && (op_code == rhs.op_code)
+ && (status == rhs.status);
+}
+
+const std::string ctrl_payload::to_string() const
+{
+ return str(
+ boost::format("ctrl_payload{dst_port:%d, dst_port:%d, seq_num:%d, timestamp:%s, "
+ "is_ack:%s, src_epid:%d, address:0x%05x, byte_enable:0x%x, "
+ "op_code:%d, status:%d, data[0]:0x%08x}\n")
+ % dst_port % src_port % int(seq_num)
+ % (timestamp.is_initialized() ? str(boost::format("0x%016x") % timestamp.get())
+ : std::string("<not present>"))
+ % (is_ack ? "true" : "false") % src_epid % address % int(byte_enable) % op_code
+ % status % data_vtr[0]);
+}
+
+//----------------------------------------------------
+// CHDR Stream Status Payload
+//----------------------------------------------------
+
+void strs_payload::populate_header(chdr_header& header) const
+{
+ header.set_pkt_type(PKT_TYPE_STRS);
+ header.set_eob(false);
+ header.set_eov(false);
+ header.set_num_mdata(0);
+}
+
+size_t strs_payload::serialize(uint64_t* buff,
+ size_t max_size_bytes,
+ const std::function<uint64_t(uint64_t)>& conv_byte_order) const
+{
+ UHD_ASSERT_THROW(max_size_bytes >= (4 * sizeof(uint64_t)));
+
+ // Populate first word
+ buff[0] = conv_byte_order(
+ ((static_cast<uint64_t>(src_epid) & mask_u64(SRC_EPID_WIDTH)) << SRC_EPID_OFFSET)
+ | ((static_cast<uint64_t>(status) & mask_u64(STATUS_WIDTH)) << STATUS_OFFSET)
+ | ((static_cast<uint64_t>(capacity_bytes) & mask_u64(CAPACITY_BYTES_WIDTH))
+ << CAPACITY_BYTES_OFFSET));
+
+ // Populate second word
+ buff[1] = conv_byte_order(
+ ((static_cast<uint64_t>(capacity_pkts) & mask_u64(CAPACITY_PKTS_WIDTH))
+ << CAPACITY_PKTS_OFFSET)
+ | ((static_cast<uint64_t>(xfer_count_pkts) & mask_u64(XFER_COUNT_PKTS_WIDTH))
+ << XFER_COUNT_PKTS_OFFSET));
+
+ // Populate third word
+ buff[2] = conv_byte_order(xfer_count_bytes);
+
+ // Populate fourth word
+ buff[3] = conv_byte_order(
+ ((static_cast<uint64_t>(buff_info) & mask_u64(BUFF_INFO_WIDTH))
+ << BUFF_INFO_OFFSET)
+ | ((static_cast<uint64_t>(status_info) & mask_u64(STATUS_INFO_WIDTH))
+ << STATUS_INFO_OFFSET));
+
+ // Return bytes written
+ return (4 * sizeof(uint64_t));
+}
+
+void strs_payload::deserialize(const uint64_t* buff,
+ size_t num_elems,
+ const std::function<uint64_t(uint64_t)>& conv_byte_order)
+{
+ UHD_ASSERT_THROW(num_elems >= 4);
+
+ // Read first word
+ uint64_t word0 = conv_byte_order(buff[0]);
+ src_epid = get_field_u64<uint16_t>(word0, SRC_EPID_OFFSET, SRC_EPID_WIDTH);
+ status = get_field_u64<strs_status_t>(word0, STATUS_OFFSET, STATUS_WIDTH);
+ capacity_bytes =
+ get_field_u64<uint64_t>(word0, CAPACITY_BYTES_OFFSET, CAPACITY_BYTES_WIDTH);
+
+ // Read second word
+ uint64_t word1 = conv_byte_order(buff[1]);
+ capacity_pkts =
+ get_field_u64<uint32_t>(word1, CAPACITY_PKTS_OFFSET, CAPACITY_PKTS_WIDTH);
+ xfer_count_pkts =
+ get_field_u64<uint64_t>(word1, XFER_COUNT_PKTS_OFFSET, XFER_COUNT_PKTS_WIDTH);
+
+ // Read third word
+ xfer_count_bytes = conv_byte_order(buff[2]);
+
+ // Read fourth word
+ uint64_t word3 = conv_byte_order(buff[3]);
+ buff_info = get_field_u64<uint16_t>(word3, BUFF_INFO_OFFSET, BUFF_INFO_WIDTH);
+ status_info = get_field_u64<uint64_t>(word3, STATUS_INFO_OFFSET, STATUS_INFO_WIDTH);
+}
+
+bool strs_payload::operator==(const strs_payload& rhs) const
+{
+ return (src_epid == rhs.src_epid) && (status == rhs.status)
+ && (capacity_bytes == rhs.capacity_bytes)
+ && (capacity_pkts == rhs.capacity_pkts)
+ && (xfer_count_pkts == rhs.xfer_count_pkts)
+ && (xfer_count_bytes == rhs.xfer_count_bytes) && (buff_info == rhs.buff_info)
+ && (status_info == rhs.status_info);
+}
+
+const std::string strs_payload::to_string() const
+{
+ return str(boost::format("strs_payload{src_epid:%lu, status:%d, capacity_bytes:%lu, "
+ "capacity_pkts:%lu, "
+ "xfer_count_pkts:%lu, xfer_count_bytes:%lu, "
+ "buff_info:0x%x, status_info:0x%x}\n")
+ % src_epid % int(status) % capacity_bytes % capacity_pkts % xfer_count_pkts
+ % xfer_count_bytes % buff_info % status_info);
+}
+
+//----------------------------------------------------
+// CHDR Stream Command Payload
+//----------------------------------------------------
+
+void strc_payload::populate_header(chdr_header& header) const
+{
+ header.set_pkt_type(PKT_TYPE_STRC);
+ header.set_eob(false);
+ header.set_eov(false);
+ header.set_num_mdata(0);
+}
+
+size_t strc_payload::serialize(uint64_t* buff,
+ size_t max_size_bytes,
+ const std::function<uint64_t(uint64_t)>& conv_byte_order) const
+{
+ UHD_ASSERT_THROW(max_size_bytes >= (2 * sizeof(uint64_t)));
+
+ // Populate first word
+ buff[0] = conv_byte_order(
+ ((static_cast<uint64_t>(src_epid) & mask_u64(SRC_EPID_WIDTH)) << SRC_EPID_OFFSET)
+ | ((static_cast<uint64_t>(op_code) & mask_u64(OP_CODE_WIDTH)) << OP_CODE_OFFSET)
+ | ((static_cast<uint64_t>(op_data) & mask_u64(OP_DATA_WIDTH)) << OP_DATA_OFFSET)
+ | ((static_cast<uint64_t>(num_pkts) & mask_u64(NUM_PKTS_WIDTH))
+ << NUM_PKTS_OFFSET));
+
+ // Populate second word
+ buff[1] = conv_byte_order(num_bytes);
+
+ // Return bytes written
+ return (2 * sizeof(uint64_t));
+}
+
+void strc_payload::deserialize(const uint64_t* buff,
+ size_t num_elems,
+ const std::function<uint64_t(uint64_t)>& conv_byte_order)
+{
+ UHD_ASSERT_THROW(num_elems >= 2);
+
+ // Read first word
+ uint64_t word0 = conv_byte_order(buff[0]);
+ src_epid = get_field_u64<uint16_t>(word0, SRC_EPID_OFFSET, SRC_EPID_WIDTH);
+ op_code = get_field_u64<strc_op_code_t>(word0, OP_CODE_OFFSET, OP_CODE_WIDTH);
+ op_data = get_field_u64<uint8_t>(word0, OP_DATA_OFFSET, OP_DATA_WIDTH);
+ num_pkts = get_field_u64<uint64_t>(word0, NUM_PKTS_OFFSET, NUM_PKTS_WIDTH);
+ // Read second word
+ num_bytes = conv_byte_order(buff[1]);
+}
+
+bool strc_payload::operator==(const strc_payload& rhs) const
+{
+ return (src_epid == rhs.src_epid) && (op_code == rhs.op_code)
+ && (op_data == rhs.op_data) && (num_pkts == rhs.num_pkts)
+ && (num_bytes == rhs.num_bytes);
+}
+
+const std::string strc_payload::to_string() const
+{
+ return str(boost::format("strc_payload{src_epid:%lu, op_code:%d, op_data:0x%x, "
+ "num_pkts:%lu, num_bytes:%lu}\n")
+ % src_epid % int(op_code) % int(op_data) % num_pkts % num_bytes);
+}
+
+//----------------------------------------------------
+// CHDR Management Payload
+//----------------------------------------------------
+
+//! Serialize this hop into a list of 64-bit words
+size_t mgmt_hop_t::serialize(std::vector<uint64_t>& target,
+ const std::function<uint64_t(uint64_t)>& conv_byte_order) const
+{
+ for (size_t i = 0; i < get_num_ops(); i++) {
+ target.push_back(
+ conv_byte_order((static_cast<uint64_t>(_ops.at(i).get_op_payload()) << 16)
+ | (static_cast<uint64_t>(_ops.at(i).get_op_code()) << 8)
+ | (static_cast<uint64_t>(get_num_ops() - i - 1) << 0)));
+ }
+ return get_num_ops();
+}
+
+//! Deserialize this hop into from list of 64-bit words
+void mgmt_hop_t::deserialize(
+ std::list<uint64_t>& src, const std::function<uint64_t(uint64_t)>& conv_byte_order)
+{
+ _ops.clear();
+ size_t ops_remaining = 0;
+ do {
+ // TODO: Change this to a legit exception
+ UHD_ASSERT_THROW(!src.empty());
+
+ uint64_t op_word = conv_byte_order(src.front());
+ ops_remaining = static_cast<size_t>(op_word & 0xFF);
+ mgmt_op_t op(static_cast<mgmt_op_t::op_code_t>((op_word >> 8) & 0xFF),
+ static_cast<uint64_t>((op_word >> 16)));
+ _ops.push_back(op);
+ src.pop_front();
+ } while (ops_remaining > 0);
+}
+
+void mgmt_payload::populate_header(chdr_header& header) const
+{
+ header.set_pkt_type(PKT_TYPE_MGMT);
+ header.set_eob(false);
+ header.set_eov(false);
+ header.set_num_mdata(0);
+ header.set_vc(0);
+ header.set_dst_epid(0);
+}
+
+size_t mgmt_payload::serialize(uint64_t* buff,
+ size_t max_size_bytes,
+ const std::function<uint64_t(uint64_t)>& conv_byte_order) const
+{
+ std::vector<uint64_t> target;
+ // Insert header
+ target.push_back(conv_byte_order(
+ (static_cast<uint64_t>(_protover) << 48)
+ | (static_cast<uint64_t>(static_cast<uint8_t>(_chdr_w) & 0x7) << 45)
+ | (static_cast<uint64_t>(get_num_hops() & 0x3FF) << 16)
+ | (static_cast<uint64_t>(_src_epid) << 0)));
+ // Insert data from each hop
+ for (const auto& hop : _hops) {
+ hop.serialize(target, conv_byte_order);
+ }
+ UHD_ASSERT_THROW(target.size() <= max_size_bytes);
+
+ // We use a vector and copy just for ease of implementation
+ // These transactions are not performance critical
+ std::copy(target.begin(), target.end(), buff);
+ return (target.size() * sizeof(uint64_t));
+}
+
+void mgmt_payload::deserialize(const uint64_t* buff,
+ size_t num_elems,
+ const std::function<uint64_t(uint64_t)>& conv_byte_order)
+{
+ UHD_ASSERT_THROW(num_elems > 1);
+
+ // We use a list and copy just for ease of implementation
+ // These transactions are not performance critical
+ std::list<uint64_t> src_list(buff, buff + num_elems);
+
+ _hops.clear();
+
+ // Deframe the header
+ uint64_t hdr = conv_byte_order(src_list.front());
+ _hops.resize(static_cast<size_t>((hdr >> 16) & 0x3FF));
+ _src_epid = static_cast<sep_id_t>(hdr & 0xFFFF);
+ _chdr_w = static_cast<chdr_w_t>((hdr >> 45) & 0x7);
+ _protover = static_cast<uint16_t>((hdr >> 48) & 0xFFFF);
+ src_list.pop_front();
+
+ // Populate all hops
+ for (size_t i = 0; i < get_num_hops(); i++) {
+ _hops[i].deserialize(src_list, conv_byte_order);
+ }
+}
+
+const std::string mgmt_payload::to_string() const
+{
+ return str(boost::format(
+ "mgmt_payload{src_epid:%lu, chdr_w:%d, protover:0x%x, num_hops:%lu}\n")
+ % _src_epid % int(_chdr_w) % _protover % _hops.size());
+}
diff --git a/host/tests/CMakeLists.txt b/host/tests/CMakeLists.txt
index 551b57bb9..f31daed50 100644
--- a/host/tests/CMakeLists.txt
+++ b/host/tests/CMakeLists.txt
@@ -52,7 +52,6 @@ set(test_sources
vrt_test.cpp
expert_test.cpp
fe_conn_test.cpp
- rfnoc_chdr_test.cpp
rfnoc_node_test.cpp
)
@@ -191,6 +190,19 @@ UHD_ADD_NONAPI_TEST(
EXTRA_SOURCES ${CMAKE_SOURCE_DIR}/lib/utils/config_parser.cpp
)
+include_directories(${CMAKE_BINARY_DIR}/lib/rfnoc/chdr/)
+include_directories(${CMAKE_SOURCE_DIR}/lib/rfnoc/chdr/)
+add_executable(rfnoc_chdr_test
+ rfnoc_chdr_test.cpp
+ ${CMAKE_SOURCE_DIR}/lib/rfnoc/chdr/chdr_types.cpp
+ ${CMAKE_SOURCE_DIR}/lib/rfnoc/chdr/chdr_packet.cpp
+)
+target_link_libraries(rfnoc_chdr_test uhd ${Boost_LIBRARIES})
+UHD_ADD_TEST(rfnoc_chdr_test rfnoc_chdr_test)
+UHD_INSTALL(TARGETS rfnoc_chdr_test RUNTIME DESTINATION ${PKG_LIB_DIR}/tests COMPONENT tests)
+
+
+
# Careful: This is to satisfy the out-of-library build of paths.cpp. This is
# duplicate code from lib/utils/CMakeLists.txt, and it's been simplified.
# TODO Figure out if this is even needed
diff --git a/host/tests/rfnoc_chdr_test.cpp b/host/tests/rfnoc_chdr_test.cpp
index 1fb2df8cf..67993cb02 100644
--- a/host/tests/rfnoc_chdr_test.cpp
+++ b/host/tests/rfnoc_chdr_test.cpp
@@ -4,92 +4,221 @@
// 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 <uhdlib/rfnoc/chdr/chdr_packet.hpp>
+#include <uhdlib/rfnoc/chdr/chdr_types.hpp>
#include <boost/format.hpp>
#include <boost/test/unit_test.hpp>
-#include <cstring>
-#include <cstdint>
+#include <iostream>
+using namespace uhd;
+using namespace uhd::rfnoc;
using namespace uhd::rfnoc::chdr;
-namespace {
- constexpr size_t MAX_BUF_SIZE = 8192;
+constexpr size_t MAX_BUF_SIZE_BYTES = 1024;
+constexpr size_t MAX_BUF_SIZE_WORDS = MAX_BUF_SIZE_BYTES / sizeof(uint64_t);
+constexpr size_t NUM_ITERS = 5000;
- // Poorman's send
- void send(const void* from, void* to, size_t len)
- {
- std::memcpy(to, from, len);
+static const chdr_packet_factory chdr64_be_factory(CHDR_W_64, ENDIANNESS_BIG);
+static const chdr_packet_factory chdr256_be_factory(CHDR_W_256, ENDIANNESS_BIG);
+static const chdr_packet_factory chdr64_le_factory(CHDR_W_64, ENDIANNESS_LITTLE);
+static const chdr_packet_factory chdr256_le_factory(CHDR_W_256, ENDIANNESS_LITTLE);
+
+uint64_t rand64()
+{
+ return ((uint64_t)rand() << 32) | rand();
+}
+
+ctrl_payload populate_ctrl_payload()
+{
+ ctrl_payload pyld;
+ pyld.dst_port = rand64() & 0x03FF;
+ pyld.src_port = rand64() & 0x03FF;
+ pyld.is_ack = rand64() & 0x1;
+ pyld.src_epid = rand64() & 0xFFFF;
+ pyld.data_vtr[0] = rand64() & 0xFFFFFFFF;
+ pyld.byte_enable = rand64() & 0xF;
+ pyld.op_code = static_cast<ctrl_opcode_t>(rand64() % 8);
+ pyld.status = static_cast<ctrl_status_t>(rand64() % 4);
+ if (rand64() % 2 == 0) {
+ pyld.timestamp = rand64();
+ } else {
+ pyld.timestamp = boost::none;
+ }
+ return pyld;
+}
+
+strs_payload populate_strs_payload()
+{
+ strs_payload pyld;
+ pyld.src_epid = rand64() & 0xFFFF;
+ pyld.status = static_cast<strs_status_t>(rand64() % 4);
+ pyld.capacity_bytes = rand64() & 0xFFFFFFFFFF;
+ pyld.capacity_pkts = 0xFFFFFF;
+ pyld.xfer_count_bytes = rand64();
+ pyld.xfer_count_pkts = rand64() & 0xFFFFFFFFFF;
+ pyld.buff_info = rand64() & 0xFFFF;
+ pyld.status_info = rand64() & 0xFFFFFFFFFFFF;
+ return pyld;
+}
+
+strc_payload populate_strc_payload()
+{
+ strc_payload pyld;
+ pyld.src_epid = rand64() & 0xFFFF;
+ pyld.op_code = static_cast<strc_op_code_t>(rand64() % 3);
+ pyld.op_data = rand64() & 0xF;
+ pyld.num_pkts = rand64() & 0xFFFFFFFFFF;
+ pyld.num_bytes = rand64();
+ return pyld;
+}
+
+void byte_swap(uint64_t* buff)
+{
+ for (size_t i = 0; i < MAX_BUF_SIZE_WORDS; i++) {
+ *(buff + i) = uhd::byteswap(*(buff + i));
}
}
-uint8_t send_packet_buff[MAX_BUF_SIZE];
-uint8_t recv_packet_buff[MAX_BUF_SIZE];
+BOOST_AUTO_TEST_CASE(chdr_ctrl_packet_no_swap_64)
+{
+ uint64_t buff[MAX_BUF_SIZE_WORDS];
+
+ chdr_ctrl_packet::uptr tx_pkt = chdr64_be_factory.make_ctrl();
+ chdr_ctrl_packet::cuptr rx_pkt = chdr64_be_factory.make_ctrl();
+
+ for (size_t i = 0; i < NUM_ITERS; i++) {
+ chdr_header hdr = chdr_header(rand64());
+ ctrl_payload pyld = populate_ctrl_payload();
+ memset(buff, 0, MAX_BUF_SIZE_BYTES);
+ tx_pkt->refresh(buff, hdr, pyld);
+ BOOST_CHECK(tx_pkt->get_chdr_header() == hdr);
+ BOOST_CHECK(tx_pkt->get_payload() == pyld);
+
+ rx_pkt->refresh(buff);
+ BOOST_CHECK(rx_pkt->get_chdr_header() == hdr);
+ BOOST_CHECK(rx_pkt->get_payload() == pyld);
+
+ std::cout << pyld.to_string();
+ }
+}
-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)
+BOOST_AUTO_TEST_CASE(chdr_ctrl_packet_no_swap_256)
{
- // 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;
+ uint64_t buff[MAX_BUF_SIZE_WORDS];
+
+ chdr_ctrl_packet::uptr tx_pkt = chdr256_be_factory.make_ctrl();
+ chdr_ctrl_packet::cuptr rx_pkt = chdr256_be_factory.make_ctrl();
+
+ for (size_t i = 0; i < NUM_ITERS; i++) {
+ chdr_header hdr = chdr_header(rand64());
+ ctrl_payload pyld = populate_ctrl_payload();
+
+ memset(buff, 0, MAX_BUF_SIZE_BYTES);
+ tx_pkt->refresh(buff, hdr, pyld);
+ BOOST_CHECK(tx_pkt->get_chdr_header() == hdr);
+ BOOST_CHECK(tx_pkt->get_payload() == pyld);
+
+ rx_pkt->refresh(buff);
+ BOOST_CHECK(rx_pkt->get_chdr_header() == hdr);
+ BOOST_CHECK(rx_pkt->get_payload() == pyld);
}
- 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);
+}
+
+BOOST_AUTO_TEST_CASE(chdr_ctrl_packet_swap_64)
+{
+ uint64_t buff[MAX_BUF_SIZE_WORDS];
+
+ chdr_ctrl_packet::uptr tx_pkt = chdr64_be_factory.make_ctrl();
+ chdr_ctrl_packet::cuptr rx_pkt = chdr64_le_factory.make_ctrl();
+
+ for (size_t i = 0; i < NUM_ITERS; i++) {
+ chdr_header hdr = chdr_header(rand64());
+ ctrl_payload pyld = populate_ctrl_payload();
+
+ memset(buff, 0, MAX_BUF_SIZE_BYTES);
+ tx_pkt->refresh(buff, hdr, pyld);
+ BOOST_CHECK(tx_pkt->get_chdr_header() == hdr);
+ BOOST_CHECK(tx_pkt->get_payload() == pyld);
+
+ byte_swap(buff);
+
+ rx_pkt->refresh(buff);
+ BOOST_CHECK(rx_pkt->get_chdr_header() == hdr);
+ BOOST_CHECK(rx_pkt->get_payload() == pyld);
}
- 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]);
+}
+
+BOOST_AUTO_TEST_CASE(chdr_ctrl_packet_swap_256)
+{
+ uint64_t buff[MAX_BUF_SIZE_WORDS];
+
+ chdr_ctrl_packet::uptr tx_pkt = chdr256_be_factory.make_ctrl();
+ chdr_ctrl_packet::cuptr rx_pkt = chdr256_le_factory.make_ctrl();
+
+ for (size_t i = 0; i < NUM_ITERS; i++) {
+ chdr_header hdr = chdr_header(rand64());
+ ctrl_payload pyld = populate_ctrl_payload();
+
+ memset(buff, 0, MAX_BUF_SIZE_BYTES);
+ tx_pkt->refresh(buff, hdr, pyld);
+ BOOST_CHECK(tx_pkt->get_chdr_header() == hdr);
+ BOOST_CHECK(tx_pkt->get_payload() == pyld);
+
+ byte_swap(buff);
+
+ rx_pkt->refresh(buff);
+ BOOST_CHECK(rx_pkt->get_chdr_header() == hdr);
+ BOOST_CHECK(rx_pkt->get_payload() == pyld);
}
- 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(chdr_strs_packet_no_swap_64)
+{
+ uint64_t buff[MAX_BUF_SIZE_WORDS];
+
+ chdr_strs_packet::uptr tx_pkt = chdr64_be_factory.make_strs();
+ chdr_strs_packet::cuptr rx_pkt = chdr64_be_factory.make_strs();
+
+ for (size_t i = 0; i < NUM_ITERS; i++) {
+ chdr_header hdr = chdr_header(rand64());
+ strs_payload pyld = populate_strs_payload();
+
+ memset(buff, 0, MAX_BUF_SIZE_BYTES);
+ tx_pkt->refresh(buff, hdr, pyld);
+ BOOST_CHECK(tx_pkt->get_chdr_header() == hdr);
+ BOOST_CHECK(tx_pkt->get_payload() == pyld);
+
+ rx_pkt->refresh(buff);
+ BOOST_CHECK(rx_pkt->get_chdr_header() == hdr);
+ BOOST_CHECK(rx_pkt->get_payload() == pyld);
+
+ std::cout << pyld.to_string();
}
}
-BOOST_AUTO_TEST_CASE(simple_read_if_chdr_pkt)
+BOOST_AUTO_TEST_CASE(chdr_strc_packet_no_swap_64)
{
- 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);
+ uint64_t buff[MAX_BUF_SIZE_WORDS];
+
+ chdr_strc_packet::uptr tx_pkt = chdr64_be_factory.make_strc();
+ chdr_strc_packet::cuptr rx_pkt = chdr64_be_factory.make_strc();
+
+ for (size_t i = 0; i < NUM_ITERS; i++) {
+ chdr_header hdr = chdr_header(rand64());
+ strc_payload pyld = populate_strc_payload();
+
+ memset(buff, 0, MAX_BUF_SIZE_BYTES);
+ tx_pkt->refresh(buff, hdr, pyld);
+ BOOST_CHECK(tx_pkt->get_chdr_header() == hdr);
+ BOOST_CHECK(tx_pkt->get_payload() == pyld);
+
+ rx_pkt->refresh(buff);
+ BOOST_CHECK(rx_pkt->get_chdr_header() == hdr);
+ BOOST_CHECK(rx_pkt->get_payload() == pyld);
+
+ std::cout << pyld.to_string();
+ }
}