| /* |
| * 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 "clocks.h" |
| #include <cassert> |
| #include "kernel/log.h" |
| #include "kernel/register.h" |
| #include "propagation.h" |
| |
| void Clocks::AddClockWires(const std::string& name, |
| const std::vector<RTLIL::Wire*>& wires, float period, |
| float rising_edge, float falling_edge) { |
| std::for_each(wires.begin(), wires.end(), [&, this](RTLIL::Wire* wire) { |
| AddClockWire(name, wire, period, rising_edge, falling_edge); |
| }); |
| } |
| |
| void Clocks::AddClockWire(const std::string& name, RTLIL::Wire* wire, |
| float period, float rising_edge, float falling_edge) { |
| auto clock = clocks_.find(name); |
| if (clock == clocks_.end()) { |
| clocks_.emplace(std::make_pair(name, Clock(name, wire, period, rising_edge, falling_edge))); |
| } else { |
| clock->second.AddClockWire(wire, period, rising_edge, falling_edge); |
| } |
| } |
| |
| void Clocks::AddClockWire(const std::string& name, ClockWire& clock_wire) { |
| AddClockWire(name, clock_wire.Wire(), clock_wire.Period(), clock_wire.RisingEdge(), clock_wire.FallingEdge()); |
| } |
| |
| std::vector<std::string> Clocks::GetClockNames() { |
| std::vector<std::string> res; |
| for (auto clock : clocks_) { |
| res.push_back(clock.first); |
| #ifdef SDC_DEBUG |
| log("Wires in clock %s:\n", clock.first.c_str()); |
| for (auto clock_wire : clock.second.GetClockWires()) { |
| log("create_clock -period %f -name %s -waveform {%f %f} %s\n", |
| clock_wire.Period(), clock.first.c_str(), |
| clock_wire.RisingEdge(), clock_wire.FallingEdge(), |
| clock_wire.WireName().c_str()); |
| } |
| #endif |
| } |
| return res; |
| } |
| |
| void Clocks::Propagate(NaturalPropagation* pass) { |
| #ifdef SDC_DEBUG |
| log("Start natural clock propagation\n"); |
| #endif |
| for (auto clock : clocks_) { |
| #ifdef SDC_DEBUG |
| log("Processing clock %s\n", clock.first.c_str()); |
| #endif |
| auto clock_wires = clock.second.GetClockWires(); |
| for (auto clock_wire : clock_wires) { |
| auto aliases = pass->FindAliasWires(clock_wire.Wire()); |
| AddClockWires(clock.first, aliases, clock_wire.Period(), |
| clock_wire.RisingEdge(), clock_wire.FallingEdge()); |
| } |
| } |
| #ifdef SDC_DEBUG |
| log("Finish natural clock propagation\n\n"); |
| #endif |
| } |
| |
| void Clocks::Propagate(BufferPropagation* pass) { |
| #ifdef SDC_DEBUG |
| log("Start buffer clock propagation\n"); |
| #endif |
| for (auto& clock : clocks_) { |
| #ifdef SDC_DEBUG |
| log("Processing clock %s\n", clock.first.c_str()); |
| #endif |
| PropagateThroughBuffer(pass, clock, IBuf()); |
| PropagateThroughBuffer(pass, clock, Bufg()); |
| } |
| #ifdef SDC_DEBUG |
| log("Finish buffer clock propagation\n\n"); |
| #endif |
| } |
| |
| void Clocks::Propagate(ClockDividerPropagation* pass) { |
| #ifdef SDC_DEBUG |
| log("Start clock divider clock propagation\n"); |
| #endif |
| for (auto& clock : clocks_) { |
| #ifdef SDC_DEBUG |
| log("Processing clock %s\n", clock.first.c_str()); |
| #endif |
| auto clock_wires = clock.second.GetClockWires(); |
| for (auto clock_wire : clock_wires) { |
| auto pll_clock_wires = pass->FindSinkClockWiresForCellType( |
| clock_wire, "PLLE2_ADV"); |
| for (auto pll_clock_wire : pll_clock_wires) { |
| #ifdef SDC_DEBUG |
| log("PLL wire: %s\n", pll_clock_wire.WireName().c_str()); |
| #endif |
| AddClockWire(pll_clock_wire.WireName(), pll_clock_wire); |
| } |
| } |
| } |
| #ifdef SDC_DEBUG |
| log("Finish clock divider clock propagation\n\n"); |
| #endif |
| } |
| |
| void Clocks::PropagateThroughBuffer(BufferPropagation* pass, |
| decltype(clocks_)::value_type clock, |
| Buffer buffer) { |
| auto clock_wires = clock.second.GetClockWires(); |
| for (auto clock_wire : clock_wires) { |
| auto buf_wires = pass->FindSinkWiresForCellType( |
| clock_wire.Wire(), buffer.name, buffer.output); |
| int path_delay(0); |
| for (auto wire : buf_wires) { |
| #ifdef SDC_DEBUG |
| log("%s wire: %s\n", buffer.name.c_str(), wire->name.c_str()); |
| #endif |
| path_delay += buffer.delay; |
| AddClockWire(wire->name.str(), wire, clock_wire.Period(), |
| clock_wire.RisingEdge() + path_delay, |
| clock_wire.FallingEdge() + path_delay); |
| } |
| } |
| } |
| |
| void Clocks::WriteSdc(std::ostream& file) { |
| for (auto& clock : clocks_) { |
| auto clock_wires = clock.second.GetClockWires(); |
| file << "create_clock"; |
| for (auto clock_wire : clock_wires) { |
| file << " -period " << clock_wire.Period(); |
| if (clock_wires.size() > 1) { |
| file << " -name " << clock.first; |
| } |
| } |
| /* log("create_clock -period %f -name %s -waveform {%f %f} %s\n", */ |
| /* clock_wire.Period(), clock.first.c_str(), */ |
| /* clock_wire.RisingEdge(), clock_wire.FallingEdge(), */ |
| /* clock_wire.WireName().c_str()); */ |
| } |
| } |
| |
| Clock::Clock(const std::string& name, RTLIL::Wire* wire, float period, |
| float rising_edge, float falling_edge) |
| : name_(name) { |
| AddClockWire(wire, period, rising_edge, falling_edge); |
| } |
| |
| void Clock::AddClockWire(RTLIL::Wire* wire, float period, float rising_edge, |
| float falling_edge) { |
| auto clock_wire = std::find_if( |
| clock_wires_.begin(), clock_wires_.end(), |
| [wire](ClockWire& clock_wire) { return clock_wire.Wire() == wire; }); |
| if (clock_wire == clock_wires_.end()) { |
| clock_wires_.emplace_back(wire, period, rising_edge, falling_edge); |
| } else { |
| clock_wire->UpdatePeriod(period); |
| clock_wire->UpdateWaveform(rising_edge, falling_edge); |
| } |
| } |
| |
| void ClockWire::UpdatePeriod(float period) { |
| period_ = period; |
| rising_edge_ = 0; |
| falling_edge_ = period / 2; |
| } |
| |
| void ClockWire::UpdateWaveform(float rising_edge, float falling_edge) { |
| rising_edge_ = rising_edge; |
| falling_edge_ = falling_edge; |
| assert(falling_edge - rising_edge == period_ / 2); |
| } |