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