Fixed various VS warnings
[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 #ifdef YOSYS_ENABLE_PLUGINS
28 # include <dlfcn.h>
29 #endif
30
31 #ifdef _WIN32
32 # include <windows.h>
33 # include <io.h>
34 #elif defined(__APPLE__)
35 # include <mach-o/dyld.h>
36 #else
37 # include <unistd.h>
38 # include <dirent.h>
39 # include <sys/types.h>
40 # include <sys/stat.h>
41 #endif
42
43 #include <limits.h>
44 #include <errno.h>
45
46 YOSYS_NAMESPACE_BEGIN
47
48 #if defined(_WIN32) && !defined(__MINGW32__)
49 const char *yosys_version_str = "Yosys for Windows (Version Information Unavailable)";
50 #endif
51
52 int autoidx = 1;
53 RTLIL::Design *yosys_design = NULL;
54
55 #ifdef YOSYS_ENABLE_TCL
56 Tcl_Interp *yosys_tcl_interp = NULL;
57 #endif
58
59 std::string stringf(const char *fmt, ...)
60 {
61 std::string string;
62 va_list ap;
63
64 va_start(ap, fmt);
65 string = vstringf(fmt, ap);
66 va_end(ap);
67
68 return string;
69 }
70
71 std::string vstringf(const char *fmt, va_list ap)
72 {
73 std::string string;
74 char *str = NULL;
75
76 #ifdef _WIN32
77 int sz = 64, rc;
78 while (1) {
79 va_list apc;
80 va_copy(apc, ap);
81 str = (char*)realloc(str, sz);
82 rc = vsnprintf(str, sz, fmt, apc);
83 va_end(apc);
84 if (rc >= 0 && rc < sz)
85 break;
86 sz *= 2;
87 }
88 #else
89 if (vasprintf(&str, fmt, ap) < 0)
90 str = NULL;
91 #endif
92
93 if (str != NULL) {
94 string = str;
95 free(str);
96 }
97
98 return string;
99 }
100
101 std::string next_token(std::string &text, const char *sep)
102 {
103 size_t pos_begin = text.find_first_not_of(sep);
104
105 if (pos_begin == std::string::npos)
106 pos_begin = text.size();
107
108 size_t pos_end = text.find_first_of(sep, pos_begin);
109
110 if (pos_end == std::string::npos)
111 pos_end = text.size();
112
113 std::string token = text.substr(pos_begin, pos_end-pos_begin);
114 text = text.substr(pos_end);
115 return token;
116 }
117
118 // this is very similar to fnmatch(). the exact rules used by this
119 // function are:
120 //
121 // ? matches any character except
122 // * matches any sequence of characters
123 // [...] matches any of the characters in the list
124 // [!..] matches any of the characters not in the list
125 //
126 // a backslash may be used to escape the next characters in the
127 // pattern. each special character can also simply match itself.
128 //
129 bool patmatch(const char *pattern, const char *string)
130 {
131 if (*pattern == 0)
132 return *string == 0;
133
134 if (*pattern == '\\') {
135 if (pattern[1] == string[0] && patmatch(pattern+2, string+1))
136 return true;
137 }
138
139 if (*pattern == '?') {
140 if (*string == 0)
141 return false;
142 return patmatch(pattern+1, string+1);
143 }
144
145 if (*pattern == '*') {
146 while (*string) {
147 if (patmatch(pattern+1, string++))
148 return true;
149 }
150 return pattern[1] == 0;
151 }
152
153 if (*pattern == '[') {
154 bool found_match = false;
155 bool inverted_list = pattern[1] == '!';
156 const char *p = pattern + (inverted_list ? 1 : 0);
157
158 while (*++p) {
159 if (*p == ']') {
160 if (found_match != inverted_list && patmatch(p+1, string+1))
161 return true;
162 break;
163 }
164
165 if (*p == '\\') {
166 if (*++p == *string)
167 found_match = true;
168 } else
169 if (*p == *string)
170 found_match = true;
171 }
172 }
173
174 if (*pattern == *string)
175 return patmatch(pattern+1, string+1);
176
177 return false;
178 }
179
180 int run_command(const std::string &command, std::function<void(const std::string&)> process_line)
181 {
182 if (!process_line)
183 return system(command.c_str());
184
185 FILE *f = popen(command.c_str(), "r");
186 if (f == nullptr)
187 return -1;
188
189 std::string line;
190 char logbuf[128];
191 while (fgets(logbuf, 128, f) != NULL) {
192 line += logbuf;
193 if (!line.empty() && line.back() == '\n')
194 process_line(line), line.clear();
195 }
196 if (!line.empty())
197 process_line(line);
198
199 int ret = pclose(f);
200 if (ret < 0)
201 return -1;
202 #ifdef _WIN32
203 return ret;
204 #else
205 return WEXITSTATUS(ret);
206 #endif
207 }
208
209 std::string make_temp_file(std::string template_str)
210 {
211 #ifdef _WIN32
212 if (template_str.rfind("/tmp/", 0) == 0) {
213 # ifdef __MINGW32__
214 char longpath[MAX_PATH + 1];
215 char shortpath[MAX_PATH + 1];
216 # else
217 WCHAR longpath[MAX_PATH + 1];
218 TCHAR shortpath[MAX_PATH + 1];
219 # endif
220 if (!GetTempPath(MAX_PATH+1, longpath))
221 log_error("GetTempPath() failed.\n");
222 if (!GetShortPathName(longpath, shortpath, MAX_PATH + 1))
223 log_error("GetShortPathName() failed.\n");
224 log_assert(sizeof(TCHAR) == sizeof(char));
225 template_str = stringf("%s\\%s", shortpath, template_str.c_str() + 5);
226 }
227
228 size_t pos = template_str.rfind("XXXXXX");
229 log_assert(pos != std::string::npos);
230
231 while (1) {
232 for (int i = 0; i < 6; i++) {
233 static std::string y = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
234 static uint32_t x = 314159265 ^ uint32_t(time(NULL));
235 x ^= x << 13, x ^= x >> 17, x ^= x << 5;
236 template_str[pos+i] = y[x % y.size()];
237 }
238 if (_access(template_str.c_str(), 0) != 0)
239 break;
240 }
241 #else
242 size_t pos = template_str.rfind("XXXXXX");
243 log_assert(pos != std::string::npos);
244
245 int suffixlen = GetSize(template_str) - pos - 6;
246
247 char *p = strdup(template_str.c_str());
248 close(mkstemps(p, suffixlen));
249 template_str = p;
250 free(p);
251 #endif
252
253 return template_str;
254 }
255
256 std::string make_temp_dir(std::string template_str)
257 {
258 #ifdef _WIN32
259 template_str = make_temp_file(template_str);
260 mkdir(template_str.c_str());
261 return template_str;
262 #else
263 size_t pos = template_str.rfind("XXXXXX");
264 log_assert(pos != std::string::npos);
265
266 int suffixlen = GetSize(template_str) - pos - 6;
267 log_assert(suffixlen == 0);
268
269 char *p = strdup(template_str.c_str());
270 p = mkdtemp(p);
271 log_assert(p != NULL);
272 template_str = p;
273 free(p);
274
275 return template_str;
276 #endif
277 }
278
279 #ifdef _WIN32
280 bool check_file_exists(std::string filename, bool)
281 {
282 return _access(filename.c_str(), 0) == 0;
283 }
284 #else
285 bool check_file_exists(std::string filename, bool is_exec)
286 {
287 return access(filename.c_str(), is_exec ? X_OK : F_OK) == 0;
288 }
289 #endif
290
291 void remove_directory(std::string dirname)
292 {
293 #ifdef _WIN32
294 run_command(stringf("rmdir /s /q \"%s\"", dirname.c_str()));
295 #else
296 struct stat stbuf;
297 struct dirent **namelist;
298 int n = scandir(dirname.c_str(), &namelist, nullptr, alphasort);
299 log_assert(n >= 0);
300 for (int i = 0; i < n; i++) {
301 if (strcmp(namelist[i]->d_name, ".") && strcmp(namelist[i]->d_name, "..")) {
302 std::string buffer = stringf("%s/%s", dirname.c_str(), namelist[i]->d_name);
303 if (!stat(buffer.c_str(), &stbuf) && S_ISREG(stbuf.st_mode)) {
304 log("Removing `%s'.\n", buffer.c_str());
305 remove(buffer.c_str());
306 } else
307 remove_directory(buffer);
308 }
309 free(namelist[i]);
310 }
311 free(namelist);
312 log("Removing `%s'.\n", dirname.c_str());
313 rmdir(dirname.c_str());
314 #endif
315 }
316
317 int GetSize(RTLIL::Wire *wire)
318 {
319 return wire->width;
320 }
321
322 void yosys_setup()
323 {
324 Pass::init_register();
325 yosys_design = new RTLIL::Design;
326 log_push();
327 }
328
329 void yosys_shutdown()
330 {
331 log_pop();
332
333 delete yosys_design;
334 yosys_design = NULL;
335
336 for (auto f : log_files)
337 if (f != stderr)
338 fclose(f);
339 log_errfile = NULL;
340 log_files.clear();
341
342 Pass::done_register();
343
344 #ifdef YOSYS_ENABLE_TCL
345 if (yosys_tcl_interp != NULL) {
346 Tcl_DeleteInterp(yosys_tcl_interp);
347 Tcl_Finalize();
348 yosys_tcl_interp = NULL;
349 }
350 #endif
351
352 #ifdef YOSYS_ENABLE_PLUGINS
353 for (auto &it : loaded_plugins)
354 dlclose(it.second);
355
356 loaded_plugins.clear();
357 loaded_plugin_aliases.clear();
358 #endif
359 }
360
361 RTLIL::IdString new_id(std::string file, int line, std::string func)
362 {
363 std::string str = "$auto$";
364 size_t pos = file.find_last_of('/');
365 str += pos != std::string::npos ? file.substr(pos+1) : file;
366 str += stringf(":%d:%s$%d", line, func.c_str(), autoidx++);
367 return str;
368 }
369
370 RTLIL::Design *yosys_get_design()
371 {
372 return yosys_design;
373 }
374
375 const char *create_prompt(RTLIL::Design *design, int recursion_counter)
376 {
377 static char buffer[100];
378 std::string str = "\n";
379 if (recursion_counter > 1)
380 str += stringf("(%d) ", recursion_counter);
381 str += "yosys";
382 if (!design->selected_active_module.empty())
383 str += stringf(" [%s]", RTLIL::unescape_id(design->selected_active_module).c_str());
384 if (!design->selection_stack.empty() && !design->selection_stack.back().full_selection) {
385 if (design->selected_active_module.empty())
386 str += "*";
387 else if (design->selection_stack.back().selected_modules.size() != 1 || design->selection_stack.back().selected_members.size() != 0 ||
388 design->selection_stack.back().selected_modules.count(design->selected_active_module) == 0)
389 str += "*";
390 }
391 snprintf(buffer, 100, "%s> ", str.c_str());
392 return buffer;
393 }
394
395 #ifdef YOSYS_ENABLE_TCL
396 static int tcl_yosys_cmd(ClientData, Tcl_Interp *interp, int argc, const char *argv[])
397 {
398 std::vector<std::string> args;
399 for (int i = 1; i < argc; i++)
400 args.push_back(argv[i]);
401
402 if (args.size() >= 1 && args[0] == "-import") {
403 for (auto &it : pass_register) {
404 std::string tcl_command_name = it.first;
405 if (tcl_command_name == "proc")
406 tcl_command_name = "procs";
407 Tcl_CmdInfo info;
408 if (Tcl_GetCommandInfo(interp, tcl_command_name.c_str(), &info) != 0) {
409 log("[TCL: yosys -import] Command name collision: found pre-existing command `%s' -> skip.\n", it.first.c_str());
410 } else {
411 std::string tcl_script = stringf("proc %s args { yosys %s {*}$args }", tcl_command_name.c_str(), it.first.c_str());
412 Tcl_Eval(interp, tcl_script.c_str());
413 }
414 }
415 return TCL_OK;
416 }
417
418 if (args.size() == 1) {
419 Pass::call(yosys_get_design(), args[0]);
420 return TCL_OK;
421 }
422
423 Pass::call(yosys_get_design(), args);
424 return TCL_OK;
425 }
426
427 extern Tcl_Interp *yosys_get_tcl_interp()
428 {
429 if (yosys_tcl_interp == NULL) {
430 yosys_tcl_interp = Tcl_CreateInterp();
431 Tcl_CreateCommand(yosys_tcl_interp, "yosys", tcl_yosys_cmd, NULL, NULL);
432 }
433 return yosys_tcl_interp;
434 }
435
436 struct TclPass : public Pass {
437 TclPass() : Pass("tcl", "execute a TCL script file") { }
438 virtual void help() {
439 log("\n");
440 log(" tcl <filename>\n");
441 log("\n");
442 log("This command executes the tcl commands in the specified file.\n");
443 log("Use 'yosys cmd' to run the yosys command 'cmd' from tcl.\n");
444 log("\n");
445 log("The tcl command 'yosys -import' can be used to import all yosys\n");
446 log("commands directly as tcl commands to the tcl shell. The yosys\n");
447 log("command 'proc' is wrapped using the tcl command 'procs' in order\n");
448 log("to avoid a name collision with the tcl builting command 'proc'.\n");
449 log("\n");
450 }
451 virtual void execute(std::vector<std::string> args, RTLIL::Design *design) {
452 if (args.size() < 2)
453 log_cmd_error("Missing script file.\n");
454 if (args.size() > 2)
455 extra_args(args, 1, design, false);
456 if (Tcl_EvalFile(yosys_get_tcl_interp(), args[1].c_str()) != TCL_OK)
457 log_cmd_error("TCL interpreter returned an error: %s\n", Tcl_GetStringResult(yosys_get_tcl_interp()));
458 }
459 } TclPass;
460 #endif
461
462 #if defined(__linux__)
463 std::string proc_self_dirname()
464 {
465 char path[PATH_MAX];
466 ssize_t buflen = readlink("/proc/self/exe", path, sizeof(path));
467 if (buflen < 0) {
468 log_error("readlink(\"/proc/self/exe\") failed: %s\n", strerror(errno));
469 }
470 while (buflen > 0 && path[buflen-1] != '/')
471 buflen--;
472 return std::string(path, buflen);
473 }
474 #elif defined(__APPLE__)
475 std::string proc_self_dirname()
476 {
477 char *path = NULL;
478 uint32_t buflen = 0;
479 while (_NSGetExecutablePath(path, &buflen) != 0)
480 path = (char *) realloc((void *) path, buflen);
481 while (buflen > 0 && path[buflen-1] != '/')
482 buflen--;
483 return std::string(path, buflen);
484 }
485 #elif defined(_WIN32)
486 std::string proc_self_dirname()
487 {
488 int i = 0;
489 # ifdef __MINGW32__
490 char longpath[MAX_PATH + 1];
491 char shortpath[MAX_PATH + 1];
492 # else
493 WCHAR longpath[MAX_PATH + 1];
494 TCHAR shortpath[MAX_PATH + 1];
495 # endif
496 if (!GetModuleFileName(0, longpath, MAX_PATH+1))
497 log_error("GetModuleFileName() failed.\n");
498 if (!GetShortPathName(longpath, shortpath, MAX_PATH+1))
499 log_error("GetShortPathName() failed.\n");
500 while (shortpath[i] != 0)
501 i++;
502 while (i > 0 && shortpath[i-1] != '/' && shortpath[i-1] != '\\')
503 shortpath[--i] = 0;
504 log_assert(sizeof(TCHAR) == sizeof(char));
505 return std::string((char*)shortpath);
506 }
507 #elif defined(EMSCRIPTEN)
508 std::string proc_self_dirname()
509 {
510 return "/";
511 }
512 #else
513 #error Dont know how to determine process executable base path!
514 #endif
515
516 std::string proc_share_dirname()
517 {
518 std::string proc_self_path = proc_self_dirname();
519 std::string proc_share_path = proc_self_path + "share/";
520 if (check_file_exists(proc_share_path, true))
521 return proc_share_path;
522 proc_share_path = proc_self_path + "../share/yosys/";
523 if (check_file_exists(proc_share_path, true))
524 return proc_share_path;
525 log_error("proc_share_dirname: unable to determine share/ directory!\n");
526 }
527
528 bool fgetline(FILE *f, std::string &buffer)
529 {
530 buffer = "";
531 char block[4096];
532 while (1) {
533 if (fgets(block, 4096, f) == NULL)
534 return false;
535 buffer += block;
536 if (buffer.size() > 0 && (buffer[buffer.size()-1] == '\n' || buffer[buffer.size()-1] == '\r')) {
537 while (buffer.size() > 0 && (buffer[buffer.size()-1] == '\n' || buffer[buffer.size()-1] == '\r'))
538 buffer.resize(buffer.size()-1);
539 return true;
540 }
541 }
542 }
543
544 static void handle_label(std::string &command, bool &from_to_active, const std::string &run_from, const std::string &run_to)
545 {
546 int pos = 0;
547 std::string label;
548
549 while (pos < GetSize(command) && (command[pos] == ' ' || command[pos] == '\t'))
550 pos++;
551
552 while (pos < GetSize(command) && command[pos] != ' ' && command[pos] != '\t' && command[pos] != '\r' && command[pos] != '\n')
553 label += command[pos++];
554
555 if (label.back() == ':' && GetSize(label) > 1)
556 {
557 label = label.substr(0, GetSize(label)-1);
558 command = command.substr(pos);
559
560 if (label == run_from)
561 from_to_active = true;
562 else if (label == run_to || (run_from == run_to && !run_from.empty()))
563 from_to_active = false;
564 }
565 }
566
567 void run_frontend(std::string filename, std::string command, RTLIL::Design *design, std::string *backend_command, std::string *from_to_label)
568 {
569 if (command == "auto") {
570 if (filename.size() > 2 && filename.substr(filename.size()-2) == ".v")
571 command = "verilog";
572 else if (filename.size() > 2 && filename.substr(filename.size()-3) == ".sv")
573 command = "verilog -sv";
574 else if (filename.size() > 3 && filename.substr(filename.size()-3) == ".il")
575 command = "ilang";
576 else if (filename.size() > 3 && filename.substr(filename.size()-3) == ".ys")
577 command = "script";
578 else if (filename == "-")
579 command = "script";
580 else
581 log_error("Can't guess frontend for input file `%s' (missing -f option)!\n", filename.c_str());
582 }
583
584 if (command == "script")
585 {
586 std::string run_from, run_to;
587 bool from_to_active = true;
588
589 if (from_to_label != NULL) {
590 size_t pos = from_to_label->find(':');
591 if (pos == std::string::npos) {
592 run_from = *from_to_label;
593 run_to = *from_to_label;
594 } else {
595 run_from = from_to_label->substr(0, pos);
596 run_to = from_to_label->substr(pos+1);
597 }
598 from_to_active = run_from.empty();
599 }
600
601 log("\n-- Executing script file `%s' --\n", filename.c_str());
602
603 FILE *f = stdin;
604
605 if (filename != "-")
606 f = fopen(filename.c_str(), "r");
607
608 if (f == NULL)
609 log_error("Can't open script file `%s' for reading: %s\n", filename.c_str(), strerror(errno));
610
611 FILE *backup_script_file = Frontend::current_script_file;
612 Frontend::current_script_file = f;
613
614 try {
615 std::string command;
616 while (fgetline(f, command)) {
617 while (!command.empty() && command[command.size()-1] == '\\') {
618 std::string next_line;
619 if (!fgetline(f, next_line))
620 break;
621 command.resize(command.size()-1);
622 command += next_line;
623 }
624 handle_label(command, from_to_active, run_from, run_to);
625 if (from_to_active)
626 Pass::call(design, command);
627 }
628
629 if (!command.empty()) {
630 handle_label(command, from_to_active, run_from, run_to);
631 if (from_to_active)
632 Pass::call(design, command);
633 }
634 }
635 catch (log_cmd_error_expection) {
636 Frontend::current_script_file = backup_script_file;
637 throw log_cmd_error_expection();
638 }
639
640 Frontend::current_script_file = backup_script_file;
641
642 if (filename != "-")
643 fclose(f);
644
645 if (backend_command != NULL && *backend_command == "auto")
646 *backend_command = "";
647
648 return;
649 }
650
651 if (filename == "-") {
652 log("\n-- Parsing stdin using frontend `%s' --\n", command.c_str());
653 } else {
654 log("\n-- Parsing `%s' using frontend `%s' --\n", filename.c_str(), command.c_str());
655 }
656
657 Frontend::frontend_call(design, NULL, filename, command);
658 }
659
660 void run_pass(std::string command, RTLIL::Design *design)
661 {
662 log("\n-- Running pass `%s' --\n", command.c_str());
663
664 Pass::call(design, command);
665 }
666
667 void run_backend(std::string filename, std::string command, RTLIL::Design *design)
668 {
669 if (command == "auto") {
670 if (filename.size() > 2 && filename.substr(filename.size()-2) == ".v")
671 command = "verilog";
672 else if (filename.size() > 3 && filename.substr(filename.size()-3) == ".il")
673 command = "ilang";
674 else if (filename.size() > 5 && filename.substr(filename.size()-5) == ".blif")
675 command = "blif";
676 else if (filename == "-")
677 command = "ilang";
678 else if (filename.empty())
679 return;
680 else
681 log_error("Can't guess backend for output file `%s' (missing -b option)!\n", filename.c_str());
682 }
683
684 if (filename.empty())
685 filename = "-";
686
687 if (filename == "-") {
688 log("\n-- Writing to stdout using backend `%s' --\n", command.c_str());
689 } else {
690 log("\n-- Writing to `%s' using backend `%s' --\n", filename.c_str(), command.c_str());
691 }
692
693 Backend::backend_call(design, NULL, filename, command);
694 }
695
696 #ifdef YOSYS_ENABLE_READLINE
697 static char *readline_cmd_generator(const char *text, int state)
698 {
699 static std::map<std::string, Pass*>::iterator it;
700 static int len;
701
702 if (!state) {
703 it = pass_register.begin();
704 len = strlen(text);
705 }
706
707 for (; it != pass_register.end(); it++) {
708 if (it->first.substr(0, len) == text)
709 return strdup((it++)->first.c_str());
710 }
711 return NULL;
712 }
713
714 static char *readline_obj_generator(const char *text, int state)
715 {
716 static std::vector<char*> obj_names;
717 static size_t idx;
718
719 if (!state)
720 {
721 idx = 0;
722 obj_names.clear();
723
724 RTLIL::Design *design = yosys_get_design();
725 int len = strlen(text);
726
727 if (design->selected_active_module.empty())
728 {
729 for (auto &it : design->modules_)
730 if (RTLIL::unescape_id(it.first).substr(0, len) == text)
731 obj_names.push_back(strdup(RTLIL::id2cstr(it.first)));
732 }
733 else
734 if (design->modules_.count(design->selected_active_module) > 0)
735 {
736 RTLIL::Module *module = design->modules_.at(design->selected_active_module);
737
738 for (auto &it : module->wires_)
739 if (RTLIL::unescape_id(it.first).substr(0, len) == text)
740 obj_names.push_back(strdup(RTLIL::id2cstr(it.first)));
741
742 for (auto &it : module->memories)
743 if (RTLIL::unescape_id(it.first).substr(0, len) == text)
744 obj_names.push_back(strdup(RTLIL::id2cstr(it.first)));
745
746 for (auto &it : module->cells_)
747 if (RTLIL::unescape_id(it.first).substr(0, len) == text)
748 obj_names.push_back(strdup(RTLIL::id2cstr(it.first)));
749
750 for (auto &it : module->processes)
751 if (RTLIL::unescape_id(it.first).substr(0, len) == text)
752 obj_names.push_back(strdup(RTLIL::id2cstr(it.first)));
753 }
754
755 std::sort(obj_names.begin(), obj_names.end());
756 }
757
758 if (idx < obj_names.size())
759 return strdup(obj_names[idx++]);
760
761 idx = 0;
762 obj_names.clear();
763 return NULL;
764 }
765
766 static char **readline_completion(const char *text, int start, int)
767 {
768 if (start == 0)
769 return rl_completion_matches(text, readline_cmd_generator);
770 if (strncmp(rl_line_buffer, "read_", 5) && strncmp(rl_line_buffer, "write_", 6))
771 return rl_completion_matches(text, readline_obj_generator);
772 return NULL;
773 }
774 #endif
775
776 void shell(RTLIL::Design *design)
777 {
778 static int recursion_counter = 0;
779
780 recursion_counter++;
781 log_cmd_error_throw = true;
782
783 #ifdef YOSYS_ENABLE_READLINE
784 rl_readline_name = "yosys";
785 rl_attempted_completion_function = readline_completion;
786 rl_basic_word_break_characters = " \t\n";
787 #endif
788
789 char *command = NULL;
790 #ifdef YOSYS_ENABLE_READLINE
791 while ((command = readline(create_prompt(design, recursion_counter))) != NULL)
792 #else
793 char command_buffer[4096];
794 while ((command = fgets(command_buffer, 4096, stdin)) != NULL)
795 #endif
796 {
797 if (command[strspn(command, " \t\r\n")] == 0)
798 continue;
799 #ifdef YOSYS_ENABLE_READLINE
800 add_history(command);
801 #endif
802
803 char *p = command + strspn(command, " \t\r\n");
804 if (!strncmp(p, "exit", 4)) {
805 p += 4;
806 p += strspn(p, " \t\r\n");
807 if (*p == 0)
808 break;
809 }
810
811 try {
812 log_assert(design->selection_stack.size() == 1);
813 Pass::call(design, command);
814 } catch (log_cmd_error_expection) {
815 while (design->selection_stack.size() > 1)
816 design->selection_stack.pop_back();
817 log_reset_stack();
818 }
819 }
820 if (command == NULL)
821 printf("exit\n");
822
823 recursion_counter--;
824 log_cmd_error_throw = false;
825 }
826
827 struct ShellPass : public Pass {
828 ShellPass() : Pass("shell", "enter interactive command mode") { }
829 virtual void help() {
830 log("\n");
831 log(" shell\n");
832 log("\n");
833 log("This command enters the interactive command mode. This can be useful\n");
834 log("in a script to interrupt the script at a certain point and allow for\n");
835 log("interactive inspection or manual synthesis of the design at this point.\n");
836 log("\n");
837 log("The command prompt of the interactive shell indicates the current\n");
838 log("selection (see 'help select'):\n");
839 log("\n");
840 log(" yosys>\n");
841 log(" the entire design is selected\n");
842 log("\n");
843 log(" yosys*>\n");
844 log(" only part of the design is selected\n");
845 log("\n");
846 log(" yosys [modname]>\n");
847 log(" the entire module 'modname' is selected using 'select -module modname'\n");
848 log("\n");
849 log(" yosys [modname]*>\n");
850 log(" only part of current module 'modname' is selected\n");
851 log("\n");
852 log("When in interactive shell, some errors (e.g. invalid command arguments)\n");
853 log("do not terminate yosys but return to the command prompt.\n");
854 log("\n");
855 log("This command is the default action if nothing else has been specified\n");
856 log("on the command line.\n");
857 log("\n");
858 log("Press Ctrl-D or type 'exit' to leave the interactive shell.\n");
859 log("\n");
860 }
861 virtual void execute(std::vector<std::string> args, RTLIL::Design *design) {
862 extra_args(args, 1, design, false);
863 shell(design);
864 }
865 } ShellPass;
866
867 #ifdef YOSYS_ENABLE_READLINE
868 struct HistoryPass : public Pass {
869 HistoryPass() : Pass("history", "show last interactive commands") { }
870 virtual void help() {
871 log("\n");
872 log(" history\n");
873 log("\n");
874 log("This command prints all commands in the shell history buffer. This are\n");
875 log("all commands executed in an interactive session, but not the commands\n");
876 log("from executed scripts.\n");
877 log("\n");
878 }
879 virtual void execute(std::vector<std::string> args, RTLIL::Design *design) {
880 extra_args(args, 1, design, false);
881 for(HIST_ENTRY **list = history_list(); *list != NULL; list++)
882 log("%s\n", (*list)->line);
883 }
884 } HistoryPass;
885 #endif
886
887 struct ScriptPass : public Pass {
888 ScriptPass() : Pass("script", "execute commands from script file") { }
889 virtual void help() {
890 log("\n");
891 log(" script <filename> [<from_label>:<to_label>]\n");
892 log("\n");
893 log("This command executes the yosys commands in the specified file.\n");
894 log("\n");
895 log("The 2nd argument can be used to only execute the section of the\n");
896 log("file between the specified labels. An empty from label is synonymous\n");
897 log("for the beginning of the file and an empty to label is synonymous\n");
898 log("for the end of the file.\n");
899 log("\n");
900 log("If only one label is specified (without ':') then only the block\n");
901 log("marked with that label (until the next label) is executed.\n");
902 log("\n");
903 }
904 virtual void execute(std::vector<std::string> args, RTLIL::Design *design) {
905 if (args.size() < 2)
906 log_cmd_error("Missing script file.\n");
907 else if (args.size() == 2)
908 run_frontend(args[1], "script", design, NULL, NULL);
909 else if (args.size() == 3)
910 run_frontend(args[1], "script", design, NULL, &args[2]);
911 else
912 extra_args(args, 2, design, false);
913 }
914 } ScriptPass;
915
916 YOSYS_NAMESPACE_END
917