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_);
}