Project
Loading...
Searching...
No Matches
testUserLogicEndpointDecoder.cxx
Go to the documentation of this file.
1// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
11
14
15#define BOOST_TEST_MODULE Test MCHRaw UserLogicElinkDecoder
16#define BOOST_TEST_MAIN
17#define BOOST_TEST_DYN_LINK
18
19#include <boost/test/unit_test.hpp>
20#include "Assertions.h"
22#include "DumpBuffer.h"
29#include "MoveBuffer.h"
30#include "RDHManip.h"
32#include <fmt/printf.h>
33#include <fstream>
34#include <iostream>
35#include <boost/test/data/test_case.hpp>
36#include <boost/mpl/list.hpp>
37
38using namespace o2::mch::raw;
39namespace bdata = boost::unit_test::data;
40
41const uint64_t CruPageOK[] = {
42 0x00000A0000124006ul,
43 0x000C4C0F00A000A0ul,
44 0x010E853D00000570ul,
45 0x0000000000000000ul,
46 0x0000000000006000ul,
47 0x0000000000000000ul,
48 0x0000000000000000ul,
49 0x0000000000000000ul,
50 ((0x0200ul << 50) & 0xFFFC000000000000ul) + 0x1555540F00113ul,
51 ((0x0200ul << 50) & 0xFFFC000000000000ul) + 0x3F04ECA103E5Cul,
52 ((0x0200ul << 50) & 0xFFFC000000000000ul) + 0x0000040215C0Dul,
53 ((0x0200ul << 50) & 0xFFFC000000000000ul) + 0x00000C0301004ul,
54 ((0x0204ul << 50) & 0xFFFC000000000000ul) + 0x0000000000400ul,
55 ((0x0200ul << 50) & 0xFFFC000000000000ul) + 0x1555540F00113ul,
56 ((0x0200ul << 50) & 0xFFFC000000000000ul) + 0x1F080CA100E4Dul,
57 ((0x0204ul << 50) & 0xFFFC000000000000ul) + 0x00044C0100001ul,
58 ((0x3FBBul << 50) & 0xFFFC000000000000ul) + 0x1DEEDFEEDDEEDul,
59 ((0x3FBBul << 50) & 0xFFFC000000000000ul) + 0x1DEEDFEEDDEEDul,
60 ((0x3FBBul << 50) & 0xFFFC000000000000ul) + 0x1DEEDFEEDDEEDul,
61 ((0x3FBBul << 50) & 0xFFFC000000000000ul) + 0x1DEEDFEEDDEEDul};
62
63const uint64_t CruPageBadClusterSize[] = {
64 0x00000A0000124006ul,
65 0x000C4C0F00A000A0ul,
66 0x010E853D00000570ul,
67 0x0000000000000000ul,
68 0x0000000000006000ul,
69 0x0000000000000000ul,
70 0x0000000000000000ul,
71 0x0000000000000000ul,
72 ((0x0200ul << 50) & 0xFFFC000000000000ul) + 0x1555540F00113ul,
73 ((0x0200ul << 50) & 0xFFFC000000000000ul) + 0x3F04ECA103E5Cul,
74 ((0x0200ul << 50) & 0xFFFC000000000000ul) + 0x0000040215C0Eul, // <== the cluster size is increased from 13 (0xD) to 14 (0xE)
75 ((0x0200ul << 50) & 0xFFFC000000000000ul) + 0x00000C0301004ul, // now the cluster size does not match anymore with the
76 ((0x0204ul << 50) & 0xFFFC000000000000ul) + 0x0000000000400ul, // number of 10-bit words in the SAMPA header, which will trigger
77 ((0x0200ul << 50) & 0xFFFC000000000000ul) + 0x1555540F00113ul, // a ErrorBadClusterSize error.
78 ((0x0200ul << 50) & 0xFFFC000000000000ul) + 0x1F080CA100E4Dul,
79 ((0x0204ul << 50) & 0xFFFC000000000000ul) + 0x00044C0100001ul,
80 ((0x3FBBul << 50) & 0xFFFC000000000000ul) + 0x1DEEDFEEDDEEDul,
81 ((0x3FBBul << 50) & 0xFFFC000000000000ul) + 0x1DEEDFEEDDEEDul,
82 ((0x3FBBul << 50) & 0xFFFC000000000000ul) + 0x1DEEDFEEDDEEDul,
83 ((0x3FBBul << 50) & 0xFFFC000000000000ul) + 0x1DEEDFEEDDEEDul};
84
85const uint64_t CruPageBadN10bitWords[] = {
86 0x00000A0000124006ul,
87 0x000C4C0F00A000A0ul,
88 0x010E853D00000570ul,
89 0x0000000000000000ul,
90 0x0000000000006000ul,
91 0x0000000000000000ul,
92 0x0000000000000000ul,
93 0x0000000000000000ul,
94 ((0x0200ul << 50) & 0xFFFC000000000000ul) + 0x1555540F00113ul,
95 ((0x0200ul << 50) & 0xFFFC000000000000ul) + 0x3F04ECA103E5Cul,
96 ((0x0200ul << 50) & 0xFFFC000000000000ul) + 0x0000040215C08ul, // <== the cluster size is decreased from 13 (0xD) to 8 (0x8)
97 //((0x0200ul<<50)&0xFFFC000000000000ul) + 0x00000C0301004ul, // and one 50-bit word is removed. In this case the cluster
98 ((0x0204ul << 50) & 0xFFFC000000000000ul) + 0x0000000000400ul, // size matches the number of samples in the data, but the
99 ((0x0200ul << 50) & 0xFFFC000000000000ul) + 0x1555540F00113ul, // end of the SAMPA packet arrives too early with respect to
100 ((0x0200ul << 50) & 0xFFFC000000000000ul) + 0x1F080CA100E4Dul, // the number of 10-bit words in the SAMPA header. This will
101 ((0x0204ul << 50) & 0xFFFC000000000000ul) + 0x00044C0100001ul, // trigger a ErrorBadIncompleteWord error.
102 ((0x3FBBul << 50) & 0xFFFC000000000000ul) + 0x1DEEDFEEDDEEDul,
103 ((0x3FBBul << 50) & 0xFFFC000000000000ul) + 0x1DEEDFEEDDEEDul,
104 ((0x3FBBul << 50) & 0xFFFC000000000000ul) + 0x1DEEDFEEDDEEDul,
105 ((0x3FBBul << 50) & 0xFFFC000000000000ul) + 0x1DEEDFEEDDEEDul, // <== a word is added at the end in order to match the
106 ((0x3FBBul << 50) & 0xFFFC000000000000ul) + 0x1DEEDFEEDDEEDul // payload size in the RDH
107};
108
110{
111 return [&result](DsElecId dsId, DualSampaChannelId channel, SampaCluster sc) {
112 result += fmt::format("{}-ch-{}-ts-{}-q", asString(dsId), channel, sc.sampaTime);
113 if (sc.isClusterSum()) {
114 result += fmt::format("-{}-cs-{}", sc.chargeSum, sc.clusterSize);
115 } else {
116 for (auto s : sc.samples) {
117 result += fmt::format("-{}", s);
118 }
119 }
120 result += "\n";
121 };
122}
123
125{
126 return [&result](DsElecId dsId, int8_t chip, uint32_t error) {
127 result += fmt::format("{}-chip-{}-error-{}", asString(dsId), chip, error);
128 result += "\n";
129 };
130}
131
132std::vector<std::byte> convertBuffer2PayloadBuffer(gsl::span<const std::byte> buffer,
133 std::optional<size_t> insertSync = std::nullopt)
134{
135 // some gym to go from a buffer coming from the encoder,
136 // which holds (DataBlockHeader,Payload) pairs in a byte buffer
137 // to a buffer that holds just Payload information
138 // to be fed to the decoder
139
140 // strip the headers
141 std::vector<std::byte> b8;
143 buffer, [&](const DataBlockRef& r) {
144 auto& b = r.block;
145 b8.insert(b8.end(), b.payload.begin(), b.payload.end());
146 });
147
148 // convert to a 64-bits buffer to be able to insert sync if needed
149 std::vector<uint64_t> b64;
150 impl::copyBuffer(b8, b64);
151
152 // insert a sync at the given position if required
153 if (insertSync.has_value() && insertSync.value() < b64.size()) {
154 uint64_t prefix = b64[0] & 0xFFFC00000000000F;
155 b64.insert(b64.begin() + insertSync.value(), prefix | sampaSyncWord);
156 }
157
158 // get back to byte buffer to return
159 std::vector<std::byte> bytes;
160 impl::copyBuffer(b64, bytes);
161 return bytes;
162}
163
164template <typename CHARGESUM, int VERSION>
165std::string decodeBuffer(int feeId, gsl::span<const std::byte> buffer)
166{
167 std::string results;
169 DecodedDataHandlers handlers;
170 handlers.sampaChannelHandler = handlePacket(results);
171 handlers.sampaErrorHandler = handleError(results);
172 UserLogicEndpointDecoder<CHARGESUM, VERSION> dec(feeId, fee2solar, handlers);
173 dec.append(buffer);
174 return results;
175}
176
177template <typename CHARGESUM, int VERSION>
180 const std::vector<SampaCluster>& clustersFirstChannel,
181 DsElecId ds2 = DsElecId{0, 0, 0},
182 DualSampaChannelId ch2 = 47,
183 const std::vector<SampaCluster>& clustersSecondChannel = {},
184 std::optional<size_t> insertSync = std::nullopt)
185{
186
188
189 auto encoder = createPayloadEncoder(solar2feelink, true, VERSION, isChargeSumMode<CHARGESUM>::value);
190
191 encoder->startHeartbeatFrame(0, 0);
192
193 uint16_t feeId{0};
194
195 auto f1 = solar2feelink(ds1.solarId());
196 if (!f1.has_value()) {
197 throw std::invalid_argument("invalid solarId for ds1");
198 }
199 if (!clustersSecondChannel.empty()) {
200 auto f2 = solar2feelink(ds2.solarId());
201 if (!f2.has_value()) {
202 throw std::invalid_argument("invalid solarId for ds2");
203 }
204 if (f2->feeId() != f1->feeId()) {
205 throw std::invalid_argument("this test is only meant to work with 2 solars in the same cru endpoint");
206 }
207 }
208
209 feeId = f1->feeId();
210
211 encoder->addChannelData(ds1, ch1, clustersFirstChannel);
212 if (!clustersSecondChannel.empty()) {
213 encoder->addChannelData(ds2, ch2, clustersSecondChannel);
214 }
215
216 std::vector<std::byte> buffer;
217 encoder->moveToBuffer(buffer);
218 auto payloadBuffer = convertBuffer2PayloadBuffer(buffer, insertSync);
219
220 return decodeBuffer<CHARGESUM, VERSION>(feeId, payloadBuffer);
221}
222
223template <int VERSION>
224std::vector<uint64_t> convert(gsl::span<const uint64_t> page);
225
226template <>
227std::vector<uint64_t> convert<0>(gsl::span<const uint64_t> page)
228{
229 return {page.begin(), page.end()};
230}
231
232template <>
233std::vector<uint64_t> convert<1>(gsl::span<const uint64_t> page)
234{
235 // convert the 14 MSB bits of page, expressed using V0 spec,
236 // to match the V1 spec
237 std::vector<uint64_t> pagev1{page.begin(), page.end()};
238 constexpr int rdhSize{8};
239 for (int i = rdhSize; i < pagev1.size(); i++) {
240 if (pagev1[i] == 0xFEEDDEEDFEEDDEED || pagev1[i] == 0) {
241 // do not mess with padding words
242 continue;
243 }
244 ULHeaderWord<0> v0{pagev1[i]};
246 v1.data = v0.data;
247 v1.error = v0.error;
248 v1.incomplete = v0.incomplete;
249 v1.dsID = v0.dsID;
250 v1.linkID = v0.linkID;
251 pagev1[i] = v1.word;
252 }
253 return pagev1;
254}
255
256template <int VERSION = 0>
257std::string testPayloadDecodeCruPages(gsl::span<const uint64_t> ipage)
258{
259 std::vector<uint64_t> page = convert<VERSION>(ipage);
260
261 const void* rdhP = reinterpret_cast<const void*>(page.data());
262 uint16_t feeId = o2::raw::RDHUtils::getFEEID(rdhP);
263 auto rdhSize = o2::raw::RDHUtils::getHeaderSize(rdhP);
264 auto payloadSize = o2::raw::RDHUtils::getMemorySize(rdhP) - rdhSize;
265
266 gsl::span<const std::byte> buffer(reinterpret_cast<const std::byte*>(page.data()), page.size() * 8);
267 gsl::span<const std::byte> payloadBuffer = buffer.subspan(rdhSize, payloadSize);
268
269 o2::mch::raw::FEEID f{feeId};
270
271 if (f.chargeSum) {
272 return decodeBuffer<ChargeSumMode, VERSION>(f.id, payloadBuffer);
273 } else {
274 return decodeBuffer<SampleMode, VERSION>(f.id, payloadBuffer);
275 }
276}
277
278struct V0 {
279 static constexpr int value = 0;
280};
281struct V1 {
282 static constexpr int value = 1;
283};
284
285typedef boost::mpl::list<V0, V1> testTypes;
286
287BOOST_AUTO_TEST_SUITE(o2_mch_raw)
288
289BOOST_AUTO_TEST_SUITE(userlogicdsdecoder)
290
292{
293 // only one channel with one very small cluster
294 // fitting within one 64-bits word
295 SampaCluster cl(345, 6789, {123, 456});
296 auto r = testPayloadDecode<SampleMode, V::value>(DsElecId{728, 1, 0}, 63, {cl});
297 BOOST_CHECK_EQUAL(r, "S728-J1-DS0-ch-63-ts-345-q-123-456\n");
298}
299
301{
302 // only one channel with one cluster, but the cluster
303 // spans 2 64-bits words.
304 SampaCluster cl(345, 6789, {123, 456, 789, 901, 902});
305 auto r = testPayloadDecode<SampleMode, V::value>(DsElecId{448, 6, 4}, 63, {cl});
306 BOOST_CHECK_EQUAL(r, "S448-J6-DS4-ch-63-ts-345-q-123-456-789-901-902\n");
307}
308
309BOOST_AUTO_TEST_CASE_TEMPLATE(SampleModeTwoChannels, V, testTypes)
310{
311 // 2 channels with one cluster
312 SampaCluster cl(345, 6789, {123, 456, 789, 901, 902});
313 SampaCluster cl2(346, 6789, {1001, 1002, 1003, 1004, 1005, 1006, 1007});
314 auto r = testPayloadDecode<SampleMode, V::value>(DsElecId{361, 6, 2}, 63, {cl}, DsElecId{361, 6, 2}, 47, {cl2});
316 "S361-J6-DS2-ch-63-ts-345-q-123-456-789-901-902\n"
317 "S361-J6-DS2-ch-47-ts-346-q-1001-1002-1003-1004-1005-1006-1007\n");
318}
319
320BOOST_AUTO_TEST_CASE_TEMPLATE(ChargeSumModeSimplest, V, testTypes)
321{
322 // only one channel with one cluster
323 // (hence fitting within one 64 bits word)
324 SampaCluster cl(345, 6789, 123456, 789);
325 auto r = testPayloadDecode<ChargeSumMode, V::value>(DsElecId{728, 1, 0}, 63, {cl});
326 BOOST_CHECK_EQUAL(r, "S728-J1-DS0-ch-63-ts-345-q-123456-cs-789\n");
327}
328
330{
331 // only one channel with 2 clusters
332 // (hence spanning 2 64-bits words)
333 SampaCluster cl1(345, 6789, 123456, 789);
334 SampaCluster cl2(346, 6789, 789012, 345);
335 auto r = testPayloadDecode<ChargeSumMode, V::value>(DsElecId{448, 6, 4}, 63, {cl1, cl2});
337 "S448-J6-DS4-ch-63-ts-345-q-123456-cs-789\n"
338 "S448-J6-DS4-ch-63-ts-346-q-789012-cs-345\n");
339}
340
341BOOST_AUTO_TEST_CASE_TEMPLATE(ChargeSumModeTwoChannels, V, testTypes)
342{
343 // two channels with 2 clusters
344 SampaCluster cl1(345, 6789, 123456, 789);
345 SampaCluster cl2(346, 6789, 789012, 345);
346 SampaCluster cl3(347, 6789, 1357, 890);
347 SampaCluster cl4(348, 6789, 7912, 345);
348 auto r = testPayloadDecode<ChargeSumMode, V::value>(DsElecId{361, 6, 2}, 63, {cl1, cl2}, DsElecId{361, 6, 2}, 47, {cl3, cl4});
350 "S361-J6-DS2-ch-63-ts-345-q-123456-cs-789\n"
351 "S361-J6-DS2-ch-63-ts-346-q-789012-cs-345\n"
352 "S361-J6-DS2-ch-47-ts-347-q-1357-cs-890\n"
353 "S361-J6-DS2-ch-47-ts-348-q-7912-cs-345\n");
354}
355
356BOOST_AUTO_TEST_CASE_TEMPLATE(SyncInTheMiddleChargeSumModeTwoChannels, V, testTypes)
357{
358 // Insert a sync word in the middle of
359 // the TwoChannels case and check the decoder is handling this fine
360 // (by just returning to wait for sync mode, i.e. dropping the 2nd part
361 // of the communication until a second sync)
362 SampaCluster cl1(345, 6789, 123456, 789);
363 SampaCluster cl2(346, 6789, 789012, 345);
364 SampaCluster cl3(347, 6789, 1357, 890);
365 SampaCluster cl4(348, 6789, 7912, 345);
366 auto r = testPayloadDecode<ChargeSumMode, V::value>(
367 DsElecId{361, 6, 2}, 63, {cl1, cl2},
368 DsElecId{361, 6, 2}, 47, {cl3, cl4},
369 5);
370 std::string r2 = "S361-J6-DS2-ch-63-ts-345-q-123456-cs-789\n";
371 r2 += "S361-J6-DS2-ch-63-ts-346-q-789012-cs-345\n";
372 r2 += fmt::format("S361-J6-DS2-chip-5-error-{}\n", (int)ErrorUnexpectedSyncPacket);
373 BOOST_CHECK_EQUAL(r, r2);
374}
375
377{
378 gsl::span<const uint64_t> page = CruPageOK;
379 std::string r = testPayloadDecodeCruPages<V::value>(page);
381 "S81-J0-DS0-ch-42-ts-87-q-2-1-0-4-4-3-3-0-0-1-0-0-0\n"
382 "S81-J0-DS0-ch-42-ts-0-q-1\n");
383}
384
385BOOST_AUTO_TEST_CASE_TEMPLATE(TestCruPageBadClusterSize, V, testTypes)
386{
387 gsl::span<const uint64_t> page = CruPageBadClusterSize;
388 std::string r = testPayloadDecodeCruPages<V::value>(page);
390 fmt::format("S81-J0-DS0-chip-1-error-{}\nS81-J0-DS0-ch-42-ts-0-q-1\n", (int)ErrorBadClusterSize));
391}
392
393BOOST_AUTO_TEST_CASE_TEMPLATE(TestCruPageBadN10bitWords, V, testTypes)
394{
395 gsl::span<const uint64_t> page = CruPageBadN10bitWords;
396 std::string r = testPayloadDecodeCruPages<V::value>(page);
397 std::string expected =
398 fmt::format("S81-J0-DS0-ch-42-ts-87-q-2-1-0-0-1-0-0-0\nS81-J0-DS0-chip-1-error-{}\nS81-J0-DS0-ch-42-ts-0-q-1\n",
401}
402
403BOOST_AUTO_TEST_SUITE_END()
404BOOST_AUTO_TEST_SUITE_END()
int32_t i
constexpr uint16_t solarId() const
solarId is an identifier that uniquely identify a solar board
Definition DsElecId.h:50
A UserLogicEndpointDecoder groups 12 x (40 UserLogicElinkDecoder objects)
size_t append(Payload bytes)
Append the equivalent n 64-bits words bytes size (=n) must be a multiple of 8.
GLuint64EXT * result
Definition glcorearb.h:5662
GLuint buffer
Definition glcorearb.h:655
GLdouble f
Definition glcorearb.h:310
GLboolean GLboolean GLboolean b
Definition glcorearb.h:1233
GLsizei const GLfloat * value
Definition glcorearb.h:819
GLfloat v0
Definition glcorearb.h:811
GLfloat GLfloat v1
Definition glcorearb.h:812
GLboolean r
Definition glcorearb.h:1233
std::function< std::optional< uint16_t >(FeeLinkId)> createFeeLink2SolarMapper< ElectronicMapperGenerated >()
@ ErrorBadIncompleteWord
Definition ErrorCodes.h:33
@ ErrorBadClusterSize
Definition ErrorCodes.h:32
@ ErrorUnexpectedSyncPacket
Definition ErrorCodes.h:36
constexpr uint64_t sampaSyncWord
std::function< void(DsElecId dsId, DualSampaChannelId channel, SampaCluster)> SampaChannelHandler
uint6_t DualSampaChannelId
Definition DataFormats.h:65
std::string asString(const SampaCluster &sc)
int forEachDataBlockRef(gsl::span< const std::byte > buffer, std::function< void(DataBlockRef ref)> f)
Definition DataBlock.cxx:23
std::function< void(DsElecId dsId, int8_t chip, uint32_t error)> SampaErrorHandler
std::function< std::optional< FeeLinkId >(uint16_t)> createSolar2FeeLinkMapper< ElectronicMapperGenerated >()
std::unique_ptr< PayloadEncoder > createPayloadEncoder(Solar2FeeLinkMapper solar2feelink, bool userLogic, int version, bool chargeSumMode)
value_T f1
Definition TrackUtils.h:91
value_T f2
Definition TrackUtils.h:92
a DataBlockRef is a pair (DataBlock,offset) The offset is an offset into some external buffer
Definition DataBlock.h:45
Piece of data for one Sampa channel.
std::map< std::string, ID > expected
boost::mpl::list< o2::dcs::DataPointIdentifier, o2::dcs::DataPointValue, o2::dcs::DataPointCompositeObject > testTypes
SampaErrorHandler handleError(std::string &result)
const uint64_t CruPageBadN10bitWords[]
std::vector< uint64_t > convert< 0 >(gsl::span< const uint64_t > page)
std::vector< std::byte > convertBuffer2PayloadBuffer(gsl::span< const std::byte > buffer, std::optional< size_t > insertSync=std::nullopt)
std::vector< uint64_t > convert(gsl::span< const uint64_t > page)
BOOST_AUTO_TEST_CASE_TEMPLATE(SampleModeSimplest, V, testTypes)
std::vector< uint64_t > convert< 1 >(gsl::span< const uint64_t > page)
std::string testPayloadDecodeCruPages(gsl::span< const uint64_t > ipage)
const uint64_t CruPageBadClusterSize[]
boost::mpl::list< V0, V1 > testTypes
std::string decodeBuffer(int feeId, gsl::span< const std::byte > buffer)
SampaChannelHandler handlePacket(std::string &result)
std::string testPayloadDecode(DsElecId ds1, DualSampaChannelId ch1, const std::vector< SampaCluster > &clustersFirstChannel, DsElecId ds2=DsElecId{0, 0, 0}, DualSampaChannelId ch2=47, const std::vector< SampaCluster > &clustersSecondChannel={}, std::optional< size_t > insertSync=std::nullopt)
const uint64_t CruPageOK[]
BOOST_CHECK_EQUAL(triggersD.size(), triggers.size())