diff options
-rw-r--r-- | kernel/driver.cc | 2 | ||||
-rw-r--r-- | kernel/log.cc | 6 | ||||
-rw-r--r-- | kernel/log.h | 1 | ||||
-rw-r--r-- | kernel/yosys.cc | 96 | ||||
-rw-r--r-- | passes/cmds/stat.cc | 4 | ||||
-rw-r--r-- | passes/cmds/tee.cc | 13 |
6 files changed, 114 insertions, 8 deletions
diff --git a/kernel/driver.cc b/kernel/driver.cc index aa90802c9..a89c790d9 100644 --- a/kernel/driver.cc +++ b/kernel/driver.cc @@ -205,6 +205,7 @@ extern char yosys_path[PATH_MAX]; #ifdef YOSYS_ENABLE_TCL namespace Yosys { extern int yosys_tcl_iterp_init(Tcl_Interp *interp); + extern void yosys_tcl_activate_repl(); }; #endif @@ -584,6 +585,7 @@ int main(int argc, char **argv) if (run_tcl_shell) { #ifdef YOSYS_ENABLE_TCL + yosys_tcl_activate_repl(); Tcl_Main(argc, argv, yosys_tcl_iterp_init); #else log_error("Can't exectue TCL shell: this version of yosys is not built with TCL support enabled.\n"); diff --git a/kernel/log.cc b/kernel/log.cc index af8c422b8..25d198744 100644 --- a/kernel/log.cc +++ b/kernel/log.cc @@ -40,6 +40,7 @@ YOSYS_NAMESPACE_BEGIN std::vector<FILE*> log_files; std::vector<std::ostream*> log_streams; +std::vector<std::string> log_scratchpads; std::map<std::string, std::set<std::string>> log_hdump; std::vector<YS_REGEX_TYPE> log_warn_regexes, log_nowarn_regexes, log_werror_regexes; dict<std::string, LogExpectedItem> log_expect_log, log_expect_warning, log_expect_error; @@ -158,6 +159,11 @@ void logv(const char *format, va_list ap) for (auto f : log_streams) *f << str; + RTLIL::Design *design = yosys_get_design(); + if (design != nullptr) + for (auto &scratchpad : log_scratchpads) + design->scratchpad[scratchpad].append(str); + static std::string linebuffer; static bool log_warn_regex_recusion_guard = false; diff --git a/kernel/log.h b/kernel/log.h index 822816cb4..35368a683 100644 --- a/kernel/log.h +++ b/kernel/log.h @@ -133,6 +133,7 @@ struct log_cmd_error_exception { }; extern std::vector<FILE*> log_files; extern std::vector<std::ostream*> log_streams; +extern std::vector<std::string> log_scratchpads; extern std::map<std::string, std::set<std::string>> log_hdump; extern std::vector<YS_REGEX_TYPE> log_warn_regexes, log_nowarn_regexes, log_werror_regexes; extern std::set<std::string> log_warnings, log_experimentals, log_experimentals_ignored; diff --git a/kernel/yosys.cc b/kernel/yosys.cc index 01085d29b..333faae6a 100644 --- a/kernel/yosys.cc +++ b/kernel/yosys.cc @@ -73,6 +73,8 @@ #include <limits.h> #include <errno.h> +#include "libs/json11/json11.hpp" + YOSYS_NAMESPACE_BEGIN int autoidx = 1; @@ -82,6 +84,7 @@ CellTypes yosys_celltypes; #ifdef YOSYS_ENABLE_TCL Tcl_Interp *yosys_tcl_interp = NULL; +bool yosys_tcl_repl_active = false; #endif std::set<std::string> yosys_input_files, yosys_output_files; @@ -709,6 +712,42 @@ void rewrite_filename(std::string &filename) } #ifdef YOSYS_ENABLE_TCL + +static Tcl_Obj *json_to_tcl(Tcl_Interp *interp, const json11::Json &json) +{ + if (json.is_null()) + return Tcl_NewStringObj("null", 4); + else if (json.is_string()) { + auto string = json.string_value(); + return Tcl_NewStringObj(string.data(), string.size()); + } else if (json.is_number()) { + double value = json.number_value(); + double round_val = std::nearbyint(value); + if (std::isfinite(round_val) && value == round_val && value >= LONG_MIN && value < -double(LONG_MIN)) + return Tcl_NewLongObj((long)round_val); + else + return Tcl_NewDoubleObj(value); + } else if (json.is_bool()) { + return Tcl_NewBooleanObj(json.bool_value()); + } else if (json.is_array()) { + auto list = json.array_items(); + Tcl_Obj *result = Tcl_NewListObj(list.size(), nullptr); + for (auto &item : list) + Tcl_ListObjAppendElement(interp, result, json_to_tcl(interp, item)); + return result; + } else if (json.is_object()) { + auto map = json.object_items(); + Tcl_Obj *result = Tcl_NewListObj(map.size() * 2, nullptr); + for (auto &item : map) { + Tcl_ListObjAppendElement(interp, result, Tcl_NewStringObj(item.first.data(), item.first.size())); + Tcl_ListObjAppendElement(interp, result, json_to_tcl(interp, item.second)); + } + return result; + } else { + log_abort(); + } +} + static int tcl_yosys_cmd(ClientData, Tcl_Interp *interp, int argc, const char *argv[]) { std::vector<std::string> args; @@ -733,12 +772,52 @@ static int tcl_yosys_cmd(ClientData, Tcl_Interp *interp, int argc, const char *a return TCL_OK; } - if (args.size() == 1) { - Pass::call(yosys_get_design(), args[0]); - return TCL_OK; + yosys_get_design()->scratchpad_unset("result.json"); + yosys_get_design()->scratchpad_unset("result.string"); + + bool in_repl = yosys_tcl_repl_active; + bool restore_log_cmd_error_throw = log_cmd_error_throw; + + log_cmd_error_throw = true; + + try { + if (args.size() == 1) { + Pass::call(yosys_get_design(), args[0]); + } else { + Pass::call(yosys_get_design(), args); + } + } catch (log_cmd_error_exception) { + if (in_repl) { + auto design = yosys_get_design(); + while (design->selection_stack.size() > 1) + design->selection_stack.pop_back(); + log_reset_stack(); + } + Tcl_SetResult(interp, (char *)"Yosys command produced an error", TCL_STATIC); + + yosys_tcl_repl_active = in_repl; + log_cmd_error_throw = restore_log_cmd_error_throw; + return TCL_ERROR; + } catch (...) { + log_error("uncaught exception during Yosys command invoked from TCL\n"); + } + + yosys_tcl_repl_active = in_repl; + log_cmd_error_throw = restore_log_cmd_error_throw; + + auto &scratchpad = yosys_get_design()->scratchpad; + auto result = scratchpad.find("result.json"); + if (result != scratchpad.end()) { + std::string err; + auto json = json11::Json::parse(result->second, err); + if (err.empty()) { + Tcl_SetObjResult(interp, json_to_tcl(interp, json)); + } else + log_warning("Ignoring result.json scratchpad value due to parse error: %s\n", err.c_str()); + } else if ((result = scratchpad.find("result.string")) != scratchpad.end()) { + Tcl_SetObjResult(interp, Tcl_NewStringObj(result->second.data(), result->second.size())); } - Pass::call(yosys_get_design(), args); return TCL_OK; } @@ -750,6 +829,11 @@ int yosys_tcl_iterp_init(Tcl_Interp *interp) return TCL_OK ; } +void yosys_tcl_activate_repl() +{ + yosys_tcl_repl_active = true; +} + extern Tcl_Interp *yosys_get_tcl_interp() { if (yosys_tcl_interp == NULL) { @@ -778,8 +862,8 @@ struct TclPass : public Pass { log("the standard $argc and $argv variables.\n"); log("\n"); log("Note, tcl will not recieve the output of any yosys command. If the output\n"); - log("of the tcl commands are needed, use the yosys command 'tee' to redirect yosys's\n"); - log("output to a temporary file.\n"); + log("of the tcl commands are needed, use the yosys command 'tee -s result.string'\n"); + log("to redirect yosys's output to the 'result.string' scratchpad value.\n"); log("\n"); } void execute(std::vector<std::string> args, RTLIL::Design *) override { diff --git a/passes/cmds/stat.cc b/passes/cmds/stat.cc index a998ab8e7..10e98952e 100644 --- a/passes/cmds/stat.cc +++ b/passes/cmds/stat.cc @@ -452,7 +452,7 @@ struct StatPass : public Pass { if (json_mode) { log("\n"); - log(" },\n"); + log(top_mod == nullptr ? " }\n" : " },\n"); } if (top_mod != nullptr) @@ -466,7 +466,7 @@ struct StatPass : public Pass { statdata_t data = hierarchy_worker(mod_stat, top_mod->name, 0, /*quiet=*/json_mode); - if (json_mode) + if (json_mode) data.log_data_json("design", true); else if (GetSize(mod_stat) > 1) { log("\n"); diff --git a/passes/cmds/tee.cc b/passes/cmds/tee.cc index 7a1f4a36b..39ed4a7a8 100644 --- a/passes/cmds/tee.cc +++ b/passes/cmds/tee.cc @@ -45,6 +45,9 @@ struct TeePass : public Pass { log(" -a logfile\n"); log(" Write output to this file, append if exists.\n"); log("\n"); + log(" -s scratchpad\n"); + log(" Write output to this scratchpad value, truncate if it exists.\n"); + log("\n"); log(" +INT, -INT\n"); log(" Add/subtract INT from the -v setting for this command.\n"); log("\n"); @@ -53,9 +56,11 @@ struct TeePass : public Pass { { std::vector<FILE*> backup_log_files, files_to_close; std::vector<std::ostream*> backup_log_streams; + std::vector<std::string> backup_log_scratchpads; int backup_log_verbose_level = log_verbose_level; backup_log_streams = log_streams; backup_log_files = log_files; + backup_log_scratchpads = log_scratchpads; size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) @@ -78,6 +83,12 @@ struct TeePass : public Pass { files_to_close.push_back(f); continue; } + if (args[argidx] == "-s" && argidx+1 < args.size()) { + auto name = args[++argidx]; + design->scratchpad[name] = ""; + log_scratchpads.push_back(name); + continue; + } if (GetSize(args[argidx]) >= 2 && (args[argidx][0] == '-' || args[argidx][0] == '+') && args[argidx][1] >= '0' && args[argidx][1] <= '9') { log_verbose_level += atoi(args[argidx].c_str()); continue; @@ -93,6 +104,7 @@ struct TeePass : public Pass { fclose(cf); log_files = backup_log_files; log_streams = backup_log_streams; + log_scratchpads = backup_log_scratchpads; throw; } @@ -102,6 +114,7 @@ struct TeePass : public Pass { log_verbose_level = backup_log_verbose_level; log_files = backup_log_files; log_streams = backup_log_streams; + log_scratchpads = backup_log_scratchpads; } } TeePass; |