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