added pointer consistency check before decoding
[u/mrichter/AliRoot.git] / RAW / AliAltroDecoder.cxx
1 // $Id$
2
3 /**************************************************************************
4  * This file is property of and copyright by the ALICE HLT Project        * 
5  * All rights reserved.                                                   *
6  *                                                                        *
7  * Primary Authors: Per Thomas Hille  <perthi@fys.uio.no>                 *
8  *                  Oystein Djuvsland <oystein.djuvsland@gmail.com>       *
9  *                                                                        *
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  **************************************************************************/
18
19 /**
20    @file AliAltroDocoder.cxx
21    @author Per Thomas Hille, Oystein Djuvsland
22    @date   
23    @brief High performance decoder class for the RCU/Altro data format
24 */
25
26 #include  <cassert>
27 #include  <Riostream.h>
28 #include  "AliAltroDecoder.h"
29 #include  "AliAltroData.h"
30 #include  "AliLog.h"
31
32 // Class for fast decoding of RCU/Altro raw data format (DDL format)
33 // to PC readable form. The decoding is done in 3 steps.
34 // 1) The RCU payload consist of a variable number of 160 bit words
35 //    denoted a DDL block. All the DDL blocks of an RCU payload are transformed
36 //    into 16 bit integers and stored into a buffer of integers big enought to contain the
37 //    biggest number of samples possible
38 // 2) The decoded buffer is then accesible for the NextChannel functions wich reads 
39 //    the altro channel data (samples) contained in the payload. The first call to NextChannel gives the
40 //    last altro channel of the back linked list, the next call the second last channel ..etc until
41 //    all channels are read.
42 // 3) (optional) For each Altro channel one chan invoke the member function NextBunch which gives on first call
43 //    The last bunch (the bunch with the highest timestamp), on next call the second last bunch..etc
44 //    untill all bunches are read
45   
46
47 ClassImp(AliAltroDecoder)
48
49 AliAltroDecoder::AliAltroDecoder() : f32DtaPtr(0),
50                                      f8DtaPtr(0),
51                                      fkN32HeaderWords(8), 
52                                      fN40AltroWords(0), 
53                                      fN40RcuAltroWords(0),
54                                      fNDDLBlocks(0), 
55                                      f32LastDDLBlockSize(5), 
56                                      f8PayloadSize(0),
57                                      fOutBufferIndex(0),
58                                      fSize(0), 
59                                      fNAltro10bitWords(0),
60                                      fComplete(0),
61                                      fInComplete(0),
62                                      fDecodeIfCorruptedTrailer(kTRUE),
63                                      fIsDecoded(kFALSE),
64                                      fIsFatalCorruptedTrailer(kTRUE) 
65 {
66  // Default constructor
67 }
68
69
70 AliAltroDecoder::~AliAltroDecoder()
71 {
72   // Default destructor
73 }
74
75
76 Bool_t AliAltroDecoder::CheckPayloadTrailer() const
77 {
78   //Check consistency between the number of 40 bit altro words given by
79   //the RCU payload and the number of 40 bit words calculated from the size of the RCU payload.
80
81   if(fN40AltroWords != fN40RcuAltroWords)
82     {
83       return  kFALSE;
84     } 
85   else
86     {
87       return kTRUE;
88     }
89 }
90
91
92 Bool_t AliAltroDecoder::Decode()
93
94   // Decodes the RCU payload (all altro channels in one go) from the DDL/Altro
95   // format to PC readable format.
96   // The 10 bit words of the 40/10 bit Altro format are transformed to separated
97   // integers.
98
99   if( fIsFatalCorruptedTrailer == kTRUE)
100     {
101       //      printf("\n AliAltroDecoder::Decode(), WARNING, attempt to decode badly corrupted data\n");
102       //     printf("\n AliAltroDecoder::Decode(). Please check on the return value (-1 if fataly corrupted) of the SetMemory() function\n");    
103       return kFALSE;
104     }
105   else if (!f8DtaPtr || !f32DtaPtr ||
106            (UChar_t*)f32DtaPtr < f8DtaPtr-fSize ||
107            (UChar_t*)f32DtaPtr > f8DtaPtr)
108     {
109       return kFALSE;
110     }
111   else
112     {
113       // see header file for class documentation
114       fComplete = 0;
115       fInComplete = 0;
116
117       Int_t tmpcnt = CountAAApaddings();
118   
119       if(tmpcnt == 3)
120         {
121           fN40AltroWords = fN40AltroWords -1;
122         } 
123       else if(tmpcnt == 5)
124         {
125           fN40AltroWords = fN40AltroWords -2;
126         } 
127       else if(tmpcnt == 8)
128         {
129           fN40AltroWords = fN40AltroWords -3;
130         } 
131
132       if(  ((CheckPayloadTrailer() == kTRUE) || fDecodeIfCorruptedTrailer == kTRUE  )  &&  (fSize > 32) )
133         {
134           //      fDDLBlockCnt = 0;
135           fOutBufferIndex = 0;
136
137           for(Int_t i = 0; i < fNDDLBlocks; i++)
138             {
139               DecodeDDLBlock();
140             }
141
142           DecodeLastDDLBlock(); 
143           fOutBufferIndex =  fN40AltroWords*4  -  1;
144   
145           //      DumpData(fOutBuffer, 400,4);
146     
147           fIsDecoded = kTRUE;
148           return kTRUE;
149         }
150
151       else
152         {
153 //        cout <<" ERROR: data integrity check failed, discarding data" << endl;
154 //        cout << "Size of datablock is  " << fSize   << endl;
155 //        cout << "fN40AltroWords = "      << fN40AltroWords   << endl;
156 //        cout << "fN40RcuAltroWords = "   << fN40RcuAltroWords  << endl;
157           return kFALSE;
158         }
159
160     }
161 }
162
163
164 Bool_t AliAltroDecoder::NextChannel(AliAltroData *altroDataPtr)
165 {
166   // Reads the next altro channel in the RCU payload after the RCU payload
167   // has been decoded. The channels are read starting from the end (backlinked list) 
168   // Returns kTRUE as long as ther are unread channels in the payload
169   // Returns kFALSE when all the channels have been read. 
170
171   if(fIsFatalCorruptedTrailer == kTRUE)
172     {
173       //    printf("\n AliAltroDecoder::NextChannel(), WARNING, attempt to decode badly corrupted data\n");
174       //    printf("\n AliAltroDecoder::NextChannel(), Please check on the return value (-1 if fataly corrupted) of the SetMemory() function\n");    
175       return kFALSE;
176     } 
177   
178   else
179     {
180
181       if(fIsDecoded != kTRUE)
182         {
183           //      cout <<"AliAltroDecoder::NextChanne, WARNING, buffer was not decoded, decoding now.. "<< endl;
184           Decode();
185         }
186
187       // an altro block must constist of at least 2 40bit words:
188       // - 40bit Altro trailer
189       // - at least 3 10bit words (bunch length, time bin, signal) padded to 40 bit
190       // we are reading backwards, so the index is already 1 inside the block
191       if(fOutBufferIndex >= 7)
192         {
193           if((fOutBuffer[fOutBufferIndex] << 4 ) | ((fOutBuffer[fOutBufferIndex-1] & 0x3c0) >> 6) == 0x2aaa)
194             {
195               altroDataPtr->SetIsComplete(kTRUE);
196               fComplete ++;
197             }
198           else
199             {
200               altroDataPtr->SetIsComplete(kFALSE);
201               fInComplete ++;
202             }
203
204           fOutBufferIndex --;
205           fNAltro10bitWords = ( (fOutBuffer[fOutBufferIndex] & 0x3f) << 4 )   |  ((fOutBuffer[fOutBufferIndex -1]  & (0xF << 6)) >> 6) ;
206           fOutBufferIndex --;
207           altroDataPtr->SetHadd( ((fOutBuffer[fOutBufferIndex] & 0x3)) << 10 | ( fOutBuffer[fOutBufferIndex-1] ) );
208       
209           fOutBufferIndex --;
210
211           if(fNAltro10bitWords%4 == 0)
212             {
213               fOutBufferIndex = fOutBufferIndex  -  fNAltro10bitWords;
214             }
215           else
216             {
217               fOutBufferIndex = fOutBufferIndex - fNAltro10bitWords -(4 - fNAltro10bitWords%4);
218             }
219
220           
221           if(fOutBufferIndex >= 0)
222             {
223           
224               //cout << " AliAltroDecoder::NextChannel fOutBufferIndex =" << fOutBufferIndex   << endl;
225               //              printf( "AliAltroDecoder::NextChannel fOutBufferIndex = %d", fOutBufferIndex);
226               altroDataPtr->SetData( &fOutBuffer[fOutBufferIndex] );
227               if(fOutBufferIndex > 0) fOutBufferIndex --;
228               altroDataPtr->SetDataSize( fNAltro10bitWords );
229               return kTRUE;
230             }
231           else
232             {
233               //TODO write a fatal log message when this happends
234               return kFALSE;
235             }
236
237
238         }
239       else
240         {
241           return kFALSE;
242         }
243
244     }
245 }
246
247
248 Int_t AliAltroDecoder::CountAAApaddings() const
249 {
250   // Use for simulated data only.
251   // Patch for incorrectly simulated data. Counts the number of 
252   // 2aaa word in the trailer of the payload and tries to figure out
253   // the correct number of 40 bit altro words in the RCU pauload
254   // 
255   // The simulated raw data differs from the real data by a number
256   // of additional 0x2aa words between the Altro payload and the
257   // RCU trailer. This is most likely to bring the total number of
258   // bytes to a common multiple of 4 and 5.
259  
260   UShort_t *tailPtr= (UShort_t *)((UChar_t*)f32DtaPtr + f8PayloadSize);
261   Int_t cnt = 0;
262
263   tailPtr --;
264
265   while(*tailPtr == 0xaaaa)
266     {
267       cnt ++;
268       tailPtr --;
269     }
270   
271   tailPtr  = tailPtr + cnt +1;
272
273   return cnt;
274 }
275
276
277 Float_t AliAltroDecoder::GetFailureRate()
278 {
279   // Prints to stdout the percent of altroblocks that
280   // is missing the 2aaa trailer.
281  
282   Float_t tmp = 0;
283   //  cout << "Number of Complete channles = " << fComplete <<endl;
284   // cout << "Number of InComplete channles = " << fInComplete <<endl;
285   tmp = (100*(Float_t)fInComplete)/((Float_t)fComplete + (Float_t)fInComplete);
286   // cout <<"There are "<<  tmp <<"% incomplete channels"<<endl;
287   return  tmp;
288 }
289
290
291 void AliAltroDecoder::PrintInfo(AliAltroData &altrodata, Int_t n, Int_t nPerLine)
292 {
293   // prints data and address information contained in altrodata 
294   // to the standard output
295
296   //  cout << "altrodata.fDataSize = " << altrodata.GetDataSize() <<  endl;
297   // cout << "altrodata.fHadd = "     << altrodata.GetHadd()  <<endl;
298   const UInt_t* data = altrodata.GetData();
299   for(Int_t i= 0; i< n; i++)
300     {
301       if( (i%nPerLine == 0) && (i != 0) )
302         {
303           printf("\n");
304         }
305       printf("%d\t", data[i]);
306     }
307   printf("\n");
308 }
309
310
311 int AliAltroDecoder::SetMemory(UChar_t *dtaPtr, UInt_t size)
312 {
313   // Sets the pointer to the memory block that should be decoded
314   // Returns a negative value if an inconsistency in the data is detected
315
316   if(dtaPtr == 0)
317     {
318       printf("\nAliAltroDecoder::SetMemory(UChar_t *dtaPtr, UInt_t size) FATAL ERROR, dtaPtr = ZERO !!!!!!!!!!!!\n");
319       printf("Please check your code that you don't give a zero pointer to the decoder \n");
320       return -99;
321     }
322
323   if (size<fkN32HeaderWords+4)
324     {
325       printf("\nAliAltroDecoder::SetMemory(UChar_t *dtaPtr, UInt_t size) FATAL ERROR, too little data (%d)\n", size);
326       printf("Data buffer must contain the CDH and at least one 32bit RCU trailer word\n");
327       return -99;
328     }
329
330
331   int iRet = 0;
332   UInt_t tmpTrailerSize;
333   fIsDecoded = kFALSE; 
334   f8DtaPtr =dtaPtr;
335   fSize = size;
336   f8DtaPtr =f8DtaPtr + fSize;
337   f32DtaPtr = (UInt_t *)f8DtaPtr;
338   tmpTrailerSize = *(f32DtaPtr - 1);
339
340   // format of the trailer has been fixed in the RCU FW2
341   // Bit 31 to 16: 0xaaaa         (16 bit)
342   // Bit 15 to  7: RCU address    ( 9 bit)
343   // Bit  6 to  0: Trailer length ( 7 bit)
344   //
345   // RCU FW1 has one trailer word containing the number of
346   // 10bit Altro words. According to some early documents,
347   // it should have at least 2 32bit words: trailer length and
348   // the number of 10bit Altro words. This is the format of
349   // the simulation at time of writing (June 2008)
350   bool haveFw2=false;
351   if ((tmpTrailerSize>>16)==0xaaaa) {
352       haveFw2=true;
353       tmpTrailerSize = tmpTrailerSize&0x7f; // 7 LSBs of the last word
354   } else
355   if(tmpTrailerSize <=  MAX_TRAILER_WORDS)
356     {
357       tmpTrailerSize = tmpTrailerSize; //assume that the last word of the buffer gives the number of trailer words 
358     }
359   // nof 10bit AltroWords * 5/4  + bytes in the CDH   + 4 bytes RCU trailer
360   else if (((*(f32DtaPtr-1)*5)/4 + fkN32HeaderWords*4 + 4)<=fSize)
361     {
362       tmpTrailerSize = 1; //assume that last word is ONE, and that the this word gives the number of 40 bit altro words
363     }
364   else
365     {
366       tmpTrailerSize=0;
367       fIsFatalCorruptedTrailer = kTRUE;
368       iRet = -1;
369     }
370
371   if(tmpTrailerSize > 0 && (fkN32HeaderWords + tmpTrailerSize)*4<=fSize)
372     {
373       f8PayloadSize = fSize - (fkN32HeaderWords + tmpTrailerSize)*4;
374       fN40AltroWords = f8PayloadSize/5; 
375       fNDDLBlocks =  fN40AltroWords/4;
376       f32LastDDLBlockSize =  ((f8PayloadSize%(4*DDL_32BLOCK_SIZE))+3)/4;
377
378       f32DtaPtr =  f32DtaPtr -  tmpTrailerSize;
379       fN40RcuAltroWords =  *f32DtaPtr;
380       f32DtaPtr = (UInt_t *)dtaPtr + fkN32HeaderWords;
381       fIsFatalCorruptedTrailer = kFALSE; 
382     }
383
384   // all subsequent consistency checks depend on the correct initialization
385   // of the pointer and size variables
386   assert(f8DtaPtr == dtaPtr + fSize);  
387   return iRet;
388
389 }
390
391
392 void AliAltroDecoder::DecodeDDLBlock()
393 {
394   //Decode one 160 bit DDL block into 16 x 16 bit integers (only least significant 10 bits are filled)
395
396   fOutBuffer[fOutBufferIndex] =  *f32DtaPtr & 0x3ff;  //s0 
397   fOutBufferIndex ++;
398   fOutBuffer[fOutBufferIndex] = (*f32DtaPtr & 0xffc00) >> 10; //s1
399   fOutBufferIndex ++; 
400   fOutBuffer[fOutBufferIndex] = (*f32DtaPtr & 0x3ff00000) >> 20; //s2
401   fOutBufferIndex ++; 
402   fOutBuffer[fOutBufferIndex] = (*f32DtaPtr & 0xc0000000) >> 30; //s3_1 
403   f32DtaPtr ++;
404   fOutBuffer[fOutBufferIndex] =  fOutBuffer[fOutBufferIndex] | ((*f32DtaPtr & 0xff) << 2); //s3_2 
405   fOutBufferIndex ++;
406   fOutBuffer[fOutBufferIndex] =  (*f32DtaPtr & 0x3ff00) >> 8; //s4
407   fOutBufferIndex ++;
408   fOutBuffer[fOutBufferIndex] =  (*f32DtaPtr & 0xffc0000) >> 18; //s5
409   fOutBufferIndex ++;
410   fOutBuffer[fOutBufferIndex] =  (*f32DtaPtr & 0xf0000000) >> 28; //s6_1
411   f32DtaPtr ++;
412   fOutBuffer[fOutBufferIndex] =  fOutBuffer[fOutBufferIndex] | ((*f32DtaPtr & 0x3f) << 4); //s6_2 
413   fOutBufferIndex ++;
414   fOutBuffer[fOutBufferIndex] =  (*f32DtaPtr & 0xffc0) >> 6; //s7
415   fOutBufferIndex ++;
416   fOutBuffer[fOutBufferIndex] =  (*f32DtaPtr & 0x3ff0000) >> 16; //s8
417   fOutBufferIndex ++;
418   fOutBuffer[fOutBufferIndex] =  (*f32DtaPtr & 0xFC000000) >> 26; //s9_1
419   f32DtaPtr ++;
420   fOutBuffer[fOutBufferIndex] =  fOutBuffer[fOutBufferIndex] | ((*f32DtaPtr & 0xf) << 6); //s9_2
421   fOutBufferIndex ++;
422   fOutBuffer[fOutBufferIndex] =  (*f32DtaPtr & 0x3ff0) >> 4; //s10
423   fOutBufferIndex ++;
424   fOutBuffer[fOutBufferIndex] =  (*f32DtaPtr & 0xffc000) >> 14; //s11
425   fOutBufferIndex ++;
426   fOutBuffer[fOutBufferIndex] =  (*f32DtaPtr & 0xff000000) >> 24; //s12_1
427   f32DtaPtr ++;
428   fOutBuffer[fOutBufferIndex] =  fOutBuffer[fOutBufferIndex] | ((*f32DtaPtr & 0x3) << 8); //s12_2
429   fOutBufferIndex ++;
430   fOutBuffer[fOutBufferIndex] =  (*f32DtaPtr & 0xffc) >> 2; //s13
431   fOutBufferIndex ++;
432   fOutBuffer[fOutBufferIndex] =  (*f32DtaPtr & 0x3ff000) >> 12; //s14
433   fOutBufferIndex ++;
434   fOutBuffer[fOutBufferIndex] =  (*f32DtaPtr & 0xffc00000) >> 22; //s15
435   f32DtaPtr ++;
436   fOutBufferIndex ++;
437 }
438
439
440 void AliAltroDecoder::DecodeLastDDLBlock()
441 {
442   // Decode one 160 bit DDL block into 16 integers. 
443   // In order to use the same decoding function (DecodeDDLBlock()) 
444   // a copy of the the last DDL block is made and  
445   // if the las block does not align with 160 bits then it is padded with zeroes 
446
447   for(Int_t i=0; i < f32LastDDLBlockSize; i++)
448     {
449       fDDLBlockDummy[i] = *f32DtaPtr;
450       f32DtaPtr ++;
451     }
452   
453   f32DtaPtr = fDDLBlockDummy; 
454   DecodeDDLBlock();
455   f32DtaPtr=(UInt_t*)(f8DtaPtr-fSize+f8PayloadSize+fkN32HeaderWords*4);
456 }
457
458 Int_t AliAltroDecoder::CopyBackward(Byte_t* pBuffer, Int_t bufferSize)
459 {
460   // Copy the original 10/40 bit encecoded data of the current channel.
461   // The funtions copies the data to the end of the provided buffer.
462   // @param pBuffer    target buffer
463   // @param bufferSize size of target buffer
464   // @return number of copied bytes, neg. error code if failed
465   Int_t iCopy=0;
466
467   if(fIsDecoded != kTRUE) {
468     AliWarning("buffer has not been decoded, no channel data available");
469     return 0;
470   }
471
472   // This method does not need to be the fastest possible implementation
473   // For the sake of stability, there are a lot of consistency checks.
474
475   // the fOutBufferIndex always points to the next channel, since we are
476   // reading backwards, this is one byte before the start of the current
477   // channel.
478   Int_t currentIndex=fOutBufferIndex+1;
479   if (fNAltro10bitWords>0 && currentIndex < fN40AltroWords*4 ) {
480
481     // calculate the position in the encoded data, beginning right
482     // after the CDH. 10 -> 8 bit: needs 5/4 times the index
483     Int_t position=(currentIndex*5)/4;
484     if (position*4==currentIndex*5) {
485       // no of 10 bit words is without the fill words to fill complete 40 bit words
486       // in addition, align to complete 40 bit words (the '+3')
487       iCopy=((fNAltro10bitWords+3)/4)*5;
488
489       // calculate the source pointer in the encoded data
490       // f8DtaPtr was set to the end of the whole data buffer
491       // f32DtaPtr is behind the payload after decoding
492       Byte_t* pSrc=f8DtaPtr-fSize+(fkN32HeaderWords*4)+position;
493       if (pSrc+5<(Byte_t*)f32DtaPtr) {
494
495         // check the first byte of the source buffer, has to be consistent
496         // with the 8 LSBs of the decoded 10 bit word at the beginning of
497         // the current channel
498         //assert(*pSrc==fOutBuffer[currentIndex]&0xff);
499         if (*pSrc==fOutBuffer[currentIndex]&0xff) {
500
501           // try to verfify the length of the channel
502           UInt_t lenCheck=*(pSrc+iCopy+2)|(*(pSrc+iCopy+3)&0x3)<<8;
503           if (lenCheck==fNAltro10bitWords) {
504
505             // copy including the 40 bit altro trailer
506             iCopy+=5; 
507             if (iCopy<=bufferSize) {
508
509               // copy to the end of the buffer
510               Byte_t* pTgt=pBuffer+bufferSize-iCopy;
511               memcpy(pTgt, pSrc, iCopy);
512             } else {
513               AliError(Form("target buffer too small: available %d, required %d", bufferSize, iCopy));
514               iCopy=-1;
515             }
516           } else {
517             AliWarning(Form("format error: can not reproduce channel length: expected %d, read %d", fNAltro10bitWords, lenCheck));
518             iCopy=-1;
519           }
520         } else {
521           AliError(Form("first byte of encoded data (%#x at %d) does not match decoded word (%#x at %d)", *pSrc, position, fOutBuffer[currentIndex]&0xff, currentIndex));
522           iCopy=-1;
523         }
524       } else {
525         AliError(Form("buffer size missmatch: payload ends at %p, but current channel pretends to end at %p", f32DtaPtr, pSrc+5));
526         iCopy=-1;
527       }
528     } else {
529       AliError(Form("current position does not match a byte: currentIndex=%d", currentIndex));
530       iCopy=-1;
531     }
532   }
533   return iCopy;
534 }
535
536 Bool_t  AliAltroDecoder::GetRCUTrailerData(UChar_t*& data) const
537 {
538   // Provide a pointer to RCU trailer.
539   // The type of the parameter might not be optimal, but the function has
540   // been chosen that way to be similar to the counterpart in
541   // AliAltroRawStream.
542   // @return kTRUE if trailer available;
543   if (!f8DtaPtr) return kFALSE;
544   data=f8DtaPtr-(fSize-(f8PayloadSize+fkN32HeaderWords*sizeof(UInt_t)));
545   assert((UChar_t*)f32DtaPtr == data);
546   return kTRUE;
547 }
548
549 Int_t   AliAltroDecoder::GetRCUTrailerSize() const
550 {
551   // Provide size of RCU trailer.
552   if (!f8DtaPtr) return 0;
553   assert(fSize>(f8PayloadSize+fkN32HeaderWords*sizeof(UInt_t)));
554   return fSize-(f8PayloadSize+fkN32HeaderWords*sizeof(UInt_t));
555 }