SDC: Add duty cycle and input clock delay to output clock

Signed-off-by: Tomasz Michalak <tmichalak@antmicro.com>
diff --git a/sdc-plugin/buffers.h b/sdc-plugin/buffers.h
index 024416b..fcce64f 100644
--- a/sdc-plugin/buffers.h
+++ b/sdc-plugin/buffers.h
@@ -61,12 +61,32 @@
 	    divclk_divisor = cell->getParam(ID(DIVCLK_DIVIDE)).as_int();
 	}
 	for (auto clk_output : outputs) {
+	    // CLKOUT[0-5]_DIVIDE
 	    RTLIL::IdString param(RTLIL::escape_id(clk_output + "_DIVIDE"));
 	    if (cell->hasParam(param)) {
 		clkout_divisors[clk_output] = cell->getParam(param).as_int();
 	    } else {
 		clkout_divisors[clk_output] = 1;
 	    }
+	    clkout_period[clk_output] = CalculatePeriod(clk_output);
+
+	    // CLKOUT[0-5]_PHASE
+	    param = RTLIL::escape_id(clk_output + "_PHASE");
+	    if (cell->hasParam(param)) {
+		clkout_phase[clk_output] = std::stof(cell->getParam(param).decode_string());
+	    } else {
+		clkout_phase[clk_output] = 0.0;
+	    }
+	    // Take the delay off the PLL into account
+	    clkout_shift[clk_output] = CalculateShift(clk_output) + delay;
+
+	    // CLKOUT[0-5]_DUTY_CYCLE
+	    param = RTLIL::escape_id(clk_output + "_DUTY_CYCLE");
+	    if (cell->hasParam(param)) {
+		clkout_duty_cycle[clk_output] = std::stof(cell->getParam(param).decode_string());
+	    } else {
+		clkout_duty_cycle[clk_output] = 0.5;
+	    }
 	}
     };
 
@@ -78,6 +98,10 @@
 	       divclk_divisor;
     }
 
+    float CalculateShift(const std::string& output) {
+	return clkout_period.at(output) * clkout_phase.at(output) / 360.0;
+    }
+
     static const float delay;
     static const std::string name;
     static const std::vector<std::string> inputs;
@@ -85,6 +109,10 @@
     RTLIL::Cell* cell;
     float clkin1_period = 0;
     float clkin2_period = 0;
+    std::unordered_map<std::string, float> clkout_period;
+    std::unordered_map<std::string, float> clkout_duty_cycle;
+    std::unordered_map<std::string, float> clkout_phase;
+    std::unordered_map<std::string, float> clkout_shift;
     std::unordered_map<std::string, int> clkout_divisors;
     int divclk_divisor = 1;
     int clk_mult = 5;
diff --git a/sdc-plugin/clocks.cc b/sdc-plugin/clocks.cc
index ce1b704..3854108 100644
--- a/sdc-plugin/clocks.cc
+++ b/sdc-plugin/clocks.cc
@@ -128,6 +128,7 @@
 #ifdef SDC_DEBUG
 		log("PLL clock: %s\n", pll_clock.Name().c_str());
 #endif
+		pll_clock.ApplyShift(clock.RisingEdge());
 		AddClock(pll_clock);
 		PropagateThroughBuffer(pass, pll_clock, Bufg());
 	    }
@@ -224,6 +225,14 @@
     falling_edge_ = falling_edge;
 }
 
+void Clock::ApplyShift(float rising_edge) {
+    rising_edge_ += rising_edge;
+    falling_edge_ += rising_edge;
+    if (falling_edge_ > period_) {
+	log_error("Phase shift exceeds 360 degrees\n");
+    }
+}
+
 std::string Clock::ClockWireName(RTLIL::Wire* wire) {
     if (!wire) {
 	return std::string();
diff --git a/sdc-plugin/clocks.h b/sdc-plugin/clocks.h
index faa8aec..28cd7f7 100644
--- a/sdc-plugin/clocks.h
+++ b/sdc-plugin/clocks.h
@@ -43,6 +43,7 @@
     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 35e86f3..39ea476 100644
--- a/sdc-plugin/propagation.cc
+++ b/sdc-plugin/propagation.cc
@@ -55,9 +55,11 @@
 	for (auto output : Pll::outputs) {
 	    RTLIL::Wire* wire = FindSinkWireOnPort(cell, output);
 	    if (wire) {
-		float period(pll.CalculatePeriod(output));
-		Clock clock(RTLIL::unescape_id(wire->name), wire, period, 0,
-		            period / 2);
+		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);
diff --git a/sdc-plugin/tests/pll/pll.golden.sdc b/sdc-plugin/tests/pll/pll.golden.sdc
index c6ba07a..4080fa4 100644
--- a/sdc-plugin/tests/pll/pll.golden.sdc
+++ b/sdc-plugin/tests/pll/pll.golden.sdc
@@ -1,6 +1,6 @@
 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 {0 5} \$auto\$clkbufmap.cc:247:execute\$1829
-create_clock -period 10 -waveform {1 6} main_clkout0
-create_clock -period 2.5 -waveform {0 1.25} \$auto\$clkbufmap.cc:247:execute\$1831
-create_clock -period 2.5 -waveform {1 2.25} main_clkout1
+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 2.5 -waveform {1 2.25} \$auto\$clkbufmap.cc:247:execute\$1831
+create_clock -period 2.5 -waveform {2 3.25} main_clkout1
diff --git a/sdc-plugin/tests/pll/pll.v b/sdc-plugin/tests/pll/pll.v
index 1bb3a97..17a0064 100644
--- a/sdc-plugin/tests/pll/pll.v
+++ b/sdc-plugin/tests/pll/pll.v
@@ -30,9 +30,9 @@
 	.CLKFBOUT_MULT(4'd12),
 	.CLKIN1_PERIOD(10.0),
 	.CLKOUT0_DIVIDE(4'd12),
-	.CLKOUT0_PHASE(1'd0),
+	.CLKOUT0_PHASE(90.0),
 	.CLKOUT1_DIVIDE(2'd3),
-	.CLKOUT1_PHASE(1'd0),
+	.CLKOUT1_PHASE(0.0),
 	.DIVCLK_DIVIDE(1'd1),
 	.REF_JITTER1(0.01),
 	.STARTUP_WAIT("FALSE")