2 * yosys -- Yosys Open SYnthesis Suite
4 * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
27 using namespace REGISTER_INTERN
;
28 #define MAX_REG_COUNT 1000
30 namespace REGISTER_INTERN
32 int raw_register_count
= 0;
33 bool raw_register_done
= false;
34 Pass
*raw_register_array
[MAX_REG_COUNT
];
36 std::map
<std::string
, Frontend
*> frontend_register
;
37 std::map
<std::string
, Pass
*> pass_register
;
38 std::map
<std::string
, Backend
*> backend_register
;
41 std::vector
<std::string
> Frontend::next_args
;
43 Pass::Pass(std::string name
, std::string short_help
) : pass_name(name
), short_help(short_help
)
45 assert(!raw_register_done
);
46 assert(raw_register_count
< MAX_REG_COUNT
);
47 raw_register_array
[raw_register_count
++] = this;
50 void Pass::run_register()
52 assert(pass_register
.count(pass_name
) == 0);
53 pass_register
[pass_name
] = this;
56 void Pass::init_register()
58 if (raw_register_done
)
60 while (raw_register_count
> 0)
61 raw_register_array
[--raw_register_count
]->run_register();
62 raw_register_done
= true;
65 void Pass::done_register()
67 frontend_register
.clear();
68 pass_register
.clear();
69 backend_register
.clear();
70 raw_register_done
= false;
80 log("No help message for command `%s'.\n", pass_name
.c_str());
84 void Pass::cmd_log_args(const std::vector
<std::string
> &args
)
88 log("Full command line:");
89 for (size_t i
= 0; i
< args
.size(); i
++)
90 log(" %s", args
[i
].c_str());
94 void Pass::cmd_error(const std::vector
<std::string
> &args
, size_t argidx
, std::string msg
)
96 std::string command_text
;
99 for (size_t i
= 0; i
< args
.size(); i
++) {
101 error_pos
+= args
[i
].size() + 1;
102 command_text
= command_text
+ (command_text
.empty() ? "" : " ") + args
[i
];
105 log("\nSyntax error in command `%s':\n", command_text
.c_str());
108 log_cmd_error("Command syntax error: %s\n> %s\n> %*s^\n",
109 msg
.c_str(), command_text
.c_str(), error_pos
, "");
112 void Pass::extra_args(std::vector
<std::string
> args
, size_t argidx
, RTLIL::Design
*design
, bool select
)
114 for (; argidx
< args
.size(); argidx
++)
116 std::string arg
= args
[argidx
];
118 if (arg
.substr(0, 1) == "-")
119 cmd_error(args
, argidx
, "Unkown option or option in arguments.");
122 cmd_error(args
, argidx
, "Extra argument.");
124 handle_extra_select_args(this, args
, argidx
, args
.size(), design
);
130 void Pass::call(RTLIL::Design
*design
, std::string command
)
132 std::vector
<std::string
> args
;
133 char *s
= strdup(command
.c_str()), *sstart
= s
, *saveptr
;
134 s
+= strspn(s
, " \t\r\n");
135 if (*s
== 0 || *s
== '#')
138 for (s
++; *s
== ' ' || *s
== '\t'; s
++) { }
139 char *p
= s
+ strlen(s
) - 1;
140 while (p
>= s
&& (*p
== '\r' || *p
== '\n'))
142 log_header("Shell command: %s\n", s
);
143 int retCode
= system(s
);
145 log_cmd_error("Shell command returned error code %d.\n", retCode
);
148 for (char *p
= strtok_r(s
, " \t\r\n", &saveptr
); p
; p
= strtok_r(NULL
, " \t\r\n", &saveptr
)) {
150 int strsz
= str
.size();
151 if (strsz
> 0 && str
[strsz
-1] == ';') {
152 int num_semikolon
= 0;
153 while (strsz
> 0 && str
[strsz
-1] == ';')
154 strsz
--, num_semikolon
++;
156 args
.push_back(str
.substr(0, strsz
));
159 if (num_semikolon
== 2)
160 call(design
, "clean");
168 void Pass::call(RTLIL::Design
*design
, std::vector
<std::string
> args
)
170 if (args
.size() == 0 || args
[0][0] == '#')
172 if (pass_register
.count(args
[0]) == 0)
173 log_cmd_error("No such command: %s (type 'help' for a command overview)\n", args
[0].c_str());
175 size_t orig_sel_stack_pos
= design
->selection_stack
.size();
176 pass_register
[args
[0]]->execute(args
, design
);
177 while (design
->selection_stack
.size() > orig_sel_stack_pos
)
178 design
->selection_stack
.pop_back();
181 Frontend::Frontend(std::string name
, std::string short_help
) : Pass("read_"+name
, short_help
), frontend_name(name
)
185 void Frontend::run_register()
187 assert(pass_register
.count(pass_name
) == 0);
188 pass_register
[pass_name
] = this;
190 assert(frontend_register
.count(frontend_name
) == 0);
191 frontend_register
[frontend_name
] = this;
194 Frontend::~Frontend()
198 void Frontend::execute(std::vector
<std::string
> args
, RTLIL::Design
*design
)
200 assert(next_args
.empty());
204 execute(f
, std::string(), args
, design
);
207 } while (!args
.empty());
210 void Frontend::extra_args(FILE *&f
, std::string
&filename
, std::vector
<std::string
> args
, size_t argidx
)
212 bool called_with_fp
= f
!= NULL
;
215 for (; argidx
< args
.size(); argidx
++)
217 std::string arg
= args
[argidx
];
219 if (arg
.substr(0, 1) == "-")
220 cmd_error(args
, argidx
, "Unkown option or option in arguments.");
222 cmd_error(args
, argidx
, "Extra filename argument in direct file mode.");
225 f
= fopen(filename
.c_str(), "r");
227 log_cmd_error("Can't open input file `%s' for reading: %s\n", filename
.c_str(), strerror(errno
));
229 if (argidx
+1 < args
.size()) {
230 next_args
.insert(next_args
.begin(), args
.begin(), args
.begin()+argidx
);
231 next_args
.insert(next_args
.begin()+argidx
, args
.begin()+argidx
+1, args
.end());
232 args
.erase(args
.begin()+argidx
+1, args
.end());
237 cmd_error(args
, argidx
, "No filename given.");
240 args
.push_back(filename
);
245 void Frontend::frontend_call(RTLIL::Design
*design
, FILE *f
, std::string filename
, std::string command
)
247 std::vector
<std::string
> args
;
248 char *s
= strdup(command
.c_str());
249 for (char *p
= strtok(s
, " \t\r\n"); p
; p
= strtok(NULL
, " \t\r\n"))
252 frontend_call(design
, f
, filename
, args
);
255 void Frontend::frontend_call(RTLIL::Design
*design
, FILE *f
, std::string filename
, std::vector
<std::string
> args
)
257 if (args
.size() == 0)
259 if (frontend_register
.count(args
[0]) == 0)
260 log_cmd_error("No such frontend: %s\n", args
[0].c_str());
263 frontend_register
[args
[0]]->execute(f
, filename
, args
, design
);
264 } else if (filename
== "-") {
265 frontend_register
[args
[0]]->execute(stdin
, "<stdin>", args
, design
);
267 if (!filename
.empty())
268 args
.push_back(filename
);
269 frontend_register
[args
[0]]->execute(args
, design
);
273 Backend::Backend(std::string name
, std::string short_help
) : Pass("write_"+name
, short_help
), backend_name(name
)
277 void Backend::run_register()
279 assert(pass_register
.count(pass_name
) == 0);
280 pass_register
[pass_name
] = this;
282 assert(backend_register
.count(backend_name
) == 0);
283 backend_register
[backend_name
] = this;
290 void Backend::execute(std::vector
<std::string
> args
, RTLIL::Design
*design
)
293 execute(f
, std::string(), args
, design
);
298 void Backend::extra_args(FILE *&f
, std::string
&filename
, std::vector
<std::string
> args
, size_t argidx
)
300 bool called_with_fp
= f
!= NULL
;
302 for (; argidx
< args
.size(); argidx
++)
304 std::string arg
= args
[argidx
];
306 if (arg
.substr(0, 1) == "-" && arg
!= "-")
307 cmd_error(args
, argidx
, "Unkown option or option in arguments.");
309 cmd_error(args
, argidx
, "Extra filename argument in direct file mode.");
312 filename
= "<stdout>";
318 f
= fopen(filename
.c_str(), "w");
320 log_cmd_error("Can't open output file `%s' for writing: %s\n", filename
.c_str(), strerror(errno
));
324 args
.push_back(filename
);
329 filename
= "<stdout>";
334 void Backend::backend_call(RTLIL::Design
*design
, FILE *f
, std::string filename
, std::string command
)
336 std::vector
<std::string
> args
;
337 char *s
= strdup(command
.c_str());
338 for (char *p
= strtok(s
, " \t\r\n"); p
; p
= strtok(NULL
, " \t\r\n"))
341 backend_call(design
, f
, filename
, args
);
344 void Backend::backend_call(RTLIL::Design
*design
, FILE *f
, std::string filename
, std::vector
<std::string
> args
)
346 if (args
.size() == 0)
348 if (backend_register
.count(args
[0]) == 0)
349 log_cmd_error("No such backend: %s\n", args
[0].c_str());
351 size_t orig_sel_stack_pos
= design
->selection_stack
.size();
354 backend_register
[args
[0]]->execute(f
, filename
, args
, design
);
355 } else if (filename
== "-") {
356 backend_register
[args
[0]]->execute(stdout
, "<stdout>", args
, design
);
358 if (!filename
.empty())
359 args
.push_back(filename
);
360 backend_register
[args
[0]]->execute(args
, design
);
363 while (design
->selection_stack
.size() > orig_sel_stack_pos
)
364 design
->selection_stack
.pop_back();
367 struct HelpPass
: public Pass
{
368 HelpPass() : Pass("help", "display help messages") { }
372 log(" help ............. list all commands\n");
373 log(" help <command> ... print help message for given command\n");
374 log(" help -all ........ print complete command reference\n");
377 void escape_tex(std::string
&tex
)
380 while ((pos
= tex
.find('_', pos
)) != std::string::npos
) {
381 tex
.replace(pos
, 1, "\\_");
385 void write_tex(FILE *f
, std::string cmd
, std::string title
, std::string text
)
387 size_t begin
= text
.find_first_not_of("\n"), end
= text
.find_last_not_of("\n");
388 if (begin
!= std::string::npos
&& end
!= std::string::npos
&& begin
< end
)
389 text
= text
.substr(begin
, end
-begin
+1);
390 std::string cmd_unescaped
= cmd
;
393 fprintf(f
, "\\section{%s -- %s}\n", cmd
.c_str(), title
.c_str());
394 fprintf(f
, "\\label{cmd:%s}\n", cmd_unescaped
.c_str());
395 fprintf(f
, "\\begin{lstlisting}[numbers=left,frame=single]\n");
396 fprintf(f
, "%s\n\\end{lstlisting}\n\n", text
.c_str());
398 void escape_html(std::string
&html
)
401 while ((pos
= html
.find_first_of("<>&", pos
)) != std::string::npos
)
404 html
.replace(pos
, 1, "<");
408 html
.replace(pos
, 1, ">");
412 html
.replace(pos
, 1, "&");
417 void write_html(FILE *idxf
, std::string cmd
, std::string title
, std::string text
)
419 FILE *f
= fopen(stringf("cmd_%s.in", cmd
.c_str()).c_str(), "wt");
420 fprintf(idxf
, "<li><a href=\"cmd_%s.html\"> ", cmd
.c_str());
426 fprintf(idxf
, "%s</a> <span>%s</span></a>\n", cmd
.c_str(), title
.c_str());
428 fprintf(f
, "@cmd_header %s@\n", cmd
.c_str());
429 fprintf(f
, "<h1>%s - %s</h1>\n", cmd
.c_str(), title
.c_str());
430 fprintf(f
, "<pre>%s</pre>\n", text
.c_str());
431 fprintf(f
, "@footer@\n");
435 virtual void execute(std::vector
<std::string
> args
, RTLIL::Design
*)
437 if (args
.size() == 1) {
439 for (auto &it
: REGISTER_INTERN::pass_register
)
440 log(" %-20s %s\n", it
.first
.c_str(), it
.second
->short_help
.c_str());
442 log("Type 'help <command>' for more information on a command.\n");
447 if (args
.size() == 2) {
448 if (args
[1] == "-all") {
449 for (auto &it
: REGISTER_INTERN::pass_register
) {
451 log("%s -- %s\n", it
.first
.c_str(), it
.second
->short_help
.c_str());
452 for (size_t i
= 0; i
< it
.first
.size() + it
.second
->short_help
.size() + 6; i
++)
458 // this option is undocumented as it is for internal use only
459 else if (args
[1] == "-write-tex-command-reference-manual") {
460 FILE *f
= fopen("command-reference-manual.tex", "wt");
461 fprintf(f
, "%% Generated using the yosys 'help -write-tex-command-reference-manual' command.\n\n");
462 for (auto &it
: REGISTER_INTERN::pass_register
) {
465 FILE *memf
= open_memstream(&memptr
, &memsize
);
466 log_files
.push_back(memf
);
468 log_files
.pop_back();
470 write_tex(f
, it
.first
, it
.second
->short_help
, memptr
);
475 // this option is undocumented as it is for internal use only
476 else if (args
[1] == "-write-web-command-reference-manual") {
477 FILE *f
= fopen("templates/cmd_index.in", "wt");
478 for (auto &it
: REGISTER_INTERN::pass_register
) {
481 FILE *memf
= open_memstream(&memptr
, &memsize
);
482 log_files
.push_back(memf
);
484 log_files
.pop_back();
486 write_html(f
, it
.first
, it
.second
->short_help
, memptr
);
491 else if (REGISTER_INTERN::pass_register
.count(args
[1]) == 0)
492 log("No such command: %s\n", args
[1].c_str());
494 REGISTER_INTERN::pass_register
.at(args
[1])->help();