1 //
2 // Copyright 2019 Ettus Research, a National Instruments Brand
3 //
4 // SPDX-License-Identifier: GPL-3.0-or-later
5 //
6 
7 #include <uhd/exception.hpp>
8 #include <uhd/rfnoc/chdr_types.hpp>
9 #include <uhd/types/endianness.hpp>
10 #include <boost/format.hpp>
11 #include <cassert>
12 #include <sstream>
13 
14 using namespace uhd;
15 using namespace uhd::rfnoc::chdr;
16 
17 //----------------------------------------------------
18 // Utility Functions
19 //----------------------------------------------------
20 
mask_u64(size_t width)21 static inline constexpr uint64_t mask_u64(size_t width)
22 {
23     return ((uint64_t(1) << width) - 1);
24 }
25 
26 template <typename field_t>
get_field_u64(uint64_t flat_hdr,size_t offset,size_t width)27 static inline constexpr field_t get_field_u64(
28     uint64_t flat_hdr, size_t offset, size_t width)
29 {
30     return static_cast<field_t>((flat_hdr >> offset) & mask_u64(width));
31 }
32 
33 //----------------------------------------------------
34 // CHDR Control Payload
35 //----------------------------------------------------
36 
populate_header(chdr_header & header) const37 void ctrl_payload::populate_header(chdr_header& header) const
38 {
39     header.set_pkt_type(PKT_TYPE_CTRL);
40     header.set_eob(false);
41     header.set_eov(false);
42     header.set_num_mdata(0);
43 }
44 
serialize(uint64_t * buff,size_t max_size_bytes,const std::function<uint64_t (uint64_t)> & conv_byte_order) const45 size_t ctrl_payload::serialize(uint64_t* buff,
46     size_t max_size_bytes,
47     const std::function<uint64_t(uint64_t)>& conv_byte_order) const
48 {
49     // Ctrl Packet Payload can't have more than 15 data -> 8 CHDR_W (RFNoC Spec.
50     // Section 2.2.3)
51     UHD_ASSERT_THROW((data_vtr.size() > 0 && data_vtr.size() < 16));
52     UHD_ASSERT_THROW(get_length() * sizeof(uint64_t) <= max_size_bytes);
53     size_t ptr = 0;
54 
55     // Populate control header
56     buff[ptr++] = conv_byte_order(
57         ((static_cast<uint64_t>(dst_port) & mask_u64(DST_PORT_WIDTH)) << DST_PORT_OFFSET)
58         | ((static_cast<uint64_t>(src_port) & mask_u64(SRC_PORT_WIDTH))
59             << SRC_PORT_OFFSET)
60         | ((static_cast<uint64_t>(data_vtr.size()) & mask_u64(NUM_DATA_WIDTH))
61             << NUM_DATA_OFFSET)
62         | ((static_cast<uint64_t>(seq_num) & mask_u64(SEQ_NUM_WIDTH)) << SEQ_NUM_OFFSET)
63         | ((static_cast<uint64_t>(timestamp.is_initialized() ? 1 : 0)
64                & mask_u64(HAS_TIME_WIDTH))
65             << HAS_TIME_OFFSET)
66         | ((static_cast<uint64_t>(is_ack) & mask_u64(IS_ACK_WIDTH)) << IS_ACK_OFFSET)
67         | ((static_cast<uint64_t>(src_epid) & mask_u64(SRC_EPID_WIDTH))
68             << SRC_EPID_OFFSET));
69 
70     // Populate optional timestamp
71     if (timestamp.is_initialized()) {
72         buff[ptr++] = conv_byte_order(timestamp.get());
73     }
74 
75     // Populate control operation word
76     buff[ptr++] = conv_byte_order(
77         ((static_cast<uint64_t>(address) & mask_u64(ADDRESS_WIDTH)) << ADDRESS_OFFSET)
78         | ((static_cast<uint64_t>(byte_enable) & mask_u64(BYTE_ENABLE_WIDTH))
79             << BYTE_ENABLE_OFFSET)
80         | ((static_cast<uint64_t>(op_code) & mask_u64(OPCODE_WIDTH)) << OPCODE_OFFSET)
81         | ((static_cast<uint64_t>(status) & mask_u64(STATUS_WIDTH)) << STATUS_OFFSET)
82         | (static_cast<uint64_t>(data_vtr[0]) << HI_DATA_OFFSET));
83 
84     // Populate the rest of the data
85     for (size_t i = 1; i < data_vtr.size(); i += 2) {
86         const uint32_t hi_data =
87             (((i + 2) >= data_vtr.size()) && (data_vtr.size() % 2 == 0))
88                 ? 0
89                 : data_vtr[i + 1];
90         buff[ptr++] =
91             conv_byte_order(static_cast<uint64_t>(hi_data) << HI_DATA_OFFSET
92                             | static_cast<uint64_t>(data_vtr[i]) << LO_DATA_OFFSET);
93     }
94 
95     // This really should be impossible but we'll leave it for safety's sake
96     UHD_ASSERT_THROW(ptr <= max_size_bytes);
97     // Return bytes written
98     return (ptr * sizeof(uint64_t));
99 }
100 
deserialize(const uint64_t * buff,size_t max_size_bytes,const std::function<uint64_t (uint64_t)> & conv_byte_order)101 void ctrl_payload::deserialize(const uint64_t* buff,
102     size_t max_size_bytes,
103     const std::function<uint64_t(uint64_t)>& conv_byte_order)
104 {
105     // We assume that buff has room to hold the entire packet
106     size_t ptr = 0;
107 
108     // Read control header
109     uint64_t ctrl_header = conv_byte_order(buff[ptr++]);
110     data_vtr.resize(get_field_u64<size_t>(ctrl_header, NUM_DATA_OFFSET, NUM_DATA_WIDTH));
111     UHD_ASSERT_THROW((data_vtr.size() > 0 && data_vtr.size() < 16));
112     dst_port = get_field_u64<uint16_t>(ctrl_header, DST_PORT_OFFSET, DST_PORT_WIDTH);
113     src_port = get_field_u64<uint16_t>(ctrl_header, SRC_PORT_OFFSET, SRC_PORT_WIDTH);
114     seq_num  = get_field_u64<uint8_t>(ctrl_header, SEQ_NUM_OFFSET, SEQ_NUM_WIDTH);
115     is_ack   = get_field_u64<bool>(ctrl_header, IS_ACK_OFFSET, IS_ACK_WIDTH);
116     src_epid = get_field_u64<uint16_t>(ctrl_header, SRC_EPID_OFFSET, SRC_EPID_WIDTH);
117 
118     // Read optional timestamp
119     if (get_field_u64<bool>(ctrl_header, HAS_TIME_OFFSET, HAS_TIME_WIDTH)) {
120         timestamp = conv_byte_order(buff[ptr++]);
121     } else {
122         timestamp = boost::none;
123     }
124 
125     // Read control operation word
126     uint64_t op_word = conv_byte_order(buff[ptr++]);
127     if (data_vtr.size() > 0) {
128         data_vtr[0] = get_field_u64<uint32_t>(op_word, HI_DATA_OFFSET, 32);
129     }
130     address     = get_field_u64<uint32_t>(op_word, ADDRESS_OFFSET, ADDRESS_WIDTH);
131     byte_enable = get_field_u64<uint8_t>(op_word, BYTE_ENABLE_OFFSET, BYTE_ENABLE_WIDTH);
132     op_code     = get_field_u64<ctrl_opcode_t>(op_word, OPCODE_OFFSET, OPCODE_WIDTH);
133     status      = get_field_u64<ctrl_status_t>(op_word, STATUS_OFFSET, STATUS_WIDTH);
134 
135     // Read the rest of the data
136     for (size_t i = 1; i < data_vtr.size(); i += 2) {
137         uint64_t data_word = conv_byte_order(buff[ptr++]);
138         if (((i + 2) < data_vtr.size()) || (data_vtr.size() % 2 != 0)) {
139             data_vtr[i + 1] = get_field_u64<uint32_t>(data_word, HI_DATA_OFFSET, 32);
140         }
141         data_vtr[i] = get_field_u64<uint32_t>(data_word, LO_DATA_OFFSET, 32);
142     }
143     UHD_ASSERT_THROW(ptr <= max_size_bytes);
144 }
145 
get_length() const146 size_t ctrl_payload::get_length() const
147 {
148     size_t length = 1;
149     if (this->has_timestamp()) {
150         length += 1;
151     }
152     size_t operations = 1 + this->data_vtr.size();
153     length += operations / 2 + operations % 2;
154     return length;
155 }
156 
operator ==(const ctrl_payload & rhs) const157 bool ctrl_payload::operator==(const ctrl_payload& rhs) const
158 {
159     return (dst_port == rhs.dst_port) && (src_port == rhs.src_port)
160            && (seq_num == rhs.seq_num)
161            && (timestamp.is_initialized() == rhs.timestamp.is_initialized())
162            && ((!timestamp.is_initialized()) || (timestamp.get() == rhs.timestamp.get()))
163            && (is_ack == rhs.is_ack) && (src_epid == rhs.src_epid)
164            && (address == rhs.address) && (data_vtr == rhs.data_vtr)
165            && (byte_enable == rhs.byte_enable) && (op_code == rhs.op_code)
166            && (status == rhs.status);
167 }
168 
to_string() const169 const std::string ctrl_payload::to_string() const
170 {
171     return str(
172         boost::format("ctrl_payload{dst_port:%d, dst_port:%d, seq_num:%d, timestamp:%s, "
173                       "is_ack:%s, src_epid:%d, address:0x%05x, byte_enable:0x%x, "
174                       "op_code:%d, status:%d, data[0]:0x%08x}\n")
175         % dst_port % src_port % int(seq_num)
176         % (timestamp.is_initialized() ? str(boost::format("0x%016x") % timestamp.get())
177                                       : std::string("<not present>"))
178         % (is_ack ? "true" : "false") % src_epid % address % int(byte_enable) % op_code
179         % status % data_vtr[0]);
180 }
181 
182 //----------------------------------------------------
183 // CHDR Stream Status Payload
184 //----------------------------------------------------
185 
populate_header(chdr_header & header) const186 void strs_payload::populate_header(chdr_header& header) const
187 {
188     header.set_pkt_type(PKT_TYPE_STRS);
189     header.set_eob(false);
190     header.set_eov(false);
191     header.set_num_mdata(0);
192 }
193 
serialize(uint64_t * buff,size_t max_size_bytes,const std::function<uint64_t (uint64_t)> & conv_byte_order) const194 size_t strs_payload::serialize(uint64_t* buff,
195     size_t max_size_bytes,
196     const std::function<uint64_t(uint64_t)>& conv_byte_order) const
197 {
198     UHD_ASSERT_THROW(max_size_bytes >= (4 * sizeof(uint64_t)));
199 
200     // Populate first word
201     buff[0] = conv_byte_order(
202         ((static_cast<uint64_t>(src_epid) & mask_u64(SRC_EPID_WIDTH)) << SRC_EPID_OFFSET)
203         | ((static_cast<uint64_t>(status) & mask_u64(STATUS_WIDTH)) << STATUS_OFFSET)
204         | ((static_cast<uint64_t>(capacity_bytes) & mask_u64(CAPACITY_BYTES_WIDTH))
205             << CAPACITY_BYTES_OFFSET));
206 
207     // Populate second word
208     buff[1] = conv_byte_order(
209         ((static_cast<uint64_t>(capacity_pkts) & mask_u64(CAPACITY_PKTS_WIDTH))
210             << CAPACITY_PKTS_OFFSET)
211         | ((static_cast<uint64_t>(xfer_count_pkts) & mask_u64(XFER_COUNT_PKTS_WIDTH))
212             << XFER_COUNT_PKTS_OFFSET));
213 
214     // Populate third word
215     buff[2] = conv_byte_order(xfer_count_bytes);
216 
217     // Populate fourth word
218     buff[3] = conv_byte_order(
219         ((static_cast<uint64_t>(buff_info) & mask_u64(BUFF_INFO_WIDTH))
220             << BUFF_INFO_OFFSET)
221         | ((static_cast<uint64_t>(status_info) & mask_u64(STATUS_INFO_WIDTH))
222             << STATUS_INFO_OFFSET));
223 
224     // Return bytes written
225     return (4 * sizeof(uint64_t));
226 }
227 
deserialize(const uint64_t * buff,size_t num_elems,const std::function<uint64_t (uint64_t)> & conv_byte_order)228 void strs_payload::deserialize(const uint64_t* buff,
229     size_t num_elems,
230     const std::function<uint64_t(uint64_t)>& conv_byte_order)
231 {
232     UHD_ASSERT_THROW(num_elems >= 4);
233 
234     // Read first word
235     uint64_t word0 = conv_byte_order(buff[0]);
236     src_epid       = get_field_u64<uint16_t>(word0, SRC_EPID_OFFSET, SRC_EPID_WIDTH);
237     status         = get_field_u64<strs_status_t>(word0, STATUS_OFFSET, STATUS_WIDTH);
238     capacity_bytes =
239         get_field_u64<uint64_t>(word0, CAPACITY_BYTES_OFFSET, CAPACITY_BYTES_WIDTH);
240 
241     // Read second word
242     uint64_t word1 = conv_byte_order(buff[1]);
243     capacity_pkts =
244         get_field_u64<uint32_t>(word1, CAPACITY_PKTS_OFFSET, CAPACITY_PKTS_WIDTH);
245     xfer_count_pkts =
246         get_field_u64<uint64_t>(word1, XFER_COUNT_PKTS_OFFSET, XFER_COUNT_PKTS_WIDTH);
247 
248     // Read third word
249     xfer_count_bytes = conv_byte_order(buff[2]);
250 
251     // Read fourth word
252     uint64_t word3 = conv_byte_order(buff[3]);
253     buff_info      = get_field_u64<uint16_t>(word3, BUFF_INFO_OFFSET, BUFF_INFO_WIDTH);
254     status_info = get_field_u64<uint64_t>(word3, STATUS_INFO_OFFSET, STATUS_INFO_WIDTH);
255 }
256 
get_length() const257 size_t strs_payload::get_length() const
258 {
259     return 4;
260 }
261 
operator ==(const strs_payload & rhs) const262 bool strs_payload::operator==(const strs_payload& rhs) const
263 {
264     return (src_epid == rhs.src_epid) && (status == rhs.status)
265            && (capacity_bytes == rhs.capacity_bytes)
266            && (capacity_pkts == rhs.capacity_pkts)
267            && (xfer_count_pkts == rhs.xfer_count_pkts)
268            && (xfer_count_bytes == rhs.xfer_count_bytes) && (buff_info == rhs.buff_info)
269            && (status_info == rhs.status_info);
270 }
271 
to_string() const272 const std::string strs_payload::to_string() const
273 {
274     return str(boost::format("strs_payload{src_epid:%lu, status:%d, capacity_bytes:%lu, "
275                              "capacity_pkts:%lu, "
276                              "xfer_count_pkts:%lu, xfer_count_bytes:%lu, "
277                              "buff_info:0x%x, status_info:0x%x}\n")
278                % src_epid % int(status) % capacity_bytes % capacity_pkts % xfer_count_pkts
279                % xfer_count_bytes % buff_info % status_info);
280 }
281 
282 //----------------------------------------------------
283 // CHDR Stream Command Payload
284 //----------------------------------------------------
285 
populate_header(chdr_header & header) const286 void strc_payload::populate_header(chdr_header& header) const
287 {
288     header.set_pkt_type(PKT_TYPE_STRC);
289     header.set_eob(false);
290     header.set_eov(false);
291     header.set_num_mdata(0);
292 }
293 
serialize(uint64_t * buff,size_t max_size_bytes,const std::function<uint64_t (uint64_t)> & conv_byte_order) const294 size_t strc_payload::serialize(uint64_t* buff,
295     size_t max_size_bytes,
296     const std::function<uint64_t(uint64_t)>& conv_byte_order) const
297 {
298     UHD_ASSERT_THROW(max_size_bytes >= (2 * sizeof(uint64_t)));
299 
300     // Populate first word
301     buff[0] = conv_byte_order(
302         ((static_cast<uint64_t>(src_epid) & mask_u64(SRC_EPID_WIDTH)) << SRC_EPID_OFFSET)
303         | ((static_cast<uint64_t>(op_code) & mask_u64(OP_CODE_WIDTH)) << OP_CODE_OFFSET)
304         | ((static_cast<uint64_t>(op_data) & mask_u64(OP_DATA_WIDTH)) << OP_DATA_OFFSET)
305         | ((static_cast<uint64_t>(num_pkts) & mask_u64(NUM_PKTS_WIDTH))
306             << NUM_PKTS_OFFSET));
307 
308     // Populate second word
309     buff[1] = conv_byte_order(num_bytes);
310 
311     // Return bytes written
312     return (2 * sizeof(uint64_t));
313 }
314 
deserialize(const uint64_t * buff,size_t num_elems,const std::function<uint64_t (uint64_t)> & conv_byte_order)315 void strc_payload::deserialize(const uint64_t* buff,
316     size_t num_elems,
317     const std::function<uint64_t(uint64_t)>& conv_byte_order)
318 {
319     UHD_ASSERT_THROW(num_elems >= 2);
320 
321     // Read first word
322     uint64_t word0 = conv_byte_order(buff[0]);
323     src_epid       = get_field_u64<uint16_t>(word0, SRC_EPID_OFFSET, SRC_EPID_WIDTH);
324     op_code        = get_field_u64<strc_op_code_t>(word0, OP_CODE_OFFSET, OP_CODE_WIDTH);
325     op_data        = get_field_u64<uint8_t>(word0, OP_DATA_OFFSET, OP_DATA_WIDTH);
326     num_pkts       = get_field_u64<uint64_t>(word0, NUM_PKTS_OFFSET, NUM_PKTS_WIDTH);
327     // Read second word
328     num_bytes = conv_byte_order(buff[1]);
329 }
330 
get_length() const331 size_t strc_payload::get_length() const
332 {
333     return 2;
334 }
335 
operator ==(const strc_payload & rhs) const336 bool strc_payload::operator==(const strc_payload& rhs) const
337 {
338     return (src_epid == rhs.src_epid) && (op_code == rhs.op_code)
339            && (op_data == rhs.op_data) && (num_pkts == rhs.num_pkts)
340            && (num_bytes == rhs.num_bytes);
341 }
342 
to_string() const343 const std::string strc_payload::to_string() const
344 {
345     return str(boost::format("strc_payload{src_epid:%lu, op_code:%d, op_data:0x%x, "
346                              "num_pkts:%lu, num_bytes:%lu}\n")
347                % src_epid % int(op_code) % int(op_data) % num_pkts % num_bytes);
348 }
349 
350 //----------------------------------------------------
351 // CHDR Management Payload
352 //----------------------------------------------------
353 
to_string() const354 const std::string mgmt_op_t::to_string() const
355 {
356     std::stringstream stream;
357     switch (get_op_code()) {
358         case mgmt_op_t::MGMT_OP_NOP:
359             stream << "NOP";
360             break;
361         case mgmt_op_t::MGMT_OP_ADVERTISE:
362             stream << "ADVERTISE";
363             break;
364         case mgmt_op_t::MGMT_OP_SEL_DEST:
365             stream << "SEL_DEST";
366             break;
367         case mgmt_op_t::MGMT_OP_RETURN:
368             stream << "RETURN";
369             break;
370         case mgmt_op_t::MGMT_OP_INFO_REQ:
371             stream << "INFO_REQ";
372             break;
373         case mgmt_op_t::MGMT_OP_INFO_RESP:
374             stream << "INFO_RESP";
375             break;
376         case mgmt_op_t::MGMT_OP_CFG_WR_REQ:
377             stream << "CFG_WR_REQ";
378             break;
379         case mgmt_op_t::MGMT_OP_CFG_RD_REQ:
380             stream << "CFG_RD_REQ";
381             break;
382         case mgmt_op_t::MGMT_OP_CFG_RD_RESP:
383             stream << "CFG_RD_RESP";
384             break;
385         default:
386             UHD_THROW_INVALID_CODE_PATH();
387     }
388     stream << ": ";
389     switch (get_op_code()) {
390         case mgmt_op_t::MGMT_OP_SEL_DEST: {
391             mgmt_op_t::sel_dest_payload payload = static_cast<uint64_t>(get_op_payload());
392             stream << "dest:" << payload.dest;
393             break;
394         }
395         case mgmt_op_t::MGMT_OP_CFG_WR_REQ:
396         case mgmt_op_t::MGMT_OP_CFG_RD_REQ:
397         case mgmt_op_t::MGMT_OP_CFG_RD_RESP: {
398             mgmt_op_t::cfg_payload payload = static_cast<uint64_t>(get_op_payload());
399             stream << str(
400                 boost::format("addr:0x%08x, data:0x%08x") % payload.addr % payload.data);
401             break;
402         }
403         case mgmt_op_t::MGMT_OP_INFO_REQ:
404         case mgmt_op_t::MGMT_OP_INFO_RESP: {
405             mgmt_op_t::node_info_payload payload =
406                 static_cast<uint64_t>(get_op_payload());
407             stream << "device_id:" << payload.device_id
408                    << ", node_type:" << payload.node_type
409                    << ", node_inst:" << payload.node_inst
410                    << ", ext_info:" << payload.ext_info;
411             break;
412         }
413         default: {
414             stream << "-";
415             break;
416         }
417     }
418     stream << "\n";
419     return stream.str();
420 }
421 
422 //! Serialize this hop into a list of 64-bit words
serialize(std::vector<uint64_t> & target,const std::function<uint64_t (uint64_t)> & conv_byte_order,const size_t padding_size) const423 size_t mgmt_hop_t::serialize(std::vector<uint64_t>& target,
424     const std::function<uint64_t(uint64_t)>& conv_byte_order,
425     const size_t padding_size) const
426 {
427     for (size_t i = 0; i < get_num_ops(); i++) {
428         target.push_back(
429             conv_byte_order((static_cast<uint64_t>(_ops.at(i).get_op_payload()) << 16)
430                             | (static_cast<uint64_t>(_ops.at(i).get_op_code()) << 8)
431                             | (static_cast<uint64_t>(get_num_ops() - i - 1) << 0)));
432         for (size_t j = 0; j < padding_size; j++) {
433             target.push_back(uint64_t(0));
434         }
435     }
436     return get_num_ops();
437 }
438 
439 //! Deserialize this hop into from list of 64-bit words
deserialize(std::list<uint64_t> & src,const std::function<uint64_t (uint64_t)> & conv_byte_order,const size_t padding_size)440 void mgmt_hop_t::deserialize(std::list<uint64_t>& src,
441     const std::function<uint64_t(uint64_t)>& conv_byte_order,
442     const size_t padding_size)
443 {
444     _ops.clear();
445     size_t ops_remaining = 0;
446     do {
447         // TODO: Change this to a legit exception
448         UHD_ASSERT_THROW(!src.empty());
449 
450         uint64_t op_word = conv_byte_order(src.front());
451         ops_remaining    = static_cast<size_t>(op_word & 0xFF);
452         mgmt_op_t op(static_cast<mgmt_op_t::op_code_t>((op_word >> 8) & 0xFF),
453             static_cast<uint64_t>((op_word >> 16)));
454         _ops.push_back(op);
455         src.pop_front();
456         for (size_t i = 0; i < padding_size; i++) {
457             src.pop_front();
458         }
459 
460     } while (ops_remaining > 0);
461 }
462 
to_string() const463 const std::string mgmt_hop_t::to_string() const
464 {
465     std::stringstream stream;
466     for (size_t op_index = 0; op_index < get_num_ops(); op_index++) {
467         if (op_index == 0) {
468             stream << " -> ";
469         } else {
470             stream << "    ";
471         }
472         const mgmt_op_t& op = get_op(op_index);
473         stream << op.to_string();
474     }
475     return stream.str();
476 }
477 
populate_header(chdr_header & header) const478 void mgmt_payload::populate_header(chdr_header& header) const
479 {
480     header.set_pkt_type(PKT_TYPE_MGMT);
481     header.set_eob(false);
482     header.set_eov(false);
483     header.set_num_mdata(0);
484     header.set_vc(0);
485     header.set_dst_epid(0);
486 }
487 
serialize(uint64_t * buff,size_t max_size_bytes,const std::function<uint64_t (uint64_t)> & conv_byte_order) const488 size_t mgmt_payload::serialize(uint64_t* buff,
489     size_t max_size_bytes,
490     const std::function<uint64_t(uint64_t)>& conv_byte_order) const
491 {
492     std::vector<uint64_t> target;
493     // Insert header
494     target.push_back(conv_byte_order(
495         (static_cast<uint64_t>(_protover) << 48)
496         | (static_cast<uint64_t>(static_cast<uint8_t>(_chdr_w) & 0x7) << 45)
497         | (static_cast<uint64_t>(get_num_hops() & 0x3FF) << 16)
498         | (static_cast<uint64_t>(_src_epid) << 0)));
499     // According to the RFNoC specification section 2.2.6, the MSBs are 0 for
500     // all widths greater than 64. This logic adds the padding.
501     for (size_t i = 0; i < _padding_size; i++) {
502         target.push_back(uint64_t(0));
503     }
504 
505     // Insert data from each hop
506     for (const auto& hop : _hops) {
507         hop.serialize(target, conv_byte_order, _padding_size);
508     }
509     UHD_ASSERT_THROW(target.size() <= max_size_bytes);
510 
511     // We use a vector and copy just for ease of implementation
512     // These transactions are not performance critical
513     std::copy(target.begin(), target.end(), buff);
514     return (target.size() * sizeof(uint64_t));
515 }
516 
deserialize(const uint64_t * buff,size_t num_elems,const std::function<uint64_t (uint64_t)> & conv_byte_order)517 void mgmt_payload::deserialize(const uint64_t* buff,
518     size_t num_elems,
519     const std::function<uint64_t(uint64_t)>& conv_byte_order)
520 {
521     UHD_ASSERT_THROW(num_elems > 1);
522 
523     // We use a list and copy just for ease of implementation
524     // These transactions are not performance critical
525     std::list<uint64_t> src_list(buff, buff + (num_elems * (_padding_size + 1)));
526 
527     _hops.clear();
528 
529     // Deframe the header
530     uint64_t hdr = conv_byte_order(src_list.front());
531     _hops.resize(static_cast<size_t>((hdr >> 16) & 0x3FF));
532     _src_epid     = static_cast<sep_id_t>(hdr & 0xFFFF);
533     _chdr_w       = static_cast<chdr_w_t>((hdr >> 45) & 0x7);
534     _protover     = static_cast<uint16_t>((hdr >> 48) & 0xFFFF);
535     _padding_size = (chdr_w_to_bits(_chdr_w) / 64) - 1;
536     src_list.pop_front();
537     // According to the RFNoC specification section 2.2.6, the MSBs are 0 for
538     // all widths greater than 64. This logic removes the padding.
539     for (size_t i = 0; i < _padding_size; i++) {
540         src_list.pop_front();
541     }
542 
543     // Populate all hops
544     for (size_t i = 0; i < get_num_hops(); i++) {
545         _hops[i].deserialize(src_list, conv_byte_order, _padding_size);
546     }
547 }
548 
get_length() const549 size_t mgmt_payload::get_length() const
550 {
551     size_t length = 1 + _padding_size; /* header */
552     for (const auto& hop : this->_hops) {
553         length += hop.get_num_ops() + _padding_size;
554     }
555     return length;
556 }
557 
to_string() const558 const std::string mgmt_payload::to_string() const
559 {
560     return str(boost::format(
561                    "mgmt_payload{src_epid:%lu, chdr_w:%d, protover:0x%x, num_hops:%lu}\n")
562                % _src_epid % int(_chdr_w) % _protover % _hops.size());
563 }
564 
hops_to_string() const565 const std::string mgmt_payload::hops_to_string() const
566 {
567     std::stringstream stream;
568     for (size_t hop_index = 0; hop_index < get_num_hops(); hop_index++) {
569         const mgmt_hop_t& hop = get_hop(hop_index);
570         stream << hop.to_string();
571     }
572     return stream.str();
573 }
574 
575 
operator ==(const mgmt_payload & rhs) const576 bool mgmt_payload::operator==(const mgmt_payload& rhs) const
577 {
578     return (_src_epid == rhs._src_epid) && (_protover == rhs._protover)
579            && (_chdr_w == rhs._chdr_w) && (_hops == rhs._hops)
580            && (_padding_size == rhs._padding_size);
581 }
582