ChimeraTK-DeviceAccess 03.27.00
Loading...
Searching...
No Matches
testSubdeviceBackendRegisterWindow.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#include "Device.h"
5#include "DummyBackend.h"
7
8#include <boost/thread/barrier.hpp>
9
10#include <thread>
11
12#define BOOST_TEST_DYN_LINK
13#define BOOST_TEST_MODULE SubdeviceBackendTest
14#define BOOST_NO_EXCEPTIONS
15#include <boost/test/unit_test.hpp>
16using namespace boost::unit_test_framework;
17#undef BOOST_NO_EXCEPTIONS
18
19using namespace ChimeraTK;
20
21#define CHECK_TIMEOUT(execPreCheck, condition, maxMilliseconds) \
22 { \
23 std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now(); \
24 execPreCheck while(!(condition)) { \
25 bool timeout_reached = (std::chrono::steady_clock::now() - t0) > std::chrono::milliseconds(maxMilliseconds); \
26 BOOST_CHECK(!timeout_reached); \
27 if(timeout_reached) break; \
28 usleep(1000); \
29 execPreCheck \
30 } \
31 }
32
33BOOST_AUTO_TEST_SUITE(SubdeviceBackendTestSuite)
34
35/**********************************************************************************************************************/
36
37BOOST_AUTO_TEST_CASE(test3regsScalar) {
38 setDMapFilePath("subdeviceTest.dmap");
39
40 Device dev;
41 dev.open("SUBDEV_REG_WINDOW_3REG_MODE");
42 Device target;
43 target.open("TARGET1");
44
45 auto acc1 = dev.getScalarRegisterAccessor<double>("APP.0.MY_REGISTER1");
46 auto acc2 = dev.getScalarRegisterAccessor<double>("APP.0.MY_REGISTER2");
47 auto accA = target.getScalarRegisterAccessor<int32_t>("APP.1.ADDRESS");
48 auto accD = target.getScalarRegisterAccessor<int32_t>("APP.1.DATA");
49 auto accS = target.getScalarRegisterAccessor<int32_t>("APP.1.STATUS");
50 std::atomic<bool> done;
51 std::thread t;
52
53 BOOST_CHECK_THROW(acc1.read(), ChimeraTK::logic_error);
54 BOOST_CHECK_THROW(acc2.read(), ChimeraTK::logic_error);
55
56 accS = 1;
57 accS.write();
58 done = false;
59 t = std::thread([&] {
60 acc2 = 42;
61 acc2.write();
62 done = true;
63 });
64 usleep(10000);
65 BOOST_CHECK(done == false);
66 accS = 0;
67 accS.write();
68 CHECK_TIMEOUT(accA.read();, (accA == 1), 5000);
69 t.join();
70 accD.read();
71 BOOST_CHECK_EQUAL(static_cast<int32_t>(accD), 42 * 4);
72
73 acc1 = 120;
74 acc1.write();
75 accA.read();
76 BOOST_CHECK_EQUAL(static_cast<int32_t>(accA), 0);
77 accD.read();
78 BOOST_CHECK_EQUAL(static_cast<int32_t>(accD), 120);
79
80 dev.close();
81}
82
83/**********************************************************************************************************************/
84
85BOOST_AUTO_TEST_CASE(test3regsArray) {
86 setDMapFilePath("subdeviceTest.dmap");
87
88 Device dev;
89 dev.open("SUBDEV_REG_WINDOW_3REG_MODE");
90 Device target;
91 target.open("TARGET1");
92
93 auto accArea = dev.getOneDRegisterAccessor<double>("APP.0.MY_AREA2");
94 auto accA = target.getScalarRegisterAccessor<int32_t>("APP.1.ADDRESS");
95 auto accD = target.getScalarRegisterAccessor<int32_t>("APP.1.DATA");
96 auto accS = target.getScalarRegisterAccessor<int32_t>("APP.1.STATUS");
97 std::atomic<bool> done;
98 std::thread t;
99
100 accS = 1;
101 accS.write();
102 done = false;
103 t = std::thread([&] {
104 accArea[0] = 123;
105 accArea[1] = 456;
106 accArea.write();
107 done = true;
108 });
109 usleep(10000);
110 BOOST_CHECK(done == false);
111 accS = 0;
112 accS.write();
113 CHECK_TIMEOUT(accA.read();, (accA == 9), 5000);
114 t.join();
115 accD.read();
116 BOOST_CHECK_EQUAL(static_cast<int32_t>(accD), 456);
117
120
121 dev.close();
122}
123
124/**********************************************************************************************************************/
125
126BOOST_AUTO_TEST_CASE(test3regsByteOffset1) {
127 setDMapFilePath("subdeviceTest.dmap");
128
129 Device dev;
130 dev.open("SUBDEV_REG_WINDOW_3REG_MODE");
131 Device target;
132 target.open("TARGET1");
133
134 auto acc = dev.getScalarRegisterAccessor<int32_t>("APP.0.MY_REGISTER_AT_BYTE_1");
135 auto accA = target.getScalarRegisterAccessor<uint32_t>("APP.1.ADDRESS");
136 auto accD = target.getScalarRegisterAccessor<uint32_t>("APP.1.DATA");
137 auto accS = target.getScalarRegisterAccessor<uint32_t>("APP.1.STATUS");
138 std::atomic<bool> done;
139 std::thread t;
140
141 BOOST_CHECK_THROW(acc.read(), ChimeraTK::logic_error);
142
143 accS = 1;
144 accS.write();
145 done = false;
146 t = std::thread([&] {
147 acc = std::bit_cast<int32_t>(0xdeadbeef);
148 acc.write();
149 done = true;
150 });
151 usleep(10000);
152 BOOST_CHECK(done == false);
153 accS = 0;
154 accS.write();
155 t.join();
156 accA.read();
157 // There have been two transfers, the last one to trasfer address 1.
158 // This is not the byte 1 in the map file!
159 BOOST_CHECK(accA == 1);
160 accD.read();
161 // Leading/trailing bytes are padded with 0s. We can just see the last word.
162 BOOST_TEST(accD == 0x000000de);
163
164 dev.close();
165}
166
167/**********************************************************************************************************************/
168
169// BOOST_AUTO_TEST_CASE(testAreaHandshake1) {
170// setDMapFilePath("subdeviceTestAreaHandshake.dmap");
171
172// Device dev;
173// dev.open("SUBDEV4");
174// Device target;
175// target.open("TARGET1");
176
177// auto acc1 = dev.getScalarRegisterAccessor<double>("APP.0.MY_REGISTER1");
178// auto acc2 = dev.getScalarRegisterAccessor<double>("APP.0.MY_REGISTER2");
179// auto acc3 = dev.getOneDRegisterAccessor<int>("APP.0.MY_AREA1", 6, 0);
180// auto accArea = target.getOneDRegisterAccessor<int32_t>("APP.0.THE_AREA", 10, 0, {AccessMode::raw});
181// auto accS = target.getScalarRegisterAccessor<int32_t>("APP.1.STATUS");
182// std::atomic<bool> done;
183// std::thread t;
184
185// BOOST_CHECK_THROW(acc1.read(), ChimeraTK::logic_error);
186// std::vector<int> vec = {1, 2, 3, 4, 5, 6};
187
188// accS = 0;
189// accS.write();
190// done = false;
191// t = std::thread([&] {
192// acc1 = 1897;
193// acc2 = 1897;
194// acc3 = vec;
195// acc1.write();
196// acc2.write();
197// acc3.write();
198// done = true;
199// });
200// BOOST_CHECK(done == false);
201// int countStatusResets = 0;
202// // the dummyForAreaHandshake backend which we use for this test does not set back the status register. we do it
203// // manually from the test, and count how often we need do so Like this we can check that the accessor waits on
204// // status==0 _each_ time before writing, in particular each array entry counts.
205// while(true) {
206// // wait for status=busy
207// do {
208// accS.read();
209// usleep(20000);
210// } while(accS == 0 && !done);
211// if(done) break;
212// countStatusResets++;
213// accS = 0;
214// accS.write();
215// }
216// BOOST_TEST(countStatusResets == 8);
217// t.join();
218// accArea.read();
219// BOOST_CHECK(accArea[0] == 1897);
220// BOOST_CHECK(accArea[1] == 1897 * 4);
221// BOOST_CHECK(accArea[2] == 65536 * vec[0]);
222// BOOST_CHECK(accArea[3] == 65536 * vec[1]);
223// dev.close();
224// }
225
226/**********************************************************************************************************************/
227
228BOOST_AUTO_TEST_CASE(test2regsScalar) {
229 setDMapFilePath("subdeviceTest.dmap");
230
231 Device dev;
232 dev.open("SUBDEV_REG_WINDOW_2REG_MODE");
233 Device target;
234 target.open("TARGET1");
235
236 auto acc2 = dev.getScalarRegisterAccessor<double>("APP.0.MY_REGISTER2");
237 auto accA = target.getScalarRegisterAccessor<int32_t>("APP.1.ADDRESS");
238 auto accD = target.getScalarRegisterAccessor<int32_t>("APP.1.DATA");
239
240 BOOST_CHECK_THROW(acc2.read(), ChimeraTK::logic_error);
241 accA = 42;
242 accA.write();
243
244 auto start = std::chrono::steady_clock::now();
245 acc2 = 666;
246 acc2.write();
247 auto stop = std::chrono::steady_clock::now();
248 std::chrono::duration<double> diff = stop - start;
249 BOOST_CHECK(diff.count() >= 1.0); // sleep time is set to 1 second
250
251 accA.read();
252 BOOST_CHECK(accA == 1);
253 accD.read();
254 BOOST_CHECK_EQUAL(static_cast<int32_t>(accD), 666 * 4);
255
256 dev.close();
257}
258
259/**********************************************************************************************************************/
260
261// BOOST_AUTO_TEST_CASE(testIsFunctional) {
262// setDMapFilePath("subdeviceTest.dmap");
263
264// Device dev;
265// dev.open("SUBDEV1");
266// Device target;
267// target.open("TARGET1");
268// BOOST_CHECK(dev.isFunctional());
269// dev.setException("Test Exception");
270// // Device should not be functional anymore
271// BOOST_CHECK(!dev.isFunctional());
272// dev.open();
273// BOOST_CHECK(dev.isFunctional());
274// dev.close();
275// BOOST_CHECK(!dev.isFunctional());
276// }
277
278/**********************************************************************************************************************/
279
280// BOOST_AUTO_TEST_CASE(TestInvolvedBackendIDs) {
281// setDMapFilePath("subdeviceTest.dmap");
282
283// ChimeraTK::Device device("SUBDEV1");
284// ChimeraTK::Device target1("TARGET1");
285
286// auto deviceIDs = device.getInvolvedBackendIDs();
287// BOOST_TEST(deviceIDs.size() == 2);
288// BOOST_TEST(deviceIDs.contains(target1.getBackend()->getBackendID()));
289// BOOST_TEST(deviceIDs.contains(device.getBackend()->getBackendID()));
290// }
291
292/**********************************************************************************************************************/
293
297 setDMapFilePath("subdeviceTest.dmap");
298
299 ChimeraTK::Device device3("SUBDEV_REG_WINDOW");
300 ChimeraTK::Device device1(
301 "(subdevice?type=regWindow&device=TARGET1&address=APP.REG_WIN.ADDRESS&writeData=APP.REG_WIN.WRITE_DATA&busy=APP."
302 "REG_WIN.BUSY&readRequest=APP.REG_WIN.READ_REQUEST&readData=APP.REG_WIN.READOUT_SINGLE&chipSelectRegister=APP."
303 "REG_WIN.CHIP_SELECT&chipIndex=1&map=Subdevice.map)");
304
305 device1.open();
306 device3.open();
307
308 auto acc1 = device1.getScalarRegisterAccessor<int32_t>("APP/0/MY_REGISTER1");
309 auto acc3 = device3.getScalarRegisterAccessor<int32_t>("APP/0/MY_REGISTER1");
310
311 ChimeraTK::Device targetDevice("TARGET1");
312 auto dummyTarget = boost::dynamic_pointer_cast<DummyBackend>(targetDevice.getBackend());
313
314 DummyRegisterAccessor<uint32_t> accChipSelect{dummyTarget.get(), "APP.REG_WIN", "CHIP_SELECT"};
315 DummyRegisterAccessor<uint32_t> accWriteData{dummyTarget.get(), "APP.REG_WIN", "WRITE_DATA"};
316
317 boost::barrier write1Reached(2);
318 boost::barrier continueWrite1(2);
319
320 std::mutex m;
321 std::condition_variable cv;
322
323 bool thread3Started{false};
324
325 std::atomic<size_t> chipSelectCounter{0};
326 std::atomic<size_t> writeCounter{0};
327
328 accChipSelect.setWriteCallback([&] { ++chipSelectCounter; });
329 accWriteData.setWriteCallback([&] {
330 auto previousWriteCount = writeCounter.load();
331 if(writeCounter.compare_exchange_weak(previousWriteCount, previousWriteCount + 1) && previousWriteCount == 0) {
332 write1Reached.wait();
333 continueWrite1.wait();
334 }
335 });
336
337 std::thread write1([&] { acc1.write(); });
338 write1Reached.wait();
339 BOOST_REQUIRE(chipSelectCounter == 1);
340
341 std::thread write3([&] {
342 {
343 std::lock_guard lk(m);
344 thread3Started = true;
345 }
346 cv.notify_one();
347 acc3.write();
348 });
349
350 {
351 std::unique_lock lk(m);
352 cv.wait(lk, [&] { return thread3Started; });
353 }
354
355 usleep(100000); // Wait a bit for acc3 write to work. It should be blocked
356
357 // The actual test: The chip select register has not been written yet because
358 // the register is still blocked.
359 BOOST_TEST(chipSelectCounter == 1);
360
361 continueWrite1.wait();
362
363 CHECK_TIMEOUT({}, (chipSelectCounter == 2), 10000);
364
365 write1.join();
366 write3.join();
367}
368
369/**********************************************************************************************************************/
370
371BOOST_AUTO_TEST_SUITE_END()
Class allows to read/write registers from device.
Definition Device.h:39
void close()
Close the device.
Definition Device.cc:66
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
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
Register accessor for accessing single word or 1D array registers internally of a DummyBackend implem...
Exception thrown when a logic error has occured.
Definition Exception.h:51
void setDMapFilePath(std::string dmapFilePath)
Set the location of the dmap file.
#define CHECK_TIMEOUT(execPreCheck, condition, maxMilliseconds)
BOOST_AUTO_TEST_CASE(test3regsScalar)