ChimeraTK-DeviceAccess 03.31.00
Loading...
Searching...
No Matches
testNumericAddressedBackendUnified.cpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: Deutsches Elektronen-Synchrotron DESY, MSK, ChimeraTK Project <chimeratk-support@desy.de>
2// SPDX-License-Identifier: LGPL-3.0-or-later
3
4#define BOOST_TEST_DYN_LINK
5// Define a name for the test module.
6#define BOOST_TEST_MODULE NumericAddressedBackendUnified
7// Only after defining the name include the unit test header.
8#include <boost/test/unit_test.hpp>
9using namespace boost::unit_test_framework;
10
11#include "BackendFactory.h"
12#include "Device.h"
13#include "DummyBackend.h"
16#include "TransferGroup.h"
17#include "UnifiedBackendTest.h"
18
19namespace ChimeraTK {
20 using namespace ChimeraTK;
21}
22using namespace ChimeraTK;
23
24/* ===============================================================================================
25 * Despite its name, this test also qualifies as an unified test for the (Exception)DummyBackend.
26 * They are thinish implementations of the NumericAddressedBackend's internal interface.
27 * ==============================================================================================*/
28
29// Create a test suite which holds all your tests.
30BOOST_AUTO_TEST_SUITE(NumericAddressedBackendUnifiedTestSuite)
31
32/**********************************************************************************************************************/
33
34static std::string cdd("(ExceptionDummy:1?map=test3.map)");
35static auto exceptionDummy =
36 boost::dynamic_pointer_cast<ExceptionDummy>(BackendFactory::getInstance().createBackend(cdd));
37
38static std::string cddMuxed("(ExceptionDummy:1?map=muxedDataAccessor.jmap)");
39static auto exceptionDummyMuxed =
40 boost::dynamic_pointer_cast<ExceptionDummy>(BackendFactory::getInstance().createBackend(cddMuxed));
41
42static std::string cddBitRange("(ExceptionDummy:1?map=unifiedTest.jmap)");
43static auto exceptionDummyBitRange =
44 boost::dynamic_pointer_cast<ExceptionDummy>(BackendFactory::getInstance().createBackend(cddBitRange));
45
46static std::string cddBitRangeChannels("(dummy?map=bitRangeChannels.jmap)");
47static auto dummyBitRangeChannels =
48 boost::dynamic_pointer_cast<DummyBackend>(BackendFactory::getInstance().createBackend(cddBitRangeChannels));
49
50/**********************************************************************************************************************/
51
53 std::string path() { return "/Integers/signed32"; }
54 bool isWriteable() { return true; }
55 bool isReadable() { return true; }
57 size_t nChannels() { return 1; }
58 size_t nElementsPerChannel() { return 1; }
59 size_t writeQueueLength() { return std::numeric_limits<size_t>::max(); }
60 size_t nRuntimeErrorCases() { return 1; }
61 typedef int32_t minimumUserType;
63
64 static constexpr auto capabilities = TestCapabilities<>()
66 .disableAsyncReadInconsistency()
67 .disableSwitchReadOnly()
68 .disableSwitchWriteOnly()
69 .disableTestWriteNeverLosesData()
70 .enableTestRawTransfer();
71
73
74 template<typename Type>
75 std::vector<std::vector<Type>> generateValue([[maybe_unused]] bool raw = false) {
76 return {{acc + 3}};
77 }
78
79 template<typename UserType>
80 std::vector<std::vector<UserType>> getRemoteValue([[maybe_unused]] bool raw = false) {
81 return {{acc}};
82 }
83
84 void setRemoteValue() { acc = generateValue<minimumUserType>()[0][0]; }
85
86 void setForceRuntimeError(bool enable, size_t) {
87 exceptionDummy->throwExceptionRead = enable;
88 exceptionDummy->throwExceptionWrite = enable;
89 exceptionDummy->throwExceptionOpen = enable;
90 }
91};
92
94 std::string path() { return "/Integers/signed32_async"; }
95 bool isWriteable() { return false; }
96 bool isReadable() { return true; }
100 size_t nChannels() { return 1; }
101 size_t nElementsPerChannel() { return 1; }
102 size_t writeQueueLength() { return std::numeric_limits<size_t>::max(); }
103 size_t nRuntimeErrorCases() { return 1; }
104 typedef int32_t minimumUserType;
106
107 static constexpr auto capabilities = TestCapabilities<>()
109 .disableAsyncReadInconsistency()
110 .disableSwitchReadOnly()
111 .disableSwitchWriteOnly()
112 .disableTestWriteNeverLosesData()
113 .enableTestRawTransfer();
114
116
117 template<typename Type>
118 std::vector<std::vector<Type>> generateValue([[maybe_unused]] bool raw = false) {
119 return {{acc + 3}};
120 }
121
122 template<typename UserType>
123 std::vector<std::vector<UserType>> getRemoteValue([[maybe_unused]] bool raw = false) {
124 return {{acc}};
125 }
126
128 acc = generateValue<minimumUserType>()[0][0];
129 if(exceptionDummy->isOpen()) exceptionDummy->triggerInterrupt(6);
130 }
131
132 void forceAsyncReadInconsistency() { acc = generateValue<minimumUserType>()[0][0]; }
133
134 void setForceRuntimeError(bool enable, size_t) {
135 exceptionDummy->throwExceptionRead = enable;
136 exceptionDummy->throwExceptionWrite = enable;
137 exceptionDummy->throwExceptionOpen = enable;
138 if(exceptionDummy->isOpen()) exceptionDummy->triggerInterrupt(6);
139 }
140};
142 // Using the DUMMY_WRITEABLE register here since usually an async register is r/o implicitly
143 std::string path() { return "/Integers/signed32_async/DUMMY_WRITEABLE"; }
144 bool isWriteable() { return true; }
145 bool isReadable() { return true; }
149 size_t nChannels() { return 1; }
150 size_t nElementsPerChannel() { return 1; }
151 size_t writeQueueLength() { return std::numeric_limits<size_t>::max(); }
152 size_t nRuntimeErrorCases() { return 1; }
153 typedef int32_t minimumUserType;
155
156 static constexpr auto capabilities = TestCapabilities<>()
158 .disableAsyncReadInconsistency()
159 .disableSwitchReadOnly()
160 .disableSwitchWriteOnly()
161 .disableTestWriteNeverLosesData()
162 .enableTestRawTransfer();
163
164 DummyRegisterAccessor<int32_t> acc{exceptionDummy.get(), "", "/Integers/signed32_async"};
165
166 template<typename Type>
167 std::vector<std::vector<Type>> generateValue([[maybe_unused]] bool raw = false) {
168 return {{acc + 3}};
169 }
170
171 template<typename UserType>
172 std::vector<std::vector<UserType>> getRemoteValue([[maybe_unused]] bool raw = false) {
173 return {{acc}};
174 }
175
177 acc = generateValue<minimumUserType>()[0][0];
178 if(exceptionDummy->isOpen()) exceptionDummy->triggerInterrupt(6);
179 }
180
181 void forceAsyncReadInconsistency() { acc = generateValue<minimumUserType>()[0][0]; }
182
183 void setForceRuntimeError(bool enable, size_t) {
184 exceptionDummy->throwExceptionRead = enable;
185 exceptionDummy->throwExceptionWrite = enable;
186 exceptionDummy->throwExceptionOpen = enable;
187 if(exceptionDummy->isOpen()) exceptionDummy->triggerInterrupt(6);
188 }
189};
190
191/**********************************************************************************************************************/
192
193template<typename Derived, typename rawUserType>
195 Derived* derived{static_cast<Derived*>(this)};
196
197 bool isWriteable() { return true; }
198 bool isReadable() { return true; }
200 size_t nChannels() { return 1; }
201 size_t nElementsPerChannel() { return 1; }
202 size_t writeQueueLength() { return std::numeric_limits<size_t>::max(); }
203 size_t nRuntimeErrorCases() { return 1; }
204
205 // The padding (i.e. the bits of the 32 bit word which are not part of the register under test but of the neighbouring
206 // registers) needs to be checked to be unchanged in write operations. This test assumes that in between the calls to
207 // generateValue() and getRemoteValue() the padding bits are not *intentionally* changed (e.g. by testing another
208 // register in between). This is true for the UnifiedBackendTest right now.
209 // TODO: In future it may be better to foresee a "no-side-effect" test in the UnifiedBackendTest, which tests that
210 // other registers stay unchanged (caveat: need to specify which registers to look at! We have intentionally
211 // overlapping registers in this test which of course will change!)
212 int32_t lastPadding{0};
214
215 static constexpr auto capabilities = TestCapabilities<>()
217 .disableAsyncReadInconsistency()
218 .disableSwitchReadOnly()
219 .disableSwitchWriteOnly()
220 .disableTestWriteNeverLosesData()
221 .enableTestRawTransfer();
222
223 // This register shares the address space with all our test registers. It gives us direct access to the 4 byte
224 // address range, so we can test the correct placement of the unaligned values.
225 DummyRegisterRawAccessor acc{exceptionDummy, "", "/Integers/unsigned32"};
226
227 rawUserType get() { return (acc & derived->bitmask) >> derived->bitshift; }
228
229 void set(rawUserType val) {
230 acc &= ~derived->bitmask;
233 acc |= (val << derived->bitshift) & derived->bitmask;
234 }
235
236 // Type can be user type or raw type, depeding of the raw flag
237 template<typename Type>
238 std::vector<std::vector<Type>> generateValue(bool raw = false) {
239 rawUserType newRawValue = static_cast<rawUserType>(get() + derived->rawIncrement);
240 Type v = ChimeraTK::numeric::convert<Type>(raw ? newRawValue : (newRawValue * derived->rawToCooked));
241 /* std::cout << "generateValue " << derived->path() << " " << float(rawUserType(get() + derived->rawIncrement))
242 << " -> " << float(v) << std::endl; */
243 lastPadding = acc & ~derived->bitmask;
245 return {{v}};
246 }
247
248 // We use the same function to implement raw and cooked reading.
249 // Do the calculation on the target type, then decide on the padding error.
250 // rawToCooked might undo the ++ effect in case
251 // of rounding. When getting cooked, the ++ must happen on the UserType.
252 // Type can be UserType or RawType.
253 template<typename Type>
254 std::vector<std::vector<Type>> getRemoteValue(bool raw = false) {
255 Type v = get() * (raw ? 1 : derived->rawToCooked);
256 if((acc & ~derived->bitmask) != lastPadding) {
258 std::cerr << "getRemoteValue(): Padding data has changed. Test will be failed by returing a false remote value "
259 << "(off by one)." << std::endl;
261 }
262 v++;
263 return {{v}};
264 }
265 /* std::cout << "getRemoteValue " << derived->path() << " " << float(get()) << " -> " << float(v) << std::endl; */
266 return {{v}};
267 }
268
270 set(get() + derived->rawIncrement);
271 /* std::cout << "setRemoteValue " << derived->path() << " " << float(get()) << " -> " <<
272 getRemoteValue<float>()[0][0]
273 << std::endl; */
274 }
275
276 void setForceRuntimeError(bool enable, size_t) {
277 exceptionDummy->throwExceptionRead = enable;
278 exceptionDummy->throwExceptionWrite = enable;
279 exceptionDummy->throwExceptionOpen = enable;
280 }
281};
282
283/**********************************************************************************************************************/
284
285struct ShortRaw_signed16 : ShortRaw_base<ShortRaw_signed16, int16_t> {
286 std::string path() { return "/ShortRaw/signed16"; }
287 typedef int16_t minimumUserType;
289
290 int16_t rawToCooked = 1;
291 // The rawIncrements are chosen such that we generate different values for different variables, and to cover the
292 // specific value range with very few values (to check signed vs. unsigned). Uncomment the above couts in the base
293 // class to check the generated values.
294 int16_t rawIncrement = 17117;
295 int bitshift = 0;
296 int32_t bitmask = 0x0000FFFF;
297};
298
299/**********************************************************************************************************************/
300
301struct ShortRaw_unsigned16 : ShortRaw_base<ShortRaw_unsigned16, uint16_t> {
302 std::string path() { return "/ShortRaw/unsigned16"; }
303 typedef uint16_t minimumUserType;
304 typedef int16_t rawUserType;
305
306 int16_t rawToCooked = 1;
307 int16_t rawIncrement = 17119;
308 int bitshift = 16;
309 int32_t bitmask = 0xFFFF0000;
310};
311
312/**********************************************************************************************************************/
313
314struct ShortRaw_fixedPoint16_8u : ShortRaw_base<ShortRaw_fixedPoint16_8u, uint16_t> {
315 std::string path() { return "/ShortRaw/fixedPoint16_8u"; }
316 typedef float minimumUserType;
317 typedef int16_t rawUserType;
318
319 float rawToCooked = 1. / 256.;
320 int16_t rawIncrement = 17121;
321 int bitshift = 0;
322 int32_t bitmask = 0x0000FFFF;
323};
324
325/**********************************************************************************************************************/
326
327struct ShortRaw_fixedPoint16_8s : ShortRaw_base<ShortRaw_fixedPoint16_8s, int16_t> {
328 std::string path() { return "/ShortRaw/fixedPoint16_8s"; }
329 typedef float minimumUserType;
330 typedef int16_t rawUserType;
331
332 float rawToCooked = 1. / 256.;
333 int16_t rawIncrement = 17123;
334 int bitshift = 16;
335 int32_t bitmask = 0xFFFF0000;
336};
337
338/**********************************************************************************************************************/
339
340struct ByteRaw_signed8 : ShortRaw_base<ByteRaw_signed8, int8_t> {
341 std::string path() { return "/ByteRaw/signed8"; }
342 typedef int8_t minimumUserType;
343 typedef int8_t rawUserType;
344
345 int8_t rawToCooked = 1;
346 int8_t rawIncrement = 119;
347 int bitshift = 0;
348 int32_t bitmask = 0x000000FF;
349};
350
351/**********************************************************************************************************************/
352
353struct ByteRaw_unsigned8 : ShortRaw_base<ByteRaw_unsigned8, uint8_t> {
354 std::string path() { return "/ByteRaw/unsigned8"; }
355 typedef uint8_t minimumUserType;
356 typedef int8_t rawUserType;
357
358 uint8_t rawToCooked = 1;
359 int8_t rawIncrement = 121;
360 int bitshift = 8;
361 int32_t bitmask = 0x0000FF00;
362};
363
364/**********************************************************************************************************************/
365
366struct ByteRaw_fixedPoint8_4u : ShortRaw_base<ByteRaw_fixedPoint8_4u, uint8_t> {
367 std::string path() { return "/ByteRaw/fixedPoint8_4u"; }
368 typedef float minimumUserType;
369 typedef int8_t rawUserType;
370
371 float rawToCooked = 1. / 16.;
372 int8_t rawIncrement = 123;
373 int bitshift = 16;
374 int32_t bitmask = 0x00FF0000;
375};
376
377/**********************************************************************************************************************/
378
379struct ByteRaw_fixedPoint8_4s : ShortRaw_base<ByteRaw_fixedPoint8_4s, int8_t> {
380 std::string path() { return "/ByteRaw/fixedPoint8_4s"; }
381 typedef float minimumUserType;
382 typedef int8_t rawUserType;
383
384 float rawToCooked = 1. / 16.;
385 int8_t rawIncrement = 125;
386 int bitshift = 24;
387 int32_t bitmask = 0xFF000000;
388};
389
390/**********************************************************************************************************************/
391
392struct AsciiData {
393 static std::string path() { return "/Text/someAsciiData"; }
394 static bool isWriteable() { return true; }
395 static bool isReadable() { return true; }
396 using minimumUserType = std::string;
398
399 static size_t nChannels() { return 1; }
400 static size_t nElementsPerChannel() { return 1; }
401 static size_t writeQueueLength() { return std::numeric_limits<size_t>::max(); }
402 static size_t nRuntimeErrorCases() { return 1; }
403
404 static constexpr auto capabilities = TestCapabilities<>()
406 .disableAsyncReadInconsistency()
407 .disableSwitchReadOnly()
408 .disableSwitchWriteOnly()
409 .disableTestWriteNeverLosesData()
410 .disableTestRawTransfer();
411
412 // Note: The DummyRegisterAccessor does not yet work properly with none 32-bit word sizes.
413 DummyRegisterAccessor<uint32_t> acc{exceptionDummy.get(), "Text", "someAsciiData_as_i32"};
414
415 std::string textBase{"Some $%#@! characters "};
416 size_t counter{0};
417 const size_t asciiLength{35};
418
419 template<typename UserType>
420 std::vector<std::vector<UserType>> generateValue() {
421 return {{textBase + std::to_string(counter++)}};
422 }
423
424 template<typename UserType>
425 std::vector<std::vector<UserType>> getRemoteValue() {
426 std::vector<std::vector<UserType>> v{{""}};
427 for(size_t i = 0; i < asciiLength; ++i) {
428 size_t word = i / 4;
429 size_t byteInWord = i % 4;
430 char ch = char((acc[word] >> (byteInWord * 8U)) & 0xFF);
431 if(ch == 0) break;
432 v[0][0] += ch;
433 }
434 std::cout << "getRemoteValue: " << v[0][0] << std::endl;
435 return v;
436 }
437
439 auto v = generateValue<minimumUserType>();
440 for(size_t i = 0; i < acc.getNumberOfElements(); ++i) {
441 acc[i] = 0;
442 }
443 for(size_t i = 0; i < std::min(asciiLength, v[0][0].length()); ++i) {
444 char ch = v[0][0][i];
445 size_t word = i / 4;
446 size_t byteInWord = i % 4;
447 acc[word] = acc[word] | uint32_t(ch) << (byteInWord * 8U);
448 }
449 std::cout << "setRemoteValue: " << v[0][0] << std::endl;
450 }
451
452 static void setForceRuntimeError(bool enable, size_t) {
453 exceptionDummy->throwExceptionRead = enable;
454 exceptionDummy->throwExceptionWrite = enable;
455 exceptionDummy->throwExceptionOpen = enable;
456 }
457};
458
459/**********************************************************************************************************************/
460
462 std::string path() { return "/TEST/NODMA"; }
463 bool isWriteable() { return true; }
464 bool isReadable() { return true; }
466
467 size_t nChannels() { return 16; }
468 size_t nElementsPerChannel() { return 4; }
469 size_t writeQueueLength() { return std::numeric_limits<size_t>::max(); }
470 size_t nRuntimeErrorCases() { return 1; }
471 typedef uint16_t minimumUserType;
473
474 static constexpr auto capabilities = TestCapabilities<>()
476 .disableAsyncReadInconsistency()
477 .disableSwitchReadOnly()
478 .disableSwitchWriteOnly()
479 .disableTestWriteNeverLosesData()
480 .disableTestRawTransfer();
481
482 DummyMultiplexedRegisterAccessor<uint16_t> acc{exceptionDummyMuxed.get(), "TEST", "NODMA"};
483
484 template<typename UserType>
485 std::vector<std::vector<UserType>> generateValue() {
486 std::vector<std::vector<UserType>> v;
487 v.resize(nChannels());
488 for(size_t c = 0; c < nChannels(); ++c) {
489 for(size_t e = 0; e < nElementsPerChannel(); ++e) {
490 v[c].push_back(uint16_t(acc[c][e] + 7 * c + 3 * e));
491 }
492 }
493 return v;
494 }
495
496 template<typename UserType>
497 std::vector<std::vector<UserType>> getRemoteValue() {
498 std::vector<std::vector<UserType>> v;
499 v.resize(nChannels());
500 for(size_t c = 0; c < nChannels(); ++c) {
501 for(size_t e = 0; e < nElementsPerChannel(); ++e) {
502 v[c].push_back(acc[c][e]);
503 }
504 }
505 return v;
506 }
507
509 auto v = generateValue<minimumUserType>();
510 for(size_t c = 0; c < nChannels(); ++c) {
511 for(size_t e = 0; e < nElementsPerChannel(); ++e) {
512 acc[c][e] = v[c][e];
513 }
514 }
515 }
516
517 void setForceRuntimeError(bool enable, size_t) {
518 exceptionDummyMuxed->throwExceptionRead = enable;
519 exceptionDummyMuxed->throwExceptionWrite = enable;
520 exceptionDummyMuxed->throwExceptionOpen = enable;
521 }
522};
523
524// Read-only 1D channel slice of the 2D register TEST/NODMA. The setRemoteValue()/getRemoteValue()
525// pair exercises the strided access: values are written into the corresponding row of the full 2D
526// register (DummyMultiplexedRegisterAccessor, already pitch-aware) and read back through the slice's
527// DummyRegisterAccessor (whose getElement() must stride by the element pitch as well).
529 std::string path() { return "/TEST/NODMA.0"; }
530 bool isWriteable() { return false; }
531 bool isReadable() { return true; }
533 size_t nChannels() { return 1; }
534 size_t nElementsPerChannel() { return 4; }
535 size_t writeQueueLength() { return std::numeric_limits<size_t>::max(); }
536 size_t nRuntimeErrorCases() { return 1; }
537 typedef uint16_t minimumUserType;
538 typedef int16_t rawUserType;
539
540 static constexpr auto capabilities = TestCapabilities<>()
542 .disableAsyncReadInconsistency()
543 .disableSwitchReadOnly()
544 .disableSwitchWriteOnly()
545 .disableTestWriteNeverLosesData()
546 .enableTestRawTransfer();
547
548 static const size_t channelIndex = 0;
549 DummyRegisterAccessor<uint16_t> acc{exceptionDummyMuxed.get(), "TEST/NODMA", "0"};
550 DummyMultiplexedRegisterAccessor<uint16_t> full{exceptionDummyMuxed.get(), "TEST", "NODMA"};
551
552 template<typename UserType>
553 std::vector<std::vector<UserType>> generateValue([[maybe_unused]] bool raw = false) {
554 std::vector<std::vector<UserType>> v(1);
555 for(size_t e = 0; e < nElementsPerChannel(); ++e) {
556 v[0].push_back(uint16_t(full[channelIndex][e] + 37 + 11 * e));
557 }
558 return v;
559 }
560
561 template<typename UserType>
562 std::vector<std::vector<UserType>> getRemoteValue([[maybe_unused]] bool raw = false) {
563 std::vector<std::vector<UserType>> v(1);
564 for(size_t e = 0; e < nElementsPerChannel(); ++e) {
565 v[0].push_back(acc[e]);
566 }
567 return v;
568 }
569
571 auto v = generateValue<minimumUserType>();
572 for(size_t e = 0; e < nElementsPerChannel(); ++e) {
573 full[channelIndex][e] = v[0][e];
574 }
575 }
576
577 void setForceRuntimeError(bool enable, size_t) {
578 exceptionDummyMuxed->throwExceptionRead = enable;
579 exceptionDummyMuxed->throwExceptionWrite = enable;
580 exceptionDummyMuxed->throwExceptionOpen = enable;
581 }
582};
583
585 std::string path() { return "/TEST/NODMA.3"; }
586 bool isWriteable() { return false; }
587 bool isReadable() { return true; }
589 size_t nChannels() { return 1; }
590 size_t nElementsPerChannel() { return 4; }
591 size_t writeQueueLength() { return std::numeric_limits<size_t>::max(); }
592 size_t nRuntimeErrorCases() { return 1; }
593 typedef uint16_t minimumUserType;
594 typedef int16_t rawUserType;
595
596 static constexpr auto capabilities = TestCapabilities<>()
598 .disableAsyncReadInconsistency()
599 .disableSwitchReadOnly()
600 .disableSwitchWriteOnly()
601 .disableTestWriteNeverLosesData()
602 .enableTestRawTransfer();
603
604 // Channel "3" sits at byte offset 6 and is *not* thus 4-byte aligned, which the Dummy backdoor accessor
605 // (getElement, working on 32-bit words) requires for a strided 16-bit channel slice. Hence we do not use
606 // it in this case and instead rely solely on the muxed backdoor accessor.
607 static const size_t channelIndex = 3;
608 DummyMultiplexedRegisterAccessor<uint16_t> full{exceptionDummyMuxed.get(), "TEST", "NODMA"};
609
610 template<typename UserType>
611 std::vector<std::vector<UserType>> generateValue([[maybe_unused]] bool raw = false) {
612 std::vector<std::vector<UserType>> v(1);
613 for(size_t e = 0; e < nElementsPerChannel(); ++e) {
614 v[0].push_back(uint16_t(full[channelIndex][e] + 53 + 7 * e));
615 }
616 return v;
617 }
618
619 template<typename UserType>
620 std::vector<std::vector<UserType>> getRemoteValue([[maybe_unused]] bool raw = false) {
621 std::vector<std::vector<UserType>> v(1);
622 for(size_t e = 0; e < nElementsPerChannel(); ++e) {
623 v[0].push_back(full[channelIndex][e]);
624 }
625 return v;
626 }
627
629 auto v = generateValue<minimumUserType>();
630 for(size_t e = 0; e < nElementsPerChannel(); ++e) {
631 full[channelIndex][e] = v[0][e];
632 }
633 }
634
635 void setForceRuntimeError(bool enable, size_t) {
636 exceptionDummyMuxed->throwExceptionRead = enable;
637 exceptionDummyMuxed->throwExceptionWrite = enable;
638 exceptionDummyMuxed->throwExceptionOpen = enable;
639 }
640};
641
643 std::string path() { return "/TEST/NODMAASYNC"; }
644 bool isWriteable() { return false; }
645 bool isReadable() { return true; }
647
648 size_t nChannels() { return 16; }
649 size_t nElementsPerChannel() { return 4; }
650 size_t writeQueueLength() { return std::numeric_limits<size_t>::max(); }
651 size_t nRuntimeErrorCases() { return 1; }
652 typedef uint16_t minimumUserType;
654
655 static constexpr auto capabilities = TestCapabilities<>()
657 .disableAsyncReadInconsistency()
658 .disableSwitchReadOnly()
659 .disableSwitchWriteOnly()
660 .disableTestWriteNeverLosesData()
661 .disableTestRawTransfer();
662
663 DummyMultiplexedRegisterAccessor<uint16_t> acc{exceptionDummyMuxed.get(), "TEST", "NODMAASYNC"};
664
665 template<typename UserType>
666 std::vector<std::vector<UserType>> generateValue() {
667 std::vector<std::vector<UserType>> v;
668 v.resize(nChannels());
669 for(size_t c = 0; c < nChannels(); ++c) {
670 for(size_t e = 0; e < nElementsPerChannel(); ++e) {
671 v[c].push_back(uint16_t(acc[c][e] + 7 * c + 3 * e));
672 }
673 }
674 return v;
675 }
676
677 template<typename UserType>
678 std::vector<std::vector<UserType>> getRemoteValue() {
679 std::vector<std::vector<UserType>> v;
680 v.resize(nChannels());
681 for(size_t c = 0; c < nChannels(); ++c) {
682 for(size_t e = 0; e < nElementsPerChannel(); ++e) {
683 v[c].push_back(acc[c][e]);
684 }
685 }
686 return v;
687 }
688
690 auto v = generateValue<minimumUserType>();
691 for(size_t c = 0; c < nChannels(); ++c) {
692 for(size_t e = 0; e < nElementsPerChannel(); ++e) {
693 acc[c][e] = v[c][e];
694 }
695 }
696 if(exceptionDummyMuxed->isOpen()) exceptionDummyMuxed->triggerInterrupt(6);
697 }
698
699 void setForceRuntimeError(bool enable, size_t) {
700 exceptionDummyMuxed->throwExceptionRead = enable;
701 exceptionDummyMuxed->throwExceptionWrite = enable;
702 exceptionDummyMuxed->throwExceptionOpen = enable;
703 if(exceptionDummyMuxed->isOpen()) exceptionDummyMuxed->triggerInterrupt(6);
704 }
705};
706
708 std::string path() { return "/TEST/NODMAASYNC.0"; }
709 bool isWriteable() { return false; }
710 bool isReadable() { return true; }
714
715 size_t nChannels() { return 1; }
716 size_t nElementsPerChannel() { return 4; }
717 size_t writeQueueLength() { return std::numeric_limits<size_t>::max(); }
718 size_t nRuntimeErrorCases() { return 1; }
719 typedef uint16_t minimumUserType;
720 typedef int16_t rawUserType;
721
722 static constexpr auto capabilities = TestCapabilities<>()
724 .disableAsyncReadInconsistency()
725 .disableSwitchReadOnly()
726 .disableSwitchWriteOnly()
727 .disableTestWriteNeverLosesData()
728 .enableTestRawTransfer();
729
730 static const size_t channelIndex = 0;
731 DummyMultiplexedRegisterAccessor<uint16_t> full{exceptionDummyMuxed.get(), "TEST", "NODMAASYNC"};
732
733 template<typename UserType>
734 std::vector<std::vector<UserType>> generateValue([[maybe_unused]] bool raw = false) {
735 std::vector<std::vector<UserType>> v(1);
736 for(size_t e = 0; e < nElementsPerChannel(); ++e) {
737 v[0].push_back(uint16_t(full[channelIndex][e] + 41 + 7 * e));
738 }
739 return v;
740 }
741
742 template<typename UserType>
743 std::vector<std::vector<UserType>> getRemoteValue([[maybe_unused]] bool raw = false) {
744 std::vector<std::vector<UserType>> v(1);
745 for(size_t e = 0; e < nElementsPerChannel(); ++e) {
746 v[0].push_back(full[channelIndex][e]);
747 }
748 return v;
749 }
750
752 auto v = generateValue<minimumUserType>();
753 for(size_t e = 0; e < nElementsPerChannel(); ++e) {
754 full[channelIndex][e] = v[0][e];
755 }
756 if(exceptionDummyMuxed->isOpen()) exceptionDummyMuxed->triggerInterrupt(6);
757 }
758
759 void setForceRuntimeError(bool enable, size_t) {
760 exceptionDummyMuxed->throwExceptionRead = enable;
761 exceptionDummyMuxed->throwExceptionWrite = enable;
762 exceptionDummyMuxed->throwExceptionOpen = enable;
763 if(exceptionDummyMuxed->isOpen()) exceptionDummyMuxed->triggerInterrupt(6);
764 }
765};
766
767/**********************************************************************************************************************/
768
770 std::string path() { return "/TEST/FLOAT"; }
771 bool isWriteable() { return true; }
772 bool isReadable() { return true; }
774
775 size_t nChannels() { return 4; }
776 size_t nElementsPerChannel() { return 8; }
777 size_t nRuntimeErrorCases() { return 1; }
778 typedef float minimumUserType;
779 // typedef int32_t rawUserType;
780
781 static constexpr auto capabilities = TestCapabilities<>()
783 .disableAsyncReadInconsistency()
784 .disableSwitchReadOnly()
785 .disableSwitchWriteOnly()
786 .disableTestWriteNeverLosesData()
787 .disableTestRawTransfer();
788
789 DummyMultiplexedRegisterAccessor<float> acc{exceptionDummyMuxed.get(), "TEST", "FLOAT"};
790
791 template<typename UserType>
792 std::vector<std::vector<UserType>> generateValue() {
793 std::vector<std::vector<UserType>> v;
794 v.resize(nChannels());
795 for(uint32_t c = 0; c < nChannels(); ++c) {
796 for(uint32_t e = 0; e < nElementsPerChannel(); ++e) {
797 v[c].push_back(acc[c][e] + 0.7f * c + 3 * e);
798 }
799 }
800 return v;
801 }
802
803 template<typename UserType>
804 std::vector<std::vector<UserType>> getRemoteValue() {
805 std::vector<std::vector<UserType>> v;
806 v.resize(nChannels());
807 for(uint32_t c = 0; c < nChannels(); ++c) {
808 for(uint32_t e = 0; e < nElementsPerChannel(); ++e) {
809 v[c].push_back(acc[c][e]);
810 }
811 }
812 return v;
813 }
814
816 auto v = generateValue<minimumUserType>();
817 for(uint32_t c = 0; c < nChannels(); ++c) {
818 for(uint32_t e = 0; e < nElementsPerChannel(); ++e) {
819 acc[c][e] = v[c][e];
820 }
821 }
822 }
823
824 void setForceRuntimeError(bool enable, size_t) {
825 exceptionDummyMuxed->throwExceptionRead = enable;
826 exceptionDummyMuxed->throwExceptionWrite = enable;
827 exceptionDummyMuxed->throwExceptionOpen = enable;
828 }
829};
830
831/**********************************************************************************************************************/
832
833// Base descriptor with defaults, used for all register descriptor which use the base classes from this file
834template<typename Derived>
836 Derived* derived{static_cast<Derived*>(this)};
837
838 static constexpr auto capabilities = TestCapabilities<>()
840 .disableAsyncReadInconsistency()
841 .disableSwitchReadOnly()
842 .disableSwitchWriteOnly()
843 .disableTestWriteNeverLosesData()
844 .enableTestRawTransfer();
845
846 bool isWriteable() { return true; }
847 bool isReadable() { return true; }
848 bool isPush() { return false; }
854 size_t writeQueueLength() { return std::numeric_limits<size_t>::max(); }
855 size_t nRuntimeErrorCases() { return 1; }
856
857 void setForceRuntimeError(bool enable, size_t) {
858 auto& dummy = dynamic_cast<ExceptionDummy&>(derived->acc.getBackend());
859 dummy.throwExceptionRead = enable;
860 dummy.throwExceptionWrite = enable;
861 dummy.throwExceptionOpen = enable;
862 if(derived->isPush() && enable) {
863 dummy.triggerInterrupt(6);
864 }
865 }
866};
867
869template<typename Derived>
871 using RegisterDescriptorBase<Derived>::derived;
872
873 size_t nChannels() { return 1; }
874
875 size_t myOffset() { return 0; }
876
877 // T is always minimumUserType, but C++ doesn't allow to use Derived::minimumUserType here (circular dependency)
878 template<typename T, typename Traw>
879 T convertRawToCooked(Traw value) {
880 return static_cast<T>(value);
881 }
882
883 template<typename UserType>
884 void generateValueHook(std::vector<UserType>&) {} // override in derived if needed
885
886 // type can be user type or raw type
887 template<typename Type>
888 std::vector<std::vector<Type>> generateValue(bool getRaw = false) {
889 std::vector<Type> v;
890 typedef typename Derived::rawUserType Traw;
891 typedef typename Derived::minimumUserType T;
892 auto cv = derived->template getRemoteValue<Traw>(true)[0];
893 for(size_t i = 0; i < derived->nElementsPerChannel(); ++i) {
894 Traw e = cv[i] + derived->increment * (static_cast<Traw>(i) + 1);
895 if(!getRaw) {
896 v.push_back(derived->template convertRawToCooked<T, Traw>(e));
897 }
898 else {
899 v.push_back(static_cast<T>(e));
900 }
901 }
902 derived->generateValueHook(v);
903 return {v};
904 }
905
906 template<typename UserType>
907 std::vector<std::vector<UserType>> getRemoteValue(bool getRaw = false) {
908 typedef typename Derived::rawUserType Traw;
909
910 std::vector<UserType> cookedValues(derived->nElementsPerChannel());
911 std::vector<Traw> rawValues(derived->nElementsPerChannel());
912
913 // Keep the scope of the dummy buffer lock as limited as possible (see #12332).
914 // The rawToCooked conversion will acquire a lock via the math plugin decorator,
915 // which will cause lock order inversion if you hold the dummy buffer lock at that point.
916 {
917 auto bufferLock = derived->acc.getBufferLock();
918
919 for(size_t i = 0; i < derived->nElementsPerChannel(); ++i) {
920 rawValues[i] = derived->acc[i + derived->myOffset()];
921 }
922 } // end of bufferLock scope
923
924 for(size_t i = 0; i < derived->nElementsPerChannel(); ++i) {
925 if(!getRaw) {
926 cookedValues[i] = derived->template convertRawToCooked<UserType, Traw>(rawValues[i]);
927 }
928 else {
929 cookedValues[i] = static_cast<UserType>(rawValues[i]);
930 }
931 }
932 return {cookedValues};
933 }
934
936 auto v = generateValue<typename Derived::rawUserType>(true)[0];
937 { // scope for the buffer lock
938 auto bufferLock = derived->acc.getBufferLock();
939 for(size_t i = 0; i < derived->nElementsPerChannel(); ++i) {
940 derived->acc[i + derived->myOffset()] = v[i];
941 }
942 } // release the buffer lock before triggering another thread
943 if(derived->isPush()) {
944 dynamic_cast<ExceptionDummy&>(derived->acc.getBackend()).triggerInterrupt(6);
945 }
946 }
947};
948
950template<typename Derived>
952 size_t nElementsPerChannel() { return 1; }
953};
954
955// Base descriptor for bit accessors
956template<typename Derived>
958 using RegisterDescriptorBase<Derived>::derived;
960
961 size_t nChannels() { return 1; }
962 size_t nElementsPerChannel() { return 1; }
963
965 size_t nValuesToTest() { return 1; }
966
967 size_t nRuntimeErrorCases() { return derived->target.nRuntimeErrorCases(); }
968
969 template<typename UserType>
970 std::vector<std::vector<UserType>> generateValue([[maybe_unused]] bool getRaw = false) {
971 return derived->target.template generateValue<UserType>();
972 }
973
974 template<typename UserType>
975 std::vector<std::vector<UserType>> getRemoteValue(bool getRaw = false) {
976 if(getRaw) {
977 // For raw mode, return the full word value (raw accessor returns full parent word)
978 return derived->target.template getRemoteValue<UserType>(true);
979 }
980 // For cooked mode, extract the bit range
981 uint64_t v = derived->target.template getRemoteValue<uint64_t>()[0][0];
982 uint64_t mask = ((1ULL << derived->width) - 1) << derived->shift;
983 UserType result = (v & mask) >> derived->shift;
984 return {{result}};
985 }
986
987 void setRemoteValue() { derived->target.setRemoteValue(); }
988
990 uint64_t debugReadRawFullWord() { return static_cast<uint64_t>(static_cast<uint32_t>(derived->target.acc)); }
991
992 void setForceRuntimeError(bool enable, size_t caseIndex) { derived->target.setForceRuntimeError(enable, caseIndex); }
993};
994
995struct BitRangeAccessorTarget : ScalarRegisterDescriptorBase<BitRangeAccessorTarget> {
996 std::string path() { return "/WORD_FIRMWARE"; }
997
998 const uint32_t increment = 0x1313'2131;
999
1000 using minimumUserType = uint32_t;
1001 using rawUserType = uint32_t;
1002 DummyRegisterAccessor<minimumUserType> acc{exceptionDummyBitRange.get(), "", "/WORD_FIRMWARE"};
1003};
1004
1005struct RegLowerHalfOfFirmware : RegBitRangeDescriptor<RegLowerHalfOfFirmware> {
1006 std::string path() { return "/WORD_FIRMWARE/BitRangeLower"; }
1007
1008 using minimumUserType = int8_t;
1009 using rawUserType = uint32_t;
1010
1011 uint16_t width = 8;
1012 uint16_t shift = 8;
1013
1015};
1016
1017struct RegUpperHalfOfFirmware : RegBitRangeDescriptor<RegUpperHalfOfFirmware> {
1018 std::string path() { return "/WORD_FIRMWARE/BitRangeUpper"; }
1019
1020 using minimumUserType = int16_t;
1021 using rawUserType = uint32_t;
1022
1023 uint16_t width = 16;
1024 uint16_t shift = 16;
1025
1027};
1028
1029struct RegBitRangeMiddle : RegBitRangeDescriptor<RegBitRangeMiddle> {
1030 std::string path() { return "/WORD_FIRMWARE/BitRangeMiddle"; }
1031
1032 using minimumUserType = int8_t;
1033 using rawUserType = uint32_t;
1034
1035 uint16_t width = 8;
1036 uint16_t shift = 4;
1037
1039};
1040
1041/**********************************************************************************************************************/
1042
1044template<typename Derived>
1046 using RegisterDescriptorBase<Derived>::derived;
1048
1049 size_t nChannels() { return 1; }
1050 size_t nElementsPerChannel() { return derived->target.nElementsPerChannel(); }
1051
1053 size_t nValuesToTest() { return 1; }
1054
1055 size_t nRuntimeErrorCases() { return derived->target.nRuntimeErrorCases(); }
1056
1057 template<typename UserType>
1058 std::vector<std::vector<UserType>> generateValue([[maybe_unused]] bool getRaw = false) {
1059 return derived->target.template generateValue<UserType>();
1060 }
1061
1062 template<typename UserType>
1063 std::vector<std::vector<UserType>> getRemoteValue(bool getRaw = false) {
1064 if(getRaw) {
1065 // For raw mode, return the full word value (raw accessor returns full parent words)
1066 return derived->target.template getRemoteValue<UserType>(true);
1067 }
1068 // For cooked mode, extract the bit range from each element
1069 auto rawValues = derived->target.template getRemoteValue<uint64_t>();
1070 std::vector<UserType> result;
1071 for(size_t i = 0; i < rawValues[0].size(); ++i) {
1072 uint64_t v = rawValues[0][i];
1073 uint64_t mask = ((1ULL << derived->width) - 1) << derived->shift;
1074 result.push_back(static_cast<UserType>((v & mask) >> derived->shift));
1075 }
1076 return {result};
1077 }
1078
1079 void setRemoteValue() { derived->target.setRemoteValue(); }
1080
1081 void setForceRuntimeError(bool enable, size_t caseIndex) { derived->target.setForceRuntimeError(enable, caseIndex); }
1082};
1083
1085 std::string path() { return "/ARRAY_REG"; }
1086
1087 // The test framework iterates generateValue a few times; use a small increment to avoid overflow across 3 elements
1088 const uint32_t increment = 0x0101'0101;
1089
1090 using minimumUserType = uint32_t;
1091 using rawUserType = uint32_t;
1092
1093 size_t nElementsPerChannel() { return 3; }
1094
1095 DummyRegisterAccessor<minimumUserType> acc{exceptionDummyBitRange.get(), "", "/ARRAY_REG"};
1096};
1097
1099 std::string path() { return "/ARRAY_REG/ArrayBitRangeLow"; }
1100
1101 using minimumUserType = uint8_t;
1102 using rawUserType = int32_t;
1103
1104 uint16_t width = 8;
1105 uint16_t shift = 0;
1106
1108};
1109
1111 std::string path() { return "/ARRAY_REG/ArrayBitRangeHigh"; }
1112
1113 using minimumUserType = uint8_t;
1114 using rawUserType = int32_t;
1115
1116 uint16_t width = 8;
1117 uint16_t shift = 8;
1118
1120};
1121
1122/**********************************************************************************************************************/
1123
1124// Double-buffered named channel slice of the 2D register TEST.DBL. The firmware-side double buffer handshake is
1125// simulated through backdoor accessors on the slice's buffer registers and the buffer-number control register,
1126// mirroring AreaType in testDoubleBuffering.cpp.
1128 std::string path() { return "/TEST/DBL.1"; }
1129 bool isWriteable() { return false; }
1130 bool isReadable() { return true; }
1132 size_t nChannels() { return 1; }
1133 size_t nElementsPerChannel() { return 4; }
1134 size_t writeQueueLength() { return std::numeric_limits<size_t>::max(); }
1135 size_t nRuntimeErrorCases() { return 1; }
1136 typedef uint16_t minimumUserType;
1137 typedef int16_t rawUserType;
1138
1139 static uint32_t _currentBufferNumber;
1140
1141 // A channel slice of a 2D register is read-only. Double buffering adds the write-losing caveats of a buffer swap
1142 // and does not support raw transfer or wait_for_new_data yet.
1143 static constexpr auto capabilities = TestCapabilities<>()
1145 .disableAsyncReadInconsistency()
1146 .disableSwitchReadOnly()
1147 .disableSwitchWriteOnly()
1148 .disableTestWriteNeverLosesData()
1149 .disableTestRawTransfer()
1150 .disableTestCatalogue();
1151
1152 template<typename UserType>
1153 std::vector<std::vector<UserType>> generateValue(bool = false) {
1154 auto values = getRemoteValue<minimumUserType>();
1155 for(size_t e = 0; e < nElementsPerChannel(); ++e) {
1156 values[0][e] += uint16_t(37 + 11 * e);
1157 }
1158 return values;
1159 }
1160
1161 DummyRegisterAccessor<uint32_t> currentBufferNumber{exceptionDummyMuxed.get(), "TEST/DOUBLE_BUF", "INACTIVE_BUF_ID"};
1162 DummyRegisterAccessor<minimumUserType> buffer0{exceptionDummyMuxed.get(), "TEST/DBL.1", "BUF0"};
1163 DummyRegisterAccessor<minimumUserType> buffer1{exceptionDummyMuxed.get(), "TEST/DBL.1", "BUF1"};
1164
1165 template<typename UserType>
1166 std::vector<std::vector<UserType>> getRemoteValue(bool = false) {
1167 std::vector<std::vector<UserType>> v(1);
1168 if(currentBufferNumber[0] == 1) {
1169 for(size_t e = 0; e < nElementsPerChannel(); ++e) {
1170 v[0].push_back(buffer0[e]);
1171 }
1172 }
1173 else {
1174 for(size_t e = 0; e < nElementsPerChannel(); ++e) {
1175 v[0].push_back(buffer1[e]);
1176 }
1177 }
1178 return v;
1179 }
1180
1183 _currentBufferNumber = _currentBufferNumber ? 0 : 1; // change current buffer no. 0->1 or 1->0
1184
1185 auto values = generateValue<minimumUserType>();
1186
1187 if(currentBufferNumber[0] == 1) {
1188 for(size_t e = 0; e < nElementsPerChannel(); ++e) {
1189 buffer0[e] = values[0][e];
1190 }
1191 }
1192 else {
1193 for(size_t e = 0; e < nElementsPerChannel(); ++e) {
1194 buffer1[e] = values[0][e];
1195 }
1196 }
1197 }
1198
1199 void setForceRuntimeError(bool enable, size_t) {
1200 exceptionDummyMuxed->throwExceptionRead = enable;
1201 exceptionDummyMuxed->throwExceptionWrite = enable;
1202 exceptionDummyMuxed->throwExceptionOpen = enable;
1203 }
1204};
1205
1207
1208/**********************************************************************************************************************/
1209
1210// Interrupt-driven double-buffered named channel slice of the 2D register TEST.DBLASYNC. Same channel slice as
1211// DoubleBufferedNamedChannelSlice, but the register is triggered by interrupt, so the read of the freshly finished
1212// buffer must go through the DoubleBufferAccessor on the async path.
1214 static std::string path() { return "/TEST/DBLASYNC.1"; }
1215 static bool isWriteable() { return false; }
1216 static bool isReadable() { return true; }
1218 static size_t nChannels() { return 1; }
1219 static size_t nElementsPerChannel() { return 4; }
1220 static size_t writeQueueLength() { return std::numeric_limits<size_t>::max(); }
1221 static size_t nRuntimeErrorCases() { return 1; }
1222 using minimumUserType = uint16_t;
1223 using rawUserType = int16_t;
1224
1225 static uint32_t _currentBufferNumber;
1226
1227 static constexpr auto capabilities = TestCapabilities<>()
1229 .disableAsyncReadInconsistency()
1230 .disableSwitchReadOnly()
1231 .disableSwitchWriteOnly()
1232 .disableTestWriteNeverLosesData()
1233 .disableTestRawTransfer()
1234 .disableTestCatalogue();
1235
1236 template<typename UserType>
1237 std::vector<std::vector<UserType>> generateValue(bool = false) {
1238 auto values = getRemoteValue<minimumUserType>();
1239 for(size_t e = 0; e < nElementsPerChannel(); ++e) {
1240 values[0][e] += uint16_t(37 + 11 * e);
1241 }
1242 return values;
1243 }
1244
1245 DummyRegisterAccessor<uint32_t> currentBufferNumber{exceptionDummyMuxed.get(), "TEST/DOUBLE_BUF", "INACTIVE_BUF_ID"};
1246 DummyRegisterAccessor<minimumUserType> buffer0{exceptionDummyMuxed.get(), "TEST/DBLASYNC.1", "BUF0"};
1247 DummyRegisterAccessor<minimumUserType> buffer1{exceptionDummyMuxed.get(), "TEST/DBLASYNC.1", "BUF1"};
1248
1249 template<typename UserType>
1250 std::vector<std::vector<UserType>> getRemoteValue(bool = false) {
1251 std::vector<std::vector<UserType>> v(1);
1252 if(currentBufferNumber[0] == 1) {
1253 for(size_t e = 0; e < nElementsPerChannel(); ++e) {
1254 v[0].push_back(buffer0[e]);
1255 }
1256 }
1257 else {
1258 for(size_t e = 0; e < nElementsPerChannel(); ++e) {
1259 v[0].push_back(buffer1[e]);
1260 }
1261 }
1262 return v;
1263 }
1264
1267 _currentBufferNumber = (_currentBufferNumber != 0) ? 0 : 1; // change current buffer no. 0->1 or 1->0
1268
1269 // Each buffer carries distinct channel data: a buffer-dependent offset is added on top of the common base
1270 // sequence produced by generateValue(). An implementation that always delivers the same buffer would then fail
1271 // one of the two buffer checks.
1272 const bool finishingBuffer0 = (currentBufferNumber[0] == 1);
1273 const uint16_t bufferOffset = finishingBuffer0 ? 0 : 1000;
1274
1275 auto values = generateValue<minimumUserType>();
1276 for(size_t e = 0; e < nElementsPerChannel(); ++e) {
1277 values[0][e] += bufferOffset;
1278 }
1279
1280 if(currentBufferNumber[0] == 1) {
1281 for(size_t e = 0; e < nElementsPerChannel(); ++e) {
1282 buffer0[e] = values[0][e];
1283 }
1284 }
1285 else {
1286 for(size_t e = 0; e < nElementsPerChannel(); ++e) {
1287 buffer1[e] = values[0][e];
1288 }
1289 }
1290 if(exceptionDummyMuxed->isOpen()) {
1291 exceptionDummyMuxed->triggerInterrupt(7);
1292 }
1293 }
1294
1295 static void setForceRuntimeError(bool enable, size_t) {
1296 exceptionDummyMuxed->throwExceptionRead = enable;
1297 exceptionDummyMuxed->throwExceptionWrite = enable;
1298 exceptionDummyMuxed->throwExceptionOpen = enable;
1299 if(exceptionDummyMuxed->isOpen()) {
1300 exceptionDummyMuxed->triggerInterrupt(7);
1301 }
1302 }
1303};
1304
1306
1307/**********************************************************************************************************************/
1308
1309BOOST_AUTO_TEST_CASE(testRegisterAccessor) {
1310 std::cout << "*** testRegisterAccessor *** " << std::endl;
1313 .addRegister<Integers_signed32_async>()
1314 /* .addRegister<Integers_signed32_async_rw>() // disabled for now as .DUMMY_WRITABLE no longer supports
1315 wait_for_new_data */
1316 .addRegister<ShortRaw_signed16>()
1317 .addRegister<ShortRaw_unsigned16>()
1318 .addRegister<ShortRaw_fixedPoint16_8u>()
1319 .addRegister<ShortRaw_fixedPoint16_8s>()
1320 .addRegister<ByteRaw_signed8>()
1321 .addRegister<ByteRaw_unsigned8>()
1322 .addRegister<ByteRaw_fixedPoint8_4s>()
1323 .addRegister<ByteRaw_fixedPoint8_4u>()
1324 .addRegister<AsciiData>()
1325 .runTests(cdd);
1326}
1327
1328/**********************************************************************************************************************/
1329
1330BOOST_AUTO_TEST_CASE(testMultiplexedRegisterAccessor) {
1331 std::cout << "*** testMultiplexedRegisterAccessor *** " << std::endl;
1334 .addRegister<MuxedNodmaAsync>()
1335 .addRegister<MuxedFloat>()
1336 .runTests(cddMuxed);
1337}
1338
1339/**********************************************************************************************************************/
1340
1341BOOST_AUTO_TEST_CASE(testNamedChannelSlices) {
1342 std::cout << "*** testNamedChannelSlices *** " << std::endl;
1343 ChimeraTK::UnifiedBackendTest<>().addRegister<NamedChannelSlice0>().addRegister<NamedChannelSlice3>().runTests(
1344 cddMuxed);
1345}
1346
1347/**********************************************************************************************************************/
1348
1349BOOST_AUTO_TEST_CASE(testNamedChannelSliceAsync) {
1350 std::cout << "*** testNamedChannelSliceAsync *** " << std::endl;
1352}
1353
1354/**********************************************************************************************************************/
1355
1356BOOST_AUTO_TEST_CASE(testDoubleBufferedNamedChannelSlices) {
1357 std::cout << "*** testDoubleBufferedNamedChannelSlices *** " << std::endl;
1359}
1360
1361/**********************************************************************************************************************/
1362
1363BOOST_AUTO_TEST_CASE(testDoubleBufferedNamedChannelSliceAsync) {
1364 std::cout << "*** testDoubleBufferedNamedChannelSliceAsync *** " << std::endl;
1366}
1367
1368/**********************************************************************************************************************/
1369
1370BOOST_AUTO_TEST_CASE(testBitRanges) {
1371 std::cout << "*** testBitRanges *** " << std::endl;
1372
1375 .addRegister<RegUpperHalfOfFirmware>()
1376 .addRegister<RegBitRangeMiddle>()
1377 .addRegister<ArrayBitRangeLow>()
1378 .addRegister<ArrayBitRangeHigh>()
1379 .runTests(cddBitRange);
1380}
1381
1382/**********************************************************************************************************************/
1383
1384// Bit ranges inside the named channels of a 2D register (bitRangeChannels.jmap). A channel carrying bit-field
1385// children (A) exposes a full-word parent slice plus one bit-range slice per child; a channel that is itself a
1386// single bit range (B) exposes a bit-range slice. All slices read the strided 2D sample data correctly. Writes to
1387// a bit-range slice are rejected and obtaining the full-2D accessor of a register with a direct bit-range channel
1388// raises a clear error.
1389BOOST_AUTO_TEST_CASE(TestBitRangeNamedChannelSlices) {
1390 std::cout << "*** TestBitRangeNamedChannelSlices *** " << std::endl;
1391
1392 ChimeraTK::Device dev(cddBitRangeChannels);
1393 dev.open();
1394
1395 // Place known sample words into the shared DMA buffer through the plain read-write view RAW (BAR 13, address
1396 // 0x4000). Each 64-bit element word of TEST/BR spans two RAW words: the lower one is channel A's word at byte
1397 // offset 0x4000 + 8*e, the upper one is channel B's word at byte offset 0x4004 + 8*e.
1398 DummyRegisterAccessor<uint32_t> raw{dummyBitRangeChannels.get(), "RAW", ""};
1399
1400 const size_t n = 4;
1401 std::vector<uint32_t> wordA(n), wordB(n);
1402 for(size_t e = 0; e < n; ++e) {
1403 wordA[e] = 0x12345678u + e * 0x01010101u;
1404 wordB[e] = 0xABCDEF01u + e * 0x00001111u;
1405 raw[2 * e] = wordA[e];
1406 raw[2 * e + 1] = wordB[e];
1407 }
1408
1409 // The full-word parent slice and the two child slices return the expected values.
1410 auto accA = dev.getOneDRegisterAccessor<uint32_t>("TEST/BR/A");
1411 auto accLo = dev.getOneDRegisterAccessor<uint32_t>("TEST/BR/A/Lo");
1412 auto accHi = dev.getOneDRegisterAccessor<uint32_t>("TEST/BR/A/Hi");
1413 // Channel B is a direct bit-range channel; its slice extracts bits 16..23 of each sample word.
1414 auto accB = dev.getOneDRegisterAccessor<uint32_t>("TEST/BR/B");
1415 BOOST_REQUIRE(accA.getNElements() == n);
1416 BOOST_REQUIRE(accLo.getNElements() == n);
1417 BOOST_REQUIRE(accHi.getNElements() == n);
1418 BOOST_REQUIRE(accB.getNElements() == n);
1419
1420 accA.read();
1421 accLo.read();
1422 accHi.read();
1423 accB.read();
1424
1425 for(size_t e = 0; e < n; ++e) {
1426 BOOST_TEST(accA[e] == wordA[e]);
1427 BOOST_TEST(accLo[e] == (wordA[e] & 0xFFu));
1428 BOOST_TEST(accHi[e] == ((wordA[e] >> 8) & 0xFFu));
1429 BOOST_TEST(accB[e] == ((wordB[e] >> 16) & 0xFFu));
1430 }
1431
1432 // Writing to a bit-range slice is not supported.
1433 BOOST_CHECK_THROW(accLo.write(), ChimeraTK::logic_error);
1434
1435 // Reading the whole 2D register that contains a direct bit-range channel raises a clear error at accessor
1436 // obtaining time.
1437 BOOST_CHECK_THROW(dev.getTwoDRegisterAccessor<uint32_t>("TEST/BR"), ChimeraTK::logic_error);
1438
1439 dev.close();
1440}
1441
1442/**********************************************************************************************************************/
1443
1444BOOST_AUTO_TEST_SUITE_END()
Set of AccessMode flags with additional functionality for an easier handling.
Definition AccessMode.h:48
void add(AccessMode flag)
Add the given flag to the set.
Definition AccessMode.cc:62
static BackendFactory & getInstance()
Static function to get an instance of factory.
Class allows to read/write registers from device.
Definition Device.h:39
TwoDRegisterAccessor< UserType > getTwoDRegisterAccessor(const RegisterPath &registerPathName, size_t numberOfElements=0, size_t elementsOffset=0, const AccessModeFlags &flags=AccessModeFlags({})) const
Get a TwoDRegisterAccessor object for the given register.
Definition Device.h:286
void close()
Close the device.
Definition Device.cc:66
OneDRegisterAccessor< UserType > getOneDRegisterAccessor(const RegisterPath &registerPathName, size_t numberOfWords=0, size_t wordOffsetInRegister=0, const AccessModeFlags &flags=AccessModeFlags({})) const
Get a OneDRegisterAccessor object for the given register.
Definition Device.h:276
void open(std::string const &aliasName)
Open a device by the given alias name from the DMAP file.
Definition Device.cc:58
std::atomic< bool > throwExceptionRead
Register accessor for accessing multiplexed 2D array registers internally of a DummyBackend implement...
Register accessor for accessing single word or 1D array registers internally of a DummyBackend implem...
unsigned int getNumberOfElements()
return number of elements
Accessor for raw 32 bit integer access to the underlying memory space.
Class to test any backend for correct behaviour.
UnifiedBackendTest< typename boost::mpl::push_back< VECTOR_OF_REGISTERS_T, REG_T >::type > addRegister()
Add a register to be used by the test.
Exception thrown when a logic error has occured.
Definition Exception.h:51
This header collects some conversion routines which should be useful both for ApplicationCore Python ...
@ wait_for_new_data
Make any read blocking until new data has arrived since the last read.
@ raw
Raw access: disable any possible conversion from the original hardware data type into the given UserT...
std::string to_string(const std::string &v)
Base descriptor for array bit-range accessors.
void setForceRuntimeError(bool enable, size_t caseIndex)
std::vector< std::vector< UserType > > getRemoteValue(bool getRaw=false)
std::vector< std::vector< UserType > > generateValue(bool getRaw=false)
DummyRegisterAccessor< minimumUserType > acc
std::vector< std::vector< UserType > > generateValue()
std::vector< std::vector< UserType > > getRemoteValue()
DummyRegisterAccessor< uint32_t > acc
static constexpr auto capabilities
static ChimeraTK::AccessModeFlags supportedFlags()
static void setForceRuntimeError(bool enable, size_t)
DummyRegisterAccessor< minimumUserType > acc
Descriptor for the test capabilities for each register.
constexpr TestCapabilities< _syncRead, TestCapability::disabled, _asyncReadInconsistency, _switchReadOnly, _switchWriteOnly, _writeNeverLosesData, _testWriteOnly, _testReadOnly, _testRawTransfer, _testCatalogue, _setRemoteValueIncrementsVersion, _testPartialAccessor > disableForceDataLossWrite() const
DummyRegisterAccessor< minimumUserType > buffer1
std::vector< std::vector< UserType > > getRemoteValue(bool=false)
std::vector< std::vector< UserType > > generateValue(bool=false)
DummyRegisterAccessor< minimumUserType > buffer0
DummyRegisterAccessor< minimumUserType > buffer0
std::vector< std::vector< UserType > > getRemoteValue(bool=false)
DummyRegisterAccessor< minimumUserType > buffer1
std::vector< std::vector< UserType > > generateValue(bool=false)
DummyRegisterAccessor< uint32_t > currentBufferNumber
std::vector< std::vector< Type > > generateValue(bool raw=false)
std::vector< std::vector< UserType > > getRemoteValue(bool raw=false)
std::vector< std::vector< Type > > generateValue(bool raw=false)
ChimeraTK::AccessModeFlags supportedFlags()
std::vector< std::vector< UserType > > getRemoteValue(bool raw=false)
DummyRegisterAccessor< int32_t > acc
std::vector< std::vector< Type > > generateValue(bool raw=false)
void setForceRuntimeError(bool enable, size_t)
DummyRegisterAccessor< int32_t > acc
ChimeraTK::AccessModeFlags supportedFlags()
std::vector< std::vector< UserType > > getRemoteValue(bool raw=false)
static constexpr auto capabilities
void setForceRuntimeError(bool enable, size_t)
std::vector< std::vector< UserType > > generateValue()
ChimeraTK::AccessModeFlags supportedFlags()
DummyMultiplexedRegisterAccessor< float > acc
std::vector< std::vector< UserType > > getRemoteValue()
std::vector< std::vector< UserType > > getRemoteValue()
DummyMultiplexedRegisterAccessor< uint16_t > acc
ChimeraTK::AccessModeFlags supportedFlags()
void setForceRuntimeError(bool enable, size_t)
std::vector< std::vector< UserType > > generateValue()
DummyMultiplexedRegisterAccessor< uint16_t > acc
void setForceRuntimeError(bool enable, size_t)
std::vector< std::vector< UserType > > getRemoteValue()
ChimeraTK::AccessModeFlags supportedFlags()
static constexpr auto capabilities
std::vector< std::vector< UserType > > generateValue()
std::vector< std::vector< UserType > > generateValue(bool raw=false)
DummyRegisterAccessor< uint16_t > acc
ChimeraTK::AccessModeFlags supportedFlags()
DummyMultiplexedRegisterAccessor< uint16_t > full
std::vector< std::vector< UserType > > getRemoteValue(bool raw=false)
void setForceRuntimeError(bool enable, size_t)
std::vector< std::vector< UserType > > generateValue(bool raw=false)
std::vector< std::vector< UserType > > getRemoteValue(bool raw=false)
void setForceRuntimeError(bool enable, size_t)
DummyMultiplexedRegisterAccessor< uint16_t > full
ChimeraTK::AccessModeFlags supportedFlags()
DummyMultiplexedRegisterAccessor< uint16_t > full
std::vector< std::vector< UserType > > getRemoteValue(bool raw=false)
ChimeraTK::AccessModeFlags supportedFlags()
std::vector< std::vector< UserType > > generateValue(bool raw=false)
Base descriptor for 1D accessors (and scalars)
std::vector< std::vector< UserType > > getRemoteValue(bool getRaw=false)
void generateValueHook(std::vector< UserType > &)
std::vector< std::vector< Type > > generateValue(bool getRaw=false)
void setForceRuntimeError(bool enable, size_t caseIndex)
static constexpr auto capabilities
uint64_t debugReadRawFullWord()
Read the raw full word value from _barContents directly via the DummyRegisterAccessor.
std::vector< std::vector< UserType > > getRemoteValue(bool getRaw=false)
ChimeraTK::AccessModeFlags supportedFlags()
std::vector< std::vector< UserType > > generateValue(bool getRaw=false)
BitRangeAccessorTarget target
Base descriptor with defaults, used for all registers.
ChimeraTK::AccessModeFlags supportedFlags()
static constexpr auto capabilities
Base descriptor for scalars.
void setForceRuntimeError(bool enable, size_t)
ChimeraTK::AccessModeFlags supportedFlags()
std::vector< std::vector< Type > > generateValue(bool raw=false)
std::vector< std::vector< Type > > getRemoteValue(bool raw=false)
std::string cdd
auto exceptionDummy
BOOST_AUTO_TEST_CASE(testRegisterAccessor)