ChimeraTK-DeviceAccess 03.31.00
Loading...
Searching...
No Matches
testGroupedDemultiplexing.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 testGroupedDemultiplexing
6#include <boost/test/unit_test.hpp>
7using namespace boost::unit_test_framework;
8
9#include "Device.h"
10#include "TransferGroup.h"
11
12#include <array>
13#include <cstddef>
14#include <cstdint>
15#include <cstring>
16#include <string>
17#include <vector>
18
19using namespace ChimeraTK;
20
21/**********************************************************************************************************************/
22
23constexpr size_t nSamples = 4;
24
27static ChimeraTK::Device openDevice() {
29 device.open("(ExceptionDummy:1?map=groupedDemux.jmap)");
30 return device;
31}
32
34static void writeTwoD(ChimeraTK::Device& device, const std::string& name, int16_t base) {
35 auto full = device.getTwoDRegisterAccessor<int16_t>(name);
36 for(int c = 0; c < 16; ++c) {
37 for(size_t e = 0; e < nSamples; ++e) {
38 full[c][e] = static_cast<int16_t>(base + c * 10 + e);
39 }
40 }
41 full.write();
42}
43
45static void writeNodma(ChimeraTK::Device& device, int16_t base) {
46 writeTwoD(device, "/TEST/NODMA", base);
47}
48
51static void writeNodma2(ChimeraTK::Device& device, int16_t base) {
52 writeTwoD(device, "/TEST/NODMA2", base);
53}
54
55/**********************************************************************************************************************/
56// Create a test suite which holds all the tests.
57BOOST_AUTO_TEST_SUITE(GroupedDemultiplexingTestSuite)
58
59/**********************************************************************************************************************/
60// Read all channel slices of a muxed 2D register in one TransferGroup, added in shuffled (non-contiguous, out-of-order)
61// order so the group members are neither contiguous nor in memory order. Channels 0 and 3 share one conversion key and
62// form one multi-channel group, while channels 1 and 2 are differently keyed, so several groups form within the single
63// shared element. MIXED lives in the 2D area at AREA byte offset 0x100.
64
65BOOST_AUTO_TEST_CASE(TestGroupedReadAllChannels) {
66 auto device = openDevice();
67
68 // Write the MIXED area with exact byte control through the raw AREA accessor. Channel c sample s sits in 4-byte word
69 // (64 + c + s*8): channel offset (0/4/8/12) in the area at word 64, pitch 32 bytes. Each 2-byte value sits in the low
70 // two bytes of the word.
71 auto area = device.getOneDRegisterAccessor<int32_t>("/AREA_DMAABLE", 0, 0, {AccessMode::raw});
72 const std::array<std::array<int16_t, nSamples>, 4> vals = {
73 {{100, 101, 102, 103}, {200, 201, 202, 203}, {30, 31, 32, 33}, {400, 401, 402, 403}}};
74 for(int c = 0; c < 4; ++c) {
75 for(size_t s = 0; s < nSamples; ++s) {
76 int32_t word = 0;
77 std::memcpy(&word, &vals[c][s], 2);
78 area[64 + c + s * 8] = word;
79 }
80 }
81 area.write();
82
83 // Distinct conversion parameters / user types, so they cannot all share one raw conversion group:
84 // 0: signed fixed point, width 16 (int16_t) - one multi-channel group with channel 3
85 // 3: signed fixed point, width 16 (int16_t) - one multi-channel group with channel 0
86 // 1: unsigned fixed point, width 16 (uint16_t) - differently keyed
87 // 2: signed fixed point, width 12 (int16_t) - differently keyed
88 const std::vector<int> channels = {3, 1, 0, 2};
89 std::vector<ChimeraTK::OneDRegisterAccessor<int16_t>> accessors;
90 TransferGroup group;
91 for(int c : channels) {
92 auto a = device.getOneDRegisterAccessor<int16_t>("/TEST/MIXED." + std::to_string(c));
93 accessors.push_back(a);
94 group.addAccessor(a);
95 }
96 group.read();
97
98 for(size_t i = 0; i < channels.size(); ++i) {
99 const int c = channels[i];
100 for(size_t e = 0; e < nSamples; ++e) {
101 BOOST_TEST(accessors[i][e] == vals[c][e]);
102 }
103 }
104
105 // All slices served by the shared raw element deliver the same version number and valid data.
106 for(const auto& accessor : accessors) {
107 BOOST_TEST(accessor.getVersionNumber() == accessors.front().getVersionNumber());
108 BOOST_TEST_REQUIRE(accessor.dataValidity() == ChimeraTK::DataValidity::ok);
109 }
110 device.close();
111}
112
113/**********************************************************************************************************************/
114// Raw-mode slices are grouped by their user type and copied without conversion.
115
116BOOST_AUTO_TEST_CASE(TestRawModeSlices) {
117 auto device = openDevice();
118 writeNodma(device, 9000);
119
120 auto r0 = device.getOneDRegisterAccessor<int16_t>("/TEST/NODMA.0", 0, 0, {AccessMode::raw});
121 auto r5 = device.getOneDRegisterAccessor<int16_t>("/TEST/NODMA.5", 0, 0, {AccessMode::raw});
122 auto r12 = device.getOneDRegisterAccessor<int16_t>("/TEST/NODMA.12", 0, 0, {AccessMode::raw});
123
124 // Slices of a non-interrupt 2D register are read-only.
125 BOOST_TEST(!r0.isWriteable());
126
127 TransferGroup group;
128 group.addAccessor(r12);
129 group.addAccessor(r0);
130 group.addAccessor(r5);
131 group.read();
132
133 for(size_t e = 0; e < nSamples; ++e) {
134 BOOST_TEST(r0[e] == static_cast<int16_t>(9000 + 0 * 10 + e));
135 BOOST_TEST(r5[e] == static_cast<int16_t>(9000 + 5 * 10 + e));
136 BOOST_TEST(r12[e] == static_cast<int16_t>(9000 + 12 * 10 + e));
137 }
138 device.close();
139}
140
141/**********************************************************************************************************************/
142// Slices of two adjacent muxed 2D registers are grouped into one TransferGroup; their channels merge into one shared
143// low-level transfer element whose raw buffer covers both registers, and each slice is demultiplexed from its own byte
144// offset within that merged element.
145
146BOOST_AUTO_TEST_CASE(TestAdjacentRegistersShareMergedElement) {
147 auto device = openDevice();
148 writeNodma(device, 2000);
149 writeNodma2(device, 4000);
150
151 auto ch0 = device.getOneDRegisterAccessor<int16_t>("/TEST/NODMA.1");
152 auto ch4 = device.getOneDRegisterAccessor<int16_t>("/TEST/NODMA.6");
153 auto chA = device.getOneDRegisterAccessor<int16_t>("/TEST/NODMA2.0");
154 auto chB = device.getOneDRegisterAccessor<int16_t>("/TEST/NODMA2.9");
155 auto chC = device.getOneDRegisterAccessor<int16_t>("/TEST/NODMA.15");
156
157 TransferGroup group;
158 group.addAccessor(chC);
159 group.addAccessor(chA);
160 group.addAccessor(ch4);
161 group.addAccessor(chB);
162 group.addAccessor(ch0);
163 group.read();
164
165 // Each slice's byte offset is relative to the single raw buffer covering NODMA and NODMA2.
166 for(size_t e = 0; e < nSamples; ++e) {
167 BOOST_TEST(ch0[e] == static_cast<int16_t>(2000 + 10 + e));
168 BOOST_TEST(ch4[e] == static_cast<int16_t>(2000 + 60 + e));
169 BOOST_TEST(chA[e] == static_cast<int16_t>(4000 + e));
170 BOOST_TEST(chB[e] == static_cast<int16_t>(4000 + 90 + e));
171 BOOST_TEST(chC[e] == static_cast<int16_t>(2000 + 150 + e));
172 }
173
174 // Slices of both adjacent registers must share one low-level element.
175 auto sharedElement = ch0.getHardwareAccessingElements().front();
176 for(auto* accessor : {&ch4, &chA, &chB, &chC}) {
177 BOOST_TEST(accessor->getHardwareAccessingElements().front().get() == sharedElement.get());
178 }
179 device.close();
180}
181
182/**********************************************************************************************************************/
183// A lone scalar register is served through the coordinator staging-and-swap path as an ungrouped single-sample
184// consumer. The value, version number and data validity are asserted.
185
186BOOST_AUTO_TEST_CASE(TestScalarReadThroughCoordinator) {
187 auto device = openDevice();
188
189 auto scalar = device.getScalarRegisterAccessor<int16_t>("/TEST/SCALAR");
190 const auto versionBefore = scalar.getVersionNumber();
191 scalar = 1234;
192 scalar.write();
193
194 scalar.read();
195 BOOST_TEST(scalar == static_cast<int16_t>(1234));
196 BOOST_TEST(scalar.getVersionNumber() > versionBefore);
197 BOOST_TEST_REQUIRE(scalar.dataValidity() == ChimeraTK::DataValidity::ok);
198 device.close();
199}
200
201/**********************************************************************************************************************/
202// A non-muxed 1D array is a non-strided consumer with nSamples > 1 and a stride equal to the element data width.
203
204BOOST_AUTO_TEST_CASE(TestNonMuxedArrayReadThroughCoordinator) {
205 auto device = openDevice();
206
207 auto array = device.getOneDRegisterAccessor<int16_t>("/TEST/ARRAY");
208 BOOST_REQUIRE_EQUAL(array.getNElements(), 4U);
209 for(size_t e = 0; e < array.getNElements(); ++e) {
210 array[e] = static_cast<int16_t>(700 + e);
211 }
212 array.write();
213
214 array.read();
215 for(size_t e = 0; e < array.getNElements(); ++e) {
216 BOOST_TEST(array[e] == static_cast<int16_t>(700 + e));
217 }
218 device.close();
219}
220
221/**********************************************************************************************************************/
222// A scalar and a non-muxed 1D array are merged into one shared low-level transfer element in one TransferGroup, and
223// each is read through its individual byte offset within that element's raw buffer.
224
225BOOST_AUTO_TEST_CASE(TestGroupedNonMuxedConsumers) {
226 auto device = openDevice();
227
228 auto scalar = device.getScalarRegisterAccessor<int16_t>("/TEST/SCALAR");
229 auto array = device.getOneDRegisterAccessor<int16_t>("/TEST/ARRAY");
230 scalar = 4321;
231 scalar.write();
232 for(size_t e = 0; e < array.getNElements(); ++e) {
233 array[e] = static_cast<int16_t>(800 + e);
234 }
235 array.write();
236
237 TransferGroup group;
238 group.addAccessor(scalar);
239 group.addAccessor(array);
240 group.read();
241
242 BOOST_TEST(scalar == static_cast<int16_t>(4321));
243 for(size_t e = 0; e < array.getNElements(); ++e) {
244 BOOST_TEST(array[e] == static_cast<int16_t>(800 + e));
245 }
246
247 // The scalar and the 1D array must merge into one shared low-level element.
248 auto sharedElement = scalar.getHardwareAccessingElements().front();
249 BOOST_TEST(array.getHardwareAccessingElements().front().get() == sharedElement.get());
250 device.close();
251}
252
253/**********************************************************************************************************************/
254// The raw-copy path with a stride equal to the element data width (non-strided raw UserType).
255
256BOOST_AUTO_TEST_CASE(TestRawNonMuxedArray) {
257 auto device = openDevice();
258
259 auto array = device.getOneDRegisterAccessor<int16_t>("/TEST/ARRAY", 0, 0, {AccessMode::raw});
260 for(size_t e = 0; e < array.getNElements(); ++e) {
261 array[e] = static_cast<int16_t>(600 + e);
262 }
263 array.write();
264
265 array.read();
266 for(size_t e = 0; e < array.getNElements(); ++e) {
267 BOOST_TEST(array[e] == static_cast<int16_t>(600 + e));
268 }
269 device.close();
270}
271
272BOOST_AUTO_TEST_SUITE_END()
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
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
Group multiple data accessors to efficiently trigger data transfers on the whole group.
void addAccessor(TransferElementAbstractor &accessor)
Add a register accessor to the group.
void read()
Trigger read transfer for all accessors in the group.
This header collects some conversion routines which should be useful both for ApplicationCore Python ...
std::string to_string(const std::string &v)
ctk::Device device
constexpr size_t nSamples
BOOST_AUTO_TEST_CASE(TestGroupedReadAllChannels)