CbmRoot
Loading...
Searching...
No Matches
rich/UnpackMS.cxx
Go to the documentation of this file.
1/* Copyright (C) 2021 Goethe-University Frankfurt, Frankfurt
2 SPDX-License-Identifier: GPL-3.0-only
3 Authors: Pascal Raisig, Dominik Smith [committer] */
4
5#include "UnpackMS.h"
6
8
9#include <cstdint>
10
11namespace cbm::algo::rich
12{
13 UnpackMS::UnpackMS(const UnpackPar& pars) : fParams(pars) {}
14 UnpackMS::~UnpackMS() = default;
15
16 // ---- Algorithm execution ---------------------------------------------
17 UnpackMS::Result_t UnpackMS::operator()(const uint8_t* msContent, const fles::MicrosliceDescriptor& msDescr,
18 const uint64_t tTimeslice) const
19 {
20 MSContext ctx;
21
22 // Clear CbmTime of MS. Used to get time offset of subtriggers to MS start
23 ctx.cbmTimeMS = 0;
24 ctx.digis.reserve(msDescr.size / sizeof(u32));
25
27 reader.SetData(msContent, msDescr.size);
28
29 const auto mstime = msDescr.idx;
30 ctx.refTime = mstime - tTimeslice;
31
32 // There are a lot of MS with 8 bytes size
33 // Does one need these MS?
34 if (reader.GetSize() <= 8) return {};
35
36 while (true) {
37 ProcessTrbPacket(reader, ctx);
38 ProcessHubBlock(reader, ctx);
39
40 // -4*2 for 2 last words which contain microslice index
41 if (reader.GetOffset() >= reader.GetSize() - 8) break;
42 // -4*3 for 0xffffffff padding and 2 last words which contain microslice index
43 if (reader.IsNextPadding() && reader.GetOffset() >= reader.GetSize() - 12) break;
44 }
45 [[maybe_unused]] uint32_t msIndexWord1 = reader.NextWord();
46 [[maybe_unused]] uint32_t msIndexWord2 = reader.NextWord();
47
48 return std::make_tuple(std::move(ctx.digis), std::move(ctx.monitor), UnpackAuxData());
49 }
50
51
53 {
54 //process CBM time
55 const uint32_t word_MSB = reader.NextWord(); // CBM 63:32
56 const uint32_t word_LSB = reader.NextWord(); // CBM 31: 0
57 ctx.cbmTimePacket = (uint64_t) word_MSB << 32 | word_LSB;
58 if (ctx.cbmTimeMS == 0) ctx.cbmTimeMS = ctx.cbmTimePacket;
59
60 //discard unused words
61 for (auto l = 0; l < 10; ++l) {
62 reader.NextWord();
63 }
64
65 //process remaining words
66 for (auto l = 0; l < 14; ++l) {
67 const uint32_t wordEpoch = reader.NextWord();
68 const uint32_t epoch = ProcessEpoch(wordEpoch);
69 const uint32_t wordTime = reader.NextWord();
70 const TdcTimeData td = ProcessTimeData(wordTime);
71 const double fullTime = CalculateTime(epoch, td.fCoarse, td.fFine);
72
73 if (l == 0) ctx.prevLastCh0ReTime[12] = fullTime;
74 if (l == 1) ctx.mbsCorr = fullTime - ctx.prevLastCh0ReTime[12]; // = MbsPrevTimeCh1 - MbsPrevTimeCh0
75 if (l > 1) ctx.prevLastCh0ReTime[l - 2] = fullTime;
76 }
77
78 [[maybe_unused]] const uint32_t trbNum = reader.NextWord(); // TRB trigger number
79 //if (isLog()) L_(debug4) << getLogHeader(reader) << "TRB Num:" << reader.GetWordAsHexString(trbNum);
80 }
81
82
84 {
85 uint32_t word = reader.NextWord();
86
87 [[maybe_unused]] const uint32_t hubId = word & 0xffff; // 16 bits
88 const uint32_t hubSize = (word >> 16) & 0xffff; // 16 bits
89
90 bool isLast = false; // if true then it is CTS sub-sub-event
91 size_t totalSize = 0;
92 ctx.currentSubSubEvent = 0;
93
94 uint32_t subSubEventId = 0, subSubEventSize = 0;
95
96 //loop over events in hub block
97 while (totalSize < hubSize) {
98 word = reader.NextWord();
99 subSubEventId = word & 0xffff; // 16 bits
100 subSubEventSize = (word >> 16) & 0xffff; // 16 bits
101 isLast = reader.IsLastSubSubEvent(subSubEventSize); // if true then it is CTS sub-sub-event
102 totalSize += (1 + subSubEventSize);
103
104 if (!isLast) { // all except last are DiRICH events
105 uint16_t sSubSubEvtIdMsb = (subSubEventId >> 12) & 0xF;
106 if ((sSubSubEvtIdMsb != 0x7) && (sSubSubEvtIdMsb != 0x9)) { // catch invalid ids: mRICH, mFSD, PASTA/MUST
108 }
109 if (totalSize == hubSize) {
111 }
112 ProcessSubSubEvent(reader, subSubEventSize, subSubEventId, ctx);
113 ctx.currentSubSubEvent++;
114 }
115 }
116
117 //last event ist expected to be CTS
118 if (totalSize != hubSize || !isLast) {
120 }
121 subSubEventSize = ProcessCtsHeader(reader, subSubEventSize, subSubEventId);
122 ProcessSubSubEvent(reader, subSubEventSize, subSubEventId, ctx);
123
124 // read last words
125 int lastWordsCounter = 0;
126 while (true) {
127 lastWordsCounter++;
128 word = reader.NextWord();
129 if (word == 0x600dda7a) {
130 if (reader.IsNextPadding()) word = reader.NextWord();
131 break;
132 }
133 if (lastWordsCounter >= 7) {
135 }
136 }
137 }
138
139 int UnpackMS::ProcessCtsHeader(rich::MicrosliceReader& reader, uint32_t subSubEventSize,
140 uint32_t /*subSubEventId*/) const
141 {
142 const uint32_t word = reader.NextWord();
143
144 [[maybe_unused]] const uint32_t ctsState = word & 0xffff; // 16 bits
145
146 const uint32_t nofInputs = (word >> 16) & 0xf; // 4 bits
147 const uint32_t nofTrigCh = (word >> 20) & 0x1f; // 5 bits
148 const uint32_t inclLastIdle = (word >> 25) & 0x1; // 1 bit
149 const uint32_t inclTrigInfo = (word >> 26) & 0x1; // 1 bit
150 const uint32_t inclTime = (word >> 27) & 0x1; // 1 bit
151 const uint32_t ETM = (word >> 28) & 0x3; // 2 bits
152
153 uint32_t ctsInfoSize = 2 * nofInputs + 2 * nofTrigCh + 2 * inclLastIdle + 3 * inclTrigInfo + inclTime; // in words
154 switch (ETM) {
155 case 0: break;
156 case 1: ctsInfoSize += 1; break;
157 case 2: ctsInfoSize += 4; break;
158 case 3: break;
159 }
160 for (uint32_t i = 0; i < ctsInfoSize; i++) {
161 reader.NextWord(); // do nothing?
162 }
163 const int nofTimeWords = subSubEventSize - ctsInfoSize - 1;
164 return nofTimeWords;
165 }
166
167 void UnpackMS::ProcessSubSubEvent(rich::MicrosliceReader& reader, int nofTimeWords, uint32_t subSubEventId,
168 MSContext& ctx) const
169 {
170 bool wasHeader = false;
171 bool wasEpoch = false;
172 bool wasTime = false;
173 bool wasTrailer = false;
174
175 uint32_t epoch = 0; // store last epoch obtained in sub-sub-event
176
177 // Store last raising edge time for each channel or -1. if no time
178 // this array is used to match raising and falling edges
179 std::vector<double> raisingTime(33, -1.);
180
181 // check if DiRICH (SubSubEvId) is masked and skip to end if so
182 if (CheckMaskedDiRICH(subSubEventId)) {
183 for (int i = 0; i < nofTimeWords; i++) {
184 reader.NextWord();
185 }
186 return;
187 }
188 // Skip SubSubEvent for CTS and unknown DiRICH addresses
189 // TODO: properly handle/skip CTS subsubevents
190 if (fParams.fElinkParams.end() == fParams.fElinkParams.find(subSubEventId)) {
191 if (0x8000 != (subSubEventId & 0xF000)) {
192 // No log for CTS subsubevents
193 L_(debug) << "Unknown DiRICH ID 0x" << std::hex << subSubEventId << std::dec
194 << ": Stopping processing of this subsubevent!!!";
195 }
198 for (int i = 0; i < nofTimeWords; i++) {
199 reader.NextWord();
200 }
201 return;
202 }
203
204 // Catch Subsubevents where some of the 3 "mandatory words" is missing
205 // but this lack was properly accounted/detected by the TRBnet/CTS
206 // => Otherwise the final readout of the Trailer word offsets the next
207 // subsubevent
208 if (nofTimeWords < 3) {
210 for (int i = 0; i < nofTimeWords; i++) {
211 reader.NextWord();
212 }
213 return;
214 }
215
216
217 // First word is expected to be of type "header"
219 L_(debug) << "DiRICH 0x" << std::hex << subSubEventId << std::dec
220 << ": 1st message is not a TDC Header, skipping subsubevent";
223 for (int i = 1; i < nofTimeWords; i++) {
224 reader.NextWord();
225 }
226 return;
227 }
228 else {
229 wasHeader = true;
230 }
231
232 // Second word is expected to be of type "epoch"
233 uint32_t word = reader.NextWord();
234 if (GetTdcWordType(word) != TdcWordType::Epoch) {
235 L_(debug) << "DiRICH 0x" << std::hex << subSubEventId << std::dec
236 << ": 2nd message is not an epoch, skipping subsubevent";
239 // Currently skip the subsubevent
240 // TODO: Check if possible to only skip to next Epoch and continue from there
241 for (int i = 2; i < nofTimeWords; i++) {
242 reader.NextWord();
243 }
244 return;
245 }
246 else {
247 epoch = ProcessEpoch(word);
248 wasEpoch = true;
249 }
250
251 // Loop over words
252 for (int i = 2; i < nofTimeWords - 1; i++) {
253 word = reader.NextWord();
254 switch (GetTdcWordType(word)) {
256 if (!wasHeader || !wasEpoch || wasTrailer) {
257 L_(debug) << "DiRICH 0x" << std::hex << subSubEventId << std::dec
258 << ": illegal position of TDC Time (before header/epoch or after trailer)";
261 // Currently skip the subsubevent
262 // TODO: Check if possible to only skip to next Epoch and continue from there
263 for (; i < nofTimeWords - 1; i++) {
264 reader.NextWord();
265 }
266 return;
267 }
268 wasTime = true;
269 ProcessTimeDataWord(epoch, word, subSubEventId, raisingTime, ctx);
270 break;
271 }
272 case TdcWordType::Epoch: {
273 if (!wasHeader || wasTrailer) {
274 L_(debug) << "DiRICH 0x" << std::hex << subSubEventId << std::dec
275 << ": illegal position of TDC Epoch (before header or after trailer)";
278 for (; i < nofTimeWords - 1; i++) {
279 reader.NextWord();
280 }
281 return;
282 }
283 wasEpoch = true;
284 epoch = ProcessEpoch(word);
285 break;
286 }
287 case TdcWordType::Header: {
288 if (wasEpoch || wasTime || wasTrailer) {
289 L_(debug) << "DiRICH 0x" << std::hex << subSubEventId << std::dec
290 << ": illegal position of TDC Header (after time/epoch/trailer)";
293 for (; i < nofTimeWords - 1; i++) {
294 reader.NextWord();
295 }
296 return;
297 }
299 break;
300 }
302 if (!wasEpoch || !wasTime || !wasHeader) {
303 L_(debug) << "DiRICH 0x" << std::hex << subSubEventId << std::dec
304 << ": illegal position of TDC Trailer (before time/epoch/header)";
307 for (; i < nofTimeWords - 1; i++) {
308 reader.NextWord();
309 }
310 return;
311 }
312 L_(debug) << "DiRICH 0x" << std::hex << subSubEventId << std::dec << ": TDC Trailer too early, "
313 << (nofTimeWords - 1 - i) << " word(s) before last";
315 wasTrailer = true;
316 break;
317 }
318 case TdcWordType::Debug: {
319 // for the moment do nothing
321 break;
322 }
323 case TdcWordType::Error: {
325 break;
326 }
327 }
328 }
329
330 // Last word is expected to be of type "trailer"
331 word = reader.NextWord();
333 L_(debug) << "DiRICH 0x" << std::hex << subSubEventId << std::dec << ": Last word not a TDC trailer";
335 if (GetTdcWordType(word) == TdcWordType::TimeData && !wasTrailer) {
336 if (ProcessTimeDataWord(epoch, word, subSubEventId, raisingTime, ctx)) {
338 L_(debug) << "DiRICH 0x" << std::hex << subSubEventId << std::dec
339 << ": Rescuing TimeData in subsubevent with missing Trailer leading to saved Digi";
340 }
341 else {
343 L_(debug) << "DiRICH 0x" << std::hex << subSubEventId << std::dec
344 << ": Rescuing TimeData in subsubevent with missing Trailer w/o extra digi";
345 }
346 }
347 }
348 }
349
350 // ---- ProcessTimeDataWord ----
351 bool UnpackMS::ProcessTimeDataWord(uint32_t epoch, uint32_t tdcWord, uint32_t subSubEventId,
352 std::vector<double>& raisingTime, MSContext& ctx) const
353 {
354 const TdcTimeData td = ProcessTimeData(tdcWord);
355 const double fullTime = CalculateTime(epoch, td.fCoarse, td.fFine);
356
357 bool madeDigi = false;
358 if (td.fChannel != 0) {
359 const double dT = fullTime - ctx.prevLastCh0ReTime[ctx.currentSubSubEvent];
360 const double subtrigOffset =
361 (ctx.cbmTimePacket - ctx.cbmTimeMS) * 25.0; // offset of SubTrigger to MS start in ns
362 const double fullTimeCorr = dT - ctx.mbsCorr + subtrigOffset;
363
364 if (td.fChannel < 1 || td.fChannel >= raisingTime.size()) {
366 return false;
367 }
368 if (td.fIsRisingEdge) {
369 // always store the latest raising edge. It means that in case RRFF situation only middle RF will be matched.
370 raisingTime[td.fChannel] = fullTimeCorr;
371 }
372 else {
373 if (raisingTime[td.fChannel] != -1.) {
374 // Matching was found, calculate ToT, if tot is in a good range -> create digi
375 const double ToT = fullTimeCorr - raisingTime[td.fChannel];
376 if (ToT >= fToTMin && ToT <= fToTMax) {
377 if (fullTimeCorr >= 0.0) {
378 WriteOutputDigi(subSubEventId, td.fChannel, raisingTime[td.fChannel], ToT, ctx);
379 madeDigi = true;
380 }
381 }
382 // pair was created, set raising edge to -1.
383 raisingTime[td.fChannel] = -1.;
384 }
385 }
386 }
387 else {
389 }
390 return madeDigi;
391 }
392
393 double UnpackMS::CalculateTime(uint32_t epoch, uint32_t coarse, uint32_t fine) const
394 {
395 return ((double) epoch) * 2048. * 5. + ((double) coarse) * 5. - ((double) fine) * 0.005;
396 }
397
398 void UnpackMS::WriteOutputDigi(int32_t fpgaID, int32_t channel, double time, double tot, MSContext& ctx) const
399 {
400 double ToTcorr = fbDoToTCorr ? fParams.fElinkParams.at(fpgaID).fToTshift[channel] : 0.;
401 int32_t pixelUID = GetPixelUID(fpgaID, channel);
402 //check ordering
403 double finalTime = time + (double) ctx.refTime - fParams.fElinkParams.at(fpgaID).fTimeOffset;
404
405 // Do not accept digis, where the MS und TS differs by more than 6 sec (mainly TS0)
406 if (6e9 < finalTime) return;
407
408 ctx.digis.emplace_back(pixelUID, finalTime, tot - ToTcorr);
409 }
410
411 bool UnpackMS::CheckMaskedDiRICH(int32_t subSubEventId) const
412 {
413 for (unsigned int i = 0; i < fMaskedDiRICHes.size(); ++i) {
414 if (fMaskedDiRICHes.at(i) == subSubEventId) return true;
415 }
416 return false;
417 }
418
419 TdcWordType UnpackMS::GetTdcWordType(uint32_t tdcWord) const
420 {
421 uint32_t tdcTimeDataMarker = (tdcWord >> 31) & 0x1; // 1 bit
422 uint32_t tdcMarker = (tdcWord >> 29) & 0x7; // 3 bits
423
424 if (tdcTimeDataMarker == 0x1) {
425 // TODO: I also include tdcMarker == 0x5, some tdc time data words have this marker. Is it correct?
426 if (tdcMarker == 0x4 || tdcMarker == 0x5) {
428 }
429 else {
430 return TdcWordType::Error;
431 }
432 }
433 if (tdcMarker == 0x0) return TdcWordType::Trailer;
434 if (tdcMarker == 0x1) return TdcWordType::Header;
435 if (tdcMarker == 0x2) return TdcWordType::Debug;
436 if (tdcMarker == 0x3) return TdcWordType::Epoch;
437 return TdcWordType::Error;
438 }
439
440 int32_t UnpackMS::GetPixelUID(int32_t fpgaID, int32_t ch) const
441 {
442 // First 16 bits are used for the FPGA ID, then
443 // 8 bits unused and then 8 bits are used for the channel
444 return ((fpgaID << 16) | (ch & 0x00FF));
445 }
446
448 {
449 TdcTimeData out;
450 out.fCoarse = static_cast<uint32_t>(tdcWord & 0x7ff); // 11 bits
451 out.fIsRisingEdge = static_cast<uint32_t>((tdcWord >> 11) & 0x1); // 1 bit
452 out.fFine = static_cast<uint32_t>((tdcWord >> 12) & 0x3ff); // 10 bits
453 out.fChannel = static_cast<uint32_t>((tdcWord >> 22) & 0x7f); // 7 bits
454 return out;
455 }
456
457 uint32_t UnpackMS::ProcessEpoch(uint32_t tdcWord) const { return static_cast<uint32_t>(tdcWord & 0xfffffff); }
458
459 uint16_t UnpackMS::ProcessHeader(uint32_t tdcWord) const
460 {
461 return static_cast<uint16_t>(tdcWord & 0xff); //8 bits
462 }
463
464 uint16_t UnpackMS::ProcessTrailer(uint32_t tdcWord) const { return static_cast<uint16_t>(tdcWord & 0xffff); }
465
466} // namespace cbm::algo::rich
#define L_(level)
std::tuple< std::vector< Digi_t >, Monitor_t, Aux_t > Result_t
void SetData(const uint8_t *data, size_t size)
bool IsLastSubSubEvent(uint32_t subSubEventSize)
double CalculateTime(uint32_t epoch, uint32_t coarse, uint32_t fine) const
bool CheckMaskedDiRICH(int32_t subSubEventId) const
UnpackPar fParams
Parameter container.
uint32_t ProcessEpoch(uint32_t tdcWord) const
UnpackMS(const UnpackPar &pars)
Construct from parameters.
std::vector< int32_t > fMaskedDiRICHes
void ProcessTrbPacket(MicrosliceReader &reader, MSContext &ctx) const
void WriteOutputDigi(int32_t fpgaID, int32_t channel, double time, double tot, MSContext &ctx) const
TdcWordType GetTdcWordType(uint32_t tdcWord) const
int ProcessCtsHeader(MicrosliceReader &reader, uint32_t subSubEventSize, uint32_t subSubEventId) const
Result_t operator()(const uint8_t *msContent, const fles::MicrosliceDescriptor &msDescr, const uint64_t tTimeslice) const override
Algorithm execution.
~UnpackMS() override
Destructor.
void ProcessSubSubEvent(MicrosliceReader &reader, int nofTimeWords, uint32_t subSubEventId, MSContext &ctx) const
int32_t GetPixelUID(int32_t fpgaID, int32_t ch) const
TdcTimeData ProcessTimeData(uint32_t tdcWord) const
void ProcessHubBlock(MicrosliceReader &reader, MSContext &ctx) const
bool ProcessTimeDataWord(uint32_t epoch, uint32_t tdcWord, uint32_t subSubEventId, std::vector< double > &raisingTime, MSContext &ctx) const
uint16_t ProcessTrailer(uint32_t tdcWord) const
uint16_t ProcessHeader(uint32_t tdcWord) const
std::uint32_t u32
Definition Definitions.h:21
std::vector< CbmRichDigi > digis
uint32_t fNumErrWildHeaderMessage
TDC header in invalid position.
uint32_t fNumErrInvalidSecondMessage
Second message is not EPOCH.
uint32_t fNumErrWildEpoch
TDC epoch in invalid position.
uint32_t fNumErrInvalidHubSize
Premature end of hub block.
uint32_t fNumErrTdcErrorWord
TDC word of error type.
uint32_t fNumErrChannelOutOfBounds
TDC channel out of bounds.
uint32_t fNumErrOrphanRecovTimeData
TimeData in last position (not TRAILER) but not creating a Digi.
uint32_t fNumErrExcessLastWords
More than expected trailing words.
uint32_t fNumCtsAndUnmappedDirich
Dirich address is unknown (or is of a CTS)
uint32_t fNumErrInvalidLastMessage
Last message is not TRAILER.
uint32_t fNumSkippedSubsubevent
Whenever subsubevent skipped due to error (but not mask)
uint32_t fNumErrInvalidFirstMessage
First message is not HEADER.
uint32_t fNumErrInvalidHubId
"SubSubEvent" has invalid ID
uint32_t fNumErrWildTdcTime
TDC time (digi) in invalid position.
uint32_t fNumDebugMessage
Received debug messages.
uint32_t fNumWarnRecoveredLastDigi
TimeData in last position (not TRAILER) led to recovered Digi.
uint32_t fNumErrWildTrailerMessage
TDC trailer in invalid position.
Parameters required for the RICH unpacking (specific to one component)
std::map< uint32_t, UnpackElinkPar > fElinkParams
Map TRB address to elink params.