ChimeraTK-DeviceAccess 03.25.00
Loading...
Searching...
No Matches
testGenericMuxedInterruptDistributor.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 BOOST_TEST_MODULE GenericMuxedInterruptDistributorTest
6#include "Device.h"
7#include "DummyBackend.h"
9
10#include <boost/test/unit_test.hpp>
11
12#include <chrono>
13#include <thread>
14
15using namespace ChimeraTK;
16using namespace boost::unit_test_framework;
17
18bool readWithTimeout(VoidRegisterAccessor& acc, size_t msTimeout = 3000) {
19 auto startTime = std::chrono::steady_clock::now();
20 auto currentTime = startTime;
21 while(currentTime < startTime + std::chrono::milliseconds(msTimeout)) {
22 std::this_thread::sleep_for(std::chrono::milliseconds(10));
23 if(acc.readNonBlocking()) {
24 return true;
25 }
26 currentTime = std::chrono::steady_clock::now();
27 }
28 return false;
29}
30
31BOOST_AUTO_TEST_SUITE(INTCHandlerTestSuite)
32
33static constexpr std::string_view testCdd{"(WriteMonitoring:xdma/slot5?map=irq_test.mapp)"};
34
35/**********************************************************************************************************************/
36
37// We need a special backend because the firmware has several "clear on 1", which internally modify
38// individual bits in a word. We have to monitor the writes and log this state. Only looking at the last write is not sufficient.
40 public:
41 WriteMonitoringBackend(const std::string& mapFileName) : DummyBackend(mapFileName) {
42 acknowledged["ISR"] = 0;
43 acknowledged["IAR"] = 0;
44 acknowledged["ICR"] = 0;
45
46 setWriteCallbackFunction(AddressRange(0, 0x00800008, 4),
47 [&] { acknowledged["ISR"] |= static_cast<uint32_t>(getRawAccessor("TEST0", "ISR")); });
48 setWriteCallbackFunction(AddressRange(0, 0x0090000C, 4),
49 [&] { acknowledged["IAR"] |= static_cast<uint32_t>(getRawAccessor("TEST1", "IAR")); });
50 setWriteCallbackFunction(AddressRange(0, 0x00A0000C, 4),
51 [&] { acknowledged["ICR"] |= static_cast<uint32_t>(getRawAccessor("TEST2", "ICR")); });
52 setWriteCallbackFunction(AddressRange(0, 0x00D00008, 4),
53 [&] { acknowledged["ISR"] |= static_cast<uint32_t>(getRawAccessor("TEST5", "ISR")); });
54 setWriteCallbackFunction(AddressRange(0, 0x00D0000C, 4), // comment for formatting
55 [&] { sie |= static_cast<uint32_t>(getRawAccessor("TEST5", "SIE")); });
56 }
57
58 static boost::shared_ptr<DeviceBackend> createInstance(
59 [[maybe_unused]] std::string address, std::map<std::string, std::string> parameters) {
60 if(parameters["map"].empty()) {
61 throw ChimeraTK::logic_error("No map file name given.");
62 }
63 return boost::shared_ptr<DeviceBackend>(new WriteMonitoringBackend(parameters["map"]));
64 }
65
66 std::map<std::string, uint32_t> acknowledged;
67 uint32_t sie{0};
68};
69
77
78static BackendRegisterer b;
79
80/**********************************************************************************************************************/
81
83 Device device{std::string{testCdd}};
87 uint32_t _interrupt;
88 boost::shared_ptr<WriteMonitoringBackend> dummyBackend;
89
90 TestFixture(uint32_t interrupt, bool activateAsyncFirst) : _interrupt(interrupt) {
91 dummyBackend = boost::dynamic_pointer_cast<WriteMonitoringBackend>(device.getBackend());
92 assert(dummyBackend);
93
94 if(activateAsyncFirst) {
95 device.open();
97 }
98
100 "!" + std::to_string(interrupt) + ":4", {ChimeraTK::AccessMode::wait_for_new_data}));
101 dummyInterrupt.replace(device.getVoidRegisterAccessor("DUMMY_INTERRUPT_" + std::to_string(interrupt)));
102
103 // Note: we simulate firmware write access to the ISR register here, but in some situations the register is also
104 // software-side writeable, in which case there is no DUMMY_WRITEABLE version of the register.
105 ChimeraTK::RegisterPath isrName = "TEST" + std::to_string(interrupt) + "/ISR";
107 isrName /= "DUMMY_WRITEABLE";
108 }
109 isr.replace(device.getScalarRegisterAccessor<uint32_t>(isrName));
110
111 if(activateAsyncFirst) {
112 // only if asyncRead is active we will get an initial value. Pop it here for convenience.
113 BOOST_TEST(readWithTimeout(accInterrupt));
114 }
115 }
116
117 virtual ~TestFixture() { device.close(); }
118};
119
120/**********************************************************************************************************************/
121
128 Device device{std::string{testCdd}};
130
131 ThrowTestFixture(uint32_t interrupt) {
132 std::string testRegister("!" + std::to_string(interrupt) + ":4");
133 try {
134 accInterrupt.replace(device.getVoidRegisterAccessor(testRegister, {ChimeraTK::AccessMode::wait_for_new_data}));
135 BOOST_CHECK_MESSAGE(false, "Creating register \"" + testRegister + "\" did not throw as expected!");
136 }
137 catch(ChimeraTK::logic_error& e) {
138 std::cout << "Caught expected exception for " << boost::unit_test::framework::current_test_case().p_name
139 << ". Print for manual check of message: " << e.what() << std::endl;
140 BOOST_CHECK(true); // just to have a hook for the last successful test
141 }
142 }
143};
144
145/**********************************************************************************************************************/
146
147struct Inactive0 : public TestFixture {
148 Inactive0() : TestFixture(0, false) {}
149};
151 device.open();
152
153 auto ier = device.getScalarRegisterAccessor<int>("TEST0/IER");
154 ier.read();
155 BOOST_TEST(0x0 == ier);
156 BOOST_TEST(dummyBackend->acknowledged["ISR"] == 0);
157
159
160 ier.read();
161 BOOST_TEST(0x10 == ier);
162 BOOST_TEST(dummyBackend->acknowledged["ISR"] == 0x10);
163 dummyBackend->acknowledged["ISR"] = 0;
164
166 ier.read();
167 BOOST_TEST(ier == 0x30);
168 BOOST_TEST(dummyBackend->acknowledged["ISR"] == 0x20); // we must NOT acknowledge 0x10 again!
169
170 accInterrupt.replace(VoidRegisterAccessor{});
171
172 // FIXME: This is the expected result if the SubDomain destructor would notify the MuxedInterruptDistributor.
173 // This is not implemented yet.
174#if false
175 ier.read();
176 BOOST_TEST(ier == 0x20); // only the second iterator remains
177#endif
178
179 // at this point the Domain and with it the MuxedInterruptDistributor goe out of scope, and the distributor's
180 // destructor is kicking in.
181 accInterrupt2.replace(VoidRegisterAccessor{});
182 ier.read();
183 BOOST_TEST(ier == 0x0);
184}
185
186struct Active0 : public TestFixture {
187 Active0() : TestFixture(0, true) {}
188};
190 auto ier = device.getScalarRegisterAccessor<int>("TEST0/IER");
191 ier.read();
192 BOOST_TEST(0x10 == ier);
193 BOOST_TEST(dummyBackend->acknowledged["ISR"] == 0x10);
194 // no need to repeat all following tests in inactiveIER here.
195}
196
197/**********************************************************************************************************************/
198BOOST_AUTO_TEST_CASE(activateOnActiveDomain) {
199 // Test that the activation is called correctly for all members if the upper layers in the distribution tree are
200 // already active Tree:
201 //
202 // Domain 3 - SubDomain [3] -- VariableDistributor<void> [3]
203 // \_ MuxedInterruptDistibutor [3] --- SubDomain [3, 0] -- VariableDistributor<void> [3, 0]
204 // \ \_ MuxedInterruptDistibutor [3, 0]
205 // \_ SubDomain [3, 1] -- VariableDistributor<void> [3, 1]
206 // \_ MuxedInterruptDistibutor [3, 1]
208 device.open(std::string{testCdd});
210
211 // Create the domain and check that it is active
212 auto accessor3 = device.getVoidRegisterAccessor("!3", {AccessMode::wait_for_new_data});
213 readWithTimeout(accessor3); // initial value
214 auto dummyInterruptTrigger =
215 device.getVoidRegisterAccessor("DUMMY_INTERRUPT_3"); // for triggering the dummy interrupt
216 dummyInterruptTrigger.write();
217 readWithTimeout(accessor3); // initial value
218
219 // Test 1: get an accessor for a sub SubDomain of MuxedInterruptDistibutor [3], which is in the already
220 // active SubDomain [3].
221 auto acc3_0 = device.getVoidRegisterAccessor("!3:0", {AccessMode::wait_for_new_data});
222 readWithTimeout(acc3_0);
223
224 // Test that the MuxedInterruptDistibutor [3] has been activated
225 // MER might be write-only
226 BOOST_TEST(
227 device.read<int32_t>("TEST3/MER/DUMMY_READABLE") == 0x3); // MER always has two bits which both have to be set
228
229 // Test that the handshake for sub-domain [3, 0] has been activated
230 BOOST_TEST(device.read<uint32_t>("TEST3/IER") == (1U << 0));
231
232 // Test 2: The the SubDomain behind the MuxedInterruptDistributor [3] itself is activated (not only the handshake) if
233 // the distributor is already active. We use SubDomain [3, 1] for this and test the SubDomain activation indirectly by
234 // the activation of the MuxedInterruptDistibutor [3, 1]
235 auto acc3_1 = device.getVoidRegisterAccessor("!3:1:3", {AccessMode::wait_for_new_data});
236 readWithTimeout(acc3_1);
237
238 BOOST_TEST(device.read<int32_t>("TEST3/SUB1/MER") == 0x3);
239 BOOST_TEST(device.read<uint32_t>("TEST3/SUB1/IER") == (1U << 3));
240}
241
243 AcknowledgeTest(uint32_t interrupt, std::string ackReg) : TestFixture(interrupt, true), ackRegister(ackReg) {}
244
246 std::string ackRegister;
247
248 void run() {
249 // acknowledge has been written when activating
250 BOOST_TEST(dummyBackend->acknowledged[ackRegister] == 0x10);
251
252 // prepare the status before sending the interrupt
253 // set one more bit to be sensitive to the handshake (need to see changes)
254 isr.setAndWrite(0x11);
255
256 dummyBackend->acknowledged[ackRegister] = 0;
257
259 // wait until interrupt handler is done
260 BOOST_TEST(readWithTimeout(accInterrupt));
261
262 BOOST_TEST(dummyBackend->acknowledged[ackRegister] == 0x10);
263
264 dummyBackend->acknowledged[ackRegister] = 0;
266 "!" + std::to_string(_interrupt) + ":5", {ChimeraTK::AccessMode::wait_for_new_data}));
267 BOOST_TEST(dummyBackend->acknowledged[ackRegister] == 0x20);
268 readWithTimeout(accInterrupt2); // pop the initial value
269
270 // Signal the first accessor
271
272 isr.setAndWrite(0x11);
273
274 dummyBackend->acknowledged[ackRegister] = 0;
276 BOOST_TEST(readWithTimeout(accInterrupt));
277 BOOST_CHECK(!accInterrupt2.readNonBlocking());
278
279 BOOST_TEST(dummyBackend->acknowledged[ackRegister] == 0x10);
280 if(ackRegister != "ISR") {
281 BOOST_TEST(isr.readAndGet() == 0x11);
282 }
283
284 // Signal the second accessor
285 isr.setAndWrite(0x21);
286
287 dummyBackend->acknowledged[ackRegister] = 0;
289 BOOST_TEST(readWithTimeout(accInterrupt2));
290 BOOST_CHECK(!accInterrupt.readNonBlocking());
291
292 BOOST_TEST(dummyBackend->acknowledged[ackRegister] == 0x20);
293 if(ackRegister != "ISR") {
294 BOOST_TEST(isr.readAndGet() == 0x21);
295 }
296
297 // Signal both
298 isr.setAndWrite(0x31);
299
300 dummyBackend->acknowledged[ackRegister] = 0;
302 BOOST_TEST(readWithTimeout(accInterrupt));
303 BOOST_TEST(readWithTimeout(accInterrupt2));
304
305 BOOST_TEST(dummyBackend->acknowledged[ackRegister] == 0x30);
306 if(ackRegister != "ISR") {
307 BOOST_TEST(isr.readAndGet() == 0x31);
308 }
309 }
310};
311
312/**********************************************************************************************************************/
313
314// ISR is used as acknowledge register
317};
319 run();
320}
321
322/**********************************************************************************************************************/
323/*if IAR is present: INTC writes 1<<n the according bit mask to IAR and not to ISR*/
326};
328 run();
329}
330
331/**********************************************************************************************************************/
332/* if ICR is present: INTC writes 1<<n the according bit mask to ICR and not to ISR */
333
336};
338 run();
339}
340
342 MasterEnableTest(uint32_t interrupt, std::string meRegister, bool enableFirst)
343 : TestFixture(interrupt, enableFirst), isEnabled(enableFirst) {
345 RegisterPath("TEST" + std::to_string(interrupt)) / meRegister / "DUMMY_READABLE"));
346 }
347
350
351 void run() {
352 if(!isEnabled) {
353 device.open();
354
355 // MER should not be set
356 BOOST_TEST(masterEnable.readAndGet() == 0x0);
357
359 }
360
361 // MER should be set now
362 BOOST_TEST(masterEnable.readAndGet() == 0x3); // last two bits active
363 }
364};
365/**********************************************************************************************************************/
366
371 run();
372}
377 run();
378}
379
380/**********************************************************************************************************************/
381BOOST_AUTO_TEST_CASE(testIMR) { // TEST4
383
384 device.open("(dummy:xdma/slot5?map=irq_test.mapp)");
385 BOOST_TEST(device.isOpened() == true);
386
387 auto imr = device.getScalarRegisterAccessor<uint32_t>("TEST4.IMR");
388 imr.setAndWrite(0x7F); // 7 bits in this register (see map file)
389
390 // IMR is not implemented yet. This is giving an exception at the moment.
391 try {
393 BOOST_CHECK_MESSAGE(false, "IMR not detected as invalid option.");
394 }
395 catch(ChimeraTK::logic_error& e) {
396 std::cout << "Caught expected exeption. Print for manual check of message: " << e.what() << std::endl;
397 // We skip the rest of the test for now, but leave the code below.
398 return;
399 }
400
402
403 BOOST_TEST(imr.readAndGet() == 0x6F);
404
405 device.close();
406}
407
408/**********************************************************************************************************************/
409
410struct Inactive5 : public TestFixture {
411 Inactive5() : TestFixture(5, false) {}
412};
414 auto sie = device.getScalarRegisterAccessor<int>("TEST5/SIE/DUMMY_READABLE");
415 auto cie = device.getScalarRegisterAccessor<int>("TEST5/CIE/DUMMY_READABLE");
416
417 device.open();
418
419 // pre-condition: both registers are 0
420 BOOST_TEST(sie.readAndGet() == 0x0);
421 BOOST_TEST(cie.readAndGet() == 0x0);
422 BOOST_TEST(dummyBackend->acknowledged["ISR"] == 0x0);
423 BOOST_TEST(isr.readAndGet() == 0x0);
424
425 // activate
427
428 // only SIE has been written
429 BOOST_TEST(sie.readAndGet() == 0x10);
430 BOOST_TEST(isr.readAndGet() == 0x10);
431 BOOST_TEST(dummyBackend->acknowledged["ISR"] == 0x10);
432 BOOST_TEST(cie.readAndGet() == 0x0);
433
434 // remove the accessor. CIE should be written
435 accInterrupt.replace(VoidRegisterAccessor{});
436
437 BOOST_TEST(cie.readAndGet() == 0x10);
438}
439
441 // Mixed activation: the second accessor is created AFTER activateAsyncRead
442 auto sie = device.getScalarRegisterAccessor<int>("TEST5/SIE/DUMMY_READABLE");
443 auto cie = device.getScalarRegisterAccessor<int>("TEST5/CIE/DUMMY_READABLE");
444
445 device.open();
447 BOOST_TEST(sie.readAndGet() == 0x10); // just to be safe, we already know this from the previous test
448 BOOST_TEST(dummyBackend->acknowledged["ISR"] == 0x10);
449 dummyBackend->acknowledged["ISR"] = 0x0;
450
452 sie.read();
453 BOOST_CHECK_MESSAGE((sie == 0x20) || // the implementation can either only set the new bit
454 (sie == 0x30), // or send the whole mask again
455 "SIE is " + std::to_string(sie) + ", but should be 0x20 or 0x30");
456 BOOST_TEST(dummyBackend->acknowledged["ISR"] == 0x20); // we must NOT acknowledge 0x10 again!
457
458 accInterrupt.replace(VoidRegisterAccessor{});
459
460 // FIXME: This is the expected result if the SubDomain destructor would notify the MuxedInterruptDistributor.
461 // This is not implemented yet.
462#if false
463 BOOST_TEST(cie.readAndGet() == 0x10);
464#endif
465
466 // at this point the Domain and with it the MuxedInterruptDistributor goe out of scope, and the distributor's
467 // destructor is kicking in.
468 accInterrupt2.replace(VoidRegisterAccessor{});
469
470 // FIXME: Finally wanted behaviour: There is only one accessor left
471#if false
472 BOOST_TEST(cie.readAndGet() == 0x20);
473#endif
474 // Actual behaviour: Both flags are written at the same time.
475 BOOST_TEST(cie.readAndGet() == 0x30);
476}
477
479 // Create both accessors first, then call activateAsyncRead()
480 // No need to check the clear section again. It's the same as above.
482
483 BOOST_TEST(dummyBackend->sie == 0x0);
484 BOOST_TEST(dummyBackend->acknowledged["ISR"] == 0x0);
485
486 device.open();
488
489 BOOST_TEST(dummyBackend->sie == 0x30); // both bits set, no matter whether at the same time or individually
490 BOOST_TEST(dummyBackend->acknowledged["ISR"] == 0x30);
491}
492
493/**********************************************************************************************************************/
494
499 run();
500}
505 run();
506}
507
508/**********************************************************************************************************************/
509
514 run();
515}
520 run();
521}
522
523/**********************************************************************************************************************/
524/* ERROR Scenarios */
525/**********************************************************************************************************************/
526
531
532/**********************************************************************************************************************/
533
537BOOST_FIXTURE_TEST_CASE(testJsonErrorInGeneralStructure, InvalidJson1TestFixture) {}
538
539/**********************************************************************************************************************/
540
544BOOST_FIXTURE_TEST_CASE(testJsonErrorInIntcSprecific, InvalidJson2TestFixture) {}
545
546/**********************************************************************************************************************/
547
552
553/**********************************************************************************************************************/
554
559
560/**********************************************************************************************************************/
561
566
567/**********************************************************************************************************************/
568
573
574/**********************************************************************************************************************/
575
580
581/**********************************************************************************************************************/
582
587
588/**********************************************************************************************************************/
589
594
595/**********************************************************************************************************************/
596
597// Adapt this when more versions are added
602
603/**********************************************************************************************************************/
604
605// Adapt this when more versions are added
610
611/**********************************************************************************************************************/
612
617
618/**********************************************************************************************************************/
619
624
625/**********************************************************************************************************************/
626
631
632/**********************************************************************************************************************/
633
638
639/**********************************************************************************************************************/
640
645
646/**********************************************************************************************************************/
647
648// ISR must be writeable if there is no ICR/IAR
653
654/**********************************************************************************************************************/
655
660
661/**********************************************************************************************************************/
662
667
668/**********************************************************************************************************************/
669
674
675/**********************************************************************************************************************/
676
681
682/**********************************************************************************************************************/
683
688
689/**********************************************************************************************************************/
690
695
696/**********************************************************************************************************************/
697
702
703/**********************************************************************************************************************/
704
709
710/**********************************************************************************************************************/
711
712BOOST_AUTO_TEST_SUITE_END()
static BackendFactory & getInstance()
Static function to get an instance of factory.
void registerBackendType(const std::string &backendType, boost::shared_ptr< DeviceBackend >(*creatorFunction)(std::string address, std::map< std::string, std::string > parameters), const std::vector< std::string > &sdmParameterNames={}, const std::string &deviceAccessVersion=CHIMERATK_DEVICEACCESS_VERSION)
Register a backend by the name backendType with the given creatorFunction.
Class allows to read/write registers from device.
Definition Device.h:39
bool isOpened() const
Check if the device is currently opened.
Definition Device.cc:73
void close()
Close the device.
Definition Device.cc:66
boost::shared_ptr< DeviceBackend > getBackend()
Obtain the backend.
Definition Device.cc:111
VoidRegisterAccessor getVoidRegisterAccessor(const RegisterPath &registerPathName, const AccessModeFlags &flags=AccessModeFlags({})) const
Get a VoidRegisterAccessor object for the given register.
Definition Device.cc:103
ScalarRegisterAccessor< UserType > getScalarRegisterAccessor(const RegisterPath &registerPathName, size_t wordOffsetInRegister=0, const AccessModeFlags &flags=AccessModeFlags({})) const
Get a ScalarRegisterObject object for the given register.
Definition Device.h:266
RegisterCatalogue getRegisterCatalogue() const
Return the register catalogue with detailed information on all registers.
Definition Device.cc:22
UserType read(const RegisterPath &registerPathName, const AccessModeFlags &flags=AccessModeFlags({})) const
Inefficient convenience function to read a single-word register without obtaining an accessor.
Definition Device.h:296
void activateAsyncRead() noexcept
Activate asyncronous read for all transfer elements where AccessMode::wait_for_new_data is set.
Definition Device.cc:91
void open(std::string const &aliasName)
Open a device by the given alias name from the DMAP file.
Definition Device.cc:58
The dummy device opens a mapping file instead of a device, and implements all registers defined in th...
DummyRegisterRawAccessor getRawAccessor(const std::string &module, const std::string &register_name)
Get a raw accessor to the underlying memory with the convenience of using register names.
void setWriteCallbackFunction(AddressRange addressRange, boost::function< void(void)> const &writeCallbackFunction)
void replace(const NDRegisterAccessorAbstractor< UserType > &newAccessor)
Assign a new accessor to this NDRegisterAccessorAbstractor.
RegisterInfo getRegister(const RegisterPath &registerPathName) const
Get register information for a given full path name.
bool isWriteable() const
Return whether the register is writeable.
Class to store a register path name.
Accessor class to read and write scalar registers transparently by using the accessor object like a v...
UserType readAndGet()
Convenience function to read and return a value of UserType.
void setAndWrite(UserType newValue, VersionNumber versionNumber={})
Convenience function to set and write new value.
bool write(ChimeraTK::VersionNumber versionNumber={})
Write the data to device.
Accessor class to read and write void-typed registers.
Exception thrown when a logic error has occured.
Definition Exception.h:51
const char * what() const noexcept override
Return the message describing what exactly went wrong.
Definition Exception.cpp:20
std::map< std::string, uint32_t > acknowledged
static boost::shared_ptr< DeviceBackend > createInstance(std::string address, std::map< std::string, std::string > parameters)
WriteMonitoringBackend(const std::string &mapFileName)
@ wait_for_new_data
Make any read blocking until new data has arrived since the last read.
std::string to_string(const std::string &v)
AcknowledgeTest(uint32_t interrupt, std::string ackReg)
MasterEnableTest(uint32_t interrupt, std::string meRegister, bool enableFirst)
ScalarRegisterAccessor< uint32_t > masterEnable
TestFixture(uint32_t interrupt, bool activateAsyncFirst)
boost::shared_ptr< WriteMonitoringBackend > dummyBackend
ScalarRegisterAccessor< uint32_t > isr
Test that a logic error is thrown as soon as you try to get an accessor with invalid map file entries...
ctk::Device device
BOOST_AUTO_TEST_CASE(activateOnActiveDomain)
bool readWithTimeout(VoidRegisterAccessor &acc, size_t msTimeout=3000)
BOOST_FIXTURE_TEST_CASE(inactiveIER, Inactive0)