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