Analysis Software
Documentation for sPHENIX simulation software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
GBTLink.h
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file GBTLink.h
1 // @file GBTLink.h
2 // @brief Declarations of helper classes for the ITS/MFT raw data decoding
3 // @sa <O2/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/GBTLink.>
4 // <760019308>
5 
6 #ifndef MVTXDECODER_GBTLINK_H
7 #define MVTXDECODER_GBTLINK_H
8 
9 #define _RAW_READER_ERROR_CHECKS_ // comment this to disable error checking
10 
13 #include "mvtx_decoder/PayLoadSG.h"
15 #include "mvtx_decoder/GBTWord.h"
17 
18 //#include "MVTXDecoder/RUDecodeData.h"
19 //#include "MVTXDecoder/RUInfo.h"
20 //#include "MVTXDecoder/RAWDataHeader.h"
21 //#include "MVTXDecoder/RDHUtils.h"
22 //#include "MVTXDecoder/PhysTrigger.h"
23 
24 #include <iostream>
25 #include <memory>
26 #include <iomanip>
27 
28 #define GBTLINK_DECODE_ERRORCHECK(errRes, errEval) \
29  errRes = errEval; \
30  if ((errRes)&uint8_t(ErrorPrinted)) { \
31  ruPtr->linkHBFToDump[(uint64_t(subSpec) << 32) + hbfEntry] = irHBF.orbit; \
32  errRes &= ~uint8_t(ErrorPrinted); \
33  } \
34  if ((errRes)&uint8_t(Abort)) { \
35  discardData(); \
36  return AbortedOnError; \
37  }
38 
39 namespace mvtx
40 {
41  using namespace mvtx_utils;
42 
43  struct TRGData
44  {
46  bool hasCDW = false;
47  GBTCalibDataWord calWord = {};
48  size_t first_hit_pos = 0;
49  size_t n_hits = 0;
50 
51  TRGData(uint64_t orb, uint16_t b) : ir(orb, b) {};
52 
53  void clear()
54  {
55  ir.clear();
56  hasCDW = false;
57  calWord = {};
58  first_hit_pos = 0;
59  n_hits = 0;
60  }
61  };
62 
63  struct mvtx_hit
64  {
65  uint8_t chip_id = 0xf;
66  uint16_t bunchcounter = 0xFFFF;
67  uint16_t row_pos = 0xFFFF;
68  uint16_t col_pos = 0xFFFF;
69  };
70 
72 struct GBTLink
73 {
74 // enum RawDataDumps : int { DUMP_NONE, // no raw data dumps on error
75 // DUMP_HBF, // dump HBF for FEEID with error
76 // DUMP_TF, // dump whole TF at error
77 // DUMP_NTYPES };
78 
79  enum CollectedDataStatus : int8_t { None,
82  DataSeen }; // None is set before starting collectROFCableData
83 
84 // enum ErrorType : uint8_t { NoError = 0x0,
85 // Warning = 0x1,
86 // Skip = 0x2,
87 // Abort = 0x4,
88 // ErrorPrinted = 0x1 << 7 };
89 
90  static constexpr int RawBufferMargin = 5000000; // keep uploaded at least this amount
91  static constexpr int RawBufferSize = 10000000 + 2 * RawBufferMargin; // size in MB
92  static constexpr uint8_t MaxCablesPerLink = 3;
93 
95 
96  uint16_t flxId = 0; // FLX ID
97  uint16_t feeId = 0; // FEE ID
98 
99  PayLoadCont data; // data buffer for single feeeid
100  std::array<PayLoadCont, MaxCablesPerLink> cableData;
101 
102 
103  uint32_t hbfEntry = 0; // entry of the current HBF page in the rawData SG list
105 
106  GBTLinkDecodingStat statistics; // link decoding statistics
107  bool hbf_found = false;
108 
109  uint32_t hbf_length = 0;
110  uint32_t prev_pck_cnt = 0;
111  uint32_t hbf_count = 0;
112 
113  PayLoadSG rawData; // scatter-gatter buffer for cached CRU pages, each starting with RDH
114  size_t dataOffset = 0; //
115  std::vector<InteractionRecord> mL1TrgTime;
116  std::vector<TRGData> mTrgData;
117 
118  std::vector<mvtx_hit *> hit_vector = {};
119 
120  //------------------------------------------------------------------------
121  GBTLink() = default;
122  GBTLink(uint16_t _flx, uint16_t _fee);
123  void clear(bool resetStat = true, bool resetTFRaw = false);
124 
125  CollectedDataStatus collectROFCableData();
126 
127  void cacheData(size_t start, size_t sz)
128  {
129  rawData.add(start, sz);
130  }
131 
132  void clearCableData()
133  {
134  for ( auto&& data : cableData )
135  {
136  data.clear();
137  }
138  }
139 
140  int readFlxWord( GBTWord* gbtwords, uint16_t &w16 );
141  int decode_lane( const uint8_t chipId, PayLoadCont& buffer );
142 
143  void getRowCol(const uint8_t reg, const uint16_t addr, uint16_t& row, uint16_t& col)
144  {
145  row = ( addr >> 0x1 ) & 0x1FF;
146  col = ( reg << 5 | ( (addr >> 9) & 0x1E ) ) | ( (addr ^ addr >> 1) & 0x1 );
147  }
148 
149  void addHit(const uint8_t laneId, const uint8_t bc, uint8_t reg, const uint16_t addr)
150  {
151  auto* hit = new mvtx_hit();
152  memset(hit, 0, sizeof(*hit));
153 
154  hit->chip_id = laneId;
155  hit->bunchcounter = bc;
156  getRowCol(reg, addr, hit->row_pos, hit->col_pos);
157 
158  hit_vector.push_back(hit);
159  }
160 
161  void check_APE(const uint8_t& chipId, const uint8_t& dataC)
162  {
163  std::cerr << "Link: " << feeId << ", Chip: " << (int)chipId;
164  switch (dataC)
165  {
166  case 0xF2:
167  std::cerr << " APE_STRIP_START" << std::endl;
168  break;
169  case 0xF4:
170  std::cerr << " APE_DET_TIMEOUT" << std::endl;
171  break;
172  case 0xF5:
173  std::cerr << " APE_OOT" << std::endl;
174  break;
175  case 0xF6:
176  std::cerr << " APE_PROTOCOL_ERROR" << std::endl;
177  break;
178  case 0xF7:
179  std::cerr << " APE_LANE_FIFO_OVERFLOW_ERROR" << std::endl;
180  break;
181  case 0xF8:
182  std::cerr << " APE_FSM_ERROR" << std::endl;
183  break;
184  case 0xF9:
185  std::cerr << " APE_PENDING_DETECTOR_EVENT_LIMIT" << std::endl;
186  break;
187  case 0xFA:
188  std::cerr << " APE_PENDING_LANE_EVENT_LIMIT" << std::endl;
189  break;
190  case 0xFB:
191  std::cerr << " APE_O2N_ERROR" << std::endl;
192  break;
193  case 0xFC:
194  std::cerr << " APE_RATE_MISSING_TRG_ERROR" << std::endl;
195  break;
196  case 0xFD:
197  std::cerr << " APE_PE_DATA_MISSING" << std::endl;
198  break;
199  case 0xFE:
200  std::cerr << " APE_OOT_DATA_MISSING" << std::endl;
201  break;
202  default:
203  std::cerr << " Unknown APE code" << std::endl;
204  }
205  return;
206  }
207 
208  void AlpideByteError(const uint8_t& chipId, PayLoadCont& buffer)
209  {
210  uint8_t dataC = 0;
211 
212  std::cerr << "Link: " << feeId << ", Chip: " << (int)chipId;
213  std::cerr << " invalid byte 0x" << std::hex << (int)(dataC) << std::endl;
214  while ( buffer.next(dataC) )
215  {
216  std::cerr << " " << std::hex << (int)(dataC) << " ";
217  }
218  std::cerr << std::endl;
219  buffer.clear();
220  return;
221  }
222 
223  void PrintFlxWord(std::ostream& os, uint8_t* pos)
224  {
225  os << std::setfill('0');
226  for ( int i = 0; i < 32 ; i++)
227  {
228  os << std::hex << std::setw(2) << (int)pos[i] << " " << std::dec;
229  }
230  os << std::setfill(' ') << std::endl;
231  }
232 
233  void PrintBlock(std::ostream& os, uint8_t* pos, size_t n)
234  {
235  for (uint32_t i = 0; i < n; ++i)
236  {
237  PrintFlxWord(os, pos + 32 * i);
238  }
239  }
240 
241 // ClassDefNV(GBTLink, 1);
242 };
243 
248 {
249  bool prev_evt_complete = false;
250  bool header_found = false;
251  bool trailer_found = false;
252  uint8_t* hbf_start = nullptr;
253 
254  status = None;
255 
256  auto currRawPiece = rawData.currentPiece();
257  dataOffset = 0;
258  while (currRawPiece)
259  { // we may loop over multiple FLX page
260  uint32_t n_no_continuation = 0;
261  uint32_t n_packet_done = 0;
262 
263  if (dataOffset >= currRawPiece->size)
264  {
265  data.movePtr(currRawPiece->size);
266  dataOffset = 0;
267  // start of the RDH
268  if ( ! (currRawPiece = rawData.nextPiece()) )
269  { // fetch next CRU page
270  break; // Data chunk (TF?) is done
271  }
272  }
273 
274  if ( currRawPiece->hasError ) // Skip
275  {
276  dataOffset = currRawPiece->size;
277  ++hbf_count;
278  continue;
279  }
280 
281  if ( !dataOffset )
282  {
283  hbf_start = data.getPtr();
284  }
285 
286  // here we always start with the RDH
287  RdhExt_t rdh = {};
288  uint8_t* rdh_start = data.getPtr() + dataOffset;
289  rdh.decode(rdh_start);
290 
291  size_t pagesize = (rdh.pageSize + 1) * FLXWordLength;
292  const size_t nFlxWords = (pagesize - (2 * FLXWordLength)) / FLXWordLength;
293  //Fill statistics
294  if ( !rdh.packetCounter )
295  {
296  if ( dataOffset )
297  {
298  log_error << "Wrong dataOffset value " << dataOffset << " at the start of a HBF" << std::endl;
299  assert(false);
300  }
301  statistics.clear();
302  //TODO: initialize/clear alpide data buffer
303  for ( uint32_t trg = GBTLinkDecodingStat::BitMaps::ORBIT; trg < GBTLinkDecodingStat::nBitMap; ++trg )
304  {
305  if ( (rdh.trgType >> trg) & 1 )
306  {
307  statistics.trgBitCounts[trg]++;
308  }
309  }
310  hbfEntry = rawData.currentPieceId(); // in case of problems with RDH, dump full TF
311  ++hbf_count;
312  }
313  else if ( !rdh.stopBit )
314  {
315  if (prev_evt_complete)
316  {
317  log_error << "Previous event was already completed" << std::endl;
318  assert(false);
319  }
320  }
321 
322  dataOffset += 2 * FLXWordLength;
323  int prev_gbt_cnt = 3;
324  GBTWord gbtWords[3];
325  uint16_t w16 = 0;
326  for ( size_t iflx = 0; iflx < nFlxWords; ++iflx )
327  {
328  readFlxWord(gbtWords, w16);
329  int16_t n_gbt_cnt = (w16 & 0x3FF) - prev_gbt_cnt;
330  prev_gbt_cnt = (w16 & 0x3FF);
331  if (n_gbt_cnt < 1 || n_gbt_cnt > 3)
332  {
333  log_error << "Bad gbt counter in the flx packet. FLX: " << flxId << ", Feeid: " << feeId << ", n_gbt_cnt: " << n_gbt_cnt \
334  << ", prev_gbt_cnt: " << prev_gbt_cnt << ", size: " << currRawPiece->size << ", dataOffset: " << dataOffset << std::endl;
335  PrintBlock(std::cerr, rdh_start, nFlxWords + 2);
336  std::cerr << "Full HBF" << std::endl;
337  PrintBlock(std::cerr, hbf_start, (currRawPiece->size/32) );
338  break;
339  }
340  for ( int i = 0; i < n_gbt_cnt; ++i )
341  {
342  auto &gbtWord = gbtWords[i];
343  if ( gbtWord.isIHW() ) // ITS HEADER WORD
344  {
345  //TODO assert first word after RDH and active lanes
346  if (! ( !gbtWord.activeLanes ||
347  ((gbtWord.activeLanes >> 0) & 0x7) == 0x7 ||
348  ((gbtWord.activeLanes >> 3) & 0x7) == 0x7 ||
349  ((gbtWord.activeLanes >> 6) & 0x7) == 0x7) )
350  {
351  log_error << "Expected all active lanes for links, but " << gbtWord.activeLanes << "found in HBF " << hbfEntry << ", " \
352  << gbtWord.asString().data() << std::endl;
353  assert(false);
354  }
355  }
356  else if ( gbtWord.isTDH() ) // TRIGGER DATA HEADER (TDH)
357  {
358  header_found = true;
359  ir.orbit = gbtWord.bco;
360  ir.bc = gbtWord.bc;
361  if ( gbtWord.bc ) //statistic trigger for first bc already filled on RDH
362  {
363  for ( uint32_t trg = GBTLinkDecodingStat::BitMaps::ORBIT; trg < GBTLinkDecodingStat::nBitMap; ++trg )
364  {
365  if ( trg == GBTLinkDecodingStat::BitMaps::FE_RST ) // TDH save first 12 bits only
366  break;
367  if ( ((gbtWord.triggerType >> trg) & 1) )
368  {
369  statistics.trgBitCounts[trg]++;
370  }
371  }
372  }
373 
374  if ( (gbtWord.triggerType >> GBTLinkDecodingStat::BitMaps::PHYSICS) & 0x1 )
375  {
376  mL1TrgTime.push_back(ir);
377  }
378 
379  if ( !gbtWord.continuation && !gbtWord.noData)
380  {
381  n_no_continuation++;
382  mTrgData.emplace_back(ir.orbit, ir.bc);
383  } // end if not cont
384  } // end TDH
385  else if ( gbtWord.isCDW() ) // CALIBRATION DATA WORD
386  {
387  mTrgData.back().hasCDW = true;
388  mTrgData.back().calWord = *(reinterpret_cast<GBTCalibDataWord*>(&gbtWord));
389  }
390  else if ( gbtWord.isTDT() )
391  {
392  trailer_found = true;
393  if ( gbtWord.packet_done )
394  {
395  n_packet_done++;
396  if (n_packet_done < n_no_continuation)
397  {
398  log_error << "TDT packet done before TDH no continuation " << n_no_continuation \
399  << " != " << n_packet_done << std::endl;
400  assert(false);
401  }
402  }
403  prev_evt_complete = gbtWord.packet_done;
404  //TODO: YCM Add warning and counter for timeout and violation
405  }
406  else if ( gbtWord.isDDW() ) // DIAGNOSTIC DATA WORD (DDW)
407  {
408  if (! rdh.stopBit)
409  {
410  log_error << "" << std::endl;
411  assert(false);
412  }
413  }
414  else if ( gbtWord.isDiagnosticIB() ) // IB DIAGNOSTIC DATA
415  {
416  std::cout << "WARNING: IB Diagnostic word found." << std::endl;
417  std::cout << "diagnostic_lane_id: " << (gbtWord.id >> 5);
418  std::cout << " lane_error_id: " << gbtWord.lane_error_id;
419  std::cout << " diasnotic_data: 0x" << std::hex << gbtWord.diagnostic_data << std::endl;
420  }
421  else if ( gbtWord.isData() ) //IS IB DATA
422  {
423  if (! header_found )
424  {
425  log_error << "Trigger header not found before chip data" << std::endl;
426  assert(false);
427  }
428  auto lane = ( gbtWord.data8[9] & 0x1F ) % 3;
429  cableData[lane].add(gbtWord.getW8(), 9);
430  }
431 
432  if ( prev_evt_complete )
433  {
434  auto&& trgData = mTrgData.back();
435  trgData.first_hit_pos = hit_vector.size();
436  for( auto&& itr = cableData.begin(); itr != cableData.end(); ++itr)
437  {
438  if (!itr->isEmpty())
439  {
440  decode_lane(std::distance(cableData.begin(), itr), *itr);
441  }
442  }
443  trgData.n_hits = hit_vector.size() - trgData.first_hit_pos;
444  prev_evt_complete = false;
445  header_found = false;
446  trailer_found = false;
447  clearCableData();
448  }
449  }
450  }
451  }
452  return (status = StoppedOnEndOfData);
453 }
454 
455 //_________________________________________________
456 inline int GBTLink::decode_lane( const uint8_t chipId, PayLoadCont& buffer)
457 {
458  int ret = 0; // currently we just print stuff, but we will add stuff to our
459  // structures and return a status later (that's why it's not a const function)
460 
461  if ( buffer.getSize() < 3 )
462  {
463  log_error << "chip data is too short: " << buffer.getSize() << std::endl;
464  assert(false);
465  }
466 
467  uint8_t dataC = 0;
468  uint16_t dataS = 0;
469 
470  bool busy_on = false, busy_off = false;
471  bool chip_header_found = false;
472  bool chip_trailer_found = false;
473 
474  uint8_t laneId = 0xFF;
475  uint8_t bc = 0xFF;
476  uint8_t reg = 0xFF;
477 
478  if ( !( (buffer[0] & 0xF0) == 0xE0 || (buffer[0] & 0xF0) == 0xA0 ||\
479  (buffer[0] == 0xF0) || (buffer[0] == 0xF1) || (buffer[0] & 0xF0) == 0xF0 ) )
480  {
481  AlpideByteError(chipId, buffer);
482  return 0;
483  }
484 
485  while ( buffer.next(dataC) )
486  {
487  if ( dataC == 0xF1 ) // BUSY ON
488  {
489  busy_on = true ;
490  }
491  else if ( dataC == 0xF0 ) // BUSY OFF
492  {
493  busy_off = true;
494  }
495  else if ( (dataC & 0xF0) == 0xF0) // APE
496  {
497  check_APE(chipId, dataC);
498  chip_trailer_found = 1;
499  busy_on = busy_off = chip_header_found = 0;
500  }
501  else if ( (dataC & 0xF0) == 0xE0 ) // EMPTY
502  {
503  chip_header_found = false;
504  chip_trailer_found = true;
505  laneId = (dataC & 0x0F) % 3;
506  if ( laneId != chipId )
507  {
508  log_error << "Error laneId " << laneId << " (" << (dataC & 0xF) << ") and chipId " << chipId << std::endl;
509  assert(false);
510  }
511  buffer.next(bc);
512  busy_on = busy_off = false;
513  }
514  else
515  {
516  if ( chip_header_found )
517  {
518  if ( (dataC & 0xE0) == 0xC0 ) // REGION HEADER
519  {
520  if ( buffer.getUnusedSize() < 2 )
521  {
522  log_error << "No data short would fit (at least a data short after region header!)" << std::endl;
523  assert(false);
524  }
525  // TODO: move first region header out of loop, asserting its existence
526  reg = dataC & 0x1F;
527  }
528  else if ( (dataC & 0xC0) == 0x40 ) // DATA SHORT
529  {
530  if( buffer.isEmpty() )
531  {
532  log_error << "data short do not fit" << std::endl;
533  assert(false);
534  }
535  if ( reg == 0xFF )
536  {
537  log_error << "data short at " << buffer.getOffset() << " before region header" << std::endl;
538  assert(false);
539  }
540  dataS = (dataC << 8);
541  buffer.next(dataC);
542  dataS |= dataC;
543  addHit(laneId, bc, reg, (dataS & 0x3FFF));
544  }
545  else if ( (dataC & 0xC0) == 0x00) // DATA LONG
546  {
547  if ( buffer.getUnusedSize() < 3 )
548  {
549  log_error << "No data long would fit (at least a data short after region header!)" << std::endl;
550  assert(false);
551  }
552  if ( reg == 0xFF )
553  {
554  log_error << "data short at " << buffer.getOffset() << " before region header" << std::endl;
555  assert(false);
556  }
557  buffer.next(dataS);
558  uint16_t addr = ((dataC & 0x3F) << 8) | ( (dataS >> 8) & 0xFF );
559  addHit(laneId, bc, reg, addr);
560  uint8_t hit_map = (dataS & 0xFF);
561  if ( hit_map & 0x80 )
562  {
563  log_error << "Wrong bit before DATA LONG bit map" << std::endl;
564  assert(false);
565  }
566  while( hit_map != 0x00 )
567  {
568  ++addr;
569  if ( hit_map & 1 )
570  {
571  addHit(laneId, bc, reg, addr);
572  }
573  hit_map >>= 1;
574  }
575  }
576  else if ( (dataC & 0xF0) == 0xB0 ) // CHIP TRAILER
577  {
578 // uint8_t flag = (dataC & 0x0F);
579  //TODO: YCM add chipdata statistic
580  chip_trailer_found = 1;
581  busy_on = busy_off = chip_header_found = 0;
582  }
583  else // ERROR
584  {
585  AlpideByteError(chipId, buffer);
586  }
587  }
588  else
589  {
590  if ( (dataC & 0xF0) == 0xA0 ) // CHIP HEADER
591  {
592  chip_header_found = true;
593  chip_trailer_found = false;
594  laneId = (dataC & 0x0F) % 3;
595  if (laneId != chipId )
596  {
597  log_error << "Error laneId " << laneId << " (" << (dataC & 0xF) << ") and chipId " << chipId << std::endl;
598  assert(false);
599  }
600  buffer.next(bc);
601  reg = 0xFF;
602  }
603  else if ( dataC == 0x00 ) // PADDING
604  {
605  continue;
606  }
607  else
608  { // ERROR
609  AlpideByteError(chipId, buffer);
610  } // else !chip_header_found
611  } // if chip_header_found
612  } // busy_on, busy_off, chip_empty, other
613  } // while
614 
615  return ret;
616 }
617 
618 
619 } // namespace mvtx
620 
621 #endif // _MVTX_DECODER_ITSMFT_GBTLINK_H_