Line data Source code
1 : /**
2 : * @file WIB2Frame_test.cxx WIB2Frame class Unit Tests
3 : *
4 : * This is part of the DUNE DAQ Application Framework, copyright 2020.
5 : * Licensing/copyright details are in the COPYING file that you should have
6 : * received with this code.
7 : */
8 :
9 : #include "fddetdataformats/WIB2Frame.hpp"
10 :
11 : /**
12 : * @brief Name of this test module
13 : */
14 : #define BOOST_TEST_MODULE WIB2Frame_test // NOLINT
15 :
16 : #include "boost/test/data/test_case.hpp"
17 : #include "boost/test/unit_test.hpp"
18 :
19 : #include <cstdint>
20 : #include <string>
21 : #include <vector>
22 :
23 : // namespace wib2unpack contains functions for manipulating WIB2
24 : // frames in the format described by EDMS document 2088713:
25 : // (https://edms.cern.ch/document/2088713/4). The code here is taken
26 : // from unpack.h in the dune-wib-firmware repository, written by Ben
27 : // Land, and lightly modified to be closer to DUNE DAQ software
28 : // conventions. It is used here as an independently-written check of the
29 : // overlay code in WIB2Frame.hpp
30 : namespace wib2unpack {
31 :
32 : // Words in the binary format of the FELIX frame14 from the WIB
33 : typedef struct
34 : {
35 : uint32_t start_frame; // NOLINT(build/unsigned)
36 : uint32_t wib_pre[4]; // NOLINT(build/unsigned)
37 : uint32_t femb_a_seg[56]; // NOLINT(build/unsigned)
38 : uint32_t femb_b_seg[56]; // NOLINT(build/unsigned)
39 : uint32_t wib_post[2]; // NOLINT(build/unsigned)
40 : uint32_t idle_frame; // NOLINT(build/unsigned)
41 : } frame14;
42 :
43 : // Samples from the U, V, X channels in a femb_*_seg of a frame as 16bit arrays
44 : typedef struct
45 : {
46 : uint16_t u[40], v[40], x[48]; // NOLINT(build/unsigned)
47 : } __attribute__((packed)) femb_data;
48 :
49 : // Byte-aligned unpacked values in a felix_frame
50 : typedef struct
51 : {
52 : uint8_t link_mask, femb_valid, fiber_num, wib_num, frame_version, crate_num; // NOLINT(build/unsigned)
53 : uint32_t wib_data; // NOLINT(build/unsigned)
54 : uint64_t timestamp; // NOLINT(build/unsigned)
55 : femb_data femb[2];
56 : uint32_t crc20; // NOLINT(build/unsigned)
57 : uint16_t flex12; // NOLINT(build/unsigned)
58 : uint32_t flex24; // NOLINT(build/unsigned)
59 : } frame14_unpacked;
60 :
61 : // Deframed data, where channels or u,v,x are time ordered uint16 samples for each channel
62 : typedef struct
63 : {
64 : size_t samples;
65 : std::vector<uint16_t> channels[2][128]; // NOLINT(build/unsigned)
66 : std::vector<uint64_t> timestamp; // NOLINT(build/unsigned)
67 : uint8_t crate_num, wib_num; // NOLINT(build/unsigned)
68 : } channel_data;
69 :
70 : typedef struct
71 : {
72 : size_t samples;
73 : std::vector<uint16_t> u[2][40]; // NOLINT(build/unsigned)
74 : std::vector<uint16_t> v[2][40]; // NOLINT(build/unsigned)
75 : std::vector<uint16_t> x[2][48]; // NOLINT(build/unsigned)
76 : std::vector<uint64_t> timestamp; // NOLINT(build/unsigned)
77 : uint8_t crate_num, wib_num; // NOLINT(build/unsigned)
78 : } uvx_data;
79 :
80 : void
81 0 : unpack14(const uint32_t* packed, uint16_t* unpacked) // NOLINT(build/unsigned)
82 : {
83 0 : for (size_t i = 0; i < 128; i++) { // i == n'th U,V,X value
84 0 : const size_t low_bit = i * 14;
85 0 : const size_t low_word = low_bit / 32;
86 0 : const size_t high_bit = (i + 1) * 14 - 1;
87 0 : const size_t high_word = high_bit / 32;
88 : // printf("word %li :: low %li (%li[%li]) high %li
89 : // (%li[%li])\n",i,low_bit,low_word,low_bit%32,high_bit,high_word,high_bit%32);
90 0 : if (low_word == high_word) { // all the bits are in the same word
91 0 : unpacked[i] = (packed[low_word] >> (low_bit % 32)) & 0x3FFF;
92 : } else { // some of the bits are in the next word
93 0 : size_t high_off = high_word * 32 - low_bit;
94 : // printf("pre_mask 0x%X post_mask 0x%X\n", (0x3FFF >> (14-high_off)), ((0x3FFF << high_off) & 0x3FFF) );
95 0 : unpacked[i] = (packed[low_word] >> (low_bit % 32)) & (0x3FFF >> (14 - high_off));
96 0 : unpacked[i] |= (packed[high_word] << high_off) & ((0x3FFF << high_off) & 0x3FFF);
97 : }
98 : }
99 0 : }
100 :
101 : void
102 200 : repack14(const uint16_t* unpacked, uint32_t* packed) // NOLINT(build/unsigned)
103 : {
104 : // zero packed data first
105 11400 : for (size_t i = 0; i < 56; i++)
106 11200 : packed[i] = 0;
107 25800 : for (size_t i = 0; i < 128; i++) { // i == n'th U,V,X value
108 25600 : const size_t low_bit = i * 14;
109 25600 : const size_t low_word = low_bit / 32;
110 25600 : const size_t high_bit = (i + 1) * 14 - 1;
111 25600 : const size_t high_word = high_bit / 32;
112 : // printf("word %li :: low %li (%li[%li]) high %li
113 : // (%li[%li])\n",i,low_bit,low_word,low_bit%32,high_bit,high_word,high_bit%32);
114 25600 : if (low_word == high_word) { // all the bits are in the same word
115 16000 : packed[low_word] |= (unpacked[i] & 0x3FFF) << (low_bit % 32);
116 : } else { // some of the bits are in the next word
117 9600 : size_t high_off = high_word * 32 - low_bit;
118 : // printf("pre_mask 0x%X post_mask 0x%X\n", (0x3FFF >> (14-high_off)), ((0x3FFF << high_off) & 0x3FFF) );
119 9600 : packed[low_word] |= (unpacked[i] & (0x3FFF >> (14 - high_off))) << (low_bit % 32);
120 9600 : packed[high_word] |= (unpacked[i] & ((0x3FFF << high_off) & 0x3FFF)) >> high_off;
121 : }
122 : }
123 200 : }
124 :
125 : void
126 0 : unpack_frame(const frame14* frame, frame14_unpacked* data)
127 : {
128 0 : data->crate_num = frame->wib_pre[0] & 0xFF;
129 0 : data->frame_version = (frame->wib_pre[0] >> 8) & 0xF;
130 0 : data->wib_num = (frame->wib_pre[0] >> 12) & 0x7;
131 0 : data->fiber_num = (frame->wib_pre[0] >> 15) & 0x1;
132 0 : data->femb_valid = (frame->wib_pre[0] >> 16) & 0x3;
133 0 : data->link_mask = (frame->wib_pre[0] >> 18) & 0xFF;
134 :
135 0 : data->wib_data = frame->wib_pre[1];
136 :
137 0 : data->timestamp = (static_cast<uint64_t>(frame->wib_pre[3]) << 32) | // NOLINT(build/unsigned)
138 0 : (static_cast<uint64_t>(frame->wib_pre[2])); // NOLINT(build/unsigned)
139 :
140 0 : unpack14(frame->femb_a_seg, reinterpret_cast<uint16_t*>(&data->femb[0])); // NOLINT
141 0 : unpack14(frame->femb_b_seg, reinterpret_cast<uint16_t*>(&data->femb[1])); // NOLINT
142 :
143 0 : data->crc20 = frame->wib_post[0] & 0xFFFFF;
144 0 : data->flex12 = (frame->wib_post[0] >> 20) & 0xFFF;
145 0 : data->flex24 = (frame->wib_post[1] >> 8) & 0xFFFFFF;
146 0 : }
147 :
148 : void
149 100 : repack_frame(frame14_unpacked* data, frame14* frame)
150 : {
151 100 : memset(frame, 0, sizeof(frame14)); // zero out frame
152 100 : frame->start_frame = 0x3C;
153 100 : frame->wib_pre[0] |= (data->crate_num & 0xFF);
154 100 : frame->wib_pre[0] |= (data->frame_version & 0xF) << 8;
155 100 : frame->wib_pre[0] |= (data->wib_num & 0x7) << 12;
156 100 : frame->wib_pre[0] |= (data->fiber_num & 0x1) << 15;
157 100 : frame->wib_pre[0] |= (data->femb_valid & 0x3) << 16;
158 100 : frame->wib_pre[0] |= (data->link_mask & 0xFF) << 18;
159 :
160 100 : frame->wib_pre[1] = 0xbabeface;
161 :
162 100 : frame->wib_pre[2] = static_cast<uint32_t>(data->timestamp & 0xFFFFFFFF); // NOLINT(build/unsigned)
163 100 : frame->wib_pre[3] = static_cast<uint32_t>((data->timestamp >> 32) & 0xFFFFFFFF); // NOLINT(build/unsigned)
164 :
165 100 : repack14(reinterpret_cast<uint16_t*>(&data->femb[0]), frame->femb_a_seg); // NOLINT
166 100 : repack14(reinterpret_cast<uint16_t*>(&data->femb[1]), frame->femb_b_seg); // NOLINT
167 :
168 100 : frame->wib_post[0] |= data->crc20 & 0xFFFFF; // FIXME calculate crc of something
169 100 : frame->wib_post[0] |= (data->flex12 & 0xFFF) << 20;
170 100 : frame->wib_post[1] |= 0xDC;
171 100 : frame->wib_post[1] |= (data->flex24 & 0xFFFFFF) << 8;
172 100 : frame->idle_frame = 0xBC;
173 100 : }
174 :
175 : size_t u_to_ch[40] = { 20, 59, 19, 60, 18, 61, 17, 62, 16, 63, 4, 43, 3, 44, 2, 45, 1, 46, 0, 47,
176 : 68, 107, 67, 108, 66, 109, 65, 110, 64, 111, 84, 123, 83, 124, 82, 125, 81, 126, 80, 127 };
177 : size_t v_to_ch[40] = { 25, 54, 24, 55, 23, 56, 22, 57, 21, 58, 9, 38, 8, 39, 7, 40, 6, 41, 5, 42,
178 : 73, 102, 72, 103, 71, 104, 70, 105, 69, 106, 89, 118, 88, 119, 87, 120, 86, 121, 85, 122 };
179 : size_t x_to_ch[48] = { 31, 48, 30, 49, 29, 50, 28, 51, 27, 52, 26, 53, 15, 32, 14, 33,
180 : 13, 34, 12, 35, 11, 36, 10, 37, 79, 96, 78, 97, 77, 98, 76, 99,
181 : 75, 100, 74, 101, 95, 112, 94, 113, 93, 114, 92, 115, 91, 116, 90, 117 };
182 :
183 : void
184 0 : deframe_data(const frame14* frame_buf, size_t nframes, channel_data& data)
185 : {
186 0 : data.samples = nframes;
187 0 : for (size_t i = 0; i < 128; i++) {
188 0 : data.channels[0][i].resize(nframes);
189 0 : data.channels[1][i].resize(nframes);
190 : }
191 0 : data.timestamp.resize(nframes);
192 : frame14_unpacked frame_data;
193 0 : for (size_t i = 0; i < nframes; i++) {
194 0 : unpack_frame(frame_buf + i, &frame_data);
195 0 : for (size_t j = 0; j < 48; j++) {
196 0 : int k;
197 0 : if (j < 40) {
198 0 : k = u_to_ch[j];
199 0 : data.channels[0][k][i] = frame_data.femb[0].u[j];
200 0 : data.channels[1][k][i] = frame_data.femb[1].u[j];
201 0 : k = v_to_ch[j];
202 0 : data.channels[0][k][i] = frame_data.femb[0].v[j];
203 0 : data.channels[1][k][i] = frame_data.femb[1].v[j];
204 : }
205 0 : k = x_to_ch[j];
206 0 : data.channels[0][k][i] = frame_data.femb[0].x[j];
207 0 : data.channels[1][k][i] = frame_data.femb[1].x[j];
208 : }
209 0 : data.timestamp[i] = frame_data.timestamp;
210 0 : ;
211 : }
212 0 : data.crate_num = frame_data.crate_num;
213 0 : data.wib_num = frame_data.wib_num;
214 0 : }
215 :
216 : void
217 0 : deframe_data(const frame14* frame_buf, size_t nframes, uvx_data& data)
218 : {
219 0 : data.samples = nframes;
220 0 : for (size_t i = 0; i < 48; i++) {
221 0 : if (i < 40) {
222 0 : data.u[0][i].resize(nframes);
223 0 : data.v[0][i].resize(nframes);
224 0 : data.u[1][i].resize(nframes);
225 0 : data.v[1][i].resize(nframes);
226 : }
227 0 : data.x[0][i].resize(nframes);
228 0 : data.x[1][i].resize(nframes);
229 : }
230 0 : data.timestamp.resize(nframes);
231 : frame14_unpacked frame_data;
232 0 : for (size_t i = 0; i < nframes; i++) {
233 0 : unpack_frame(frame_buf + i, &frame_data);
234 0 : for (size_t j = 0; j < 48; j++) {
235 0 : if (j < 40) {
236 0 : data.u[0][j][i] = frame_data.femb[0].u[j];
237 0 : data.v[0][j][i] = frame_data.femb[0].v[j];
238 0 : data.u[1][j][i] = frame_data.femb[1].u[j];
239 0 : data.v[1][j][i] = frame_data.femb[1].v[j];
240 : }
241 0 : data.x[0][j][i] = frame_data.femb[0].x[j];
242 0 : data.x[1][j][i] = frame_data.femb[1].x[j];
243 : }
244 0 : data.timestamp[i] = frame_data.timestamp;
245 : }
246 0 : data.crate_num = frame_data.crate_num;
247 0 : data.wib_num = frame_data.wib_num;
248 : // FIXME frame_version femb_valid link_mask fiber_num
249 0 : }
250 :
251 : void
252 0 : reframe_data(frame14* frame_buf, size_t nframes, const channel_data& data)
253 : {
254 0 : frame14_unpacked frame_data;
255 0 : for (size_t i = 0; i < nframes; i++) {
256 0 : for (size_t j = 0; j < 48; j++) {
257 0 : int k;
258 0 : if (j < 40) {
259 0 : k = u_to_ch[j];
260 0 : frame_data.femb[0].u[j] = data.channels[0][k][i];
261 0 : frame_data.femb[1].u[j] = data.channels[1][k][i];
262 0 : k = v_to_ch[j];
263 0 : frame_data.femb[0].v[j] = data.channels[0][k][i];
264 0 : frame_data.femb[1].v[j] = data.channels[1][k][i];
265 : }
266 0 : k = x_to_ch[j];
267 0 : frame_data.femb[0].x[j] = data.channels[0][k][i];
268 0 : frame_data.femb[1].x[j] = data.channels[1][k][i];
269 : }
270 0 : frame_data.timestamp = data.timestamp[i];
271 0 : frame_data.crate_num = data.crate_num;
272 0 : frame_data.wib_num = data.wib_num;
273 : // FIXME frame_version femb_valid link_mask fiber_num
274 0 : repack_frame(&frame_data, frame_buf + i);
275 : }
276 0 : }
277 :
278 : void
279 0 : fake_data(frame14* buffer, size_t nframes)
280 : {
281 0 : channel_data data;
282 0 : data.samples = nframes;
283 0 : data.wib_num = 37;
284 0 : data.crate_num = 57;
285 0 : for (size_t i = 0; i < 128; i++) {
286 0 : data.channels[0][i].resize(nframes);
287 0 : data.channels[1][i].resize(nframes);
288 0 : int phase = rand() % 500; // NOLINT(runtime/threadsafe_fn)
289 0 : for (size_t j = 0; j < nframes; j++) {
290 0 : data.channels[0][i][j] = 16384.0 * (sin(2 * 3.1415926 * j / (i * 5 + 100.0) + phase) + 1.0) / 2.0;
291 0 : data.channels[1][i][j] = 16384.0 * (cos(2 * 3.1415926 * j / (i * 5 + 100.0) + phase) + 1.0) / 2.0;
292 : }
293 : }
294 0 : data.timestamp.resize(nframes);
295 0 : for (size_t i = 0; i < nframes; i++)
296 0 : data.timestamp[i] = i;
297 0 : reframe_data(buffer, nframes, data);
298 0 : }
299 :
300 : } // namespace wib2unpack
301 :
302 : using namespace dunedaq::fddetdataformats;
303 :
304 : typedef std::array<uint16_t, 256> vals_type; // NOLINT(build/unsigned)
305 :
306 : // Tell boost not to try to print vals_type objects with <<
307 : BOOST_TEST_DONT_PRINT_LOG_VALUE(vals_type)
308 :
309 : BOOST_AUTO_TEST_SUITE(WIB2Frame_test)
310 :
311 : std::vector<vals_type>
312 1 : make_vals()
313 : {
314 1 : std::vector<vals_type> ret;
315 :
316 1 : std::default_random_engine e1(10);
317 1 : std::uniform_int_distribution<int> uniform_dist(0, (1 << 14) - 1);
318 :
319 1 : const int n_fuzz = 100;
320 101 : for (int i = 0; i < n_fuzz; ++i) {
321 : std::array<uint16_t, 256> vals; // NOLINT(build/unsigned)
322 25700 : for (size_t j = 0; j < vals.size(); ++j) {
323 25600 : vals[j] = uniform_dist(e1);
324 : }
325 100 : ret.push_back(vals);
326 : }
327 :
328 1 : return ret;
329 0 : }
330 :
331 200 : BOOST_DATA_TEST_CASE(CompareToUnpack, boost::unit_test::data::make(make_vals()), vals)
332 : {
333 : // Check the WIB2 frame overlay code in WIB2Frame.hpp by comparing to
334 : // the output of the (independently-written) code in WIB2Unpack.hpp
335 : wib2unpack::frame14_unpacked unpacked;
336 : // Zero it out just to be on the safe side
337 100 : std::memset(&unpacked, 0, sizeof(wib2unpack::frame14_unpacked));
338 :
339 : wib2unpack::frame14 packed;
340 : // Zero it out just to be on the safe side
341 100 : std::memset(&packed, 0, sizeof(wib2unpack::frame14));
342 :
343 : // Fill the unpacked frame from the input `vals` array
344 100 : int counter = 0;
345 300 : for (int femb = 0; femb < 2; ++femb) {
346 8200 : for (int i = 0; i < 40; ++i) {
347 8000 : unpacked.femb[femb].u[i] = vals[counter];
348 8000 : counter++;
349 : }
350 8200 : for (int i = 0; i < 40; ++i) {
351 8000 : unpacked.femb[femb].v[i] = vals[counter];
352 8000 : counter++;
353 : }
354 9800 : for (int i = 0; i < 48; ++i) {
355 9600 : unpacked.femb[femb].x[i] = vals[counter];
356 9600 : counter++;
357 : }
358 : }
359 :
360 : // Create the packed array from the unpacked array
361 100 : wib2unpack::repack_frame(&unpacked, &packed);
362 :
363 : //WIB2Frame* wib2frame = reinterpret_cast<WIB2Frame*>(&packed); // NOLINT
364 100 : size_t num_errors = 0;
365 :
366 : // TODO: replace with some meaningul test in the future
367 : // for (int femb = 0; femb < 2; ++femb) {
368 : // for (int i = 0; i < 40; ++i) {
369 : // uint16_t gold = unpacked.femb[femb].u[i]; // NOLINT(build/unsigned)
370 : // uint16_t test = wib2frame->get_u(femb, i); // NOLINT(build/unsigned)
371 : // if (gold != test) {
372 : // num_errors++;
373 : // BOOST_CHECK_EQUAL(gold, test);
374 : // }
375 : // }
376 : // for (int i = 0; i < 40; ++i) {
377 : // uint16_t gold = unpacked.femb[femb].v[i]; // NOLINT(build/unsigned)
378 : // uint16_t test = wib2frame->get_v(femb, i); // NOLINT(build/unsigned)
379 : // if (gold != test) {
380 : // num_errors++;
381 : // BOOST_CHECK_EQUAL(gold, test);
382 : // }
383 : // }
384 : // for (int i = 0; i < 48; ++i) {
385 : // uint16_t gold = unpacked.femb[femb].x[i]; // NOLINT(build/unsigned)
386 : // uint16_t test = wib2frame->get_x(femb, i); // NOLINT(build/unsigned)
387 : // if (gold != test) {
388 : // num_errors++;
389 : // BOOST_CHECK_EQUAL(gold, test);
390 : // }
391 : // }
392 : // } // loop over femb
393 :
394 100 : BOOST_REQUIRE_EQUAL(num_errors, 0);
395 100 : }
396 :
397 2 : BOOST_AUTO_TEST_CASE(WIB2Frame_ADCDataMutators)
398 : {
399 1 : std::random_device dev;
400 1 : std::mt19937 rng(dev());
401 1 : std::uniform_int_distribution<std::mt19937::result_type> dist(1,(1<<14)-1);
402 1 : std::vector<int> v;
403 257 : for(int i=0; i<256; i++) {
404 256 : v.push_back(dist(rng));
405 : }
406 :
407 1 : dunedaq::fddetdataformats::WIB2Frame wib2frame {};
408 257 : for(int i=0; i<256; i++) {
409 256 : wib2frame.set_adc(i, v[i]);
410 : }
411 :
412 257 : for(int i=0; i<256; i++) {
413 256 : BOOST_REQUIRE_EQUAL(wib2frame.get_adc(i), v[i]);
414 : }
415 :
416 1 : }
417 :
418 : BOOST_AUTO_TEST_SUITE_END()
|