diff options
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/driver.cc | 4 | ||||
-rw-r--r-- | kernel/log.cc | 20 | ||||
-rw-r--r-- | kernel/log.h | 60 | ||||
-rw-r--r-- | kernel/modtools.h | 4 | ||||
-rw-r--r-- | kernel/register.cc | 109 | ||||
-rw-r--r-- | kernel/yosys.cc | 102 | ||||
-rw-r--r-- | kernel/yosys.h | 3 |
7 files changed, 138 insertions, 164 deletions
diff --git a/kernel/driver.cc b/kernel/driver.cc index aa90802c9..ef8e77924 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 @@ -296,7 +297,7 @@ int main(int argc, char **argv) #endif printf("\n"); printf(" -p command\n"); - printf(" execute the commands\n"); + printf(" execute the commands (to chain commands, separate them with semicolon + whitespace: 'cmd1; cmd2')\n"); printf("\n"); printf(" -m module_file\n"); printf(" load the specified module (aka plugin)\n"); @@ -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..0092871f0 100644 --- a/kernel/log.cc +++ b/kernel/log.cc @@ -40,8 +40,9 @@ 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; +std::vector<std::regex> log_warn_regexes, log_nowarn_regexes, log_werror_regexes; dict<std::string, LogExpectedItem> log_expect_log, log_expect_warning, log_expect_error; std::set<std::string> log_warnings, log_experimentals, log_experimentals_ignored; int log_warnings_count = 0; @@ -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; @@ -175,11 +181,11 @@ void logv(const char *format, va_list ap) if (!linebuffer.empty() && linebuffer.back() == '\n') { for (auto &re : log_warn_regexes) - if (YS_REGEX_NS::regex_search(linebuffer, re)) + if (std::regex_search(linebuffer, re)) log_warning("Found log message matching -W regex:\n%s", str.c_str()); for (auto &item : log_expect_log) - if (YS_REGEX_NS::regex_search(linebuffer, item.second.pattern)) + if (std::regex_search(linebuffer, item.second.pattern)) item.second.current_count++; linebuffer.clear(); @@ -236,7 +242,7 @@ static void logv_warning_with_prefix(const char *prefix, bool suppressed = false; for (auto &re : log_nowarn_regexes) - if (YS_REGEX_NS::regex_search(message, re)) + if (std::regex_search(message, re)) suppressed = true; if (suppressed) @@ -249,12 +255,12 @@ static void logv_warning_with_prefix(const char *prefix, log_make_debug = 0; for (auto &re : log_werror_regexes) - if (YS_REGEX_NS::regex_search(message, re)) + if (std::regex_search(message, re)) log_error("%s", message.c_str()); bool warning_match = false; for (auto &item : log_expect_warning) - if (YS_REGEX_NS::regex_search(message, item.second.pattern)) { + if (std::regex_search(message, item.second.pattern)) { item.second.current_count++; warning_match = true; } @@ -343,7 +349,7 @@ static void logv_error_with_prefix(const char *prefix, log_make_debug = bak_log_make_debug; for (auto &item : log_expect_error) - if (YS_REGEX_NS::regex_search(log_last_error, item.second.pattern)) + if (std::regex_search(log_last_error, item.second.pattern)) item.second.current_count++; log_check_expected(); diff --git a/kernel/log.h b/kernel/log.h index 822816cb4..3a6ec8758 100644 --- a/kernel/log.h +++ b/kernel/log.h @@ -24,51 +24,14 @@ #include <time.h> -// In the libstdc++ headers that are provided by GCC 4.8, std::regex is not -// working correctly. In order to make features using regular expressions -// work, a replacement regex library is used. Just checking for GCC version -// is not enough though, because at least on RHEL7/CentOS7 even when compiling -// with Clang instead of GCC, the GCC 4.8 headers are still used for std::regex. -// We have to check the version of the libstdc++ headers specifically, not the -// compiler version. GCC headers of libstdc++ before version 3.4 define -// __GLIBCPP__, later versions define __GLIBCXX__. GCC 7 and newer additionaly -// define _GLIBCXX_RELEASE with a version number. -// Include limits std C++ header, so we get the version macros defined: -#if defined(__cplusplus) -# include <limits> -#endif -// Check if libstdc++ is from GCC -#if defined(__GLIBCPP__) || defined(__GLIBCXX__) -// Check if version could be 4.8 or lower (this also matches for some 4.9 and -// 5.0 releases). See: -// https://gcc.gnu.org/onlinedocs/libstdc++/manual/abi.html#abi.versioning -# if !defined(_GLIBCXX_RELEASE) && (defined(__GLIBCPP__) || __GLIBCXX__ <= 20150623) -# define YS_HAS_BAD_STD_REGEX -# endif -#endif -#if defined(YS_HAS_BAD_STD_REGEX) - #include <boost/xpressive/xpressive.hpp> - #define YS_REGEX_TYPE boost::xpressive::sregex - #define YS_REGEX_MATCH_TYPE boost::xpressive::smatch - #define YS_REGEX_NS boost::xpressive - #define YS_REGEX_COMPILE(param) boost::xpressive::sregex::compile(param, \ - boost::xpressive::regex_constants::nosubs | \ - boost::xpressive::regex_constants::optimize) - #define YS_REGEX_COMPILE_WITH_SUBS(param) boost::xpressive::sregex::compile(param, \ - boost::xpressive::regex_constants::optimize) -# else - #include <regex> - #define YS_REGEX_TYPE std::regex - #define YS_REGEX_MATCH_TYPE std::smatch - #define YS_REGEX_NS std - #define YS_REGEX_COMPILE(param) std::regex(param, \ - std::regex_constants::nosubs | \ - std::regex_constants::optimize | \ - std::regex_constants::egrep) - #define YS_REGEX_COMPILE_WITH_SUBS(param) std::regex(param, \ - std::regex_constants::optimize | \ - std::regex_constants::egrep) -#endif +#include <regex> +#define YS_REGEX_COMPILE(param) std::regex(param, \ + std::regex_constants::nosubs | \ + std::regex_constants::optimize | \ + std::regex_constants::egrep) +#define YS_REGEX_COMPILE_WITH_SUBS(param) std::regex(param, \ + std::regex_constants::optimize | \ + std::regex_constants::egrep) #if defined(_WIN32) # include <intrin.h> @@ -133,8 +96,9 @@ 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::vector<std::regex> log_warn_regexes, log_nowarn_regexes, log_werror_regexes; extern std::set<std::string> log_warnings, log_experimentals, log_experimentals_ignored; extern int log_warnings_count; extern int log_warnings_count_noexpect; @@ -223,11 +187,11 @@ void log_flush(); struct LogExpectedItem { - LogExpectedItem(const YS_REGEX_TYPE &pat, int expected) : + LogExpectedItem(const std::regex &pat, int expected) : pattern(pat), expected_count(expected), current_count(0) {} LogExpectedItem() : expected_count(0), current_count(0) {} - YS_REGEX_TYPE pattern; + std::regex pattern; int expected_count; int current_count; }; diff --git a/kernel/modtools.h b/kernel/modtools.h index 4cbaf78d0..34a23b379 100644 --- a/kernel/modtools.h +++ b/kernel/modtools.h @@ -409,7 +409,6 @@ struct ModWalker // get_* methods -- single RTLIL::SigBit - template<typename T> inline bool get_drivers(pool<PortBit> &result, RTLIL::SigBit bit) const { bool found = false; @@ -421,7 +420,6 @@ struct ModWalker return found; } - template<typename T> inline bool get_consumers(pool<PortBit> &result, RTLIL::SigBit bit) const { bool found = false; @@ -433,7 +431,6 @@ struct ModWalker return found; } - template<typename T> inline bool get_inputs(pool<RTLIL::SigBit> &result, RTLIL::SigBit bit) const { bool found = false; @@ -442,7 +439,6 @@ struct ModWalker return found; } - template<typename T> inline bool get_outputs(pool<RTLIL::SigBit> &result, RTLIL::SigBit bit) const { bool found = false; diff --git a/kernel/register.cc b/kernel/register.cc index 9449890b1..0e4d503be 100644 --- a/kernel/register.cc +++ b/kernel/register.cc @@ -108,7 +108,9 @@ Pass::Pass(std::string name, std::string short_help) : pass_name(name), short_he void Pass::run_register() { - log_assert(pass_register.count(pass_name) == 0); + if (pass_register.count(pass_name)) + log_error("Unable to register pass '%s', pass already exists!\n", pass_name.c_str()); + pass_register[pass_name] = this; } @@ -445,10 +447,13 @@ Frontend::Frontend(std::string name, std::string short_help) : void Frontend::run_register() { - log_assert(pass_register.count(pass_name) == 0); + if (pass_register.count(pass_name)) + log_error("Unable to register pass '%s', pass already exists!\n", pass_name.c_str()); pass_register[pass_name] = this; - log_assert(frontend_register.count(frontend_name) == 0); + if (frontend_register.count(frontend_name)) + log_error("Unable to register frontend '%s', frontend already exists!\n", frontend_name.c_str()); + frontend_register[frontend_name] = this; } @@ -626,10 +631,12 @@ Backend::Backend(std::string name, std::string short_help) : void Backend::run_register() { - log_assert(pass_register.count(pass_name) == 0); + if (pass_register.count(pass_name)) + log_error("Unable to register pass '%s', pass already exists!\n", pass_name.c_str()); pass_register[pass_name] = this; - log_assert(backend_register.count(backend_name) == 0); + if (backend_register.count(backend_name)) + log_error("Unable to register backend '%s', backend already exists!\n", backend_name.c_str()); backend_register[backend_name] = this; } @@ -765,63 +772,6 @@ struct HelpPass : public Pass { log(" help <celltype>+ .... print verilog code for given cell type\n"); log("\n"); } - void escape_tex(std::string &tex) - { - for (size_t pos = 0; (pos = tex.find('_', pos)) != std::string::npos; pos += 2) - tex.replace(pos, 1, "\\_"); - for (size_t pos = 0; (pos = tex.find('$', pos)) != std::string::npos; pos += 2) - tex.replace(pos, 1, "\\$"); - } - void write_tex(FILE *f, std::string cmd, std::string title, std::string text) - { - size_t begin = text.find_first_not_of("\n"), end = text.find_last_not_of("\n"); - if (begin != std::string::npos && end != std::string::npos && begin < end) - text = text.substr(begin, end-begin+1); - std::string cmd_unescaped = cmd; - escape_tex(cmd); - escape_tex(title); - fprintf(f, "\\section{%s -- %s}\n", cmd.c_str(), title.c_str()); - fprintf(f, "\\label{cmd:%s}\n", cmd_unescaped.c_str()); - fprintf(f, "\\begin{lstlisting}[numbers=left,frame=single]\n"); - fprintf(f, "%s\n\\end{lstlisting}\n\n", text.c_str()); - } - void escape_html(std::string &html) - { - size_t pos = 0; - while ((pos = html.find_first_of("<>&", pos)) != std::string::npos) - switch (html[pos]) { - case '<': - html.replace(pos, 1, "<"); - pos += 4; - break; - case '>': - html.replace(pos, 1, ">"); - pos += 4; - break; - case '&': - html.replace(pos, 1, "&"); - pos += 5; - break; - } - } - void write_html(FILE *idxf, std::string cmd, std::string title, std::string text) - { - FILE *f = fopen(stringf("cmd_%s.in", cmd.c_str()).c_str(), "wt"); - fprintf(idxf, "<li><a href=\"cmd_%s.html\"> ", cmd.c_str()); - - escape_html(cmd); - escape_html(title); - escape_html(text); - - fprintf(idxf, "%s</a> <span>%s</span></a>\n", cmd.c_str(), title.c_str()); - - fprintf(f, "@cmd_header %s@\n", cmd.c_str()); - fprintf(f, "<h1>%s - %s</h1>\n", cmd.c_str(), title.c_str()); - fprintf(f, "<pre>%s</pre>\n", text.c_str()); - fprintf(f, "@footer@\n"); - - fclose(f); - } void write_rst(std::string cmd, std::string title, std::string text) { FILE *f = fopen(stringf("docs/source/cmd/%s.rst", cmd.c_str()).c_str(), "wt"); @@ -958,24 +908,6 @@ struct HelpPass : public Pass { return; } // this option is undocumented as it is for internal use only - else if (args[1] == "-write-tex-command-reference-manual") { - FILE *f = fopen("command-reference-manual.tex", "wt"); - fprintf(f, "%% Generated using the yosys 'help -write-tex-command-reference-manual' command.\n\n"); - for (auto &it : pass_register) { - std::ostringstream buf; - log_streams.push_back(&buf); - it.second->help(); - if (it.second->experimental_flag) { - log("\n"); - log("WARNING: THE '%s' COMMAND IS EXPERIMENTAL.\n", it.first.c_str()); - log("\n"); - } - log_streams.pop_back(); - write_tex(f, it.first, it.second->short_help, buf.str()); - } - fclose(f); - } - // this option is undocumented as it is for internal use only else if (args[1] == "-write-rst-command-reference-manual") { for (auto &it : pass_register) { std::ostringstream buf; @@ -990,23 +922,6 @@ struct HelpPass : public Pass { write_rst(it.first, it.second->short_help, buf.str()); } } - // this option is undocumented as it is for internal use only - else if (args[1] == "-write-web-command-reference-manual") { - FILE *f = fopen("templates/cmd_index.in", "wt"); - for (auto &it : pass_register) { - std::ostringstream buf; - log_streams.push_back(&buf); - it.second->help(); - if (it.second->experimental_flag) { - log("\n"); - log("WARNING: THE '%s' COMMAND IS EXPERIMENTAL.\n", it.first.c_str()); - log("\n"); - } - log_streams.pop_back(); - write_html(f, it.first, it.second->short_help, buf.str()); - } - fclose(f); - } else if (pass_register.count(args[1])) { pass_register.at(args[1])->help(); if (pass_register.at(args[1])->experimental_flag) { diff --git a/kernel/yosys.cc b/kernel/yosys.cc index 4a22d0a7b..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; @@ -590,7 +593,9 @@ void yosys_shutdown() #ifdef YOSYS_ENABLE_TCL if (yosys_tcl_interp != NULL) { - Tcl_DeleteInterp(yosys_tcl_interp); + if (!Tcl_InterpDeleted(yosys_tcl_interp)) { + Tcl_DeleteInterp(yosys_tcl_interp); + } Tcl_Finalize(); yosys_tcl_interp = NULL; } @@ -707,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; @@ -731,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; } @@ -748,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) { @@ -776,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 { @@ -789,11 +875,13 @@ struct TclPass : public Pass { script_args.push_back(Tcl_NewStringObj((*it).c_str(), (*it).size())); Tcl_Interp *interp = yosys_get_tcl_interp(); + Tcl_Preserve(interp); Tcl_ObjSetVar2(interp, Tcl_NewStringObj("argc", 4), NULL, Tcl_NewIntObj(script_args.size()), 0); Tcl_ObjSetVar2(interp, Tcl_NewStringObj("argv", 4), NULL, Tcl_NewListObj(script_args.size(), script_args.data()), 0); Tcl_ObjSetVar2(interp, Tcl_NewStringObj("argv0", 5), NULL, Tcl_NewStringObj(args[1].c_str(), args[1].size()), 0); if (Tcl_EvalFile(interp, args[1].c_str()) != TCL_OK) log_cmd_error("TCL interpreter returned an error: %s\n", Tcl_GetStringResult(interp)); + Tcl_Release(interp); } } TclPass; #endif diff --git a/kernel/yosys.h b/kernel/yosys.h index b5b1553f2..29415ff84 100644 --- a/kernel/yosys.h +++ b/kernel/yosys.h @@ -82,6 +82,9 @@ # ifdef YOSYS_MXE_HACKS extern Tcl_Command Tcl_CreateCommand(Tcl_Interp *interp, const char *cmdName, Tcl_CmdProc *proc, ClientData clientData, Tcl_CmdDeleteProc *deleteProc); extern Tcl_Interp *Tcl_CreateInterp(void); +extern void Tcl_Preserve(ClientData data); +extern void Tcl_Release(ClientData clientData); +extern int Tcl_InterpDeleted(Tcl_Interp *interp); extern void Tcl_DeleteInterp(Tcl_Interp *interp); extern int Tcl_Eval(Tcl_Interp *interp, const char *script); extern int Tcl_EvalFile(Tcl_Interp *interp, const char *fileName); |