blob: 3a856c36b9fa6034e222f63bdf393a666fe573ed [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 <cmath>
#include <regex>
#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(), [&](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) {
wire->set_string_attribute(RTLIL::escape_id("CLOCK_SIGNAL"), "yes");
wire->set_string_attribute(RTLIL::escape_id("CLASS"), "clock");
wire->set_string_attribute(RTLIL::escape_id("NAME"), name);
wire->set_string_attribute(RTLIL::escape_id("SOURCE_PINS"), Clock::ClockWireName(wire));
wire->set_string_attribute(RTLIL::escape_id("PERIOD"), std::to_string(period));
std::string waveform(std::to_string(rising_edge) + " " + std::to_string(falling_edge));
wire->set_string_attribute(RTLIL::escape_id("WAVEFORM"), waveform);
}
void Clocks::AddClock(RTLIL::Wire* wire, float period,
float rising_edge, float falling_edge) {
AddClock(Clock::ClockWireName(wire), wire, period, rising_edge, falling_edge);
}
const std::vector<RTLIL::Wire*> Clocks::GetClocks(RTLIL::Design* design) {
std::vector<RTLIL::Wire*> clock_wires;
RTLIL::Module* top_module = design->top_module();
for (auto& wire_obj : top_module->wires_) {
auto& wire = wire_obj.second;
if (wire->has_attribute(RTLIL::escape_id("CLOCK_SIGNAL"))) {
if (wire->get_string_attribute(RTLIL::escape_id("CLOCK_SIGNAL")) == "yes") {
clock_wires.push_back(wire);
}
}
}
return clock_wires;
}
void Clocks::Propagate(RTLIL::Design* design, NaturalPropagation* pass) {
#ifdef SDC_DEBUG
log("Start natural clock propagation\n");
#endif
for (auto& clock_wire : Clocks::GetClocks(design)) {
#ifdef SDC_DEBUG
log("Processing clock %s\n", RTLIL::id2cstr(clock_wire->name));
#endif
auto aliases = pass->FindAliasWires(clock_wire);
AddClock(Clock::ClockWireName(clock_wire), aliases,
Clock::Period(clock_wire), Clock::RisingEdge(clock_wire),
Clock::FallingEdge(clock_wire));
}
#ifdef SDC_DEBUG
log("Finish natural clock propagation\n\n");
#endif
}
void Clocks::Propagate(RTLIL::Design* design, BufferPropagation* pass) {
#ifdef SDC_DEBUG
log("Start buffer clock propagation\n");
log("IBUF pass\n");
#endif
PropagateThroughBuffers(pass, design, IBuf());
#ifdef SDC_DEBUG
log("BUFG pass\n");
#endif
PropagateThroughBuffers(pass, design, Bufg());
#ifdef SDC_DEBUG
log("Finish buffer clock propagation\n\n");
#endif
}
void Clocks::Propagate(RTLIL::Design* design, ClockDividerPropagation* pass) {
#ifdef SDC_DEBUG
log("Start clock divider clock propagation\n");
#endif
for (auto& clock_wire : Clocks::GetClocks(design)) {
#ifdef SDC_DEBUG
log("Processing clock %s\n", RTLIL::id2cstr(clock_wire->name));
#endif
pass->PropagateClocksForCellType(clock_wire, "PLLE2_ADV");
PropagateThroughBuffers(pass, design, Bufg());
}
#ifdef SDC_DEBUG
log("Finish clock divider clock propagation\n\n");
#endif
}
void Clocks::PropagateThroughBuffers(Propagation* pass, RTLIL::Design* design,
Buffer buffer) {
for (auto& clock_wire : Clocks::GetClocks(design)) {
#ifdef SDC_DEBUG
log("Clock wire %s\n", Clock::ClockWireName(clock_wire).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::id2cstr(wire->name));
#endif
path_delay += buffer.delay;
AddClock(wire, Clock::Period(clock_wire),
Clock::RisingEdge(clock_wire) + path_delay,
Clock::FallingEdge(clock_wire) + path_delay);
}
}
}
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); });
}
Clock::Clock(RTLIL::Wire* wire, float period,
float rising_edge, float falling_edge)
: Clock(RTLIL::unescape_id(wire->name), wire, period, rising_edge, falling_edge) {}
float Clock::Period(RTLIL::Wire* clock_wire) {
if (!clock_wire->has_attribute(RTLIL::escape_id("PERIOD"))) {
log_warning("Period has not been specified\n Default value 0 will be used\n");
return 0;
}
return std::stof(clock_wire->get_string_attribute(RTLIL::escape_id("PERIOD")));
}
std::pair<float, float> Clock::Waveform(RTLIL::Wire* clock_wire) {
if (!clock_wire->has_attribute(RTLIL::escape_id("WAVEFORM"))) {
float period(Period(clock_wire));
if (!period) {
log_cmd_error("Neither PERIOD nor WAVEFORM has been specified for wire %s\n", ClockWireName(clock_wire).c_str());
return std::make_pair(0,0);
}
float falling_edge = period / 2;
log_warning("Waveform has not been specified\n Default value {0 %f} will be used\n", falling_edge);
return std::make_pair(0, falling_edge);
}
float rising_edge(0);
float falling_edge(0);
std::string waveform(clock_wire->get_string_attribute(RTLIL::escape_id("WAVEFORM")));
std::sscanf(waveform.c_str(), "%f %f", &rising_edge, &falling_edge);
return std::make_pair(rising_edge, falling_edge);
}
float Clock::RisingEdge(RTLIL::Wire* clock_wire) {
return Waveform(clock_wire).first;
}
float Clock::FallingEdge(RTLIL::Wire* clock_wire) {
return Waveform(clock_wire).second;
}
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_ = fmod(rising_edge, period_);
falling_edge_ = fmod(falling_edge, period_);
}
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{"\\$"}, "\\$");
}