Belle II Software development
SVDUnpackerModule.cc
1/**************************************************************************
2 * basf2 (Belle II Analysis Software Framework) *
3 * Author: The Belle II Collaboration *
4 * *
5 * See git log for contributors and copyright holders. *
6 * This file is licensed under LGPL-3.0, see LICENSE.md. *
7 **************************************************************************/
8
9#include <svd/modules/svdUnpacker/SVDUnpackerModule.h>
10
11#include <framework/datastore/DataStore.h>
12#include <framework/datastore/StoreObjPtr.h>
13#include <framework/logging/Logger.h>
14
15#include <boost/crc.hpp> // for boost::crc_basic, boost::augmented_crc
16#define CRC16POLYREV 0x8005 // CRC-16 polynomial, normal representation
17
18#include <arpa/inet.h>
19
20#include <sstream>
21#include <iomanip>
22#include <cstring>
23#include <vector>
24#include <set>
25#include <map>
26#include <utility>
27#include <algorithm>
28
29using namespace std;
30using namespace Belle2;
31using namespace Belle2::SVD;
32
33//-----------------------------------------------------------------
34// Register the Module
35//-----------------------------------------------------------------
36REG_MODULE(SVDUnpacker);
37
38//-----------------------------------------------------------------
39// Implementation
40//-----------------------------------------------------------------
41
42std::string Belle2::SVD::SVDUnpackerModule::m_xmlFileName = std::string("SVDChannelMapping.xml");
43
44// seenHeadersAndTrailers is a bit-field set explicitly at the start of each event
45// cppcheck-suppress uninitMemberVar
50{
51 //Set module properties
52 setDescription("Produce SVDShaperDigits from RawSVD. NOTE: only zero-suppressed mode is currently supported!");
54
55 addParam("SVDEventInfo", m_svdEventInfoName, "Name of the SVDEventInfo object", string(""));
56 addParam("rawSVDListName", m_rawSVDListName, "Name of the raw SVD List", string(""));
57 addParam("svdShaperDigitListName", m_svdShaperDigitListName, "Name of the SVDShaperDigits list", string(""));
58 addParam("shutUpFTBError", m_shutUpFTBError,
59 "if >0 is the number of reported FTB header ERRORs before quiet operations. If <0 full log produced.", -1);
60 addParam("FADCTriggerNumberOffset", m_FADCTriggerNumberOffset,
61 "number to be added to the FADC trigger number to match the main trigger number", 0);
62 addParam("svdDAQDiagnosticsListName", m_svdDAQDiagnosticsListName, "Name of the DAQDiagnostics list", string(""));
63 addParam("softwarePipelineAddressEmulation", m_emulatePipelineAddress, "Estimate emulated pipeline address", bool(true));
64 addParam("killDigitsFromUpsetAPVs", m_killUpsetDigits, "Delete digits from upset APVs", bool(false));
65 addParam("silentlyAppend", m_silentAppend, "Append digits to a pre-existing non-empty storeArray", bool(false));
66 addParam("badMappingFatal", m_badMappingFatal, "Throw B2FATAL if there's a wrong payload in the database", bool(false));
67 addParam("UnpackerErrorRate", m_errorRate, "Unpacker will print one error every UnpackerErrorRate", int(1000));
68 addParam("PrintRawData", m_printRaw, "Printing Raw data words for debugging", bool(false));
69}
70
74
76{
77 m_eventMetaDataPtr.isRequired();
78 // Don't panic if no SVD data.
79 m_rawSVD.isOptional(m_rawSVDListName);
80
81 // Register default SVDEventInfo for unpacking Raw Data
83
86
89
90 m_diagnostics.reserve(2048);
91 m_digitsWithDiag.reserve(8192);
92 m_digitSortKeys.reserve(8192);
93 m_apvsByPipeline.reserve(2048);
94
95}
96
98{
99 if (!m_mapping.isValid())
100 B2FATAL("no valid SVD Channel Mapping. We stop here.");
101
102 m_wrongFTBcrc = 0;
103 if (m_mapping.hasChanged()) { m_map = std::make_unique<SVDOnlineToOfflineMap>(m_mapping->getFileName()); }
104
105 if (! m_map) { //give up
106 B2ERROR("SVD xml map not loaded." << std::endl <<
107 "No SVDShaperDigit will be produced for this run!");
108 return;
109 }
110
111 //number of FADC boards
112 nFADCboards = m_map->getFADCboardsNumber();
113
114 //passing APV<->FADC mapping from SVDOnlineToOfflineMap object
115 APVmap = &(m_map->APVforFADCmap);
116
117 //setting UnpackerErrorRate factor to use it for BadMapping error suppression
118 m_map->setErrorRate(m_errorRate);
119
122 nUpsetAPVsErrors = -1;
123 nSEURecoveryCase = -1;
126 nFADCMatchErrors = -1;
127 nAPVErrors = -1;
128 nFTBFlagsErrors = -1;
130
132
133 //get the relative time shift
134 if (!m_svdGlobalConfig.isValid())
135 B2FATAL("SVDGlobalConfigParameters not valid!!");
136
137 m_relativeTimeShift = m_svdGlobalConfig->getRelativeTimeShift();
138
139}
140
141#ifndef __clang__
142#pragma GCC diagnostic push
143#pragma GCC diagnostic ignored "-Wstack-usage="
144#endif
146{
147 if (!m_rawSVD || !m_rawSVD.getEntries())
148 return;
149
151 B2WARNING("Unpacking SVDShaperDigits to a non-empty pre-existing \n"
152 << "StoreArray. This can lead to undesired behaviour. At least\n"
153 << "remember to use SVDShaperDigitSorter in your path and \n"
154 << "set the silentlyAppend parameter of SVDUnpacker to true.");
155
156 // Diagnostics, digits and pipeline addresses are accumulated in event-local
157 // buffers and moved to the DataStore only once, at the end of the event.
158 m_diagnostics.clear();
159 m_digitsWithDiag.clear();
160 m_digitSortKeys.clear();
161 m_apvsByPipeline.clear();
162
163 // index in m_diagnostics of the diagnostic of the current APV
164 size_t currentDiagIdx = size_t(-1);
165
166 if (!m_eventMetaDataPtr.isValid()) { // give up...
167 B2ERROR("Missing valid EventMetaData." << std::endl << "No SVDShaperDigit produced for this event!");
168 return;
169 }
170
171 bool nAPVmatch = true;
172 bool badMapping = false;
173 bool badHeader = false;
174 bool badTrailer = false;
175 bool missedHeader = false;
176 bool missedTrailer = false;
177
178 // flag to set SVDEventInfo once per event
179 bool isSetEventInfo = false;
180
181 //flag to set nAPVsamples in SVDEventInfo once per event
182 bool isSetNAPVsamples = false;
183
184 unsigned short nAPVheaders = 999;
185 uint64_t seenAPVHeaders = 0; // bit mask of the (6-bit) APV numbers seen since the last FADC trailer
186
187 unsigned short nEntries_rawSVD = m_rawSVD.getEntries();
188 auto eventNo = m_eventMetaDataPtr->getEvent();
189
190 short fadc = 255, apv = 63;
191
192 // cache of the online-to-offline sensor lookup, valid for the current (fadc, apv)
193 int cachedChipID = -1;
194 SVDOnlineToOfflineMap::SensorInfo cachedSensorInfo{};
195
196 // scratch buffers hoisted out of the loops to avoid re-allocations
197 std::vector<unsigned short> nWords;
198 std::vector<uint32_t*> data32tab; //vector of pointers
199 vector<uint32_t> crc16vec; // input words for the CRC16 check, already converted with htonl
200
201 unsigned short cntFADCboards = 0;
202 for (unsigned int i = 0; i < nEntries_rawSVD; i++) {
203
204 RawSVD* rawSVD = m_rawSVD[i];
205 unsigned int numEntries_rawSVD = rawSVD->GetNumEntries();
206 for (unsigned int j = 0; j < numEntries_rawSVD; j++) {
207
208 const unsigned short maxNumOfCh = rawSVD->GetMaxNumOfCh(j);
209
210 nWords.resize(maxNumOfCh);
211 data32tab.resize(maxNumOfCh);
212 for (unsigned int k = 0; k < maxNumOfCh; k++) {
213 nWords[k] = rawSVD->GetDetectorNwords(j, k);
214 data32tab[k] = reinterpret_cast<uint32_t*>(rawSVD->GetDetectorBuffer(j, k)); // points at the beginning of the 1st buffer
215 }
216
217 unsigned short ftbError = 0;
218 unsigned short trgType = 0;
219 unsigned short trgNumber = 0;
220 unsigned short daqMode = -1;
221 unsigned short daqType = 0;
222 unsigned short cmc1;
223 unsigned short cmc2;
224 unsigned short apvErrors;
225 unsigned short pipAddr;
226 unsigned short ftbFlags = 0;
227 unsigned short apvErrorsOR = 0;
228
229 bool is3sampleData = false;
230 bool is6sampleData = false;
231
232 for (unsigned int buf = 0; buf < maxNumOfCh; buf++) { // loop over 4(COPPER) or 48(PCIe40) buffers
233
234 if (data32tab[buf] == nullptr || nWords[buf] == 0) {
235 if (data32tab[buf] != nullptr || nWords[buf] != 0) {
236 B2WARNING("Invalid combination of buffer pointer and nWords:" <<
237 LogVar("COPPER/PCIe40 ID", i) <<
238 LogVar("COPPER/PCIe40 Entry", j) <<
239 LogVar("COPPER/PCIe40 Channel", buf) <<
240 LogVar("data32tab[buf]", data32tab[buf]) <<
241 LogVar("nWords[buf]", nWords[buf]));
242 }
243 continue;
244 }
245 if (m_printRaw) printB2Debug(data32tab[buf], data32tab[buf], &data32tab[buf][nWords[buf] - 1], nWords[buf]);
246
247 cntFADCboards++;
248
249 missedHeader = false;
250 missedTrailer = false;
251
252 // diagnostics created for this buffer: [bufDiagStart, m_diagnostics.size())
253 const size_t bufDiagStart = m_diagnostics.size();
254
255 uint32_t* data32_it = data32tab[buf];
256 short strip, sample[6];
257 crc16vec.clear();
258
259 //reset value for headers and trailers check
261
262 for (; data32_it != &data32tab[buf][nWords[buf]]; data32_it++) {
263 m_data32 = *data32_it; //put current 32-bit frame to union
264
265 if (m_data32 == 0xffaa0000) { // first part of FTB header
266 crc16vec.clear(); // clear the input container for crc16 calculation
267 crc16vec.push_back(htonl(m_data32));
268
269 seenHeadersAndTrailers |= 0x1; // we found FTB header
270
271 data32_it++; // go to 2nd part of FTB header
272 crc16vec.push_back(htonl(*data32_it));
273
274 m_data32 = *data32_it; //put the second 32-bit frame to union
275
276 ftbError = m_FTBHeader.errorsField;
277
278 if (ftbError != 240) {
280
282 switch (ftbError - 240) {
283 case 3:
284 B2ERROR("FADC Event Number is different from (FTB & TTD) Event Numbers");
285 break;
286 case 5:
287 B2ERROR("TTD Event Number is different from (FTB & FADC) Event Numbers");
288 break;
289 case 6:
290 B2ERROR("FTB Event Number is different from (TTD & FADC) Event Numbers");
291 break;
292 case 7:
293 B2ERROR("(FTB, TTD & FADC) Event Numbers are different from each other");
294 break;
295 default:
296 B2ERROR("Problem with errorsField variable in FTB Header" << LogVar("abnormal value", ftbError));
297 }
298 }
299 }
300
301 if (m_FTBHeader.eventNumber !=
302 (eventNo & 0xFFFFFF)) {
305 m_shutUpFTBError -= 1;
306 B2ERROR("Event number mismatch detected! The event number given by EventMetaData object is different from the one in the FTB Header."
307 << LogVar("Expected event number & 0xFFFFFF",
308 (eventNo & 0xFFFFFF)) << LogVar("Event number in the FTB", m_FTBHeader.eventNumber));
309 }
310 }
311
312 continue;
313 } // is FTB Header
314
315 crc16vec.push_back(htonl(m_data32));
316
317 if (m_MainHeader.check == 6) { // FADC header
318
319 seenHeadersAndTrailers |= 0x2; //we found FADC Header
320
321 fadc = m_MainHeader.FADCnum;
322 trgType = m_MainHeader.trgType;
323 trgNumber = m_MainHeader.trgNumber;
324 daqMode = m_MainHeader.DAQMode;
325 daqType = m_MainHeader.DAQType;
326
327 //Let's add run-dependent info: daqMode="11" in case of 3-mixed-6 sample acquisition mode.
328 if (daqType) daqMode = 3;
329
330 nAPVheaders = 0; // start counting APV headers for this FADC
331 nAPVmatch = true; //assume correct # of APV headers
332 badMapping = false; //assume correct mapping
333 badHeader = false;
334 badTrailer = false;
335
336 is3sampleData = false;
337 is6sampleData = false;
338
339 if (daqMode == 0) B2ERROR("SVDDataFormatCheck: the event " << eventNo <<
340 " is apparently taken with 1-sample mode, this is not expected.");
341 if (daqMode == 1) is3sampleData = true;
342 if (daqMode == 2) is6sampleData = true;
343
344 if (
345 m_MainHeader.trgNumber !=
346 ((eventNo - m_FADCTriggerNumberOffset) & 0xFF)) {
347
350 B2ERROR("Event number mismatch detected! The event number given by EventMetaData object is different from the one in the FADC Header. "
351 << LogVar("Event number", eventNo) << LogVar("FADC", fadc) << LogVar("Trigger number LSByte reported by the FADC",
352 m_MainHeader.trgNumber) << LogVar("+ offset", m_FADCTriggerNumberOffset) << LogVar("expected", (eventNo & 0xFF)));
353 badHeader = true;
354 }
355
356 // create SVDModeByte object from MainHeader vars
357 m_SVDModeByte = SVDModeByte(m_MainHeader.runType, 0, daqMode, m_MainHeader.trgTiming);
358
359 // create SVDEventInfo and fill it with SVDModeByte & SVDTriggerType objects
360 if (!isSetEventInfo) {
362 m_svdEventInfoPtr.create();
363 m_svdEventInfoPtr->setModeByte(m_SVDModeByte);
364 m_svdEventInfoPtr->setTriggerType(m_SVDTriggerType);
365 m_svdEventInfoPtr->setAPVClock(m_hwClock);
366
367 //set relative time shift
368 m_svdEventInfoPtr->setRelativeShift(m_relativeTimeShift);
369 // set X-talk info online from Raw Data
370 m_svdEventInfoPtr->setCrossTalk(m_MainHeader.xTalk);
371
372 isSetEventInfo = true;
373 } else { // let's check if the current SVDModeByte and SVDTriggerType are consistent with the one stored in SVDEventInfo
374 if (m_SVDModeByte != m_svdEventInfoPtr->getModeByte()) {m_svdEventInfoPtr->setMatchModeByte(false); badHeader = true; nEventInfoMatchErrors++;}
375 if (trgType != (m_svdEventInfoPtr->getTriggerType()).getType()) { m_svdEventInfoPtr->setMatchTriggerType(false); badHeader = true; nEventInfoMatchErrors++;}
376 }
377 } // is FADC header
378
379 if (m_APVHeader.check == 2) { // APV header
380
381 nAPVheaders++;
382 apv = m_APVHeader.APVnum;
383 seenAPVHeaders |= (uint64_t(1) << apv);
384
385 cmc1 = m_APVHeader.CMC1;
386 cmc2 = m_APVHeader.CMC2;
387 apvErrors = m_APVHeader.apvErr;
388 pipAddr = m_APVHeader.pipelineAddr;
389
390 if (apvErrors != 0) {
391 nAPVErrors++;
392 if (!(nAPVErrors % m_errorRate)
393 or nAPVErrors < 100) B2ERROR("APV error has been detected." << LogVar("FADC", fadc) << LogVar("APV", apv) << LogVar("Error value",
394 apvErrors));
395 }
396 // temporary SVDDAQDiagnostic object (no info from trailers and APVmatch code)
397 m_diagnostics.emplace_back(trgNumber, trgType, pipAddr, cmc1, cmc2, apvErrors, ftbError, true,
398 nAPVmatch,
399 badHeader, missedHeader, missedTrailer,
400 fadc, apv);
401 currentDiagIdx = m_diagnostics.size() - 1;
402
403 m_apvsByPipeline.push_back((uint32_t(pipAddr) << 16) | (uint32_t(fadc) << 8) | uint32_t(apv));
404
405 // Let's check if the data frame does not come after APV header with piplAddr = 255 (special SEU recovery pseudo-data)
406 if (pipAddr == 255) {
407 data32_it++;
408 m_data32 = *data32_it;
409 if (m_data_A.check == 0) B2ERROR("The strip data frame is detected for the special SEU recovery mode. " << LogVar("pipline address",
410 pipAddr) << "This is unexpected!");
411 data32_it--;
412 m_data32 = *data32_it;
413 }
414
415 } //is APV Header
416
417 if (m_data_A.check == 0) { // data
418 strip = m_data_A.stripNum;
419
420 sample[0] = m_data_A.sample1;
421 sample[1] = m_data_A.sample2;
422 sample[2] = m_data_A.sample3;
423
424 sample[3] = 0;
425 sample[4] = 0;
426 sample[5] = 0;
427
428 // Let's check the next rawdata word to determine if we acquired 3 or 6 sample
429 data32_it++;
430 m_data32 = *data32_it;
431
432 if (m_data_B.check == 0 && strip == m_data_B.stripNum) { // 2nd data frame with the same strip number -> six samples
433
434 if (!isSetNAPVsamples) {
435 m_svdEventInfoPtr->setNSamples(6);
436 isSetNAPVsamples = true;
437 } else {
438 if (is3sampleData)
439 B2ERROR("DAQMode value (indicating 3-sample acquisition mode) doesn't correspond to the actual number of samples (6) in the data! The data might be corrupted!");
440 }
441
442 crc16vec.push_back(htonl(m_data32));
443
444 sample[3] = m_data_B.sample4;
445 sample[4] = m_data_B.sample5;
446 sample[5] = m_data_B.sample6;
447 }
448
449 else { // three samples
450 data32_it--;
451 m_data32 = *data32_it;
452
453 if (!isSetNAPVsamples) {
454 m_svdEventInfoPtr->setNSamples(3);
455 isSetNAPVsamples = true;
456 } else {
457 if (is6sampleData)
458 B2ERROR("DAQMode value (indicating 6-sample acquisition mode) doesn't correspond to the actual number of samples (3) in the data! The data might be corrupted!");
459 }
460 }
461
462 // Generating SVDShaperDigit object; the sensor lookup is cached
463 // and refreshed only when the (fadc, apv) combination changes
464 const int chipID = (int(fadc) << 8) | int(apv);
465 if (chipID != cachedChipID) {
466 cachedSensorInfo = m_map->getSensorInfo(fadc, apv);
467 cachedChipID = chipID;
468 }
469 if (cachedSensorInfo.m_sensorID) {
470 const short cellID = m_map->getStripNumber(strip, cachedSensorInfo);
471 // key packing (sensorID, side with U first, strip, insertion index):
472 // sorting the keys reproduces the SVDShaperDigit::operator< ordering,
473 // with ties resolved in favour of the first inserted digit
474 m_digitSortKeys.push_back((uint64_t(VxdID::baseType(cachedSensorInfo.m_sensorID)) << 48)
475 | (uint64_t(cachedSensorInfo.m_uSide ? 0 : 1) << 47)
476 | (uint64_t(uint16_t(cellID) ^ 0x8000) << 31)
477 | uint64_t(m_digitsWithDiag.size()));
478 m_digitsWithDiag.emplace_back(SVDShaperDigit(cachedSensorInfo.m_sensorID, cachedSensorInfo.m_uSide, cellID, sample, 0),
479 currentDiagIdx);
480 } else if (m_badMappingFatal) {
481 B2FATAL("Respective FADC/APV combination not found -->> incorrect payload in the database! ");
482 } else {
483 badMapping = true;
484 }
485
486 } //is data frame
487
488
489 if (m_FADCTrailer.check == 14) { // FADC trailer
490
491 seenHeadersAndTrailers |= 0x4; // we found FADC trailer
492
493 //additional check if we have a faulty/fake FADC that is not in the map
494 if (APVmap->find(fadc) == APVmap->end()) badMapping = true;
495
496 //comparing number of APV chips and the number of APV headers, for the current FADC
497 unsigned short nAPVs = APVmap->count(fadc);
498
499 if (nAPVheaders == 0) {
500 m_diagnostics.emplace_back(0, 0, 0, 0, 0, 0, ftbError, true, nAPVmatch, badHeader, 0, 0, fadc, 0);
501 currentDiagIdx = m_diagnostics.size() - 1;
502 }
503
504 if (nAPVs != nAPVheaders) {
505 // There is an APV missing, detect which it is.
506 for (const auto& fadcApv : *APVmap) {
507 if (fadcApv.first != fadc) continue;
508 if (!((seenAPVHeaders >> fadcApv.second) & 1)) {
509 // We have a missing APV. Look if it is a known one.
510 auto missingRec = m_missingAPVs.find(make_pair(fadcApv.first, fadcApv.second));
511 if (missingRec != m_missingAPVs.end()) {
512 // This is known to be missing, so keep quiet and just update event counters
513 if (missingRec->second.first > eventNo)
514 missingRec->second.first = eventNo;
515 if (missingRec->second.second < eventNo)
516 missingRec->second.second = eventNo;
517 } else {
518 // We haven't seen this previously.
520 m_missingAPVs.insert(make_pair(
521 make_pair(fadcApv.first, fadcApv.second),
522 make_pair(eventNo, eventNo)
523 ));
524 if (!(nMissingAPVsErrors % m_errorRate)) B2ERROR("missing APV header! " << LogVar("Event number", eventNo) << LogVar("APV",
525 int(fadcApv.second)) << LogVar("FADC",
526 int(fadcApv.first)));
527 }
528 }
529 }
530 nAPVmatch = false;
531 } // is nAPVs != nAPVheaders
532
533 seenAPVHeaders = 0;
534
535 ftbFlags = m_FADCTrailer.FTBFlags;
536 if ((ftbFlags >> 5) != 0) badTrailer = true;
537 if (ftbFlags != 0) {
539 if (!(nFTBFlagsErrors % m_errorRate) or nFTBFlagsErrors < 100) {
540 B2ERROR(" FTB Flags variable has an active error bit(s)" << LogVar("on FADC number", fadc));
541
542 if (ftbFlags & 16) B2ERROR("----> CRC error has been detected. Data might be corrupted!");
543 if (ftbFlags & 8) B2ERROR("----> Bad Event indication has been detected. Data might be corrupted!");
544 if (ftbFlags & 4) B2ERROR("----> Double Header has been detected. Data might be corrupted!");
545 if (ftbFlags & 2) B2ERROR("----> Time Out has been detected. Data might be corrupted!");
546 if (ftbFlags & 1) B2ERROR("----> Event Too Long! Data might be corrupted!");
547 }
548 }
549
550 apvErrorsOR = m_FADCTrailer.apvErrOR;
551
552
553 }// is FADC trailer
554
555 if (m_FTBTrailer.controlWord == 0xff55) {// FTB trailer
556
557 seenHeadersAndTrailers |= 0x8; // we found FTB trailer
558
559 //check CRC16: the trailer word itself is not part of the checksum,
560 //the words were already converted with htonl when collected
561 crc16vec.pop_back();
562
563 //verify CRC16 (table-driven, same parameters and result as
564 //boost::crc_basic<16>(0x8005, 0xffff, 0, false, false))
565 boost::crc_optimal<16, 0x8005, 0xFFFF, 0, false, false> bcrc;
566 bcrc.process_bytes(crc16vec.data(), crc16vec.size() * sizeof(uint32_t));
567 unsigned int checkCRC = bcrc.checksum();
568
569 if (checkCRC != m_FTBTrailer.crc16) {
570 B2WARNING("FTB CRC16 checksum DOES NOT MATCH" << LogVar("for FADC no.", fadc));
572 }
573
574 } // is FTB trailer
575
576 } // end loop over 32-bit frames in each buffer
577
578 //Let's check if all the headers and trailers were in place in the last frame
579 if (seenHeadersAndTrailers != 0xf) {
580 if (!(seenHeadersAndTrailers & 1)) {B2ERROR("Missing FTB Header is detected. SVD data might be corrupted!" << LogVar("Event number", eventNo) << LogVar("FADC", fadc)); missedHeader = true;}
581 if (!(seenHeadersAndTrailers & 2)) {B2ERROR("Missing FADC Header is detected -> related FADC number couldn't be retrieved. SVD data might be corrupted! " << LogVar("Event number", eventNo) << LogVar("previous FADC", fadc)); missedHeader = true;}
582 if (!(seenHeadersAndTrailers & 4)) {B2ERROR("Missing FADC Trailer is detected. SVD data might be corrupted!" << LogVar("Event number", eventNo) << LogVar("FADC", fadc)); missedTrailer = true;}
583 if (!(seenHeadersAndTrailers & 8)) {B2ERROR("Missing FTB Trailer is detected. SVD data might be corrupted!" << LogVar("Event number", eventNo) << LogVar("FADC", fadc)); missedTrailer = true;}
584 }
585
586 for (size_t d = bufDiagStart; d < m_diagnostics.size(); d++) {
587 // adding remaining info to Diagnostic object
589 p.setFTBFlags(ftbFlags);
590 p.setApvErrorOR(apvErrorsOR);
591 p.setAPVMatch(nAPVmatch);
592 p.setBadMapping(badMapping);
593 p.setBadTrailer(badTrailer);
594 p.setMissedHeader(missedHeader);
595 p.setMissedTrailer(missedTrailer);
596 }
597
598 } // end iteration on 4(COPPER)/48(PCIe40) data buffers
599
600 } // end event loop
601
602 }// end loop over RawSVD objects
603
604 // Check the number of FADC boards
605 if (cntFADCboards != nFADCboards) { // nFADCboards=52
607 if (!(nFADCMatchErrors % m_errorRate)) B2ERROR("Number of data objects in rawSVD do not match the number of FADC boards" <<
608 LogVar("# of data objects in rawSVD",
609 cntFADCboards) << LogVar("# of FADCs", nFADCboards) << LogVar("Event number", eventNo));
610
611 // We override all FADCMatch fields in diagnostics and set it to false.
612 for (auto& p : m_diagnostics) {
613 p.setFADCMatch(false);
614 }
615 }
616
617 // Sort the (pipeline address, FADC, APV) words and drop duplicates: groups of
618 // entries sharing the pipeline address then appear in the same order as in
619 // the former map<pipeline, set<pair<fadc, apv>>>
620 std::sort(m_apvsByPipeline.begin(), m_apvsByPipeline.end());
621 m_apvsByPipeline.erase(std::unique(m_apvsByPipeline.begin(), m_apvsByPipeline.end()), m_apvsByPipeline.end());
622
623 // Check if we have special data for SEU recovery mode (pipAddr = 0xFF)
624 // If so, let's monitor affected FADC/APV's and the event range with seuRecMap
625 // (the entries with pipAddr = 255 are the tail of the sorted list)
626 auto seuBegin = std::lower_bound(m_apvsByPipeline.begin(), m_apvsByPipeline.end(), uint32_t(255) << 16);
627 for (auto it = seuBegin; it != m_apvsByPipeline.end(); ++it) {
628 const unsigned short fadcNo = (*it >> 8) & 0xff;
629 const unsigned short apvNo = *it & 0xff;
630
631 auto seuRec = m_seuRecMap.find(make_pair(fadcNo, apvNo));
632 if (seuRec != m_seuRecMap.end()) {
633 if (seuRec->second.first > eventNo)
634 seuRec->second.first = eventNo;
635 if (seuRec->second.second < eventNo)
636 seuRec->second.second = eventNo;
637 } else {
639 m_seuRecMap.insert(make_pair(
640 make_pair(fadcNo, apvNo),
641 make_pair(eventNo, eventNo)
642 ));
644 B2ERROR("Special Recovery Data (Dummy APV Header) found due to the detection of Single Event Upset (SEU)!!!" << LogVar("APV",
645 int(apvNo)) << LogVar("FADC", int(fadcNo)) << LogVar("Event number", eventNo));
646
647 }
648 for (auto& pp : m_diagnostics) {
649 if (pp.getFADCNumber() == fadcNo and pp.getAPVNumber() == apvNo)
650 pp.setSEURecoData(true);
651 }
652 }
653
654 // Detect upset APVs and report/treat.
655 // Find the pipeline address seen by most APVs; ties are resolved in favour
656 // of the lowest address, as max_element did on the former map.
657 unsigned short majorPipeline = 0;
658 size_t majorCount = 0;
659 unsigned short nPipelines = 0;
660 for (size_t b = 0; b < m_apvsByPipeline.size();) {
661 const unsigned short pip = m_apvsByPipeline[b] >> 16;
662 size_t e = b;
663 while (e < m_apvsByPipeline.size() && (m_apvsByPipeline[e] >> 16) == pip) e++;
664 nPipelines++;
665 if (e - b > majorCount) { majorCount = e - b; majorPipeline = pip; }
666 b = e;
667 }
668
669 // We set emuPipelineAddress fields in diagnostics to this.
670 if (m_emulatePipelineAddress && majorCount > 0)
671 for (auto& p : m_diagnostics)
672 p.setEmuPipelineAddress(majorPipeline);
673
674 unsigned short apvsByPipelineSize = nPipelines;
675 if (majorCount > 0 && majorPipeline == 255) apvsByPipelineSize = 1;
676
677 // And report any upset apvs or update records
678 if (apvsByPipelineSize > 1)
679 for (size_t b = 0; b < m_apvsByPipeline.size();) {
680 const unsigned short pip = m_apvsByPipeline[b] >> 16;
681 size_t e = b;
682 while (e < m_apvsByPipeline.size() && (m_apvsByPipeline[e] >> 16) == pip) e++;
683 if (pip == majorPipeline or pip == 255) { b = e; continue; }
684 for (size_t k = b; k < e; k++) {
685 const unsigned short fadcNo = (m_apvsByPipeline[k] >> 8) & 0xff;
686 const unsigned short apvNo = m_apvsByPipeline[k] & 0xff;
687 // We have an upset APV. Look if it is a known one.
688 auto upsetRec = m_upsetAPVs.find(make_pair(fadcNo, apvNo));
689 if (upsetRec != m_upsetAPVs.end()) {
690 // This is known to be upset, so keep quiet and update event counters
691 if (upsetRec->second.first > eventNo)
692 upsetRec->second.first = eventNo;
693 if (upsetRec->second.second < eventNo)
694 upsetRec->second.second = eventNo;
695 } else {
696 // We haven't seen this one previously.
698 m_upsetAPVs.insert(make_pair(
699 make_pair(fadcNo, apvNo),
700 make_pair(eventNo, eventNo)
701 ));
702
703 if (!(nUpsetAPVsErrors % m_errorRate)) B2ERROR("Upset APV detected!!!" << LogVar("APV", int(apvNo)) << LogVar("FADC",
704 int(fadcNo)) << LogVar("Event number", eventNo));
705 }
706
707 for (auto& pp : m_diagnostics) {
708
709 if (pp.getFADCNumber() == fadcNo and pp.getAPVNumber() == apvNo)
710 pp.setUpsetAPV(true);
711 }
712
713 }
714 b = e;
715 }
716
717 // Sort the digit keys and drop duplicated strips keeping the first occurrence:
718 // same ordering and deduplication as the former map<SVDShaperDigit, ...>
719 std::sort(m_digitSortKeys.begin(), m_digitSortKeys.end());
720
721 // Here we can delete digits coming from upset APVs. We detect them by comparing
722 // actual and emulated pipeline address fields in DAQDiagnostics.
723 uint64_t lastKeptStrip = ~uint64_t(0); // sentinel that cannot match any key
724 for (const uint64_t key : m_digitSortKeys) {
725
726 if ((key >> 31) == lastKeptStrip) continue; // duplicated strip
727 lastKeptStrip = key >> 31;
728
729 const auto& p = m_digitsWithDiag[key & 0x7fffffff];
730 if (p.second >= m_diagnostics.size()) continue; // data frame without APV header
731 const SVDDAQDiagnostic& d = m_diagnostics[p.second];
732 if ((m_killUpsetDigits && d.getPipelineAddress() != d.getEmuPipelineAddress()) || d.getFTBError() != 240
733 || d.getFTBFlags() || d.getAPVError() || !(d.getAPVMatch()) || !(d.getFADCMatch())
734 || d.getBadHeader()
735 || d.getBadMapping() || d.getUpsetAPV() || d.getMissedHeader() || d.getMissedTrailer()) continue;
736 m_storeShaperDigits.appendNew(p.first);
737 }
738
739 // Finally move the diagnostics to the DataStore, in one go.
740 for (const auto& d : m_diagnostics)
741 m_storeDAQDiagnostics.appendNew(d);
742
743 if (!m_svdEventInfoPtr->getMatchTriggerType()) {if (!(nEventInfoMatchErrors % m_errorRate) or nEventInfoMatchErrors < 200) B2WARNING("Inconsistent SVD Trigger Type value for: " << LogVar("Event number", eventNo));}
744 if (!m_svdEventInfoPtr->getMatchModeByte()) {if (!(nEventInfoMatchErrors % m_errorRate) or nEventInfoMatchErrors < 200) B2WARNING("Inconsistent SVD ModeByte object for: " << LogVar("Event number", eventNo));}
745
746
747} //end event function
748#ifndef __clang__
749#pragma GCC diagnostic pop
750#endif
751
753{
754
755 // Summary report on missing APVs
756 if (m_missingAPVs.size() > 0) {
757 B2WARNING("SVDUnpacker summary 1: Missing APVs");
758 for (const auto& miss : m_missingAPVs)
759 B2WARNING(LogVar("Missing APV", miss.first.second) << LogVar("FADC", miss.first.first) << LogVar("since event",
760 miss.second.first) << LogVar("to event", miss.second.second));
761 }
762 if (m_upsetAPVs.size() > 0) {
763 B2WARNING("SVDUnpacker summary 2: Upset APVs");
764 for (const auto& upst : m_upsetAPVs)
765 B2WARNING(LogVar("Upset APV", upst.first.second) << LogVar("FADC", upst.first.first) <<
766 LogVar("since event", upst.second.first) << LogVar("to event", upst.second.second));
767 }
768
769 if (m_seuRecMap.size() > 0) {
770 B2WARNING("SVDUnpacker summary 2: Special SEU Recovery Data");
771 for (const auto& seu : m_seuRecMap)
772 B2WARNING(LogVar("SEU recovery data: APV", seu.first.second) << LogVar("FADC", seu.first.first) <<
773 LogVar("since event", seu.second.first) << LogVar("to event", seu.second.second));
774 }
775
776}
777
778
779// additional printing function
780void SVDUnpackerModule::printB2Debug(const uint32_t* data32, const uint32_t* data32_min, const uint32_t* data32_max, int nWords)
781{
782
783 const uint32_t* min = std::max((data32 - nWords), data32_min);
784 const uint32_t* max = std::min((data32 + nWords), data32_max);
785
786 size_t counter{0};
787 std::stringstream os;
788 os << std::hex << std::setfill('0');
789 for (const uint32_t* ptr = min; ptr <= max; ++ptr) {
790 os << std::setw(8) << *ptr;
791 if (++counter % 10 == 0) os << std::endl;
792 else os << " ";
793 }
794
795 os << std::endl;
796 B2INFO(os.str());
797 return;
798
799}
@ c_ErrorIfAlreadyRegistered
If the object/array was already registered, produce an error (aborting initialisation).
Definition DataStore.h:72
void setDescription(const std::string &description)
Sets the description of the module.
Definition Module.cc:214
void setPropertyFlags(unsigned int propertyFlags)
Sets the flags for the module properties.
Definition Module.cc:208
Module()
Constructor.
Definition Module.cc:30
@ c_ParallelProcessingCertified
This module can be run in parallel processing mode safely (All I/O must be done through the data stor...
Definition Module.h:80
virtual int GetNumEntries()
get # of data blocks = (# of nodes)*(# of events)
The Raw SVD class Class for RawCOPPER class data taken by SVD Currently, this class is almost same as...
Definition RawSVD.h:26
Class to store SVD DAQ diagnostic information.
Class to store SVD mode information.
Definition SVDModeByte.h:69
The SVD ShaperDigit class.
Class to store Trigger Type information.
int m_wrongFTBcrc
FTB CRC no-Match counter.
StoreArray< SVDDAQDiagnostic > m_storeDAQDiagnostics
SVDDAQDiagnostic array.
unsigned short seenHeadersAndTrailers
this 4-bits value should be 1111 if no headers/trailers are missing
int m_shutUpFTBError
regulates the number of "Event number mismatch" errors reported
MainHeader m_MainHeader
Implementation of FADC Header.
StoreArray< RawSVD > m_rawSVD
output for RawSVD
FADCTrailer m_FADCTrailer
Implementation of FADC Trailer.
StoreObjPtr< SVDEventInfo > m_svdEventInfoPtr
SVDEventInfo output per event.
virtual void initialize() override
Initializes the Module.
std::vector< uint64_t > m_digitSortKeys
Event-local sort keys of the digits: (sensorID, side, strip, insertion index) packed so that sorting ...
StoreArray< SVDShaperDigit > m_storeShaperDigits
SVDShaperDigit array.
virtual void event() override
event
SVDUnpackerModule()
Constructor of the module.
int nAPVErrors
counter of APV errors
FTBHeader m_FTBHeader
Implementation of FTB Header.
virtual void endRun() override
end run
std::string m_rawSVDListName
RawSVD StoreArray name.
std::vector< SVDDAQDiagnostic > m_diagnostics
Event-local buffer of diagnostics; moved to the StoreArray once per event.
DBObjPtr< PayloadFile > m_mapping
pointer to the payload with the mapping
int nFADCMatchErrors
counter of FADC boards =/= n of RawData objects errors
bool m_killUpsetDigits
Optionally, we can kill digits coming from upset APVs right in the unpacker.
data_B m_data_B
Implementation of 2nd data word.
int m_FADCTriggerNumberOffset
FADC Trigger Offset.
std::map< std::pair< unsigned short, unsigned short >, std::pair< std::size_t, std::size_t > > m_missingAPVs
Map to store a list of missing APVs.
int nMissingAPVsErrors
counter of missing APVs errors
static std::string m_xmlFileName
XML filename.
int nEventMatchErrors
counter of Event match errors
FTBTrailer m_FTBTrailer
Implementation of FTB Trailer.
int m_errorRate
The parameter that indicates what fraction of B2ERRORs messages should be suppressed to not overload ...
APVHeader m_APVHeader
Implementation of APV Header.
int nUpsetAPVsErrors
counter of upset APV errors
virtual void beginRun() override
begin run
std::string m_svdShaperDigitListName
SVDShaperDigit StoreArray name.
DBObjPtr< HardwareClockSettings > m_hwClock
system clock
std::vector< uint32_t > m_apvsByPipeline
Event-local list of (pipeline address << 16 | FADC << 8 | APV) words.
std::string m_svdEventInfoName
SVDEventInfo name.
DBObjPtr< SVDGlobalConfigParameters > m_svdGlobalConfig
SVDGlobal Configuration payload.
bool m_printRaw
Optionally we can get printout of Raw Data words.
int nEventInfoMatchErrors
counter of inconsistencies in SVDEventInfo within an event
bool m_badMappingFatal
Optionally we can stop the unpacking if there is a missing APV/FADC combination in the mapping -> wro...
int nErrorFieldErrors
counter of event mismatch errors in FTB's ErrorField
std::map< std::pair< unsigned short, unsigned short >, std::pair< std::size_t, std::size_t > > m_seuRecMap
Map to store a list of APVs for special data for SEU recovery.
data_A m_data_A
Implementation of 1st data word.
uint32_t m_data32
Input 32-bit data word.
StoreObjPtr< EventMetaData > m_eventMetaDataPtr
Required input for EventMetaData.
bool m_silentAppend
Silently append new SVDShaperDigits to a pre-existing non-empty SVDShaperDigits storeArray.
std::unordered_multimap< unsigned char, unsigned char > * APVmap
pointer to APVforFADCmap filled by mapping procedure
SVDTriggerType m_SVDTriggerType
SVDTriggerType object.
SVDModeByte m_SVDModeByte
instance of SVDModeByte for the event
virtual ~SVDUnpackerModule() override
Destructor of the module.
std::unique_ptr< SVDOnlineToOfflineMap > m_map
Pointer to online-to-offline map.
int m_relativeTimeShift
latency difference between the 3- and 6-sample acquired events in units of APV clock / 4,...
std::map< std::pair< unsigned short, unsigned short >, std::pair< std::size_t, std::size_t > > m_upsetAPVs
Map to store a list of upset APVs.
int nFTBFlagsErrors
counter of errors in FTBFlags variable
int nSEURecoveryCase
counter of SEU Special Recovery data cases
bool m_emulatePipelineAddress
Software emulation of pipeline address This is a replacement of hardware pipeline address emulation.
std::vector< std::pair< SVDShaperDigit, std::size_t > > m_digitsWithDiag
Event-local buffer of digits, each with the index of its diagnostic in m_diagnostics.
std::string m_svdDAQDiagnosticsListName
SVDDAQDiagnostic StoreArray name.
int nTriggerMatchErrors
counters for specific ERRORS produced by the Unpacker
static void printB2Debug(const uint32_t *data32, const uint32_t *data32_min, const uint32_t *data32_max, int nWords)
additional function that prints raw data words
unsigned short nFADCboards
how many FADCs we have
unsigned short baseType
The base integer type for VxdID.
Definition VxdID.h:35
Class to store variables with their name which were sent to the logging service.
void addParam(const std::string &name, T &paramVariable, const std::string &description, const T &defaultValue)
Adds a new parameter to the module.
Definition Module.h:559
#define REG_MODULE(moduleName)
Register the given module (without 'Module' suffix) with the framework.
Definition Module.h:649
int GetDetectorNwords(int n, int finesse_num)
get Detector buffer length
Definition RawCOPPER.h:657
int * GetDetectorBuffer(int n, int finesse_num)
get Detector buffer
Definition RawCOPPER.h:681
int GetMaxNumOfCh(int n)
Get the max number of channels in a readout board.
Definition RawCOPPER.h:750
Namespace to encapsulate code needed for simulation and reconstrucion of the SVD.
Abstract base class for different kinds of events.
STL namespace.
Struct to hold data about a sensor.