Belle II Software  release-05-02-19
CDCTrigger2DFinderFirmwareModule.cc
1 /**************************************************************************
2  * BASF2 (Belle Analysis Framework 2) *
3  * Copyright(C) 2017 - Belle II Collaboration *
4  * *
5  * Author: The Belle II Collaboration *
6  * Contributors: Tzu-An Sheng *
7  * *
8  * This software is provided "as is" without any warranty. *
9  **************************************************************************/
10 
11 #include "trg/cdc/modules/houghtracking/xsi_loader.h"
12 
13 #include <trg/cdc/modules/houghtracking/CDCTrigger2DFinderFirmwareModule.h>
14 #include <trg/cdc/Cosim.h>
15 #include <trg/cdc/CDCTrigger.h>
16 #include <framework/datastore/StoreArray.h>
17 #include <framework/logging/Logger.h>
18 #include <trg/cdc/dataobjects/CDCTriggerSegmentHit.h>
19 #include <trg/cdc/dataobjects/CDCTriggerTrack.h>
20 #include <trg/cdc/dataobjects/Bitstream.h>
21 
22 #include <string>
23 #include <cstring>
24 #include <iostream>
25 #include <vector>
26 #include <array>
27 #include <algorithm>
28 #include <numeric>
29 #include <bitset>
30 #include <experimental/array>
31 
32 using std::experimental::to_array;
33 constexpr double pi() { return std::atan(1) * 4; }
34 
35 int Xsi::Loader::get_port_number_or_exit(std::string name)
36 {
37  int portNumber = get_port_number(name.c_str());
38  if (portNumber < 0) {
39  std::cerr << "ERROR: " << name << " not found" << std::endl;
40  throw;
41  }
42  return portNumber;
43 }
44 
45 
46 using namespace Belle2;
47 using namespace Cosim;
48 using namespace std;
49 using namespace CDCTrigger;
50 
51 
52 REG_MODULE(CDCTrigger2DFinderFirmware)
53 
55  Xsi_Instance(0)
56 // Xsi_Instance->design_libname, simengine_libname)
57 {
58  //Set module properties
59  setDescription("Firmware simulation of Hough tracking algorithm for CDC trigger.");
60 
61  // Define module parameters
62  addParam("hitCollectionName", m_hitCollectionName,
63  "Name of the input StoreArray of CDCTriggerSegmentHits.",
64  string(""));
65  addParam("outputCollectionName", m_outputCollectionName,
66  "Name of the StoreArray holding the tracks found in the Hough tracking.",
67  string("TRGCDC2DFinderFirmwareTracks"));
68  addParam("bitstreamNameTo2D", m_bitstreamNameTo2D,
69  "Name of the StoreArray holding the raw bit content to 2D trackers",
70  string("BitstreamTSF-2D"));
71  addParam("fromFastSim", m_fromFastSim,
72  "flag to read TS hits from the output of TSF firmware simulation",
73  false);
74  addParam("nClocks", m_nClockPerEvent,
75  "how many clocks to simulate in each event",
76  32);
77 
78 }
79 
80 CDCTrigger2DFinderFirmwareModule::~CDCTrigger2DFinderFirmwareModule()
81 {
82  if (Xsi_Instance) {
83  Xsi_Instance->close();
84  delete Xsi_Instance;
85  Xsi_Instance = 0;
86  }
87 }
88 
89 void CDCTrigger2DFinderFirmwareModule::initialize()
90 {
91  Xsi_Instance = new Xsi::Loader(design_libname, simengine_libname);
92  //StoreArray<CDCTriggerTrack>::registerPersistent(m_outputCollectionName);
93  StoreArray<CDCTriggerSegmentHit> segmentHits(m_hitCollectionName);
94  //StoreArray<CDCTriggerTrack> tracks(m_outputCollectionName);
95  StoreArray<CDCTriggerTrack> tracks(m_outputCollectionName);
96  tracks.registerInDataStore();
97  tracks.registerRelationTo(segmentHits);
98 
99  m_debugLevel = getLogConfig().getDebugLevel();
100  std::cout << "Design DLL : " << design_libname << std::endl;
101  std::cout << "Sim Engine DLL : " << simengine_libname << std::endl;
102  try {
103  memset(&info, 0, sizeof(info));
104  char logName[] = "firmware.log";
105  // info.logFileName = NULL;
106  info.logFileName = logName;
107  char wdbName[] = "test.wdb";
108  info.wdbFileName = wdbName;
109  Xsi_Instance->open(&info);
110  Xsi_Instance->trace_all();
111 
112  clk = Xsi_Instance->get_port_number_or_exit("Top_clkData_s");
113  for (int iSL = 0; iSL < 5; ++iSL) {
114  string name = "TSF" + to_string(iSL * 2) + "_input_i";
115  tsfPort[iSL] = Xsi_Instance->get_port_number_or_exit(name);
116  tsfInput[iSL].fill(zero_val);
117  Xsi_Instance->put_value(tsfPort[iSL], tsfInput[iSL].data());
118  }
119  out = Xsi_Instance->get_port_number_or_exit("Main_out");
120 
121  // Start low clock
122  Xsi_Instance->put_value(clk, &zero_val);
123  Xsi_Instance->run(10);
124  } catch (std::exception& e) {
125  std::cerr << "ERROR: An exception occurred: " << e.what() << std::endl;
126  } catch (...) {
127  std::cerr << "ERROR: An unknown exception occurred." << std::endl;
128  // Xsi_Instance->get_error_info();
129  throw;
130  }
131 }
132 
133 int CDCTrigger2DFinderFirmwareModule::localSegmentID(int globalID)
134 {
135  std::array<int, 9> nWiresPerLayer = {160, 160, 192, 224, 256, 288, 320, 352, 384};
136  // std::array<int, 8> nFEPerLayer = {5, 5, 6, 7, 8, 9, 10, 11, 12};
137  std::array<int, 10> nAccumulate;
138  std::partial_sum(nWiresPerLayer.begin(), nWiresPerLayer.end(), nAccumulate.begin() + 1);
139  // shift the ID by 16 in SL8
140  if (globalID > nAccumulate[7]) {
141  globalID += 16;
142  }
143  auto nWiresInside = std::lower_bound(nAccumulate.begin(), nAccumulate.end(), globalID) - 1;
144  return globalID - *nWiresInside;
145 }
146 
147 auto CDCTrigger2DFinderFirmwareModule::encodeTSHit(CDCTriggerSegmentHit const* hit)
148 {
149  bitset<8> id(localSegmentID(hit->getSegmentID()));
150  bitset<9> priorityTime(hit->priorityTime());
151  bitset<2> LR(hit->getLeftRight());
152  bitset<2> priorityPosition(hit->getPriorityPosition());
153  string joined = id.to_string() + priorityTime.to_string() +
154  LR.to_string() + priorityPosition.to_string();
155  bitset<m_tsVectorWidth> tsInfo(joined);
156  B2DEBUG(10, "encoded ts hit: " << tsInfo << " with local ID " << localSegmentID(hit->getSegmentID()));
157  return tsInfo;
158 }
159 
160 auto CDCTrigger2DFinderFirmwareModule::toTSSLV(const std::bitset<m_tsVectorWidth> ts)
161 {
162  std::string word = ts.to_string();
163  std::array<char, m_tsVectorWidth> vec;
164  for (unsigned i = 0; i < word.size(); ++i) {
165  vec[i] = (word[i] == '0') ? zero_val : one_val;
166  }
167  return vec;
168 }
169 
170 TRGFinderTrack CDCTrigger2DFinderFirmwareModule::decodeTrack(string trackIn)
171 {
172  constexpr unsigned lenCharge = 2;
173  constexpr unsigned lenOmega = 7;
174  constexpr unsigned lenPhi0 = 7;
175  constexpr unsigned lenTS = 21;
176  constexpr array<unsigned, 3> trackLens = {lenCharge, lenOmega, lenPhi0};
177  array<unsigned, 4> trackPos{ 0 };
178  partial_sum(trackLens.begin(), trackLens.end(), trackPos.begin() + 1);
179  const unsigned shift = 16 - lenOmega;
180  TRGFinderTrack trackOut;
181  bitset<trackLens[1]> omega(trackIn.substr(trackPos[1], trackLens[1]));
182  // shift omega to 16 bits, cast it to signed 16-bit int, and shift it back to 7 bits
183  // thus the signed bit is preserved (when right-shifting)
184  int omegaFirm = (int16_t (omega.to_ulong() << shift)) >> shift;
185  trackOut.omega = 29.97 * 1.5e-4 * omegaFirm;
186  int phi0 = bitset<trackLens[2]>(trackIn.substr(trackPos[2], trackLens[2])).to_ulong();
187  trackOut.phi0 = pi() / 4 + pi() / 2 / 80 * (phi0 + 1);
188  for (unsigned i = 0; i < 5; ++i) {
189  trackOut.ts[i] = decodeTSHit(trackIn.substr(trackPos.back() + i * lenTS, lenTS));
190  }
191  return trackOut;
192 }
193 
194 void CDCTrigger2DFinderFirmwareModule::decodeOutput(short latency)
195 {
196  const unsigned lenTrack = 121;
197  array<int, 4> posTrack;
198  for (unsigned i = 0; i < posTrack.size(); ++i) {
199  posTrack[i] = 6 + lenTrack * i;
200  }
201  string strOutput = slv_to_bin_string(finderOutput);
202  StoreArray<CDCTriggerTrack> storeTracks(m_outputCollectionName);
203  for (unsigned i = 0; i < m_nOutTracksPerClock; ++i) {
204  if (finderOutput[i] == one_val) {
205  TRGFinderTrack trk = decodeTrack(strOutput.substr(posTrack[i], lenTrack));
206  // const CDCTriggerTrack* track =
207  storeTracks.appendNew(trk.phi0, trk.omega, 0., latency);
208  // TODO: dig out the TS hits in datastore, and
209  // add relations to them
210  B2DEBUG(10, "phi0:" << trk.phi0 << ", omega:" << trk.omega
211  << ", at clock " << latency);
212  }
213  }
214 }
215 
216 void CDCTrigger2DFinderFirmwareModule::loadFromFirmware(int iclock)
217 {
218  static constexpr int inWidth = 429;
219  // nTrackers is already declared in trg/cdc/include/CDCTrigger.h
220  //static constexpr int nTrackers = 4;
221  static constexpr int nTSFs = 5;
222  using localInputVector = std::array<char, inWidth>;
223  using inputArray = std::array<localInputVector, nTrackers>;
224  using signalBus = std::array<inputArray, nTSFs>;
225  using signalBitStream = Bitstream<signalBus>;
227  if (iclock < bitsTo2D.getEntries()) {
228  int iAx = 0;
229  for (const auto& sl : bitsTo2D[iclock]->signal()) {
230  // copy input to 2D0
231  std::copy(sl[0].cbegin(), sl[0].cbegin() + width_in, tsfInput[iAx].begin());
232  iAx++;
233  }
234  } else {
235  for (auto& sl : tsfInput) {
236  sl.fill(zero_val);
237  }
238  }
239 }
240 
241 void CDCTrigger2DFinderFirmwareModule::initializeFastSim()
242 {
243  StoreArray<CDCTriggerSegmentHit> tsHits(m_hitCollectionName);
244 
245  B2DEBUG(10, tsHits.getEntries() << " hits in this event");
246  // index of TS hits, sorted by TS found time
247  // std::vector<int> iHit(tsHits.getEntries());
248  // std::iota(iHit.begin(), iHit.end(), 0);
249  std::vector<int> iHit;
250  for (int i = 0; i < tsHits.getEntries(); ++i) {
251  if (tsHits[i]->getISuperLayer() % 2 == 0) {
252  iHit.push_back(i);
253  }
254  }
255  std::sort(iHit.begin(), iHit.end(), [tsHits](int i, int j) {
256  return tsHits[i]->foundTime() < tsHits[j]->foundTime();
257  });
258 
259  for (auto hit : iHit) {
260  B2DEBUG(10, "sorted found time: " << tsHits[hit]->foundTime());
261  }
262  // data clock period (32ns) in unit of 2ns
263  const int clockPeriod = 16;
264 
265  // classify hits by clocks
266  // number of clocks before the last input hit
267  int nClocks = tsHits[iHit.back()]->foundTime() / clockPeriod;
268  // each element of seqHit holds a list of all the hits within a clock cycle
269  vector<vector<int> > seqHit(nClocks);
270  auto itrHit = iHit.begin();
271  // For each clock,
272  for (int i = 0; i < nClocks; ++i) {
273  if (tsHits[*itrHit]->foundTime() < 0) {
274  B2WARNING("Negative found time! The clock assignment will be wrong.");
275  }
276  // Fill in all the hits whose found time is less than the next clock edge,
277  while (tsHits[*itrHit]->foundTime() < (i + 1) * clockPeriod &&
278  itrHit != iHit.end()) {
279  seqHit[i].push_back(*itrHit);
280  ++itrHit;
281  }
282  if (seqHit[i].size() > m_nInTSPerClock) {
283  B2WARNING("Oops! Too many input hits in a clock!");
284  }
285  }
286 
287  m_tsHitVector.clear();
288  m_tsHitVector.resize(nClocks);
289 
290  // assign hits to SLV
291  for (unsigned iClock = 0; iClock < seqHit.size(); ++iClock) {
292  for (auto iTS : seqHit[iClock]) {
293  B2DEBUG(10, "encoding ts ID: " << tsHits[iTS]->getSegmentID() <<
294  ", SL " << tsHits[iTS]->getISuperLayer() <<
295  ", clock " << iClock
296  );
297  bitset<m_tsVectorWidth> ts(encodeTSHit(tsHits[iTS]));
298  m_tsHitVector[iClock][tsHits[iTS]->getISuperLayer() / 2].push_back(ts);
299  }
300  }
301 
302 }
303 
304 void CDCTrigger2DFinderFirmwareModule::loadFromFastSim(unsigned iclock)
305 {
306  for (unsigned iSL = 0; iSL < nAxialSuperLayer; ++iSL) {
307  // firstly, clear the input signal vectors
308  tsfInput[iSL].fill(zero_val);
309  // then, copy the TS info to the input signal vectors
310  // unless there is no more hit in the SL
311  auto itr = tsfInput[iSL].begin() + 9;
312  if (iclock < m_tsHitVector.size()) {
313  for (auto ts : m_tsHitVector[iSL][iclock]) {
314  tsVector inTS(toTSSLV(ts));
315  copy(inTS.begin(), inTS.end(), itr);
316  advance(itr, m_tsVectorWidth);
317  }
318  }
319  }
320 }
321 
322 void CDCTrigger2DFinderFirmwareModule::event()
323 {
324  int status = 0;
325  if (m_fromFastSim) {
326  initializeFastSim();
327  }
328  for (int iClock = 0; iClock < m_nClockPerEvent; iClock++) {
329  if (m_fromFastSim) {
330  loadFromFastSim(iClock);
331  } else {
332  loadFromFirmware(iClock);
333  }
334  std::cout << "------------------" << "\n";
335  std::cout << "clock #" << iClock << "\n";
336  // assign input signals
337  try {
338  // Put clk to one
339  Xsi_Instance->put_value(clk, &one_val);
340  Xsi_Instance->run(10);
341  // cout << display_value(tsfInput[iSL].data(), 9) << " " <<
342  // display_value(tsfInput[iSL].data() + 9, 210) << "\n";
343  for (int iAx = 0; iAx < 5; ++iAx) {
344  if (m_debugLevel >= 100 ||
345  std::any_of(tsfInput[iAx].cbegin() + 9, tsfInput[iAx].cend(), [](char i) {
346  return i != zero_val;
347  })) {
348  cout << "TSF" << iAx * 2 << " input: ";
349  display_hex(tsfInput[iAx]);
350  }
351  // write the input
352  Xsi_Instance->put_value(tsfPort[iAx], tsfInput[iAx].data());
353  }
354  // read the output
355  Xsi_Instance->get_value(out, t2d_out);
356 
357  // Put clk to zero
358  Xsi_Instance->put_value(clk, &zero_val);
359  Xsi_Instance->run(10);
360 
361  string found = display_value(t2d_out, 6);
362  if (m_debugLevel >= 100 ||
363  found.find("1") != std::string::npos) {
364  std::cout << "output: " << found << "\n";
365  for (unsigned j = 0; j < count(found.begin(), found.end(), '1'); j++) {
366  std::cout << display_value(t2d_out + 6 + 121 * j, 121) << "\n";
367  }
368  }
369  finderOutput = to_array(t2d_out);
370  decodeOutput(iClock);
371  } catch (std::exception& e) {
372  std::cerr << "ERROR: An exception occurred: " << e.what() << std::endl;
373  status = 2;
374  } catch (...) {
375  std::cerr << "ERROR: An unknown exception occurred." << std::endl;
376  status = 3;
377  }
378  }
379  std::cout << "status code:" << status << std::endl;
380 }
Belle2::CDCTrigger2DFinderFirmwareModule
This class is the interface between the 2D finder fast simulation module and the firmware simulation ...
Definition: CDCTrigger2DFinderFirmwareModule.h:43
REG_MODULE
#define REG_MODULE(moduleName)
Register the given module (without 'Module' suffix) with the framework.
Definition: Module.h:652
Cosim::slv_to_bin_string
std::string slv_to_bin_string(std::array< char, N > signal, bool padding=false)
Transform into string.
Definition: Cosim.h:57
Belle2::Bitstream
Class to hold one clock cycle of raw bit content.
Definition: Bitstream.h:47
Cosim
Helper class for software (C++) / firmware (VHDL) co-simulation.
Definition: Cosim.h:15
Cosim::display_hex
void display_hex(const std::array< char, N > &signal)
Display signal in hex.
Definition: Cosim.h:74
Cosim::zero_val
const char zero_val
'0' in XSI VHDL simulation
Definition: Cosim.h:38
Belle2::TRGFinderTrack::omega
double omega
omega of a 2D track
Definition: CDCTrigger2DFinderFirmwareModule.h:31
Belle2::Module
Base class for Modules.
Definition: Module.h:74
Belle2
Abstract base class for different kinds of events.
Definition: MillepedeAlgorithm.h:19
Belle2::TRGFinderTrack
TRGCDC 2D Finder track.
Definition: CDCTrigger2DFinderFirmwareModule.h:29
Cosim::one_val
const char one_val
'1' in XSI VHDL simulation
Definition: Cosim.h:36
Belle2::StoreArray::end
iterator end()
Return iterator to last entry +1.
Definition: StoreArray.h:330
Xsi::Loader
Definition: xsi_loader.h:23
Belle2::TRGFinderTrack::ts
tsOutArray ts
TS array of a 2D track.
Definition: CDCTrigger2DFinderFirmwareModule.h:35
Belle2::StoreArray
Accessor to arrays stored in the data store.
Definition: ECLMatchingPerformanceExpertModule.h:33
Cosim::display_value
std::string display_value(const char *count, int size)
Display value of the signal.
Definition: Cosim.h:41
Belle2::TRGFinderTrack::phi0
double phi0
phi0 of a 2D track
Definition: CDCTrigger2DFinderFirmwareModule.h:33
Belle2::CDCTriggerSegmentHit
Combination of several CDCHits to a track segment hit for the trigger.
Definition: CDCTriggerSegmentHit.h:16
Belle2::StoreArray::getEntries
int getEntries() const
Get the number of objects in the array.
Definition: StoreArray.h:226
Belle2::CDCTrigger2DFinderFirmwareModule::tsVector
std::array< char, m_tsVectorWidth > tsVector
array of TS
Definition: CDCTrigger2DFinderFirmwareModule.h:99