27 #include <cxxopts.hpp>
43 #include <sys/types.h>
47 const std::string default_config_file_name =
"mapper_config.ini";
53 int main(
int argc,
char* argv[])
69 value =
static_cast<VerilogType>(std::stoi(text, &end_pos));
70 if (end_pos != text.size()) {
74 throw std::logic_error(
"can't convert " + text +
" to VerilogType");
84 std::cout << std::endl;
85 std::cout <<
"CGRA - Modelling and Exploration (http://cgra-me.ece.utoronto.ca/)" << std::endl;
86 std::cout <<
"Copyright (c) 2015-2023 University of Toronto. All Rights Reserved." << std::endl;
87 std::cout <<
"For research and academic purposes only. Commercial use is prohibited." << std::endl;
88 std::cout <<
"Please email questions to: Jason Anderson (janders@ece.utoronto.ca)" << std::endl;
89 std::cout <<
"Compiled: " << __DATE__ <<
" " << __TIME__ << std::endl;
90 std::cout << std::endl;
98 bool exit_after_optimizing_dfg =
false;
99 bool list_cpp_architectures =
false;
100 bool make_testbench =
false;
101 bool no_dfg_transformations =
false;
102 bool no_reduce_mrrg =
false;
103 bool print_mapper_help_and_exit =
false;
104 bool print_mapper_list =
false;
105 bool printarch =
false;
107 double timelimit = 7200.0;
111 std::string arch_opts = {};
112 std::string arch_opts_list_id = {};
113 std::string cpp_arch_id = {};
114 std::string dfg_filename = {};
115 std::string fix_port_filename = {};
116 std::string gen_config_file = {};
117 std::string load_mapping_filename = {};
118 std::string mapper_id =
"BothSmallTimeThenHeurFullTime";
119 std::string mapper_opts = {};
120 std::string mapping_output_filename = {};
121 std::string print_final_dfg_filename = {};
122 std::string print_mrrg_filename = {};
123 std::string verilog_gen_output_path = {};
124 std::string verilog_opts = {};
130 std::string *argument_list[14] = {
131 &option_vals.arch_opts,
132 &option_vals.arch_opts_list_id,
133 &option_vals.cpp_arch_id,
134 &option_vals.dfg_filename,
135 &option_vals.fix_port_filename,
136 &option_vals.gen_config_file,
137 &option_vals.load_mapping_filename,
138 &option_vals.mapper_id,
139 &option_vals.mapper_opts,
140 &option_vals.mapping_output_filename,
141 &option_vals.print_final_dfg_filename,
142 &option_vals.print_mrrg_filename,
143 &option_vals.verilog_gen_output_path,
144 &option_vals.verilog_opts
151 cxxopts::Options options(
"CGRA-ME",
"CGRA - Modelling and Exploration");
154 options.add_options()
155 (
"arch-list",
"Show the List of Avaliable C++ Architectures with IDs",
156 cxxopts::value(option_vals.list_cpp_architectures))
157 (
"arch-opts",
"Override a C++ architecture's default parameters. Space-separated list of <Key>=<Value> pairs (must be a single arugment)",
158 cxxopts::value(option_vals.arch_opts),
"<\"opts\">")
159 (
"arch-opts-list",
"Show the List of Avaliable Options for a C++ Architecture ID",
160 cxxopts::value(option_vals.arch_opts_list_id),
"id")
161 (
"c,cpp",
"C++ architecture Identifier. See --arch-list for list.",
162 cxxopts::value(option_vals.cpp_arch_id),
"id")
163 (
"g,dfg",
"The DFG file to map in dot format",
164 cxxopts::value(option_vals.dfg_filename))
165 (
"exit-after-optimizing-dfg",
"Only proceed as far as optimizing the DFG, then exit with success",
166 cxxopts::value(option_vals.exit_after_optimizing_dfg))
167 (
"fixed-ports",
"User can input fixed ports to mapper",
168 cxxopts::value(option_vals.fix_port_filename))
169 (
"gen-testbench",
"Generate testbench for use in a simulation of the DFG with configuration bitstream",
170 cxxopts::value(option_vals.make_testbench))
171 (
"gen-config-file",
"Create a config file at the given path, or the default name in the current directory if none is given. And do nothing else.",
172 cxxopts::value(option_vals.gen_config_file)->implicit_value(default_config_file_name))
173 (
"gen-verilog",
"Generate Verilog Implementation of Architecture and Dump to Specified Directory",
174 cxxopts::value(option_vals.verilog_gen_output_path),
"<Directorypath>")
175 (
"h,help",
"Print Help")
176 (
"i,II",
"Architecture Contexts Used",
177 cxxopts::value(option_vals.II)->default_value(
std::to_string(option_vals.II)),
"<#>")
178 (
"l,load-mapping",
"Load the mapping from a dot file",
179 cxxopts::value(option_vals.load_mapping_filename))
180 (
"m,mapper",
"Which mapper to use. See --mapper-list for choices.",
181 cxxopts::value(option_vals.mapper_id)->default_value(option_vals.mapper_id),
"<#>")
182 (
"mapper-list",
"List all available mappers",
183 cxxopts::value(option_vals.print_mapper_list),
"<Name>")
184 (
"mapper-opts",
"Override default mapper settings",
185 cxxopts::value(option_vals.mapper_opts),
"<Key>=<Value> ...")
186 (
"mapper-help",
"Show the list of options and defaults for the selected mapper, then exit",
187 cxxopts::value(option_vals.print_mapper_help_and_exit))
188 (
"no-dfg-transformations",
"Disable all DFG transformations and optimizations; pass the input DFG directly the mapper",
189 cxxopts::value(option_vals.no_dfg_transformations))
190 (
"no-reduce-mrrg",
"Don't reduce the mrrg prior to mapping",
191 cxxopts::value(option_vals.no_reduce_mrrg))
192 (
"print-arch",
"Print Architecture to stdout",
193 cxxopts::value(option_vals.printarch))
194 (
"print-final-dfg",
"Print the DFG that is sent to mapper to this file",
195 cxxopts::value(option_vals.print_final_dfg_filename))
196 (
"print-mrrg",
"Print Architecture MRRG to file",
197 cxxopts::value(option_vals.print_mrrg_filename),
"<Filename>")
198 (
"o,save-to-dot",
"Save the mapping to a dot file",
199 cxxopts::value(option_vals.mapping_output_filename))
200 (
"t,timelimit",
"Mapper Timelimit",
201 cxxopts::value(option_vals.timelimit)->default_value(
std::to_string(option_vals.timelimit)),
"<#>")
202 (
"verbose",
"Output will contain extra information detailing steps taken by this tool",
203 cxxopts::value(option_vals.verbosity)->default_value(
std::to_string(option_vals.verbosity))->implicit_value(
std::to_string(option_vals.verbosity)),
"level")
204 (
"verilog-opts",
"Verilog options with hybrid system and memory size. Space-separated list of <Key>=<Value> pairs (must be a single arugment)",
205 cxxopts::value(option_vals.verilog_opts),
"<\"opts\">")
208 options.parse(argc, argv);
210 std::throw_with_nested(
cgrame_error(
"Problem parsing command-line options"));
213 int arguments_size =
sizeof(argument_list)/
sizeof(argument_list[0]);
214 for(
int i = 0; i< arguments_size; i++)
216 if(argument_list[i]->rfind(
"-",0)==0){
217 throw cgrame_error(
"An argument is missing. "+ *argument_list[i] +
" cannot be accepted as an argument");
220 timing_seqence.
tick(
"parse arguments", 0.01);
222 char full_path[1024] = {0};
223 ssize_t count = readlink(
"/proc/self/exe", full_path,
sizeof(full_path)/
sizeof(full_path[0]) - 1);
224 std::string exe_path;
226 exe_path = std::string(dirname(full_path));
228 throw cgrame_error(
"Readlink is not able to get the executable path");
238 if(options.count(
"help") || arg_count == 1) {
239 std::cout << options.help({
""}) << std::endl;
243 if (not option_vals.gen_config_file.empty()) {
244 std::cout <<
"Writing a config file to " << option_vals.gen_config_file <<
'\n';
245 std::ofstream conf_file(option_vals.gen_config_file);
247 throw cgrame_error(
"Unable to open " + option_vals.gen_config_file +
"for writing");
249 mapper_registry.printDefaultsAsINI(conf_file);
251 throw cgrame_error(
"Problem while writing " + option_vals.gen_config_file);
256 if(option_vals.list_cpp_architectures) {
257 std::cout <<
"List of C++ CGRA Architectures: [ID: description]" << std::endl;
258 for(
const auto & id_and_arch_gen : userarchs)
259 std::cout << id_and_arch_gen.first <<
": " << id_and_arch_gen.second.description <<
'\n';
263 if(not option_vals.arch_opts_list_id.empty()) {
264 const auto& arch_generator = userarchs.
getGenerator(option_vals.arch_opts_list_id);
265 std::cout <<
"List of options and defaults for architecture `" << option_vals.arch_opts_list_id <<
"' - `" << arch_generator.
description <<
"'\n" << arch_generator.args_and_defaults <<
'\n';
269 if (option_vals.print_mapper_list) {
270 std::cout <<
"The list of registered mappers follows. The currently selected mapper is " << option_vals.mapper_id <<
"\n";
271 mapper_registry.printMapperList(std::cout);
278 const std::string ini_full_path = exe_path +
"/" + default_config_file_name;
279 std::ifstream ini_file(ini_full_path);
281 throw cgrame_error(
"Missing mapper_config.ini, Cannot Read Default Parameters for Mappers");
287 {std::istreambuf_iterator<char>(ini_file), std::istreambuf_iterator<char>()},
288 option_vals.mapper_opts,
289 option_vals.cpp_arch_id,
290 option_vals.verbosity
293 std::throw_with_nested(
"Problem parsing config. INI file used was at " + ini_full_path);
297 if(option_vals.print_mapper_help_and_exit) {
298 if (option_vals.mapper_id.empty()) {
299 std::cout <<
"A mapper must be selected to print its options\n";
302 std::cout <<
"Help for mapper `" << option_vals.mapper_id <<
"':\n\n";
303 mapper_registry.printHelpForMapper(option_vals.mapper_id, effective_inis, std::cout);
311 if(option_vals.verilog_gen_output_path.empty() && option_vals.dfg_filename.empty()) {
312 if (!(option_vals.printarch || !option_vals.print_mrrg_filename.empty())){
313 throw cgrame_error(
"DFG File Path is Missing, Exiting...");
317 if(!option_vals.fix_port_filename.empty() && !option_vals.make_testbench) {
318 throw cgrame_error(
"fix_port option need to be used for generating bitstream");
322 if (option_vals.timelimit < 0) {
323 throw make_from_stream<cgrame_error>([&](
auto&& s) {
324 s <<
"Time limit must not be less than zero (" << option_vals.timelimit <<
" was given)";
328 timing_seqence.
tick(
"argument checking", 0.01);
334 std::shared_ptr<CGRA> arch =
nullptr;
338 if (not option_vals.load_mapping_filename.empty()) {
340 option_vals.load_mapping_filename,
341 &option_vals.cpp_arch_id,
342 &option_vals.arch_opts,
344 timing_seqence.
tick(
"load mapping", 0.01);
349 if(!option_vals.cpp_arch_id.empty())
351 createArchitectureCPP(&arch,option_vals.cpp_arch_id,option_vals.arch_opts,&arch_attrs,&userarchs, option_vals.II);
358 timing_seqence.
tick(
"create architecture", 0.01);
361 if(option_vals.printarch)
363 std::cout << std::endl <<
"[ARCHGRAPH]" << std::endl;
364 arch->getTopLevelModule().print();
365 arch->getTopLevelModule().print_dot();
369 if (not option_vals.print_mrrg_filename.empty()) {
370 std::cout <<
"Dumping MRRG with option_vals.II = " << option_vals.II <<
" to file `" << option_vals.print_mrrg_filename <<
"'\n";
371 std::ofstream file_stream{option_vals.print_mrrg_filename};
372 arch->getMRRG(option_vals.II).printDot(file_stream);
375 timing_seqence.
tick(
"print arch and/or MRRG", 0.01);
378 if(option_vals.verilog_gen_output_path.empty() && option_vals.dfg_filename.empty()){
383 if(not option_vals.verilog_gen_output_path.empty()) {
384 generateVerilog(arch, option_vals.verilog_gen_output_path, option_vals.verilog_opts, option_vals.verilog_gen_type, option_vals.II);
389 timing_seqence.
tick(
"generate Verilog", 0.01);
393 auto dfg_ifstream = std::ifstream(option_vals.dfg_filename);
394 const auto parsed_dfg_dot =
parseDot(dfg_ifstream, option_vals.dfg_filename);
395 timing_seqence.
tick(
"load DFG", 0.01);
397 if (not option_vals.no_dfg_transformations) {
399 if (option_vals.arch_opts.find(
"pred=1") == std::string::npos)
405 timing_seqence.
tick(
"optimize DFG", 0.01);
408 if(not option_vals.print_final_dfg_filename.empty()) {
409 cgrame_msg(
"Printing DFG to " + option_vals.print_final_dfg_filename);
410 auto final_dfg_ofstream = std::ofstream{option_vals.print_final_dfg_filename};
411 opgraph->serialize(final_dfg_ofstream);
412 if (not final_dfg_ofstream) {
413 cgrame_warn(
"DFG printing may not have been successful");
415 timing_seqence.
tick(
"print DFG", 0.01);
418 if (option_vals.exit_after_optimizing_dfg) {
422 std::cout <<
"[INFO] Creating MRRG..." << std::endl;
423 const auto& mrrg = arch->getMRRG(option_vals.II);
424 timing_seqence.
tick(
"create MRRG", 0.01);
427 std::unordered_map<std::string, std::string> fix_port;
428 if (!option_vals.fix_port_filename.empty()) {
429 std::ifstream infile(option_vals.fix_port_filename);
433 while (std::getline(infile, line)) {
434 std::string::size_type pos = line.find(
':');
435 if(line.npos != pos) {
436 second = line.substr(pos + 1);
437 first = line.substr(0, pos);
438 fix_port[first] = second;
440 throw cgrame_error(
"Format for fix_port is not right, \":\" is used as delimiter");
449 if (not option_vals.load_mapping_filename.empty()) {
450 std::cout <<
"[INFO] Mapping using information from DOT file: " << option_vals.load_mapping_filename << std::endl;
455 mapping_result.
check();
456 timing_seqence.
tick(
"created loaded mapping", 0.01);
460 std::cout <<
"[INFO] Creating Mapper '" << option_vals.mapper_id <<
"'..." << std::endl;
462 auto mapper = mapper_registry.createMapper(option_vals.mapper_id, arch, option_vals.timelimit, effective_inis);
464 timing_seqence.
tick(
"mapper setup", 0.01);
467 const auto map_given_mrrg = [&timing_seqence, &mapper, &option_vals, &opgraph, &fix_port](
const MRRG& given_mrrg) {
468 std::cout <<
"[INFO] Mapping..." << std::endl;
469 auto mapping = mapper->mapOpGraph(opgraph, option_vals.II, given_mrrg, fix_port);
470 auto time = timing_seqence.
tick(
"mapping");
471 return std::make_pair(mapping, time);
475 std::tie(mapping_result, mapping_tick) = [&]() {
477 if (not option_vals.no_reduce_mrrg) {
478 std::cout <<
"[INFO] Reducing MRRG..." << std::endl;
479 const auto mrrg_reduction_result =
reduceLosslessly(arch->getMRRG(option_vals.II), {});
480 timing_seqence.
tick(
"reduce MRRG", 0.01);
482 auto mapping_and_tick = map_given_mrrg(mrrg_reduction_result.transformed_mrrg);
483 auto untransformed_mapping =
transformToOriginalMRRG(std::move(mapping_and_tick.first), mrrg_reduction_result);
484 timing_seqence.
tick(
"transform mapping to original", 0.01);
485 return std::make_pair(std::move(untransformed_mapping), mapping_and_tick.second);
487 return map_given_mrrg(mrrg);
493 std::cout <<
"[INFO] Mapping Success: " << mapping_result.
isMapped() << std::endl;
494 std::cout <<
"[INFO] Mapping Time: " << solve_time << std::endl;
495 std::cout <<
"[INFO] Mapping Timeout: " << mapping_result.
isTimeout() << std::endl;
497 if (not mapping_result.
isMapped()) {
return 1; }
500 mapping_result.
check();
505 const auto final_mapping_graph =
removeIsolatedRoutingNodes(create_mapping_graph_result.mg, mrrg, create_mapping_graph_result.toMRRG);
507 for (
auto message : final_mapping_graph.verify(mrrg, create_mapping_graph_result.toMRRG)) {
508 std::cout << message.message;
515 timing_seqence.
tick(
"post mapping checks and transforms", 0.01);
518 if (not option_vals.mapping_output_filename.empty()) {
519 std::cout <<
"[INFO] Saving mapping result to '" << option_vals.mapping_output_filename <<
'\'' << std::endl;
520 std::ofstream mappingStream(option_vals.mapping_output_filename);
521 final_mapping_graph.printDot(mappingStream, mrrg, *opgraph, create_mapping_graph_result.toMRRG, arch_attrs);
522 if (not mappingStream) { std::cerr <<
"Problem writing mapping to '" << option_vals.mapping_output_filename <<
'\'' << std::endl; }
523 timing_seqence.
tick(
"saving mapping", 0.01);
527 if (option_vals.make_testbench)
530 timing_seqence.
tick(
"generate testbench", 0.01);
537 const std::string& ini_str,
538 const std::string& mapper_opts,
539 const std::string& cpp_arch_id,
544 std::stringstream ss_mapper_opts(mapper_opts);
546 std::string opt_pair;
547 if (not (ss_mapper_opts >> opt_pair)) {
break; }
548 auto assign_pos = opt_pair.find(
'=');
550 if(assign_pos == opt_pair.npos) {
551 throw cgrame_error(
"Ill-formatted command-line override: " + opt_pair);
554 command_line_overrides.
addString(opt_pair.substr(0, assign_pos), opt_pair.substr(assign_pos + 1));
559 if(not raw_inis.parse(ini_str)) {
565 for (
const auto& ini_and_value : raw_inis) {
566 const auto& key = ini_and_value.first;
567 if (key.empty()) {
continue; }
568 if (key.find_first_not_of(
"\t ") == key.npos) {
continue; }
569 if (key.at(key.find_first_not_of(
"\t ")) ==
'#') {
continue; }
570 const auto dot_pos = key.find_first_of(
".");
571 if (dot_pos != key.npos && key.at(key.find_first_not_of(
"\t ", dot_pos+1)) ==
'#') {
continue; }
572 if (key.front() ==
'.') {
573 if (key.size() > 1) {
574 parsed_inis.
setString(key.substr(1), ini_and_value.second);
577 parsed_inis.
setString(key, ini_and_value.second);
582 parsed_inis.
setString(
"AllMappers.arch_id", cpp_arch_id);
583 parsed_inis.
setInt(
"AllMappers.verbosity", verbosity);
587 return effective_inis;
591 std::string verilog_gen_output_path,
592 std::string verilog_opts,
597 auto res = stat(verilog_gen_output_path.c_str(), &fs_info);
601 throw cgrame_error(
"Cannot open \"" + verilog_gen_output_path +
"\"");
603 bool is_dir = fs_info.st_mode & S_IFDIR;
606 throw cgrame_error(
"\"" + verilog_gen_output_path +
"\" is not a directory");
609 std::cout <<
"[INFO] Generating Verilog Implementation of Specified Architecture Using Generator "<< verilog_gen_type << std::endl;
610 if (verilog_gen_output_path.back() !=
'/')
611 verilog_gen_output_path = verilog_gen_output_path +
"/";
612 arch->genVerilog(verilog_gen_type, verilog_gen_output_path, contexts);
614 std::stringstream ss_verilog_opts(verilog_opts);
615 std::string opt_pair;
616 while(ss_verilog_opts >> opt_pair)
618 auto n = opt_pair.find(
'=');
619 if(n == std::string::npos) { make_and_throw<cgrame_error>([&](
auto&& s) {
620 s <<
"Ill-formatted verilog option: " << opt_pair;
622 auto key = opt_pair.substr(0, n);
623 auto value = opt_pair.substr(n + 1, std::string::npos);
624 if (verilog_attrs.
hasKey(key)) {
625 std::cout <<
"[WARNING] duplicate key `" << key <<
"' verilog arguments. Overwriting with new value `" << value <<
"'\n";
627 verilog_attrs.
setString(std::move(key), std::move(value));
629 if (verilog_attrs.
hasKey(
"hybrid")) {
630 if(verilog_attrs.
getString(
"hybrid").find(
"on") != std::string::npos) {
631 int mem_size = stoi(verilog_attrs.
getStringOr(
"memSize",
"22"));
632 arch->genHybrid(verilog_gen_type, verilog_gen_output_path, mem_size);
639 std::string* cpp_arch_id,
640 std::string* arch_opts,
645 std::cout <<
"[INFO] Getting architecture information from DOT file: " << load_mapping_filename << std::endl;
647 auto mapping_ifstream = std::ifstream(load_mapping_filename);
648 parsed_mapping_dot =
parseDot(mapping_ifstream, load_mapping_filename);
651 if (mapping_attrs.hasKey(
"cpp_arch_id")) {
652 *cpp_arch_id = mapping_attrs.
getString(
"cpp_arch_id");
653 *arch_opts = mapping_attrs.getStringOr(
"arch_opts",
"");
656 *II = mapping_attrs.getInt(
"II");
657 return parsed_mapping_dot;
659 std::throw_with_nested(
cgrame_error(
"Problem parsing input mapping file `" + load_mapping_filename +
"'"));
664 std::shared_ptr<CGRA>* arch,
665 std::string cpp_arch_id,
666 std::string arch_opts,
671 std::cout <<
"[INFO] Using C++ architecture ID " << cpp_arch_id <<
'\n';
672 auto& arch_generator = userarchs->
getGenerator(cpp_arch_id);
673 std::cout <<
"[INFO] Architecture description: " << arch_generator.
description <<
'\n';
675 std::stringstream ss_arch_opts(arch_opts);
676 std::string opt_pair;
677 while(ss_arch_opts >> opt_pair)
679 auto n = opt_pair.find(
'=');
680 if(n == std::string::npos) { make_and_throw<cgrame_error>([&](
auto&& s) {
681 s <<
"Ill-formatted C++ architecture option: " << opt_pair;
683 auto key = opt_pair.substr(0, n);
684 auto value = opt_pair.substr(n + 1, std::string::npos);
685 if (arch_attrs->
hasKey(key)) {
686 std::cout <<
"[WARNING] duplicate key `" << key <<
"' in C++ architecture arguments. Overwriting with new value `" << value <<
"'\n";
688 (*arch_attrs).setString(std::move(key), std::move(value));
691 std::cout <<
"[INFO] Creating \"" << cpp_arch_id <<
"\" Architecture from C++..." << std::endl;
692 arch_attrs->
setInt(
"II", II);
693 (*arch) = arch_generator(*arch_attrs);
694 arch_attrs->
setString(
"cpp_arch_id", cpp_arch_id);
695 arch_attrs->
setString(
"arch_opts", arch_opts);
696 if (arch_attrs->
hasKey(
"rows")) {
697 (*arch)->setNumRows(arch_attrs->
getInt(
"rows"));
699 if (arch_attrs->
hasKey(
"cols")) {
700 (*arch)->setNumCols(arch_attrs->
getInt(
"cols"));
705 std::shared_ptr<CGRA> arch,
709 std::ofstream tb_file(
"testbench.v");
710 auto bitstream = arch->genBitStream(mapping_result);
711 std::cout << bitstream;
712 bitstream.printTestbench(tb_file, II);