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