Merge pull request #51 from antmicro/sdc_attributes
Keep the SDC information in Yosys's design structures
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..8d4f8c9
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "third_party/googletest"]
+ path = third_party/googletest
+ url = https://github.com/google/googletest
diff --git a/Makefile_plugin.common b/Makefile_plugin.common
index 6215cbb..4289bd6 100644
--- a/Makefile_plugin.common
+++ b/Makefile_plugin.common
@@ -37,7 +37,7 @@
# |-- example2-plugin
# |-- ...
CXX = $(shell yosys-config --cxx)
-CXXFLAGS = $(shell yosys-config --cxxflags)
+CXXFLAGS = $(shell yosys-config --cxxflags) #-DSDC_DEBUG
LDFLAGS = $(shell yosys-config --ldflags)
LDLIBS = $(shell yosys-config --ldlibs)
PLUGINS_DIR = $(shell yosys-config --datdir)/plugins
diff --git a/Makefile_test.common b/Makefile_test.common
index fe8ffce..a2a3509 100644
--- a/Makefile_test.common
+++ b/Makefile_test.common
@@ -12,11 +12,18 @@
# test1_verify = $(call diff_test,test1,ext) && test $$(grep "PASS" test1/test1.txt | wc -l) -eq 2
# test2_verify = $(call diff_test,test2,ext)
#
+GTEST_DIR = ../../third_party/googletest/googletest
+CXX = $(shell yosys-config --cxx)
+CXXFLAGS = $(shell yosys-config --cxxflags) -I.. -I$(GTEST_DIR)/include
+LDLIBS = $(shell yosys-config --ldlibs) -L$(GTEST_DIR)/build/lib -lgtest -lgtest_main -lpthread
+LDFLAGS = $(shell yosys-config --ldflags)
+
define test_tpl =
$(1): $(1)/ok
- @$$($(1)_verify); \
- RETVAL=$$$$? ; \
- if [ $$$$RETVAL -eq 0 ]; then \
+ @echo "Verifying result of test $(1)"
+ @set +e; \
+ $$($(1)_verify); \
+ if [ $$$$? -eq 0 ]; then \
echo "Test $(1) PASSED"; \
touch $$<; \
true; \
@@ -27,19 +34,56 @@
$(1)/ok: $(1)/$(1).v
@echo "Running test $(1)"
- @cd $(1); \
+ @set +e; \
+ cd $(1); \
DESIGN_TOP=$(1) \
- yosys -c $(1).tcl -q -l $(1).log
+ yosys -c $(1).tcl -q -l $(1).log; \
+ RETVAL=$$$$?; \
+ if [ ! -z "$$($(1)_negative)" ] && [ $$($(1)_negative) -eq 1 ]; then \
+ if [ $$$$RETVAL -ne 0 ]; then \
+ echo "Negative test $(1) PASSED"; \
+ true; \
+ else \
+ echo "Negative test $(1) FAILED"; \
+ false; \
+ fi \
+ else \
+ if [ $$$$RETVAL -ne 0 ]; then \
+ echo "Unexpected runtime error"; \
+ false; \
+ fi \
+ fi
+
+endef
+
+define unit_test_tpl =
+$(1): $(1)/$(1).test
+ @$$<
+
+$(1)/$(1).test: $(1)/$(1).test.o $$(GTEST_DIR)/build/lib/libgtest.a
+ @$(CXX) $(LDFLAGS) -o $$@ $$< $(LDLIBS)
+
+$(1)/$(1).test.o: $(1)/$(1).test.cc
+ @$(CXX) $(CXXFLAGS) $(LDFLAGS) -c $$< -o $$@
endef
diff_test = diff $(1)/$(1).golden.$(2) $(1)/$(1).$(2)
-all: $(TESTS)
-.PHONY: all clean $(TESTS)
+all: $(TESTS) $(UNIT_TESTS)
+
+$(GTEST_DIR)/build/lib/libgtest.a $(GTEST_DIR)/build/lib/libgtest_main.a:
+ @mkdir -p $(GTEST_DIR)/build
+ @cd $(GTEST_DIR)/build; \
+ cmake ..; \
+ make
+
+.PHONY: all clean $(TESTS) $(UNIT_TESTS)
$(foreach test,$(TESTS),$(eval $(call test_tpl,$(test))))
+$(foreach test,$(UNIT_TESTS),$(eval $(call unit_test_tpl,$(test))))
clean:
@rm -rf $(foreach test,$(TESTS),$(test)/$(test).sdc $(test)/$(test).txt $(test)/$(test).eblif $(test)/$(test).json)
+ @rm -rf $(foreach test,$(UNIT_TESTS),$(test)/$(test).test.o $(test)/$(test).test.d $(test)/$(test).test)
@find . -name "ok" -or -name "*.log" | xargs rm -rf
diff --git a/sdc-plugin/buffers.cc b/sdc-plugin/buffers.cc
index 919de28..a48a26a 100644
--- a/sdc-plugin/buffers.cc
+++ b/sdc-plugin/buffers.cc
@@ -15,9 +15,9 @@
* 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>
-#include "buffers.h"
const std::vector<std::string> Pll::inputs = {"CLKIN1", "CLKIN2"};
const std::vector<std::string> Pll::outputs = {"CLKOUT0", "CLKOUT1", "CLKOUT2",
@@ -25,7 +25,9 @@
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) {
+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);
@@ -35,7 +37,9 @@
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();
+ 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 "
@@ -53,7 +57,8 @@
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_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
@@ -63,22 +68,30 @@
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;
+ // 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);
+ 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&& param_name, float default_value) {
+float Pll::FetchParam(RTLIL::Cell* cell, std::string&& param_name,
+ float default_value) {
RTLIL::IdString param(RTLIL::escape_id(param_name));
if (cell->hasParam(param)) {
auto param_obj = cell->parameters.at(param);
diff --git a/sdc-plugin/buffers.h b/sdc-plugin/buffers.h
index 5f51535..ba3faa4 100644
--- a/sdc-plugin/buffers.h
+++ b/sdc-plugin/buffers.h
@@ -26,10 +26,10 @@
USING_YOSYS_NAMESPACE
struct Buffer {
- Buffer(float delay, const std::string& name, const std::string& output)
- : delay(delay), name(name), output(output) {}
+ Buffer(float delay, const std::string& type, const std::string& output)
+ : delay(delay), type(type), output(output) {}
float delay;
- std::string name;
+ std::string type;
std::string output;
};
@@ -41,8 +41,14 @@
Bufg() : Buffer(0, "BUFG", "O"){};
};
-struct Pll {
- Pll(RTLIL::Cell* cell, float input_clock_period, float input_clock_rising_edge);
+struct ClockDivider {
+ std::string type;
+};
+
+struct Pll : public ClockDivider {
+ Pll() : ClockDivider({"PLLE2_ADV"}) {}
+ Pll(RTLIL::Cell* cell, float input_clock_period,
+ float input_clock_rising_edge);
// Helper function to fetch a cell parameter or return a default value
static float FetchParam(RTLIL::Cell* cell, std::string&& param_name,
diff --git a/sdc-plugin/clocks.cc b/sdc-plugin/clocks.cc
index c177f6a..95ebaad 100644
--- a/sdc-plugin/clocks.cc
+++ b/sdc-plugin/clocks.cc
@@ -19,220 +19,121 @@
#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(), [&, this](RTLIL::Wire* wire) {
- AddClock(name, wire, period, rising_edge, falling_edge);
+void Clock::Add(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::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) {
+ std::for_each(wires.begin(), wires.end(), [&](RTLIL::Wire* wire) {
+ Add(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 {
- rising_edge = fmod(rising_edge, period);
- falling_edge = fmod(falling_edge, period);
- clocks_.emplace_back(name, wire, period, rising_edge, falling_edge);
- log("Added clock %s with period %f, rising_edge:%f, falling_edge:%f\n", name.c_str(),
- period, rising_edge, falling_edge);
+void Clock::Add(RTLIL::Wire* wire, float period, float rising_edge,
+ float falling_edge) {
+ Add(Clock::WireName(wire), wire, period, rising_edge, falling_edge);
+}
+
+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;
}
-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) << " ";
+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);
}
- 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
+ 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);
}
- 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());
- }
+ 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());
}
-#ifdef SDC_DEBUG
- log("Finish natural clock propagation\n\n");
-#endif
+ return std::make_pair(rising_edge, falling_edge);
}
-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());
+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"));
}
-#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
+ return WireName(clock_wire);
}
-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 pll_clocks =
- pass->FindSinkClocksForCellType(clock, "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); });
-}
-
-Clock::Clock(RTLIL::Wire* wire, float period,
- float rising_edge, float falling_edge)
- : Clock(RTLIL::id2cstr(wire->name), wire, period, rising_edge, falling_edge) {}
-
-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) {
+std::string Clock::WireName(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{"\\$"}, "\\$");
+ return AddEscaping(RTLIL::unescape_id(wire->name));
}
+
+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;
+}
+
diff --git a/sdc-plugin/clocks.h b/sdc-plugin/clocks.h
index 67ca272..2bcdf75 100644
--- a/sdc-plugin/clocks.h
+++ b/sdc-plugin/clocks.h
@@ -18,6 +18,7 @@
#ifndef _CLOCKS_H_
#define _CLOCKS_H_
+#include <map>
#include <vector>
#include "buffers.h"
#include "kernel/rtlil.h"
@@ -31,53 +32,28 @@
class Clock {
public:
- Clock(const std::string& name, RTLIL::Wire* wire, float period,
- float rising_edge, float falling_edge);
- Clock(const std::string& name, std::vector<RTLIL::Wire*> wires,
- float period, float rising_edge, float falling_edge);
- Clock(RTLIL::Wire* wire, float period,
- float rising_edge, float falling_edge);
- std::vector<RTLIL::Wire*> GetClockWires() { return clock_wires_; }
- const std::string& Name() const { return name_; }
- float Period() { return period_; }
- float RisingEdge() { return rising_edge_; }
- float FallingEdge() { return falling_edge_; }
- void UpdateClock(RTLIL::Wire* wire, float period, float rising_edge,
- float falling_edge);
- static std::string ClockWireName(RTLIL::Wire* wire);
+ static void Add(const std::string& name, RTLIL::Wire* wire, float period,
+ float rising_edge, float falling_edge);
+ static void Add(const std::string& name, std::vector<RTLIL::Wire*> wires,
+ float period, float rising_edge, float falling_edge);
+ static void Add(RTLIL::Wire* wire, float period, float rising_edge,
+ float falling_edge);
+ static float Period(RTLIL::Wire* clock_wire);
+ static float RisingEdge(RTLIL::Wire* clock_wire);
+ static float FallingEdge(RTLIL::Wire* clock_wire);
+ static std::string Name(RTLIL::Wire* clock_wire);
+ static std::string WireName(RTLIL::Wire* wire);
+ static std::string AddEscaping(const std::string& name) {
+ return std::regex_replace(name, std::regex{"\\$"}, "\\$");
+ }
private:
- std::string name_;
- std::vector<RTLIL::Wire*> clock_wires_;
- float period_;
- float rising_edge_;
- float falling_edge_;
-
- void UpdateWires(RTLIL::Wire* wire);
- void UpdatePeriod(float period);
- void UpdateWaveform(float rising_edge, float falling_edge);
+ static std::pair<float, float> Waveform(RTLIL::Wire* clock_wire);
};
class Clocks {
public:
- void AddClock(const std::string& name, std::vector<RTLIL::Wire*> wires,
- float period, float rising_edge, float falling_edge);
- void AddClock(const std::string& name, RTLIL::Wire* wire, float period,
- float rising_edge, float falling_edge);
- void AddClock(Clock& clock);
- std::vector<std::string> GetClockNames();
- void Propagate(NaturalPropagation* pass);
- void Propagate(BufferPropagation* pass);
- void Propagate(ClockDividerPropagation* pass);
- void WriteSdc(std::ostream& file);
- const std::vector<Clock> GetClocks() {
- return clocks_;
- }
-
- private:
- std::vector<Clock> clocks_;
- void PropagateThroughBuffer(Propagation* pass, Clock& clock,
- Buffer buffer);
+ static const std::map<std::string, RTLIL::Wire*> GetClocks(RTLIL::Design* design);
};
#endif // _CLOCKS_H_
diff --git a/sdc-plugin/propagation.cc b/sdc-plugin/propagation.cc
index e741e1a..a2dffe2 100644
--- a/sdc-plugin/propagation.cc
+++ b/sdc-plugin/propagation.cc
@@ -20,53 +20,45 @@
USING_YOSYS_NAMESPACE
-std::vector<RTLIL::Wire*> NaturalPropagation::FindAliasWires(
- RTLIL::Wire* wire) {
- RTLIL::Module* top_module = design_->top_module();
- assert(top_module);
- std::vector<RTLIL::Wire*> alias_wires;
- pass_->extra_args(
- std::vector<std::string>{
- top_module->name.str() + "/w:" + wire->name.str(), "%a"},
- 0, design_);
- for (auto module : design_->selected_modules()) {
- for (auto wire : module->selected_wires()) {
- alias_wires.push_back(wire);
+void Propagation::PropagateThroughBuffers(Buffer buffer) {
+ for (auto& clock : Clocks::GetClocks(design_)) {
+ auto& clock_wire = clock.second;
+#ifdef SDC_DEBUG
+ log("Clock wire %s\n", Clock::WireName(clock_wire).c_str());
+#endif
+ auto buf_wires =
+ FindSinkWiresForCellType(clock_wire, buffer.type, buffer.output);
+ int path_delay(0);
+ for (auto wire : buf_wires) {
+#ifdef SDC_DEBUG
+ log("%s wire: %s\n", buffer.type.c_str(),
+ RTLIL::id2cstr(wire->name));
+#endif
+ path_delay += buffer.delay;
+ Clock::Add(wire, Clock::Period(clock_wire),
+ Clock::RisingEdge(clock_wire) + path_delay,
+ Clock::FallingEdge(clock_wire) + path_delay);
}
}
- return alias_wires;
}
-std::vector<Clock> ClockDividerPropagation::FindSinkClocksForCellType(
- Clock driving_clock, const std::string& cell_type) {
- std::vector<Clock> clocks;
- auto clock_wires = driving_clock.GetClockWires();
- for (auto clock_wire : clock_wires) {
- if (cell_type == "PLLE2_ADV") {
- RTLIL::Cell* cell = NULL;
- for (auto input : Pll::inputs) {
- cell = FindSinkCellOnPort(clock_wire, input);
- if (cell and RTLIL::unescape_id(cell->type) == cell_type) {
- break;
- }
- }
- if (!cell) {
- return clocks;
- }
- Pll pll(cell, driving_clock.Period(), driving_clock.RisingEdge());
- for (auto output : Pll::outputs) {
- RTLIL::Wire* wire = FindSinkWireOnPort(cell, output);
- if (wire) {
- float clkout_period(pll.clkout_period.at(output));
- float clkout_rising_edge(pll.clkout_rising_edge.at(output));
- float clkout_falling_edge(pll.clkout_falling_edge.at(output));
- Clock clock(wire, clkout_period, clkout_rising_edge, clkout_falling_edge);
- clocks.push_back(clock);
- }
- }
- }
+std::vector<RTLIL::Wire*> Propagation::FindSinkWiresForCellType(
+ RTLIL::Wire* driver_wire, const std::string& cell_type,
+ const std::string& cell_port) {
+ std::vector<RTLIL::Wire*> wires;
+ if (!driver_wire) {
+ return wires;
}
- return clocks;
+ auto cell = FindSinkCellOfType(driver_wire, cell_type);
+ RTLIL::Wire* wire = FindSinkWireOnPort(cell, cell_port);
+ if (wire) {
+ wires.push_back(wire);
+ auto further_wires =
+ FindSinkWiresForCellType(wire, cell_type, cell_port);
+ std::copy(further_wires.begin(), further_wires.end(),
+ std::back_inserter(wires));
+ }
+ return wires;
}
RTLIL::Cell* Propagation::FindSinkCellOfType(RTLIL::Wire* wire,
@@ -95,25 +87,6 @@
return sink_cell;
}
-std::vector<RTLIL::Wire*> Propagation::FindSinkWiresForCellType(
- RTLIL::Wire* driver_wire, const std::string& cell_type,
- const std::string& cell_port) {
- std::vector<RTLIL::Wire*> wires;
- if (!driver_wire) {
- return wires;
- }
- auto cell = FindSinkCellOfType(driver_wire, cell_type);
- RTLIL::Wire* wire = FindSinkWireOnPort(cell, cell_port);
- if (wire) {
- wires.push_back(wire);
- auto further_wires =
- FindSinkWiresForCellType(wire, cell_type, cell_port);
- std::copy(further_wires.begin(), further_wires.end(),
- std::back_inserter(wires));
- }
- return wires;
-}
-
RTLIL::Cell* Propagation::FindSinkCellOnPort(RTLIL::Wire* wire,
const std::string& port) {
RTLIL::Cell* sink_cell = NULL;
@@ -167,3 +140,104 @@
}
return sink_wire;
}
+void NaturalPropagation::Run() {
+#ifdef SDC_DEBUG
+ log("Start natural clock propagation\n");
+#endif
+ for (auto& clock : Clocks::GetClocks(design_)) {
+ auto& clock_wire = clock.second;
+#ifdef SDC_DEBUG
+ log("Processing clock %s\n", RTLIL::id2cstr(clock_wire->name));
+#endif
+ auto aliases = FindAliasWires(clock_wire);
+ Clock::Add(Clock::WireName(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
+}
+
+std::vector<RTLIL::Wire*> NaturalPropagation::FindAliasWires(
+ RTLIL::Wire* wire) {
+ RTLIL::Module* top_module = design_->top_module();
+ assert(top_module);
+ std::vector<RTLIL::Wire*> alias_wires;
+ pass_->extra_args(
+ std::vector<std::string>{
+ top_module->name.str() + "/w:" + wire->name.str(), "%a"},
+ 0, design_);
+ for (auto module : design_->selected_modules()) {
+ for (auto wire : module->selected_wires()) {
+ alias_wires.push_back(wire);
+ }
+ }
+ return alias_wires;
+}
+
+void BufferPropagation::Run() {
+#ifdef SDC_DEBUG
+ log("Start buffer clock propagation\n");
+ log("IBUF pass\n");
+#endif
+ PropagateThroughBuffers(IBuf());
+#ifdef SDC_DEBUG
+ log("BUFG pass\n");
+#endif
+ PropagateThroughBuffers(Bufg());
+#ifdef SDC_DEBUG
+ log("Finish buffer clock propagation\n\n");
+#endif
+}
+
+void ClockDividerPropagation::Run() {
+#ifdef SDC_DEBUG
+ log("Start clock divider clock propagation\n");
+#endif
+ PropagateThroughClockDividers(Pll());
+ PropagateThroughBuffers(Bufg());
+#ifdef SDC_DEBUG
+ log("Finish clock divider clock propagation\n\n");
+#endif
+}
+
+void ClockDividerPropagation::PropagateThroughClockDividers(
+ ClockDivider divider) {
+ for (auto& clock : Clocks::GetClocks(design_)) {
+ auto& clock_wire = clock.second;
+#ifdef SDC_DEBUG
+ log("Processing clock %s\n", Clock::WireName(clock_wire).c_str());
+#endif
+ PropagateClocksForCellType(clock_wire, divider.type);
+ }
+}
+
+void ClockDividerPropagation::PropagateClocksForCellType(
+ RTLIL::Wire* driver_wire, const std::string& cell_type) {
+ if (cell_type == "PLLE2_ADV") {
+ RTLIL::Cell* cell = NULL;
+ for (auto input : Pll::inputs) {
+ cell = FindSinkCellOnPort(driver_wire, input);
+ if (cell and RTLIL::unescape_id(cell->type) == cell_type) {
+ break;
+ }
+ }
+ if (!cell) {
+ return;
+ }
+ Pll pll(cell, Clock::Period(driver_wire),
+ Clock::RisingEdge(driver_wire));
+ for (auto output : Pll::outputs) {
+ RTLIL::Wire* wire = FindSinkWireOnPort(cell, output);
+ if (wire) {
+ float clkout_period(pll.clkout_period.at(output));
+ float clkout_rising_edge(pll.clkout_rising_edge.at(output));
+ float clkout_falling_edge(pll.clkout_falling_edge.at(output));
+ Clock::Add(wire, clkout_period, clkout_rising_edge,
+ clkout_falling_edge);
+ }
+ }
+ }
+}
+
diff --git a/sdc-plugin/propagation.h b/sdc-plugin/propagation.h
index 6774062..788b138 100644
--- a/sdc-plugin/propagation.h
+++ b/sdc-plugin/propagation.h
@@ -26,17 +26,18 @@
public:
Propagation(RTLIL::Design* design, Pass* pass)
: design_(design), pass_(pass) {}
- virtual ~Propagation(){}
+ virtual ~Propagation() {}
- virtual void Run(Clocks& clocks) = 0;
- std::vector<RTLIL::Wire*> FindSinkWiresForCellType(
- RTLIL::Wire* driver_wire, const std::string& cell_type,
- const std::string& cell_port);
+ virtual void Run() = 0;
protected:
RTLIL::Design* design_;
Pass* pass_;
+ void PropagateThroughBuffers(Buffer buffer);
+ std::vector<RTLIL::Wire*> FindSinkWiresForCellType(
+ RTLIL::Wire* driver_wire, const std::string& cell_type,
+ const std::string& cell_port);
RTLIL::Cell* FindSinkCellOfType(RTLIL::Wire* wire, const std::string& type);
RTLIL::Cell* FindSinkCellOnPort(RTLIL::Wire* wire, const std::string& port);
RTLIL::Wire* FindSinkWireOnPort(RTLIL::Cell* cell,
@@ -48,7 +49,7 @@
NaturalPropagation(RTLIL::Design* design, Pass* pass)
: Propagation(design, pass) {}
- void Run(Clocks& clocks) override { clocks.Propagate(this); }
+ void Run() override;
std::vector<RTLIL::Wire*> FindAliasWires(RTLIL::Wire* wire);
};
@@ -57,7 +58,7 @@
BufferPropagation(RTLIL::Design* design, Pass* pass)
: Propagation(design, pass) {}
- void Run(Clocks& clocks) override { clocks.Propagate(this); }
+ void Run() override;
};
class ClockDividerPropagation : public Propagation {
@@ -65,8 +66,9 @@
ClockDividerPropagation(RTLIL::Design* design, Pass* pass)
: Propagation(design, pass) {}
- void Run(Clocks& clocks) override { clocks.Propagate(this); }
- std::vector<Clock> FindSinkClocksForCellType(Clock driver_wire,
- const std::string& cell_type);
+ void Run() override;
+ void PropagateClocksForCellType(RTLIL::Wire* driver_wire,
+ const std::string& cell_type);
+ void PropagateThroughClockDividers(ClockDivider divider);
};
#endif // PROPAGATION_H_
diff --git a/sdc-plugin/sdc.cc b/sdc-plugin/sdc.cc
index c138b04..880eeb2 100644
--- a/sdc-plugin/sdc.cc
+++ b/sdc-plugin/sdc.cc
@@ -61,8 +61,8 @@
};
struct WriteSdcCmd : public Backend {
- WriteSdcCmd(Clocks& clocks, SdcWriter& sdc_writer)
- : Backend("sdc", "Write SDC file"), clocks_(clocks), sdc_writer_(sdc_writer) {}
+ WriteSdcCmd(SdcWriter& sdc_writer)
+ : Backend("sdc", "Write SDC file"), sdc_writer_(sdc_writer) {}
void help() override {
log("\n");
@@ -73,22 +73,21 @@
}
void execute(std::ostream*& f, std::string filename,
- std::vector<std::string> args, RTLIL::Design*) override {
+ std::vector<std::string> args, RTLIL::Design* design) override {
if (args.size() < 2) {
log_cmd_error("Missing output file.\n");
}
log("\nWriting out clock constraints file(SDC)\n");
extra_args(f, filename, args, 1);
- sdc_writer_.WriteSdc(clocks_, *f);
+ sdc_writer_.WriteSdc(design, *f);
}
- Clocks& clocks_;
SdcWriter& sdc_writer_;
};
struct CreateClockCmd : public Pass {
- CreateClockCmd(Clocks& clocks)
- : Pass("create_clock", "Create clock object"), clocks_(clocks) {}
+ CreateClockCmd()
+ : Pass("create_clock", "Create clock object") {}
void help() override {
log("\n");
@@ -170,7 +169,7 @@
rising_edge = 0;
falling_edge = period / 2;
}
- clocks_.AddClock(name, selected_wires, period, rising_edge,
+ Clock::Add(name, selected_wires, period, rising_edge,
falling_edge);
}
@@ -179,13 +178,11 @@
std::transform(selection_begin, args.end(), selection_begin,
[](std::string& w) { return "w:" + w; });
}
-
- Clocks& clocks_;
};
struct GetClocksCmd : public Pass {
- GetClocksCmd(Clocks& clocks)
- : Pass("get_clocks", "Create clock object"), clocks_(clocks) {}
+ GetClocksCmd()
+ : Pass("get_clocks", "Create clock object") {}
void help() override {
log("\n");
@@ -195,28 +192,30 @@
log("\n");
}
- void execute(__attribute__((unused)) std::vector<std::string> args,
- __attribute__((unused)) RTLIL::Design* design) override {
- std::vector<std::string> clock_names(clocks_.GetClockNames());
- if (clock_names.size() == 0) {
+ void execute(std::vector<std::string> args,
+ RTLIL::Design* design) override {
+ if (args.size() > 1) {
+ log_warning("Command doesn't support arguments, so they will be ignored.\n");
+ }
+ std::map<std::string, RTLIL::Wire*> clocks(Clocks::GetClocks(design));
+ if (clocks.size() == 0) {
log_warning("No clocks found in design\n");
}
Tcl_Interp* interp = yosys_get_tcl_interp();
Tcl_Obj* tcl_list = Tcl_NewListObj(0, NULL);
- for (auto name : clock_names) {
- Tcl_Obj* name_obj = Tcl_NewStringObj(name.c_str(), name.size());
+ for (auto& clock : clocks) {
+ auto& wire = clock.second;
+ const char* name = RTLIL::id2cstr(wire->name);
+ Tcl_Obj* name_obj = Tcl_NewStringObj(name, -1);
Tcl_ListObjAppendElement(interp, tcl_list, name_obj);
}
Tcl_SetObjResult(interp, tcl_list);
}
-
- Clocks& clocks_;
};
struct PropagateClocksCmd : public Pass {
- PropagateClocksCmd(Clocks& clocks)
- : Pass("propagate_clocks", "Propagate clock information"),
- clocks_(clocks) {}
+ PropagateClocksCmd()
+ : Pass("propagate_clocks", "Propagate clock information") {}
void help() override {
log("\n");
@@ -226,35 +225,33 @@
log("\n");
}
- void execute(__attribute__((unused)) std::vector<std::string> args,
+ void execute(std::vector<std::string> args,
RTLIL::Design* design) override {
+ if (args.size() > 1) {
+ log_warning("Command accepts no arguments.\nAll will be ignored.\n");
+ }
if (!design->top_module()) {
log_cmd_error("No top module selected\n");
}
std::array<std::unique_ptr<Propagation>, 2> passes{
- std::unique_ptr<BufferPropagation>(
+ std::unique_ptr<Propagation>(
new BufferPropagation(design, this)),
- std::unique_ptr<ClockDividerPropagation>(
+ std::unique_ptr<Propagation>(
new ClockDividerPropagation(design, this))};
log("Perform clock propagation\n");
for (auto& pass : passes) {
- pass->Run(clocks_);
+ pass->Run();
}
}
-
- Clocks& clocks_;
};
class SdcPlugin {
public:
SdcPlugin()
- : write_sdc_cmd_(clocks_, sdc_writer_),
- create_clock_cmd_(clocks_),
- get_clocks_cmd_(clocks_),
- propagate_clocks_cmd_(clocks_),
+ : write_sdc_cmd_(sdc_writer_),
set_false_path_cmd_(sdc_writer_),
set_max_delay_cmd_(sdc_writer_),
set_clock_groups_cmd_(sdc_writer_) {
@@ -271,7 +268,6 @@
SetClockGroups set_clock_groups_cmd_;
private:
- Clocks clocks_;
SdcWriter sdc_writer_;
} SdcPlugin;
diff --git a/sdc-plugin/sdc_writer.cc b/sdc-plugin/sdc_writer.cc
index 4c7ae57..80c8e5e 100644
--- a/sdc-plugin/sdc_writer.cc
+++ b/sdc-plugin/sdc_writer.cc
@@ -37,30 +37,24 @@
clock_groups_.Add(clock_group, relation);
}
-void SdcWriter::WriteSdc(Clocks& clocks, std::ostream& file) {
- WriteClocks(clocks, file);
+void SdcWriter::WriteSdc(RTLIL::Design* design, std::ostream& file) {
+ WriteClocks(design, file);
WriteFalsePaths(file);
WriteMaxDelay(file);
WriteClockGroups(file);
}
-void SdcWriter::WriteClocks(Clocks& clocks, std::ostream& file) {
- for (auto clock : clocks.GetClocks()) {
- auto clock_wires = clock.GetClockWires();
+void SdcWriter::WriteClocks(RTLIL::Design* design, std::ostream& file) {
+ for (auto& clock : Clocks::GetClocks(design)) {
// 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; })) {
+ auto& clock_wire = clock.second;
+ if (clock_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 << "create_clock -period " << Clock::Period(clock_wire);
+ file << " -waveform {" << Clock::RisingEdge(clock_wire) << " "
+ << Clock::FallingEdge(clock_wire) << "}";
+ file << " " << Clock::WireName(clock_wire);
file << std::endl;
}
}
diff --git a/sdc-plugin/sdc_writer.h b/sdc-plugin/sdc_writer.h
index 9bcbcf1..5cdf8a2 100644
--- a/sdc-plugin/sdc_writer.h
+++ b/sdc-plugin/sdc_writer.h
@@ -61,10 +61,10 @@
void AddFalsePath(FalsePath false_path);
void SetMaxDelay(TimingPath timing_path);
void AddClockGroup(ClockGroups::ClockGroup clock_group, ClockGroups::ClockGroupRelation relation);
- void WriteSdc(Clocks& clocks, std::ostream& file);
+ void WriteSdc(RTLIL::Design* design, std::ostream& file);
private:
- void WriteClocks(Clocks& clocks, std::ostream& file);
+ void WriteClocks(RTLIL::Design* design, std::ostream& file);
void WriteFalsePaths(std::ostream& file);
void WriteMaxDelay(std::ostream& file);
void WriteClockGroups(std::ostream& file);
diff --git a/sdc-plugin/tests/Makefile b/sdc-plugin/tests/Makefile
index 3b45b57..3d602b7 100644
--- a/sdc-plugin/tests/Makefile
+++ b/sdc-plugin/tests/Makefile
@@ -2,6 +2,10 @@
# set_false_path - test the set_false_path command
# set_max_delay - test the set_max_delay command
# set_clock_groups - test the set_clock_groups command
+# restore_from_json - test clock propagation when design restored from json instead verilog
+# period_check - test if the clock propagation fails if a clock wire is missing the PERIOD attribute
+# waveform_check - test if the WAVEFORM attribute value is correct on wire
+# period_format_check - test if PERIOD attribute value is correct on wire
TESTS = counter \
counter2 \
@@ -11,7 +15,13 @@
pll_approx_equal \
set_false_path \
set_max_delay \
- set_clock_groups
+ set_clock_groups \
+ restore_from_json \
+ period_check \
+ waveform_check \
+ period_format_check
+
+UNIT_TESTS = escaping
include $(shell pwd)/../../Makefile_test.common
@@ -24,3 +34,10 @@
set_false_path_verify = $(call diff_test,set_false_path,sdc)
set_max_delay_verify = $(call diff_test,set_max_delay,sdc)
set_clock_groups_verify = $(call diff_test,set_clock_groups,sdc)
+restore_from_json_verify = diff restore_from_json/restore_from_json_1.sdc restore_from_json/restore_from_json_2.sdc
+period_check_verify = true
+period_check_negative = 1
+waveform_check_verify = true
+waveform_check_negative = 1
+period_format_check_verify = true
+period_format_check_negative = 1
diff --git a/sdc-plugin/tests/counter/counter.golden.sdc b/sdc-plugin/tests/counter/counter.golden.sdc
index c1a2ea1..5be53e3 100644
--- a/sdc-plugin/tests/counter/counter.golden.sdc
+++ b/sdc-plugin/tests/counter/counter.golden.sdc
@@ -1,6 +1,6 @@
-create_clock -period 10 -waveform {0 5} clk_int_1
-create_clock -period 10 -waveform {0 5} ibuf_proxy_out
create_clock -period 10 -waveform {0 5} \$auto\$clkbufmap.cc:247:execute\$1918
create_clock -period 10 -waveform {0 5} \$auto\$clkbufmap.cc:247:execute\$1920
+create_clock -period 10 -waveform {0 5} clk_int_1
+create_clock -period 10 -waveform {0 5} ibuf_proxy_out
create_clock -period 10 -waveform {0 5} middle_inst_1.clk_int
create_clock -period 10 -waveform {0 5} middle_inst_4.clk
diff --git a/sdc-plugin/tests/counter/counter.golden.txt b/sdc-plugin/tests/counter/counter.golden.txt
index 065b110..78277ed 100644
--- a/sdc-plugin/tests/counter/counter.golden.txt
+++ b/sdc-plugin/tests/counter/counter.golden.txt
@@ -1 +1 @@
-clk_int_1 clk ibuf_proxy_out {$auto$clkbufmap.cc:247:execute$1918} {$auto$clkbufmap.cc:247:execute$1920} middle_inst_1.clk_int middle_inst_4.clk
+{$auto$clkbufmap.cc:247:execute$1918} {$auto$clkbufmap.cc:247:execute$1920} clk clk2 clk_int_1 ibuf_proxy_out middle_inst_1.clk_int middle_inst_4.clk
diff --git a/sdc-plugin/tests/counter/counter.tcl b/sdc-plugin/tests/counter/counter.tcl
index 4fcf9be..b045b3c 100644
--- a/sdc-plugin/tests/counter/counter.tcl
+++ b/sdc-plugin/tests/counter/counter.tcl
@@ -24,3 +24,4 @@
# Write out the SDC file after the clock propagation step
write_sdc $::env(DESIGN_TOP).sdc
+write_json $::env(DESIGN_TOP).json
diff --git a/sdc-plugin/tests/counter2/counter2.golden.sdc b/sdc-plugin/tests/counter2/counter2.golden.sdc
index 4bf0bf2..33152b3 100644
--- a/sdc-plugin/tests/counter2/counter2.golden.sdc
+++ b/sdc-plugin/tests/counter2/counter2.golden.sdc
@@ -1,6 +1,6 @@
-create_clock -period 10 -waveform {0 5} clk_int_1
-create_clock -period 10 -waveform {0 5} ibuf_proxy_out
create_clock -period 10 -waveform {0 5} \$auto\$clkbufmap.cc:247:execute\$1918
create_clock -period 10 -waveform {1 6} \$auto\$clkbufmap.cc:247:execute\$1920
+create_clock -period 10 -waveform {0 5} clk_int_1
+create_clock -period 10 -waveform {0 5} ibuf_proxy_out
create_clock -period 10 -waveform {0 5} middle_inst_1.clk_int
create_clock -period 10 -waveform {1 6} middle_inst_4.clk
diff --git a/sdc-plugin/tests/counter2/counter2.golden.txt b/sdc-plugin/tests/counter2/counter2.golden.txt
index ca1b187..78277ed 100644
--- a/sdc-plugin/tests/counter2/counter2.golden.txt
+++ b/sdc-plugin/tests/counter2/counter2.golden.txt
@@ -1 +1 @@
-clk_int_1 clk clk2 ibuf_proxy_out {$auto$clkbufmap.cc:247:execute$1918} {$auto$clkbufmap.cc:247:execute$1920} middle_inst_1.clk_int middle_inst_4.clk
+{$auto$clkbufmap.cc:247:execute$1918} {$auto$clkbufmap.cc:247:execute$1920} clk clk2 clk_int_1 ibuf_proxy_out middle_inst_1.clk_int middle_inst_4.clk
diff --git a/sdc-plugin/tests/escaping/escaping.test.cc b/sdc-plugin/tests/escaping/escaping.test.cc
new file mode 100644
index 0000000..e17eef6
--- /dev/null
+++ b/sdc-plugin/tests/escaping/escaping.test.cc
@@ -0,0 +1,11 @@
+#include <clocks.h>
+
+#include <gtest/gtest.h>
+
+TEST(ClockTest, EscapeDollarSign) {
+ // convert wire_name to wire_name, i.e. unchanged
+ EXPECT_EQ(Clock::AddEscaping("wire_name"), "wire_name");
+ // convert $wire_name to \$wire_name
+ EXPECT_EQ(Clock::AddEscaping("$wire_name"), "\\$wire_name");
+}
+
diff --git a/sdc-plugin/tests/period_check/period_check.tcl b/sdc-plugin/tests/period_check/period_check.tcl
new file mode 100644
index 0000000..bc613de
--- /dev/null
+++ b/sdc-plugin/tests/period_check/period_check.tcl
@@ -0,0 +1,17 @@
+yosys -import
+plugin -i sdc
+# Import the commands from the plugins to the tcl interpreter
+yosys -import
+
+read_verilog $::env(DESIGN_TOP).v
+read_verilog -specify -lib -D_EXPLICIT_CARRY +/xilinx/cells_sim.v
+read_verilog -lib +/xilinx/cells_xtra.v
+hierarchy -check -auto-top
+# Start flow after library reading
+synth_xilinx -vpr -flatten -abc9 -nosrl -nodsp -iopad -run prepare:check
+
+# Propagate the clocks
+propagate_clocks
+
+# Write out the SDC file after the clock propagation step
+write_sdc $::env(DESIGN_TOP).sdc
diff --git a/sdc-plugin/tests/period_check/period_check.v b/sdc-plugin/tests/period_check/period_check.v
new file mode 100644
index 0000000..77c1f9c
--- /dev/null
+++ b/sdc-plugin/tests/period_check/period_check.v
@@ -0,0 +1,36 @@
+module top((* CLOCK_SIGNAL = "yes", WAVEFORM = "0 5" *) input clk,
+ input clk2,
+ input [1:0] in,
+ output [5:0] out );
+
+reg [1:0] cnt = 0;
+wire clk_int_1, clk_int_2;
+IBUF ibuf_proxy(.I(clk), .O(ibuf_proxy_out));
+IBUF ibuf_inst(.I(ibuf_proxy_out), .O(ibuf_out));
+assign clk_int_1 = ibuf_out;
+assign clk_int_2 = clk_int_1;
+
+always @(posedge clk_int_2) begin
+ cnt <= cnt + 1;
+end
+
+middle middle_inst_1(.clk(ibuf_out), .out(out[2]));
+middle middle_inst_2(.clk(clk_int_1), .out(out[3]));
+middle middle_inst_3(.clk(clk_int_2), .out(out[4]));
+middle middle_inst_4(.clk(clk2), .out(out[5]));
+
+assign out[1:0] = {cnt[0], in[0]};
+endmodule
+
+module middle(input clk,
+ output out);
+
+reg [1:0] cnt = 0;
+wire clk_int;
+assign clk_int = clk;
+always @(posedge clk_int) begin
+ cnt <= cnt + 1;
+end
+
+assign out = cnt[0];
+endmodule
diff --git a/sdc-plugin/tests/period_format_check/period_format_check.tcl b/sdc-plugin/tests/period_format_check/period_format_check.tcl
new file mode 100644
index 0000000..bc613de
--- /dev/null
+++ b/sdc-plugin/tests/period_format_check/period_format_check.tcl
@@ -0,0 +1,17 @@
+yosys -import
+plugin -i sdc
+# Import the commands from the plugins to the tcl interpreter
+yosys -import
+
+read_verilog $::env(DESIGN_TOP).v
+read_verilog -specify -lib -D_EXPLICIT_CARRY +/xilinx/cells_sim.v
+read_verilog -lib +/xilinx/cells_xtra.v
+hierarchy -check -auto-top
+# Start flow after library reading
+synth_xilinx -vpr -flatten -abc9 -nosrl -nodsp -iopad -run prepare:check
+
+# Propagate the clocks
+propagate_clocks
+
+# Write out the SDC file after the clock propagation step
+write_sdc $::env(DESIGN_TOP).sdc
diff --git a/sdc-plugin/tests/period_format_check/period_format_check.v b/sdc-plugin/tests/period_format_check/period_format_check.v
new file mode 100644
index 0000000..e6ab294
--- /dev/null
+++ b/sdc-plugin/tests/period_format_check/period_format_check.v
@@ -0,0 +1,36 @@
+module top((* CLOCK_SIGNAL = "yes", PERIOD = "bad value", WAVEFORM = "0 5" *) input clk,
+ input clk2,
+ input [1:0] in,
+ output [5:0] out );
+
+reg [1:0] cnt = 0;
+wire clk_int_1, clk_int_2;
+IBUF ibuf_proxy(.I(clk), .O(ibuf_proxy_out));
+IBUF ibuf_inst(.I(ibuf_proxy_out), .O(ibuf_out));
+assign clk_int_1 = ibuf_out;
+assign clk_int_2 = clk_int_1;
+
+always @(posedge clk_int_2) begin
+ cnt <= cnt + 1;
+end
+
+middle middle_inst_1(.clk(ibuf_out), .out(out[2]));
+middle middle_inst_2(.clk(clk_int_1), .out(out[3]));
+middle middle_inst_3(.clk(clk_int_2), .out(out[4]));
+middle middle_inst_4(.clk(clk2), .out(out[5]));
+
+assign out[1:0] = {cnt[0], in[0]};
+endmodule
+
+module middle(input clk,
+ output out);
+
+reg [1:0] cnt = 0;
+wire clk_int;
+assign clk_int = clk;
+always @(posedge clk_int) begin
+ cnt <= cnt + 1;
+end
+
+assign out = cnt[0];
+endmodule
diff --git a/sdc-plugin/tests/pll/pll.golden.sdc b/sdc-plugin/tests/pll/pll.golden.sdc
index 7fe2b2a..da025c5 100644
--- a/sdc-plugin/tests/pll/pll.golden.sdc
+++ b/sdc-plugin/tests/pll/pll.golden.sdc
@@ -1,8 +1,8 @@
create_clock -period 10 -waveform {0 5} \$auto\$clkbufmap.cc:247:execute\$1829
-create_clock -period 10 -waveform {0 5} \$techmap1716\FDCE_0.C
create_clock -period 10 -waveform {2.5 7.5} \$auto\$clkbufmap.cc:247:execute\$1831
-create_clock -period 10 -waveform {2.5 7.5} main_clkout0
create_clock -period 2.5 -waveform {0 1.25} \$auto\$clkbufmap.cc:247:execute\$1833
-create_clock -period 2.5 -waveform {0 1.25} main_clkout1
create_clock -period 5 -waveform {1.25 3.75} \$auto\$clkbufmap.cc:247:execute\$1835
+create_clock -period 10 -waveform {0 5} \$techmap1716\FDCE_0.C
+create_clock -period 10 -waveform {2.5 7.5} main_clkout0
+create_clock -period 2.5 -waveform {0 1.25} main_clkout1
create_clock -period 5 -waveform {1.25 3.75} main_clkout2
diff --git a/sdc-plugin/tests/pll_approx_equal/pll_approx_equal.golden.sdc b/sdc-plugin/tests/pll_approx_equal/pll_approx_equal.golden.sdc
index add4f6a..b97da3e 100644
--- a/sdc-plugin/tests/pll_approx_equal/pll_approx_equal.golden.sdc
+++ b/sdc-plugin/tests/pll_approx_equal/pll_approx_equal.golden.sdc
@@ -1,8 +1,8 @@
create_clock -period 10 -waveform {0 5} \$auto\$clkbufmap.cc:247:execute\$1829
-create_clock -period 10 -waveform {0 5} \$techmap1716\FDCE_0.C
create_clock -period 9.99999 -waveform {0 5} \$auto\$clkbufmap.cc:247:execute\$1831
-create_clock -period 9.99999 -waveform {0 5} main_clkout_x1
create_clock -period 5 -waveform {-2.5 0} \$auto\$clkbufmap.cc:247:execute\$1833
-create_clock -period 5 -waveform {-2.5 0} main_clkout_x2
create_clock -period 2.5 -waveform {-1.875 -0.624999} \$auto\$clkbufmap.cc:247:execute\$1835
+create_clock -period 10 -waveform {0 5} \$techmap1716\FDCE_0.C
+create_clock -period 9.99999 -waveform {0 5} main_clkout_x1
+create_clock -period 5 -waveform {-2.5 0} main_clkout_x2
create_clock -period 2.5 -waveform {-1.875 -0.624999} main_clkout_x4
diff --git a/sdc-plugin/tests/pll_div/pll_div.golden.sdc b/sdc-plugin/tests/pll_div/pll_div.golden.sdc
index 14ea0bc..06031cf 100644
--- a/sdc-plugin/tests/pll_div/pll_div.golden.sdc
+++ b/sdc-plugin/tests/pll_div/pll_div.golden.sdc
@@ -1,8 +1,8 @@
create_clock -period 10 -waveform {0 5} \$auto\$clkbufmap.cc:247:execute\$1829
-create_clock -period 10 -waveform {0 5} \$techmap1716\FDCE_0.C
create_clock -period 20 -waveform {5 15} \$auto\$clkbufmap.cc:247:execute\$1831
-create_clock -period 20 -waveform {5 15} main_clkout0
create_clock -period 5 -waveform {0 2.5} \$auto\$clkbufmap.cc:247:execute\$1833
-create_clock -period 5 -waveform {0 2.5} main_clkout1
create_clock -period 10 -waveform {2.5 7.5} \$auto\$clkbufmap.cc:247:execute\$1835
+create_clock -period 10 -waveform {0 5} \$techmap1716\FDCE_0.C
+create_clock -period 20 -waveform {5 15} main_clkout0
+create_clock -period 5 -waveform {0 2.5} main_clkout1
create_clock -period 10 -waveform {2.5 7.5} main_clkout2
diff --git a/sdc-plugin/tests/pll_fbout_phase/pll_fbout_phase.golden.sdc b/sdc-plugin/tests/pll_fbout_phase/pll_fbout_phase.golden.sdc
index e3f281c..df8301d 100644
--- a/sdc-plugin/tests/pll_fbout_phase/pll_fbout_phase.golden.sdc
+++ b/sdc-plugin/tests/pll_fbout_phase/pll_fbout_phase.golden.sdc
@@ -1,8 +1,8 @@
create_clock -period 10 -waveform {0 5} \$auto\$clkbufmap.cc:247:execute\$1829
-create_clock -period 10 -waveform {0 5} \$techmap1716\FDCE_0.C
create_clock -period 10 -waveform {0 5} \$auto\$clkbufmap.cc:247:execute\$1831
-create_clock -period 10 -waveform {0 5} main_clkout_x1
create_clock -period 5 -waveform {-2.5 0} \$auto\$clkbufmap.cc:247:execute\$1833
-create_clock -period 5 -waveform {-2.5 0} main_clkout_x2
create_clock -period 2.5 -waveform {-1.875 -0.625} \$auto\$clkbufmap.cc:247:execute\$1835
+create_clock -period 10 -waveform {0 5} \$techmap1716\FDCE_0.C
+create_clock -period 10 -waveform {0 5} main_clkout_x1
+create_clock -period 5 -waveform {-2.5 0} main_clkout_x2
create_clock -period 2.5 -waveform {-1.875 -0.625} main_clkout_x4
diff --git a/sdc-plugin/tests/restore_from_json/restore_from_json.tcl b/sdc-plugin/tests/restore_from_json/restore_from_json.tcl
new file mode 100644
index 0000000..288419b
--- /dev/null
+++ b/sdc-plugin/tests/restore_from_json/restore_from_json.tcl
@@ -0,0 +1,16 @@
+yosys -import
+
+plugin -i sdc
+
+yosys -import
+
+read_verilog $::env(DESIGN_TOP).v
+synth_xilinx
+create_clock -period 10 clk
+propagate_clocks
+write_sdc $::env(DESIGN_TOP)_1.sdc
+write_json $::env(DESIGN_TOP).json
+
+design -push
+read_json $::env(DESIGN_TOP).json
+write_sdc $::env(DESIGN_TOP)_2.sdc
diff --git a/sdc-plugin/tests/restore_from_json/restore_from_json.v b/sdc-plugin/tests/restore_from_json/restore_from_json.v
new file mode 100644
index 0000000..0c35ede
--- /dev/null
+++ b/sdc-plugin/tests/restore_from_json/restore_from_json.v
@@ -0,0 +1,11 @@
+module top(input clk, input i, output o);
+
+reg [0:0] outff = 0;
+
+assign o = outff;
+
+always @(posedge clk) begin
+ outff <= i;
+end
+
+endmodule
diff --git a/sdc-plugin/tests/waveform_check/waveform_check.tcl b/sdc-plugin/tests/waveform_check/waveform_check.tcl
new file mode 100644
index 0000000..bc613de
--- /dev/null
+++ b/sdc-plugin/tests/waveform_check/waveform_check.tcl
@@ -0,0 +1,17 @@
+yosys -import
+plugin -i sdc
+# Import the commands from the plugins to the tcl interpreter
+yosys -import
+
+read_verilog $::env(DESIGN_TOP).v
+read_verilog -specify -lib -D_EXPLICIT_CARRY +/xilinx/cells_sim.v
+read_verilog -lib +/xilinx/cells_xtra.v
+hierarchy -check -auto-top
+# Start flow after library reading
+synth_xilinx -vpr -flatten -abc9 -nosrl -nodsp -iopad -run prepare:check
+
+# Propagate the clocks
+propagate_clocks
+
+# Write out the SDC file after the clock propagation step
+write_sdc $::env(DESIGN_TOP).sdc
diff --git a/sdc-plugin/tests/waveform_check/waveform_check.v b/sdc-plugin/tests/waveform_check/waveform_check.v
new file mode 100644
index 0000000..a7ff226
--- /dev/null
+++ b/sdc-plugin/tests/waveform_check/waveform_check.v
@@ -0,0 +1,36 @@
+module top((* CLOCK_SIGNAL = "yes", PERIOD = "10", WAVEFORM = "bad value" *) input clk,
+ input clk2,
+ input [1:0] in,
+ output [5:0] out );
+
+reg [1:0] cnt = 0;
+wire clk_int_1, clk_int_2;
+IBUF ibuf_proxy(.I(clk), .O(ibuf_proxy_out));
+IBUF ibuf_inst(.I(ibuf_proxy_out), .O(ibuf_out));
+assign clk_int_1 = ibuf_out;
+assign clk_int_2 = clk_int_1;
+
+always @(posedge clk_int_2) begin
+ cnt <= cnt + 1;
+end
+
+middle middle_inst_1(.clk(ibuf_out), .out(out[2]));
+middle middle_inst_2(.clk(clk_int_1), .out(out[3]));
+middle middle_inst_3(.clk(clk_int_2), .out(out[4]));
+middle middle_inst_4(.clk(clk2), .out(out[5]));
+
+assign out[1:0] = {cnt[0], in[0]};
+endmodule
+
+module middle(input clk,
+ output out);
+
+reg [1:0] cnt = 0;
+wire clk_int;
+assign clk_int = clk;
+always @(posedge clk_int) begin
+ cnt <= cnt + 1;
+end
+
+assign out = cnt[0];
+endmodule
diff --git a/third_party/googletest b/third_party/googletest
new file mode 160000
index 0000000..41b5f14
--- /dev/null
+++ b/third_party/googletest
@@ -0,0 +1 @@
+Subproject commit 41b5f149ab306e96b5b2faf523505d75acffd98a