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.
20 #include "kernel/compatibility.h"
21 #include "kernel/register.h"
22 #include "kernel/log.h"
28 using namespace REGISTER_INTERN
;
29 #define MAX_REG_COUNT 1000
31 namespace REGISTER_INTERN
33 bool echo_mode
= false;
34 Pass
*first_queued_pass
;
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 next_queued_pass
= first_queued_pass
;
46 first_queued_pass
= this;
50 void Pass::run_register()
52 log_assert(pass_register
.count(pass_name
) == 0);
53 pass_register
[pass_name
] = this;
56 void Pass::init_register()
58 while (first_queued_pass
) {
59 first_queued_pass
->run_register();
60 first_queued_pass
= first_queued_pass
->next_queued_pass
;
64 void Pass::done_register()
66 frontend_register
.clear();
67 pass_register
.clear();
68 backend_register
.clear();
69 log_assert(first_queued_pass
== NULL
);
79 log("No help message for command `%s'.\n", pass_name
.c_str());
83 void Pass::cmd_log_args(const std::vector
<std::string
> &args
)
87 log("Full command line:");
88 for (size_t i
= 0; i
< args
.size(); i
++)
89 log(" %s", args
[i
].c_str());
93 void Pass::cmd_error(const std::vector
<std::string
> &args
, size_t argidx
, std::string msg
)
95 std::string command_text
;
98 for (size_t i
= 0; i
< args
.size(); i
++) {
100 error_pos
+= args
[i
].size() + 1;
101 command_text
= command_text
+ (command_text
.empty() ? "" : " ") + args
[i
];
104 log("\nSyntax error in command `%s':\n", command_text
.c_str());
107 log_cmd_error("Command syntax error: %s\n> %s\n> %*s^\n",
108 msg
.c_str(), command_text
.c_str(), error_pos
, "");
111 void Pass::extra_args(std::vector
<std::string
> args
, size_t argidx
, RTLIL::Design
*design
, bool select
)
113 for (; argidx
< args
.size(); argidx
++)
115 std::string arg
= args
[argidx
];
117 if (arg
.substr(0, 1) == "-")
118 cmd_error(args
, argidx
, "Unkown option or option in arguments.");
121 cmd_error(args
, argidx
, "Extra argument.");
123 handle_extra_select_args(this, args
, argidx
, args
.size(), design
);
126 // cmd_log_args(args);
129 void Pass::call(RTLIL::Design
*design
, std::string command
)
131 std::vector
<std::string
> args
;
132 char *s
= strdup(command
.c_str()), *sstart
= s
, *saveptr
;
133 s
+= strspn(s
, " \t\r\n");
134 if (*s
== 0 || *s
== '#') {
139 for (s
++; *s
== ' ' || *s
== '\t'; s
++) { }
140 char *p
= s
+ strlen(s
) - 1;
141 while (p
>= s
&& (*p
== '\r' || *p
== '\n'))
143 log_header("Shell command: %s\n", s
);
144 int retCode
= system(s
);
146 log_cmd_error("Shell command returned error code %d.\n", retCode
);
150 for (char *p
= strtok_r(s
, " \t\r\n", &saveptr
); p
; p
= strtok_r(NULL
, " \t\r\n", &saveptr
)) {
152 int strsz
= str
.size();
155 if (strsz
> 0 && str
[strsz
-1] == ';') {
156 int num_semikolon
= 0;
157 while (strsz
> 0 && str
[strsz
-1] == ';')
158 strsz
--, num_semikolon
++;
160 args
.push_back(str
.substr(0, strsz
));
163 if (num_semikolon
== 2)
164 call(design
, "clean");
165 if (num_semikolon
== 3)
166 call(design
, "clean -purge");
174 void Pass::call(RTLIL::Design
*design
, std::vector
<std::string
> args
)
176 if (args
.size() == 0 || args
[0][0] == '#')
180 log("%s", create_prompt(design
, 0));
181 for (size_t i
= 0; i
< args
.size(); i
++)
182 log("%s%s", i
? " " : "", args
[i
].c_str());
186 if (pass_register
.count(args
[0]) == 0)
187 log_cmd_error("No such command: %s (type 'help' for a command overview)\n", args
[0].c_str());
189 size_t orig_sel_stack_pos
= design
->selection_stack
.size();
190 pass_register
[args
[0]]->call_counter
++;
191 pass_register
[args
[0]]->execute(args
, design
);
192 while (design
->selection_stack
.size() > orig_sel_stack_pos
)
193 design
->selection_stack
.pop_back();
198 void Pass::call_on_selection(RTLIL::Design
*design
, const RTLIL::Selection
&selection
, std::string command
)
200 std::string backup_selected_active_module
= design
->selected_active_module
;
201 design
->selected_active_module
.clear();
202 design
->selection_stack
.push_back(selection
);
204 Pass::call(design
, command
);
206 design
->selection_stack
.pop_back();
207 design
->selected_active_module
= backup_selected_active_module
;
210 void Pass::call_on_selection(RTLIL::Design
*design
, const RTLIL::Selection
&selection
, std::vector
<std::string
> args
)
212 std::string backup_selected_active_module
= design
->selected_active_module
;
213 design
->selected_active_module
.clear();
214 design
->selection_stack
.push_back(selection
);
216 Pass::call(design
, args
);
218 design
->selection_stack
.pop_back();
219 design
->selected_active_module
= backup_selected_active_module
;
222 void Pass::call_on_module(RTLIL::Design
*design
, RTLIL::Module
*module
, std::string command
)
224 std::string backup_selected_active_module
= design
->selected_active_module
;
225 design
->selected_active_module
= module
->name
;
226 design
->selection_stack
.push_back(RTLIL::Selection(false));
227 design
->selection_stack
.back().select(module
);
229 Pass::call(design
, command
);
231 design
->selection_stack
.pop_back();
232 design
->selected_active_module
= backup_selected_active_module
;
235 void Pass::call_on_module(RTLIL::Design
*design
, RTLIL::Module
*module
, std::vector
<std::string
> args
)
237 std::string backup_selected_active_module
= design
->selected_active_module
;
238 design
->selected_active_module
= module
->name
;
239 design
->selection_stack
.push_back(RTLIL::Selection(false));
240 design
->selection_stack
.back().select(module
);
242 Pass::call(design
, args
);
244 design
->selection_stack
.pop_back();
245 design
->selected_active_module
= backup_selected_active_module
;
248 Frontend::Frontend(std::string name
, std::string short_help
) : Pass("read_"+name
, short_help
), frontend_name(name
)
252 void Frontend::run_register()
254 log_assert(pass_register
.count(pass_name
) == 0);
255 pass_register
[pass_name
] = this;
257 log_assert(frontend_register
.count(frontend_name
) == 0);
258 frontend_register
[frontend_name
] = this;
261 Frontend::~Frontend()
265 void Frontend::execute(std::vector
<std::string
> args
, RTLIL::Design
*design
)
267 log_assert(next_args
.empty());
272 execute(f
, std::string(), args
, design
);
275 } while (!args
.empty());
278 FILE *Frontend::current_script_file
= NULL
;
279 std::string
Frontend::last_here_document
;
281 void Frontend::extra_args(FILE *&f
, std::string
&filename
, std::vector
<std::string
> args
, size_t argidx
)
283 bool called_with_fp
= f
!= NULL
;
286 for (; argidx
< args
.size(); argidx
++)
288 std::string arg
= args
[argidx
];
290 if (arg
.substr(0, 1) == "-")
291 cmd_error(args
, argidx
, "Unkown option or option in arguments.");
293 cmd_error(args
, argidx
, "Extra filename argument in direct file mode.");
296 if (filename
== "<<" && argidx
+1 < args
.size())
297 filename
+= args
[++argidx
];
298 if (filename
.substr(0, 2) == "<<") {
299 if (Frontend::current_script_file
== NULL
)
300 log_error("Unexpected here document '%s' outside of script!\n", filename
.c_str());
301 if (filename
.size() <= 2)
302 log_error("Missing EOT marker in here document!\n");
303 std::string eot_marker
= filename
.substr(2);
304 last_here_document
.clear();
309 if (fgets(block
, 4096, Frontend::current_script_file
) == NULL
)
310 log_error("Unexpected end of file in here document '%s'!\n", filename
.c_str());
312 if (buffer
.size() > 0 && (buffer
[buffer
.size() - 1] == '\n' || buffer
[buffer
.size() - 1] == '\r'))
315 int indent
= buffer
.find_first_not_of(" \t\r\n");
316 if (buffer
.substr(indent
, eot_marker
.size()) == eot_marker
)
318 last_here_document
+= buffer
;
320 f
= fmemopen((void*)last_here_document
.c_str(), last_here_document
.size(), "r");
322 f
= fopen(filename
.c_str(), "r");
324 log_cmd_error("Can't open input file `%s' for reading: %s\n", filename
.c_str(), strerror(errno
));
326 for (size_t i
= argidx
+1; i
< args
.size(); i
++)
327 if (args
[i
].substr(0, 1) == "-")
328 cmd_error(args
, i
, "Found option, expected arguments.");
330 if (argidx
+1 < args
.size()) {
331 next_args
.insert(next_args
.begin(), args
.begin(), args
.begin()+argidx
);
332 next_args
.insert(next_args
.begin()+argidx
, args
.begin()+argidx
+1, args
.end());
333 args
.erase(args
.begin()+argidx
+1, args
.end());
338 cmd_error(args
, argidx
, "No filename given.");
341 args
.push_back(filename
);
343 // cmd_log_args(args);
346 void Frontend::frontend_call(RTLIL::Design
*design
, FILE *f
, std::string filename
, std::string command
)
348 std::vector
<std::string
> args
;
349 char *s
= strdup(command
.c_str());
350 for (char *p
= strtok(s
, " \t\r\n"); p
; p
= strtok(NULL
, " \t\r\n"))
353 frontend_call(design
, f
, filename
, args
);
356 void Frontend::frontend_call(RTLIL::Design
*design
, FILE *f
, std::string filename
, std::vector
<std::string
> args
)
358 if (args
.size() == 0)
360 if (frontend_register
.count(args
[0]) == 0)
361 log_cmd_error("No such frontend: %s\n", args
[0].c_str());
364 frontend_register
[args
[0]]->call_counter
++;
365 frontend_register
[args
[0]]->execute(f
, filename
, args
, design
);
366 } else if (filename
== "-") {
367 FILE *f_stdin
= stdin
; // workaround for OpenBSD 'stdin' implementation
368 frontend_register
[args
[0]]->call_counter
++;
369 frontend_register
[args
[0]]->execute(f_stdin
, "<stdin>", args
, design
);
371 if (!filename
.empty())
372 args
.push_back(filename
);
373 frontend_register
[args
[0]]->execute(args
, design
);
379 Backend::Backend(std::string name
, std::string short_help
) :
380 Pass(name
.substr(0, 1) == "=" ? name
.substr(1) : "write_"+name
, short_help
),
381 backend_name(name
.substr(0, 1) == "=" ? name
.substr(1) : name
)
385 void Backend::run_register()
387 log_assert(pass_register
.count(pass_name
) == 0);
388 pass_register
[pass_name
] = this;
390 log_assert(backend_register
.count(backend_name
) == 0);
391 backend_register
[backend_name
] = this;
398 void Backend::execute(std::vector
<std::string
> args
, RTLIL::Design
*design
)
402 execute(f
, std::string(), args
, design
);
407 void Backend::extra_args(FILE *&f
, std::string
&filename
, std::vector
<std::string
> args
, size_t argidx
)
409 bool called_with_fp
= f
!= NULL
;
411 for (; argidx
< args
.size(); argidx
++)
413 std::string arg
= args
[argidx
];
415 if (arg
.substr(0, 1) == "-" && arg
!= "-")
416 cmd_error(args
, argidx
, "Unkown option or option in arguments.");
418 cmd_error(args
, argidx
, "Extra filename argument in direct file mode.");
421 filename
= "<stdout>";
427 f
= fopen(filename
.c_str(), "w");
429 log_cmd_error("Can't open output file `%s' for writing: %s\n", filename
.c_str(), strerror(errno
));
433 args
.push_back(filename
);
435 // cmd_log_args(args);
438 filename
= "<stdout>";
443 void Backend::backend_call(RTLIL::Design
*design
, FILE *f
, std::string filename
, std::string command
)
445 std::vector
<std::string
> args
;
446 char *s
= strdup(command
.c_str());
447 for (char *p
= strtok(s
, " \t\r\n"); p
; p
= strtok(NULL
, " \t\r\n"))
450 backend_call(design
, f
, filename
, args
);
453 void Backend::backend_call(RTLIL::Design
*design
, FILE *f
, std::string filename
, std::vector
<std::string
> args
)
455 if (args
.size() == 0)
457 if (backend_register
.count(args
[0]) == 0)
458 log_cmd_error("No such backend: %s\n", args
[0].c_str());
460 size_t orig_sel_stack_pos
= design
->selection_stack
.size();
463 backend_register
[args
[0]]->call_counter
++;
464 backend_register
[args
[0]]->execute(f
, filename
, args
, design
);
465 } else if (filename
== "-") {
466 FILE *f_stdout
= stdout
; // workaround for OpenBSD 'stdout' implementation
467 backend_register
[args
[0]]->call_counter
++;
468 backend_register
[args
[0]]->execute(f_stdout
, "<stdout>", args
, design
);
470 if (!filename
.empty())
471 args
.push_back(filename
);
472 backend_register
[args
[0]]->execute(args
, design
);
475 while (design
->selection_stack
.size() > orig_sel_stack_pos
)
476 design
->selection_stack
.pop_back();
481 struct HelpPass
: public Pass
{
482 HelpPass() : Pass("help", "display help messages") { }
486 log(" help ............. list all commands\n");
487 log(" help <command> ... print help message for given command\n");
488 log(" help -all ........ print complete command reference\n");
491 void escape_tex(std::string
&tex
)
494 while ((pos
= tex
.find('_', pos
)) != std::string::npos
) {
495 tex
.replace(pos
, 1, "\\_");
499 void write_tex(FILE *f
, std::string cmd
, std::string title
, std::string text
)
501 size_t begin
= text
.find_first_not_of("\n"), end
= text
.find_last_not_of("\n");
502 if (begin
!= std::string::npos
&& end
!= std::string::npos
&& begin
< end
)
503 text
= text
.substr(begin
, end
-begin
+1);
504 std::string cmd_unescaped
= cmd
;
507 fprintf(f
, "\\section{%s -- %s}\n", cmd
.c_str(), title
.c_str());
508 fprintf(f
, "\\label{cmd:%s}\n", cmd_unescaped
.c_str());
509 fprintf(f
, "\\begin{lstlisting}[numbers=left,frame=single]\n");
510 fprintf(f
, "%s\n\\end{lstlisting}\n\n", text
.c_str());
512 void escape_html(std::string
&html
)
515 while ((pos
= html
.find_first_of("<>&", pos
)) != std::string::npos
)
518 html
.replace(pos
, 1, "<");
522 html
.replace(pos
, 1, ">");
526 html
.replace(pos
, 1, "&");
531 void write_html(FILE *idxf
, std::string cmd
, std::string title
, std::string text
)
533 FILE *f
= fopen(stringf("cmd_%s.in", cmd
.c_str()).c_str(), "wt");
534 fprintf(idxf
, "<li><a href=\"cmd_%s.html\"> ", cmd
.c_str());
540 fprintf(idxf
, "%s</a> <span>%s</span></a>\n", cmd
.c_str(), title
.c_str());
542 fprintf(f
, "@cmd_header %s@\n", cmd
.c_str());
543 fprintf(f
, "<h1>%s - %s</h1>\n", cmd
.c_str(), title
.c_str());
544 fprintf(f
, "<pre>%s</pre>\n", text
.c_str());
545 fprintf(f
, "@footer@\n");
549 virtual void execute(std::vector
<std::string
> args
, RTLIL::Design
*)
551 if (args
.size() == 1) {
553 for (auto &it
: REGISTER_INTERN::pass_register
)
554 log(" %-20s %s\n", it
.first
.c_str(), it
.second
->short_help
.c_str());
556 log("Type 'help <command>' for more information on a command.\n");
561 if (args
.size() == 2) {
562 if (args
[1] == "-all") {
563 for (auto &it
: REGISTER_INTERN::pass_register
) {
565 log("%s -- %s\n", it
.first
.c_str(), it
.second
->short_help
.c_str());
566 for (size_t i
= 0; i
< it
.first
.size() + it
.second
->short_help
.size() + 6; i
++)
572 // this option is undocumented as it is for internal use only
573 else if (args
[1] == "-write-tex-command-reference-manual") {
574 FILE *f
= fopen("command-reference-manual.tex", "wt");
575 fprintf(f
, "%% Generated using the yosys 'help -write-tex-command-reference-manual' command.\n\n");
576 for (auto &it
: REGISTER_INTERN::pass_register
) {
579 FILE *memf
= open_memstream(&memptr
, &memsize
);
580 log_files
.push_back(memf
);
582 log_files
.pop_back();
584 write_tex(f
, it
.first
, it
.second
->short_help
, memptr
);
589 // this option is undocumented as it is for internal use only
590 else if (args
[1] == "-write-web-command-reference-manual") {
591 FILE *f
= fopen("templates/cmd_index.in", "wt");
592 for (auto &it
: REGISTER_INTERN::pass_register
) {
595 FILE *memf
= open_memstream(&memptr
, &memsize
);
596 log_files
.push_back(memf
);
598 log_files
.pop_back();
600 write_html(f
, it
.first
, it
.second
->short_help
, memptr
);
605 else if (REGISTER_INTERN::pass_register
.count(args
[1]) == 0)
606 log("No such command: %s\n", args
[1].c_str());
608 REGISTER_INTERN::pass_register
.at(args
[1])->help();
616 struct EchoPass
: public Pass
{
617 EchoPass() : Pass("echo", "turning echoing back of commands on and off") { }
623 log("Print all commands to log before executing them.\n");
628 log("Do not print all commands to log before executing them. (default)\n");
631 virtual void execute(std::vector
<std::string
> args
, RTLIL::Design
*)
634 cmd_error(args
, 2, "Unexpected argument.");
636 if (args
.size() == 2) {
639 else if (args
[1] == "off")
642 cmd_error(args
, 1, "Unexpected argument.");
645 log("echo %s\n", echo_mode
? "on" : "off");