Added ";;" as shortcut for "; clean;"
[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 "register.h"
21 #include "log.h"
22 #include <assert.h>
23 #include <string.h>
24 #include <stdlib.h>
25 #include <stdio.h>
26
27 using namespace REGISTER_INTERN;
28 #define MAX_REG_COUNT 1000
29
30 namespace REGISTER_INTERN
31 {
32 int raw_register_count = 0;
33 bool raw_register_done = false;
34 Pass *raw_register_array[MAX_REG_COUNT];
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 assert(!raw_register_done);
46 assert(raw_register_count < MAX_REG_COUNT);
47 raw_register_array[raw_register_count++] = this;
48 }
49
50 void Pass::run_register()
51 {
52 assert(pass_register.count(pass_name) == 0);
53 pass_register[pass_name] = this;
54 }
55
56 void Pass::init_register()
57 {
58 if (raw_register_done)
59 done_register();
60 while (raw_register_count > 0)
61 raw_register_array[--raw_register_count]->run_register();
62 raw_register_done = true;
63 }
64
65 void Pass::done_register()
66 {
67 frontend_register.clear();
68 pass_register.clear();
69 backend_register.clear();
70 raw_register_done = false;
71 }
72
73 Pass::~Pass()
74 {
75 }
76
77 void Pass::help()
78 {
79 log("\n");
80 log("No help message for command `%s'.\n", pass_name.c_str());
81 log("\n");
82 }
83
84 void Pass::cmd_log_args(const std::vector<std::string> &args)
85 {
86 if (args.size() <= 1)
87 return;
88 log("Full command line:");
89 for (size_t i = 0; i < args.size(); i++)
90 log(" %s", args[i].c_str());
91 log("\n");
92 }
93
94 void Pass::cmd_error(const std::vector<std::string> &args, size_t argidx, std::string msg)
95 {
96 std::string command_text;
97 int error_pos = 0;
98
99 for (size_t i = 0; i < args.size(); i++) {
100 if (i < argidx)
101 error_pos += args[i].size() + 1;
102 command_text = command_text + (command_text.empty() ? "" : " ") + args[i];
103 }
104
105 log("\nSyntax error in command `%s':\n", command_text.c_str());
106 help();
107
108 log_cmd_error("Command syntax error: %s\n> %s\n> %*s^\n",
109 msg.c_str(), command_text.c_str(), error_pos, "");
110 }
111
112 void Pass::extra_args(std::vector<std::string> args, size_t argidx, RTLIL::Design *design, bool select)
113 {
114 for (; argidx < args.size(); argidx++)
115 {
116 std::string arg = args[argidx];
117
118 if (arg.substr(0, 1) == "-")
119 cmd_error(args, argidx, "Unkown option or option in arguments.");
120
121 if (!select)
122 cmd_error(args, argidx, "Extra argument.");
123
124 handle_extra_select_args(this, args, argidx, args.size(), design);
125 break;
126 }
127 cmd_log_args(args);
128 }
129
130 void Pass::call(RTLIL::Design *design, std::string command)
131 {
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 == '#')
136 return;
137 if (*s == '!') {
138 for (s++; *s == ' ' || *s == '\t'; s++) { }
139 char *p = s + strlen(s) - 1;
140 while (p >= s && (*p == '\r' || *p == '\n'))
141 *(p--) = 0;
142 log_header("Shell command: %s\n", s);
143 int retCode = system(s);
144 if (retCode != 0)
145 log_cmd_error("Shell command returned error code %d.\n", retCode);
146 return;
147 }
148 for (char *p = strtok_r(s, " \t\r\n", &saveptr); p; p = strtok_r(NULL, " \t\r\n", &saveptr)) {
149 std::string str = p;
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++;
155 if (strsz > 0)
156 args.push_back(str.substr(0, strsz));
157 call(design, args);
158 args.clear();
159 if (num_semikolon == 2)
160 call(design, "clean");
161 } else
162 args.push_back(str);
163 }
164 free(sstart);
165 call(design, args);
166 }
167
168 void Pass::call(RTLIL::Design *design, std::vector<std::string> args)
169 {
170 if (args.size() == 0 || args[0][0] == '#')
171 return;
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());
174
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();
179 }
180
181 Frontend::Frontend(std::string name, std::string short_help) : Pass("read_"+name, short_help), frontend_name(name)
182 {
183 }
184
185 void Frontend::run_register()
186 {
187 assert(pass_register.count(pass_name) == 0);
188 pass_register[pass_name] = this;
189
190 assert(frontend_register.count(frontend_name) == 0);
191 frontend_register[frontend_name] = this;
192 }
193
194 Frontend::~Frontend()
195 {
196 }
197
198 void Frontend::execute(std::vector<std::string> args, RTLIL::Design *design)
199 {
200 assert(next_args.empty());
201 do {
202 FILE *f = NULL;
203 next_args.clear();
204 execute(f, std::string(), args, design);
205 args = next_args;
206 fclose(f);
207 } while (!args.empty());
208 }
209
210 void Frontend::extra_args(FILE *&f, std::string &filename, std::vector<std::string> args, size_t argidx)
211 {
212 bool called_with_fp = f != NULL;
213
214 next_args.clear();
215 for (; argidx < args.size(); argidx++)
216 {
217 std::string arg = args[argidx];
218
219 if (arg.substr(0, 1) == "-")
220 cmd_error(args, argidx, "Unkown option or option in arguments.");
221 if (f != NULL)
222 cmd_error(args, argidx, "Extra filename argument in direct file mode.");
223
224 filename = arg;
225 f = fopen(filename.c_str(), "r");
226 if (f == NULL)
227 log_cmd_error("Can't open input file `%s' for reading: %s\n", filename.c_str(), strerror(errno));
228
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());
233 }
234 break;
235 }
236 if (f == NULL)
237 cmd_error(args, argidx, "No filename given.");
238
239 if (called_with_fp)
240 args.push_back(filename);
241 args[0] = pass_name;
242 cmd_log_args(args);
243 }
244
245 void Frontend::frontend_call(RTLIL::Design *design, FILE *f, std::string filename, std::string command)
246 {
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"))
250 args.push_back(p);
251 free(s);
252 frontend_call(design, f, filename, args);
253 }
254
255 void Frontend::frontend_call(RTLIL::Design *design, FILE *f, std::string filename, std::vector<std::string> args)
256 {
257 if (args.size() == 0)
258 return;
259 if (frontend_register.count(args[0]) == 0)
260 log_cmd_error("No such frontend: %s\n", args[0].c_str());
261
262 if (f != NULL) {
263 frontend_register[args[0]]->execute(f, filename, args, design);
264 } else if (filename == "-") {
265 frontend_register[args[0]]->execute(stdin, "<stdin>", args, design);
266 } else {
267 if (!filename.empty())
268 args.push_back(filename);
269 frontend_register[args[0]]->execute(args, design);
270 }
271 }
272
273 Backend::Backend(std::string name, std::string short_help) : Pass("write_"+name, short_help), backend_name(name)
274 {
275 }
276
277 void Backend::run_register()
278 {
279 assert(pass_register.count(pass_name) == 0);
280 pass_register[pass_name] = this;
281
282 assert(backend_register.count(backend_name) == 0);
283 backend_register[backend_name] = this;
284 }
285
286 Backend::~Backend()
287 {
288 }
289
290 void Backend::execute(std::vector<std::string> args, RTLIL::Design *design)
291 {
292 FILE *f = NULL;
293 execute(f, std::string(), args, design);
294 if (f != stdout)
295 fclose(f);
296 }
297
298 void Backend::extra_args(FILE *&f, std::string &filename, std::vector<std::string> args, size_t argidx)
299 {
300 bool called_with_fp = f != NULL;
301
302 for (; argidx < args.size(); argidx++)
303 {
304 std::string arg = args[argidx];
305
306 if (arg.substr(0, 1) == "-" && arg != "-")
307 cmd_error(args, argidx, "Unkown option or option in arguments.");
308 if (f != NULL)
309 cmd_error(args, argidx, "Extra filename argument in direct file mode.");
310
311 if (arg == "-") {
312 filename = "<stdout>";
313 f = stdout;
314 continue;
315 }
316
317 filename = arg;
318 f = fopen(filename.c_str(), "w");
319 if (f == NULL)
320 log_cmd_error("Can't open output file `%s' for writing: %s\n", filename.c_str(), strerror(errno));
321 }
322
323 if (called_with_fp)
324 args.push_back(filename);
325 args[0] = pass_name;
326 cmd_log_args(args);
327
328 if (f == NULL) {
329 filename = "<stdout>";
330 f = stdout;
331 }
332 }
333
334 void Backend::backend_call(RTLIL::Design *design, FILE *f, std::string filename, std::string command)
335 {
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"))
339 args.push_back(p);
340 free(s);
341 backend_call(design, f, filename, args);
342 }
343
344 void Backend::backend_call(RTLIL::Design *design, FILE *f, std::string filename, std::vector<std::string> args)
345 {
346 if (args.size() == 0)
347 return;
348 if (backend_register.count(args[0]) == 0)
349 log_cmd_error("No such backend: %s\n", args[0].c_str());
350
351 size_t orig_sel_stack_pos = design->selection_stack.size();
352
353 if (f != NULL) {
354 backend_register[args[0]]->execute(f, filename, args, design);
355 } else if (filename == "-") {
356 backend_register[args[0]]->execute(stdout, "<stdout>", args, design);
357 } else {
358 if (!filename.empty())
359 args.push_back(filename);
360 backend_register[args[0]]->execute(args, design);
361 }
362
363 while (design->selection_stack.size() > orig_sel_stack_pos)
364 design->selection_stack.pop_back();
365 }
366
367 struct HelpPass : public Pass {
368 HelpPass() : Pass("help", "display help messages") { }
369 virtual void help()
370 {
371 log("\n");
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");
375 log("\n");
376 }
377 void escape_tex(std::string &tex)
378 {
379 size_t pos = 0;
380 while ((pos = tex.find('_', pos)) != std::string::npos) {
381 tex.replace(pos, 1, "\\_");
382 pos += 2;
383 }
384 }
385 void write_tex(FILE *f, std::string cmd, std::string title, std::string text)
386 {
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;
391 escape_tex(cmd);
392 escape_tex(title);
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());
397 }
398 void escape_html(std::string &html)
399 {
400 size_t pos = 0;
401 while ((pos = html.find_first_of("<>&", pos)) != std::string::npos)
402 switch (html[pos]) {
403 case '<':
404 html.replace(pos, 1, "&lt;");
405 pos += 4;
406 break;
407 case '>':
408 html.replace(pos, 1, "&gt;");
409 pos += 4;
410 break;
411 case '&':
412 html.replace(pos, 1, "&amp;");
413 pos += 5;
414 break;
415 }
416 }
417 void write_html(FILE *idxf, std::string cmd, std::string title, std::string text)
418 {
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());
421
422 escape_html(cmd);
423 escape_html(title);
424 escape_html(text);
425
426 fprintf(idxf, "%s</a> <span>%s</span></a>\n", cmd.c_str(), title.c_str());
427
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");
432
433 fclose(f);
434 }
435 virtual void execute(std::vector<std::string> args, RTLIL::Design*)
436 {
437 if (args.size() == 1) {
438 log("\n");
439 for (auto &it : REGISTER_INTERN::pass_register)
440 log(" %-20s %s\n", it.first.c_str(), it.second->short_help.c_str());
441 log("\n");
442 log("Type 'help <command>' for more information on a command.\n");
443 log("\n");
444 return;
445 }
446
447 if (args.size() == 2) {
448 if (args[1] == "-all") {
449 for (auto &it : REGISTER_INTERN::pass_register) {
450 log("\n\n");
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++)
453 log("=");
454 log("\n");
455 it.second->help();
456 }
457 }
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) {
463 size_t memsize;
464 char *memptr;
465 FILE *memf = open_memstream(&memptr, &memsize);
466 log_files.push_back(memf);
467 it.second->help();
468 log_files.pop_back();
469 fclose(memf);
470 write_tex(f, it.first, it.second->short_help, memptr);
471 free(memptr);
472 }
473 fclose(f);
474 }
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) {
479 size_t memsize;
480 char *memptr;
481 FILE *memf = open_memstream(&memptr, &memsize);
482 log_files.push_back(memf);
483 it.second->help();
484 log_files.pop_back();
485 fclose(memf);
486 write_html(f, it.first, it.second->short_help, memptr);
487 free(memptr);
488 }
489 fclose(f);
490 }
491 else if (REGISTER_INTERN::pass_register.count(args[1]) == 0)
492 log("No such command: %s\n", args[1].c_str());
493 else
494 REGISTER_INTERN::pass_register.at(args[1])->help();
495 return;
496 }
497
498 help();
499 }
500 } HelpPass;
501