SDC: Handle input clock phase shift on clock generators

Signed-off-by: Tomasz Michalak <tmichalak@antmicro.com>
diff --git a/sdc-plugin/buffers.cc b/sdc-plugin/buffers.cc
index eb4d0a7..5b42129 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,59 @@
                                                "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_shift) {
+    assert(RTLIL::unescape_id(cell->type) == "PLLE2_ADV");
+
+    FetchParams(cell);
+    CalculateOutputClockPeriods();
+    CalculateOutputClockWaveforms(input_clock_period, input_clock_shift);
+}
+
+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] = clkin1_period * clkout_divisor.at(output) / clk_mult *
+	    divclk_divisor;
+    }
+}
+
+void Pll::CalculateOutputClockWaveforms(float input_clock_period, float input_clock_shift) {
+    for (auto output : outputs) {
+	float output_clock_period = clkout_period.at(output);
+	clkout_rising_edge[output] = fmod(input_clock_shift - (clk_fbout_phase / 360.0) * input_clock_period + 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..d3f7c30 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,39 @@
 };
 
 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_shift);
 
-	    // CLKOUT[0-5]_PHASE
-	    clkout_phase[clk_output] = FetchParam(cell, clk_output + "_PHASE", 0.0);
+    // Fetch cell's parameters needed for further calculations
+    void FetchParams(RTLIL::Cell* cell);
 
-	    // Take the delay off the PLL into account
-	    clkout_shift[clk_output] = CalculateShift(clk_output) + delay;
+    // Calculate the period on the output clocks
+    void CalculateOutputClockPeriods();
 
-	    // CLKOUT[0-5]_DUTY_CYCLE
-	    clkout_duty_cycle[clk_output] = FetchParam(cell, clk_output + "_DUTY_CYCLE", 0.5);
-	}
-    };
+    // Calculate the rising and falling edges of the output clocks
+    void CalculateOutputClockWaveforms(float input_clock_period, float input_clock_shift);
 
-    // 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;
-    }
-
-    float CalculateShift(const std::string& output) {
-	return clkout_period.at(output) * clkout_phase.at(output) / 360.0;
-    }
-
-    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;
-    }
+    // 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);
 
     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;
+    std::unordered_map<std::string, float> clkout_rising_edge;
+    std::unordered_map<std::string, float> clkout_falling_edge;
     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_);
 	}