diff options
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/driver.cc | 6 | ||||
-rw-r--r-- | kernel/log.cc | 77 | ||||
-rw-r--r-- | kernel/log.h | 19 | ||||
-rw-r--r-- | kernel/rtlil.cc | 8 | ||||
-rw-r--r-- | kernel/timinginfo.h | 179 |
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 |