Belle II Software  release-06-02-00
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(MergerOut field, unsigned iTS, TSF::registeredStructElement& reg)
332 {
333  return ! reg[field][iTS];
334 }
335 
336 void TSF::registerHit(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  return (get<field>(output)[i / width][i % width]) ? one_val : zero_val;}});
528  input += number * width;
529 }
530 
532 {
533  m_bitsTo2D.appendNew(outputToTracker);
534 }
535 
536 constexpr std::array<int, 9> TSF::nMergers;
537 
538 void TSF::saveFastOutput(short iclock)
539 {
540  const int ccWidth = 9;
541  // number of widended clocks in TSF
542  const int widenedClocks = 16;
543  // TSF latency in unit of data clocks
544  const int latency = 3;
545  // N.B. widenedClocks and latency might be inaccurate
546 
547  int iAx = 0;
548  for (const auto& sl : outputToTracker) {
549  // keep a list of found TS hit in the same SL so that outputs to different
550  // trackers won't be recorded twice
551  std::map<unsigned, int> foundTS;
552  unsigned short iTracker = 0;
553  for (auto& tracker : sl) {
554  string output = slv_to_bin_string(tracker);
555  // loop through all TS hits in the output
556  for (int i = ccWidth; i < width_out; i += tsInfoWidth) {
557  string ts = output.substr(i, tsInfoWidth);
558  tsOut decoded = decodeTSHit(ts);
559  // finish if no more TS hit is present
560  if (decoded[3] == 0) {
561  break;
562  }
563  const unsigned nCellsInSL = nAxialMergers[iAx] * nCellsInLayer;
564  // get global TS ID
565  unsigned iTS = decoded[0] + nCellsInSL * iTracker / nTrackers;
566  // periodic ID overflow when phi0 > 0 for the 4th tracker
567  if (iTS >= nCellsInSL) {
568  iTS -= nCellsInSL;
569  }
570  // ID in SL8 is shifted by 16
571  if (iAx == 4) {
572  if (iTS < 16) {
573  iTS += nCellsInSL;
574  }
575  iTS -= 16;
576  }
577  int iHit = -1;
578  int firstClock = iclock - widenedClocks - latency;
579  if (firstClock < 0) {
580  firstClock = 0;
581  }
582  int lastClock = iclock - latency;
583  B2DEBUG(10, "iAx:" << iAx << ", iTS:" << iTS << ", iTracker: " << iTracker);
584  // scan through all CDC hits to get the priority hit
585  for (int iclkPri = firstClock; iclkPri < lastClock; ++iclkPri) {
586  // get the map storing the hit index in the corresponding merger
587  auto priMap = m_priorityHit[iclkPri][2 * iAx][iTS / nSegmentsInMerger];
588  unsigned itsInMerger = iTS % nSegmentsInMerger;
589  // Pick up the first CDCHit which agrees to the priority position
590  // of firmware sim output
591  if (priMap.find(itsInMerger) != priMap.end() &&
592  toPriority(decoded[3]) == priority(priMap[itsInMerger])) {
593  iHit = priMap[itsInMerger];
594  B2DEBUG(10, "iHit:" << iHit);
595  B2DEBUG(10, "TDC: " << m_cdcHits[iHit]->getTDCCount() << ", TSF: " << decoded[1]);
596  break;
597  }
598  }
599  // check if the same TS hit to another tracker is already there
600  // TODO: some duplicates are from different clocks,
601  // so they won't be caught here
602  // This mostly likely means the wrong phase of TSF firmware output is used
603  if (foundTS.find(iTS) != foundTS.end()) {
604  if (iHit != foundTS[iTS]) {
605  B2WARNING("Same TS ID exists, but they point to different CDC hit");
606  }
607  continue;
608  }
609  foundTS.insert({iTS, iHit});
610  if (iHit < 0) {
611  B2WARNING("No corresponding priority CDC hit can be found.");
612  B2WARNING("Maybe the latency and number of widened clocks are wrong.");
613  B2WARNING("In event " << StoreObjPtr<EventMetaData>()->getEvent());
614  B2DEBUG(20, "priority " << decoded[3]);
615  for (int iclkPri = 0; iclkPri < m_nClockPerEvent; ++iclkPri) {
616  auto priMap = m_priorityHit[iclkPri][2 * iAx][iTS / 16];
617  for (auto& m : priMap) {
618  B2DEBUG(20, "iWire: " << m.first << ", iLayer: " <<
619  m_cdcHits[m.second]->getILayer() << ", iClock: " << iclkPri);
620  }
621  }
622  if (iclock < 15) {
623  B2WARNING("It could be the left over TS hits from the last event.\n" <<
624  "Try increasing m_nClockPerEvent");
625  }
626  }
627  CDCHit cdchit = (iHit < 0) ? CDCHit() : *m_cdcHits[iHit];
628  m_tsHits.appendNew(cdchit,
629  globalSegmentID(iTS, 2 * iAx),
630  decoded[3], // priority position
631  decoded[2], // L/R
632  // TODO: pri time should be the same as the CDCHit in 1st arg
633  // when the proper clock counter is given to firmware TSF
634  decoded[1],
635  // TODO: fastest time from ETF output
636  0,
637  iclock);
638  }
639  ++iTracker;
640  }
641  ++iAx;
642  }
643 }
644 
646 {
647  // TODO: what if there is 0 CDC hit?
648  int status = 0;
649  initializeMerger();
650  try {
651  // clear accumulative merger output
652  for (unsigned iAx = 0; iAx < m_nSubModules; ++iAx) {
653  dataAcrossClocks[2 * iAx] = mergerStruct<5> (nAxialMergers[iAx]);
654  }
655  for (int iClock = 0; iClock < m_nClockPerEvent; iClock++) {
656  B2INFO(string(15, '=') << " clock #" << iClock << " " << string(15, '='));
657  if (iClock == m_nClockPerEvent - 1) {
658  cout << "Accumulative hitmap:" << "\n";
659  }
660  simulateMerger(iClock);
661  if (m_mergerOnly) {
662  cout << std::hex;
663  int iSL = 0;
664  for (const auto& sl : inputToTSF) {
665  int iMerger = 0;
666  for (const auto& mergerOut : sl) {
667  if (std::any_of(mergerOut.begin(), mergerOut.end(), [](char i) {
668  return i != zero_val;
669  })) {
670  cout << "Merger " << iSL * 2 << "-" << iMerger / 2 << " u" << iMerger % 2 << ": ";
671  display_hex(mergerOut);
672  }
673  // print accumulative hitmap
674  if (iClock == m_nClockPerEvent - 1) {
675  auto accuOut = dataAcrossClocks[2 * iSL][iMerger];
676  if (get<MergerOut::hitmap>(accuOut)[0].any()) {
677  cout << "Merger " << iSL * 2 << "-" << iMerger / 2 << " u" << iMerger % 2 << ": ";
678  cout << get<MergerOut::hitmap>(accuOut)[0] << "\n";
679  string priTime, fasTime;
680  for (int i = 0; i < 16; ++i) {
681  priTime += get<MergerOut::priorityTime>(accuOut)[i].to_string() + ",";
682  fasTime += get<MergerOut::fastestTime>(accuOut)[i].to_string() + ",";
683  }
684  cout << "pritime: " << priTime << "\n";
685  cout << "fastime: " << fasTime << "\n";
686  }
687  }
688  iMerger++;
689  }
690  iSL++;
691  }
692  cout << std::dec;
693  continue;
694  }
695 
696  auto TSF0 = getData<0>(inputToTSF);
697  auto TSF2 = getData<1>(inputToTSF);
698  auto TSF4 = getData<2>(inputToTSF);
699  auto TSF6 = getData<3>(inputToTSF);
700  auto TSF8 = getData<4>(inputToTSF);
701  array<char*, m_nSubModules> rawInputToTSF = {TSF0, TSF2, TSF4, TSF6, TSF8};
702 
703  for (unsigned iSL = 0; iSL < m_nSubModules; ++iSL) {
704  B2DEBUG(100, "input to TSF" << iSL * 2 << ": \n" <<
705  display_value(rawInputToTSF[iSL], nAxialMergers[iSL] * mergerWidth));
706  write(rawInputToTSF[iSL], stream[iSL][0]);
707  }
708  // don't mess up stdout order with child processes
709  if (m_debugLevel >= 50) {
710  usleep(2000 * m_debugLevel);
711  }
712  for (unsigned iSL = 0; iSL < m_nSubModules; ++iSL) {
713  B2DEBUG(50, "Reading buffer from TSF " << iSL * 2 << ":");
714  outputToTracker[iSL] = read(stream[iSL][1]);
715  B2DEBUG(30, "received TSF " << iSL * 2 << ":");
716  if (m_debugLevel >= 30) {
717  for (const auto& out : outputToTracker[iSL]) {
718  display_hex(out);
719  }
720  }
721  }
722  saveFirmwareOutput();
723  saveFastOutput(iClock);
724  }
725  } catch (std::exception& e) {
726  B2ERROR("ERROR: An exception occurred: " << e.what());
727  status = 2;
728  } catch (...) {
729  B2ERROR("ERROR: An unknown exception occurred.");
730  status = 3;
731  }
732  B2DEBUG(50, "status code:" << status);
733 }
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:95
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
#define REG_MODULE(moduleName)
Register the given module (without 'Module' suffix) with the framework.
Definition: Module.h:650
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