1 //
2 // Copyright 2019 Ettus Research, a National Instruments Brand
3 //
4 // SPDX-License-Identifier: GPL-3.0-or-later
5 //
6
7 #include <uhdlib/rfnoc/chdr_packet_writer.hpp>
8 #include <cassert>
9 #include <functional>
10 #include <memory>
11
12 using namespace uhd;
13 using namespace uhd::rfnoc;
14 using namespace uhd::rfnoc::chdr;
15
16 chdr_packet_writer::~chdr_packet_writer() = default;
17
18 //------------------------------------------------------------
19 // chdr_packet
20 //------------------------------------------------------------
21 // endianness is the link endianness, not the host endianness
22 template <size_t chdr_w, endianness_t endianness>
23 class chdr_packet_impl : public chdr_packet_writer
24 {
25 public:
26 chdr_packet_impl() = delete;
chdr_packet_impl(size_t mtu_bytes)27 chdr_packet_impl(size_t mtu_bytes) : _mtu_bytes(mtu_bytes) {}
28 ~chdr_packet_impl() = default;
29
refresh(const void * pkt_buff) const30 virtual void refresh(const void* pkt_buff) const
31 {
32 assert(pkt_buff);
33 _pkt_buff = const_cast<uint64_t*>(reinterpret_cast<const uint64_t*>(pkt_buff));
34 _mdata_offset = _compute_mdata_offset(get_chdr_header());
35 }
36
refresh(void * pkt_buff,chdr_header & header,uint64_t timestamp=0)37 virtual void refresh(void* pkt_buff, chdr_header& header, uint64_t timestamp = 0)
38 {
39 assert(pkt_buff);
40 _pkt_buff = reinterpret_cast<uint64_t*>(pkt_buff);
41 _pkt_buff[0] = u64_from_host(header);
42 if (_has_timestamp(header)) {
43 _pkt_buff[1] = u64_from_host(timestamp);
44 }
45 _mdata_offset = _compute_mdata_offset(get_chdr_header());
46 }
47
update_payload_size(size_t payload_size_bytes)48 virtual void update_payload_size(size_t payload_size_bytes)
49 {
50 chdr_header header = get_chdr_header();
51 header.set_length(((_mdata_offset + header.get_num_mdata()) * chdr_w_bytes)
52 + payload_size_bytes);
53 _pkt_buff[0] = u64_from_host(header);
54 }
55
get_byte_order() const56 virtual endianness_t get_byte_order() const
57 {
58 return endianness;
59 }
60
get_mtu_bytes() const61 virtual size_t get_mtu_bytes() const
62 {
63 return _mtu_bytes;
64 }
65
get_chdr_header() const66 virtual chdr_header get_chdr_header() const
67 {
68 assert(_pkt_buff);
69 return std::move(chdr_header(u64_to_host(_pkt_buff[0])));
70 }
71
get_timestamp() const72 virtual boost::optional<uint64_t> get_timestamp() const
73 {
74 if (_has_timestamp(get_chdr_header())) {
75 // In a unit64_t buffer, the timestamp is always immediately after the header
76 // regardless of chdr_w.
77 return u64_to_host(_pkt_buff[1]);
78 } else {
79 return boost::none;
80 }
81 }
82
get_mdata_size() const83 virtual size_t get_mdata_size() const
84 {
85 return get_chdr_header().get_num_mdata() * chdr_w_bytes;
86 }
87
get_mdata_const_ptr() const88 virtual const void* get_mdata_const_ptr() const
89 {
90 return const_cast<void*>(
91 const_cast<chdr_packet_impl<chdr_w, endianness>*>(this)->get_mdata_ptr());
92 }
93
get_mdata_ptr()94 virtual void* get_mdata_ptr()
95 {
96 return reinterpret_cast<void*>(_pkt_buff + (chdr_w_stride * _mdata_offset));
97 }
98
get_payload_size() const99 virtual size_t get_payload_size() const
100 {
101 return get_chdr_header().get_length() - get_mdata_size()
102 - (chdr_w_bytes * _mdata_offset);
103 }
104
get_payload_const_ptr() const105 virtual const void* get_payload_const_ptr() const
106 {
107 return const_cast<void*>(
108 const_cast<chdr_packet_impl<chdr_w, endianness>*>(this)->get_payload_ptr());
109 }
110
get_payload_ptr()111 virtual void* get_payload_ptr()
112 {
113 return reinterpret_cast<void*>(
114 _pkt_buff
115 + (chdr_w_stride * (_mdata_offset + get_chdr_header().get_num_mdata())));
116 }
117
calculate_payload_offset(const packet_type_t pkt_type,const uint8_t num_mdata=0) const118 virtual size_t calculate_payload_offset(
119 const packet_type_t pkt_type, const uint8_t num_mdata = 0) const
120 {
121 chdr_header header;
122 header.set_pkt_type(pkt_type);
123 return (_compute_mdata_offset(header) + num_mdata) * chdr_w_bytes;
124 }
125
126 private:
_has_timestamp(const chdr_header & header) const127 inline bool _has_timestamp(const chdr_header& header) const
128 {
129 return (header.get_pkt_type() == PKT_TYPE_DATA_WITH_TS);
130 }
131
_compute_mdata_offset(const chdr_header & header) const132 inline size_t _compute_mdata_offset(const chdr_header& header) const
133 {
134 // The metadata offset depends on the chdr_w and whether we have a timestamp
135 if (chdr_w == 64) {
136 return _has_timestamp(header) ? 2 : 1;
137 } else {
138 return 1;
139 }
140 }
141
u64_to_host(uint64_t word)142 inline static uint64_t u64_to_host(uint64_t word)
143 {
144 return (endianness == ENDIANNESS_BIG) ? uhd::ntohx<uint64_t>(word)
145 : uhd::wtohx<uint64_t>(word);
146 }
147
u64_from_host(uint64_t word)148 inline static uint64_t u64_from_host(uint64_t word)
149 {
150 return (endianness == ENDIANNESS_BIG) ? uhd::htonx<uint64_t>(word)
151 : uhd::htowx<uint64_t>(word);
152 }
153
154 static const size_t chdr_w_bytes = (chdr_w / 8);
155 static const size_t chdr_w_stride = (chdr_w / 64);
156
157 // Packet state
158 const size_t _mtu_bytes = 0;
159 mutable uint64_t* _pkt_buff = nullptr;
160 mutable size_t _mdata_offset = 0;
161 };
162
chdr_packet_factory(chdr_w_t chdr_w,endianness_t endianness)163 chdr_packet_factory::chdr_packet_factory(chdr_w_t chdr_w, endianness_t endianness)
164 : _chdr_w(chdr_w), _endianness(endianness)
165 {
166 }
167
make_generic(size_t mtu_bytes) const168 chdr_packet_writer::uptr chdr_packet_factory::make_generic(size_t mtu_bytes) const
169 {
170 if (_endianness == ENDIANNESS_BIG) {
171 switch (_chdr_w) {
172 case CHDR_W_512:
173 return std::make_unique<chdr_packet_impl<512, ENDIANNESS_BIG>>(mtu_bytes);
174 case CHDR_W_256:
175 return std::make_unique<chdr_packet_impl<256, ENDIANNESS_BIG>>(mtu_bytes);
176 case CHDR_W_128:
177 return std::make_unique<chdr_packet_impl<128, ENDIANNESS_BIG>>(mtu_bytes);
178 case CHDR_W_64:
179 return std::make_unique<chdr_packet_impl<64, ENDIANNESS_BIG>>(mtu_bytes);
180 default:
181 assert(0);
182 }
183 } else {
184 switch (_chdr_w) {
185 case CHDR_W_512:
186 return std::make_unique<chdr_packet_impl<512, ENDIANNESS_LITTLE>>(
187 mtu_bytes);
188 case CHDR_W_256:
189 return std::make_unique<chdr_packet_impl<256, ENDIANNESS_LITTLE>>(
190 mtu_bytes);
191 case CHDR_W_128:
192 return std::make_unique<chdr_packet_impl<128, ENDIANNESS_LITTLE>>(
193 mtu_bytes);
194 case CHDR_W_64:
195 return std::make_unique<chdr_packet_impl<64, ENDIANNESS_LITTLE>>(
196 mtu_bytes);
197 default:
198 assert(0);
199 }
200 }
201 return chdr_packet_writer::uptr();
202 }
203
make_ctrl(size_t mtu_bytes) const204 chdr_ctrl_packet::uptr chdr_packet_factory::make_ctrl(size_t mtu_bytes) const
205 {
206 return std::make_unique<chdr_ctrl_packet>(make_generic(mtu_bytes));
207 }
208
make_strs(size_t mtu_bytes) const209 chdr_strs_packet::uptr chdr_packet_factory::make_strs(size_t mtu_bytes) const
210 {
211 return std::make_unique<chdr_strs_packet>(make_generic(mtu_bytes));
212 }
213
make_strc(size_t mtu_bytes) const214 chdr_strc_packet::uptr chdr_packet_factory::make_strc(size_t mtu_bytes) const
215 {
216 return std::make_unique<chdr_strc_packet>(make_generic(mtu_bytes));
217 }
218
make_mgmt(size_t mtu_bytes) const219 chdr_mgmt_packet::uptr chdr_packet_factory::make_mgmt(size_t mtu_bytes) const
220 {
221 return std::make_unique<chdr_mgmt_packet>(make_generic(mtu_bytes));
222 }
223