blob: ce1b7046a8bedd88e7c5cd1fd94dc699d1c67170 [file] [log] [blame]
/*
* 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::AddClock(const std::string& name, std::vector<RTLIL::Wire*> wires,
float period, float rising_edge, float falling_edge) {
std::for_each(wires.begin(), wires.end(), [&, this](RTLIL::Wire* wire) {
AddClock(name, wire, period, rising_edge, falling_edge);
});
}
void Clocks::AddClock(const std::string& name, RTLIL::Wire* wire, float period,
float rising_edge, float falling_edge) {
auto clock =
std::find_if(clocks_.begin(), clocks_.end(),
[&](Clock& clock) { return clock.Name() == name; });
if (clock != clocks_.end()) {
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);
clocks_.emplace_back(name, wire, period, rising_edge, falling_edge);
}
}
void Clocks::AddClock(Clock& clock) {
AddClock(clock.Name(), clock.GetClockWires(), clock.Period(),
clock.RisingEdge(), clock.FallingEdge());
}
std::vector<std::string> Clocks::GetClockNames() {
std::vector<std::string> res;
for (auto clock : clocks_) {
res.push_back(clock.Name());
#ifdef SDC_DEBUG
std::stringstream ss;
for (auto clock_wire : clock.GetClockWires()) {
ss << RTLIL::unescape_id(clock_wire->name) << " ";
}
log("create_clock -period %f -name %s -waveform {%f %f} %s\n",
clock.Period(), clock.Name().c_str(), clock.RisingEdge(),
clock.FallingEdge(), ss.str().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.Name().c_str());
#endif
auto clock_wires = clock.GetClockWires();
for (auto clock_wire : clock_wires) {
auto aliases = pass->FindAliasWires(clock_wire);
AddClock(clock.Name(), aliases, clock.Period(),
clock.RisingEdge(), clock.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");
log("IBUF pass\n");
#endif
std::vector<Clock> clocks(clocks_);
for (auto clock : clocks) {
#ifdef SDC_DEBUG
log("Processing clock %s\n", clock.Name().c_str());
#endif
PropagateThroughBuffer(pass, clock, IBuf());
}
#ifdef SDC_DEBUG
log("BUFG pass\n");
#endif
clocks = clocks_;
for (auto clock : clocks) {
#ifdef SDC_DEBUG
log("Processing clock %s\n", clock.Name().c_str());
#endif
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.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) {
#ifdef SDC_DEBUG
log("PLL clock: %s\n", pll_clock.Name().c_str());
#endif
AddClock(pll_clock);
PropagateThroughBuffer(pass, pll_clock, Bufg());
}
}
}
#ifdef SDC_DEBUG
log("Finish clock divider clock propagation\n\n");
#endif
}
void Clocks::PropagateThroughBuffer(Propagation* pass, Clock& clock,
Buffer buffer) {
auto clock_wires = clock.GetClockWires();
for (auto clock_wire : clock_wires) {
#ifdef SDC_DEBUG
log("Clock wire %s\n", RTLIL::unescape_id(clock_wire->name).c_str());
#endif
auto buf_wires = pass->FindSinkWiresForCellType(clock_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(),
RTLIL::unescape_id(wire->name).c_str());
#endif
path_delay += buffer.delay;
AddClock(RTLIL::unescape_id(wire->name), wire, clock.Period(),
clock.RisingEdge() + path_delay,
clock.FallingEdge() + path_delay);
}
}
}
void Clocks::WriteSdc(std::ostream& file) {
for (auto& clock : clocks_) {
auto clock_wires = clock.GetClockWires();
// FIXME: Input port nets are not found in VPR
if (std::all_of(clock_wires.begin(), clock_wires.end(),
[&](RTLIL::Wire* wire) { return wire->port_input; })) {
continue;
}
file << "create_clock -period " << clock.Period();
file << " -waveform {" << clock.RisingEdge() << " "
<< clock.FallingEdge() << "}";
for (auto clock_wire : clock_wires) {
if (clock_wire->port_input) {
continue;
}
file << " " << Clock::ClockWireName(clock_wire);
}
file << std::endl;
}
}
Clock::Clock(const std::string& name, RTLIL::Wire* wire, float period,
float rising_edge, float falling_edge)
: name_(name),
period_(period),
rising_edge_(rising_edge),
falling_edge_(falling_edge) {
UpdateWires(wire);
}
Clock::Clock(const std::string& name, std::vector<RTLIL::Wire*> wires,
float period, float rising_edge, float falling_edge)
: name_(name),
period_(period),
rising_edge_(rising_edge),
falling_edge_(falling_edge) {
std::for_each(wires.begin(), wires.end(),
[&, this](RTLIL::Wire* wire) { UpdateWires(wire); });
}
void Clock::UpdateClock(RTLIL::Wire* wire, float period, float rising_edge,
float falling_edge) {
UpdateWires(wire);
UpdatePeriod(period);
UpdateWaveform(rising_edge, falling_edge);
}
void Clock::UpdateWires(RTLIL::Wire* wire) {
if (std::find(clock_wires_.begin(), clock_wires_.end(), wire) ==
clock_wires_.end()) {
clock_wires_.push_back(wire);
}
}
void Clock::UpdatePeriod(float period) {
period_ = period;
}
void Clock::UpdateWaveform(float rising_edge, float falling_edge) {
rising_edge_ = rising_edge;
falling_edge_ = falling_edge;
}
std::string Clock::ClockWireName(RTLIL::Wire* wire) {
if (!wire) {
return std::string();
}
std::string wire_name(RTLIL::unescape_id(wire->name));
return std::regex_replace(wire_name, std::regex{"\\$"}, "\\$");
}