Belle II Software  release-08-01-10
CDCTriggerTSFFirmwareModule.cc
1 /**************************************************************************
2  * basf2 (Belle II Analysis Software Framework) *
3  * Author: The Belle II Collaboration *
4  * *
5  * See git log for contributors and copyright holders. *
6  * This file is licensed under LGPL-3.0, see LICENSE.md. *
7  **************************************************************************/
8 
9 #include <trg/cdc/modules/trgcdc/CDCTriggerTSFFirmwareModule.h>
10 #include <trg/cdc/CDCTrigger.h>
11 #include <trg/cdc/Cosim.h>
12 #include <framework/datastore/StoreArray.h>
13 #include <framework/datastore/StoreObjPtr.h>
14 #include <framework/dataobjects/EventMetaData.h>
15 #include <trg/cdc/dataobjects/CDCTriggerSegmentHit.h>
16 #include <cdc/dataobjects/CDCHit.h>
17 #include <framework/logging/Logger.h>
18 
19 #include <vector>
20 #include <array>
21 #include <bitset>
22 #include <string>
23 #include <algorithm>
24 #include <numeric>
25 
26 #include <cstdio>
27 #include <unistd.h>
28 #include <sys/wait.h>
29 
30 using namespace Belle2;
31 using namespace Cosim;
32 using namespace std;
33 using namespace CDCTrigger;
35 
36 REG_MODULE(CDCTriggerTSFFirmware);
37 
38 constexpr std::array<int, TSF::m_nSubModules> TSF::nAxialMergers;
39 
41  Module(), m_cdcHits{""}
42 
43 {
44  for (unsigned iAx = 0; iAx < m_nSubModules; ++iAx) {
45  inputToTSF[iAx].resize(nAxialMergers[iAx]);
46  for (auto& clock : m_priorityHit) {
47  clock.insert({2 * iAx, priorityHitStructInSL(nAxialMergers[iAx])});
48  }
49  dataAcrossClocks.insert({2 * iAx, mergerStruct<5> (nAxialMergers[iAx])});
50  }
51  //Set module properties
52  setDescription("Firmware simulation of the Track Segment Finder for CDC trigger.");
53 
54  // Define module parameters
55  addParam("hitCollectionName", m_hitCollectionName,
56  "Name of the input StoreArray of CDCHits.",
57  string("CDCHits"));
58  addParam("outputCollectionName", m_outputCollectionName,
59  "Name of the StoreArray holding the found TS hits.",
60  string("CDCTriggerFirmwareSegmentHits"));
61  addParam("outputBitstreamNameTo2D", m_outputBitstreamNameTo2D,
62  "Name of the StoreArray holding the raw bit content to 2D trackers",
63  string("BitstreamTSFto2D"));
64  addParam("mergerOnly", m_mergerOnly,
65  "Flag to only simulate merger and not TSF",
66  false);
67  addParam("simulateCC", m_simulateCC,
68  "Flag to run the front-end clock counter",
69  false);
70  std::vector<bool> defaultStub(m_nSubModules, false);
71  addParam("stubLUT", m_stubLUT,
72  "list of flags to run each TSF firmware simulation with dummy L/R LUT (to speed up loading)",
73  defaultStub);
74 }
75 
76 TSF::~CDCTriggerTSFFirmwareModule()
77 {}
78 
79 Priority TSF::priority(int index)
80 {
81  CDCHit* hit = m_cdcHits[index];
82  int offset = (hit->getISuperLayer() == 0) ? 1 : 0;
83  switch (hit->getILayer() - 2 - offset) {
84  case 0: return Priority::first;
85  case 1: return Priority::second;
86  default: return Priority::nothing;
87  }
88 }
89 
90 void TSF::write(const char* message, FILE* outstream)
91 {
92  // write input to TSF firmware
93  fprintf(outstream, "%s\n", message);
94  fflush(outstream);
95 }
96 
97 TSF::outputArray TSF::read(FILE* instream)
98 {
99  // read output from TSF firmware
100  array<char, 2048> buffer;
101  outputArray output;
102  buffer.fill(one_val);
103  if (fgets(buffer.data(), buffer.size(), instream) == NULL) {
104  B2ERROR("fgets reached end unexpectedly");
105  return output;
106  }
107  // ins->getline(buffer.data(), buffer.size());
108  for (auto i2d = 0; i2d < 4; ++i2d) {
109  B2DEBUG(50, display_value(buffer.data() + width_out * i2d, width_out));
110  }
111  auto bufferItr = buffer.cbegin();
112  // for (int iTracker = 0; iTracker < nTrackers; ++iTracker) {
113  for (int iTracker = nTrackers - 1; iTracker >= 0; --iTracker) {
114  copy(bufferItr, bufferItr + width_out, output[iTracker].begin());
115  bufferItr += width_out;
116  }
117  return output;
118 }
119 
121 {
122  m_cdcHits.isRequired(m_hitCollectionName);
123  m_debugLevel = getLogConfig().getDebugLevel();
124  if (m_mergerOnly) {
125  computeEdges();
126  return;
127  }
128  m_bitsTo2D.registerInDataStore(m_outputBitstreamNameTo2D);
129  m_tsHits.registerInDataStore(m_outputCollectionName);
130  for (unsigned i = 0; i < m_nSubModules; ++i) {
131  // i: input to worker (output from module)
132  // o: output from worker (input to module)
133  /* Create pipe and place the two-end pipe file descriptors*/
134  pipe(inputFileDescriptor[i].data());
135  pipe(outputFileDescriptor[i].data());
136  string str_fd[] = {to_string(inputFileDescriptor[i][0]), to_string(outputFileDescriptor[i][1])};
137  // spawn a child process
138  pid_t pid = fork();
139  // pid_t pid = 1;
140  if (pid < 0) {
141  B2FATAL("Fork failed!");
142  } else if (pid == (pid_t) 0) {
143  /* Child process (worker) */
144  // close the unused ends of the file descriptors
145  close(inputFileDescriptor[i][1]);
146  close(outputFileDescriptor[i][0]);
147  if (m_stubLUT[i]) {
148  design_libname_post = "_stub" + design_libname_post;
149  }
150  string design = design_libname_pre + to_string(i * 2) + design_libname_post;
151  string waveform = wdbName_pre + to_string(i * 2) + wdbName_post;
152  // execute the standalone worker program
153  execlp("CDCTriggerTSFFirmwareWorker", "CDCTriggerTSFFirmwareWorker",
154  str_fd[0].c_str(), str_fd[1].c_str(), design.c_str(), waveform.c_str(),
155  to_string(nAxialMergers[i]).c_str(), nullptr);
156  B2FATAL("The firmware simulation program didn't launch!");
157  } else {
158  /* Parent process (basf2) */
159  B2DEBUG(100, "parent " << i);
160  m_pid[i] = pid;
161  // Close the copy of the fds read/write end
162  close(inputFileDescriptor[i][0]);
163  close(outputFileDescriptor[i][1]);
164  // open the fds
165  stream[i][0] = fdopen(inputFileDescriptor[i][1], "w");
166  stream[i][1] = fdopen(outputFileDescriptor[i][0], "r");
167  // ins = createInStreamFromFD(outputFileDescriptor[i][0]);
168  B2DEBUG(20, "pid for TSF" << i * 2 << ": " << m_pid[i]);
169  }
170  }
171  computeEdges();
172  B2INFO("It can take a while for TSF0 to load the LUT.");
173  for (unsigned i = 0; i < m_nSubModules; ++i) {
174  // wait for worker initialization
175  read(stream[i][1]);
176  }
177 }
178 
180 {
181  B2DEBUG(10, "Waiting for TSF firmware termination...");
182  for (unsigned i = 0; i < m_nSubModules; ++i) {
183  close(inputFileDescriptor[i][1]);
184  close(outputFileDescriptor[i][0]);
185  }
186  // wait for all processes to exit
187  for (unsigned i = 0; i < m_nSubModules; ++i) {
188  wait(nullptr);
189  }
190 }
191 
193 {
194  for (unsigned short iEdge = 0; iEdge < 5; ++iEdge) {
195  for (const auto& cell : innerInvEdge[iEdge]) {
196  m_edge[0][cell].push_back(iEdge);
197  }
198  }
199  for (unsigned short iEdge = 0; iEdge < 3; ++iEdge) {
200  for (const auto& cell : outerInvEdge[iEdge]) {
201  m_edge[1][cell].push_back(iEdge);
202  }
203  }
204 }
205 
206 template<int iSL>
208 {
209  static array<char, mergerWidth* nAxialMergers[iSL]> data;
210  // 2-D array in XSI is totally in LSB, like this
211  // ((012), (345), (678))
212  auto itr = data.end() - mergerWidth;
213  for (const auto& merger : input[iSL]) {
214  copy(merger.begin(), merger.end(), itr);
215  itr -= mergerWidth;
216  }
217  return data.data();
218 }
219 
220 unsigned short TSF::trgTime(int index, int iFirstHit)
221 {
222  if (m_allPositiveTime) {
223  return (m_cdcHits[iFirstHit]->getTDCCount() / 2 - m_cdcHits[index]->getTDCCount() / 2);
224  } else {
225  short time = (m_TDCCountForT0 / 2 - m_cdcHits[index]->getTDCCount() / 2);
226  return (time < 0) ? time + (1 << 9) : time;
227  }
228 }
229 
230 std::bitset<4> TSF::timeStamp(int index, int iFirstHit)
231 {
232  return std::bitset<4> (trgTime(index, iFirstHit) % clockPeriod);
233 }
234 
235 unsigned short TSF::mergerCellID(int index)
236 {
237  const CDCHit& hit = (*m_cdcHits[index]);
238  const unsigned offset = (hit.getISuperLayer() == 0) ? 3 : 0;
239  return (hit.getILayer() - offset) * nSegmentsInMerger +
240  hit.getIWire() % nSegmentsInMerger;
241 }
242 
243 unsigned short TSF::mergerNumber(int index)
244 {
245  const CDCHit& hit = (*m_cdcHits[index]);
246  return hit.getIWire() / nSegmentsInMerger;
247 }
248 
249 using WireSet = std::vector<unsigned short>;
250 using TSMap = std::unordered_map<int, WireSet>;
251 
253 {
254  const bool innerMost = (m_cdcHits[iHit]->getISuperLayer() == 0);
255  const unsigned short iLayer = m_cdcHits[iHit]->getILayer();
256  const unsigned short iCell = mergerCellID(iHit);
257  TSMap& tsMap = m_tsMap[static_cast<int>(! innerMost)];
258  tsMap.reserve(nWiresInMerger);
259  if (tsMap.find(iCell) != tsMap.end()) {
260  return tsMap[iCell];
261  } else {
262  WireSet id;
263  // distance to the first priority layer: 0-4 (SL0) or 0-2 (outer)
264  // The further it is, the more TS it would be involved in.
265  const unsigned short distance = (innerMost) ? iLayer - 3 : abs(iLayer - 2);
266  id.resize(distance + 1);
267  std::iota(id.begin(), id.end(), (iCell - distance / 2) % nCellsInLayer);
268  id.erase(std::remove_if(id.begin(), id.end(), [](short i) {
269  return (i < 0 || i >= nCellsInLayer);
270  }), id.end());
271  tsMap.insert({iCell, id});
272  return id;
273  }
274 }
275 
277 {
278  /* The CDCHits array in DataStore contains all the hits in an event,
279  * regardless of when them at the trigger system. Their real timing behavior
280  * is replayed/simulated by looking at the TDC count (timestamp) of each hit.
281  */
282 
283  vector<int> iAxialHit;
284  for (int i = 0; i < m_cdcHits.getEntries(); ++i) {
285  // discard hits in the layers unused by trigger
286  if ((m_cdcHits[i]->getISuperLayer() != 0 && m_cdcHits[i]->getILayer() == 5) ||
287  m_cdcHits[i]->getICLayer() < 3) {
288  continue;
289  }
290  if (m_cdcHits[i]->getISuperLayer() % 2 == 0) {
291  iAxialHit.push_back(i);
292  }
293  }
294  // sort by TDC count: larger count comes first
295  std::sort(iAxialHit.begin(), iAxialHit.end(), [this](int i, int j) {
296  return m_cdcHits[i]->getTDCCount() > m_cdcHits[j]->getTDCCount();
297  });
298 
299  // assign the hits to clock edges based on TDC count
300  iAxialHitInClock.clear();
301  iAxialHitInClock.resize(1 + trgTime(iAxialHit.back(), iAxialHit.front()) / clockPeriod);
302  auto itr = iAxialHitInClock.begin();
303  int clockEdge = 1;
304  for (const auto& ihit : iAxialHit) {
305  B2DEBUG(20, "sorted TDC count: " << m_cdcHits[ihit]->getTDCCount() <<
306  ", SL" << m_cdcHits[ihit]->getISuperLayer() << ", layer" << m_cdcHits[ihit]->getILayer() <<
307  ", wire " << m_cdcHits[ihit]->getIWire() << ", ihit: " << ihit);
308  // if (m_cdcHits[ihit]->getTDCCount() >= 4999) {
309  // B2DEBUG(20, "skipping this hit");
310  // continue;
311  // }
312  // if (trgTime(ihit, iAxialHit.front()) >= clockEdge * clockPeriod) {
313  while (trgTime(ihit, iAxialHit.front()) >= clockEdge * clockPeriod) {
314  ++clockEdge;
315  ++itr;
316  }
317  itr->push_back(ihit);
318  }
319  m_iFirstHit = iAxialHit.front();
320 
321  // In addition, clear the list holding priority hit indices
322  for (auto& clock : m_priorityHit) {
323  for (auto& sl : clock) {
324  for (auto& merger : sl.second) {
325  merger.clear();
326  }
327  }
328  }
329 }
330 
331 bool TSF::notHit(CDCTrigger::MergerOut field, unsigned iTS, TSF::registeredStructElement& reg)
332 {
333  return ! reg[field][iTS];
334 }
335 
336 void TSF::registerHit(CDCTrigger::MergerOut field, unsigned iTS, TSF::registeredStructElement& reg)
337 {
338  reg[field].set(iTS);
339 }
340 
341 void TSF::setSecondPriority(unsigned priTS,
342  unsigned iHit,
343  timeVec hitTime,
344  unsigned lr,
345  mergerStructElement<5>& mergerData,
346  registeredStructElement& registeredCell,
347  priorityHitInMerger& priorityHit)
348 {
349  timeVec& priorityTime = (get<MergerOut::priorityTime>(mergerData))[priTS];
350  // when there is not already a (1st or 2nd priority) hit
351  if (notHit(MergerOut::priorityTime, priTS, registeredCell)) {
352  priorityTime = hitTime;
353  registerHit(MergerOut::priorityTime, priTS, registeredCell);
354  get<MergerOut::secondPriorityHit>(mergerData)[0].set(priTS, ! lr);
355  priorityHit.insert({priTS, iHit});
356  // set the 2nd pri bit to right when T_left == T_right
357  } else if (priority(priorityHit[priTS]) == Priority::second &&
358  hitTime.to_ulong() == priorityTime.to_ulong() && lr == 1) {
359  get<MergerOut::secondPriorityHit>(mergerData)[0].reset(priTS);
360  priorityHit[priTS] = iHit;
361  }
362 }
363 
364 void TSF::simulateMerger(unsigned iClock)
365 {
366  // clean up TSF input signals
367  for (auto& sl : inputToTSF) {
368  for (auto& merger : sl) {
369  merger.fill(zero_val);
370  }
371  }
372  // don't do simulation if there are no more CDC hits
373  if (iClock >= iAxialHitInClock.size()) {
374  return;
375  }
376  // mergerStruct<5> innerDataInClock(nAxialMergers[0]);
377  // map<unsigned, mergerStruct<3> > outerDataInClock;
378  // mergers in outer super layer has only 3 edge fields,
379  // but for the sake of coding simplicity and consistency,
380  // let's declare them as the same type of SL0,
381  // at the cost of 2 wasted slots.
382  map<unsigned, mergerStruct<5> > dataInClock;
383  map<unsigned, registeredStruct> registered;
384  for (int iAx = 0; iAx < m_nSubModules; ++iAx) {
385  dataInClock.insert({2 * iAx, mergerStruct<5> (nAxialMergers[iAx])});
386  registered.insert({2 * iAx, registeredStruct(nAxialMergers[iAx])});
387  }
388  auto clock = iAxialHitInClock[iClock];
389  // move first priority hits forward, so that we can just use the 1st hit
390  // but still preserve order in each group
391  std::stable_partition(clock.begin(), clock.end(),
392  [this](int hit) {return priority(hit) == Priority::first;});
393  for (const auto& iHit : clock) {
394  const CDCHit& hit = *m_cdcHits[iHit];
395  const unsigned short iSL = hit.getISuperLayer();
396  const short outer = (iSL == 0) ? 0 : 1;
397  const unsigned short iMerger = mergerNumber(iHit);
398  const unsigned short iCell = mergerCellID(iHit);
399  const WireSet tsList = segmentID(iHit);
400  const timeVec hitTime = timeStamp(iHit, m_iFirstHit);
401  auto& mergerData = dataInClock[iSL][iMerger];
402  auto& registeredCell = registered[iSL][iMerger];
403  // register the priority hit in the corresponding merger
404  auto& priorityHit = m_priorityHit[iClock][iSL][iMerger];
405  B2DEBUG(50, "iHit: " << iHit << ", Merger" << iSL << "-" << iMerger <<
406  ", iCell: " << iCell);
407  // update hit map
408  get<MergerOut::hitmap>(mergerData)[0].set(iCell);
409  // update fastest time
410  for (const auto& iTS : tsList) {
411  timeVec& fastestTime = (get<MergerOut::fastestTime>(mergerData))[iTS];
412  // when there is not already a hit
413  if (notHit(MergerOut::fastestTime, iTS, registeredCell) ||
414  // or when this one is faster
415  hitTime.to_ulong() < fastestTime.to_ulong()) {
416  // note: if we move this up to before partition, we won't need to
417  // compare timing, since they are already sorted. However,
418  // leaving it here makes the code a bit cleaner.
419  fastestTime = hitTime;
420  registerHit(MergerOut::fastestTime, iTS, registeredCell);
421  }
422  }
423  // get edge hit timing or local fastest time
424  if (m_edge[outer].find(iCell) != m_edge[outer].end()) {
425  for (auto& iEdgeTime : m_edge[outer][iCell]) {
426  timeVec& edgeTime = (get<MergerOut::edgeTime>(mergerData))[iEdgeTime];
427  if (notHit(MergerOut::edgeTime, iEdgeTime, registeredCell) ||
428  hitTime.to_ulong() < edgeTime.to_ulong()) {
429  edgeTime = hitTime;
430  registerHit(MergerOut::edgeTime, iEdgeTime, registeredCell);
431  }
432  }
433  }
434  switch (priority(iHit)) {
435  case Priority::first: {
436  // update priority time
437  unsigned priTS = iCell % nSegmentsInMerger;
438  // cppcheck-suppress variableScope
439  timeVec& priorityTime = (get<MergerOut::priorityTime>(mergerData))[priTS];
440  // when there is not already a (first priority) hit
441  if (notHit(MergerOut::priorityTime, priTS, registeredCell)) {
442  priorityTime = hitTime;
443  registerHit(MergerOut::priorityTime, priTS, registeredCell);
444  get<MergerOut::secondPriorityHit>(mergerData)[0].reset(priTS);
445  priorityHit.insert({priTS, iHit});
446  }
447  break;
448  }
449  case Priority::second: {
450  // update the 2 priority times and the sc bits
451  for (unsigned i = 0; i < 2; ++i) {
452  unsigned priTS = iCell % nSegmentsInMerger + i;
453  if (priTS == nSegmentsInMerger) {
454  // crossing the left edge
455  // set the 2nd priority left of the first TS in the next merger
456  priTS = 0;
457  const unsigned short iMergerPlus1 = (iMerger == nMergers[iSL] - 1) ? 0 : iMerger + 1;
458  auto& nextMergerData = dataInClock[iSL][iMergerPlus1];
459  auto& nextRegisteredCell = registered[iSL][iMergerPlus1];
460  auto& nextPriorityHit = m_priorityHit[iClock][iSL][iMergerPlus1];
461  setSecondPriority(priTS, iHit, hitTime, i, nextMergerData, nextRegisteredCell, nextPriorityHit);
462  } else {
463  setSecondPriority(priTS, iHit, hitTime, i, mergerData, registeredCell, priorityHit);
464  }
465  }
466  break;
467  }
468  default:
469  break;
470  }
471  }
472  // pack the output from merger into input to TSF
473  for (auto iAx = 0; iAx < m_nSubModules; ++iAx) {
474  unsigned nEdges = (iAx == 0) ? 5 : 3;
475  for (unsigned iMerger = 0; iMerger < inputToTSF[iAx].size(); ++iMerger) {
476  auto input = inputToTSF[iAx][iMerger].rbegin();
477  auto& output = dataInClock[2 * iAx][iMerger];
478  pack<MergerOut::hitmap, 1> (input, nWiresInMerger, output);
479  pack<MergerOut::priorityTime, timeWidth> (input, nCellsInLayer, output);
480  pack<MergerOut::fastestTime, timeWidth> (input, nCellsInLayer, output);
481  pack<MergerOut::secondPriorityHit, 1> (input, nCellsInLayer, output);
482  pack<MergerOut::edgeTime, timeWidth> (input, nEdges, output);
483  // cppcheck-suppress variableScope
484  auto& outputAcrossClocks = dataAcrossClocks[2 * iAx][iMerger];
485  if (get<MergerOut::hitmap>(output)[0].any()) {
486  string priTime, fasTime, edgTime;
487  for (int i = 0; i < nSegmentsInMerger; ++i) {
488  priTime += get<MergerOut::priorityTime>(output)[i].to_string() + ",";
489  fasTime += get<MergerOut::fastestTime>(output)[i].to_string() + ",";
490  edgTime += get<MergerOut::edgeTime>(output)[i].to_string() + ",";
491  get<MergerOut::priorityTime>(outputAcrossClocks)[i] |=
492  get<MergerOut::priorityTime>(output)[i];
493  get<MergerOut::fastestTime>(outputAcrossClocks)[i] |=
494  get<MergerOut::fastestTime>(output)[i];
495  }
496  B2DEBUG(150, "merger " << iAx * 2 << "-" << iMerger <<
497  "\nhitmap: " << get<MergerOut::hitmap>(output)[0] <<
498  "\nsecond hit: " << get<MergerOut::secondPriorityHit>(output)[0] <<
499  "\nprioiry Time: " << priTime <<
500  "\nfastest Time: " << fasTime <<
501  "\nedge Time: " << edgTime);
502  get<MergerOut::hitmap>(outputAcrossClocks)[0] |= get<MergerOut::hitmap>(output)[0];
503  }
504  // simulate clock counter
505  if (m_simulateCC) {
506  bitset<9> cc(iClock);
507  const int nBitsReserved = (iAx == 0) ? 3 : 4;
508  generate(inputToTSF[iAx][iMerger].rend() - 9 - nBitsReserved,
509  inputToTSF[iAx][iMerger].rend() - nBitsReserved,
510  [&cc, i = 0]() mutable {
511  return (cc[i++]) ? one_val : zero_val;});
512  }
513  }
514  }
515 }
516 
517 template<MergerOut field, size_t width>
518 void TSF::pack(inputVector::reverse_iterator& input, unsigned number,
519  mergerStructElement<5>& output)
520 {
521  std::generate(input, input + number * width, [&, n = 0]() mutable {
522  int i = n++;
523  if (width == 1)
524  {
525  return (get<field>(output)[0][i]) ? one_val : zero_val;
526  } else
527  {
528  return (get<field>(output)[i / width][i % width]) ? one_val : zero_val;
529  }});
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 }
Class containing the result of the unpacker in raw data and the result of the digitizer in simulation...
Definition: CDCHit.h:40
This class is the interface between TSim/basf2 TSF module and the firmware simulation core of XSim/IS...
std::vector< mergerStructElement< nEdges > > mergerStruct
data structure to hold merger output
void initializeMerger()
Get CDC hits from the DataStore and distribute them to clocks.
static constexpr std::array< int, 9 > nMergers
number of mergers in each super layer
void computeEdges()
Compute the map from merger cell ID to all its related edge fields.
bool m_simulateCC
flag to simulate front-end clock counter
unsigned short trgTime(int index, int iFirstHit)
Get the trigger time of the CDC hit.
std::bitset< 4 > timeStamp(int index, int iFirstHit)
Get the trigger time stamp of a hit.
outputArray read(FILE *instream)
write TSF output signals from the worker
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),...
std::vector< registeredStructElement > registeredStruct
vector of registeredStructElement
bool notHit(CDCTrigger::MergerOut field, unsigned iTS, registeredStructElement &reg)
Whether a time field in a merger has been hit in the clock cycle.
priorityHitStruct m_priorityHit
list keeping the index of priority hit of a TS for making fastsim ts hit object
WireSet segmentID(int iHit)
Get the list of associated track segments with a hit.
void initialize() override
spawn child process for workers, open pipes to pass data
void pack(inputVector::reverse_iterator &rInput, unsigned number, mergerStructElement< 5 > &output)
Pack the merger output data structure to TSF input vector.
void event() override
Things to do for each event.
CDCTrigger::Priority priority(int index)
write TSF input signals to the worker
std::string m_outputCollectionName
Name of the StoreArray holding the found TS hits.
std::array< inputFromMerger, m_nSubModules > inputToTSFArray
input array to TSF
std::bitset< timeWidth > timeVec
element of data structure to hold merger output
std::string m_outputBitstreamNameTo2D
Name of the StoreArray holding the raw bit content to 2D trackers.
void terminate() override
close the pipes and wait for children to die.
std::array< outputVector, nTrackers > outputArray
output array
void setSecondPriority(unsigned priTS, unsigned iHit, timeVec hitTime, unsigned lr, mergerStructElement< 5 > &mergerData, registeredStructElement &registeredCell, priorityHitInMerger &priorityHit)
set 2nd priority info
static constexpr std::array< int, m_nSubModules > nAxialMergers
number of mergers in axial super layers
std::vector< bool > m_stubLUT
list of flags to run a TSF firmware simulation with dummy L/R LUT (to speed up loading)
char * getData(inputToTSFArray)
get the XSI compliant format from the bits format TSF input
void write(const char *message, FILE *outstream)
write TSF input signals to the worker
inputToTSFArray inputToTSF
XSI compliant format of input to TSF.
void saveFastOutput(short iclock)
save fast TSIM output
unsigned short mergerNumber(int index)
Get the merger unit ID in a super layer.
bool m_mergerOnly
flag to only simulation merger and not TSF
std::map< unsigned, mergerStruct< 5 > > dataAcrossClocks
data structure to hold merger output
void simulateMerger(unsigned iclock)
Simulate 1 clock of merger.
std::string m_hitCollectionName
Name of the StoreArray containing the input CDC hits.
std::array< std::bitset< nCellsInLayer >, 3 > registeredStructElement
record when a time slow has been registered by a hit <priority time, fast time, edge timing>
std::unordered_map< short, WireSet > TSMap
TS map.
static constexpr int m_nSubModules
number of TSF to simulate
std::vector< priorityHitInMerger > priorityHitStructInSL
priority hits map in Merger for a SL
std::map< unsigned, int > priorityHitInMerger
priority hits map in Merger
unsigned short mergerCellID(int index)
Get the cell ID in the merger.
void registerHit(CDCTrigger::MergerOut field, unsigned iTS, registeredStructElement &reg)
Register the timing field so that later hits won't overwrite it.
Base class for Modules.
Definition: Module.h:72
void setDescription(const std::string &description)
Sets the description of the module.
Definition: Module.cc:214
Type-safe access to single objects in the data store.
Definition: StoreObjPtr.h:96
REG_MODULE(arichBtest)
Register the Module.
void addParam(const std::string &name, T &paramVariable, const std::string &description, const T &defaultValue)
Adds a new parameter to the module.
Definition: Module.h:560
static constexpr int mergerWidth
Merger data width.
Abstract base class for different kinds of events.
Helper class for software (C++) / firmware (VHDL) co-simulation.
Definition: Cosim.h:22
const char one_val
'1' in XSI VHDL simulation
Definition: Cosim.h:43
const char zero_val
'0' in XSI VHDL simulation
Definition: Cosim.h:45
std::string slv_to_bin_string(std::array< char, N > signal, bool padding=false)
Transform into string.
Definition: Cosim.h:64
void display_hex(const std::array< char, N > &signal)
Display signal in hex.
Definition: Cosim.h:81
std::string display_value(const char *count, int size)
Display value of the signal.
Definition: Cosim.h:48