aboutsummaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'kernel')
-rw-r--r--kernel/driver.cc4
-rw-r--r--kernel/log.cc20
-rw-r--r--kernel/log.h60
-rw-r--r--kernel/modtools.h4
-rw-r--r--kernel/register.cc109
-rw-r--r--kernel/yosys.cc102
-rw-r--r--kernel/yosys.h3
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, "&lt;");
- pos += 4;
- break;
- case '>':
- html.replace(pos, 1, "&gt;");
- pos += 4;
- break;
- case '&':
- html.replace(pos, 1, "&amp;");
- 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);