Merge pull request #3572 from jix/tcl-recover

tcl: Don't exit repl on recoverable command errors
This commit is contained in:
Miodrag Milanović 2022-12-05 17:00:16 +01:00 committed by GitHub
commit b60baad662
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 114 additions and 8 deletions

View File

@ -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");

View File

@ -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;

View File

@ -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;

View File

@ -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 {

View File

@ -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");

View File

@ -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;