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 mPreTriggerPhase &= 0x0f;
305 mPreTriggerPhase /= 3; // remove the "gaps" in the pre trigger phase, so we dont have to sort it out later.
306 LOGP(debug, "Found pretrigger phase of Phase:{:x}", mPreTriggerPhase);
307
308 headersfound.set(0);
309 if ((header1.numtimebins > TIMEBINS) || (header1.numtimebins < 3) || mTimeBinsFixed && header1.numtimebins != mTimeBins) {
310 if (mOptions[TRDVerboseErrorsBit]) {
311 LOGF(warn, "According to Digit HC Header 1 there are %i time bins configured", (int)header1.numtimebins);
312 printDigitHCHeader(mDigitHCHeader, headers.data());
313 }
315 return false;
316 }
317 mTimeBins = header1.numtimebins;
318 break;
319
320 case 2: // header header2;
321 if (headersfound.test(1)) {
322 // we have a problem, we already have a Digit HC Header2, we are hereby lost.
323 if (mOptions[TRDVerboseErrorsBit]) {
324 LOGF(warn, "We have more than one DigitHCHeader of type 2. Current word in hex %08x", headers[headerwordcount]);
325 printDigitHCHeader(mDigitHCHeader, headers.data());
326 }
328 return false;
329 }
330 /* Currently we don't do anything with the information stored in DigitHCHeader2
331 DigitHCHeader2 header2;
332 header2.word = headers[headerwordcount];
333 */
334 headersfound.set(1);
335 break;
336
337 case 3: // header header3;
338 if (headersfound.test(2)) {
339 // we have a problem, we already have a Digit HC Header3, we are hereby lost.
340 if (mOptions[TRDVerboseErrorsBit]) {
341 LOG(info) << "We have a >1 Digit HC Header 2 : " << std::hex << " raw: 0x" << headers[headerwordcount];
342 printDigitHCHeader(mDigitHCHeader, headers.data());
343 }
345 return false;
346 }
347 DigitHCHeader3 header3;
348 header3.word = headers[headerwordcount];
349 headersfound.set(2);
350 if (mHaveSeenDigitHCHeader3) {
351 if (header3.svnver != mPreviousDigitHCHeadersvnver || header3.svnrver != mPreviousDigitHCHeadersvnrver) {
352 if (mOptions[TRDVerboseErrorsBit]) {
353 LOG(warning) << "Conflicting SVN in DigitHCHeader3";
354 printDigitHCHeader(mDigitHCHeader, headers.data());
355 }
357 return false;
358 }
359 } else {
360 mPreviousDigitHCHeadersvnver = header3.svnver;
361 mPreviousDigitHCHeadersvnrver = header3.svnrver;
362 mHaveSeenDigitHCHeader3 = true;
363 }
364 break;
365
366 default:
367 incrementErrors(DigitHeaderWrongType, hcid, fmt::format("Failed to determine DigitHCHeader type for {:#010x}", headers[headerwordcount]));
368 return false;
369 }
370 }
371 if (mOptions[TRDVerboseBit]) {
372 printDigitHCHeader(mDigitHCHeader, &headers[0]);
373 }
374
375 return true;
376}
377
379{
380 // process data from one half-CRU
381 // iteration corresponds to the trigger number within the HBF
382
383 // this should only hit that instance where the cru payload is a "blank event" of CRUPADDING32
384 if (mHBFPayload[mHBFoffset32] == CRUPADDING32) {
385 if (mOptions[TRDVerboseBit]) {
386 LOG(info) << "blank rdh payload data at " << mHBFoffset32 << ": 0x" << std::hex << mHBFPayload[mHBFoffset32] << " and 0x" << mHBFPayload[mHBFoffset32 + 1];
387 }
388 int loopcount = 0;
389 while (mHBFPayload[mHBFoffset32] == CRUPADDING32 && loopcount < 8) { // can only ever be an entire 256 bit word hence a limit of 8 here.
390 // TODO: check with Guido if it could not actually be more padding words
391 mHBFoffset32++;
392 loopcount++;
393 }
394 return true;
395 }
396
397 auto crustart = std::chrono::high_resolution_clock::now();
398
399 memcpy(&mCurrentHalfCRUHeader, &(mHBFPayload[mHBFoffset32]), sizeof(HalfCRUHeader));
400 mHBFoffset32 += sizeof(HalfCRUHeader) / 4; // advance past the header.
401 if (mOptions[TRDVerboseBit]) {
402 //output the cru half chamber header : raw/parsed
403 printHalfCRUHeader(mCurrentHalfCRUHeader);
404 }
405 if (!halfCRUHeaderSanityCheck(mCurrentHalfCRUHeader)) {
406 incrementErrors(HalfCRUCorrupt, -1, fmt::format("HalfCRU header failed sanity check for FEEID with {:#x} ", (unsigned int)mFEEID.word));
407 mWordsRejected += (mTotalHBFPayLoad / 4) - mHBFoffset32 + sizeof(HalfCRUHeader) / 4;
408 return false; // not recoverable, since we don't know when the next half-cru header would start
409 }
410
411 o2::trd::getHalfCRULinkDataSizes(mCurrentHalfCRUHeader, mCurrentHalfCRULinkLengths);
412 o2::trd::getHalfCRULinkErrorFlags(mCurrentHalfCRUHeader, mCurrentHalfCRULinkErrorFlags);
413 uint32_t totalHalfCRUDataLength256 = std::accumulate(mCurrentHalfCRULinkLengths.begin(),
414 mCurrentHalfCRULinkLengths.end(),
415 0U);
416 uint32_t totalHalfCRUDataLength32 = totalHalfCRUDataLength256 * 8; // convert to 32-bit words
417
418 if (mOptions[TRDOnlyCalibrationTriggerBit] && mCurrentHalfCRUHeader.EventType == o2::trd::constants::ETYPEPHYSICSTRIGGER) {
419 // skip triggers without digits
420 mWordsRejected += totalHalfCRUDataLength32;
421 mHBFoffset32 += totalHalfCRUDataLength32;
422 return true;
423 }
424 if (mCRUEndpoint != mCurrentHalfCRUHeader.EndPoint) {
425 if (mMaxWarnPrinted > 0) {
426 LOGF(warn, "End point mismatch detected. HalfCRUHeader says %i, RDH says %i", mCurrentHalfCRUHeader.EndPoint, mCRUEndpoint);
427 checkNoWarn();
428 }
429 // try next trigger (HalfCRUHeader)
430 mHBFoffset32 += totalHalfCRUDataLength32;
431 mWordsRejected += totalHalfCRUDataLength32;
432 return true;
433 }
434
435 if (mPreviousHalfCRUHeaderSet) {
436 // for the second trigger (and thus second HalfCRUHeader we see) we can do some more sanity checks
437 // in case one check fails, we try to go to the next HalfCRUHeader
438 if (mCurrentHalfCRUHeader.EndPoint != mPreviousHalfCRUHeader.EndPoint) {
439 if (mMaxWarnPrinted > 0) {
440 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);
441 checkNoWarn();
442 }
444 mWordsRejected += totalHalfCRUDataLength32;
445 mHBFoffset32 += totalHalfCRUDataLength32;
446 return true;
447 }
448 if (mCurrentHalfCRUHeader.StopBit != mPreviousHalfCRUHeader.StopBit) {
449 if (mMaxWarnPrinted > 0) {
450 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);
451 checkNoWarn();
452 }
454 mWordsRejected += totalHalfCRUDataLength32;
455 mHBFoffset32 += totalHalfCRUDataLength32;
456 return true;
457 }
458 }
459 mPreviousHalfCRUHeader = mCurrentHalfCRUHeader;
460 mPreviousHalfCRUHeaderSet = true;
461
462 //can this half cru length fit into the available space of the rdh accumulated payload
463 if (totalHalfCRUDataLength32 > (mTotalHBFPayLoad / 4) - mHBFoffset32) {
464 if (mMaxWarnPrinted > 0) {
465 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));
466 checkNoWarn();
467 }
469 mWordsRejected += (mTotalHBFPayLoad / 4) - mHBFoffset32;
470 return false; // not recoverable, since we don't know when the next half-cru header would start
471 }
472
473 mIR.bc = mCurrentHalfCRUHeader.BunchCrossing; // TRD BC is obtained from the HalfCRUHeader
474 if (o2::ctp::TriggerOffsetsParam::Instance().LM_L0 > (int)mIR.bc) {
475 // applying the configured BC shift would lead to negative BC, hence we reject this trigger
476 // dump to the end of this cruhalfchamberheader
477 // data to dump is totalHalfCRUDataLength32
478 mHBFoffset32 += totalHalfCRUDataLength32; // go to the end of this halfcruheader and payload.
479 mWordsRejected += totalHalfCRUDataLength32; // add the rejected data to the accounting;
480 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));
481 return true; // nothing particularly wrong with the data, we just dont want it, as a trigger problem
482 } else {
483 // apply CTP offset shift
485 }
486 mEventRecords.setCurrentEventRecord(mIR);
487 if (mCurrentHalfCRUHeader.EventType == ETYPECALIBRATIONTRIGGER) {
489 }
490
491 //loop over links
492 uint32_t linksizeAccum32 = 0; // accumulated size of all links in 32-bit words
493 auto hbfOffsetTmp = mHBFoffset32; // store current position at the beginning of the half-CRU payload data
494 for (int currentlinkindex = 0; currentlinkindex < NLINKSPERHALFCRU; currentlinkindex++) {
495 bool linkOK = true; // flag links which could be processed successfully, without any rejected word
496 int cruIdx = mFEEID.supermodule * 2 + mFEEID.side; // 2 CRUs per SM, side defining A/C-side CRU
497 int halfCruIdx = cruIdx * 2 + mFEEID.endpoint; // endpoint (0 or 1) defines half-CRU
498 int linkIdxGlobal = halfCruIdx * NLINKSPERHALFCRU + currentlinkindex; // global link ID [0..1079]
499 int halfChamberId = mLinkMap->getHCID(linkIdxGlobal);
500 mEventRecords.getCurrentEventRecord().getCounters().mLinkWords[halfChamberId] += mCurrentHalfCRULinkLengths[currentlinkindex];
501 mEventRecords.getCurrentEventRecord().getCounters().mLinkErrorFlag[halfChamberId] |= mCurrentHalfCRULinkErrorFlags[currentlinkindex];
502 mEventRecords.incLinkErrorFlags(halfChamberId, mCurrentHalfCRULinkErrorFlags[currentlinkindex]); // TODO maybe has more meaning on a per event basis?
503 mEventRecords.incLinkWords(halfChamberId, mCurrentHalfCRULinkLengths[currentlinkindex]);
504 uint32_t currentlinksize32 = mCurrentHalfCRULinkLengths[currentlinkindex] * 8; // x8 to go from 256 bits to 32 bit;
505 uint32_t endOfCurrentLink = mHBFoffset32 + currentlinksize32;
506 if (mOptions[TRDVerboseBit] && currentlinksize32 > 0) {
507 LOGP(info, "Reading {} (link ID {}, HCID {}) with {} 32-bit words", HelperMethods::getSectorStackLayerSide(halfChamberId), linkIdxGlobal, halfChamberId, currentlinksize32);
508 }
509 linksizeAccum32 += currentlinksize32;
510 if (currentlinksize32 == 0) {
511 mEventRecords.incLinkNoData(halfChamberId);
512 }
513 if (mOptions[TRDVerboseBit]) {
514 if (currentlinksize32 > 0) {
515 LOGF(info, "Half-CRU link %i raw dump before parsing starts:", currentlinkindex);
516 for (uint32_t dumpoffset = mHBFoffset32; dumpoffset < mHBFoffset32 + currentlinksize32; dumpoffset += 8) {
517 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]);
518 }
519 } else {
520 LOGF(info, "Half-CRU link %i has zero link size", currentlinkindex);
521 }
522 }
523 if (currentlinksize32 > 0) { // if link is not empty
524 auto trackletparsingstart = std::chrono::high_resolution_clock::now();
525 if (mOptions[TRDVerboseBit]) {
526 LOGF(info, "Tracklet parser starting at offset %u and processing up to %u words", mHBFoffset32, currentlinksize32);
527 }
528 int trackletWordsRejected = 0;
529 int trackletWordsReadOK = 0;
530 int numberOfTrackletsFound = 0; // count the number of found tracklets for this link
531 int trackletWordsRead = parseTrackletLinkData(currentlinksize32, halfChamberId, trackletWordsRejected, trackletWordsReadOK, numberOfTrackletsFound);
532 std::chrono::duration<float, std::micro> trackletparsingtime = std::chrono::high_resolution_clock::now() - trackletparsingstart;
533 if (mOptions[TRDVerboseBit]) {
534 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));
535 }
536 if (trackletWordsRead == -1) {
537 // something went wrong bailout of here.
538 mHBFoffset32 = hbfOffsetTmp + linksizeAccum32;
540 continue; // move to next link of this half-CRU
541 }
542 if (trackletWordsRejected > 0) {
543 linkOK = false;
544 }
545 mHBFoffset32 += trackletWordsRead;
546 if (mCurrentHalfCRUHeader.EventType == ETYPEPHYSICSTRIGGER &&
547 endOfCurrentLink - mHBFoffset32 >= 8) {
548 if (mMaxWarnPrinted > 0) {
549 LOGF(warn, "After successfully parsing the tracklet data for link %i there are %u words remaining which did not get parsed", currentlinkindex, endOfCurrentLink - mHBFoffset32);
550 checkNoWarn();
551 }
552 incrementErrors(UnparsedTrackletDataRemaining, halfChamberId, fmt::format("On link {} there are {} words remaining which did not get parsed", currentlinkindex, endOfCurrentLink - mHBFoffset32));
553 linkOK = false;
554 }
555 mEventRecords.getCurrentEventRecord().incTrackletTime(trackletparsingtime.count());
556 if (mOptions[TRDVerboseBit]) {
557 LOGF(debug, "Read %i tracklet words and rejected %i words", trackletWordsRead, trackletWordsRejected);
558 }
559 mTrackletWordsRejected += trackletWordsRejected;
560 mTrackletWordsRead += trackletWordsRead;
561 mEventRecords.incLinkWordsRead(halfChamberId, trackletWordsRead);
562 mEventRecords.incLinkWordsRejected(halfChamberId, trackletWordsRejected);
563
564 /****************
565 ** DIGITS NOW ***
566 *****************/
567 // 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.
568 if (mHBFoffset32 != endOfCurrentLink &&
569 (mCurrentHalfCRUHeader.EventType == ETYPECALIBRATIONTRIGGER || mOptions[TRDIgnore2StageTrigger]) &&
570 (mHBFPayload[mHBFoffset32] != CRUPADDING32)) {
571 // 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
572
573 uint32_t offsetBeforeDigitParsing = mHBFoffset32;
574 // the digit HC headers come first
575 if (!parseDigitHCHeaders(halfChamberId)) {
576 mHBFoffset32 = hbfOffsetTmp + linksizeAccum32;
577 continue; // move to next link of this half-CRU
578 }
579 if (mHBFoffset32 - offsetBeforeDigitParsing != 1U + mDigitHCHeader.numberHCW) {
580 if (mMaxErrsPrinted > 0) {
581 LOGF(error, "Did not read as many digit headers (%i) as expected (%i)",
582 mHBFoffset32 - offsetBeforeDigitParsing, mDigitHCHeader.numberHCW + 1);
583 checkNoErr();
584 }
585 mHBFoffset32 = hbfOffsetTmp + linksizeAccum32;
586 continue; // move to next link of this half-CRU
587 }
588
589 mEventRecords.incMajorVersion(mDigitHCHeader.major); // 127 is max histogram goes to 256
590
591 if (mDigitHCHeader.major == 0x47) {
592 // config event so ignore for now and bail out of parsing.
593 //advance data pointers to the end;
594 mHBFoffset32 = hbfOffsetTmp + currentlinksize32;
595 mDigitWordsRejected += hbfOffsetTmp + currentlinksize32; // count full link as rejected
596 LOG(info) << "Configuration event ";
597 } else {
598 auto digitsparsingstart = std::chrono::high_resolution_clock::now();
599 int digitWordsRejected = 0;
600 int digitWordsRead = parseDigitLinkData(endOfCurrentLink - mHBFoffset32, halfChamberId, digitWordsRejected);
601 std::chrono::duration<float, std::micro> digitsparsingtime = std::chrono::high_resolution_clock::now() - digitsparsingstart;
602 if (digitWordsRead == -1) {
603 // something went wrong bailout of here.
604 mHBFoffset32 = hbfOffsetTmp + linksizeAccum32;
605 continue; // move to next link of this half-CRU
606 }
607 mHBFoffset32 += digitWordsRead;
608 if (endOfCurrentLink - mHBFoffset32 >= 8) {
609 // check if some data is lost (probably due to bug in CRU user logic)
610 // we should have max 7 padding words to align the link to 256 bits
611 if (mMaxWarnPrinted > 0) {
612 LOGF(warn, "After successfully parsing the digit data for link %i there are %u words remaining which did not get parsed", currentlinkindex, endOfCurrentLink - mHBFoffset32);
613 checkNoWarn();
614 }
615 incrementErrors(UnparsedDigitDataRemaining, halfChamberId, fmt::format("On link {} there are {} words remaining which did not get parsed", currentlinkindex, endOfCurrentLink - mHBFoffset32));
616 linkOK = false;
617 }
618 if (digitWordsRejected > 0) {
619 linkOK = false;
620 }
621 mEventRecords.getCurrentEventRecord().incDigitTime(digitsparsingtime.count());
622 mEventRecords.incLinkWordsRead(halfChamberId, digitWordsRead);
623 mEventRecords.incLinkWordsRejected(halfChamberId, digitWordsRejected);
624
625 if (mOptions[TRDVerboseBit]) {
626 LOGF(info, "Read %i digit words and rejected %i words", digitWordsRead, digitWordsRejected);
627 }
628 mDigitWordsRead += digitWordsRead;
629 mDigitWordsRejected += digitWordsRejected;
630 }
631 }
632 if (linkOK) {
633 incrementErrors(NoError, halfChamberId);
634 }
635 } else {
636 // OS: link is empty, what does this block mean???
637 if (mCurrentHalfCRUHeader.EventType == ETYPEPHYSICSTRIGGER) {
638 mEventRecords.incMajorVersion(128); // 127 is max histogram goes to 256
639 }
640 }
641 if (mHBFoffset32 != hbfOffsetTmp + linksizeAccum32) {
642 // 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
643 LOGF(debug, "After processing link %i the payload offset of %u is not the expected %u + %u", currentlinkindex, mHBFoffset32, hbfOffsetTmp, linksizeAccum32);
644 mHBFoffset32 = hbfOffsetTmp + linksizeAccum32;
645 }
646 } // for loop over link index.
647 if (mHBFoffset32 != hbfOffsetTmp + totalHalfCRUDataLength32) {
648 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);
649 mHBFoffset32 = hbfOffsetTmp + totalHalfCRUDataLength32;
650 }
651 // we have read in all the digits and tracklets for this event.
652 //digits and tracklets are sitting inside the parsing classes.
653 //extract the vectors and copy them to tracklets and digits here, building the indexing(triggerrecords)
654 //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
655
656 std::chrono::duration<float, std::micro> cruparsingtime = std::chrono::high_resolution_clock::now() - crustart;
657 mEventRecords.getCurrentEventRecord().incTime(cruparsingtime.count());
658
659 //if we get here all is ok.
660 return true;
661}
662
664{
665 if (!sanityCheckTrackletHCHeader(header)) {
666 return false;
667 }
668 int detHeader = HelperMethods::getDetector(((~header.supermodule) & 0x1f), ((~header.stack) & 0x7), ((~header.layer) & 0x7));
669 int hcidHeader = (detHeader * 2 + ((~header.side) & 0x1));
670 if (hcid != hcidHeader) {
671 mHalfChamberMismatches.insert(std::make_pair(hcid, hcidHeader));
672 return false;
673 } else {
674 mHalfChamberHeaderOK.insert(hcid);
675 return true;
676 }
677}
678
679int CruRawReader::parseDigitLinkData(int maxWords32, int hcid, int& wordsRejected)
680{
681 int wordsRead = 0;
683 DigitMCMHeader mcmHeader;
684 DigitMCMADCMask adcMask;
685
686 // data is expected to be ordered
687 int previousMcm = -1;
688 int previousRob = -1;
689 // TODO add check for event counter of DigitMCMHeader?
690 // are the counters expected to be the same for all MCMs for one trigger?
691
692 while (wordsRead < maxWords32 && state != StateFinished) {
693 uint32_t currWord = mHBFPayload[mHBFoffset32 + wordsRead];
694
695 if (state == StateDigitMCMHeader) {
696 ++wordsRead;
697 if (currWord == DIGITENDMARKER) {
699 continue;
700 }
701 mcmHeader.word = currWord;
702 if (!sanityCheckDigitMCMHeader(mcmHeader)) {
703 incrementErrors(DigitMCMHeaderSanityCheckFailure, hcid, fmt::format("DigitMCMHeader {:#010x} failed sanity check", currWord));
704 ++wordsRejected;
705 state = StateMoveToEndMarker; // give up for this link and try to find end markers
706 continue;
707 }
708 // check correct ordering of link data
709 if (previousMcm >= 0) {
710 // first DigitMCMHeader we see for this link
711 if (previousRob > mcmHeader.rob || (mcmHeader.rob == previousRob && mcmHeader.mcm < previousMcm)) {
712 incrementErrors(DigitMCMNotIncreasing, hcid, fmt::format("Current rob/mcm = {}/{}, previous rob/mcm = {}/{}", (int)mcmHeader.rob, (int)mcmHeader.mcm, previousRob, previousMcm));
713 } else if (previousRob == mcmHeader.rob && previousMcm == mcmHeader.mcm) {
714 incrementErrors(DigitMCMDuplicate, hcid, fmt::format("Second MCM header {:#010x} for rob/mcm = {}/{}", currWord, previousRob, previousMcm));
715 }
716 } else {
717 previousMcm = mcmHeader.mcm;
718 previousRob = mcmHeader.rob;
719 }
720 if (mDigitHCHeader.major & 0x20) {
721 // zero suppression (ZS) is ON, we expect ADC mask next
723 } else {
724 // full readout, expect ADC data next
726 }
727 LOGF(debug, "Got DigitMCMHeader 0x%08x", currWord);
728 continue;
729 }
730
731 else if (state == StateDigitADCMask) {
732 ++wordsRead;
733 adcMask.word = currWord;
734 if (!sanityCheckDigitMCMADCMask(adcMask)) {
735 incrementErrors(DigitADCMaskInvalid, hcid, fmt::format("DigitADCMask {:#010x} failed sanity check", currWord));
736 ++wordsRejected;
737 state = StateMoveToEndMarker; // give up for this link and try to find end markers
738 continue;
739 }
741 LOGF(debug, "Got DigitADCMask 0x%08x", currWord);
742 continue;
743 }
744
745 else if (state == StateMoveToDigitMCMHeader) {
746 // the parsing of ADC values went wrong, we search for the next MCM header
747 bool foundHeader = false;
748 while (wordsRead < maxWords32 && !foundHeader) {
749 if (currWord == DIGITENDMARKER) {
750 ++wordsRead;
752 break;
753 }
754 DigitMCMHeader tmpHeader;
755 tmpHeader.word = currWord;
756 if (sanityCheckDigitMCMHeader(tmpHeader)) {
757 foundHeader = true;
759 break;
760 }
761 ++wordsRead;
762 ++wordsRejected;
763 currWord = mHBFPayload[mHBFoffset32 + wordsRead];
764 }
766 // we could neither find a MCM header, nor an endmarker
767 break;
768 }
769 continue;
770 }
771
772 else if (state == StateDigitMCMData) {
773 bool exitChannelLoop = false;
774 state = StateDigitMCMHeader; // after we are done reading the ADC data, by default we expect another MCM header
775 for (int iChannel = 0; iChannel < NADCMCM; ++iChannel) {
776 std::array<uint16_t, TIMEBINS> adcValues{};
777 if (!(mDigitHCHeader.major & 0x20) || adcMask.adcmask & (1UL << iChannel)) {
778 // either ZS is OFF, or the adcMask has iChannel flagged as active
780 int timebin = 0;
781 while (timebin < mTimeBins) {
782 if (currWord == DIGITENDMARKER) {
783 incrementErrors(DigitEndMarkerWrongState, hcid, "Expected Digit ADC data, but found end marker instead");
784 exitChannelLoop = true;
786 ++wordsRead;
787 wordsRejected += timebin / 3; // we are rejecting all ADC data we have already read for this channel
788 break;
789 }
790 data.word = currWord;
791 if ((((iChannel % 2) == 0) && (data.f != 0x3)) || ((iChannel % 2) && (data.f != 0x2))) {
792 incrementErrors(DigitSanityCheck, hcid, fmt::format("Current channel {}, check bits {}. Word {:#010x}", iChannel, (int)data.f, currWord));
793 exitChannelLoop = true;
795 ++wordsRead;
796 ++wordsRejected;
797 break;
798 }
799 adcValues[timebin++] = data.z;
800 adcValues[timebin++] = data.y;
801 adcValues[timebin++] = data.x;
802 ++wordsRead;
803 currWord = mHBFPayload[mHBFoffset32 + wordsRead];
804 } // end time bin loop
805 if (exitChannelLoop) {
806 break;
807 }
808 LOGP(debug, "Adding digit to event record det: {} rob: {} mcm: {} channel: {} Phase:{:x}", hcid / 2, (int)mcmHeader.rob, (int)mcmHeader.mcm, iChannel, mPreTriggerPhase);
809 mEventRecords.getCurrentEventRecord().addDigit(Digit(hcid / 2, (int)mcmHeader.rob, (int)mcmHeader.mcm, iChannel, adcValues, mPreTriggerPhase));
810 ++mDigitsFound;
811 } // end active channel
812 } // end channel loop
813 continue;
814 }
815
816 else if (state == StateMoveToEndMarker) {
817 ++wordsRead;
818 if (currWord == DIGITENDMARKER) {
820 } else {
821 ++wordsRejected;
822 }
823 continue;
824 } // StateMoveToEndMarker
825
826 else if (state == StateSecondEndmarker) {
827 ++wordsRead;
828 if (currWord != DIGITENDMARKER) {
829 incrementErrors(DigitParsingNoSecondEndmarker, hcid, fmt::format("Expected second digit end marker, but found {:#010x} instead", currWord));
830 return -1;
831 }
833 continue;
834 } // StateSecondEndmarker
835 }
836
837 if (state == StateFinished) {
838 // all good, we exited the state machine in the expected state
839 return wordsRead;
840 } else {
841 // not good, something went wrong with digit parsing
842 // e.g. we tried to move to the end marker but reached the link size
843 // without finding one.
844 incrementErrors(DigitParsingExitInWrongState, hcid, fmt::format("Done with digit parsing but state is not StateFinished but in state {}", state));
845 return -1;
846 }
847}
848
849// Returns number of words read (>=0) or error state (<0)
850int CruRawReader::parseTrackletLinkData(int linkSize32, int& hcid, int& wordsRejected, int& wordsReadOK, int& trackletsFound)
851{
852 int wordsRead = 0; // count the number of words which were parsed (both successful and not successful)
853 int state = StateTrackletHCHeader; // we expect to always see a TrackletHCHeader at the beginning of the link
854 // tracklet data for one link is expected to arrive ordered
855 // first MCM in row=0, column=0, then row=0, column=1, ... row=1, column=0, ...
856 int previousColumn = -1;
857 int previousRow = -1;
858 TrackletHCHeader hcHeader;
859 TrackletMCMHeader mcmHeader;
860
861 // main loop we exit only when we reached the end of the link or have seen two tracklet end markers
862 while (wordsRead < linkSize32 && state != StateFinished) {
863 uint32_t currWord = mHBFPayload[mHBFoffset32 + wordsRead];
864
866 ++wordsRead;
867 if (mTrackletHCHeaderState == 0) {
868 LOG(error) << "It's not allowed anymore to have the TrackletHCHeader never present";
869 return -1;
870 } else if (mTrackletHCHeaderState == 1) {
871 // in case tracklet data is present we have a TrackletHCHeader, otherwise we should
872 // see a DigitHCHeader
873 hcHeader.word = currWord;
874 if (!isTrackletHCHeaderOK(hcHeader, hcid)) {
875 // either the TrackletHCHeader is corrupt or we have only digits on this link
876 return 0;
877 }
878 ++wordsReadOK;
879 state = StateTrackletMCMHeader; // we do expect tracklets following
880 } else {
881 // we always expect a TrackletHCHeader as first word for a link (default)
882 hcHeader.word = currWord;
883 if (!isTrackletHCHeaderOK(hcHeader, hcid)) {
884 // we have a corrupt TrackletHCHeader
885 incrementErrors(TrackletHCHeaderFailure, hcid, fmt::format("Invalid word {:#010x} for the expected TrackletHCHeader", currWord));
887 ++wordsRejected;
888 continue;
889 }
890 ++wordsReadOK;
891 state = StateTrackletMCMHeader; // we might have tracklets following or tracklet end markers
892 }
893 } // StateTrackletHCHeader
894
895 else if (state == StateTrackletMCMHeader) {
896 if (currWord == TRACKLETENDMARKER) {
897 ++wordsReadOK;
898 state = StateSecondEndmarker; // we expect a second tracklet end marker to follow
899 } else {
900 mcmHeader.word = currWord;
901 if (!sanityCheckTrackletMCMHeader(mcmHeader)) {
902 incrementErrors(TrackletMCMHeaderSanityCheckFailure, hcid, fmt::format("Invalid word {:#010x} for the expected TrackletMCMHeader", currWord));
903 // invalid MCM header, but we can try to find another valid MCM header before the end markers.
904 // If there are no end markers we can anyhow not parse digits if they were there.
905 // Other invalid MCM headers can be caught by the check on their ordering (row/column)
906 // state = StateMoveToEndMarker;
907 ++wordsRead;
908 ++wordsRejected;
909 continue;
910 }
911 // check ordering by MCM index
912 if (previousColumn >= 0) {
913 if (mcmHeader.padrow < previousRow || (mcmHeader.padrow == previousRow && mcmHeader.col < previousColumn)) {
914 incrementErrors(TrackletDataWrongOrdering, hcid, fmt::format("Current padrow/column = {}/{}, previous padrow/column = {}/{}", (int)mcmHeader.padrow, (int)mcmHeader.col, previousRow, previousColumn));
915 ++wordsRead;
916 ++wordsRejected;
917 continue;
918 } else if (mcmHeader.padrow == previousRow && mcmHeader.col == previousColumn) {
919 incrementErrors(TrackletDataDuplicateMCM, hcid, fmt::format("Second MCM header {:#010x} for padrow/column = {}/{}", currWord, previousRow, previousColumn));
920 ++wordsRead;
921 ++wordsRejected;
922 continue;
923 }
924 } else {
925 previousColumn = mcmHeader.col;
926 previousRow = mcmHeader.padrow;
927 }
928 ++wordsReadOK;
929 state = StateTrackletMCMData; // tracklet words must be following, unless the HC header format indicates sending of empty MCM headers
930 }
931 ++wordsRead;
932 } // StateTrackletMCMHeader
933
934 else if (state == StateTrackletMCMData) {
935 bool addedTracklet = false; // flag whether we actually found a tracklet
936 for (int iCpu = 0; iCpu < 3; ++iCpu) {
937 if (((mcmHeader.word >> (1 + iCpu * 8)) & 0xff) == 0xff) {
938 // iCpu did not produce a tracklet.
939 // Since no empty tracklet words are sent, we don't need to move to the next word.
940 // Instead, we check if the next CPU is supposed to have sent a tracklet
941 continue;
942 }
943 if (currWord == TRACKLETENDMARKER) {
944 if ((hcHeader.format & 0x2) == 0x2) {
945 // format indicates no empty MCM headers are sent, so we should not see an end marker here
946 incrementErrors(TrackletDataMissing, hcid, fmt::format("For the MCM Header {:#010x} we expected a tracklet from CPU {}, but got an endmarker instead", mcmHeader.word, iCpu));
947 }
948 state = StateSecondEndmarker; // we expect a second tracklet end marker to follow
949 break;
950 }
951 if ((currWord & 0x1) == 0x1) {
952 // the reserved bit of the tracklet MCM data is set, we don't create a tracklet from this word
953 incrementErrors(TrackletMCMDataFailure, hcid, fmt::format("Invalid word {:#010x} for the expected TrackletMCMData", currWord));
954 ++wordsRejected;
955 ++wordsRead;
956 continue;
957 }
958 TrackletMCMData mcmData;
959 mcmData.word = currWord;
960 mEventRecords.getCurrentEventRecord().addTracklet(assembleTracklet64(hcHeader.format, mcmHeader, mcmData, iCpu, hcid));
961 ++trackletsFound;
962 ++mTrackletsFound;
963 addedTracklet = true;
964 ++wordsRead;
965 ++wordsReadOK;
966 if (wordsRead == linkSize32) {
967 incrementErrors(TrackletNoTrackletEndMarker, hcid, fmt::format("After reading the word {:#010x} we are at the end of the link data", currWord));
968 return -1;
969 }
970 currWord = mHBFPayload[mHBFoffset32 + wordsRead];
971 }
973 ++wordsRead;
974 continue;
975 }
976 if (!addedTracklet) {
977 // we did not add a tracklet -> do we expect empty MCM headers?
978 if ((hcHeader.format & 0x2) != 0x2) {
979 // yes, this is OK and the next word should be MCM header or end marker
980 ++wordsRead;
982 continue;
983 } else {
984 // no tracklet was found, but we should have found one
985 ++wordsRead;
986 ++wordsRejected;
988 continue;
989 }
990 }
992 continue;
993 } // StateTrackletMCMData
994
995 else if (state == StateSecondEndmarker) {
996 ++wordsRead;
997 if (currWord != TRACKLETENDMARKER) {
998 incrementErrors(TrackletNoSecondEndMarker, hcid, fmt::format("Invalid word {:#010x} for the expected second end marker", currWord));
999 return -1;
1000 }
1001 ++wordsReadOK;
1003 } // StateSecondEndmarker
1004
1005 else if (state == StateMoveToEndMarker) {
1006 ++wordsRead;
1007 if (currWord == TRACKLETENDMARKER) {
1009 } else {
1010 ++wordsRejected;
1011 }
1012 continue;
1013 } // StateMoveToEndMarker
1014
1015 else {
1016 // should never happen
1017 LOG(error) << "Tracklet parser ended up in unknown state";
1018 }
1019
1020 } // end of state machine
1021
1022 if (mTrackletHCHeaderState == 1 && trackletsFound == 0) {
1023 if (mMaxErrsPrinted > 0) {
1024 LOG(error) << "We found a TrackletHCHeader in mode 1, but did not add any tracklets";
1025 checkNoErr();
1026 }
1027 }
1028
1029 if (state == StateFinished) {
1030 // all good, we exited the state machine in the expected state
1031 return wordsRead;
1032 } else {
1033 // not good, something went wrong with tracklet parsing
1034 // e.g. we tried to move to the end marker but reached the link size
1035 // without finding one.
1036 incrementErrors(TrackletExitingNoTrackletEndMarker, hcid, "Exiting tracklet parsing not in the state finished");
1037 return -1;
1038 }
1039}
1040
1042{
1043 // TODO take care of special tracklet formats?
1044 uint16_t pos = mcmData.pos;
1045 uint16_t slope = mcmData.slope;
1046 // The 8-th bit of position and slope are always flipped in the FEE.
1047 // We flip them back while reading the raw data so that they are stored
1048 // without flipped bits in the CTFs
1049 pos ^= 0x80;
1050 slope ^= 0x80;
1051 uint32_t hpid = (mcmHeader.word >> (1 + cpu * 8)) & 0xff;
1052 uint32_t lpid = mcmData.pid;
1053 // The combined 20 bit PID information from the MCM header and the tracklet word
1054 // is given by ((hpid << 12) | lpid).
1055 // hpid holds the upper 8 bit and lpid the lower 12 bit.
1056 int q0, q1, q2;
1057 if (format & 0x1) {
1058 // DQR enabled
1059 int scaleFactor = (hpid >> 6) & 0x3;
1060 q0 = (scaleFactor << 6) | (lpid & 0x3f);
1061 q1 = (scaleFactor << 6) | ((lpid >> 6) & 0x3f);
1062 q2 = (scaleFactor << 6) | (hpid & 0x3f);
1063 } else {
1064 // DQR disabled
1065 q0 = lpid & 0x7f;
1066 q1 = ((hpid & 0x3) << 5) | ((lpid >> 7) & 0x1f);
1067 q2 = (hpid >> 2) & 0x3f;
1068 }
1069 return Tracklet64(format, hcid, mcmHeader.padrow, mcmHeader.col, pos, slope, q0, q1, q2);
1070}
1071
1073{
1074 // we print 8 32-bit words per line
1075 LOG(info) << "Dumping full input payload ----->";
1076 for (int iWord = 0; iWord < (mDataBufferSize / 4); iWord += 8) {
1077 LOGF(info, "Word %4i/%4i: 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x",
1078 iWord, mDataBufferSize / 4,
1079 *((uint32_t*)mDataBufferPtr + iWord), *((uint32_t*)mDataBufferPtr + iWord + 1), *((uint32_t*)mDataBufferPtr + iWord + 2), *((uint32_t*)mDataBufferPtr + iWord + 3),
1080 *((uint32_t*)mDataBufferPtr + iWord + 4), *((uint32_t*)mDataBufferPtr + iWord + 5), *((uint32_t*)mDataBufferPtr + iWord + 6), *((uint32_t*)mDataBufferPtr + iWord + 7));
1081 }
1082 LOG(info) << "<------ Done dumping full input payload";
1083}
1084
1086{
1087 if (!mLinkMap) {
1088 LOG(error) << "No mapping for Link ID to HCID provided from CCDB";
1089 return;
1090 }
1091 if (mOptions[TRDVerboseBit]) {
1093 }
1094
1095 mCurrRdhPtr = mDataBufferPtr; // set the pointer to the current RDH to the beginning of the payload
1096 while ((mCurrRdhPtr - mDataBufferPtr) < mDataBufferSize) {
1097
1098 int dataRead = processHBFs();
1099
1100 if (dataRead < 0) {
1101 if (mMaxWarnPrinted > 0) {
1102 LOG(warn) << "Received invalid RDH, rejecting given HBF entirely";
1103 checkNoWarn();
1104 }
1105 break;
1106 } else {
1107 if (mOptions[TRDVerboseBit]) {
1108 LOGP(info, "Done processing HBFs. Total input size was {} bytes (including all headers and padding words, excluding 64 bytes for stop RDH)", dataRead);
1109 }
1110 }
1111 }
1112};
1113
1115{
1116 LOG(info) << "Listing the half-chambers from which we have seen correct TrackletHCHeaders:";
1117 int prevSec = -1;
1118 int currSec = -1;
1119 std::string message;
1120 for (auto hcid : mHalfChamberHeaderOK) {
1121 int currDet = hcid / 2;
1122 currSec = HelperMethods::getSector(currDet);
1123 std::string side = (hcid % 2 == 0) ? "A" : "B";
1124 if (currSec != prevSec) {
1125 if (!message.empty()) {
1126 LOG(info) << message;
1127 message.clear();
1128 }
1129 prevSec = currSec;
1130 }
1131 message += fmt::format("{:#02}_{}_{}{} ", currSec, HelperMethods::getStack(currDet), HelperMethods::getLayer(currDet), side.c_str());
1132 }
1133 if (!message.empty()) {
1134 LOG(info) << message;
1135 }
1136
1137 if (!mHalfChamberMismatches.empty()) {
1138 LOG(warn) << "Found HCID mismatch(es). Printing one by one.";
1139 }
1140 for (const auto& elem : mHalfChamberMismatches) {
1141 LOGF(info, "HCID deduced from RDH (link ID): %i, HCID from TrackletHCHeader: %i", elem.first, elem.second);
1142 }
1143}
1144
1145//write the output data directly to the given DataAllocator from the datareader task.
1147{
1148 mEventRecords.sendData(pc, mOptions[TRDGenerateStats], mOptions[TRDSortDigits], mOptions[TRDLinkStats]);
1149}
1150
1152{
1153 mEventRecords.reset();
1154 mTrackletsFound = 0;
1155 mDigitsFound = 0;
1156 mDigitWordsRead = 0;
1157 mDigitWordsRejected = 0;
1158 mTrackletWordsRead = 0;
1159 mTrackletWordsRejected = 0;
1160 mWordsRejected = 0;
1161}
1162
1163void CruRawReader::checkNoWarn(bool silently)
1164{
1165 if (!mOptions[TRDVerboseErrorsBit]) {
1166 if (--mMaxWarnPrinted == 0) {
1167 if (silently) {
1168 // put the warning message into the log file without polluting the InfoLogger
1169 LOG(warn) << "Warnings limit reached, the following ones will be suppressed";
1170 } else {
1171 // makes sense only for warnings with "alarm" severity
1172 LOG(alarm) << "Warnings limit reached, the following ones will be suppressed";
1173 }
1174 }
1175 }
1176}
1177
1179{
1180 if (!mOptions[TRDVerboseErrorsBit]) {
1181 if (--mMaxErrsPrinted == 0) {
1182 LOG(error) << "Errors limit reached, the following ones will be suppressed";
1183 }
1184 }
1185}
1186
1187} // namespace o2::trd
benchmark::State & state
TRD raw data translator.
Global TRD definitions and constants.
std::ostringstream debug
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
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"