| /* | 
 |  *  yosys -- Yosys Open SYnthesis Suite | 
 |  * | 
 |  *  Copyright (C) 2020  The Symbiflow Authors | 
 |  * | 
 |  *  Permission to use, copy, modify, and/or distribute this software for any | 
 |  *  purpose with or without fee is hereby granted, provided that the above | 
 |  *  copyright notice and this permission notice appear in all copies. | 
 |  * | 
 |  *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | 
 |  *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | 
 |  *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | 
 |  *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | 
 |  *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | 
 |  *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | 
 |  *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | 
 |  */ | 
 | #include "buffers.h" | 
 | #include <cassert> | 
 | #include <cmath> | 
 |  | 
 | const std::vector<std::string> Pll::inputs = {"CLKIN1", "CLKIN2"}; | 
 | const std::vector<std::string> Pll::outputs = {"CLKOUT0", "CLKOUT1", "CLKOUT2", "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) : ClockDivider({"PLLE2_ADV"}) | 
 | { | 
 |     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 < std::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 &¶m_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; | 
 | } |