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