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
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;
34 if (print_all_messages) {
36 os <<
"MRRG verify FAILED";
38 os <<
"MRRG verify passed";
42 os <<
". Begin report:\n";
44 for (
auto& msg : messages) {
45 os << msg.type <<
": " << msg.message <<
'\n';
48 os <<
"End MRRG verify report\n";
50 os <<
", and nothing to report.\n";
55 return contains_error;
60 for (
auto& elem :
fanout) {
63 for (
auto& elem :
fanin) {
69 std::unordered_map<const MRRGNode*, MRRGNode*> rename_map;
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));
84 name_and_nodeptr.second->applyReferenceRename(rename_map);
93 const auto& find_result = nodes_this_cycle.find(node.
name);
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));
100 nodes_this_cycle.emplace(std::move(node_name_copy), std::move(new_node)).first->second.get(),
105 return { find_result->second.get(),
false };
111 auto name_and_node_it = nodes_in_cycle.find(
getNodeRef(n).name);
114 if (name_and_node_it ==
end(nodes_in_cycle)) {
115 make_and_throw<cgrame_error>([&](
auto&& s) {
117 <<
" but a node with that name doesn't exist!";
122 if (name_and_node_it->second.get() != n) {
123 make_and_throw<cgrame_error>([&](
auto&& s) {
125 <<
" but the node is not the one expected!";
139 nodes_in_cycle.erase(name_and_node_it);
146 std::unique_ptr<MRRGNode> default_value =
nullptr;
153 std::stringstream arch_args(archAttrs.
getString(
"arch_opts"));
154 std::string opt_pair;
157 while(arch_args >> opt_pair)
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;
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);
169 if (key.find(
"cols") != std::string::npos) {
170 cols = std::stoi(value);
174 std::cout<<rows<<
" "<<cols<<std::endl;
175 if (rows == 0 || cols == 0){
176 std::cout<<
"ERROR"<<std::endl;
184 os <<
"digraph " <<topModule->
getName()<<
" {\ngraph[";
186 for (
auto & attr : archAttrs) {
187 os << attr.first <<
"=\"" << attr.second <<
"\", ";
190 os <<
"splines=ortho, concentrate=true, landscape=false];\nnode[shape=record, fixedsize=true, height=0.8, width=2, fontsize=7, fontname=\"times bold\"];\n";
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";
219 for(
int i = 0; i < (std::ptrdiff_t)
nodes.size(); i++)
223 auto& node = n->second;
224 if (node->parent != module.second)
continue;
225 os <<
"\"" << *(n->second) <<
"\"";
227 hasFunctionNode =
true;
228 os <<
"[ type=MRRG_NODE_ROUTING_FUNCTION, latency=" <<node->latency<<
", supportedOps=\"";
229 for (
auto op : node->supported_ops){
232 os <<
"\" supportedOperands=\"";
233 for (
auto operand_tag : node->supported_operand_tags){
234 os <<operand_tag<<
" ";
237 os <<
"[ type=MRRG_NODE_ROUTING, latency=" <<node->latency<<
", supportedOperands=\"";
238 for (
auto operand_tag : node->supported_operand_tags){
239 os <<operand_tag<<
" ";
242 hasFunctionNode =
true;
243 os <<
"[ type=MRRG_NODE_FUNCTION, latency=" <<node->latency<<
", supportedOps=\"";
244 for (
auto op : node->supported_ops){
254 for(
int i = 0; i < (std::ptrdiff_t)
nodes.size(); i++)
259 auto& node = n->second;
260 if (node->parent != module.second)
continue;
263 if ((*fanout)->parent == module.second)
264 os <<
"\"" << *(n->second) <<
"\"->\"" << **
fanout <<
"\";\n";
270 if (!module.second->submodules.empty() & !hasFunctionNode){
275 for(
int i = 0; i < (std::ptrdiff_t)
nodes.size(); i++)
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++)
283 if (!topModule->
isSubModule((*fanout)->parent) && (*fanout)->parent != topModule)
continue;
284 if (n->second->parent == (*fanout)->parent)
continue;
285 os <<
"\"" << *(n->second) <<
"\"->\"" << **
fanout <<
"\";\n";
295 os <<
"subgraph cluster_" << module.first<<
" {\n";
296 bool hasFunctionNode =
false;
297 for(
int i = 0; i < (std::ptrdiff_t)
nodes.size(); i++)
302 auto& node = n->second;
303 if (node->parent != module.second)
continue;
304 os <<
"\"" << *(n->second) <<
"\"";
306 hasFunctionNode =
true;
307 os <<
"[ type=MRRG_NODE_ROUTING_FUNCTION, latency=" <<node->latency<<
", supportedOps=\"";
308 for (
auto op : node->supported_ops){
311 os <<
"\" supportedOperands=\"";
312 for (
auto operand_tag : node->supported_operand_tags){
313 os <<operand_tag<<
" ";
316 os <<
"[ type=MRRG_NODE_ROUTING, latency=" <<node->latency<<
", supportedOperands=\"";
317 for (
auto operand_tag : node->supported_operand_tags){
318 os <<operand_tag<<
" ";
321 hasFunctionNode =
true;
322 os <<
"[ type=MRRG_NODE_FUNCTION, latency=" <<node->latency<<
", supportedOps=\"";
323 for (
auto op : node->supported_ops){
333 for(
int i = 0; i < (std::ptrdiff_t)
nodes.size(); i++)
337 auto& node = n->second;
338 if (node->parent != module.second)
continue;
341 if ((*fanout)->parent == module.second)
342 os <<
"\"" << *(n->second) <<
"\"->\"" << **
fanout <<
"\";\n";
348 if (!module.second->submodules.empty() & !hasFunctionNode){
353 for(
int i = 0; i < (std::ptrdiff_t)
nodes.size(); i++)
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++)
360 if (!submodule->
isSubModule((*fanout)->parent) && (*fanout)->parent != submodule)
continue;
361 if (n->second->parent == (*fanout)->parent)
continue;
362 os <<
"\"" << *(n->second) <<
"\"->\"" << **
fanout <<
"\";\n";
371 std::set<std::pair<OpGraphOpCode, int>> supported_operands;
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));
381 for (
auto supp_operand : supported_operands){
382 os <<
"\t" << supp_operand.first <<
"[OP_LATENCY = " << supp_operand.second <<
"]" <<
"\n";
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";
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 <<
"\" [";
406 os << comma <<
"CGRAME_latency=" << node.getLatency();
409 if (has_operand_tags) {
410 os << comma <<
"CGRAME_operand_tags=\"";
411 for (
const auto& t : node.supported_operand_tags) os << t <<
';';
417 for (
const auto&
fanout : node.fanout) {
418 os <<
"\"" << node <<
"\"->\"" << *
fanout <<
"\";\n";
428 for(
int i = 0; i < std::min((std::ptrdiff_t)s1.size(), (std::ptrdiff_t)s2.size()); i++)
432 if(s1[i] ==
'.' || s1[i] ==
':')
440 return s1.substr(0, last_dot);
445 if(c == sub || c.size() >= sub.size())
448 bool found_dot =
false;
450 for(; i < (std::ptrdiff_t)c.size(); i++)
465 for(; i < (std::ptrdiff_t)c.size(); i++)
474 bool silent_on_no_errors,
bool throw_if_errors,
const ConfigStore& extra_opts
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"; });
481 return not found_errors;
485 static void print_subcluster(std::map<std::string, std::string> & clusters, std::string current_cluster, std::ostream& os)
487 std::string s = current_cluster;
488 if (current_cluster ==
"")
491 std::string contents = clusters[current_cluster];
493 os <<
"subgraph \"cluster_" << s <<
"\"{\n";
494 for(
auto c = clusters.begin(); c != clusters.end(); ++c)
501 clusters[current_cluster] =
"";
502 os << contents <<
"\nlabel = \"" << s <<
"\";\n}\n";
507 std::map<std::string, std::string> clusters;
516 for(
int i = 0; i < (std::ptrdiff_t)
nodes.size(); i++)
520 for(
auto fanout = n->second->fanout.begin();
fanout != n->second->fanout.end();
fanout++)
522 std::string srcname = n->second->getFullName();
523 std::string dstname = (*fanout)->getFullName();
527 clusters[subgraph] +=
"\"" + srcname +
"\"->\"" + dstname +
"\";\n";
542 std::unordered_set<Module*> unique_modules;
543 std::ptrdiff_t num_edges = 0;
546 num_edges += name_and_nodeptr.second->fanout.size();
547 unique_modules.insert(name_and_nodeptr.second->parent);
552 <<
' ' <<
size() <<
" vertices,"
553 <<
' ' << num_edges <<
" edges,"
554 <<
' ' << unique_modules.size() <<
" unique module instances,"
555 <<
" and end statistics";
559 std::ptrdiff_t num_nodes = 0;
562 (void)name_and_nodeptr;
587 if (op->
opcode == supported_op)
595 return out << node.
cycle <<
":" << node.
name;
598 template <
typename T>
604 for(
int i = 0; i < (std::ptrdiff_t)v.size() - 1; i++)
606 for(
int j = i + 1; j < (std::ptrdiff_t)v.size(); j++)
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);
625 else {
throw std::logic_error(
626 "don't understand MRRG verify check option " + opt_name +
"=" + opt_value
635 {
"check_mux_exclusivity",
"ignore"},
639 const auto mux_exclusivity_enabled_and_msg_type = interpret_verify_msg_option(options,
"check_mux_exclusivity");
641 std::vector<VerifyMessage> result;
648 (void)add_warning; (void)add_info;
650 const auto& is_operand_node = [&](
NodeDescriptor n) {
return !n->supported_operand_tags.empty(); };
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 <<
'\'';
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;
668 for(
auto & r : node_classes.routing_nodes)
673 add_error([&](
auto&& s) { s <<
"Fanins not unique for node: " << *r; });
677 add_error([&](
auto&& s) { s <<
"Fanouts not unique for node: " << *r; });
680 for(
auto & fo : r->fanout)
683 bool found_r =
false;
684 for(
auto & fi : fo->fanin)
695 add_error([&](
auto&& s) { s <<
"Missing back link: " << *r <<
" <- " << *fo; });
700 if (is_operand_node(r)) {
701 std::unordered_set<NodeDescriptor> visited = {r};
702 std::queue<NodeDescriptor> toVisit;
703 for (
auto &
fanout : r->fanout) {
708 while (!toVisit.empty()) {
709 auto n = toVisit.front();
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"; });
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.";
722 for (
auto &
fanout : n->fanout) {
731 for(
auto & r : node_classes.function_nodes)
736 add_error([&](
auto&& s) { s <<
"Fanins not unique for node: " << *r; });
741 add_error([&](
auto&& s) { s <<
"Fanouts not unique for node: " << *r; });
745 for(
auto & fo : r->fanout)
748 bool found_r =
false;
749 for(
auto & fi : fo->fanin)
759 add_error([&](
auto&& s) { s <<
"Missing back link: " << *r <<
" <- " << *fo; });
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();
770 for (
const auto& fanoutptr : node.fanout) {
771 const auto&
fanout = *fanoutptr;
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 "
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();
801 auto& nodes_in_cycle =
nodes.at(the_node.cycle);
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";
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;
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);
825 driver->
fanout.push_back(fanout);
826 fanout->
fanin.push_back(driver);
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";
838 driver->
fanout.erase(elem);
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";
846 fanout->
fanin.erase(elem1);