Belle II Software  release-05-02-19
CDCTriggerTSFFirmwareModule.cc
1 /**************************************************************************
2  * BASF2 (Belle Analysis Framework 2) *
3  * Copyright(C) 2017 - Belle II Collaboration *
4  * *
5  * Author: The Belle II Collaboration *
6  * Contributors: Tzu-An Sheng *
7  * *
8  * This software is provided "as is" without any warranty. *
9  **************************************************************************/
10 
11 #include <trg/cdc/modules/trgcdc/CDCTriggerTSFFirmwareModule.h>
12 #include <trg/cdc/CDCTrigger.h>
13 #include <trg/cdc/Cosim.h>
14 #include <framework/datastore/StoreArray.h>
15 #include <framework/datastore/StoreObjPtr.h>
16 #include <framework/dataobjects/EventMetaData.h>
17 #include <trg/cdc/dataobjects/CDCTriggerSegmentHit.h>
18 #include <cdc/dataobjects/CDCHit.h>
19 #include <framework/logging/Logger.h>
20 
21 #include <vector>
22 #include <array>
23 #include <bitset>
24 #include <string>
25 #include <algorithm>
26 #include <numeric>
27 
28 #include <cstdio>
29 #include <unistd.h>
30 #include <sys/wait.h>
31 
32 using namespace Belle2;
33 using namespace Cosim;
34 using namespace std;
35 using namespace CDCTrigger;
37 
38 REG_MODULE(CDCTriggerTSFFirmware)
39 
40 constexpr std::array<int, TSF::m_nSubModules> TSF::nAxialMergers;
41 
43  Module(), m_cdcHits{""}
44 
45 {
46  for (unsigned iAx = 0; iAx < m_nSubModules; ++iAx) {
47  inputToTSF[iAx].resize(nAxialMergers[iAx]);
48  for (auto& clock : m_priorityHit) {
49  clock.insert({2 * iAx, priorityHitStructInSL(nAxialMergers[iAx])});
50  }
51  dataAcrossClocks.insert({2 * iAx, mergerStruct<5> (nAxialMergers[iAx])});
52  }
53  //Set module properties
54  setDescription("Firmware simulation of the Track Segment Finder for CDC trigger.");
55 
56  // Define module parameters
57  addParam("hitCollectionName", m_hitCollectionName,
58  "Name of the input StoreArray of CDCHits.",
59  string("CDCHits"));
60  addParam("outputCollectionName", m_outputCollectionName,
61  "Name of the StoreArray holding the found TS hits.",
62  string("CDCTriggerFirmwareSegmentHits"));
63  addParam("outputBitstreamNameTo2D", m_outputBitstreamNameTo2D,
64  "Name of the StoreArray holding the raw bit content to 2D trackers",
65  string("BitstreamTSFto2D"));
66  addParam("mergerOnly", m_mergerOnly,
67  "Flag to only simulate merger and not TSF",
68  false);
69  addParam("simulateCC", m_simulateCC,
70  "Flag to run the front-end clock counter",
71  false);
72  std::vector<bool> defaultStub(m_nSubModules, false);
73  addParam("stubLUT", m_stubLUT,
74  "list of flags to run each TSF firmware simulation with dummy L/R LUT (to speed up loading)",
75  defaultStub);
76 }
77 
78 TSF::~CDCTriggerTSFFirmwareModule()
79 {}
80 
81 Priority TSF::priority(int index)
82 {
83  CDCHit* hit = m_cdcHits[index];
84  int offset = (hit->getISuperLayer() == 0) ? 1 : 0;
85  switch (hit->getILayer() - 2 - offset) {
86  case 0: return Priority::first;
87  case 1: return Priority::second;
88  default: return Priority::nothing;
89  }
90 }
91 
92 void TSF::write(const char* message, FILE* outstream)
93 {
94  // write input to TSF firmware
95  fprintf(outstream, "%s\n" , message);
96  fflush(outstream);
97 }
98 
99 TSF::outputArray TSF::read(FILE* instream)
100 {
101  // read output from TSF firmware
102  array<char, 2048> buffer;
103  outputArray output;
104  buffer.fill(one_val);
105  if (fgets(buffer.data(), buffer.size(), instream) == NULL) {
106  B2ERROR("fgets reached end unexpectedly");
107  return output;
108  }
109  // ins->getline(buffer.data(), buffer.size());
110  for (auto i2d = 0; i2d < 4; ++i2d) {
111  B2DEBUG(50, display_value(buffer.data() + width_out * i2d, width_out));
112  }
113  auto bufferItr = buffer.cbegin();
114  // for (int iTracker = 0; iTracker < nTrackers; ++iTracker) {
115  for (int iTracker = nTrackers - 1; iTracker >= 0; --iTracker) {
116  copy(bufferItr, bufferItr + width_out, output[iTracker].begin());
117  bufferItr += width_out;
118  }
119  return output;
120 }
121 
123 {
124  m_cdcHits.isRequired(m_hitCollectionName);
125  m_debugLevel = getLogConfig().getDebugLevel();
126  if (m_mergerOnly) {
127  computeEdges();
128  return;
129  }
130  m_bitsTo2D.registerInDataStore(m_outputBitstreamNameTo2D);
131  m_tsHits.registerInDataStore(m_outputCollectionName);
132  for (unsigned i = 0; i < m_nSubModules; ++i) {
133  // i: input to worker (output from module)
134  // o: output from worker (input to module)
135  /* Create pipe and place the two-end pipe file descriptors*/
136  pipe(inputFileDescriptor[i].data());
137  pipe(outputFileDescriptor[i].data());
138  string str_fd[] = {to_string(inputFileDescriptor[i][0]), to_string(outputFileDescriptor[i][1])};
139  // spawn a child process
140  pid_t pid = fork();
141  // pid_t pid = 1;
142  if (pid < 0) {
143  B2FATAL("Fork failed!");
144  } else if (pid == (pid_t) 0) {
145  /* Child process (worker) */
146  // close the unused ends of the file descriptors
147  close(inputFileDescriptor[i][1]);
148  close(outputFileDescriptor[i][0]);
149  if (m_stubLUT[i]) {
150  design_libname_post = "_stub" + design_libname_post;
151  }
152  string design = design_libname_pre + to_string(i * 2) + design_libname_post;
153  string waveform = wdbName_pre + to_string(i * 2) + wdbName_post;
154  // execute the standalone worker program
155  execlp("CDCTriggerTSFFirmwareWorker", "CDCTriggerTSFFirmwareWorker",
156  str_fd[0].c_str(), str_fd[1].c_str(), design.c_str(), waveform.c_str(),
157  to_string(nAxialMergers[i]).c_str(), nullptr);
158  B2FATAL("The firmware simulation program didn't launch!");
159  } else {
160  /* Parent process (BASF2) */
161  B2DEBUG(100, "parent " << i);
162  m_pid[i] = pid;
163  // Close the copy of the fds read/write end
164  close(inputFileDescriptor[i][0]);
165  close(outputFileDescriptor[i][1]);
166  // open the fds
167  stream[i][0] = fdopen(inputFileDescriptor[i][1], "w");
168  stream[i][1] = fdopen(outputFileDescriptor[i][0], "r");
169  // ins = createInStreamFromFD(outputFileDescriptor[i][0]);
170  B2DEBUG(20, "pid for TSF" << i * 2 << ": " << m_pid[i]);
171  }
172  }
173  computeEdges();
174  B2INFO("It can take a while for TSF0 to load the LUT.");
175  for (unsigned i = 0; i < m_nSubModules; ++i) {
176  // wait for worker initialization
177  read(stream[i][1]);
178  }
179 }
180 
182 {
183  B2DEBUG(10, "Waiting for TSF firmware termination...");
184  for (unsigned i = 0; i < m_nSubModules; ++i) {
185  close(inputFileDescriptor[i][1]);
186  close(outputFileDescriptor[i][0]);
187  }
188  // wait for all processes to exit
189  for (unsigned i = 0; i < m_nSubModules; ++i) {
190  wait(nullptr);
191  }
192 }
193 
195 {
196  for (unsigned short iEdge = 0; iEdge < 5; ++iEdge) {
197  for (const auto& cell : innerInvEdge[iEdge]) {
198  m_edge[0][cell].push_back(iEdge);
199  }
200  }
201  for (unsigned short iEdge = 0; iEdge < 3; ++iEdge) {
202  for (const auto& cell : outerInvEdge[iEdge]) {
203  m_edge[1][cell].push_back(iEdge);
204  }
205  }
206 }
207 
208 template<int iSL>
210 {
211  static array<char, mergerWidth* nAxialMergers[iSL]> data;
212  // 2-D array in XSI is totally in LSB, like this
213  // ((012), (345), (678))
214  auto itr = data.end() - mergerWidth;
215  for (const auto& merger : input[iSL]) {
216  copy(merger.begin(), merger.end(), itr);
217  itr -= mergerWidth;
218  }
219  return data.data();
220 }
221 
222 unsigned short TSF::trgTime(int index, int iFirstHit)
223 {
224  if (m_allPositiveTime) {
225  return (m_cdcHits[iFirstHit]->getTDCCount() / 2 - m_cdcHits[index]->getTDCCount() / 2);
226  } else {
227  short time = (m_TDCCountForT0 / 2 - m_cdcHits[index]->getTDCCount() / 2);
228  return (time < 0) ? time + (1 << 9) : time;
229  }
230 }
231 
232 std::bitset<4> TSF::timeStamp(int index, int iFirstHit)
233 {
234  return std::bitset<4> (trgTime(index, iFirstHit) % clockPeriod);
235 }
236 
237 unsigned short TSF::mergerCellID(int index)
238 {
239  const CDCHit& hit = (*m_cdcHits[index]);
240  const unsigned offset = (hit.getISuperLayer() == 0) ? 3 : 0;
241  return (hit.getILayer() - offset) * nSegmentsInMerger +
242  hit.getIWire() % nSegmentsInMerger;
243 }
244 
245 unsigned short TSF::mergerNumber(int index)
246 {
247  const CDCHit& hit = (*m_cdcHits[index]);
248  return hit.getIWire() / nSegmentsInMerger;
249 }
250 
251 using WireSet = std::vector<unsigned short>;
252 using TSMap = std::unordered_map<int, WireSet>;
253 
255 {
256  const bool innerMost = (m_cdcHits[iHit]->getISuperLayer() == 0);
257  const unsigned short iLayer = m_cdcHits[iHit]->getILayer();
258  const unsigned short iCell = mergerCellID(iHit);
259  TSMap& tsMap = m_tsMap[static_cast<int>(! innerMost)];
260  tsMap.reserve(nWiresInMerger);
261  if (tsMap.find(iCell) != tsMap.end()) {
262  return tsMap[iCell];
263  } else {
264  WireSet id;
265  // distance to the first priority layer: 0-4 (SL0) or 0-2 (outer)
266  // The further it is, the more TS it would be involved in.
267  const unsigned short distance = (innerMost) ? iLayer - 3 : abs(iLayer - 2);
268  id.resize(distance + 1);
269  std::iota(id.begin(), id.end(), (iCell - distance / 2) % nCellsInLayer);
270  id.erase(std::remove_if(id.begin(), id.end(), [](short i) {
271  return (i < 0 || i >= nCellsInLayer);
272  }), id.end());
273  tsMap.insert({iCell, id});
274  return id;
275  }
276 }
277 
279 {
280  /* The CDCHits array in DataStore contains all the hits in an event,
281  * regardless of when them at the trigger system. Their real timing behavior
282  * is replayed/simulated by looking at the TDC count (timestamp) of each hit.
283  */
284 
285  vector<int> iAxialHit;
286  for (int i = 0; i < m_cdcHits.getEntries(); ++i) {
287  // discard hits in the layers unused by trigger
288  if ((m_cdcHits[i]->getISuperLayer() != 0 && m_cdcHits[i]->getILayer() == 5) ||
289  m_cdcHits[i]->getICLayer() < 3) {
290  continue;
291  }
292  if (m_cdcHits[i]->getISuperLayer() % 2 == 0) {
293  iAxialHit.push_back(i);
294  }
295  }
296  // sort by TDC count: larger count comes first
297  std::sort(iAxialHit.begin(), iAxialHit.end(), [this](int i, int j) {
298  return m_cdcHits[i]->getTDCCount() > m_cdcHits[j]->getTDCCount();
299  });
300 
301  // assign the hits to clock edges based on TDC count
302  iAxialHitInClock.clear();
303  iAxialHitInClock.resize(1 + trgTime(iAxialHit.back(), iAxialHit.front()) / clockPeriod);
304  auto itr = iAxialHitInClock.begin();
305  int clockEdge = 1;
306  for (const auto& ihit : iAxialHit) {
307  B2DEBUG(20, "sorted TDC count: " << m_cdcHits[ihit]->getTDCCount() <<
308  ", SL" << m_cdcHits[ihit]->getISuperLayer() << ", layer" << m_cdcHits[ihit]->getILayer() <<
309  ", wire " << m_cdcHits[ihit]->getIWire() << ", ihit: " << ihit);
310  // if (m_cdcHits[ihit]->getTDCCount() >= 4999) {
311  // B2DEBUG(20, "skipping this hit");
312  // continue;
313  // }
314  // if (trgTime(ihit, iAxialHit.front()) >= clockEdge * clockPeriod) {
315  while (trgTime(ihit, iAxialHit.front()) >= clockEdge * clockPeriod) {
316  ++clockEdge;
317  ++itr;
318  }
319  itr->push_back(ihit);
320  }
321  m_iFirstHit = iAxialHit.front();
322 
323  // In addition, clear the list holding priority hit indices
324  for (auto& clock : m_priorityHit) {
325  for (auto& sl : clock) {
326  for (auto& merger : sl.second) {
327  merger.clear();
328  }
329  }
330  }
331 }
332 
333 bool TSF::notHit(MergerOut field, unsigned iTS, TSF::registeredStructElement& reg)
334 {
335  return ! reg[field][iTS];
336 }
337 
338 void TSF::registerHit(MergerOut field, unsigned iTS, TSF::registeredStructElement& reg)
339 {
340  reg[field].set(iTS);
341 }
342 
343 void TSF::setSecondPriority(unsigned priTS,
344  unsigned iHit,
345  timeVec hitTime,
346  unsigned lr,
347  mergerStructElement<5>& mergerData,
348  registeredStructElement& registeredCell,
349  priorityHitInMerger& priorityHit)
350 {
351  timeVec& priorityTime = (get<MergerOut::priorityTime>(mergerData))[priTS];
352  // when there is not already a (1st or 2nd priority) hit
353  if (notHit(MergerOut::priorityTime, priTS, registeredCell)) {
354  priorityTime = hitTime;
355  registerHit(MergerOut::priorityTime, priTS, registeredCell);
356  get<MergerOut::secondPriorityHit>(mergerData)[0].set(priTS, ! lr);
357  priorityHit.insert({priTS, iHit});
358  // set the 2nd pri bit to right when T_left == T_right
359  } else if (priority(priorityHit[priTS]) == Priority::second &&
360  hitTime.to_ulong() == priorityTime.to_ulong() && lr == 1) {
361  get<MergerOut::secondPriorityHit>(mergerData)[0].reset(priTS);
362  priorityHit[priTS] = iHit;
363  }
364 }
365 
366 void TSF::simulateMerger(unsigned iClock)
367 {
368  // clean up TSF input signals
369  for (auto& sl : inputToTSF) {
370  for (auto& merger : sl) {
371  merger.fill(zero_val);
372  }
373  }
374  // don't do simulation if there are no more CDC hits
375  if (iClock >= iAxialHitInClock.size()) {
376  return;
377  }
378  // mergerStruct<5> innerDataInClock(nAxialMergers[0]);
379  // map<unsigned, mergerStruct<3> > outerDataInClock;
380  // mergers in outer super layer has only 3 edge fields,
381  // but for the sake of coding simplicity and consistency,
382  // let's declare them as the same type of SL0,
383  // at the cost of 2 wasted slots.
384  map<unsigned, mergerStruct<5> > dataInClock;
385  map<unsigned, registeredStruct> registered;
386  for (int iAx = 0; iAx < m_nSubModules; ++iAx) {
387  dataInClock.insert({2 * iAx, mergerStruct<5> (nAxialMergers[iAx])});
388  registered.insert({2 * iAx, registeredStruct(nAxialMergers[iAx])});
389  }
390  auto clock = iAxialHitInClock[iClock];
391  // move first priority hits forward, so that we can just use the 1st hit
392  // but still preserve order in each group
393  std::stable_partition(clock.begin(), clock.end(),
394  [this](int hit) {return priority(hit) == Priority::first;});
395  for (const auto& iHit : clock) {
396  const CDCHit& hit = *m_cdcHits[iHit];
397  const unsigned short iSL = hit.getISuperLayer();
398  const short outer = (iSL == 0) ? 0 : 1;
399  const unsigned short iMerger = mergerNumber(iHit);
400  const unsigned short iCell = mergerCellID(iHit);
401  const WireSet tsList = segmentID(iHit);
402  const timeVec hitTime = timeStamp(iHit, m_iFirstHit);
403  auto& mergerData = dataInClock[iSL][iMerger];
404  auto& registeredCell = registered[iSL][iMerger];
405  // register the priority hit in the corresponding merger
406  auto& priorityHit = m_priorityHit[iClock][iSL][iMerger];
407  B2DEBUG(50, "iHit: " << iHit << ", Merger" << iSL << "-" << iMerger <<
408  ", iCell: " << iCell);
409  // update hit map
410  get<MergerOut::hitmap>(mergerData)[0].set(iCell);
411  // update fastest time
412  for (const auto& iTS : tsList) {
413  timeVec& fastestTime = (get<MergerOut::fastestTime>(mergerData))[iTS];
414  // when there is not already a hit
415  if (notHit(MergerOut::fastestTime, iTS, registeredCell) ||
416  // or when this one is faster
417  hitTime.to_ulong() < fastestTime.to_ulong()) {
418  // note: if we move this up to before partition, we won't need to
419  // compare timing, since they are already sorted. However,
420  // leaving it here makes the code a bit cleaner.
421  fastestTime = hitTime;
422  registerHit(MergerOut::fastestTime, iTS, registeredCell);
423  }
424  }
425  // get edge hit timing or local fastest time
426  if (m_edge[outer].find(iCell) != m_edge[outer].end()) {
427  for (auto& iEdgeTime : m_edge[outer][iCell]) {
428  timeVec& edgeTime = (get<MergerOut::edgeTime>(mergerData))[iEdgeTime];
429  if (notHit(MergerOut::edgeTime, iEdgeTime, registeredCell) ||
430  hitTime.to_ulong() < edgeTime.to_ulong()) {
431  edgeTime = hitTime;
432  registerHit(MergerOut::edgeTime, iEdgeTime, registeredCell);
433  }
434  }
435  }
436  switch (priority(iHit)) {
437  case Priority::first: {
438  // update priority time
439  unsigned priTS = iCell % nSegmentsInMerger;
440  /* cppcheck-suppress variableScope */
441  timeVec& priorityTime = (get<MergerOut::priorityTime>(mergerData))[priTS];
442  // when there is not already a (first priority) hit
443  if (notHit(MergerOut::priorityTime, priTS, registeredCell)) {
444  priorityTime = hitTime;
445  registerHit(MergerOut::priorityTime, priTS, registeredCell);
446  get<MergerOut::secondPriorityHit>(mergerData)[0].reset(priTS);
447  priorityHit.insert({priTS, iHit});
448  }
449  break;
450  }
451  case Priority::second: {
452  // update the 2 priority times and the sc bits
453  for (unsigned i = 0; i < 2; ++i) {
454  unsigned priTS = iCell % nSegmentsInMerger + i;
455  if (priTS == nSegmentsInMerger) {
456  // crossing the left edge
457  // set the 2nd priority left of the first TS in the next merger
458  priTS = 0;
459  const unsigned short iMergerPlus1 = (iMerger == nMergers[iSL] - 1) ? 0 : iMerger + 1;
460  auto& nextMergerData = dataInClock[iSL][iMergerPlus1];
461  auto& nextRegisteredCell = registered[iSL][iMergerPlus1];
462  auto& nextPriorityHit = m_priorityHit[iClock][iSL][iMergerPlus1];
463  setSecondPriority(priTS, iHit, hitTime, i, nextMergerData, nextRegisteredCell, nextPriorityHit);
464  } else {
465  setSecondPriority(priTS, iHit, hitTime, i, mergerData, registeredCell, priorityHit);
466  }
467  }
468  break;
469  }
470  default:
471  break;
472  }
473  }
474  // pack the output from merger into input to TSF
475  for (auto iAx = 0; iAx < m_nSubModules; ++iAx) {
476  unsigned nEdges = (iAx == 0) ? 5 : 3;
477  for (unsigned iMerger = 0; iMerger < inputToTSF[iAx].size(); ++iMerger) {
478  auto input = inputToTSF[iAx][iMerger].rbegin();
479  auto& output = dataInClock[2 * iAx][iMerger];
480  pack<MergerOut::hitmap, 1> (input, nWiresInMerger, output);
481  pack<MergerOut::priorityTime, timeWidth> (input, nCellsInLayer, output);
482  pack<MergerOut::fastestTime, timeWidth> (input, nCellsInLayer, output);
483  pack<MergerOut::secondPriorityHit, 1> (input, nCellsInLayer, output);
484  pack<MergerOut::edgeTime, timeWidth> (input, nEdges, output);
485  /* cppcheck-suppress variableScope */
486  auto& outputAcrossClocks = dataAcrossClocks[2 * iAx][iMerger];
487  if (get<MergerOut::hitmap>(output)[0].any()) {
488  string priTime, fasTime, edgTime;
489  for (int i = 0; i < nSegmentsInMerger; ++i) {
490  priTime += get<MergerOut::priorityTime>(output)[i].to_string() + ",";
491  fasTime += get<MergerOut::fastestTime>(output)[i].to_string() + ",";
492  edgTime += get<MergerOut::edgeTime>(output)[i].to_string() + ",";
493  get<MergerOut::priorityTime>(outputAcrossClocks)[i] |=
494  get<MergerOut::priorityTime>(output)[i];
495  get<MergerOut::fastestTime>(outputAcrossClocks)[i] |=
496  get<MergerOut::fastestTime>(output)[i];
497  }
498  B2DEBUG(150, "merger " << iAx * 2 << "-" << iMerger <<
499  "\nhitmap: " << get<MergerOut::hitmap>(output)[0] <<
500  "\nsecond hit: " << get<MergerOut::secondPriorityHit>(output)[0] <<
501  "\nprioiry Time: " << priTime <<
502  "\nfastest Time: " << fasTime <<
503  "\nedge Time: " << edgTime);
504  get<MergerOut::hitmap>(outputAcrossClocks)[0] |= get<MergerOut::hitmap>(output)[0];
505  }
506  // simulate clock counter
507  if (m_simulateCC) {
508  bitset<9> cc(iClock);
509  const int nBitsReserved = (iAx == 0) ? 3 : 4;
510  generate(inputToTSF[iAx][iMerger].rend() - 9 - nBitsReserved,
511  inputToTSF[iAx][iMerger].rend() - nBitsReserved,
512  [&cc, i = 0]() mutable {
513  return (cc[i++]) ? one_val : zero_val;});
514  }
515  }
516  }
517 }
518 
519 template<MergerOut field, size_t width>
520 void TSF::pack(inputVector::reverse_iterator& input, unsigned number,
521  mergerStructElement<5>& output)
522 {
523  std::generate(input, input + number * width, [&, n = 0]() mutable {
524  int i = n++;
525  if (width == 1)
526  {
527  return (get<field>(output)[0][i]) ? one_val : zero_val;
528  } else {
529  return (get<field>(output)[i / width][i % width]) ? one_val : zero_val;}});
530  input += number * width;
531 }
532 
534 {
535  m_bitsTo2D.appendNew(outputToTracker);
536 }
537 
538 constexpr std::array<int, 9> TSF::nMergers;
539 
540 void TSF::saveFastOutput(short iclock)
541 {
542  const int ccWidth = 9;
543  // number of widended clocks in TSF
544  const int widenedClocks = 16;
545  // TSF latency in unit of data clocks
546  const int latency = 3;
547  // N.B. widenedClocks and latency might be inaccurate
548 
549  int iAx = 0;
550  for (const auto& sl : outputToTracker) {
551  // keep a list of found TS hit in the same SL so that outputs to different
552  // trackers won't be recorded twice
553  std::map<unsigned, int> foundTS;
554  unsigned short iTracker = 0;
555  for (auto& tracker : sl) {
556  string output = slv_to_bin_string(tracker);
557  // loop through all TS hits in the output
558  for (int i = ccWidth; i < width_out; i += tsInfoWidth) {
559  string ts = output.substr(i, tsInfoWidth);
560  tsOut decoded = decodeTSHit(ts);
561  // finish if no more TS hit is present
562  if (decoded[3] == 0) {
563  break;
564  }
565  const unsigned nCellsInSL = nAxialMergers[iAx] * nCellsInLayer;
566  // get global TS ID
567  unsigned iTS = decoded[0] + nCellsInSL * iTracker / nTrackers;
568  // periodic ID overflow when phi0 > 0 for the 4th tracker
569  if (iTS >= nCellsInSL) {
570  iTS -= nCellsInSL;
571  }
572  // ID in SL8 is shifted by 16
573  if (iAx == 4) {
574  if (iTS < 16) {
575  iTS += nCellsInSL;
576  }
577  iTS -= 16;
578  }
579  int iHit = -1;
580  int firstClock = iclock - widenedClocks - latency;
581  if (firstClock < 0) {
582  firstClock = 0;
583  }
584  int lastClock = iclock - latency;
585  B2DEBUG(10, "iAx:" << iAx << ", iTS:" << iTS << ", iTracker: " << iTracker);
586  // scan through all CDC hits to get the priority hit
587  for (int iclkPri = firstClock; iclkPri < lastClock; ++iclkPri) {
588  // get the map storing the hit index in the corresponding merger
589  auto priMap = m_priorityHit[iclkPri][2 * iAx][iTS / nSegmentsInMerger];
590  unsigned itsInMerger = iTS % nSegmentsInMerger;
591  // Pick up the first CDCHit which agrees to the priority position
592  // of firmware sim output
593  if (priMap.find(itsInMerger) != priMap.end() &&
594  toPriority(decoded[3]) == priority(priMap[itsInMerger])) {
595  iHit = priMap[itsInMerger];
596  B2DEBUG(10, "iHit:" << iHit);
597  B2DEBUG(10, "TDC: " << m_cdcHits[iHit]->getTDCCount() << ", TSF: " << decoded[1]);
598  break;
599  }
600  }
601  // check if the same TS hit to another tracker is already there
602  // TODO: some duplicates are from different clocks,
603  // so they won't be caught here
604  // This mostly likely means the wrong phase of TSF firmware output is used
605  if (foundTS.find(iTS) != foundTS.end()) {
606  if (iHit != foundTS[iTS]) {
607  B2WARNING("Same TS ID exists, but they point to different CDC hit");
608  }
609  continue;
610  }
611  foundTS.insert({iTS, iHit});
612  if (iHit < 0) {
613  B2WARNING("No corresponding priority CDC hit can be found.");
614  B2WARNING("Maybe the latency and number of widened clocks are wrong.");
615  B2WARNING("In event " << StoreObjPtr<EventMetaData>()->getEvent());
616  B2DEBUG(20, "priority " << decoded[3]);
617  for (int iclkPri = 0; iclkPri < m_nClockPerEvent; ++iclkPri) {
618  auto priMap = m_priorityHit[iclkPri][2 * iAx][iTS / 16];
619  for (auto& m : priMap) {
620  B2DEBUG(20, "iWire: " << m.first << ", iLayer: " <<
621  m_cdcHits[m.second]->getILayer() << ", iClock: " << iclkPri);
622  }
623  }
624  if (iclock < 15) {
625  B2WARNING("It could be the left over TS hits from the last event.\n" <<
626  "Try increasing m_nClockPerEvent");
627  }
628  }
629  CDCHit cdchit = (iHit < 0) ? CDCHit() : *m_cdcHits[iHit];
630  m_tsHits.appendNew(cdchit,
631  globalSegmentID(iTS, 2 * iAx),
632  decoded[3], // priority position
633  decoded[2], // L/R
634  // TODO: pri time should be the same as the CDCHit in 1st arg
635  // when the proper clock counter is given to firmware TSF
636  decoded[1],
637  // TODO: fastest time from ETF output
638  0,
639  iclock);
640  }
641  ++iTracker;
642  }
643  ++iAx;
644  }
645 }
646 
648 {
649  // TODO: what if there is 0 CDC hit?
650  int status = 0;
651  initializeMerger();
652  try {
653  // clear accumulative merger output
654  for (unsigned iAx = 0; iAx < m_nSubModules; ++iAx) {
655  dataAcrossClocks[2 * iAx] = mergerStruct<5> (nAxialMergers[iAx]);
656  }
657  for (int iClock = 0; iClock < m_nClockPerEvent; iClock++) {
658  B2INFO(string(15, '=') << " clock #" << iClock << " " << string(15, '='));
659  if (iClock == m_nClockPerEvent - 1) {
660  cout << "Accumulative hitmap:" << "\n";
661  }
662  simulateMerger(iClock);
663  if (m_mergerOnly) {
664  cout << std::hex;
665  int iSL = 0;
666  for (const auto& sl : inputToTSF) {
667  int iMerger = 0;
668  for (const auto& mergerOut : sl) {
669  if (std::any_of(mergerOut.begin(), mergerOut.end(), [](char i) {
670  return i != zero_val;
671  })) {
672  cout << "Merger " << iSL * 2 << "-" << iMerger / 2 << " u" << iMerger % 2 << ": ";
673  display_hex(mergerOut);
674  }
675  // print accumulative hitmap
676  if (iClock == m_nClockPerEvent - 1) {
677  auto accuOut = dataAcrossClocks[2 * iSL][iMerger];
678  if (get<MergerOut::hitmap>(accuOut)[0].any()) {
679  cout << "Merger " << iSL * 2 << "-" << iMerger / 2 << " u" << iMerger % 2 << ": ";
680  cout << get<MergerOut::hitmap>(accuOut)[0] << "\n";
681  string priTime, fasTime;
682  for (int i = 0; i < 16; ++i) {
683  priTime += get<MergerOut::priorityTime>(accuOut)[i].to_string() + ",";
684  fasTime += get<MergerOut::fastestTime>(accuOut)[i].to_string() + ",";
685  }
686  cout << "pritime: " << priTime << "\n";
687  cout << "fastime: " << fasTime << "\n";
688  }
689  }
690  iMerger++;
691  }
692  iSL++;
693  }
694  cout << std::dec;
695  continue;
696  }
697 
698  auto TSF0 = getData<0>(inputToTSF);
699  auto TSF2 = getData<1>(inputToTSF);
700  auto TSF4 = getData<2>(inputToTSF);
701  auto TSF6 = getData<3>(inputToTSF);
702  auto TSF8 = getData<4>(inputToTSF);
703  array<char*, m_nSubModules> rawInputToTSF = {TSF0, TSF2, TSF4, TSF6, TSF8};
704 
705  for (unsigned iSL = 0; iSL < m_nSubModules; ++iSL) {
706  B2DEBUG(100, "input to TSF" << iSL * 2 << ": \n" <<
707  display_value(rawInputToTSF[iSL], nAxialMergers[iSL] * mergerWidth));
708  write(rawInputToTSF[iSL], stream[iSL][0]);
709  }
710  // don't mess up stdout order with child processes
711  if (m_debugLevel >= 50) {
712  usleep(2000 * m_debugLevel);
713  }
714  for (unsigned iSL = 0; iSL < m_nSubModules; ++iSL) {
715  B2DEBUG(50, "Reading buffer from TSF " << iSL * 2 << ":");
716  outputToTracker[iSL] = read(stream[iSL][1]);
717  B2DEBUG(30, "received TSF " << iSL * 2 << ":");
718  if (m_debugLevel >= 30) {
719  for (const auto& out : outputToTracker[iSL]) {
720  display_hex(out);
721  }
722  }
723  }
724  saveFirmwareOutput();
725  saveFastOutput(iClock);
726  }
727  } catch (std::exception& e) {
728  B2ERROR("ERROR: An exception occurred: " << e.what());
729  status = 2;
730  } catch (...) {
731  B2ERROR("ERROR: An unknown exception occurred.");
732  status = 3;
733  }
734  B2DEBUG(50, "status code:" << status);
735 }
Belle2::CDCTriggerTSFFirmwareModule::mergerCellID
unsigned short mergerCellID(int index)
Get the cell ID in the merger.
Definition: CDCTriggerTSFFirmwareModule.cc:237
Belle2::CDCTriggerTSFFirmwareModule::mergerStructElement
std::tuple< std::array< timeVec, nSegmentsInMerger >, std::array< timeVec, nSegmentsInMerger >, std::array< timeVec, nEdges >, std::array< std::bitset< nWiresInMerger >, 1 >, std::array< std::bitset< nSegmentsInMerger >, 1 > > mergerStructElement
data structure to hold merger output <priority time (4 bits x 16), fast time (4 bits x 16),...
Definition: CDCTriggerTSFFirmwareModule.h:259
Belle2::CDCTriggerTSFFirmwareModule::outputArray
std::array< outputVector, nTrackers > outputArray
output array
Definition: CDCTriggerTSFFirmwareModule.h:128
Belle2::CDCTriggerTSFFirmwareModule::pack
void pack(inputVector::reverse_iterator &rInput, unsigned number, mergerStructElement< 5 > &output)
Pack the merger output data structure to TSF input vector.
Definition: CDCTriggerTSFFirmwareModule.cc:520
Belle2::CDCTriggerTSFFirmwareModule::terminate
void terminate() override
close the pipes and wait for children to die.
Definition: CDCTriggerTSFFirmwareModule.cc:181
Belle2::CDCTriggerTSFFirmwareModule::priority
CDCTrigger::Priority priority(int index)
write TSF input signals to the worker
Definition: CDCTriggerTSFFirmwareModule.cc:81
Belle2::CDCTriggerTSFFirmwareModule::write
void write(const char *message, FILE *outstream)
write TSF input signals to the worker
Definition: CDCTriggerTSFFirmwareModule.cc:92
REG_MODULE
#define REG_MODULE(moduleName)
Register the given module (without 'Module' suffix) with the framework.
Definition: Module.h:652
Cosim::slv_to_bin_string
std::string slv_to_bin_string(std::array< char, N > signal, bool padding=false)
Transform into string.
Definition: Cosim.h:57
Belle2::CDCHit
Class containing the result of the unpacker in raw data and the result of the digitizer in simulation...
Definition: CDCHit.h:51
Cosim
Helper class for software (C++) / firmware (VHDL) co-simulation.
Definition: Cosim.h:15
Belle2::CDCTriggerTSFFirmwareModule
This class is the interface between TSim/Basf2 TSF module and the firmware simulation core of XSim/IS...
Definition: CDCTriggerTSFFirmwareModule.h:48
Belle2::CDCTriggerTSFFirmwareModule::mergerNumber
unsigned short mergerNumber(int index)
Get the merger unit ID in a super layer.
Definition: CDCTriggerTSFFirmwareModule.cc:245
Belle2::CDCTriggerTSFFirmwareModule::notHit
bool notHit(CDCTrigger::MergerOut field, unsigned iTS, registeredStructElement &reg)
Whether a time field in a merger has been hit in the clock cycle.
Belle2::CDCTriggerTSFFirmwareModule::timeStamp
std::bitset< 4 > timeStamp(int index, int iFirstHit)
Get the trigger time stamp of a hit.
Definition: CDCTriggerTSFFirmwareModule.cc:232
Belle2::CDCTriggerTSFFirmwareModule::getData
char * getData(inputToTSFArray)
get the XSI compliant format from the bits format TSF input
Definition: CDCTriggerTSFFirmwareModule.cc:209
Cosim::display_hex
void display_hex(const std::array< char, N > &signal)
Display signal in hex.
Definition: Cosim.h:74
Cosim::zero_val
const char zero_val
'0' in XSI VHDL simulation
Definition: Cosim.h:38
Belle2::CDCTriggerTSFFirmwareModule::nMergers
static constexpr std::array< int, 9 > nMergers
number of mergers in each super layer
Definition: CDCTriggerTSFFirmwareModule.h:77
Belle2::CDCTriggerTSFFirmwareModule::initialize
void initialize() override
spawn child process for workers, open pipes to pass data
Definition: CDCTriggerTSFFirmwareModule.cc:122
Belle2::Module
Base class for Modules.
Definition: Module.h:74
Belle2::CDCTriggerTSFFirmwareModule::segmentID
WireSet segmentID(int iHit)
Get the list of associated track segments with a hit.
Definition: CDCTriggerTSFFirmwareModule.cc:254
Belle2::CDCTriggerTSFFirmwareModule::simulateMerger
void simulateMerger(unsigned iclock)
Simulate 1 clock of merger.
Definition: CDCTriggerTSFFirmwareModule.cc:366
Belle2::CDCTriggerTSFFirmwareModule::registerHit
void registerHit(CDCTrigger::MergerOut field, unsigned iTS, registeredStructElement &reg)
Register the timing field so that later hits won't overwrite it.
Belle2::mergerWidth
static constexpr int mergerWidth
Merger data width.
Definition: CDCTriggerUnpackerModule.h:53
Belle2::CDCTriggerTSFFirmwareModule::mergerStruct
std::vector< mergerStructElement< nEdges > > mergerStruct
data structure to hold merger output
Definition: CDCTriggerTSFFirmwareModule.h:262
Belle2
Abstract base class for different kinds of events.
Definition: MillepedeAlgorithm.h:19
Belle2::StoreObjPtr
Type-safe access to single objects in the data store.
Definition: ParticleList.h:33
Belle2::CDCTriggerTSFFirmwareModule::priorityHitInMerger
std::map< unsigned, int > priorityHitInMerger
priority hits map in Merger
Definition: CDCTriggerTSFFirmwareModule.h:273
Belle2::CDCTriggerTSFFirmwareModule::TSMap
std::unordered_map< short, WireSet > TSMap
TS map.
Definition: CDCTriggerTSFFirmwareModule.h:288
Belle2::CDCTriggerTSFFirmwareModule::nAxialMergers
static constexpr std::array< int, m_nSubModules > nAxialMergers
number of mergers in axial super layers
Definition: CDCTriggerTSFFirmwareModule.h:80
Belle2::tsOut
std::array< unsigned, 4 > tsOut
TS.
Definition: CDCTrigger2DFinderFirmwareModule.h:25
Cosim::one_val
const char one_val
'1' in XSI VHDL simulation
Definition: Cosim.h:36
Belle2::CDCTriggerTSFFirmwareModule::saveFastOutput
void saveFastOutput(short iclock)
save fast TSIM output
Definition: CDCTriggerTSFFirmwareModule.cc:540
Belle2::CDCTriggerTSFFirmwareModule::saveFirmwareOutput
void saveFirmwareOutput()
save firmware output
Definition: CDCTriggerTSFFirmwareModule.cc:533
Belle2::CDCTriggerTSFFirmwareModule::event
void event() override
Things to do for each event.
Definition: CDCTriggerTSFFirmwareModule.cc:647
Belle2::CDCTriggerTSFFirmwareModule::timeVec
std::bitset< timeWidth > timeVec
element of data structure to hold merger output
Definition: CDCTriggerTSFFirmwareModule.h:246
Belle2::CDCTriggerTSFFirmwareModule::computeEdges
void computeEdges()
Compute the map from merger cell ID to all its related edge fields.
Definition: CDCTriggerTSFFirmwareModule.cc:194
Belle2::CDCTriggerTSFFirmwareModule::WireSet
std::vector< short > WireSet
Wire set.
Definition: CDCTriggerTSFFirmwareModule.h:286
Belle2::CDCTriggerTSFFirmwareModule::registeredStructElement
std::array< std::bitset< nCellsInLayer >, 3 > registeredStructElement
record when a time slow has been registered by a hit <priority time, fast time, edge timing>
Definition: CDCTriggerTSFFirmwareModule.h:268
Belle2::CDCTriggerTSFFirmwareModule::CDCTriggerTSFFirmwareModule
CDCTriggerTSFFirmwareModule()
Constructor.
Definition: CDCTriggerTSFFirmwareModule.cc:42
Belle2::CDCTriggerTSFFirmwareModule::inputToTSFArray
std::array< inputFromMerger, m_nSubModules > inputToTSFArray
input array to TSF
Definition: CDCTriggerTSFFirmwareModule.h:199
Belle2::CDCTriggerTSFFirmwareModule::read
outputArray read(FILE *instream)
write TSF output signals from the worker
Definition: CDCTriggerTSFFirmwareModule.cc:99
Cosim::display_value
std::string display_value(const char *count, int size)
Display value of the signal.
Definition: Cosim.h:41
Belle2::CDCTriggerTSFFirmwareModule::setSecondPriority
void setSecondPriority(unsigned priTS, unsigned iHit, timeVec hitTime, unsigned lr, mergerStructElement< 5 > &mergerData, registeredStructElement &registeredCell, priorityHitInMerger &priorityHit)
set 2nd priority info
Definition: CDCTriggerTSFFirmwareModule.cc:343
Belle2::CDCTriggerTSFFirmwareModule::initializeMerger
void initializeMerger()
Get CDC hits from the DataStore and distribute them to clocks.
Definition: CDCTriggerTSFFirmwareModule.cc:278
Belle2::CDCTriggerTSFFirmwareModule::trgTime
unsigned short trgTime(int index, int iFirstHit)
Get the trigger time of the CDC hit.
Definition: CDCTriggerTSFFirmwareModule.cc:222
Belle2::CDCTriggerTSFFirmwareModule::registeredStruct
std::vector< registeredStructElement > registeredStruct
vector of registeredStructElement
Definition: CDCTriggerTSFFirmwareModule.h:270