Introspection: Add initial version of plugin with get_nets command Signed-off-by: Tomasz Michalak <tmichalak@antmicro.com>
diff --git a/Makefile b/Makefile index 72611b4..c397859 100644 --- a/Makefile +++ b/Makefile
@@ -1,4 +1,4 @@ -PLUGIN_LIST := fasm xdc params selection sdc get_count ql-iob +PLUGIN_LIST := fasm xdc params selection sdc get_count ql-iob design_introspection PLUGINS := $(foreach plugin,$(PLUGIN_LIST),$(plugin).so) PLUGINS_INSTALL := $(foreach plugin,$(PLUGIN_LIST),install_$(plugin)) PLUGINS_CLEAN := $(foreach plugin,$(PLUGIN_LIST),clean_$(plugin))
diff --git a/design_introspection-plugin/Makefile b/design_introspection-plugin/Makefile new file mode 100644 index 0000000..1b65892 --- /dev/null +++ b/design_introspection-plugin/Makefile
@@ -0,0 +1,26 @@ +CXX = $(shell yosys-config --cxx) +CXXFLAGS = $(shell yosys-config --cxxflags) +LDFLAGS = $(shell yosys-config --ldflags) +LDLIBS = $(shell yosys-config --ldlibs) +PLUGINS_DIR = $(shell yosys-config --datdir)/plugins + +NAME = design_introspection +OBJS = $(NAME).o get_nets.o + +$(NAME).so: $(OBJS) + $(CXX) $(CXXFLAGS) $(LDFLAGS) -shared -o $@ $^ $(LDLIBS) + +install_plugin: $(NAME).so + mkdir -p $(PLUGINS_DIR) + cp $< $(PLUGINS_DIR)/$< + +test: + $(MAKE) -C tests all + +.PHONY: install +install: install_plugin + +clean: + rm -f *.d *.o *.so + $(MAKE) -C tests clean +
diff --git a/design_introspection-plugin/design_introspection.cc b/design_introspection-plugin/design_introspection.cc new file mode 100644 index 0000000..51921b0 --- /dev/null +++ b/design_introspection-plugin/design_introspection.cc
@@ -0,0 +1,33 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2019 The Symbiflow Authors + * + * 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 "get_nets.h" + +USING_YOSYS_NAMESPACE + +PRIVATE_NAMESPACE_BEGIN + +struct DesignIntrospection { + DesignIntrospection(){} + GetNets get_nets_cmd; +} DesignIntrospection; + + +PRIVATE_NAMESPACE_END
diff --git a/design_introspection-plugin/get_nets.cc b/design_introspection-plugin/get_nets.cc new file mode 100644 index 0000000..4b5e4a0 --- /dev/null +++ b/design_introspection-plugin/get_nets.cc
@@ -0,0 +1,138 @@ +#include "get_nets.h" +#include <regex> +#include "kernel/log.h" +#include "kernel/register.h" +#include "kernel/rtlil.h" + +USING_YOSYS_NAMESPACE + +void GetNets::help() { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" get_nets [-quiet] [-filter filter_expression] <net_selection> \n"); + log("\n"); + log("Get matching nets\n"); + log("\n"); + log("Print the output to stdout too. This is useful when all Yosys is " + "executed.\n"); + log("\n"); + log(" -filter\n"); + log(" Name and value of attribute to be taken into account.\n"); + log(" e.g. -filter { attr == \"true\" }\n"); + log("\n"); + log(" -quiet\n"); + log(" Don't print the result of the execution to stdout.\n"); + log("\n"); + log(" <net_selection>\n"); + log(" Selection of net name. Default are all nets in the design.\n"); + log("\n"); +} + +void GetNets::execute(std::vector<std::string> args, RTLIL::Design* design) { + RTLIL::Module* top_module = design->top_module(); + if (top_module == nullptr) { + log_cmd_error("No top module detected\n"); + } + + size_t argidx; + std::vector<std::pair<std::string, std::string>> filters; + bool is_quiet = false; + bool has_filter = false; + + // Parse command arguments + for (argidx = 1; argidx < args.size(); argidx++) { + std::string arg = args[argidx]; + if (arg == "-quiet") { + is_quiet = true; + continue; + } + + if (arg == "-filter" and argidx + 1 < args.size()) { + std::string filter_arg = args[++argidx]; + + // Remove spaces + filter_arg.erase( + std::remove_if(filter_arg.begin(), filter_arg.end(), isspace), + filter_arg.end()); + + // Parse filters + std::regex filter_attr_regex("(\\w+\\s?==\\s?\\w+)([(||)(&&)]*)"); + std::regex_token_iterator<std::string::iterator> regex_end; + std::regex_token_iterator<std::string::iterator> matches( + filter_arg.begin(), filter_arg.end(), filter_attr_regex, 1); + if (matches == regex_end) { + log_warning( + "Currently -filter switch supports only a single " + "'equal(==)' condition expression, the rest will be " + "ignored\n"); + } + + while (matches != regex_end) { + std::string filter(*matches++); + auto separator = filter.find("=="); + if (separator == std::string::npos) { + log_cmd_error("Incorrect filter expression: %s\n", + args[argidx].c_str()); + } + filters.emplace_back(filter.substr(0, separator), + filter.substr(separator + 2)); + } + size_t filter_cnt = filters.size(); + has_filter = filter_cnt > 0; + if (filter_cnt > 1) { + log_warning( + "Currently -filter switch supports only a single " + "'equal(==)' condition expression, the rest will be " + "ignored\n"); + } + continue; + } + + if (arg.size() > 0 and arg[0] == '-') { + log_cmd_error("Unknown option %s.\n", arg.c_str()); + } + + break; + } + + // Add name of top module to selection string + std::vector<std::string> selection_args; + std::transform(args.begin() + argidx, args.end(), + std::back_inserter(selection_args), [&](std::string& net) { + return RTLIL::unescape_id(top_module->name) + + "/w:" + net; + }); + + // Execute the selection + extra_args(selection_args, 0, design); + if (design->selected_modules().empty()) { + if (!is_quiet) { + log_warning("Specified net not found in design\n"); + } + } + + // Pack the selected nets into Tcl List + Tcl_Interp* interp = yosys_get_tcl_interp(); + Tcl_Obj* tcl_list = Tcl_NewListObj(0, NULL); + for (auto module : design->selected_modules()) { + for (auto wire : module->selected_wires()) { + if (has_filter) { + std::pair<std::string, std::string> filter = filters.at(0); + std::string attr_value = wire->get_string_attribute( + RTLIL::IdString(RTLIL::escape_id(filter.first))); + if (attr_value.compare(filter.second)) { + continue; + } + } + if (!is_quiet) { + log("%s ", id2cstr(wire->name)); + } + Tcl_Obj* value_obj = Tcl_NewStringObj(id2cstr(wire->name), -1); + Tcl_ListObjAppendElement(interp, tcl_list, value_obj); + } + } + if (!is_quiet) { + log("\n"); + } + Tcl_SetObjResult(interp, tcl_list); +}
diff --git a/design_introspection-plugin/get_nets.h b/design_introspection-plugin/get_nets.h new file mode 100644 index 0000000..8b66abe --- /dev/null +++ b/design_introspection-plugin/get_nets.h
@@ -0,0 +1,10 @@ +#include "kernel/register.h" + +USING_YOSYS_NAMESPACE + +struct GetNets : public Pass { + GetNets() : Pass("get_nets", "Print matching nets") {} + + void help() override; + void execute(std::vector<std::string> args, RTLIL::Design* design) override; +};
diff --git a/design_introspection-plugin/tests/Makefile b/design_introspection-plugin/tests/Makefile new file mode 100644 index 0000000..e31bab6 --- /dev/null +++ b/design_introspection-plugin/tests/Makefile
@@ -0,0 +1,32 @@ +TESTS = get_nets +.PHONY: $(TESTS) + +get_nets_verify = $(call compare,get_nets,txt) + +all: $(TESTS) +compare = diff $(1)/$(1).golden.$(2) $(1)/$(1).$(2) + +define test_tpl = +$(1): $(1)/$(1).sdc + $$($(1)_verify) + RETVAL=$$$$? ; \ + if [ $$$$RETVAL -eq 0 ]; then \ + echo "$(1) PASS"; \ + true; \ + else \ + echo "$(1) FAIL"; \ + false; \ + fi + +$(1)/$(1).sdc: $(1)/$(1).v + cd $(1); \ + INPUT_SDC_FILE=$(1).input.sdc \ + OUTPUT_SDC_FILE=$(1).sdc \ + yosys -p "tcl $(1).tcl" -l yosys.log + +endef + +$(foreach test,$(TESTS),$(eval $(call test_tpl,$(test)))) + +clean: + rm -rf $(foreach test,$(TESTS),$(test)/$(test).sdc $(test)/$(test).txt $(test)/yosys.log)