Project
Loading...
Searching...
No Matches
AlpideCoder.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 ALICEO2_ITSMFT_ALPIDE_CODER_H
13#define ALICEO2_ITSMFT_ALPIDE_CODER_H
14#include <Rtypes.h>
15#include <cstdio>
16#include <cstdint>
17#include <vector>
18#include <string>
19#include <cstdint>
20#include "Framework/Logger.h"
21#include "PayLoadCont.h"
22#include <map>
23#include <fmt/format.h>
24#include <iomanip>
25
29
30#define ALPIDE_DECODING_STAT
31
35
36namespace o2
37{
38namespace itsmft
39{
40
44
46{
47
48 public:
49 struct HitsRecord { // single record for hits (i.e. DATASHORT or DATALONG)
50 HitsRecord() = default;
51 ~HitsRecord() = default;
52 HitsRecord(uint8_t r, uint8_t dc, uint16_t adr, uint8_t hmap) : region(r), dcolumn(dc), address(adr), hitmap(hmap) {}
53 uint8_t region = 0; // region ID
54 uint8_t dcolumn = 0; // double column ID
55 uint16_t address = 0; // address in double column
56 uint8_t hitmap = 0; // hitmap for extra hits
57
58 ClassDefNV(HitsRecord, 1); // TODO remove
59 };
60
61 struct PixLink { // single pixel on the selected row, referring eventually to the next pixel on the same row
62 PixLink(short r = 0, short c = 0, int next = -1) : row(r), col(c), nextInRow(next) {}
63 short row = 0;
64 short col = 0;
65 int nextInRow = -1; // index of the next pixel (link) on the same row
66
67 ClassDefNV(PixLink, 1); // TODO remove
68 };
69 //
70 static constexpr uint32_t ExpectChipHeader = 0x1 << 0;
71 static constexpr uint32_t ExpectChipTrailer = 0x1 << 1;
72 static constexpr uint32_t ExpectChipEmpty = 0x1 << 2;
73 static constexpr uint32_t ExpectRegion = 0x1 << 3;
74 static constexpr uint32_t ExpectData = 0x1 << 4;
75 static constexpr uint32_t ExpectBUSY = 0x1 << 5;
76 static constexpr uint32_t ExpectNextChip = ExpectChipHeader | ExpectChipEmpty;
77 static constexpr int NRows = 512;
78 static constexpr int RowMask = NRows - 1;
79 static constexpr int NCols = 1024;
80 static constexpr int NRegions = 32;
81 static constexpr int NDColInReg = NCols / NRegions / 2;
82 static constexpr int HitMapSize = 7;
83
84 // masks for records components
85 static constexpr uint32_t MaskEncoder = 0x3c00; // encoder (double column) ID takes 4 bit max (0:15)
86 static constexpr uint32_t MaskPixID = 0x3ff; // pixel ID within encoder (double column) takes 10 bit max (0:1023)
87 static constexpr uint32_t MaskDColID = MaskEncoder | MaskPixID; // mask for encoder + dcolumn combination
88 static constexpr uint32_t MaskRegion = 0x1f; // region ID takes 5 bits max (0:31)
89 static constexpr uint32_t MaskChipID = 0x0f; // chip id in module takes 4 bit max
90 static constexpr uint32_t MaskROFlags = 0x0f; // RO flags in chip trailer takes 4 bit max
91 static constexpr uint8_t MaskErrBusyViolation = 0x1 << 3;
92 static constexpr uint8_t MaskErrDataOverrun = 0x3 << 2;
93 static constexpr uint8_t MaskErrFatal = 0x7 << 1;
94 static constexpr uint8_t MaskErrFlushedIncomplete = 0x1 << 2;
95 static constexpr uint8_t MaskErrStrobeExtended = 0x1 << 1;
96 static constexpr uint32_t MaskTimeStamp = 0xff; // Time stamps as BUNCH_COUNTER[10:3] bits
97 static constexpr uint32_t MaskReserved = 0xff; // mask for reserved byte
98 static constexpr uint32_t MaskHitMap = 0x7f; // mask for hit map: at most 7 hits in bits (0:6)
99 //
100 // flags for data records
101 static constexpr uint32_t REGION = 0xc0; // flag for region
102 static constexpr uint32_t REGION_MASK = 0xe0; // mask for detecting the region
103 static constexpr uint32_t CHIPHEADER = 0xa0; // flag for chip header
104 static constexpr uint32_t CHIPTRAILER = 0xb0; // flag for chip trailer
105 static constexpr uint32_t CHIPEMPTY = 0xe0; // flag for empty chip
106 static constexpr uint32_t DATALONG = 0x0000; // flag for DATALONG
107 static constexpr uint32_t DATASHORT = 0x4000; // flag for DATASHORT
108 static constexpr uint32_t BUSYOFF = 0xf0; // flag for BUSY_OFF
109 static constexpr uint32_t BUSYON = 0xf1; // flag for BUSY_ON
110 static constexpr uint32_t ERROR_MASK = 0xf0; // flag for all error triggers
111
112 // true if corresponds to DATALONG or DATASHORT: highest bit must be 0
113 static bool isData(uint16_t v) { return (v & (0x1 << 15)) == 0; }
114 static bool isData(uint8_t v) { return (v & (0x1 << 7)) == 0; }
115
116 static constexpr int Error = -1; // flag for decoding error
117 static constexpr int EOFFlag = -100; // flag for EOF in reading
118
119 AlpideCoder() = default;
120 ~AlpideCoder() = default;
121
122 static bool isEmptyChip(uint8_t b) { return (b & CHIPEMPTY) == CHIPEMPTY; }
123
124 static void setNoisyPixels(const NoiseMap* noise) { mNoisyPixels = noise; }
125
127 template <class T, typename CG>
128 static int decodeChip(ChipPixelData& chipData, T& buffer, std::vector<uint16_t>& seenChips, CG cidGetter)
129 {
130 // read record for single non-empty chip, updating on change module and cycle.
131 // return number of records filled (>0), EOFFlag or Error
132 //
133 bool needSorting = false; // if DColumns order is wrong, do explicit reordering
134 auto roErrHandler = [&chipData](uint8_t roErr) {
135#ifdef ALPIDE_DECODING_STAT
136 if (roErr == MaskErrBusyViolation) {
138 } else if (roErr == MaskErrDataOverrun) {
140 } else if (roErr == MaskErrFatal) {
141 chipData.setError(ChipStat::Fatal);
142 } else if (roErr == MaskErrFlushedIncomplete) {
144 } else if (roErr == MaskErrStrobeExtended) {
146 }
147#endif
148 };
149
150 uint8_t dataC = 0, timestamp = 0;
151 uint16_t dataS = 0, region = 0;
152#ifdef ALPIDE_DECODING_STAT
153 uint16_t rowPrev = 0xffff;
154#endif
155 //
156 int nRightCHits = 0; // counter for the hits in the right column of the current double column
157 std::uint16_t rightColHits[NRows]; // buffer for the accumulation of hits in the right column
158 std::uint16_t colDPrev = 0xffff; // previously processed double column (to dected change of the double column)
159
160 uint32_t expectInp = ExpectNextChip; // data must always start with chip header or chip empty flag
161
162 chipData.clear();
163 LOG(debug) << "NewEntry";
164 while (buffer.next(dataC)) {
165 //
166 LOGP(debug, "dataC: {:#x} expect {:#b}", int(dataC), int(expectInp));
167
168 // Busy ON / OFF can appear at any point of the data stream, checking it with priority
169 if (dataC == BUSYON) {
170#ifdef ALPIDE_DECODING_STAT
171 chipData.setError(ChipStat::BusyOn);
172#endif
173 continue;
174 }
175 if (dataC == BUSYOFF) {
176#ifdef ALPIDE_DECODING_STAT
177 chipData.setError(ChipStat::BusyOff);
178#endif
179 continue;
180 }
181
182 if ((expectInp & ExpectChipEmpty) && isChipEmpty(dataC)) { // empty chip was expected
183 uint16_t chipIDGlo = cidGetter(dataC & MaskChipID);
184 if (chipIDGlo == 0xffff) {
185 chipData.setChipID(chipIDGlo);
186#ifdef ALPIDE_DECODING_STAT
187 chipData.setErrorInfo(dataC & MaskChipID);
189#endif
190 chipData.getData().clear();
191 return unexpectedEOF("CHIP_EMPTY:WrongChipID"); // abandon cable data
192 }
193 chipData.setChipID(chipIDGlo); // here we set the global chip ID
194 if (!buffer.next(timestamp)) {
195#ifdef ALPIDE_DECODING_STAT
197#endif
198 return unexpectedEOF("CHIP_EMPTY:Timestamp"); // abandon cable data
199 }
200 seenChips.push_back(chipIDGlo);
201 chipData.resetChipID();
202 expectInp = ExpectNextChip;
203 continue;
204 }
205
206 if ((expectInp & ExpectChipHeader) && isChipHeader(dataC)) { // chip header was expected
207 uint16_t chipIDGlo = cidGetter(dataC & MaskChipID);
208 if (chipIDGlo == 0xffff) {
209 chipData.setChipID(chipIDGlo);
210#ifdef ALPIDE_DECODING_STAT
211 chipData.setErrorInfo(dataC & MaskChipID);
213#endif
214 chipData.getData().clear();
215 return unexpectedEOF("CHIP_EMPTY:WrongChipID"); // abandon cable data
216 }
217 chipData.setChipID(chipIDGlo); // here we set the global chip ID
218 if (!buffer.next(timestamp)) {
219#ifdef ALPIDE_DECODING_STAT
221#endif
222 return unexpectedEOF("CHIP_HEADER"); // abandon cable data
223 }
224 expectInp = ExpectRegion; // now expect region info
225 continue;
226 }
227
228 // region info ?
229 if ((expectInp & ExpectRegion) && (dataC & REGION_MASK) == REGION) { // chip header was seen, or hit data read
230 region = dataC & MaskRegion;
231 expectInp = ExpectData;
232 continue;
233 }
234
235 if ((expectInp & ExpectChipTrailer) && isChipTrailer(dataC)) { // chip trailer was expected
236 expectInp = ExpectNextChip;
237 chipData.setROFlags(dataC & MaskROFlags);
238#ifdef ALPIDE_DECODING_STAT
239 uint8_t roErr = dataC & MaskROFlags;
240 if (roErr) {
241 roErrHandler(roErr);
242 }
243#endif
244 // in case there are entries in the "right" columns buffer, add them to the container
245 if (nRightCHits) {
246 colDPrev++;
247 for (int ihr = 0; ihr < nRightCHits; ihr++) {
248 addHit(chipData, rightColHits[ihr], colDPrev);
249 }
250 }
251 break;
252 }
253
254 // hit info ?
255 if ((expectInp & ExpectData)) {
256 if (isData(dataC)) { // region header was seen, expect data
257 // note that here we are checking on the byte rather than the short, need complete to ushort
258 dataS = dataC << 8;
259 if (!buffer.next(dataC)) {
260#ifdef ALPIDE_DECODING_STAT
262#endif
263 return unexpectedEOF("CHIPDATA"); // abandon cable data
264 }
265 dataS |= dataC;
266 LOGP(debug, "dataC: {:#x} dataS: {:#x} expect {:#b} in ExpectData", int(dataC), int(dataS), int(expectInp));
267
268 // we are decoding the pixel addres, if this is a DATALONG, we will fetch the mask later
269 uint16_t dColID = (dataS & MaskEncoder) >> 10;
270 uint16_t pixID = dataS & MaskPixID;
271
272 // convert data to usual row/pixel format
273 uint16_t row = pixID >> 1;
274 // abs id of left column in double column
275 uint16_t colD = (region * NDColInReg + dColID) << 1; // TODO consider <<4 instead of *NDColInReg?
276 bool rightC = (row & 0x1) ? !(pixID & 0x1) : (pixID & 0x1); // true for right column / false for left
277
278 if (colD == colDPrev) {
279 bool skip = false;
280 if (row == rowPrev) { // this is a special test to exclude repeated data of the same pixel fired
281 skip = true;
282#ifdef ALPIDE_DECODING_STAT
284 chipData.addErrorInfo((uint64_t(colD + rightC) << 16) | uint64_t(row));
285#endif
286 } else if (rowPrev < 0xffff && row < rowPrev) {
287#ifdef ALPIDE_DECODING_STAT
289 chipData.addErrorInfo((uint64_t(colD + rightC) << 16) | uint64_t(row));
290#endif
291 return unexpectedEOF("DECREASING_ROW"); // abandon cable data
292 }
293 if (skip) {
294 if ((dataS & (~MaskDColID)) == DATALONG) { // skip pattern w/o decoding
295 uint8_t hitsPattern = 0;
296 if (!buffer.next(hitsPattern)) {
297#ifdef ALPIDE_DECODING_STAT
299#endif
300 return unexpectedEOF("CHIP_DATA_LONG:Pattern"); // abandon cable data
301 }
302 if (hitsPattern & (~MaskHitMap)) {
303#ifdef ALPIDE_DECODING_STAT
305#endif
306 return unexpectedEOF("CHIP_DATA_LONG:Pattern"); // abandon cable data
307 }
308 LOGP(debug, "hitsPattern: {:#b} expect {:#b}", int(hitsPattern), int(expectInp));
309 }
311 continue; // end of DATA(SHORT or LONG) processing
312 }
313 } else {
314 // if we start new double column, transfer the hits accumulated in the right column buffer of prev. double column
315 if (colD < colDPrev && colDPrev != 0xffff) {
316#ifdef ALPIDE_DECODING_STAT
317 chipData.setError(ChipStat::WrongDColOrder); // abandon cable data
318#endif
319 return unexpectedEOF("Wrong column order"); // abandon cable data
320 needSorting = true; // effectively disabled
321 }
322 colDPrev++;
323 for (int ihr = 0; ihr < nRightCHits; ihr++) {
324 addHit(chipData, rightColHits[ihr], colDPrev);
325 }
326 nRightCHits = 0; // reset the buffer
327 }
328 rowPrev = row;
329 colDPrev = colD;
330
331 // we want to have hits sorted in column/row, so the hits in right column of given double column
332 // are first collected in the temporary buffer
333 // real columnt id is col = colD + 1;
334 if (rightC) {
335 rightColHits[nRightCHits++] = row;
336 } else {
337 addHit(chipData, row, colD); // col = colD, left column hits are added directly to the container
338 }
339
340 if ((dataS & (~MaskDColID)) == DATALONG) { // multiple hits ?
341 uint8_t hitsPattern = 0;
342 if (!buffer.next(hitsPattern)) {
343#ifdef ALPIDE_DECODING_STAT
345#endif
346 return unexpectedEOF("CHIP_DATA_LONG:Pattern"); // abandon cable data
347 }
348 LOGP(debug, "hitsPattern: {:#b} expect {:#b}", int(hitsPattern), int(expectInp));
349 if (hitsPattern & (~MaskHitMap)) {
350#ifdef ALPIDE_DECODING_STAT
352#endif
353 return unexpectedEOF("CHIP_DATA_LONG:Pattern"); // abandon cable data
354 }
355 for (int ip = 0; ip < HitMapSize; ip++) {
356 if (hitsPattern & (0x1 << ip)) {
357 uint16_t addr = pixID + ip + 1, rowE = addr >> 1;
358 if (addr & ~MaskPixID) {
359#ifdef ALPIDE_DECODING_STAT
361#endif
362 return unexpectedEOF(fmt::format("Non-existing encoder {} decoded, DataLong was {:x}", pixID, dataS)); // abandon cable data
363 }
364 rightC = ((rowE & 0x1) ? !(addr & 0x1) : (addr & 0x1)); // true for right column / lalse for left
365 // the real columnt is int colE = colD + rightC;
366 if (rightC) { // same as above
367 rightColHits[nRightCHits++] = rowE;
368 } else {
369 addHit(chipData, rowE, colD); // left column hits are added directly to the container
370 }
371 }
372 }
373 }
374 } else if (ChipStat::getAPENonCritical(dataC) >= 0) { // check for recoverable APE, if on: continue with ExpectChipTrailer | ExpectData | ExpectRegion expectation
375#ifdef ALPIDE_DECODING_STAT
377#endif
378 } else {
379#ifdef ALPIDE_DECODING_STAT
381#endif
382 return unexpectedEOF(fmt::format("Expected DataShort or DataLong mask, got {:x}", dataS)); // abandon cable data
383 }
385 continue; // end of DATA(SHORT or LONG) processing
386 }
387
388 if (!dataC) {
389 if (expectInp == ExpectNextChip) {
390 continue;
391 }
393 return unexpectedEOF("Abandon on 0-padding"); // abandon cable data
394 }
395
396 if ((expectInp & ExpectRegion) && isChipTrailer(dataC)) {
397 if (dataC & MaskROFlags) {
398 // in case of BUSY VIOLATION the Trailer may come directly after the Header
399 expectInp = ExpectNextChip;
400 chipData.setROFlags(dataC & MaskROFlags);
401 roErrHandler(dataC & MaskROFlags);
402 break;
403 } else {
404#ifdef ALPIDE_DECODING_STAT
406#endif
407 return unexpectedEOF("Trailer after header"); // abandon cable data
408 }
409 }
410
411 // check for APE errors, see https://alice.its.cern.ch/jira/browse/O2-1717?focusedCommentId=274714&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-274714
412 bool fatalAPE = false;
413 auto codeAPE = ChipStat::getAPECode(dataC, fatalAPE);
414 if (codeAPE >= 0) {
415#ifdef ALPIDE_DECODING_STAT
416 chipData.setError(ChipStat::DecErrors(codeAPE));
417#endif
418 if (fatalAPE) {
419 return unexpectedEOF(fmt::format("APE error {:#02x} [expectation = {:#02x}]", int(dataC), int(expectInp))); // abandon cable data
420 } else {
421 LOGP(error, "Code should not have entered here, APE: {:#02x}, expectation: {:#02x}", codeAPE, int(expectInp));
422 return unexpectedEOF(fmt::format("APE error {:#02x} [expectation = {:#02x}]", int(dataC), int(expectInp))); // abandon cable data
423 }
424 }
425#ifdef ALPIDE_DECODING_STAT
427 // fill the error buffer with a few bytes of wrong data
428 const uint8_t* begPtr = buffer.data();
429 const uint8_t* endPtr = buffer.getEnd();
430 const uint8_t* curPtr = buffer.getPtr();
431 size_t offsBack = std::min(ChipPixelData::MAXDATAERRBYTES - ChipPixelData::MAXDATAERRBYTES_AFTER, size_t(curPtr - begPtr));
432 size_t offsAfter = std::min(ChipPixelData::MAXDATAERRBYTES_AFTER, size_t(endPtr - curPtr));
433 std::memcpy(chipData.getRawErrBuff().data(), curPtr - offsBack, offsBack + offsAfter);
434 chipData.setNBytesInRawBuff(offsBack + offsAfter);
435#endif
436 return unexpectedEOF(fmt::format("Unknown word 0x{:x} [expectation = 0x{:x}]", int(dataC), int(expectInp))); // abandon cable data
437 }
438
439 if (!(expectInp & ExpectNextChip)) {
440#ifdef ALPIDE_DECODING_STAT
442#endif
443 return unexpectedEOF("Missing CHIP_TRAILER"); // abandon cable data
444 }
445
446 if (needSorting && chipData.getData().size()) { // d.columns were in a wrong order, need to sort the data, RS: effectively disabled
447 LOGP(error, "This code path should have been disabled");
448 auto& pixData = chipData.getData();
449 std::sort(pixData.begin(), pixData.end(),
450 [](PixelData& a, PixelData& b) { return a.getCol() < b.getCol() || (a.getCol() == b.getCol() && a.getRowDirect() < b.getRowDirect()); });
451 // if the columns ordering was wrong, detection of same pixel fired twice might have failed, make sure there are no duplicates
452 auto currPix = pixData.begin(), prevPix = currPix++;
453 while (currPix != pixData.end()) {
454 if (prevPix->getCol() == currPix->getCol() && prevPix->getRowDirect() == currPix->getRowDirect()) {
455 currPix = pixData.erase(prevPix);
456 }
457 prevPix = currPix++;
458 }
459 }
460 if (chipData.getData().size()) {
461 seenChips.push_back(chipData.getChipID());
462 }
463 return chipData.getData().size();
464 }
465
468 template <typename LG, typename CG>
470 std::map<int, ChipPixelData*>& seenChips, PayLoadCont& buffer,
471 std::vector<uint16_t>& seenChipIDs, LG lidGetter, CG cidGetter)
472 {
473 PayLoadCont reconstructedData;
474
475 // Ensure the length of the reconstructed buffer.
476 int bufferLength = 0;
477 for (auto it = seenChips.begin(); it != seenChips.end(); ++it) {
478 bufferLength += it->second->getData().size();
479 }
480 bufferLength += seenChipIDs.size() * 2;
481 reconstructedData.ensureFreeCapacity(40 * bufferLength);
482
483 // Encode the seen chips in the order they were decoded.
484 for (int ID : seenChipIDs) {
485 ChipPixelData currentChip;
486 int localID = lidGetter(ID);
487 if (seenChips.count(ID)) {
488 currentChip = *seenChips[ID];
489 }
490 AlpideCoder encoder;
491 encoder.encodeChip(reconstructedData, currentChip, localID,
492 /*dummy bc*/ 0, currentChip.getROFlags());
493 }
494
495 // Pad the end of the reconstructed buffer with the zero bytes.
496 if (buffer.getSize() > reconstructedData.getSize())
497 reconstructedData.fill(0x00,
498 buffer.getSize() - reconstructedData.getSize());
499
500 auto hexToString = [](uint8_t v) {
501 std::stringstream ss;
502 ss << "0x" << std::setfill('0') << std::setw(2) << std::hex
503 << std::uppercase << (0xFF & v);
504 return ss.str();
505 };
506
507 auto reportError = [&](std::string message) {
508 LOG(error) << "Error during decoder verification: " << message;
509 LOG(debug) << "Raw Data:";
510 buffer.rewind();
511 uint8_t dataC = 0;
512 int index = 1;
513 while (buffer.next(dataC)) {
514 LOG(debug) << index++ << ". " << hexToString(dataC);
515 }
516 LOG(debug) << "Reconstructed Data:";
517 reconstructedData.rewind();
518 index = 1;
519 while (reconstructedData.next(dataC)) {
520 LOG(debug) << index++ << ". " << hexToString(dataC);
521 }
522 };
523
524 // The reconstructed buffer is very similar to the original data flow
525 // with the exception to several pieces of information that get lost
526 // during the decoding:
527 // 1. Error trigger words: these are absent in the reconstructed buffer.
528 // In case of BUSYON/BUSYOFF words, the verification is allowed to
529 // continue.
530 // 2. Bunch counter for frame: the reconstructed buffer does contain the
531 // corresponding words, but has a dummy value.
532
533 ChipPixelData* currentChip = nullptr;
534 if (seenChipIDs.size())
535 currentChip = seenChips[seenChipIDs[0]];
536 buffer.rewind();
537 while (true) {
538 uint8_t dataRec = 0;
539 uint8_t dataRaw = 0;
540
541 if (reconstructedData.isEmpty() || buffer.isEmpty()) {
542 // If either buffer is empty, verify that both buffers reached the end.
543 // If one of the streams is non-empty, then verify that the remaining
544 // bytes are zeroes.
545 if (reconstructedData.isEmpty() && buffer.isEmpty()) {
546 break;
547 }
548 PayLoadCont& nonEmptyBuffer =
549 !buffer.isEmpty() ? buffer : reconstructedData;
550 uint8_t dataC = 0;
551 while (nonEmptyBuffer.next(dataC)) {
552 if (dataC != 0x00) {
553 reportError("Buffer sizes mismatch.");
554 return false;
555 }
556 }
557 break;
558 }
559
560 reconstructedData.current(dataRec);
561 buffer.current(dataRaw);
562 if (dataRaw == dataRec) {
563 if (isChipHeaderOrEmpty(dataRaw)) {
564 uint16_t ID = cidGetter(dataRaw & MaskChipID);
565 if (seenChips.count(ID)) {
566 currentChip = seenChips[ID];
567 } else {
568 currentChip = nullptr;
569 }
570 // If the data correspond to the CHIPHEADER or CHIPEMPTY data words,
571 // skip the next byte that represent bunch counters.
572 buffer.next(dataRaw);
573 reconstructedData.next(dataRec);
574 }
575 buffer.next(dataRaw);
576 reconstructedData.next(dataRec);
577 continue;
578 }
579
580 if (dataRaw == BUSYON || dataRaw == BUSYOFF) {
581 // Placement of BUSYON and BUSYOFF triggers is arbitrary, just ignore
582 // the byte in the raw stream and move forward.
583 buffer.next(dataRaw);
584 continue;
585 }
586
588 handleVerifierMismatch(buffer, reconstructedData, currentChip);
589 switch (res) {
591 LOG(debug) << "Mismatch " << hexToString(dataRaw) << " / "
592 << hexToString(dataRec)
593 << " was resolved, able to continue verification";
594 continue;
596 LOG(debug) << "Mismatch " << hexToString(dataRaw) << " / "
597 << hexToString(dataRec)
598 << " was expected, aborting the verification";
599 return true;
601 // If the read bytes is not related to the special cases, report
602 // error.
603 std::stringstream errorStream;
604 errorStream
605 << "Unexpected byte mismatch during decoder verification. "
606 "Expected: "
607 << hexToString(dataRaw)
608 << ", Reconstructed: " << hexToString(dataRec);
609 reportError(errorStream.str());
610 }
611 }
612 return false;
613 }
614 return true;
615 }
616
618 UNEXPECTED_MISMATCH, // Genuine mismatch, stop verification
619 EXPECTED_MISMATCH, // Mismatch expected, need to abort verification
620 RESOLVED // Mismatch resolved, can continue
621 };
622
624 PayLoadCont& buffer, PayLoadCont& reconstructedData,
625 ChipPixelData* currentChip)
626 {
628 uint8_t dataRec = 0;
629 uint8_t dataRaw = 0;
630 reconstructedData.current(dataRec);
631 buffer.current(dataRaw);
632 auto inner = [&](int errIdx) {
634 dataRaw == dataRec) {
635 // The mismatch was resolved, no need to check the rest of the errors
636 return;
637 }
638 switch (errIdx) {
641 case ChipStat::Fatal:
642 break;
643 case ChipStat::BusyOn:
645 // We don't need to do anything with these errors.
646 break;
648 // This error cannot cause mismatch since it can be reconstructed
649 // via the encoder.
650 break;
653 if (isChipHeader(dataRaw) && isChipEmpty(dataRec)) {
654 // In case of TruncatedChipHeader, the raw data must have a chip
655 // header while the reconstructed chip is empty. The verifier
656 // cannot continue the verification further.
658 }
659 break;
662 if (isData(dataRaw) && isChipTrailer(dataRec)) {
663 // If the decoder encountered an issue with DATALONG, the verifier
664 // must have a mismatch between data on the raw stream and trailer
665 // on the reconstructed stream
667 }
668 break;
673 break;
685 uint8_t errorByte = ChipStat::getAPEByte((ChipStat::DecErrors)errIdx);
686 if (dataRaw == errorByte) {
687 buffer.next(dataRaw); // Skipping error byte
688 // If we encountered the byte corresponding to the APE error,
689 // check that the rest of the raw stream consists of only
690 // padding.
691 while (buffer.next(dataRaw)) {
692 if (dataRaw != 0x00) {
693 break;
694 }
695 }
696 if (buffer.isEmpty()) {
698 }
699 }
700 break;
701 }
707 break;
709 if (isChipHeader(dataRaw) && isChipEmpty(dataRec)) {
710 // This error can be verified by skipping a bunch counter byte and
711 // checking that the following byte corresponds to the chip trailer
712 buffer.next(dataRaw); // Skipping chip header
713 buffer.next(dataRaw); // Skipping bunch counter
714 buffer.current(dataRaw);
715 if (isChipTrailer(dataRaw)) {
717 }
718 }
719 break;
722 break;
724 // If the chip doesn't have a valid ID, we must stop the verification
726 break;
727 default:
728 LOG(error) << "Unknown error set by chip during verifier mismatch";
729 }
730 };
731 if (currentChip) {
732 currentChip->forEachSetError(inner);
733 }
734 return res;
735 }
736
737 static bool isChipEmpty(uint8_t v) { return (v & (~MaskChipID)) == CHIPEMPTY; }
738 static bool isChipHeader(uint8_t v) { return (v & (~MaskChipID)) == CHIPHEADER; }
739 static bool isChipTrailer(uint8_t v) { return (v & (~MaskChipID)) == CHIPTRAILER; }
740
742 static bool isChipHeaderOrEmpty(uint8_t v)
743 {
744 return isChipHeader(v) || isChipEmpty(v);
745 }
746 // methods to use for data encoding
747
748 static uint8_t bc2TimeStamp(int bc) { return (bc >> 3) & MaskTimeStamp; }
749 static uint16_t timeStamp2BC(uint8_t ts) { return uint16_t(ts) << 3; }
750
752 uint16_t chipInModule, uint16_t bc, uint16_t roflags = 0);
753
754 // Add empty record for the chip with chipID within its module for the bc
755 void addEmptyChip(PayLoadCont& buffer, int chipInMod, int bc)
756 {
757 buffer.addFast(makeChipEmpty(chipInMod, bc));
758 }
759 //
760 void print() const;
761 void reset();
762
763 private:
765 static void addHit(ChipPixelData& chipData, short row, short col)
766 {
767 if (mNoisyPixels) {
768 auto chipID = chipData.getChipID();
769 if (mNoisyPixels->isNoisy(chipID, row, col)) {
770 return;
771 }
772 }
773 LOGP(debug, "Add hit#{} at r:{}/c:{} of chip:{}", chipData.getData().size(), row, col, chipData.getChipID());
774 chipData.getData().emplace_back(row, col);
775 }
776
778 void addPixel(short row, short col)
779 {
780 int last = mPix2Encode.size();
781 mPix2Encode.emplace_back(row, col);
782 if (last && row == mPix2Encode[last - 1].row) { // extend current row
783 mPix2Encode[last - 1].nextInRow = last; // refer to new link in the same row
784 } else { // create new row
785 mFirstInRow.push_back(last);
786 }
787 }
788
790 static uint16_t makeChipHeader(short chipID, short bc)
791 {
792 uint16_t v = CHIPHEADER | (MaskChipID & chipID);
793 v = (v << 8) | bc2TimeStamp(bc);
794 return v;
795 }
796
798 static uint8_t makeChipTrailer(short roflags)
799 {
800 uint8_t v = CHIPTRAILER | (MaskROFlags & roflags);
801 return v;
802 }
803
805 static uint16_t makeChipEmpty(short chipID, short bc)
806 {
807 uint16_t v = CHIPEMPTY | (MaskChipID & chipID);
808 v = (v << 8) | bc2TimeStamp(bc);
809 return v;
810 }
811
813 static uint8_t makeRegion(short reg)
814 {
815 uint8_t v = REGION | (reg & MaskRegion);
816 return v;
817 }
818
820 static uint16_t makeDataShort(short encoder, short address)
821 {
822 uint16_t v = DATASHORT | (MaskEncoder & (encoder << 10)) | (address & MaskPixID);
823 return v;
824 }
825
826 // packs the address for data long
827 static uint16_t makeDataLong(short encoder, short address)
828 {
829 uint16_t v = DATALONG | (MaskEncoder & (encoder << 10)) | (address & MaskPixID);
830 return v;
831 }
832
833 // ENCODING: converting hitmap to raw data
834 int procDoubleCol(PayLoadCont& buffer, short reg, short dcol);
835
837 int procRegion(PayLoadCont& buffer, short reg)
838 {
839 int nfound = 0;
840 for (int idc = 0; idc < NDColInReg; idc++) {
841 nfound += procDoubleCol(buffer, reg, idc);
842 }
843 return nfound;
844 }
845
846 void resetMap();
847
849 static int unexpectedEOF(const std::string& message)
850 {
851 LOG(debug) << message;
852 return Error;
853 }
854
855 // =====================================================================
856 //
857
858 static const NoiseMap* mNoisyPixels;
859
860 // cluster map used for the ENCODING only
861 std::vector<int> mFirstInRow;
862 std::vector<PixLink> mPix2Encode;
863 //
864 ClassDefNV(AlpideCoder, 3);
865};
866
867} // namespace itsmft
868} // namespace o2
869
870#endif
Alpide Chip and GBT link decoding statistics.
uint64_t bc
Definition RawEventData.h:5
Definition of the ITSMFT NoiseMap.
Declaration of class for continuos buffer of ALPIDE data.
Transient data classes for single pixel and set of pixels from current chip.
uint32_t col
Definition RawData.h:4
uint32_t res
Definition RawData.h:0
uint32_t c
Definition RawData.h:2
std::ostringstream debug
static constexpr int RowMask
Definition AlpideCoder.h:78
static bool isData(uint8_t v)
static constexpr uint32_t MaskReserved
Definition AlpideCoder.h:97
static constexpr uint32_t ERROR_MASK
static constexpr uint32_t DATASHORT
static constexpr uint32_t MaskEncoder
Definition AlpideCoder.h:85
static constexpr uint32_t CHIPEMPTY
static bool verifyDecodedCable(std::map< int, ChipPixelData * > &seenChips, PayLoadCont &buffer, std::vector< uint16_t > &seenChipIDs, LG lidGetter, CG cidGetter)
static uint8_t bc2TimeStamp(int bc)
static uint16_t timeStamp2BC(uint8_t ts)
static constexpr uint32_t BUSYOFF
static constexpr uint32_t MaskChipID
Definition AlpideCoder.h:89
static constexpr int NDColInReg
Definition AlpideCoder.h:81
static constexpr int NCols
Definition AlpideCoder.h:79
static bool isData(uint16_t v)
static bool isChipHeader(uint8_t v)
static constexpr uint8_t MaskErrFatal
Definition AlpideCoder.h:93
static constexpr uint32_t BUSYON
static constexpr uint32_t REGION_MASK
int encodeChip(PayLoadCont &buffer, const o2::itsmft::ChipPixelData &chipData, uint16_t chipInModule, uint16_t bc, uint16_t roflags=0)
static VerifierMismatchResult handleVerifierMismatch(PayLoadCont &buffer, PayLoadCont &reconstructedData, ChipPixelData *currentChip)
static constexpr int EOFFlag
static constexpr uint32_t ExpectChipEmpty
Definition AlpideCoder.h:72
static constexpr uint32_t MaskROFlags
Definition AlpideCoder.h:90
static constexpr uint8_t MaskErrDataOverrun
Definition AlpideCoder.h:92
static bool isEmptyChip(uint8_t b)
static constexpr uint32_t ExpectBUSY
Definition AlpideCoder.h:75
static constexpr uint32_t MaskHitMap
Definition AlpideCoder.h:98
static constexpr uint32_t REGION
static constexpr uint8_t MaskErrBusyViolation
Definition AlpideCoder.h:91
static constexpr uint32_t CHIPHEADER
static constexpr uint32_t CHIPTRAILER
static void setNoisyPixels(const NoiseMap *noise)
static bool isChipEmpty(uint8_t v)
static constexpr uint8_t MaskErrStrobeExtended
Definition AlpideCoder.h:95
static constexpr uint32_t ExpectChipHeader
Definition AlpideCoder.h:70
static constexpr uint32_t ExpectRegion
Definition AlpideCoder.h:73
static constexpr uint32_t DATALONG
static constexpr uint32_t ExpectData
Definition AlpideCoder.h:74
static constexpr uint32_t MaskTimeStamp
Definition AlpideCoder.h:96
void addEmptyChip(PayLoadCont &buffer, int chipInMod, int bc)
static constexpr int HitMapSize
Definition AlpideCoder.h:82
static constexpr uint32_t MaskDColID
Definition AlpideCoder.h:87
static constexpr uint32_t MaskRegion
Definition AlpideCoder.h:88
static constexpr uint32_t MaskPixID
Definition AlpideCoder.h:86
static constexpr uint8_t MaskErrFlushedIncomplete
Definition AlpideCoder.h:94
static constexpr int NRegions
Definition AlpideCoder.h:80
static constexpr uint32_t ExpectNextChip
Definition AlpideCoder.h:76
static int decodeChip(ChipPixelData &chipData, T &buffer, std::vector< uint16_t > &seenChips, CG cidGetter)
decode alpide data for the next non-empty chip from the buffer
static constexpr int NRows
Definition AlpideCoder.h:77
static constexpr uint32_t ExpectChipTrailer
Definition AlpideCoder.h:71
static constexpr int Error
static bool isChipTrailer(uint8_t v)
static bool isChipHeaderOrEmpty(uint8_t v)
check if the byte corresponds to chip_header or chip_empty flag
static constexpr size_t MAXDATAERRBYTES
Definition PixelData.h:104
uint8_t getROFlags() const
Definition PixelData.h:107
void setError(ChipStat::DecErrors i)
Definition PixelData.h:125
void setErrorInfo(uint64_t b)
Definition PixelData.h:127
void setNBytesInRawBuff(int n)
Definition PixelData.h:134
void setChipID(uint16_t id)
Definition PixelData.h:119
void setROFlags(uint8_t f=0)
Definition PixelData.h:118
void addErrorInfo(uint64_t b)
Definition PixelData.h:126
static constexpr size_t MAXDATAERRBYTES_AFTER
Definition PixelData.h:104
uint16_t getChipID() const
Definition PixelData.h:108
const std::vector< PixelData > & getData() const
Definition PixelData.h:115
void forEachSetError(Func f) const
Definition PixelData.h:247
NoiseMap class for the ITS and MFT.
Definition NoiseMap.h:39
bool isNoisy(int chip, int row, int col) const
Definition NoiseMap.h:151
void ensureFreeCapacity(size_t n)
fill n bytes with given symbol w/o checking for the size
Definition PayLoadCont.h:76
void rewind()
move all data between the mPtr and mEnd to the head of the buffer
size_t getSize() const
get offset of the current ptr from the head
Definition PayLoadCont.h:64
void fill(const uint8_t c, size_t n)
add n bytes to the buffer, expand if needed. no check for overlap
bool next(uint8_t &v)
read short value from buffer
bool isEmpty() const
make buffer empty w/o deallocating it
Definition PayLoadCont.h:51
bool current(uint8_t &v) const
read character value from buffer
< single pixel datum, with possibility to set a flag of pixel being masked out
Definition PixelData.h:33
GLuint GLuint64EXT address
Definition glcorearb.h:5846
GLuint buffer
Definition glcorearb.h:655
const GLdouble * v
Definition glcorearb.h:832
GLuint index
Definition glcorearb.h:781
GLboolean GLboolean GLboolean b
Definition glcorearb.h:1233
GLuint GLsizei const GLchar * message
Definition glcorearb.h:2517
GLboolean r
Definition glcorearb.h:1233
GLboolean GLboolean GLboolean GLboolean a
Definition glcorearb.h:1233
uint8_t itsSharedClusterMap uint8_t
a couple of static helper functions to create timestamp values for CCDB queries or override obsolete ...
HitsRecord(uint8_t r, uint8_t dc, uint16_t adr, uint8_t hmap)
Definition AlpideCoder.h:52
static int getAPENonCritical(uint8_t c)
static int getAPECode(uint8_t c, bool &ft)
static uint8_t getAPEByte(DecErrors c)
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"
std::vector< int > row