aboutsummaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'kernel')
-rw-r--r--kernel/driver.cc6
-rw-r--r--kernel/log.cc77
-rw-r--r--kernel/log.h19
-rw-r--r--kernel/rtlil.cc8
-rw-r--r--kernel/timinginfo.h179
5 files changed, 285 insertions, 4 deletions
diff --git a/kernel/driver.cc b/kernel/driver.cc
index 9040408bc..398c89e03 100644
--- a/kernel/driver.cc
+++ b/kernel/driver.cc
@@ -558,6 +558,10 @@ int main(int argc, char **argv)
fprintf(f, "\n");
}
+ if (log_expect_no_warnings && log_warnings_count_noexpect)
+ log_error("Unexpected warnings found: %d unique messages, %d total, %d expected\n", GetSize(log_warnings),
+ log_warnings_count, log_warnings_count - log_warnings_count_noexpect);
+
if (print_stats)
{
std::string hash = log_hasher->final().substr(0, 10);
@@ -664,6 +668,8 @@ int main(int argc, char **argv)
}
#endif
+ log_check_expected();
+
yosys_atexit();
memhasher_off();
diff --git a/kernel/log.cc b/kernel/log.cc
index f5d6c488e..72181ebe8 100644
--- a/kernel/log.cc
+++ b/kernel/log.cc
@@ -42,8 +42,11 @@ std::vector<FILE*> log_files;
std::vector<std::ostream*> log_streams;
std::map<std::string, std::set<std::string>> log_hdump;
std::vector<std::regex> log_warn_regexes, log_nowarn_regexes, log_werror_regexes;
+std::vector<std::pair<std::regex,LogExpectedItem>> log_expect_log, log_expect_warning, log_expect_error;
std::set<std::string> log_warnings, log_experimentals, log_experimentals_ignored;
int log_warnings_count = 0;
+int log_warnings_count_noexpect = 0;
+bool log_expect_no_warnings = false;
bool log_hdump_all = false;
FILE *log_errfile = NULL;
SHA1 *log_hasher = NULL;
@@ -68,6 +71,8 @@ int string_buf_index = -1;
static struct timeval initial_tv = { 0, 0 };
static bool next_print_log = false;
static int log_newline_count = 0;
+static bool check_expected_logs = true;
+static bool display_error_log_msg = true;
static void log_id_cache_clear()
{
@@ -162,7 +167,7 @@ void logv(const char *format, va_list ap)
{
log_warn_regex_recusion_guard = true;
- if (log_warn_regexes.empty())
+ if (log_warn_regexes.empty() && log_expect_log.empty())
{
linebuffer.clear();
}
@@ -174,6 +179,11 @@ void logv(const char *format, va_list ap)
for (auto &re : log_warn_regexes)
if (std::regex_search(linebuffer, re))
log_warning("Found log message matching -W regex:\n%s", str.c_str());
+
+ for (auto &item : log_expect_log)
+ if (std::regex_search(linebuffer, item.first))
+ item.second.current_count++;
+
linebuffer.clear();
}
}
@@ -244,6 +254,13 @@ static void logv_warning_with_prefix(const char *prefix,
if (std::regex_search(message, re))
log_error("%s", message.c_str());
+ bool warning_match = false;
+ for (auto &item : log_expect_warning)
+ if (std::regex_search(message, item.first)) {
+ item.second.current_count++;
+ warning_match = true;
+ }
+
if (log_warnings.count(message))
{
log("%s%s", prefix, message.c_str());
@@ -263,6 +280,8 @@ static void logv_warning_with_prefix(const char *prefix,
log_warnings.insert(message);
}
+ if (!warning_match)
+ log_warnings_count_noexpect++;
log_warnings_count++;
log_make_debug = bak_log_make_debug;
}
@@ -320,7 +339,8 @@ static void logv_error_with_prefix(const char *prefix,
f = stderr;
log_last_error = vstringf(format, ap);
- log("%s%s", prefix, log_last_error.c_str());
+ if (display_error_log_msg)
+ log("%s%s", prefix, log_last_error.c_str());
log_flush();
log_make_debug = bak_log_make_debug;
@@ -328,6 +348,12 @@ static void logv_error_with_prefix(const char *prefix,
if (log_error_atexit)
log_error_atexit();
+ for (auto &item : log_expect_error)
+ if (std::regex_search(log_last_error, item.first))
+ item.second.current_count++;
+
+ if (check_expected_logs)
+ log_check_expected();
#ifdef EMSCRIPTEN
log_files = backup_log_files;
throw 0;
@@ -636,6 +662,53 @@ void log_wire(RTLIL::Wire *wire, std::string indent)
log("%s", buf.str().c_str());
}
+void log_check_expected()
+{
+ check_expected_logs = false;
+
+ for (auto &item : log_expect_warning) {
+ if (item.second.current_count == 0) {
+ log_warn_regexes.clear();
+ log_error("Expected warning pattern '%s' not found !\n", item.second.pattern.c_str());
+ }
+ if (item.second.current_count != item.second.expected_count) {
+ log_warn_regexes.clear();
+ log_error("Expected warning pattern '%s' found %d time(s), instead of %d time(s) !\n",
+ item.second.pattern.c_str(), item.second.current_count, item.second.expected_count);
+ }
+ }
+
+ for (auto &item : log_expect_log) {
+ if (item.second.current_count == 0) {
+ log_warn_regexes.clear();
+ log_error("Expected log pattern '%s' not found !\n", item.second.pattern.c_str());
+ }
+ if (item.second.current_count != item.second.expected_count) {
+ log_warn_regexes.clear();
+ log_error("Expected log pattern '%s' found %d time(s), instead of %d time(s) !\n",
+ item.second.pattern.c_str(), item.second.current_count, item.second.expected_count);
+ }
+ }
+
+ for (auto &item : log_expect_error)
+ if (item.second.current_count == item.second.expected_count) {
+ log_warn_regexes.clear();
+ log("Expected error pattern '%s' found !!!\n", item.second.pattern.c_str());
+ #ifdef EMSCRIPTEN
+ log_files = backup_log_files;
+ throw 0;
+ #elif defined(_MSC_VER)
+ _exit(0);
+ #else
+ _Exit(0);
+ #endif
+ } else {
+ display_error_log_msg = false;
+ log_warn_regexes.clear();
+ log_error("Expected error pattern '%s' not found !\n", item.second.pattern.c_str());
+ }
+}
+
// ---------------------------------------------------
// This is the magic behind the code coverage counters
// ---------------------------------------------------
diff --git a/kernel/log.h b/kernel/log.h
index 9db8efaa5..603938f4c 100644
--- a/kernel/log.h
+++ b/kernel/log.h
@@ -52,6 +52,8 @@ extern std::map<std::string, std::set<std::string>> log_hdump;
extern std::vector<std::regex> log_warn_regexes, log_nowarn_regexes, log_werror_regexes;
extern std::set<std::string> log_warnings, log_experimentals, log_experimentals_ignored;
extern int log_warnings_count;
+extern int log_warnings_count_noexpect;
+extern bool log_expect_no_warnings;
extern bool log_hdump_all;
extern FILE *log_errfile;
extern SHA1 *log_hasher;
@@ -135,6 +137,23 @@ void log_backtrace(const char *prefix, int levels);
void log_reset_stack();
void log_flush();
+struct LogExpectedItem
+{
+ LogExpectedItem(std::string pattern, int expected) :
+ expected_count(expected),
+ current_count(0),
+ pattern(pattern)
+ {
+ }
+
+ int expected_count;
+ int current_count;
+ std::string pattern;
+};
+
+extern std::vector<std::pair<std::regex,LogExpectedItem>> log_expect_log, log_expect_warning, log_expect_error;
+void log_check_expected();
+
const char *log_signal(const RTLIL::SigSpec &sig, bool autoint = true);
const char *log_const(const RTLIL::Const &value, bool autoint = true);
const char *log_id(RTLIL::IdString id);
diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc
index f286d139f..5d7e61901 100644
--- a/kernel/rtlil.cc
+++ b/kernel/rtlil.cc
@@ -1258,8 +1258,12 @@ namespace {
param_bool(ID(SRC_POL));
param_bool(ID(DST_PEN));
param_bool(ID(DST_POL));
- param(ID(T_LIMIT));
- param(ID(T_LIMIT2));
+ param(ID(T_LIMIT_MIN));
+ param(ID(T_LIMIT_TYP));
+ param(ID(T_LIMIT_MAX));
+ param(ID(T_LIMIT2_MIN));
+ param(ID(T_LIMIT2_TYP));
+ param(ID(T_LIMIT2_MAX));
port(ID(SRC_EN), 1);
port(ID(DST_EN), 1);
port(ID(SRC), param(ID(SRC_WIDTH)));
diff --git a/kernel/timinginfo.h b/kernel/timinginfo.h
new file mode 100644
index 000000000..4b77c02e8
--- /dev/null
+++ b/kernel/timinginfo.h
@@ -0,0 +1,179 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * (C) 2020 Eddie Hung <eddie@fpgeh.com>
+ *
+ * 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.
+ *
+ */
+
+#ifndef TIMINGINFO_H
+#define TIMINGINFO_H
+
+#include "kernel/yosys.h"
+
+YOSYS_NAMESPACE_BEGIN
+
+struct TimingInfo
+{
+ struct NameBit
+ {
+ RTLIL::IdString name;
+ int offset;
+ NameBit() : offset(0) {}
+ NameBit(const RTLIL::IdString name, int offset) : name(name), offset(offset) {}
+ explicit NameBit(const RTLIL::SigBit &b) : name(b.wire->name), offset(b.offset) {}
+ bool operator==(const NameBit& nb) const { return nb.name == name && nb.offset == offset; }
+ bool operator!=(const NameBit& nb) const { return !operator==(nb); }
+ unsigned int hash() const { return mkhash_add(name.hash(), offset); }
+ };
+ struct BitBit
+ {
+ NameBit first, second;
+ BitBit(const NameBit &first, const NameBit &second) : first(first), second(second) {}
+ BitBit(const SigBit &first, const SigBit &second) : first(first), second(second) {}
+ bool operator==(const BitBit& bb) const { return bb.first == first && bb.second == second; }
+ unsigned int hash() const { return mkhash_add(first.hash(), second.hash()); }
+ };
+
+ struct ModuleTiming
+ {
+ RTLIL::IdString type;
+ dict<BitBit, int> comb;
+ dict<NameBit, int> arrival, required;
+ };
+
+ dict<RTLIL::IdString, ModuleTiming> data;
+
+ TimingInfo()
+ {
+ }
+
+ TimingInfo(RTLIL::Design *design)
+ {
+ setup(design);
+ }
+
+ void setup(RTLIL::Design *design)
+ {
+ for (auto module : design->modules()) {
+ if (!module->get_blackbox_attribute())
+ continue;
+ setup_module(module);
+ }
+ }
+
+ const ModuleTiming& setup_module(RTLIL::Module *module)
+ {
+ auto r = data.insert(module->name);
+ log_assert(r.second);
+ auto &t = r.first->second;
+
+ for (auto cell : module->cells()) {
+ if (cell->type == ID($specify2)) {
+ auto src = cell->getPort(ID(SRC));
+ auto dst = cell->getPort(ID(DST));
+ for (const auto &c : src.chunks())
+ if (!c.wire->port_input)
+ log_error("Module '%s' contains specify cell '%s' where SRC '%s' is not a module input.\n", log_id(module), log_id(cell), log_signal(src));
+ for (const auto &c : dst.chunks())
+ if (!c.wire->port_output)
+ log_error("Module '%s' contains specify cell '%s' where DST '%s' is not a module output.\n", log_id(module), log_id(cell), log_signal(dst));
+ int rise_max = cell->getParam(ID(T_RISE_MAX)).as_int();
+ int fall_max = cell->getParam(ID(T_FALL_MAX)).as_int();
+ int max = std::max(rise_max,fall_max);
+ if (max < 0)
+ log_error("Module '%s' contains specify cell '%s' with T_{RISE,FALL}_MAX < 0.\n", log_id(module), log_id(cell));
+ if (cell->getParam(ID(FULL)).as_bool()) {
+ for (const auto &s : src)
+ for (const auto &d : dst) {
+ auto r = t.comb.insert(BitBit(s,d));
+ if (!r.second)
+ log_error("Module '%s' contains multiple specify cells for SRC '%s' and DST '%s'.\n", log_id(module), log_signal(s), log_signal(d));
+ r.first->second = max;
+ }
+ }
+ else {
+ log_assert(GetSize(src) == GetSize(dst));
+ for (auto i = 0; i < GetSize(src); i++) {
+ const auto &s = src[i];
+ const auto &d = dst[i];
+ auto r = t.comb.insert(BitBit(s,d));
+ if (!r.second)
+ log_error("Module '%s' contains multiple specify cells for SRC '%s' and DST '%s'.\n", log_id(module), log_signal(s), log_signal(d));
+ r.first->second = max;
+ }
+ }
+ }
+ else if (cell->type == ID($specify3)) {
+ auto src = cell->getPort(ID(SRC));
+ auto dst = cell->getPort(ID(DST));
+ for (const auto &c : src.chunks())
+ if (!c.wire->port_input)
+ log_error("Module '%s' contains specify cell '%s' where SRC '%s' is not a module input.\n", log_id(module), log_id(cell), log_signal(src));
+ for (const auto &c : dst.chunks())
+ if (!c.wire->port_output)
+ log_error("Module '%s' contains specify cell '%s' where DST '%s' is not a module output.\n", log_id(module), log_id(cell), log_signal(dst));
+ int rise_max = cell->getParam(ID(T_RISE_MAX)).as_int();
+ int fall_max = cell->getParam(ID(T_FALL_MAX)).as_int();
+ int max = std::max(rise_max,fall_max);
+ if (max < 0)
+ log_warning("Module '%s' contains specify cell '%s' with T_{RISE,FALL}_MAX < 0 which is currently unsupported. Ignoring.\n", log_id(module), log_id(cell));
+ if (max <= 0) {
+ log_debug("Module '%s' contains specify cell '%s' with T_{RISE,FALL}_MAX <= 0 which is currently unsupported. Ignoring.\n", log_id(module), log_id(cell));
+ continue;
+ }
+ for (const auto &d : dst) {
+ auto &v = t.arrival[NameBit(d)];
+ v = std::max(v, max);
+ }
+ }
+ else if (cell->type == ID($specrule)) {
+ auto type = cell->getParam(ID(TYPE)).decode_string();
+ if (type != "$setup" && type != "$setuphold")
+ continue;
+ auto src = cell->getPort(ID(SRC));
+ auto dst = cell->getPort(ID(DST));
+ for (const auto &c : src.chunks())
+ if (!c.wire->port_input)
+ log_error("Module '%s' contains specify cell '%s' where SRC '%s' is not a module input.\n", log_id(module), log_id(cell), log_signal(src));
+ for (const auto &c : dst.chunks())
+ if (!c.wire->port_input)
+ log_error("Module '%s' contains specify cell '%s' where DST '%s' is not a module input.\n", log_id(module), log_id(cell), log_signal(dst));
+ int max = cell->getParam(ID(T_LIMIT_MAX)).as_int();
+ if (max < 0)
+ log_warning("Module '%s' contains specify cell '%s' with T_LIMIT_MAX < 0 which is currently unsupported. Ignoring.\n", log_id(module), log_id(cell));
+ if (max <= 0) {
+ log_debug("Module '%s' contains specify cell '%s' with T_LIMIT_MAX <= 0 which is currently unsupported. Ignoring.\n", log_id(module), log_id(cell));
+ continue;
+ }
+ for (const auto &s : src) {
+ auto &v = t.required[NameBit(s)];
+ v = std::max(v, max);
+ }
+ }
+ }
+
+ return t;
+ }
+
+ decltype(data)::const_iterator find(RTLIL::IdString module_name) const { return data.find(module_name); }
+ decltype(data)::const_iterator end() const { return data.end(); }
+ int count(RTLIL::IdString module_name) const { return data.count(module_name); }
+ const ModuleTiming& at(RTLIL::IdString module_name) const { return data.at(module_name); }
+};
+
+YOSYS_NAMESPACE_END
+
+#endif