Project
Loading...
Searching...
No Matches
CruRawReader.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
17#include "Headers/RDHAny.h"
19#include "TRDBase/FeeParam.h"
24
30#include "Framework/Output.h"
33
34#include <string>
35#include <numeric>
36#include <iomanip>
37
38using namespace o2::trd::constants;
39
40namespace o2::trd
41{
42
43void CruRawReader::configure(int tracklethcheader, int halfchamberwords, int halfchambermajor, std::bitset<16> options)
44{
45 mTrackletHCHeaderState = tracklethcheader;
46 mHalfChamberWords = halfchamberwords;
47 mHalfChamberMajor = halfchambermajor;
48 mOptions = options;
49 if (mOptions[TRDVerboseErrorsBit] && (ParsingErrorsString.size() - 1) != TRDLastParsingError) {
50 LOG(error) << "Verbose error reporting requested, but the mapping of error code to error string is not complete";
51 }
52}
53
54void CruRawReader::incrementErrors(int error, int hcid, std::string message)
55{
56 mEventRecords.incParsingError(error, hcid);
57 if (mOptions[TRDVerboseErrorsBit] && error != NoError) {
58 std::string logMessage = "Detected PE " + ParsingErrorsString.at(error);
59 if (hcid >= 0) {
60 logMessage += " HCID " + std::to_string(hcid);
61 }
62 if (!message.empty()) {
63 logMessage += " Message: " + message;
64 }
65 LOG(info) << logMessage;
66 }
67}
68
70{
71 // first check for FEEID from unconfigured CRU
72 TRDFeeID feeid;
73 feeid.word = o2::raw::RDHUtils::getFEEID(rdh);
74 if (((feeid.word) >> 4) == 0xfff) { // error condition is 0xfff? as the end point is known to the cru, but the rest is configured.
75 if (mMaxErrsPrinted > 0) {
76 LOG(error) << "RDH check failed due to 0xfff. FLP not configured, call TRD on call. Whole feeid = " << std::hex << (unsigned int)feeid.word;
77 checkNoErr();
78 }
79 incrementErrors(FEEIDIsFFFF, -1, fmt::format("failed due to 0xfff? : {} whole feeid: {} ", feeid.word, (unsigned int)feeid.word));
80 return false;
81 }
82 if (feeid.supermodule > 17) {
83 if (mMaxWarnPrinted > 0) {
84 LOG(warn) << "Wrong supermodule number " << std::dec << (int)feeid.supermodule << " detected in RDH. Whole feeid : " << std::hex << (unsigned int)feeid.word;
86 }
87 incrementErrors(FEEIDBadSector, -1, fmt::format("Wrong supermodule number {} detected in RDH whole feeid: {}", (int)feeid.supermodule, (unsigned int)feeid.word));
88 return false;
89 }
90 if (o2::raw::RDHUtils::getMemorySize(rdh) <= 0) {
91 if (mMaxWarnPrinted > 0) {
92 LOG(warn) << "Received RDH header with invalid memory size (<= 0) ";
94 }
95 incrementErrors(BadRDHMemSize, -1, fmt::format("Received RDH header with invalid memory size (<= 0) "));
96 return false;
97 }
98 return true;
99}
100
102{
103 if (o2::raw::RDHUtils::getFEEID(rdhPrev) != o2::raw::RDHUtils::getFEEID(rdhCurr)) {
104 if (mMaxWarnPrinted > 0) {
105 LOG(warn) << "ERDH FEEID are not identical in rdh.";
106 checkNoWarn();
107 }
109 return false;
110 }
111 if (o2::raw::RDHUtils::getEndPointID(rdhPrev) != o2::raw::RDHUtils::getEndPointID(rdhCurr)) {
112 if (mMaxWarnPrinted > 0) {
113 LOG(warn) << "ERDH EndPointID are not identical in rdh.";
114 checkNoWarn();
115 }
117 return false;
118 }
119 if (o2::raw::RDHUtils::getTriggerOrbit(rdhPrev) != o2::raw::RDHUtils::getTriggerOrbit(rdhCurr)) {
120 if (mMaxWarnPrinted > 0) {
121 LOG(warn) << "ERDH Orbit are not identical in rdh.";
122 checkNoWarn();
123 }
125 return false;
126 }
127 if (o2::raw::RDHUtils::getCRUID(rdhPrev) != o2::raw::RDHUtils::getCRUID(rdhCurr)) {
128 if (mMaxWarnPrinted > 0) {
129 LOG(warn) << "ERDH CRUID are not identical in rdh.";
130 checkNoWarn();
131 }
133 return false;
134 }
135 uint8_t rdhExpected = o2::raw::RDHUtils::getPacketCounter(rdhPrev) + 1; // packet counter is 8 bits
136 if (o2::raw::RDHUtils::getPacketCounter(rdhCurr) != rdhExpected) {
137 if (mMaxWarnPrinted > 0) {
138 LOG(warn) << "ERDH PacketCounters are not sequential in rdh.";
139 checkNoWarn();
140 }
142 return false;
143 }
144 return true;
145}
146
148{
149 const o2::header::RDHAny* rdh = reinterpret_cast<const o2::header::RDHAny*>(mCurrRdhPtr);
150 auto rdhPrevious = rdh;
151 bool firstRdh = true;
152 uint32_t totalDataInputSize = 0;
153 mTotalHBFPayLoad = 0;
154 if (o2::raw::RDHUtils::getStop(rdh)) {
155 if (mMaxErrsPrinted > 0) {
156 LOGP(error, "First RDH for given HBF for FEE ID {:#04x} has stop bit set", o2::raw::RDHUtils::getFEEID(rdh));
157 checkNoErr();
158 }
159 return -1;
160 }
161
162 // loop until RDH stop header
163 while (!o2::raw::RDHUtils::getStop(rdh)) { // carry on till the end of the event.
164 if (mOptions[TRDVerboseBit]) {
165 LOG(info) << "Current RDH is as follows:";
166 try {
168 } catch (std::runtime_error& e) {
169 LOG(error) << e.what();
170 }
171 LOGP(debug, "mDataBufferSize {}, mDataBufferPtr {}, mCurrRdhPtr {}, totalDataInputSize {}. Already read: {}. Current payload {}", mDataBufferSize, fmt::ptr(mDataBufferPtr), fmt::ptr(mCurrRdhPtr), totalDataInputSize, mCurrRdhPtr - mDataBufferPtr, o2::raw::RDHUtils::getMemorySize(rdh));
172 }
173 if (!checkRDH(rdh)) {
174 return -1;
175 }
176 if (!firstRdh && !compareRDH(rdhPrevious, rdh)) {
177 // previous and current RDHs are inconsistent, this should not happen
178 return -1;
179 }
180 rdhPrevious = rdh;
181 firstRdh = false;
182 auto headerSize = o2::raw::RDHUtils::getHeaderSize(rdh);
183 auto memorySize = o2::raw::RDHUtils::getMemorySize(rdh);
184 auto offsetToNext = o2::raw::RDHUtils::getOffsetToNext(rdh);
185 auto rdhpayload = memorySize - headerSize;
186 mFEEID.word = o2::raw::RDHUtils::getFEEID(rdh);
187 mCRUEndpoint = o2::raw::RDHUtils::getEndPointID(rdh); // the upper or lower half of the currently parsed cru 0-14 or 15-29
188 mCRUID = o2::raw::RDHUtils::getCRUID(rdh);
189 mIR = o2::raw::RDHUtils::getTriggerIR(rdh); // the orbit counter is taken from the RDH here, the bc is overwritten later from the HalfCRUHeader
190
191 if (totalDataInputSize + memorySize >= mDataBufferSize) {
192 // the size of the current RDH is larger than it can possibly be (we still expect a STOP RDH)
193 if (mMaxErrsPrinted > 0) {
194 LOGP(error, "RDH memory size of {} + already read data size {} = {} >= {} (total available buffer size) from CRU with FEE ID {:#04x}",
195 memorySize, totalDataInputSize, memorySize + totalDataInputSize, mDataBufferSize, mFEEID.word);
196 checkNoErr();
197 }
198 // we drop this broken RDH block, but try to process what we have already put into mHBFPayload
199 break;
200 }
201
202 // copy the contents of the current RDH into the buffer to be parsed, RDH payload is memory size minus header size
203 std::memcpy((char*)&mHBFPayload[0] + mTotalHBFPayLoad, ((char*)rdh) + headerSize, rdhpayload);
204 // copy the contents of the current rdh into the buffer to be parsed
205 mTotalHBFPayLoad += rdhpayload;
206 totalDataInputSize += offsetToNext;
207 // move to next rdh
208 mCurrRdhPtr += offsetToNext;
209 rdh = reinterpret_cast<const o2::header::RDHAny*>(mCurrRdhPtr);
210 }
211 // move past the STOP RDH
212 mCurrRdhPtr += o2::raw::RDHUtils::getOffsetToNext(rdh);
213
214 if (mOptions[TRDVerboseBit]) {
215 LOG(info) << "Current RDH is as follows (should have STOP bit set):";
216 try {
218 } catch (std::runtime_error& e) {
219 LOG(error) << e.what();
220 }
221 }
222
223 // at this point the entire HBF data payload is sitting in mHBFPayload and the total data count is mTotalHBFPayLoad
224 int iteration = 0;
225 mHBFoffset32 = 0;
226 mPreviousHalfCRUHeaderSet = false;
227 while (mHBFoffset32 < (mTotalHBFPayLoad / 4)) {
228 if (mOptions[TRDVerboseBit]) {
229 LOGP(info, "Current half-CRU iteration {}, current offset in the HBF payload {}, total payload in number of 32-bit words {}", iteration, mHBFoffset32, mTotalHBFPayLoad / 4);
230 }
231 if (!processHalfCRU(iteration)) {
232 //dump rest of this rdh payload, something screwed up.
233 break;
234 }
235 iteration++;
236 } // loop of halfcru's while there is still data in the heart beat frame.
237
238 return totalDataInputSize;
239}
240
242{
243 // mHBFoffset32 is the current offset into the current buffer,
244 //
245 mDigitHCHeader.word = mHBFPayload[mHBFoffset32++];
246
247 // in case DigitHCHeader1 is not available for providing the phase, flag with invalid one
248 mPreTriggerPhase = INVALIDPRETRIGGERPHASE;
249
250 // a hack used to make old data readable (e.g. Kr from 2021)
251 if (mDigitHCHeader.major == 0 && mDigitHCHeader.minor == 0 && mDigitHCHeader.numberHCW == 0) {
252 mDigitHCHeader.major = mHalfChamberMajor;
253 mDigitHCHeader.minor = 42;
254 mDigitHCHeader.numberHCW = mHalfChamberWords;
255 if (mHalfChamberWords == 0 || mHalfChamberMajor == 0) {
256 if (mMaxWarnPrinted > 0) {
257 LOG(alarm) << "DigitHCHeader is corrupted and using a hack as workaround is not configured";
258 checkNoWarn(false);
259 }
260 return false;
261 }
262 }
263
264 // compare the half chamber ID from the digit HC header with the reference one obtained from the link ID
265 int halfChamberIdHeader = mDigitHCHeader.supermodule * NHCPERSEC + mDigitHCHeader.stack * NLAYER * 2 + mDigitHCHeader.layer * 2 + mDigitHCHeader.side;
266 if (hcid != halfChamberIdHeader) {
267 incrementErrors(DigitHCHeaderMismatch, hcid, fmt::format("HCID mismatch detected. HCID from DigitHCHeader: {}, HCID from RDH: {}", halfChamberIdHeader, hcid));
268 if (mMaxWarnPrinted > 0) {
269 LOGF(warning, "HCID mismatch in DigitHCHeader detected for ref HCID %i. DigitHCHeader says HCID is %i", hcid, halfChamberIdHeader);
270 checkNoWarn();
271 }
272 return false;
273 }
274
275 int additionalHeaderWords = mDigitHCHeader.numberHCW;
276 if (additionalHeaderWords >= 3) {
278 if (mMaxWarnPrinted > 0) {
279 LOGF(warn, "Found too many additional words (%i) in DigitHCHeader 0x%08x", additionalHeaderWords, mDigitHCHeader.word);
280 checkNoWarn();
281 }
282 return false;
283 }
284 std::bitset<3> headersfound;
285 std::array<uint32_t, 3> headers{0};
286
287 for (int headerwordcount = 0; headerwordcount < additionalHeaderWords; ++headerwordcount) {
288 headers[headerwordcount] = mHBFPayload[mHBFoffset32++];
289 switch (getDigitHCHeaderWordType(headers[headerwordcount])) {
290
291 case 1: // header header1;
292 if (headersfound.test(0)) {
293 // we have a problem, we already have a Digit HC Header1, we are lost.
294 if (mOptions[TRDVerboseErrorsBit]) {
295 LOGF(warn, "We have more than one DigitHCHeader of type 1. Current word in hex %08x", headers[headerwordcount]);
296 printDigitHCHeader(mDigitHCHeader, headers.data());
297 }
299 return false;
300 }
301 DigitHCHeader1 header1;
302 header1.word = headers[headerwordcount];
303 mPreTriggerPhase = header1.ptrigphase;
304
305 headersfound.set(0);
306 if ((header1.numtimebins > TIMEBINS) || (header1.numtimebins < 3) || mTimeBinsFixed && header1.numtimebins != mTimeBins) {
307 if (mOptions[TRDVerboseErrorsBit]) {
308 LOGF(warn, "According to Digit HC Header 1 there are %i time bins configured", (int)header1.numtimebins);
309 printDigitHCHeader(mDigitHCHeader, headers.data());
310 }
312 return false;
313 }
314 mTimeBins = header1.numtimebins;
315 break;
316
317 case 2: // header header2;
318 if (headersfound.test(1)) {
319 // we have a problem, we already have a Digit HC Header2, we are hereby lost.
320 if (mOptions[TRDVerboseErrorsBit]) {
321 LOGF(warn, "We have more than one DigitHCHeader of type 2. Current word in hex %08x", headers[headerwordcount]);
322 printDigitHCHeader(mDigitHCHeader, headers.data());
323 }
325 return false;
326 }
327 /* Currently we don't do anything with the information stored in DigitHCHeader2
328 DigitHCHeader2 header2;
329 header2.word = headers[headerwordcount];
330 */
331 headersfound.set(1);
332 break;
333
334 case 3: // header header3;
335 if (headersfound.test(2)) {
336 // we have a problem, we already have a Digit HC Header3, we are hereby lost.
337 if (mOptions[TRDVerboseErrorsBit]) {
338 LOG(info) << "We have a >1 Digit HC Header 2 : " << std::hex << " raw: 0x" << headers[headerwordcount];
339 printDigitHCHeader(mDigitHCHeader, headers.data());
340 }
342 return false;
343 }
344 DigitHCHeader3 header3;
345 header3.word = headers[headerwordcount];
346 headersfound.set(2);
347 if (mHaveSeenDigitHCHeader3) {
348 if (header3.svnver != mPreviousDigitHCHeadersvnver || header3.svnrver != mPreviousDigitHCHeadersvnrver) {
349 if (mOptions[TRDVerboseErrorsBit]) {
350 LOG(warning) << "Conflicting SVN in DigitHCHeader3";
351 printDigitHCHeader(mDigitHCHeader, headers.data());
352 }
354 return false;
355 }
356 } else {
357 mPreviousDigitHCHeadersvnver = header3.svnver;
358 mPreviousDigitHCHeadersvnrver = header3.svnrver;
359 mHaveSeenDigitHCHeader3 = true;
360 }
361 break;
362
363 default:
364 incrementErrors(DigitHeaderWrongType, hcid, fmt::format("Failed to determine DigitHCHeader type for {:#010x}", headers[headerwordcount]));
365 return false;
366 }
367 }
368 if (mOptions[TRDVerboseBit]) {
369 printDigitHCHeader(mDigitHCHeader, &headers[0]);
370 }
371
372 return true;
373}
374
376{
377 // process data from one half-CRU
378 // iteration corresponds to the trigger number within the HBF
379
380 // this should only hit that instance where the cru payload is a "blank event" of CRUPADDING32
381 if (mHBFPayload[mHBFoffset32] == CRUPADDING32) {
382 if (mOptions[TRDVerboseBit]) {
383 LOG(info) << "blank rdh payload data at " << mHBFoffset32 << ": 0x" << std::hex << mHBFPayload[mHBFoffset32] << " and 0x" << mHBFPayload[mHBFoffset32 + 1];
384 }
385 int loopcount = 0;
386 while (mHBFPayload[mHBFoffset32] == CRUPADDING32 && loopcount < 8) { // can only ever be an entire 256 bit word hence a limit of 8 here.
387 // TODO: check with Guido if it could not actually be more padding words
388 mHBFoffset32++;
389 loopcount++;
390 }
391 return true;
392 }
393
394 auto crustart = std::chrono::high_resolution_clock::now();
395
396 memcpy(&mCurrentHalfCRUHeader, &(mHBFPayload[mHBFoffset32]), sizeof(HalfCRUHeader));
397 mHBFoffset32 += sizeof(HalfCRUHeader) / 4; // advance past the header.
398 if (mOptions[TRDVerboseBit]) {
399 //output the cru half chamber header : raw/parsed
400 printHalfCRUHeader(mCurrentHalfCRUHeader);
401 }
402 if (!halfCRUHeaderSanityCheck(mCurrentHalfCRUHeader)) {
403 incrementErrors(HalfCRUCorrupt, -1, fmt::format("HalfCRU header failed sanity check for FEEID with {:#x} ", (unsigned int)mFEEID.word));
404 mWordsRejected += (mTotalHBFPayLoad / 4) - mHBFoffset32 + sizeof(HalfCRUHeader) / 4;
405 return false; // not recoverable, since we don't know when the next half-cru header would start
406 }
407
408 o2::trd::getHalfCRULinkDataSizes(mCurrentHalfCRUHeader, mCurrentHalfCRULinkLengths);
409 o2::trd::getHalfCRULinkErrorFlags(mCurrentHalfCRUHeader, mCurrentHalfCRULinkErrorFlags);
410 uint32_t totalHalfCRUDataLength256 = std::accumulate(mCurrentHalfCRULinkLengths.begin(),
411 mCurrentHalfCRULinkLengths.end(),
412 0U);
413 uint32_t totalHalfCRUDataLength32 = totalHalfCRUDataLength256 * 8; // convert to 32-bit words
414
415 if (mOptions[TRDOnlyCalibrationTriggerBit] && mCurrentHalfCRUHeader.EventType == o2::trd::constants::ETYPEPHYSICSTRIGGER) {
416 // skip triggers without digits
417 mWordsRejected += totalHalfCRUDataLength32;
418 mHBFoffset32 += totalHalfCRUDataLength32;
419 return true;
420 }
421 if (mCRUEndpoint != mCurrentHalfCRUHeader.EndPoint) {
422 if (mMaxWarnPrinted > 0) {
423 LOGF(warn, "End point mismatch detected. HalfCRUHeader says %i, RDH says %i", mCurrentHalfCRUHeader.EndPoint, mCRUEndpoint);
424 checkNoWarn();
425 }
426 // try next trigger (HalfCRUHeader)
427 mHBFoffset32 += totalHalfCRUDataLength32;
428 mWordsRejected += totalHalfCRUDataLength32;
429 return true;
430 }
431
432 if (mPreviousHalfCRUHeaderSet) {
433 // for the second trigger (and thus second HalfCRUHeader we see) we can do some more sanity checks
434 // in case one check fails, we try to go to the next HalfCRUHeader
435 if (mCurrentHalfCRUHeader.EndPoint != mPreviousHalfCRUHeader.EndPoint) {
436 if (mMaxWarnPrinted > 0) {
437 LOGF(warn, "For current half-CRU index %i we have end point %i, while the previous end point was %i", iteration, mCurrentHalfCRUHeader.EndPoint, mPreviousHalfCRUHeader.EndPoint);
438 checkNoWarn();
439 }
441 mWordsRejected += totalHalfCRUDataLength32;
442 mHBFoffset32 += totalHalfCRUDataLength32;
443 return true;
444 }
445 if (mCurrentHalfCRUHeader.StopBit != mPreviousHalfCRUHeader.StopBit) {
446 if (mMaxWarnPrinted > 0) {
447 LOGF(warn, "For current half-CRU index %i we have stop bit %i, while the previous stop bit was %i", iteration, mCurrentHalfCRUHeader.StopBit, mPreviousHalfCRUHeader.StopBit);
448 checkNoWarn();
449 }
451 mWordsRejected += totalHalfCRUDataLength32;
452 mHBFoffset32 += totalHalfCRUDataLength32;
453 return true;
454 }
455 }
456 mPreviousHalfCRUHeader = mCurrentHalfCRUHeader;
457 mPreviousHalfCRUHeaderSet = true;
458
459 //can this half cru length fit into the available space of the rdh accumulated payload
460 if (totalHalfCRUDataLength32 > (mTotalHBFPayLoad / 4) - mHBFoffset32) {
461 if (mMaxWarnPrinted > 0) {
462 LOGP(warn, "HalfCRU header says it contains more data ({} 32-bit words) than is remaining in the payload ({} 32-bit words)", totalHalfCRUDataLength32, ((mTotalHBFPayLoad / 4) - mHBFoffset32));
463 checkNoWarn();
464 }
466 mWordsRejected += (mTotalHBFPayLoad / 4) - mHBFoffset32;
467 return false; // not recoverable, since we don't know when the next half-cru header would start
468 }
469
470 mIR.bc = mCurrentHalfCRUHeader.BunchCrossing; // TRD BC is obtained from the HalfCRUHeader
471 if (o2::ctp::TriggerOffsetsParam::Instance().LM_L0 > (int)mIR.bc) {
472 // applying the configured BC shift would lead to negative BC, hence we reject this trigger
473 // dump to the end of this cruhalfchamberheader
474 // data to dump is totalHalfCRUDataLength32
475 mHBFoffset32 += totalHalfCRUDataLength32; // go to the end of this halfcruheader and payload.
476 mWordsRejected += totalHalfCRUDataLength32; // add the rejected data to the accounting;
477 incrementErrors(HalfCRUBadBC, -1, fmt::format("Trigger rejected, since BC shift would move the BC to a negative value. LM_L0: {} bc: {}", o2::ctp::TriggerOffsetsParam::Instance().LM_L0, (int)mIR.bc));
478 return true; // nothing particularly wrong with the data, we just dont want it, as a trigger problem
479 } else {
480 // apply CTP offset shift
482 }
483 mEventRecords.setCurrentEventRecord(mIR);
484 if (mCurrentHalfCRUHeader.EventType == ETYPECALIBRATIONTRIGGER) {
486 }
487
488 //loop over links
489 uint32_t linksizeAccum32 = 0; // accumulated size of all links in 32-bit words
490 auto hbfOffsetTmp = mHBFoffset32; // store current position at the beginning of the half-CRU payload data
491 for (int currentlinkindex = 0; currentlinkindex < NLINKSPERHALFCRU; currentlinkindex++) {
492 bool linkOK = true; // flag links which could be processed successfully, without any rejected word
493 int cruIdx = mFEEID.supermodule * 2 + mFEEID.side; // 2 CRUs per SM, side defining A/C-side CRU
494 int halfCruIdx = cruIdx * 2 + mFEEID.endpoint; // endpoint (0 or 1) defines half-CRU
495 int linkIdxGlobal = halfCruIdx * NLINKSPERHALFCRU + currentlinkindex; // global link ID [0..1079]
496 int halfChamberId = mLinkMap->getHCID(linkIdxGlobal);
497 mEventRecords.getCurrentEventRecord().getCounters().mLinkWords[halfChamberId] += mCurrentHalfCRULinkLengths[currentlinkindex];
498 mEventRecords.getCurrentEventRecord().getCounters().mLinkErrorFlag[halfChamberId] |= mCurrentHalfCRULinkErrorFlags[currentlinkindex];
499 mEventRecords.incLinkErrorFlags(halfChamberId, mCurrentHalfCRULinkErrorFlags[currentlinkindex]); // TODO maybe has more meaning on a per event basis?
500 mEventRecords.incLinkWords(halfChamberId, mCurrentHalfCRULinkLengths[currentlinkindex]);
501 uint32_t currentlinksize32 = mCurrentHalfCRULinkLengths[currentlinkindex] * 8; // x8 to go from 256 bits to 32 bit;
502 uint32_t endOfCurrentLink = mHBFoffset32 + currentlinksize32;
503 if (mOptions[TRDVerboseBit] && currentlinksize32 > 0) {
504 LOGP(info, "Reading {} (link ID {}, HCID {}) with {} 32-bit words", HelperMethods::getSectorStackLayerSide(halfChamberId), linkIdxGlobal, halfChamberId, currentlinksize32);
505 }
506 linksizeAccum32 += currentlinksize32;
507 if (currentlinksize32 == 0) {
508 mEventRecords.incLinkNoData(halfChamberId);
509 }
510 if (mOptions[TRDVerboseBit]) {
511 if (currentlinksize32 > 0) {
512 LOGF(info, "Half-CRU link %i raw dump before parsing starts:", currentlinkindex);
513 for (uint32_t dumpoffset = mHBFoffset32; dumpoffset < mHBFoffset32 + currentlinksize32; dumpoffset += 8) {
514 LOGF(info, "0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x", mHBFPayload[dumpoffset], mHBFPayload[dumpoffset + 1], mHBFPayload[dumpoffset + 2], mHBFPayload[dumpoffset + 3], mHBFPayload[dumpoffset + 4], mHBFPayload[dumpoffset + 5], mHBFPayload[dumpoffset + 6], mHBFPayload[dumpoffset + 7]);
515 }
516 } else {
517 LOGF(info, "Half-CRU link %i has zero link size", currentlinkindex);
518 }
519 }
520 if (currentlinksize32 > 0) { // if link is not empty
521 auto trackletparsingstart = std::chrono::high_resolution_clock::now();
522 if (mOptions[TRDVerboseBit]) {
523 LOGF(info, "Tracklet parser starting at offset %u and processing up to %u words", mHBFoffset32, currentlinksize32);
524 }
525 int trackletWordsRejected = 0;
526 int trackletWordsReadOK = 0;
527 int numberOfTrackletsFound = 0; // count the number of found tracklets for this link
528 int trackletWordsRead = parseTrackletLinkData(currentlinksize32, halfChamberId, trackletWordsRejected, trackletWordsReadOK, numberOfTrackletsFound);
529 std::chrono::duration<float, std::micro> trackletparsingtime = std::chrono::high_resolution_clock::now() - trackletparsingstart;
530 if (mOptions[TRDVerboseBit]) {
531 LOGP(info, "Could read {} 32-bit words w/o errors, found {} tracklets, rejected {} 32-bit words for {}. Parsing OK? {}", trackletWordsReadOK, numberOfTrackletsFound, trackletWordsRejected, HelperMethods::getSectorStackLayerSide(halfChamberId), (trackletWordsRead != -1 && trackletWordsRejected == 0));
532 }
533 if (trackletWordsRead == -1) {
534 // something went wrong bailout of here.
535 mHBFoffset32 = hbfOffsetTmp + linksizeAccum32;
537 continue; // move to next link of this half-CRU
538 }
539 if (trackletWordsRejected > 0) {
540 linkOK = false;
541 }
542 mHBFoffset32 += trackletWordsRead;
543 if (mCurrentHalfCRUHeader.EventType == ETYPEPHYSICSTRIGGER &&
544 endOfCurrentLink - mHBFoffset32 >= 8) {
545 if (mMaxWarnPrinted > 0) {
546 LOGF(warn, "After successfully parsing the tracklet data for link %i there are %u words remaining which did not get parsed", currentlinkindex, endOfCurrentLink - mHBFoffset32);
547 checkNoWarn();
548 }
549 incrementErrors(UnparsedTrackletDataRemaining, halfChamberId, fmt::format("On link {} there are {} words remaining which did not get parsed", currentlinkindex, endOfCurrentLink - mHBFoffset32));
550 linkOK = false;
551 }
552 mEventRecords.getCurrentEventRecord().incTrackletTime(trackletparsingtime.count());
553 if (mOptions[TRDVerboseBit]) {
554 LOGF(debug, "Read %i tracklet words and rejected %i words", trackletWordsRead, trackletWordsRejected);
555 }
556 mTrackletWordsRejected += trackletWordsRejected;
557 mTrackletWordsRead += trackletWordsRead;
558 mEventRecords.incLinkWordsRead(halfChamberId, trackletWordsRead);
559 mEventRecords.incLinkWordsRejected(halfChamberId, trackletWordsRejected);
560
561 /****************
562 ** DIGITS NOW ***
563 *****************/
564 // Check if we have a calibration trigger ergo we do actually have digits data. check if we are now at the end of the data due to bugs, i.e. if trackletparsing read padding words.
565 if (mHBFoffset32 != endOfCurrentLink &&
566 (mCurrentHalfCRUHeader.EventType == ETYPECALIBRATIONTRIGGER || mOptions[TRDIgnore2StageTrigger]) &&
567 (mHBFPayload[mHBFoffset32] != CRUPADDING32)) {
568 // we still have data on this link, we have a calibration trigger (or ignore the event type) and we are not reading a padding word
569
570 uint32_t offsetBeforeDigitParsing = mHBFoffset32;
571 // the digit HC headers come first
572 if (!parseDigitHCHeaders(halfChamberId)) {
573 mHBFoffset32 = hbfOffsetTmp + linksizeAccum32;
574 continue; // move to next link of this half-CRU
575 }
576 if (mHBFoffset32 - offsetBeforeDigitParsing != 1U + mDigitHCHeader.numberHCW) {
577 if (mMaxErrsPrinted > 0) {
578 LOGF(error, "Did not read as many digit headers (%i) as expected (%i)",
579 mHBFoffset32 - offsetBeforeDigitParsing, mDigitHCHeader.numberHCW + 1);
580 checkNoErr();
581 }
582 mHBFoffset32 = hbfOffsetTmp + linksizeAccum32;
583 continue; // move to next link of this half-CRU
584 }
585
586 mEventRecords.incMajorVersion(mDigitHCHeader.major); // 127 is max histogram goes to 256
587
588 if (mDigitHCHeader.major == 0x47) {
589 // config event so ignore for now and bail out of parsing.
590 //advance data pointers to the end;
591 mHBFoffset32 = hbfOffsetTmp + currentlinksize32;
592 mDigitWordsRejected += hbfOffsetTmp + currentlinksize32; // count full link as rejected
593 LOG(info) << "Configuration event ";
594 } else {
595 auto digitsparsingstart = std::chrono::high_resolution_clock::now();
596 int digitWordsRejected = 0;
597 int digitWordsRead = parseDigitLinkData(endOfCurrentLink - mHBFoffset32, halfChamberId, digitWordsRejected);
598 std::chrono::duration<float, std::micro> digitsparsingtime = std::chrono::high_resolution_clock::now() - digitsparsingstart;
599 if (digitWordsRead == -1) {
600 // something went wrong bailout of here.
601 mHBFoffset32 = hbfOffsetTmp + linksizeAccum32;
602 continue; // move to next link of this half-CRU
603 }
604 mHBFoffset32 += digitWordsRead;
605 if (endOfCurrentLink - mHBFoffset32 >= 8) {
606 // check if some data is lost (probably due to bug in CRU user logic)
607 // we should have max 7 padding words to align the link to 256 bits
608 if (mMaxWarnPrinted > 0) {
609 LOGF(warn, "After successfully parsing the digit data for link %i there are %u words remaining which did not get parsed", currentlinkindex, endOfCurrentLink - mHBFoffset32);
610 checkNoWarn();
611 }
612 incrementErrors(UnparsedDigitDataRemaining, halfChamberId, fmt::format("On link {} there are {} words remaining which did not get parsed", currentlinkindex, endOfCurrentLink - mHBFoffset32));
613 linkOK = false;
614 }
615 if (digitWordsRejected > 0) {
616 linkOK = false;
617 }
618 mEventRecords.getCurrentEventRecord().incDigitTime(digitsparsingtime.count());
619 mEventRecords.incLinkWordsRead(halfChamberId, digitWordsRead);
620 mEventRecords.incLinkWordsRejected(halfChamberId, digitWordsRejected);
621
622 if (mOptions[TRDVerboseBit]) {
623 LOGF(info, "Read %i digit words and rejected %i words", digitWordsRead, digitWordsRejected);
624 }
625 mDigitWordsRead += digitWordsRead;
626 mDigitWordsRejected += digitWordsRejected;
627 }
628 }
629 if (linkOK) {
630 incrementErrors(NoError, halfChamberId);
631 }
632 } else {
633 // OS: link is empty, what does this block mean???
634 if (mCurrentHalfCRUHeader.EventType == ETYPEPHYSICSTRIGGER) {
635 mEventRecords.incMajorVersion(128); // 127 is max histogram goes to 256
636 }
637 }
638 if (mHBFoffset32 != hbfOffsetTmp + linksizeAccum32) {
639 // if only tracklet data is available the tracklet parser advances to the tracklet end marker, but there can still be padding words on the link
640 LOGF(debug, "After processing link %i the payload offset of %u is not the expected %u + %u", currentlinkindex, mHBFoffset32, hbfOffsetTmp, linksizeAccum32);
641 mHBFoffset32 = hbfOffsetTmp + linksizeAccum32;
642 }
643 } // for loop over link index.
644 if (mHBFoffset32 != hbfOffsetTmp + totalHalfCRUDataLength32) {
645 LOGF(debug, "After processing half-CRU data the offset (%u) is not pointing to the expected position (%u + %u = %u)", mHBFoffset32, hbfOffsetTmp, totalHalfCRUDataLength32, totalHalfCRUDataLength32 + hbfOffsetTmp);
646 mHBFoffset32 = hbfOffsetTmp + totalHalfCRUDataLength32;
647 }
648 // we have read in all the digits and tracklets for this event.
649 //digits and tracklets are sitting inside the parsing classes.
650 //extract the vectors and copy them to tracklets and digits here, building the indexing(triggerrecords)
651 //as this is for a single cru half chamber header all the tracklets and digits are for the same trigger defined by the bc and orbit in the rdh which we hold in mIR
652
653 std::chrono::duration<float, std::micro> cruparsingtime = std::chrono::high_resolution_clock::now() - crustart;
654 mEventRecords.getCurrentEventRecord().incTime(cruparsingtime.count());
655
656 //if we get here all is ok.
657 return true;
658}
659
661{
662 if (!sanityCheckTrackletHCHeader(header)) {
663 return false;
664 }
665 int detHeader = HelperMethods::getDetector(((~header.supermodule) & 0x1f), ((~header.stack) & 0x7), ((~header.layer) & 0x7));
666 int hcidHeader = (detHeader * 2 + ((~header.side) & 0x1));
667 if (hcid != hcidHeader) {
668 mHalfChamberMismatches.insert(std::make_pair(hcid, hcidHeader));
669 return false;
670 } else {
671 mHalfChamberHeaderOK.insert(hcid);
672 return true;
673 }
674}
675
676int CruRawReader::parseDigitLinkData(int maxWords32, int hcid, int& wordsRejected)
677{
678 int wordsRead = 0;
680 DigitMCMHeader mcmHeader;
681 DigitMCMADCMask adcMask;
682
683 // data is expected to be ordered
684 int previousMcm = -1;
685 int previousRob = -1;
686 // TODO add check for event counter of DigitMCMHeader?
687 // are the counters expected to be the same for all MCMs for one trigger?
688
689 while (wordsRead < maxWords32 && state != StateFinished) {
690 uint32_t currWord = mHBFPayload[mHBFoffset32 + wordsRead];
691
692 if (state == StateDigitMCMHeader) {
693 ++wordsRead;
694 if (currWord == DIGITENDMARKER) {
696 continue;
697 }
698 mcmHeader.word = currWord;
699 if (!sanityCheckDigitMCMHeader(mcmHeader)) {
700 incrementErrors(DigitMCMHeaderSanityCheckFailure, hcid, fmt::format("DigitMCMHeader {:#010x} failed sanity check", currWord));
701 ++wordsRejected;
702 state = StateMoveToEndMarker; // give up for this link and try to find end markers
703 continue;
704 }
705 // check correct ordering of link data
706 if (previousMcm >= 0) {
707 // first DigitMCMHeader we see for this link
708 if (previousRob > mcmHeader.rob || (mcmHeader.rob == previousRob && mcmHeader.mcm < previousMcm)) {
709 incrementErrors(DigitMCMNotIncreasing, hcid, fmt::format("Current rob/mcm = {}/{}, previous rob/mcm = {}/{}", (int)mcmHeader.rob, (int)mcmHeader.mcm, previousRob, previousMcm));
710 } else if (previousRob == mcmHeader.rob && previousMcm == mcmHeader.mcm) {
711 incrementErrors(DigitMCMDuplicate, hcid, fmt::format("Second MCM header {:#010x} for rob/mcm = {}/{}", currWord, previousRob, previousMcm));
712 }
713 } else {
714 previousMcm = mcmHeader.mcm;
715 previousRob = mcmHeader.rob;
716 }
717 if (mDigitHCHeader.major & 0x20) {
718 // zero suppression (ZS) is ON, we expect ADC mask next
720 } else {
721 // full readout, expect ADC data next
723 }
724 LOGF(debug, "Got DigitMCMHeader 0x%08x", currWord);
725 continue;
726 }
727
728 else if (state == StateDigitADCMask) {
729 ++wordsRead;
730 adcMask.word = currWord;
731 if (!sanityCheckDigitMCMADCMask(adcMask)) {
732 incrementErrors(DigitADCMaskInvalid, hcid, fmt::format("DigitADCMask {:#010x} failed sanity check", currWord));
733 ++wordsRejected;
734 state = StateMoveToEndMarker; // give up for this link and try to find end markers
735 continue;
736 }
738 LOGF(debug, "Got DigitADCMask 0x%08x", currWord);
739 continue;
740 }
741
742 else if (state == StateMoveToDigitMCMHeader) {
743 // the parsing of ADC values went wrong, we search for the next MCM header
744 bool foundHeader = false;
745 while (wordsRead < maxWords32 && !foundHeader) {
746 if (currWord == DIGITENDMARKER) {
747 ++wordsRead;
749 break;
750 }
751 DigitMCMHeader tmpHeader;
752 tmpHeader.word = currWord;
753 if (sanityCheckDigitMCMHeader(tmpHeader)) {
754 foundHeader = true;
756 break;
757 }
758 ++wordsRead;
759 ++wordsRejected;
760 currWord = mHBFPayload[mHBFoffset32 + wordsRead];
761 }
763 // we could neither find a MCM header, nor an endmarker
764 break;
765 }
766 continue;
767 }
768
769 else if (state == StateDigitMCMData) {
770 bool exitChannelLoop = false;
771 state = StateDigitMCMHeader; // after we are done reading the ADC data, by default we expect another MCM header
772 for (int iChannel = 0; iChannel < NADCMCM; ++iChannel) {
773 std::array<uint16_t, TIMEBINS> adcValues{};
774 if (!(mDigitHCHeader.major & 0x20) || adcMask.adcmask & (1UL << iChannel)) {
775 // either ZS is OFF, or the adcMask has iChannel flagged as active
777 int timebin = 0;
778 while (timebin < mTimeBins) {
779 if (currWord == DIGITENDMARKER) {
780 incrementErrors(DigitEndMarkerWrongState, hcid, "Expected Digit ADC data, but found end marker instead");
781 exitChannelLoop = true;
783 ++wordsRead;
784 wordsRejected += timebin / 3; // we are rejecting all ADC data we have already read for this channel
785 break;
786 }
787 data.word = currWord;
788 if ((((iChannel % 2) == 0) && (data.f != 0x3)) || ((iChannel % 2) && (data.f != 0x2))) {
789 incrementErrors(DigitSanityCheck, hcid, fmt::format("Current channel {}, check bits {}. Word {:#010x}", iChannel, (int)data.f, currWord));
790 exitChannelLoop = true;
792 ++wordsRead;
793 ++wordsRejected;
794 break;
795 }
796 adcValues[timebin++] = data.z;
797 adcValues[timebin++] = data.y;
798 adcValues[timebin++] = data.x;
799 ++wordsRead;
800 currWord = mHBFPayload[mHBFoffset32 + wordsRead];
801 } // end time bin loop
802 if (exitChannelLoop) {
803 break;
804 }
805 mEventRecords.getCurrentEventRecord().addDigit(Digit(hcid / 2, (int)mcmHeader.rob, (int)mcmHeader.mcm, iChannel, adcValues, mPreTriggerPhase));
806 ++mDigitsFound;
807 } // end active channel
808 } // end channel loop
809 continue;
810 }
811
812 else if (state == StateMoveToEndMarker) {
813 ++wordsRead;
814 if (currWord == DIGITENDMARKER) {
816 } else {
817 ++wordsRejected;
818 }
819 continue;
820 } // StateMoveToEndMarker
821
822 else if (state == StateSecondEndmarker) {
823 ++wordsRead;
824 if (currWord != DIGITENDMARKER) {
825 incrementErrors(DigitParsingNoSecondEndmarker, hcid, fmt::format("Expected second digit end marker, but found {:#010x} instead", currWord));
826 return -1;
827 }
829 continue;
830 } // StateSecondEndmarker
831 }
832
833 if (state == StateFinished) {
834 // all good, we exited the state machine in the expected state
835 return wordsRead;
836 } else {
837 // not good, something went wrong with digit parsing
838 // e.g. we tried to move to the end marker but reached the link size
839 // without finding one.
840 incrementErrors(DigitParsingExitInWrongState, hcid, fmt::format("Done with digit parsing but state is not StateFinished but in state {}", state));
841 return -1;
842 }
843}
844
845// Returns number of words read (>=0) or error state (<0)
846int CruRawReader::parseTrackletLinkData(int linkSize32, int& hcid, int& wordsRejected, int& wordsReadOK, int& trackletsFound)
847{
848 int wordsRead = 0; // count the number of words which were parsed (both successful and not successful)
849 int state = StateTrackletHCHeader; // we expect to always see a TrackletHCHeader at the beginning of the link
850 // tracklet data for one link is expected to arrive ordered
851 // first MCM in row=0, column=0, then row=0, column=1, ... row=1, column=0, ...
852 int previousColumn = -1;
853 int previousRow = -1;
854 TrackletHCHeader hcHeader;
855 TrackletMCMHeader mcmHeader;
856
857 // main loop we exit only when we reached the end of the link or have seen two tracklet end markers
858 while (wordsRead < linkSize32 && state != StateFinished) {
859 uint32_t currWord = mHBFPayload[mHBFoffset32 + wordsRead];
860
862 ++wordsRead;
863 if (mTrackletHCHeaderState == 0) {
864 LOG(error) << "It's not allowed anymore to have the TrackletHCHeader never present";
865 return -1;
866 } else if (mTrackletHCHeaderState == 1) {
867 // in case tracklet data is present we have a TrackletHCHeader, otherwise we should
868 // see a DigitHCHeader
869 hcHeader.word = currWord;
870 if (!isTrackletHCHeaderOK(hcHeader, hcid)) {
871 // either the TrackletHCHeader is corrupt or we have only digits on this link
872 return 0;
873 }
874 ++wordsReadOK;
875 state = StateTrackletMCMHeader; // we do expect tracklets following
876 } else {
877 // we always expect a TrackletHCHeader as first word for a link (default)
878 hcHeader.word = currWord;
879 if (!isTrackletHCHeaderOK(hcHeader, hcid)) {
880 // we have a corrupt TrackletHCHeader
881 incrementErrors(TrackletHCHeaderFailure, hcid, fmt::format("Invalid word {:#010x} for the expected TrackletHCHeader", currWord));
883 ++wordsRejected;
884 continue;
885 }
886 ++wordsReadOK;
887 state = StateTrackletMCMHeader; // we might have tracklets following or tracklet end markers
888 }
889 } // StateTrackletHCHeader
890
891 else if (state == StateTrackletMCMHeader) {
892 if (currWord == TRACKLETENDMARKER) {
893 ++wordsReadOK;
894 state = StateSecondEndmarker; // we expect a second tracklet end marker to follow
895 } else {
896 mcmHeader.word = currWord;
897 if (!sanityCheckTrackletMCMHeader(mcmHeader)) {
898 incrementErrors(TrackletMCMHeaderSanityCheckFailure, hcid, fmt::format("Invalid word {:#010x} for the expected TrackletMCMHeader", currWord));
899 // invalid MCM header, but we can try to find another valid MCM header before the end markers.
900 // If there are no end markers we can anyhow not parse digits if they were there.
901 // Other invalid MCM headers can be caught by the check on their ordering (row/column)
902 // state = StateMoveToEndMarker;
903 ++wordsRead;
904 ++wordsRejected;
905 continue;
906 }
907 // check ordering by MCM index
908 if (previousColumn >= 0) {
909 if (mcmHeader.padrow < previousRow || (mcmHeader.padrow == previousRow && mcmHeader.col < previousColumn)) {
910 incrementErrors(TrackletDataWrongOrdering, hcid, fmt::format("Current padrow/column = {}/{}, previous padrow/column = {}/{}", (int)mcmHeader.padrow, (int)mcmHeader.col, previousRow, previousColumn));
911 ++wordsRead;
912 ++wordsRejected;
913 continue;
914 } else if (mcmHeader.padrow == previousRow && mcmHeader.col == previousColumn) {
915 incrementErrors(TrackletDataDuplicateMCM, hcid, fmt::format("Second MCM header {:#010x} for padrow/column = {}/{}", currWord, previousRow, previousColumn));
916 ++wordsRead;
917 ++wordsRejected;
918 continue;
919 }
920 } else {
921 previousColumn = mcmHeader.col;
922 previousRow = mcmHeader.padrow;
923 }
924 ++wordsReadOK;
925 state = StateTrackletMCMData; // tracklet words must be following, unless the HC header format indicates sending of empty MCM headers
926 }
927 ++wordsRead;
928 } // StateTrackletMCMHeader
929
930 else if (state == StateTrackletMCMData) {
931 bool addedTracklet = false; // flag whether we actually found a tracklet
932 for (int iCpu = 0; iCpu < 3; ++iCpu) {
933 if (((mcmHeader.word >> (1 + iCpu * 8)) & 0xff) == 0xff) {
934 // iCpu did not produce a tracklet.
935 // Since no empty tracklet words are sent, we don't need to move to the next word.
936 // Instead, we check if the next CPU is supposed to have sent a tracklet
937 continue;
938 }
939 if (currWord == TRACKLETENDMARKER) {
940 if ((hcHeader.format & 0x2) == 0x2) {
941 // format indicates no empty MCM headers are sent, so we should not see an end marker here
942 incrementErrors(TrackletDataMissing, hcid, fmt::format("For the MCM Header {:#010x} we expected a tracklet from CPU {}, but got an endmarker instead", mcmHeader.word, iCpu));
943 }
944 state = StateSecondEndmarker; // we expect a second tracklet end marker to follow
945 break;
946 }
947 if ((currWord & 0x1) == 0x1) {
948 // the reserved bit of the tracklet MCM data is set, we don't create a tracklet from this word
949 incrementErrors(TrackletMCMDataFailure, hcid, fmt::format("Invalid word {:#010x} for the expected TrackletMCMData", currWord));
950 ++wordsRejected;
951 ++wordsRead;
952 continue;
953 }
954 TrackletMCMData mcmData;
955 mcmData.word = currWord;
956 mEventRecords.getCurrentEventRecord().addTracklet(assembleTracklet64(hcHeader.format, mcmHeader, mcmData, iCpu, hcid));
957 ++trackletsFound;
958 ++mTrackletsFound;
959 addedTracklet = true;
960 ++wordsRead;
961 ++wordsReadOK;
962 if (wordsRead == linkSize32) {
963 incrementErrors(TrackletNoTrackletEndMarker, hcid, fmt::format("After reading the word {:#010x} we are at the end of the link data", currWord));
964 return -1;
965 }
966 currWord = mHBFPayload[mHBFoffset32 + wordsRead];
967 }
969 ++wordsRead;
970 continue;
971 }
972 if (!addedTracklet) {
973 // we did not add a tracklet -> do we expect empty MCM headers?
974 if ((hcHeader.format & 0x2) != 0x2) {
975 // yes, this is OK and the next word should be MCM header or end marker
976 ++wordsRead;
978 continue;
979 } else {
980 // no tracklet was found, but we should have found one
981 ++wordsRead;
982 ++wordsRejected;
984 continue;
985 }
986 }
988 continue;
989 } // StateTrackletMCMData
990
991 else if (state == StateSecondEndmarker) {
992 ++wordsRead;
993 if (currWord != TRACKLETENDMARKER) {
994 incrementErrors(TrackletNoSecondEndMarker, hcid, fmt::format("Invalid word {:#010x} for the expected second end marker", currWord));
995 return -1;
996 }
997 ++wordsReadOK;
999 } // StateSecondEndmarker
1000
1001 else if (state == StateMoveToEndMarker) {
1002 ++wordsRead;
1003 if (currWord == TRACKLETENDMARKER) {
1005 } else {
1006 ++wordsRejected;
1007 }
1008 continue;
1009 } // StateMoveToEndMarker
1010
1011 else {
1012 // should never happen
1013 LOG(error) << "Tracklet parser ended up in unknown state";
1014 }
1015
1016 } // end of state machine
1017
1018 if (mTrackletHCHeaderState == 1 && trackletsFound == 0) {
1019 if (mMaxErrsPrinted > 0) {
1020 LOG(error) << "We found a TrackletHCHeader in mode 1, but did not add any tracklets";
1021 checkNoErr();
1022 }
1023 }
1024
1025 if (state == StateFinished) {
1026 // all good, we exited the state machine in the expected state
1027 return wordsRead;
1028 } else {
1029 // not good, something went wrong with tracklet parsing
1030 // e.g. we tried to move to the end marker but reached the link size
1031 // without finding one.
1032 incrementErrors(TrackletExitingNoTrackletEndMarker, hcid, "Exiting tracklet parsing not in the state finished");
1033 return -1;
1034 }
1035}
1036
1038{
1039 // TODO take care of special tracklet formats?
1040 uint16_t pos = mcmData.pos;
1041 uint16_t slope = mcmData.slope;
1042 // The 8-th bit of position and slope are always flipped in the FEE.
1043 // We flip them back while reading the raw data so that they are stored
1044 // without flipped bits in the CTFs
1045 pos ^= 0x80;
1046 slope ^= 0x80;
1047 uint32_t hpid = (mcmHeader.word >> (1 + cpu * 8)) & 0xff;
1048 uint32_t lpid = mcmData.pid;
1049 // The combined 20 bit PID information from the MCM header and the tracklet word
1050 // is given by ((hpid << 12) | lpid).
1051 // hpid holds the upper 8 bit and lpid the lower 12 bit.
1052 int q0, q1, q2;
1053 if (format & 0x1) {
1054 // DQR enabled
1055 int scaleFactor = (hpid >> 6) & 0x3;
1056 q0 = (scaleFactor << 6) | (lpid & 0x3f);
1057 q1 = (scaleFactor << 6) | ((lpid >> 6) & 0x3f);
1058 q2 = (scaleFactor << 6) | (hpid & 0x3f);
1059 } else {
1060 // DQR disabled
1061 q0 = lpid & 0x7f;
1062 q1 = ((hpid & 0x3) << 5) | ((lpid >> 7) & 0x1f);
1063 q2 = (hpid >> 2) & 0x3f;
1064 }
1065 return Tracklet64(format, hcid, mcmHeader.padrow, mcmHeader.col, pos, slope, q0, q1, q2);
1066}
1067
1069{
1070 // we print 8 32-bit words per line
1071 LOG(info) << "Dumping full input payload ----->";
1072 for (int iWord = 0; iWord < (mDataBufferSize / 4); iWord += 8) {
1073 LOGF(info, "Word %4i/%4i: 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x",
1074 iWord, mDataBufferSize / 4,
1075 *((uint32_t*)mDataBufferPtr + iWord), *((uint32_t*)mDataBufferPtr + iWord + 1), *((uint32_t*)mDataBufferPtr + iWord + 2), *((uint32_t*)mDataBufferPtr + iWord + 3),
1076 *((uint32_t*)mDataBufferPtr + iWord + 4), *((uint32_t*)mDataBufferPtr + iWord + 5), *((uint32_t*)mDataBufferPtr + iWord + 6), *((uint32_t*)mDataBufferPtr + iWord + 7));
1077 }
1078 LOG(info) << "<------ Done dumping full input payload";
1079}
1080
1082{
1083 if (!mLinkMap) {
1084 LOG(error) << "No mapping for Link ID to HCID provided from CCDB";
1085 return;
1086 }
1087 if (mOptions[TRDVerboseBit]) {
1089 }
1090
1091 mCurrRdhPtr = mDataBufferPtr; // set the pointer to the current RDH to the beginning of the payload
1092 while ((mCurrRdhPtr - mDataBufferPtr) < mDataBufferSize) {
1093
1094 int dataRead = processHBFs();
1095
1096 if (dataRead < 0) {
1097 if (mMaxWarnPrinted > 0) {
1098 LOG(warn) << "Received invalid RDH, rejecting given HBF entirely";
1099 checkNoWarn();
1100 }
1101 break;
1102 } else {
1103 if (mOptions[TRDVerboseBit]) {
1104 LOGP(info, "Done processing HBFs. Total input size was {} bytes (including all headers and padding words, excluding 64 bytes for stop RDH)", dataRead);
1105 }
1106 }
1107 }
1108};
1109
1111{
1112 LOG(info) << "Listing the half-chambers from which we have seen correct TrackletHCHeaders:";
1113 int prevSec = -1;
1114 int currSec = -1;
1115 std::string message;
1116 for (auto hcid : mHalfChamberHeaderOK) {
1117 int currDet = hcid / 2;
1118 currSec = HelperMethods::getSector(currDet);
1119 std::string side = (hcid % 2 == 0) ? "A" : "B";
1120 if (currSec != prevSec) {
1121 if (!message.empty()) {
1122 LOG(info) << message;
1123 message.clear();
1124 }
1125 prevSec = currSec;
1126 }
1127 message += fmt::format("{:#02}_{}_{}{} ", currSec, HelperMethods::getStack(currDet), HelperMethods::getLayer(currDet), side.c_str());
1128 }
1129 if (!message.empty()) {
1130 LOG(info) << message;
1131 }
1132
1133 if (!mHalfChamberMismatches.empty()) {
1134 LOG(warn) << "Found HCID mismatch(es). Printing one by one.";
1135 }
1136 for (const auto& elem : mHalfChamberMismatches) {
1137 LOGF(info, "HCID deduced from RDH (link ID): %i, HCID from TrackletHCHeader: %i", elem.first, elem.second);
1138 }
1139}
1140
1141//write the output data directly to the given DataAllocator from the datareader task.
1143{
1144 mEventRecords.sendData(pc, mOptions[TRDGenerateStats], mOptions[TRDSortDigits], mOptions[TRDLinkStats]);
1145}
1146
1148{
1149 mEventRecords.reset();
1150 mTrackletsFound = 0;
1151 mDigitsFound = 0;
1152 mDigitWordsRead = 0;
1153 mDigitWordsRejected = 0;
1154 mTrackletWordsRead = 0;
1155 mTrackletWordsRejected = 0;
1156 mWordsRejected = 0;
1157}
1158
1159void CruRawReader::checkNoWarn(bool silently)
1160{
1161 if (!mOptions[TRDVerboseErrorsBit]) {
1162 if (--mMaxWarnPrinted == 0) {
1163 if (silently) {
1164 // put the warning message into the log file without polluting the InfoLogger
1165 LOG(warn) << "Warnings limit reached, the following ones will be suppressed";
1166 } else {
1167 // makes sense only for warnings with "alarm" severity
1168 LOG(alarm) << "Warnings limit reached, the following ones will be suppressed";
1169 }
1170 }
1171 }
1172}
1173
1175{
1176 if (!mOptions[TRDVerboseErrorsBit]) {
1177 if (--mMaxErrsPrinted == 0) {
1178 LOG(error) << "Errors limit reached, the following ones will be suppressed";
1179 }
1180 }
1181}
1182
1183} // namespace o2::trd
benchmark::State & state
TRD raw data translator.
Global TRD definitions and constants.
A helper class to iteratate over all parts of all input routes.
uint16_t pos
Definition RawData.h:3
uint32_t cpu
Definition RawData.h:6
uint32_t side
Definition RawData.h:0
uint16_t slope
Definition RawData.h:1
std::ostringstream debug
int parseTrackletLinkData(int linkSize32, int &hcid, int &trackletWordsRejected, int &trackletWordsReadOK, int &numberOfTrackletsFound)
Tracklet64 assembleTracklet64(int format, TrackletMCMHeader &mcmHeader, TrackletMCMData &mcmData, int cpu, int hcid) const
void incrementErrors(int error, int hcid=-1, std::string message="")
bool checkRDH(const o2::header::RDHAny *rdh)
bool processHalfCRU(int iteration)
int parseDigitLinkData(int maxWords32, int hcid, int &digitWordsRejected)
void printHalfChamberHeaderReport() const
void buildDPLOutputs(o2::framework::ProcessingContext &outputs)
bool isTrackletHCHeaderOK(const TrackletHCHeader &header, int &hcid)
bool parseDigitHCHeaders(int hcid)
bool compareRDH(const o2::header::RDHAny *rdhPrev, const o2::header::RDHAny *rdhCurr)
void configure(int tracklethcheader, int halfchamberwords, int halfchambermajor, std::bitset< 16 > options)
void dumpInputPayload() const
void checkNoWarn(bool silently=true)
void incLinkErrorFlags(int hcid, unsigned int flag)
Definition EventRecord.h:93
void incLinkWordsRead(int hcid, int count)
Definition EventRecord.h:96
void incLinkWordsRejected(int hcid, int count)
Definition EventRecord.h:97
void incMajorVersion(int version)
Definition EventRecord.h:98
void incParsingError(int error, int hcid)
void incLinkWords(int hcid, int count)
Definition EventRecord.h:95
void sendData(o2::framework::ProcessingContext &pc, bool generatestats, bool sortDigits, bool sendLinkStats)
EventRecord & getCurrentEventRecord()
Definition EventRecord.h:90
void setCurrentEventRecord(const InteractionRecord &ir)
void incDigitTime(float timeadd)
Definition EventRecord.h:63
DataCountersPerTrigger getCounters() const
Definition EventRecord.h:53
void incTrackletTime(float timeadd)
Definition EventRecord.h:62
void addTracklet(Tracklet64 tracklet)
Definition EventRecord.h:45
void addDigit(Digit digit)
Definition EventRecord.h:44
void incTime(float duration)
Definition EventRecord.h:64
GLboolean * data
Definition glcorearb.h:298
GLuint GLsizei const GLchar * message
Definition glcorearb.h:2517
GLint GLint GLsizei GLint GLenum format
Definition glcorearb.h:275
constexpr unsigned int CRUPADDING32
padding word used in the cru.
Definition Constants.h:85
constexpr int TRACKLETENDMARKER
marker for the end of tracklets in raw data, 2 of these.
Definition Constants.h:88
constexpr int DIGITENDMARKER
marker for the end of digits in raw data, 2 of these
Definition Constants.h:90
constexpr int NLAYER
the number of layers
Definition Constants.h:27
constexpr unsigned int ETYPECALIBRATIONTRIGGER
CRU Half Chamber header eventtype definition.
Definition Constants.h:98
constexpr int TIMEBINS
the number of time bins
Definition Constants.h:74
constexpr int NLINKSPERHALFCRU
the number of links per half cru or cru end point.
Definition Constants.h:34
constexpr unsigned int ETYPEPHYSICSTRIGGER
CRU Half Chamber header eventtype definition.
Definition Constants.h:97
constexpr int NADCMCM
the number of ADC channels per MCM
Definition Constants.h:52
constexpr int NHCPERSEC
the number of half-chambers per sector
Definition Constants.h:29
constexpr int INVALIDPRETRIGGERPHASE
Invalid value for phase, used to signify there is no hcheader.
Definition Constants.h:100
void printHalfCRUHeader(const o2::trd::HalfCRUHeader &halfcru)
Definition RawData.cxx:230
bool sanityCheckDigitMCMHeader(const o2::trd::DigitMCMHeader &header)
Definition RawData.cxx:298
int getDigitHCHeaderWordType(uint32_t word)
Definition RawData.cxx:349
@ TrackletNoSecondEndMarker
@ TrackletDataWrongOrdering
@ DigitHCHeaderSVNMismatch
@ DigitHeaderWrongType
@ TrackletDataMissing
@ TrackletExitingNoTrackletEndMarker
@ TrackletNoTrackletEndMarker
@ DigitSanityCheck
@ DigitEndMarkerWrongState
@ TrackletDataDuplicateMCM
@ DigitParsingNoSecondEndmarker
@ DigitMCMDuplicate
@ DigitHCHeader1Problem
@ DigitMCMHeaderSanityCheckFailure
@ TrackletsReturnedMinusOne
@ DigitHCHeaderMismatch
@ DigitParsingExitInWrongState
@ TrackletHCHeaderFailure
@ BadRDHPacketCounter
@ DigitADCMaskInvalid
@ BadRDHEndPoint
@ TRDLastParsingError
@ HalfCRUSumLength
@ DigitHCHeader2Problem
@ FEEIDBadSector
@ DigitHCHeader3Problem
@ BadRDHMemSize
@ DigitHeaderCountGT3
@ HalfCRUCorrupt
@ TrackletMCMDataFailure
@ DigitMCMNotIncreasing
@ UnparsedTrackletDataRemaining
@ UnparsedDigitDataRemaining
@ TrackletMCMHeaderSanityCheckFailure
void getHalfCRULinkErrorFlags(const HalfCRUHeader &cruheader, std::array< uint8_t, 15 > &linkerrorflags)
Definition RawData.cxx:131
bool sanityCheckTrackletHCHeader(const o2::trd::TrackletHCHeader &header)
Definition RawData.cxx:269
@ TRDVerboseErrorsBit
@ TRDGenerateStats
@ TRDOnlyCalibrationTriggerBit
@ TRDIgnore2StageTrigger
bool sanityCheckDigitMCMADCMask(const o2::trd::DigitMCMADCMask &mask)
Definition RawData.cxx:310
void getHalfCRULinkDataSizes(const HalfCRUHeader &cruheader, std::array< uint16_t, 15 > &linksizes)
Definition RawData.cxx:139
bool halfCRUHeaderSanityCheck(const o2::trd::HalfCRUHeader &header)
Definition RawData.cxx:249
bool sanityCheckTrackletMCMHeader(const o2::trd::TrackletMCMHeader &header)
Definition RawData.cxx:286
void printDigitHCHeader(o2::trd::DigitHCHeader &header, uint32_t headers[3])
Definition RawData.cxx:393
std::string to_string(gsl::span< T, Size > span)
Definition common.h:52
uint16_t bc
bunch crossing ID of interaction
static void printRDH(const RDHv4 &rdh)
Definition RDHUtils.cxx:26
std::array< uint16_t, constants::MAXHALFCHAMBER > mLinkWords
std::array< uint8_t, constants::MAXHALFCHAMBER > mLinkErrorFlag
Header for half a cru, each cru has 2 output, 1 for each pciid.
Definition RawData.h:35
uint64_t BunchCrossing
Definition RawData.h:89
static std::string getSectorStackLayerSide(int hcid)
static int getDetector(int sector, int stack, int layer)
static int getStack(int det)
static int getLayer(int det)
static int getSector(int det)
int getHCID(int link) const
Definition RawData.h:423
Frontend Electronics ID, is made up of supermodule, a/c side and the end point encoded as below.
Definition RawData.h:207
uint8_t supermodule
Definition RawData.h:223
uint16_t word
Definition RawData.h:217
uint8_t endpoint
Definition RawData.h:219
Header for each half chamber.
Definition RawData.h:129
Header for MCM tracklet data outuput.
Definition RawData.h:162
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"