| /* |
| * 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/register.h" |
| #include "propagation.h" |
| |
| void Clock::Add(const std::string& name, RTLIL::Wire* wire, float period, |
| float rising_edge, float falling_edge, ClockType type) { |
| wire->set_string_attribute(RTLIL::escape_id("CLOCK_SIGNAL"), "yes"); |
| wire->set_bool_attribute(RTLIL::escape_id("IS_GENERATED"), type == GENERATED); |
| wire->set_bool_attribute(RTLIL::escape_id("IS_EXPLICIT"), type == EXPLICIT); |
| wire->set_bool_attribute(RTLIL::escape_id("IS_PROPAGATED"), type == PROPAGATED); |
| 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::WireName(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 Clock::Add(const std::string& name, std::vector<RTLIL::Wire*> wires, |
| float period, float rising_edge, float falling_edge, ClockType type) { |
| std::for_each(wires.begin(), wires.end(), [&](RTLIL::Wire* wire) { |
| Add(name, wire, period, rising_edge, falling_edge, type); |
| }); |
| } |
| |
| void Clock::Add(RTLIL::Wire* wire, float period, float rising_edge, |
| float falling_edge, ClockType type) { |
| Add(Clock::WireName(wire), wire, period, rising_edge, falling_edge, type); |
| } |
| |
| float Clock::Period(RTLIL::Wire* clock_wire) { |
| if (!clock_wire->has_attribute(RTLIL::escape_id("PERIOD"))) { |
| log_cmd_error("PERIOD has not been specified on wire '%s'.\n", |
| WireName(clock_wire).c_str()); |
| } |
| float period(0); |
| std::string period_str; |
| try { |
| period_str = |
| clock_wire->get_string_attribute(RTLIL::escape_id("PERIOD")); |
| period = std::stof(period_str); |
| } catch (const std::invalid_argument& e) { |
| log_cmd_error( |
| "Incorrect value '%s' specifed on PERIOD attribute for wire " |
| "'%s'.\nPERIOD needs to be a float value.\n", |
| period_str.c_str(), WireName(clock_wire).c_str()); |
| } |
| return 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", |
| WireName(clock_wire).c_str()); |
| return std::make_pair(0, 0); |
| } |
| float falling_edge = period / 2; |
| log_warning( |
| "Waveform has not been specified on wire '%s'.\nDefault value {0 %f} " |
| "will be used\n", |
| WireName(clock_wire).c_str(), 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"))); |
| if (std::sscanf(waveform.c_str(), "%f %f", &rising_edge, &falling_edge) != |
| 2) { |
| log_cmd_error( |
| "Incorrect value '%s' specifed on WAVEFORM attribute for wire " |
| "'%s'.\nWAVEFORM needs to be specified in form of '<rising_edge> " |
| "<falling_edge>' where the edge values are floats.\n", |
| waveform.c_str(), WireName(clock_wire).c_str()); |
| } |
| 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; |
| } |
| |
| std::string Clock::Name(RTLIL::Wire* clock_wire) { |
| if (clock_wire->has_attribute(RTLIL::escape_id("NAME"))) { |
| return clock_wire->get_string_attribute(RTLIL::escape_id("NAME")); |
| } |
| return WireName(clock_wire); |
| } |
| |
| std::string Clock::WireName(RTLIL::Wire* clock_wire) { |
| if (!clock_wire) { |
| return std::string(); |
| } |
| return AddEscaping(RTLIL::unescape_id(clock_wire->name)); |
| } |
| |
| std::string Clock::SourcePinName(RTLIL::Wire* clock_wire) { |
| if (clock_wire->has_attribute(RTLIL::escape_id("SOURCE_PINS"))) { |
| return clock_wire->get_string_attribute(RTLIL::escape_id("SOURCE_PINS")); |
| } |
| return Name(clock_wire); |
| } |
| |
| bool Clock::GetClockWireBoolAttribute(RTLIL::Wire* wire, const std::string& attribute_name) { |
| if (wire->has_attribute(RTLIL::escape_id(attribute_name))) { |
| return wire->get_bool_attribute(RTLIL::escape_id(attribute_name)); |
| } |
| return false; |
| } |
| |
| const std::map<std::string, RTLIL::Wire*> Clocks::GetClocks( |
| RTLIL::Design* design) { |
| std::map<std::string, 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.insert(std::make_pair(Clock::WireName(wire), wire)); |
| } |
| } |
| } |
| return clock_wires; |
| } |
| |