From ccd98d21c1e531b01b21955eefae0219528602c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Wed, 16 Oct 2024 11:43:02 +0200 Subject: [PATCH 1/7] Start Tcl design inspection methods --- kernel/yosys.cc | 298 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 298 insertions(+) diff --git a/kernel/yosys.cc b/kernel/yosys.cc index 374b07d06b6..ca6f0c48059 100644 --- a/kernel/yosys.cc +++ b/kernel/yosys.cc @@ -829,11 +829,309 @@ static int tcl_yosys_cmd(ClientData, Tcl_Interp *interp, int argc, const char *a return TCL_OK; } +#define FLAG(name) \ + if (!strcmp(argv[i], "-" #name)) { \ + name##_flag = true; \ + continue; \ + } \ + +#define ERROR(str) \ + { \ + Tcl_SetResult(interp, (char *)(str), TCL_STATIC); \ + return TCL_ERROR; \ + } + +static int tcl_get_attr(ClientData, Tcl_Interp *interp, int argc, const char *argv[]) +{ + int i; + bool mod_flag = false, string_flag = false, int_flag = false, bool_flag = false; + for (i = 1; i < argc; i++) { + FLAG(mod) + FLAG(string) + FLAG(int) + FLAG(bool) + break; + } + + if ((mod_flag && i != argc - 2) || + (!mod_flag && i != argc - 3) || + (string_flag + int_flag + bool_flag > 1)) + ERROR("bad usage: expected \"get_attr -mod [-string|-int|-bool] \"" + " or \"get_attr [-string|-int|-bool] \"") + + IdString mod_id, obj_id, attr_id; + mod_id = RTLIL::escape_id(argv[i++]); + if (!mod_flag) + obj_id = RTLIL::escape_id(argv[i++]); + attr_id = RTLIL::escape_id(argv[i++]); + + RTLIL::Module *mod = yosys_design->module(mod_id); + if (!mod) + ERROR("module not found") + + RTLIL::AttrObject *obj = nullptr; + if (mod_flag) { + obj = mod; + } else { + obj = mod->wire(obj_id); + if (!obj) + obj = mod->memories.at(obj_id, nullptr); + if (!obj) + obj = mod->cell(obj_id); + if (!obj) + obj = mod->processes.at(obj_id, nullptr); + } + + if (!obj) + ERROR("object not found") + + if (string_flag) { + Tcl_SetResult(interp, (char *) obj->get_string_attribute(attr_id).c_str(), TCL_VOLATILE); + } else if (int_flag) { + if (!obj->has_attribute(attr_id)) + ERROR("attribute missing (required for -int)"); + + RTLIL::Const &value = obj->attributes.at(attr_id); + if (value.size() > 32) + ERROR("value too large") + + // FIXME: 32'hffffffff will return as negative despite is_signed=false + Tcl_SetResult(interp, (char *) std::to_string(value.as_int()).c_str(), TCL_VOLATILE); + } else if (bool_flag) { + bool value = obj->get_bool_attribute(attr_id); + Tcl_SetResult(interp, (char *) std::to_string(value).c_str(), TCL_VOLATILE); + } else { + if (!obj->has_attribute(attr_id)) + ERROR("attribute missing (required unless -bool or -string)") + + Tcl_SetResult(interp, (char *) obj->attributes.at(attr_id).as_string().c_str(), TCL_VOLATILE); + } + + return TCL_OK; +} + +static int tcl_has_attr(ClientData, Tcl_Interp *interp, int argc, const char *argv[]) +{ + int i; + bool mod_flag = false; + for (i = 1; i < argc; i++) { + FLAG(mod) + break; + } + + if ((mod_flag && i != argc - 2) || + (!mod_flag && i != argc - 3)) + ERROR("bad usage: expected \"has_attr -mod \"" + " or \"has_attr \"") + + IdString mod_id, obj_id, attr_id; + mod_id = RTLIL::escape_id(argv[i++]); + if (!mod_flag) + obj_id = RTLIL::escape_id(argv[i++]); + attr_id = RTLIL::escape_id(argv[i++]); + + RTLIL::Module *mod = yosys_design->module(mod_id); + if (!mod) + ERROR("module not found") + + RTLIL::AttrObject *obj = nullptr; + if (mod_flag) { + obj = mod; + } else { + obj = mod->wire(obj_id); + if (!obj) + obj = mod->memories.at(obj_id, nullptr); + if (!obj) + obj = mod->cell(obj_id); + if (!obj) + obj = mod->processes.at(obj_id, nullptr); + } + + if (!obj) + ERROR("object not found") + + Tcl_SetResult(interp, (char *) std::to_string(obj->has_attribute(attr_id)).c_str(), TCL_VOLATILE); + return TCL_OK; +} + +static int tcl_set_attr(ClientData, Tcl_Interp *interp, int argc, const char *argv[]) +{ + int i; + bool mod_flag = false, string_flag = false, int_flag = false, bool_flag = false; + bool false_flag = false, true_flag = false; + for (i = 1; i < argc; i++) { + FLAG(mod) + FLAG(string) + FLAG(int) + FLAG(bool) + FLAG(false) + FLAG(true) + break; + } + + if ((i != argc - (2 + !mod_flag + !(true_flag || false_flag))) || + (string_flag + int_flag + bool_flag + true_flag + false_flag > 1)) + ERROR("bad usage: expected \"set_attr -mod [-string|-int|-bool] \"" + " or \"set_attr [-string|-int|-bool] \"" + " or \"set_attr [-true|-false] \"" + " or \"set_attr -mod [-true|-false| \"") + + IdString mod_id, obj_id, attr_id; + mod_id = RTLIL::escape_id(argv[i++]); + if (!mod_flag) + obj_id = RTLIL::escape_id(argv[i++]); + attr_id = RTLIL::escape_id(argv[i++]); + + RTLIL::Module *mod = yosys_design->module(mod_id); + if (!mod) + ERROR("module not found") + + RTLIL::AttrObject *obj = nullptr; + if (mod_flag) { + obj = mod; + } else { + obj = mod->wire(obj_id); + if (!obj) + obj = mod->memories.at(obj_id, nullptr); + if (!obj) + obj = mod->cell(obj_id); + if (!obj) + obj = mod->processes.at(obj_id, nullptr); + } + + if (!obj) + ERROR("object not found") + + if (string_flag) { + obj->set_string_attribute(attr_id, argv[i++]); + } else if (int_flag) { + obj->attributes[attr_id] = atoi(argv[i++]); + } else if (bool_flag) { + obj->set_bool_attribute(attr_id, atoi(argv[i++]) != 0); + } else if (true_flag) { + obj->set_bool_attribute(attr_id, true); + } else if (false_flag) { + obj->set_bool_attribute(attr_id, false); + } else { + obj->attributes[attr_id] = Const::from_string(std::string(argv[i++])); + } + + return TCL_OK; +} + +static int tcl_get_param(ClientData, Tcl_Interp *interp, int argc, const char *argv[]) +{ + int i; + bool string_flag = false, int_flag = false; + for (i = 1; i < argc; i++) { + FLAG(string) + FLAG(int) + break; + } + + if ((i != argc - 3) || + (string_flag + int_flag > 1)) + ERROR("bad usage: expected \"get_param [-string|-int] ") + + IdString mod_id, cell_id, param_id; + mod_id = RTLIL::escape_id(argv[i++]); + cell_id = RTLIL::escape_id(argv[i++]); + param_id = RTLIL::escape_id(argv[i++]); + + RTLIL::Module *mod = yosys_design->module(mod_id); + if (!mod) + ERROR("module not found") + + RTLIL::Cell *cell = mod->cell(cell_id); + if (!cell) + ERROR("object not found") + + if (!cell->hasParam(param_id)) + ERROR("parameter missing") + + const RTLIL::Const &value = cell->getParam(param_id); + + if (string_flag) { + Tcl_SetResult(interp, (char *) value.decode_string().c_str(), TCL_VOLATILE); + } else if (int_flag) { + if (value.size() > 32) + ERROR("value too large") + + Tcl_SetResult(interp, (char *) std::to_string(value.as_int()).c_str(), TCL_VOLATILE); + } else { + Tcl_SetResult(interp, (char *) value.as_string().c_str(), TCL_VOLATILE); + } + return TCL_OK; +} + +static int tcl_set_param(ClientData, Tcl_Interp *interp, int argc, const char *argv[]) +{ + int i; + bool string_flag = false, int_flag = false; + for (i = 1; i < argc; i++) { + FLAG(string) + FLAG(int) + break; + } + + if ((i != argc - 4) || + (string_flag + int_flag > 1)) + ERROR("bad usage: expected \"get_param [-string|-int] ") + + IdString mod_id, cell_id, param_id; + mod_id = RTLIL::escape_id(argv[i++]); + cell_id = RTLIL::escape_id(argv[i++]); + param_id = RTLIL::escape_id(argv[i++]); + + RTLIL::Module *mod = yosys_design->module(mod_id); + if (!mod) + ERROR("module not found") + + RTLIL::Cell *cell = mod->cell(cell_id); + if (!cell) + ERROR("object not found") + + if (string_flag) { + cell->setParam(param_id, Const(std::string(argv[i++]))); + } else if (int_flag) { + cell->setParam(param_id, Const(atoi(argv[i++]))); + } else { + cell->setParam(param_id, Const::from_string(std::string(argv[i++]))); + } + return TCL_OK; +} + int yosys_tcl_iterp_init(Tcl_Interp *interp) { if (Tcl_Init(interp)!=TCL_OK) log_warning("Tcl_Init() call failed - %s\n",Tcl_ErrnoMsg(Tcl_GetErrno())); Tcl_CreateCommand(interp, "yosys", tcl_yosys_cmd, NULL, NULL); + Tcl_CreateCommand(interp, "rtlil::get_attr", tcl_get_attr, NULL, NULL); + Tcl_CreateCommand(interp, "rtlil::has_attr", tcl_has_attr, NULL, NULL); + Tcl_CreateCommand(interp, "rtlil::set_attr", tcl_set_attr, NULL, NULL); + Tcl_CreateCommand(interp, "rtlil::get_param", tcl_get_param, NULL, NULL); + Tcl_CreateCommand(interp, "rtlil::set_param", tcl_set_param, NULL, NULL); + + // TODO: + // + // port_list + // wire_list + // cell_list + // wire_width + // + // add_wire + // add_cell + // rename_wire + // rename_cell + // remove + // + // SigSpec land + // + // get_conn + // set_conn + // unpack + // pack + return TCL_OK ; } From cba9460cbafcd8ef42d85dfa75adba95f09f2200 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Mon, 4 Nov 2024 16:17:02 +0100 Subject: [PATCH 2/7] Split off Tcl APIs into `tclapi.cc` --- Makefile | 2 +- kernel/tclapi.cc | 449 +++++++++++++++++++++++++++++++++++++++++++++++ kernel/yosys.cc | 423 +------------------------------------------- 3 files changed, 452 insertions(+), 422 deletions(-) create mode 100644 kernel/tclapi.cc diff --git a/Makefile b/Makefile index 5be8c64e9cf..5407433cfda 100644 --- a/Makefile +++ b/Makefile @@ -639,7 +639,7 @@ $(eval $(call add_include_file,frontends/blif/blifparse.h)) $(eval $(call add_include_file,backends/rtlil/rtlil_backend.h)) OBJS += kernel/driver.o kernel/register.o kernel/rtlil.o kernel/log.o kernel/calc.o kernel/yosys.o -OBJS += kernel/binding.o +OBJS += kernel/binding.o kernel/tclapi.o OBJS += kernel/cellaigs.o kernel/celledges.o kernel/cost.o kernel/satgen.o kernel/scopeinfo.o kernel/qcsat.o kernel/mem.o kernel/ffmerge.o kernel/ff.o kernel/yw.o kernel/json.o kernel/fmt.o kernel/sexpr.o OBJS += kernel/drivertools.o kernel/functional.o ifeq ($(ENABLE_ZLIB),1) diff --git a/kernel/tclapi.cc b/kernel/tclapi.cc new file mode 100644 index 00000000000..4a8e039edec --- /dev/null +++ b/kernel/tclapi.cc @@ -0,0 +1,449 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Claire Xenia Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/yosys.h" +#include "kernel/rtlil.h" +#include "libs/json11/json11.hpp" + +YOSYS_NAMESPACE_BEGIN + +#ifdef YOSYS_ENABLE_TCL +bool yosys_tcl_repl_active = false; + +void yosys_tcl_activate_repl() +{ + yosys_tcl_repl_active = true; +} + +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 args; + for (int i = 1; i < argc; i++) + args.push_back(argv[i]); + + if (args.size() >= 1 && args[0] == "-import") { + for (auto &it : pass_register) { + std::string tcl_command_name = it.first; + if (tcl_command_name == "proc") + tcl_command_name = "procs"; + else if (tcl_command_name == "rename") + tcl_command_name = "renames"; + Tcl_CmdInfo info; + if (Tcl_GetCommandInfo(interp, tcl_command_name.c_str(), &info) != 0) { + log("[TCL: yosys -import] Command name collision: found pre-existing command `%s' -> skip.\n", it.first.c_str()); + } else { + std::string tcl_script = stringf("proc %s args { yosys %s {*}$args }", tcl_command_name.c_str(), it.first.c_str()); + Tcl_Eval(interp, tcl_script.c_str()); + } + } + 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())); + } + + return TCL_OK; +} + +#define FLAG(name) \ + if (!strcmp(argv[i], "-" #name)) { \ + name##_flag = true; \ + continue; \ + } \ + +#define ERROR(str) \ + { \ + Tcl_SetResult(interp, (char *)(str), TCL_STATIC); \ + return TCL_ERROR; \ + } + +static int tcl_get_attr(ClientData, Tcl_Interp *interp, int argc, const char *argv[]) +{ + int i; + bool mod_flag = false, string_flag = false, int_flag = false, bool_flag = false; + for (i = 1; i < argc; i++) { + FLAG(mod) + FLAG(string) + FLAG(int) + FLAG(bool) + break; + } + + if ((mod_flag && i != argc - 2) || + (!mod_flag && i != argc - 3) || + (string_flag + int_flag + bool_flag > 1)) + ERROR("bad usage: expected \"get_attr -mod [-string|-int|-bool] \"" + " or \"get_attr [-string|-int|-bool] \"") + + IdString mod_id, obj_id, attr_id; + mod_id = RTLIL::escape_id(argv[i++]); + if (!mod_flag) + obj_id = RTLIL::escape_id(argv[i++]); + attr_id = RTLIL::escape_id(argv[i++]); + + RTLIL::Module *mod = yosys_design->module(mod_id); + if (!mod) + ERROR("module not found") + + RTLIL::AttrObject *obj = nullptr; + if (mod_flag) { + obj = mod; + } else { + obj = mod->wire(obj_id); + if (!obj) + obj = mod->memories.at(obj_id, nullptr); + if (!obj) + obj = mod->cell(obj_id); + if (!obj) + obj = mod->processes.at(obj_id, nullptr); + } + + if (!obj) + ERROR("object not found") + + if (string_flag) { + Tcl_SetResult(interp, (char *) obj->get_string_attribute(attr_id).c_str(), TCL_VOLATILE); + } else if (int_flag) { + if (!obj->has_attribute(attr_id)) + ERROR("attribute missing (required for -int)"); + + RTLIL::Const &value = obj->attributes.at(attr_id); + if (value.size() > 32) + ERROR("value too large") + + // FIXME: 32'hffffffff will return as negative despite is_signed=false + Tcl_SetResult(interp, (char *) std::to_string(value.as_int()).c_str(), TCL_VOLATILE); + } else if (bool_flag) { + bool value = obj->get_bool_attribute(attr_id); + Tcl_SetResult(interp, (char *) std::to_string(value).c_str(), TCL_VOLATILE); + } else { + if (!obj->has_attribute(attr_id)) + ERROR("attribute missing (required unless -bool or -string)") + + Tcl_SetResult(interp, (char *) obj->attributes.at(attr_id).as_string().c_str(), TCL_VOLATILE); + } + + return TCL_OK; +} + +static int tcl_has_attr(ClientData, Tcl_Interp *interp, int argc, const char *argv[]) +{ + int i; + bool mod_flag = false; + for (i = 1; i < argc; i++) { + FLAG(mod) + break; + } + + if ((mod_flag && i != argc - 2) || + (!mod_flag && i != argc - 3)) + ERROR("bad usage: expected \"has_attr -mod \"" + " or \"has_attr \"") + + IdString mod_id, obj_id, attr_id; + mod_id = RTLIL::escape_id(argv[i++]); + if (!mod_flag) + obj_id = RTLIL::escape_id(argv[i++]); + attr_id = RTLIL::escape_id(argv[i++]); + + RTLIL::Module *mod = yosys_design->module(mod_id); + if (!mod) + ERROR("module not found") + + RTLIL::AttrObject *obj = nullptr; + if (mod_flag) { + obj = mod; + } else { + obj = mod->wire(obj_id); + if (!obj) + obj = mod->memories.at(obj_id, nullptr); + if (!obj) + obj = mod->cell(obj_id); + if (!obj) + obj = mod->processes.at(obj_id, nullptr); + } + + if (!obj) + ERROR("object not found") + + Tcl_SetResult(interp, (char *) std::to_string(obj->has_attribute(attr_id)).c_str(), TCL_VOLATILE); + return TCL_OK; +} + +static int tcl_set_attr(ClientData, Tcl_Interp *interp, int argc, const char *argv[]) +{ + int i; + bool mod_flag = false, string_flag = false, int_flag = false, bool_flag = false; + bool false_flag = false, true_flag = false; + for (i = 1; i < argc; i++) { + FLAG(mod) + FLAG(string) + FLAG(int) + FLAG(bool) + FLAG(false) + FLAG(true) + break; + } + + if ((i != argc - (2 + !mod_flag + !(true_flag || false_flag))) || + (string_flag + int_flag + bool_flag + true_flag + false_flag > 1)) + ERROR("bad usage: expected \"set_attr -mod [-string|-int|-bool] \"" + " or \"set_attr [-string|-int|-bool] \"" + " or \"set_attr [-true|-false] \"" + " or \"set_attr -mod [-true|-false| \"") + + IdString mod_id, obj_id, attr_id; + mod_id = RTLIL::escape_id(argv[i++]); + if (!mod_flag) + obj_id = RTLIL::escape_id(argv[i++]); + attr_id = RTLIL::escape_id(argv[i++]); + + RTLIL::Module *mod = yosys_design->module(mod_id); + if (!mod) + ERROR("module not found") + + RTLIL::AttrObject *obj = nullptr; + if (mod_flag) { + obj = mod; + } else { + obj = mod->wire(obj_id); + if (!obj) + obj = mod->memories.at(obj_id, nullptr); + if (!obj) + obj = mod->cell(obj_id); + if (!obj) + obj = mod->processes.at(obj_id, nullptr); + } + + if (!obj) + ERROR("object not found") + + if (string_flag) { + obj->set_string_attribute(attr_id, argv[i++]); + } else if (int_flag) { + obj->attributes[attr_id] = atoi(argv[i++]); + } else if (bool_flag) { + obj->set_bool_attribute(attr_id, atoi(argv[i++]) != 0); + } else if (true_flag) { + obj->set_bool_attribute(attr_id, true); + } else if (false_flag) { + obj->set_bool_attribute(attr_id, false); + } else { + obj->attributes[attr_id] = Const::from_string(std::string(argv[i++])); + } + + return TCL_OK; +} + +static int tcl_get_param(ClientData, Tcl_Interp *interp, int argc, const char *argv[]) +{ + int i; + bool string_flag = false, int_flag = false; + for (i = 1; i < argc; i++) { + FLAG(string) + FLAG(int) + break; + } + + if ((i != argc - 3) || + (string_flag + int_flag > 1)) + ERROR("bad usage: expected \"get_param [-string|-int] ") + + IdString mod_id, cell_id, param_id; + mod_id = RTLIL::escape_id(argv[i++]); + cell_id = RTLIL::escape_id(argv[i++]); + param_id = RTLIL::escape_id(argv[i++]); + + RTLIL::Module *mod = yosys_design->module(mod_id); + if (!mod) + ERROR("module not found") + + RTLIL::Cell *cell = mod->cell(cell_id); + if (!cell) + ERROR("object not found") + + if (!cell->hasParam(param_id)) + ERROR("parameter missing") + + const RTLIL::Const &value = cell->getParam(param_id); + + if (string_flag) { + Tcl_SetResult(interp, (char *) value.decode_string().c_str(), TCL_VOLATILE); + } else if (int_flag) { + if (value.size() > 32) + ERROR("value too large") + + Tcl_SetResult(interp, (char *) std::to_string(value.as_int()).c_str(), TCL_VOLATILE); + } else { + Tcl_SetResult(interp, (char *) value.as_string().c_str(), TCL_VOLATILE); + } + return TCL_OK; +} + +static int tcl_set_param(ClientData, Tcl_Interp *interp, int argc, const char *argv[]) +{ + int i; + bool string_flag = false, int_flag = false; + for (i = 1; i < argc; i++) { + FLAG(string) + FLAG(int) + break; + } + + if ((i != argc - 4) || + (string_flag + int_flag > 1)) + ERROR("bad usage: expected \"get_param [-string|-int] ") + + IdString mod_id, cell_id, param_id; + mod_id = RTLIL::escape_id(argv[i++]); + cell_id = RTLIL::escape_id(argv[i++]); + param_id = RTLIL::escape_id(argv[i++]); + + RTLIL::Module *mod = yosys_design->module(mod_id); + if (!mod) + ERROR("module not found") + + RTLIL::Cell *cell = mod->cell(cell_id); + if (!cell) + ERROR("object not found") + + if (string_flag) { + cell->setParam(param_id, Const(std::string(argv[i++]))); + } else if (int_flag) { + cell->setParam(param_id, Const(atoi(argv[i++]))); + } else { + cell->setParam(param_id, Const::from_string(std::string(argv[i++]))); + } + return TCL_OK; +} + +int yosys_tcl_iterp_init(Tcl_Interp *interp) +{ + if (Tcl_Init(interp)!=TCL_OK) + log_warning("Tcl_Init() call failed - %s\n",Tcl_ErrnoMsg(Tcl_GetErrno())); + Tcl_CreateCommand(interp, "yosys", tcl_yosys_cmd, NULL, NULL); + Tcl_CreateCommand(interp, "rtlil::get_attr", tcl_get_attr, NULL, NULL); + Tcl_CreateCommand(interp, "rtlil::has_attr", tcl_has_attr, NULL, NULL); + Tcl_CreateCommand(interp, "rtlil::set_attr", tcl_set_attr, NULL, NULL); + Tcl_CreateCommand(interp, "rtlil::get_param", tcl_get_param, NULL, NULL); + Tcl_CreateCommand(interp, "rtlil::set_param", tcl_set_param, NULL, NULL); + + // TODO: + // + // port_list + // wire_list + // cell_list + // wire_width + // + // add_wire + // add_cell + // rename_wire + // rename_cell + // remove + // + // SigSpec land + // + // get_conn + // set_conn + // unpack + // pack + + return TCL_OK ; +} +#endif + +YOSYS_NAMESPACE_END diff --git a/kernel/yosys.cc b/kernel/yosys.cc index ca6f0c48059..82a9ffbf860 100644 --- a/kernel/yosys.cc +++ b/kernel/yosys.cc @@ -73,8 +73,6 @@ #include #include -#include "libs/json11/json11.hpp" - YOSYS_NAMESPACE_BEGIN int autoidx = 1; @@ -84,7 +82,6 @@ CellTypes yosys_celltypes; #ifdef YOSYS_ENABLE_TCL Tcl_Interp *yosys_tcl_interp = NULL; -bool yosys_tcl_repl_active = false; #endif std::set yosys_input_files, yosys_output_files; @@ -721,424 +718,8 @@ 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 args; - for (int i = 1; i < argc; i++) - args.push_back(argv[i]); - - if (args.size() >= 1 && args[0] == "-import") { - for (auto &it : pass_register) { - std::string tcl_command_name = it.first; - if (tcl_command_name == "proc") - tcl_command_name = "procs"; - else if (tcl_command_name == "rename") - tcl_command_name = "renames"; - Tcl_CmdInfo info; - if (Tcl_GetCommandInfo(interp, tcl_command_name.c_str(), &info) != 0) { - log("[TCL: yosys -import] Command name collision: found pre-existing command `%s' -> skip.\n", it.first.c_str()); - } else { - std::string tcl_script = stringf("proc %s args { yosys %s {*}$args }", tcl_command_name.c_str(), it.first.c_str()); - Tcl_Eval(interp, tcl_script.c_str()); - } - } - 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())); - } - - return TCL_OK; -} - -#define FLAG(name) \ - if (!strcmp(argv[i], "-" #name)) { \ - name##_flag = true; \ - continue; \ - } \ - -#define ERROR(str) \ - { \ - Tcl_SetResult(interp, (char *)(str), TCL_STATIC); \ - return TCL_ERROR; \ - } - -static int tcl_get_attr(ClientData, Tcl_Interp *interp, int argc, const char *argv[]) -{ - int i; - bool mod_flag = false, string_flag = false, int_flag = false, bool_flag = false; - for (i = 1; i < argc; i++) { - FLAG(mod) - FLAG(string) - FLAG(int) - FLAG(bool) - break; - } - - if ((mod_flag && i != argc - 2) || - (!mod_flag && i != argc - 3) || - (string_flag + int_flag + bool_flag > 1)) - ERROR("bad usage: expected \"get_attr -mod [-string|-int|-bool] \"" - " or \"get_attr [-string|-int|-bool] \"") - - IdString mod_id, obj_id, attr_id; - mod_id = RTLIL::escape_id(argv[i++]); - if (!mod_flag) - obj_id = RTLIL::escape_id(argv[i++]); - attr_id = RTLIL::escape_id(argv[i++]); - - RTLIL::Module *mod = yosys_design->module(mod_id); - if (!mod) - ERROR("module not found") - - RTLIL::AttrObject *obj = nullptr; - if (mod_flag) { - obj = mod; - } else { - obj = mod->wire(obj_id); - if (!obj) - obj = mod->memories.at(obj_id, nullptr); - if (!obj) - obj = mod->cell(obj_id); - if (!obj) - obj = mod->processes.at(obj_id, nullptr); - } - - if (!obj) - ERROR("object not found") - - if (string_flag) { - Tcl_SetResult(interp, (char *) obj->get_string_attribute(attr_id).c_str(), TCL_VOLATILE); - } else if (int_flag) { - if (!obj->has_attribute(attr_id)) - ERROR("attribute missing (required for -int)"); - - RTLIL::Const &value = obj->attributes.at(attr_id); - if (value.size() > 32) - ERROR("value too large") - - // FIXME: 32'hffffffff will return as negative despite is_signed=false - Tcl_SetResult(interp, (char *) std::to_string(value.as_int()).c_str(), TCL_VOLATILE); - } else if (bool_flag) { - bool value = obj->get_bool_attribute(attr_id); - Tcl_SetResult(interp, (char *) std::to_string(value).c_str(), TCL_VOLATILE); - } else { - if (!obj->has_attribute(attr_id)) - ERROR("attribute missing (required unless -bool or -string)") - - Tcl_SetResult(interp, (char *) obj->attributes.at(attr_id).as_string().c_str(), TCL_VOLATILE); - } - - return TCL_OK; -} - -static int tcl_has_attr(ClientData, Tcl_Interp *interp, int argc, const char *argv[]) -{ - int i; - bool mod_flag = false; - for (i = 1; i < argc; i++) { - FLAG(mod) - break; - } - - if ((mod_flag && i != argc - 2) || - (!mod_flag && i != argc - 3)) - ERROR("bad usage: expected \"has_attr -mod \"" - " or \"has_attr \"") - - IdString mod_id, obj_id, attr_id; - mod_id = RTLIL::escape_id(argv[i++]); - if (!mod_flag) - obj_id = RTLIL::escape_id(argv[i++]); - attr_id = RTLIL::escape_id(argv[i++]); - - RTLIL::Module *mod = yosys_design->module(mod_id); - if (!mod) - ERROR("module not found") - - RTLIL::AttrObject *obj = nullptr; - if (mod_flag) { - obj = mod; - } else { - obj = mod->wire(obj_id); - if (!obj) - obj = mod->memories.at(obj_id, nullptr); - if (!obj) - obj = mod->cell(obj_id); - if (!obj) - obj = mod->processes.at(obj_id, nullptr); - } - - if (!obj) - ERROR("object not found") - - Tcl_SetResult(interp, (char *) std::to_string(obj->has_attribute(attr_id)).c_str(), TCL_VOLATILE); - return TCL_OK; -} - -static int tcl_set_attr(ClientData, Tcl_Interp *interp, int argc, const char *argv[]) -{ - int i; - bool mod_flag = false, string_flag = false, int_flag = false, bool_flag = false; - bool false_flag = false, true_flag = false; - for (i = 1; i < argc; i++) { - FLAG(mod) - FLAG(string) - FLAG(int) - FLAG(bool) - FLAG(false) - FLAG(true) - break; - } - - if ((i != argc - (2 + !mod_flag + !(true_flag || false_flag))) || - (string_flag + int_flag + bool_flag + true_flag + false_flag > 1)) - ERROR("bad usage: expected \"set_attr -mod [-string|-int|-bool] \"" - " or \"set_attr [-string|-int|-bool] \"" - " or \"set_attr [-true|-false] \"" - " or \"set_attr -mod [-true|-false| \"") - - IdString mod_id, obj_id, attr_id; - mod_id = RTLIL::escape_id(argv[i++]); - if (!mod_flag) - obj_id = RTLIL::escape_id(argv[i++]); - attr_id = RTLIL::escape_id(argv[i++]); - - RTLIL::Module *mod = yosys_design->module(mod_id); - if (!mod) - ERROR("module not found") - - RTLIL::AttrObject *obj = nullptr; - if (mod_flag) { - obj = mod; - } else { - obj = mod->wire(obj_id); - if (!obj) - obj = mod->memories.at(obj_id, nullptr); - if (!obj) - obj = mod->cell(obj_id); - if (!obj) - obj = mod->processes.at(obj_id, nullptr); - } - - if (!obj) - ERROR("object not found") - - if (string_flag) { - obj->set_string_attribute(attr_id, argv[i++]); - } else if (int_flag) { - obj->attributes[attr_id] = atoi(argv[i++]); - } else if (bool_flag) { - obj->set_bool_attribute(attr_id, atoi(argv[i++]) != 0); - } else if (true_flag) { - obj->set_bool_attribute(attr_id, true); - } else if (false_flag) { - obj->set_bool_attribute(attr_id, false); - } else { - obj->attributes[attr_id] = Const::from_string(std::string(argv[i++])); - } - - return TCL_OK; -} - -static int tcl_get_param(ClientData, Tcl_Interp *interp, int argc, const char *argv[]) -{ - int i; - bool string_flag = false, int_flag = false; - for (i = 1; i < argc; i++) { - FLAG(string) - FLAG(int) - break; - } - - if ((i != argc - 3) || - (string_flag + int_flag > 1)) - ERROR("bad usage: expected \"get_param [-string|-int] ") - - IdString mod_id, cell_id, param_id; - mod_id = RTLIL::escape_id(argv[i++]); - cell_id = RTLIL::escape_id(argv[i++]); - param_id = RTLIL::escape_id(argv[i++]); - - RTLIL::Module *mod = yosys_design->module(mod_id); - if (!mod) - ERROR("module not found") - - RTLIL::Cell *cell = mod->cell(cell_id); - if (!cell) - ERROR("object not found") - - if (!cell->hasParam(param_id)) - ERROR("parameter missing") - - const RTLIL::Const &value = cell->getParam(param_id); - - if (string_flag) { - Tcl_SetResult(interp, (char *) value.decode_string().c_str(), TCL_VOLATILE); - } else if (int_flag) { - if (value.size() > 32) - ERROR("value too large") - - Tcl_SetResult(interp, (char *) std::to_string(value.as_int()).c_str(), TCL_VOLATILE); - } else { - Tcl_SetResult(interp, (char *) value.as_string().c_str(), TCL_VOLATILE); - } - return TCL_OK; -} - -static int tcl_set_param(ClientData, Tcl_Interp *interp, int argc, const char *argv[]) -{ - int i; - bool string_flag = false, int_flag = false; - for (i = 1; i < argc; i++) { - FLAG(string) - FLAG(int) - break; - } - - if ((i != argc - 4) || - (string_flag + int_flag > 1)) - ERROR("bad usage: expected \"get_param [-string|-int] ") - - IdString mod_id, cell_id, param_id; - mod_id = RTLIL::escape_id(argv[i++]); - cell_id = RTLIL::escape_id(argv[i++]); - param_id = RTLIL::escape_id(argv[i++]); - - RTLIL::Module *mod = yosys_design->module(mod_id); - if (!mod) - ERROR("module not found") - - RTLIL::Cell *cell = mod->cell(cell_id); - if (!cell) - ERROR("object not found") - - if (string_flag) { - cell->setParam(param_id, Const(std::string(argv[i++]))); - } else if (int_flag) { - cell->setParam(param_id, Const(atoi(argv[i++]))); - } else { - cell->setParam(param_id, Const::from_string(std::string(argv[i++]))); - } - return TCL_OK; -} - -int yosys_tcl_iterp_init(Tcl_Interp *interp) -{ - if (Tcl_Init(interp)!=TCL_OK) - log_warning("Tcl_Init() call failed - %s\n",Tcl_ErrnoMsg(Tcl_GetErrno())); - Tcl_CreateCommand(interp, "yosys", tcl_yosys_cmd, NULL, NULL); - Tcl_CreateCommand(interp, "rtlil::get_attr", tcl_get_attr, NULL, NULL); - Tcl_CreateCommand(interp, "rtlil::has_attr", tcl_has_attr, NULL, NULL); - Tcl_CreateCommand(interp, "rtlil::set_attr", tcl_set_attr, NULL, NULL); - Tcl_CreateCommand(interp, "rtlil::get_param", tcl_get_param, NULL, NULL); - Tcl_CreateCommand(interp, "rtlil::set_param", tcl_set_param, NULL, NULL); - - // TODO: - // - // port_list - // wire_list - // cell_list - // wire_width - // - // add_wire - // add_cell - // rename_wire - // rename_cell - // remove - // - // SigSpec land - // - // get_conn - // set_conn - // unpack - // pack - - return TCL_OK ; -} - -void yosys_tcl_activate_repl() -{ - yosys_tcl_repl_active = true; -} +// defined in tclapi.cc +extern int yosys_tcl_iterp_init(Tcl_Interp *interp); extern Tcl_Interp *yosys_get_tcl_interp() { From 23922faecc3d061e9cb66e7cf58d75e427600c15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Mon, 4 Nov 2024 16:18:50 +0100 Subject: [PATCH 3/7] Test new Tcl methods --- tests/various/tcl_apis.tcl | 16 ++++++++++++++++ tests/various/tcl_apis.v | 11 +++++++++++ tests/various/tcl_apis.ys | 1 + 3 files changed, 28 insertions(+) create mode 100644 tests/various/tcl_apis.tcl create mode 100644 tests/various/tcl_apis.v create mode 100644 tests/various/tcl_apis.ys diff --git a/tests/various/tcl_apis.tcl b/tests/various/tcl_apis.tcl new file mode 100644 index 00000000000..74f1aaa52be --- /dev/null +++ b/tests/various/tcl_apis.tcl @@ -0,0 +1,16 @@ +yosys read_verilog tcl_apis.v + +if {[rtlil::get_attr -string -mod top foo] != "bar"} { + error "bad top module attribute" +} + +if {[rtlil::get_attr -bool top w dont_touch] != 1} { + error "bad w wire attribute" +} + +if {[rtlil::get_param -int top inst PARAM] != 4} { + error "bad parameter" +} + +rtlil::set_attr -true -mod top marked +yosys select -assert-any A:marked diff --git a/tests/various/tcl_apis.v b/tests/various/tcl_apis.v new file mode 100644 index 00000000000..cc379c20aa4 --- /dev/null +++ b/tests/various/tcl_apis.v @@ -0,0 +1,11 @@ +module m; +parameter PARAM = 0; +endmodule + +(* foo="bar" *) +module top; + (* dont_touch *) + wire w; + + m #(.PARAM(4)) inst(); +endmodule diff --git a/tests/various/tcl_apis.ys b/tests/various/tcl_apis.ys new file mode 100644 index 00000000000..141f08a3b04 --- /dev/null +++ b/tests/various/tcl_apis.ys @@ -0,0 +1 @@ +yosys tcl tcl_apis.tcl From f7400a06cd2d819917ca6a5a7433e4ea4b64f78f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Mon, 4 Nov 2024 16:19:59 +0100 Subject: [PATCH 4/7] Fix test --- tests/various/tcl_apis.ys | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/various/tcl_apis.ys b/tests/various/tcl_apis.ys index 141f08a3b04..4bc13f16b28 100644 --- a/tests/various/tcl_apis.ys +++ b/tests/various/tcl_apis.ys @@ -1 +1 @@ -yosys tcl tcl_apis.tcl +tcl tcl_apis.tcl From f0704b6edefd57ade9172f4acea838227345fbee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Mon, 2 Dec 2024 19:56:51 +0100 Subject: [PATCH 5/7] Redo integer passing on top of bignum --- kernel/tclapi.cc | 240 ++++++++++++++++++++++++++++--------- tests/various/tcl_apis.tcl | 49 +++++++- tests/various/tcl_apis.v | 3 +- 3 files changed, 233 insertions(+), 59 deletions(-) diff --git a/kernel/tclapi.cc b/kernel/tclapi.cc index 4a8e039edec..f6728be1539 100644 --- a/kernel/tclapi.cc +++ b/kernel/tclapi.cc @@ -21,6 +21,11 @@ #include "kernel/rtlil.h" #include "libs/json11/json11.hpp" +#ifdef YOSYS_ENABLE_TCL +#include "tclTomMath.h" +#include "tclTomMathDecls.h" +#endif + YOSYS_NAMESPACE_BEGIN #ifdef YOSYS_ENABLE_TCL @@ -145,29 +150,106 @@ static int tcl_yosys_cmd(ClientData, Tcl_Interp *interp, int argc, const char *a continue; \ } \ +#define FLAG2(name) \ + if (!strcmp(Tcl_GetString(objv[i]), "-" #name)) { \ + name##_flag = true; \ + continue; \ + } \ + #define ERROR(str) \ { \ Tcl_SetResult(interp, (char *)(str), TCL_STATIC); \ return TCL_ERROR; \ } +bool const_to_mp_int(const Const &a, mp_int *b, bool force_signed, bool force_unsigned) +{ + if (!a.is_fully_def()) + return false; + + if (mp_init(b)) + return false; + + bool negative = ((a.flags & RTLIL::CONST_FLAG_SIGNED) || force_signed) && + !force_unsigned && + !a.empty() && (a.back() == RTLIL::S1); + + for (int i = a.size() - 1; i >= 0; i--) { + if (mp_mul_2d(b, 1, b)) { + mp_clear(b); + return false; + } + + if ((a[i] == RTLIL::S1) ^ negative) { + if (mp_add_d(b, 1, b)) { + mp_clear(b); + return false; + } + } + } + + if (negative) { + if (mp_add_d(b, 1, b) || mp_neg(b, b)) { + mp_clear(b); + return false; + } + } + + return true; +} + +bool mp_int_to_const(mp_int *a, Const &b, bool is_signed) +{ + bool negative = (mp_cmp_d(a, 0) == MP_LT); + if (negative && !is_signed) + return false; + + if (negative) { + mp_neg(a, a); + mp_sub_d(a, 1, a); + } + + std::vector buf; + buf.resize(mp_ubin_size(a)); + size_t written; // dummy + mp_to_ubin(a, buf.data(), buf.size(), &written); + + b.bits().reserve(mp_count_bits(a) + is_signed); + for (int i = 0; i < mp_count_bits(a);) { + for (int j = 0; j < 8 && i < mp_count_bits(a); j++, i++) { + bool bv = ((buf.back() & (1 << j)) != 0) ^ negative; + b.bits().push_back(bv ? RTLIL::S1 : RTLIL::S0); + } + buf.pop_back(); + } + + if (is_signed) { + b.bits().push_back(negative ? RTLIL::S1 : RTLIL::S0); + } + + return true; +} + static int tcl_get_attr(ClientData, Tcl_Interp *interp, int argc, const char *argv[]) { int i; - bool mod_flag = false, string_flag = false, int_flag = false, bool_flag = false; + bool mod_flag = false, string_flag = false, bool_flag = false; + bool int_flag = false, sint_flag = false, uint_flag = false; for (i = 1; i < argc; i++) { FLAG(mod) FLAG(string) FLAG(int) + FLAG(sint) + FLAG(uint) FLAG(bool) break; } if ((mod_flag && i != argc - 2) || (!mod_flag && i != argc - 3) || - (string_flag + int_flag + bool_flag > 1)) - ERROR("bad usage: expected \"get_attr -mod [-string|-int|-bool] \"" - " or \"get_attr [-string|-int|-bool] \"") + (string_flag + int_flag + sint_flag + uint_flag + bool_flag > 1)) + ERROR("bad usage: expected \"get_attr -mod [-string|-int|-sint|-uint|-bool] \"" + " or \"get_attr [-string|-int|-sint|-uint|-bool] \"") IdString mod_id, obj_id, attr_id; mod_id = RTLIL::escape_id(argv[i++]); @@ -197,19 +279,17 @@ static int tcl_get_attr(ClientData, Tcl_Interp *interp, int argc, const char *ar if (string_flag) { Tcl_SetResult(interp, (char *) obj->get_string_attribute(attr_id).c_str(), TCL_VOLATILE); - } else if (int_flag) { + } else if (int_flag || uint_flag || sint_flag) { if (!obj->has_attribute(attr_id)) ERROR("attribute missing (required for -int)"); - RTLIL::Const &value = obj->attributes.at(attr_id); - if (value.size() > 32) - ERROR("value too large") - // FIXME: 32'hffffffff will return as negative despite is_signed=false - Tcl_SetResult(interp, (char *) std::to_string(value.as_int()).c_str(), TCL_VOLATILE); + mp_int value_mp; + if (!const_to_mp_int(value, &value_mp, sint_flag, uint_flag)) + ERROR("bignum manipulation failed"); + Tcl_SetObjResult(interp, Tcl_NewBignumObj(&value_mp)); } else if (bool_flag) { - bool value = obj->get_bool_attribute(attr_id); - Tcl_SetResult(interp, (char *) std::to_string(value).c_str(), TCL_VOLATILE); + Tcl_SetObjResult(interp, Tcl_NewBooleanObj(obj->get_bool_attribute(attr_id))); } else { if (!obj->has_attribute(attr_id)) ERROR("attribute missing (required unless -bool or -string)") @@ -264,33 +344,34 @@ static int tcl_has_attr(ClientData, Tcl_Interp *interp, int argc, const char *ar return TCL_OK; } -static int tcl_set_attr(ClientData, Tcl_Interp *interp, int argc, const char *argv[]) +static int tcl_set_attr(ClientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { int i; - bool mod_flag = false, string_flag = false, int_flag = false, bool_flag = false; - bool false_flag = false, true_flag = false; - for (i = 1; i < argc; i++) { - FLAG(mod) - FLAG(string) - FLAG(int) - FLAG(bool) - FLAG(false) - FLAG(true) + bool mod_flag = false, string_flag = false, bool_flag = false; + bool true_flag = false, false_flag = false, sint_flag = false, uint_flag = false; + for (i = 1; i < objc; i++) { + FLAG2(mod) + FLAG2(string) + FLAG2(true) + FLAG2(false) + FLAG2(sint) + FLAG2(uint) + FLAG2(bool) break; } - if ((i != argc - (2 + !mod_flag + !(true_flag || false_flag))) || - (string_flag + int_flag + bool_flag + true_flag + false_flag > 1)) - ERROR("bad usage: expected \"set_attr -mod [-string|-int|-bool] \"" - " or \"set_attr [-string|-int|-bool] \"" + if ((i != objc - (2 + !mod_flag + !(true_flag || false_flag))) || + (string_flag + sint_flag + uint_flag + bool_flag + true_flag + false_flag > 1)) + ERROR("bad usage: expected \"set_attr -mod [-string|-sint|-uint|-bool] \"" + " or \"set_attr [-string|-sint|-uint|-bool] \"" " or \"set_attr [-true|-false] \"" " or \"set_attr -mod [-true|-false| \"") IdString mod_id, obj_id, attr_id; - mod_id = RTLIL::escape_id(argv[i++]); + mod_id = RTLIL::escape_id(Tcl_GetString(objv[i++])); if (!mod_flag) - obj_id = RTLIL::escape_id(argv[i++]); - attr_id = RTLIL::escape_id(argv[i++]); + obj_id = RTLIL::escape_id(Tcl_GetString(objv[i++])); + attr_id = RTLIL::escape_id(Tcl_GetString(objv[i++])); RTLIL::Module *mod = yosys_design->module(mod_id); if (!mod) @@ -313,17 +394,35 @@ static int tcl_set_attr(ClientData, Tcl_Interp *interp, int argc, const char *ar ERROR("object not found") if (string_flag) { - obj->set_string_attribute(attr_id, argv[i++]); - } else if (int_flag) { - obj->attributes[attr_id] = atoi(argv[i++]); + obj->set_string_attribute(attr_id, Tcl_GetString(objv[i++])); + } else if (sint_flag || uint_flag) { + RTLIL::Const const_; + mp_int value_mp; + + if (Tcl_TakeBignumFromObj(interp, objv[i++], &value_mp)) + ERROR("non-integral value") + + if (!mp_int_to_const(&value_mp, const_, sint_flag)) + ERROR("bignum manipulation failed"); + + if (sint_flag) { + const_.flags |= RTLIL::CONST_FLAG_SIGNED; + if (const_.size() < 32) + const_.exts(32); + } else { + if (const_.size() < 32) + const_.extu(32); + } + + obj->attributes[attr_id] = const_; } else if (bool_flag) { - obj->set_bool_attribute(attr_id, atoi(argv[i++]) != 0); + obj->set_bool_attribute(attr_id, atoi(Tcl_GetString(objv[i++])) != 0); } else if (true_flag) { obj->set_bool_attribute(attr_id, true); } else if (false_flag) { obj->set_bool_attribute(attr_id, false); } else { - obj->attributes[attr_id] = Const::from_string(std::string(argv[i++])); + obj->attributes[attr_id] = Const::from_string(std::string(Tcl_GetString(objv[i++]))); } return TCL_OK; @@ -332,10 +431,14 @@ static int tcl_set_attr(ClientData, Tcl_Interp *interp, int argc, const char *ar static int tcl_get_param(ClientData, Tcl_Interp *interp, int argc, const char *argv[]) { int i; - bool string_flag = false, int_flag = false; + bool string_flag = false, bool_flag = false; + bool int_flag = false, sint_flag = false, uint_flag = false; for (i = 1; i < argc; i++) { FLAG(string) FLAG(int) + FLAG(sint) + FLAG(uint) + FLAG(bool) break; } @@ -363,35 +466,36 @@ static int tcl_get_param(ClientData, Tcl_Interp *interp, int argc, const char *a if (string_flag) { Tcl_SetResult(interp, (char *) value.decode_string().c_str(), TCL_VOLATILE); - } else if (int_flag) { - if (value.size() > 32) - ERROR("value too large") - - Tcl_SetResult(interp, (char *) std::to_string(value.as_int()).c_str(), TCL_VOLATILE); + } else if (int_flag || uint_flag || sint_flag) { + mp_int value_mp; + if (!const_to_mp_int(value, &value_mp, sint_flag, uint_flag)) + ERROR("bignum manipulation failed"); + Tcl_SetObjResult(interp, Tcl_NewBignumObj(&value_mp)); } else { Tcl_SetResult(interp, (char *) value.as_string().c_str(), TCL_VOLATILE); } return TCL_OK; } -static int tcl_set_param(ClientData, Tcl_Interp *interp, int argc, const char *argv[]) +static int tcl_set_param(ClientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { int i; - bool string_flag = false, int_flag = false; - for (i = 1; i < argc; i++) { - FLAG(string) - FLAG(int) + bool string_flag = false, sint_flag = false, uint_flag = false; + for (i = 1; i < objc; i++) { + FLAG2(string) + FLAG2(sint) + FLAG2(uint) break; } - if ((i != argc - 4) || - (string_flag + int_flag > 1)) - ERROR("bad usage: expected \"get_param [-string|-int] ") + if ((i != objc - 4) || + (string_flag + sint_flag + uint_flag > 1)) + ERROR("bad usage: expected \"set_param [-string|-sint|-uint] ") IdString mod_id, cell_id, param_id; - mod_id = RTLIL::escape_id(argv[i++]); - cell_id = RTLIL::escape_id(argv[i++]); - param_id = RTLIL::escape_id(argv[i++]); + mod_id = RTLIL::escape_id(Tcl_GetString(objv[i++])); + cell_id = RTLIL::escape_id(Tcl_GetString(objv[i++])); + param_id = RTLIL::escape_id(Tcl_GetString(objv[i++])); RTLIL::Module *mod = yosys_design->module(mod_id); if (!mod) @@ -402,11 +506,29 @@ static int tcl_set_param(ClientData, Tcl_Interp *interp, int argc, const char *a ERROR("object not found") if (string_flag) { - cell->setParam(param_id, Const(std::string(argv[i++]))); - } else if (int_flag) { - cell->setParam(param_id, Const(atoi(argv[i++]))); + cell->setParam(param_id, Const(std::string(Tcl_GetString(objv[i++])))); + } else if (sint_flag || uint_flag) { + RTLIL::Const const_; + mp_int value_mp; + + if (Tcl_TakeBignumFromObj(interp, objv[i++], &value_mp)) + ERROR("non-integral value") + + if (!mp_int_to_const(&value_mp, const_, sint_flag)) + ERROR("bignum manipulation failed"); + + if (sint_flag) { + const_.flags |= RTLIL::CONST_FLAG_SIGNED; + if (const_.size() < 32) + const_.exts(32); + } else { + if (const_.size() < 32) + const_.extu(32); + } + + cell->setParam(param_id, const_); } else { - cell->setParam(param_id, Const::from_string(std::string(argv[i++]))); + cell->setParam(param_id, Const::from_string(std::string(Tcl_GetString(objv[i++])))); } return TCL_OK; } @@ -418,9 +540,9 @@ int yosys_tcl_iterp_init(Tcl_Interp *interp) Tcl_CreateCommand(interp, "yosys", tcl_yosys_cmd, NULL, NULL); Tcl_CreateCommand(interp, "rtlil::get_attr", tcl_get_attr, NULL, NULL); Tcl_CreateCommand(interp, "rtlil::has_attr", tcl_has_attr, NULL, NULL); - Tcl_CreateCommand(interp, "rtlil::set_attr", tcl_set_attr, NULL, NULL); + Tcl_CreateObjCommand(interp, "rtlil::set_attr", tcl_set_attr, NULL, NULL); Tcl_CreateCommand(interp, "rtlil::get_param", tcl_get_param, NULL, NULL); - Tcl_CreateCommand(interp, "rtlil::set_param", tcl_set_param, NULL, NULL); + Tcl_CreateObjCommand(interp, "rtlil::set_param", tcl_set_param, NULL, NULL); // TODO: // @@ -442,6 +564,10 @@ int yosys_tcl_iterp_init(Tcl_Interp *interp) // unpack // pack + // Note (dev jf 24-12-02): Make log_id escape everything that’s not a valid + // verilog identifier before adding any tcl API that returns IdString values + // to avoid -option injection + return TCL_OK ; } #endif diff --git a/tests/various/tcl_apis.tcl b/tests/various/tcl_apis.tcl index 74f1aaa52be..ddb4437d4b9 100644 --- a/tests/various/tcl_apis.tcl +++ b/tests/various/tcl_apis.tcl @@ -4,13 +4,60 @@ if {[rtlil::get_attr -string -mod top foo] != "bar"} { error "bad top module attribute" } +if {[rtlil::get_attr -int -mod top val] != 4294967295} { + error "bad top module attribute 2" +} + +if {[rtlil::get_attr -sint -mod top val] != -1} { + error "bad top module attribute 3" +} + if {[rtlil::get_attr -bool top w dont_touch] != 1} { error "bad w wire attribute" } -if {[rtlil::get_param -int top inst PARAM] != 4} { +if {[rtlil::get_param -int top inst PARAM] != -3} { error "bad parameter" } +if {[rtlil::get_param -uint top inst PARAM] != 4294967293} { + error "bad parameter 2" +} rtlil::set_attr -true -mod top marked yosys select -assert-any A:marked + +# write a 32-bit constant with most bits set +rtlil::set_attr -mod -uint top f 4294967294 +# read it back as a signed integer +if {[rtlil::get_attr -mod -sint top f] != -2} { + error "bad int roundtrip" +} +# read it back as an unsigned integer (no signedness flag) +if {[rtlil::get_attr -mod -int top f] != 4294967294} { + error "bad int roundtrip 2" +} +# read it back as an unsigned integer +if {[rtlil::get_attr -mod -uint top f] != 4294967294} { + error "bad int roundtrip 3" +} + +# write a signed 32-bit constant +rtlil::set_attr -mod -sint top f -3 +# read it back as a signed integer +if {[rtlil::get_attr -mod -sint top f] != -3} { + error "bad int roundtrip 4" +} +# read it back as a signed integer (due to signedness flag) +if {[rtlil::get_attr -mod -int top f] != -3} { + error "bad int roundtrip 5" +} +# read it back as an unsigned integer +if {[rtlil::get_attr -mod -uint top f] != 4294967293} { + error "bad int roundtrip 6" +} + +# write a constant larger than 32 bits +rtlil::set_attr -mod -sint top prime 87178291199 +if {[rtlil::get_attr -mod -int top prime] != 87178291199} { + error "bad int roundtrip 7" +} diff --git a/tests/various/tcl_apis.v b/tests/various/tcl_apis.v index cc379c20aa4..993f08e8b0c 100644 --- a/tests/various/tcl_apis.v +++ b/tests/various/tcl_apis.v @@ -3,9 +3,10 @@ parameter PARAM = 0; endmodule (* foo="bar" *) +(* val=32'hffffffff *) module top; (* dont_touch *) wire w; - m #(.PARAM(4)) inst(); + m #(.PARAM(-3)) inst(); endmodule From cac6dd9bcbd2f43c40cca565b7ba4928dd0b7bf8 Mon Sep 17 00:00:00 2001 From: Jannis Harder Date: Tue, 3 Dec 2024 17:53:00 +0100 Subject: [PATCH 6/7] tclapi: Use older bignum API and handle external tommath for mac os --- Makefile | 3 +++ kernel/tclapi.cc | 5 ++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 5407433cfda..90baa5f6977 100644 --- a/Makefile +++ b/Makefile @@ -449,6 +449,9 @@ LIBS += -ltcl86 -lwsock32 -lws2_32 -lnetapi32 -lz -luserenv else CXXFLAGS += $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) $(PKG_CONFIG) --silence-errors --cflags tcl || echo -I$(TCL_INCLUDE)) -DYOSYS_ENABLE_TCL LIBS += $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) $(PKG_CONFIG) --silence-errors --libs tcl || echo $(TCL_LIBS)) +ifneq (,$(findstring TCL_WITH_EXTERNAL_TOMMATH,$(CXXFLAGS))) +LIBS += $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) $(PKG_CONFIG) --silence-errors --libs libtommath || echo) +endif endif endif diff --git a/kernel/tclapi.cc b/kernel/tclapi.cc index f6728be1539..4516f73cf20 100644 --- a/kernel/tclapi.cc +++ b/kernel/tclapi.cc @@ -210,9 +210,8 @@ bool mp_int_to_const(mp_int *a, Const &b, bool is_signed) } std::vector buf; - buf.resize(mp_ubin_size(a)); - size_t written; // dummy - mp_to_ubin(a, buf.data(), buf.size(), &written); + buf.resize(mp_unsigned_bin_size(a)); + mp_to_unsigned_bin(a, buf.data()); b.bits().reserve(mp_count_bits(a) + is_signed); for (int i = 0; i < mp_count_bits(a);) { From 81de54f436ce1ad478f83897b3b8a821251c6915 Mon Sep 17 00:00:00 2001 From: Jannis Harder Date: Tue, 3 Dec 2024 18:59:52 +0100 Subject: [PATCH 7/7] tclapi: Remove unused -bool flag from get_param and document -[su]int --- kernel/tclapi.cc | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/kernel/tclapi.cc b/kernel/tclapi.cc index 4516f73cf20..91874d5b910 100644 --- a/kernel/tclapi.cc +++ b/kernel/tclapi.cc @@ -430,20 +430,19 @@ static int tcl_set_attr(ClientData, Tcl_Interp *interp, int objc, Tcl_Obj *const static int tcl_get_param(ClientData, Tcl_Interp *interp, int argc, const char *argv[]) { int i; - bool string_flag = false, bool_flag = false; + bool string_flag = false; bool int_flag = false, sint_flag = false, uint_flag = false; for (i = 1; i < argc; i++) { FLAG(string) FLAG(int) FLAG(sint) FLAG(uint) - FLAG(bool) break; } if ((i != argc - 3) || (string_flag + int_flag > 1)) - ERROR("bad usage: expected \"get_param [-string|-int] ") + ERROR("bad usage: expected \"get_param [-string|-int|-sint|-uint] ") IdString mod_id, cell_id, param_id; mod_id = RTLIL::escape_id(argv[i++]);