Merge pull request #37 from antmicro/clock_divider_input_phase_fix

SDC: Handle input clock phase shift on clock generators
diff --git a/sdc-plugin/buffers.cc b/sdc-plugin/buffers.cc
index eb4d0a7..cb54743 100644
--- a/sdc-plugin/buffers.cc
+++ b/sdc-plugin/buffers.cc
@@ -15,6 +15,8 @@
  *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
+#include <cassert>
+#include <cmath>
 #include "buffers.h"
 
 const std::vector<std::string> Pll::inputs = {"CLKIN1", "CLKIN2"};
@@ -22,3 +24,71 @@
                                                "CLKOUT3", "CLKOUT4", "CLKOUT5"};
 const float Pll::delay = 0;
 const std::string Pll::name = "PLLE2_ADV";
+
+Pll::Pll(RTLIL::Cell* cell, float input_clock_period, float input_clock_rising_edge) {
+    assert(RTLIL::unescape_id(cell->type) == "PLLE2_ADV");
+    FetchParams(cell);
+    CheckInputClockPeriod(cell, input_clock_period);
+    CalculateOutputClockPeriods();
+    CalculateOutputClockWaveforms(input_clock_rising_edge);
+}
+
+void Pll::CheckInputClockPeriod(RTLIL::Cell* cell, float input_clock_period) {
+    float abs_diff = fabs(ClkinPeriod() - input_clock_period);
+    bool approx_equal = abs_diff < max(ClkinPeriod(), input_clock_period) * 10 * std::numeric_limits<float>::epsilon();
+    if (!approx_equal) {
+	log_cmd_error(
+	    "CLKIN[1/2]_PERIOD doesn't match the virtual clock constraint "
+	    "propagated to the CLKIN[1/2] input of the clock divider cell: "
+	    "%s.\nInput clock period: %f, CLKIN[1/2]_PERIOD: %f\n",
+	    RTLIL::id2cstr(cell->name), input_clock_period, ClkinPeriod());
+    }
+}
+
+void Pll::FetchParams(RTLIL::Cell* cell) {
+    clkin1_period = FetchParam(cell, "CLKIN1_PERIOD", 0.0);
+    clkin2_period = FetchParam(cell, "CLKIN2_PERIOD", 0.0);
+    clk_mult = FetchParam(cell, "CLKFBOUT_MULT", 5.0);
+    clk_fbout_phase = FetchParam(cell, "CLKFBOUT_PHASE", 0.0);
+    divclk_divisor = FetchParam(cell, "DIVCLK_DIVIDE", 1.0);
+    for (auto output : outputs) {
+	// CLKOUT[0-5]_DUTY_CYCLE
+	clkout_duty_cycle[output] = FetchParam(cell, output + "_DUTY_CYCLE", 0.5);
+	// CLKOUT[0-5]_DIVIDE
+	clkout_divisor[output] = FetchParam(cell, output + "_DIVIDE", 1.0);
+	// CLKOUT[0-5]_PHASE
+	clkout_phase[output] = FetchParam(cell, output + "_PHASE", 0.0);
+    }
+}
+
+void Pll::CalculateOutputClockPeriods() {
+    for (auto output : outputs) {
+	// CLKOUT[0-5]_PERIOD = CLKIN1_PERIOD * CLKOUT[0-5]_DIVIDE * DIVCLK_DIVIDE /
+	// CLKFBOUT_MULT
+	clkout_period[output] = ClkinPeriod() * clkout_divisor.at(output) / clk_mult *
+	    divclk_divisor;
+    }
+}
+
+void Pll::CalculateOutputClockWaveforms(float input_clock_rising_edge) {
+    for (auto output : outputs) {
+	float output_clock_period = clkout_period.at(output);
+	clkout_rising_edge[output] = fmod(input_clock_rising_edge - (clk_fbout_phase / 360.0) * ClkinPeriod() + output_clock_period * (clkout_phase[output] / 360.0), output_clock_period);
+	clkout_falling_edge[output] = fmod(clkout_rising_edge[output] + clkout_duty_cycle[output] * output_clock_period, output_clock_period);
+    }
+}
+
+float Pll::FetchParam(RTLIL::Cell* cell, std::string&& param_name, float default_value) {
+    RTLIL::IdString param(RTLIL::escape_id(param_name));
+    if (cell->hasParam(param)) {
+	auto param_obj = cell->parameters.at(param);
+	std::string value;
+	if (param_obj.flags & RTLIL::CONST_FLAG_STRING) {
+	    value = param_obj.decode_string();
+	} else {
+	    value = std::to_string(param_obj.as_int());
+	}
+	return std::stof(value);
+    }
+    return default_value;
+}
diff --git a/sdc-plugin/buffers.h b/sdc-plugin/buffers.h
index 7b512e2..5f51535 100644
--- a/sdc-plugin/buffers.h
+++ b/sdc-plugin/buffers.h
@@ -18,8 +18,6 @@
 #ifndef _BUFFERS_H_
 #define _BUFFERS_H_
 
-#include <cassert>
-#include <initializer_list>
 #include <string>
 #include <unordered_map>
 #include <vector>
@@ -40,73 +38,50 @@
 };
 
 struct Bufg : Buffer {
-    Bufg() : Buffer(1, "BUFG", "O"){};
+    Bufg() : Buffer(0, "BUFG", "O"){};
 };
 
 struct Pll {
-    Pll(RTLIL::Cell* cell) : cell(cell) {
-	assert(RTLIL::unescape_id(cell->type) == "PLLE2_ADV");
-	clkin1_period = FetchParam(cell, "CLKIN1_PERIOD", 0.0);
-	clkin2_period = FetchParam(cell, "CLKIN2_PERIOD", 0.0);
-	clk_mult = FetchParam(cell, "CLKFBOUT_MULT", 5.0);
-	divclk_divisor = FetchParam(cell, "DIVCLK_DIVIDE", 1.0);
-	for (auto clk_output : outputs) {
-	    // CLKOUT[0-5]_DIVIDE
-	    clkout_divisors[clk_output] = FetchParam(cell, clk_output + "_DIVIDE", 1.0);
-	    clkout_period[clk_output] = CalculatePeriod(clk_output);
+    Pll(RTLIL::Cell* cell, float input_clock_period, float input_clock_rising_edge);
 
-	    // CLKOUT[0-5]_PHASE
-	    clkout_phase[clk_output] = FetchParam(cell, clk_output + "_PHASE", 0.0);
+    // Helper function to fetch a cell parameter or return a default value
+    static float FetchParam(RTLIL::Cell* cell, std::string&& param_name,
+                            float default_value);
 
-	    // Take the delay off the PLL into account
-	    clkout_shift[clk_output] = CalculateShift(clk_output) + delay;
+    // Get the period of the input clock
+    // TODO Add support for CLKINSEL
+    float ClkinPeriod() { return clkin1_period; }
 
-	    // CLKOUT[0-5]_DUTY_CYCLE
-	    clkout_duty_cycle[clk_output] = FetchParam(cell, clk_output + "_DUTY_CYCLE", 0.5);
-	}
-    };
+    static const std::vector<std::string> inputs;
+    static const std::vector<std::string> outputs;
+    std::unordered_map<std::string, float> clkout_period;
+    std::unordered_map<std::string, float> clkout_duty_cycle;
+    std::unordered_map<std::string, float> clkout_rising_edge;
+    std::unordered_map<std::string, float> clkout_falling_edge;
 
-    // CLKOUT[0-5]_PERIOD = CLKIN1_PERIOD * CLKOUT[0-5]_DIVIDE * DIVCLK_DIVIDE /
-    // CLKFBOUT_MULT
-    // TODO Check the value on CLKINSEL
-    float CalculatePeriod(const std::string& output) {
-	return clkin1_period * clkout_divisors.at(output) / clk_mult *
-	       divclk_divisor;
-    }
+   private:
+    // Approximate equality check of the input clock period and specified in
+    // CLKIN[1/2]_PERIOD parameter
+    void CheckInputClockPeriod(RTLIL::Cell* cell, float input_clock_period);
 
-    float CalculateShift(const std::string& output) {
-	return clkout_period.at(output) * clkout_phase.at(output) / 360.0;
-    }
+    // Fetch cell's parameters needed for further calculations
+    void FetchParams(RTLIL::Cell* cell);
 
-    float FetchParam(RTLIL::Cell* cell, std::string&& param_name, float default_value) {
-	    RTLIL::IdString param(RTLIL::escape_id(param_name));
-	    if (cell->hasParam(param)) {
-		auto param_obj = cell->parameters.at(param);
-		std::string value;
-		if (param_obj.flags & RTLIL::CONST_FLAG_STRING) {
-		    value = param_obj.decode_string();
-		} else {
-		    value = std::to_string(param_obj.as_int());
-		}
-		return std::stof(value);
-	    }
-	    return default_value;
-    }
+    // Calculate the period on the output clocks
+    void CalculateOutputClockPeriods();
+
+    // Calculate the rising and falling edges of the output clocks
+    void CalculateOutputClockWaveforms(float input_clock_rising_edge);
 
     static const float delay;
     static const std::string name;
-    static const std::vector<std::string> inputs;
-    static const std::vector<std::string> outputs;
-    RTLIL::Cell* cell;
-    std::unordered_map<std::string, float> clkout_period;
-    std::unordered_map<std::string, float> clkout_duty_cycle;
+    std::unordered_map<std::string, float> clkout_divisor;
     std::unordered_map<std::string, float> clkout_phase;
-    std::unordered_map<std::string, float> clkout_shift;
-    std::unordered_map<std::string, float> clkout_divisors;
     float clkin1_period;
     float clkin2_period;
     float divclk_divisor;
     float clk_mult;
+    float clk_fbout_phase;
 };
 
 #endif  // _BUFFERS_H_
diff --git a/sdc-plugin/clocks.cc b/sdc-plugin/clocks.cc
index 9fa811e..0a1572a 100644
--- a/sdc-plugin/clocks.cc
+++ b/sdc-plugin/clocks.cc
@@ -17,6 +17,7 @@
  */
 #include "clocks.h"
 #include <cassert>
+#include <cmath>
 #include "kernel/log.h"
 #include "kernel/register.h"
 #include "propagation.h"
@@ -37,12 +38,11 @@
 	log("Clock %s already exists and will be overwritten\n", name.c_str());
 	clock->UpdateClock(wire, period, rising_edge, falling_edge);
     } else {
-	log("Inserting clock %s with period %f, r:%f, f:%f\n", name.c_str(),
-	    period, rising_edge, falling_edge);
-	if (falling_edge > period) {
-	    log_error("Phase shift on clock %s exceeds 360 degrees\nRising edge: %f, Falling edge: %f, Clock period:%f\n", name.c_str(), rising_edge, falling_edge, period);
-	}
+	rising_edge = fmod(rising_edge, period);
+	falling_edge = fmod(falling_edge, period);
 	clocks_.emplace_back(name, wire, period, rising_edge, falling_edge);
+	log("Added clock %s with period %f, rising_edge:%f, falling_edge:%f\n", name.c_str(),
+	    period, rising_edge, falling_edge);
     }
 }
 
@@ -123,18 +123,14 @@
 #ifdef SDC_DEBUG
 	log("Processing clock %s\n", clock.Name().c_str());
 #endif
-	auto clock_wires = clock.GetClockWires();
-	for (auto clock_wire : clock_wires) {
-	    auto pll_clocks =
-	        pass->FindSinkClocksForCellType(clock_wire, "PLLE2_ADV");
-	    for (auto pll_clock : pll_clocks) {
+	auto pll_clocks =
+	    pass->FindSinkClocksForCellType(clock, "PLLE2_ADV");
+	for (auto pll_clock : pll_clocks) {
 #ifdef SDC_DEBUG
-		log("PLL clock: %s\n", pll_clock.Name().c_str());
+	    log("PLL clock: %s\n", pll_clock.Name().c_str());
 #endif
-		pll_clock.ApplyShift(clock.RisingEdge());
-		AddClock(pll_clock);
-		PropagateThroughBuffer(pass, pll_clock, Bufg());
-	    }
+	    AddClock(pll_clock);
+	    PropagateThroughBuffer(pass, pll_clock, Bufg());
 	}
     }
 #ifdef SDC_DEBUG
@@ -205,6 +201,10 @@
                   [&, this](RTLIL::Wire* wire) { UpdateWires(wire); });
 }
 
+Clock::Clock(RTLIL::Wire* wire, float period,
+             float rising_edge, float falling_edge)
+    : Clock(RTLIL::id2cstr(wire->name), wire, period, rising_edge, falling_edge) {}
+
 void Clock::UpdateClock(RTLIL::Wire* wire, float period, float rising_edge,
                         float falling_edge) {
     UpdateWires(wire);
@@ -224,17 +224,8 @@
 }
 
 void Clock::UpdateWaveform(float rising_edge, float falling_edge) {
-    rising_edge_ = rising_edge;
-    falling_edge_ = falling_edge;
-    if (falling_edge_ > period_) {
-	log_error("Phase shift on clock %s exceeds 360 degrees\nRising edge: %f, Falling edge: %f, Clock period:%f\n", name_.c_str(), rising_edge_, falling_edge_, period_);
-    }
-}
-
-void Clock::ApplyShift(float rising_edge) {
-    float new_rising_edge = rising_edge_ + rising_edge;
-    float new_falling_edge = falling_edge_ + rising_edge;
-    UpdateWaveform(new_rising_edge, new_falling_edge);
+    rising_edge_ = fmod(rising_edge, period_);
+    falling_edge_ = fmod(falling_edge, period_);
 }
 
 std::string Clock::ClockWireName(RTLIL::Wire* wire) {
diff --git a/sdc-plugin/clocks.h b/sdc-plugin/clocks.h
index 28cd7f7..ff02bd5 100644
--- a/sdc-plugin/clocks.h
+++ b/sdc-plugin/clocks.h
@@ -36,6 +36,8 @@
           float rising_edge, float falling_edge);
     Clock(const std::string& name, std::vector<RTLIL::Wire*> wires,
           float period, float rising_edge, float falling_edge);
+    Clock(RTLIL::Wire* wire, float period,
+	    float rising_edge, float falling_edge);
     std::vector<RTLIL::Wire*> GetClockWires() { return clock_wires_; }
     const std::string& Name() const { return name_; }
     float Period() { return period_; }
@@ -43,7 +45,6 @@
     float FallingEdge() { return falling_edge_; }
     void UpdateClock(RTLIL::Wire* wire, float period, float rising_edge,
                      float falling_edge);
-    void ApplyShift(float shift);
     static std::string ClockWireName(RTLIL::Wire* wire);
 
    private:
diff --git a/sdc-plugin/propagation.cc b/sdc-plugin/propagation.cc
index 39ea476..e741e1a 100644
--- a/sdc-plugin/propagation.cc
+++ b/sdc-plugin/propagation.cc
@@ -38,33 +38,31 @@
 }
 
 std::vector<Clock> ClockDividerPropagation::FindSinkClocksForCellType(
-    RTLIL::Wire* driver_wire, const std::string& cell_type) {
+    Clock driving_clock, const std::string& cell_type) {
     std::vector<Clock> clocks;
-    if (cell_type == "PLLE2_ADV") {
-	RTLIL::Cell* cell = NULL;
-	for (auto input : Pll::inputs) {
-	    cell = FindSinkCellOnPort(driver_wire, input);
-	    if (cell and RTLIL::unescape_id(cell->type) == cell_type) {
-		break;
+    auto clock_wires = driving_clock.GetClockWires();
+    for (auto clock_wire : clock_wires) {
+	if (cell_type == "PLLE2_ADV") {
+	    RTLIL::Cell* cell = NULL;
+	    for (auto input : Pll::inputs) {
+		cell = FindSinkCellOnPort(clock_wire, input);
+		if (cell and RTLIL::unescape_id(cell->type) == cell_type) {
+		    break;
+		}
 	    }
-	}
-	if (!cell) {
-	    return clocks;
-	}
-	Pll pll(cell);
-	for (auto output : Pll::outputs) {
-	    RTLIL::Wire* wire = FindSinkWireOnPort(cell, output);
-	    if (wire) {
-		float clkout_period(pll.clkout_period.at(output));
-		float clkout_shift(pll.clkout_shift.at(output));
-		float clkout_duty_cycle(pll.clkout_duty_cycle.at(output));
-		Clock clock(RTLIL::unescape_id(wire->name), wire, clkout_period, clkout_shift,
-		            clkout_shift + clkout_duty_cycle * clkout_period);
-		clocks.push_back(clock);
-		auto further_clocks =
-		    FindSinkClocksForCellType(wire, cell_type);
-		std::copy(further_clocks.begin(), further_clocks.end(),
-		          std::back_inserter(clocks));
+	    if (!cell) {
+		return clocks;
+	    }
+	    Pll pll(cell, driving_clock.Period(), driving_clock.RisingEdge());
+	    for (auto output : Pll::outputs) {
+		RTLIL::Wire* wire = FindSinkWireOnPort(cell, output);
+		if (wire) {
+		    float clkout_period(pll.clkout_period.at(output));
+		    float clkout_rising_edge(pll.clkout_rising_edge.at(output));
+		    float clkout_falling_edge(pll.clkout_falling_edge.at(output));
+		    Clock clock(wire, clkout_period, clkout_rising_edge, clkout_falling_edge);
+		    clocks.push_back(clock);
+		}
 	    }
 	}
     }
diff --git a/sdc-plugin/propagation.h b/sdc-plugin/propagation.h
index 1f3125c..bc7a6d2 100644
--- a/sdc-plugin/propagation.h
+++ b/sdc-plugin/propagation.h
@@ -65,7 +65,7 @@
         : Propagation(design, pass) {}
 
     void Run(Clocks& clocks) override { clocks.Propagate(this); }
-    std::vector<Clock> FindSinkClocksForCellType(RTLIL::Wire* driver_wire,
+    std::vector<Clock> FindSinkClocksForCellType(Clock driver_wire,
                                                  const std::string& cell_type);
 };
 #endif  // PROPAGATION_H_
diff --git a/sdc-plugin/sdc.cc b/sdc-plugin/sdc.cc
index 888fbdf..4233d01 100644
--- a/sdc-plugin/sdc.cc
+++ b/sdc-plugin/sdc.cc
@@ -167,8 +167,6 @@
 	}
 	clocks_.AddClock(name, selected_wires, period, rising_edge,
 	                 falling_edge);
-	log("Created clock %s with period %f, waveform {%f %f}\n", name.c_str(),
-	    period, rising_edge, falling_edge);
     }
 
     void AddWirePrefix(std::vector<std::string>& args, size_t argidx) {
@@ -235,6 +233,8 @@
 	    std::unique_ptr<ClockDividerPropagation>(
 	        new ClockDividerPropagation(design, this))};
 
+	log("Perform clock propagation\n");
+
 	for (auto& pass : passes) {
 	    pass->Run(clocks_);
 	}
diff --git a/sdc-plugin/tests/Makefile b/sdc-plugin/tests/Makefile
index 68c3e45..d19f19d 100644
--- a/sdc-plugin/tests/Makefile
+++ b/sdc-plugin/tests/Makefile
@@ -1,9 +1,12 @@
-TESTS = counter counter2 pll
+TESTS = counter counter2 pll pll_div pll_fbout_phase pll_approx_equal
 .PHONY: $(TESTS)
 
 counter_verify = $(call compare,counter,sdc) && $(call compare,counter,txt)
 counter2_verify = $(call compare,counter2,sdc) && $(call compare,counter2,txt)
 pll_verify = $(call compare,pll,sdc)
+pll_div_verify = $(call compare,pll_div,sdc)
+pll_fbout_phase_verify = $(call compare,pll_fbout_phase,sdc)
+pll_approx_equal_verify = $(call compare,pll_approx_equal,sdc)
 
 all: $(TESTS)
 compare = diff $(1)/$(1).golden.$(2) $(1)/$(1).$(2)
diff --git a/sdc-plugin/tests/counter/counter.golden.sdc b/sdc-plugin/tests/counter/counter.golden.sdc
index a70e95d..c1a2ea1 100644
--- a/sdc-plugin/tests/counter/counter.golden.sdc
+++ b/sdc-plugin/tests/counter/counter.golden.sdc
@@ -2,5 +2,5 @@
 create_clock -period 10 -waveform {0 5} ibuf_proxy_out
 create_clock -period 10 -waveform {0 5} \$auto\$clkbufmap.cc:247:execute\$1918
 create_clock -period 10 -waveform {0 5} \$auto\$clkbufmap.cc:247:execute\$1920
-create_clock -period 10 -waveform {1 6} middle_inst_1.clk_int
-create_clock -period 10 -waveform {1 6} middle_inst_4.clk
+create_clock -period 10 -waveform {0 5} middle_inst_1.clk_int
+create_clock -period 10 -waveform {0 5} middle_inst_4.clk
diff --git a/sdc-plugin/tests/counter2/counter2.golden.sdc b/sdc-plugin/tests/counter2/counter2.golden.sdc
index b3e8cca..4bf0bf2 100644
--- a/sdc-plugin/tests/counter2/counter2.golden.sdc
+++ b/sdc-plugin/tests/counter2/counter2.golden.sdc
@@ -2,5 +2,5 @@
 create_clock -period 10 -waveform {0 5} ibuf_proxy_out
 create_clock -period 10 -waveform {0 5} \$auto\$clkbufmap.cc:247:execute\$1918
 create_clock -period 10 -waveform {1 6} \$auto\$clkbufmap.cc:247:execute\$1920
-create_clock -period 10 -waveform {1 6} middle_inst_1.clk_int
-create_clock -period 10 -waveform {2 7} middle_inst_4.clk
+create_clock -period 10 -waveform {0 5} middle_inst_1.clk_int
+create_clock -period 10 -waveform {1 6} middle_inst_4.clk
diff --git a/sdc-plugin/tests/pll/pll.golden.sdc b/sdc-plugin/tests/pll/pll.golden.sdc
index 8f82a93..7fe2b2a 100644
--- a/sdc-plugin/tests/pll/pll.golden.sdc
+++ b/sdc-plugin/tests/pll/pll.golden.sdc
@@ -1,6 +1,8 @@
-create_clock -period 10 -waveform {0 5} \$auto\$clkbufmap.cc:247:execute\$1827
-create_clock -period 10 -waveform {1 6} \$techmap1716\FDCE_0.C
-create_clock -period 10 -waveform {3.5 8.5} \$auto\$clkbufmap.cc:247:execute\$1829
-create_clock -period 10 -waveform {4.5 9.5} main_clkout0
-create_clock -period 5 -waveform {1 3.5} \$auto\$clkbufmap.cc:247:execute\$1831
-create_clock -period 5 -waveform {2 4.5} main_clkout1
+create_clock -period 10 -waveform {0 5} \$auto\$clkbufmap.cc:247:execute\$1829
+create_clock -period 10 -waveform {0 5} \$techmap1716\FDCE_0.C
+create_clock -period 10 -waveform {2.5 7.5} \$auto\$clkbufmap.cc:247:execute\$1831
+create_clock -period 10 -waveform {2.5 7.5} main_clkout0
+create_clock -period 2.5 -waveform {0 1.25} \$auto\$clkbufmap.cc:247:execute\$1833
+create_clock -period 2.5 -waveform {0 1.25} main_clkout1
+create_clock -period 5 -waveform {1.25 3.75} \$auto\$clkbufmap.cc:247:execute\$1835
+create_clock -period 5 -waveform {1.25 3.75} main_clkout2
diff --git a/sdc-plugin/tests/pll/pll.v b/sdc-plugin/tests/pll/pll.v
index 64b9750..63542da 100644
--- a/sdc-plugin/tests/pll/pll.v
+++ b/sdc-plugin/tests/pll/pll.v
@@ -2,10 +2,10 @@
 	input clk,
 	input cpu_reset,
 	input data_in,
-	output[4:0] data_out
+	output[5:0] data_out
 );
 
-wire [4:0] data_out;
+wire [5:0] data_out;
 wire builder_pll_fb;
 wire fdce_0_out, fdce_1_out;
 wire main_locked;
@@ -31,8 +31,10 @@
 	.CLKIN1_PERIOD(10.0),
 	.CLKOUT0_DIVIDE(4'd12),
 	.CLKOUT0_PHASE(90.0),
-	.CLKOUT1_DIVIDE(3'd6),
+	.CLKOUT1_DIVIDE(2'd3),
 	.CLKOUT1_PHASE(0.0),
+	.CLKOUT2_DIVIDE(3'd6),
+	.CLKOUT2_PHASE(90.0),
 	.DIVCLK_DIVIDE(1'd1),
 	.REF_JITTER1(0.01),
 	.STARTUP_WAIT("FALSE")
@@ -43,10 +45,11 @@
 	.CLKFBOUT(builder_pll_fb),
 	.CLKOUT0(main_clkout0),
 	.CLKOUT1(main_clkout1),
+	.CLKOUT2(main_clkout2),
 	.LOCKED(main_locked)
 );
 
-FDCE FDCE_PLLx1 (
+FDCE FDCE_PLLx1_PH90 (
 	.D(data_in),
 	.C(main_clkout0),
 	.CE(1'b1),
@@ -54,7 +57,7 @@
 	.Q(data_out[1])
 );
 
-FDCE FDCE_PLLx4_0 (
+FDCE FDCE_PLLx4_PH0_0 (
 	.D(data_in),
 	.C(main_clkout1),
 	.CE(1'b1),
@@ -62,7 +65,7 @@
 	.Q(data_out[2])
 );
 
-FDCE FDCE_PLLx4_1 (
+FDCE FDCE_PLLx4_PH0_1 (
 	.D(data_in),
 	.C(main_clkout1),
 	.CE(1'b1),
@@ -70,11 +73,19 @@
 	.Q(data_out[3])
 );
 
-FDCE FDCE_PLLx4_2 (
+FDCE FDCE_PLLx4_PH0_2 (
 	.D(data_in),
 	.C(main_clkout1),
 	.CE(1'b1),
 	.CLR(1'b0),
 	.Q(data_out[4])
 );
+
+FDCE FDCE_PLLx2_PH90_0 (
+	.D(data_in),
+	.C(main_clkout2),
+	.CE(1'b1),
+	.CLR(1'b0),
+	.Q(data_out[5])
+);
 endmodule
diff --git a/sdc-plugin/tests/pll_approx_equal/pll_approx_equal.golden.sdc b/sdc-plugin/tests/pll_approx_equal/pll_approx_equal.golden.sdc
new file mode 100644
index 0000000..add4f6a
--- /dev/null
+++ b/sdc-plugin/tests/pll_approx_equal/pll_approx_equal.golden.sdc
@@ -0,0 +1,8 @@
+create_clock -period 10 -waveform {0 5} \$auto\$clkbufmap.cc:247:execute\$1829
+create_clock -period 10 -waveform {0 5} \$techmap1716\FDCE_0.C
+create_clock -period 9.99999 -waveform {0 5} \$auto\$clkbufmap.cc:247:execute\$1831
+create_clock -period 9.99999 -waveform {0 5} main_clkout_x1
+create_clock -period 5 -waveform {-2.5 0} \$auto\$clkbufmap.cc:247:execute\$1833
+create_clock -period 5 -waveform {-2.5 0} main_clkout_x2
+create_clock -period 2.5 -waveform {-1.875 -0.624999} \$auto\$clkbufmap.cc:247:execute\$1835
+create_clock -period 2.5 -waveform {-1.875 -0.624999} main_clkout_x4
diff --git a/sdc-plugin/tests/pll_approx_equal/pll_approx_equal.input.sdc b/sdc-plugin/tests/pll_approx_equal/pll_approx_equal.input.sdc
new file mode 100644
index 0000000..00354d7
--- /dev/null
+++ b/sdc-plugin/tests/pll_approx_equal/pll_approx_equal.input.sdc
@@ -0,0 +1 @@
+create_clock -period 10 -waveform {0 5} clk
diff --git a/sdc-plugin/tests/pll_approx_equal/pll_approx_equal.tcl b/sdc-plugin/tests/pll_approx_equal/pll_approx_equal.tcl
new file mode 100644
index 0000000..13c8890
--- /dev/null
+++ b/sdc-plugin/tests/pll_approx_equal/pll_approx_equal.tcl
@@ -0,0 +1,21 @@
+yosys -import
+plugin -i sdc
+# Import the commands from the plugins to the tcl interpreter
+yosys -import
+
+read_verilog pll_approx_equal.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 -vpr -flatten -abc9 -nosrl -nodsp -iopad -run prepare:check
+
+# Read the design timing constraints
+read_sdc $::env(INPUT_SDC_FILE)
+
+# Propagate the clocks
+propagate_clocks
+
+# Write out the SDC file after the clock propagation step
+write_sdc $::env(OUTPUT_SDC_FILE)
diff --git a/sdc-plugin/tests/pll_approx_equal/pll_approx_equal.v b/sdc-plugin/tests/pll_approx_equal/pll_approx_equal.v
new file mode 100644
index 0000000..11913d2
--- /dev/null
+++ b/sdc-plugin/tests/pll_approx_equal/pll_approx_equal.v
@@ -0,0 +1,91 @@
+module top(
+	input clk,
+	input cpu_reset,
+	input data_in,
+	output[5:0] data_out
+);
+
+wire [5:0] data_out;
+wire builder_pll_fb;
+wire fdce_0_out, fdce_1_out;
+wire main_locked;
+
+FDCE FDCE_0 (
+	.D(data_in),
+	.C(clk),
+	.CE(1'b1),
+	.CLR(1'b0),
+	.Q(fdce_0_out)
+);
+
+FDCE FDCE_1 (
+	.D(fdce_0_out),
+	.C(clk),
+	.CE(1'b1),
+	.CLR(1'b0),
+	.Q(data_out[0])
+);
+
+PLLE2_ADV #(
+	.CLKFBOUT_MULT(4'd12),
+	.CLKFBOUT_PHASE(90.0),
+	.CLKIN1_PERIOD(9.99999),
+	.CLKOUT0_DIVIDE(4'd12),
+	.CLKOUT0_PHASE(90.0),
+	.CLKOUT1_DIVIDE(3'd6),
+	.CLKOUT1_PHASE(0.0),
+	.CLKOUT2_DIVIDE(2'd3),
+	.CLKOUT2_PHASE(90.0),
+	.REF_JITTER1(0.01),
+	.STARTUP_WAIT("FALSE")
+) PLLE2_ADV (
+	.CLKFBIN(builder_pll_fb),
+	.CLKIN1(clk),
+	.RST(cpu_reset),
+	.CLKFBOUT(builder_pll_fb),
+	.CLKOUT0(main_clkout_x1),
+	.CLKOUT1(main_clkout_x2),
+	.CLKOUT2(main_clkout_x4),
+	.LOCKED(main_locked)
+);
+
+FDCE FDCE_PLLx1_PH90 (
+	.D(data_in),
+	.C(main_clkout_x1),
+	.CE(1'b1),
+	.CLR(1'b0),
+	.Q(data_out[1])
+);
+
+FDCE FDCE_PLLx4_PH0_0 (
+	.D(data_in),
+	.C(main_clkout_x2),
+	.CE(1'b1),
+	.CLR(1'b0),
+	.Q(data_out[2])
+);
+
+FDCE FDCE_PLLx4_PH0_1 (
+	.D(data_in),
+	.C(main_clkout_x2),
+	.CE(1'b1),
+	.CLR(1'b0),
+	.Q(data_out[3])
+);
+
+FDCE FDCE_PLLx4_PH0_2 (
+	.D(data_in),
+	.C(main_clkout_x2),
+	.CE(1'b1),
+	.CLR(1'b0),
+	.Q(data_out[4])
+);
+
+FDCE FDCE_PLLx2_PH90_0 (
+	.D(data_in),
+	.C(main_clkout_x4),
+	.CE(1'b1),
+	.CLR(1'b0),
+	.Q(data_out[5])
+);
+endmodule
diff --git a/sdc-plugin/tests/pll_div/pll_div.golden.sdc b/sdc-plugin/tests/pll_div/pll_div.golden.sdc
new file mode 100644
index 0000000..14ea0bc
--- /dev/null
+++ b/sdc-plugin/tests/pll_div/pll_div.golden.sdc
@@ -0,0 +1,8 @@
+create_clock -period 10 -waveform {0 5} \$auto\$clkbufmap.cc:247:execute\$1829
+create_clock -period 10 -waveform {0 5} \$techmap1716\FDCE_0.C
+create_clock -period 20 -waveform {5 15} \$auto\$clkbufmap.cc:247:execute\$1831
+create_clock -period 20 -waveform {5 15} main_clkout0
+create_clock -period 5 -waveform {0 2.5} \$auto\$clkbufmap.cc:247:execute\$1833
+create_clock -period 5 -waveform {0 2.5} main_clkout1
+create_clock -period 10 -waveform {2.5 7.5} \$auto\$clkbufmap.cc:247:execute\$1835
+create_clock -period 10 -waveform {2.5 7.5} main_clkout2
diff --git a/sdc-plugin/tests/pll_div/pll_div.input.sdc b/sdc-plugin/tests/pll_div/pll_div.input.sdc
new file mode 100644
index 0000000..00354d7
--- /dev/null
+++ b/sdc-plugin/tests/pll_div/pll_div.input.sdc
@@ -0,0 +1 @@
+create_clock -period 10 -waveform {0 5} clk
diff --git a/sdc-plugin/tests/pll_div/pll_div.tcl b/sdc-plugin/tests/pll_div/pll_div.tcl
new file mode 100644
index 0000000..f8f0a9f
--- /dev/null
+++ b/sdc-plugin/tests/pll_div/pll_div.tcl
@@ -0,0 +1,21 @@
+yosys -import
+plugin -i sdc
+# Import the commands from the plugins to the tcl interpreter
+yosys -import
+
+read_verilog pll_div.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 -vpr -flatten -abc9 -nosrl -nodsp -iopad -run prepare:check
+
+# Read the design timing constraints
+read_sdc $::env(INPUT_SDC_FILE)
+
+# Propagate the clocks
+propagate_clocks
+
+# Write out the SDC file after the clock propagation step
+write_sdc $::env(OUTPUT_SDC_FILE)
diff --git a/sdc-plugin/tests/pll_div/pll_div.v b/sdc-plugin/tests/pll_div/pll_div.v
new file mode 100644
index 0000000..b4055ef
--- /dev/null
+++ b/sdc-plugin/tests/pll_div/pll_div.v
@@ -0,0 +1,91 @@
+module top(
+	input clk,
+	input cpu_reset,
+	input data_in,
+	output[5:0] data_out
+);
+
+wire [5:0] data_out;
+wire builder_pll_fb;
+wire fdce_0_out, fdce_1_out;
+wire main_locked;
+
+FDCE FDCE_0 (
+	.D(data_in),
+	.C(clk),
+	.CE(1'b1),
+	.CLR(1'b0),
+	.Q(fdce_0_out)
+);
+
+FDCE FDCE_1 (
+	.D(fdce_0_out),
+	.C(clk),
+	.CE(1'b1),
+	.CLR(1'b0),
+	.Q(data_out[0])
+);
+
+PLLE2_ADV #(
+	.CLKFBOUT_MULT(4'd12),
+	.CLKIN1_PERIOD(10.0),
+	.CLKOUT0_DIVIDE(4'd12),
+	.CLKOUT0_PHASE(90.0),
+	.CLKOUT1_DIVIDE(2'd3),
+	.CLKOUT1_PHASE(0.0),
+	.CLKOUT2_DIVIDE(3'd6),
+	.CLKOUT2_PHASE(90.0),
+	.DIVCLK_DIVIDE(2'd2),
+	.REF_JITTER1(0.01),
+	.STARTUP_WAIT("FALSE")
+) PLLE2_ADV (
+	.CLKFBIN(builder_pll_fb),
+	.CLKIN1(clk),
+	.RST(cpu_reset),
+	.CLKFBOUT(builder_pll_fb),
+	.CLKOUT0(main_clkout0),
+	.CLKOUT1(main_clkout1),
+	.CLKOUT2(main_clkout2),
+	.LOCKED(main_locked)
+);
+
+FDCE FDCE_PLLx1_PH90 (
+	.D(data_in),
+	.C(main_clkout0),
+	.CE(1'b1),
+	.CLR(1'b0),
+	.Q(data_out[1])
+);
+
+FDCE FDCE_PLLx4_PH0_0 (
+	.D(data_in),
+	.C(main_clkout1),
+	.CE(1'b1),
+	.CLR(1'b0),
+	.Q(data_out[2])
+);
+
+FDCE FDCE_PLLx4_PH0_1 (
+	.D(data_in),
+	.C(main_clkout1),
+	.CE(1'b1),
+	.CLR(1'b0),
+	.Q(data_out[3])
+);
+
+FDCE FDCE_PLLx4_PH0_2 (
+	.D(data_in),
+	.C(main_clkout1),
+	.CE(1'b1),
+	.CLR(1'b0),
+	.Q(data_out[4])
+);
+
+FDCE FDCE_PLLx2_PH90_0 (
+	.D(data_in),
+	.C(main_clkout2),
+	.CE(1'b1),
+	.CLR(1'b0),
+	.Q(data_out[5])
+);
+endmodule
diff --git a/sdc-plugin/tests/pll_fbout_phase/pll_fbout_phase.golden.sdc b/sdc-plugin/tests/pll_fbout_phase/pll_fbout_phase.golden.sdc
new file mode 100644
index 0000000..e3f281c
--- /dev/null
+++ b/sdc-plugin/tests/pll_fbout_phase/pll_fbout_phase.golden.sdc
@@ -0,0 +1,8 @@
+create_clock -period 10 -waveform {0 5} \$auto\$clkbufmap.cc:247:execute\$1829
+create_clock -period 10 -waveform {0 5} \$techmap1716\FDCE_0.C
+create_clock -period 10 -waveform {0 5} \$auto\$clkbufmap.cc:247:execute\$1831
+create_clock -period 10 -waveform {0 5} main_clkout_x1
+create_clock -period 5 -waveform {-2.5 0} \$auto\$clkbufmap.cc:247:execute\$1833
+create_clock -period 5 -waveform {-2.5 0} main_clkout_x2
+create_clock -period 2.5 -waveform {-1.875 -0.625} \$auto\$clkbufmap.cc:247:execute\$1835
+create_clock -period 2.5 -waveform {-1.875 -0.625} main_clkout_x4
diff --git a/sdc-plugin/tests/pll_fbout_phase/pll_fbout_phase.input.sdc b/sdc-plugin/tests/pll_fbout_phase/pll_fbout_phase.input.sdc
new file mode 100644
index 0000000..00354d7
--- /dev/null
+++ b/sdc-plugin/tests/pll_fbout_phase/pll_fbout_phase.input.sdc
@@ -0,0 +1 @@
+create_clock -period 10 -waveform {0 5} clk
diff --git a/sdc-plugin/tests/pll_fbout_phase/pll_fbout_phase.tcl b/sdc-plugin/tests/pll_fbout_phase/pll_fbout_phase.tcl
new file mode 100644
index 0000000..6b6db73
--- /dev/null
+++ b/sdc-plugin/tests/pll_fbout_phase/pll_fbout_phase.tcl
@@ -0,0 +1,21 @@
+yosys -import
+plugin -i sdc
+# Import the commands from the plugins to the tcl interpreter
+yosys -import
+
+read_verilog pll_fbout_phase.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 -vpr -flatten -abc9 -nosrl -nodsp -iopad -run prepare:check
+
+# Read the design timing constraints
+read_sdc $::env(INPUT_SDC_FILE)
+
+# Propagate the clocks
+propagate_clocks
+
+# Write out the SDC file after the clock propagation step
+write_sdc $::env(OUTPUT_SDC_FILE)
diff --git a/sdc-plugin/tests/pll_fbout_phase/pll_fbout_phase.v b/sdc-plugin/tests/pll_fbout_phase/pll_fbout_phase.v
new file mode 100644
index 0000000..2dc6c00
--- /dev/null
+++ b/sdc-plugin/tests/pll_fbout_phase/pll_fbout_phase.v
@@ -0,0 +1,91 @@
+module top(
+	input clk,
+	input cpu_reset,
+	input data_in,
+	output[5:0] data_out
+);
+
+wire [5:0] data_out;
+wire builder_pll_fb;
+wire fdce_0_out, fdce_1_out;
+wire main_locked;
+
+FDCE FDCE_0 (
+	.D(data_in),
+	.C(clk),
+	.CE(1'b1),
+	.CLR(1'b0),
+	.Q(fdce_0_out)
+);
+
+FDCE FDCE_1 (
+	.D(fdce_0_out),
+	.C(clk),
+	.CE(1'b1),
+	.CLR(1'b0),
+	.Q(data_out[0])
+);
+
+PLLE2_ADV #(
+	.CLKFBOUT_MULT(4'd12),
+	.CLKFBOUT_PHASE(90.0),
+	.CLKIN1_PERIOD(10.0),
+	.CLKOUT0_DIVIDE(4'd12),
+	.CLKOUT0_PHASE(90.0),
+	.CLKOUT1_DIVIDE(3'd6),
+	.CLKOUT1_PHASE(0.0),
+	.CLKOUT2_DIVIDE(2'd3),
+	.CLKOUT2_PHASE(90.0),
+	.REF_JITTER1(0.01),
+	.STARTUP_WAIT("FALSE")
+) PLLE2_ADV (
+	.CLKFBIN(builder_pll_fb),
+	.CLKIN1(clk),
+	.RST(cpu_reset),
+	.CLKFBOUT(builder_pll_fb),
+	.CLKOUT0(main_clkout_x1),
+	.CLKOUT1(main_clkout_x2),
+	.CLKOUT2(main_clkout_x4),
+	.LOCKED(main_locked)
+);
+
+FDCE FDCE_PLLx1_PH90 (
+	.D(data_in),
+	.C(main_clkout_x1),
+	.CE(1'b1),
+	.CLR(1'b0),
+	.Q(data_out[1])
+);
+
+FDCE FDCE_PLLx4_PH0_0 (
+	.D(data_in),
+	.C(main_clkout_x2),
+	.CE(1'b1),
+	.CLR(1'b0),
+	.Q(data_out[2])
+);
+
+FDCE FDCE_PLLx4_PH0_1 (
+	.D(data_in),
+	.C(main_clkout_x2),
+	.CE(1'b1),
+	.CLR(1'b0),
+	.Q(data_out[3])
+);
+
+FDCE FDCE_PLLx4_PH0_2 (
+	.D(data_in),
+	.C(main_clkout_x2),
+	.CE(1'b1),
+	.CLR(1'b0),
+	.Q(data_out[4])
+);
+
+FDCE FDCE_PLLx2_PH90_0 (
+	.D(data_in),
+	.C(main_clkout_x4),
+	.CE(1'b1),
+	.CLR(1'b0),
+	.Q(data_out[5])
+);
+endmodule