verilator/src/V3Error.h

696 lines
32 KiB
C
Raw Normal View History

// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Error handling
//
2019-11-08 04:33:59 +01:00
// Code available from: https://verilator.org
//
//*************************************************************************
//
2023-01-01 16:18:39 +01:00
// Copyright 2003-2023 by Wilson Snyder. This program is free software; you
// can redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License
// Version 2.0.
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
#ifndef VERILATOR_V3ERROR_H_
#define VERILATOR_V3ERROR_H_
#include "config_build.h"
#include "verilatedos.h"
#include "verilated_threads.h"
// Limited V3 headers here - this is a base class for Vlc etc
#include "V3String.h"
#ifndef V3ERROR_NO_GLOBAL_
#include "V3ThreadPool.h"
#endif
2020-11-16 13:31:12 +01:00
#include <array>
#include <bitset>
#include <cassert>
#include <cctype>
#include <deque>
#include <map>
2012-05-22 03:31:52 +02:00
#include <set>
#include <sstream>
//######################################################################
class V3ErrorCode final {
public:
// clang-format off
enum en: uint8_t {
EC_MIN=0, // Keep first
//
EC_INFO, // General information out
EC_FATAL, // Kill the program
EC_FATALEXIT, // Kill the program, suppress with --quiet-exit
EC_FATALSRC, // Kill the program, for internal source errors
EC_ERROR, // General error out, can't suppress
EC_FIRST_NAMED, // Just a code so the program knows where to start info/errors
// Boolean information we track per-line, but aren't errors
I_CELLDEFINE, // Inside cell define from `celldefine/`endcelldefine
I_COVERAGE, // Coverage is on/off from /*verilator coverage_on/off*/
I_TRACING, // Tracing is on/off from /*verilator tracing_on/off*/
I_LINT, // All lint messages
I_UNUSED, // Unused genvar, parameter or signal message (Backward Compatibility)
I_DEF_NETTYPE_WIRE, // `default_nettype is WIRE (false=NONE)
Timing support (#3363) Adds timing support to Verilator. It makes it possible to use delays, event controls within processes (not just at the start), wait statements, and forks. Building a design with those constructs requires a compiler that supports C++20 coroutines (GCC 10, Clang 5). The basic idea is to have processes and tasks with delays/event controls implemented as C++20 coroutines. This allows us to suspend and resume them at any time. There are five main runtime classes responsible for managing suspended coroutines: * `VlCoroutineHandle`, a wrapper over C++20's `std::coroutine_handle` with move semantics and automatic cleanup. * `VlDelayScheduler`, for coroutines suspended by delays. It resumes them at a proper simulation time. * `VlTriggerScheduler`, for coroutines suspended by event controls. It resumes them if its corresponding trigger was set. * `VlForkSync`, used for syncing `fork..join` and `fork..join_any` blocks. * `VlCoroutine`, the return type of all verilated coroutines. It allows for suspending a stack of coroutines (normally, C++ coroutines are stackless). There is a new visitor in `V3Timing.cpp` which: * scales delays according to the timescale, * simplifies intra-assignment timing controls and net delays into regular timing controls and assignments, * simplifies wait statements into loops with event controls, * marks processes and tasks with timing controls in them as suspendable, * creates delay, trigger scheduler, and fork sync variables, * transforms timing controls and fork joins into C++ awaits There are new functions in `V3SchedTiming.cpp` (used by `V3Sched.cpp`) that integrate static scheduling with timing. This involves providing external domains for variables, so that the necessary combinational logic gets triggered after coroutine resumption, as well as statements that need to be injected into the design eval function to perform this resumption at the correct time. There is also a function that transforms forked processes into separate functions. See the comments in `verilated_timing.h`, `verilated_timing.cpp`, `V3Timing.cpp`, and `V3SchedTiming.cpp`, as well as the internals documentation for more details. Signed-off-by: Krzysztof Bieganski <kbieganski@antmicro.com>
2022-08-22 14:26:32 +02:00
I_TIMING, // Enable timing from /*verilator timing_on/off*/
// Error codes:
E_PORTSHORT, // Error: Output port is connected to a constant, electrical short
E_UNSUPPORTED, // Error: Unsupported (generally)
E_TASKNSVAR, // Error: Task I/O not simple
Timing support (#3363) Adds timing support to Verilator. It makes it possible to use delays, event controls within processes (not just at the start), wait statements, and forks. Building a design with those constructs requires a compiler that supports C++20 coroutines (GCC 10, Clang 5). The basic idea is to have processes and tasks with delays/event controls implemented as C++20 coroutines. This allows us to suspend and resume them at any time. There are five main runtime classes responsible for managing suspended coroutines: * `VlCoroutineHandle`, a wrapper over C++20's `std::coroutine_handle` with move semantics and automatic cleanup. * `VlDelayScheduler`, for coroutines suspended by delays. It resumes them at a proper simulation time. * `VlTriggerScheduler`, for coroutines suspended by event controls. It resumes them if its corresponding trigger was set. * `VlForkSync`, used for syncing `fork..join` and `fork..join_any` blocks. * `VlCoroutine`, the return type of all verilated coroutines. It allows for suspending a stack of coroutines (normally, C++ coroutines are stackless). There is a new visitor in `V3Timing.cpp` which: * scales delays according to the timescale, * simplifies intra-assignment timing controls and net delays into regular timing controls and assignments, * simplifies wait statements into loops with event controls, * marks processes and tasks with timing controls in them as suspendable, * creates delay, trigger scheduler, and fork sync variables, * transforms timing controls and fork joins into C++ awaits There are new functions in `V3SchedTiming.cpp` (used by `V3Sched.cpp`) that integrate static scheduling with timing. This involves providing external domains for variables, so that the necessary combinational logic gets triggered after coroutine resumption, as well as statements that need to be injected into the design eval function to perform this resumption at the correct time. There is also a function that transforms forked processes into separate functions. See the comments in `verilated_timing.h`, `verilated_timing.cpp`, `V3Timing.cpp`, and `V3SchedTiming.cpp`, as well as the internals documentation for more details. Signed-off-by: Krzysztof Bieganski <kbieganski@antmicro.com>
2022-08-22 14:26:32 +02:00
E_NEEDTIMINGOPT, // Error: --timing/--no-timing option not specified
E_NOTIMING, // Timing control encountered with --no-timing
//
// Warning codes:
EC_FIRST_WARN, // Just a code so the program knows where to start warnings
//
ALWCOMBORDER, // Always_comb with unordered statements
ASSIGNDLY, // Assignment delays
ASSIGNIN, // Assigning to input
BADSTDPRAGMA, // Any error related to pragmas
BLKANDNBLK, // Blocked and non-blocking assignments to same variable
BLKLOOPINIT, // Delayed assignment to array inside for loops
BLKSEQ, // Blocking assignments in sequential block
BSSPACE, // Backslash space
CASEINCOMPLETE, // Case statement has missing values
CASEOVERLAP, // Case statements overlap
CASEWITHX, // Case with X values
CASEX, // Casex
CASTCONST, // Cast is constant
2023-02-14 04:49:51 +01:00
CDCRSTLOGIC, // Logic in async reset path. Historical, never issued.
CLKDATA, // Clock used as data. Historical, never issued.
CMPCONST, // Comparison is constant due to limited range
COLONPLUS, // :+ instead of +:
COMBDLY, // Combinatorial delayed assignment
CONTASSREG, // Continuous assignment on reg
DECLFILENAME, // Declaration doesn't match filename
DEFPARAM, // Style: Defparam
DEPRECATED, // Feature will be deprecated
ENCAPSULATED, // Error: local/protected violation
ENDLABEL, // End lable name mismatch
ENUMVALUE, // Error: enum type needs explicit cast
EOFNEWLINE, // End-of-file missing newline
GENCLK, // Generated Clock. Historical, never issued.
HIERBLOCK, // Ignored hierarchical block setting
IFDEPTH, // If statements too deep
2019-09-09 13:50:21 +02:00
IGNOREDRETURN, // Ignoring return value (function as task)
IMPERFECTSCH, // Imperfect schedule (disabled by default). Historical, never issued.
IMPLICIT, // Implicit wire
IMPLICITSTATIC, // Implicit static function
IMPORTSTAR, // Import::* in $unit
IMPURE, // Impure function not being inlined
INCABSPATH, // Include has absolute path
2017-12-27 03:35:08 +01:00
INFINITELOOP, // Infinite loop
INITIALDLY, // Initial delayed statement
INSECURE, // Insecure options
LATCH, // Latch detected outside of always_latch block
LITENDIAN, // Little bit endian vector
MINTYPMAXDLY, // Unsupported: min/typ/max delay expressions
MODDUP, // Duplicate module
MULTIDRIVEN, // Driven from multiple blocks
MULTITOP, // Multiple top level modules
NOLATCH, // No latch detected in always_latch block
2021-04-09 16:39:46 +02:00
NULLPORT, // Null port detected in module definition
PINCONNECTEMPTY,// Cell pin connected by name with empty reference
PINMISSING, // Cell pin not specified
PINNOCONNECT, // Cell pin not connected
PINNOTFOUND, // instance port name not found in it's module
PKGNODECL, // Error: Package/class needs to be predeclared
PROCASSWIRE, // Procedural assignment on wire
PROFOUTOFDATE, // Profile data out of date
PROTECTED, // detected `pragma protected
RANDC, // Unsupported: 'randc' converted to 'rand'
REALCVT, // Real conversion
REDEFMACRO, // Redefining existing define macro
RISEFALLDLY, // Unsupported: rise/fall/turn-off delays
SELRANGE, // Selection index out of range
SHORTREAL, // Shortreal not supported
SPLITVAR, // Cannot split the variable
STMTDLY, // Delayed statement
SYMRSVDWORD, // Symbol is Reserved Word
SYNCASYNCNET, // Mixed sync + async reset
2018-09-23 21:09:47 +02:00
TICKCOUNT, // Too large tick count
TIMESCALEMOD, // Need timescale for module
UNDRIVEN, // No drivers
UNOPT, // Unoptimizable block. Historical, never issued.
UNOPTFLAT, // Unoptimizable block after flattening
2018-07-22 18:09:27 +02:00
UNOPTTHREADS, // Thread partitioner unable to fill all requested threads
UNPACKED, // Unsupported unpacked
UNSIGNED, // Comparison is constant due to unsigned arithmetic
UNUSEDGENVAR, // No receivers for genvar
UNUSEDPARAM, // No receivers for parameters
UNUSEDSIGNAL, // No receivers for signals
USERERROR, // Elaboration time $error
USERFATAL, // Elaboration time $fatal
USERINFO, // Elaboration time $info
USERWARN, // Elaboration time $warning
VARHIDDEN, // Hiding variable
Timing support (#3363) Adds timing support to Verilator. It makes it possible to use delays, event controls within processes (not just at the start), wait statements, and forks. Building a design with those constructs requires a compiler that supports C++20 coroutines (GCC 10, Clang 5). The basic idea is to have processes and tasks with delays/event controls implemented as C++20 coroutines. This allows us to suspend and resume them at any time. There are five main runtime classes responsible for managing suspended coroutines: * `VlCoroutineHandle`, a wrapper over C++20's `std::coroutine_handle` with move semantics and automatic cleanup. * `VlDelayScheduler`, for coroutines suspended by delays. It resumes them at a proper simulation time. * `VlTriggerScheduler`, for coroutines suspended by event controls. It resumes them if its corresponding trigger was set. * `VlForkSync`, used for syncing `fork..join` and `fork..join_any` blocks. * `VlCoroutine`, the return type of all verilated coroutines. It allows for suspending a stack of coroutines (normally, C++ coroutines are stackless). There is a new visitor in `V3Timing.cpp` which: * scales delays according to the timescale, * simplifies intra-assignment timing controls and net delays into regular timing controls and assignments, * simplifies wait statements into loops with event controls, * marks processes and tasks with timing controls in them as suspendable, * creates delay, trigger scheduler, and fork sync variables, * transforms timing controls and fork joins into C++ awaits There are new functions in `V3SchedTiming.cpp` (used by `V3Sched.cpp`) that integrate static scheduling with timing. This involves providing external domains for variables, so that the necessary combinational logic gets triggered after coroutine resumption, as well as statements that need to be injected into the design eval function to perform this resumption at the correct time. There is also a function that transforms forked processes into separate functions. See the comments in `verilated_timing.h`, `verilated_timing.cpp`, `V3Timing.cpp`, and `V3SchedTiming.cpp`, as well as the internals documentation for more details. Signed-off-by: Krzysztof Bieganski <kbieganski@antmicro.com>
2022-08-22 14:26:32 +02:00
WAITCONST, // Wait condition is constant
WIDTH, // Width mismatch
WIDTHTRUNC, // Width mismatch- lhs < rhs
WIDTHEXPAND, // Width mismatch- lhs > rhs
WIDTHXZEXPAND, // Width mismatch- lhs > rhs xz filled
WIDTHCONCAT, // Unsized numbers/parameters in concatenations
Timing support (#3363) Adds timing support to Verilator. It makes it possible to use delays, event controls within processes (not just at the start), wait statements, and forks. Building a design with those constructs requires a compiler that supports C++20 coroutines (GCC 10, Clang 5). The basic idea is to have processes and tasks with delays/event controls implemented as C++20 coroutines. This allows us to suspend and resume them at any time. There are five main runtime classes responsible for managing suspended coroutines: * `VlCoroutineHandle`, a wrapper over C++20's `std::coroutine_handle` with move semantics and automatic cleanup. * `VlDelayScheduler`, for coroutines suspended by delays. It resumes them at a proper simulation time. * `VlTriggerScheduler`, for coroutines suspended by event controls. It resumes them if its corresponding trigger was set. * `VlForkSync`, used for syncing `fork..join` and `fork..join_any` blocks. * `VlCoroutine`, the return type of all verilated coroutines. It allows for suspending a stack of coroutines (normally, C++ coroutines are stackless). There is a new visitor in `V3Timing.cpp` which: * scales delays according to the timescale, * simplifies intra-assignment timing controls and net delays into regular timing controls and assignments, * simplifies wait statements into loops with event controls, * marks processes and tasks with timing controls in them as suspendable, * creates delay, trigger scheduler, and fork sync variables, * transforms timing controls and fork joins into C++ awaits There are new functions in `V3SchedTiming.cpp` (used by `V3Sched.cpp`) that integrate static scheduling with timing. This involves providing external domains for variables, so that the necessary combinational logic gets triggered after coroutine resumption, as well as statements that need to be injected into the design eval function to perform this resumption at the correct time. There is also a function that transforms forked processes into separate functions. See the comments in `verilated_timing.h`, `verilated_timing.cpp`, `V3Timing.cpp`, and `V3SchedTiming.cpp`, as well as the internals documentation for more details. Signed-off-by: Krzysztof Bieganski <kbieganski@antmicro.com>
2022-08-22 14:26:32 +02:00
ZERODLY, // #0 delay
_ENUM_MAX
// ***Add new elements below also***
};
// clang-format on
enum en m_e;
V3ErrorCode()
: m_e{EC_MIN} {}
2015-10-04 04:33:06 +02:00
// cppcheck-suppress noExplicitConstructor
constexpr V3ErrorCode(en _e)
: m_e{_e} {}
explicit V3ErrorCode(const char* msgp); // Matching code or ERROR
explicit V3ErrorCode(int _e)
2020-08-18 14:10:44 +02:00
: m_e(static_cast<en>(_e)) {} // Need () or GCC 4.8 false warning
constexpr operator en() const VL_MT_SAFE { return m_e; }
const char* ascii() const VL_MT_SAFE {
// clang-format off
2020-05-29 03:04:36 +02:00
static const char* const names[] = {
// Leading spaces indicate it can't be disabled.
" MIN", " INFO", " FATAL", " FATALEXIT", " FATALSRC", " ERROR", " FIRST_NAMED",
// Boolean
" I_CELLDEFINE", " I_COVERAGE", " I_TRACING", " I_LINT", " I_UNUSED", " I_DEF_NETTYPE_WIRE", " I_TIMING",
// Errors
"PORTSHORT", "UNSUPPORTED", "TASKNSVAR", "NEEDTIMINGOPT", "NOTIMING",
// Warnings
" EC_FIRST_WARN",
"ALWCOMBORDER", "ASSIGNDLY", "ASSIGNIN", "BADSTDPRAGMA",
"BLKANDNBLK", "BLKLOOPINIT", "BLKSEQ", "BSSPACE",
"CASEINCOMPLETE", "CASEOVERLAP", "CASEWITHX", "CASEX", "CASTCONST", "CDCRSTLOGIC", "CLKDATA",
"CMPCONST", "COLONPLUS", "COMBDLY", "CONTASSREG",
"DECLFILENAME", "DEFPARAM", "DEPRECATED",
"ENCAPSULATED", "ENDLABEL", "ENUMVALUE", "EOFNEWLINE", "GENCLK", "HIERBLOCK",
2019-03-10 19:57:01 +01:00
"IFDEPTH", "IGNOREDRETURN",
"IMPERFECTSCH", "IMPLICIT", "IMPLICITSTATIC", "IMPORTSTAR", "IMPURE",
"INCABSPATH", "INFINITELOOP", "INITIALDLY", "INSECURE",
"LATCH", "LITENDIAN", "MINTYPMAXDLY", "MODDUP",
"MULTIDRIVEN", "MULTITOP", "NOLATCH", "NULLPORT", "PINCONNECTEMPTY",
"PINMISSING", "PINNOCONNECT", "PINNOTFOUND", "PKGNODECL", "PROCASSWIRE",
"PROFOUTOFDATE", "PROTECTED", "RANDC", "REALCVT", "REDEFMACRO", "RISEFALLDLY",
"SELRANGE", "SHORTREAL", "SPLITVAR", "STMTDLY", "SYMRSVDWORD", "SYNCASYNCNET",
"TICKCOUNT", "TIMESCALEMOD",
2019-12-15 03:39:47 +01:00
"UNDRIVEN", "UNOPT", "UNOPTFLAT", "UNOPTTHREADS",
"UNPACKED", "UNSIGNED", "UNUSEDGENVAR", "UNUSEDPARAM", "UNUSEDSIGNAL",
"USERERROR", "USERFATAL", "USERINFO", "USERWARN",
"VARHIDDEN", "WAITCONST", "WIDTH", "WIDTHTRUNC", "WIDTHEXPAND", "WIDTHXZEXPAND", "WIDTHCONCAT", "ZERODLY",
" MAX"
};
// clang-format on
return names[m_e];
}
// Warnings that default to off
bool defaultsOff() const VL_MT_SAFE {
return (m_e == IMPERFECTSCH || m_e == I_CELLDEFINE || styleError());
}
// Warnings that warn about nasty side effects
bool dangerous() const VL_MT_SAFE { return (m_e == COMBDLY); }
// Warnings we'll present to the user as errors
// Later -Werror- options may make more of these.
bool pretendError() const VL_MT_SAFE {
return (m_e == ASSIGNIN || m_e == BADSTDPRAGMA || m_e == BLKANDNBLK || m_e == BLKLOOPINIT
|| m_e == CONTASSREG || m_e == ENCAPSULATED || m_e == ENDLABEL || m_e == ENUMVALUE
|| m_e == IMPURE || m_e == PINNOTFOUND || m_e == PKGNODECL
|| m_e == PROCASSWIRE // Says IEEE
Timing support (#3363) Adds timing support to Verilator. It makes it possible to use delays, event controls within processes (not just at the start), wait statements, and forks. Building a design with those constructs requires a compiler that supports C++20 coroutines (GCC 10, Clang 5). The basic idea is to have processes and tasks with delays/event controls implemented as C++20 coroutines. This allows us to suspend and resume them at any time. There are five main runtime classes responsible for managing suspended coroutines: * `VlCoroutineHandle`, a wrapper over C++20's `std::coroutine_handle` with move semantics and automatic cleanup. * `VlDelayScheduler`, for coroutines suspended by delays. It resumes them at a proper simulation time. * `VlTriggerScheduler`, for coroutines suspended by event controls. It resumes them if its corresponding trigger was set. * `VlForkSync`, used for syncing `fork..join` and `fork..join_any` blocks. * `VlCoroutine`, the return type of all verilated coroutines. It allows for suspending a stack of coroutines (normally, C++ coroutines are stackless). There is a new visitor in `V3Timing.cpp` which: * scales delays according to the timescale, * simplifies intra-assignment timing controls and net delays into regular timing controls and assignments, * simplifies wait statements into loops with event controls, * marks processes and tasks with timing controls in them as suspendable, * creates delay, trigger scheduler, and fork sync variables, * transforms timing controls and fork joins into C++ awaits There are new functions in `V3SchedTiming.cpp` (used by `V3Sched.cpp`) that integrate static scheduling with timing. This involves providing external domains for variables, so that the necessary combinational logic gets triggered after coroutine resumption, as well as statements that need to be injected into the design eval function to perform this resumption at the correct time. There is also a function that transforms forked processes into separate functions. See the comments in `verilated_timing.h`, `verilated_timing.cpp`, `V3Timing.cpp`, and `V3SchedTiming.cpp`, as well as the internals documentation for more details. Signed-off-by: Krzysztof Bieganski <kbieganski@antmicro.com>
2022-08-22 14:26:32 +02:00
|| m_e == ZERODLY);
}
// Warnings to mention manual
bool mentionManual() const VL_MT_SAFE {
return (m_e == EC_FATALSRC || m_e == SYMRSVDWORD || pretendError());
}
// Warnings that are lint only
bool lintError() const VL_MT_SAFE {
return (m_e == ALWCOMBORDER || m_e == BSSPACE || m_e == CASEINCOMPLETE
|| m_e == CASEOVERLAP || m_e == CASEWITHX || m_e == CASEX || m_e == CASTCONST
|| m_e == CMPCONST || m_e == COLONPLUS || m_e == IMPLICIT || m_e == IMPLICITSTATIC
|| m_e == LATCH || m_e == LITENDIAN || m_e == PINMISSING || m_e == REALCVT
|| m_e == UNSIGNED || m_e == WIDTH || m_e == WIDTHTRUNC || m_e == WIDTHEXPAND
|| m_e == WIDTHXZEXPAND);
}
// Warnings that are style only
bool styleError() const VL_MT_SAFE {
return (m_e == ASSIGNDLY // More than style, but for backward compatibility
|| m_e == BLKSEQ || m_e == DEFPARAM || m_e == DECLFILENAME || m_e == EOFNEWLINE
|| m_e == IMPORTSTAR || m_e == INCABSPATH || m_e == PINCONNECTEMPTY
|| m_e == PINNOCONNECT || m_e == SYNCASYNCNET || m_e == UNDRIVEN
|| m_e == UNUSEDGENVAR || m_e == UNUSEDPARAM || m_e == UNUSEDSIGNAL
|| m_e == VARHIDDEN);
}
// Warnings that are unused only
bool unusedError() const VL_MT_SAFE {
return (m_e == UNUSEDGENVAR || m_e == UNUSEDPARAM || m_e == UNUSEDSIGNAL);
}
V3ErrorCode renamedTo() const {
// Return a new error this error has been renamed to
// e.g. if (m_e == LITENDIAN) return V3ErrorCode{RANGEASC};
return V3ErrorCode{EC_MIN}; // Not renamed; see isRenamed()
}
bool isRenamed() const { return renamedTo() != V3ErrorCode{EC_MIN}; }
bool isUnder(V3ErrorCode other) {
// backwards compatibility inheritance-like warnings
if (m_e == other) { return true; }
if (other == V3ErrorCode::WIDTH) {
return (m_e == WIDTH || m_e == WIDTHEXPAND || m_e == WIDTHTRUNC
|| m_e == WIDTHXZEXPAND);
}
if (other == V3ErrorCode::I_UNUSED) {
return (m_e == UNUSEDGENVAR || m_e == UNUSEDPARAM || m_e == UNUSEDSIGNAL);
}
return false;
}
2022-10-18 01:52:01 +02:00
static bool unusedMsg(const char* msgp) { return 0 == VL_STRCASECMP(msgp, "UNUSED"); }
};
constexpr bool operator==(const V3ErrorCode& lhs, const V3ErrorCode& rhs) {
return lhs.m_e == rhs.m_e;
}
constexpr bool operator==(const V3ErrorCode& lhs, V3ErrorCode::en rhs) { return lhs.m_e == rhs; }
constexpr bool operator==(V3ErrorCode::en lhs, const V3ErrorCode& rhs) { return lhs == rhs.m_e; }
inline std::ostream& operator<<(std::ostream& os, const V3ErrorCode& rhs) {
return os << rhs.ascii();
}
// ######################################################################
class V3ErrorGuarded final {
// Should only be used by V3ErrorGuarded::m_mutex is already locked
// contains guarded members
public:
using MessagesSet = std::set<std::string>;
using ErrorExitCb = void (*)(void);
2012-05-22 03:31:52 +02:00
private:
static constexpr unsigned MAX_ERRORS = 50; // Fatal after this may errors
bool m_describedWarnings VL_GUARDED_BY(m_mutex) = false; // Told user how to disable warns
// Tell user to see manual, 0=not yet, 1=doit, 2=disable
int m_tellManual VL_GUARDED_BY(m_mutex) = 0;
V3ErrorCode m_errorCode VL_GUARDED_BY(m_mutex)
= V3ErrorCode::EC_FATAL; // Error string being formed will abort
bool m_errorSuppressed VL_GUARDED_BY(m_mutex)
= false; // Error being formed should be suppressed
MessagesSet m_messages VL_GUARDED_BY(m_mutex); // What errors we've outputted
ErrorExitCb m_errorExitCb VL_GUARDED_BY(m_mutex)
= nullptr; // Callback when error occurs for dumping
bool m_errorContexted VL_GUARDED_BY(m_mutex) = false; // Error being formed got context
int m_warnCount VL_GUARDED_BY(m_mutex) = 0; // Warning count
int m_errCount VL_GUARDED_BY(m_mutex) = 0; // Error count
// Pretend this warning is an error
std::array<bool, V3ErrorCode::_ENUM_MAX> m_pretendError VL_GUARDED_BY(m_mutex);
bool m_describedWeb VL_GUARDED_BY(m_mutex) = false; // Told user to see web
// Told user specifics about this warning
std::array<bool, V3ErrorCode::_ENUM_MAX> m_describedEachWarn VL_GUARDED_BY(m_mutex);
int m_debugDefault = 0; // Option: --debugi Default debugging level
int m_errorLimit VL_GUARDED_BY(m_mutex)
= MAX_ERRORS; // Option: --error-limit Number of errors before exit
bool m_warnFatal VL_GUARDED_BY(m_mutex) = true; // Option: --warnFatal Warnings are fatal
std::ostringstream m_errorStr VL_GUARDED_BY(m_mutex); // Error string being formed
public:
VerilatedMutex m_mutex; // Make sure only single thread is in class
string msgPrefix() VL_REQUIRES(m_mutex); // returns %Error/%Warn
string warnMore() VL_REQUIRES(m_mutex);
void execErrorExitCb() VL_REQUIRES(m_mutex) {
if (m_errorExitCb) m_errorExitCb();
}
void errorExitCb(ErrorExitCb cb) VL_REQUIRES(m_mutex) { m_errorExitCb = cb; }
ErrorExitCb errorExitCb() VL_REQUIRES(m_mutex) { return m_errorExitCb; }
bool isError(V3ErrorCode code, bool supp) VL_REQUIRES(m_mutex);
void vlAbortOrExit() VL_REQUIRES(m_mutex);
void errorContexted(bool flag) VL_REQUIRES(m_mutex) { m_errorContexted = flag; }
void incWarnings() VL_REQUIRES(m_mutex) { m_warnCount++; }
void incErrors() VL_REQUIRES(m_mutex) {
m_errCount++;
if (errorCount() == errorLimit()) { // Not >= as would otherwise recurse
v3errorEnd(
(v3errorPrep(V3ErrorCode::EC_FATALEXIT),
(v3errorStr() << "Exiting due to too many errors encountered; --error-limit="
<< errorCount() << endl),
v3errorStr()));
assert(0); // LCOV_EXCL_LINE
VL_UNREACHABLE;
}
}
int errorCount() VL_REQUIRES(m_mutex) { return m_errCount; }
bool pretendError(int errorCode) VL_REQUIRES(m_mutex) { return m_pretendError[errorCode]; }
void pretendError(V3ErrorCode code, bool flag) VL_REQUIRES(m_mutex) {
if (code == V3ErrorCode::WIDTH) {
m_pretendError[V3ErrorCode::WIDTHTRUNC] = flag;
m_pretendError[V3ErrorCode::WIDTHEXPAND] = flag;
m_pretendError[V3ErrorCode::WIDTHXZEXPAND] = flag;
}
m_pretendError[code] = flag;
}
void debugDefault(int level) VL_MT_UNSAFE { m_debugDefault = level; }
int debugDefault() VL_MT_SAFE { return m_debugDefault; }
void errorLimit(int level) VL_REQUIRES(m_mutex) { m_errorLimit = level; }
int errorLimit() VL_REQUIRES(m_mutex) { return m_errorLimit; }
void warnFatal(bool flag) VL_REQUIRES(m_mutex) { m_warnFatal = flag; }
bool warnFatal() VL_REQUIRES(m_mutex) { return m_warnFatal; }
void v3errorPrep(V3ErrorCode code) VL_REQUIRES(m_mutex) {
m_errorStr.str("");
m_errorCode = code;
m_errorContexted = false;
m_errorSuppressed = false;
}
std::ostringstream& v3errorStr() VL_REQUIRES(m_mutex) { return m_errorStr; }
V3ErrorCode errorCode() VL_REQUIRES(m_mutex) { return m_errorCode; }
bool errorContexted() VL_REQUIRES(m_mutex) { return m_errorContexted; }
int warnCount() VL_REQUIRES(m_mutex) { return m_warnCount; }
bool errorSuppressed() VL_REQUIRES(m_mutex) { return m_errorSuppressed; }
void errorSuppressed(bool flag) VL_REQUIRES(m_mutex) { m_errorSuppressed = flag; }
bool describedWeb() VL_REQUIRES(m_mutex) { return m_describedWeb; }
void describedWeb(bool flag) VL_REQUIRES(m_mutex) { m_describedWeb = flag; }
bool describedEachWarn(V3ErrorCode code) VL_REQUIRES(m_mutex) {
return m_describedEachWarn[code];
}
void describedEachWarn(V3ErrorCode code, bool flag) VL_REQUIRES(m_mutex) {
m_describedEachWarn[code] = flag;
}
bool describedWarnings() VL_REQUIRES(m_mutex) { return m_describedWarnings; }
void describedWarnings(bool flag) VL_REQUIRES(m_mutex) { m_describedWarnings = flag; }
int tellManual() VL_REQUIRES(m_mutex) { return m_tellManual; }
void tellManual(int level) VL_REQUIRES(m_mutex) { m_tellManual = level; }
void v3errorEnd(std::ostringstream& sstr, const string& extra = "") VL_REQUIRES(m_mutex);
void suppressThisWarning() VL_REQUIRES(m_mutex);
string warnContextNone() VL_REQUIRES(m_mutex) {
errorContexted(true);
return "";
}
};
// ######################################################################
class V3Error final {
// Base class for any object that wants debugging and error reporting
private:
// CONSTRUCTORS
V3Error() {
std::cerr << ("Static class");
V3Error::vlAbort();
}
public:
static V3ErrorGuarded& s() VL_MT_SAFE { // Singleton
static V3ErrorGuarded s_s;
return s_s;
}
// ACCESSORS
static void debugDefault(int level) VL_MT_UNSAFE { s().debugDefault(level); }
static int debugDefault() VL_MT_SAFE { return s().debugDefault(); }
static void errorLimit(int level) VL_MT_SAFE_EXCLUDES(s().m_mutex) {
const VerilatedLockGuard guard{s().m_mutex};
s().errorLimit(level);
}
static int errorLimit() VL_MT_SAFE_EXCLUDES(s().m_mutex) {
const VerilatedLockGuard guard{s().m_mutex};
return s().errorLimit();
}
static void warnFatal(bool flag) VL_MT_SAFE_EXCLUDES(s().m_mutex) {
const VerilatedLockGuard guard{s().m_mutex};
s().warnFatal(flag);
}
static bool warnFatal() VL_MT_SAFE_EXCLUDES(s().m_mutex) {
const VerilatedLockGuard guard{s().m_mutex};
return s().warnFatal();
}
// returns %Error/%Warn
static string msgPrefix() VL_MT_SAFE_EXCLUDES(s().m_mutex) {
const VerilatedLockGuard guard{s().m_mutex};
return s().msgPrefix();
}
static int errorCount() VL_MT_SAFE_EXCLUDES(s().m_mutex) {
const VerilatedLockGuard guard{s().m_mutex};
return s().errorCount();
}
static bool pretendError(int errorCode) VL_MT_SAFE_EXCLUDES(s().m_mutex) {
const VerilatedLockGuard guard{s().m_mutex};
return s().pretendError(errorCode);
}
static int warnCount() VL_MT_SAFE_EXCLUDES(s().m_mutex) {
const VerilatedLockGuard guard{s().m_mutex};
return s().warnCount();
}
static bool errorContexted() VL_MT_SAFE_EXCLUDES(s().m_mutex) {
const VerilatedLockGuard guard{s().m_mutex};
return s().errorContexted();
}
static void errorContexted(bool flag) VL_MT_SAFE_EXCLUDES(s().m_mutex) {
const VerilatedLockGuard guard{s().m_mutex};
s().errorContexted(flag);
}
static void describedEachWarn(V3ErrorCode code, bool flag) VL_MT_SAFE_EXCLUDES(s().m_mutex) {
const VerilatedLockGuard guard{s().m_mutex};
s().describedEachWarn(code, flag);
}
// METHODS
static void incErrors() VL_MT_SAFE_EXCLUDES(s().m_mutex) {
const VerilatedLockGuard guard{s().m_mutex};
s().incErrors();
}
static void incWarnings() VL_MT_SAFE_EXCLUDES(s().m_mutex) {
const VerilatedLockGuard guard{s().m_mutex};
s().incWarnings();
}
static void init();
static void abortIfErrors() {
if (errorCount()) abortIfWarnings();
}
static void abortIfWarnings();
// Suppress next %Warn if user has it off
static void suppressThisWarning() VL_MT_SAFE_EXCLUDES(s().m_mutex) {
const VerilatedLockGuard guard{s().m_mutex};
s().suppressThisWarning();
}
static void pretendError(V3ErrorCode code, bool flag) VL_MT_SAFE_EXCLUDES(s().m_mutex) {
const VerilatedLockGuard guard{s().m_mutex};
s().pretendError(code, flag);
}
static string lineStr(const char* filename, int lineno) VL_PURE;
static V3ErrorCode errorCode() VL_MT_SAFE_EXCLUDES(s().m_mutex) {
const VerilatedLockGuard guard{s().m_mutex};
return s().errorCode();
}
static void errorExitCb(V3ErrorGuarded::ErrorExitCb cb) VL_MT_SAFE_EXCLUDES(s().m_mutex) {
const VerilatedLockGuard guard{s().m_mutex};
s().errorExitCb(cb);
}
// When printing an error/warning, print prefix for multiline message
static string warnMore() VL_REQUIRES(s().m_mutex) { return s().warnMore(); }
// This function should only be used when it is impossible to
// generate whole error message inside v3warn macros and it needs to be
// streamed directly to cerr.
// Use with caution as this function isn't MT_SAFE.
static string warnMoreStandalone() VL_EXCLUDES(s().m_mutex) VL_MT_UNSAFE {
const VerilatedLockGuard guard{s().m_mutex};
return s().warnMore();
}
// This function marks place in error message from which point message
// should be printed after information on the error code.
// The post-processing is done in v3errorEnd function.
static string warnAdditionalInfo() VL_MT_SAFE { return "__WARNADDITIONALINFO__"; }
/// When building an error, don't show context info
static string warnContextNone() {
V3Error::errorContexted(true);
return "";
}
// Internals for v3error()/v3fatal() macros only
// Error end takes the string stream to output, be careful to seek() as needed
static void v3errorPrep(V3ErrorCode code) VL_MT_SAFE_EXCLUDES(s().m_mutex) {
const VerilatedLockGuard guard{s().m_mutex};
s().v3errorPrep(code);
}
static std::ostringstream& v3errorStr() VL_MT_SAFE_EXCLUDES(s().m_mutex) {
const VerilatedLockGuard guard{s().m_mutex};
return s().v3errorStr();
}
static void vlAbort();
// static, but often overridden in classes.
static void v3errorEnd(std::ostringstream& sstr, const string& extra = "")
VL_MT_SAFE_EXCLUDES(s().m_mutex) VL_MT_SAFE {
const VerilatedLockGuard guard{s().m_mutex};
s().v3errorEnd(sstr, extra);
}
// We can't call 's().v3errorEnd' directly in 'v3ErrorEnd'/'v3errorEndFatal',
// due to bug in GCC (tested on 11.3.0 version with --enable-m32)
// causing internal error when backtrace is printed.
// Instead use this wrapper.
static void v3errorEndGuardedCall(std::ostringstream& sstr, const string& extra = "")
VL_REQUIRES(s().m_mutex) VL_MT_SAFE {
s().v3errorEnd(sstr, extra);
}
};
// Global versions, so that if the class doesn't define a operator, we get the functions anyways.
inline void v3errorEnd(std::ostringstream& sstr) VL_REQUIRES(V3Error::s().m_mutex) VL_MT_SAFE {
V3Error::v3errorEndGuardedCall(sstr);
}
inline void v3errorEndFatal(std::ostringstream& sstr)
VL_REQUIRES(V3Error::s().m_mutex) VL_MT_SAFE {
V3Error::v3errorEndGuardedCall(sstr);
assert(0); // LCOV_EXCL_LINE
VL_UNREACHABLE;
}
#ifndef V3ERROR_NO_GLOBAL_
#define V3ErrorLockAndCheckStopRequested \
V3Error::s().m_mutex.lockCheckStopRequest( \
[]() -> void { V3ThreadPool::s().waitIfStopRequested(); })
#else
#define V3ErrorLockAndCheckStopRequested V3Error::s().m_mutex.lock()
#endif
// Theses allow errors using << operators: v3error("foo"<<"bar");
// Careful, you can't put () around msg, as you would in most macro definitions
2020-01-25 02:10:44 +01:00
// Note the commas are the comma operator, not separating arguments. These are needed to ensure
// evaluation order as otherwise we couldn't ensure v3errorPrep is called first.
// Note: due to limitations of clang thread-safety analysis, we can't use
// lock guard here, instead we are locking the mutex as first operation in temporary,
// but we are unlocking the mutex after function using comma operator.
// This way macros should also work when they are in 'if' stmt without '{}'.
#define v3warnCode(code, msg) \
v3errorEnd((V3ErrorLockAndCheckStopRequested, V3Error::s().v3errorPrep(code), \
(V3Error::s().v3errorStr() << msg), V3Error::s().v3errorStr())), \
V3Error::s().m_mutex.unlock()
#define v3warnCodeFatal(code, msg) \
v3errorEndFatal((V3ErrorLockAndCheckStopRequested, V3Error::s().v3errorPrep(code), \
(V3Error::s().v3errorStr() << msg), V3Error::s().v3errorStr())), \
V3Error::s().m_mutex.unlock()
#define v3warn(code, msg) v3warnCode(V3ErrorCode::code, msg)
#define v3info(msg) v3warnCode(V3ErrorCode::EC_INFO, msg)
#define v3error(msg) v3warnCode(V3ErrorCode::EC_ERROR, msg)
#define v3fatal(msg) v3warnCodeFatal(V3ErrorCode::EC_FATAL, msg)
// Use this instead of fatal() if message gets suppressed with --quiet-exit
#define v3fatalExit(msg) v3warnCodeFatal(V3ErrorCode::EC_FATALEXIT, msg)
// Use this instead of fatal() to mention the source code line.
#define v3fatalSrc(msg) \
v3warnCodeFatal(V3ErrorCode::EC_FATALSRC, \
__FILE__ << ":" << std::dec << __LINE__ << ": " << msg)
// Use this when normal v3fatal is called in static method that overrides fileline.
#define v3fatalStatic(msg) \
(::v3errorEndFatal((V3ErrorLockAndCheckStopRequested, \
V3Error::s().v3errorPrep(V3ErrorCode::EC_FATAL), \
(V3Error::s().v3errorStr() << msg), V3Error::s().v3errorStr()))), \
V3Error::s().m_mutex.unlock()
#define UINFO(level, stmsg) \
do { \
if (VL_UNCOVERABLE(debug() >= (level))) { \
cout << "- " << V3Error::lineStr(__FILE__, __LINE__) << stmsg; \
} \
} while (false)
#define UINFONL(level, stmsg) \
do { \
if (VL_UNCOVERABLE(debug() >= (level))) { cout << stmsg; } \
} while (false)
#ifdef VL_DEBUG
#define UDEBUGONLY(stmts) \
do { stmts } while (false)
#else
#define UDEBUGONLY(stmts) \
do { \
if (false) { stmts } \
} while (false)
#endif
// Assertion without object, generally UOBJASSERT preferred
#define UASSERT(condition, stmsg) \
do { \
if (VL_UNCOVERABLE(!(condition))) v3fatalSrc(stmsg); \
} while (false)
// Assertion with object
#define UASSERT_OBJ(condition, obj, stmsg) \
do { \
if (VL_UNCOVERABLE(!(condition))) (obj)->v3fatalSrc(stmsg); \
} while (false)
// For use in V3Ast static functions only
#define UASSERT_STATIC(condition, stmsg) \
do { \
if (VL_UNCOVERABLE(!(condition))) { \
std::cerr << "Internal Error: " << __FILE__ << ":" << std::dec << __LINE__ << ":" \
<< (stmsg) << std::endl; \
V3Error::vlAbort(); \
} \
} while (false)
// Check self test values for expected value. Safe from side-effects.
// Type argument can be removed when go to C++11 (use auto).
#define UASSERT_SELFTEST(Type, got, exp) \
do { \
Type g = (got); \
Type e = (exp); \
UASSERT(g == e, "Self-test failed '" #got "==" #exp "'" \
" got=" \
<< g << " expected=" << e); \
} while (false)
#define V3ERROR_NA \
do { \
v3error("Internal: Unexpected Call"); \
v3fatalSrc("Unexpected Call"); \
} while (false)
/// Throw fatal and return a value. The return value will never really be
/// needed, but required to avoid compiler error.
#define V3ERROR_NA_RETURN(value) \
V3ERROR_NA; \
return value
// Helper macros for VL_DEFINE_DEBUG_FUNCTIONS
2022-12-22 18:19:09 +01:00
// Takes an optional "name" (as __VA_ARGS__)
#define VL_DEFINE_DEBUG(...) \
VL_ATTR_UNUSED static int debug##__VA_ARGS__() { \
static int level = -1; \
if (VL_UNLIKELY(level < 0)) { \
2022-12-22 18:19:09 +01:00
std::string tag{VL_STRINGIFY(__VA_ARGS__)}; \
tag[0] = std::tolower(tag[0]); \
const unsigned debugTag = v3Global.opt.debugLevel(tag); \
const unsigned debugSrc = v3Global.opt.debugSrcLevel(__FILE__); \
const unsigned debugLevel = debugTag >= debugSrc ? debugTag : debugSrc; \
if (!v3Global.opt.available()) return static_cast<int>(debugLevel); \
level = static_cast<int>(debugLevel); \
} \
return level; \
} \
static_assert(true, "")
2022-12-22 18:19:09 +01:00
// Takes an optional "name" (as __VA_ARGS__)
#define VL_DEFINE_DUMP(...) \
VL_ATTR_UNUSED static int dump##__VA_ARGS__() { \
static int level = -1; \
if (VL_UNLIKELY(level < 0)) { \
2022-12-22 18:19:09 +01:00
std::string tag{VL_STRINGIFY(__VA_ARGS__)}; \
tag[0] = std::tolower(tag[0]); \
const unsigned dumpTag = v3Global.opt.dumpLevel(tag); \
const unsigned dumpSrc = v3Global.opt.dumpSrcLevel(__FILE__); \
const unsigned dumpLevel = dumpTag >= dumpSrc ? dumpTag : dumpSrc; \
if (!v3Global.opt.available()) return static_cast<int>(dumpLevel); \
level = static_cast<int>(dumpLevel); \
} \
return level; \
} \
static_assert(true, "")
// Define debug*() and dump*() routines. This needs to be added to every compilation unit so that
// --debugi-<tag/srcfile> and --dumpi-<tag/srcfile> can be used to control debug prints and dumping
#define VL_DEFINE_DEBUG_FUNCTIONS \
VL_DEFINE_DEBUG(); /* Define 'int debug()' */ \
VL_DEFINE_DUMP(); /* Define 'int dump()' */ \
Introduce DFG based combinational logic optimizer (#3527) Added a new data-flow graph (DFG) based combinational logic optimizer. The capabilities of this covers a combination of V3Const and V3Gate, but is also more capable of transforming combinational logic into simplified forms and more. This entail adding a new internal representation, `DfgGraph`, and appropriate `astToDfg` and `dfgToAst` conversion functions. The graph represents some of the combinational equations (~continuous assignments) in a module, and for the duration of the DFG passes, it takes over the role of AstModule. A bulk of the Dfg vertices represent expressions. These vertex classes, and the corresponding conversions to/from AST are mostly auto-generated by astgen, together with a DfgVVisitor that can be used for dynamic dispatch based on vertex (operation) types. The resulting combinational logic graph (a `DfgGraph`) is then optimized in various ways. Currently we perform common sub-expression elimination, variable inlining, and some specific peephole optimizations, but there is scope for more optimizations in the future using the same representation. The optimizer is run directly before and after inlining. The pre inline pass can operate on smaller graphs and hence converges faster, but still has a chance of substantially reducing the size of the logic on some designs, making inlining both faster and less memory intensive. The post inline pass can then optimize across the inlined module boundaries. No optimization is performed across a module boundary. For debugging purposes, each peephole optimization can be disabled individually via the -fno-dfg-peepnole-<OPT> option, where <OPT> is one of the optimizations listed in V3DfgPeephole.h, for example -fno-dfg-peephole-remove-not-not. The peephole patterns currently implemented were mostly picked based on the design that inspired this work, and on that design the optimizations yields ~30% single threaded speedup, and ~50% speedup on 4 threads. As you can imagine not having to haul around redundant combinational networks in the rest of the compilation pipeline also helps with memory consumption, and up to 30% peak memory usage of Verilator was observed on the same design. Gains on other arbitrary designs are smaller (and can be improved by analyzing those designs). For example OpenTitan gains between 1-15% speedup depending on build type.
2022-09-23 17:46:22 +02:00
VL_DEFINE_DUMP(Dfg); /* Define 'int dumpDfg()' */ \
VL_DEFINE_DUMP(Graph); /* Define 'int dumpGraph()' */ \
VL_DEFINE_DUMP(Tree); /* Define 'int dumpTree()' */ \
static_assert(true, "")
//----------------------------------------------------------------------
#endif // Guard