1 //
2 // Copyright 2020 Ettus Research, a National Instruments Brand
3 //
4 // SPDX-License-Identifier: GPL-3.0-or-later
5 //
6 
7 #include <uhd/utils/chdr/chdr_packet.hpp>
8 #include <uhdlib/rfnoc/chdr_packet_writer.hpp>
9 #include <boost/format.hpp>
10 
11 namespace chdr_rfnoc = uhd::rfnoc::chdr;
12 namespace chdr_util  = uhd::utils::chdr;
13 using namespace uhd;
14 
chdr_packet(uhd::rfnoc::chdr_w_t chdr_w,chdr_rfnoc::chdr_header header,std::vector<uint8_t> payload,boost::optional<uint64_t> timestamp,std::vector<uint64_t> mdata)15 chdr_util::chdr_packet::chdr_packet(uhd::rfnoc::chdr_w_t chdr_w,
16     chdr_rfnoc::chdr_header header,
17     std::vector<uint8_t> payload,
18     boost::optional<uint64_t> timestamp,
19     std::vector<uint64_t> mdata)
20     : _chdr_w(chdr_w)
21     , _header(header)
22     , _payload(std::move(payload))
23     , _timestamp(timestamp)
24     , _mdata(std::move(mdata))
25 {
26     set_header_lengths();
27 }
28 
u64_to_host(uhd::endianness_t endianness,uint64_t word)29 inline static uint64_t u64_to_host(uhd::endianness_t endianness, uint64_t word)
30 {
31     return (endianness == ENDIANNESS_BIG) ? uhd::ntohx<uint64_t>(word)
32                                           : uhd::wtohx<uint64_t>(word);
33 }
34 
u64_from_host(uhd::endianness_t endianness,uint64_t word)35 inline static uint64_t u64_from_host(uhd::endianness_t endianness, uint64_t word)
36 {
37     return (endianness == ENDIANNESS_BIG) ? uhd::htonx<uint64_t>(word)
38                                           : uhd::htowx<uint64_t>(word);
39 }
40 
get_header() const41 chdr_rfnoc::chdr_header chdr_util::chdr_packet::get_header() const
42 {
43     return this->_header;
44 }
45 
set_header(chdr_rfnoc::chdr_header header)46 void chdr_util::chdr_packet::set_header(chdr_rfnoc::chdr_header header)
47 {
48     _header = header;
49     set_header_lengths();
50 }
51 
get_timestamp() const52 boost::optional<uint64_t> chdr_util::chdr_packet::get_timestamp() const
53 {
54     return this->_timestamp;
55 }
56 
set_timestamp(boost::optional<uint64_t> timestamp)57 void chdr_util::chdr_packet::set_timestamp(boost::optional<uint64_t> timestamp)
58 {
59     _timestamp = timestamp;
60     set_header_lengths();
61 }
62 
serialize_to_byte_vector(endianness_t endianness) const63 std::vector<uint8_t> chdr_util::chdr_packet::serialize_to_byte_vector(
64     endianness_t endianness) const
65 {
66     std::vector<uint8_t> buffer(get_packet_len(), 0);
67     serialize(buffer.begin(), buffer.end(), endianness);
68     return buffer;
69 }
70 
serialize_ptr(endianness_t endianness,void * start,void * end) const71 void chdr_util::chdr_packet::serialize_ptr(
72     endianness_t endianness, void* start, void* end) const
73 {
74     size_t len = static_cast<uint8_t*>(end) - static_cast<uint8_t*>(start);
75     UHD_ASSERT_THROW(get_packet_len() <= len);
76     chdr_rfnoc::chdr_packet_factory factory(_chdr_w, endianness);
77     chdr_rfnoc::chdr_packet_writer::uptr packet_writer =
78         factory.make_generic(std::numeric_limits<size_t>::max());
79     chdr_rfnoc::chdr_header header = _header;
80     packet_writer->refresh(start, header, (_timestamp ? *_timestamp : 0));
81 
82     uint64_t* mdata_ptr  = static_cast<uint64_t*>(packet_writer->get_mdata_ptr());
83     auto mdata_src_begin = this->_mdata.begin();
84     auto mdata_src_end   = this->_mdata.end();
85     std::transform(
86         mdata_src_begin, mdata_src_end, mdata_ptr, [endianness](uint64_t value) {
87             return u64_from_host(endianness, value);
88         });
89 
90     uint8_t* payload_ptr = static_cast<uint8_t*>(packet_writer->get_payload_ptr());
91     std::copy(_payload.begin(), _payload.end(), payload_ptr);
92 }
93 
deserialize_ptr(uhd::rfnoc::chdr_w_t chdr_w,endianness_t endianness,const void * start,const void * end)94 chdr_util::chdr_packet chdr_util::chdr_packet::deserialize_ptr(
95     uhd::rfnoc::chdr_w_t chdr_w,
96     endianness_t endianness,
97     const void* start,
98     const void* end)
99 {
100     chdr_rfnoc::chdr_packet_factory factory(chdr_w, endianness);
101     chdr_rfnoc::chdr_packet_writer::uptr packet_writer =
102         factory.make_generic(std::numeric_limits<size_t>::max());
103     packet_writer->refresh(start);
104 
105     chdr_rfnoc::chdr_header header      = packet_writer->get_chdr_header();
106     boost::optional<uint64_t> timestamp = packet_writer->get_timestamp();
107 
108     const size_t mdata_size_words = packet_writer->get_mdata_size() / 8;
109     const uint64_t* mdata_src_begin =
110         static_cast<const uint64_t*>(packet_writer->get_mdata_const_ptr());
111     const uint64_t* mdata_src_end = mdata_src_begin + mdata_size_words;
112     std::vector<uint64_t> mdata(mdata_size_words, 0);
113     uint64_t* mdata_ptr = mdata.data();
114     UHD_ASSERT_THROW(mdata_src_end < static_cast<const uint64_t*>(end));
115     std::transform(
116         mdata_src_begin, mdata_src_end, mdata_ptr, [endianness](uint64_t value) {
117             return u64_from_host(endianness, value);
118         });
119 
120     size_t payload_size = packet_writer->get_payload_size();
121     const uint8_t* payload_begin =
122         static_cast<const uint8_t*>(packet_writer->get_payload_const_ptr());
123     const uint8_t* payload_end = payload_begin + payload_size;
124     std::vector<uint8_t> payload(payload_size, 0);
125     UHD_ASSERT_THROW(payload_end <= static_cast<const uint8_t*>(end));
126     std::copy(payload_begin, payload_end, payload.begin());
127 
128     return chdr_util::chdr_packet(chdr_w, header, payload, timestamp, mdata);
129 }
130 
set_metadata(std::vector<uint64_t> metadata)131 void chdr_util::chdr_packet::set_metadata(std::vector<uint64_t> metadata)
132 {
133     _mdata = std::move(metadata);
134     set_header_lengths();
135 }
136 
get_metadata() const137 const std::vector<uint64_t>& chdr_util::chdr_packet::get_metadata() const
138 {
139     return _mdata;
140 }
141 
get_packet_len() const142 size_t chdr_util::chdr_packet::get_packet_len() const
143 {
144     size_t chdr_w_bytes = uhd::rfnoc::chdr_w_to_bits(_chdr_w) / 8;
145     // The timestamp and header take up 2 chdr_w lengths only when CHDR_W = 64 bits and
146     // there is a timestamp (RFNoC Specifcation section 2.2.1)
147     return _payload.size() * sizeof(uint8_t) + _mdata.size() * sizeof(uint64_t)
148            + ((_timestamp != boost::none && _chdr_w == uhd::rfnoc::CHDR_W_64) ? 2 : 1)
149                  * chdr_w_bytes;
150 }
151 
get_payload_bytes() const152 const std::vector<uint8_t>& chdr_util::chdr_packet::get_payload_bytes() const
153 {
154     return _payload;
155 }
156 
set_payload_bytes(std::vector<uint8_t> bytes)157 void chdr_util::chdr_packet::set_payload_bytes(std::vector<uint8_t> bytes)
158 {
159     this->_payload = std::move(bytes);
160     set_header_lengths();
161 }
162 
163 //! Return a string representation of this object
to_string() const164 const std::string chdr_util::chdr_packet::to_string() const
165 {
166     return str(boost::format("chdr_packet{chdr_w:%u}\n%s")
167                % uhd::rfnoc::chdr_w_to_bits(_chdr_w) % _header.to_string());
168 }
169