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>
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
unsigned short int uint16_t
unsigned int uint32_t
std::vector< std::string > header
int32_t i
Timeframe settings Average distance in cm of events merged into Randomize distance around average distance of merged events Simulate correct bunch interactions instead of placing only the average number of events A value[n] sets TFlen for[n] collisions in Number of bunches per trainsort Number of bunch trains Instantaneous interaction rate Do not simulate border Total number of collisions to be placed in the interior of all time Do not select random but walk over array of events in stride steps h Event generator settings h configStandalone Use CPU for GPU Force usage of the specified GPU device no CPU fallback Continue processing after an error s
Timeframe settings Average distance in cm of events merged into Randomize distance around average distance of merged events Simulate correct bunch interactions instead of placing only the average number of events A value[n] sets TFlen for[n] collisions in Number of bunches per trainsort Number of bunch trains Instantaneous interaction rate Do not simulate border Total number of collisions to be placed in the interior of all time Do not select random but walk over array of events in stride steps h Event generator settings h configStandalone Use CPU for GPU Force usage of the specified GPU device no CPU fallback Continue processing after an error First event to Number of events to Number of iterations to Number of iterations to Number of initial iterations excluded from const char Directory with events to Events directory is and not inside events Show standalone event Run event generator maximum time bin of continuous for triggered for automatic continuous for automatic continuous triggered uint64_t
Timeframe settings Average distance in cm of events merged into Randomize distance around average distance of merged events Simulate correct bunch interactions instead of placing only the average number of events A value[n] sets TFlen for[n] collisions in Number of bunches per trainsort Number of bunch trains Instantaneous interaction rate Do not simulate border Total number of collisions to be placed in the interior of all time Do not select random but walk over array of events in stride steps h Event generator settings h configStandalone Use CPU for GPU Force usage of the specified GPU device no CPU fallback Continue processing after an error First event to Number of events to Number of iterations to Number of iterations to Number of initial iterations excluded from const char e
char error[CURL_ERROR_SIZE]
SurfaceTrackState state
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 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()