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 if (((subSubEventId >> 12) & 0xF) != 0x7) { // catch invalid ids
107 }
108 if (totalSize == hubSize) {
110 }
111 ProcessSubSubEvent(reader, subSubEventSize, subSubEventId, ctx);
112 ctx.currentSubSubEvent++;
113 }
114 }
115
116 //last event ist expected to be CTS
117 if (totalSize != hubSize || !isLast) {
119 }
120 subSubEventSize = ProcessCtsHeader(reader, subSubEventSize, subSubEventId);
121 ProcessSubSubEvent(reader, subSubEventSize, subSubEventId, ctx);
122
123 // read last words
124 int lastWordsCounter = 0;
125 while (true) {
126 lastWordsCounter++;
127 word = reader.NextWord();
128 if (word == 0x600dda7a) {
129 if (reader.IsNextPadding()) word = reader.NextWord();
130 break;
131 }
132 if (lastWordsCounter >= 7) {
134 }
135 }
136 }
137
138 int UnpackMS::ProcessCtsHeader(rich::MicrosliceReader& reader, uint32_t subSubEventSize,
139 uint32_t /*subSubEventId*/) const
140 {
141 const uint32_t word = reader.NextWord();
142
143 [[maybe_unused]] const uint32_t ctsState = word & 0xffff; // 16 bits
144
145 const uint32_t nofInputs = (word >> 16) & 0xf; // 4 bits
146 const uint32_t nofTrigCh = (word >> 20) & 0x1f; // 5 bits
147 const uint32_t inclLastIdle = (word >> 25) & 0x1; // 1 bit
148 const uint32_t inclTrigInfo = (word >> 26) & 0x1; // 1 bit
149 const uint32_t inclTime = (word >> 27) & 0x1; // 1 bit
150 const uint32_t ETM = (word >> 28) & 0x3; // 2 bits
151
152 uint32_t ctsInfoSize = 2 * nofInputs + 2 * nofTrigCh + 2 * inclLastIdle + 3 * inclTrigInfo + inclTime; // in words
153 switch (ETM) {
154 case 0: break;
155 case 1: ctsInfoSize += 1; break;
156 case 2: ctsInfoSize += 4; break;
157 case 3: break;
158 }
159 for (uint32_t i = 0; i < ctsInfoSize; i++) {
160 reader.NextWord(); // do nothing?
161 }
162 const int nofTimeWords = subSubEventSize - ctsInfoSize - 1;
163 return nofTimeWords;
164 }
165
166 void UnpackMS::ProcessSubSubEvent(rich::MicrosliceReader& reader, int nofTimeWords, uint32_t subSubEventId,
167 MSContext& ctx) const
168 {
169 bool wasHeader = false;
170 bool wasEpoch = false;
171 bool wasTime = false;
172 bool wasTrailer = false;
173
174 uint32_t epoch = 0; // store last epoch obtained in sub-sub-event
175
176 // Store last raising edge time for each channel or -1. if no time
177 // this array is used to match raising and falling edges
178 std::vector<double> raisingTime(33, -1.);
179
180 // check if DiRICH (SubSubEvId) is masked and skip to end if so
181 if (CheckMaskedDiRICH(subSubEventId)) {
182 for (int i = 0; i < nofTimeWords; i++) {
183 reader.NextWord();
184 }
185 return;
186 }
187 // Skip SubSubEvent for CTS and unknown DiRICH addresses
188 // TODO: properly handle/skip CTS subsubevents
189 if (fParams.fElinkParams.end() == fParams.fElinkParams.find(subSubEventId)) {
190 if (0x8000 != (subSubEventId & 0xF000)) {
191 // No log for CTS subsubevents
192 L_(debug) << "Unknown DiRICH ID 0x" << std::hex << subSubEventId << std::dec
193 << ": Stopping processing of this subsubevent!!!";
194 }
197 for (int i = 0; i < nofTimeWords; i++) {
198 reader.NextWord();
199 }
200 return;
201 }
202
203 // Catch Subsubevents where some of the 3 "mandatory words" is missing
204 // but this lack was properly accounted/detected by the TRBnet/CTS
205 // => Otherwise the final readout of the Trailer word offsets the next
206 // subsubevent
207 if (nofTimeWords < 3) {
209 for (int i = 0; i < nofTimeWords; i++) {
210 reader.NextWord();
211 }
212 return;
213 }
214
215
216 // First word is expected to be of type "header"
218 L_(debug) << "DiRICH 0x" << std::hex << subSubEventId << std::dec
219 << ": 1st message is not a TDC Header, skipping subsubevent";
222 for (int i = 1; i < nofTimeWords; i++) {
223 reader.NextWord();
224 }
225 return;
226 }
227 else {
228 wasHeader = true;
229 }
230
231 // Second word is expected to be of type "epoch"
232 uint32_t word = reader.NextWord();
233 if (GetTdcWordType(word) != TdcWordType::Epoch) {
234 L_(debug) << "DiRICH 0x" << std::hex << subSubEventId << std::dec
235 << ": 2nd message is not an epoch, skipping subsubevent";
238 // Currently skip the subsubevent
239 // TODO: Check if possible to only skip to next Epoch and continue from there
240 for (int i = 2; i < nofTimeWords; i++) {
241 reader.NextWord();
242 }
243 return;
244 }
245 else {
246 epoch = ProcessEpoch(word);
247 wasEpoch = true;
248 }
249
250 // Loop over words
251 for (int i = 2; i < nofTimeWords - 1; i++) {
252 word = reader.NextWord();
253 switch (GetTdcWordType(word)) {
255 if (!wasHeader || !wasEpoch || wasTrailer) {
256 L_(debug) << "DiRICH 0x" << std::hex << subSubEventId << std::dec
257 << ": illegal position of TDC Time (before header/epoch or after trailer)";
260 // Currently skip the subsubevent
261 // TODO: Check if possible to only skip to next Epoch and continue from there
262 for (; i < nofTimeWords - 1; i++) {
263 reader.NextWord();
264 }
265 return;
266 }
267 wasTime = true;
268 ProcessTimeDataWord(epoch, word, subSubEventId, raisingTime, ctx);
269 break;
270 }
271 case TdcWordType::Epoch: {
272 if (!wasHeader || wasTrailer) {
273 L_(debug) << "DiRICH 0x" << std::hex << subSubEventId << std::dec
274 << ": illegal position of TDC Epoch (before header or after trailer)";
277 for (; i < nofTimeWords - 1; i++) {
278 reader.NextWord();
279 }
280 return;
281 }
282 wasEpoch = true;
283 epoch = ProcessEpoch(word);
284 break;
285 }
286 case TdcWordType::Header: {
287 if (wasEpoch || wasTime || wasTrailer) {
288 L_(debug) << "DiRICH 0x" << std::hex << subSubEventId << std::dec
289 << ": illegal position of TDC Header (after time/epoch/trailer)";
292 for (; i < nofTimeWords - 1; i++) {
293 reader.NextWord();
294 }
295 return;
296 }
298 break;
299 }
301 if (!wasEpoch || !wasTime || !wasHeader) {
302 L_(debug) << "DiRICH 0x" << std::hex << subSubEventId << std::dec
303 << ": illegal position of TDC Trailer (before time/epoch/header)";
306 for (; i < nofTimeWords - 1; i++) {
307 reader.NextWord();
308 }
309 return;
310 }
311 L_(debug) << "DiRICH 0x" << std::hex << subSubEventId << std::dec << ": TDC Trailer too early, "
312 << (nofTimeWords - 1 - i) << " word(s) before last";
314 wasTrailer = true;
315 break;
316 }
317 case TdcWordType::Debug: {
318 // for the moment do nothing
320 break;
321 }
322 case TdcWordType::Error: {
324 break;
325 }
326 }
327 }
328
329 // Last word is expected to be of type "trailer"
330 word = reader.NextWord();
332 L_(debug) << "DiRICH 0x" << std::hex << subSubEventId << std::dec << ": Last word not a TDC trailer";
334 if (GetTdcWordType(word) == TdcWordType::TimeData && !wasTrailer) {
335 if (ProcessTimeDataWord(epoch, word, subSubEventId, raisingTime, ctx)) {
337 L_(debug) << "DiRICH 0x" << std::hex << subSubEventId << std::dec
338 << ": Rescuing TimeData in subsubevent with missing Trailer leading to saved Digi";
339 }
340 else {
342 L_(debug) << "DiRICH 0x" << std::hex << subSubEventId << std::dec
343 << ": Rescuing TimeData in subsubevent with missing Trailer w/o extra digi";
344 }
345 }
346 }
347 }
348
349 // ---- ProcessTimeDataWord ----
350 bool UnpackMS::ProcessTimeDataWord(uint32_t epoch, uint32_t tdcWord, uint32_t subSubEventId,
351 std::vector<double>& raisingTime, MSContext& ctx) const
352 {
353 const TdcTimeData td = ProcessTimeData(tdcWord);
354 const double fullTime = CalculateTime(epoch, td.fCoarse, td.fFine);
355
356 bool madeDigi = false;
357 if (td.fChannel != 0) {
358 const double dT = fullTime - ctx.prevLastCh0ReTime[ctx.currentSubSubEvent];
359 const double subtrigOffset =
360 (ctx.cbmTimePacket - ctx.cbmTimeMS) * 25.0; // offset of SubTrigger to MS start in ns
361 const double fullTimeCorr = dT - ctx.mbsCorr + subtrigOffset;
362
363 if (td.fChannel < 1 || td.fChannel >= raisingTime.size()) {
365 return false;
366 }
367 if (td.fIsRisingEdge) {
368 // always store the latest raising edge. It means that in case RRFF situation only middle RF will be matched.
369 raisingTime[td.fChannel] = fullTimeCorr;
370 }
371 else {
372 if (raisingTime[td.fChannel] != -1.) {
373 // Matching was found, calculate ToT, if tot is in a good range -> create digi
374 const double ToT = fullTimeCorr - raisingTime[td.fChannel];
375 if (ToT >= fToTMin && ToT <= fToTMax) {
376 if (fullTimeCorr >= 0.0) {
377 WriteOutputDigi(subSubEventId, td.fChannel, raisingTime[td.fChannel], ToT, ctx);
378 madeDigi = true;
379 }
380 }
381 // pair was created, set raising edge to -1.
382 raisingTime[td.fChannel] = -1.;
383 }
384 }
385 }
386 else {
388 }
389 return madeDigi;
390 }
391
392 double UnpackMS::CalculateTime(uint32_t epoch, uint32_t coarse, uint32_t fine) const
393 {
394 return ((double) epoch) * 2048. * 5. + ((double) coarse) * 5. - ((double) fine) * 0.005;
395 }
396
397 void UnpackMS::WriteOutputDigi(int32_t fpgaID, int32_t channel, double time, double tot, MSContext& ctx) const
398 {
399 double ToTcorr = fbDoToTCorr ? fParams.fElinkParams.at(fpgaID).fToTshift[channel] : 0.;
400 int32_t pixelUID = GetPixelUID(fpgaID, channel);
401 //check ordering
402 double finalTime = time + (double) ctx.refTime - fParams.fElinkParams.at(fpgaID).fTimeOffset;
403
404 // Do not accept digis, where the MS und TS differs by more than 6 sec (mainly TS0)
405 if (6e9 < finalTime) return;
406
407 ctx.digis.emplace_back(pixelUID, finalTime, tot - ToTcorr);
408 }
409
410 bool UnpackMS::CheckMaskedDiRICH(int32_t subSubEventId) const
411 {
412 for (unsigned int i = 0; i < fMaskedDiRICHes.size(); ++i) {
413 if (fMaskedDiRICHes.at(i) == subSubEventId) return true;
414 }
415 return false;
416 }
417
418 TdcWordType UnpackMS::GetTdcWordType(uint32_t tdcWord) const
419 {
420 uint32_t tdcTimeDataMarker = (tdcWord >> 31) & 0x1; // 1 bit
421 uint32_t tdcMarker = (tdcWord >> 29) & 0x7; // 3 bits
422
423 if (tdcTimeDataMarker == 0x1) {
424 // TODO: I also include tdcMarker == 0x5, some tdc time data words have this marker. Is it correct?
425 if (tdcMarker == 0x4 || tdcMarker == 0x5) {
427 }
428 else {
429 return TdcWordType::Error;
430 }
431 }
432 if (tdcMarker == 0x0) return TdcWordType::Trailer;
433 if (tdcMarker == 0x1) return TdcWordType::Header;
434 if (tdcMarker == 0x2) return TdcWordType::Debug;
435 if (tdcMarker == 0x3) return TdcWordType::Epoch;
436 return TdcWordType::Error;
437 }
438
439 int32_t UnpackMS::GetPixelUID(int32_t fpgaID, int32_t ch) const
440 {
441 // First 16 bits are used for the FPGA ID, then
442 // 8 bits unused and then 8 bits are used for the channel
443 return ((fpgaID << 16) | (ch & 0x00FF));
444 }
445
447 {
448 TdcTimeData out;
449 out.fCoarse = static_cast<uint32_t>(tdcWord & 0x7ff); // 11 bits
450 out.fIsRisingEdge = static_cast<uint32_t>((tdcWord >> 11) & 0x1); // 1 bit
451 out.fFine = static_cast<uint32_t>((tdcWord >> 12) & 0x3ff); // 10 bits
452 out.fChannel = static_cast<uint32_t>((tdcWord >> 22) & 0x7f); // 7 bits
453 return out;
454 }
455
456 uint32_t UnpackMS::ProcessEpoch(uint32_t tdcWord) const { return static_cast<uint32_t>(tdcWord & 0xfffffff); }
457
458 uint16_t UnpackMS::ProcessHeader(uint32_t tdcWord) const
459 {
460 return static_cast<uint16_t>(tdcWord & 0xff); //8 bits
461 }
462
463 uint16_t UnpackMS::ProcessTrailer(uint32_t tdcWord) const { return static_cast<uint16_t>(tdcWord & 0xffff); }
464
465} // 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.