Merge pull request #52 from antmicro/get_clocks

Add -of switch and -include_generated_clocks to get_clocks command
diff --git a/sdc-plugin/sdc.cc b/sdc-plugin/sdc.cc
index 880eeb2..a9b7f6a 100644
--- a/sdc-plugin/sdc.cc
+++ b/sdc-plugin/sdc.cc
@@ -21,10 +21,10 @@
 #include "kernel/register.h"
 #include "kernel/rtlil.h"
 #include "propagation.h"
+#include "sdc_writer.h"
+#include "set_clock_groups.h"
 #include "set_false_path.h"
 #include "set_max_delay.h"
-#include "set_clock_groups.h"
-#include "sdc_writer.h"
 
 USING_YOSYS_NAMESPACE
 
@@ -73,7 +73,8 @@
     }
 
     void execute(std::ostream*& f, std::string filename,
-                 std::vector<std::string> args, RTLIL::Design* design) override {
+                 std::vector<std::string> args,
+                 RTLIL::Design* design) override {
 	if (args.size() < 2) {
 	    log_cmd_error("Missing output file.\n");
 	}
@@ -86,8 +87,7 @@
 };
 
 struct CreateClockCmd : public Pass {
-    CreateClockCmd()
-        : Pass("create_clock", "Create clock object") {}
+    CreateClockCmd() : Pass("create_clock", "Create clock object") {}
 
     void help() override {
 	log("\n");
@@ -169,8 +169,7 @@
 	    rising_edge = 0;
 	    falling_edge = period / 2;
 	}
-	Clock::Add(name, selected_wires, period, rising_edge,
-	                 falling_edge);
+	Clock::Add(name, selected_wires, period, rising_edge, falling_edge);
     }
 
     void AddWirePrefix(std::vector<std::string>& args, size_t argidx) {
@@ -181,29 +180,104 @@
 };
 
 struct GetClocksCmd : public Pass {
-    GetClocksCmd()
-        : Pass("get_clocks", "Create clock object") {}
+    GetClocksCmd() : Pass("get_clocks", "Create clock object") {}
 
     void help() override {
 	log("\n");
-	log("    get_clocks\n");
+	log("    get_clocks [-include_generated_clocks] [-of <nets>] "
+	    "[<patterns>]\n");
 	log("\n");
 	log("Returns all clocks in the design.\n");
 	log("\n");
+	log("    -include_generated_clocks\n");
+	log("        Include auto-generated clocks.\n");
+	log("\n");
+	log("    -of\n");
+	log("        Get clocks of these nets.\n");
+	log("\n");
+	log("    <pattern>\n");
+	log("        Pattern of clock names. Default are all clocks in the "
+	    "design.\n");
+	log("\n");
+    }
+
+    std::vector<std::string> extract_list(const std::string& args) {
+	std::vector<std::string> port_list;
+	std::stringstream ss(args);
+	std::istream_iterator<std::string> begin(ss);
+	std::istream_iterator<std::string> end;
+	std::copy(begin, end, std::back_inserter(port_list));
+	return port_list;
+    }
+
+    // TODO Check for GENERATED_CLOCK clock wire attribute
+    // Issue https://github.com/SymbiFlow/yosys-symbiflow-plugins/issues/53
+    // For now don't treat any of the added clocks as auto-generated
+    bool IsGeneratedClock(RTLIL::Wire* clock_wire) {
+	(void)clock_wire;
+	return false;
     }
 
     void execute(std::vector<std::string> args,
                  RTLIL::Design* design) override {
-	if (args.size() > 1) {
-	    log_warning("Command doesn't support arguments, so they will be ignored.\n");
+
+	// Parse command arguments
+	bool generated_clocks(false);
+	std::vector<std::string> clocks_nets;
+	size_t argidx(0);
+
+	// Parse command switches
+	for (argidx = 1; argidx < args.size(); argidx++) {
+	    std::string arg = args[argidx];
+	    if (arg == "-include_generated_clocks") {
+		generated_clocks = true;
+		continue;
+	    }
+	    if (arg == "-of" and argidx + 1 < args.size()) {
+		clocks_nets = extract_list(args[++argidx]);
+#ifdef SDC_DEBUG
+		for (auto clock_net : clocks_nets) {
+		    log("Clock filter %s\n", clock_net.c_str());
+		}
+#endif
+		continue;
+	    }
+	    if (arg.size() > 0 and arg[0] == '-') {
+		log_cmd_error("Unknown option %s.\n", arg.c_str());
+	    }
+
+	    break;
 	}
+
+	// Parse object patterns
+	std::vector<std::string> clocks_list(args.begin() + argidx, args.end());
+
+	// Fetch clocks in the design
 	std::map<std::string, RTLIL::Wire*> clocks(Clocks::GetClocks(design));
 	if (clocks.size() == 0) {
 	    log_warning("No clocks found in design\n");
 	}
+
+	// Extract clocks into tcl list
 	Tcl_Interp* interp = yosys_get_tcl_interp();
 	Tcl_Obj* tcl_list = Tcl_NewListObj(0, NULL);
 	for (auto& clock : clocks) {
+	    // Skip generated clocks if -include_generated_clocks is not specified
+	    if (IsGeneratedClock(clock.second) and !generated_clocks) {
+		continue;
+	    }
+	    // Check if clock name is in the list of design clocks
+	    if (clocks_list.size() > 0 and
+	        std::find(clocks_list.begin(), clocks_list.end(),
+	                  clock.first) == clocks_list.end()) {
+		continue;
+	    }
+	    // Check if clock wire is in the -of list
+	    if (clocks_nets.size() > 0 and
+	        std::find(clocks_nets.begin(), clocks_nets.end(),
+	                  Clock::WireName(clock.second)) == clocks_nets.end()) {
+		continue;
+	    }
 	    auto& wire = clock.second;
 	    const char* name = RTLIL::id2cstr(wire->name);
 	    Tcl_Obj* name_obj = Tcl_NewStringObj(name, -1);
@@ -228,15 +302,15 @@
     void execute(std::vector<std::string> args,
                  RTLIL::Design* design) override {
 	if (args.size() > 1) {
-	    log_warning("Command accepts no arguments.\nAll will be ignored.\n");
+	    log_warning(
+	        "Command accepts no arguments.\nAll will be ignored.\n");
 	}
 	if (!design->top_module()) {
 	    log_cmd_error("No top module selected\n");
 	}
 
 	std::array<std::unique_ptr<Propagation>, 2> passes{
-	    std::unique_ptr<Propagation>(
-	        new BufferPropagation(design, this)),
+	    std::unique_ptr<Propagation>(new BufferPropagation(design, this)),
 	    std::unique_ptr<Propagation>(
 	        new ClockDividerPropagation(design, this))};
 
diff --git a/sdc-plugin/tests/Makefile b/sdc-plugin/tests/Makefile
index 3d602b7..8604f82 100644
--- a/sdc-plugin/tests/Makefile
+++ b/sdc-plugin/tests/Makefile
@@ -19,7 +19,8 @@
 	restore_from_json \
 	period_check \
 	waveform_check \
-	period_format_check
+	period_format_check \
+	get_clocks
 
 UNIT_TESTS = escaping
 
@@ -41,3 +42,4 @@
 waveform_check_negative = 1
 period_format_check_verify = true
 period_format_check_negative = 1
+get_clocks_verify = $(call diff_test,get_clocks,txt)
diff --git a/sdc-plugin/tests/get_clocks/get_clocks.golden.txt b/sdc-plugin/tests/get_clocks/get_clocks.golden.txt
new file mode 100644
index 0000000..9c70eed
--- /dev/null
+++ b/sdc-plugin/tests/get_clocks/get_clocks.golden.txt
@@ -0,0 +1,6 @@
+{$auto$clkbufmap.cc:247:execute$1913} {$auto$clkbufmap.cc:247:execute$1915} clk clk2 clk_int_1 middle_inst_1.clk_int middle_inst_4.clk
+{$auto$clkbufmap.cc:247:execute$1913} {$auto$clkbufmap.cc:247:execute$1915} clk clk2 clk_int_1 middle_inst_1.clk_int middle_inst_4.clk
+clk2
+clk_int_1
+clk clk2 clk_int_1 middle_inst_1.clk_int middle_inst_4.clk
+clk clk2 clk_int_1
diff --git a/sdc-plugin/tests/get_clocks/get_clocks.input.sdc b/sdc-plugin/tests/get_clocks/get_clocks.input.sdc
new file mode 100644
index 0000000..01debad
--- /dev/null
+++ b/sdc-plugin/tests/get_clocks/get_clocks.input.sdc
@@ -0,0 +1,2 @@
+create_clock -period 10.0 -waveform {0.000 5.000} clk_int_1
+create_clock -period 10.0 -name clk -waveform {0.000 5.000} clk clk2
diff --git a/sdc-plugin/tests/get_clocks/get_clocks.tcl b/sdc-plugin/tests/get_clocks/get_clocks.tcl
new file mode 100644
index 0000000..6f0eea7
--- /dev/null
+++ b/sdc-plugin/tests/get_clocks/get_clocks.tcl
@@ -0,0 +1,36 @@
+yosys -import
+plugin -i sdc
+plugin -i design_introspection
+# Import the commands from the plugins to the tcl interpreter
+yosys -import
+
+read_verilog $::env(DESIGN_TOP).v
+read_verilog -specify -lib -D_EXPLICIT_CARRY +/xilinx/cells_sim.v
+read_verilog -lib +/xilinx/cells_xtra.v
+hierarchy -check -auto-top
+# Start flow after library reading
+synth_xilinx -flatten -abc9 -nosrl -nodsp -iopad -run prepare:check
+#synth_xilinx
+
+# Read the design's timing constraints
+read_sdc $::env(DESIGN_TOP).input.sdc
+
+# Propagate the clocks
+propagate_clocks
+
+# Write the clocks to file
+set fh [open $::env(DESIGN_TOP).txt w]
+
+puts $fh [get_clocks]
+
+puts $fh [get_clocks -include_generated_clocks]
+
+puts $fh [get_clocks -include_generated_clocks clk2]
+
+puts $fh [get_clocks -of [get_nets clk_int_1 clk1] -include_generated_clocks clk_int_1]
+
+puts $fh [get_clocks -of [get_nets]]
+
+puts $fh [get_clocks -of [concat [get_nets clk2] [get_nets clk_int_1 clk]]]
+
+close $fh
diff --git a/sdc-plugin/tests/get_clocks/get_clocks.v b/sdc-plugin/tests/get_clocks/get_clocks.v
new file mode 100644
index 0000000..59531d2
--- /dev/null
+++ b/sdc-plugin/tests/get_clocks/get_clocks.v
@@ -0,0 +1,35 @@
+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_inst(.I(clk), .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