diff options
author | Jannis Harder <me@jix.one> | 2022-11-28 19:06:59 +0100 |
---|---|---|
committer | Jannis Harder <me@jix.one> | 2022-12-02 14:36:19 +0100 |
commit | 5524d5185d0d10246fb5410025c39e458e1a9abb (patch) | |
tree | cb9b5b513268cb5500bd7e8005b46c630904d5d5 | |
parent | 7036a312bf1fba8ea88c0b1227bf0f84362e5f9a (diff) | |
download | yosys-5524d5185d0d10246fb5410025c39e458e1a9abb.tar.gz yosys-5524d5185d0d10246fb5410025c39e458e1a9abb.tar.bz2 yosys-5524d5185d0d10246fb5410025c39e458e1a9abb.zip |
tcl: Return scratchpad result.json and result.string as tcl objects
This makes it possible for yosys commands to return values when invoked
as tcl commands. Right now no commands natively support this, but the
tee command can be used with json output like this:
```tcl
set stat [yosys tee -q -s result.json stat -json -top top]
dict get $stat modules \\top num_cells_by_type \$pmux
```
Or with newline separated lists like this:
```tcl
split [yosys tee -q -s result.string select -list top] "\n"
```
-rw-r--r-- | kernel/yosys.cc | 59 |
1 files changed, 57 insertions, 2 deletions
diff --git a/kernel/yosys.cc b/kernel/yosys.cc index 01085d29b..c75500997 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; @@ -709,6 +711,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 +771,29 @@ static int tcl_yosys_cmd(ClientData, Tcl_Interp *interp, int argc, const char *a return TCL_OK; } + yosys_get_design()->scratchpad_unset("result.json"); + if (args.size() == 1) { Pass::call(yosys_get_design(), args[0]); - return TCL_OK; + } else { + Pass::call(yosys_get_design(), args); + } + + 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)); + scratchpad.erase(result); + } 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())); + scratchpad.erase(result); } - Pass::call(yosys_get_design(), args); return TCL_OK; } |