|  | /* | 
|  | *  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; | 
|  | } |