1 #ifndef ALIMUONTRACKERDDLDECODER_H
2 #define ALIMUONTRACKERDDLDECODER_H
3 /**************************************************************************
4 * This file is property of and copyright by the ALICE HLT Project *
5 * All rights reserved. *
8 * Artur Szostak <artursz@iafrica.com> *
10 * Permission to use, copy, modify and distribute this software and its *
11 * documentation strictly for non-commercial purposes is hereby granted *
12 * without fee, provided that the above copyright notice appears in all *
13 * copies and that both the copyright notice and this permission notice *
14 * appear in the supporting documentation. The authors make no claims *
15 * about the suitability of this software for any purpose. It is *
16 * provided "as is" without express or implied warranty. *
17 **************************************************************************/
22 /// \file AliMUONTrackerDDLDecoder.h
23 /// \author Artur Szostak <artursz@iafrica.com>
25 /// \brief Implementation of a high performance DDL decoder for the muon tracking stations.
27 /// This file implementes the AliMUONTrackerDDLDecoder class, which contains
28 /// the core logic for decoding the payload in DDL streams coming from the muon
29 /// spectrometer's tracking chambers in a very efficient manner.
31 /// This implementation is derived from work done by Christian Finck for the
32 /// AliMUONPayloadTracker.
34 /// Note to maintainers: Please remember that this file is used by the online
35 /// dHLT system. As an online system, the dHLT requires the fastest code possible
36 /// in the decoders to satisfy its timing constraints. The performance impact
37 /// must be checked before any proposed modification is made to this file.
40 #include "AliMUONTrackerDDLDecoderEventHandler.h"
43 /// \class AliMUONTrackerDDLDecoder
44 /// \brief A high performance decoder class for MUON tracking DDL data.
46 /// This class implements a high performance decoder for decoding DDL payload
47 /// data coming from the muon spectrometers tracking chambers.
48 /// It has been implemented using the event driven paradigm with templates,
49 /// which allows us to minimise the number of method calls made in the inner
50 /// loops of the algorithm and minimise the memory footprint. At least for
51 /// optimised production compilations.
52 /// The decoder class only contains the basic decoding and error checking logic.
53 /// It calls methods such as OnNewBlock, OnNewBusPatch, OnData etc in
54 /// the event handler during the decoding to return the decoded data.
55 /// The event handler class is nothing more than a callback interface to deliver
56 /// the next chunks of decoded data.
57 /// To actually do something with the data, one needs to implement a custom
58 /// event handler (callback) class by inheriting from AliMUONTrackerDDLDecoderEventHandler
59 /// and overriding the callback methods like so:
61 /// class MyCustomHandler : public AliMUONTrackerDDLDecoderEventHandler
64 /// void OnData(UInt_t data, bool parityError)
66 /// // I can do something with 'data' here and check if there was
67 /// // a parity error with 'parityError'.
72 /// Once the custom handler is written then the decoder is instantiated as
73 /// shown below, to use your new custom handler. Also to start decoding one needs
74 /// to call the Decode() method of the decoder.
76 /// AliMUONTrackerDDLDecoder<MyCustomHandler> myDecoder;
77 /// muDecoder.Decoder(buffer, bufferSize);
80 /// Note that this class was written as a template on purpose. To maximise the
81 /// compilers chance to make optimisations and inline the code we must use a template.
82 /// Depending on exactly what you do inside your handler, the decoder could be
83 /// significantly slower if run time polymorphism was used, i.e. making the class
84 /// AliMUONTrackerDDLDecoderEventHandler abstract and using virtual methods.
86 /// There has been a change to the data format that the real detector generates.
87 /// Two trailer words are added to the end of the DDL payload which indicated
88 /// the end of data. The decoder is initialised by default to automatically
89 /// check for these and deal with it correctly, if they exist or not.
90 /// However, if you want to override this behaviour then set the flag
91 /// fAutoDetectTrailer to false with AutoDetectTrailer(false). Then if you have
92 /// data with the old data format you should set fCheckForTrailer to false with
93 /// CheckForTrailer(false), otherwise for real data it should be
94 /// fCheckForTrailer = true. Only when fAutoDetectTrailer is true will the
95 /// fCheckForTrailer flag be ignored and no warnings will be generated for an
96 /// incorrect data format.
98 /// \author Artur Szostak <artursz@iafrica.com>
100 template <class EventHandler>
101 class AliMUONTrackerDDLDecoder
105 /// Default contructor.
106 AliMUONTrackerDDLDecoder() :
107 fExitOnError(true), fTryRecover(false),
108 fSendDataOnParityError(false), fHadError(false),
109 fAutoDetectTrailer(true), fCheckForTrailer(true),
110 fMaxBlocks(2), fMaxDSPs(5), fMaxBusPatches(5), fHandler()
113 /// Constant method to return the event handler instance.
114 const EventHandler& GetHandler() const { return fHandler; }
116 /// Returns the event handler instance.
117 EventHandler& GetHandler() { return fHandler; }
119 /// Returns the "exit on error" flag.
120 /// i.e. should the decoder stop on the very first error found.
121 bool ExitOnError() const { return fExitOnError; }
123 /// Sets the "exit on error" flag.
124 /// i.e. should the decoder stop on the very first error found.
125 void ExitOnError(bool value) { fExitOnError = value; }
127 /// Returns the "try to recover from errors" flag.
128 /// i.e. should the decoder try to recover from errors found in the
129 /// payload headers or trailers.
130 bool TryRecover() const { return fTryRecover; }
132 /// Sets the "try to recover from errors" flag.
133 /// i.e. should the decoder try to recover from errors found in the
134 /// payload headers or trailers.
135 void TryRecover(bool value) { fTryRecover = value; }
137 /// Returns the flag indicating if the raw data words in the bus patches
138 /// that failed their parity tests (i.e. parity error / bit flip in the
139 /// raw data word) will be sent to the event handler anyway through OnData.
140 bool SendDataOnParityError() const { return fSendDataOnParityError; }
142 /// Sets the flag indicating if the raw data words in the bus patches
143 /// that failed their parity tests (i.e. parity error / bit flip in the
144 /// raw data word) will be sent to the event handler anyway through OnData.
145 void SendDataOnParityError(bool value) { fSendDataOnParityError = value; }
147 /// Returns the maximum block count expected in the DDL payload.
148 UInt_t MaxBlocks() const { return fMaxBlocks; }
150 /// Sets the maximum block count expected in the DDL payload.
151 void MaxBlocks(UInt_t n) { fMaxBlocks = n; }
153 /// Returns the maximum DSP header count expected in any given block
154 /// structure within the DDL payload.
155 UInt_t MaxDSPs() const { return fMaxDSPs; }
157 /// Sets the maximum DSP header count expected in any given block structure
158 /// within the DDL payload.
159 void MaxDSPs(UInt_t n) { fMaxDSPs = n; }
161 /// Returns the maximum number of bus patches expected in any given DSP
162 /// structure within the DDL payload.
163 UInt_t MaxBusPatches() const { return fMaxBusPatches; }
165 /// Sets the maximum number of bus patches expected in any given DSP
166 /// structure within the DDL payload.
167 void MaxBusPatches(UInt_t n) { fMaxBusPatches = n; }
169 /// Returns the value of the auto-detect trailer flag.
170 bool AutoDetectTrailer() const { return fAutoDetectTrailer; }
172 /// Sets the value of the auto-detect trailer flag.
173 void AutoDetectTrailer(bool value) { fAutoDetectTrailer = value; }
175 /// Returns the value of the flag to check for the end of DDL trailer.
176 bool CheckForTrailer() const { return fCheckForTrailer; }
178 /// Sets the value of the flag to check for the end of DDL trailer.
179 void CheckForTrailer(bool value) { fCheckForTrailer = value; }
181 /// This method decodes the DDL payload contained in the buffer.
182 bool Decode(const void* buffer, UInt_t bufferSize);
184 /// Returns the block marker key.
185 static UInt_t BlockDataKeyWord() { return fgkBlockDataKey; }
187 /// Returns the DSP marker key.
188 static UInt_t DspDataKeyWord() { return fgkDSPDataKey; }
190 /// Returns the bus patch marker key.
191 static UInt_t BusPatchDataKeyWord() { return fgkBusPatchDataKey; }
193 /// Returns the expected padding word value.
194 static UInt_t PaddingWord() { return fgkPaddingWord; }
196 /// Returns the expected end of DDL marker.
197 static UInt_t EndOfDDLWord() { return fgkEndOfDDL; }
201 bool fExitOnError; ///< Indicates if we should exit on the very first error.
202 bool fTryRecover; ///< Indicates if we should try recover from a corrupt structure header or DDL trailer.
203 bool fSendDataOnParityError; ///< If set to true then we issue a OnData() event even if the data word had a parity error.
204 bool fHadError; ///< Indicates if we had an error decoding the data.
205 bool fAutoDetectTrailer; ///< Indicates if we should automatically check for the end of DDL trailer (Default = true).
206 bool fCheckForTrailer; ///< Indicates if we should check for the end of DDL trailer (Default = true). This flag is ignored if fAutoDetectTrailer is true.
207 UInt_t fMaxBlocks; ///< Maximum number of block structures allowed in a DDL stream.
208 UInt_t fMaxDSPs; ///< Maximum number of DSP structures allowed in a DDL stream.
209 UInt_t fMaxBusPatches; ///< Maximum number of bus patch structures allowed in a DDL stream.
210 EventHandler fHandler; ///< The event handler which deals with parsing events.
212 void DecodeBuffer(const UChar_t* start, const UChar_t* end);
214 bool DecodeBlockData(
215 const AliMUONBlockHeaderStruct* blockHeader,
216 const UChar_t* start, const UChar_t* end
219 bool DecodeDSPData(const UChar_t* start, const UChar_t* end);
221 bool DecodeBusPatchData(const UChar_t* start, const UChar_t* end);
223 /// Possible results that can be returned by the TryRecoverStruct method.
226 kRecoverFailed, ///< The recovery failed. Cannot continue parsing.
227 kStructRecovered, ///< Indicates that we recovered from a corrupt structure header and can continue processing the given structure.
228 kContinueToNextStruct ///< Must continue parsing the next structure and ignore the current one.
231 RecoverResult TryRecoverStruct(
236 const UChar_t* structStart,
237 const UChar_t* bufferEnd,
238 const UChar_t*& dataEnd,
239 const UChar_t*& structEnd,
240 const UChar_t*& current
243 const UChar_t* FindKey(
244 UInt_t key, const UChar_t* start, const UChar_t* end
247 bool ParityIsOk(UInt_t data);
249 static const UInt_t fgkBlockDataKey; ///< The key word expected to identify block structure headers.
250 static const UInt_t fgkDSPDataKey; ///< The key word expected to identify DSP structure headers.
251 static const UInt_t fgkBusPatchDataKey; ///< The key word expected to identify bus patch headers.
252 static const UInt_t fgkPaddingWord; ///< The expected format of the padding word in the DDL payload.
253 static const UInt_t fgkEndOfDDL; ///< The end of DDL trailer word.
256 //_____________________________________________________________________________
258 // The following are the structure header keys which are used to identify the kind
259 // of structure header we are dealing with: block, DSP or bus patch header.
260 template <class EventHandler>
261 const UInt_t AliMUONTrackerDDLDecoder<EventHandler>::fgkBlockDataKey = 0xFC0000FC;
262 template <class EventHandler>
263 const UInt_t AliMUONTrackerDDLDecoder<EventHandler>::fgkDSPDataKey = 0xF000000F;
264 template <class EventHandler>
265 const UInt_t AliMUONTrackerDDLDecoder<EventHandler>::fgkBusPatchDataKey = 0xB000000B;
266 template <class EventHandler>
267 const UInt_t AliMUONTrackerDDLDecoder<EventHandler>::fgkPaddingWord = 0xBEEFFACE;
268 template <class EventHandler>
269 const UInt_t AliMUONTrackerDDLDecoder<EventHandler>::fgkEndOfDDL = 0xD000000D;
272 template <class EventHandler>
273 bool AliMUONTrackerDDLDecoder<EventHandler>::Decode(const void* buffer, UInt_t bufferSize)
275 /// This method should be called to actually decode the DDL payload
276 /// contained in a memory buffer. The payload should be for a muon tracking
277 /// chamber DDL stream.
278 /// As the decoder progresses it will make method calls to the event handler
279 /// instance (which can be accessed with the GetHandler() method) to indicate
280 /// the start of the new block, DSP and bus patch headers. For every raw
281 /// data word the OnData method of the event handler is called.
283 /// If an error occurs during the parse because the data is corrupt then
284 /// the OnError method is called indicating what the problem was.
285 /// Decoding will stop at this point unless the fExitOnError flag is set
286 /// to false. Also raw data words which contain a parity error are only
287 /// sent to the event handler with OnData if the fSendDataOnParityError
288 /// flag is set to true. There is also an optional flag fTryRecover which
289 /// can enable logic which will attempt to recover the header structures found
290 /// in the DDL payload if they are found to be inconsistent (assumed corrupt).
291 /// fTryRecover set to true will also enable recovery from a corrupt
292 /// DDL trailer marking the end of DDL payload.
294 /// \param buffer This is the pointer to the start of the memory buffer
295 /// containing the DDL payload. Remember that this must be the start of
296 /// the payload and not the DDL stream. That is, this pointer should be
297 /// equal to: DDL start pointer + 8 * sizeof(UInt_t).
298 /// \param bufferSize This is the pointer to the first byte just past the
299 /// end of the block structure.
300 /// \return Returns false if there was any problem with decoding the data,
301 /// and true otherwise. Note: the data may have been partially decoded
302 /// even if false was returned, which would be indicated by at least one
303 /// call to the event handlers OnData method.
305 assert( buffer != NULL );
309 // We are basically implementing something like a recursive decent parser.
310 // So start by marking the start of buffer position and end of buffer.
311 const UChar_t* start = reinterpret_cast<const UChar_t*>(buffer);
312 const UChar_t* end = start + bufferSize;
314 fHandler.OnNewBuffer(buffer, bufferSize);
315 DecodeBuffer(start, end);
316 fHandler.OnEndOfBuffer(buffer, bufferSize);
317 return not fHadError;
321 template <class EventHandler>
322 void AliMUONTrackerDDLDecoder<EventHandler>::DecodeBuffer(
323 const UChar_t* start, const UChar_t* end
326 /// This method decodes the buffer's payload data. It unpacks the block
327 /// structures contained inside and then for each block it calls the
328 /// OnNewBlock method for the event handler to signal the start of each new
329 /// block structure. OnEndOfBlock is called once each block is processed.
330 /// \param start This is the pointer to the start of the buffer.
331 /// \param end This is the pointer to the first byte just past the
332 /// end of the buffer.
333 /// fHadError is set to true if there were any errors decoding the buffer
334 /// and the OnError method of the callback event handler is called for
337 const UChar_t* current = start;
338 const UInt_t* bufferStart = reinterpret_cast<const UInt_t*>(start);
339 const UInt_t* bufferEnd = reinterpret_cast<const UInt_t*>(end);
340 bool problemWithTrailer = false;
342 // The DDL payload normally has a 2 word trailer which contains the end of
343 // DDL markers 0xD000000D. But this is not the case for older simulated
344 // data so if we are auto-detecting the trailer then we need to carefully
345 // check if these words are there or not.
346 const UChar_t* endOfBlocks = end;
347 const UInt_t* trailerWords = reinterpret_cast<const UInt_t*>(end) - 2;
348 if (fAutoDetectTrailer)
350 if (trailerWords >= bufferStart and *trailerWords == fgkEndOfDDL
351 and *(trailerWords+1) == fgkEndOfDDL
354 // Found the trailer so reposition the end of blocks marker.
355 endOfBlocks = reinterpret_cast<const UChar_t*>(trailerWords);
357 // else assume we are dealing with the older data format.
359 else if (fCheckForTrailer)
361 if (trailerWords >= bufferStart and *trailerWords == fgkEndOfDDL
362 and *(trailerWords+1) == fgkEndOfDDL
365 // Found the trailer so reposition the end of blocks marker.
366 endOfBlocks = reinterpret_cast<const UChar_t*>(trailerWords);
370 if (trailerWords+1 >= bufferStart and *(trailerWords+1) == fgkEndOfDDL)
371 fHandler.OnError(EventHandler::kTooFewDDLTrailerWords, trailerWords+1);
372 else if (trailerWords >= bufferStart and *(trailerWords) == fgkEndOfDDL)
373 fHandler.OnError(EventHandler::kTooFewDDLTrailerWords, trailerWords);
375 fHandler.OnError(EventHandler::kNoDDLTrailerWords, end);
377 // Stop the decoding if so requested by the user, otherwise
378 // remember about the error so that we return false from the
379 // Decode() method and continue decoding.
381 if (fExitOnError) return;
383 // Mark that there was a problem with the trailer so that
384 // for subsequent errors we try to deal with this better.
385 problemWithTrailer = true;
387 // We can also try figure out how many trailer words there
388 // actually are and move the end of blocks marker back.
392 trailerWords = bufferEnd;
393 // There should only be a max of 2 trailer words.
394 if (*(trailerWords-1) == fgkEndOfDDL)
396 else if (*(trailerWords-1) == fgkEndOfDDL)
398 endOfBlocks = reinterpret_cast<const UChar_t*>(trailerWords);
403 UInt_t blockCount = 0; // Indicates the number of blocks decoded.
404 while (current < endOfBlocks)
406 // Mark the start of the block structure.
407 const UChar_t* blockStart = current;
409 // Get the block header, move the current pointer just past the end
410 // of the header and check that we have not overflowed the buffer.
411 const AliMUONBlockHeaderStruct* blockHeader
412 = reinterpret_cast<const AliMUONBlockHeaderStruct*>(blockStart);
413 current += sizeof(AliMUONBlockHeaderStruct);
414 if (current > endOfBlocks)
416 // We first check if we actually hit the end of DDL markers
417 // If we did then either we did not/could not recover from
418 // a corrupt trailer or we did not detect a correct trailer
419 // in auto-detect mode.
420 trailerWords = reinterpret_cast<const UInt_t*>(blockHeader);
421 // The "trailerWords+1 <= bufferEnd" checks that we are
422 // not reading beyond the end of the buffer.
423 if (trailerWords+1 <= bufferEnd and *trailerWords == fgkEndOfDDL)
425 // If we aready knew the trailer was corrupt then just
426 // return because the error was already announced.
427 if (problemWithTrailer) return;
429 if (fAutoDetectTrailer)
431 // If we got here then there is at least one correct trailer
432 // word, but since we did not detect a correct trailer then
433 // there must be only one. Announce the error and exit.
434 fHandler.OnError(EventHandler::kTooFewDDLTrailerWords, trailerWords);
440 // So we only got part of a block header at the very end
441 // of the buffer. Nothing to do but report the error and exit.
442 if (blockCount == fMaxBlocks)
443 // Special case where we got all the blocks we
444 // expected, so the remaining data must be rubbish.
445 fHandler.OnError(EventHandler::kBufferTooBig, blockHeader);
447 fHandler.OnError(EventHandler::kNoBlockHeader, blockHeader);
452 // The header fits the buffer so we can mark the data start and
453 // read from the header to find the end of data and block pointers.
454 const UChar_t* dataStart = current;
455 current += blockHeader->fLength * sizeof(UInt_t);
456 const UChar_t* dataEnd = current;
457 const UChar_t* blockEnd = blockStart
458 + blockHeader->fTotalLength * sizeof(UInt_t);
460 // Now we need to check for the following things:
461 // 1) Is the end of block or end of data pointer outside the buffer
463 // 2) Are the values for these pointers the same.
464 // 3) Is the expected data key in the header present.
465 // If any of the above fail then we know there is a problem with
466 // the block header. It must be corrupted somehow.
467 if (blockHeader->fDataKey != fgkBlockDataKey
468 or dataEnd > endOfBlocks or blockEnd > endOfBlocks or dataEnd != blockEnd)
470 // So let us see what exactly is wrong and report this.
471 if (blockCount == fMaxBlocks)
473 // Special case where we got all the blocks we
474 // expected, so the remaining data must be rubbish.
475 // Don't even bother trying to recover the data.
476 fHandler.OnError(EventHandler::kBufferTooBig, blockHeader);
480 if (blockHeader->fDataKey != fgkBlockDataKey)
481 fHandler.OnError(EventHandler::kBadBlockKey, &blockHeader->fDataKey);
482 if (blockEnd > endOfBlocks)
483 fHandler.OnError(EventHandler::kBadBlockLength, &blockHeader->fLength);
484 if (dataEnd > endOfBlocks)
485 fHandler.OnError(EventHandler::kBadBlockTotalLength, &blockHeader->fTotalLength);
486 if (dataEnd != blockEnd)
487 fHandler.OnError(EventHandler::kBlockLengthMismatch, blockHeader);
489 // Stop the decoding if so requested by the user, otherwise
490 // remember about the error so that we return false from the
491 // Decode() method and continue decoding.
493 if (fExitOnError) return;
495 // Try to recover from the corrupt header.
496 RecoverResult result = TryRecoverStruct(
497 fgkBlockDataKey, sizeof(AliMUONBlockHeaderStruct),
498 blockHeader->fTotalLength, blockHeader->fLength,
499 blockStart, endOfBlocks, dataEnd, blockEnd, current
501 if (result == kContinueToNextStruct)
502 continue; // Try the next block at 'current'.
503 if (result == kRecoverFailed) return;
506 // At this point we certainly have a valid block header, so we
507 // need to check if we have more blocks than we expected. If not
508 // then we can indicate we have another block and decode its data.
509 if (++blockCount > fMaxBlocks)
511 fHandler.OnError(EventHandler::kTooManyBlocks, current);
513 // In this case we stop the decoding because clearly
514 // something is seriously wrong with the data if we are
515 // getting more blocks than expected.
520 fHandler.OnNewBlock(blockHeader, dataStart);
521 if (not DecodeBlockData(blockHeader, dataStart, dataEnd))
523 // At this point we had a problem decoding the block structure's
524 // data. Thus we should stop further decoding if so requested by
525 // the user. Note the fHadError flag is already marked inside
529 fHandler.OnEndOfBlock(blockHeader, dataStart);
533 fHandler.OnEndOfBlock(blockHeader, dataStart);
538 template <class EventHandler>
539 bool AliMUONTrackerDDLDecoder<EventHandler>::DecodeBlockData(
540 const AliMUONBlockHeaderStruct* blockHeader,
541 const UChar_t* start, const UChar_t* end
544 /// This method decodes a block structure's data payload. It unpacks the
545 /// DSP structures contained inside and then for each DSP it calls the
546 /// OnNewDSP method for the event handler to signal the start of each new
548 /// \param blockHeader
549 /// \param start This is the pointer to the start of the block
550 /// structure's data.
551 /// \param end This is the pointer to the first byte just past the
552 /// end of the block structure.
553 /// \return If the block structure's data was decoded without errors
554 /// or we could recover from the errors, then true is returned.
555 /// False is returned otherwise.
557 const UChar_t* current = start;
559 UInt_t dspCount = 0; // Indicates the number of DSPs decoded.
560 while (current < end)
562 // Mark the start of the DSP structure.
563 const UChar_t* dspStart = current;
565 // Get the DSP header, move the current pointer just past the end
566 // of the header and check that we have not overflowed the buffer.
567 const AliMUONDSPHeaderStruct* dspHeader
568 = reinterpret_cast<const AliMUONDSPHeaderStruct*>(dspStart);
569 current += sizeof(AliMUONDSPHeaderStruct);
572 // So we only got part of a DSP header at the very end of
573 // the block structure buffer. Nothing to do but report the
574 // error and exit. Set fHadError in case of further decoding.
575 fHandler.OnError(EventHandler::kNoDSPHeader, dspHeader);
580 // The header fits the buffer so we can mark the data start and
581 // read from the header to find the end of data and DSP pointers.
582 const UChar_t* dataStart = current;
583 current += dspHeader->fLength * sizeof(UInt_t);
584 const UChar_t* dataEnd = current;
585 const UChar_t* dspEnd = dspStart + dspHeader->fTotalLength * sizeof(UInt_t);
587 // Now we need to check for the following things:
588 // 1) Is the end of DSP or end of data pointer outside the buffer
590 // 2) Are the values for these pointers the same.
591 // 3) Is the expected data key in the header present.
592 // If any of the above fail then we know there is a problem with
593 // the DSP header. It must be corrupted somehow.
594 if (dspHeader->fDataKey != fgkDSPDataKey
595 or dataEnd > end or dspEnd > end or dataEnd != dspEnd)
597 // So let us see what exactly is wrong and report this.
598 if (dspHeader->fDataKey != fgkDSPDataKey)
599 fHandler.OnError(EventHandler::kBadDSPKey, &dspHeader->fDataKey);
601 fHandler.OnError(EventHandler::kBadDSPLength, &dspHeader->fLength);
603 fHandler.OnError(EventHandler::kBadDSPTotalLength, &dspHeader->fTotalLength);
604 if (dataEnd != dspEnd)
605 fHandler.OnError(EventHandler::kDSPLengthMismatch, dspHeader);
607 // Indicate we had and error and stop the decoding if so
608 // requested by the user.
610 if (fExitOnError) return false;
612 // Try to recover from the corrupt header.
613 RecoverResult result = TryRecoverStruct(
614 fgkDSPDataKey, sizeof(AliMUONDSPHeaderStruct),
615 dspHeader->fTotalLength, dspHeader->fLength,
616 dspStart, end, dataEnd, dspEnd, current
618 if (result == kContinueToNextStruct)
619 continue; // Try the next DSP at 'current'.
620 if (result == kRecoverFailed) return false;
623 // At this point we certainly have a valid DSP header, so we
624 // need to check if we have more DSPs than we expected. If not
625 // then we can indicate we have another DSP and decode its data.
626 if (++dspCount > fMaxDSPs)
628 fHandler.OnError(EventHandler::kTooManyDSPs, current);
630 // In this case we stop further decoding of the block
631 // structure data because clearly something is seriously
632 // wrong if we are getting more DSPs than expected.
633 // Indicate that we had an error so the Decode() method
639 fHandler.OnNewDSP(dspHeader, dataStart);
641 // Check the error word in the header.
642 if (dspHeader->fErrorWord == (0x000000B1 | blockHeader->fDSPId)
643 or dspHeader->fErrorWord == (0x00000091 | blockHeader->fDSPId)
646 // An event with a glitch in the readout has been detected.
647 // It means that somewhere a 1 byte word has been randomly
648 // inserted and all the readout sequence is shifted until
650 fHandler.OnError(EventHandler::kGlitchFound, &dspHeader->fErrorWord);
654 fHandler.OnEndOfDSP(dspHeader, dataStart);
658 // Try recover by finding the very next DSP and continue
659 // decoding from there. Note: to achieve all we have to do
660 // is continue to the next iteration, because the logic
661 // will land up calling the FindKey method within the
662 // TryRecoverStruct method above.
663 if (fTryRecover) continue;
666 // Check if we are padding. If we are, then the bus patch data is
667 // actually 4 bytes smaller and the last word is a padding word.
668 if (dspHeader->fPaddingWord == 1)
670 dataEnd -= sizeof(UInt_t);
672 // Check the pad word is correct.
673 const UInt_t* padWord = reinterpret_cast<const UInt_t*>(dataEnd);
674 if (*padWord != fgkPaddingWord)
676 fHandler.OnError(EventHandler::kBadPaddingWord, padWord);
680 fHandler.OnEndOfDSP(dspHeader, dataStart);
686 if (not DecodeDSPData(dataStart, dataEnd))
688 // At this point we had a problem decoding the DSP structure's
689 // data, thus we should stop further decoding if so requested by
690 // the user. Note the fHadError flag is already marked inside
694 fHandler.OnEndOfDSP(dspHeader, dataStart);
698 fHandler.OnEndOfDSP(dspHeader, dataStart);
705 template <class EventHandler>
706 bool AliMUONTrackerDDLDecoder<EventHandler>::DecodeDSPData(
707 const UChar_t* start, const UChar_t* end
710 /// This method decodes a DSP structure's data payload. It finds all the
711 /// bus patches found inside and for each it calls the OnNewBusPatch method
712 /// for the event handler to signal the start of each new bus patch.
713 /// \param start This is the pointer to the start of the DSP structure's data.
714 /// \param end This is the pointer to the first byte just past the
715 /// end of the DSP structure.
716 /// \return If the DSP structure's data was decoded without errors
717 /// or we could recover from the errors, then true is returned.
718 /// False is returned otherwise.
720 const UChar_t* current = start;
722 UInt_t busPatchCount = 0; // Indicates the number of bus patches decoded.
723 while (current < end)
725 // Mark the start of the bus patch structure.
726 const UChar_t* busPatchStart = current;
728 // Get the bus patch header, move the current pointer just past
729 // the end of the header and check that we have not overflowed
731 const AliMUONBusPatchHeaderStruct* busPatchHeader
732 = reinterpret_cast<const AliMUONBusPatchHeaderStruct*>(busPatchStart);
733 current += sizeof(AliMUONBusPatchHeaderStruct);
736 // So we only got part of a bus patch header at the very
737 // end of the DSP structure buffer. Nothing to do but
738 // report the error and exit. Set fHadError in case of
740 fHandler.OnError(EventHandler::kNoBusPatchHeader, busPatchHeader);
745 // The header fits the buffer so we can mark the data start and
746 // read from the header to find the end of data and bus patch
747 // structure pointers.
748 const UChar_t* dataStart = current;
749 current += busPatchHeader->fLength * sizeof(UInt_t);
750 const UChar_t* dataEnd = current;
751 const UChar_t* busPatchEnd = busPatchStart
752 + busPatchHeader->fTotalLength * sizeof(UInt_t);
754 // Now we need to check for the following things:
755 // 1) Is the end of bus patch structure or end of data pointer
756 // outside the buffer boundaries.
757 // 2) Are the values for these pointers the same.
758 // 3) Is the expected data key in the header present.
759 // If any of the above fail then we know there is a problem with
760 // the bus patch header. It must be corrupted somehow.
761 if (busPatchHeader->fDataKey != fgkBusPatchDataKey
762 or dataEnd > end or busPatchEnd > end or dataEnd != busPatchEnd)
764 // So let us see what exactly is wrong and report this.
765 if (busPatchHeader->fDataKey != fgkBusPatchDataKey)
766 fHandler.OnError(EventHandler::kBadBusPatchKey, &busPatchHeader->fDataKey);
767 if (busPatchEnd > end)
768 fHandler.OnError(EventHandler::kBadBusPatchLength, &busPatchHeader->fLength);
770 fHandler.OnError(EventHandler::kBadBusPatchTotalLength, &busPatchHeader->fTotalLength);
771 if (dataEnd != busPatchEnd)
772 fHandler.OnError(EventHandler::kBusPatchLengthMismatch, busPatchHeader);
774 // Indicate we had and error and stop the decoding if so
775 // requested by the user.
777 if (fExitOnError) return false;
779 // Try to recover from the corrupt header.
780 RecoverResult result = TryRecoverStruct(
781 fgkBusPatchDataKey, sizeof(AliMUONBusPatchHeaderStruct),
782 busPatchHeader->fTotalLength, busPatchHeader->fLength,
783 busPatchStart, end, dataEnd, busPatchEnd, current
785 if (result == kContinueToNextStruct)
786 continue; // Try the next bus patch at 'current'.
787 if (result == kRecoverFailed) return false;
790 // At this point we certainly have a valid bus patch header, so
791 // we need to check if we have more bus patches than we expected.
792 // If not then we can indicate we have another bus patch and
794 if (++busPatchCount > fMaxBusPatches)
796 fHandler.OnError(EventHandler::kTooManyBusPatches, current);
798 // In this case we stop further decoding of the DSP
799 // structure's data because clearly something is seriously
800 // wrong if we are getting more bus patches than expected.
801 // Indicate that we had an error so the Decode() method
807 fHandler.OnNewBusPatch(busPatchHeader, dataStart);
808 if (not DecodeBusPatchData(dataStart, dataEnd))
810 // At this point we had a problem decoding the bus patch data,
811 // thus we should stop further decoding if so requested by the
812 // user. Note the fHadError flag is already marked inside
813 // DecodeBusPatchData.
816 fHandler.OnEndOfBusPatch(busPatchHeader, dataStart);
820 fHandler.OnEndOfBusPatch(busPatchHeader, dataStart);
827 template <class EventHandler>
828 bool AliMUONTrackerDDLDecoder<EventHandler>::DecodeBusPatchData(
829 const UChar_t* start, const UChar_t* end
832 /// This method decodes a single bus patch's data payload.
833 /// It will check the parity of the raw data words and send them
834 /// to the event handler instance with calls to OnData.
835 /// \param start This is the pointer to the start of the bus patch
836 /// structure's data.
837 /// \param end This is the pointer to the first byte just past the
838 /// end of the bus patch structure.
839 /// \return If the bus patch's data was decoded without errors
840 /// or we could recover from the errors, then true is returned.
841 /// False is returned otherwise.
843 // Assert that 'end' is always larger than start by n*sizeof(UInt_t)
844 // where n is a positive integer. This should be the case because we
845 // always add multiples of sizeof(UInt_t) to the 'current' pointer in
846 // all the DecodeXYZ methods.
847 assert( UInt_t(end - start) % 4 == 0 );
849 // Now step through all the data words and issue OnData events.
850 // We also need to check parity and signal OnError if it is not valid
851 // for any of the data words.
852 const UInt_t* data = reinterpret_cast<const UInt_t*>(start);
853 const UInt_t* dataEnd = reinterpret_cast<const UInt_t*>(end);
854 for (; data < dataEnd; data++)
856 if (ParityIsOk(*data))
858 fHandler.OnData(*data, false);
862 // Indicate we had a parity error and exit immediately
863 // if the user so requested.
864 fHandler.OnError(EventHandler::kParityError, data);
866 if (fExitOnError) return false;
868 if (fSendDataOnParityError)
869 fHandler.OnData(*data, true);
877 template <class EventHandler>
878 typename AliMUONTrackerDDLDecoder<EventHandler>::RecoverResult
879 AliMUONTrackerDDLDecoder<EventHandler>::TryRecoverStruct(
884 const UChar_t* structStart,
885 const UChar_t* bufferEnd,
886 const UChar_t*& dataEnd,
887 const UChar_t*& structEnd,
888 const UChar_t*& current
891 /// This method attempts to recover from a corrupt structure header by
892 /// figuring out which of the structure size indicators is correct.
893 /// This is possible because each header has some redundant information.
894 /// The recovery procedure is only attempted if fTryRecover was set to
895 /// true. If the recovery procedure is successful then this method will
896 /// also update the pointers indicating the start of data, end of structure
897 /// and current parsing position with the correct values.
899 /// [in] \param expectedKey This is the expected block key for the header
900 /// currently being processed.
901 /// [in] \param headerSize The expected header size as given by the sizeof
902 /// operator for example.
903 /// [in] \param totalLength The total length as given by the fTotalLength
904 /// field in the current header being handled.
905 /// [in] \param length The data length as given by the fLength field
906 /// in the current header being handled.
907 /// [in] \param structStart A pointer to the start of the structure header.
908 /// [in] \param bufferEnd A pointer to the first byte just past the end
909 /// of the buffer. This could be the pointer to the first byte
910 /// just past the end of the parent structure if we are dealing
911 /// with a DSP structure or bus patch. The parent structure for
912 /// the DSP is a block structure and for a bus patch it is a DSP.
913 /// [out] \param dataEnd This is the pointer to the first byte just past
914 /// the end of the structure being processed. It should be equal to
915 /// structStart + sizeof(structure header) + fLength, where fLength
916 /// is the field found in the structure's header itself. This value
917 /// will be corrected and updated if we could recover from the
918 /// corruption in the header.
919 /// [out] \param structEnd A pointer to the first byte just past the end of
920 /// the structure. This value should be set equal to
921 /// structStart + fTotalLength * sizeof(UInt_t), where fTotalLength
922 /// is the field found in the structure's header itself. This value
923 /// will be corrected and updated if we could recover from the
924 /// corruption in the header.
925 /// [out] \param current This is the pointer to the current location in
926 /// the DDL payload being parsed. It should in principle point
927 /// to the start of the structures data. This value will be
928 /// corrected and updated if we could recover from the corruption
931 /// \return Returns the result of the recovery attempt, which can be one
932 /// of the following:
933 /// kRecoverFailed - The recovery failed completely so the caller
934 /// cannot continue parsing any more structures. If the failure
935 /// is within a DSP then one could still continue parsing
936 /// from the next block. Similarly for bus patches, parsing could
937 /// continue from the next DSP structure.
938 /// kStructRecovered - Indicates that we recovered from a corrupt
939 /// structure header and can continue processing the data of the
940 /// structure in question.
941 /// kContinueToNextStruct - Either fTryRecover was set to false or we
942 /// could not recover from the corrupt header but we did find the
943 /// start of another header matching the expected key so parsing
944 /// can continue from the updated current position.
946 // Check if the user wants us to try and recover from a corrupt header.
947 if (not fTryRecover) return kContinueToNextStruct;
949 // If the user wants us to try recover, then try to recover what the
950 // correct values for dataEnd, structEnd and current were supposed to be.
951 // The recovery procedure is as follows: We have 4 conditions for a correct
953 // 1) The header key is what we expect.
954 // 2) The totalLength equals length + headerSize.
955 // 3) The word at dataEnd contains a valid key. (implies length is
957 // 4) The word at structEnd contains a valid key. (implies totalLength
959 // If any 2 of these conditions hold then we know that only one of the
960 // header fields is corrupt and we have enough information to reconstruct
961 // the third field. Note that if conditions 3 and 4 are true then this
962 // implies 2 is also true. (not necessarily the other way around though.)
963 // The valid key mentioned above at dataEnd and structEnd should be:
964 // a) A bus patch key, DSP key or end of buffer if expectedKey indicates
966 // b) A DSP key, block structure key or end of buffer if expectedKey
968 // c) A block structure key or end of buffer if expectedKey indicates
970 const UInt_t* headerKey = reinterpret_cast<const UInt_t*>(structStart);
971 bool headerKeyOk = (expectedKey == *headerKey);
973 bool lengthsMatch = (totalLength*4 == length*4 + headerSize);
975 bool lengthIsCorrect = false;
976 bool totalLengthIsCorrect = false;
977 const UInt_t* keyAtDataEnd = reinterpret_cast<const UInt_t*>(dataEnd);
978 const UInt_t* keyAtStructEnd = reinterpret_cast<const UInt_t*>(structEnd);
981 if ( expectedKey == fgkBlockDataKey )
983 if (dataEnd == bufferEnd)
985 // Are we at the end of the buffer?
986 lengthIsCorrect = true;
990 // Must check that we can read another 4 bytes before
991 // checking the key at dataEnd.
992 if (dataEnd + sizeof(UInt_t) <= bufferEnd)
994 if (*keyAtDataEnd == fgkBlockDataKey)
995 lengthIsCorrect = true;
999 if (structEnd == bufferEnd)
1001 // Are we at the end of the buffer?
1002 totalLengthIsCorrect = true;
1006 // Must check that we can read another 4 bytes before
1007 // checking the key at structEnd.
1008 if (structEnd + sizeof(UInt_t) <= bufferEnd)
1010 if (*keyAtStructEnd == fgkBlockDataKey)
1011 totalLengthIsCorrect = true;
1016 else if ( expectedKey == fgkDSPDataKey )
1018 if (dataEnd == bufferEnd)
1020 // Are we at the end of the buffer?
1021 lengthIsCorrect = true;
1025 // Must check that we can read another 4 bytes before
1026 // checking the key at dataEnd.
1027 if (dataEnd + sizeof(UInt_t) <= bufferEnd)
1029 if (*keyAtDataEnd == fgkBlockDataKey
1030 or *keyAtDataEnd == fgkDSPDataKey)
1031 lengthIsCorrect = true;
1035 if (structEnd == bufferEnd)
1037 // Are we at the end of the buffer?
1038 totalLengthIsCorrect = true;
1042 // Must check that we can read another 4 bytes before
1043 // checking the key at structEnd.
1044 if (structEnd + sizeof(UInt_t) <= bufferEnd)
1046 if (*keyAtStructEnd == fgkBlockDataKey
1047 or *keyAtStructEnd == fgkDSPDataKey)
1048 totalLengthIsCorrect = true;
1052 else if ( expectedKey == fgkBusPatchDataKey )
1054 if (dataEnd == bufferEnd)
1056 // Are we at the end of the buffer?
1057 lengthIsCorrect = true;
1061 // Must check that we can read another 4 bytes before
1062 // checking the key at dataEnd.
1063 if (dataEnd + sizeof(UInt_t) <= bufferEnd)
1065 if (*keyAtDataEnd == fgkDSPDataKey
1066 or *keyAtDataEnd == fgkBusPatchDataKey)
1067 lengthIsCorrect = true;
1071 if (structEnd == bufferEnd)
1073 // Are we at the end of the buffer?
1074 totalLengthIsCorrect = true;
1078 // Must check that we can read another 4 bytes before
1079 // checking the key at structEnd.
1080 if (structEnd + sizeof(UInt_t) <= bufferEnd)
1082 if (*keyAtStructEnd == fgkDSPDataKey
1083 or *keyAtStructEnd == fgkBusPatchDataKey)
1084 totalLengthIsCorrect = true;
1089 if (headerKeyOk and lengthIsCorrect)
1091 // totalLength was wrong, dataEnd is correct.
1092 structEnd = dataEnd;
1094 return kStructRecovered;
1096 if (headerKeyOk and totalLengthIsCorrect)
1098 // Length was wrong, structEnd is correct.
1099 dataEnd = structEnd;
1100 current = structEnd;
1101 return kStructRecovered;
1103 if (lengthsMatch and lengthIsCorrect and totalLengthIsCorrect)
1105 // The header's key was wrong but the lengths and pointers are OK.
1106 return kStructRecovered;
1109 // Could not recover the header from the available information, so find
1110 // the next key in the stream that is the same as the currently expected
1111 // one and continue decoding from there.
1112 const UChar_t* location = FindKey(
1113 expectedKey, structStart + sizeof(UInt_t), bufferEnd
1115 if (location != NULL)
1118 return kContinueToNextStruct;
1121 return kRecoverFailed;
1125 template <class EventHandler>
1126 const UChar_t* AliMUONTrackerDDLDecoder<EventHandler>::FindKey(
1127 UInt_t key, const UChar_t* start, const UChar_t* end
1130 /// Searches for the first occurrence of the key value in the buffer marked by
1131 /// 'start' and 'end'. 'start' should point to the start of the buffer and 'end'
1132 /// should point to 'start + bufferSize', i.e. just past the last byte of the
1133 /// buffer. If the key was found then the pointer to that location is returned
1134 /// otherwise NULL is returned.
1136 const UChar_t* current = start;
1137 while (current + sizeof(UInt_t) <= end)
1139 UInt_t data = * reinterpret_cast<const UInt_t*>(current);
1140 if (data == key) return current;
1147 template <class EventHandler>
1148 bool AliMUONTrackerDDLDecoder<EventHandler>::ParityIsOk(UInt_t data)
1150 /// Optimised parity check addapted from:
1151 /// http://graphics.stanford.edu/~seander/bithacks.html#ParityParallel
1153 // parity of the 32 bits must be zero if the last bit is equal
1154 // to the parity of the first 31 bits.
1155 // Reason: the parity bit xor the parity of the first 31 bits must give
1156 // zero, unless there was a bit error.
1161 data = ((0x6996 >> data) & 1);
1165 #endif // ALIMUONTRACKERDDLDECODER_H