Moved patmatch() to yosys.cc
[yosys.git] / kernel / yosys.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/yosys.h"
21
22 #ifdef YOSYS_ENABLE_READLINE
23 # include <readline/readline.h>
24 # include <readline/history.h>
25 #endif
26
27 #include <dlfcn.h>
28 #include <unistd.h>
29 #include <limits.h>
30 #include <errno.h>
31
32 YOSYS_NAMESPACE_BEGIN
33
34 int autoidx = 1;
35 RTLIL::Design *yosys_design = NULL;
36
37 #ifdef YOSYS_ENABLE_TCL
38 Tcl_Interp *yosys_tcl_interp = NULL;
39 #endif
40
41 std::string stringf(const char *fmt, ...)
42 {
43 std::string string;
44 va_list ap;
45
46 va_start(ap, fmt);
47 string = vstringf(fmt, ap);
48 va_end(ap);
49
50 return string;
51 }
52
53 std::string vstringf(const char *fmt, va_list ap)
54 {
55 std::string string;
56 char *str = NULL;
57
58 if (vasprintf(&str, fmt, ap) < 0)
59 str = NULL;
60
61 if (str != NULL) {
62 string = str;
63 free(str);
64 }
65
66 return string;
67 }
68
69 // this is very similar to fnmatch(). the exact rules used by this
70 // function are:
71 //
72 // ? matches any character except
73 // * matches any sequence of characters
74 // [...] matches any of the characters in the list
75 // [!..] matches any of the characters not in the list
76 //
77 // a backslash may be used to escape the next characters in the
78 // pattern. each special character can also simply match itself.
79 //
80 static bool patmatch(const char *pattern, const char *string)
81 {
82 if (*pattern == 0)
83 return *string == 0;
84
85 if (*pattern == '\\') {
86 if (pattern[1] == string[0] && patmatch(pattern+2, string+1))
87 return true;
88 }
89
90 if (*pattern == '?') {
91 if (*string == 0)
92 return false;
93 return patmatch(pattern+1, string+1);
94 }
95
96 if (*pattern == '*') {
97 while (*string) {
98 if (patmatch(pattern+1, string++))
99 return true;
100 }
101 return pattern[1] == 0;
102 }
103
104 if (*pattern == '[') {
105 bool found_match = false;
106 bool inverted_list = pattern[1] == '!';
107 const char *p = pattern + (inverted_list ? 1 : 0);
108
109 while (*++p) {
110 if (*p == ']') {
111 if (found_match != inverted_list && patmatch(p+1, string+1))
112 return true;
113 break;
114 }
115
116 if (*p == '\\') {
117 if (*++p == *string)
118 found_match = true;
119 } else
120 if (*p == *string)
121 found_match = true;
122 }
123 }
124
125 if (*pattern == *string)
126 return patmatch(pattern+1, string+1);
127
128 return false;
129 }
130
131 int GetSize(RTLIL::Wire *wire)
132 {
133 return wire->width;
134 }
135
136 void yosys_setup()
137 {
138 Pass::init_register();
139 yosys_design = new RTLIL::Design;
140 log_push();
141 }
142
143 void yosys_shutdown()
144 {
145 log_pop();
146
147 delete yosys_design;
148 yosys_design = NULL;
149
150 for (auto f : log_files)
151 if (f != stderr)
152 fclose(f);
153 log_errfile = NULL;
154 log_files.clear();
155
156 Pass::done_register();
157
158 #ifdef YOSYS_ENABLE_TCL
159 if (yosys_tcl_interp != NULL) {
160 Tcl_DeleteInterp(yosys_tcl_interp);
161 Tcl_Finalize();
162 yosys_tcl_interp = NULL;
163 }
164 #endif
165
166 for (auto &it : loaded_plugins)
167 dlclose(it.second);
168
169 loaded_plugins.clear();
170 loaded_plugin_aliases.clear();
171 }
172
173 RTLIL::IdString new_id(std::string file, int line, std::string func)
174 {
175 std::string str = "$auto$";
176 size_t pos = file.find_last_of('/');
177 str += pos != std::string::npos ? file.substr(pos+1) : file;
178 str += stringf(":%d:%s$%d", line, func.c_str(), autoidx++);
179 return str;
180 }
181
182 RTLIL::Design *yosys_get_design()
183 {
184 return yosys_design;
185 }
186
187 const char *create_prompt(RTLIL::Design *design, int recursion_counter)
188 {
189 static char buffer[100];
190 std::string str = "\n";
191 if (recursion_counter > 1)
192 str += stringf("(%d) ", recursion_counter);
193 str += "yosys";
194 if (!design->selected_active_module.empty())
195 str += stringf(" [%s]", RTLIL::unescape_id(design->selected_active_module).c_str());
196 if (!design->selection_stack.empty() && !design->selection_stack.back().full_selection) {
197 if (design->selected_active_module.empty())
198 str += "*";
199 else if (design->selection_stack.back().selected_modules.size() != 1 || design->selection_stack.back().selected_members.size() != 0 ||
200 design->selection_stack.back().selected_modules.count(design->selected_active_module) == 0)
201 str += "*";
202 }
203 snprintf(buffer, 100, "%s> ", str.c_str());
204 return buffer;
205 }
206
207 #ifdef YOSYS_ENABLE_TCL
208 static int tcl_yosys_cmd(ClientData, Tcl_Interp *interp, int argc, const char *argv[])
209 {
210 std::vector<std::string> args;
211 for (int i = 1; i < argc; i++)
212 args.push_back(argv[i]);
213
214 if (args.size() >= 1 && args[0] == "-import") {
215 for (auto &it : pass_register) {
216 std::string tcl_command_name = it.first;
217 if (tcl_command_name == "proc")
218 tcl_command_name = "procs";
219 Tcl_CmdInfo info;
220 if (Tcl_GetCommandInfo(interp, tcl_command_name.c_str(), &info) != 0) {
221 log("[TCL: yosys -import] Command name collision: found pre-existing command `%s' -> skip.\n", it.first.c_str());
222 } else {
223 std::string tcl_script = stringf("proc %s args { yosys %s {*}$args }", tcl_command_name.c_str(), it.first.c_str());
224 Tcl_Eval(interp, tcl_script.c_str());
225 }
226 }
227 return TCL_OK;
228 }
229
230 if (args.size() == 1) {
231 Pass::call(yosys_get_design(), args[0]);
232 return TCL_OK;
233 }
234
235 Pass::call(yosys_get_design(), args);
236 return TCL_OK;
237 }
238
239 extern Tcl_Interp *yosys_get_tcl_interp()
240 {
241 if (yosys_tcl_interp == NULL) {
242 yosys_tcl_interp = Tcl_CreateInterp();
243 Tcl_CreateCommand(yosys_tcl_interp, "yosys", tcl_yosys_cmd, NULL, NULL);
244 }
245 return yosys_tcl_interp;
246 }
247
248 struct TclPass : public Pass {
249 TclPass() : Pass("tcl", "execute a TCL script file") { }
250 virtual void help() {
251 log("\n");
252 log(" tcl <filename>\n");
253 log("\n");
254 log("This command executes the tcl commands in the specified file.\n");
255 log("Use 'yosys cmd' to run the yosys command 'cmd' from tcl.\n");
256 log("\n");
257 log("The tcl command 'yosys -import' can be used to import all yosys\n");
258 log("commands directly as tcl commands to the tcl shell. The yosys\n");
259 log("command 'proc' is wrapped using the tcl command 'procs' in order\n");
260 log("to avoid a name collision with the tcl builting command 'proc'.\n");
261 log("\n");
262 }
263 virtual void execute(std::vector<std::string> args, RTLIL::Design *design) {
264 if (args.size() < 2)
265 log_cmd_error("Missing script file.\n");
266 if (args.size() > 2)
267 extra_args(args, 1, design, false);
268 if (Tcl_EvalFile(yosys_get_tcl_interp(), args[1].c_str()) != TCL_OK)
269 log_cmd_error("TCL interpreter returned an error: %s\n", Tcl_GetStringResult(yosys_get_tcl_interp()));
270 }
271 } TclPass;
272 #endif
273
274 #if defined(__linux__)
275 std::string proc_self_dirname ()
276 {
277 char path [PATH_MAX];
278 ssize_t buflen = readlink("/proc/self/exe", path, sizeof(path));
279 if (buflen < 0) {
280 log_error("readlink(\"/proc/self/exe\") failed: %s\n", strerror(errno));
281 }
282 while (buflen > 0 && path[buflen-1] != '/')
283 buflen--;
284 return std::string(path, buflen);
285 }
286 #elif defined(__APPLE__)
287 #include <mach-o/dyld.h>
288 std::string proc_self_dirname ()
289 {
290 char * path = NULL;
291 uint32_t buflen = 0;
292 while (_NSGetExecutablePath(path, &buflen) != 0)
293 path = (char *) realloc((void *) path, buflen);
294 while (buflen > 0 && path[buflen-1] != '/')
295 buflen--;
296 return std::string(path, buflen);
297 }
298 #elif defined(EMSCRIPTEN)
299 std::string proc_self_dirname ()
300 {
301 return "/";
302 }
303 #else
304 #error Dont know how to determine process executable base path!
305 #endif
306
307 std::string proc_share_dirname ()
308 {
309 std::string proc_self_path = proc_self_dirname();
310 std::string proc_share_path = proc_self_path + "share/";
311 if (access(proc_share_path.c_str(), X_OK) == 0)
312 return proc_share_path;
313 proc_share_path = proc_self_path + "../share/yosys/";
314 if (access(proc_share_path.c_str(), X_OK) == 0)
315 return proc_share_path;
316 log_error("proc_share_dirname: unable to determine share/ directory!\n");
317 }
318
319 bool fgetline(FILE *f, std::string &buffer)
320 {
321 buffer = "";
322 char block[4096];
323 while (1) {
324 if (fgets(block, 4096, f) == NULL)
325 return false;
326 buffer += block;
327 if (buffer.size() > 0 && (buffer[buffer.size()-1] == '\n' || buffer[buffer.size()-1] == '\r')) {
328 while (buffer.size() > 0 && (buffer[buffer.size()-1] == '\n' || buffer[buffer.size()-1] == '\r'))
329 buffer.resize(buffer.size()-1);
330 return true;
331 }
332 }
333 }
334
335 static void handle_label(std::string &command, bool &from_to_active, const std::string &run_from, const std::string &run_to)
336 {
337 int pos = 0;
338 std::string label;
339
340 while (pos < GetSize(command) && (command[pos] == ' ' || command[pos] == '\t'))
341 pos++;
342
343 while (pos < GetSize(command) && command[pos] != ' ' && command[pos] != '\t' && command[pos] != '\r' && command[pos] != '\n')
344 label += command[pos++];
345
346 if (label.back() == ':' && GetSize(label) > 1)
347 {
348 label = label.substr(0, GetSize(label)-1);
349 command = command.substr(pos);
350
351 if (label == run_from)
352 from_to_active = true;
353 else if (label == run_to || (run_from == run_to && !run_from.empty()))
354 from_to_active = false;
355 }
356 }
357
358 void run_frontend(std::string filename, std::string command, RTLIL::Design *design, std::string *backend_command, std::string *from_to_label)
359 {
360 if (command == "auto") {
361 if (filename.size() > 2 && filename.substr(filename.size()-2) == ".v")
362 command = "verilog";
363 else if (filename.size() > 2 && filename.substr(filename.size()-3) == ".sv")
364 command = "verilog -sv";
365 else if (filename.size() > 3 && filename.substr(filename.size()-3) == ".il")
366 command = "ilang";
367 else if (filename.size() > 3 && filename.substr(filename.size()-3) == ".ys")
368 command = "script";
369 else if (filename == "-")
370 command = "script";
371 else
372 log_error("Can't guess frontend for input file `%s' (missing -f option)!\n", filename.c_str());
373 }
374
375 if (command == "script")
376 {
377 std::string run_from, run_to;
378 bool from_to_active = true;
379
380 if (from_to_label != NULL) {
381 size_t pos = from_to_label->find(':');
382 if (pos == std::string::npos) {
383 run_from = *from_to_label;
384 run_to = *from_to_label;
385 } else {
386 run_from = from_to_label->substr(0, pos);
387 run_to = from_to_label->substr(pos+1);
388 }
389 from_to_active = run_from.empty();
390 }
391
392 log("\n-- Executing script file `%s' --\n", filename.c_str());
393
394 FILE *f = stdin;
395
396 if (filename != "-")
397 f = fopen(filename.c_str(), "r");
398
399 if (f == NULL)
400 log_error("Can't open script file `%s' for reading: %s\n", filename.c_str(), strerror(errno));
401
402 FILE *backup_script_file = Frontend::current_script_file;
403 Frontend::current_script_file = f;
404
405 try {
406 std::string command;
407 while (fgetline(f, command)) {
408 while (!command.empty() && command[command.size()-1] == '\\') {
409 std::string next_line;
410 if (!fgetline(f, next_line))
411 break;
412 command.resize(command.size()-1);
413 command += next_line;
414 }
415 handle_label(command, from_to_active, run_from, run_to);
416 if (from_to_active)
417 Pass::call(design, command);
418 }
419
420 if (!command.empty()) {
421 handle_label(command, from_to_active, run_from, run_to);
422 if (from_to_active)
423 Pass::call(design, command);
424 }
425 }
426 catch (log_cmd_error_expection) {
427 Frontend::current_script_file = backup_script_file;
428 throw log_cmd_error_expection();
429 }
430
431 Frontend::current_script_file = backup_script_file;
432
433 if (filename != "-")
434 fclose(f);
435
436 if (backend_command != NULL && *backend_command == "auto")
437 *backend_command = "";
438
439 return;
440 }
441
442 if (filename == "-") {
443 log("\n-- Parsing stdin using frontend `%s' --\n", command.c_str());
444 } else {
445 log("\n-- Parsing `%s' using frontend `%s' --\n", filename.c_str(), command.c_str());
446 }
447
448 Frontend::frontend_call(design, NULL, filename, command);
449 }
450
451 void run_pass(std::string command, RTLIL::Design *design)
452 {
453 log("\n-- Running pass `%s' --\n", command.c_str());
454
455 Pass::call(design, command);
456 }
457
458 void run_backend(std::string filename, std::string command, RTLIL::Design *design)
459 {
460 if (command == "auto") {
461 if (filename.size() > 2 && filename.substr(filename.size()-2) == ".v")
462 command = "verilog";
463 else if (filename.size() > 3 && filename.substr(filename.size()-3) == ".il")
464 command = "ilang";
465 else if (filename.size() > 5 && filename.substr(filename.size()-5) == ".blif")
466 command = "blif";
467 else if (filename == "-")
468 command = "ilang";
469 else if (filename.empty())
470 return;
471 else
472 log_error("Can't guess backend for output file `%s' (missing -b option)!\n", filename.c_str());
473 }
474
475 if (filename.empty())
476 filename = "-";
477
478 if (filename == "-") {
479 log("\n-- Writing to stdout using backend `%s' --\n", command.c_str());
480 } else {
481 log("\n-- Writing to `%s' using backend `%s' --\n", filename.c_str(), command.c_str());
482 }
483
484 Backend::backend_call(design, NULL, filename, command);
485 }
486
487 #ifdef YOSYS_ENABLE_READLINE
488 static char *readline_cmd_generator(const char *text, int state)
489 {
490 static std::map<std::string, Pass*>::iterator it;
491 static int len;
492
493 if (!state) {
494 it = pass_register.begin();
495 len = strlen(text);
496 }
497
498 for (; it != pass_register.end(); it++) {
499 if (it->first.substr(0, len) == text)
500 return strdup((it++)->first.c_str());
501 }
502 return NULL;
503 }
504
505 static char *readline_obj_generator(const char *text, int state)
506 {
507 static std::vector<char*> obj_names;
508 static size_t idx;
509
510 if (!state)
511 {
512 idx = 0;
513 obj_names.clear();
514
515 RTLIL::Design *design = yosys_get_design();
516 int len = strlen(text);
517
518 if (design->selected_active_module.empty())
519 {
520 for (auto &it : design->modules_)
521 if (RTLIL::unescape_id(it.first).substr(0, len) == text)
522 obj_names.push_back(strdup(RTLIL::id2cstr(it.first)));
523 }
524 else
525 if (design->modules_.count(design->selected_active_module) > 0)
526 {
527 RTLIL::Module *module = design->modules_.at(design->selected_active_module);
528
529 for (auto &it : module->wires_)
530 if (RTLIL::unescape_id(it.first).substr(0, len) == text)
531 obj_names.push_back(strdup(RTLIL::id2cstr(it.first)));
532
533 for (auto &it : module->memories)
534 if (RTLIL::unescape_id(it.first).substr(0, len) == text)
535 obj_names.push_back(strdup(RTLIL::id2cstr(it.first)));
536
537 for (auto &it : module->cells_)
538 if (RTLIL::unescape_id(it.first).substr(0, len) == text)
539 obj_names.push_back(strdup(RTLIL::id2cstr(it.first)));
540
541 for (auto &it : module->processes)
542 if (RTLIL::unescape_id(it.first).substr(0, len) == text)
543 obj_names.push_back(strdup(RTLIL::id2cstr(it.first)));
544 }
545
546 std::sort(obj_names.begin(), obj_names.end());
547 }
548
549 if (idx < obj_names.size())
550 return strdup(obj_names[idx++]);
551
552 idx = 0;
553 obj_names.clear();
554 return NULL;
555 }
556
557 static char **readline_completion(const char *text, int start, int)
558 {
559 if (start == 0)
560 return rl_completion_matches(text, readline_cmd_generator);
561 if (strncmp(rl_line_buffer, "read_", 5) && strncmp(rl_line_buffer, "write_", 6))
562 return rl_completion_matches(text, readline_obj_generator);
563 return NULL;
564 }
565 #endif
566
567 void shell(RTLIL::Design *design)
568 {
569 static int recursion_counter = 0;
570
571 recursion_counter++;
572 log_cmd_error_throw = true;
573
574 #ifdef YOSYS_ENABLE_READLINE
575 rl_readline_name = "yosys";
576 rl_attempted_completion_function = readline_completion;
577 rl_basic_word_break_characters = " \t\n";
578 #endif
579
580 char *command = NULL;
581 #ifdef YOSYS_ENABLE_READLINE
582 while ((command = readline(create_prompt(design, recursion_counter))) != NULL)
583 #else
584 char command_buffer[4096];
585 while ((command = fgets(command_buffer, 4096, stdin)) != NULL)
586 #endif
587 {
588 if (command[strspn(command, " \t\r\n")] == 0)
589 continue;
590 #ifdef YOSYS_ENABLE_READLINE
591 add_history(command);
592 #endif
593
594 char *p = command + strspn(command, " \t\r\n");
595 if (!strncmp(p, "exit", 4)) {
596 p += 4;
597 p += strspn(p, " \t\r\n");
598 if (*p == 0)
599 break;
600 }
601
602 try {
603 log_assert(design->selection_stack.size() == 1);
604 Pass::call(design, command);
605 } catch (log_cmd_error_expection) {
606 while (design->selection_stack.size() > 1)
607 design->selection_stack.pop_back();
608 log_reset_stack();
609 }
610 }
611 if (command == NULL)
612 printf("exit\n");
613
614 recursion_counter--;
615 log_cmd_error_throw = false;
616 }
617
618 struct ShellPass : public Pass {
619 ShellPass() : Pass("shell", "enter interactive command mode") { }
620 virtual void help() {
621 log("\n");
622 log(" shell\n");
623 log("\n");
624 log("This command enters the interactive command mode. This can be useful\n");
625 log("in a script to interrupt the script at a certain point and allow for\n");
626 log("interactive inspection or manual synthesis of the design at this point.\n");
627 log("\n");
628 log("The command prompt of the interactive shell indicates the current\n");
629 log("selection (see 'help select'):\n");
630 log("\n");
631 log(" yosys>\n");
632 log(" the entire design is selected\n");
633 log("\n");
634 log(" yosys*>\n");
635 log(" only part of the design is selected\n");
636 log("\n");
637 log(" yosys [modname]>\n");
638 log(" the entire module 'modname' is selected using 'select -module modname'\n");
639 log("\n");
640 log(" yosys [modname]*>\n");
641 log(" only part of current module 'modname' is selected\n");
642 log("\n");
643 log("When in interactive shell, some errors (e.g. invalid command arguments)\n");
644 log("do not terminate yosys but return to the command prompt.\n");
645 log("\n");
646 log("This command is the default action if nothing else has been specified\n");
647 log("on the command line.\n");
648 log("\n");
649 log("Press Ctrl-D or type 'exit' to leave the interactive shell.\n");
650 log("\n");
651 }
652 virtual void execute(std::vector<std::string> args, RTLIL::Design *design) {
653 extra_args(args, 1, design, false);
654 shell(design);
655 }
656 } ShellPass;
657
658 #ifdef YOSYS_ENABLE_READLINE
659 struct HistoryPass : public Pass {
660 HistoryPass() : Pass("history", "show last interactive commands") { }
661 virtual void help() {
662 log("\n");
663 log(" history\n");
664 log("\n");
665 log("This command prints all commands in the shell history buffer. This are\n");
666 log("all commands executed in an interactive session, but not the commands\n");
667 log("from executed scripts.\n");
668 log("\n");
669 }
670 virtual void execute(std::vector<std::string> args, RTLIL::Design *design) {
671 extra_args(args, 1, design, false);
672 for(HIST_ENTRY **list = history_list(); *list != NULL; list++)
673 log("%s\n", (*list)->line);
674 }
675 } HistoryPass;
676 #endif
677
678 struct ScriptPass : public Pass {
679 ScriptPass() : Pass("script", "execute commands from script file") { }
680 virtual void help() {
681 log("\n");
682 log(" script <filename> [<from_label>:<to_label>]\n");
683 log("\n");
684 log("This command executes the yosys commands in the specified file.\n");
685 log("\n");
686 log("The 2nd argument can be used to only execute the section of the\n");
687 log("file between the specified labels. An empty from label is synonymous\n");
688 log("for the beginning of the file and an empty to label is synonymous\n");
689 log("for the end of the file.\n");
690 log("\n");
691 log("If only one label is specified (without ':') then only the block\n");
692 log("marked with that label (until the next label) is executed.\n");
693 log("\n");
694 }
695 virtual void execute(std::vector<std::string> args, RTLIL::Design *design) {
696 if (args.size() < 2)
697 log_cmd_error("Missing script file.\n");
698 else if (args.size() == 2)
699 run_frontend(args[1], "script", design, NULL, NULL);
700 else if (args.size() == 3)
701 run_frontend(args[1], "script", design, NULL, &args[2]);
702 else
703 extra_args(args, 2, design, false);
704 }
705 } ScriptPass;
706
707 YOSYS_NAMESPACE_END
708