mirror of
https://github.com/YosysHQ/yosys
synced 2025-04-29 11:55:52 +00:00
Merge branch 'master' of github.com:YosysHQ/yosys into claire/eqystuff
This commit is contained in:
commit
6d56d4ecfc
103 changed files with 385 additions and 20236 deletions
|
@ -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");
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue