Merge pull request #29 from antmicro/selection_plugin

Selection plugin: Initial version of selection_to_tcl_list command
diff --git a/Makefile b/Makefile
index fe3f92d..6818481 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,4 @@
-PLUGIN_LIST := fasm xdc params
+PLUGIN_LIST := fasm xdc params selection
 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/selection-plugin/Makefile b/selection-plugin/Makefile
new file mode 100644
index 0000000..6c6f96f
--- /dev/null
+++ b/selection-plugin/Makefile
@@ -0,0 +1,25 @@
+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
+
+OBJS = selection.o
+
+selection.so: $(OBJS)
+	$(CXX) $(CXXFLAGS) $(LDFLAGS) -shared -o $@ $^ $(LDLIBS)
+
+install_plugin: selection.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/selection-plugin/selection.cc b/selection-plugin/selection.cc
new file mode 100644
index 0000000..6f224ff
--- /dev/null
+++ b/selection-plugin/selection.cc
@@ -0,0 +1,91 @@
+/*
+ *  yosys -- Yosys Open SYnthesis Suite
+ *
+ *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at>
+ *  Copyright (C) 2020  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 "kernel/register.h"
+#include "kernel/rtlil.h"
+#include "kernel/log.h"
+
+USING_YOSYS_NAMESPACE
+
+PRIVATE_NAMESPACE_BEGIN
+
+
+struct SelectionToTclList : public Pass {
+	SelectionToTclList() : Pass("selection_to_tcl_list", "Extract selection to TCL list") {}
+
+	void help() override
+	{
+		//   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+		log("\n");
+		log("   selection_to_tcl_list selection\n");
+		log("\n");
+		log("Extract the current selection to a Tcl List with selection object names. \n");
+		log("\n");
+	}
+
+	void AddObjectNameToTclList(RTLIL::IdString& module, RTLIL::IdString& object, Tcl_Obj* tcl_list) {
+		std::string name = RTLIL::unescape_id(module) + "/" + RTLIL::unescape_id(object);
+		Tcl_Obj* value_obj = Tcl_NewStringObj(name.c_str(), name.size());
+		Tcl_ListObjAppendElement(yosys_get_tcl_interp(), tcl_list, value_obj);
+	}
+
+	void execute(std::vector<std::string> args, RTLIL::Design* design) override
+	{
+		if (args.size() == 1) {
+			log_error("Incorrect number of arguments");
+		}
+		extra_args(args, 1, design);
+
+		Tcl_Interp *interp = yosys_get_tcl_interp();
+		Tcl_Obj* tcl_list = Tcl_NewListObj(0, NULL);
+
+		auto& selection = design->selection();
+		if (selection.empty()) {
+		        log_warning("Selection is empty\n");
+		}
+
+                for (auto mod : design->modules()) {
+                        if (selection.selected_module(mod->name)) {
+                                for (auto wire : mod->wires()) {
+                                        if (selection.selected_member(mod->name, wire->name)) {
+						AddObjectNameToTclList(mod->name, wire->name, tcl_list);
+					}
+				}
+                                for (auto &it : mod->memories) {
+                                        if (selection.selected_member(mod->name, it.first)) {
+						AddObjectNameToTclList(mod->name, it.first, tcl_list);
+					}
+				}
+                                for (auto cell : mod->cells()) {
+                                        if (selection.selected_member(mod->name, cell->name)) {
+						AddObjectNameToTclList(mod->name, cell->name, tcl_list);
+					}
+				}
+                                for (auto &it : mod->processes) {
+                                        if (selection.selected_member(mod->name, it.first)) {
+						AddObjectNameToTclList(mod->name, it.first, tcl_list);
+					}
+				}
+                        }
+                }
+		Tcl_SetObjResult(interp, tcl_list);
+	}
+
+} SelectionToTclList;
+
+PRIVATE_NAMESPACE_END
diff --git a/selection-plugin/tests/Makefile b/selection-plugin/tests/Makefile
new file mode 100644
index 0000000..e29a406
--- /dev/null
+++ b/selection-plugin/tests/Makefile
@@ -0,0 +1,32 @@
+TESTS = counter
+.PHONY: $(TESTS)
+
+counter_verify = $(call compare,counter,txt)
+
+all: $(TESTS)
+compare = diff $(1)/$(1).golden.$(2) $(1)/$(1).$(2)
+
+define test_tpl =
+$(1): $(1)/$(1).txt
+	$$($(1)_verify)
+	RETVAL=$$$$? ; \
+	if [ $$$$RETVAL -eq 0 ]; then \
+		echo "$(1) PASS"; \
+		true; \
+	else \
+		echo "$(1) FAIL"; \
+		false; \
+	fi
+
+$(1)/$(1).txt: $(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)
diff --git a/selection-plugin/tests/counter/counter.golden.txt b/selection-plugin/tests/counter/counter.golden.txt
new file mode 100644
index 0000000..7122e8b
--- /dev/null
+++ b/selection-plugin/tests/counter/counter.golden.txt
@@ -0,0 +1,3 @@
+{middle/$add$counter.v:32$5} top/middle_inst_4 top/middle_inst_3 top/middle_inst_2 top/middle_inst_1 {top/$add$counter.v:14$2} top/ibuf_inst top/ibuf_proxy
+{middle/$1\cnt[1:0]} {middle/$add$counter.v:32$5_Y} {middle/$0\cnt[1:0]} middle/clk_int middle/cnt middle/out middle/clk {top/$1\cnt[1:0]} {top/$add$counter.v:14$2_Y} {top/$0\cnt[1:0]} top/ibuf_out top/ibuf_proxy_out top/clk_int_2 top/clk_int_1 top/cnt top/out top/in top/clk2 top/clk
+{middle/$1\cnt[1:0]} {middle/$add$counter.v:32$5_Y} {middle/$0\cnt[1:0]} middle/clk_int middle/cnt middle/out middle/clk {middle/$add$counter.v:32$5} {middle/$proc$counter.v:28$6} {middle/$proc$counter.v:31$4} {top/$1\cnt[1:0]} {top/$add$counter.v:14$2_Y} {top/$0\cnt[1:0]} top/ibuf_out top/ibuf_proxy_out top/clk_int_2 top/clk_int_1 top/cnt top/out top/in top/clk2 top/clk top/middle_inst_4 top/middle_inst_3 top/middle_inst_2 top/middle_inst_1 {top/$add$counter.v:14$2} top/ibuf_inst top/ibuf_proxy {top/$proc$counter.v:6$3} {top/$proc$counter.v:13$1}
diff --git a/selection-plugin/tests/counter/counter.tcl b/selection-plugin/tests/counter/counter.tcl
new file mode 100644
index 0000000..5475822
--- /dev/null
+++ b/selection-plugin/tests/counter/counter.tcl
@@ -0,0 +1,43 @@
+yosys -import
+plugin -i selection
+
+# Import the commands from the plugins to the tcl interpreter
+yosys -import
+
+proc selection_to_tcl_list_through_file { selection } {
+    set file_name "[pid].txt"
+    select $selection -write $file_name
+    set fh [open $file_name r]
+    set result [list]
+    while {[gets $fh line] >= 0} {
+	lappend result $line
+    }
+    close $fh
+    file delete $file_name
+    return $result
+}
+
+proc test_selection { rfh selection } {
+    if {[expr {[selection_to_tcl_list_through_file $selection] != [selection_to_tcl_list $selection]}]} {
+    	puts "List from file: [selection_to_tcl_list_through_file $selection]"
+    	puts "List in selection: [selection_to_tcl_list $selection]"
+	error "Test with selection: $selection failed"
+    } else {
+	puts $rfh [selection_to_tcl_list $selection]
+    }
+}
+
+read_verilog counter.v
+read_verilog -specify -lib -D_EXPLICIT_CARRY +/xilinx/cells_sim.v
+read_verilog -lib +/xilinx/cells_xtra.v
+hierarchy -check -auto-top
+
+# Test the selection command and write results to file
+set rfh [open counter.txt w]
+
+set selection_tests [list "t:*" "w:*" "*"]
+foreach test $selection_tests {
+    test_selection $rfh $test
+}
+
+close $rfh
diff --git a/selection-plugin/tests/counter/counter.v b/selection-plugin/tests/counter/counter.v
new file mode 100644
index 0000000..564fae5
--- /dev/null
+++ b/selection-plugin/tests/counter/counter.v
@@ -0,0 +1,36 @@
+module top(input clk,
+        input clk2,
+	input [1:0] in,
+	output [5:0] out );
+
+reg [1:0] cnt = 0;
+wire clk_int_1, clk_int_2;
+IBUF ibuf_proxy(.I(clk), .O(ibuf_proxy_out));
+IBUF ibuf_inst(.I(ibuf_proxy_out), .O(ibuf_out));
+assign clk_int_1 = ibuf_out;
+assign clk_int_2 = clk_int_1;
+
+always @(posedge clk_int_2) begin
+	cnt <= cnt + 1;
+end
+
+middle middle_inst_1(.clk(ibuf_out), .out(out[2]));
+middle middle_inst_2(.clk(clk_int_1), .out(out[3]));
+middle middle_inst_3(.clk(clk_int_2), .out(out[4]));
+middle middle_inst_4(.clk(clk2), .out(out[5]));
+
+assign out[1:0] = {cnt[0], in[0]};
+endmodule
+
+module middle(input clk,
+	output out);
+
+reg [1:0] cnt = 0;
+wire clk_int;
+assign clk_int = clk;
+always @(posedge clk_int) begin
+	cnt <= cnt + 1;
+end
+
+assign out = cnt[0];
+endmodule