4569481fadb4bef9ebe63d24ae4fb5f3e4e9f63f
[yosys.git] / kernel / register.cc
1 /*
2 * yosys -- Yosys Open SYnthesis Suite
3 *
4 * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
5 *
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.
9 *
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.
17 *
18 */
19
20 #include "kernel/compatibility.h"
21 #include "kernel/register.h"
22 #include "kernel/log.h"
23 #include <string.h>
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <errno.h>
27
28 using namespace REGISTER_INTERN;
29 #define MAX_REG_COUNT 1000
30
31 namespace REGISTER_INTERN
32 {
33 bool echo_mode = false;
34 Pass *first_queued_pass;
35
36 std::map<std::string, Frontend*> frontend_register;
37 std::map<std::string, Pass*> pass_register;
38 std::map<std::string, Backend*> backend_register;
39 }
40
41 std::vector<std::string> Frontend::next_args;
42
43 Pass::Pass(std::string name, std::string short_help) : pass_name(name), short_help(short_help)
44 {
45 next_queued_pass = first_queued_pass;
46 first_queued_pass = this;
47 call_counter = 0;
48 }
49
50 void Pass::run_register()
51 {
52 log_assert(pass_register.count(pass_name) == 0);
53 pass_register[pass_name] = this;
54 }
55
56 void Pass::init_register()
57 {
58 while (first_queued_pass) {
59 first_queued_pass->run_register();
60 first_queued_pass = first_queued_pass->next_queued_pass;
61 }
62 }
63
64 void Pass::done_register()
65 {
66 frontend_register.clear();
67 pass_register.clear();
68 backend_register.clear();
69 log_assert(first_queued_pass == NULL);
70 }
71
72 Pass::~Pass()
73 {
74 }
75
76 void Pass::help()
77 {
78 log("\n");
79 log("No help message for command `%s'.\n", pass_name.c_str());
80 log("\n");
81 }
82
83 void Pass::cmd_log_args(const std::vector<std::string> &args)
84 {
85 if (args.size() <= 1)
86 return;
87 log("Full command line:");
88 for (size_t i = 0; i < args.size(); i++)
89 log(" %s", args[i].c_str());
90 log("\n");
91 }
92
93 void Pass::cmd_error(const std::vector<std::string> &args, size_t argidx, std::string msg)
94 {
95 std::string command_text;
96 int error_pos = 0;
97
98 for (size_t i = 0; i < args.size(); i++) {
99 if (i < argidx)
100 error_pos += args[i].size() + 1;
101 command_text = command_text + (command_text.empty() ? "" : " ") + args[i];
102 }
103
104 log("\nSyntax error in command `%s':\n", command_text.c_str());
105 help();
106
107 log_cmd_error("Command syntax error: %s\n> %s\n> %*s^\n",
108 msg.c_str(), command_text.c_str(), error_pos, "");
109 }
110
111 void Pass::extra_args(std::vector<std::string> args, size_t argidx, RTLIL::Design *design, bool select)
112 {
113 for (; argidx < args.size(); argidx++)
114 {
115 std::string arg = args[argidx];
116
117 if (arg.substr(0, 1) == "-")
118 cmd_error(args, argidx, "Unkown option or option in arguments.");
119
120 if (!select)
121 cmd_error(args, argidx, "Extra argument.");
122
123 handle_extra_select_args(this, args, argidx, args.size(), design);
124 break;
125 }
126 // cmd_log_args(args);
127 }
128
129 void Pass::call(RTLIL::Design *design, std::string command)
130 {
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 == '#') {
135 free(sstart);
136 return;
137 }
138 if (*s == '!') {
139 for (s++; *s == ' ' || *s == '\t'; s++) { }
140 char *p = s + strlen(s) - 1;
141 while (p >= s && (*p == '\r' || *p == '\n'))
142 *(p--) = 0;
143 log_header("Shell command: %s\n", s);
144 int retCode = system(s);
145 if (retCode != 0)
146 log_cmd_error("Shell command returned error code %d.\n", retCode);
147 free(sstart);
148 return;
149 }
150 for (char *p = strtok_r(s, " \t\r\n", &saveptr); p; p = strtok_r(NULL, " \t\r\n", &saveptr)) {
151 std::string str = p;
152 int strsz = str.size();
153 if (str == "#")
154 break;
155 if (strsz > 0 && str[strsz-1] == ';') {
156 int num_semikolon = 0;
157 while (strsz > 0 && str[strsz-1] == ';')
158 strsz--, num_semikolon++;
159 if (strsz > 0)
160 args.push_back(str.substr(0, strsz));
161 call(design, args);
162 args.clear();
163 if (num_semikolon == 2)
164 call(design, "clean");
165 if (num_semikolon == 3)
166 call(design, "clean -purge");
167 } else
168 args.push_back(str);
169 }
170 free(sstart);
171 call(design, args);
172 }
173
174 void Pass::call(RTLIL::Design *design, std::vector<std::string> args)
175 {
176 if (args.size() == 0 || args[0][0] == '#')
177 return;
178
179 if (echo_mode) {
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());
183 log("\n");
184 }
185
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());
188
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();
194
195 design->check();
196 }
197
198 void Pass::call_on_selection(RTLIL::Design *design, const RTLIL::Selection &selection, std::string command)
199 {
200 std::string backup_selected_active_module = design->selected_active_module;
201 design->selected_active_module.clear();
202 design->selection_stack.push_back(selection);
203
204 Pass::call(design, command);
205
206 design->selection_stack.pop_back();
207 design->selected_active_module = backup_selected_active_module;
208 }
209
210 void Pass::call_on_selection(RTLIL::Design *design, const RTLIL::Selection &selection, std::vector<std::string> args)
211 {
212 std::string backup_selected_active_module = design->selected_active_module;
213 design->selected_active_module.clear();
214 design->selection_stack.push_back(selection);
215
216 Pass::call(design, args);
217
218 design->selection_stack.pop_back();
219 design->selected_active_module = backup_selected_active_module;
220 }
221
222 void Pass::call_on_module(RTLIL::Design *design, RTLIL::Module *module, std::string command)
223 {
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);
228
229 Pass::call(design, command);
230
231 design->selection_stack.pop_back();
232 design->selected_active_module = backup_selected_active_module;
233 }
234
235 void Pass::call_on_module(RTLIL::Design *design, RTLIL::Module *module, std::vector<std::string> args)
236 {
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);
241
242 Pass::call(design, args);
243
244 design->selection_stack.pop_back();
245 design->selected_active_module = backup_selected_active_module;
246 }
247
248 Frontend::Frontend(std::string name, std::string short_help) : Pass("read_"+name, short_help), frontend_name(name)
249 {
250 }
251
252 void Frontend::run_register()
253 {
254 log_assert(pass_register.count(pass_name) == 0);
255 pass_register[pass_name] = this;
256
257 log_assert(frontend_register.count(frontend_name) == 0);
258 frontend_register[frontend_name] = this;
259 }
260
261 Frontend::~Frontend()
262 {
263 }
264
265 void Frontend::execute(std::vector<std::string> args, RTLIL::Design *design)
266 {
267 log_assert(next_args.empty());
268 do {
269 FILE *f = NULL;
270 next_args.clear();
271 call_counter++;
272 execute(f, std::string(), args, design);
273 args = next_args;
274 fclose(f);
275 } while (!args.empty());
276 }
277
278 FILE *Frontend::current_script_file = NULL;
279 std::string Frontend::last_here_document;
280
281 void Frontend::extra_args(FILE *&f, std::string &filename, std::vector<std::string> args, size_t argidx)
282 {
283 bool called_with_fp = f != NULL;
284
285 next_args.clear();
286 for (; argidx < args.size(); argidx++)
287 {
288 std::string arg = args[argidx];
289
290 if (arg.substr(0, 1) == "-")
291 cmd_error(args, argidx, "Unkown option or option in arguments.");
292 if (f != NULL)
293 cmd_error(args, argidx, "Extra filename argument in direct file mode.");
294
295 filename = arg;
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();
305 while (1) {
306 std::string buffer;
307 char block[4096];
308 while (1) {
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());
311 buffer += block;
312 if (buffer.size() > 0 && (buffer[buffer.size() - 1] == '\n' || buffer[buffer.size() - 1] == '\r'))
313 break;
314 }
315 int indent = buffer.find_first_not_of(" \t\r\n");
316 if (buffer.substr(indent, eot_marker.size()) == eot_marker)
317 break;
318 last_here_document += buffer;
319 }
320 f = fmemopen((void*)last_here_document.c_str(), last_here_document.size(), "r");
321 } else
322 f = fopen(filename.c_str(), "r");
323 if (f == NULL)
324 log_cmd_error("Can't open input file `%s' for reading: %s\n", filename.c_str(), strerror(errno));
325
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.");
329
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());
334 }
335 break;
336 }
337 if (f == NULL)
338 cmd_error(args, argidx, "No filename given.");
339
340 if (called_with_fp)
341 args.push_back(filename);
342 args[0] = pass_name;
343 // cmd_log_args(args);
344 }
345
346 void Frontend::frontend_call(RTLIL::Design *design, FILE *f, std::string filename, std::string command)
347 {
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"))
351 args.push_back(p);
352 free(s);
353 frontend_call(design, f, filename, args);
354 }
355
356 void Frontend::frontend_call(RTLIL::Design *design, FILE *f, std::string filename, std::vector<std::string> args)
357 {
358 if (args.size() == 0)
359 return;
360 if (frontend_register.count(args[0]) == 0)
361 log_cmd_error("No such frontend: %s\n", args[0].c_str());
362
363 if (f != NULL) {
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);
370 } else {
371 if (!filename.empty())
372 args.push_back(filename);
373 frontend_register[args[0]]->execute(args, design);
374 }
375
376 design->check();
377 }
378
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)
382 {
383 }
384
385 void Backend::run_register()
386 {
387 log_assert(pass_register.count(pass_name) == 0);
388 pass_register[pass_name] = this;
389
390 log_assert(backend_register.count(backend_name) == 0);
391 backend_register[backend_name] = this;
392 }
393
394 Backend::~Backend()
395 {
396 }
397
398 void Backend::execute(std::vector<std::string> args, RTLIL::Design *design)
399 {
400 FILE *f = NULL;
401 call_counter++;
402 execute(f, std::string(), args, design);
403 if (f != stdout)
404 fclose(f);
405 }
406
407 void Backend::extra_args(FILE *&f, std::string &filename, std::vector<std::string> args, size_t argidx)
408 {
409 bool called_with_fp = f != NULL;
410
411 for (; argidx < args.size(); argidx++)
412 {
413 std::string arg = args[argidx];
414
415 if (arg.substr(0, 1) == "-" && arg != "-")
416 cmd_error(args, argidx, "Unkown option or option in arguments.");
417 if (f != NULL)
418 cmd_error(args, argidx, "Extra filename argument in direct file mode.");
419
420 if (arg == "-") {
421 filename = "<stdout>";
422 f = stdout;
423 continue;
424 }
425
426 filename = arg;
427 f = fopen(filename.c_str(), "w");
428 if (f == NULL)
429 log_cmd_error("Can't open output file `%s' for writing: %s\n", filename.c_str(), strerror(errno));
430 }
431
432 if (called_with_fp)
433 args.push_back(filename);
434 args[0] = pass_name;
435 // cmd_log_args(args);
436
437 if (f == NULL) {
438 filename = "<stdout>";
439 f = stdout;
440 }
441 }
442
443 void Backend::backend_call(RTLIL::Design *design, FILE *f, std::string filename, std::string command)
444 {
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"))
448 args.push_back(p);
449 free(s);
450 backend_call(design, f, filename, args);
451 }
452
453 void Backend::backend_call(RTLIL::Design *design, FILE *f, std::string filename, std::vector<std::string> args)
454 {
455 if (args.size() == 0)
456 return;
457 if (backend_register.count(args[0]) == 0)
458 log_cmd_error("No such backend: %s\n", args[0].c_str());
459
460 size_t orig_sel_stack_pos = design->selection_stack.size();
461
462 if (f != NULL) {
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);
469 } else {
470 if (!filename.empty())
471 args.push_back(filename);
472 backend_register[args[0]]->execute(args, design);
473 }
474
475 while (design->selection_stack.size() > orig_sel_stack_pos)
476 design->selection_stack.pop_back();
477
478 design->check();
479 }
480
481 struct HelpPass : public Pass {
482 HelpPass() : Pass("help", "display help messages") { }
483 virtual void help()
484 {
485 log("\n");
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");
489 log("\n");
490 }
491 void escape_tex(std::string &tex)
492 {
493 size_t pos = 0;
494 while ((pos = tex.find('_', pos)) != std::string::npos) {
495 tex.replace(pos, 1, "\\_");
496 pos += 2;
497 }
498 }
499 void write_tex(FILE *f, std::string cmd, std::string title, std::string text)
500 {
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;
505 escape_tex(cmd);
506 escape_tex(title);
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());
511 }
512 void escape_html(std::string &html)
513 {
514 size_t pos = 0;
515 while ((pos = html.find_first_of("<>&", pos)) != std::string::npos)
516 switch (html[pos]) {
517 case '<':
518 html.replace(pos, 1, "&lt;");
519 pos += 4;
520 break;
521 case '>':
522 html.replace(pos, 1, "&gt;");
523 pos += 4;
524 break;
525 case '&':
526 html.replace(pos, 1, "&amp;");
527 pos += 5;
528 break;
529 }
530 }
531 void write_html(FILE *idxf, std::string cmd, std::string title, std::string text)
532 {
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());
535
536 escape_html(cmd);
537 escape_html(title);
538 escape_html(text);
539
540 fprintf(idxf, "%s</a> <span>%s</span></a>\n", cmd.c_str(), title.c_str());
541
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");
546
547 fclose(f);
548 }
549 virtual void execute(std::vector<std::string> args, RTLIL::Design*)
550 {
551 if (args.size() == 1) {
552 log("\n");
553 for (auto &it : REGISTER_INTERN::pass_register)
554 log(" %-20s %s\n", it.first.c_str(), it.second->short_help.c_str());
555 log("\n");
556 log("Type 'help <command>' for more information on a command.\n");
557 log("\n");
558 return;
559 }
560
561 if (args.size() == 2) {
562 if (args[1] == "-all") {
563 for (auto &it : REGISTER_INTERN::pass_register) {
564 log("\n\n");
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++)
567 log("=");
568 log("\n");
569 it.second->help();
570 }
571 }
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) {
577 size_t memsize;
578 char *memptr;
579 FILE *memf = open_memstream(&memptr, &memsize);
580 log_files.push_back(memf);
581 it.second->help();
582 log_files.pop_back();
583 fclose(memf);
584 write_tex(f, it.first, it.second->short_help, memptr);
585 free(memptr);
586 }
587 fclose(f);
588 }
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) {
593 size_t memsize;
594 char *memptr;
595 FILE *memf = open_memstream(&memptr, &memsize);
596 log_files.push_back(memf);
597 it.second->help();
598 log_files.pop_back();
599 fclose(memf);
600 write_html(f, it.first, it.second->short_help, memptr);
601 free(memptr);
602 }
603 fclose(f);
604 }
605 else if (REGISTER_INTERN::pass_register.count(args[1]) == 0)
606 log("No such command: %s\n", args[1].c_str());
607 else
608 REGISTER_INTERN::pass_register.at(args[1])->help();
609 return;
610 }
611
612 help();
613 }
614 } HelpPass;
615
616 struct EchoPass : public Pass {
617 EchoPass() : Pass("echo", "turning echoing back of commands on and off") { }
618 virtual void help()
619 {
620 log("\n");
621 log(" echo on\n");
622 log("\n");
623 log("Print all commands to log before executing them.\n");
624 log("\n");
625 log("\n");
626 log(" echo off\n");
627 log("\n");
628 log("Do not print all commands to log before executing them. (default)\n");
629 log("\n");
630 }
631 virtual void execute(std::vector<std::string> args, RTLIL::Design*)
632 {
633 if (args.size() > 2)
634 cmd_error(args, 2, "Unexpected argument.");
635
636 if (args.size() == 2) {
637 if (args[1] == "on")
638 echo_mode = true;
639 else if (args[1] == "off")
640 echo_mode = false;
641 else
642 cmd_error(args, 1, "Unexpected argument.");
643 }
644
645 log("echo %s\n", echo_mode ? "on" : "off");
646 }
647 } EchoPass;
648