CGRA-ME
TatumInterface.cpp
Go to the documentation of this file.
1 /*******************************************************************************
2  * The software programs comprising "CGRA-ME" and the documentation provided
3  * with them are copyright by its authors and the University of Toronto. Only
4  * non-commercial, not-for-profit use of this software is permitted without ex-
5  * plicit permission. This software is provided "as is" with no warranties or
6  * guarantees of support. See the LICENCE for more details. You should have re-
7  * ceived a copy of the full licence along with this software. If not, see
8  * <http://cgra-me.ece.utoronto.ca/license/>.
9  ******************************************************************************/
10 
11 #include <CGRA/TatumInterface.h>
12 
13 #include <CGRA/GraphAlgorithms.h>
14 
15 TatumInterface::TatumInterface(std::map<std::string, double>& t, PerfEngine* p)
16  : timingModels(t)
17  , parent(p)
18 {
19 }
20 
21 // Shrunk the MRRG by merging all HW_WIRE nodes into edges
23 {
24  std::vector<MRRG::NodeDescriptor> uniq;
25  std::vector<MRRG::NodeDescriptor> nonWire; // MRRG nodes that are not HW_WIRE type
26  EdgeList invOrig; // inverse of orig[src] giving snk, invOig[snk] gives src
27 
28  // create list of unique nodes
29  for (auto it = orig.begin(); it != orig.end(); it++)
30  {
31  auto src = it->first;
32  auto snk = it->second;
33  std::vector<MRRG::NodeDescriptor> pair = {src, snk};
34  for (auto i = 0; i < 2; i++)
35  {
36  if (std::find(uniq.begin(), uniq.end(), pair[i]) == uniq.end())
37  uniq.push_back(pair[i]);
38  }
39  }
40 
41  // populate list of non-HW_WIRE nodes
42  for (auto& n : uniq)
43  {
44  if (n->et != HW_WIRE)
45  nonWire.push_back(n);
46  }
47 
48  // construct invOrig
49  for (auto it = orig.begin(); it != orig.end(); it++)
50  invOrig.insert(std::pair<MRRG::NodeDescriptor, MRRG::NodeDescriptor> (it->second, it->first));
51 
52  // identify each non-wire node's non-wire source(s) and sink(s)
53  for (auto& n : nonWire)
54  {
55  // find the source nodes
56  if (invOrig.count(n) != 0) // has source node(s)
57  {
58  std::vector<MRRG::NodeDescriptor> srcs;
59  auto begin = invOrig.lower_bound(n);
60  auto end = invOrig.upper_bound(n);
61  for (auto it = begin; it != end; it++)
62  {
63  srcs.push_back(it->second);
64  }
65  for (auto s : srcs)
66  {
67  // trace back to find first non-wire source
68  while (s->et == HW_WIRE)
69  s = invOrig.find(s)->second;
70  reduced.insert(std::pair<MRRG::NodeDescriptor, MRRG::NodeDescriptor> (s, n));
71  }
72  }
73  }
74 
75  /*
76  */
77  // Dump MRRG dot of mapped result without wire nodes
78  std::cout << "digraph ReducedMRRG {" << std::endl;
79  for(auto it = reduced.begin(); it != reduced.end(); it = reduced.upper_bound(it->first))
80  {
81  auto i = it;
82  while (i != reduced.upper_bound(it->first))
83  {
84  auto src = i->first;
85  auto des = i->second;
86  std::cout << "\"" << *src << "\" -> \"" << *des << "\";" << std::endl;
87  i++;
88  }
89  }
90  std::cout << "}\n" << std::endl;
91  /*
92  */
93 }
94 
96 {
97  // timing graph and name resolver constructor
98  std::shared_ptr<TimingGraph> tg_(new tatum::TimingGraph());
99  tg = tg_;
100  std::shared_ptr<MRRGNameResolver> nr_(new MRRGNameResolver(*tg));
101  nr = nr_;
102  EdgeList mrrg;
103  removeWireNodes(m, mrrg);
104 
105  clk = tg->add_node(NodeType::SOURCE);
106  nr->set_node_name(clk, "CGRA_Clock");
107 
108  std::vector<MRRG::NodeDescriptor> created;
109  for (auto it = mrrg.begin(); it != mrrg.end(); it++)
110  {
111  auto src = it->first;
112  auto snk = it->second;
113 #define createNewOnly(n, l) \
114 { \
115  if (std::find(l.begin(), l.end(), n) == l.end())\
116  { \
117  createTGEntity(n); \
118  connectTGClock(n); \
119  l.push_back(n); \
120  } \
121 }
122  createNewOnly(src, created);
123  createNewOnly(snk, created);
124 #undef createNewOnly
125  connectTGEntities(src, snk);
126  }
127 
128  tg->levelize();
129 }
130 
132 {
133  // construct delay calculator and timing constraints
134  std::shared_ptr<tatum::FixedDelayCalculator> dc_(
135  new tatum::FixedDelayCalculator(
136  maxComb,
137  setup,
138  minComb,
139  hold
140  )
141  );
142  dc = dc_;
143  std::shared_ptr<tatum::TimingConstraints> tc_(
144  new tatum::TimingConstraints()
145  );
146  tc = tc_;
147  clkDid = tc->create_clock_domain("CGRA_Clock");
148  tc->set_clock_domain_source(clk, clkDid);
149 
150  // set constraints to target zero clock period
151  tc->set_setup_constraint(clkDid, clkDid, Time(0.));
152  tc->set_hold_constraint(clkDid, clkDid, Time(0.));
153 
154  // generate list of entities acting as driver of the kernel/benchmark
155  std::vector<MRRG::NodeDescriptor> drivers;
156  for (auto it = m.begin(); it != m.end(); it++)
157  {
158  // appends every source node
159  auto src = it->first;
160  auto match = std::find(drivers.begin(), drivers.end(), src);
161  if (match == drivers.end())
162  drivers.push_back(src);
163  }
164  for (auto it = m.begin(); it != m.end(); it++)
165  {
166  // leave list with nodes which are strictly sources
167  auto snk = it->second;
168  auto match = std::find(drivers.begin(), drivers.end(), snk);
169  if (match != drivers.end())
170  drivers.erase(match);
171  }
172 
173  // mark these drivers' IPINs as constant generator constraints
174  for (auto& d : drivers)
175  {
176  auto entity = mrrg2tg[d];
177  for (auto& l : entity->ipins)
178  {
179  for (auto& ipin : l)
180  {
181  tc->set_constant_generator(ipin);
182  }
183  }
184  }
185 
186  // mark mem unit data OPINs as constant generator constraints
187  for (auto& n : memOut)
188  {
189  tc->set_constant_generator(n);
190  }
191 
192  // construct analyzer and reporter object
193  analyzer =
194  tatum::AnalyzerFactory<tatum::SetupHoldAnalysis>::make(
195  *tg,
196  *tc,
197  *dc
198  );
199  analyzer->update_timing();
200  std::shared_ptr<tatum::TimingReporter> tr_(
201  new tatum::TimingReporter(
202  *nr,
203  *tg,
204  *tc
205  )
206  );
207  tr = tr_;
208 }
209 
211 {
212  /*
213  */
214  auto dot_writer = tatum::make_graphviz_dot_writer(*tg, *dc);
215  dot_writer.write_dot_file("/tmp/CGRAME_TatumTG.dot");
216  tr->report_unconstrained_setup("/tmp/CGRAME_TimingReport_UnconstrainedSetupAnalysis.rpt", *analyzer);
217  /*
218  */
219  cgrame_msg("Performing STA...");
220  tr->report_timing_setup("/tmp/CGRAME_TimingReport_SetupAnalysis.rpt", *analyzer);
221  cgrame_msg("STA completed. Report dumped to: `/tmp/CGRAME_TimingReport_SetupAnalysis.rpt`");
222 }
223 
224 //void TatumInterface::reportHoldAnalysis()
225 //{
226 // cgrame_msg("Performing STA...");
227 // //tr->report_unconstrained_hold("/tmp/CGRAME_TimingReport_UnconstrainedHoldAnalysis.rpt", *analyzer);
228 // tr->report_timing_hold("/tmp/CGRAME_TimingReport_HoldAnalysis.rpt", *analyzer);
229 // cgrame_msg("STA completed. Report dumped to: `/tmp/CGRAME_TimingReport_HoldAnalysis.rpt`");
230 //}
231 
233 {
234  // for brevity
235  const auto ET_COMB = tatum::EdgeType::PRIMITIVE_COMBINATIONAL;
236  const auto ET_INTERC = tatum::EdgeType::INTERCONNECT;
237  const auto ET_C_LAUN = tatum::EdgeType::PRIMITIVE_CLOCK_LAUNCH;
238  const auto ET_C_CAPT = tatum::EdgeType::PRIMITIVE_CLOCK_CAPTURE;
239 
240  std::string n_name = std::to_string(n->cycle) + ":" + n->name;
241  switch (n->et)
242  {
243  case HW_REG:
244  {
245  // In Tatum, a flip-flop(1-bit) is modelled as such:
246  // ipin ──┐ ┌─> opin
247  // v ╵
248  // sink <─┐┌─> source
249  // ╵╵
250  // cpin
251  // where specific timing parameters are moddelled:
252  // setup: cpin ─> sink
253  // hold: cpin ─> sink
254  // clk2q: source ─> opin
255  // skew: *(external) ─> cpin
256 
257  // look up data size
258  auto width = n->parent->getSize();
259 
260  std::shared_ptr<TGEntity> reg(new TGEntity);
261  reg->type = TG_REG;
262  reg->width = width;
263  reg->parent = n;
264  mrrg2tg[n] = reg;
265 
266  // look up timing parameters
267  auto moduleName = n->parent->GenericName();
268  size_t index = moduleName.find("_");
269  auto moduleBasename = moduleName.substr(0, index);
270  auto moduleSuffix = moduleName.substr(index + 1, moduleName.length());
271  auto key = moduleBasename + "." + moduleSuffix;
272  auto kSetup = key + ".setup";
273  auto kHold = key + ".hold";
274  auto kC2q = key + ".c2q";
275  tatum::Time t_su, t_h, t_c2q;
276  if (timingModels.count(kSetup) == 0)
277  throw cgrame_error("Timing model of register type \"" + kSetup + "\" does not exist...");
278  if (timingModels.count(kHold) == 0)
279  throw cgrame_error("Timing model of register type \"" + kHold + "\" does not exist...");
280  if (timingModels.count(kC2q) == 0)
281  throw cgrame_error("Timing model of register type \"" + kC2q + "\" does not exist...");
282  t_su = tatum::Time(timingModels[kSetup]);
283  t_h = tatum::Time(timingModels[kHold]);
284  t_c2q = tatum::Time(timingModels[kC2q]);
285 
286  std::vector<tatum::NodeId> empty;
287  reg->ipins.push_back(empty);
288 
289  // Special case: ConstUnit's constant value registers are not
290  // clocked by CGRA_Clock, and should be handled like hardcode
291  // value which won't require any CPIN, SOURCE, or SINK node.
292  if (n->type == MRRG_NODE_FUNCTION)
293  {
294  for (unsigned i = 0; i < (reduce?1:width); i++)
295  {
296  auto ipin = tg->add_node(tatum::NodeType::IPIN);
297  auto opin = tg->add_node(tatum::NodeType::OPIN);
298  tg2mrrg[ipin] = n;
299  tg2mrrg[opin] = n;
300  nr->set_node_name(ipin, n_name + ".Q[" + std::to_string(i) + "] (" + moduleName + ")");
301  nr->set_node_name(opin, n_name + ".out[" + std::to_string(i) + "] (" + moduleName + ")");
302  auto ipin2opin = tg->add_edge(ET_INTERC, ipin, opin);
303  reg->ipins[0].push_back(ipin);
304  reg->opins.push_back(opin);
305  reg->ipins2opins.push_back(ipin2opin);
306  maxComb.insert(ipin2opin, tatum::Time(0.));
307  minComb.insert(ipin2opin, tatum::Time(0.));
308  }
309 
310  break;
311  }
312 
313 
314  // create the timing graph of this entity
315  for (unsigned i = 0; i < (reduce?1:width); i++)
316  {
317  // create Tatum timing graph nodes
318  auto ipin = tg->add_node(tatum::NodeType::IPIN);
319  auto opin = tg->add_node(tatum::NodeType::OPIN);
320  auto cpin = tg->add_node(tatum::NodeType::CPIN);
321  auto src = tg->add_node(tatum::NodeType::SOURCE);
322  auto snk = tg->add_node(tatum::NodeType::SINK);
323  tg2mrrg[ipin] = n;
324  tg2mrrg[opin] = n;
325  tg2mrrg[cpin] = n;
326  tg2mrrg[src] = n;
327  tg2mrrg[snk] = n;
328  nr->set_node_name(ipin, n_name + ".D[" + std::to_string(i) + "] (" + moduleName + ")");
329  nr->set_node_name(opin, n_name + ".Q[" + std::to_string(i) + "] (" + moduleName + ")");
330  nr->set_node_name(cpin, n_name + ".clk");
331  nr->set_node_name(snk, n_name + ".D_sink[" + std::to_string(i) + "] (" + moduleName + ")");
332  nr->set_node_name(src, n_name + ".Q_source[" + std::to_string(i) + "] (" + moduleName + ")");
333 
334  // create Tatum timing graph edges
335  auto ipin2snk = tg->add_edge(ET_COMB, ipin, snk);
336  auto src2opin = tg->add_edge(ET_C_LAUN, src, opin);
337  auto cpin2snk = tg->add_edge(ET_C_CAPT, cpin, snk);
338  auto cpin2src = tg->add_edge(ET_COMB, cpin, src);
339 
340  reg->ipins[0].push_back(ipin);
341  reg->opins.push_back(opin);
342  reg->cpins.push_back(cpin);
343  reg->srcs.push_back(src);
344  reg->snks.push_back(snk);
345  reg->ipins2snks.push_back(ipin2snk);
346  reg->srcs2opins.push_back(src2opin);
347  reg->cpins2snks.push_back(cpin2snk);
348  reg->cpins2srcs.push_back(cpin2src);
349 
350  // fill in timing parameters
351  setup.insert(cpin2snk, t_su);
352  hold.insert(cpin2snk, t_h);
353  maxComb.insert(cpin2src, tatum::Time(0.));
354  minComb.insert(cpin2src, tatum::Time(0.));
355  maxComb.insert(ipin2snk, tatum::Time(0.));
356  minComb.insert(ipin2snk, tatum::Time(0.));
357  maxComb.insert(src2opin, t_c2q); // t_clk2q
358  minComb.insert(src2opin, t_c2q); // t_clk2q
359  }
360  break;
361  }
362  case HW_MUX:
363  {
364  // TODO/FIXME: for realistic modelling, config storage driving
365  // select signals should also be added
366 
367  // look up data size
368  auto width = n->parent->getSize();
369 
370  std::shared_ptr<TGEntity> mux(new TGEntity);
371  mux->type = TG_COMB;
372  mux->width = width;
373  mux->parent = n;
374  mrrg2tg[n] = mux;
375 
376  // look up timing parameters
377  auto moduleName = n->parent->GenericName();
378  size_t index = moduleName.find("_");
379  auto moduleBasename = moduleName.substr(0, index);
380  auto moduleSuffix = moduleName.substr(index + 1, moduleName.length());
381  auto key = moduleBasename + "." + moduleSuffix;
382  tatum::Time t_max, t_min; // max and min combinational delay
383  // TODO: min & max should use separate key
384  if (timingModels.count(key) == 0)
385  throw cgrame_error("Timing model of multiplexor type \"" + key + "\" does not exist...");
386  t_max = tatum::Time(timingModels[key]);
387  t_min = tatum::Time(timingModels[key]);
388 
389  std::vector<tatum::NodeId> empty;
390  mux->ipins.push_back(empty);
391 
392  // create the timing graph of this entity
393  for (unsigned i = 0; i < (reduce?1:width); i++)
394  {
395  // create TG nodes and edges
396  auto ipin = tg->add_node(tatum::NodeType::IPIN);
397  auto opin = tg->add_node(tatum::NodeType::OPIN);
398  tg2mrrg[ipin] = n;
399  tg2mrrg[opin] = n;
400  // FIXME: figure out which input index is being used (assumed 0 for now)
401  nr->set_node_name(ipin, n_name + ".in0[" + std::to_string(i) + "] (" + moduleName + ")");
402  nr->set_node_name(opin, n_name + ".out[" + std::to_string(i) + "] (" + moduleName + ")");
403  auto ipin2opin = tg->add_edge(ET_COMB, ipin, opin);
404  mux->ipins[0].push_back(ipin);
405  mux->opins.push_back(opin);
406  mux->ipins2opins.push_back(ipin2opin);
407 
408  // fill in timing parameters
409  maxComb.insert(ipin2opin, t_max);
410  minComb.insert(ipin2opin, t_min);
411  }
412  break;
413  }
414  case HW_COMB:
415  {
416  auto moduleName = n->parent->GenericName();
417 
418  //retrieve op information from MRRGNode
419  // could either be an ALU op or a memory op
420  std::vector<OpGraphOpCode> aluOps =
421  {
422  OpCode::ADD,
423  OpCode::SUB,
424  OpCode::MUL,
425  OpCode::DIV,
426  OpCode::AND,
427  OpCode::OR,
428  OpCode::XOR,
429  OpCode::SHL,
430  OpCode::ASHR,
431  OpCode::LSHR,
432  };
433  std::vector<OpGraphOpCode> memOps =
434  {
435  OpCode::LOAD,
437  };
438 
439  //auto opCode = n->mapped_opcode;
440  auto opCode = parent->mapped_node2opcode[n];
441  auto opName = to_string(opCode);
442  auto width = n->parent->getSize();
443  auto key = opName + "_" + std::to_string(width) + "b";
444  if (std::find(aluOps.begin(), aluOps.end(), opCode) != aluOps.end())
445  {
446  // TODO: latency of ALU is currently unused on the mapper side,
447  // so this portion of code will need update once mapper does
448  // fully support latency specification.
449  // TODO: should have t_comb_max and t_comb_min
450  std::shared_ptr<TGEntity> fu(new TGEntity);
451  fu->type = TG_COMB;
452  fu->width = width;
453  fu->parent = n;
454  mrrg2tg[n] = fu;
455  tatum::Time t_comb;
456  key = "op." + key;
457  if (timingModels.count(key) == 0)
458  throw cgrame_error("Timing model of compute unit type \"" + key + "\" does not exist...");
459  t_comb = tatum::Time(timingModels[key]);
460 
461  std::vector<tatum::NodeId> ipinsA;
462  std::vector<tatum::NodeId> ipinsB;
463  fu->ipins.push_back(ipinsA);
464  fu->ipins.push_back(ipinsB);
465 
466  // create fully connected in[*]->out[*]
467  std::vector<tatum::NodeId> opins;
468  for (unsigned i = 0; i < (reduce?1:width); i++)
469  {
470  auto opin = tg->add_node(tatum::NodeType::OPIN);
471  opins.push_back(opin);
472  tg2mrrg[opin] = n;
473  nr->set_node_name(opin, n_name + ".out[" + std::to_string(i) + "] (" + moduleName + ")");
474  fu->opins.push_back(opin);
475  }
476  for (unsigned i = 0; i < (reduce?1:width); i++)
477  {
478  auto ipinA = tg->add_node(tatum::NodeType::IPIN);
479  auto ipinB = tg->add_node(tatum::NodeType::IPIN);
480  tg2mrrg[ipinA] = n;
481  tg2mrrg[ipinB] = n;
482  nr->set_node_name(ipinA, n_name + ".in_a[" + std::to_string(i) + "] (" + moduleName + ")");
483  nr->set_node_name(ipinB, n_name + ".in_b[" + std::to_string(i) + "] (" + moduleName + ")");
484  fu->ipins[0].push_back(ipinA);
485  fu->ipins[1].push_back(ipinB);
486  for (unsigned j = 0; j < (reduce?1:width); j++)
487  {
488  auto ipinA2opin = tg->add_edge(ET_COMB, ipinA, opins[j]);
489  auto ipinB2opin = tg->add_edge(ET_COMB, ipinB, opins[j]);
490  fu->ipins2opins.push_back(ipinA2opin);
491  fu->ipins2opins.push_back(ipinB2opin);
492  maxComb.insert(ipinA2opin, t_comb);
493  minComb.insert(ipinA2opin, t_comb);
494  maxComb.insert(ipinB2opin, t_comb);
495  minComb.insert(ipinB2opin, t_comb);
496  }
497  }
498  }
499  else if (std::find(memOps.begin(), memOps.end(), opCode) != memOps.end())
500  {
501  std::shared_ptr<TGEntity> mem(new TGEntity);
502  mem->type = TG_COMB;
503  mem->width = width;
504  mem->parent = n;
505  mrrg2tg[n] = mem;
506  tatum::Time t_acc;
507  key = "memUnit." + key;
508  if (timingModels.count(key) == 0)
509  throw cgrame_error("Timing model of memory unit \"" + key + "\" does not exist...");
510  t_acc = tatum::Time(timingModels[key]);
511  if (opCode == OpCode::LOAD)
512  {
513  std::vector<tatum::NodeId> empty;
514  mem->ipins.push_back(empty);
515  for (unsigned i = 0; i < (reduce?1:width); i++)
516  {
517  // TODO: currently ipin[i] points to opin[i], we should
518  // make sure this TG representation is acceptable
519  auto ipin = tg->add_node(tatum::NodeType::IPIN);
520  auto opin = tg->add_node(tatum::NodeType::OPIN);
521  tg2mrrg[ipin] = n;
522  tg2mrrg[opin] = n;
523  nr->set_node_name(ipin, n_name + ".addr[" + std::to_string(i) + "] (" + moduleName + ")");
524  nr->set_node_name(opin, n_name + ".out[" + std::to_string(i) + "] (" + moduleName + ")");
525 
526  //FIXME: remove any use for memOut once OpenRAM results are in
527  //auto ipin2opin = tg->add_edge(ET_COMB, ipin, opin);
528  memOut.push_back(opin);
529 
530  mem->ipins[0].push_back(ipin);
531  mem->opins.push_back(opin);
532  //mem->ipins2opins.push_back(ipin2opin);
533 
534  // fill in timing parameters
535  //maxComb.insert(ipin2opin, t_acc);
536  //minComb.insert(ipin2opin, t_acc);
537  }
538  }
539  else if (opCode == OpCode::STORE)
540  {
541  std::vector<tatum::NodeId> ipinsAddr;
542  std::vector<tatum::NodeId> ipinsData;
543  mem->ipins.push_back(ipinsAddr);
544  mem->ipins.push_back(ipinsData);
545  for (unsigned i = 0; i < (reduce?1:width); i++)
546  {
547  auto ipinAddr = tg->add_node(tatum::NodeType::IPIN);
548  auto opinAddr = tg->add_node(tatum::NodeType::OPIN);
549  auto ipinData = tg->add_node(tatum::NodeType::IPIN);
550  auto opinData = tg->add_node(tatum::NodeType::OPIN);
551  nr->set_node_name(ipinAddr, n_name + ".addr[" + std::to_string(i) + "] (" + moduleName + ")");
552  nr->set_node_name(opinAddr, n_name + ".extAddr[" + std::to_string(i) + "] (" + moduleName + ")");
553  nr->set_node_name(ipinData, n_name + ".data[" + std::to_string(i) + "] (" + moduleName + ")");
554  nr->set_node_name(opinData, n_name + ".extData[" + std::to_string(i) + "] (" + moduleName + ")");
555  tg2mrrg[ipinAddr] = n;
556  tg2mrrg[opinAddr] = n;
557  tg2mrrg[ipinData] = n;
558  tg2mrrg[opinData] = n;
559  auto ipin2opinAddr = tg->add_edge(ET_COMB, ipinAddr, opinAddr);
560  auto ipin2opinData = tg->add_edge(ET_COMB, ipinData, opinData);
561  mem->ipins[0].push_back(ipinAddr);
562  mem->ipins[1].push_back(ipinData);
563  mem->opins.push_back(opinAddr);
564  mem->opins.push_back(opinData);
565  mem->ipins2opins.push_back(ipin2opinAddr);
566  mem->ipins2opins.push_back(ipin2opinData);
567 
568  // fill in timing parameters
569  maxComb.insert(ipin2opinAddr, t_acc);
570  minComb.insert(ipin2opinAddr, t_acc);
571  maxComb.insert(ipin2opinData, t_acc);
572  minComb.insert(ipin2opinData, t_acc);
573  }
574  }
575  else
576  throw cgrame_error("Non-load/store operation mapped to a memUnit");
577  } else if (opCode == OpCode::OUTPUT) {
578  // not sure if this the correct way to model IO
579  auto io = std::make_shared<TGEntity>();
580  io->type = TG_COMB;
581  io->width = width;
582  io->parent = n;
583  mrrg2tg[n] = io;
584 
585  auto ipin = tg->add_node(tatum::NodeType::IPIN);
586  nr->set_node_name(ipin, n_name + " output pin");
587  tg2mrrg[ipin] = n;
588 
589  io->ipins.emplace_back();
590  io->ipins.at(0).push_back(ipin);
591  } else if (opCode == OpCode::INPUT) {
592  // not sure if this the correct way to model IO
593  auto io = std::make_shared<TGEntity>();
594  io->type = TG_COMB;
595  io->width = width;
596  io->parent = n;
597  mrrg2tg[n] = io;
598 
599  auto opin = tg->add_node(tatum::NodeType::OPIN);
600  nr->set_node_name(opin, n_name + " input pin");
601  tg2mrrg[opin] = n;
602 
603  io->opins.push_back(opin);
604  }
605  else
606  throw cgrame_error("Unexpected non-routing combinational entity");
607  break;
608  }
609  case HW_WIRE:
610  // NOTE: wires are transformed/merged into mere MRRG edges and
611  // shouldn't be encountered in this method, refer to class method
612  // removeWireNodes() within method createTatumTimingGraph().
613  throw cgrame_error("Wire entity is not a valid timing graph node");
614  break;
615  case HW_UNSPECIFIED:
616  throw cgrame_error("MRRG node is of unspecified HW entity");
617  break;
618  }
619 }
620 
622 {
623  auto tg_src = mrrg2tg.at(src);
624  auto tg_snk = mrrg2tg.at(snk);
625 
626  std::string src_name = std::to_string(src->cycle) + ":" + src->name;
627  std::string snk_name = std::to_string(snk->cycle) + ":" + snk->name;
628 
629  unsigned width;
630  unsigned in_index;
631 
632  if (snk->et == HW_COMB && tg_snk->used_input > 1)
633  throw cgrame_error("All ports of fanout hardware `" + snk_name + "` are assigned");
634  else
635  in_index = tg_snk->used_input;
636 
637  if (tg_src->width != tg_snk->width)
638  throw cgrame_error("Edges can't be made between entities of different data width");
639  else
640  width = tg_src->width;
641 
642  tg_snk->used_input++; //FIXME: hacky mechanism to choose which ipins
643 
644  double est_RC_delay = getFanoutBasedRCDelay(src);
645 
646  for (unsigned i = 0; i < (reduce?1:width); i++)
647  {
648  // FIXME: currently, this doesn't check which input is for inputA/B in
649  // FuncUnit case, and it also doesn't check which input addr/data in
650  // MEMUnit case.
651  auto wire = tg->add_edge(tatum::EdgeType::INTERCONNECT, tg_src->opins[i], tg_snk->ipins[in_index][i]);
652  maxComb.insert(wire, tatum::Time(est_RC_delay)); //FIXME: should be part of user input
653  minComb.insert(wire, tatum::Time(est_RC_delay)); //FIXME: should be part of user input
654  }
655 }
656 
657 // DF traversal on fanout list locating all fanout primitives
658 std::set<MRRG::NodeDescriptor> getFanoutPrimitives(MRRG::NodeDescriptor s)
659 {
660  struct LocalMRRG {
661  auto& fanout(MRRG::NodeDescriptor n) const { return n->fanout; }
662  } mrrg;
663 
664  struct V : DoNothingGraphVisitor<MRRG::NodeDescriptor> {
665  Module* starting_module;
666  std::set<MRRG::NodeDescriptor> found_non_wires;
667 
668  // records any nodes with significant delay (ie. aren't wires)
669  void onExamine(const MRRG::NodeDescriptor& v) {
670  if (v->parent != starting_module and v->et != HW_WIRE) {
671  found_non_wires.insert(v);
672  }
673  }
674 
675  // don't explore beyond any nodes as found in onExamine
676  bool shouldIgnoreEdge(const MRRG::NodeDescriptor& source, const MRRG::NodeDescriptor&) {
677  return found_non_wires.find(source) != found_non_wires.end();
678  }
679  } v;
680  v.starting_module = s->parent;
681 
683  g_algos.depthFirstVisit(mrrg, s, visitAllVertices(), v);
684 
685  return std::move(v.found_non_wires);
686 }
687 
689 {
690  double delay = 0.;
691  Module* src_module = src->parent;
692  std::vector<MRRG::NodeDescriptor> fanout_nodes;
693  std::vector<MRRG::NodeDescriptor> false_path_checklist;
694 
695  getFanoutPrimitives(src);
696 
697  auto overriden = src_module->overridenFanoutCount;
698  auto fanoutCount = (overriden == -1) ? fanout_nodes.size() : overriden;
699  auto base = timingModels["wireload.base"];
700  auto scale = timingModels["wireload.scale"];
701  delay = scale * (double)fanoutCount + base;
702 
703  return delay;
704 }
705 
707 {
708  auto tg_entity = mrrg2tg[n];
709  if (n->et == HW_REG && n->type != MRRG_NODE_FUNCTION)
710  {
711  auto len = tg_entity->cpins.size();
712  for (auto i = 0; i < (reduce?1:len); i++)
713  {
714  tatum::Time t_skew = tatum::Time(0.); //FIXME: should be part of user input
715  auto clkWire = tg->add_edge(EdgeType::INTERCONNECT, clk, tg_entity->cpins[i]);
716  maxComb.insert(clkWire, t_skew);
717  minComb.insert(clkWire, t_skew);
718  }
719  }
720 }
MRRGNode::et
HWEntityType et
Definition: MRRG.h:108
OpCode::DIV
@ DIV
HW_WIRE
@ HW_WIRE
Definition: MRRG.h:49
GraphAlgorithms
Definition: GraphAlgorithms.h:72
GraphAlgorithms.h
MRRGNode::type
MRRGNode_Type type
Definition: MRRG.h:107
TG_COMB
@ TG_COMB
Definition: TatumInterface.h:53
OpCode::ASHR
@ ASHR
TatumInterface::mrrg2tg
std::map< MRRG::NodeDescriptor, std::shared_ptr< TGEntity > > mrrg2tg
Definition: TatumInterface.h:108
TatumInterface.h
OpCode::OR
@ OR
begin
auto begin(const SingleItemImmutableSet< VertexID > &siis)
Definition: Collections.h:137
TatumInterface::maxComb
linear_map< tatum::EdgeId, tatum::Time > maxComb
Definition: TatumInterface.h:103
TatumInterface::clkDid
tatum::DomainId clkDid
Definition: TatumInterface.h:95
OpCode::STORE
@ STORE
visitAllVertices
auto visitAllVertices()
Definition: GraphAlgorithmHelpers.h:38
TatumInterface::dc
std::shared_ptr< tatum::FixedDelayCalculator > dc
Definition: TatumInterface.h:98
OpCode::ADD
@ ADD
PerfEngine::mapped_node2opcode
std::map< MRRG::NodeDescriptor, OpGraphOpCode > mapped_node2opcode
Definition: PerfEngine.h:36
TatumInterface::TatumInterface
TatumInterface(std::map< std::string, double > &, PerfEngine *)
Definition: TatumInterface.cpp:15
TatumInterface::parent
PerfEngine * parent
Definition: TatumInterface.h:102
DoNothingGraphVisitor::onExamine
void onExamine(const VertexID &)
Definition: GraphAlgorithmHelpers.h:55
TatumInterface::setup
linear_map< tatum::EdgeId, tatum::Time > setup
Definition: TatumInterface.h:105
to_string
const std::string & to_string(const OpGraphOpCode &opcode)
Definition: OpGraph.cpp:111
OpCode::LOAD
@ LOAD
HW_MUX
@ HW_MUX
Definition: MRRG.h:51
TatumInterface::connectTGClock
void connectTGClock(MRRG::NodeDescriptor n, bool reduce=true)
Definition: TatumInterface.cpp:706
DoNothingGraphVisitor::shouldIgnoreEdge
bool shouldIgnoreEdge(const VertexID &, const VertexID &)
Definition: GraphAlgorithmHelpers.h:70
MRRGNode::parent
Module * parent
Definition: MRRG.h:119
OpCode::LSHR
@ LSHR
TatumInterface::tc
std::shared_ptr< tatum::TimingConstraints > tc
Definition: TatumInterface.h:99
EdgeList
std::multimap< MRRG::NodeDescriptor, MRRG::NodeDescriptor > EdgeList
Definition: PerfEngine.h:26
Module::overridenFanoutCount
int overridenFanoutCount
Definition: Module.h:241
TatumInterface::tg
std::shared_ptr< tatum::TimingGraph > tg
Definition: TatumInterface.h:96
createNewOnly
#define createNewOnly(n, l)
Module::GenericName
virtual std::string GenericName()
Definition: Module.cpp:260
TatumInterface::clk
tatum::NodeId clk
Definition: TatumInterface.h:94
OpCode::XOR
@ XOR
MRRGNode::cycle
unsigned int cycle
Definition: MRRG.h:127
OpCode::INPUT
@ INPUT
HW_COMB
@ HW_COMB
Definition: MRRG.h:52
HW_REG
@ HW_REG
Definition: MRRG.h:50
TatumInterface::tg2mrrg
std::map< tatum::NodeId, MRRG::NodeDescriptor > tg2mrrg
Definition: TatumInterface.h:109
TatumInterface::tr
std::shared_ptr< tatum::TimingReporter > tr
Definition: TatumInterface.h:101
MRRG_NODE_FUNCTION
@ MRRG_NODE_FUNCTION
Definition: MRRG.h:46
cgrame_msg
#define cgrame_msg(m)
Definition: Util.h:17
Module
Definition: Module.h:163
OpCode::SHL
@ SHL
MRRGNameResolver
Definition: TatumInterface.h:117
HW_UNSPECIFIED
@ HW_UNSPECIFIED
Definition: MRRG.h:53
TatumInterface::connectTGEntities
void connectTGEntities(MRRG::NodeDescriptor from, MRRG::NodeDescriptor to, bool reduce=true)
Definition: TatumInterface.cpp:621
TatumInterface::minComb
linear_map< tatum::EdgeId, tatum::Time > minComb
Definition: TatumInterface.h:104
TatumInterface::initializeTatumEngine
void initializeTatumEngine(EdgeList &m)
Definition: TatumInterface.cpp:131
TatumInterface::getFanoutBasedRCDelay
double getFanoutBasedRCDelay(MRRG::NodeDescriptor src)
Definition: TatumInterface.cpp:688
DoNothingGraphVisitor
Definition: GraphAlgorithmHelpers.h:49
end
auto end(const SingleItemImmutableSet< VertexID > &siis)
Definition: Collections.h:138
TatumInterface::memOut
std::vector< tatum::NodeId > memOut
Definition: TatumInterface.h:107
MRRGNode
Definition: MRRG.h:60
Module::getSize
int getSize() const
Definition: Module.h:246
OpCode::OUTPUT
@ OUTPUT
TatumInterface::timingModels
std::map< std::string, double > & timingModels
Definition: TatumInterface.h:93
TGEntity
Definition: TatumInterface.h:56
MRRGNode::fanout
std::vector< MRRGNode * > fanout
Definition: MRRG.h:113
OpCode::AND
@ AND
TatumInterface::createTatumTimingGraph
void createTatumTimingGraph(EdgeList &m)
Definition: TatumInterface.cpp:95
OpCode::SUB
@ SUB
OpCode::MUL
@ MUL
PerfEngine
Definition: PerfEngine.h:28
TatumInterface::removeWireNodes
void removeWireNodes(EdgeList &orig, EdgeList &reduced)
Definition: TatumInterface.cpp:22
TatumInterface::nr
std::shared_ptr< MRRGNameResolver > nr
Definition: TatumInterface.h:97
TatumInterface::hold
linear_map< tatum::EdgeId, tatum::Time > hold
Definition: TatumInterface.h:106
TG_REG
@ TG_REG
Definition: TatumInterface.h:52
TatumInterface::createTGEntity
void createTGEntity(MRRG::NodeDescriptor n, bool reduce=true)
Definition: TatumInterface.cpp:232
TatumInterface::analyzer
std::shared_ptr< tatum::SetupHoldTimingAnalyzer > analyzer
Definition: TatumInterface.h:100
TatumInterface::reportSetupAnalysis
void reportSetupAnalysis()
Definition: TatumInterface.cpp:210
getFanoutPrimitives
std::set< MRRG::NodeDescriptor > getFanoutPrimitives(MRRG::NodeDescriptor s)
Definition: TatumInterface.cpp:658
MRRGNode::name
std::string name
Definition: MRRG.h:106
cgrame_error
Definition: Exception.h:20