mirror of https://github.com/YosysHQ/yosys.git
Merge pull request #4678 from povik/tcl-rtlil-api
Start Tcl design inspection methods
This commit is contained in:
commit
b0708a38bf
5
Makefile
5
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
|
||||
|
||||
|
@ -639,7 +642,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)
|
||||
|
|
|
@ -0,0 +1,573 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
|
||||
*
|
||||
* 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"
|
||||
|
||||
#ifdef YOSYS_ENABLE_TCL
|
||||
#include "tclTomMath.h"
|
||||
#include "tclTomMathDecls.h"
|
||||
#endif
|
||||
|
||||
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<std::string> 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 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<unsigned char> buf;
|
||||
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);) {
|
||||
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, 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 + sint_flag + uint_flag + bool_flag > 1))
|
||||
ERROR("bad usage: expected \"get_attr -mod [-string|-int|-sint|-uint|-bool] <module> <attrname>\""
|
||||
" or \"get_attr [-string|-int|-sint|-uint|-bool] <module> <identifier> <attrname>\"")
|
||||
|
||||
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 || uint_flag || sint_flag) {
|
||||
if (!obj->has_attribute(attr_id))
|
||||
ERROR("attribute missing (required for -int)");
|
||||
RTLIL::Const &value = obj->attributes.at(attr_id);
|
||||
|
||||
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) {
|
||||
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)")
|
||||
|
||||
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 <module> <attrname>\""
|
||||
" or \"has_attr <module> <identifier> <attrname>\"")
|
||||
|
||||
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 objc, Tcl_Obj *const objv[])
|
||||
{
|
||||
int i;
|
||||
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 != 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] <module> <attrname> <value>\""
|
||||
" or \"set_attr [-string|-sint|-uint|-bool] <module> <identifier> <attrname> <value>\""
|
||||
" or \"set_attr [-true|-false] <module> <identifier> <attrname>\""
|
||||
" or \"set_attr -mod [-true|-false| <module> <attrname>\"")
|
||||
|
||||
IdString mod_id, obj_id, attr_id;
|
||||
mod_id = RTLIL::escape_id(Tcl_GetString(objv[i++]));
|
||||
if (!mod_flag)
|
||||
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)
|
||||
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, 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(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(Tcl_GetString(objv[i++])));
|
||||
}
|
||||
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
static int tcl_get_param(ClientData, Tcl_Interp *interp, int argc, const char *argv[])
|
||||
{
|
||||
int i;
|
||||
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)
|
||||
break;
|
||||
}
|
||||
|
||||
if ((i != argc - 3) ||
|
||||
(string_flag + int_flag > 1))
|
||||
ERROR("bad usage: expected \"get_param [-string|-int|-sint|-uint] <module> <cellid> <paramname>")
|
||||
|
||||
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 || 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 objc, Tcl_Obj *const objv[])
|
||||
{
|
||||
int i;
|
||||
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 != objc - 4) ||
|
||||
(string_flag + sint_flag + uint_flag > 1))
|
||||
ERROR("bad usage: expected \"set_param [-string|-sint|-uint] <module> <cellid> <paramname> <value>")
|
||||
|
||||
IdString mod_id, cell_id, param_id;
|
||||
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)
|
||||
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(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(Tcl_GetString(objv[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_CreateObjCommand(interp, "rtlil::set_attr", tcl_set_attr, NULL, NULL);
|
||||
Tcl_CreateCommand(interp, "rtlil::get_param", tcl_get_param, NULL, NULL);
|
||||
Tcl_CreateObjCommand(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
|
||||
|
||||
// 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
|
||||
|
||||
YOSYS_NAMESPACE_END
|
125
kernel/yosys.cc
125
kernel/yosys.cc
|
@ -73,8 +73,6 @@
|
|||
#include <limits.h>
|
||||
#include <errno.h>
|
||||
|
||||
#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<std::string> yosys_input_files, yosys_output_files;
|
||||
|
@ -722,126 +719,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<std::string> 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;
|
||||
}
|
||||
|
||||
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);
|
||||
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()
|
||||
{
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
yosys read_verilog tcl_apis.v
|
||||
|
||||
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] != -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"
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
module m;
|
||||
parameter PARAM = 0;
|
||||
endmodule
|
||||
|
||||
(* foo="bar" *)
|
||||
(* val=32'hffffffff *)
|
||||
module top;
|
||||
(* dont_touch *)
|
||||
wire w;
|
||||
|
||||
m #(.PARAM(-3)) inst();
|
||||
endmodule
|
|
@ -0,0 +1 @@
|
|||
tcl tcl_apis.tcl
|
Loading…
Reference in New Issue