Belle II Software development
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
30using namespace Belle2;
31using namespace Cosim;
32using namespace std;
33using namespace CDCTrigger;
35
36REG_MODULE(CDCTriggerTSFFirmware);
37
38constexpr 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
76TSF::~CDCTriggerTSFFirmwareModule()
77{}
78
79Priority 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
90void 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
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
206template<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
220unsigned 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
230std::bitset<4> TSF::timeStamp(int index, int iFirstHit)
231{
232 return std::bitset<4> (trgTime(index, iFirstHit) % clockPeriod);
233}
234
235unsigned 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
243unsigned short TSF::mergerNumber(int index)
244{
245 const CDCHit& hit = (*m_cdcHits[index]);
246 return hit.getIWire() / nSegmentsInMerger;
247}
248
249using WireSet = std::vector<unsigned short>;
250using 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
331bool TSF::notHit(CDCTrigger::MergerOut field, unsigned iTS, TSF::registeredStructElement& reg)
332{
333 return ! reg[field][iTS];
334}
335
336void TSF::registerHit(CDCTrigger::MergerOut field, unsigned iTS, TSF::registeredStructElement& reg)
337{
338 reg[field].set(iTS);
339}
340
341void 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
364void 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
517template<MergerOut field, size_t width>
518void TSF::pack(inputVector::reverse_iterator& input, unsigned number,
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
538constexpr std::array<int, 9> TSF::nMergers;
539
540void 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
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
void display_hex(const std::array< char, N > &signal)
Display signal in hex.
Definition: Cosim.h:81
std::string slv_to_bin_string(std::array< char, N > signal, bool padding=false)
Transform into string.
Definition: Cosim.h:64
std::string display_value(const char *count, int size)
Display value of the signal.
Definition: Cosim.h:48
const char zero_val
'0' in XSI VHDL simulation
Definition: Cosim.h:45
STL namespace.