CGRA-ME
MRRG.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 <algorithm>
12 #include <iostream>
13 #include <queue>
14 #include <sstream>
15 #include <vector>
16 
17 #include <CGRA/Exception.h>
18 #include <CGRA/MRRG.h>
19 #include <CGRA/MRRGProcedures.h>
21 
22 namespace {
23  template<typename OSTREAM>
24  bool analyze_mrrg_verify_results(OSTREAM&& os, const std::vector<MRRG::VerifyMessage>& messages,
25  const bool silent_on_no_errors
26  ) {
27  using Type = MRRG::VerifyMessage::Type;
28 
29  const auto contains_error = std::any_of(begin(messages), end(messages),
30  [](auto&& msg) { return msg.type == Type::Error; });
31  const auto has_messages = !messages.empty();
32  const auto print_all_messages = contains_error || not silent_on_no_errors;
33 
34  if (print_all_messages) {
35  if (contains_error) {
36  os << "MRRG verify FAILED";
37  } else {
38  os << "MRRG verify passed";
39  }
40 
41  if (has_messages) {
42  os << ". Begin report:\n";
43 
44  for (auto& msg : messages) {
45  os << msg.type << ": " << msg.message << '\n';
46  }
47 
48  os << "End MRRG verify report\n";
49  } else {
50  os << ", and nothing to report.\n";
51  }
52 
53  }
54 
55  return contains_error;
56  }
57 }
58 
59 void MRRGNode::applyReferenceRename(const std::unordered_map<const MRRGNode*, MRRGNode*>& rename_map) {
60  for (auto& elem : fanout) {
61  elem = value_for_key_or(rename_map, elem, elem);
62  }
63  for (auto& elem : fanin) {
64  elem = value_for_key_or(rename_map, elem, elem);
65  }
66 }
67 
68 MRRG& MRRG::operator=(const MRRG& rhs) {
69  std::unordered_map<const MRRGNode*, MRRGNode*> rename_map;
70 
71  this->nodes.clear();
72  this->nodes.resize(rhs.initiationInterval());
73 
74  for (int cycle = 0; cycle < rhs.initiationInterval(); ++cycle) {
75  for (auto& name_and_nodeptr : rhs.allNodesByCycle().at(cycle)) {
76  auto new_node = std::make_unique<MRRGNode>(std::move(MRRGNode(*name_and_nodeptr.second)));
77  rename_map[name_and_nodeptr.second.get()] = new_node.get();
78  this->nodes.at(cycle).emplace(new_node->name, std::move(new_node));
79  }
80  }
81 
82  for (int cycle = 0; cycle < this->initiationInterval(); ++cycle) {
83  for (auto& name_and_nodeptr : this->allNodesByCycle().at(cycle)) {
84  name_and_nodeptr.second->applyReferenceRename(rename_map);
85  }
86  }
87 
88  return *this;
89 }
90 
91 std::pair<MRRG::NodeDescriptor,bool> MRRG::insert(MRRGNode node) {
92  auto& nodes_this_cycle = nodes.at(node.cycle);
93  const auto& find_result = nodes_this_cycle.find(node.name);
94 
95  if (find_result == end(nodes_this_cycle)) {
96  auto node_name_copy = node.name;
97  auto new_node = std::make_unique<MRRGNode>(std::move(node));
98 
99  return {
100  nodes_this_cycle.emplace(std::move(node_name_copy), std::move(new_node)).first->second.get(),
101  true,
102  };
103 
104  } else {
105  return { find_result->second.get(), false };
106  }
107 }
108 
110  auto& nodes_in_cycle = nodes.at(getNodeRef(n).cycle);
111  auto name_and_node_it = nodes_in_cycle.find(getNodeRef(n).name);
112 
113  // check the name
114  if (name_and_node_it == end(nodes_in_cycle)) {
115  make_and_throw<cgrame_error>([&](auto&& s) {
116  s << "tried to erase a node named " << this->getNodeRef(n).getFullName()
117  << " but a node with that name doesn't exist!";
118  });
119  }
120 
121  // check that it's the same node
122  if (name_and_node_it->second.get() != n) {
123  make_and_throw<cgrame_error>([&](auto&& s) {
124  s << "tried to erase a node named " << this->getNodeRef(n).getHierarchyQualifiedName()
125  << " but the node is not the one expected!";
126  });
127  }
128 
129  // unlink everything. Reversed so that we don't walk over ourselves
130  // as unlink removes elements from these lists
131  for (auto& fi : reversed(getNodeRef(n).fanin)) {
132  unlink(fi, n);
133  }
134  for (auto& fo : reversed(getNodeRef(n).fanout)) {
135  unlink(n, fo);
136  }
137 
138  // free the node & remove it from the graph
139  nodes_in_cycle.erase(name_and_node_it);
140 }
141 
142 MRRG::NodeDescriptor MRRG::getNode(int cycle, const std::string& name) const {
143  if (cycle < 0 || cycle >= initiationInterval()) {
144  return nullptr;
145  } else {
146  std::unique_ptr<MRRGNode> default_value = nullptr;
147  return value_for_key_or(nodes.at(cycle), name, default_value).get();
148  }
149 }
150 
151 void MRRG::printDot(std::ostream& os, Module* topModule, const ConfigStore& archAttrs) const
152 {
153  std::stringstream arch_args(archAttrs.getString("arch_opts"));
154  std::string opt_pair;
155  int rows = 0;
156  int cols = 0;
157  while(arch_args >> opt_pair)
158  {
159  auto n = opt_pair.find('=');
160  if(n == std::string::npos) { make_and_throw<cgrame_error>([&](auto&& s) {
161  s << "Ill-formatted C++ architecture option: " << opt_pair;
162  });}
163  auto key = opt_pair.substr(0, n);
164  auto value = opt_pair.substr(n + 1, std::string::npos);
165  if (key.find("row") != std::string::npos) {
166  rows = std::stoi(value);
167  std::cout<<value;
168  }
169  if (key.find("cols") != std::string::npos) {
170  cols = std::stoi(value);
171  std::cout<<value;
172  }
173  }
174  std::cout<<rows<<" "<<cols<<std::endl;
175  if (rows == 0 || cols == 0){
176  std::cout<<"ERROR"<<std::endl;
177  return;
178  }
179  std::string top = std::to_string(0);
180  std::string bottom = std::to_string(cols + 1) ;
181  std::string right = std::to_string(rows + 1);
182  std::string left = std::to_string(0);
183 
184  os << "digraph " <<topModule->getName()<<" {\ngraph[";
185  os << "II=" << this->initiationInterval() << ", ";
186  for (auto & attr : archAttrs) {
187  os << attr.first << "=\"" << attr.second << "\", ";
188  }
189  // Visual attributes
190  os << "splines=ortho, concentrate=true, landscape=false];\nnode[shape=record, fixedsize=true, height=0.8, width=2, fontsize=7, fontname=\"times bold\"];\n";
191 
192  for (auto& module : topModule->submodules){
193  os << "subgraph cluster_" << module.first<<" {\n";
194  bool hasFunctionNode = false;
195  std::string moduleName = module.second->getName();
196  auto n =moduleName.find("");
197  if ((n = moduleName.find("mem_")) != std::string::npos){
198  auto row = moduleName.substr(n+4, std::string::npos);
199  os << "x_pos = " <<left<<" \n";
200  os << "y_pos = " <<row<<" \n";
201  } else if ((n = moduleName.find("top_")) != std::string::npos){
202  auto col = moduleName.substr(n + 4, std::string::npos);
203  os << "x_pos = " <<col<<" \n";
204  os << "y_pos = " <<top<<" \n";
205  } else if ((n = moduleName.find("bottom_")) != std::string::npos){
206  auto col = moduleName.substr(n + 7, std::string::npos);
207  os << "x_pos = " <<col<<" \n";
208  os << "y_pos = " <<bottom<<" \n";
209  } else if ((n = moduleName.find("right_")) != std::string::npos){
210  auto row = moduleName.substr(n + 6, std::string::npos);
211  os << "x_pos = " <<right<<" \n";
212  os << "y_pos = " <<row<<" \n";
213  } else if ((n = moduleName.find("pe_")) != std::string::npos){
214  auto col = moduleName.substr(n + 4, moduleName.find("_r"));
215  auto row = moduleName.substr(moduleName.find("_r") + 2);
216  os << "x_pos = " <<std::stoi(col) + 1<<" \n";
217  os << "y_pos = " <<std::stoi(row) + 1<<" \n";
218  }
219  for(int i = 0; i < (std::ptrdiff_t)nodes.size(); i++)
220  {
221  for(auto n = nodes[i].begin(); n != nodes[i].end(); n++)
222  {
223  auto& node = n->second;
224  if (node->parent != module.second) continue;
225  os << "\"" << *(n->second) << "\"";
226  if (node->type == MRRG_NODE_ROUTING_FUNCTION) {
227  hasFunctionNode = true;
228  os <<"[ type=MRRG_NODE_ROUTING_FUNCTION, latency=" <<node->latency<<", supportedOps=\"";
229  for (auto op : node->supported_ops){
230  os <<op<<"\t";
231  }
232  os << "\" supportedOperands=\"";
233  for (auto operand_tag : node->supported_operand_tags){
234  os <<operand_tag<<" ";
235  }
236  } else if (n->second->type == MRRG_NODE_ROUTING){
237  os <<"[ type=MRRG_NODE_ROUTING, latency=" <<node->latency<<", supportedOperands=\"";
238  for (auto operand_tag : node->supported_operand_tags){
239  os <<operand_tag<<" ";
240  }
241  } else if (n->second->type == MRRG_NODE_FUNCTION){
242  hasFunctionNode = true;
243  os <<"[ type=MRRG_NODE_FUNCTION, latency=" <<node->latency<<", supportedOps=\"";
244  for (auto op : node->supported_ops){
245  os <<op<<" ";
246  }
247  } else {
248  //TODO::Add an error message
249  }
250  os << "\"];\n";
251  }
252  }
253 
254  for(int i = 0; i < (std::ptrdiff_t)nodes.size(); i++)
255  {
256 
257  for(auto n = nodes[i].begin(); n != nodes[i].end(); n++)
258  {
259  auto& node = n->second;
260  if (node->parent != module.second) continue;
261  for(auto fanout = node->fanout.begin(); fanout != node->fanout.end(); fanout++)
262  {
263  if ((*fanout)->parent == module.second)
264  os << "\"" << *(n->second) << "\"->\"" << **fanout << "\";\n";
265  }
266 
267 
268  }
269  }
270  if (!module.second->submodules.empty() & !hasFunctionNode){
271  printSubmoduleDot(os, module.second);
272  }
273  os <<"}\n";
274  }
275  for(int i = 0; i < (std::ptrdiff_t)nodes.size(); i++)
276  {
277  for(auto n = nodes[i].begin(); n != nodes[i].end(); n++)
278  {
279  if (!topModule->isSubModule(n->second->parent) && n->second->parent != topModule) continue;
280  for(auto fanout = n->second->fanout.begin(); fanout != n->second->fanout.end(); fanout++)
281  {
282 
283  if (!topModule->isSubModule((*fanout)->parent) && (*fanout)->parent != topModule) continue;
284  if (n->second->parent == (*fanout)->parent) continue;
285  os << "\"" << *(n->second) << "\"->\"" << **fanout << "\";\n";
286  }
287  }
288  }
289  os << "}\n";
290 }
291 
292 void MRRG::printSubmoduleDot(std::ostream& os, Module* submodule) const
293 {
294  for (auto& module : submodule->submodules){
295  os << "subgraph cluster_" << module.first<<" {\n";
296  bool hasFunctionNode = false;
297  for(int i = 0; i < (std::ptrdiff_t)nodes.size(); i++)
298  {
299 
300  for(auto n = nodes[i].begin(); n != nodes[i].end(); n++)
301  {
302  auto& node = n->second;
303  if (node->parent != module.second) continue;
304  os << "\"" << *(n->second) << "\"";
305  if (node->type == MRRG_NODE_ROUTING_FUNCTION) {
306  hasFunctionNode = true;
307  os <<"[ type=MRRG_NODE_ROUTING_FUNCTION, latency=" <<node->latency<<", supportedOps=\"";
308  for (auto op : node->supported_ops){
309  os <<op<<"\t";
310  }
311  os << "\" supportedOperands=\"";
312  for (auto operand_tag : node->supported_operand_tags){
313  os <<operand_tag<<" ";
314  }
315  } else if (n->second->type == MRRG_NODE_ROUTING){
316  os <<"[ type=MRRG_NODE_ROUTING, latency=" <<node->latency<<", supportedOperands=\"";
317  for (auto operand_tag : node->supported_operand_tags){
318  os <<operand_tag<<" ";
319  }
320  } else if (n->second->type == MRRG_NODE_FUNCTION){
321  hasFunctionNode = true;
322  os <<"[ type=MRRG_NODE_FUNCTION, latency=" <<node->latency<<", supportedOps=\"";
323  for (auto op : node->supported_ops){
324  os <<op<<" ";
325  }
326  } else {
327  //TODO::Add an error message
328  }
329  os << "\"];\n";
330  }
331  }
332 
333  for(int i = 0; i < (std::ptrdiff_t)nodes.size(); i++)
334  {
335  for(auto n = nodes[i].begin(); n != nodes[i].end(); n++)
336  {
337  auto& node = n->second;
338  if (node->parent != module.second) continue;
339  for(auto fanout = node->fanout.begin(); fanout != node->fanout.end(); fanout++)
340  {
341  if ((*fanout)->parent == module.second)
342  os << "\"" << *(n->second) << "\"->\"" << **fanout << "\";\n";
343  }
344 
345 
346  }
347  }
348  if (!module.second->submodules.empty() & !hasFunctionNode){
349  printSubmoduleDot(os, module.second);
350  }
351  os <<"}\n";
352  }
353  for(int i = 0; i < (std::ptrdiff_t)nodes.size(); i++)
354  {
355  for(auto n = nodes[i].begin(); n != nodes[i].end(); n++)
356  {
357  if (!submodule->isSubModule(n->second->parent) && n->second->parent != submodule) continue;
358  for(auto fanout = n->second->fanout.begin(); fanout != n->second->fanout.end(); fanout++)
359  {
360  if (!submodule->isSubModule((*fanout)->parent) && (*fanout)->parent != submodule) continue;
361  if (n->second->parent == (*fanout)->parent) continue;
362  os << "\"" << *(n->second) << "\"->\"" << **fanout << "\";\n";
363  }
364  }
365  }
366  return;
367 }
368 
369 void MRRG::printSupportedOps(std::ostream& os) const
370 {
371  std::set<std::pair<OpGraphOpCode, int>> supported_operands;
372  const auto classes = computeNodeClassLists(*this);
373 
374  for (const auto& mrrg_node : classes.function_nodes) {
375  for (auto supported_operand : mrrg_node->supported_ops) {
376  supported_operands.emplace(std::make_pair(supported_operand, mrrg_node->latency));
377  }
378  }
379 
380  os << "digraph {\n";
381  for (auto supp_operand : supported_operands){
382  os << "\t" << supp_operand.first << "[OP_LATENCY = " << supp_operand.second << "]" << "\n";
383  }
384  for (auto supp_operand_i : supported_operands){
385  for (auto supp_operand_j : supported_operands){
386  os << "\t\"" << supp_operand_i.first << "\"->\"" << supp_operand_j.first << "\" [";
387  os << "LOWER_BOUND_NETWORK_LATENCY = 1, UPPER_BOUND_NETWORK_LATENCY = "<<std::to_string(1000000)<<"];\n";
388  }
389  }
390 
391  os << "}\n";
392 }
393 
394 void MRRG::printDot(std::ostream& os) const {
395  os << "digraph {\n";
396  for (const auto& nodes_in_cycle : nodes) {
397  for (const auto& name_and_node : nodes_in_cycle) {
398  const auto& node = *name_and_node.second;
399  const auto has_latency = node.getLatency() != 0;
400  const auto has_operand_tags = not node.supported_operand_tags.empty();
401  const bool has_properties = has_latency || has_operand_tags;
402  if (has_properties) {
403  os << '"' << node << "\" [";
404  auto comma = "";
405  if (has_latency) {
406  os << comma << "CGRAME_latency=" << node.getLatency();
407  comma = ",";
408  }
409  if (has_operand_tags) {
410  os << comma << "CGRAME_operand_tags=\"";
411  for (const auto& t : node.supported_operand_tags) os << t << ';';
412  os << '"';
413  comma = ",";
414  }
415  os << "];\n";
416  }
417  for (const auto& fanout : node.fanout) {
418  os << "\"" << node << "\"->\"" << *fanout << "\";\n";
419  }
420  }
421  }
422  os << "}\n";
423 }
424 
425 static std::string find_cluster_name(std::string s1, std::string s2)
426 {
427  int last_dot = 0;
428  for(int i = 0; i < std::min((std::ptrdiff_t)s1.size(), (std::ptrdiff_t)s2.size()); i++)
429  {
430  if(s1[i] == s2[i])
431  {
432  if(s1[i] == '.' || s1[i] == ':')
433  last_dot = i;
434  }
435  else
436  break;
437  }
438 
439  // std::cout << "last dot of (" << s1 << "," << s2 << ") is " << last_dot << "\n";
440  return s1.substr(0, last_dot);
441 }
442 
443 static bool isDirectSubCluster(std::string c, std::string sub)
444 {
445  if(c == sub || c.size() >= sub.size())
446  return false;
447 
448  bool found_dot = false;
449  int i = 0;
450  for(; i < (std::ptrdiff_t)c.size(); i++)
451  {
452  if(c[i] == sub[i])
453  {
454  if(sub[i] == '.')
455  found_dot = true;
456  }
457  else
458  {
459  if(found_dot)
460  break;
461  else
462  return false;
463  }
464  }
465  for(; i < (std::ptrdiff_t)c.size(); i++)
466  {
467  if(sub[i] == '.')
468  return false;
469  }
470  return true;
471 }
472 
473 bool verifyAndPrintReport(const MRRG& mrrg, std::ostream& os,
474  bool silent_on_no_errors, bool throw_if_errors, const ConfigStore& extra_opts
475 ) {
476  const auto verify_output = mrrg.verify(extra_opts);
477  const auto found_errors = analyze_mrrg_verify_results(os, verify_output, silent_on_no_errors);
478  if (throw_if_errors && found_errors) {
479  make_and_throw<cgrame_error>([&](auto&& s) { s << "MRRG verify check failed! check stdout for results"; });
480  } else {
481  return not found_errors;
482  }
483 }
484 
485 static void print_subcluster(std::map<std::string, std::string> & clusters, std::string current_cluster, std::ostream& os)
486 {
487  std::string s = current_cluster;
488  if (current_cluster == "")
489  return;
490 
491  std::string contents = clusters[current_cluster];
492 
493  os << "subgraph \"cluster_" << s << "\"{\n";
494  for(auto c = clusters.begin(); c != clusters.end(); ++c)
495  {
496  if(isDirectSubCluster(s, c->first))
497  {
498  print_subcluster(clusters, c->first, os);
499  }
500  }
501  clusters[current_cluster] = "";
502  os << contents << "\nlabel = \"" << s << "\";\n}\n";
503 }
504 
505 void MRRG::printDotClustered(std::ostream& os) const
506 {
507  std::map<std::string, std::string> clusters;
508 
509  clusters[""] = "";
510 
511  for(int i = 0; i < initiationInterval(); i++)
512  {
513  clusters[std::to_string(i)] = "";
514  }
515 
516  for(int i = 0; i < (std::ptrdiff_t)nodes.size(); i++) // nodes.begin(); it != nodes.end(); ++it)
517  {
518  for(auto n = nodes[i].begin(); n != nodes[i].end(); n++)
519  {
520  for(auto fanout = n->second->fanout.begin(); fanout != n->second->fanout.end(); fanout++)
521  {
522  std::string srcname = n->second->getFullName();
523  std::string dstname = (*fanout)->getFullName();
524 
525  std::string subgraph = find_cluster_name(srcname, dstname);
526 
527  clusters[subgraph] += "\"" + srcname + "\"->\"" + dstname + "\";\n";
528  }
529  }
530  }
531 
532  os << "digraph {\n";
533  for(int i = 0; i < initiationInterval(); i++)
534  {
535  print_subcluster(clusters, std::to_string(i), os);
536  }
537 
538  os << "}\n";
539 }
540 
541 void MRRG::printBasicStats(std::ostream& os) const {
542  std::unordered_set<Module*> unique_modules;
543  std::ptrdiff_t num_edges = 0;
544  for (int cycle = 0; cycle < initiationInterval(); ++cycle) {
545  for (auto& name_and_nodeptr : allNodesByCycle().at(cycle)) {
546  num_edges += name_and_nodeptr.second->fanout.size();
547  unique_modules.insert(name_and_nodeptr.second->parent);
548  }
549  }
550 
551  os
552  << ' ' << size() << " vertices,"
553  << ' ' << num_edges << " edges,"
554  << ' ' << unique_modules.size() << " unique module instances,"
555  << " and end statistics";
556 }
557 
558 std::ptrdiff_t MRRG::size() const {
559  std::ptrdiff_t num_nodes = 0;
560  for (int cycle = 0; cycle < initiationInterval(); ++cycle) {
561  for (auto& name_and_nodeptr : allNodesByCycle().at(cycle)) {
562  (void)name_and_nodeptr;
563  num_nodes += 1;
564  }
565  }
566  return num_nodes;
567 }
568 
569 std::string MRRGNode::getFullName() const
570 {
571  return std::to_string(cycle) + ":" + name;
572 }
573 
574 /*
575 float MRRGNode::getCost(float penalty_factor)
576 {
577  return base_cost * occupancy + (occupancy <= capacity ? 0.0 : (occupancy - capacity) * penalty_factor);
578 }
579 
580 */
581 bool MRRGNode::canMapOp(OpGraphOp const* op) const {
582  // If the operation bitwidth does not the mrrg node bitwidth
583  // return false
584  if (op->bitwidth != bitwidth) return false;
585 
586  for (const auto& supported_op : supported_ops) {
587  if (op->opcode == supported_op)
588  return true;
589  }
590  return false;
591 }
592 
593 std::ostream& operator<< (std::ostream& out, const MRRGNode& node)
594 {
595  return out << node.cycle << ":" << node.name;
596 }
597 
598 template <typename T>
599 static bool check_unique(std::vector<T> v)
600 {
601  if(v.size() < 2)
602  return true;
603 
604  for(int i = 0; i < (std::ptrdiff_t)v.size() - 1; i++)
605  {
606  for(int j = i + 1; j < (std::ptrdiff_t)v.size(); j++)
607  {
608  if(v[i] == v[j])
609  return false;
610  }
611  }
612 
613  return true;
614 }
615 
616 namespace {
617 
618 std::pair<bool, MRRG::VerifyMessage::Type> interpret_verify_msg_option(const ConfigStore& options, const std::string& opt_name) {
619  const auto opt_value = options.getString(opt_name);
620  if (false) { }
621  else if (opt_value == "error") { return {true, MRRG::VerifyMessage::Type::Error}; }
622  else if (opt_value == "warning") { return {true, MRRG::VerifyMessage::Type::Warning}; }
623  else if (opt_value == "info") { return {true, MRRG::VerifyMessage::Type::Info}; }
624  else if (opt_value == "ignore") { return {false, MRRG::VerifyMessage::Type::Info}; }
625  else { throw std::logic_error(
626  "don't understand MRRG verify check option " + opt_name + "=" + opt_value
627  ); }
628 };
629 
630 } // end anon namespace
631 
632 std::vector<MRRG::VerifyMessage> MRRG::verify(const ConfigStore& overrides) const
633 {
634  ConfigStore options {
635  {"check_mux_exclusivity", "ignore"},
636  };
637  override_all(options, overrides); // throws if something is mis-spelt
638 
639  const auto mux_exclusivity_enabled_and_msg_type = interpret_verify_msg_option(options, "check_mux_exclusivity");
640 
641  std::vector<VerifyMessage> result;
642  const auto& add_result = [&](VerifyMessage::Type type, auto&& f) {
643  result.emplace_back(VerifyMessage{ type, string_from_stream(std::forward<decltype(f)>(f)) });
644  };
645  const auto& add_error = [&](auto&& f) { add_result(VerifyMessage::Type::Error, std::forward<decltype(f)>(f)); };
646  const auto& add_warning = [&](auto&& f) { add_result(VerifyMessage::Type::Warning, std::forward<decltype(f)>(f)); };
647  const auto& add_info = [&](auto&& f) { add_result(VerifyMessage::Type::Info, std::forward<decltype(f)>(f)); };
648  (void)add_warning; (void)add_info; // Remove this line when these are used (it silences a warning)
649 
650  const auto& is_operand_node = [&](NodeDescriptor n) {return !n->supported_operand_tags.empty(); };
651 
652  for (int cycle = 0; cycle < initiationInterval(); ++cycle) {
653  for (auto& name_and_node : nodes.at(cycle)) {
654  if (name_and_node.first != name_and_node.second->name) { add_error([&](auto&& s) {
655  s << "Node " << *name_and_node.second
656  << " is stored under the incorrect name '" << name_and_node.first << '\'';
657  });}
658  if (cycle != name_and_node.second->cycle) { add_error([&](auto&& s) {
659  s << "Node " << *name_and_node.second
660  << " is stored under the incorrect cycle number of " << cycle;
661  });}
662  }
663  }
664 
665  const auto node_classes = computeNodeClassLists(*this);
666 
667  // Check every routing node in the MRRG
668  for(auto & r : node_classes.routing_nodes)
669  {
670  // check that fanins and fanouts are unique (i.e. no duplicate pointers)
671  if(!check_unique(r->fanin))
672  {
673  add_error([&](auto&& s) { s << "Fanins not unique for node: " << *r; });
674  }
675  if(!check_unique(r->fanout))
676  {
677  add_error([&](auto&& s) { s << "Fanouts not unique for node: " << *r; });
678  }
679  // check that for every fanout, there is a fanin
680  for(auto & fo : r->fanout)
681  {
682 
683  bool found_r = false;
684  for(auto & fi : fo->fanin)
685  {
686  if(fi == r)
687  {
688  found_r = true;
689  break;
690  }
691  }
692 
693  if(!found_r)
694  {
695  add_error([&](auto&& s) { s << "Missing back link: " << *r << " <- " << *fo; });
696  }
697  }
698 
699  // Check that paths between operand nodes have a FU node through BFS
700  if (is_operand_node(r)) {
701  std::unordered_set<NodeDescriptor> visited = {r};
702  std::queue<NodeDescriptor> toVisit;
703  for (auto & fanout : r->fanout) {
704  if (fanout->type == MRRG_NODE_ROUTING) {
705  toVisit.emplace(fanout);
706  }
707  }
708  while (!toVisit.empty()) {
709  auto n = toVisit.front();
710  toVisit.pop();
711  if (is_operand_node(n)) {
712  add_error([&](auto&& s) { s << "Path between operand nodes: " << *r << " and " << *n << " does not contain a FU node"; });
713  }
714  if (fanin(n).size() != 1) {
715  add_error([&](auto&& s) {
716  s << "Routing node between operand node " << *n << " and it's FU has more than one fanin."
717  << " There should be no other way to reach these nodes via fanouts."
718  << " This allows CGRA-ME to use simpler routing algorithms.";
719  });
720  }
721  visited.emplace(n);
722  for (auto & fanout : n->fanout) {
723  if (fanout->type == MRRG_NODE_ROUTING && visited.find(fanout) == visited.end()) {
724  toVisit.emplace(fanout);
725  }
726  }
727  }
728  }
729  }
730 
731  for(auto & r : node_classes.function_nodes)
732  {
733  // check that fanins and fanouts are unique (i.e. no duplicate pointers)
734  if(!check_unique(r->fanin))
735  {
736  add_error([&](auto&& s) { s << "Fanins not unique for node: " << *r; });
737  }
738 
739  if(!check_unique(r->fanout))
740  {
741  add_error([&](auto&& s) { s << "Fanouts not unique for node: " << *r; });
742  }
743 
744  // check that for every fanout, there is a fanin
745  for(auto & fo : r->fanout)
746  {
747 
748  bool found_r = false;
749  for(auto & fi : fo->fanin)
750  {
751  if(fi == r)
752  {
753  found_r = true;
754  break;
755  }
756  }
757 
758  if(!found_r) {
759  add_error([&](auto&& s) { s << "Missing back link: " << *r << " <- " << *fo; });
760  }
761  }
762  }
763 
764  for (const auto& nodes_for_ii : nodes) {
765  for (const auto& name_and_nodeptr : nodes_for_ii) {
766  const auto& node = *name_and_nodeptr.second;
767  const auto& expected_fanout_cycle = (node.cycle + node.latency) % initiationInterval();
768 
769  // is latency annotated correctly
770  for (const auto& fanoutptr : node.fanout) {
771  const auto& fanout = *fanoutptr;
772 
773  if (fanout.cycle != (int)expected_fanout_cycle) {
774  add_error([&](auto&& s) {
775  s << "Node {" << node << "}, with latency " << node.latency
776  << " fans-out to node {" << fanout << "}, which does not have the expected cycle of "
777  << expected_fanout_cycle << " (II == " << this->initiationInterval() << ")";
778  });
779  }
780  }
781 
782  // 'mux invariant'
783  if (mux_exclusivity_enabled_and_msg_type.first && node.fanin.size() > 1) {
784  for (const auto& faninptr : node.fanin) {
785  const auto& fanin = *faninptr;
786  if (fanin.fanout.size() > 1) {
787  add_result(mux_exclusivity_enabled_and_msg_type.second, [&](auto&& s) {
788  s << "Node {" << node << "}, with fanin degree, greater than one, of " << node.fanin.size() << " has fanin node {" << fanin << "} which also has fanout degree, greater than one, of " << fanin.fanout.size();
789  });
790  }
791  }
792  }
793  }
794  }
795 
796  return result;
797 }
798 
799 void MRRG::renameNode(NodeDescriptor ndesc, std::string new_name) {
800  auto& the_node = getNodeRef(ndesc);
801  auto& nodes_in_cycle = nodes.at(the_node.cycle);
802 
803  auto old_node_position = nodes_in_cycle.find(the_node.name);
804  if (old_node_position == end(nodes_in_cycle)) {
805  make_and_throw<cgrame_error>([&](auto&& s) {
806  s << "This node " << the_node << " does not exist in this graph";
807  });
808  }
809 
810  auto seach_result_for_new_name = nodes_in_cycle.find(new_name);
811  if (seach_result_for_new_name != end(nodes_in_cycle)) {
812  make_and_throw<cgrame_error>([&](auto&& s) {
813  s << "A node named " << new_name << " already exists! Cannot rename " << the_node;
814  });
815  }
816 
817  nodes_in_cycle[new_name] = std::move(old_node_position->second);
818  nodes_in_cycle.erase(old_node_position);
819  the_node.name = std::move(new_name);
820 }
821 
822 static void link(MRRGNode* driver, MRRGNode* fanout)
823 {
824  // std::cout << "linking {" << driver << "} -> {" << fanout << "}\n"; // for debugging. Prepare for spam.
825  driver->fanout.push_back(fanout);
826  fanout->fanin.push_back(driver);
827 }
828 
829 static void unlink(MRRGNode* driver, MRRGNode* fanout)
830 {
831  // std::cout << "unlinking {" << driver << "} -> {" << fanout << "}\n"; // for debugging. Prepare for spam.
832  auto elem = std::find(driver->fanout.begin(), driver->fanout.end(), fanout);
833  if(elem == driver->fanout.end()) {
834  make_and_throw<cgrame_error>([&](auto&& s) {
835  s << "trying to remove link {" << driver << "} -> {" << fanout << "} but link doesn't exist";
836  });
837  }
838  driver->fanout.erase(elem);
839 
840  auto elem1 = std::find(fanout->fanin.begin(), fanout->fanin.end(), driver);
841  if(elem1 == fanout->fanin.end()) {
842  make_and_throw<cgrame_error>([&](auto&& s) {
843  s << "trying to remove link {" << driver << "} -> {" << fanout << "} but link wasn't set up properly";
844  });
845  }
846  fanout->fanin.erase(elem1);
847 }
848 
Module::submodules
std::map< std::string, Module * > submodules
Definition: Module.h:227
MRRG::link
void link(MRRG::NodeDescriptor driver, MRRG::NodeDescriptor fanout)
Definition: MRRG.cpp:849
MRRGNode::getFullName
std::string getFullName() const
Definition: MRRG.cpp:569
MRRG_NODE_ROUTING_FUNCTION
@ MRRG_NODE_ROUTING_FUNCTION
Definition: MRRG.h:47
MRRG::VerifyMessage::Type::Warning
@ Warning
verifyAndPrintReport
bool verifyAndPrintReport(const MRRG &mrrg, std::ostream &os, bool silent_on_no_errors, bool throw_if_errors, const ConfigStore &extra_opts)
Definition: MRRG.cpp:473
MRRG::allNodesByCycle
const auto & allNodesByCycle() const
Definition: MRRG.h:357
MRRG
Definition: MRRG.h:216
Module::isSubModule
bool isSubModule(Module *)
Definition: Module.cpp:1160
MRRG::printSubmoduleDot
void printSubmoduleDot(std::ostream &os, Module *) const
Definition: MRRG.cpp:292
begin
auto begin(const SingleItemImmutableSet< VertexID > &siis)
Definition: Collections.h:137
link
static void link(MRRGNode *driver, MRRGNode *fanout)
Definition: MRRG.cpp:822
MRRGNode::applyReferenceRename
void applyReferenceRename(const std::unordered_map< const MRRGNode *, MRRGNode * > &rename_map)
Definition: MRRG.cpp:59
MRRG::operator=
MRRG & operator=(const MRRG &rhs)
Definition: MRRG.cpp:68
find_cluster_name
static std::string find_cluster_name(std::string s1, std::string s2)
Definition: MRRG.cpp:425
override_all
ConfigStore & override_all(ConfigStore &into, const ConfigStore &from)
Definition: ConfigStore.h:257
operator<<
std::ostream & operator<<(std::ostream &out, const MRRGNode &node)
Definition: MRRG.cpp:593
MRRG::erase
void erase(MRRG::NodeDescriptor n)
Definition: MRRG.cpp:109
MRRG::renameNode
void renameNode(NodeDescriptor ndesc, std::string new_name)
Definition: MRRG.cpp:799
ConfigStore
Definition: ConfigStore.h:76
print_subcluster
static void print_subcluster(std::map< std::string, std::string > &clusters, std::string current_cluster, std::ostream &os)
Definition: MRRG.cpp:485
MRRG::VerifyMessage::Type::Error
@ Error
MRRG::verify
std::vector< VerifyMessage > verify(const ConfigStore &options={}) const
Definition: MRRG.cpp:632
to_string
const std::string & to_string(const OpGraphOpCode &opcode)
Definition: OpGraph.cpp:111
MRRG::printDot
void printDot(std::ostream &os, Module *, const ConfigStore &archAttrs) const
Definition: MRRG.cpp:151
MRRG::insert
std::pair< NodeDescriptor, bool > insert(MRRGNode node)
Definition: MRRG.cpp:91
MRRG::VerifyMessage::Type::Info
@ Info
Exception.h
MRRG::printSupportedOps
void printSupportedOps(std::ostream &os) const
Definition: MRRG.cpp:369
MRRG::unlink
void unlink(MRRG::NodeDescriptor driver, MRRG::NodeDescriptor fanout)
Definition: MRRG.cpp:850
MRRG::nodes
std::vector< std::map< std::string, std::unique_ptr< MRRGNode > > > nodes
Definition: MRRG.h:365
MRRG::fanout
auto & fanout(NodeDescriptor ndesc) const
Definition: MRRG.h:288
MRRGNode::cycle
unsigned int cycle
Definition: MRRG.h:127
MRRGNode::canMapOp
bool canMapOp(OpGraphOp const *op) const
Definition: MRRG.cpp:581
MRRG::getNodeRef
MRRGNode & getNodeRef(NodeDescriptor ndesc)
Definition: MRRG.h:273
MRRGNode::fanin
std::vector< MRRGNode * > fanin
Definition: MRRG.h:114
MRRG_NODE_FUNCTION
@ MRRG_NODE_FUNCTION
Definition: MRRG.h:46
Module
Definition: Module.h:163
string_from_stream
std::string string_from_stream(F &&f)
Definition: Exception.h:95
unlink
static void unlink(MRRGNode *driver, MRRGNode *fanout)
Definition: MRRG.cpp:829
MRRG::printDotClustered
void printDotClustered(std::ostream &os) const
Definition: MRRG.cpp:505
MRRG::fanin
auto & fanin(NodeDescriptor ndesc) const
Definition: MRRG.h:293
OpGraphOp::bitwidth
int bitwidth
Definition: OpGraph.h:159
MRRG_NODE_ROUTING
@ MRRG_NODE_ROUTING
Definition: MRRG.h:45
MRRGNode::supported_ops
std::vector< OpGraphOpCode > supported_ops
Definition: MRRG.h:111
MRRGNode::getHierarchyQualifiedName
const std::string & getHierarchyQualifiedName() const
Definition: MRRG.h:122
Collections.h
end
auto end(const SingleItemImmutableSet< VertexID > &siis)
Definition: Collections.h:138
OpGraphOp
Definition: OpGraph.h:131
MRRGNode
Definition: MRRG.h:60
value_for_key_or
auto value_for_key_or(ASSOCIATIVE_COLLECTION &assoc_collection, const KEY &key, DEFAULT_VALUE &default_value) -> std::conditional_t< std::is_lvalue_reference< DEFAULT_VALUE >::value, decltype(false ? default_value :assoc_collection.find(key) ->second)&, decltype(false ? default_value :assoc_collection.find(key) ->second) >
Definition: Collections.h:339
ConfigStore::getString
const std::string & getString(const std::string &key) const
Definition: ConfigStore.h:136
Module::getName
auto & getName() const
Definition: Module.h:247
isDirectSubCluster
static bool isDirectSubCluster(std::string c, std::string sub)
Definition: MRRG.cpp:443
MRRGNode::fanout
std::vector< MRRGNode * > fanout
Definition: MRRG.h:113
MRRG::size
std::ptrdiff_t size() const
Definition: MRRG.cpp:558
MRRG::getNode
NodeDescriptor getNode(int cycle, const std::string &name) const
Definition: MRRG.cpp:142
check_unique
static bool check_unique(std::vector< T > v)
Definition: MRRG.cpp:599
MRRGNode::bitwidth
unsigned int bitwidth
Definition: MRRG.h:129
MRRG::initiationInterval
int initiationInterval() const
Definition: MRRG.h:346
MRRGProcedures.h
reversed
auto reversed(Range &r)
Definition: Collections.h:116
MRRG::VerifyMessage
Definition: MRRG.h:303
MRRG.h
MRRG::VerifyMessage::Type
Type
Definition: MRRG.h:304
OpGraphOp::opcode
OpGraphOpCode opcode
Definition: OpGraph.h:158
MRRGNode::name
std::string name
Definition: MRRG.h:106
MRRG::printBasicStats
void printBasicStats(std::ostream &os) const
Definition: MRRG.cpp:541
computeNodeClassLists
MRRGNodeClassLists computeNodeClassLists(const MRRG &mrrg)
Definition: MRRGProcedures.cpp:169