Project
Loading...
Searching...
No Matches
UserLogicElinkDecoder.h
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
12#ifndef O2_MCH_RAW_USER_LOGIC_ELINK_DECODER_H
13#define O2_MCH_RAW_USER_LOGIC_ELINK_DECODER_H
14
15#include "Debug.h"
21#include <bitset>
22#include <fmt/format.h>
23#include <fmt/printf.h>
24#include <functional>
25#include <iostream>
26#include <stdexcept>
27#include <vector>
28#include <sstream>
29#include <set>
30
31namespace o2::mch::raw
32{
33
34template <typename CHARGESUM>
36{
37 public:
38 UserLogicElinkDecoder(DsElecId dsId, DecodedDataHandlers decodedDataHandlers);
39
41 void append(uint64_t data50, uint8_t error, bool incomplete);
42
45 void reset();
46
47 private:
49 enum class State : int {
50 WaitingSync,
51 WaitingHeader,
52 WaitingSize,
53 WaitingTime,
54 WaitingSample
55 };
56 std::string asString(State state) const;
57
58 using uint10_t = uint16_t;
59
60 template <typename T>
61 friend std::ostream& operator<<(std::ostream& os, const o2::mch::raw::UserLogicElinkDecoder<T>& e);
62
63 const DsElecId& dsId() const { return mDsId; }
64
65 void clear();
66 bool hasError() const;
67 bool isHeaderComplete() const { return mHeaderParts.size() == 5; }
68 bool moreSampleToRead() const { return mSamplesToRead > 0; }
69 bool moreWordsToRead() const { return mNof10BitWords > 0; }
70 std::ostream& debugHeader() const;
71 std::string errorMessage() const;
72 bool append10(uint10_t data10);
73 void completeHeader();
74 void oneLess10BitWord();
75 void prepareAndSendCluster();
76 bool checkDataHeader();
77 void sendCluster(const SampaCluster& sc) const;
78 void sendHBPacket();
79 void sendError(int8_t chip, uint32_t error) const;
80 void setClusterSize(uint10_t value);
81 void setClusterTime(uint10_t value);
82 void setHeaderPart(uint10_t data10);
83 void setSample(uint10_t value);
84 void transition(State to);
85
86 private:
87 DsElecId mDsId;
88 DecodedDataHandlers mDecodedDataHandlers;
89 State mState;
90 std::vector<uint10_t> mSamples{};
91 std::vector<uint10_t> mHeaderParts{};
92 SampaHeader mSampaHeader{};
93 uint10_t mNof10BitWords{};
94 uint10_t mClusterSize{};
95 uint10_t mSamplesToRead{};
96 uint10_t mClusterTime{};
97 std::optional<std::string> mErrorMessage{std::nullopt};
98};
99
100constexpr bool isSync(uint64_t data)
101{
102 constexpr uint64_t sampaSyncWord{0x1555540f00113};
103 return data == sampaSyncWord;
104};
105
106template <typename CHARGESUM>
108 DecodedDataHandlers decodedDataHandlers)
109 : mDsId{dsId}, mDecodedDataHandlers{decodedDataHandlers}, mState{State::WaitingSync}
110{
111}
112
113template <typename CHARGESUM>
114void UserLogicElinkDecoder<CHARGESUM>::append(uint64_t data50, uint8_t error, bool incomplete)
115{
116#ifdef ULDEBUG
117 debugHeader() << (*this) << fmt::format(" --> append50 {:013x} error {} incomplete {} data10={:d} {:d} {:d} {:d} {:d}\n", data50, error, incomplete, static_cast<uint10_t>(data50 & 0x3FF), static_cast<uint10_t>((data50 & 0xFFC00) >> 10), static_cast<uint10_t>((data50 & 0x3FF00000) >> 20), static_cast<uint10_t>((data50 & 0xFFC0000000) >> 30), static_cast<uint10_t>((data50 & 0x3FF0000000000) >> 40));
118#endif
119
120 if (error & 0x1) {
121#ifdef ULDEBUG
122 debugHeader() << (*this) << " data truncated by User Logic --> resetting\n";
123#endif
124 sendError(static_cast<int8_t>(mSampaHeader.chipAddress()), static_cast<uint32_t>(ErrorTruncatedDataUL));
125 reset();
126 return;
127 }
128
129 if (isSync(data50)) {
130#ifdef ULDEBUG
131 debugHeader() << (*this) << fmt::format(" --> SYNC word found {:013x} state={}\n", data50, asString(mState));
132#endif
133 if (mState != State::WaitingHeader && mState != State::WaitingSync) {
134#ifdef ULDEBUG
135 debugHeader() << (*this) << " SYNC word found while decoding payload --> resetting\n";
136#endif
137 sendError(static_cast<int8_t>(mSampaHeader.chipAddress()), static_cast<uint32_t>(ErrorUnexpectedSyncPacket));
138 reset();
139 } else {
140 clear();
141 transition(State::WaitingHeader);
142 }
143 return;
144 }
145
146 auto data = data50;
147
148 int i;
149 for (i = 0; i < 5; i++) {
150 bool packetEnd = append10(static_cast<uint10_t>(data & 0x3FF));
151 data >>= 10;
152#ifdef ULDEBUG
153 if (incomplete) {
154 debugHeader() << (*this) << fmt::format(" --> incomplete {} packetEnd @i={}\n", incomplete, packetEnd, i);
155 }
156#endif
157 if (hasError()) {
158#ifdef ULDEBUG
159 debugHeader() << (*this) << " reset due to hasError\n";
160#endif
161 reset();
162 break;
163 }
164 if (incomplete && packetEnd) {
165#ifdef ULDEBUG
166 debugHeader() << (*this) << " stop due to isIncomplete\n";
167#endif
168 break;
169 }
170 }
171
172 if (incomplete && (i == 5) && (mState != State::WaitingSync)) {
173#ifdef ULDEBUG
174 debugHeader() << (*this) << " data packet end not found when isIncomplete --> resetting\n";
175#endif
176 sendError(static_cast<int8_t>(mSampaHeader.chipAddress()), static_cast<uint32_t>(ErrorBadIncompleteWord));
177 reset();
178 }
179} // namespace o2::mch::raw
180
181template <typename CHARGESUM>
183
184template <>
186 static constexpr uint8_t value = 1;
187};
188
189template <>
191 static constexpr uint8_t value = 2;
192};
193
194template <typename CHARGESUM>
196{
197 bool result = false;
198#ifdef ULDEBUG
199 debugHeader() << (*this) << fmt::format(" --> data10 {:d} state {}\n", data10, asString(mState));
200#endif
201 switch (mState) {
202 case State::WaitingHeader:
203 setHeaderPart(data10);
204 if (isHeaderComplete()) {
205 completeHeader();
206 if (mSampaHeader.packetType() == SampaPacketType::Sync) {
207 if (!isSync(mSampaHeader.uint64())) {
208 mErrorMessage = "badly formatted Sync packet";
209 sendError(static_cast<int8_t>(mSampaHeader.chipAddress()), static_cast<uint32_t>(ErrorBadSyncPacket));
210 }
211 reset();
212 } else if (mSampaHeader.packetType() == SampaPacketType::HeartBeat) {
213 if (mSampaHeader.isHeartbeat()) {
214 sendHBPacket();
215 transition(State::WaitingHeader);
216 result = true;
217 } else {
218 mErrorMessage = "badly formatted Heartbeat packet";
219 sendError(static_cast<int8_t>(mSampaHeader.chipAddress()), static_cast<uint32_t>(ErrorBadHeartBeatPacket));
220 reset();
221 }
222 } else {
223 if (checkDataHeader()) {
224 if (mSampaHeader.nof10BitWords() > 2) {
225 transition(State::WaitingSize);
226 } else {
227 reset();
228 }
229 } else {
230 mErrorMessage = "badly formatted Data packet";
231 sendError(static_cast<int8_t>(mSampaHeader.chipAddress()), static_cast<uint32_t>(ErrorBadDataPacket));
232 reset();
233 }
234 }
235 }
236 break;
237 case State::WaitingSize:
238 if (moreWordsToRead()) {
239 setClusterSize(data10);
240 if (hasError()) {
241 return false;
242 }
243 transition(State::WaitingTime);
244 } else {
245 mErrorMessage = "WaitingSize but no more words";
246 return false;
247 }
248 break;
249 case State::WaitingTime:
250 if (moreWordsToRead()) {
251 setClusterTime(data10);
252 transition(State::WaitingSample);
253 } else {
254 mErrorMessage = "WaitingTime but no more words";
255 return false;
256 }
257 break;
258 case State::WaitingSample:
259 if (moreSampleToRead()) {
260 setSample(data10);
261 }
262 if (!moreSampleToRead()) {
263 if (moreWordsToRead()) {
264 transition(State::WaitingSize);
265 } else {
266 transition(State::WaitingHeader);
267 result = true;
268 }
269 }
270 break;
271 default:
272 break;
273 };
274 return result;
275}
276
277template <typename CHARGESUM>
278std::string UserLogicElinkDecoder<CHARGESUM>::asString(State s) const
279{
280 switch (s) {
281 case State::WaitingSync:
282 return "WaitingSync";
283 break;
284 case State::WaitingHeader:
285 return "WaitingHeader";
286 break;
287 case State::WaitingSize:
288 return "WaitingSize";
289 break;
290 case State::WaitingTime:
291 return "WaitingTime";
292 break;
293 case State::WaitingSample:
294 return "WaitingSample";
295 break;
296 default:
297 return "Unknown";
298 break;
299 };
300}
301
302template <typename CHARGESUM>
303void UserLogicElinkDecoder<CHARGESUM>::clear()
304{
305 mSamples.clear();
306 mHeaderParts.clear();
307 mNof10BitWords = 0;
308 mClusterSize = 0;
309 mErrorMessage = std::nullopt;
310}
311
312template <typename CHARGESUM>
313void UserLogicElinkDecoder<CHARGESUM>::completeHeader()
314{
315 uint64_t header{0};
316 for (auto i = 0; i < mHeaderParts.size(); i++) {
317 header += (static_cast<uint64_t>(mHeaderParts[i]) << (10 * i));
318 }
319
320 mSampaHeader = SampaHeader(header);
321 mNof10BitWords = mSampaHeader.nof10BitWords();
322
323#ifdef ULDEBUG
324 debugHeader() << (*this) << fmt::format(" --> completeHeader {:013X}\n", header);
325 debugHeader() << "\n";
326 std::stringstream s(o2::mch::raw::asString(mSampaHeader));
327 std::string part;
328 while (std::getline(s, part, '\n')) {
329 debugHeader() << (*this) << part << "\n";
330 }
331 debugHeader() << "\n";
332#endif
333
334 mHeaderParts.clear();
335}
336
337template <typename CHARGESUM>
338std::ostream& UserLogicElinkDecoder<CHARGESUM>::debugHeader() const
339{
340 return std::cout << "---";
341}
342
343template <typename CHARGESUM>
344std::string UserLogicElinkDecoder<CHARGESUM>::errorMessage() const
345{
346 return hasError() ? mErrorMessage.value() : "";
347}
348
349template <typename CHARGESUM>
350bool UserLogicElinkDecoder<CHARGESUM>::hasError() const
351{
352 return mErrorMessage.has_value();
353}
354
355template <typename CHARGESUM>
357{
358#ifdef ULDEBUG
359 debugHeader() << (*this) << " ---> reset\n";
360#endif
361 clear();
362 transition(State::WaitingSync);
363}
364
365template <typename CHARGESUM>
367{
368#ifdef ULDEBUG
369 debugHeader() << (*this) << " --> "
370 << fmt::format(" calling channelHandler for {} ch {} = {}\n",
372 getDualSampaChannelId(mSampaHeader),
374#endif
375 mDecodedDataHandlers.sampaChannelHandler(mDsId, getDualSampaChannelId(mSampaHeader), sc);
376}
377
378template <typename CHARGESUM>
379void UserLogicElinkDecoder<CHARGESUM>::sendError(int8_t chip, uint32_t error) const
380{
381#ifdef ULDEBUG
382 debugHeader() << (*this) << " --> "
383 << fmt::format(" calling errorHandler for {} chip {} = {}\n",
384 o2::mch::raw::asString(mDsId), chip, error);
385#endif
386 SampaErrorHandler handler = mDecodedDataHandlers.sampaErrorHandler;
387 if (handler) {
388 handler(mDsId, chip, error);
389 }
390}
391
392template <typename CHARGESUM>
393void UserLogicElinkDecoder<CHARGESUM>::oneLess10BitWord()
394{
395 mNof10BitWords = std::max(0, mNof10BitWords - 1);
396}
397
398template <typename CHARGESUM>
399void UserLogicElinkDecoder<CHARGESUM>::setClusterSize(uint10_t value)
400{
401 oneLess10BitWord();
402 mClusterSize = value;
403 if (CHARGESUM()()) {
404 mSamplesToRead = 2;
405 } else {
406 mSamplesToRead = mClusterSize;
407 }
408 int checkSize = mSamplesToRead + 2 - mSampaHeader.nof10BitWords();
409 mErrorMessage = std::nullopt;
410 if (mClusterSize == 0) {
411 mErrorMessage = "cluster size is zero";
412 sendError(static_cast<int8_t>(mSampaHeader.chipAddress()), static_cast<uint32_t>(ErrorBadClusterSize));
413 }
414 if (checkSize > 0) {
415 mErrorMessage = "number of samples bigger than nof10BitWords";
416 sendError(static_cast<int8_t>(mSampaHeader.chipAddress()), static_cast<uint32_t>(ErrorBadClusterSize));
417 }
418#ifdef ULDEBUG
419 debugHeader() << (*this) << " --> size=" << mClusterSize << " samples=" << mSamplesToRead << "\n";
420#endif
421}
422
423template <typename CHARGESUM>
424void UserLogicElinkDecoder<CHARGESUM>::setClusterTime(uint10_t value)
425{
426 oneLess10BitWord();
427 mClusterTime = value;
428#ifdef ULDEBUG
429 debugHeader() << (*this) << " --> time=" << mClusterTime << "\n";
430#endif
431}
432
433template <typename CHARGESUM>
434void UserLogicElinkDecoder<CHARGESUM>::setHeaderPart(uint10_t a)
435{
436 oneLess10BitWord();
437 mHeaderParts.emplace_back(a);
438#ifdef ULDEBUG
439 debugHeader() << (*this) << fmt::format(" --> readHeader {:08X}\n", a);
440#endif
441}
442
443template <typename CHARGESUM>
444void UserLogicElinkDecoder<CHARGESUM>::setSample(uint10_t sample)
445{
446#ifdef ULDEBUG
447 debugHeader() << (*this) << " --> sample = " << sample << "\n";
448#endif
449 --mSamplesToRead;
450 oneLess10BitWord();
451 mSamples.emplace_back(sample);
452
453 if (mSamplesToRead == 0) {
454 prepareAndSendCluster();
455 }
456}
457
458template <typename CHARGESUM>
459void UserLogicElinkDecoder<CHARGESUM>::sendHBPacket()
460{
461 SampaHeartBeatHandler handler = mDecodedDataHandlers.sampaHeartBeatHandler;
462 if (handler) {
463 handler(mDsId, mSampaHeader.chipAddress() % 2, mSampaHeader.bunchCrossingCounter());
464 }
465}
466
467template <typename CHARGESUM>
468void UserLogicElinkDecoder<CHARGESUM>::transition(State to)
469{
470#ifdef ULDEBUG
471 debugHeader() << (*this) << " --> Transition from " << asString(mState) << " to " << asString(to) << "\n";
472#endif
473 mState = to;
474}
475
476template <typename T>
477std::ostream& operator<<(std::ostream& os, const o2::mch::raw::UserLogicElinkDecoder<T>& e)
478{
479 os << fmt::format("{} n10={:4d} size={:4d} t={:4d} ", asString(e.mDsId), e.mNof10BitWords, e.mClusterSize, e.mClusterTime);
480 os << fmt::format("h({:2d})= ", e.mHeaderParts.size());
481 for (auto h : e.mHeaderParts) {
482 os << fmt::format("{:4d} ", h);
483 }
484 os << fmt::format("s({:2d})= ", e.mSamples.size());
485 for (auto s : e.mSamples) {
486 os << fmt::format("{:4d} ", s);
487 }
488 if (!e.mDecodedDataHandlers.sampaChannelHandler) {
489 os << " empty handler ";
490 }
491
492 os << fmt::format("moreWords: {:5} moreSample: {:5} ",
493 e.moreWordsToRead(), e.moreSampleToRead());
494
495 if (e.hasError()) {
496 os << " ERROR:" << e.errorMessage();
497 }
498 return os;
499}
500
501} // namespace o2::mch::raw
502
503#endif
benchmark::State & state
int32_t i
Class for time synchronization of RawReader instances.
SampaHeader is the 50-bits header word used in Sampa data transmission protocol.
Definition SampaHeader.h:51
void append(uint64_t data50, uint8_t error, bool incomplete)
Append 50 bits-worth of data.
friend std::ostream & operator<<(std::ostream &os, const o2::mch::raw::UserLogicElinkDecoder< T > &e)
UserLogicElinkDecoder(DsElecId dsId, DecodedDataHandlers decodedDataHandlers)
GLuint64EXT * result
Definition glcorearb.h:5662
GLsizei const GLfloat * value
Definition glcorearb.h:819
GLboolean * data
Definition glcorearb.h:298
GLboolean GLboolean GLboolean GLboolean a
Definition glcorearb.h:1233
DualSampaChannelId getDualSampaChannelId(const SampaHeader &sh)
Return channel number (0..63)
uint16_t uint10_t
Definition DataFormats.h:67
@ ErrorTruncatedDataUL
Definition ErrorCodes.h:35
@ ErrorBadHeartBeatPacket
Definition ErrorCodes.h:30
@ ErrorBadIncompleteWord
Definition ErrorCodes.h:33
@ ErrorBadClusterSize
Definition ErrorCodes.h:32
@ ErrorUnexpectedSyncPacket
Definition ErrorCodes.h:36
constexpr uint64_t sampaSyncWord
constexpr bool isSync(uint64_t data)
std::string asString(const SampaCluster &sc)
std::function< void(DsElecId dsId, int8_t chip, uint32_t error)> SampaErrorHandler
std::function< void(DsElecId dsId, uint8_t chip, uint20_t bunchCrossing)> SampaHeartBeatHandler
std::ostream & operator<<(std::ostream &stream, o2::InteractionRecord const &ir)
Piece of data for one Sampa channel.
vec clear()