From 3ed2af638ba6da6b29768693c6c10c09f28e4a39 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Wed, 7 Jul 2021 20:50:11 -0400 Subject: [PATCH 01/84] devel release --- Changes | 6 ++++++ configure.ac | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Changes b/Changes index ca5a3183b..4ce4d40f5 100644 --- a/Changes +++ b/Changes @@ -8,6 +8,12 @@ The changes in each Verilator version are described below. The contributors that suggested a given feature are shown in []. Thanks! +Verilator 4.211 devel +========================== + +**Minor:** + + Verilator 4.210 2021-07-07 ========================== diff --git a/configure.ac b/configure.ac index 174afb803..246f6e162 100644 --- a/configure.ac +++ b/configure.ac @@ -7,7 +7,7 @@ #AC_INIT([Verilator],[#.### YYYY-MM-DD]) #AC_INIT([Verilator],[#.### devel]) -AC_INIT([Verilator],[4.210 2021-07-07], +AC_INIT([Verilator],[4.211 devel], [https://verilator.org], [verilator],[https://verilator.org]) # When releasing, also update header of Changes file From 76b3776fa3557bfa9fe63902b022940d88c9f5a5 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Tue, 22 Jun 2021 13:16:23 +0100 Subject: [PATCH 02/84] Change generated tracing routines to use snake_case For consistency with the rest of the generated code, generated methods related to tracing now use snake_case instead of camelCase. No functional change intended. --- src/V3EmitCModel.cpp | 12 ++++++------ src/V3Trace.cpp | 20 ++++++++++---------- src/V3TraceDecl.cpp | 4 ++-- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/V3EmitCModel.cpp b/src/V3EmitCModel.cpp index 6b67f6fad..dd0e6a64c 100644 --- a/src/V3EmitCModel.cpp +++ b/src/V3EmitCModel.cpp @@ -484,12 +484,12 @@ class EmitCModel final : public EmitCFunc { putSectionDelimiter("Trace configuration"); // Forward declaration - puts("\nvoid " + topModNameProtected + "__" + protect("traceInitTop") + "(" + puts("\nvoid " + topModNameProtected + "__" + protect("trace_init_top") + "(" + topModNameProtected + "* vlSelf, " + v3Global.opt.traceClassBase() + "* tracep);\n"); // Static helper function - puts("\nstatic void " + protect("traceInit") + "(void* voidSelf, " + puts("\nstatic void " + protect("trace_init") + "(void* voidSelf, " + v3Global.opt.traceClassBase() + "* tracep, uint32_t code) {\n"); putsDecoration("// Callback from tracep->open()\n"); puts(voidSelfAssign(modp)); @@ -502,20 +502,20 @@ class EmitCModel final : public EmitCFunc { puts("vlSymsp->__Vm_baseCode = code;\n"); puts("tracep->module(vlSymsp->name());\n"); puts("tracep->scopeEscape(' ');\n"); - puts(topModNameProtected + "__" + protect("traceInitTop") + "(vlSelf, tracep);\n"); + puts(topModNameProtected + "__" + protect("trace_init_top") + "(vlSelf, tracep);\n"); puts("tracep->scopeEscape('.');\n"); // Restore so later traced files won't break puts("}\n"); // Forward declaration - puts("\nvoid " + topModNameProtected + "__" + protect("traceRegister") + "(" + puts("\nvoid " + topModNameProtected + "__" + protect("trace_register") + "(" + topModNameProtected + "* vlSelf, " + v3Global.opt.traceClassBase() + "* tracep);\n"); // ::trace puts("\nvoid " + topClassName() + "::trace("); puts(v3Global.opt.traceClassBase() + "C* tfp, int, int) {\n"); - puts("tfp->spTrace()->addInitCb(&" + protect("traceInit") + ", &(vlSymsp->TOP));\n"); - puts(topModNameProtected + "__" + protect("traceRegister") + puts("tfp->spTrace()->addInitCb(&" + protect("trace_init") + ", &(vlSymsp->TOP));\n"); + puts(topModNameProtected + "__" + protect("trace_register") + "(&(vlSymsp->TOP), tfp->spTrace());\n"); puts("}\n"); } diff --git a/src/V3Trace.cpp b/src/V3Trace.cpp index 92ecc8b5b..062cd2ea1 100644 --- a/src/V3Trace.cpp +++ b/src/V3Trace.cpp @@ -476,10 +476,10 @@ private: // Create new function string name; switch (type) { - case AstCFuncType::TRACE_FULL: name = "traceFullTop"; break; - case AstCFuncType::TRACE_FULL_SUB: name = "traceFullSub"; break; - case AstCFuncType::TRACE_CHANGE: name = "traceChgTop"; break; - case AstCFuncType::TRACE_CHANGE_SUB: name = "traceChgSub"; break; + case AstCFuncType::TRACE_FULL: name = "trace_full_top_"; break; + case AstCFuncType::TRACE_FULL_SUB: name = "trace_full_sub_"; break; + case AstCFuncType::TRACE_CHANGE: name = "trace_chg_top_"; break; + case AstCFuncType::TRACE_CHANGE_SUB: name = "trace_chg_sub_"; break; default: m_topScopep->v3fatalSrc("Bad trace function type"); } name += cvtToStr(funcNump++); @@ -593,8 +593,8 @@ private: } } if (topFuncp) { // might be nullptr if all trailing entries were duplicates - UINFO(5, "traceFullTop" << topFuncNum - 1 << " codes: " << nCodes << "/" - << maxCodes << endl); + UINFO(5, "trace_full_top" << topFuncNum - 1 << " codes: " << nCodes << "/" + << maxCodes << endl); } } } @@ -667,15 +667,15 @@ private: nCodes += declp->codeInc(); } if (topFuncp) { // might be nullptr if all trailing entries were duplicates/constants - UINFO(5, "traceChgTop" << topFuncNum - 1 << " codes: " << nCodes << "/" << maxCodes - << endl); + UINFO(5, "trace_chg_top" << topFuncNum - 1 << " codes: " << nCodes << "/" + << maxCodes << endl); } } } void createCleanupFunction(AstCFunc* regFuncp) { FileLine* const fl = m_topScopep->fileline(); - AstCFunc* const cleanupFuncp = new AstCFunc(fl, "traceCleanup", m_topScopep); + AstCFunc* const cleanupFuncp = new AstCFunc(fl, "trace_cleanup", m_topScopep); cleanupFuncp->argTypes("void* voidSelf, " + v3Global.opt.traceClassBase() + "* /*unused*/"); cleanupFuncp->funcType(AstCFuncType::TRACE_CLEANUP); @@ -736,7 +736,7 @@ private: // Create the trace registration function AstCFunc* const regFuncp - = new AstCFunc(m_topScopep->fileline(), "traceRegister", m_topScopep); + = new AstCFunc(m_topScopep->fileline(), "trace_register", m_topScopep); regFuncp->argTypes(v3Global.opt.traceClassBase() + "* tracep"); regFuncp->funcType(AstCFuncType::TRACE_REGISTER); regFuncp->slow(true); diff --git a/src/V3TraceDecl.cpp b/src/V3TraceDecl.cpp index 873d66b80..b7f61a01f 100644 --- a/src/V3TraceDecl.cpp +++ b/src/V3TraceDecl.cpp @@ -94,7 +94,7 @@ private: basep->addStmtsp(callp); } AstCFunc* newCFuncSub(AstCFunc* basep) { - const string name = "traceInitSub" + cvtToStr(m_funcNum++); + const string name = "trace_init_sub_" + cvtToStr(m_funcNum++); AstCFunc* const funcp = newCFunc(AstCFuncType::TRACE_INIT_SUB, name); if (!m_interface) callCFuncSub(basep, funcp, nullptr); return funcp; @@ -135,7 +135,7 @@ private: virtual void visit(AstTopScope* nodep) override { m_topScopep = nodep->scopep(); // Create the trace init function - m_initFuncp = newCFunc(AstCFuncType::TRACE_INIT, "traceInitTop"); + m_initFuncp = newCFunc(AstCFuncType::TRACE_INIT, "trace_init_top"); // Create initial sub function m_initSubFuncp = newCFuncSub(m_initFuncp); // And find variables From 686baaf2cf9ed1a1fd9ec0d0b0b4113351a21f00 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Tue, 22 Jun 2021 13:50:21 +0100 Subject: [PATCH 03/84] Internals: Streamline trace function generation Remove magic code fragments form EmitCTrace, so Emit need not be aware that a function is tracing related or not (apart from the purpose of file name generation). All necessary code is now generated via text nodes in V3TraceDecl and V3Trace. No functional change intended. --- src/V3Ast.h | 33 -------- src/V3AstNodes.h | 20 +++-- src/V3EmitC.cpp | 156 ++++++++------------------------------ src/V3EmitC.h | 1 - src/V3EmitCFunc.h | 2 - src/V3Trace.cpp | 178 ++++++++++++++++++++++---------------------- src/V3TraceDecl.cpp | 11 ++- src/Verilator.cpp | 1 - 8 files changed, 143 insertions(+), 259 deletions(-) diff --git a/src/V3Ast.h b/src/V3Ast.h index afa26f75d..b569d76a9 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -238,39 +238,6 @@ inline bool operator==(AstPragmaType::en lhs, const AstPragmaType& rhs) { return //###################################################################### -class AstCFuncType final { -public: - enum en : uint8_t { - FT_NORMAL, - TRACE_REGISTER, - TRACE_INIT, - TRACE_INIT_SUB, - TRACE_FULL, - TRACE_FULL_SUB, - TRACE_CHANGE, - TRACE_CHANGE_SUB, - TRACE_CLEANUP - }; - enum en m_e; - inline AstCFuncType() - : m_e{FT_NORMAL} {} - // cppcheck-suppress noExplicitConstructor - inline AstCFuncType(en _e) - : m_e{_e} {} - explicit inline AstCFuncType(int _e) - : m_e(static_cast(_e)) {} // Need () or GCC 4.8 false warning - operator en() const { return m_e; } - // METHODS - bool isTrace() const { return m_e != FT_NORMAL; } -}; -inline bool operator==(const AstCFuncType& lhs, const AstCFuncType& rhs) { - return lhs.m_e == rhs.m_e; -} -inline bool operator==(const AstCFuncType& lhs, AstCFuncType::en rhs) { return lhs.m_e == rhs; } -inline bool operator==(AstCFuncType::en lhs, const AstCFuncType& rhs) { return lhs == rhs.m_e; } - -//###################################################################### - class VEdgeType final { public: // REMEMBER to edit the strings below too diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index 731bcc1dc..f75c67a82 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -5244,11 +5244,14 @@ class AstTraceInc final : public AstNodeStmt { private: AstTraceDecl* m_declp; // Pointer to declaration const bool m_full; // Is this a full vs incremental dump + const uint32_t m_baseCode; // Trace code base value in function containing this AstTraceInc + public: - AstTraceInc(FileLine* fl, AstTraceDecl* declp, bool full) + AstTraceInc(FileLine* fl, AstTraceDecl* declp, bool full, uint32_t baseCode = 0) : ASTGEN_SUPER_TraceInc(fl) , m_declp{declp} - , m_full{full} { + , m_full{full} + , m_baseCode{baseCode} { dtypeFrom(declp); addOp2p(declp->valuep()->cloneTree(true)); } @@ -5276,6 +5279,7 @@ public: AstNode* valuep() const { return op2p(); } AstTraceDecl* declp() const { return m_declp; } bool full() const { return m_full; } + uint32_t baseCode() const { return m_baseCode; } }; class AstActive final : public AstNode { @@ -8728,7 +8732,6 @@ class AstCFunc final : public AstNode { // Parents: MODULE/SCOPE // Children: VAR/statements private: - AstCFuncType m_funcType; AstScope* m_scopep; string m_name; string m_cname; // C name, for dpiExports @@ -8738,6 +8741,7 @@ private: string m_ifdef; // #ifdef symbol around this function VBoolOrUnknown m_isConst; // Function is declared const (*this not changed) bool m_isStatic : 1; // Function is static (no need for a 'this' pointer) + bool m_isTrace : 1; // Function is related to tracing bool m_dontCombine : 1; // V3Combine shouldn't compare this func tree, it's special bool m_declPrivate : 1; // Declare it private bool m_formCallTree : 1; // Make a global function to call entire tree of functions @@ -8759,12 +8763,12 @@ private: public: AstCFunc(FileLine* fl, const string& name, AstScope* scopep, const string& rtnType = "") : ASTGEN_SUPER_CFunc(fl) { - m_funcType = AstCFuncType::FT_NORMAL; m_isConst = VBoolOrUnknown::BU_UNKNOWN; // Unknown until analyzed m_scopep = scopep; m_name = name; m_rtnType = rtnType; m_isStatic = false; + m_isTrace = false; m_dontCombine = false; m_declPrivate = false; m_formCallTree = false; @@ -8793,7 +8797,7 @@ public: virtual void dump(std::ostream& str = std::cout) const override; virtual bool same(const AstNode* samep) const override { const AstCFunc* asamep = static_cast(samep); - return ((funcType() == asamep->funcType()) && (rtnTypeVoid() == asamep->rtnTypeVoid()) + return ((isTrace() == asamep->isTrace()) && (rtnTypeVoid() == asamep->rtnTypeVoid()) && (argTypes() == asamep->argTypes()) && (ctorInits() == asamep->ctorInits()) && isLoose() == asamep->isLoose() && (!(dpiImportPrototype() || dpiExportImpl()) || name() == asamep->name())); @@ -8806,12 +8810,14 @@ public: void isConst(VBoolOrUnknown flag) { m_isConst = flag; } bool isStatic() const { return m_isStatic; } void isStatic(bool flag) { m_isStatic = flag; } + bool isTrace() const { return m_isTrace; } + void isTrace(bool flag) { m_isTrace = flag; } void cname(const string& name) { m_cname = name; } string cname() const { return m_cname; } AstScope* scopep() const { return m_scopep; } void scopep(AstScope* nodep) { m_scopep = nodep; } string rtnTypeVoid() const { return ((m_rtnType == "") ? "void" : m_rtnType); } - bool dontCombine() const { return m_dontCombine || funcType() != AstCFuncType::FT_NORMAL; } + bool dontCombine() const { return m_dontCombine || isTrace(); } void dontCombine(bool flag) { m_dontCombine = flag; } bool dontInline() const { return dontCombine() || slow() || funcPublic(); } bool declPrivate() const { return m_declPrivate; } @@ -8828,8 +8834,6 @@ public: string ctorInits() const { return m_ctorInits; } void ifdef(const string& str) { m_ifdef = str; } string ifdef() const { return m_ifdef; } - void funcType(AstCFuncType flag) { m_funcType = flag; } - AstCFuncType funcType() const { return m_funcType; } bool isConstructor() const { return m_isConstructor; } void isConstructor(bool flag) { m_isConstructor = flag; } bool isDestructor() const { return m_isDestructor; } diff --git a/src/V3EmitC.cpp b/src/V3EmitC.cpp index fc8cb08fa..4e7f7cdae 100644 --- a/src/V3EmitC.cpp +++ b/src/V3EmitC.cpp @@ -74,7 +74,7 @@ class EmitCImp final : EmitCFunc { using EmitCFunc::visit; // Suppress hidden overloaded virtual function warning virtual void visit(AstCFunc* nodep) override { // TRACE_* and DPI handled elsewhere - if (nodep->funcType().isTrace()) return; + if (nodep->isTrace()) return; if (nodep->dpiImportPrototype()) return; if (nodep->dpiExportDispatcher()) return; if (!(nodep->slow() ? m_slow : m_fast)) return; @@ -675,10 +675,8 @@ class EmitCTrace final : EmitCFunc { AstUser1InUse m_inuser1; // MEMBERS - AstCFunc* m_cfuncp = nullptr; // Function we're in now - bool m_slow; // Making slow file + const bool m_slow; // Making slow file int m_enumNum = 0; // Enumeration number (whole netlist) - int m_baseCode = -1; // Code of first AstTraceInc in this function // METHODS void newOutCFile(int filenum) { @@ -703,10 +701,6 @@ class EmitCTrace final : EmitCFunc { m_ofp->puts("// DESCR" "IPTION: Verilator output: Tracing implementation internals\n"); - emitTraceHeader(); - } - - void emitTraceHeader() { // Includes puts("#include \"" + v3Global.opt.traceSourceLang() + ".h\"\n"); puts("#include \"" + symClassName() + ".h\"\n"); @@ -895,12 +889,13 @@ class EmitCTrace final : EmitCFunc { const uint32_t offset = (arrayindex < 0) ? 0 : (arrayindex * nodep->declp()->widthWords()); const uint32_t code = nodep->declp()->code() + offset; puts(v3Global.opt.trueTraceThreads() && !nodep->full() ? "(base+" : "(oldp+"); - puts(cvtToStr(code - m_baseCode)); + puts(cvtToStr(code - nodep->baseCode())); puts(","); emitTraceValue(nodep, arrayindex); if (emitWidth) puts("," + cvtToStr(nodep->declp()->widthMin())); puts(");\n"); } + void emitTraceValue(AstTraceInc* nodep, int arrayindex) { if (AstVarRef* const varrefp = VN_CAST(nodep->valuep(), VarRef)) { AstVar* varp = varrefp->varp(); @@ -939,92 +934,20 @@ class EmitCTrace final : EmitCFunc { // VISITORS using EmitCFunc::visit; // Suppress hidden overloaded virtual function warning - virtual void visit(AstNetlist* nodep) override { - // Top module only - iterate(nodep->topModulep()); - } - virtual void visit(AstNodeModule* nodep) override { - m_modp = nodep; - iterateChildren(nodep); - m_modp = nullptr; - } virtual void visit(AstCFunc* nodep) override { + if (!nodep->isTrace()) return; if (nodep->slow() != m_slow) return; - VL_RESTORER(m_cfuncp); - VL_RESTORER(m_useSelfForThis); - if (nodep->funcType().isTrace()) { // TRACE_* - m_cfuncp = nodep; - if (splitNeeded()) { - // Splitting file, so using parallel build. - v3Global.useParallelBuild(true); - // Close old file - VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr); - // Open a new file - newOutCFile(splitFilenumInc()); - } - - splitSizeInc(nodep); - - puts("\n"); - m_lazyDecls.emit(nodep); - emitCFuncHeader(nodep, m_modp, /* withScope: */ true); - puts(" {\n"); - - if (nodep->isLoose()) { - m_lazyDecls.declared(nodep); // Defined here, so no longer needs declaration - if (!nodep->isStatic()) { // Standard prologue - puts("if (false && vlSelf) {} // Prevent unused\n"); - m_useSelfForThis = true; - puts(symClassAssign()); - } - } - - if (nodep->initsp()) { - string section; - emitVarList(nodep->initsp(), EVL_FUNC_ALL, "", section /*ref*/); - iterateAndNextNull(nodep->initsp()); - } - - m_baseCode = -1; - - if (nodep->funcType() == AstCFuncType::TRACE_CHANGE_SUB) { - const AstNode* const stmtp = nodep->stmtsp(); - const AstIf* const ifp = VN_CAST_CONST(stmtp, If); - const AstTraceInc* const tracep - = VN_CAST_CONST(ifp ? ifp->ifsp() : stmtp, TraceInc); - // On rare occasions we can end up with an empty sub function - m_baseCode = tracep ? tracep->declp()->code() : 0; - if (v3Global.opt.trueTraceThreads()) { - puts("const vluint32_t base = vlSymsp->__Vm_baseCode + " + cvtToStr(m_baseCode) - + ";\n"); - puts("if (false && tracep && base) {} // Prevent unused\n"); - } else { - puts("vluint32_t* const oldp = tracep->oldp(vlSymsp->__Vm_baseCode + " - + cvtToStr(m_baseCode) + ");\n"); - puts("if (false && oldp) {} // Prevent unused\n"); - } - } else if (nodep->funcType() == AstCFuncType::TRACE_FULL_SUB) { - m_baseCode = 0; - puts("vluint32_t* const oldp = tracep->oldp(vlSymsp->__Vm_baseCode);\n"); - puts("if (false && oldp) {} // Prevent unused\n"); - } else if (nodep->funcType() == AstCFuncType::TRACE_INIT_SUB) { - puts("const int c = vlSymsp->__Vm_baseCode;\n"); - puts("if (false && tracep && c) {} // Prevent unused\n"); - } - - if (nodep->stmtsp()) { - putsDecoration("// Body\n"); - puts("{\n"); - iterateAndNextNull(nodep->stmtsp()); - puts("}\n"); - } - if (nodep->finalsp()) { - putsDecoration("// Final\n"); - iterateAndNextNull(nodep->finalsp()); - } - puts("}\n"); + if (splitNeeded()) { + // Splitting file, so using parallel build. + v3Global.useParallelBuild(true); + // Close old file + VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr); + // Open a new file + newOutCFile(splitFilenumInc()); } + + EmitCFunc::visit(nodep); } virtual void visit(AstTraceDecl* nodep) override { const int enumNum = emitTraceDeclDType(nodep->dtypep()); @@ -1047,28 +970,32 @@ class EmitCTrace final : EmitCFunc { emitTraceChangeOne(nodep, -1); } } - virtual void visit(AstCoverDecl* nodep) override {} - virtual void visit(AstCoverInc* nodep) override {} -public: - explicit EmitCTrace(bool slow) - : m_slow{slow} {} - virtual ~EmitCTrace() override = default; - void main() { - // Put out the file + explicit EmitCTrace(AstNodeModule* modp, bool slow) + : m_slow{slow} { + m_modp = modp; + // Open output file newOutCFile(0); - - iterate(v3Global.rootp()); - + // Emit functions + for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { + if (AstCFunc* const funcp = VN_CAST(nodep, CFunc)) { iterate(funcp); } + } + // Close output file VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr); } + virtual ~EmitCTrace() override = default; + +public: + static void main(AstNodeModule* modp, bool slow) { EmitCTrace(modp, slow); } }; //###################################################################### // EmitC class functions -static void setParentClassPointers() { +void V3EmitC::emitc() { + UINFO(2, __FUNCTION__ << ": " << endl); // Set user4p in all CFunc and Var to point to the containing AstNodeModule + AstUser4InUse user4InUse; const auto setAll = [](AstNodeModule* modp) -> void { for (AstNode* nodep = VN_CAST(modp, NodeModule)->stmtsp(); nodep; nodep = nodep->nextp()) { if (VN_IS(nodep, CFunc) || VN_IS(nodep, Var)) nodep->user4p(modp); @@ -1078,13 +1005,7 @@ static void setParentClassPointers() { setAll(VN_CAST(modp, NodeModule)); } setAll(v3Global.rootp()->constPoolp()->modp()); -} -void V3EmitC::emitc() { - UINFO(2, __FUNCTION__ << ": " << endl); - // Set user4 to parent module - AstUser4InUse user4InUse; - setParentClassPointers(); // Process each module in turn for (AstNodeModule* nodep = v3Global.rootp()->modulesp(); nodep; nodep = VN_CAST(nodep->nextp(), NodeModule)) { @@ -1099,22 +1020,11 @@ void V3EmitC::emitc() { fast.mainImp(nodep, false); } } -} -void V3EmitC::emitcTrace() { - UINFO(2, __FUNCTION__ << ": " << endl); + // Emit trace routines (currently they can only exist in the top module) if (v3Global.opt.trace()) { - // Set user4 to parent module - AstUser4InUse user4InUse; - setParentClassPointers(); - { - EmitCTrace slow(true); - slow.main(); - } - { - EmitCTrace fast(false); - fast.main(); - } + EmitCTrace::main(v3Global.rootp()->topModulep(), /* slow: */ true); + EmitCTrace::main(v3Global.rootp()->topModulep(), /* slow: */ false); } } diff --git a/src/V3EmitC.h b/src/V3EmitC.h index 406a00429..e52552734 100644 --- a/src/V3EmitC.h +++ b/src/V3EmitC.h @@ -32,7 +32,6 @@ public: static void emitcInlines(); static void emitcModel(); static void emitcSyms(bool dpiHdrOnly = false); - static void emitcTrace(); static void emitcFiles(); }; diff --git a/src/V3EmitCFunc.h b/src/V3EmitCFunc.h index bee0293a4..38f18a00e 100644 --- a/src/V3EmitCFunc.h +++ b/src/V3EmitCFunc.h @@ -1223,8 +1223,6 @@ public: virtual void visit(AstCell*) override {} // Handled outside the Visit class virtual void visit(AstVar*) override {} // Handled outside the Visit class virtual void visit(AstNodeText*) override {} // Handled outside the Visit class - virtual void visit(AstTraceDecl*) override {} // Handled outside the Visit class - virtual void visit(AstTraceInc*) override {} // Handled outside the Visit class virtual void visit(AstCFile*) override {} // Handled outside the Visit class virtual void visit(AstCellInline*) override {} // Handled outside visit (in EmitCSyms) virtual void visit(AstCUse*) override {} // Handled outside the Visit class diff --git a/src/V3Trace.cpp b/src/V3Trace.cpp index 062cd2ea1..a5901d1e8 100644 --- a/src/V3Trace.cpp +++ b/src/V3Trace.cpp @@ -171,6 +171,7 @@ private: AstNodeModule* m_topModp = nullptr; // Module to add variables to AstScope* m_topScopep = nullptr; // Scope to add variables to AstCFunc* m_cfuncp = nullptr; // C function adding to graph + AstCFunc* m_regFuncp = nullptr; // Trace registration function AstTraceDecl* m_tracep = nullptr; // Trace function adding to graph AstVarScope* m_activityVscp = nullptr; // Activity variable uint32_t m_activityNumber = 0; // Count of fields in activity variable @@ -472,69 +473,77 @@ private: } } - AstCFunc* newCFunc(AstCFuncType type, AstCFunc* callfromp, AstCFunc* regp, int& funcNump) { + AstCFunc* newCFunc(bool full, AstCFunc* topFuncp, int& funcNump, uint32_t baseCode = 0) { // Create new function - string name; - switch (type) { - case AstCFuncType::TRACE_FULL: name = "trace_full_top_"; break; - case AstCFuncType::TRACE_FULL_SUB: name = "trace_full_sub_"; break; - case AstCFuncType::TRACE_CHANGE: name = "trace_chg_top_"; break; - case AstCFuncType::TRACE_CHANGE_SUB: name = "trace_chg_sub_"; break; - default: m_topScopep->v3fatalSrc("Bad trace function type"); - } - name += cvtToStr(funcNump++); - FileLine* const flp = m_topScopep->fileline(); - AstCFunc* const funcp = new AstCFunc(flp, name, m_topScopep); - funcp->funcType(type); - funcp->dontCombine(true); - const bool isTopFunc - = type == AstCFuncType::TRACE_FULL || type == AstCFuncType::TRACE_CHANGE; - if (isTopFunc) { - funcp->argTypes("void* voidSelf, " + v3Global.opt.traceClassBase() + "* tracep"); - funcp->isStatic(true); - funcp->addInitsp(new AstCStmt( - flp, prefixNameProtect(m_topModp) + "* const __restrict vlSelf = static_cast<" - + prefixNameProtect(m_topModp) + "*>(voidSelf);\n")); - funcp->addInitsp(new AstCStmt(flp, symClassAssign())); + const bool isTopFunc = topFuncp == nullptr; + const string baseName = full && isTopFunc ? "trace_full_top_" + : full ? "trace_full_sub_" + : isTopFunc ? "trace_chg_top_" + : "trace_chg_sub_"; - } else { - funcp->argTypes(v3Global.opt.traceClassBase() + "* tracep"); - funcp->isStatic(false); - } + FileLine* const flp = m_topScopep->fileline(); + AstCFunc* const funcp = new AstCFunc(flp, baseName + cvtToStr(funcNump++), m_topScopep); + funcp->isTrace(true); + funcp->dontCombine(true); funcp->isLoose(true); - funcp->slow(type == AstCFuncType::TRACE_FULL || type == AstCFuncType::TRACE_FULL_SUB); + funcp->slow(full); + funcp->isStatic(isTopFunc); // Add it to top scope m_topScopep->addActivep(funcp); - // Add call to new function - if (callfromp) { + const auto addInitStr = [funcp, flp](const string& str) -> void { + funcp->addInitsp(new AstCStmt(flp, str)); + }; + if (isTopFunc) { + // Top functions + funcp->argTypes("void* voidSelf, " + v3Global.opt.traceClassBase() + "* tracep"); + addInitStr(voidSelfAssign(m_topModp)); + addInitStr(symClassAssign()); + // Add global activity check to change dump functions + if (!full) { // + addInitStr("if (VL_UNLIKELY(!vlSymsp->__Vm_activity)) return;\n"); + } + // Register function + if (full) { + m_regFuncp->addStmtsp(new AstText(flp, "tracep->addFullCb(", true)); + } else { + m_regFuncp->addStmtsp(new AstText(flp, "tracep->addChgCb(", true)); + } + m_regFuncp->addStmtsp(new AstAddrOfCFunc(flp, funcp)); + m_regFuncp->addStmtsp(new AstText(flp, ", vlSelf);\n", true)); + } else { + // Sub functions + funcp->argTypes(v3Global.opt.traceClassBase() + "* tracep"); + // Setup base references. Note in rare occasions we can end up with an empty trace + // sub function, hence the VL_ATTR_UNUSED attributes. + if (full) { + // Full dump sub function + addInitStr("vluint32_t* const oldp VL_ATTR_UNUSED = " + "tracep->oldp(vlSymsp->__Vm_baseCode);\n"); + } else { + // Change dump sub function + if (v3Global.opt.trueTraceThreads()) { + addInitStr("const vluint32_t base VL_ATTR_UNUSED = " + "vlSymsp->__Vm_baseCode + " + + cvtToStr(baseCode) + ";\n"); + addInitStr("if (false && tracep) {} // Prevent unused\n"); + } else { + addInitStr("vluint32_t* const oldp VL_ATTR_UNUSED = " + "tracep->oldp(vlSymsp->__Vm_baseCode + " + + cvtToStr(baseCode) + ");\n"); + } + } + // Add call to top function AstCCall* callp = new AstCCall(funcp->fileline(), funcp); callp->argTypes("tracep"); - callfromp->addStmtsp(callp); - } - // Register function - if (regp) { - if (type == AstCFuncType::TRACE_FULL) { - regp->addStmtsp(new AstText(flp, "tracep->addFullCb(", true)); - } else if (type == AstCFuncType::TRACE_CHANGE) { - regp->addStmtsp(new AstText(flp, "tracep->addChgCb(", true)); - } else { - funcp->v3fatalSrc("Don't know how to register this type of function"); - } - regp->addStmtsp(new AstAddrOfCFunc(flp, funcp)); - regp->addStmtsp(new AstText(flp, ", vlSelf);\n", true)); - } - // Add global activity check to TRACE_CHANGE functions - if (type == AstCFuncType::TRACE_CHANGE) { - funcp->addInitsp( - new AstCStmt(flp, string("if (VL_UNLIKELY(!vlSymsp->__Vm_activity)) return;\n"))); + topFuncp->addStmtsp(callp); } // Done UINFO(5, " newCFunc " << funcp << endl); return funcp; } - void createFullTraceFunction(const TraceVec& traces, uint32_t nAllCodes, uint32_t parallelism, - AstCFunc* regFuncp) { + void createFullTraceFunction(const TraceVec& traces, uint32_t nAllCodes, + uint32_t parallelism) { const int splitLimit = v3Global.opt.outputSplitCTrace() ? v3Global.opt.outputSplitCTrace() : std::numeric_limits::max(); @@ -571,20 +580,17 @@ private: ++m_statUniqSigs; // Create top function if not yet created - if (!topFuncp) { - topFuncp - = newCFunc(AstCFuncType::TRACE_FULL, nullptr, regFuncp, topFuncNum); - } + if (!topFuncp) { topFuncp = newCFunc(/* full: */ true, nullptr, topFuncNum); } // Crate new sub function if required if (!subFuncp || subStmts > splitLimit) { subStmts = 0; - subFuncp = newCFunc(AstCFuncType::TRACE_FULL_SUB, topFuncp, nullptr, - subFuncNum); + subFuncp = newCFunc(/* full: */ true, topFuncp, subFuncNum); } // Add TraceInc node - AstTraceInc* const incp = new AstTraceInc(declp->fileline(), declp, true); + AstTraceInc* const incp + = new AstTraceInc(declp->fileline(), declp, /* full: */ true); subFuncp->addStmtsp(incp); subStmts += EmitCBaseCounterVisitor(incp).count(); @@ -599,8 +605,8 @@ private: } } - void createChgTraceFunctions(const TraceVec& traces, uint32_t nAllCodes, uint32_t parallelism, - AstCFunc* regFuncp) { + void createChgTraceFunctions(const TraceVec& traces, uint32_t nAllCodes, + uint32_t parallelism) { const int splitLimit = v3Global.opt.outputSplitCTrace() ? v3Global.opt.outputSplitCTrace() : std::numeric_limits::max(); int topFuncNum = 0; @@ -615,6 +621,7 @@ private: uint32_t nCodes = 0; const ActCodeSet* prevActSet = nullptr; AstIf* ifp = nullptr; + uint32_t baseCode = 0; for (; nCodes < maxCodes && it != traces.end(); ++it) { const TraceTraceVertex* const vtxp = it->second; // This is a duplicate decl, no need to add it to incremental dump @@ -623,16 +630,16 @@ private: // Traced value never changes, no need to add it to incremental dump if (actSet.count(TraceActivityVertex::ACTIVITY_NEVER)) continue; - // Create top function if not yet created - if (!topFuncp) { - topFuncp = newCFunc(AstCFuncType::TRACE_CHANGE, nullptr, regFuncp, topFuncNum); - } + AstTraceDecl* const declp = vtxp->nodep(); - // Crate new sub function if required + // Create top function if not yet created + if (!topFuncp) { topFuncp = newCFunc(/* full: */ false, nullptr, topFuncNum); } + + // Create new sub function if required if (!subFuncp || subStmts > splitLimit) { + baseCode = declp->code(); subStmts = 0; - subFuncp - = newCFunc(AstCFuncType::TRACE_CHANGE_SUB, topFuncp, nullptr, subFuncNum); + subFuncp = newCFunc(/* full: */ false, topFuncp, subFuncNum, baseCode); prevActSet = nullptr; ifp = nullptr; } @@ -658,8 +665,8 @@ private: } // Add TraceInc node - AstTraceDecl* const declp = vtxp->nodep(); - AstTraceInc* const incp = new AstTraceInc(declp->fileline(), declp, VAccess::READ); + AstTraceInc* const incp + = new AstTraceInc(declp->fileline(), declp, /* full: */ false, baseCode); ifp->addIfsp(incp); subStmts += EmitCBaseCounterVisitor(incp).count(); @@ -673,25 +680,23 @@ private: } } - void createCleanupFunction(AstCFunc* regFuncp) { + void createCleanupFunction() { FileLine* const fl = m_topScopep->fileline(); AstCFunc* const cleanupFuncp = new AstCFunc(fl, "trace_cleanup", m_topScopep); cleanupFuncp->argTypes("void* voidSelf, " + v3Global.opt.traceClassBase() + "* /*unused*/"); - cleanupFuncp->funcType(AstCFuncType::TRACE_CLEANUP); + cleanupFuncp->isTrace(true); cleanupFuncp->slow(false); cleanupFuncp->isStatic(true); cleanupFuncp->isLoose(true); m_topScopep->addActivep(cleanupFuncp); - cleanupFuncp->addInitsp(new AstCStmt( - fl, prefixNameProtect(m_topModp) + "* const __restrict vlSelf = static_cast<" - + prefixNameProtect(m_topModp) + "*>(voidSelf);\n")); + cleanupFuncp->addInitsp(new AstCStmt(fl, voidSelfAssign(m_topModp))); cleanupFuncp->addInitsp(new AstCStmt(fl, symClassAssign())); // Register it - regFuncp->addStmtsp(new AstText(fl, "tracep->addCleanupCb(", true)); - regFuncp->addStmtsp(new AstAddrOfCFunc(fl, cleanupFuncp)); - regFuncp->addStmtsp(new AstText(fl, ", vlSelf);\n", true)); + m_regFuncp->addStmtsp(new AstText(fl, "tracep->addCleanupCb(", true)); + m_regFuncp->addStmtsp(new AstAddrOfCFunc(fl, cleanupFuncp)); + m_regFuncp->addStmtsp(new AstText(fl, ", vlSelf);\n", true)); // Clear global activity flag cleanupFuncp->addStmtsp( @@ -735,22 +740,21 @@ private: // last value vector is more compact // Create the trace registration function - AstCFunc* const regFuncp - = new AstCFunc(m_topScopep->fileline(), "trace_register", m_topScopep); - regFuncp->argTypes(v3Global.opt.traceClassBase() + "* tracep"); - regFuncp->funcType(AstCFuncType::TRACE_REGISTER); - regFuncp->slow(true); - regFuncp->isStatic(false); - regFuncp->isLoose(true); - m_topScopep->addActivep(regFuncp); + m_regFuncp = new AstCFunc(m_topScopep->fileline(), "trace_register", m_topScopep); + m_regFuncp->argTypes(v3Global.opt.traceClassBase() + "* tracep"); + m_regFuncp->isTrace(true); + m_regFuncp->slow(true); + m_regFuncp->isStatic(false); + m_regFuncp->isLoose(true); + m_topScopep->addActivep(m_regFuncp); const int parallelism = 1; // Note: will bump this later, code below works for any value // Create the full dump functions, also allocates signal numbers - createFullTraceFunction(traces, nFullCodes, parallelism, regFuncp); + createFullTraceFunction(traces, nFullCodes, parallelism); // Create the incremental dump functions - createChgTraceFunctions(traces, nChgCodes, parallelism, regFuncp); + createChgTraceFunctions(traces, nChgCodes, parallelism); // Remove refs to traced values from TraceDecl nodes, these have now moved under // TraceInc @@ -761,7 +765,7 @@ private: } // Create the trace cleanup function clearing the activity flags - createCleanupFunction(regFuncp); + createCleanupFunction(); } TraceCFuncVertex* getCFuncVertexp(AstCFunc* nodep) { diff --git a/src/V3TraceDecl.cpp b/src/V3TraceDecl.cpp index b7f61a01f..07a62629d 100644 --- a/src/V3TraceDecl.cpp +++ b/src/V3TraceDecl.cpp @@ -69,13 +69,13 @@ private: return nullptr; } - AstCFunc* newCFunc(AstCFuncType type, const string& name) { + AstCFunc* newCFunc(const string& name) { FileLine* const flp = m_topScopep->fileline(); AstCFunc* const funcp = new AstCFunc(flp, name, m_topScopep); string argTypes = v3Global.opt.traceClassBase() + "* tracep"; if (m_interface) argTypes += ", int scopet, const char* scopep"; funcp->argTypes(argTypes); - funcp->funcType(type); + funcp->isTrace(true); funcp->isStatic(false); funcp->isLoose(true); funcp->slow(true); @@ -94,8 +94,11 @@ private: basep->addStmtsp(callp); } AstCFunc* newCFuncSub(AstCFunc* basep) { + FileLine* const fl = basep->fileline(); const string name = "trace_init_sub_" + cvtToStr(m_funcNum++); - AstCFunc* const funcp = newCFunc(AstCFuncType::TRACE_INIT_SUB, name); + AstCFunc* const funcp = newCFunc(name); + funcp->addInitsp(new AstCStmt(fl, "const int c = vlSymsp->__Vm_baseCode;\n")); + funcp->addInitsp(new AstCStmt(fl, "if (false && tracep && c) {} // Prevent unused\n")); if (!m_interface) callCFuncSub(basep, funcp, nullptr); return funcp; } @@ -135,7 +138,7 @@ private: virtual void visit(AstTopScope* nodep) override { m_topScopep = nodep->scopep(); // Create the trace init function - m_initFuncp = newCFunc(AstCFuncType::TRACE_INIT, "trace_init_top"); + m_initFuncp = newCFunc("trace_init_top"); // Create initial sub function m_initSubFuncp = newCFuncSub(m_initFuncp); // And find variables diff --git a/src/Verilator.cpp b/src/Verilator.cpp index 9a742cafd..842fa04a9 100644 --- a/src/Verilator.cpp +++ b/src/Verilator.cpp @@ -512,7 +512,6 @@ static void process() { V3EmitC::emitcSyms(); V3EmitC::emitcConstPool(); V3EmitC::emitcModel(); - V3EmitC::emitcTrace(); } else if (v3Global.opt.dpiHdrOnly()) { V3EmitC::emitcSyms(true); } From a4f5d95648530974e3e6c6f8da49dc6338b9c63a Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Thu, 8 Jul 2021 13:42:25 +0100 Subject: [PATCH 04/84] Fix -G to treat simple integer literals as signed (#3060) The -G option now correctly parses simple integer literals as signed numbers, which is in line with the standard and is significant when overriding parameters without a type specifier. Fixes #3060 --- Changes | 2 ++ src/V3AstNodes.cpp | 2 +- test_regress/t/t_flag_parameter.v | 7 +++++++ test_regress/t/t_flag_parameter.vc | 2 ++ 4 files changed, 12 insertions(+), 1 deletion(-) diff --git a/Changes b/Changes index 4ce4d40f5..ad852bf44 100644 --- a/Changes +++ b/Changes @@ -13,6 +13,8 @@ Verilator 4.211 devel **Minor:** +* Fix -G to treat simple integer literals as signed (#3060). [Anikin1610] + Verilator 4.210 2021-07-07 ========================== diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index d878aa615..4ece401fd 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -267,7 +267,7 @@ AstConst* AstConst::parseParamLiteral(FileLine* fl, const string& literal) { char* endp; int v = strtol(literal.c_str(), &endp, 0); if ((v != 0) && (endp[0] == 0)) { // C literal - return new AstConst(fl, AstConst::WidthedValue(), 32, v); + return new AstConst(fl, AstConst::Signed32(), v); } else { // Try a Verilog literal (fatals if not) return new AstConst(fl, AstConst::StringToParse(), literal.c_str()); } diff --git a/test_regress/t/t_flag_parameter.v b/test_regress/t/t_flag_parameter.v index 33f1c102e..4fe3ad8ee 100644 --- a/test_regress/t/t_flag_parameter.v +++ b/test_regress/t/t_flag_parameter.v @@ -53,6 +53,8 @@ module t; parameter int52 = 1; parameter int61 = 1; parameter int62 = 1; + parameter int71 = 1; + parameter int72 = 1; initial begin `check(string1,"New String"); @@ -83,6 +85,11 @@ module t; `check(int52,32'hdeadbeef); `check(int61,32'hdeadbeef); `check(int62,32'hdeadbeef); + `check(int71,-1000); + `check(int72,-1000); + + // Check parameter assigned simple integer literal is signed + if ((int11 << 27) >>> 31 != -1) $stop; $write("*-* All Finished *-*\n"); $finish; diff --git a/test_regress/t/t_flag_parameter.vc b/test_regress/t/t_flag_parameter.vc index 955540440..35687e39e 100644 --- a/test_regress/t/t_flag_parameter.vc +++ b/test_regress/t/t_flag_parameter.vc @@ -26,3 +26,5 @@ -pvalue+int52=32\'hdead_beef -Gint61="32'hdead_beef" -pvalue+int62="32'hdead_beef" +-Gint71=-1000 +-pvalue+int72=-1000 From e6b75f752bd9a0f744ca66a9f82f07b31f4140e9 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Fri, 9 Jul 2021 17:46:06 -0400 Subject: [PATCH 05/84] Commentary --- README.rst | 3 +++ docs/guide/install.rst | 2 ++ 2 files changed, 5 insertions(+) diff --git a/README.rst b/README.rst index d13cd58ba..033567427 100644 --- a/README.rst +++ b/README.rst @@ -5,6 +5,8 @@ :target: https://www.gnu.org/licenses/lgpl-3.0] .. image:: https://img.shields.io/badge/License-Artistic%202.0-0298c3.svg :target: https://opensource.org/licenses/Artistic-2.0 +.. image:: https://repology.org/badge/tiny-repos/verilator.svg?header=distro%20packages + :target: https://repology.org/project/verilator/versions .. image:: https://api.codacy.com/project/badge/Grade/fa78caa433c84a4ab9049c43e9debc6f :target: https://www.codacy.com/gh/verilator/verilator .. image:: https://codecov.io/gh/verilator/verilator/branch/master/graph/badge.svg @@ -12,6 +14,7 @@ .. image:: https://github.com/verilator/verilator/workflows/build/badge.svg :target: https://github.com/verilator/verilator/actions?query=workflow%3Abuild + Welcome to Verilator ==================== diff --git a/docs/guide/install.rst b/docs/guide/install.rst index 5f2c50b12..ab075cf7d 100644 --- a/docs/guide/install.rst +++ b/docs/guide/install.rst @@ -23,6 +23,8 @@ package: apt-get install verilator # On Ubuntu +For other distributions refer to `Repology Verilator Distro Packages +`__. .. _Git Install: From 61e2e55ba58a80cd8f897aec53bf3e5fc2cf8485 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Fri, 9 Jul 2021 18:11:59 -0400 Subject: [PATCH 06/84] Internals: Fix coverage holes. No functional change. --- src/V3Options.cpp | 17 +------------ src/V3Options.h | 1 - src/VlcMain.cpp | 2 +- .../t/{t_math_msvc_64.v => t_flag_compiler.v} | 0 test_regress/t/t_flag_compiler_clang.pl | 24 +++++++++++++++++++ test_regress/t/t_flag_compiler_gcc.pl | 24 +++++++++++++++++++ ...ath_msvc_64.pl => t_flag_compiler_msvc.pl} | 2 ++ test_regress/t/t_flag_j_bad.out | 2 ++ test_regress/t/t_flag_j_bad.pl | 22 +++++++++++++++++ test_regress/t/t_flag_make_bad.out | 1 + test_regress/t/t_flag_make_bad.pl | 20 ++++++++++++++++ test_regress/t/t_flag_make_gmake.pl | 21 ++++++++++++++++ 12 files changed, 118 insertions(+), 18 deletions(-) rename test_regress/t/{t_math_msvc_64.v => t_flag_compiler.v} (100%) create mode 100755 test_regress/t/t_flag_compiler_clang.pl create mode 100755 test_regress/t/t_flag_compiler_gcc.pl rename test_regress/t/{t_math_msvc_64.pl => t_flag_compiler_msvc.pl} (94%) create mode 100644 test_regress/t/t_flag_j_bad.out create mode 100755 test_regress/t/t_flag_j_bad.pl create mode 100644 test_regress/t/t_flag_make_bad.out create mode 100755 test_regress/t/t_flag_make_bad.pl create mode 100755 test_regress/t/t_flag_make_gmake.pl diff --git a/src/V3Options.cpp b/src/V3Options.cpp index 3710dfd3a..f5e141701 100644 --- a/src/V3Options.cpp +++ b/src/V3Options.cpp @@ -1467,7 +1467,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char i += consumed; } else { fl->v3fatal("Invalid option: " << argv[i] << parser.getSuggestion(argv[i])); - ++i; + ++i; // LCOV_EXCL_LINE } } else { // Filename @@ -1643,21 +1643,6 @@ string V3Options::parseFileArg(const string& optdir, const string& relfilename) //====================================================================== -//! Utility to see if we have a language extension argument and if so add it. -bool V3Options::parseLangExt(const char* swp, //!< argument text - const char* langswp, //!< option to match - const V3LangCode& lc) { //!< language code - const int len = strlen(langswp); - if (!strncmp(swp, langswp, len)) { - addLangExt(swp + len, lc); - return true; - } else { - return false; - } -} - -//====================================================================== - void V3Options::showVersion(bool verbose) { cout << version(); cout << endl; diff --git a/src/V3Options.h b/src/V3Options.h index b8b2c7082..3668fe605 100644 --- a/src/V3Options.h +++ b/src/V3Options.h @@ -376,7 +376,6 @@ private: void coverage(bool flag) { m_coverageLine = m_coverageToggle = m_coverageUser = flag; } static bool suffixed(const string& sw, const char* arg); static string parseFileArg(const string& optdir, const string& relfilename); - bool parseLangExt(const char* swp, const char* langswp, const V3LangCode& lc); string filePathCheckOneDir(const string& modname, const string& dirname); static int stripOptionsForChildRun(const string& opt, bool forTop); diff --git a/src/VlcMain.cpp b/src/VlcMain.cpp index f29fef2c4..f8c57d7c8 100644 --- a/src/VlcMain.cpp +++ b/src/VlcMain.cpp @@ -83,7 +83,7 @@ void VlcOptions::parseOptsList(int argc, char** argv) { i += consumed; } else { v3fatal("Invalid option: " << argv[i] << parser.getSuggestion(argv[i])); - ++i; + ++i; // LCOV_EXCL_LINE } } else { addReadFile(argv[i]); diff --git a/test_regress/t/t_math_msvc_64.v b/test_regress/t/t_flag_compiler.v similarity index 100% rename from test_regress/t/t_math_msvc_64.v rename to test_regress/t/t_flag_compiler.v diff --git a/test_regress/t/t_flag_compiler_clang.pl b/test_regress/t/t_flag_compiler_clang.pl new file mode 100755 index 000000000..edd331316 --- /dev/null +++ b/test_regress/t/t_flag_compiler_clang.pl @@ -0,0 +1,24 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 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 + +scenarios(simulator => 1); + +top_filename("t/t_flag_compiler.v"); + +compile( + verilator_flags2 => ["--compiler clang"], + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_flag_compiler_gcc.pl b/test_regress/t/t_flag_compiler_gcc.pl new file mode 100755 index 000000000..faaedc67a --- /dev/null +++ b/test_regress/t/t_flag_compiler_gcc.pl @@ -0,0 +1,24 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 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 + +scenarios(simulator => 1); + +top_filename("t/t_flag_compiler.v"); + +compile( + verilator_flags2 => ["--compiler gcc"], + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_math_msvc_64.pl b/test_regress/t/t_flag_compiler_msvc.pl similarity index 94% rename from test_regress/t/t_math_msvc_64.pl rename to test_regress/t/t_flag_compiler_msvc.pl index 2769c27e2..01c70152c 100755 --- a/test_regress/t/t_math_msvc_64.pl +++ b/test_regress/t/t_flag_compiler_msvc.pl @@ -10,6 +10,8 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); +top_filename("t/t_flag_compiler.v"); + compile( verilator_flags2 => ["--compiler msvc"], # Bug requires msvc ); diff --git a/test_regress/t/t_flag_j_bad.out b/test_regress/t/t_flag_j_bad.out new file mode 100644 index 000000000..2fe652c5d --- /dev/null +++ b/test_regress/t/t_flag_j_bad.out @@ -0,0 +1,2 @@ +%Error: -j accepts positive integer, but '0' is passed +%Error: Exiting due to diff --git a/test_regress/t/t_flag_j_bad.pl b/test_regress/t/t_flag_j_bad.pl new file mode 100755 index 000000000..ee3d0fdc8 --- /dev/null +++ b/test_regress/t/t_flag_j_bad.pl @@ -0,0 +1,22 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 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 + +scenarios(vlt => 1); + +top_filename("t/t_flag_werror.v"); + +lint( + fails => 1, + verilator_flags => [qw(-j 0 --build)], + expect_filename => $Self->{golden_filename}, + ); + +ok(1); +1; diff --git a/test_regress/t/t_flag_make_bad.out b/test_regress/t/t_flag_make_bad.out new file mode 100644 index 000000000..d3a472dd3 --- /dev/null +++ b/test_regress/t/t_flag_make_bad.out @@ -0,0 +1 @@ +%Error: Unknown --make system specified: 'bad_one' diff --git a/test_regress/t/t_flag_make_bad.pl b/test_regress/t/t_flag_make_bad.pl new file mode 100755 index 000000000..1c6f23aec --- /dev/null +++ b/test_regress/t/t_flag_make_bad.pl @@ -0,0 +1,20 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 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 + +scenarios(vlt => 1); + +lint( + verilator_flags2 => ["--make bad_one"], + fails => 1, + expect_filename => $Self->{golden_filename}, + ); + +ok(1); +1; diff --git a/test_regress/t/t_flag_make_gmake.pl b/test_regress/t/t_flag_make_gmake.pl new file mode 100755 index 000000000..e5caca9d8 --- /dev/null +++ b/test_regress/t/t_flag_make_gmake.pl @@ -0,0 +1,21 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2008 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 + + +scenarios(simulator => 1); + +top_filename("t/t_flag_make_cmake.v"); + +compile( + verilator_flags2 => ['--make gmake'], + ); + +ok(1); +1; From 825d6b87a82414ea9c942b76706b38b3e789ca0d Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Sat, 10 Jul 2021 08:01:58 +0100 Subject: [PATCH 07/84] Internals: Put AstNode flags in padding gap, initialize spare bits The bool AstNode flags fit in a padding gap after m_type. This reduces memory consumption by about 2% on OpenTitan. Initializing the unused bits in the flags then avoids a read-modify-write in the constructor (replacing it with a store constant). Overall verilation speed is about 1% faster. --- src/V3Ast.cpp | 7 ++++--- src/V3Ast.h | 38 ++++++++++++++++++++++++-------------- 2 files changed, 28 insertions(+), 17 deletions(-) diff --git a/src/V3Ast.cpp b/src/V3Ast.cpp index e4d36cb60..1f87e4586 100644 --- a/src/V3Ast.cpp +++ b/src/V3Ast.cpp @@ -74,9 +74,10 @@ AstNode::AstNode(AstType t, FileLine* fl) m_clonep = nullptr; m_cloneCnt = 0; // Attributes - m_didWidth = false; - m_doingWidth = false; - m_protect = true; + m_flags.didWidth = false; + m_flags.doingWidth = false; + m_flags.protect = true; + m_flags.unused = 0; // Initializing this avoids a read-modify-write on construction m_user1u = VNUser(0); m_user1Cnt = 0; m_user2u = VNUser(0); diff --git a/src/V3Ast.h b/src/V3Ast.h index b569d76a9..1bceac0b6 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -1334,8 +1334,24 @@ class AstNode VL_NOT_FINAL { const AstType m_type; // Node sub-type identifier // ^ ASTNODE_PREFETCH depends on above ordering of members - // padding - 2 extra bytes here after m_type - int m_cloneCnt; // Mark of when userp was set + // AstType is 2 bytes, so we can stick another 6 bytes after it to utilize what would + // otherwise be padding (on a 64-bit system). We stick the attribute flags and the clone + // count here. + + struct { + bool didWidth : 1; // Did V3Width computation + bool doingWidth : 1; // Inside V3Width + bool protect : 1; // Protect name if protection is on + uint16_t unused : 13; // Space for more flags here (there must be 16 bits in total) + } m_flags; // Attribute flags + + int m_cloneCnt; // Sequence number for when last clone was made + +#if defined(__x86_64__) && defined(__gnu_linux__) + // Only assert this on known platforms, as it only affects performance, not correctness + static_assert(sizeof(m_type) + sizeof(m_flags) + sizeof(m_cloneCnt) <= sizeof(void*), + "packing requires padding"); +#endif AstNodeDType* m_dtypep; // Data type of output or assignment (etc) AstNode* m_headtailp; // When at begin/end of list, the opposite end of the list @@ -1348,12 +1364,6 @@ class AstNode VL_NOT_FINAL { AstNode* m_clonep; // Pointer to clone of/ source of this module (for *LAST* cloneTree() ONLY) static int s_cloneCntGbl; // Count of which userp is set - // Attributes - bool m_didWidth : 1; // Did V3Width computation - bool m_doingWidth : 1; // Inside V3Width - bool m_protect : 1; // Protect name if protection is on - // // Space for more bools here - // This member ordering both allows 64 bit alignment and puts associated data together VNUser m_user1u; // Contains any information the user iteration routine wants uint32_t m_user1Cnt; // Mark of when userp was set @@ -1512,17 +1522,17 @@ public: void fileline(FileLine* fl) { m_fileline = fl; } bool width1() const; int widthInstrs() const; - void didWidth(bool flag) { m_didWidth = flag; } - bool didWidth() const { return m_didWidth; } + void didWidth(bool flag) { m_flags.didWidth = flag; } + bool didWidth() const { return m_flags.didWidth; } bool didWidthAndSet() { if (didWidth()) return true; didWidth(true); return false; } - bool doingWidth() const { return m_doingWidth; } - void doingWidth(bool flag) { m_doingWidth = flag; } - bool protect() const { return m_protect; } - void protect(bool flag) { m_protect = flag; } + bool doingWidth() const { return m_flags.doingWidth; } + void doingWidth(bool flag) { m_flags.doingWidth = flag; } + bool protect() const { return m_flags.protect; } + void protect(bool flag) { m_flags.protect = flag; } // TODO stomp these width functions out, and call via dtypep() instead int width() const; From bdceb08963f82d77c2a2839b17254ae9652b8456 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 10 Jul 2021 07:11:34 -0400 Subject: [PATCH 08/84] Internals: Fix off-by-one #line. --- src/verilog.l | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/verilog.l b/src/verilog.l index 75739e32a..d000c3fad 100644 --- a/src/verilog.l +++ b/src/verilog.l @@ -87,12 +87,9 @@ static double lexParseDouble(FileLine* fl, const char* textp, size_t length) { ws [ \t\f\r]+ wsnr [ \t\f]+ crnl [\r]*[\n] - /* identifier */ id [a-zA-Z_][a-zA-Z0-9_$]* - /* escaped identifier */ escid \\[^ \t\f\r\n]+ word [a-zA-Z0-9_]+ - /* verilog numbers, constructed to not match the ' that begins a '( or '{ */ vnum1 [0-9]*?[''][sS]?[bcodhBCODH][ \t\n]*[A-Fa-f0-9xXzZ_?]* vnum2 [0-9]*?[''][sS]?[01xXzZ] vnum3 [0-9][_0-9]*[ \t\n]*[''][sS]?[bcodhBCODH]?[ \t\n]*[A-Fa-f0-9xXzZ_?]+ @@ -102,7 +99,7 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5} %% -.|\n {BEGIN STATE_VERILOG_RECENT; yyless(0); } +.|\n { BEGIN STATE_VERILOG_RECENT; yyless(0); } /************************************************************************/ /* Verilator control files */ From f55177a49f793c93e5a7b805c300d94a05ca03f3 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 10 Jul 2021 07:11:50 -0400 Subject: [PATCH 09/84] Tests: Fix coverage holes. No functional change. --- test_regress/t/t_gate_basic.v | 91 +++++++++++++++++++------------ test_regress/t/t_gate_strength.pl | 18 ++++++ test_regress/t/t_gate_strength.v | 75 +++++++++++++++++++++++++ test_regress/t/t_vams_basic.v | 33 +++++++---- 4 files changed, 171 insertions(+), 46 deletions(-) create mode 100755 test_regress/t/t_gate_strength.pl create mode 100644 test_regress/t/t_gate_strength.v diff --git a/test_regress/t/t_gate_basic.v b/test_regress/t/t_gate_basic.v index 8f120463a..9a3e7fbab 100644 --- a/test_regress/t/t_gate_basic.v +++ b/test_regress/t/t_gate_basic.v @@ -16,8 +16,8 @@ module t (/*AUTOARG*/ reg [31:0] b; wire [2:0] bf; buf BF0 (bf[0], a[0]), - BF1 (bf[1], a[1]), - BF2 (bf[2], a[2]); + BF1 (bf[1], a[1]), + BF2 (bf[2], a[2]); // verilator lint_off IMPLICIT not #(0.108) NT0 (nt0, a[0]); @@ -48,43 +48,66 @@ module t (/*AUTOARG*/ // path delays (A1 *> Q) = (a$A1$Y, a$A1$Y); (A0 *> Q) = (b$A0$Y, a$A0$Z); + + if (C1) (IN => OUT) = (1,1); + ifnone (IN => OUT) = (2,2); + + showcancelled Q; + noshowcancelled Q; + pulsestyle_ondetect Q; + pulsestyle_onevent Q; + + // other unimplemented stuff + $fullskew(); + $hold(); + $nochange(); + $period(); + $recovery(); + $recrem(); + $removal(); + $setup(); + $setuphold(); + $skew(); + $timeskew(); + $width(); + endspecify `endif always @ (posedge clk) begin if (cyc!=0) begin - cyc <= cyc + 1; - if (cyc==1) begin - a <= 32'h18f6b034; - b <= 32'h834bf892; - end - if (cyc==2) begin - a <= 32'h529ab56f; - b <= 32'h7835a237; - if (bf !== 3'b100) $stop; - if (nt0 !== 1'b1) $stop; - if (an0 !== 1'b0) $stop; - if (nd0 !== 1'b1) $stop; - if (or0 !== 1'b0) $stop; - if (nr0 !== 1'b1) $stop; - if (xo0 !== 1'b0) $stop; - if (xn0 !== 1'b1) $stop; - if (ba != 32'h18f6b034) $stop; - end - if (cyc==3) begin - if (bf !== 3'b111) $stop; - if (nt0 !== 1'b0) $stop; - if (an0 !== 1'b1) $stop; - if (nd0 !== 1'b0) $stop; - if (or0 !== 1'b1) $stop; - if (nr0 !== 1'b0) $stop; - if (xo0 !== 1'b0) $stop; - if (xn0 !== 1'b0) $stop; - end - if (cyc==4) begin - $write("*-* All Finished *-*\n"); - $finish; - end + cyc <= cyc + 1; + if (cyc==1) begin + a <= 32'h18f6b034; + b <= 32'h834bf892; + end + if (cyc==2) begin + a <= 32'h529ab56f; + b <= 32'h7835a237; + if (bf !== 3'b100) $stop; + if (nt0 !== 1'b1) $stop; + if (an0 !== 1'b0) $stop; + if (nd0 !== 1'b1) $stop; + if (or0 !== 1'b0) $stop; + if (nr0 !== 1'b1) $stop; + if (xo0 !== 1'b0) $stop; + if (xn0 !== 1'b1) $stop; + if (ba != 32'h18f6b034) $stop; + end + if (cyc==3) begin + if (bf !== 3'b111) $stop; + if (nt0 !== 1'b0) $stop; + if (an0 !== 1'b1) $stop; + if (nd0 !== 1'b0) $stop; + if (or0 !== 1'b1) $stop; + if (nr0 !== 1'b0) $stop; + if (xo0 !== 1'b0) $stop; + if (xn0 !== 1'b0) $stop; + end + if (cyc==4) begin + $write("*-* All Finished *-*\n"); + $finish; + end end end diff --git a/test_regress/t/t_gate_strength.pl b/test_regress/t/t_gate_strength.pl new file mode 100755 index 000000000..dd8b670e0 --- /dev/null +++ b/test_regress/t/t_gate_strength.pl @@ -0,0 +1,18 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2004 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 + +scenarios(simulator => 1); + +compile( + verilator_flags2 => ['-bbox-unsup'], + ); + +ok(1); +1; diff --git a/test_regress/t/t_gate_strength.v b/test_regress/t/t_gate_strength.v new file mode 100644 index 000000000..038ca9cfb --- /dev/null +++ b/test_regress/t/t_gate_strength.v @@ -0,0 +1,75 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2004 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + integer cyc; initial cyc=1; + + logic [31:0] a; + + // verilator lint_off IMPLICIT + assign (highz0, supply1) nt00 = a[0]; + assign (supply0, highz1) nt01 = a[0]; + assign (supply0, supply1) nt02 = a[0]; + assign (strong0, strong1) nt03 = a[0]; + assign (pull0, pull1) nt04 = a[0]; + assign (weak0, weak1) nt05 = a[0]; + + assign (highz0, supply1) nt10 = a[0]; + assign (supply0, highz1) nt11 = a[0]; + assign (supply0, supply1) nt12 = a[0]; + assign (strong0, strong1) nt13 = a[0]; + assign (pull0, pull1) nt14 = a[0]; + assign (weak0, weak1) nt15 = a[0]; + // verilator lint_on IMPLICIT + + always @ (posedge clk) begin + if (cyc!=0) begin + cyc <= cyc + 1; + if (cyc==1) begin + a <= 32'h18f6b030; + end + if (cyc==2) begin + a <= 32'h18f6b03f; + if (nt00 !== 1'b0) $stop; + if (nt01 !== 1'b0) $stop; + if (nt02 !== 1'b0) $stop; + if (nt03 !== 1'b0) $stop; + if (nt04 !== 1'b0) $stop; + if (nt05 !== 1'b0) $stop; + if (nt10 !== 1'b0) $stop; + if (nt11 !== 1'b0) $stop; + if (nt12 !== 1'b0) $stop; + if (nt13 !== 1'b0) $stop; + if (nt14 !== 1'b0) $stop; + if (nt15 !== 1'b0) $stop; + end + if (cyc==3) begin + if (nt00 !== 1'b1) $stop; + if (nt01 !== 1'b1) $stop; + if (nt02 !== 1'b1) $stop; + if (nt03 !== 1'b1) $stop; + if (nt04 !== 1'b1) $stop; + if (nt05 !== 1'b1) $stop; + if (nt10 !== 1'b1) $stop; + if (nt11 !== 1'b1) $stop; + if (nt12 !== 1'b1) $stop; + if (nt13 !== 1'b1) $stop; + if (nt14 !== 1'b1) $stop; + if (nt15 !== 1'b1) $stop; + end + if (cyc==4) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + end + +endmodule diff --git a/test_regress/t/t_vams_basic.v b/test_regress/t/t_vams_basic.v index c658d64e2..8a71bfe42 100644 --- a/test_regress/t/t_vams_basic.v +++ b/test_regress/t/t_vams_basic.v @@ -15,9 +15,9 @@ module t (/*AUTOARG*/ task check (integer line, real got, real expec); real delta; delta = got-expec; - if (delta > 0.001) begin - $display("Line%d: Got %g Exp %g\n", line, got, expec); - $stop; + if (delta > 0.001 || delta < -0.001) begin + $write("Line%0d: Got %g Exp %g\n", line, got, expec); + $stop; end endtask @@ -27,21 +27,30 @@ module t (/*AUTOARG*/ sub sub (.*); initial begin - check(`__LINE__, sqrt(2.0) , 1.414); - check(`__LINE__, pow(2.0,2.0) , 4.0); - check(`__LINE__, ln(2.0) , 0.693147); - check(`__LINE__, log(2.0) , 0.30103); - check(`__LINE__, floor(2.5) , 2.0); - check(`__LINE__, exp(2.0) , 7.38906); - check(`__LINE__, ceil(2.5) , 3.0); + check(`__LINE__, atan(0.5) , 0.463648); + check(`__LINE__, atan2(0.5, 0.3) , 1.03038); + check(`__LINE__, atanh(0.5) , 0.549306); + check(`__LINE__, ceil(2.5) , 3.0); + check(`__LINE__, cos(0.5) , 0.877583); + check(`__LINE__, cosh(0.5) , 1.12763); + check(`__LINE__, exp(2.0) , 7.38906); + check(`__LINE__, floor(2.5) , 2.0); + check(`__LINE__, ln(2.0) , 0.693147); + check(`__LINE__, log(2.0) , 0.30103); + check(`__LINE__, pow(2.0,2.0) , 4.0); + check(`__LINE__, sin(0.5) , 0.479426); + check(`__LINE__, sinh(0.5) , 0.521095); + check(`__LINE__, sqrt(2.0) , 1.414); + check(`__LINE__, tan(0.5) , 0.546302); + check(`__LINE__, tanh(0.5) , 0.462117); $write("*-* All Finished *-*\n"); $finish; end endmodule module sub ( - input wreal wr - ); + input wreal wr + ); initial begin if (wr != 1.1) $stop; end From add3811f46f4eb38cacd30ca7817160868da3e00 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Sat, 10 Jul 2021 12:03:51 +0100 Subject: [PATCH 10/84] Internals: Fix debug prints racing with option parsing. debug() declared by VL_DEGUB_FUNC used to cache the result of the debug level lookup (which depends on options) in a static. This meant that if the debug() function was called before option parsing, the default debug level of 0 would be used for the rest of the program, even if a --debug option was given. Fixed by not caching the debug level until after option parsing is complete. --- src/V3Error.h | 6 +++++- src/V3Options.cpp | 3 +++ src/V3Options.h | 3 +++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/V3Error.h b/src/V3Error.h index 870893c8e..cac812106 100644 --- a/src/V3Error.h +++ b/src/V3Error.h @@ -417,7 +417,11 @@ inline void v3errorEndFatal(std::ostringstream& sstr) { #define VL_DEBUG_FUNC \ static int debug() { \ static int level = -1; \ - if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__); \ + if (VL_UNLIKELY(level < 0)) { \ + const int debugSrcLevel = v3Global.opt.debugSrcLevel(__FILE__); \ + if (!v3Global.opt.available()) return debugSrcLevel; \ + level = debugSrcLevel; \ + } \ return level; \ } diff --git a/src/V3Options.cpp b/src/V3Options.cpp index f5e141701..8e52d8761 100644 --- a/src/V3Options.cpp +++ b/src/V3Options.cpp @@ -791,6 +791,9 @@ void V3Options::notify() { if (coverage() && savable()) { cmdfl->v3error("--coverage and --savable not supported together"); } + + // Mark options as available + m_available = true; } //###################################################################### diff --git a/src/V3Options.h b/src/V3Options.h index 3668fe605..6d42ccabe 100644 --- a/src/V3Options.h +++ b/src/V3Options.h @@ -361,6 +361,8 @@ private: bool m_oTable; // main switch: -Oa: lookup table creation // clang-format on + bool m_available = false; // Set to true at the end of option parsing + private: // METHODS void addArg(const string& arg); @@ -402,6 +404,7 @@ public: void addVFile(const string& filename); void addForceInc(const string& filename); void notify(); + bool available() const { return m_available; } // ACCESSORS (options) bool preprocOnly() const { return m_preprocOnly; } From 766ad14ae0957c1893321da7f06df0272fa0245b Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Thu, 8 Jul 2021 14:58:57 +0100 Subject: [PATCH 11/84] Check function locals are referenced only when in scope V3Broken now checks that AstVar nodes referenced in an AstCFunc are either external, or appear in the tree before the reference, and are in scope. Fix V3Begin to move lifted AstVars to beginning of FTask, rather than end, which trips the above check. --- src/V3Begin.cpp | 45 ++++++++++++++-------- src/V3Broken.cpp | 97 +++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 125 insertions(+), 17 deletions(-) diff --git a/src/V3Begin.cpp b/src/V3Begin.cpp index 9c1e3674a..5bee0a7f4 100644 --- a/src/V3Begin.cpp +++ b/src/V3Begin.cpp @@ -62,6 +62,7 @@ private: BeginState* m_statep; // Current global state AstNodeModule* m_modp = nullptr; // Current module AstNodeFTask* m_ftaskp = nullptr; // Current function/task + AstNode* m_liftedp = nullptr; // Local nodes we are lifting into m_ftaskp string m_namedScope; // Name of begin blocks above us string m_unnamedScope; // Name of begin blocks, including unnamed blocks int m_ifDepth = 0; // Current if depth @@ -74,6 +75,21 @@ private: return a + "__DOT__" + b; } + void liftNode(AstNode* nodep) { + nodep->unlinkFrBack(); + if (m_ftaskp) { + // AstBegin under ftask, just move into the ftask + if (!m_liftedp) { + m_liftedp = nodep; + } else { + m_liftedp->addNext(nodep); + } + } else { + // Move to module + m_modp->addStmtp(nodep); + } + } + // VISITORS virtual void visit(AstNodeModule* nodep) override { VL_RESTORER(m_modp); @@ -101,7 +117,17 @@ private: m_namedScope = ""; m_unnamedScope = ""; m_ftaskp = nodep; + m_liftedp = nullptr; iterateChildren(nodep); + if (m_liftedp) { + // Place lifted nodes at beginning of stmtsp, so Var nodes appear before referenced + if (AstNode* const stmtsp = nodep->stmtsp()) { + stmtsp->unlinkFrBackWithNext(); + m_liftedp->addNext(stmtsp); + } + nodep->addStmtsp(m_liftedp); + m_liftedp = nullptr; + } m_ftaskp = nullptr; } } @@ -157,13 +183,8 @@ private: // Rename it nodep->name(dot(m_unnamedScope, nodep->name())); m_statep->userMarkChanged(nodep); - // Move to module - nodep->unlinkFrBack(); - if (m_ftaskp) { - m_ftaskp->addStmtsp(nodep); // Begins under funcs just move into the func - } else { - m_modp->addStmtp(nodep); - } + // Move it under enclosing tree + liftNode(nodep); } } virtual void visit(AstTypedef* nodep) override { @@ -171,14 +192,8 @@ private: // Rename it nodep->name(dot(m_unnamedScope, nodep->name())); m_statep->userMarkChanged(nodep); - // Move to module - nodep->unlinkFrBack(); - // Begins under funcs just move into the func - if (m_ftaskp) { - m_ftaskp->addStmtsp(nodep); - } else { - m_modp->addStmtp(nodep); - } + // Move it under enclosing tree + liftNode(nodep); } } virtual void visit(AstCell* nodep) override { diff --git a/src/V3Broken.cpp b/src/V3Broken.cpp index 3205ce912..b9bae7707 100644 --- a/src/V3Broken.cpp +++ b/src/V3Broken.cpp @@ -18,6 +18,7 @@ // Entire netlist // Mark all nodes // Check all links point to marked nodes +// Check local variables in CFuncs appear before they are referenced // //************************************************************************* @@ -33,6 +34,7 @@ #include #include +#include //###################################################################### @@ -240,13 +242,23 @@ public: class BrokenCheckVisitor final : public AstNVisitor { bool m_inScope = false; // Under AstScope + // Current CFunc, if any + const AstCFunc* m_cfuncp = nullptr; + // All local variables declared in current function + std::unordered_set m_localVars; + // Variable references in current function that do not reference an in-scope local + std::unordered_map m_suspectRefs; + // Local variables declared in the scope of the current statement + std::vector> m_localsStack; + private: static void checkWidthMin(const AstNode* nodep) { UASSERT_OBJ(nodep->width() == nodep->widthMin() || v3Global.widthMinUsage() != VWidthMinUsage::MATCHES_WIDTH, nodep, "Width != WidthMin"); } - void processAndIterate(AstNode* nodep) { + + void processEnter(AstNode* nodep) { BrokenTable::setUnder(nodep, true); const char* whyp = nodep->broken(); UASSERT_OBJ(!whyp, nodep, @@ -270,8 +282,30 @@ private: if (const AstNodeDType* dnodep = VN_CAST(nodep, NodeDType)) checkWidthMin(dnodep); } checkWidthMin(nodep); + } + void processExit(AstNode* nodep) { BrokenTable::setUnder(nodep, false); } + void processAndIterate(AstNode* nodep) { + processEnter(nodep); iterateChildrenConst(nodep); - BrokenTable::setUnder(nodep, false); + processExit(nodep); + } + void processAndIterateList(AstNode* nodep) { + while (nodep) { + processAndIterate(nodep); + nodep = nodep->nextp(); + }; + } + void pushLocalScope() { + if (m_cfuncp) m_localsStack.emplace_back(); + } + void popLocalScope() { + if (m_cfuncp) m_localsStack.pop_back(); + } + bool isInScopeLocal(const AstVar* varp) const { + for (const auto& set : m_localsStack) { + if (set.count(varp)) return true; + } + return false; } virtual void visit(AstNodeAssign* nodep) override { processAndIterate(nodep); @@ -295,6 +329,65 @@ private: UASSERT_OBJ( !(v3Global.assertScoped() && m_inScope && nodep->varp() && !nodep->varScopep()), nodep, "VarRef missing VarScope pointer"); + if (m_cfuncp) { + // Check if variable is an in-scope local, otherwise mark as suspect + if (const AstVar* const varp = nodep->varp()) { + if (!isInScopeLocal(varp)) { + // This only stores the first ref for each Var, which is what we want + m_suspectRefs.emplace(varp, nodep); + } + } + } + } + virtual void visit(AstCFunc* nodep) override { + UASSERT_OBJ(!m_cfuncp, nodep, "Nested AstCFunc"); + m_cfuncp = nodep; + m_localVars.clear(); + m_suspectRefs.clear(); + m_localsStack.clear(); + pushLocalScope(); + + processAndIterate(nodep); + + // Check suspect references are all to non-locals + for (const auto& pair : m_suspectRefs) { + UASSERT_OBJ(m_localVars.count(pair.first) == 0, pair.second, + "Local variable not in scope where referenced: " << pair.first); + } + + m_cfuncp = nullptr; + } + virtual void visit(AstNodeIf* nodep) override { + // Each branch is a separate local variable scope + pushLocalScope(); + processEnter(nodep); + processAndIterate(nodep->condp()); + if (AstNode* const ifsp = nodep->ifsp()) { + pushLocalScope(); + processAndIterateList(ifsp); + popLocalScope(); + } + if (AstNode* const elsesp = nodep->elsesp()) { + pushLocalScope(); + processAndIterateList(elsesp); + popLocalScope(); + } + processExit(nodep); + popLocalScope(); + } + virtual void visit(AstNodeStmt* nodep) override { + // For local variable checking act as if any statement introduces a new scope. + // This is aggressive but conservatively correct. + pushLocalScope(); + processAndIterate(nodep); + popLocalScope(); + } + virtual void visit(AstVar* nodep) override { + processAndIterate(nodep); + if (m_cfuncp) { + m_localVars.insert(nodep); + m_localsStack.back().insert(nodep); + } } virtual void visit(AstNode* nodep) override { // Process not just iterate From 896b18e024d8a4b6263e3ebe4e1362d327f47f72 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Fri, 2 Jul 2021 16:55:57 +0100 Subject: [PATCH 12/84] Emit function locals in the place they appear in the tree Do not sort and hoist function local variables to the top of the function definition. The stack layout of automatic variables is not defined by C so the compilers can lay these out optimally. Simplifies internals for follow on work. Effect on model performance is neutral to very slight improvement, so we do not seem to be loosing anything. --- src/V3EmitCFunc.cpp | 5 ++--- src/V3EmitCFunc.h | 35 ++++++++++++++++++++++------------- 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/src/V3EmitCFunc.cpp b/src/V3EmitCFunc.cpp index 5a5613981..14a82ea82 100644 --- a/src/V3EmitCFunc.cpp +++ b/src/V3EmitCFunc.cpp @@ -544,7 +544,6 @@ void EmitCFunc::emitVarList(AstNode* firstp, EisWhich which, const string& prefi doit = (varp->isParam() && !VN_IS(varp->valuep(), Const)); break; case EVL_CLASS_ALL: doit = true; break; - case EVL_FUNC_ALL: doit = true; break; default: v3fatalSrc("Bad Case"); } if (varp->isStatic() ? !isstatic : isstatic) doit = false; @@ -574,8 +573,8 @@ void EmitCFunc::emitVarList(AstNode* firstp, EisWhich which, const string& prefi && !varp->isSc() // Aggregates can't be anon && (varp->basicp() && !varp->basicp()->isOpaque()) // Aggregates can't be anon - && which != EVL_FUNC_ALL); // Anon not legal in funcs, and gcc - // bug free there anyhow + ); + if (anonOk) { varAnonMap[sortbytes].push_back(varp); } else { diff --git a/src/V3EmitCFunc.h b/src/V3EmitCFunc.h index 38f18a00e..534739694 100644 --- a/src/V3EmitCFunc.h +++ b/src/V3EmitCFunc.h @@ -130,6 +130,7 @@ protected: EmitCLazyDecls m_lazyDecls; // Visitor for emitting lazy declarations bool m_useSelfForThis = false; // Replace "this" with "vlSelf" AstNodeModule* m_modp = nullptr; // Current module being emitted + AstCFunc* m_cfuncp = nullptr; // Current function being emitted public: // METHODS @@ -159,8 +160,7 @@ public: EVL_CLASS_SIG, EVL_CLASS_TEMP, EVL_CLASS_PAR, - EVL_CLASS_ALL, - EVL_FUNC_ALL + EVL_CLASS_ALL }; void emitVarList(AstNode* firstp, EisWhich which, const string& prefixIfImp, string& sectionr); static void emitVarSort(const VarSortMap& vmap, VarVec* sortedp); @@ -203,6 +203,8 @@ public: // VISITORS virtual void visit(AstCFunc* nodep) override { VL_RESTORER(m_useSelfForThis); + VL_RESTORER(m_cfuncp); + m_cfuncp = nodep; m_blkChangeDetVec.clear(); @@ -238,25 +240,28 @@ public: puts(nodep->isLoose() ? "__" : "::"); puts(nodep->nameProtect() + "\\n\"); );\n"); - if (nodep->initsp()) putsDecoration("// Variables\n"); for (AstNode* subnodep = nodep->argsp(); subnodep; subnodep = subnodep->nextp()) { if (AstVar* varp = VN_CAST(subnodep, Var)) { if (varp->isFuncReturn()) emitVarDecl(varp, ""); } } - string section; - emitVarList(nodep->initsp(), EVL_FUNC_ALL, "", section /*ref*/); - emitVarList(nodep->stmtsp(), EVL_FUNC_ALL, "", section /*ref*/); - iterateAndNextNull(nodep->initsp()); + if (nodep->initsp()) { + putsDecoration("// Init\n"); + iterateAndNextNull(nodep->initsp()); + } + + if (nodep->stmtsp()) { + putsDecoration("// Body\n"); + iterateAndNextNull(nodep->stmtsp()); + } - if (nodep->stmtsp()) putsDecoration("// Body\n"); - iterateAndNextNull(nodep->stmtsp()); if (!m_blkChangeDetVec.empty()) emitChangeDet(); - if (nodep->finalsp()) putsDecoration("// Final\n"); - iterateAndNextNull(nodep->finalsp()); - // + if (nodep->finalsp()) { + putsDecoration("// Final\n"); + iterateAndNextNull(nodep->finalsp()); + } if (!m_blkChangeDetVec.empty()) puts("return __req;\n"); @@ -264,6 +269,11 @@ public: if (nodep->ifdef() != "") puts("#endif // " + nodep->ifdef() + "\n"); } + virtual void visit(AstVar* nodep) override { + UASSERT_OBJ(m_cfuncp, nodep, "Cannot emit non-local variable"); + emitVarDecl(nodep, ""); + } + virtual void visit(AstNodeAssign* nodep) override { bool paren = true; bool decind = false; @@ -1221,7 +1231,6 @@ public: virtual void visit(AstTypedef*) override {} virtual void visit(AstPragma*) override {} virtual void visit(AstCell*) override {} // Handled outside the Visit class - virtual void visit(AstVar*) override {} // Handled outside the Visit class virtual void visit(AstNodeText*) override {} // Handled outside the Visit class virtual void visit(AstCFile*) override {} // Handled outside the Visit class virtual void visit(AstCellInline*) override {} // Handled outside visit (in EmitCSyms) From a8168d5d62b7f7be8bcd0ff9c60eaff5f7894905 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sun, 11 Jul 2021 09:46:00 -0400 Subject: [PATCH 13/84] Commentary --- docs/guide/contributors.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/guide/contributors.rst b/docs/guide/contributors.rst index c38cb8515..1ed5a867f 100644 --- a/docs/guide/contributors.rst +++ b/docs/guide/contributors.rst @@ -13,7 +13,8 @@ When possible, please instead report bugs at `Verilator Issues Primary author is Wilson Snyder . -Major concepts by Paul Wasson, Duane Galbi, John Coiner and Jie Xu. +Major concepts by Paul Wasson, Duane Galbi, John Coiner, Giza Lore, Yutetsu +Takatsukasa, and Jie Xu. Contributors From f1bb0bb7251cf6f737ec7f5e4e5e0dfd976c0910 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sun, 11 Jul 2021 10:25:13 -0400 Subject: [PATCH 14/84] Tests: Whitespace check all git files --- .clang-format | 1 - test_regress/t/t_dist_whitespace.pl | 14 ++++++-------- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/.clang-format b/.clang-format index ad282b495..251eebe52 100644 --- a/.clang-format +++ b/.clang-format @@ -108,4 +108,3 @@ Standard: Cpp11 TabWidth: 8 UseTab: Never ... - diff --git a/test_regress/t/t_dist_whitespace.pl b/test_regress/t/t_dist_whitespace.pl index 4056e1053..bf7f6ca36 100755 --- a/test_regress/t/t_dist_whitespace.pl +++ b/test_regress/t/t_dist_whitespace.pl @@ -14,10 +14,11 @@ my $root = ".."; my $Debug; ### Must trim output before and after our file list -my %files = %{get_manifest_files($root)}; +my %files = %{get_source_files($root)}; foreach my $file (sort keys %files) { my $filename = "$root/$file"; + next if !-f $file; # git file might be deleted but not yet staged my $contents = file_contents($filename); if ($file =~ /\.out$/) { # Ignore golden files @@ -79,15 +80,12 @@ if (keys %warns) { ok(1); 1; -sub get_manifest_files { +sub get_source_files { my $root = shift; - `cd $root && $ENV{MAKE} dist-file-list`; - my $manifest_files = `cd $root && $ENV{MAKE} dist-file-list`; - $manifest_files =~ s!.*begin-dist-file-list:!!sg; - $manifest_files =~ s!end-dist-file-list:.*$!!sg; - print "MF $manifest_files\n" if $Self->{verbose}; + my $git_files = `cd $root && git ls-files`; + print "MF $git_files\n" if $Self->{verbose}; my %files; - foreach my $file (split /\s+/,$manifest_files) { + foreach my $file (split /\s+/, $git_files) { next if $file eq ''; $files{$file} |= 1; } From 687dd440a94e24a69b6cbc4947a9285fdd934e50 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sun, 11 Jul 2021 10:25:24 -0400 Subject: [PATCH 15/84] Removed deprecated Verilator tarball generation makefile rules. --- MANIFEST.SKIP | 76 --------------- Makefile.in | 150 +----------------------------- test_regress/t/t_dist_manifest.pl | 97 ------------------- 3 files changed, 3 insertions(+), 320 deletions(-) delete mode 100644 MANIFEST.SKIP delete mode 100755 test_regress/t/t_dist_manifest.pl diff --git a/MANIFEST.SKIP b/MANIFEST.SKIP deleted file mode 100644 index 4a964bc40..000000000 --- a/MANIFEST.SKIP +++ /dev/null @@ -1,76 +0,0 @@ -\.ccache/ -\.clang-format -\.clang-tidy -\.git/ -\.git$ -\.github/ -\.svn/ -\.(bak|old)/ -\.(bak|old)$ -\.tar\. -.*\.tgz -.*\.log -\..*\.swp -.*\.tmp -.*\.tidy -.*\.tex -.*\.key -.*\.vcd -.*\.1 -\.codacy\.yml -\.travis\.yml -/_build/ -/build/ -/obj_dbg/ -/obj_dir/ -/obj_dist/ -/obj_iv/ -/obj_ms/ -/obj_nc/ -/obj_opt/ -/obj_vcs/ -/obj_vlt/ -/obj_vltmt/ -INCA_libs/ -/cov_work/ -/logs/ -^Makefile$ -README.html -bin/verilator_bin.* -bin/verilator_coverage_bin.* -docs/.*\.html$ -docs/_build/ -docs/clang-format.txt$ -docs/doxygen-doc/.* -docs/spelling.txt -examples/xml_py/copied/ -examples/xml_py/graph.* -sonar-project.properties -src/Makefile$ -src/Makefile_obj$ -include/verilated.mk$ -test_regress/.gdbinit$ -test_regress/transcript -codecov.yml -config.cache$ -config.status$ -verilator\.log -verilator\.tex -verilator-config.cmake$ -verilator-config-version.cmake$ -verilator.pc$ -verilator_bin.* -verilator_coverage_bin.* -.vcsmx_rebuild$ -ncverilog.history -autom4te\.cache/ -nodist/ -ci/ -/simv$ -/simv.daidir/ -/vc_hdrs.h$ -/csrc/ -obj_dir.* -TAGS -gmon.out -.*~ diff --git a/Makefile.in b/Makefile.in index 38c73b19d..7b09873c8 100644 --- a/Makefile.in +++ b/Makefile.in @@ -118,81 +118,6 @@ INFOS = verilator.html verilator.pdf INFOS_OLD = README README.html README.pdf -# Files that can be generated, but should be up to date for a distribution. -DISTDEP = info Makefile - -DISTFILES1 = $(INFOS) .gitignore \ - *.in *.ac \ - Artistic \ - Changes \ - LICENSE \ - MANIFEST.SKIP \ - README.rst \ - verilator-config.cmake.in \ - verilator-config-version.cmake.in \ - bin/verilator \ - bin/verilator_ccache_report \ - bin/verilator_coverage \ - bin/verilator_difftree \ - bin/verilator_gantt \ - bin/verilator_includer \ - bin/verilator_profcfunc \ - docs/.gitignore \ - docs/CONTRIBUTING.rst \ - docs/CONTRIBUTORS \ - docs/Makefile \ - docs/_static/*.png \ - docs/_static/css/* \ - docs/bin/* \ - docs/gen/* \ - docs/guide/*.py \ - docs/guide/*.rst \ - docs/guide/figures/* \ - docs/install.rst \ - docs/internals.rst \ - docs/internals.rst \ - docs/verilated.dox \ - docs/xml.rst \ - install-sh configure *.pod \ - include/*.[chv]* \ - include/*.in \ - include/.*ignore \ - include/gtkwave/*.[chv]* \ - include/vltstd/*.[chv]* \ - .*attributes */.*attributes */*/.*attributes \ - src/.*ignore src/*.in src/*.cpp src/*.[chly] \ - src/astgen src/bisonpre src/*fix src/cppcheck_filtered \ - src/config_rev \ - src/vlcovgen src/mkinstalldirs \ - src/.gdbinit \ - src/*.pl src/*.pod \ - examples/*/.*ignore examples/*/Makefile* \ - examples/*/*.[chv]* examples/*/vl_* \ - examples/*/CMakeLists.txt \ - -DISTFILES2 = \ - test_*/.*ignore test_*/Makefile* test_*/*.cpp \ - test_*/*.pl test_*/*.v test_*/*.vc test_*/*.vh \ - test_regress/*.pl \ - test_regress/Makefile \ - test_regress/Makefile_obj \ - test_regress/input.vc \ - test_regress/CMakeLists.txt \ - test_regress/t/t*/*.sv* \ - test_regress/t/t*/*.v* \ - test_regress/t/t*/*/*.sv* \ - test_regress/t/t*/*/*.v* \ - test_regress/t/t*/*.cpp \ - test_regress/t/t*/CMakeLists.txt \ - test_regress/t/*.cpp \ - test_regress/t/*.h \ - test_regress/t/*.dat \ - test_regress/t/*.mem \ - test_regress/t/*.out \ - test_regress/t/*.pl \ - test_regress/t/*.pf \ - test_regress/t/*.v* \ - INST_PROJ_FILES = \ bin/verilator \ bin/verilator_ccache_report \ @@ -386,44 +311,7 @@ uninstall: install: all_nomsg install-all install-all: installbin installman installdata install-msg -install-here: installman ftp - -ifeq ($(VERILATOR_AUTHOR_SITE),1) # Local... Else don't burden users -DISTNAMEREV = $(shell sed -e '/DTVERSION/!d' -e 's/.*verilator_\([^"]*\).*/\1/' -e q ${srcdir}/src/config_rev.h) - -VERILATOR_CAD_DIR = $(CAD_DIR)/verilator/$(DISTNAMEREV)/$(DIRPROJECT_ARCH) -INST_PROJ_CVS = cp_if_cvs_diff - -install-cadtools: dist - @echo "Install-project to $(CAD_DIR)" - strip bin/verilator_bin* - strip bin/verilator_coverage_bin* - $(MAKE) install-cadtools-quick - $(MKINSTALLDIRS) $(VERILATOR_CAD_DIR)/man/man1 - for p in $(VL_INST_MAN_FILES) ; do \ - $(INSTALL_DATA) $$p $(VERILATOR_CAD_DIR)/man/man1/$$p; \ - done - $(INST_PROJ_CVS) $(DISTNAME).tgz $(VERILATOR_CAD_DIR)/verilator.tgz - rm $(DISTNAME).tgz - -install-cadtools-quick: -ifeq ($(CFG_WITH_DEFENV),yes) - @echo "%Error: Reconfigure with './configure --disable-defenv' to avoid hardcoded paths." - false -endif - @echo "Install-cadtools-quick (no strip) to $(VERILATOR_CAD_DIR)" - $(MKINSTALLDIRS) $(VERILATOR_CAD_DIR)/include/gtkwave - $(MKINSTALLDIRS) $(VERILATOR_CAD_DIR)/include/vltstd - $(MKINSTALLDIRS) $(VERILATOR_CAD_DIR)/bin - for p in $(INST_PROJ_FILES) ; do \ - $(INST_PROJ_CVS) $$p $(VERILATOR_CAD_DIR)/$$p; \ - done - for p in $(INST_PROJ_BIN_FILES) ; do \ - $(INST_PROJ_CVS) $$p $(VERILATOR_CAD_DIR)/$$p; \ - done - -# VERILATOR_AUTHOR_SITE -endif +install-here: installman info # Use --xml flag to see the cppcheck code to use for suppression CPPCHECK_CPP = $(wildcard \ @@ -524,9 +412,6 @@ lint-py: format-pl-exec: -chmod a+x test_regress/t/*.pl - -ftp: info - install-msg: @echo @echo "Installed binaries to $(DESTDIR)$(bindir)/verilator" @@ -597,15 +482,6 @@ TAGS: $(TAGFILES) doxygen: $(MAKE) -C docs doxygen -###################################################################### -# Test targets - -dist-file-list: - @echo "begin-dist-file-list:"; # Scripts look for this - @echo $(wildcard $(DISTFILES1)) - @echo $(wildcard $(DISTFILES2)) - @echo "end-dist-file-list:"; # Scripts look for this - ###################################################################### # Distributions @@ -613,36 +489,16 @@ DISTTITLE := Verilator $(word 1,$(PACKAGE_VERSION)) DISTNAME := verilator-$(word 1,$(PACKAGE_VERSION)) DISTDATEPRE := $(word 2,$(PACKAGE_VERSION)) DISTDATE := $(subst /,-,$(DISTDATEPRE)) - DISTTAGNAME := $(subst .,_,$(subst -,_,$(DISTNAME))) tag: svnorcvs tag $(DISTTAGNAME) -# Don't depend on DISTFILES because there's no rule for "standards.info*". -dist: $(DISTDEP) maintainer-copy - -rm -fr $(DISTNAME) - for file in $(DISTFILES1); do \ - mkdir -p `dirname $(DISTNAME)/$$file` >/dev/null ; \ - ln $$file $(DISTNAME)/$$file \ - || { echo copying $$file instead; cp -p $$file $(DISTNAME)/$$file;}; \ - done; true; - for file in $(DISTFILES2); do \ - mkdir -p `dirname $(DISTNAME)/$$file` >/dev/null ; \ - ln $$file $(DISTNAME)/$$file \ - || { echo copying $$file instead; cp -p $$file $(DISTNAME)/$$file;}; \ - done; true; - chmod -R a+r $(DISTNAME) - tar chf $(DISTNAME).tar $(DISTNAME) - gzip --force --best $(DISTNAME).tar - mv $(DISTNAME).tar.gz $(DISTNAME).tgz - rm -fr $(DISTNAME) - maintainer-diff: svnorcvs diff $(DISTTAGNAME) preexist: svnorcvs nexists $(DISTTAGNAME) -maintainer-dist: preexist dist tag - svnorcvs release $(DISTNAME).tgz +maintainer-dist: preexist tag + svnorcvs release $(DISTTAGNAME) diff --git a/test_regress/t/t_dist_manifest.pl b/test_regress/t/t_dist_manifest.pl deleted file mode 100755 index dcd6ef8f3..000000000 --- a/test_regress/t/t_dist_manifest.pl +++ /dev/null @@ -1,97 +0,0 @@ -#!/usr/bin/env perl -if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } -# DESCRIPTION: Verilator: Verilog Test driver/expect definition -# -# Copyright 2003 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 - -scenarios(dist => 1); - -my $root = ".."; - -### Must trim output before and after our file list -my %files = %{get_manifest_files($root)}; - -my $all_files = `cd $root && find . -type f -print`; -foreach my $file (split /\s+/,$all_files) { - next if $file eq ''; - $file =~ s!^\./!!; - $files{$file} |= 2; -} - -my %file_regexps; -my $skip = file_contents("$root/MANIFEST.SKIP"); -foreach my $file (sort keys %files) { - foreach my $skip (split /\s+/,$skip) { - if ($file =~ /$skip/) { - $files{$file} |= 4; - $file_regexps{$file} = $skip; - } - } -} - -# The repo may be a Git worktree -my $git_dir = `cd $root ; git rev-parse --git-common-dir`; -chomp $git_dir; -if (! -d $git_dir) { - $git_dir = ".git"; -} - -# Ignore files locally excluded -my $git_exclude = `cd $root && git ls-files --others --ignored --exclude-from $git_dir/info/exclude`; -foreach my $exclude (split /\s+/, $git_exclude) { - if (exists $files{$exclude}) { - $files{$exclude} |= 8; - } -} - -my %warns; -foreach my $file (sort keys %files) { - my $tar = $files{$file}&1; - my $dir = $files{$file}&2; - my $skip = $files{$file}&4; - my $exclude = $files{$file}&8; - - print +(($tar ? "TAR ":" ") - .($dir ? "DIR ":" ") - .($skip ? "SKIP ":" ") - .($exclude ? "EXCL ":" ") - ." $file\n") if $Self->{verbose}; - - if ($dir && !$tar && !$skip && !$exclude) { - $warns{$file} = "File not in manifest or MANIFEST.SKIP: $file"; - } elsif (!$dir && $tar && !$skip) { - $warns{$file} = "File in manifest, but not directory: $file"; - } elsif ($dir && $tar && $skip) { - $warns{$file} = "File in manifest and also MANIFEST.SKIP, too general skip regexp '$file_regexps{$file}'?: $file"; - } -} - -if (keys %warns) { - # First warning lists everything as that's shown in the driver summary - error("Files mismatch with manifest: ",join(' ',sort keys %warns)); - foreach my $file (sort keys %warns) { - error($warns{$file}); - } -} - -ok(1); -1; - -sub get_manifest_files { - my $root = shift; - `cd $root && $ENV{MAKE} dist-file-list`; - my $manifest_files = `cd $root && $ENV{MAKE} dist-file-list`; - $manifest_files =~ s!.*begin-dist-file-list:!!sg; - $manifest_files =~ s!end-dist-file-list:.*$!!sg; - print "MF $manifest_files\n" if $Self->{verbose}; - my %files; - foreach my $file (split /\s+/,$manifest_files) { - next if $file eq ''; - $files{$file} |= 1; - } - return \%files; -} From c7499133b207b3c6a946ea3d6ff937dd34ab7dc3 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sun, 11 Jul 2021 10:42:32 -0400 Subject: [PATCH 16/84] Internals: C++11 for bool. No functional change. --- src/V3Cdc.cpp | 2 +- src/V3Const.cpp | 4 ++-- src/V3EmitCSyms.cpp | 2 +- src/V3EmitMk.cpp | 2 +- src/V3Partition.cpp | 3 +-- 5 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/V3Cdc.cpp b/src/V3Cdc.cpp index 22d2d0333..f4e8f2bd8 100644 --- a/src/V3Cdc.cpp +++ b/src/V3Cdc.cpp @@ -495,7 +495,7 @@ private: UINFO(3, __FUNCTION__ << ": " << endl); // Trace all sources and sinks - for (int traceDests = 0; traceDests < 2; ++traceDests) { + for (const bool& traceDests : {false, true}) { UINFO(9, " Trace Direction " << (traceDests ? "dst" : "src") << endl); m_graph.userClearVertices(); // user1: bool - was analyzed for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) { diff --git a/src/V3Const.cpp b/src/V3Const.cpp index 49dc1a377..29161330b 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -404,11 +404,11 @@ class ConstBitOpTreeVisitor final : public AstNVisitor { VL_RESTORER(m_curOpp); VL_RESTORER(m_leafp); - for (int i = 0; i < 2; ++i) { + for (const bool& right : {false, true}) { Restorer restorer{*this}; LeafInfo leafInfo; m_leafp = &leafInfo; - m_curOpp = i == 0 ? nodep->lhsp() : nodep->rhsp(); + m_curOpp = right ? nodep->rhsp() : nodep->lhsp(); const bool origFailed = m_failed; iterate(m_curOpp); if (leafInfo.m_constp || m_failed) { diff --git a/src/V3EmitCSyms.cpp b/src/V3EmitCSyms.cpp index 412773feb..de012617e 100644 --- a/src/V3EmitCSyms.cpp +++ b/src/V3EmitCSyms.cpp @@ -631,7 +631,7 @@ void EmitCSyms::emitSymImp() { emitSymImpPreamble(); if (v3Global.opt.savable()) { - for (int de = 0; de < 2; ++de) { + for (const bool& de : {false, true}) { const string classname = de ? "VerilatedDeserialize" : "VerilatedSerialize"; const string funcname = de ? "__Vdeserialize" : "__Vserialize"; const string op = de ? ">>" : "<<"; diff --git a/src/V3EmitMk.cpp b/src/V3EmitMk.cpp index 23e89fea8..b27ef2fbb 100644 --- a/src/V3EmitMk.cpp +++ b/src/V3EmitMk.cpp @@ -80,7 +80,7 @@ public: of.puts("\n### Object file lists...\n"); for (int support = 0; support < 3; ++support) { - for (int slow = 0; slow < 2; ++slow) { + for (const bool& slow : {false, true}) { if (support == 2) { of.puts("# Global classes, need linked once per executable"); } else if (support) { diff --git a/src/V3Partition.cpp b/src/V3Partition.cpp index 0bff6bde5..1b9441c0c 100644 --- a/src/V3Partition.cpp +++ b/src/V3Partition.cpp @@ -996,8 +996,7 @@ static V3GraphEdge* partBlastEdgep(GraphWay way, V3GraphEdge* edgep) { // non-transitive edges only ever increase. static void partMergeEdgesFrom(V3Graph* mtasksp, LogicMTask* recipientp, LogicMTask* donorp, V3Scoreboard* sbp) { - for (unsigned wi = 0; wi < 2; ++wi) { - const GraphWay way = wi ? GraphWay::REVERSE : GraphWay::FORWARD; + for (const auto& way : {GraphWay::FORWARD, GraphWay::REVERSE}) { for (V3GraphEdge* edgep = donorp->beginp(way); edgep; edgep = partBlastEdgep(way, edgep)) { MTaskEdge* tedgep = MTaskEdge::cast(edgep); if (sbp && !tedgep->removedFromSb()) sbp->removeElem(tedgep); From 8a9d6d225be0eed181b512c8bdc25094eb2293bb Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Sun, 11 Jul 2021 14:40:41 +0100 Subject: [PATCH 17/84] Internals: Add source file/line info to BROKEN_RTN messages --- src/V3Ast.h | 2 +- test_regress/t/t_func_tasknsvar_bad.out | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/V3Ast.h b/src/V3Ast.h index 1bceac0b6..9f8d12b91 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -50,7 +50,7 @@ using MTaskIdSet = std::set; // Set of mtaskIds for Var sorting // For broken() function, return error string if have a match #define BROKEN_RTN(test) \ do { \ - if (VL_UNCOVERABLE(test)) return #test; \ + if (VL_UNCOVERABLE(test)) return "'" #test "' @ " __FILE__ ":" VL_STRINGIFY(__LINE__); \ } while (false) // For broken() function, return error string if a base of this class has a match #define BROKEN_BASE_RTN(test) \ diff --git a/test_regress/t/t_func_tasknsvar_bad.out b/test_regress/t/t_func_tasknsvar_bad.out index b2338cb62..723df5dc4 100644 --- a/test_regress/t/t_func_tasknsvar_bad.out +++ b/test_regress/t/t_func_tasknsvar_bad.out @@ -2,7 +2,7 @@ 16 | foo(bus_we_select_from[2]); | ^ ... For error description see https://verilator.org/warn/TASKNSVAR?v=latest -%Error: Internal Error: t/t_func_tasknsvar_bad.v:10:7: ../V3Broken.cpp:#: Broken link in node (or something without maybePointedTo): m_varScopep && !m_varScopep->brokeExists() +%Error: Internal Error: t/t_func_tasknsvar_bad.v:10:7: ../V3Broken.cpp:#: Broken link in node (or something without maybePointedTo): 'm_varScopep && !m_varScopep->brokeExists()' @ ../V3AstNodes.cpp:51 10 | sig = '1; | ^~~ ... See the manual at https://verilator.org/verilator_doc.html for more assistance. From 8073e8bb463c0458f4dcddecc3b365d30c0b60aa Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Sun, 11 Jul 2021 16:51:47 +0100 Subject: [PATCH 18/84] Fix typo --- docs/guide/contributors.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guide/contributors.rst b/docs/guide/contributors.rst index 1ed5a867f..849fa795b 100644 --- a/docs/guide/contributors.rst +++ b/docs/guide/contributors.rst @@ -13,7 +13,7 @@ When possible, please instead report bugs at `Verilator Issues Primary author is Wilson Snyder . -Major concepts by Paul Wasson, Duane Galbi, John Coiner, Giza Lore, Yutetsu +Major concepts by Paul Wasson, Duane Galbi, John Coiner, Geza Lore, Yutetsu Takatsukasa, and Jie Xu. From 5ad3c4e499d4533d397f4675c34160f19eb3b2aa Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Sat, 10 Jul 2021 16:13:05 +0100 Subject: [PATCH 19/84] Internals: Speed up and improve V3Broken This patch makes OpenTitan verilation with --debug-check 22% faster, and the same with --debug --no-dump-tree 91% faster. Functionality is the same (including when VL_LEAK_CHECKS is defined), except V3Broken can now always find duplicate references via child/next pointers if the target node is not `maybePointedTo()` (previously this only happened when compiled with VL_LEAK_CHECKS). The main change relates to storing the v3Broken traversal state in the AstNode by stealing a byte from what used to be unused flags. We retain an unordered_set only for marking pointers as valid to be referenced via a non-child/non-next member pointer. --- src/V3Ast.h | 28 +++-- src/V3Broken.cpp | 288 ++++++++++++++++++----------------------------- src/V3Broken.h | 13 ++- 3 files changed, 137 insertions(+), 192 deletions(-) diff --git a/src/V3Ast.h b/src/V3Ast.h index 9f8d12b91..9a26091ea 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -24,6 +24,7 @@ #include "V3FileLine.h" #include "V3Number.h" #include "V3Global.h" +#include "V3Broken.h" #include #include @@ -1335,21 +1336,29 @@ class AstNode VL_NOT_FINAL { // ^ ASTNODE_PREFETCH depends on above ordering of members // AstType is 2 bytes, so we can stick another 6 bytes after it to utilize what would - // otherwise be padding (on a 64-bit system). We stick the attribute flags and the clone - // count here. + // otherwise be padding (on a 64-bit system). We stick the attribute flags, broken state, + // and the clone count here. struct { bool didWidth : 1; // Did V3Width computation bool doingWidth : 1; // Inside V3Width bool protect : 1; // Protect name if protection is on - uint16_t unused : 13; // Space for more flags here (there must be 16 bits in total) + // Space for more flags here (there must be 8 bits in total) + uint8_t unused : 5; } m_flags; // Attribute flags + // State variable used by V3Broken for consistency checking. The top bit of this is byte is a + // flag, representing V3Broken is currently proceeding under this node. The bottom 7 bits are + // a generation number. This is hot when --debug-checks so we access as a whole to avoid bit + // field masking resulting in unnecessary read-modify-write ops. + uint8_t m_brokenState = 0; + int m_cloneCnt; // Sequence number for when last clone was made #if defined(__x86_64__) && defined(__gnu_linux__) // Only assert this on known platforms, as it only affects performance, not correctness - static_assert(sizeof(m_type) + sizeof(m_flags) + sizeof(m_cloneCnt) <= sizeof(void*), + static_assert(sizeof(m_type) + sizeof(m_flags) + sizeof(m_brokenState) + sizeof(m_cloneCnt) + <= sizeof(void*), "packing requires padding"); #endif @@ -1466,9 +1475,14 @@ public: AstNode* firstAbovep() const { // Returns nullptr when second or later in list return ((backp() && backp()->nextp() != this) ? backp() : nullptr); } - bool brokeExists() const; - bool brokeExistsAbove() const; - bool brokeExistsBelow() const; + uint8_t brokenState() const { return m_brokenState; } + void brokenState(uint8_t value) { m_brokenState = value; } + + // Used by AstNode::broken() + bool brokeExists() const { return V3Broken::isLinkable(this); } + bool brokeExistsAbove() const { return brokeExists() && (m_brokenState >> 7); } + bool brokeExistsBelow() const { return brokeExists() && !(m_brokenState >> 7); } + // Note: brokeExistsBelow is not quite precise, as it is true for sibling nodes as well // CONSTRUCTORS virtual ~AstNode() = default; diff --git a/src/V3Broken.cpp b/src/V3Broken.cpp index b9bae7707..49190f2eb 100644 --- a/src/V3Broken.cpp +++ b/src/V3Broken.cpp @@ -32,202 +32,129 @@ // This visitor does not edit nodes, and is called at error-exit, so should use constant iterators #include "V3AstConstOnly.h" -#include #include #include //###################################################################### +// Generation counter for AstNode::m_brokenState -class BrokenTable VL_NOT_FINAL : public AstNVisitor { - // Table of brokenExists node pointers +static class BrokenCntGlobal { + // This is a 7 bit generation counter, stored in the bottom 7 bits of AstNode::m_brokenState, + // used to mark a node as being present under the root AstNetlist in the current traversal. A + // value 0 is invalid, as the AstNode constructor uses that to initialize m_brokenState + static constexpr uint8_t MIN_VALUE = 1; + static constexpr uint8_t MAX_VALUE = 127; + + uint8_t m_count = MIN_VALUE; + +public: + uint8_t get() { + UASSERT(MIN_VALUE <= m_count && m_count <= MAX_VALUE, "Invalid generation number"); + return m_count; + } + + void inc() { + ++m_count; + if (m_count > MAX_VALUE) m_count = MIN_VALUE; + } +} s_brokenCntGlobal; + +//###################################################################### +// Table of allocated AstNode pointers + +static class AllocTable final { private: // MEMBERS - // For each node, we keep if it exists or not. - using NodeMap = std::unordered_map; // Performance matters (when --debug) - static NodeMap s_nodes; // Set of all nodes that exist - // BITMASK - enum { FLAG_ALLOCATED = 0x01 }; // new() and not delete()ed - enum { FLAG_IN_TREE = 0x02 }; // Is in netlist tree - enum { FLAG_LINKABLE = 0x04 }; // Is in netlist tree, can be linked to - enum { FLAG_LEAKED = 0x08 }; // Known to have been leaked - enum { FLAG_UNDER_NOW = 0x10 }; // Is in tree as parent of current node + std::unordered_set m_allocated; // Set of all nodes allocated but not freed public: // METHODS - static void deleted(const AstNode* nodep) { - // Called by operator delete on any node - only if VL_LEAK_CHECKS - if (debug() >= 9) cout << "-nodeDel: " << cvtToHex(nodep) << endl; - const auto iter = s_nodes.find(nodep); - UASSERT_OBJ(!(iter == s_nodes.end() || !(iter->second & FLAG_ALLOCATED)), - reinterpret_cast(nodep), - "Deleting AstNode object that was never tracked or already deleted"); - if (iter != s_nodes.end()) s_nodes.erase(iter); - } -#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ == 4 - // GCC 4.4.* compiler warning bug, https://gcc.gnu.org/bugzilla/show_bug.cgi?id=39390 -#pragma GCC diagnostic ignored "-Wstrict-aliasing" -#endif - static void addNewed(const AstNode* nodep) { + void addNewed(const AstNode* nodep) { // Called by operator new on any node - only if VL_LEAK_CHECKS - if (debug() >= 9) cout << "-nodeNew: " << cvtToHex(nodep) << endl; - const auto iter = s_nodes.find(nodep); - UASSERT_OBJ(!(iter != s_nodes.end() && (iter->second & FLAG_ALLOCATED)), nodep, - "Newing AstNode object that is already allocated"); - if (iter == s_nodes.end()) { - const int flags = FLAG_ALLOCATED; // This int needed to appease GCC 4.1.2 - s_nodes.emplace(nodep, flags); + // LCOV_EXCL_START + if (VL_UNCOVERABLE(!m_allocated.emplace(nodep).second)) { + nodep->v3fatalSrc("Newing AstNode object that is already allocated"); } + // LCOV_EXCL_STOP } - static void setUnder(const AstNode* nodep, bool flag) { - // Called by BrokenCheckVisitor when each node entered/exited - if (!okIfLinkedTo(nodep)) return; - const auto iter = s_nodes.find(nodep); - if (iter != s_nodes.end()) { - iter->second &= ~FLAG_UNDER_NOW; - if (flag) iter->second |= FLAG_UNDER_NOW; + void deleted(const AstNode* nodep) { + // Called by operator delete on any node - only if VL_LEAK_CHECKS + // LCOV_EXCL_START + if (VL_UNCOVERABLE(m_allocated.erase(nodep) == 0)) { + nodep->v3fatalSrc("Deleting AstNode object that was not allocated or already freed"); } + // LCOV_EXCL_STOP } - static void addInTree(AstNode* nodep, bool linkable) { -#ifndef VL_LEAK_CHECKS - // cppcheck-suppress knownConditionTrueFalse - if (!linkable) return; // save some time, else the map will get huge! -#endif - const auto iter = s_nodes.find(nodep); - if (VL_UNCOVERABLE(iter == s_nodes.end())) { -#ifdef VL_LEAK_CHECKS - nodep->v3fatalSrc("AstNode is in tree, but not allocated"); -#endif - } else { -#ifdef VL_LEAK_CHECKS - UASSERT_OBJ(iter->second & FLAG_ALLOCATED, nodep, - "AstNode is in tree, but not allocated"); -#endif - UASSERT_OBJ(!(iter->second & FLAG_IN_TREE), nodep, - "AstNode is already in tree at another location"); - } - const int or_flags = FLAG_IN_TREE | (linkable ? FLAG_LINKABLE : 0); - if (iter == s_nodes.end()) { - s_nodes.emplace(nodep, or_flags); - } else { - iter->second |= or_flags; - } - } - static bool isAllocated(const AstNode* nodep) { - // Some generic node has a pointer to this node. Is it allocated? - // Use this when might not be in tree; otherwise use okIfLinkedTo(). -#ifdef VL_LEAK_CHECKS - const auto iter = s_nodes.find(nodep); - if (iter == s_nodes.end()) return false; - if (!(iter->second & FLAG_ALLOCATED)) return false; -#endif - return true; - } - static bool okIfLinkedTo(const AstNode* nodep) { - // Some node in tree has a pointer to this node. Is it kosher? - const auto iter = s_nodes.find(nodep); - if (iter == s_nodes.end()) return false; -#ifdef VL_LEAK_CHECKS - if (!(iter->second & FLAG_ALLOCATED)) return false; -#endif - if (!(iter->second & FLAG_IN_TREE)) return false; - if (!(iter->second & FLAG_LINKABLE)) return false; - return true; - } - static bool okIfAbove(const AstNode* nodep) { - // Must be linked to and below current node - if (!okIfLinkedTo(nodep)) return false; - const auto iter = s_nodes.find(nodep); - if (iter == s_nodes.end()) return false; - if ((iter->second & FLAG_UNDER_NOW)) return false; - return true; - } - static bool okIfBelow(const AstNode* nodep) { - // Must be linked to and below current node - if (!okIfLinkedTo(nodep)) return false; - const auto iter = s_nodes.find(nodep); - if (iter == s_nodes.end()) return false; - if (!(iter->second & FLAG_UNDER_NOW)) return false; - return true; - } - static void prepForTree() { -#ifndef VL_LEAK_CHECKS - s_nodes.clear(); -#else - for (NodeMap::iterator it = s_nodes.begin(); it != s_nodes.end(); ++it) { - it->second &= ~FLAG_IN_TREE; - it->second &= ~FLAG_LINKABLE; - } -#endif - } - static void doneWithTree() { - for (int backs = 0; backs < 2; - backs++) { // Those with backp() are probably under one leaking without - for (NodeMap::iterator it = s_nodes.begin(); it != s_nodes.end(); ++it) { - // LCOV_EXCL_START - if (VL_UNCOVERABLE((it->second & FLAG_ALLOCATED) && !(it->second & FLAG_IN_TREE) - && !(it->second & FLAG_LEAKED) - && (it->first->backp() ? backs == 1 : backs == 0))) { + bool isAllocated(const AstNode* nodep) const { return m_allocated.count(nodep) != 0; } + void checkForLeaks() { + if (!v3Global.opt.debugCheck()) return; + const uint8_t brokenCntCurrent = s_brokenCntGlobal.get(); + + // Those with backp() are probably under a parent that was leaked and has no backp() + for (const bool withBack : {false, true}) { + for (const AstNode* const nodep : m_allocated) { + // LCOV_EXCL_START + // Most likely not leaked, so check that first + if (VL_UNCOVERABLE(nodep->brokenState() != brokenCntCurrent)) { + const bool hasBack = nodep->backp() != nullptr; + if (hasBack != withBack) continue; // Use only AstNode::dump instead of the virtual one, as there // may be varp() and other cross links that are bad. - if (v3Global.opt.debugCheck()) { - // When get this message, find what forgot to delete the - // node by running GDB, where for node "" use: - // watch AstNode::s_editCntGbl==#### - // run - // bt - std::cerr << "%Error: LeakedNode" - << (it->first->backp() ? "Back: " : ": "); - AstNode* rawp - = const_cast(static_cast(it->first)); - rawp->AstNode::dump(std::cerr); - std::cerr << endl; - V3Error::incErrors(); - } - it->second |= FLAG_LEAKED; + // When get this message, find what forgot to delete the + // node by running GDB, where for node "" use: + // watch AstNode::s_editCntGbl==#### + // run + // bt + std::cerr << "%Error: LeakedNode" << (withBack ? "with back pointer: " : ": "); + nodep->AstNode::dump(std::cerr); + std::cerr << endl; + V3Error::incErrors(); } // LCOV_EXCL_STOP } } } +} s_allocTable; - // CONSTRUCTORS - BrokenTable() = default; - virtual ~BrokenTable() override = default; -}; - -BrokenTable::NodeMap BrokenTable::s_nodes; - -bool AstNode::brokeExists() const { - // Called by node->broken() routines to do table lookup - return BrokenTable::okIfLinkedTo(this); -} -bool AstNode::brokeExistsAbove() const { - // Called by node->broken() routines to do table lookup - return BrokenTable::okIfBelow(this); -} -bool AstNode::brokeExistsBelow() const { - // Called by node->broken() routines to do table lookup - return BrokenTable::okIfAbove(this); -} +void V3Broken::addNewed(const AstNode* nodep) { s_allocTable.addNewed(nodep); } +void V3Broken::deleted(const AstNode* nodep) { s_allocTable.deleted(nodep); } //###################################################################### +// Table of AstNode pointers that can be linked to via member pointers + +static class LinkableTable final { +private: + // MEMBERS + std::unordered_set m_linkable; // Set of all nodes allocated but not freed + +public: + // METHODS + void clear() { m_linkable.clear(); } + inline void addLinkable(const AstNode* nodep) { m_linkable.emplace(nodep); } + inline bool isLinkable(const AstNode* nodep) const { return m_linkable.count(nodep) != 0; } +} s_linkableTable; + +bool V3Broken::isLinkable(const AstNode* nodep) { return s_linkableTable.isLinkable(nodep); } + +//###################################################################### +// Mark every node in the tree class BrokenMarkVisitor final : public AstNVisitor { - // Mark every node in the tree private: - // NODE STATE - // Nothing! // This may be called deep inside other routines - // // so userp and friends may not be used - // METHODS - void processAndIterate(AstNode* nodep) { - BrokenTable::addInTree(nodep, nodep->maybePointedTo()); - iterateChildrenConst(nodep); - } + const uint8_t m_brokenCntCurrent = s_brokenCntGlobal.get(); + // VISITORS virtual void visit(AstNode* nodep) override { - // Process not just iterate - processAndIterate(nodep); +#ifdef VL_LEAK_CHECKS + UASSERT_OBJ(s_allocTable.isAllocated(nodep), nodep, + "AstNode is in tree, but not allocated"); +#endif + UASSERT_OBJ(nodep->brokenState() != m_brokenCntCurrent, nodep, + "AstNode is already in tree at another location"); + if (nodep->maybePointedTo()) s_linkableTable.addLinkable(nodep); + nodep->brokenState(m_brokenCntCurrent); + iterateChildrenConst(nodep); } public: @@ -237,11 +164,15 @@ public: }; //###################################################################### -// Broken state, as a visitor of each AstNode +// Check every node in tree class BrokenCheckVisitor final : public AstNVisitor { bool m_inScope = false; // Under AstScope + // Constants for marking we are under/not under a node + const uint8_t m_brokenCntCurrentNotUnder = s_brokenCntGlobal.get(); // Top bit is clear + const uint8_t m_brokenCntCurrentUnder = m_brokenCntCurrentNotUnder | 0x80; // Top bit is set + // Current CFunc, if any const AstCFunc* m_cfuncp = nullptr; // All local variables declared in current function @@ -259,7 +190,7 @@ private: } void processEnter(AstNode* nodep) { - BrokenTable::setUnder(nodep, true); + nodep->brokenState(m_brokenCntCurrentUnder); const char* whyp = nodep->broken(); UASSERT_OBJ(!whyp, nodep, "Broken link in node (or something without maybePointedTo): " << whyp); @@ -283,7 +214,7 @@ private: } checkWidthMin(nodep); } - void processExit(AstNode* nodep) { BrokenTable::setUnder(nodep, false); } + void processExit(AstNode* nodep) { nodep->brokenState(m_brokenCntCurrentNotUnder); } void processAndIterate(AstNode* nodep) { processEnter(nodep); iterateChildrenConst(nodep); @@ -401,7 +332,7 @@ public: }; //###################################################################### -// Broken class functions +// Broken check entry point void V3Broken::brokenAll(AstNetlist* nodep) { // UINFO(9, __FUNCTION__ << ": " << endl); @@ -411,24 +342,23 @@ void V3Broken::brokenAll(AstNetlist* nodep) { UINFO(1, "Broken called under broken, skipping recursion.\n"); // LCOV_EXCL_LINE } else { inBroken = true; - BrokenTable::prepForTree(); BrokenMarkVisitor mvisitor(nodep); BrokenCheckVisitor cvisitor(nodep); - BrokenTable::doneWithTree(); + s_allocTable.checkForLeaks(); + s_linkableTable.clear(); + s_brokenCntGlobal.inc(); inBroken = false; } } -void V3Broken::addNewed(AstNode* nodep) { BrokenTable::addNewed(nodep); } -void V3Broken::deleted(AstNode* nodep) { BrokenTable::deleted(nodep); } -bool V3Broken::isAllocated(AstNode* nodep) { return BrokenTable::isAllocated(nodep); } +//###################################################################### +// Self test + void V3Broken::selfTest() { - // Warmup addNewed and deleted for coverage, as otherwise only with VL_LEAK_CHECKS - FileLine* fl = new FileLine(FileLine::commandLineFilename()); - auto* newp = new AstBegin(fl, "[EditWrapper]", nullptr); -#ifndef VL_LEAK_CHECKS + // Exercise addNewed and deleted for coverage, as otherwise only used with VL_LEAK_CHECKS + FileLine* const fl = new FileLine(FileLine::commandLineFilename()); + const AstNode* const newp = new AstBegin(fl, "[EditWrapper]", nullptr); addNewed(newp); deleted(newp); -#endif VL_DO_DANGLING(delete newp, newp); } diff --git a/src/V3Broken.h b/src/V3Broken.h index 16103baa0..94387ef06 100644 --- a/src/V3Broken.h +++ b/src/V3Broken.h @@ -20,17 +20,18 @@ #include "config_build.h" #include "verilatedos.h" -#include "V3Error.h" -#include "V3Ast.h" - //============================================================================ +// Forward declare so we can include this in V3Ast.h +class AstNode; +class AstNetlist; + class V3Broken final { public: static void brokenAll(AstNetlist* nodep); - static void addNewed(AstNode* nodep); - static void deleted(AstNode* nodep); - static bool isAllocated(AstNode* nodep); + static bool isLinkable(const AstNode* nodep); + static void addNewed(const AstNode* nodep); + static void deleted(const AstNode* nodep); static void selfTest(); }; From 8ecdc85cf760d98985a28f450ec7f36e2760afe3 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sun, 11 Jul 2021 18:42:01 -0400 Subject: [PATCH 20/84] Internals: C++11 style cleanups. No functional change. --- src/V3Active.cpp | 28 ++++++++++----------- src/V3ActiveTop.cpp | 2 +- src/V3Assert.cpp | 2 +- src/V3AssertPre.cpp | 2 +- src/V3Ast.cpp | 2 +- src/V3AstNodes.h | 6 ++--- src/V3Begin.cpp | 2 +- src/V3Branch.cpp | 2 +- src/V3Broken.cpp | 4 +-- src/V3CCtors.cpp | 49 +++++++++++++++++++------------------ src/V3CUse.cpp | 45 ++++++++++++++++++---------------- src/V3Case.cpp | 4 +-- src/V3Cast.cpp | 7 +++--- src/V3Cdc.cpp | 6 ++--- src/V3Changed.cpp | 6 ++--- src/V3Class.cpp | 2 +- src/V3Clean.cpp | 2 +- src/V3Clock.cpp | 2 +- src/V3Combine.cpp | 2 +- src/V3Const.cpp | 55 +++++++++++++++++++++--------------------- src/V3Coverage.cpp | 2 +- src/V3CoverageJoin.cpp | 2 +- src/V3Dead.cpp | 12 ++++----- src/V3Delayed.cpp | 2 +- src/V3Depth.cpp | 12 ++++----- src/V3DepthBlock.cpp | 2 +- src/V3Descope.cpp | 2 +- src/V3DupFinder.cpp | 2 +- src/V3EmitCMain.cpp | 6 ++--- src/V3EmitCMake.cpp | 4 +-- src/V3EmitCModel.cpp | 4 +-- src/V3EmitCSyms.cpp | 6 ++--- src/V3EmitV.cpp | 6 ++--- src/V3EmitXml.cpp | 6 ++--- src/V3Expand.cpp | 2 +- src/V3File.cpp | 6 ++--- src/V3File.h | 6 ++--- src/V3Gate.cpp | 16 ++++++------ src/V3GenClk.cpp | 4 +-- src/V3Global.h | 4 +-- src/V3Graph.cpp | 2 +- src/V3GraphDfa.h | 4 +-- src/V3Hasher.cpp | 4 +-- src/V3HierBlock.cpp | 4 +-- src/V3Inline.cpp | 10 ++++---- src/V3Inst.cpp | 10 ++++---- src/V3InstrCount.cpp | 2 +- src/V3Life.cpp | 6 ++--- src/V3LifePost.cpp | 4 +-- src/V3LinkCells.cpp | 2 +- src/V3LinkDot.cpp | 12 ++++----- src/V3LinkInc.cpp | 2 +- src/V3LinkJump.cpp | 2 +- src/V3LinkLValue.cpp | 4 +-- src/V3LinkParse.cpp | 2 +- src/V3LinkResolve.cpp | 4 +-- src/V3Localize.cpp | 2 +- src/V3MergeCond.cpp | 8 +++--- src/V3Name.cpp | 2 +- src/V3Options.cpp | 2 +- src/V3Order.cpp | 14 +++++------ src/V3Os.cpp | 6 ++--- src/V3Param.cpp | 2 +- src/V3Partition.cpp | 10 ++++---- src/V3Premit.cpp | 2 +- src/V3Randomize.cpp | 4 +-- src/V3Reloop.cpp | 2 +- src/V3Scope.cpp | 4 +-- src/V3Slice.cpp | 2 +- src/V3Split.cpp | 8 +++--- src/V3SplitAs.cpp | 8 +++--- src/V3SplitVar.cpp | 4 +-- src/V3Stats.cpp | 2 +- src/V3StatsReport.cpp | 2 +- src/V3Subst.cpp | 6 ++--- src/V3SymTable.h | 2 +- src/V3TSP.cpp | 2 +- src/V3Table.cpp | 18 +++++++------- src/V3Task.cpp | 8 +++--- src/V3Trace.cpp | 2 +- src/V3TraceDecl.cpp | 2 +- src/V3Tristate.cpp | 6 ++--- src/V3Undriven.cpp | 2 +- src/V3Unknown.cpp | 2 +- src/V3Waiver.cpp | 2 +- src/V3Width.cpp | 16 ++++++------ src/VlcBucket.h | 2 +- src/VlcOptions.h | 8 +++--- src/VlcPoint.h | 2 +- src/VlcTest.h | 2 +- src/VlcTop.cpp | 18 +++++++------- 91 files changed, 300 insertions(+), 294 deletions(-) diff --git a/src/V3Active.cpp b/src/V3Active.cpp index ee7debb29..d00dce620 100644 --- a/src/V3Active.cpp +++ b/src/V3Active.cpp @@ -64,9 +64,9 @@ private: public: LatchDetectGraphVertex(V3Graph* graphp, const string& name, VertexType type = VT_BLOCK) - : V3GraphVertex(graphp) - , m_name(name) - , m_type(type) {} + : V3GraphVertex{graphp} + , m_name{name} + , m_type{type} {} virtual string name() const { return m_name + " " + typestr(); } virtual string dotColor() const { return user() ? "green" : "black"; } virtual int type() const { return m_type; } @@ -142,17 +142,17 @@ public: // Add a new control path and connect it to its parent LatchDetectGraphVertex* addPathVertex(LatchDetectGraphVertex* parent, const string& name, bool branch = false) { - m_curVertexp = new LatchDetectGraphVertex(this, name, + m_curVertexp = new LatchDetectGraphVertex{this, name, branch ? LatchDetectGraphVertex::VT_BRANCH - : LatchDetectGraphVertex::VT_BLOCK); - new V3GraphEdge(this, parent, m_curVertexp, 1); + : LatchDetectGraphVertex::VT_BLOCK}; + new V3GraphEdge{this, parent, m_curVertexp, 1}; return m_curVertexp; } // Add a new output variable vertex and store a pointer to it in the user1 field of the // variables AstNode LatchDetectGraphVertex* addOutputVertex(AstVarRef* nodep) { LatchDetectGraphVertex* outVertexp - = new LatchDetectGraphVertex(this, nodep->name(), LatchDetectGraphVertex::VT_OUTPUT); + = new LatchDetectGraphVertex{this, nodep->name(), LatchDetectGraphVertex::VT_OUTPUT}; nodep->varp()->user1p(outVertexp); m_outputs.push_back(nodep); return outVertexp; @@ -437,7 +437,7 @@ private: virtual void visit(AstInitial* nodep) override { // Relink to IACTIVE, unless already under it UINFO(4, " INITIAL " << nodep << endl); - ActiveDlyVisitor dlyvisitor(nodep, ActiveDlyVisitor::CT_INITIAL); + ActiveDlyVisitor dlyvisitor{nodep, ActiveDlyVisitor::CT_INITIAL}; AstActive* wantactivep = m_namer.getIActive(nodep->fileline()); nodep->unlinkFrBack(); wantactivep->addStmtsp(nodep); @@ -470,7 +470,7 @@ private: VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep); return; } - ActiveDlyVisitor dlyvisitor(nodep, ActiveDlyVisitor::CT_INITIAL); + ActiveDlyVisitor dlyvisitor{nodep, ActiveDlyVisitor::CT_INITIAL}; if (!m_scopeFinalp) { m_scopeFinalp = new AstCFunc( nodep->fileline(), "_final_" + m_namer.scopep()->nameDotless(), m_namer.scopep()); @@ -540,14 +540,14 @@ private: // Warn and/or convert any delayed assignments if (combo && !sequent) { - ActiveLatchCheckVisitor latchvisitor(nodep, kwd); + ActiveLatchCheckVisitor latchvisitor{nodep, kwd}; if (kwd == VAlwaysKwd::ALWAYS_LATCH) { - ActiveDlyVisitor dlyvisitor(nodep, ActiveDlyVisitor::CT_LATCH); + ActiveDlyVisitor dlyvisitor{nodep, ActiveDlyVisitor::CT_LATCH}; } else { - ActiveDlyVisitor dlyvisitor(nodep, ActiveDlyVisitor::CT_COMBO); + ActiveDlyVisitor dlyvisitor{nodep, ActiveDlyVisitor::CT_COMBO}; } } else if (!combo && sequent) { - ActiveDlyVisitor dlyvisitor(nodep, ActiveDlyVisitor::CT_SEQ); + ActiveDlyVisitor dlyvisitor{nodep, ActiveDlyVisitor::CT_SEQ}; } } virtual void visit(AstAlways* nodep) override { @@ -620,6 +620,6 @@ public: void V3Active::activeAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { ActiveVisitor visitor(nodep); } // Destruct before checking + { ActiveVisitor visitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("active", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3ActiveTop.cpp b/src/V3ActiveTop.cpp index 0745c8ed7..6706dc357 100644 --- a/src/V3ActiveTop.cpp +++ b/src/V3ActiveTop.cpp @@ -136,6 +136,6 @@ public: void V3ActiveTop::activeTopAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { ActiveTopVisitor visitor(nodep); } // Destruct before checking + { ActiveTopVisitor visitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("activetop", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3Assert.cpp b/src/V3Assert.cpp index 22ae310ba..40aa060da 100644 --- a/src/V3Assert.cpp +++ b/src/V3Assert.cpp @@ -474,6 +474,6 @@ public: void V3Assert::assertAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { AssertVisitor visitor(nodep); } // Destruct before checking + { AssertVisitor visitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("assert", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3AssertPre.cpp b/src/V3AssertPre.cpp index 9049d6146..12bd7fddc 100644 --- a/src/V3AssertPre.cpp +++ b/src/V3AssertPre.cpp @@ -205,6 +205,6 @@ public: void V3AssertPre::assertPreAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { AssertPreVisitor visitor(nodep); } // Destruct before checking + { AssertPreVisitor visitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("assertpre", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3Ast.cpp b/src/V3Ast.cpp index 1f87e4586..ae9cb530c 100644 --- a/src/V3Ast.cpp +++ b/src/V3Ast.cpp @@ -1124,7 +1124,7 @@ void AstNode::dumpTreeFile(const string& filename, bool append, bool doDump, boo if (doDump) { { // Write log & close UINFO(2, "Dumping " << filename << endl); - const std::unique_ptr logsp(V3File::new_ofstream(filename, append)); + const std::unique_ptr logsp{V3File::new_ofstream(filename, append)}; if (logsp->fail()) v3fatal("Can't write " << filename); *logsp << "Verilator Tree Dump (format 0x3900) from to \n"; diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index f75c67a82..120121d3b 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -8497,9 +8497,9 @@ private: public: AstNodeCoverOrAssert(AstType t, FileLine* fl, AstNode* propp, AstNode* passsp, bool immediate, const string& name = "") - : AstNodeStmt(t, fl) - , m_immediate(immediate) - , m_name(name) { + : AstNodeStmt{t, fl} + , m_immediate{immediate} + , m_name{name} { addOp1p(propp); addNOp4p(passsp); } diff --git a/src/V3Begin.cpp b/src/V3Begin.cpp index 5bee0a7f4..404b96803 100644 --- a/src/V3Begin.cpp +++ b/src/V3Begin.cpp @@ -308,7 +308,7 @@ void V3Begin::debeginAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); { BeginState state; - { BeginVisitor bvisitor(nodep, &state); } + { BeginVisitor bvisitor{nodep, &state}; } if (state.anyFuncInBegin()) { BeginRelinkVisitor brvisitor(nodep, &state); } } // Destruct before checking V3Global::dumpCheckGlobalTree("begin", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); diff --git a/src/V3Branch.cpp b/src/V3Branch.cpp index b1a5de551..ff3413815 100644 --- a/src/V3Branch.cpp +++ b/src/V3Branch.cpp @@ -123,5 +123,5 @@ public: void V3Branch::branchAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - BranchVisitor visitor(nodep); + BranchVisitor visitor{nodep}; } diff --git a/src/V3Broken.cpp b/src/V3Broken.cpp index 49190f2eb..a1b081523 100644 --- a/src/V3Broken.cpp +++ b/src/V3Broken.cpp @@ -342,8 +342,8 @@ void V3Broken::brokenAll(AstNetlist* nodep) { UINFO(1, "Broken called under broken, skipping recursion.\n"); // LCOV_EXCL_LINE } else { inBroken = true; - BrokenMarkVisitor mvisitor(nodep); - BrokenCheckVisitor cvisitor(nodep); + BrokenMarkVisitor mvisitor{nodep}; + BrokenCheckVisitor cvisitor{nodep}; s_allocTable.checkForLeaks(); s_linkableTable.clear(); s_brokenCntGlobal.inc(); diff --git a/src/V3CCtors.cpp b/src/V3CCtors.cpp index 2becaa25d..d1b9daba0 100644 --- a/src/V3CCtors.cpp +++ b/src/V3CCtors.cpp @@ -60,7 +60,7 @@ private: AstCFunc* makeNewFunc() { const int funcNum = m_newFunctions.size(); const string funcName = m_basename + "_" + cvtToStr(funcNum); - AstCFunc* const funcp = new AstCFunc(m_modp->fileline(), funcName, nullptr, "void"); + AstCFunc* const funcp = new AstCFunc{m_modp->fileline(), funcName, nullptr, "void"}; funcp->isStatic(false); funcp->isLoose(!m_type.isClass()); funcp->declPrivate(true); @@ -74,7 +74,7 @@ private: preventUnusedStmt = "if (false && first) {} // Prevent unused\n"; } if (!preventUnusedStmt.empty()) { - funcp->addStmtsp(new AstCStmt(m_modp->fileline(), preventUnusedStmt)); + funcp->addStmtsp(new AstCStmt{m_modp->fileline(), preventUnusedStmt}); } m_modp->addStmtp(funcp); m_numStmts = 0; @@ -91,9 +91,9 @@ public: } V3CCtorsBuilder(AstNodeModule* nodep, const string& basename, VCtorType type) - : m_modp(nodep) + : m_modp{nodep} , m_basename{basename} - , m_type(type) { + , m_type{type} { // Note: The constructor is always called, even if empty, so we must always create at least // one. m_newFunctions.push_back(makeNewFunc()); @@ -108,7 +108,7 @@ public: AstCFunc* const rootFuncp = makeNewFunc(); rootFuncp->name(m_basename); for (AstCFunc* const funcp : m_newFunctions) { - AstCCall* const callp = new AstCCall(m_modp->fileline(), funcp); + AstCCall* const callp = new AstCCall{m_modp->fileline(), funcp}; if (m_type.isClass()) { callp->argTypes("vlSymsp"); } else { @@ -127,8 +127,9 @@ private: //###################################################################### void V3CCtors::evalAsserts() { - AstNodeModule* modp = v3Global.rootp()->modulesp(); // Top module wrapper - AstCFunc* funcp = new AstCFunc(modp->fileline(), "_eval_debug_assertions", nullptr, "void"); + AstNodeModule* const modp = v3Global.rootp()->modulesp(); // Top module wrapper + AstCFunc* const funcp + = new AstCFunc{modp->fileline(), "_eval_debug_assertions", nullptr, "void"}; funcp->declPrivate(true); funcp->isStatic(false); funcp->isLoose(true); @@ -136,7 +137,7 @@ void V3CCtors::evalAsserts() { funcp->ifdef("VL_DEBUG"); modp->addStmtp(funcp); for (AstNode* np = modp->stmtsp(); np; np = np->nextp()) { - if (AstVar* varp = VN_CAST(np, Var)) { + if (AstVar* const varp = VN_CAST(np, Var)) { if (varp->isPrimaryInish() && !varp->isSc()) { if (AstBasicDType* basicp = VN_CAST(varp->dtypeSkipRefp(), BasicDType)) { const int storedWidth = basicp->widthAlignBytes() * 8; @@ -144,22 +145,22 @@ void V3CCtors::evalAsserts() { if (lastWordWidth != 0) { // if (signal & CONST(upper_non_clean_mask)) { fail; } AstVarRef* const vrefp - = new AstVarRef(varp->fileline(), varp, VAccess::READ); + = new AstVarRef{varp->fileline(), varp, VAccess::READ}; vrefp->selfPointer("this"); AstNode* newp = vrefp; if (varp->isWide()) { - newp = new AstWordSel( + newp = new AstWordSel{ varp->fileline(), newp, - new AstConst(varp->fileline(), varp->widthWords() - 1)); + new AstConst(varp->fileline(), varp->widthWords() - 1)}; } - uint64_t value = VL_MASK_Q(storedWidth) & ~VL_MASK_Q(lastWordWidth); - newp = new AstAnd(varp->fileline(), newp, + const uint64_t value = VL_MASK_Q(storedWidth) & ~VL_MASK_Q(lastWordWidth); + newp = new AstAnd{varp->fileline(), newp, new AstConst(varp->fileline(), AstConst::WidthedValue(), - storedWidth, value)); - AstNodeIf* ifp = new AstIf( + storedWidth, value)}; + AstNodeIf* const ifp = new AstIf{ varp->fileline(), newp, - new AstCStmt(varp->fileline(), "Verilated::overWidthError(\"" - + varp->prettyName() + "\");")); + new AstCStmt{varp->fileline(), "Verilated::overWidthError(\"" + + varp->prettyName() + "\");"}}; ifp->branchPred(VBranchPred::BP_UNLIKELY); newp = ifp; funcp->addStmtsp(newp); @@ -177,20 +178,20 @@ void V3CCtors::cctorsAll() { modp = VN_CAST(modp->nextp(), NodeModule)) { // Process each module in turn { - V3CCtorsBuilder var_reset(modp, "_ctor_var_reset", - VN_IS(modp, Class) ? VCtorType::CLASS : VCtorType::MODULE); + V3CCtorsBuilder var_reset{modp, "_ctor_var_reset", + VN_IS(modp, Class) ? VCtorType::CLASS : VCtorType::MODULE}; for (AstNode* np = modp->stmtsp(); np; np = np->nextp()) { if (AstVar* const varp = VN_CAST(np, Var)) { if (!varp->isIfaceParent() && !varp->isIfaceRef() && !varp->noReset()) { - const auto vrefp = new AstVarRef(varp->fileline(), varp, VAccess::WRITE); - var_reset.add(new AstCReset(varp->fileline(), vrefp)); + const auto vrefp = new AstVarRef{varp->fileline(), varp, VAccess::WRITE}; + var_reset.add(new AstCReset{varp->fileline(), vrefp}); } } } } if (v3Global.opt.coverage()) { - V3CCtorsBuilder configure_coverage(modp, "_configure_coverage", VCtorType::COVERAGE); + V3CCtorsBuilder configure_coverage{modp, "_configure_coverage", VCtorType::COVERAGE}; for (AstNode* np = modp->stmtsp(); np; np = np->nextp()) { if (AstCoverDecl* const coverp = VN_CAST(np, CoverDecl)) { np = coverp->backp(); @@ -198,8 +199,8 @@ void V3CCtors::cctorsAll() { } } } - if (AstClass* classp = VN_CAST(modp, Class)) { - AstCFunc* funcp = new AstCFunc(modp->fileline(), "~", nullptr, ""); + if (AstClass* const classp = VN_CAST(modp, Class)) { + AstCFunc* const funcp = new AstCFunc{modp->fileline(), "~", nullptr, ""}; funcp->isDestructor(true); funcp->isStatic(false); // If can be referred to by base pointer, need virtual delete diff --git a/src/V3CUse.cpp b/src/V3CUse.cpp index 3eb65f13e..2964c2b8f 100644 --- a/src/V3CUse.cpp +++ b/src/V3CUse.cpp @@ -55,9 +55,9 @@ private: public: AstCUse* newUse(AstNode* nodep, VUseType useType, const string& name) { - UseString key(useType, name); + UseString key{useType, name}; if (m_didUse.find(key) == m_didUse.end()) { - AstCUse* newp = new AstCUse(nodep->fileline(), useType, name); + AstCUse* const newp = new AstCUse{nodep->fileline(), useType, name}; m_modInsertp->addStmtp(newp); UINFO(8, "Insert " << newp << endl); m_didUse[key] = newp; @@ -123,46 +123,49 @@ class CUseVisitor final : public AstNVisitor { // Module use builders void makeUseCells(AstNodeModule* nodep) { for (AstNode* itemp = nodep->stmtsp(); itemp; itemp = itemp->nextp()) { - if (AstCell* cellp = VN_CAST(itemp, Cell)) { + if (AstCell* const cellp = VN_CAST(itemp, Cell)) { // Currently no include because we include __Syms which has them all m_state.newUse(nodep, VUseType::INT_FWD_CLASS, cellp->modp()->name()); } } } void makeVlToString(AstClass* nodep) { - AstCFunc* funcp = new AstCFunc(nodep->fileline(), "VL_TO_STRING", nullptr, "std::string"); + AstCFunc* const funcp + = new AstCFunc{nodep->fileline(), "VL_TO_STRING", nullptr, "std::string"}; funcp->argTypes("const VlClassRef<" + EmitCBaseVisitor::prefixNameProtect(nodep) + ">& obj"); funcp->isMethod(false); funcp->isConst(false); funcp->isStatic(false); funcp->protect(false); - AstNode* exprp = new AstCMath(nodep->fileline(), "obj ? obj->to_string() : \"null\"", 0); + AstNode* const exprp + = new AstCMath{nodep->fileline(), "obj ? obj->to_string() : \"null\"", 0}; exprp->dtypeSetString(); - funcp->addStmtsp(new AstCReturn(nodep->fileline(), exprp)); + funcp->addStmtsp(new AstCReturn{nodep->fileline(), exprp}); nodep->addStmtp(funcp); } void makeToString(AstClass* nodep) { - AstCFunc* funcp = new AstCFunc(nodep->fileline(), "to_string", nullptr, "std::string"); + AstCFunc* const funcp + = new AstCFunc{nodep->fileline(), "to_string", nullptr, "std::string"}; funcp->isConst(true); funcp->isStatic(false); funcp->protect(false); - AstNode* exprp = new AstCMath(nodep->fileline(), - R"(std::string("'{") + to_string_middle() + "}")", 0); + AstNode* const exprp = new AstCMath{nodep->fileline(), + R"(std::string("'{") + to_string_middle() + "}")", 0}; exprp->dtypeSetString(); - funcp->addStmtsp(new AstCReturn(nodep->fileline(), exprp)); + funcp->addStmtsp(new AstCReturn{nodep->fileline(), exprp}); nodep->addStmtp(funcp); } void makeToStringMiddle(AstClass* nodep) { - AstCFunc* funcp - = new AstCFunc(nodep->fileline(), "to_string_middle", nullptr, "std::string"); + AstCFunc* const funcp + = new AstCFunc{nodep->fileline(), "to_string_middle", nullptr, "std::string"}; funcp->isConst(true); funcp->isStatic(false); funcp->protect(false); - funcp->addStmtsp(new AstCStmt(nodep->fileline(), "std::string out;\n")); + funcp->addStmtsp(new AstCStmt{nodep->fileline(), "std::string out;\n"}); std::string comma; for (AstNode* itemp = nodep->membersp(); itemp; itemp = itemp->nextp()) { - if (auto* varp = VN_CAST(itemp, Var)) { + if (auto* const varp = VN_CAST(itemp, Var)) { if (!varp->isParam()) { string stmt = "out += \""; stmt += comma; @@ -179,7 +182,7 @@ class CUseVisitor final : public AstNVisitor { stmt += itemp->nameProtect(); stmt += ");\n"; nodep->user1(true); // So what we extend dumps this - funcp->addStmtsp(new AstCStmt(nodep->fileline(), stmt)); + funcp->addStmtsp(new AstCStmt{nodep->fileline(), stmt}); } } } @@ -190,22 +193,22 @@ class CUseVisitor final : public AstNVisitor { stmt += nodep->extendsp()->dtypep()->nameProtect(); stmt += "::to_string_middle();\n"; nodep->user1(true); // So what we extend dumps this - funcp->addStmtsp(new AstCStmt(nodep->fileline(), stmt)); + funcp->addStmtsp(new AstCStmt{nodep->fileline(), stmt}); } - funcp->addStmtsp(new AstCStmt(nodep->fileline(), "return out;\n")); + funcp->addStmtsp(new AstCStmt{nodep->fileline(), "return out;\n"}); nodep->addStmtp(funcp); } // VISITORS virtual void visit(AstNodeModule* nodep) override { if (v3Global.opt.trace()) { - AstCUse* usep + AstCUse* const usep = m_state.newUse(nodep, VUseType::INT_FWD_CLASS, v3Global.opt.traceClassBase()); usep->protect(false); } makeUseCells(nodep); - { CUseDTypeVisitor dtypeVisitor(nodep, m_state); } - if (AstClass* classp = VN_CAST(nodep, Class)) { + { CUseDTypeVisitor dtypeVisitor{nodep, m_state}; } + if (AstClass* const classp = VN_CAST(nodep, Class)) { makeVlToString(classp); makeToString(classp); makeToStringMiddle(classp); @@ -233,7 +236,7 @@ void V3CUse::cUseAll() { modp = VN_CAST(modp->nextp(), NodeModule)) { // Insert under this module; someday we should e.g. make Ast // for each output file and put under that - CUseVisitor visitor(modp); + CUseVisitor visitor{modp}; } V3Global::dumpCheckGlobalTree("cuse", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3Case.cpp b/src/V3Case.cpp index 23653a592..843f804d3 100644 --- a/src/V3Case.cpp +++ b/src/V3Case.cpp @@ -530,10 +530,10 @@ public: void V3Case::caseAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { CaseVisitor visitor(nodep); } // Destruct before checking + { CaseVisitor visitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("case", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } void V3Case::caseLint(AstNodeCase* nodep) { UINFO(4, __FUNCTION__ << ": " << endl); - CaseLintVisitor visitor(nodep); + CaseLintVisitor visitor{nodep}; } diff --git a/src/V3Cast.cpp b/src/V3Cast.cpp index c8e5536cf..0406149c8 100644 --- a/src/V3Cast.cpp +++ b/src/V3Cast.cpp @@ -66,7 +66,8 @@ private: AstNRelinker relinkHandle; nodep->unlinkFrBack(&relinkHandle); // - AstCCast* castp = new AstCCast(nodep->fileline(), nodep, needsize, nodep->widthMin()); + AstCCast* const castp + = new AstCCast{nodep->fileline(), nodep, needsize, nodep->widthMin()}; relinkHandle.relink(castp); // if (debug() > 8) castp->dumpTree(cout, "-castins: "); // @@ -103,7 +104,7 @@ private: if (!VN_IS(nodep->backp(), NullCheck)) { AstNRelinker relinkHandle; nodep->unlinkFrBack(&relinkHandle); - AstNode* newp = new AstNullCheck(nodep->fileline(), nodep); + AstNode* const newp = new AstNullCheck{nodep->fileline(), nodep}; relinkHandle.relink(newp); } } @@ -201,6 +202,6 @@ public: void V3Cast::castAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { CastVisitor visitor(nodep); } // Destruct before checking + { CastVisitor visitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("cast", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3Cdc.cpp b/src/V3Cdc.cpp index f4e8f2bd8..98b9168c3 100644 --- a/src/V3Cdc.cpp +++ b/src/V3Cdc.cpp @@ -337,7 +337,7 @@ private: int filelineWidth() { if (!m_filelineWidth) { - CdcWidthVisitor visitor(v3Global.rootp()); + CdcWidthVisitor visitor{v3Global.rootp()}; m_filelineWidth = visitor.maxWidth(); } return m_filelineWidth; @@ -508,7 +508,7 @@ private: const string filename = v3Global.opt.makeDir() + "/" + v3Global.opt.prefix() + "__cdc_edges.txt"; - const std::unique_ptr ofp(V3File::new_ofstream(filename)); + const std::unique_ptr ofp{V3File::new_ofstream(filename)}; if (ofp->fail()) v3fatal("Can't write " << filename); *ofp << "Edge Report for " << v3Global.opt.prefix() << '\n'; @@ -758,5 +758,5 @@ public: void V3Cdc::cdcAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - CdcVisitor visitor(nodep); + CdcVisitor visitor{nodep}; } diff --git a/src/V3Changed.cpp b/src/V3Changed.cpp index 79e2ad14d..62957bc21 100644 --- a/src/V3Changed.cpp +++ b/src/V3Changed.cpp @@ -129,7 +129,7 @@ private: AstAssign* initp = new AstAssign(m_vscp->fileline(), m_newLvEqnp->cloneTree(true), m_varEqnp->cloneTree(true)); m_statep->m_chgFuncp->addFinalsp(initp); - EmitCBaseCounterVisitor visitor(initp); + EmitCBaseCounterVisitor visitor{initp}; m_statep->m_numStmts += visitor.count(); } @@ -228,7 +228,7 @@ private: void genChangeDet(AstVarScope* vscp) { vscp->v3warn(IMPERFECTSCH, "Imperfect scheduling of variable: " << vscp->prettyNameQ()); - ChangedInsertVisitor visitor(vscp, m_statep); + ChangedInsertVisitor visitor{vscp, m_statep}; } // VISITORS @@ -288,7 +288,7 @@ void V3Changed::changedAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); { ChangedState state; - ChangedVisitor visitor(nodep, &state); + ChangedVisitor visitor{nodep, &state}; } // Destruct before checking V3Global::dumpCheckGlobalTree("changed", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3Class.cpp b/src/V3Class.cpp index a47b052d1..da29a24c1 100644 --- a/src/V3Class.cpp +++ b/src/V3Class.cpp @@ -153,6 +153,6 @@ public: void V3Class::classAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { ClassVisitor visitor(nodep); } // Destruct before checking + { ClassVisitor visitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("class", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3Clean.cpp b/src/V3Clean.cpp index c9087bab7..5afa46e57 100644 --- a/src/V3Clean.cpp +++ b/src/V3Clean.cpp @@ -314,6 +314,6 @@ public: void V3Clean::cleanAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { CleanVisitor visitor(nodep); } // Destruct before checking + { CleanVisitor visitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("clean", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3Clock.cpp b/src/V3Clock.cpp index ddb20dec0..6bb9a461c 100644 --- a/src/V3Clock.cpp +++ b/src/V3Clock.cpp @@ -435,6 +435,6 @@ public: void V3Clock::clockAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { ClockVisitor visitor(nodep); } // Destruct before checking + { ClockVisitor visitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("clock", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3Combine.cpp b/src/V3Combine.cpp index b5cd7ff20..71582a60f 100644 --- a/src/V3Combine.cpp +++ b/src/V3Combine.cpp @@ -223,6 +223,6 @@ public: void V3Combine::combineAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { CombineVisitor visitor(nodep); } // Destruct before checking + { CombineVisitor visitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("combine", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3Const.cpp b/src/V3Const.cpp index 29161330b..858a043fa 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -95,9 +95,9 @@ class ConstBitOpTreeVisitor final : public AstNVisitor { bool m_polarity; int m_bit; BitPolarityEntry(const LeafInfo& info, bool pol, int bit) - : m_info(info) - , m_polarity(pol) - , m_bit(bit) {} + : m_info{info} + , m_polarity{pol} + , m_bit{bit} {} BitPolarityEntry() = default; }; @@ -111,12 +111,12 @@ class ConstBitOpTreeVisitor final : public AstNVisitor { public: explicit Restorer(ConstBitOpTreeVisitor& visitor) - : m_visitor(visitor) - , m_polaritiesSize(visitor.m_bitPolarities.size()) - , m_frozenSize(visitor.m_frozenNodes.size()) - , m_ops(visitor.m_ops) - , m_polarity(visitor.m_polarity) - , m_restore(true) {} + : m_visitor{visitor} + , m_polaritiesSize{visitor.m_bitPolarities.size()} + , m_frozenSize{visitor.m_frozenNodes.size()} + , m_ops{visitor.m_ops} + , m_polarity{visitor.m_polarity} + , m_restore{true} {} ~Restorer() { UASSERT(m_visitor.m_bitPolarities.size() >= m_polaritiesSize, "m_bitPolarities must grow monotorilaclly"); @@ -222,9 +222,9 @@ class ConstBitOpTreeVisitor final : public AstNVisitor { // CONSTRUCTORS VarInfo(ConstBitOpTreeVisitor* parent, AstVarRef* refp) - : m_parentp(parent) - , m_refp(refp) - , m_bitPolarity(refp, refp->isWide() ? VL_EDATASIZE : refp->width()) { + : m_parentp{parent} + , m_refp{refp} + , m_bitPolarity{refp, refp->isWide() ? VL_EDATASIZE : refp->width()} { m_bitPolarity.setAllBitsX(); } }; @@ -482,8 +482,8 @@ class ConstBitOpTreeVisitor final : public AstNVisitor { // CONSTRUCTORS ConstBitOpTreeVisitor(AstNode* nodep, int ops) - : m_ops(ops) - , m_rootp(nodep) { + : m_ops{ops} + , m_rootp{nodep} { // Fill nullptr at [0] because AstVarScope::user4 is 0 by default m_varInfos.push_back(nullptr); CONST_BITOP_RETURN_IF(!isAndTree() && !isOrTree() && !isXorTree(), nodep); @@ -1495,9 +1495,10 @@ private: // -> EXTEND(nodep) // like a AstExtend{$rhsp}, but we need to set the width correctly from base node arg0p->unlinkFrBack(); - AstNode* newp = (VN_IS(nodep, ExtendS) - ? static_cast(new AstExtendS(nodep->fileline(), arg0p)) - : static_cast(new AstExtend(nodep->fileline(), arg0p))); + AstNode* const newp + = (VN_IS(nodep, ExtendS) + ? static_cast(new AstExtendS{nodep->fileline(), arg0p}) + : static_cast(new AstExtend{nodep->fileline(), arg0p})); newp->dtypeFrom(nodep); nodep->replaceWith(newp); VL_DO_DANGLING(nodep->deleteTree(), nodep); @@ -1713,8 +1714,8 @@ private: // Note only do this (need user4) when m_warn, which is // done as unique visitor AstUser4InUse m_inuser4; - ConstVarMarkVisitor mark(nodep->lhsp()); - ConstVarFindVisitor find(nodep->rhsp()); + ConstVarMarkVisitor mark{nodep->lhsp()}; + ConstVarFindVisitor find{nodep->rhsp()}; if (find.found()) need_temp = true; } if (need_temp) { @@ -3233,7 +3234,7 @@ AstNode* V3Const::constifyParamsEdit(AstNode* nodep) { // Make sure we've sized everything first nodep = V3Width::widthParamsEdit(nodep); - ConstVisitor visitor(ConstVisitor::PROC_PARAMS); + ConstVisitor visitor{ConstVisitor::PROC_PARAMS}; if (AstVar* varp = VN_CAST(nodep, Var)) { // If a var wants to be constified, it's really a param, and // we want the value to be constant. We aren't passed just the @@ -3263,7 +3264,7 @@ AstNode* V3Const::constifyGenerateParamsEdit(AstNode* nodep) { // Make sure we've sized everything first nodep = V3Width::widthGenerateParamsEdit(nodep); - ConstVisitor visitor(ConstVisitor::PROC_GENERATE); + ConstVisitor visitor{ConstVisitor::PROC_GENERATE}; if (AstVar* varp = VN_CAST(nodep, Var)) { // If a var wants to be constified, it's really a param, and // we want the value to be constant. We aren't passed just the @@ -3281,7 +3282,7 @@ void V3Const::constifyAllLint(AstNetlist* nodep) { // Only call from Verilator.cpp, as it uses user#'s UINFO(2, __FUNCTION__ << ": " << endl); { - ConstVisitor visitor(ConstVisitor::PROC_V_WARN); + ConstVisitor visitor{ConstVisitor::PROC_V_WARN}; (void)visitor.mainAcceptEdit(nodep); } // Destruct before checking V3Global::dumpCheckGlobalTree("const", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); @@ -3290,14 +3291,14 @@ void V3Const::constifyAllLint(AstNetlist* nodep) { void V3Const::constifyCpp(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); { - ConstVisitor visitor(ConstVisitor::PROC_CPP); + ConstVisitor visitor{ConstVisitor::PROC_CPP}; (void)visitor.mainAcceptEdit(nodep); } // Destruct before checking V3Global::dumpCheckGlobalTree("const_cpp", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } AstNode* V3Const::constifyEdit(AstNode* nodep) { - ConstVisitor visitor(ConstVisitor::PROC_V_NOWARN); + ConstVisitor visitor{ConstVisitor::PROC_V_NOWARN}; nodep = visitor.mainAcceptEdit(nodep); return nodep; } @@ -3308,7 +3309,7 @@ void V3Const::constifyAllLive(AstNetlist* nodep) { // IE doesn't prune dead statements, as we need to do some usability checks after this UINFO(2, __FUNCTION__ << ": " << endl); { - ConstVisitor visitor(ConstVisitor::PROC_LIVE); + ConstVisitor visitor{ConstVisitor::PROC_LIVE}; (void)visitor.mainAcceptEdit(nodep); } // Destruct before checking V3Global::dumpCheckGlobalTree("const", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); @@ -3318,14 +3319,14 @@ void V3Const::constifyAll(AstNetlist* nodep) { // Only call from Verilator.cpp, as it uses user#'s UINFO(2, __FUNCTION__ << ": " << endl); { - ConstVisitor visitor(ConstVisitor::PROC_V_EXPENSIVE); + ConstVisitor visitor{ConstVisitor::PROC_V_EXPENSIVE}; (void)visitor.mainAcceptEdit(nodep); } // Destruct before checking V3Global::dumpCheckGlobalTree("const", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } AstNode* V3Const::constifyExpensiveEdit(AstNode* nodep) { - ConstVisitor visitor(ConstVisitor::PROC_V_EXPENSIVE); + ConstVisitor visitor{ConstVisitor::PROC_V_EXPENSIVE}; nodep = visitor.mainAcceptEdit(nodep); return nodep; } diff --git a/src/V3Coverage.cpp b/src/V3Coverage.cpp index 2f9524df8..76dd38fd8 100644 --- a/src/V3Coverage.cpp +++ b/src/V3Coverage.cpp @@ -543,6 +543,6 @@ public: void V3Coverage::coverage(AstNetlist* rootp) { UINFO(2, __FUNCTION__ << ": " << endl); - { CoverageVisitor visitor(rootp); } // Destruct before checking + { CoverageVisitor visitor{rootp}; } // Destruct before checking V3Global::dumpCheckGlobalTree("coverage", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3CoverageJoin.cpp b/src/V3CoverageJoin.cpp index f0f30c098..8ceb3ccd5 100644 --- a/src/V3CoverageJoin.cpp +++ b/src/V3CoverageJoin.cpp @@ -114,6 +114,6 @@ public: void V3CoverageJoin::coverageJoin(AstNetlist* rootp) { UINFO(2, __FUNCTION__ << ": " << endl); - { CoverageJoinVisitor visitor(rootp); } // Destruct before checking + { CoverageJoinVisitor visitor{rootp}; } // Destruct before checking V3Global::dumpCheckGlobalTree("coveragejoin", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3Dead.cpp b/src/V3Dead.cpp index b08c5e78c..dafc1110c 100644 --- a/src/V3Dead.cpp +++ b/src/V3Dead.cpp @@ -318,7 +318,7 @@ private: // And its children may now be killable too; correct counts // Recurse, as cells may not be directly under the module but in a generate if (!modp->dead()) { // If was dead didn't increment user1's - DeadModVisitor visitor(modp); + DeadModVisitor visitor{modp}; } VL_DO_DANGLING(modp->unlinkFrBack()->deleteTree(), modp); retry = true; @@ -463,31 +463,31 @@ public: void V3Dead::deadifyModules(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { DeadVisitor visitor(nodep, false, false, false, false); } // Destruct before checking + { DeadVisitor visitor{nodep, false, false, false, false}; } // Destruct before checking V3Global::dumpCheckGlobalTree("deadModules", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 6); } void V3Dead::deadifyDTypes(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { DeadVisitor visitor(nodep, false, true, false, false); } // Destruct before checking + { DeadVisitor visitor{nodep, false, true, false, false}; } // Destruct before checking V3Global::dumpCheckGlobalTree("deadDtypes", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } void V3Dead::deadifyDTypesScoped(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { DeadVisitor visitor(nodep, false, true, true, false); } // Destruct before checking + { DeadVisitor visitor{nodep, false, true, true, false}; } // Destruct before checking V3Global::dumpCheckGlobalTree("deadDtypesScoped", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } void V3Dead::deadifyAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { DeadVisitor visitor(nodep, true, true, false, true); } // Destruct before checking + { DeadVisitor visitor{nodep, true, true, false, true}; } // Destruct before checking V3Global::dumpCheckGlobalTree("deadAll", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } void V3Dead::deadifyAllScoped(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { DeadVisitor visitor(nodep, true, true, true, true); } // Destruct before checking + { DeadVisitor visitor{nodep, true, true, true, true}; } // Destruct before checking V3Global::dumpCheckGlobalTree("deadAllScoped", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3Delayed.cpp b/src/V3Delayed.cpp index 3e3cd280f..7b4c0f88b 100644 --- a/src/V3Delayed.cpp +++ b/src/V3Delayed.cpp @@ -510,6 +510,6 @@ public: void V3Delayed::delayedAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { DelayedVisitor visitor(nodep); } // Destruct before checking + { DelayedVisitor visitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("delayed", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3Depth.cpp b/src/V3Depth.cpp index 9aef1f3b6..05110d509 100644 --- a/src/V3Depth.cpp +++ b/src/V3Depth.cpp @@ -53,16 +53,16 @@ private: // if (debug() >= 9) nodep->dumpTree(cout, "deep:"); const string newvarname = (string("__Vdeeptemp") + cvtToStr(m_modp->varNumGetInc())); - AstVar* varp - = new AstVar(nodep->fileline(), AstVarType::STMTTEMP, newvarname, nodep->dtypep()); + AstVar* const varp + = new AstVar{nodep->fileline(), AstVarType::STMTTEMP, newvarname, nodep->dtypep()}; UASSERT_OBJ(m_cfuncp, nodep, "Deep expression not under a function"); m_cfuncp->addInitsp(varp); // Replace node tree with reference to var - AstVarRef* newp = new AstVarRef(nodep->fileline(), varp, VAccess::READ); + AstVarRef* const newp = new AstVarRef{nodep->fileline(), varp, VAccess::READ}; nodep->replaceWith(newp); // Put assignment before the referencing statement - AstAssign* assp = new AstAssign( - nodep->fileline(), new AstVarRef(nodep->fileline(), varp, VAccess::WRITE), nodep); + AstAssign* const assp = new AstAssign{ + nodep->fileline(), new AstVarRef{nodep->fileline(), varp, VAccess::WRITE}, nodep}; AstNRelinker linker2; m_stmtp->unlinkFrBack(&linker2); assp->addNext(m_stmtp); @@ -158,6 +158,6 @@ public: void V3Depth::depthAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { DepthVisitor visitor(nodep); } // Destruct before checking + { DepthVisitor visitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("depth", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 6); } diff --git a/src/V3DepthBlock.cpp b/src/V3DepthBlock.cpp index 54a131a75..5fa2c3804 100644 --- a/src/V3DepthBlock.cpp +++ b/src/V3DepthBlock.cpp @@ -129,6 +129,6 @@ public: void V3DepthBlock::depthBlockAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { DepthBlockVisitor visitor(nodep); } // Destruct before checking + { DepthBlockVisitor visitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("deepblock", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3Descope.cpp b/src/V3Descope.cpp index 9b2e2cf76..fc10d5879 100644 --- a/src/V3Descope.cpp +++ b/src/V3Descope.cpp @@ -266,6 +266,6 @@ public: void V3Descope::descopeAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { DescopeVisitor visitor(nodep); } // Destruct before checking + { DescopeVisitor visitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("descope", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3DupFinder.cpp b/src/V3DupFinder.cpp index 061e659db..9b8187f1a 100644 --- a/src/V3DupFinder.cpp +++ b/src/V3DupFinder.cpp @@ -44,7 +44,7 @@ V3DupFinder::iterator V3DupFinder::findDuplicate(AstNode* nodep, V3DupFinderUser } void V3DupFinder::dumpFile(const string& filename, bool tree) { - const std::unique_ptr logp(V3File::new_ofstream(filename)); + const std::unique_ptr logp{V3File::new_ofstream(filename)}; if (logp->fail()) v3fatal("Can't write " << filename); std::unordered_map dist; diff --git a/src/V3EmitCMain.cpp b/src/V3EmitCMain.cpp index 493aca407..e4ace105f 100644 --- a/src/V3EmitCMain.cpp +++ b/src/V3EmitCMain.cpp @@ -35,14 +35,14 @@ class EmitCMain final : EmitCBaseVisitor { public: // CONSTRUCTORS - explicit EmitCMain(AstNetlist*) { emitInt(); } + EmitCMain() { emitInt(); } private: // MAIN METHOD void emitInt() { const string filename = v3Global.opt.makeDir() + "/" + topClassName() + "__main.cpp"; newCFile(filename, false /*slow*/, true /*source*/); - V3OutCFile cf(filename); + V3OutCFile cf{filename}; m_ofp = &cf; // Not defining main_time/vl_time_stamp, so @@ -101,5 +101,5 @@ private: void V3EmitCMain::emit() { UINFO(2, __FUNCTION__ << ": " << endl); - EmitCMain(v3Global.rootp()); + { EmitCMain visitor; } } diff --git a/src/V3EmitCMake.cpp b/src/V3EmitCMake.cpp index 82ea3c1f8..890634a86 100644 --- a/src/V3EmitCMake.cpp +++ b/src/V3EmitCMake.cpp @@ -78,8 +78,8 @@ class CMakeEmitter final { } static void emitOverallCMake() { - const std::unique_ptr of( - V3File::new_ofstream(v3Global.opt.makeDir() + "/" + v3Global.opt.prefix() + ".cmake")); + const std::unique_ptr of{ + V3File::new_ofstream(v3Global.opt.makeDir() + "/" + v3Global.opt.prefix() + ".cmake")}; const string name = v3Global.opt.prefix(); *of << "# Verilated -*- CMake -*-\n"; diff --git a/src/V3EmitCModel.cpp b/src/V3EmitCModel.cpp index dd0e6a64c..8796a9c4c 100644 --- a/src/V3EmitCModel.cpp +++ b/src/V3EmitCModel.cpp @@ -588,8 +588,8 @@ class EmitCModel final : public EmitCFunc { + "__Dpi_Export_" + cvtToStr(splitFilenumInc() - 1) + ".cpp"; newCFile(filename, /* slow: */ false, /* source: */ true); - m_ofp = v3Global.opt.systemC() ? new V3OutScFile(filename) - : new V3OutCFile(filename); + m_ofp = v3Global.opt.systemC() ? new V3OutScFile{filename} + : new V3OutCFile{filename}; m_lazyDecls.reset(); m_ofp->putsHeader(); puts( diff --git a/src/V3EmitCSyms.cpp b/src/V3EmitCSyms.cpp index de012617e..335f09426 100644 --- a/src/V3EmitCSyms.cpp +++ b/src/V3EmitCSyms.cpp @@ -670,7 +670,7 @@ void EmitCSyms::emitSymImp() { + topClassName() + "* modelp)\n"); puts(" : VerilatedSyms{contextp}\n"); puts(" // Setup internal state of the Syms class\n"); - puts(" , __Vm_modelp(modelp)\n"); + puts(" , __Vm_modelp{modelp}\n"); if (v3Global.opt.mtasks()) { // TODO -- For now each model creates its own ThreadPool here, @@ -694,9 +694,9 @@ void EmitCSyms::emitSymImp() { // Note we create N-1 threads in the thread pool. The thread // that calls eval() becomes the final Nth thread for the // duration of the eval call. - puts(" , __Vm_threadPoolp(new VlThreadPool(_vm_contextp__, " + puts(" , __Vm_threadPoolp{new VlThreadPool{_vm_contextp__, " + cvtToStr(v3Global.opt.threads() - 1) + ", " + cvtToStr(v3Global.opt.profThreads()) - + "))\n"); + + "}}\n"); } puts(" // Setup module instances\n"); diff --git a/src/V3EmitV.cpp b/src/V3EmitV.cpp index aad3f2348..28d36222f 100644 --- a/src/V3EmitV.cpp +++ b/src/V3EmitV.cpp @@ -831,7 +831,7 @@ void V3EmitV::verilogForTree(AstNode* nodep, std::ostream& os) { EmitVStreamVisi void V3EmitV::verilogPrefixedTree(AstNode* nodep, std::ostream& os, const string& prefix, int flWidth, AstSenTree* domainp, bool user3mark) { - EmitVPrefixedVisitor(nodep, os, prefix, flWidth, domainp, user3mark); + EmitVPrefixedVisitor{nodep, os, prefix, flWidth, domainp, user3mark}; } void V3EmitV::emitvFiles() { @@ -843,7 +843,7 @@ void V3EmitV::emitvFiles() { V3OutVFile of(vfilep->name()); of.puts("// DESCR" "IPTION: Verilator generated Verilog\n"); - EmitVFileVisitor visitor(vfilep->tblockp(), &of, true, false); + EmitVFileVisitor visitor{vfilep->tblockp(), &of, true, false}; } } } @@ -853,5 +853,5 @@ void V3EmitV::debugEmitV(const string& stage) { const string filename = v3Global.opt.makeDir() + "/" + v3Global.opt.prefix() + "__" + stage + ".v"; V3OutVFile of(filename); - EmitVFileVisitor visitor(v3Global.rootp(), &of, true, true); + EmitVFileVisitor visitor{v3Global.rootp(), &of, true, true}; } diff --git a/src/V3EmitXml.cpp b/src/V3EmitXml.cpp index 1e38e5552..c2facba08 100644 --- a/src/V3EmitXml.cpp +++ b/src/V3EmitXml.cpp @@ -392,10 +392,10 @@ void V3EmitXml::emitxml() { } { std::stringstream sstr; - ModuleFilesXmlVisitor moduleFilesVisitor(v3Global.rootp(), sstr); - HierCellsXmlVisitor cellsVisitor(v3Global.rootp(), sstr); + ModuleFilesXmlVisitor moduleFilesVisitor{v3Global.rootp(), sstr}; + HierCellsXmlVisitor cellsVisitor{v3Global.rootp(), sstr}; of.puts(sstr.str()); } - EmitXmlFileVisitor visitor(v3Global.rootp(), &of); + EmitXmlFileVisitor visitor{v3Global.rootp(), &of}; of.puts("\n"); } diff --git a/src/V3Expand.cpp b/src/V3Expand.cpp index ca6353204..8696c02fa 100644 --- a/src/V3Expand.cpp +++ b/src/V3Expand.cpp @@ -945,6 +945,6 @@ public: void V3Expand::expandAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { ExpandVisitor visitor(nodep); } // Destruct before checking + { ExpandVisitor visitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("expand", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3File.cpp b/src/V3File.cpp index fe9550838..f26e60c67 100644 --- a/src/V3File.cpp +++ b/src/V3File.cpp @@ -145,7 +145,7 @@ V3FileDependImp dependImp; // Depend implementation class // V3FileDependImp inline void V3FileDependImp::writeDepend(const string& filename) { - const std::unique_ptr ofp(V3File::new_ofstream(filename)); + const std::unique_ptr ofp{V3File::new_ofstream(filename)}; if (ofp->fail()) v3fatal("Can't write " << filename); for (const DependFile& i : m_filenameList) { @@ -178,7 +178,7 @@ inline std::vector V3FileDependImp::getAllDeps() const { } inline void V3FileDependImp::writeTimes(const string& filename, const string& cmdlineIn) { - const std::unique_ptr ofp(V3File::new_ofstream(filename)); + const std::unique_ptr ofp{V3File::new_ofstream(filename)}; if (ofp->fail()) v3fatal("Can't write " << filename); const string cmdline = stripQuotes(cmdlineIn); @@ -213,7 +213,7 @@ inline void V3FileDependImp::writeTimes(const string& filename, const string& cm } inline bool V3FileDependImp::checkTimes(const string& filename, const string& cmdlineIn) { - const std::unique_ptr ifp(V3File::new_ifstream_nodepend(filename)); + const std::unique_ptr ifp{V3File::new_ifstream_nodepend(filename)}; if (ifp->fail()) { UINFO(2, " --check-times failed: no input " << filename << endl); return false; diff --git a/src/V3File.h b/src/V3File.h index 785a2d0c9..08abc7607 100644 --- a/src/V3File.h +++ b/src/V3File.h @@ -38,7 +38,7 @@ public: return new_ifstream_nodepend(filename); } static std::ifstream* new_ifstream_nodepend(const string& filename) { - return new std::ifstream(filename.c_str()); + return new std::ifstream{filename.c_str()}; } static std::ofstream* new_ofstream(const string& filename, bool append = false) { addTgtDepend(filename); @@ -47,9 +47,9 @@ public: static std::ofstream* new_ofstream_nodepend(const string& filename, bool append = false) { createMakeDirFor(filename); if (append) { - return new std::ofstream(filename.c_str(), std::ios::app); + return new std::ofstream{filename.c_str(), std::ios::app}; } else { - return new std::ofstream(filename.c_str()); + return new std::ofstream{filename.c_str()}; } } static FILE* new_fopen_w(const string& filename) { diff --git a/src/V3Gate.cpp b/src/V3Gate.cpp index 867248bb3..9e36482a9 100644 --- a/src/V3Gate.cpp +++ b/src/V3Gate.cpp @@ -570,7 +570,7 @@ void GateVisitor::optimizeSignals(bool allowMultiIn) { AstNode* logicp = logicVertexp->nodep(); if (logicVertexp->reducible()) { // Can we eliminate? - GateOkVisitor okVisitor(logicp, vvertexp->isClock(), false); + GateOkVisitor okVisitor{logicp, vvertexp->isClock(), false}; const bool multiInputs = okVisitor.rhsVarRefs().size() > 1; // Was it ok? bool doit = okVisitor.isSimple(); @@ -884,7 +884,7 @@ public: void GateVisitor::optimizeElimVar(AstVarScope* varscp, AstNode* substp, AstNode* consumerp) { if (debug() >= 5) consumerp->dumpTree(cout, " elimUsePre: "); - GateElimVisitor elimVisitor(consumerp, varscp, substp, nullptr); + GateElimVisitor elimVisitor{consumerp, varscp, substp, nullptr}; if (elimVisitor.didReplace()) { if (debug() >= 9) consumerp->dumpTree(cout, " elimUseCns: "); // Caution: Can't let V3Const change our handle to consumerp, such as by @@ -1148,7 +1148,7 @@ private: UASSERT_OBJ(vvertexp->dedupable(), vvertexp->varScp(), "GateLogicVertex* visit should have returned nullptr " "if consumer var vertex is not dedupable."); - GateOkVisitor okVisitor(lvertexp->nodep(), false, true); + GateOkVisitor okVisitor{lvertexp->nodep(), false, true}; if (okVisitor.isSimple()) { AstVarScope* dupVarScopep = dupVarRefp->varScopep(); GateVarVertex* dupVvertexp @@ -1226,7 +1226,7 @@ public: void GateVisitor::dedupe() { AstNode::user2ClearTree(); - GateDedupeGraphVisitor deduper(&m_graph); + GateDedupeGraphVisitor deduper{&m_graph}; // Traverse starting from each of the clocks UINFO(9, "Gate dedupe() clocks:\n"); for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) { @@ -1367,7 +1367,7 @@ public: void GateVisitor::mergeAssigns() { UINFO(6, "mergeAssigns\n"); - GateMergeAssignsGraphVisitor merger(&m_graph); + GateMergeAssignsGraphVisitor merger{&m_graph}; for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) { if (GateVarVertex* vvertexp = dynamic_cast(itp)) { merger.mergeAssignsTree(vvertexp); @@ -1553,7 +1553,7 @@ public: void GateVisitor::decomposeClkVectors() { UINFO(9, "Starting clock decomposition" << endl); AstNode::user2ClearTree(); - GateClkDecompGraphVisitor decomposer(&m_graph); + GateClkDecompGraphVisitor decomposer{&m_graph}; for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) { if (GateVarVertex* vertp = dynamic_cast(itp)) { AstVarScope* vsp = vertp->varScp(); @@ -1601,8 +1601,8 @@ public: void V3Gate::gateAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); { - GateVisitor visitor(nodep); - GateDeassignVisitor deassign(nodep); + GateVisitor visitor{nodep}; + GateDeassignVisitor deassign{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("gate", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3GenClk.cpp b/src/V3GenClk.cpp index dc6dc2ec9..48c39b63d 100644 --- a/src/V3GenClk.cpp +++ b/src/V3GenClk.cpp @@ -147,7 +147,7 @@ private: { // Make the new clock signals and replace any activate references // See rename, it does some AstNode::userClearTree()'s - GenClkRenameVisitor visitor(nodep, m_topModp); + GenClkRenameVisitor visitor{nodep, m_topModp}; } } virtual void visit(AstNodeModule* nodep) override { @@ -222,6 +222,6 @@ public: void V3GenClk::genClkAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { GenClkReadVisitor visitor(nodep); } // Destruct before checking + { GenClkReadVisitor visitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("genclk", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3Global.h b/src/V3Global.h index b0a35f9e0..2eba07885 100644 --- a/src/V3Global.h +++ b/src/V3Global.h @@ -53,8 +53,8 @@ template class VRestorer { public: explicit VRestorer(T& permr) - : m_ref(permr) - , m_saved(permr) {} + : m_ref{permr} + , m_saved{permr} {} ~VRestorer() { m_ref = m_saved; } VL_UNCOPYABLE(VRestorer); }; diff --git a/src/V3Graph.cpp b/src/V3Graph.cpp index 71070cee2..6cbe5dc65 100644 --- a/src/V3Graph.cpp +++ b/src/V3Graph.cpp @@ -314,7 +314,7 @@ void V3Graph::dumpDotFilePrefixedAlways(const string& nameComment, bool colorAsS void V3Graph::dumpDotFile(const string& filename, bool colorAsSubgraph) const { // This generates a file used by graphviz, https://www.graphviz.org // "hardcoded" parameters: - const std::unique_ptr logp(V3File::new_ofstream(filename)); + const std::unique_ptr logp{V3File::new_ofstream(filename)}; if (logp->fail()) v3fatal("Can't write " << filename); // Header diff --git a/src/V3GraphDfa.h b/src/V3GraphDfa.h index 79e6e465e..8550bdad4 100644 --- a/src/V3GraphDfa.h +++ b/src/V3GraphDfa.h @@ -122,8 +122,8 @@ public: // CONSTRUCTORS DfaEdge(DfaGraph* graphp, DfaVertex* fromp, DfaVertex* top, const DfaInput& input) : V3GraphEdge{graphp, fromp, top, 1} - , m_input(input) - , m_complement(false) {} + , m_input{input} + , m_complement{false} {} DfaEdge(DfaGraph* graphp, DfaVertex* fromp, DfaVertex* top, const DfaEdge* copyfrom) : V3GraphEdge{graphp, fromp, top, copyfrom->weight()} , m_input{copyfrom->input()} diff --git a/src/V3Hasher.cpp b/src/V3Hasher.cpp index 775cee846..b83f8386a 100644 --- a/src/V3Hasher.cpp +++ b/src/V3Hasher.cpp @@ -488,11 +488,11 @@ public: // V3Hasher methods V3Hash V3Hasher::operator()(AstNode* nodep) const { - if (!nodep->user4()) { HasherVisitor visitor(nodep); } + if (!nodep->user4()) { HasherVisitor visitor{nodep}; } return V3Hash(nodep->user4()); } V3Hash V3Hasher::uncachedHash(const AstNode* nodep) { - HasherVisitor visitor(nodep); + HasherVisitor visitor{nodep}; return visitor.finalHash(); } diff --git a/src/V3HierBlock.cpp b/src/V3HierBlock.cpp index a11323d8e..dc7ba5a95 100644 --- a/src/V3HierBlock.cpp +++ b/src/V3HierBlock.cpp @@ -208,7 +208,7 @@ string V3HierBlock::vFileIfNecessary() const { } void V3HierBlock::writeCommandArgsFile(bool forCMake) const { - std::unique_ptr of(V3File::new_ofstream(commandArgsFileName(forCMake))); + std::unique_ptr of{V3File::new_ofstream(commandArgsFileName(forCMake))}; *of << "--cc\n"; if (!forCMake) { @@ -398,7 +398,7 @@ void V3HierBlockPlan::writeCommandArgsFiles(bool forCMake) const { it->second->writeCommandArgsFile(forCMake); } // For the top module - std::unique_ptr of(V3File::new_ofstream(topCommandArgsFileName(forCMake))); + std::unique_ptr of{V3File::new_ofstream(topCommandArgsFileName(forCMake))}; if (!forCMake) { // Load wrappers first not to be overwritten by the original HDL for (const_iterator it = begin(); it != end(); ++it) { diff --git a/src/V3Inline.cpp b/src/V3Inline.cpp index 01b62ae81..680eacfc6 100644 --- a/src/V3Inline.cpp +++ b/src/V3Inline.cpp @@ -548,7 +548,7 @@ private: // Clear var markings and find cell cross references AstNode::user2ClearTree(); AstNode::user4ClearTree(); - { InlineCollectVisitor(nodep->modp()); } // {} to destroy visitor immediately + { InlineCollectVisitor{nodep->modp()}; } // {} to destroy visitor immediately // Create data for dotted variable resolution AstCellInline* inlinep = new AstCellInline(nodep->fileline(), nodep->name(), nodep->modp()->origName(), @@ -591,7 +591,7 @@ private: && pinNewVarp->direction() == VDirection::OUTPUT); } // Cleanup var names, etc, to not conflict - { InlineRelinkVisitor(newmodp, m_modp, nodep); } + { InlineRelinkVisitor{newmodp, m_modp, nodep}; } // Move statements to top module if (debug() >= 9) newmodp->dumpTree(cout, "fixmod:"); AstNode* stmtsp = newmodp->stmtsp(); @@ -713,8 +713,8 @@ void V3Inline::inlineAll(AstNetlist* nodep) { AstUser1InUse m_inuser1; // output of InlineMarkVisitor, // input to InlineVisitor. // Scoped to clean up temp userN's - { InlineMarkVisitor mvisitor(nodep); } - { InlineVisitor visitor(nodep); } + { InlineMarkVisitor mvisitor{nodep}; } + { InlineVisitor visitor{nodep}; } // Remove all modules that were inlined // V3Dead will also clean them up, but if we have debug on, it's a good // idea to avoid dumping the hugely exploded tree. @@ -725,6 +725,6 @@ void V3Inline::inlineAll(AstNetlist* nodep) { VL_DO_DANGLING(modp->unlinkFrBack()->deleteTree(), modp); } } - { InlineIntfRefVisitor crvisitor(nodep); } + { InlineIntfRefVisitor crvisitor{nodep}; } V3Global::dumpCheckGlobalTree("inline", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3Inst.cpp b/src/V3Inst.cpp index ff4a2961c..97508c9f3 100644 --- a/src/V3Inst.cpp +++ b/src/V3Inst.cpp @@ -487,12 +487,12 @@ private: static AstNode* extendOrSel(FileLine* fl, AstNode* rhsp, AstNode* cmpWidthp) { if (cmpWidthp->width() > rhsp->width()) { - rhsp = (rhsp->isSigned() ? static_cast(new AstExtendS(fl, rhsp)) - : static_cast(new AstExtend(fl, rhsp))); + rhsp = (rhsp->isSigned() ? static_cast(new AstExtendS{fl, rhsp}) + : static_cast(new AstExtend{fl, rhsp})); // Need proper widthMin, which may differ from AstSel created above rhsp->dtypeFrom(cmpWidthp); } else if (cmpWidthp->width() < rhsp->width()) { - rhsp = new AstSel(fl, rhsp, 0, cmpWidthp->width()); + rhsp = new AstSel{fl, rhsp, 0, cmpWidthp->width()}; // Need proper widthMin, which may differ from AstSel created above rhsp->dtypeFrom(cmpWidthp); } @@ -610,12 +610,12 @@ void V3Inst::checkOutputShort(AstPin* nodep) { void V3Inst::instAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { InstVisitor visitor(nodep); } // Destruct before checking + { InstVisitor visitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("inst", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } void V3Inst::dearrayAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { InstDeVisitor visitor(nodep); } // Destruct before checking + { InstDeVisitor visitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("dearray", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 6); } diff --git a/src/V3InstrCount.cpp b/src/V3InstrCount.cpp index b26628cbf..49e67e0d7 100644 --- a/src/V3InstrCount.cpp +++ b/src/V3InstrCount.cpp @@ -294,7 +294,7 @@ private: }; uint32_t V3InstrCount::count(AstNode* nodep, bool assertNoDups, std::ostream* osp) { - InstrCountVisitor visitor(nodep, assertNoDups, osp); + InstrCountVisitor visitor{nodep, assertNoDups, osp}; if (osp) InstrCountDumpVisitor dumper(nodep, osp); return visitor.instrCount(); } diff --git a/src/V3Life.cpp b/src/V3Life.cpp index c3dafa9f7..af09be5ea 100644 --- a/src/V3Life.cpp +++ b/src/V3Life.cpp @@ -461,12 +461,12 @@ private: virtual void visit(AstCFunc* nodep) override { if (nodep->entryPoint()) { // Usage model 1: Simulate all C code, doing lifetime analysis - LifeVisitor visitor(nodep, m_statep); + LifeVisitor visitor{nodep, m_statep}; } } virtual void visit(AstNodeProcedure* nodep) override { // Usage model 2: Cleanup basic blocks - LifeVisitor visitor(nodep, m_statep); + LifeVisitor visitor{nodep, m_statep}; } virtual void visit(AstVar*) override {} // Accelerate virtual void visit(AstNodeStmt*) override {} // Accelerate @@ -489,7 +489,7 @@ void V3Life::lifeAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); { LifeState state; - LifeTopVisitor visitor(nodep, &state); + LifeTopVisitor visitor{nodep, &state}; } // Destruct before checking V3Global::dumpCheckGlobalTree("life", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3LifePost.cpp b/src/V3LifePost.cpp index 86bfe48b3..f8306c38f 100644 --- a/src/V3LifePost.cpp +++ b/src/V3LifePost.cpp @@ -271,7 +271,7 @@ private: squashAssignposts(); // Replace any node4p varscopes with the new scope - LifePostElimVisitor visitor(nodep); + LifePostElimVisitor visitor{nodep}; } virtual void visit(AstVarRef* nodep) override { // Consumption/generation of a variable, @@ -348,6 +348,6 @@ public: void V3LifePost::lifepostAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); // Mark redundant AssignPost - { LifePostDlyVisitor visitor(nodep); } // Destruct before checking + { LifePostDlyVisitor visitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("life_post", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3LinkCells.cpp b/src/V3LinkCells.cpp index 0713c4d70..c6ee709b3 100644 --- a/src/V3LinkCells.cpp +++ b/src/V3LinkCells.cpp @@ -523,5 +523,5 @@ public: void V3LinkCells::link(AstNetlist* nodep, VInFilter* filterp, V3ParseSym* parseSymp) { UINFO(4, __FUNCTION__ << ": " << endl); - LinkCellsVisitor visitor(nodep, filterp, parseSymp); + LinkCellsVisitor visitor{nodep, filterp, parseSymp}; } diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index 33a576ebc..d8d13d9c5 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -165,7 +165,7 @@ public: void dump(const string& nameComment = "linkdot", bool force = false) { if (debug() >= 6 || force) { const string filename = v3Global.debugFilename(nameComment) + ".txt"; - const std::unique_ptr logp(V3File::new_ofstream(filename)); + const std::unique_ptr logp{V3File::new_ofstream(filename)}; if (logp->fail()) v3fatal("Can't write " << filename); std::ostream& os = *logp; m_syms.dump(os); @@ -1766,7 +1766,7 @@ void LinkDotState::computeIfaceModSyms() { for (const auto& itr : m_ifaceModSyms) { AstIface* nodep = itr.first; VSymEnt* symp = itr.second; - LinkDotIfaceVisitor(nodep, symp, this); + LinkDotIfaceVisitor{nodep, symp, this}; } m_ifaceModSyms.clear(); } @@ -3042,13 +3042,13 @@ void V3LinkDot::linkDotGuts(AstNetlist* rootp, VLinkDotStep step) { v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("prelinkdot.tree")); } LinkDotState state(rootp, step); - LinkDotFindVisitor visitor(rootp, &state); + LinkDotFindVisitor visitor{rootp, &state}; if (LinkDotState::debug() >= 5 || v3Global.opt.dumpTree() >= 9) { v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("prelinkdot-find.tree")); } if (step == LDS_PRIMARY || step == LDS_PARAMED) { // Initial link stage, resolve parameters - LinkDotParamVisitor visitors(rootp, &state); + LinkDotParamVisitor visitors{rootp, &state}; if (LinkDotState::debug() >= 5 || v3Global.opt.dumpTree() >= 9) { v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("prelinkdot-param.tree")); } @@ -3056,7 +3056,7 @@ void V3LinkDot::linkDotGuts(AstNetlist* rootp, VLinkDotStep step) { } else if (step == LDS_SCOPED) { // Well after the initial link when we're ready to operate on the flat design, // process AstScope's. This needs to be separate pass after whole hierarchy graph created. - LinkDotScopeVisitor visitors(rootp, &state); + LinkDotScopeVisitor visitors{rootp, &state}; v3Global.assertScoped(true); if (LinkDotState::debug() >= 5 || v3Global.opt.dumpTree() >= 9) { v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("prelinkdot-scoped.tree")); @@ -3069,5 +3069,5 @@ void V3LinkDot::linkDotGuts(AstNetlist* rootp, VLinkDotStep step) { state.computeIfaceVarSyms(); state.computeScopeAliases(); state.dump(); - LinkDotResolveVisitor visitorb(rootp, &state); + LinkDotResolveVisitor visitorb{rootp, &state}; } diff --git a/src/V3LinkInc.cpp b/src/V3LinkInc.cpp index 00e9ef6ea..fc949abcd 100644 --- a/src/V3LinkInc.cpp +++ b/src/V3LinkInc.cpp @@ -246,6 +246,6 @@ public: void V3LinkInc::linkIncrements(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { LinkIncVisitor bvisitor(nodep); } // Destruct before checking + { LinkIncVisitor bvisitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("linkInc", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3LinkJump.cpp b/src/V3LinkJump.cpp index c81bf09f8..5d4a35425 100644 --- a/src/V3LinkJump.cpp +++ b/src/V3LinkJump.cpp @@ -287,6 +287,6 @@ public: void V3LinkJump::linkJump(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { LinkJumpVisitor bvisitor(nodep); } // Destruct before checking + { LinkJumpVisitor bvisitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("link", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3LinkLValue.cpp b/src/V3LinkLValue.cpp index ae87110db..9d9fadace 100644 --- a/src/V3LinkLValue.cpp +++ b/src/V3LinkLValue.cpp @@ -298,12 +298,12 @@ public: void V3LinkLValue::linkLValue(AstNetlist* nodep) { UINFO(4, __FUNCTION__ << ": " << endl); - { LinkLValueVisitor visitor(nodep, VAccess::NOCHANGE); } // Destruct before checking + { LinkLValueVisitor visitor{nodep, VAccess::NOCHANGE}; } // Destruct before checking V3Global::dumpCheckGlobalTree("linklvalue", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 6); } void V3LinkLValue::linkLValueSet(AstNode* nodep) { // Called by later link functions when it is known a node needs // to be converted to a lvalue. UINFO(9, __FUNCTION__ << ": " << endl); - LinkLValueVisitor visitor(nodep, VAccess::WRITE); + LinkLValueVisitor visitor{nodep, VAccess::WRITE}; } diff --git a/src/V3LinkParse.cpp b/src/V3LinkParse.cpp index 310a5ff37..22cc38dc8 100644 --- a/src/V3LinkParse.cpp +++ b/src/V3LinkParse.cpp @@ -656,6 +656,6 @@ public: void V3LinkParse::linkParse(AstNetlist* rootp) { UINFO(4, __FUNCTION__ << ": " << endl); - { LinkParseVisitor visitor(rootp); } // Destruct before checking + { LinkParseVisitor visitor{rootp}; } // Destruct before checking V3Global::dumpCheckGlobalTree("linkparse", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 6); } diff --git a/src/V3LinkResolve.cpp b/src/V3LinkResolve.cpp index e8713e179..6c0b2c02a 100644 --- a/src/V3LinkResolve.cpp +++ b/src/V3LinkResolve.cpp @@ -573,8 +573,8 @@ public: void V3LinkResolve::linkResolve(AstNetlist* rootp) { UINFO(4, __FUNCTION__ << ": " << endl); { - LinkResolveVisitor visitor(rootp); - LinkBotupVisitor visitorb(rootp); + LinkResolveVisitor visitor{rootp}; + LinkBotupVisitor visitorb{rootp}; } // Destruct before checking V3Global::dumpCheckGlobalTree("linkresolve", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 6); } diff --git a/src/V3Localize.cpp b/src/V3Localize.cpp index 0563c9866..deb1f301e 100644 --- a/src/V3Localize.cpp +++ b/src/V3Localize.cpp @@ -201,6 +201,6 @@ public: void V3Localize::localizeAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { LocalizeVisitor visitor(nodep); } // Destruct before checking + { LocalizeVisitor visitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("localize", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 6); } diff --git a/src/V3MergeCond.cpp b/src/V3MergeCond.cpp index 0cb527c42..32c7d6da0 100644 --- a/src/V3MergeCond.cpp +++ b/src/V3MergeCond.cpp @@ -179,17 +179,17 @@ private: } if (AstAnd* const andp = VN_CAST(rhsp, And)) { UASSERT_OBJ(andp->rhsp() == condp, rhsp, "Should not try to fold this"); - return new AstAnd(andp->fileline(), andp->lhsp()->cloneTree(false), resp); + return new AstAnd{andp->fileline(), andp->lhsp()->cloneTree(false), resp}; } } else if (AstAnd* const andp = VN_CAST(rhsp, And)) { if (andp->lhsp()->sameTree(m_mgCondp)) { return condTrue ? maskLsb(andp->rhsp()->unlinkFrBack()) - : new AstConst(rhsp->fileline(), AstConst::BitFalse()); + : new AstConst{rhsp->fileline(), AstConst::BitFalse()}; } else { UASSERT_OBJ(andp->rhsp()->sameTree(m_mgCondp), rhsp, "AstAnd doesn't hold condition expression"); return condTrue ? maskLsb(andp->lhsp()->unlinkFrBack()) - : new AstConst(rhsp->fileline(), AstConst::BitFalse()); + : new AstConst{rhsp->fileline(), AstConst::BitFalse()}; } } rhsp->v3fatalSrc("Don't know how to fold expression"); @@ -337,6 +337,6 @@ public: void V3MergeCond::mergeAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { MergeCondVisitor visitor(nodep); } + { MergeCondVisitor visitor{nodep}; } V3Global::dumpCheckGlobalTree("merge_cond", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 6); } diff --git a/src/V3Name.cpp b/src/V3Name.cpp index ef90a8265..d448ad0da 100644 --- a/src/V3Name.cpp +++ b/src/V3Name.cpp @@ -142,6 +142,6 @@ public: void V3Name::nameAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { NameVisitor visitor(nodep); } // Destruct before checking + { NameVisitor visitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("name", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 6); } diff --git a/src/V3Options.cpp b/src/V3Options.cpp index 8e52d8761..6677c795c 100644 --- a/src/V3Options.cpp +++ b/src/V3Options.cpp @@ -1499,7 +1499,7 @@ void V3Options::parseOptsFile(FileLine* fl, const string& filename, bool rel) { // Read the specified -f filename and process as arguments UINFO(1, "Reading Options File " << filename << endl); - const std::unique_ptr ifp(V3File::new_ifstream(filename)); + const std::unique_ptr ifp{V3File::new_ifstream(filename)}; if (ifp->fail()) { fl->v3error("Cannot open -f command file: " + filename); return; diff --git a/src/V3Order.cpp b/src/V3Order.cpp index 011bed102..0ea3e82c0 100644 --- a/src/V3Order.cpp +++ b/src/V3Order.cpp @@ -1177,13 +1177,13 @@ private: virtual void visit(AstAlwaysPublic* nodep) override { iterateNewStmt(nodep); } virtual void visit(AstAssignAlias* nodep) override { iterateNewStmt(nodep); } virtual void visit(AstAssignW* nodep) override { - OrderClkAssVisitor visitor(nodep); + OrderClkAssVisitor visitor{nodep}; m_inClkAss = visitor.isClkAss(); iterateNewStmt(nodep); m_inClkAss = false; } virtual void visit(AstAssignPre* nodep) override { - OrderClkAssVisitor visitor(nodep); + OrderClkAssVisitor visitor{nodep}; m_inClkAss = visitor.isClkAss(); m_inPre = true; iterateNewStmt(nodep); @@ -1191,7 +1191,7 @@ private: m_inClkAss = false; } virtual void visit(AstAssignPost* nodep) override { - OrderClkAssVisitor visitor(nodep); + OrderClkAssVisitor visitor{nodep}; m_inClkAss = visitor.isClkAss(); m_inPost = true; iterateNewStmt(nodep); @@ -1545,7 +1545,7 @@ void OrderVisitor::processDomainsIterate(OrderEitherVertex* vertexp) { void OrderVisitor::processEdgeReport() { // Make report of all signal names and what clock edges they have const string filename = v3Global.debugFilename("order_edges.txt"); - const std::unique_ptr logp(V3File::new_ofstream(filename)); + const std::unique_ptr logp{V3File::new_ofstream(filename)}; if (logp->fail()) v3fatal("Can't write " << filename); // Testing emitter: V3EmitV::verilogForTree(v3Global.rootp(), *logp); @@ -1777,7 +1777,7 @@ AstActive* OrderVisitor::processMoveOneLogic(const OrderLogicVertex* lvertexp, newFuncpr->addStmtsp(nodep); if (v3Global.opt.outputSplitCFuncs()) { // Add in the number of nodes we're adding - EmitCBaseCounterVisitor visitor(nodep); + EmitCBaseCounterVisitor visitor{nodep}; newStmtsr += visitor.count(); } } @@ -2005,7 +2005,7 @@ void OrderVisitor::process() { if (false && debug()) { const string dfilename = v3Global.opt.makeDir() + "/" + v3Global.opt.prefix() + "_INT_order"; - const std::unique_ptr logp(V3File::new_ofstream(dfilename)); + const std::unique_ptr logp{V3File::new_ofstream(dfilename)}; if (logp->fail()) v3fatal("Can't write " << dfilename); m_graph.dump(*logp); } @@ -2017,7 +2017,7 @@ void OrderVisitor::process() { void V3Order::orderAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); { - OrderClkMarkVisitor markVisitor(nodep); + OrderClkMarkVisitor markVisitor{nodep}; OrderVisitor visitor; visitor.main(nodep); } // Destruct before checking diff --git a/src/V3Os.cpp b/src/V3Os.cpp index 683aa8b99..9b6e6b428 100644 --- a/src/V3Os.cpp +++ b/src/V3Os.cpp @@ -236,8 +236,8 @@ void V3Os::createDir(const string& dirname) { } void V3Os::unlinkRegexp(const string& dir, const string& regexp) { - if (DIR* dirp = opendir(dir.c_str())) { - while (struct dirent* direntp = readdir(dirp)) { + if (DIR* const dirp = opendir(dir.c_str())) { + while (struct dirent* const direntp = readdir(dirp)) { if (VString::wildmatch(direntp->d_name, regexp.c_str())) { const string fullname = dir + "/" + string(direntp->d_name); #if defined(_WIN32) || defined(__MINGW32__) @@ -273,7 +273,7 @@ string V3Os::trueRandom(size_t size) { BCRYPT_USE_SYSTEM_PREFERRED_RNG); if (!BCRYPT_SUCCESS(hr)) v3fatal("Could not acquire random data."); #else - std::ifstream is("/dev/urandom", std::ios::in | std::ios::binary); + std::ifstream is{"/dev/urandom", std::ios::in | std::ios::binary}; // This read uses the size of the buffer. // Flawfinder: ignore if (VL_UNCOVERABLE(!is.read(data, size))) { diff --git a/src/V3Param.cpp b/src/V3Param.cpp index a09dca9e4..9976028bb 100644 --- a/src/V3Param.cpp +++ b/src/V3Param.cpp @@ -1223,6 +1223,6 @@ public: void V3Param::param(AstNetlist* rootp) { UINFO(2, __FUNCTION__ << ": " << endl); - { ParamVisitor visitor(rootp); } // Destruct before checking + { ParamVisitor visitor{rootp}; } // Destruct before checking V3Global::dumpCheckGlobalTree("param", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 6); } diff --git a/src/V3Partition.cpp b/src/V3Partition.cpp index 1b9441c0c..f12a1bce3 100644 --- a/src/V3Partition.cpp +++ b/src/V3Partition.cpp @@ -624,8 +624,8 @@ public: static void dumpCpFilePrefixed(const V3Graph* graphp, const string& nameComment) { const string filename = v3Global.debugFilename(nameComment) + ".txt"; UINFO(1, "Writing " << filename << endl); - std::unique_ptr ofp(V3File::new_ofstream(filename)); - std::ostream* osp = &(*ofp); // &* needed to deref unique_ptr + std::unique_ptr ofp{V3File::new_ofstream(filename)}; + std::ostream* const osp = &(*ofp); // &* needed to deref unique_ptr if (osp->fail()) v3fatalStatic("Can't write " << filename); // Find start vertex with longest CP @@ -2077,7 +2077,7 @@ void ThreadSchedule::dumpDotFilePrefixedAlways(const string& nameComment) const void ThreadSchedule::dumpDotFile(const string& filename) const { // This generates a file used by graphviz, https://www.graphviz.org - const std::unique_ptr logp(V3File::new_ofstream(filename)); + const std::unique_ptr logp{V3File::new_ofstream(filename)}; if (logp->fail()) v3fatal("Can't write " << filename); auto* depGraph = v3Global.rootp()->execGraphp()->depGraphp(); @@ -2765,8 +2765,8 @@ static const std::vector createThreadFunctions(const ThreadSchedule& funcp->argTypes("void* voidSelf, bool even_cycle"); // Setup vlSelf an vlSyms - funcp->addStmtsp(new AstCStmt(fl, EmitCBaseVisitor::voidSelfAssign(modp))); - funcp->addStmtsp(new AstCStmt(fl, EmitCBaseVisitor::symClassAssign())); + funcp->addStmtsp(new AstCStmt{fl, EmitCBaseVisitor::voidSelfAssign(modp)}); + funcp->addStmtsp(new AstCStmt{fl, EmitCBaseVisitor::symClassAssign()}); // Invoke each mtask scheduled to this thread from the thread function for (const ExecMTask* const mtaskp : thread) { diff --git a/src/V3Premit.cpp b/src/V3Premit.cpp index 897f25445..948155f20 100644 --- a/src/V3Premit.cpp +++ b/src/V3Premit.cpp @@ -427,6 +427,6 @@ public: void V3Premit::premitAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { PremitVisitor visitor(nodep); } // Destruct before checking + { PremitVisitor visitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("premit", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3Randomize.cpp b/src/V3Randomize.cpp index 1289b5a0d..6b6b1d8d4 100644 --- a/src/V3Randomize.cpp +++ b/src/V3Randomize.cpp @@ -246,8 +246,8 @@ public: void V3Randomize::randomizeNetlist(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); { - RandomizeMarkVisitor markVisitor(nodep); - RandomizeVisitor visitor(nodep); + RandomizeMarkVisitor markVisitor{nodep}; + RandomizeVisitor visitor{nodep}; } V3Global::dumpCheckGlobalTree("randomize", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3Reloop.cpp b/src/V3Reloop.cpp index e95239473..646758819 100644 --- a/src/V3Reloop.cpp +++ b/src/V3Reloop.cpp @@ -267,6 +267,6 @@ public: void V3Reloop::reloopAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { ReloopVisitor visitor(nodep); } // Destruct before checking + { ReloopVisitor visitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("reloop", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 6); } diff --git a/src/V3Scope.cpp b/src/V3Scope.cpp index e90ed7a38..5cafafd3b 100644 --- a/src/V3Scope.cpp +++ b/src/V3Scope.cpp @@ -402,8 +402,8 @@ public: void V3Scope::scopeAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); { - ScopeVisitor visitor(nodep); - ScopeCleanupVisitor cleanVisitor(nodep); + ScopeVisitor visitor{nodep}; + ScopeCleanupVisitor cleanVisitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("scope", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3Slice.cpp b/src/V3Slice.cpp index 695d93082..f2a809797 100644 --- a/src/V3Slice.cpp +++ b/src/V3Slice.cpp @@ -236,6 +236,6 @@ public: void V3Slice::sliceAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { SliceVisitor visitor(nodep); } // Destruct before checking + { SliceVisitor visitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("slice", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3Split.cpp b/src/V3Split.cpp index 4c8b1a60d..551dfd25c 100644 --- a/src/V3Split.cpp +++ b/src/V3Split.cpp @@ -932,7 +932,7 @@ protected: // Map each AstNodeIf to the set of colors (split always blocks) // it must participate in. Also find the whole set of colors. - IfColorVisitor ifColor(nodep); + IfColorVisitor ifColor{nodep}; if (ifColor.colors().size() > 1) { // Counting original always blocks rather than newly-split @@ -942,7 +942,7 @@ protected: // Visit through the original always block one more time, // and emit the split always blocks into m_replaceBlocks: - EmitSplitVisitor emitSplit(nodep, &ifColor, &(m_replaceBlocks[nodep])); + EmitSplitVisitor emitSplit{nodep, &ifColor, &(m_replaceBlocks[nodep])}; emitSplit.go(); } } @@ -964,11 +964,11 @@ private: void V3Split::splitReorderAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { ReorderVisitor visitor(nodep); } // Destruct before checking + { ReorderVisitor visitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("reorder", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } void V3Split::splitAlwaysAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { SplitVisitor visitor(nodep); } // Destruct before checking + { SplitVisitor visitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("split", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3SplitAs.cpp b/src/V3SplitAs.cpp index 5263a8a9a..ee9bf9236 100644 --- a/src/V3SplitAs.cpp +++ b/src/V3SplitAs.cpp @@ -144,11 +144,11 @@ private: newp->user1(true); // So we don't clone it again nodep->addNextHere(newp); { // Delete stuff we don't want in old - SplitAsCleanVisitor visitor(nodep, m_splitVscp, false); + SplitAsCleanVisitor visitor{nodep, m_splitVscp, false}; if (debug() >= 9) nodep->dumpTree(cout, "-out0: "); } { // Delete stuff we don't want in new - SplitAsCleanVisitor visitor(newp, m_splitVscp, true); + SplitAsCleanVisitor visitor{newp, m_splitVscp, true}; if (debug() >= 9) newp->dumpTree(cout, "-out1: "); } } @@ -159,7 +159,7 @@ private: AstVarScope* lastSplitVscp = nullptr; while (!nodep->user1()) { // Find any splittable variables - SplitAsFindVisitor visitor(nodep); + SplitAsFindVisitor visitor{nodep}; m_splitVscp = visitor.splitVscp(); if (m_splitVscp && m_splitVscp == lastSplitVscp) { // We did this last time! Something's stuck! @@ -194,6 +194,6 @@ public: void V3SplitAs::splitAsAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { SplitAsVisitor visitor(nodep); } // Destruct before checking + { SplitAsVisitor visitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("splitas", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3SplitVar.cpp b/src/V3SplitVar.cpp index 5a3f8ab78..d47b198d6 100644 --- a/src/V3SplitVar.cpp +++ b/src/V3SplitVar.cpp @@ -1250,11 +1250,11 @@ void V3SplitVar::splitVariable(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); SplitVarRefsMap refs; { - SplitUnpackedVarVisitor visitor(nodep); + SplitUnpackedVarVisitor visitor{nodep}; refs = visitor.getPackedVarRefs(); } V3Global::dumpCheckGlobalTree("split_var", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 9); - { SplitPackedVarVisitor visitor(nodep, refs); } + { SplitPackedVarVisitor visitor{nodep, refs}; } V3Global::dumpCheckGlobalTree("split_var", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 9); } diff --git a/src/V3Stats.cpp b/src/V3Stats.cpp index ba11b2f4f..3084b233b 100644 --- a/src/V3Stats.cpp +++ b/src/V3Stats.cpp @@ -279,7 +279,7 @@ public: // Top Stats class void V3Stats::statsStageAll(AstNetlist* nodep, const string& stage, bool fast) { - StatsVisitor visitor(nodep, stage, fast); + StatsVisitor visitor{nodep, stage, fast}; } void V3Stats::statsFinalAll(AstNetlist* nodep) { diff --git a/src/V3StatsReport.cpp b/src/V3StatsReport.cpp index 24c148d3d..5833f8db6 100644 --- a/src/V3StatsReport.cpp +++ b/src/V3StatsReport.cpp @@ -222,7 +222,7 @@ void V3Stats::statsReport() { // Open stats file const string filename = v3Global.opt.hierTopDataDir() + "/" + v3Global.opt.prefix() + "__stats.txt"; - std::ofstream* ofp(V3File::new_ofstream(filename)); + std::ofstream* ofp{V3File::new_ofstream(filename)}; if (ofp->fail()) v3fatal("Can't write " << filename); StatsReport reporter(ofp); diff --git a/src/V3Subst.cpp b/src/V3Subst.cpp index a3c51797e..a9d422344 100644 --- a/src/V3Subst.cpp +++ b/src/V3Subst.cpp @@ -314,7 +314,7 @@ private: SubstVarEntry* entryp = getEntryp(varrefp); if (AstNode* substp = entryp->substWord(nodep, word)) { // Check that the RHS hasn't changed value since we recorded it. - SubstUseVisitor visitor(substp, entryp->getWordStep(word)); + SubstUseVisitor visitor{substp, entryp->getWordStep(word)}; if (visitor.ok()) { VL_DO_DANGLING(replaceSubstEtc(nodep, substp), nodep); } else { @@ -341,7 +341,7 @@ private: entryp->assignComplex(); } else if (AstNode* substp = entryp->substWhole(nodep)) { // Check that the RHS hasn't changed value since we recorded it. - SubstUseVisitor visitor(substp, entryp->getWholeStep()); + SubstUseVisitor visitor{substp, entryp->getWholeStep()}; if (visitor.ok()) { UINFO(8, " USEwhole " << nodep << endl); VL_DO_DANGLING(replaceSubstEtc(nodep, substp), nodep); @@ -380,6 +380,6 @@ public: void V3Subst::substituteAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { SubstVisitor visitor(nodep); } // Destruct before checking + { SubstVisitor visitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("subst", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3SymTable.h b/src/V3SymTable.h index 6db11ae30..e7ff1905f 100644 --- a/src/V3SymTable.h +++ b/src/V3SymTable.h @@ -317,7 +317,7 @@ public: if (v3Global.opt.dumpTree()) { const string filename = v3Global.debugFilename(nameComment) + ".txt"; UINFO(2, "Dumping " << filename << endl); - const std::unique_ptr logp(V3File::new_ofstream(filename)); + const std::unique_ptr logp{V3File::new_ofstream(filename)}; if (logp->fail()) v3fatal("Can't write " << filename); dump(*logp, ""); } diff --git a/src/V3TSP.cpp b/src/V3TSP.cpp index 6e3cc079d..eb5a53a64 100644 --- a/src/V3TSP.cpp +++ b/src/V3TSP.cpp @@ -355,7 +355,7 @@ public: void dumpGraphFilePrefixed(const string& nameComment) const { if (v3Global.opt.dumpTree()) { const string filename = v3Global.debugFilename(nameComment) + ".txt"; - const std::unique_ptr logp(V3File::new_ofstream(filename)); + const std::unique_ptr logp{V3File::new_ofstream(filename)}; if (logp->fail()) v3fatal("Can't write " << filename); dumpGraph(*logp, nameComment); } diff --git a/src/V3Table.cpp b/src/V3Table.cpp index 330873ced..f81c83021 100644 --- a/src/V3Table.cpp +++ b/src/V3Table.cpp @@ -97,9 +97,9 @@ public: v3Global.rootp()->typeTablep()->addTypesp(tableDTypep); // Create table initializer (with default value 0) AstConst* const defaultp = elemDType->isString() - ? new AstConst(m_fl, AstConst::String(), "") - : new AstConst(m_fl, AstConst::WidthedValue(), width, 0); - m_initp = new AstInitArray(m_fl, tableDTypep, defaultp); + ? new AstConst{m_fl, AstConst::String(), ""} + : new AstConst{m_fl, AstConst::WidthedValue(), width, 0}; + m_initp = new AstInitArray{m_fl, tableDTypep, defaultp}; } void addValue(unsigned index, const V3Number& value) { @@ -196,7 +196,7 @@ private: m_outVarps.clear(); // Collect stats - TableSimulateVisitor chkvis(this); + TableSimulateVisitor chkvis{this}; chkvis.mainTableCheck(nodep); m_assignDly = chkvis.isAssignDly(); // Also sets m_inWidthBits @@ -280,7 +280,7 @@ private: // There may be a simulation path by which the output doesn't change value. // We could bail on these cases, or we can have a "change it" boolean. // We've chosen the latter route, since recirc is common in large FSMs. - TableSimulateVisitor simvis(this); + TableSimulateVisitor simvis{this}; for (uint32_t i = 0; i <= VL_MASK_I(m_inWidthBits); ++i) { const uint32_t inValue = i; // Make a new simulation structure so we can set new input values @@ -354,11 +354,11 @@ private: AstVarScope* outputAssignedTableVscp) { FileLine* const fl = nodep->fileline(); for (TableOutputVar& tov : m_outVarps) { - AstNode* const alhsp = new AstVarRef(fl, tov.varScopep(), VAccess::WRITE); + AstNode* const alhsp = new AstVarRef{fl, tov.varScopep(), VAccess::WRITE}; AstNode* const arhsp = select(fl, tov.tabeVarScopep(), indexVscp); AstNode* outsetp = m_assignDly - ? static_cast(new AstAssignDly(fl, alhsp, arhsp)) - : static_cast(new AstAssign(fl, alhsp, arhsp)); + ? static_cast(new AstAssignDly{fl, alhsp, arhsp}) + : static_cast(new AstAssign{fl, alhsp, arhsp}); // If this output is unassigned on some code paths, wrap the assignment in an If if (tov.mayBeUnassigned()) { @@ -425,6 +425,6 @@ void TableSimulateVisitor::varRefCb(AstVarRef* nodep) { void V3Table::tableAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { TableVisitor visitor(nodep); } // Destruct before checking + { TableVisitor visitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("table", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3Task.cpp b/src/V3Task.cpp index 13ab8a148..9b2226468 100644 --- a/src/V3Task.cpp +++ b/src/V3Task.cpp @@ -545,7 +545,7 @@ private: // Iteration requires a back, so put under temporary node { AstBegin* tempp = new AstBegin(beginp->fileline(), "[EditWrapper]", beginp); - TaskRelinkVisitor visitor(tempp); + TaskRelinkVisitor visitor{tempp}; tempp->stmtsp()->unlinkFrBackWithNext(); VL_DO_DANGLING(tempp->deleteTree(), tempp); } @@ -1230,7 +1230,7 @@ private: // Iteration requires a back, so put under temporary node { AstBegin* tempp = new AstBegin(cfuncp->fileline(), "[EditWrapper]", cfuncp); - TaskRelinkVisitor visitor(tempp); + TaskRelinkVisitor visitor{tempp}; tempp->stmtsp()->unlinkFrBackWithNext(); VL_DO_DANGLING(tempp->deleteTree(), tempp); } @@ -1728,8 +1728,8 @@ const char* V3Task::dpiTemporaryVarSuffix() { void V3Task::taskAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); { - TaskStateVisitor visitors(nodep); - TaskVisitor visitor(nodep, &visitors); + TaskStateVisitor visitors{nodep}; + TaskVisitor visitor{nodep, &visitors}; } // Destruct before checking V3Global::dumpCheckGlobalTree("task", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3Trace.cpp b/src/V3Trace.cpp index a5901d1e8..168ae24d4 100644 --- a/src/V3Trace.cpp +++ b/src/V3Trace.cpp @@ -907,6 +907,6 @@ public: void V3Trace::traceAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { TraceVisitor visitor(nodep); } // Destruct before checking + { TraceVisitor visitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("trace", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3TraceDecl.cpp b/src/V3TraceDecl.cpp index 07a62629d..3bbe20f31 100644 --- a/src/V3TraceDecl.cpp +++ b/src/V3TraceDecl.cpp @@ -361,6 +361,6 @@ public: void V3TraceDecl::traceDeclAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { TraceDeclVisitor visitor(nodep); } // Destruct before checking + { TraceDeclVisitor visitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("tracedecl", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3Tristate.cpp b/src/V3Tristate.cpp index dc85754bb..6be7c1621 100644 --- a/src/V3Tristate.cpp +++ b/src/V3Tristate.cpp @@ -1213,7 +1213,7 @@ class TristateVisitor final : public TristateBaseVisitor { // simple, it will flip ArraySel's and such, but if the // pin is an input the earlier reconnectSimple made it // a VarRef without any ArraySel, etc - TristatePinVisitor visitor(outexprp, m_tgraph, true); + TristatePinVisitor visitor{outexprp, m_tgraph, true}; } if (debug() >= 9) outpinp->dumpTree(cout, "-pin-opr: "); outAssignp = V3Inst::pinReconnectSimple(outpinp, m_cellp, @@ -1224,7 +1224,7 @@ class TristateVisitor final : public TristateBaseVisitor { } // Existing pin becomes an input, and we mark each resulting signal as tristate - TristatePinVisitor visitor(nodep->exprp(), m_tgraph, false); + TristatePinVisitor visitor{nodep->exprp(), m_tgraph, false}; AstNode* inAssignp = V3Inst::pinReconnectSimple( nodep, m_cellp, true); // Note may change nodep->exprp() if (debug() >= 9) nodep->dumpTree(cout, "-pin-in: "); @@ -1424,6 +1424,6 @@ public: void V3Tristate::tristateAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { TristateVisitor visitor(nodep); } // Destruct before checking + { TristateVisitor visitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("tristate", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3Undriven.cpp b/src/V3Undriven.cpp index 4f8f4bcb4..ccf6ea283 100644 --- a/src/V3Undriven.cpp +++ b/src/V3Undriven.cpp @@ -461,5 +461,5 @@ public: void V3Undriven::undrivenAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - UndrivenVisitor visitor(nodep); + UndrivenVisitor visitor{nodep}; } diff --git a/src/V3Unknown.cpp b/src/V3Unknown.cpp index fb0d89866..5c86e25ed 100644 --- a/src/V3Unknown.cpp +++ b/src/V3Unknown.cpp @@ -487,6 +487,6 @@ public: void V3Unknown::unknownAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { UnknownVisitor visitor(nodep); } // Destruct before checking + { UnknownVisitor visitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("unknown", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3Waiver.cpp b/src/V3Waiver.cpp index 31b6b9c86..7e96d7ca3 100644 --- a/src/V3Waiver.cpp +++ b/src/V3Waiver.cpp @@ -30,7 +30,7 @@ void V3Waiver::addEntry(V3ErrorCode errorCode, const std::string& filename, } void V3Waiver::write(const std::string& filename) { - const std::unique_ptr ofp(V3File::new_ofstream(filename)); + const std::unique_ptr ofp{V3File::new_ofstream(filename)}; if (ofp->fail()) v3fatal("Can't write " << filename); *ofp << "// DESCR" diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 5f67404e4..cf2c264ae 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -5079,9 +5079,9 @@ private: case EXTEND_LHS: doSigned = nodep->isSigned(); break; default: nodep->v3fatalSrc("bad case"); } - AstNode* newp - = (doSigned ? static_cast(new AstExtendS(nodep->fileline(), nodep)) - : static_cast(new AstExtend(nodep->fileline(), nodep))); + AstNode* const newp + = (doSigned ? static_cast(new AstExtendS{nodep->fileline(), nodep}) + : static_cast(new AstExtend{nodep->fileline(), nodep})); linker.relink(newp); nodep = newp; } @@ -6158,8 +6158,8 @@ void V3Width::width(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); { // We should do it in bottom-up module order, but it works in any order. - WidthClearVisitor cvisitor(nodep); - WidthVisitor visitor(false, false); + WidthClearVisitor cvisitor{nodep}; + WidthVisitor visitor{false, false}; (void)visitor.mainAcceptEdit(nodep); WidthRemoveVisitor rvisitor; (void)rvisitor.mainAcceptEdit(nodep); @@ -6172,7 +6172,7 @@ void V3Width::width(AstNetlist* nodep) { AstNode* V3Width::widthParamsEdit(AstNode* nodep) { UINFO(4, __FUNCTION__ << ": " << nodep << endl); // We should do it in bottom-up module order, but it works in any order. - WidthVisitor visitor(true, false); + WidthVisitor visitor{true, false}; nodep = visitor.mainAcceptEdit(nodep); // No WidthRemoveVisitor, as don't want to drop $signed etc inside gen blocks return nodep; @@ -6191,7 +6191,7 @@ AstNode* V3Width::widthGenerateParamsEdit( AstNode* nodep) { //!< [in] AST whose parameters widths are to be analysed. UINFO(4, __FUNCTION__ << ": " << nodep << endl); // We should do it in bottom-up module order, but it works in any order. - WidthVisitor visitor(true, true); + WidthVisitor visitor{true, true}; nodep = visitor.mainAcceptEdit(nodep); // No WidthRemoveVisitor, as don't want to drop $signed etc inside gen blocks return nodep; @@ -6199,6 +6199,6 @@ AstNode* V3Width::widthGenerateParamsEdit( void V3Width::widthCommit(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { WidthCommitVisitor visitor(nodep); } // Destruct before checking + { WidthCommitVisitor visitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("widthcommit", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 6); } diff --git a/src/VlcBucket.h b/src/VlcBucket.h index 3add0dd46..89e758c45 100644 --- a/src/VlcBucket.h +++ b/src/VlcBucket.h @@ -39,7 +39,7 @@ private: if (m_dataSize < point) m_dataSize = (point + 64) & ~63ULL; // Keep power of two m_dataSize *= 2; // UINFO(9, "Realloc "<(std::realloc(m_datap, allocSize())); + vluint64_t* const newp = static_cast(std::realloc(m_datap, allocSize())); if (VL_UNCOVERABLE(!newp)) { // cppcheck-suppress doubleFree // cppcheck 1.90 bug - realloc doesn't free on fail free(m_datap); // LCOV_EXCL_LINE diff --git a/src/VlcOptions.h b/src/VlcOptions.h index 22d98dd4a..6bebf0902 100644 --- a/src/VlcOptions.h +++ b/src/VlcOptions.h @@ -35,11 +35,11 @@ class VlcOptions final { // MEMBERS (general options) // clang-format off string m_annotateOut; // main switch: --annotate I - bool m_annotateAll=false; // main switch: --annotate-all - int m_annotateMin=10; // main switch: --annotate-min I + bool m_annotateAll = false; // main switch: --annotate-all + int m_annotateMin = 10; // main switch: --annotate-min I VlStringSet m_readFiles; // main switch: --read - bool m_rank=false; // main switch: --rank - bool m_unlink=false; // main switch: --unlink + bool m_rank = false; // main switch: --rank + bool m_unlink = false; // main switch: --unlink string m_writeFile; // main switch: --write string m_writeInfoFile; // main switch: --write-info // clang-format on diff --git a/src/VlcPoint.h b/src/VlcPoint.h index 26c8f5c65..c9ea193bb 100644 --- a/src/VlcPoint.h +++ b/src/VlcPoint.h @@ -127,7 +127,7 @@ public: m_points[pointnum].countInc(count); } else { pointnum = m_numPoints++; - VlcPoint point(name, pointnum); + VlcPoint point{name, pointnum}; point.countInc(count); m_points.push_back(point); m_nameMap.emplace(point.name(), point.pointNum()); diff --git a/src/VlcTest.h b/src/VlcTest.h index 7aa4cb77a..cf354d6aa 100644 --- a/src/VlcTest.h +++ b/src/VlcTest.h @@ -112,7 +112,7 @@ public: for (const auto& testp : m_tests) testp->dump(bucketsToo); } VlcTest* newTest(const string& name, vluint64_t testrun, double comp) { - VlcTest* testp = new VlcTest(name, testrun, comp); + VlcTest* const testp = new VlcTest{name, testrun, comp}; m_tests.push_back(testp); return testp; } diff --git a/src/VlcTop.cpp b/src/VlcTop.cpp index 606404666..219218232 100644 --- a/src/VlcTop.cpp +++ b/src/VlcTop.cpp @@ -27,14 +27,14 @@ void VlcTop::readCoverage(const string& filename, bool nonfatal) { UINFO(2, "readCoverage " << filename << endl); - std::ifstream is(filename.c_str()); + std::ifstream is{filename.c_str()}; if (!is) { if (!nonfatal) v3fatal("Can't read " << filename); return; } // Testrun and computrons argument unsupported as yet - VlcTest* testp = tests().newTest(filename, 0, 0); + VlcTest* const testp = tests().newTest(filename, 0, 0); while (!is.eof()) { const string line = V3Os::getline(is); @@ -63,7 +63,7 @@ void VlcTop::readCoverage(const string& filename, bool nonfatal) { void VlcTop::writeCoverage(const string& filename) { UINFO(2, "writeCoverage " << filename << endl); - std::ofstream os(filename.c_str()); + std::ofstream os{filename.c_str()}; if (!os) { v3fatal("Can't write " << filename); return; @@ -79,7 +79,7 @@ void VlcTop::writeCoverage(const string& filename) { void VlcTop::writeInfo(const string& filename) { UINFO(2, "writeInfo " << filename << endl); - std::ofstream os(filename.c_str()); + std::ofstream os{filename.c_str()}; if (!os) { v3fatal("Can't write " << filename); return; @@ -157,7 +157,7 @@ void VlcTop::rank() { VlcBuckets remaining; for (const auto& i : m_points) { - VlcPoint* pointp = &points().pointNumber(i.second); + VlcPoint* const pointp = &points().pointNumber(i.second); // If any tests hit this point, then we'll need to cover it. if (pointp->testsCovering()) remaining.addData(pointp->pointNum(), 1); } @@ -182,7 +182,7 @@ void VlcTop::rank() { } } } - if (VlcTest* testp = bestTestp) { + if (VlcTest* const testp = bestTestp) { testp->rank(nextrank++); testp->rankPoints(bestRemain); remaining.orData(bestTestp->buckets()); @@ -227,7 +227,7 @@ void VlcTop::annotateCalc() { } else if (*covp == '-') { range = true; } else if (std::isdigit(*covp)) { - const char* digitsp = covp; + const char* const digitsp = covp; while (std::isdigit(*covp)) ++covp; --covp; // Will inc in for loop if (!range) start = std::atoi(digitsp); @@ -279,13 +279,13 @@ void VlcTop::annotateOutputFiles(const string& dirname) { UINFO(1, "annotateOutputFile " << filename << " -> " << outfilename << endl); - std::ifstream is(filename.c_str()); + std::ifstream is{filename.c_str()}; if (!is) { v3error("Can't read " << filename); return; } - std::ofstream os(outfilename.c_str()); + std::ofstream os{outfilename.c_str()}; if (!os) { v3fatal("Can't write " << outfilename); return; From 17cc452f79d17edc8bf48bad1de119bc8700e0d4 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Tue, 29 Jun 2021 17:57:07 +0100 Subject: [PATCH 21/84] Add V3VariableOrder pass A separate V3VariableOrder pass is now used to order module variables before Emit. All variables are now ordered together, without consideration for whether they are ports, signals form the design, or additional internal variables added by Verilator (which used to be ordered and emitted as separate groups in Emit). For single threaded models, this is performance neutral. For multi-threaded models, the MTask affinity based sorting was slightly modified, so variables with no MTask affinity are emitted last, otherwise the MTask affinity sets are sorted using the TSP sorter as before, but again, ports, signals, and internal variables are not differentiated. This yields a 2%+ speedup for the multithreaded model on OpenTitan. --- src/Makefile_obj.in | 1 + src/V3AstUserAllocator.h | 9 +- src/V3EmitC.cpp | 134 ++++++++-- src/V3EmitCBase.cpp | 4 +- src/V3EmitCBase.h | 9 +- src/V3EmitCFunc.cpp | 267 -------------------- src/V3EmitCFunc.h | 20 +- src/V3EmitCModel.cpp | 2 +- src/V3VariableOrder.cpp | 207 +++++++++++++++ src/V3VariableOrder.h | 30 +++ src/Verilator.cpp | 4 + test_regress/t/t_dpi_var.pl | 8 +- test_regress/t/t_dpi_var_vlt.pl | 8 +- test_regress/t/t_inst_tree_inl1_pub0.pl | 6 +- test_regress/t/t_unopt_combo_isolate.pl | 10 +- test_regress/t/t_unopt_combo_isolate_vlt.pl | 10 +- 16 files changed, 396 insertions(+), 333 deletions(-) create mode 100644 src/V3VariableOrder.cpp create mode 100644 src/V3VariableOrder.h diff --git a/src/Makefile_obj.in b/src/Makefile_obj.in index 47288dec6..f5ccd3131 100644 --- a/src/Makefile_obj.in +++ b/src/Makefile_obj.in @@ -254,6 +254,7 @@ RAW_OBJS = \ V3Undriven.o \ V3Unknown.o \ V3Unroll.o \ + V3VariableOrder.o \ V3Waiver.o \ V3Width.o \ V3WidthSel.o \ diff --git a/src/V3AstUserAllocator.h b/src/V3AstUserAllocator.h index f00e35550..637b6d2aa 100644 --- a/src/V3AstUserAllocator.h +++ b/src/V3AstUserAllocator.h @@ -33,7 +33,7 @@ template class AstUserAllocatorBase VL private: std::vector m_allocated; - inline T_Data* getUserp(T_Node* nodep) const { + inline T_Data* getUserp(const T_Node* nodep) const { // This simplifies statically as T_UserN is constant. In C++17, use 'if constexpr'. if (T_UserN == 1) { const VNUser user = nodep->user1u(); @@ -100,6 +100,13 @@ public: } return *userp; } + + // Get a reference to the user data + T_Data& operator()(const T_Node* nodep) { + T_Data* userp = getUserp(nodep); + UASSERT_OBJ(userp, nodep, "Missing User data on const AstNode"); + return *userp; + } }; // User pointer allocator classes. T_Node is the type of node the allocator should be applied to diff --git a/src/V3EmitC.cpp b/src/V3EmitC.cpp index 4e7f7cdae..3d573409d 100644 --- a/src/V3EmitC.cpp +++ b/src/V3EmitC.cpp @@ -123,7 +123,7 @@ class EmitCImp final : EmitCFunc { } } } - void emitParams(AstNodeModule* modp, bool init, bool* firstp, string& sectionr) { + void emitParams(AstNodeModule* modp, bool init, string& sectionr) { bool anyi = false; for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { if (const AstVar* varp = VN_CAST(nodep, Var)) { @@ -207,6 +207,99 @@ class EmitCImp final : EmitCFunc { emitCFuncDecl(funcp, modp); } } + void emitVarDecls(const AstNodeModule* modp) { + // Output a list of variable declarations + + std::vector varList; + bool lastAnon = false; // initial value is not important, but is used + + const auto emitCurrentList = [this, &varList, &lastAnon]() { + if (varList.empty()) return; + + if (lastAnon) { // Output as anons + const int anonMembers = varList.size(); + const int lim = v3Global.opt.compLimitMembers(); + int anonL3s = 1; + int anonL2s = 1; + int anonL1s = 1; + if (anonMembers > (lim * lim * lim)) { + anonL3s = (anonMembers + (lim * lim * lim) - 1) / (lim * lim * lim); + anonL2s = lim; + anonL1s = lim; + } else if (anonMembers > (lim * lim)) { + anonL2s = (anonMembers + (lim * lim) - 1) / (lim * lim); + anonL1s = lim; + } else if (anonMembers > lim) { + anonL1s = (anonMembers + lim - 1) / lim; + } + if (anonL1s != 1) + puts("// Anonymous structures to workaround compiler member-count bugs\n"); + auto it = varList.cbegin(); + for (int l3 = 0; l3 < anonL3s && it != varList.cend(); ++l3) { + if (anonL3s != 1) puts("struct {\n"); + for (int l2 = 0; l2 < anonL2s && it != varList.cend(); ++l2) { + if (anonL2s != 1) puts("struct {\n"); + for (int l1 = 0; l1 < anonL1s && it != varList.cend(); ++l1) { + if (anonL1s != 1) puts("struct {\n"); + for (int l0 = 0; l0 < lim && it != varList.cend(); ++l0) { + emitVarDecl(*it); + ++it; + } + if (anonL1s != 1) puts("};\n"); + } + if (anonL2s != 1) puts("};\n"); + } + if (anonL3s != 1) puts("};\n"); + } + // Leftovers, just in case off by one error somewhere above + for (; it != varList.cend(); ++it) emitVarDecl(*it); + } else { // Output as nonanons + for (const auto& pair : varList) emitVarDecl(pair); + } + + varList.clear(); + }; + + // Emit variables in consecutive anon and non-anon batches + for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { + if (const AstVar* const varp = VN_CAST_CONST(nodep, Var)) { + if (varp->isIO() || varp->isSignal() || varp->isClassMember() || varp->isTemp() + || (varp->isParam() && !VN_IS(varp->valuep(), Const))) { + const bool anon = isAnonOk(varp); + if (anon != lastAnon) emitCurrentList(); + lastAnon = anon; + varList.emplace_back(varp); + } + } + } + + // Emit final batch + emitCurrentList(); + } + void emitVarCtors(const AstNodeModule* modp) { + ofp()->indentInc(); + for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { + if (const AstVar* const varp = VN_CAST_CONST(nodep, Var)) { + const AstBasicDType* const dtypep = VN_CAST(varp->dtypeSkipRefp(), BasicDType); + if (!dtypep) continue; + if (varp->isIO() && varp->isSc()) { + puts(", "); + puts(varp->nameProtect()); + puts("("); + putsQuoted(varp->nameProtect()); + puts(")\n"); + } + if (dtypep->keyword().isMTaskState()) { + puts(", "); + puts(varp->nameProtect()); + puts("("); + iterate(varp->valuep()); + puts(")\n"); + } + } + } + ofp()->indentDec(); + } // Medium level void emitCtorImp(AstNodeModule* modp); @@ -248,9 +341,8 @@ void EmitCImp::emitCoverageDecl(AstNodeModule*) { void EmitCImp::emitCtorImp(AstNodeModule* modp) { puts("\n"); - bool first = true; string section; - emitParams(modp, true, &first, section /*ref*/); + emitParams(modp, true, section); const string modName = prefixNameProtect(modp); @@ -264,9 +356,8 @@ void EmitCImp::emitCtorImp(AstNodeModule* modp) { } else { puts(modName + "::" + modName + "(const char* _vcname__)\n"); puts(" : VerilatedModule(_vcname__)\n"); - first = false; // printed the first ':' } - emitVarCtors(&first); + emitVarCtors(modp); puts(" {\n"); @@ -500,15 +591,8 @@ void EmitCImp::emitInt(AstNodeModule* modp) { emitTypedefs(modp->stmtsp()); - string section; - section = "\n// PORTS\n"; - emitVarList(modp->stmtsp(), EVL_CLASS_IO, "", section /*ref*/); - - section = "\n// LOCAL SIGNALS\n"; - emitVarList(modp->stmtsp(), EVL_CLASS_SIG, "", section /*ref*/); - - section = "\n// LOCAL VARIABLES\n"; - emitVarList(modp->stmtsp(), EVL_CLASS_TEMP, "", section /*ref*/); + puts("\n// DESIGN SPECIFIC STATE\n"); + emitVarDecls(modp); puts("\n// INTERNAL VARIABLES\n"); if (!VN_IS(modp, Class)) { // Avoid clang unused error (& don't want in every object) @@ -518,12 +602,10 @@ void EmitCImp::emitInt(AstNodeModule* modp) { ofp()->putsPrivate(false); // public: emitCoverageDecl(modp); // may flip public/private - section = "\n// PARAMETERS\n"; - ofp()->putsPrivate(false); // public: - emitVarList(modp->stmtsp(), EVL_CLASS_PAR, "", - section /*ref*/); // Only those that are non-CONST - bool first = true; - emitParams(modp, false, &first, section /*ref*/); + { + string section = "\n// PARAMETERS\n"; + emitParams(modp, false, section); + } if (!VN_IS(modp, Class)) { puts("\n// CONSTRUCTORS\n"); @@ -588,8 +670,16 @@ void EmitCImp::emitImpTop() { void EmitCImp::emitImp(AstNodeModule* modp) { puts("\n//==========\n"); if (m_slow) { - string section; - emitVarList(modp->stmtsp(), EVL_CLASS_ALL, prefixNameProtect(modp), section /*ref*/); + // Emit static variable definitions + const string prefix = prefixNameProtect(modp); + for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { + if (const AstVar* const varp = VN_CAST_CONST(nodep, Var)) { + if (varp->isStatic()) { + puts(varp->vlArgType(true, false, false, prefix)); + puts(";\n"); + } + } + } if (!VN_IS(modp, Class)) emitCtorImp(modp); if (!VN_IS(modp, Class)) emitConfigureImp(modp); if (!VN_IS(modp, Class)) emitDestructorImp(modp); diff --git a/src/V3EmitCBase.cpp b/src/V3EmitCBase.cpp index f955f99dc..39bc76b0f 100644 --- a/src/V3EmitCBase.cpp +++ b/src/V3EmitCBase.cpp @@ -114,7 +114,7 @@ void EmitCBaseVisitor::emitCFuncDecl(const AstCFunc* funcp, const AstNodeModule* if (!funcp->ifdef().empty()) puts("#endif // " + funcp->ifdef() + "\n"); } -void EmitCBaseVisitor::emitVarDecl(const AstVar* nodep, const string& prefixIfImp, bool asRef) { +void EmitCBaseVisitor::emitVarDecl(const AstVar* nodep, bool asRef) { const AstBasicDType* const basicp = nodep->basicp(); bool refNeedParens = VN_IS(nodep->dtypeSkipRefp(), UnpackArrayDType); @@ -199,7 +199,7 @@ void EmitCBaseVisitor::emitVarDecl(const AstVar* nodep, const string& prefixIfIm && name.substr(name.size() - suffix.size()) == suffix; if (beStatic) puts("static VL_THREAD_LOCAL "); } - puts(nodep->vlArgType(true, false, false, prefixIfImp, asRef)); + puts(nodep->vlArgType(true, false, false, "", asRef)); puts(";\n"); } } diff --git a/src/V3EmitCBase.h b/src/V3EmitCBase.h index b9362991c..043214a53 100644 --- a/src/V3EmitCBase.h +++ b/src/V3EmitCBase.h @@ -75,11 +75,18 @@ public: return modp == v3Global.rootp()->constPoolp()->modp(); } + static bool isAnonOk(const AstVar* varp) { + return v3Global.opt.compLimitMembers() != 0 // Enabled + && !varp->isStatic() // Not a static variable + && !varp->isSc() // Aggregates can't be anon + && (varp->basicp() && !varp->basicp()->isOpaque()); // Aggregates can't be anon + } + static AstCFile* newCFile(const string& filename, bool slow, bool source); string cFuncArgs(const AstCFunc* nodep); void emitCFuncHeader(const AstCFunc* funcp, const AstNodeModule* modp, bool withScope); void emitCFuncDecl(const AstCFunc* funcp, const AstNodeModule* modp, bool cLinkage = false); - void emitVarDecl(const AstVar* nodep, const string& prefixIfImp, bool asRef = false); + void emitVarDecl(const AstVar* nodep, bool asRef = false); void emitModCUse(AstNodeModule* modp, VUseType useType); // CONSTRUCTORS diff --git a/src/V3EmitCFunc.cpp b/src/V3EmitCFunc.cpp index 14a82ea82..e27f628a9 100644 --- a/src/V3EmitCFunc.cpp +++ b/src/V3EmitCFunc.cpp @@ -28,90 +28,9 @@ // We use a static char array in VL_VALUE_STRING constexpr int VL_VALUE_STRING_MAX_WIDTH = 8192; -//###################################################################### -// Establish mtask variable sort order in mtasks mode - -class EmitVarTspSorter final : public V3TSP::TspStateBase { -private: - // MEMBERS - const MTaskIdSet& m_mtaskIds; // Mtask we're ordering - static unsigned s_serialNext; // Unique ID to establish serial order - unsigned m_serial; // Serial ordering -public: - // CONSTRUCTORS - explicit EmitVarTspSorter(const MTaskIdSet& mtaskIds) - : m_mtaskIds(mtaskIds) { // Cannot be {} or GCC 4.8 false warning - m_serial = ++s_serialNext; // Cannot be ()/{} or GCC 4.8 false warning - } - virtual ~EmitVarTspSorter() = default; - // METHODS - virtual bool operator<(const TspStateBase& other) const override { - return operator<(dynamic_cast(other)); - } - bool operator<(const EmitVarTspSorter& other) const { return m_serial < other.m_serial; } - const MTaskIdSet& mtaskIds() const { return m_mtaskIds; } - virtual int cost(const TspStateBase* otherp) const override { - return cost(dynamic_cast(otherp)); - } - virtual int cost(const EmitVarTspSorter* otherp) const { - int cost = diffs(m_mtaskIds, otherp->m_mtaskIds); - cost += diffs(otherp->m_mtaskIds, m_mtaskIds); - return cost; - } - // Returns the number of elements in set_a that don't appear in set_b - static int diffs(const MTaskIdSet& set_a, const MTaskIdSet& set_b) { - int diffs = 0; - for (int i : set_a) { - if (set_b.find(i) == set_b.end()) ++diffs; - } - return diffs; - } -}; - -unsigned EmitVarTspSorter::s_serialNext = 0; - //###################################################################### // EmitCFunc -void EmitCFunc::emitCtorSep(bool* firstp) { - if (*firstp) { - puts(" : "); - *firstp = false; - } else { - puts(", "); - } - if (ofp()->exceededWidth()) puts("\n "); -} - -void EmitCFunc::emitVarCtors(bool* firstp) { - if (!m_ctorVarsVec.empty()) { - ofp()->indentInc(); - if (*firstp) puts("\n"); - for (const AstVar* varp : m_ctorVarsVec) { - const AstBasicDType* const dtypep = VN_CAST(varp->dtypeSkipRefp(), BasicDType); - if (!dtypep) { - puts("// Skipping array: "); - puts(varp->nameProtect()); - puts("\n"); - } else if (dtypep->keyword().isMTaskState()) { - emitCtorSep(firstp); - puts(varp->nameProtect()); - puts("("); - iterate(varp->valuep()); - puts(")"); - } else { - emitCtorSep(firstp); - puts(varp->nameProtect()); - puts("("); - putsQuoted(varp->nameProtect()); - puts(")"); - } - } - puts("\n"); - ofp()->indentDec(); - } -} - bool EmitCFunc::emitSimpleOk(AstNodeMath* nodep) { // Can we put out a simple (A + B) instead of VL_ADD_III(A,B)? if (nodep->emitSimpleOperator() == "") return false; @@ -514,192 +433,6 @@ void EmitCFunc::displayNode(AstNode* nodep, AstScopeName* scopenamep, const stri displayEmit(nodep, isScan); } -void EmitCFunc::emitVarList(AstNode* firstp, EisWhich which, const string& prefixIfImp, - string& sectionr) { - // Put out a list of signal declarations - // in order of 0:clocks, 1:vluint8, 2:vluint16, 4:vluint32, 5:vluint64, 6:wide, 7:arrays - // This aids cache packing and locality - // - // Largest->smallest reduces the number of pad variables. Also - // experimented with alternating between large->small and small->large - // on successive Mtask groups, but then when a new mtask gets added may - // cause a huge delta. - // - // TODO: Move this sort to an earlier visitor stage. - VarSortMap varAnonMap; - VarSortMap varNonanonMap; - - for (int isstatic = 1; isstatic >= 0; isstatic--) { - if (prefixIfImp != "" && !isstatic) continue; - for (AstNode* nodep = firstp; nodep; nodep = nodep->nextp()) { - if (const AstVar* varp = VN_CAST(nodep, Var)) { - bool doit = true; - switch (which) { - case EVL_CLASS_IO: doit = varp->isIO(); break; - case EVL_CLASS_SIG: - doit = ((varp->isSignal() || varp->isClassMember()) && !varp->isIO()); - break; - case EVL_CLASS_TEMP: doit = (varp->isTemp() && !varp->isIO()); break; - case EVL_CLASS_PAR: - doit = (varp->isParam() && !VN_IS(varp->valuep(), Const)); - break; - case EVL_CLASS_ALL: doit = true; break; - default: v3fatalSrc("Bad Case"); - } - if (varp->isStatic() ? !isstatic : isstatic) doit = false; - if (doit) { - const int sigbytes = varp->dtypeSkipRefp()->widthAlignBytes(); - int sortbytes = 9; - if (varp->isUsedClock() && varp->widthMin() == 1) { - sortbytes = 0; - } else if (VN_IS(varp->dtypeSkipRefp(), UnpackArrayDType)) { - sortbytes = 8; - } else if (varp->basicp() && varp->basicp()->isOpaque()) { - sortbytes = 7; - } else if (varp->isScBv() || varp->isScBigUint()) { - sortbytes = 6; - } else if (sigbytes == 8) { - sortbytes = 5; - } else if (sigbytes == 4) { - sortbytes = 4; - } else if (sigbytes == 2) { - sortbytes = 2; - } else if (sigbytes == 1) { - sortbytes = 1; - } - const bool anonOk - = (v3Global.opt.compLimitMembers() != 0 // Enabled - && !varp->isStatic() && !varp->isIO() // Confusing to user - && !varp->isSc() // Aggregates can't be anon - && (varp->basicp() - && !varp->basicp()->isOpaque()) // Aggregates can't be anon - ); - - if (anonOk) { - varAnonMap[sortbytes].push_back(varp); - } else { - varNonanonMap[sortbytes].push_back(varp); - } - } - } - } - } - - if (!varAnonMap.empty() || !varNonanonMap.empty()) { - if (!sectionr.empty()) { - puts(sectionr); - sectionr = ""; - } - VarVec anons; - VarVec nonanons; - emitVarSort(varAnonMap, &anons); - emitVarSort(varNonanonMap, &nonanons); - emitSortedVarList(anons, nonanons, prefixIfImp); - } -} - -void EmitCFunc::emitVarSort(const VarSortMap& vmap, VarVec* sortedp) { - UASSERT(sortedp->empty(), "Sorted should be initially empty"); - if (!v3Global.opt.mtasks()) { - // Plain old serial mode. Sort by size, from small to large, - // to optimize for both packing and small offsets in code. - for (const auto& itr : vmap) { - for (VarVec::const_iterator jt = itr.second.begin(); jt != itr.second.end(); ++jt) { - sortedp->push_back(*jt); - } - } - return; - } - - // MacroTask mode. Sort by MTask-affinity group first, size second. - using MTaskVarSortMap = std::map; - MTaskVarSortMap m2v; - for (VarSortMap::const_iterator it = vmap.begin(); it != vmap.end(); ++it) { - const int size_class = it->first; - const VarVec& vec = it->second; - for (const AstVar* varp : vec) { m2v[varp->mtaskIds()][size_class].push_back(varp); } - } - - // Create a TSP sort state for each MTaskIdSet footprint - V3TSP::StateVec states; - for (MTaskVarSortMap::iterator it = m2v.begin(); it != m2v.end(); ++it) { - states.push_back(new EmitVarTspSorter(it->first)); - } - - // Do the TSP sort - V3TSP::StateVec sorted_states; - V3TSP::tspSort(states, &sorted_states); - - for (V3TSP::StateVec::iterator it = sorted_states.begin(); it != sorted_states.end(); ++it) { - const EmitVarTspSorter* statep = dynamic_cast(*it); - const VarSortMap& localVmap = m2v[statep->mtaskIds()]; - // use rbegin/rend to sort size large->small - for (VarSortMap::const_reverse_iterator jt = localVmap.rbegin(); jt != localVmap.rend(); - ++jt) { - const VarVec& vec = jt->second; - for (VarVec::const_iterator kt = vec.begin(); kt != vec.end(); ++kt) { - sortedp->push_back(*kt); - } - } - VL_DO_DANGLING(delete statep, statep); - } -} - -void EmitCFunc::emitSortedVarList(const VarVec& anons, const VarVec& nonanons, - const string& prefixIfImp) { - string curVarCmt; - // Output anons - { - const int anonMembers = anons.size(); - const int lim = v3Global.opt.compLimitMembers(); - int anonL3s = 1; - int anonL2s = 1; - int anonL1s = 1; - if (anonMembers > (lim * lim * lim)) { - anonL3s = (anonMembers + (lim * lim * lim) - 1) / (lim * lim * lim); - anonL2s = lim; - anonL1s = lim; - } else if (anonMembers > (lim * lim)) { - anonL2s = (anonMembers + (lim * lim) - 1) / (lim * lim); - anonL1s = lim; - } else if (anonMembers > lim) { - anonL1s = (anonMembers + lim - 1) / lim; - } - if (anonL1s != 1) - puts("// Anonymous structures to workaround compiler member-count bugs\n"); - auto it = anons.cbegin(); - for (int l3 = 0; l3 < anonL3s && it != anons.cend(); ++l3) { - if (anonL3s != 1) puts("struct {\n"); - for (int l2 = 0; l2 < anonL2s && it != anons.cend(); ++l2) { - if (anonL2s != 1) puts("struct {\n"); - for (int l1 = 0; l1 < anonL1s && it != anons.cend(); ++l1) { - if (anonL1s != 1) puts("struct {\n"); - for (int l0 = 0; l0 < lim && it != anons.cend(); ++l0) { - const AstVar* varp = *it; - emitVarDecl(varp, prefixIfImp); - ++it; - } - if (anonL1s != 1) puts("};\n"); - } - if (anonL2s != 1) puts("};\n"); - } - if (anonL3s != 1) puts("};\n"); - } - // Leftovers, just in case off by one error somewhere above - for (; it != anons.end(); ++it) { - const AstVar* varp = *it; - emitVarDecl(varp, prefixIfImp); - } - } - // Output nonanons - for (const AstVar* varp : nonanons) { - if (varp->isIO() && varp->isSc()) { m_ctorVarsVec.push_back(varp); } - AstBasicDType* const basicp = varp->basicp(); - if (basicp && basicp->keyword().isMTaskState()) { m_ctorVarsVec.push_back(varp); } - emitVarDecl(varp, prefixIfImp); - } -} - void EmitCFunc::emitCCallArgs(AstNodeCCall* nodep) { bool comma = false; if (nodep->funcp()->isLoose() && !nodep->funcp()->isStatic()) { diff --git a/src/V3EmitCFunc.h b/src/V3EmitCFunc.h index 534739694..295aeee0a 100644 --- a/src/V3EmitCFunc.h +++ b/src/V3EmitCFunc.h @@ -114,12 +114,8 @@ public: class EmitCFunc VL_NOT_FINAL : public EmitCBaseVisitor { private: - using VarVec = std::vector; - using VarSortMap = std::map; // Map size class to VarVec - bool m_suppressSemi; AstVarRef* m_wideTempRefp; // Variable that _WW macros should be setting - VarVec m_ctorVarsVec; // All variables in constructor order int m_labelNum; // Next label number int m_splitSize; // # of cfunc nodes placed into output file int m_splitFilenum; // File number being created, 0 = primary @@ -155,18 +151,6 @@ public: void displayArg(AstNode* dispp, AstNode** elistp, bool isScan, const string& vfmt, bool ignore, char fmtLetter); - enum EisWhich : uint8_t { - EVL_CLASS_IO, - EVL_CLASS_SIG, - EVL_CLASS_TEMP, - EVL_CLASS_PAR, - EVL_CLASS_ALL - }; - void emitVarList(AstNode* firstp, EisWhich which, const string& prefixIfImp, string& sectionr); - static void emitVarSort(const VarSortMap& vmap, VarVec* sortedp); - void emitSortedVarList(const VarVec& anons, const VarVec& nonanons, const string& prefixIfImp); - void emitVarCtors(bool* firstp); - void emitCtorSep(bool* firstp); bool emitSimpleOk(AstNodeMath* nodep); void emitIQW(AstNode* nodep) { // Other abbrevs: "C"har, "S"hort, "F"loat, "D"ouble, stri"N"g @@ -242,7 +226,7 @@ public: for (AstNode* subnodep = nodep->argsp(); subnodep; subnodep = subnodep->nextp()) { if (AstVar* varp = VN_CAST(subnodep, Var)) { - if (varp->isFuncReturn()) emitVarDecl(varp, ""); + if (varp->isFuncReturn()) emitVarDecl(varp); } } @@ -271,7 +255,7 @@ public: virtual void visit(AstVar* nodep) override { UASSERT_OBJ(m_cfuncp, nodep, "Cannot emit non-local variable"); - emitVarDecl(nodep, ""); + emitVarDecl(nodep); } virtual void visit(AstNodeAssign* nodep) override { diff --git a/src/V3EmitCModel.cpp b/src/V3EmitCModel.cpp index 8796a9c4c..78565b568 100644 --- a/src/V3EmitCModel.cpp +++ b/src/V3EmitCModel.cpp @@ -90,7 +90,7 @@ class EmitCModel final : public EmitCFunc { for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { if (const AstVar* const varp = VN_CAST_CONST(nodep, Var)) { if (varp->isPrimaryIO()) { // - emitVarDecl(varp, "", /* asRef: */ true); + emitVarDecl(varp, /* asRef: */ true); } } } diff --git a/src/V3VariableOrder.cpp b/src/V3VariableOrder.cpp new file mode 100644 index 000000000..ea88cd98f --- /dev/null +++ b/src/V3VariableOrder.cpp @@ -0,0 +1,207 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// DESCRIPTION: Verilator: Variable ordering +// +// Code available from: https://verilator.org +// +//************************************************************************* +// +// Copyright 2003-2021 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 +// +//************************************************************************* +// V3VariableOrder's Transformations: +// +// Each module: +// Order module variables +// +//************************************************************************* + +#include "config_build.h" +#include "verilatedos.h" + +#include "V3Global.h" +#include "V3VariableOrder.h" +#include "V3Ast.h" +#include "V3AstUserAllocator.h" +#include "V3EmitCBase.h" +#include "V3TSP.h" + +#include +#include + +//###################################################################### +// Establish mtask variable sort order in mtasks mode + +class VarTspSorter final : public V3TSP::TspStateBase { +private: + // MEMBERS + const MTaskIdSet& m_mtaskIds; // Mtask we're ordering + static unsigned s_serialNext; // Unique ID to establish serial order + unsigned m_serial; // Serial ordering +public: + // CONSTRUCTORS + explicit VarTspSorter(const MTaskIdSet& mtaskIds) + : m_mtaskIds(mtaskIds) { // Cannot be {} or GCC 4.8 false warning + m_serial = ++s_serialNext; // Cannot be ()/{} or GCC 4.8 false warning + } + virtual ~VarTspSorter() = default; + // METHODS + virtual bool operator<(const TspStateBase& other) const override { + return operator<(dynamic_cast(other)); + } + bool operator<(const VarTspSorter& other) const { return m_serial < other.m_serial; } + const MTaskIdSet& mtaskIds() const { return m_mtaskIds; } + virtual int cost(const TspStateBase* otherp) const override { + return cost(dynamic_cast(otherp)); + } + virtual int cost(const VarTspSorter* otherp) const { + int cost = diffs(m_mtaskIds, otherp->m_mtaskIds); + cost += diffs(otherp->m_mtaskIds, m_mtaskIds); + return cost; + } + // Returns the number of elements in set_a that don't appear in set_b + static int diffs(const MTaskIdSet& set_a, const MTaskIdSet& set_b) { + int diffs = 0; + for (int i : set_a) { + if (set_b.find(i) == set_b.end()) ++diffs; + } + return diffs; + } +}; + +unsigned VarTspSorter::s_serialNext = 0; + +class VariableOrder final { + // NODE STATE + // AstVar::user1() -> attributes, via m_attributes + AstUser1InUse m_user1InUse; // AstVar + + struct VarAttributes { + uint32_t stratum; // Roughly equivalent to alignment requirement, to avoid padding + bool anonOk; // Can be emitted as part of anonymous structure + }; + + AstUser1Allocator m_attributes; // Attributes used for sorting + + //###################################################################### + + // Simple sort + void simpleSortVars(std::vector& varps) { + stable_sort(varps.begin(), varps.end(), + [this](const AstVar* ap, const AstVar* bp) -> bool { + if (ap->isStatic() != bp->isStatic()) { // Non-statics before statics + return bp->isStatic(); + } + const auto& attrA = m_attributes(ap); + const auto& attrB = m_attributes(bp); + if (attrA.anonOk != attrB.anonOk) { // Anons before non-anons + return attrA.anonOk; + } + return attrA.stratum < attrB.stratum; // Finally sort by stratum + }); + } + + // Sort by MTask-affinity first, then the same as simpleSortVars + void tspSortVars(std::vector& varps) { + // Map from "MTask affinity" -> "variable list" + std::map> m2v; + for (AstVar* const varp : varps) { m2v[varp->mtaskIds()].push_back(varp); } + + // Create a TSP sort state for each unique MTaskIdSet, except for the empty set + V3TSP::StateVec states; + for (const auto& pair : m2v) { + if (pair.first.empty()) continue; + states.push_back(new VarTspSorter(pair.first)); + } + + // Do the TSP sort + V3TSP::StateVec sortedStates; + V3TSP::tspSort(states, &sortedStates); + + varps.clear(); + + // Helper function to sort given vector, then append to 'varps' + const auto sortAndAppend = [this, &varps](std::vector& subVarps) { + simpleSortVars(subVarps); + for (AstVar* const varp : subVarps) { varps.push_back(varp); } + }; + + // Enumerate by sorted MTaskIdSet, sort within the set separately + for (const V3TSP::TspStateBase* const stateBasep : sortedStates) { + const VarTspSorter* const statep = dynamic_cast(stateBasep); + sortAndAppend(m2v[statep->mtaskIds()]); + VL_DO_DANGLING(delete statep, statep); + } + + // Finally add the variables with no known MTask affinity + sortAndAppend(m2v[MTaskIdSet()]); + } + + void orderModuleVars(AstNodeModule* modp) { + std::vector varps; + + // Unlink all module variables from the module, compute attributes + for (AstNode *nodep = modp->stmtsp(), *nextp; nodep; nodep = nextp) { + nextp = nodep->nextp(); + if (AstVar* const varp = VN_CAST(nodep, Var)) { + // Unlink, add to vector + varp->unlinkFrBack(); + varps.push_back(varp); + // Compute attributes up front + auto& attributes = m_attributes(varp); + // Stratum + const int sigbytes = varp->dtypeSkipRefp()->widthAlignBytes(); + attributes.stratum = (varp->isUsedClock() && varp->widthMin() == 1) ? 0 + : VN_IS(varp->dtypeSkipRefp(), UnpackArrayDType) ? 8 + : (varp->basicp() && varp->basicp()->isOpaque()) ? 7 + : (varp->isScBv() || varp->isScBigUint()) ? 6 + : (sigbytes == 8) ? 5 + : (sigbytes == 4) ? 4 + : (sigbytes == 2) ? 2 + : (sigbytes == 1) ? 1 + : 9; + // Anonymous structure ok + attributes.anonOk = EmitCBaseVisitor::isAnonOk(varp); + } + } + + if (!varps.empty()) { + // Sort variables + if (!v3Global.opt.mtasks()) { + simpleSortVars(varps); + } else { + tspSortVars(varps); + } + + // Insert them back under the module, in the new order, but at + // the front of the list so they come out first in dumps/XML. + auto it = varps.cbegin(); + AstVar* const firstp = *it++; + for (; it != varps.cend(); ++it) firstp->addNext(*it); + if (AstNode* const stmtsp = modp->stmtsp()) { + stmtsp->unlinkFrBackWithNext(); + firstp->addNext(stmtsp); + } + modp->addStmtp(firstp); + } + } + +public: + static void processModule(AstNodeModule* modp) { VariableOrder().orderModuleVars(modp); } +}; + +//###################################################################### +// V3VariableOrder static functions + +void V3VariableOrder::orderAll() { + UINFO(2, __FUNCTION__ << ": " << endl); + for (AstNodeModule* modp = v3Global.rootp()->modulesp(); modp; + modp = VN_CAST(modp->nextp(), NodeModule)) { + VariableOrder::processModule(modp); + } + V3Global::dumpCheckGlobalTree("variableorder", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); +} diff --git a/src/V3VariableOrder.h b/src/V3VariableOrder.h new file mode 100644 index 000000000..0b84c9894 --- /dev/null +++ b/src/V3VariableOrder.h @@ -0,0 +1,30 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// DESCRIPTION: Verilator: Variable ordering +// +// Code available from: https://verilator.org +// +//************************************************************************* +// +// Copyright 2003-2021 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_V3VARIABLEORDER_H_ +#define VERILATOR_V3VARIABLEORDER_H_ + +#include "config_build.h" +#include "verilatedos.h" + +//============================================================================ + +class V3VariableOrder final { +public: + static void orderAll(); +}; + +#endif // Guard diff --git a/src/Verilator.cpp b/src/Verilator.cpp index 842fa04a9..b61275ccc 100644 --- a/src/Verilator.cpp +++ b/src/Verilator.cpp @@ -96,6 +96,7 @@ #include "V3Undriven.h" #include "V3Unknown.h" #include "V3Unroll.h" +#include "V3VariableOrder.h" #include "V3Waiver.h" #include "V3Width.h" @@ -507,6 +508,9 @@ static void process() { // Must be before V3EmitC V3CUse::cUseAll(); + // Order variables + V3VariableOrder::orderAll(); + // emitcInlines is first, as it may set needHInlines which other emitters read V3EmitC::emitcInlines(); V3EmitC::emitcSyms(); diff --git a/test_regress/t/t_dpi_var.pl b/test_regress/t/t_dpi_var.pl index e7f19cd2a..7f3485f5a 100755 --- a/test_regress/t/t_dpi_var.pl +++ b/test_regress/t/t_dpi_var.pl @@ -18,10 +18,10 @@ compile( ); if ($Self->{vlt_all}) { - file_grep("$out_filename", qr/\/i); - file_grep("$out_filename", qr/\/i); - file_grep("$out_filename", qr/\/i); - file_grep("$out_filename", qr/\/i); + file_grep("$out_filename", qr/\/i); + file_grep("$out_filename", qr/\/i); + file_grep("$out_filename", qr/\/i); + file_grep("$out_filename", qr/\/i); } execute( diff --git a/test_regress/t/t_dpi_var_vlt.pl b/test_regress/t/t_dpi_var_vlt.pl index a9d16c3c4..476882c77 100755 --- a/test_regress/t/t_dpi_var_vlt.pl +++ b/test_regress/t/t_dpi_var_vlt.pl @@ -20,10 +20,10 @@ compile( ); if ($Self->{vlt_all}) { - file_grep("$out_filename", qr/\/i); - file_grep("$out_filename", qr/\/i); - file_grep("$out_filename", qr/\/i); - file_grep("$out_filename", qr/\/i); + file_grep("$out_filename", qr/\/i); + file_grep("$out_filename", qr/\/i); + file_grep("$out_filename", qr/\/i); + file_grep("$out_filename", qr/\/i); } execute( diff --git a/test_regress/t/t_inst_tree_inl1_pub0.pl b/test_regress/t/t_inst_tree_inl1_pub0.pl index ec48a4e06..9a8a6aa92 100755 --- a/test_regress/t/t_inst_tree_inl1_pub0.pl +++ b/test_regress/t/t_inst_tree_inl1_pub0.pl @@ -18,9 +18,9 @@ compile( ); if ($Self->{vlt_all}) { - file_grep("$out_filename", qr/\/i); - file_grep("$out_filename", qr/\/i); - file_grep("$out_filename", qr/\/i); + file_grep("$out_filename", qr/\/i); + file_grep("$out_filename", qr/\/i); + file_grep("$out_filename", qr/\/i); } execute( diff --git a/test_regress/t/t_unopt_combo_isolate.pl b/test_regress/t/t_unopt_combo_isolate.pl index 1fd8571d3..cd9c893bb 100755 --- a/test_regress/t/t_unopt_combo_isolate.pl +++ b/test_regress/t/t_unopt_combo_isolate.pl @@ -19,11 +19,11 @@ compile( if ($Self->{vlt_all}) { file_grep($Self->{stats}, qr/Optimizations, isolate_assignments blocks\s+5/i); - file_grep("$out_filename", qr/\/i); - file_grep("$out_filename", qr/\/i); - file_grep("$out_filename", qr/\/i); - file_grep("$out_filename", qr/\/i); - file_grep("$out_filename", qr/\/i); + file_grep("$out_filename", qr/\/i); + file_grep("$out_filename", qr/\/i); + file_grep("$out_filename", qr/\/i); + file_grep("$out_filename", qr/\/i); + file_grep("$out_filename", qr/\/i); } execute( diff --git a/test_regress/t/t_unopt_combo_isolate_vlt.pl b/test_regress/t/t_unopt_combo_isolate_vlt.pl index 8010fe402..a71d1a004 100755 --- a/test_regress/t/t_unopt_combo_isolate_vlt.pl +++ b/test_regress/t/t_unopt_combo_isolate_vlt.pl @@ -19,11 +19,11 @@ compile( if ($Self->{vlt_all}) { file_grep($Self->{stats}, qr/Optimizations, isolate_assignments blocks\s+5/i); - file_grep("$out_filename", qr/\/i); - file_grep("$out_filename", qr/\/i); - file_grep("$out_filename", qr/\/i); - file_grep("$out_filename", qr/\/i); - file_grep("$out_filename", qr/\/i); + file_grep("$out_filename", qr/\/i); + file_grep("$out_filename", qr/\/i); + file_grep("$out_filename", qr/\/i); + file_grep("$out_filename", qr/\/i); + file_grep("$out_filename", qr/\/i); } execute( From 1691bbfe253265daa7591782875aacbd1822840f Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Tue, 13 Jul 2021 14:20:17 +0100 Subject: [PATCH 22/84] Remove unnecesasry and incorrect trace class CUse. --- src/V3CUse.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/V3CUse.cpp b/src/V3CUse.cpp index 2964c2b8f..c4da89455 100644 --- a/src/V3CUse.cpp +++ b/src/V3CUse.cpp @@ -201,11 +201,6 @@ class CUseVisitor final : public AstNVisitor { // VISITORS virtual void visit(AstNodeModule* nodep) override { - if (v3Global.opt.trace()) { - AstCUse* const usep - = m_state.newUse(nodep, VUseType::INT_FWD_CLASS, v3Global.opt.traceClassBase()); - usep->protect(false); - } makeUseCells(nodep); { CUseDTypeVisitor dtypeVisitor{nodep, m_state}; } if (AstClass* const classp = VN_CAST(nodep, Class)) { From 4081a1a53991057537e4277b34a5d112c772bf45 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Wed, 7 Jul 2021 19:16:40 +0100 Subject: [PATCH 23/84] Internals: Separate emitting of C++ headers and implementation Internal AstNodeModule headers (.h) and implementation (.cpp) files are now emitted separately in V3EmitC::emitcHeaders() and V3EmitC::emitcImp() respectively. No functional change intended --- src/Makefile_obj.in | 3 +- src/V3EmitC.cpp | 1133 ---------------------------------------- src/V3EmitC.h | 3 +- src/V3EmitCBase.cpp | 23 +- src/V3EmitCBase.h | 3 +- src/V3EmitCFunc.h | 4 +- src/V3EmitCHeaders.cpp | 351 +++++++++++++ src/V3EmitCImp.cpp | 739 ++++++++++++++++++++++++++ src/V3EmitCModel.cpp | 3 +- src/Verilator.cpp | 5 +- 10 files changed, 1124 insertions(+), 1143 deletions(-) delete mode 100644 src/V3EmitC.cpp create mode 100644 src/V3EmitCHeaders.cpp create mode 100644 src/V3EmitCImp.cpp diff --git a/src/Makefile_obj.in b/src/Makefile_obj.in index f5ccd3131..95e860637 100644 --- a/src/Makefile_obj.in +++ b/src/Makefile_obj.in @@ -179,10 +179,11 @@ RAW_OBJS = \ V3DepthBlock.o \ V3Descope.o \ V3DupFinder.o \ - V3EmitC.o \ V3EmitCBase.o \ V3EmitCConstPool.o \ V3EmitCFunc.o \ + V3EmitCHeaders.o \ + V3EmitCImp.o \ V3EmitCInlines.o \ V3EmitCMain.o \ V3EmitCMake.o \ diff --git a/src/V3EmitC.cpp b/src/V3EmitC.cpp deleted file mode 100644 index 3d573409d..000000000 --- a/src/V3EmitC.cpp +++ /dev/null @@ -1,1133 +0,0 @@ -// -*- mode: C++; c-file-style: "cc-mode" -*- -//************************************************************************* -// DESCRIPTION: Verilator: Emit C++ for tree -// -// Code available from: https://verilator.org -// -//************************************************************************* -// -// Copyright 2003-2021 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 -// -//************************************************************************* - -#include "config_build.h" -#include "verilatedos.h" - -#include "V3Global.h" -#include "V3EmitC.h" -#include "V3EmitCFunc.h" - -#include -#include - -//###################################################################### -// Internal EmitC implementation - -class EmitCImp final : EmitCFunc { - // MEMBERS - AstNodeModule* m_fileModp = nullptr; // Files (names, headers) constructed using this module - bool m_slow = false; // Creating __Slow file - bool m_fast = false; // Creating non __Slow file (or both) - - //--------------------------------------- - // METHODS - - V3OutCFile* newOutCFile(bool slow, bool source, int filenum = 0) { - m_lazyDecls.reset(); // Need to emit new lazy declarations - - string filenameNoExt = v3Global.opt.makeDir() + "/" + prefixNameProtect(m_fileModp); - if (filenum) filenameNoExt += "__" + cvtToStr(filenum); - filenameNoExt += (slow ? "__Slow" : ""); - V3OutCFile* ofp = nullptr; - if (v3Global.opt.lintOnly()) { - // Unfortunately we have some lint checks here, so we can't just skip processing. - // We should move them to a different stage. - const string filename = VL_DEV_NULL; - newCFile(filename, slow, source); - ofp = new V3OutCFile(filename); - } else if (optSystemC()) { - const string filename = filenameNoExt + (source ? ".cpp" : ".h"); - newCFile(filename, slow, source); - ofp = new V3OutScFile(filename); - } else { - const string filename = filenameNoExt + (source ? ".cpp" : ".h"); - newCFile(filename, slow, source); - ofp = new V3OutCFile(filename); - } - - ofp->putsHeader(); - if (source) { - ofp->puts("// DESCRIPTION: Verilator output: Design implementation internals\n"); - } else { - ofp->puts("// DESCRIPTION: Verilator output: Design internal header\n"); - } - ofp->puts("// See " + topClassName() + ".h for the primary calling header\n"); - return ofp; - } - - //--------------------------------------- - // VISITORS - using EmitCFunc::visit; // Suppress hidden overloaded virtual function warning - virtual void visit(AstCFunc* nodep) override { - // TRACE_* and DPI handled elsewhere - if (nodep->isTrace()) return; - if (nodep->dpiImportPrototype()) return; - if (nodep->dpiExportDispatcher()) return; - if (!(nodep->slow() ? m_slow : m_fast)) return; - - maybeSplit(); - - EmitCFunc::visit(nodep); - } - - //--------------------------------------- - // ACCESSORS - - // METHODS - // Low level - void emitTypedefs(AstNode* firstp) { - bool first = true; - for (AstNode* loopp = firstp; loopp; loopp = loopp->nextp()) { - if (const AstTypedef* nodep = VN_CAST(loopp, Typedef)) { - if (nodep->attrPublic()) { - if (first) { - first = false; - puts("\n// TYPEDEFS\n"); - puts("// That were declared public\n"); - } else { - puts("\n"); - } - if (const AstEnumDType* adtypep - = VN_CAST(nodep->dtypep()->skipRefToEnump(), EnumDType)) { - if (adtypep->width() > 64) { - putsDecoration("// enum " + nodep->nameProtect() - + " // Ignored: Too wide for C++\n"); - } else { - puts("enum " + nodep->name() + " {\n"); - for (AstEnumItem* itemp = adtypep->itemsp(); itemp; - itemp = VN_CAST(itemp->nextp(), EnumItem)) { - puts(itemp->nameProtect()); - puts(" = "); - iterateAndNextNull(itemp->valuep()); - if (VN_IS(itemp->nextp(), EnumItem)) puts(","); - puts("\n"); - } - puts("};\n"); - } - } - } - } - } - } - void emitParams(AstNodeModule* modp, bool init, string& sectionr) { - bool anyi = false; - for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { - if (const AstVar* varp = VN_CAST(nodep, Var)) { - if (varp->isParam() && (varp->isUsedParam() || varp->isSigPublic())) { - if (!init && sectionr != "") { - puts(sectionr); - sectionr = ""; - } - UASSERT_OBJ(varp->valuep(), nodep, "No init for a param?"); - // These should be static const values, however older MSVC++ did't - // support them; should be ok now under C++11, need to refactor. - if (varp->isWide()) { // Unsupported for output - if (!init) { - putsDecoration("// enum WData " + varp->nameProtect() + " //wide"); - } - } else if (varp->isString()) { - if (init) { - puts("const std::string "); - puts(prefixNameProtect(modp) + "::" + protect("var_" + varp->name()) - + "("); - iterateAndNextNull(varp->valuep()); - puts(");\n"); - anyi = true; - } else { - puts("static const std::string " + protect("var_" + varp->name()) - + ";\n"); - } - } else if (!VN_IS(varp->valuep(), Const)) { // Unsupported for output - // putsDecoration("// enum ..... "+varp->nameProtect() - // +"not simple value, see variable above instead"); - } else if (VN_IS(varp->dtypep(), BasicDType) - && VN_CAST(varp->dtypep(), BasicDType) - ->isOpaque()) { // Can't put out e.g. doubles - } else { - if (init) { - puts(varp->isQuad() ? "const QData " : "const IData "); - puts(prefixNameProtect(modp) + "::" + protect("var_" + varp->name()) - + "("); - iterateAndNextNull(varp->valuep()); - puts(");\n"); - anyi = true; - } else { - // enum - puts(varp->isQuad() ? "enum _QData" : "enum _IData"); - puts("" + varp->nameProtect() + " { " + varp->nameProtect() + " = "); - iterateAndNextNull(varp->valuep()); - puts("};\n"); - // var - puts(varp->isQuad() ? "static const QData " : "static const IData "); - puts(protect("var_" + varp->name()) + ";\n"); - } - } - } - } - } - if (anyi) puts("\n"); - } - void emitIntFuncDecls(AstNodeModule* modp, bool inClassBody) { - std::vector funcsp; - - for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { - if (const AstCFunc* funcp = VN_CAST(nodep, CFunc)) { - if (funcp->dpiImportPrototype()) // Declared in __Dpi.h - continue; - if (funcp->dpiExportDispatcher()) // Declared in __Dpi.h - continue; - if (funcp->isMethod() != inClassBody) // Only methods go inside class - continue; - if (funcp->isMethod() && funcp->isLoose()) // Loose methods are declared lazily - continue; - funcsp.push_back(funcp); - } - } - - stable_sort(funcsp.begin(), funcsp.end(), [](const AstNode* ap, const AstNode* bp) { // - return ap->name() < bp->name(); - }); - - for (const AstCFunc* funcp : funcsp) { - if (inClassBody) ofp()->putsPrivate(funcp->declPrivate()); - emitCFuncDecl(funcp, modp); - } - } - void emitVarDecls(const AstNodeModule* modp) { - // Output a list of variable declarations - - std::vector varList; - bool lastAnon = false; // initial value is not important, but is used - - const auto emitCurrentList = [this, &varList, &lastAnon]() { - if (varList.empty()) return; - - if (lastAnon) { // Output as anons - const int anonMembers = varList.size(); - const int lim = v3Global.opt.compLimitMembers(); - int anonL3s = 1; - int anonL2s = 1; - int anonL1s = 1; - if (anonMembers > (lim * lim * lim)) { - anonL3s = (anonMembers + (lim * lim * lim) - 1) / (lim * lim * lim); - anonL2s = lim; - anonL1s = lim; - } else if (anonMembers > (lim * lim)) { - anonL2s = (anonMembers + (lim * lim) - 1) / (lim * lim); - anonL1s = lim; - } else if (anonMembers > lim) { - anonL1s = (anonMembers + lim - 1) / lim; - } - if (anonL1s != 1) - puts("// Anonymous structures to workaround compiler member-count bugs\n"); - auto it = varList.cbegin(); - for (int l3 = 0; l3 < anonL3s && it != varList.cend(); ++l3) { - if (anonL3s != 1) puts("struct {\n"); - for (int l2 = 0; l2 < anonL2s && it != varList.cend(); ++l2) { - if (anonL2s != 1) puts("struct {\n"); - for (int l1 = 0; l1 < anonL1s && it != varList.cend(); ++l1) { - if (anonL1s != 1) puts("struct {\n"); - for (int l0 = 0; l0 < lim && it != varList.cend(); ++l0) { - emitVarDecl(*it); - ++it; - } - if (anonL1s != 1) puts("};\n"); - } - if (anonL2s != 1) puts("};\n"); - } - if (anonL3s != 1) puts("};\n"); - } - // Leftovers, just in case off by one error somewhere above - for (; it != varList.cend(); ++it) emitVarDecl(*it); - } else { // Output as nonanons - for (const auto& pair : varList) emitVarDecl(pair); - } - - varList.clear(); - }; - - // Emit variables in consecutive anon and non-anon batches - for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { - if (const AstVar* const varp = VN_CAST_CONST(nodep, Var)) { - if (varp->isIO() || varp->isSignal() || varp->isClassMember() || varp->isTemp() - || (varp->isParam() && !VN_IS(varp->valuep(), Const))) { - const bool anon = isAnonOk(varp); - if (anon != lastAnon) emitCurrentList(); - lastAnon = anon; - varList.emplace_back(varp); - } - } - } - - // Emit final batch - emitCurrentList(); - } - void emitVarCtors(const AstNodeModule* modp) { - ofp()->indentInc(); - for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { - if (const AstVar* const varp = VN_CAST_CONST(nodep, Var)) { - const AstBasicDType* const dtypep = VN_CAST(varp->dtypeSkipRefp(), BasicDType); - if (!dtypep) continue; - if (varp->isIO() && varp->isSc()) { - puts(", "); - puts(varp->nameProtect()); - puts("("); - putsQuoted(varp->nameProtect()); - puts(")\n"); - } - if (dtypep->keyword().isMTaskState()) { - puts(", "); - puts(varp->nameProtect()); - puts("("); - iterate(varp->valuep()); - puts(")\n"); - } - } - } - ofp()->indentDec(); - } - - // Medium level - void emitCtorImp(AstNodeModule* modp); - void emitConfigureImp(AstNodeModule* modp); - void emitCoverageDecl(AstNodeModule* modp); - void emitCoverageImp(AstNodeModule* modp); - void emitDestructorImp(AstNodeModule* modp); - void emitSavableImp(AstNodeModule* modp); - void emitTextSection(AstType type); - // High level - void emitImpTop(); - void emitImp(AstNodeModule* modp); - void emitIntTop(const AstNodeModule* modp); - void emitInt(AstNodeModule* modp); - void maybeSplit(); - -public: - EmitCImp() {} - virtual ~EmitCImp() override = default; - void mainImp(AstNodeModule* modp, bool slow); - void mainInt(AstNodeModule* modp); - void mainDoFunc(AstCFunc* nodep) { iterate(nodep); } -}; - -//###################################################################### -// Internal EmitC - -void EmitCImp::emitCoverageDecl(AstNodeModule*) { - if (v3Global.opt.coverage()) { - ofp()->putsPrivate(false); // Accessed from loose methods - putsDecoration("// Coverage\n"); - puts("void __vlCoverInsert("); - puts(v3Global.opt.threads() ? "std::atomic" : "uint32_t"); - puts("* countp, bool enable, const char* filenamep, int lineno, int column,\n"); - puts("const char* hierp, const char* pagep, const char* commentp, const char* " - "linescovp);\n"); - } -} - -void EmitCImp::emitCtorImp(AstNodeModule* modp) { - puts("\n"); - string section; - emitParams(modp, true, section); - - const string modName = prefixNameProtect(modp); - - puts("\n"); - m_lazyDecls.emit("void " + modName + "__", protect("_ctor_var_reset"), - "(" + modName + "* vlSelf);"); - puts("\n"); - - if (VN_IS(modp, Class)) { - modp->v3fatalSrc("constructors should be AstCFuncs instead"); - } else { - puts(modName + "::" + modName + "(const char* _vcname__)\n"); - puts(" : VerilatedModule(_vcname__)\n"); - } - emitVarCtors(modp); - - puts(" {\n"); - - putsDecoration("// Reset structure values\n"); - puts(modName + "__" + protect("_ctor_var_reset") + "(this);\n"); - emitTextSection(AstType::atScCtor); - - puts("}\n"); -} - -void EmitCImp::emitConfigureImp(AstNodeModule* modp) { - const string modName = prefixNameProtect(modp); - - if (v3Global.opt.coverage()) { - puts("\n"); - m_lazyDecls.emit("void " + modName + "__", protect("_configure_coverage"), - "(" + modName + "* vlSelf, bool first);"); - } - - puts("\nvoid " + modName + "::" + protect("__Vconfigure") + "(" + symClassName() - + "* _vlSymsp, bool first) {\n"); - puts("if (false && first) {} // Prevent unused\n"); - puts("this->vlSymsp = _vlSymsp;\n"); // First, as later stuff needs it. - if (v3Global.opt.coverage()) { - puts(modName + "__" + protect("_configure_coverage") + "(this, first);\n"); - } - puts("}\n"); - splitSizeInc(10); -} - -void EmitCImp::emitCoverageImp(AstNodeModule*) { - if (v3Global.opt.coverage()) { - puts("\n// Coverage\n"); - // Rather than putting out VL_COVER_INSERT calls directly, we do it via this function - // This gets around gcc slowness constructing all of the template arguments. - puts("void " + prefixNameProtect(m_modp) + "::__vlCoverInsert("); - puts(v3Global.opt.threads() ? "std::atomic" : "uint32_t"); - puts("* countp, bool enable, const char* filenamep, int lineno, int column,\n"); - puts("const char* hierp, const char* pagep, const char* commentp, const char* linescovp) " - "{\n"); - if (v3Global.opt.threads()) { - puts("assert(sizeof(uint32_t) == sizeof(std::atomic));\n"); - puts("uint32_t* count32p = reinterpret_cast(countp);\n"); - } else { - puts("uint32_t* count32p = countp;\n"); - } - // static doesn't need save-restore as is constant - puts("static uint32_t fake_zero_count = 0;\n"); - // Used for second++ instantiation of identical bin - puts("if (!enable) count32p = &fake_zero_count;\n"); - puts("*count32p = 0;\n"); - puts("VL_COVER_INSERT(vlSymsp->_vm_contextp__->coveragep(), count32p,"); - puts(" \"filename\",filenamep,"); - puts(" \"lineno\",lineno,"); - puts(" \"column\",column,\n"); - // Need to move hier into scopes and back out if do this - // puts( "\"hier\",std::string(vlSymsp->name())+hierp,"); - puts("\"hier\",std::string(name())+hierp,"); - puts(" \"page\",pagep,"); - puts(" \"comment\",commentp,"); - puts(" (linescovp[0] ? \"linescov\" : \"\"), linescovp);\n"); - puts("}\n"); - splitSizeInc(10); - } -} - -void EmitCImp::emitDestructorImp(AstNodeModule* modp) { - puts("\n"); - puts(prefixNameProtect(modp) + "::~" + prefixNameProtect(modp) + "() {\n"); - emitTextSection(AstType::atScDtor); - puts("}\n"); - splitSizeInc(10); -} - -void EmitCImp::emitSavableImp(AstNodeModule* modp) { - if (v3Global.opt.savable()) { - puts("\n// Savable\n"); - for (int de = 0; de < 2; ++de) { - const string classname = de ? "VerilatedDeserialize" : "VerilatedSerialize"; - const string funcname = de ? "__Vdeserialize" : "__Vserialize"; - const string op = de ? ">>" : "<<"; - // NOLINTNEXTLINE(performance-inefficient-string-concatenation) - puts("void " + prefixNameProtect(modp) + "::" + protect(funcname) + "(" + classname - + "& os) {\n"); - // Place a computed checksum to ensure proper structure save/restore formatting - // OK if this hash includes some things we won't dump, since - // just looking for loading the wrong model - VHashSha256 hash; - for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { - if (const AstVar* varp = VN_CAST(nodep, Var)) { - hash.insert(varp->name()); - hash.insert(varp->dtypep()->width()); - } - } - ofp()->printf("vluint64_t __Vcheckval = 0x%" VL_PRI64 "xULL;\n", - static_cast(hash.digestUInt64())); - if (de) { - puts("os.readAssert(__Vcheckval);\n"); - } else { - puts("os << __Vcheckval;\n"); - } - - // Save context - // If multiple models save the same context we'll save it multiple - // times, but is harmless, and doing it otherwise would break - // backwards compatibility. - puts("os " + op + " vlSymsp->_vm_contextp__;\n"); - - // Save all members - for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { - if (const AstVar* varp = VN_CAST(nodep, Var)) { - if (varp->isIO() && modp->isTop() && optSystemC()) { - // System C top I/O doesn't need loading, as the - // lower level subinst code does it. - } else if (varp->isParam()) { - } else if (varp->isStatic() && varp->isConst()) { - } else { - int vects = 0; - AstNodeDType* elementp = varp->dtypeSkipRefp(); - for (AstUnpackArrayDType* arrayp = VN_CAST(elementp, UnpackArrayDType); - arrayp; arrayp = VN_CAST(elementp, UnpackArrayDType)) { - const int vecnum = vects++; - UASSERT_OBJ(arrayp->hi() >= arrayp->lo(), varp, - "Should have swapped msb & lsb earlier."); - const string ivar = string("__Vi") + cvtToStr(vecnum); - puts("for (int __Vi" + cvtToStr(vecnum) + "=" + cvtToStr(0)); - puts("; " + ivar + "<" + cvtToStr(arrayp->elementsConst())); - puts("; ++" + ivar + ") {\n"); - elementp = arrayp->subDTypep()->skipRefp(); - } - const AstBasicDType* const basicp = elementp->basicp(); - // Do not save MTask state, only matters within an evaluation - if (basicp && basicp->keyword().isMTaskState()) continue; - // Want to detect types that are represented as arrays - // (i.e. packed types of more than 64 bits). - if (elementp->isWide() - && !(basicp && basicp->keyword() == AstBasicDTypeKwd::STRING)) { - const int vecnum = vects++; - const string ivar = string("__Vi") + cvtToStr(vecnum); - puts("for (int __Vi" + cvtToStr(vecnum) + "=" + cvtToStr(0)); - puts("; " + ivar + "<" + cvtToStr(elementp->widthWords())); - puts("; ++" + ivar + ") {\n"); - } - puts("os" + op + varp->nameProtect()); - for (int v = 0; v < vects; ++v) puts("[__Vi" + cvtToStr(v) + "]"); - puts(";\n"); - for (int v = 0; v < vects; ++v) puts("}\n"); - } - } - } - - puts("}\n"); - } - } -} - -void EmitCImp::emitTextSection(AstType type) { - int last_line = -999; - for (AstNode* nodep = m_modp->stmtsp(); nodep; nodep = nodep->nextp()) { - if (const AstNodeText* textp = VN_CAST(nodep, NodeText)) { - if (nodep->type() == type) { - if (last_line != nodep->fileline()->lineno()) { - if (last_line < 0) { - puts("\n//*** Below code from `systemc in Verilog file\n"); - } - putsDecoration( - ifNoProtect("// From `systemc at " + nodep->fileline()->ascii() + "\n")); - last_line = nodep->fileline()->lineno(); - } - ofp()->putsNoTracking(textp->text()); - last_line++; - } - } - } - if (last_line > 0) puts("//*** Above code from `systemc in Verilog file\n\n"); -} - -void EmitCImp::emitIntTop(const AstNodeModule* modp) { - // Always have this first; gcc has short circuiting if #ifdef is first in a file - ofp()->putsGuard(); - puts("\n"); - - ofp()->putsIntTopInclude(); - puts("#include \"verilated_heavy.h\"\n"); - if (v3Global.opt.mtasks()) puts("#include \"verilated_threads.h\"\n"); - if (v3Global.opt.savable()) puts("#include \"verilated_save.h\"\n"); - if (v3Global.opt.coverage()) puts("#include \"verilated_cov.h\"\n"); -} - -void EmitCImp::emitInt(AstNodeModule* modp) { - puts("\n//==========\n\n"); - - if (AstClass* classp = VN_CAST(modp, Class)) { - if (classp->extendsp()) - puts("#include \"" + prefixNameProtect(classp->extendsp()->classp()->classOrPackagep()) - + ".h\"\n"); - } - - emitModCUse(modp, VUseType::INT_INCLUDE); - - // Declare foreign instances up front to make C++ happy - puts("class " + symClassName() + ";\n"); - emitModCUse(modp, VUseType::INT_FWD_CLASS); - - puts("\n//----------\n\n"); - emitTextSection(AstType::atScHdr); - - if (AstClass* classp = VN_CAST(modp, Class)) { - puts("class " + prefixNameProtect(modp)); - if (classp->extendsp()) - puts(" : public " + prefixNameProtect(classp->extendsp()->classp())); - puts(" {\n"); - } else { - puts("VL_MODULE(" + prefixNameProtect(modp) + ") {\n"); - } - ofp()->resetPrivate(); - ofp()->putsPrivate(false); // public: - - { // Instantiated cells - bool did = false; - for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { - if (AstCell* cellp = VN_CAST(nodep, Cell)) { - if (!did) { - did = true; - putsDecoration("// CELLS\n"); - } - puts(prefixNameProtect(cellp->modp()) + "* " + cellp->nameProtect() + ";\n"); - } - } - } - - emitTypedefs(modp->stmtsp()); - - puts("\n// DESIGN SPECIFIC STATE\n"); - emitVarDecls(modp); - - puts("\n// INTERNAL VARIABLES\n"); - if (!VN_IS(modp, Class)) { // Avoid clang unused error (& don't want in every object) - ofp()->putsPrivate(false); // public: so loose methods can pick it up - puts(symClassName() + "* vlSymsp; // Symbol table\n"); - } - ofp()->putsPrivate(false); // public: - emitCoverageDecl(modp); // may flip public/private - - { - string section = "\n// PARAMETERS\n"; - emitParams(modp, false, section); - } - - if (!VN_IS(modp, Class)) { - puts("\n// CONSTRUCTORS\n"); - ofp()->resetPrivate(); - // We don't need a private copy constructor, as VerilatedModule has one for us. - ofp()->putsPrivate(true); - puts("VL_UNCOPYABLE(" + prefixNameProtect(modp) + "); ///< Copying not allowed\n"); - } - - if (VN_IS(modp, Class)) { - // CFuncs with isConstructor/isDestructor used instead - } else { - ofp()->putsPrivate(false); // public: - puts(prefixNameProtect(modp) + "(const char* name);\n"); - puts("~" + prefixNameProtect(modp) + "();\n"); - } - - emitTextSection(AstType::atScInt); - - puts("\n// INTERNAL METHODS\n"); - - if (!VN_IS(modp, Class)) { - ofp()->putsPrivate(false); // public: - puts("void " + protect("__Vconfigure") + "(" + symClassName() + "* symsp, bool first);\n"); - } - - ofp()->putsPrivate(false); // public: - emitIntFuncDecls(modp, true); - - if (v3Global.opt.savable()) { - ofp()->putsPrivate(false); // public: - puts("void " + protect("__Vserialize") + "(VerilatedSerialize& os);\n"); - puts("void " + protect("__Vdeserialize") + "(VerilatedDeserialize& os);\n"); - } - - puts("}"); - if (!VN_IS(modp, Class)) puts(" VL_ATTR_ALIGNED(VL_CACHE_LINE_BYTES)"); - puts(";\n"); - - puts("\n//----------\n\n"); - emitIntFuncDecls(modp, false); -} - -//---------------------------------------------------------------------- - -void EmitCImp::emitImpTop() { - puts("\n"); - puts("#include \"" + prefixNameProtect(m_fileModp) + ".h\"\n"); - puts("#include \"" + symClassName() + ".h\"\n"); - - if (v3Global.dpi()) { - puts("\n"); - puts("#include \"verilated_dpi.h\"\n"); - } - - emitModCUse(m_fileModp, VUseType::IMP_INCLUDE); - emitModCUse(m_fileModp, VUseType::IMP_FWD_CLASS); - - emitTextSection(AstType::atScImpHdr); -} - -void EmitCImp::emitImp(AstNodeModule* modp) { - puts("\n//==========\n"); - if (m_slow) { - // Emit static variable definitions - const string prefix = prefixNameProtect(modp); - for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { - if (const AstVar* const varp = VN_CAST_CONST(nodep, Var)) { - if (varp->isStatic()) { - puts(varp->vlArgType(true, false, false, prefix)); - puts(";\n"); - } - } - } - if (!VN_IS(modp, Class)) emitCtorImp(modp); - if (!VN_IS(modp, Class)) emitConfigureImp(modp); - if (!VN_IS(modp, Class)) emitDestructorImp(modp); - emitSavableImp(modp); - emitCoverageImp(modp); - } - - if (m_fast) { emitTextSection(AstType::atScImp); } - - // Blocks - for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { - if (AstCFunc* funcp = VN_CAST(nodep, CFunc)) { mainDoFunc(funcp); } - } -} - -//###################################################################### - -void EmitCImp::maybeSplit() { - if (!splitNeeded()) return; - - // Splitting file, so using parallel build. - v3Global.useParallelBuild(true); - // Close old file - VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr); - // Open a new file - m_ofp = newOutCFile(!m_fast, true /*source*/, splitFilenumInc()); - emitImpTop(); -} - -void EmitCImp::mainInt(AstNodeModule* modp) { - m_modp = modp; - m_fileModp = modp; - m_slow = true; - m_fast = true; - - UINFO(5, " Emitting " << prefixNameProtect(modp) << endl); - - m_ofp = newOutCFile(false /*slow*/, false /*source*/); - emitIntTop(modp); - emitInt(modp); - if (AstClassPackage* packagep = VN_CAST(modp, ClassPackage)) { - // Put the non-static class implementation in same h file for speed - m_modp = packagep->classp(); - emitInt(packagep->classp()); - m_modp = modp; - } - ofp()->putsEndGuard(); - VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr); -} - -void EmitCImp::mainImp(AstNodeModule* modp, bool slow) { - // Output a module - m_modp = modp; - m_fileModp = modp; - m_slow = slow; - m_fast = !slow; - - UINFO(5, " Emitting " << prefixNameProtect(modp) << endl); - - m_ofp = newOutCFile(!m_fast, true /*source*/); - emitImpTop(); - emitImp(modp); - - if (AstClassPackage* packagep = VN_CAST(modp, ClassPackage)) { - // Put the non-static class implementation in same C++ files as - // often optimizations are possible when both are seen by the - // compiler together - m_modp = packagep->classp(); - emitImp(packagep->classp()); - m_modp = modp; - } - - VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr); -} - -//###################################################################### -// Tracing routines - -class EmitCTrace final : EmitCFunc { - // NODE STATE/TYPES - // Cleared on netlist - // AstNode::user1() -> int. Enum number - AstUser1InUse m_inuser1; - - // MEMBERS - const bool m_slow; // Making slow file - int m_enumNum = 0; // Enumeration number (whole netlist) - - // METHODS - void newOutCFile(int filenum) { - m_lazyDecls.reset(); // Need to emit new lazy declarations - - string filename - = (v3Global.opt.makeDir() + "/" + topClassName() + "_" + protect("_Trace")); - if (filenum) filename += "__" + cvtToStr(filenum); - filename += (m_slow ? "__Slow" : ""); - filename += ".cpp"; - - AstCFile* cfilep = newCFile(filename, m_slow, true /*source*/); - cfilep->support(true); - - if (m_ofp) v3fatalSrc("Previous file not closed"); - if (optSystemC()) { - m_ofp = new V3OutScFile(filename); - } else { - m_ofp = new V3OutCFile(filename); - } - m_ofp->putsHeader(); - m_ofp->puts("// DESCR" - "IPTION: Verilator output: Tracing implementation internals\n"); - - // Includes - puts("#include \"" + v3Global.opt.traceSourceLang() + ".h\"\n"); - puts("#include \"" + symClassName() + ".h\"\n"); - puts("\n"); - } - - bool emitTraceIsScBv(AstTraceInc* nodep) { - const AstVarRef* varrefp = VN_CAST(nodep->declp()->valuep(), VarRef); - if (!varrefp) return false; - AstVar* varp = varrefp->varp(); - return varp->isSc() && varp->isScBv(); - } - - bool emitTraceIsScBigUint(AstTraceInc* nodep) { - const AstVarRef* varrefp = VN_CAST(nodep->declp()->valuep(), VarRef); - if (!varrefp) return false; - AstVar* varp = varrefp->varp(); - return varp->isSc() && varp->isScBigUint(); - } - - bool emitTraceIsScUint(AstTraceInc* nodep) { - const AstVarRef* varrefp = VN_CAST(nodep->declp()->valuep(), VarRef); - if (!varrefp) return false; - AstVar* varp = varrefp->varp(); - return varp->isSc() && varp->isScUint(); - } - - void emitTraceInitOne(AstTraceDecl* nodep, int enumNum) { - if (nodep->dtypep()->basicp()->isDouble()) { - puts("tracep->declDouble"); - } else if (nodep->isWide()) { - puts("tracep->declArray"); - } else if (nodep->isQuad()) { - puts("tracep->declQuad"); - } else if (nodep->bitRange().ranged()) { - puts("tracep->declBus"); - } else { - puts("tracep->declBit"); - } - - puts("(c+" + cvtToStr(nodep->code())); - if (nodep->arrayRange().ranged()) puts("+i*" + cvtToStr(nodep->widthWords())); - puts(","); - if (nodep->isScoped()) puts("Verilated::catName(scopep,"); - putsQuoted(VIdProtect::protectWordsIf(nodep->showname(), nodep->protect())); - if (nodep->isScoped()) puts(",(int)scopet,\" \")"); - // Direction - if (v3Global.opt.traceFormat().fst()) { - puts("," + cvtToStr(enumNum)); - // fstVarDir - if (nodep->declDirection().isInoutish()) { - puts(",FST_VD_INOUT"); - } else if (nodep->declDirection().isWritable()) { - puts(",FST_VD_OUTPUT"); - } else if (nodep->declDirection().isNonOutput()) { - puts(",FST_VD_INPUT"); - } else { - puts(", FST_VD_IMPLICIT"); - } - // - // fstVarType - const AstVarType vartype = nodep->varType(); - const AstBasicDTypeKwd kwd = nodep->declKwd(); - string fstvt; - // Doubles have special decoding properties, so must indicate if a double - if (nodep->dtypep()->basicp()->isDouble()) { - if (vartype == AstVarType::GPARAM || vartype == AstVarType::LPARAM) { - fstvt = "FST_VT_VCD_REAL_PARAMETER"; - } else { - fstvt = "FST_VT_VCD_REAL"; - } - } - // clang-format off - else if (vartype == AstVarType::GPARAM) { fstvt = "FST_VT_VCD_PARAMETER"; } - else if (vartype == AstVarType::LPARAM) { fstvt = "FST_VT_VCD_PARAMETER"; } - else if (vartype == AstVarType::SUPPLY0) { fstvt = "FST_VT_VCD_SUPPLY0"; } - else if (vartype == AstVarType::SUPPLY1) { fstvt = "FST_VT_VCD_SUPPLY1"; } - else if (vartype == AstVarType::TRI0) { fstvt = "FST_VT_VCD_TRI0"; } - else if (vartype == AstVarType::TRI1) { fstvt = "FST_VT_VCD_TRI1"; } - else if (vartype == AstVarType::TRIWIRE) { fstvt = "FST_VT_VCD_TRI"; } - else if (vartype == AstVarType::WIRE) { fstvt = "FST_VT_VCD_WIRE"; } - else if (vartype == AstVarType::PORT) { fstvt = "FST_VT_VCD_WIRE"; } - // - else if (kwd == AstBasicDTypeKwd::INTEGER) { fstvt = "FST_VT_VCD_INTEGER"; } - else if (kwd == AstBasicDTypeKwd::BIT) { fstvt = "FST_VT_SV_BIT"; } - else if (kwd == AstBasicDTypeKwd::LOGIC) { fstvt = "FST_VT_SV_LOGIC"; } - else if (kwd == AstBasicDTypeKwd::INT) { fstvt = "FST_VT_SV_INT"; } - else if (kwd == AstBasicDTypeKwd::SHORTINT) { fstvt = "FST_VT_SV_SHORTINT"; } - else if (kwd == AstBasicDTypeKwd::LONGINT) { fstvt = "FST_VT_SV_LONGINT"; } - else if (kwd == AstBasicDTypeKwd::BYTE) { fstvt = "FST_VT_SV_BYTE"; } - else { fstvt = "FST_VT_SV_BIT"; } - // clang-format on - // - // Not currently supported - // FST_VT_VCD_EVENT - // FST_VT_VCD_PORT - // FST_VT_VCD_SHORTREAL - // FST_VT_VCD_REALTIME - // FST_VT_VCD_SPARRAY - // FST_VT_VCD_TRIAND - // FST_VT_VCD_TRIOR - // FST_VT_VCD_TRIREG - // FST_VT_VCD_WAND - // FST_VT_VCD_WOR - // FST_VT_SV_ENUM - // FST_VT_GEN_STRING - puts("," + fstvt); - } - // Range - if (nodep->arrayRange().ranged()) { - puts(", true,(i+" + cvtToStr(nodep->arrayRange().lo()) + ")"); - } else { - puts(", false,-1"); - } - if (!nodep->dtypep()->basicp()->isDouble() && nodep->bitRange().ranged()) { - puts(", " + cvtToStr(nodep->bitRange().left()) + "," - + cvtToStr(nodep->bitRange().right())); - } - puts(");"); - } - - int emitTraceDeclDType(AstNodeDType* nodep) { - // Return enum number or -1 for none - if (v3Global.opt.traceFormat().fst()) { - // Skip over refs-to-refs, but stop before final ref so can get data type name - // Alternatively back in V3Width we could push enum names from upper typedefs - if (AstEnumDType* enump = VN_CAST(nodep->skipRefToEnump(), EnumDType)) { - int enumNum = enump->user1(); - if (!enumNum) { - enumNum = ++m_enumNum; - enump->user1(enumNum); - int nvals = 0; - puts("{\n"); - puts("const char* " + protect("__VenumItemNames") + "[]\n"); - puts("= {"); - for (AstEnumItem* itemp = enump->itemsp(); itemp; - itemp = VN_CAST(itemp->nextp(), EnumItem)) { - if (++nvals > 1) puts(", "); - putbs("\"" + itemp->prettyName() + "\""); - } - puts("};\n"); - nvals = 0; - puts("const char* " + protect("__VenumItemValues") + "[]\n"); - puts("= {"); - for (AstEnumItem* itemp = enump->itemsp(); itemp; - itemp = VN_CAST(itemp->nextp(), EnumItem)) { - AstConst* constp = VN_CAST(itemp->valuep(), Const); - if (++nvals > 1) puts(", "); - putbs("\"" + constp->num().displayed(nodep, "%0b") + "\""); - } - puts("};\n"); - puts("tracep->declDTypeEnum(" + cvtToStr(enumNum) + ", \"" - + enump->prettyName() + "\", " + cvtToStr(nvals) + ", " - + cvtToStr(enump->widthMin()) + ", " + protect("__VenumItemNames") + ", " - + protect("__VenumItemValues") + ");\n"); - puts("}\n"); - } - return enumNum; - } - } - return -1; - } - - void emitTraceChangeOne(AstTraceInc* nodep, int arrayindex) { - iterateAndNextNull(nodep->precondsp()); - const string func = nodep->full() ? "full" : "chg"; - bool emitWidth = true; - if (nodep->dtypep()->basicp()->isDouble()) { - puts("tracep->" + func + "Double"); - emitWidth = false; - } else if (nodep->isWide() || emitTraceIsScBv(nodep) || emitTraceIsScBigUint(nodep)) { - puts("tracep->" + func + "WData"); - } else if (nodep->isQuad()) { - puts("tracep->" + func + "QData"); - } else if (nodep->declp()->widthMin() > 16) { - puts("tracep->" + func + "IData"); - } else if (nodep->declp()->widthMin() > 8) { - puts("tracep->" + func + "SData"); - } else if (nodep->declp()->widthMin() > 1) { - puts("tracep->" + func + "CData"); - } else { - puts("tracep->" + func + "Bit"); - emitWidth = false; - } - - const uint32_t offset = (arrayindex < 0) ? 0 : (arrayindex * nodep->declp()->widthWords()); - const uint32_t code = nodep->declp()->code() + offset; - puts(v3Global.opt.trueTraceThreads() && !nodep->full() ? "(base+" : "(oldp+"); - puts(cvtToStr(code - nodep->baseCode())); - puts(","); - emitTraceValue(nodep, arrayindex); - if (emitWidth) puts("," + cvtToStr(nodep->declp()->widthMin())); - puts(");\n"); - } - - void emitTraceValue(AstTraceInc* nodep, int arrayindex) { - if (AstVarRef* const varrefp = VN_CAST(nodep->valuep(), VarRef)) { - AstVar* varp = varrefp->varp(); - puts("("); - if (emitTraceIsScBigUint(nodep)) { - puts("(vluint32_t*)"); - } else if (emitTraceIsScBv(nodep)) { - puts("VL_SC_BV_DATAP("); - } - iterate(varrefp); // Put var name out - // Tracing only supports 1D arrays - if (nodep->declp()->arrayRange().ranged()) { - if (arrayindex == -2) { - puts("[i]"); - } else if (arrayindex == -1) { - puts("[0]"); - } else { - puts("[" + cvtToStr(arrayindex) + "]"); - } - } - if (varp->isSc()) puts(".read()"); - if (emitTraceIsScUint(nodep)) { - puts(nodep->isQuad() ? ".to_uint64()" : ".to_uint()"); - } else if (emitTraceIsScBigUint(nodep)) { - puts(".get_raw()"); - } else if (emitTraceIsScBv(nodep)) { - puts(")"); - } - puts(")"); - } else { - puts("("); - iterate(nodep->valuep()); - puts(")"); - } - } - - // VISITORS - using EmitCFunc::visit; // Suppress hidden overloaded virtual function warning - virtual void visit(AstCFunc* nodep) override { - if (!nodep->isTrace()) return; - if (nodep->slow() != m_slow) return; - - if (splitNeeded()) { - // Splitting file, so using parallel build. - v3Global.useParallelBuild(true); - // Close old file - VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr); - // Open a new file - newOutCFile(splitFilenumInc()); - } - - EmitCFunc::visit(nodep); - } - virtual void visit(AstTraceDecl* nodep) override { - const int enumNum = emitTraceDeclDType(nodep->dtypep()); - if (nodep->arrayRange().ranged()) { - puts("{int i; for (i=0; i<" + cvtToStr(nodep->arrayRange().elements()) + "; i++) {\n"); - emitTraceInitOne(nodep, enumNum); - puts("}}\n"); - } else { - emitTraceInitOne(nodep, enumNum); - puts("\n"); - } - } - virtual void visit(AstTraceInc* nodep) override { - if (nodep->declp()->arrayRange().ranged()) { - // It traces faster if we unroll the loop - for (int i = 0; i < nodep->declp()->arrayRange().elements(); i++) { - emitTraceChangeOne(nodep, i); - } - } else { - emitTraceChangeOne(nodep, -1); - } - } - - explicit EmitCTrace(AstNodeModule* modp, bool slow) - : m_slow{slow} { - m_modp = modp; - // Open output file - newOutCFile(0); - // Emit functions - for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { - if (AstCFunc* const funcp = VN_CAST(nodep, CFunc)) { iterate(funcp); } - } - // Close output file - VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr); - } - virtual ~EmitCTrace() override = default; - -public: - static void main(AstNodeModule* modp, bool slow) { EmitCTrace(modp, slow); } -}; - -//###################################################################### -// EmitC class functions - -void V3EmitC::emitc() { - UINFO(2, __FUNCTION__ << ": " << endl); - // Set user4p in all CFunc and Var to point to the containing AstNodeModule - AstUser4InUse user4InUse; - const auto setAll = [](AstNodeModule* modp) -> void { - for (AstNode* nodep = VN_CAST(modp, NodeModule)->stmtsp(); nodep; nodep = nodep->nextp()) { - if (VN_IS(nodep, CFunc) || VN_IS(nodep, Var)) nodep->user4p(modp); - } - }; - for (AstNode* modp = v3Global.rootp()->modulesp(); modp; modp = modp->nextp()) { - setAll(VN_CAST(modp, NodeModule)); - } - setAll(v3Global.rootp()->constPoolp()->modp()); - - // Process each module in turn - for (AstNodeModule* nodep = v3Global.rootp()->modulesp(); nodep; - nodep = VN_CAST(nodep->nextp(), NodeModule)) { - if (VN_IS(nodep, Class)) continue; // Imped with ClassPackage - { - EmitCImp cint; - cint.mainInt(nodep); - cint.mainImp(nodep, true); - } - { - EmitCImp fast; - fast.mainImp(nodep, false); - } - } - - // Emit trace routines (currently they can only exist in the top module) - if (v3Global.opt.trace()) { - EmitCTrace::main(v3Global.rootp()->topModulep(), /* slow: */ true); - EmitCTrace::main(v3Global.rootp()->topModulep(), /* slow: */ false); - } -} - -void V3EmitC::emitcFiles() { - UINFO(2, __FUNCTION__ << ": " << endl); - for (AstNodeFile* filep = v3Global.rootp()->filesp(); filep; - filep = VN_CAST(filep->nextp(), NodeFile)) { - AstCFile* cfilep = VN_CAST(filep, CFile); - if (cfilep && cfilep->tblockp()) { - V3OutCFile of(cfilep->name()); - of.puts("// DESCR" - "IPTION: Verilator generated C++\n"); - EmitCFunc visitor(cfilep->tblockp(), &of, true); - } - } -} diff --git a/src/V3EmitC.h b/src/V3EmitC.h index e52552734..a9e6c5ea9 100644 --- a/src/V3EmitC.h +++ b/src/V3EmitC.h @@ -27,8 +27,9 @@ class V3EmitC final { public: - static void emitc(); static void emitcConstPool(); + static void emitcHeaders(); + static void emitcImp(); static void emitcInlines(); static void emitcModel(); static void emitcSyms(bool dpiHdrOnly = false); diff --git a/src/V3EmitCBase.cpp b/src/V3EmitCBase.cpp index 39bc76b0f..0e740bbb2 100644 --- a/src/V3EmitCBase.cpp +++ b/src/V3EmitCBase.cpp @@ -204,7 +204,7 @@ void EmitCBaseVisitor::emitVarDecl(const AstVar* nodep, bool asRef) { } } -void EmitCBaseVisitor::emitModCUse(AstNodeModule* modp, VUseType useType) { +void EmitCBaseVisitor::emitModCUse(const AstNodeModule* modp, VUseType useType) { string nl; for (AstNode* itemp = modp->stmtsp(); itemp; itemp = itemp->nextp()) { if (AstCUse* usep = VN_CAST(itemp, CUse)) { @@ -221,3 +221,24 @@ void EmitCBaseVisitor::emitModCUse(AstNodeModule* modp, VUseType useType) { } puts(nl); } + +void EmitCBaseVisitor::emitTextSection(const AstNodeModule* modp, AstType type) { + int last_line = -999; + for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { + if (const AstNodeText* textp = VN_CAST(nodep, NodeText)) { + if (nodep->type() == type) { + if (last_line != nodep->fileline()->lineno()) { + if (last_line < 0) { + puts("\n//*** Below code from `systemc in Verilog file\n"); + } + putsDecoration( + ifNoProtect("// From `systemc at " + nodep->fileline()->ascii() + "\n")); + last_line = nodep->fileline()->lineno(); + } + ofp()->putsNoTracking(textp->text()); + last_line++; + } + } + } + if (last_line > 0) puts("//*** Above code from `systemc in Verilog file\n\n"); +} diff --git a/src/V3EmitCBase.h b/src/V3EmitCBase.h index 043214a53..7204012ff 100644 --- a/src/V3EmitCBase.h +++ b/src/V3EmitCBase.h @@ -87,7 +87,8 @@ public: void emitCFuncHeader(const AstCFunc* funcp, const AstNodeModule* modp, bool withScope); void emitCFuncDecl(const AstCFunc* funcp, const AstNodeModule* modp, bool cLinkage = false); void emitVarDecl(const AstVar* nodep, bool asRef = false); - void emitModCUse(AstNodeModule* modp, VUseType useType); + void emitModCUse(const AstNodeModule* modp, VUseType useType); + void emitTextSection(const AstNodeModule* modp, AstType type); // CONSTRUCTORS EmitCBaseVisitor() = default; diff --git a/src/V3EmitCFunc.h b/src/V3EmitCFunc.h index 295aeee0a..533984eb3 100644 --- a/src/V3EmitCFunc.h +++ b/src/V3EmitCFunc.h @@ -125,7 +125,7 @@ private: protected: EmitCLazyDecls m_lazyDecls; // Visitor for emitting lazy declarations bool m_useSelfForThis = false; // Replace "this" with "vlSelf" - AstNodeModule* m_modp = nullptr; // Current module being emitted + const AstNodeModule* m_modp = nullptr; // Current module being emitted AstCFunc* m_cfuncp = nullptr; // Current function being emitted public: @@ -135,7 +135,7 @@ public: // ACCESSORS int splitFilenumInc() { m_splitSize = 0; - return ++m_splitFilenum; + return m_splitFilenum++; } int splitSize() const { return m_splitSize; } void splitSizeInc(int count) { m_splitSize += count; } diff --git a/src/V3EmitCHeaders.cpp b/src/V3EmitCHeaders.cpp new file mode 100644 index 000000000..7e63d8dc5 --- /dev/null +++ b/src/V3EmitCHeaders.cpp @@ -0,0 +1,351 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// DESCRIPTION: Verilator: Emit C++ for tree +// +// Code available from: https://verilator.org +// +//************************************************************************* +// +// Copyright 2003-2021 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 +// +//************************************************************************* + +#include "config_build.h" +#include "verilatedos.h" + +#include "V3Global.h" +#include "V3EmitC.h" +#include "V3EmitCFunc.h" + +#include +#include + +//###################################################################### +// Internal EmitC implementation + +class EmitCHeader final : public EmitCFunc { + // METHODS + void decorateFirst(bool& first, const string& str) { + if (first) { + putsDecoration(str); + first = false; + } + } + void emitCellDecls(const AstNodeModule* modp) { + bool first = true; + for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { + if (const AstCell* const cellp = VN_CAST_CONST(nodep, Cell)) { + decorateFirst(first, "// CELLS\n"); + puts(prefixNameProtect(cellp->modp()) + "* " + cellp->nameProtect() + ";\n"); + } + } + } + void emitDesignVarDecls(const AstNodeModule* modp) { + bool first = true; + std::vector varList; + bool lastAnon = false; // initial value is not important, but is used + + const auto emitCurrentList = [this, &first, &varList, &lastAnon]() { + if (varList.empty()) return; + + decorateFirst(first, "\n// DESIGN SPECIFIC STATE\n"); + + if (lastAnon) { // Output as anons + const int anonMembers = varList.size(); + const int lim = v3Global.opt.compLimitMembers(); + int anonL3s = 1; + int anonL2s = 1; + int anonL1s = 1; + if (anonMembers > (lim * lim * lim)) { + anonL3s = (anonMembers + (lim * lim * lim) - 1) / (lim * lim * lim); + anonL2s = lim; + anonL1s = lim; + } else if (anonMembers > (lim * lim)) { + anonL2s = (anonMembers + (lim * lim) - 1) / (lim * lim); + anonL1s = lim; + } else if (anonMembers > lim) { + anonL1s = (anonMembers + lim - 1) / lim; + } + if (anonL1s != 1) + puts("// Anonymous structures to workaround compiler member-count bugs\n"); + auto it = varList.cbegin(); + for (int l3 = 0; l3 < anonL3s && it != varList.cend(); ++l3) { + if (anonL3s != 1) puts("struct {\n"); + for (int l2 = 0; l2 < anonL2s && it != varList.cend(); ++l2) { + if (anonL2s != 1) puts("struct {\n"); + for (int l1 = 0; l1 < anonL1s && it != varList.cend(); ++l1) { + if (anonL1s != 1) puts("struct {\n"); + for (int l0 = 0; l0 < lim && it != varList.cend(); ++l0) { + emitVarDecl(*it); + ++it; + } + if (anonL1s != 1) puts("};\n"); + } + if (anonL2s != 1) puts("};\n"); + } + if (anonL3s != 1) puts("};\n"); + } + // Leftovers, just in case off by one error somewhere above + for (; it != varList.cend(); ++it) emitVarDecl(*it); + } else { // Output as nonanons + for (const auto& pair : varList) emitVarDecl(pair); + } + + varList.clear(); + }; + + // Emit variables in consecutive anon and non-anon batches + for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { + if (const AstVar* const varp = VN_CAST_CONST(nodep, Var)) { + if (varp->isIO() || varp->isSignal() || varp->isClassMember() || varp->isTemp() + || (varp->isParam() && !VN_IS(varp->valuep(), Const))) { + const bool anon = isAnonOk(varp); + if (anon != lastAnon) emitCurrentList(); + lastAnon = anon; + varList.emplace_back(varp); + } + } + } + + // Emit final batch + emitCurrentList(); + } + void emitInternalVarDecls(const AstNodeModule* modp) { + if (!VN_IS(modp, Class)) { + putsDecoration("\n// INTERNAL VARIABLES\n"); + puts(symClassName() + "* vlSymsp; // Symbol table\n"); + } + } + void emitParamDecls(const AstNodeModule* modp) { + bool first = true; + for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { + if (const AstVar* const varp = VN_CAST(nodep, Var)) { + if (varp->isParam() && (varp->isUsedParam() || varp->isSigPublic())) { + decorateFirst(first, "\n// PARAMETERS\n"); + UASSERT_OBJ(varp->valuep(), nodep, "No init for a param?"); + // These should be static const values, however older MSVC++ did't + // support them; should be ok now under C++11, need to refactor. + if (varp->isWide()) { // Unsupported for output + putsDecoration("// enum WData " + varp->nameProtect() + " //wide"); + } else if (varp->isString()) { + puts("static const std::string " + protect("var_" + varp->name()) + ";\n"); + } else if (!VN_IS(varp->valuep(), Const)) { // Unsupported for output + // putsDecoration("// enum ..... "+varp->nameProtect() + // +"not simple value, see variable above instead"); + } else if (VN_IS(varp->dtypep(), BasicDType) + && VN_CAST(varp->dtypep(), BasicDType) + ->isOpaque()) { // Can't put out e.g. doubles + } else { + // enum + puts(varp->isQuad() ? "enum _QData" : "enum _IData"); + puts("" + varp->nameProtect() + " { " + varp->nameProtect() + " = "); + iterateAndNextNull(varp->valuep()); + puts("};\n"); + // var + puts(varp->isQuad() ? "static const QData " : "static const IData "); + puts(protect("var_" + varp->name()) + ";\n"); + } + } + } + } + } + void emitCtorDtorDecls(const AstNodeModule* modp) { + if (!VN_IS(modp, Class)) { // Classes use CFuncs with isConstructor/isDestructor + const string& name = prefixNameProtect(modp); + putsDecoration("\n// CONSTRUCTORS\n"); + puts(name + "(const char* name);\n"); + puts("~" + name + "();\n"); + puts("VL_UNCOPYABLE(" + name + ");\n"); + } + } + void emitInternalMethodDecls(const AstNodeModule* modp) { + bool first = true; + const string section = "\n// INTERNAL METHODS\n"; + + if (!VN_IS(modp, Class)) { + decorateFirst(first, section); + puts("void " + protect("__Vconfigure") + "(" + symClassName() + + "* symsp, bool first);\n"); + } + + if (v3Global.opt.coverage()) { + decorateFirst(first, section); + puts("void __vlCoverInsert("); + puts(v3Global.opt.threads() ? "std::atomic" : "uint32_t"); + puts("* countp, bool enable, const char* filenamep, int lineno, int column,\n"); + puts("const char* hierp, const char* pagep, const char* commentp, const char* " + "linescovp);\n"); + } + + if (v3Global.opt.savable()) { + decorateFirst(first, section); + puts("void " + protect("__Vserialize") + "(VerilatedSerialize& os);\n"); + puts("void " + protect("__Vdeserialize") + "(VerilatedDeserialize& os);\n"); + } + } + void emitEnums(const AstNodeModule* modp) { + bool first = true; + for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { + const AstTypedef* const tdefp = VN_CAST_CONST(nodep, Typedef); + if (!tdefp) continue; + if (!tdefp->attrPublic()) continue; + const AstEnumDType* const edtypep + = VN_CAST(tdefp->dtypep()->skipRefToEnump(), EnumDType); + if (!edtypep) continue; + decorateFirst(first, "\n// ENUMS (that were declared public)\n"); + if (edtypep->width() > 64) { + putsDecoration("// enum " + tdefp->nameProtect() + " ignored: Too wide for C++\n"); + } else { + puts("enum " + tdefp->name() + " {\n"); + for (const AstEnumItem* itemp = edtypep->itemsp(); itemp; + itemp = VN_CAST(itemp->nextp(), EnumItem)) { + puts(itemp->nameProtect()); + puts(" = "); + iterate(itemp->valuep()); + if (VN_IS(itemp->nextp(), EnumItem)) puts(","); + puts("\n"); + } + puts("};\n"); + } + } + } + void emitFuncDecls(const AstNodeModule* modp, bool inClassBody) { + std::vector funcsp; + + for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { + if (const AstCFunc* const funcp = VN_CAST_CONST(nodep, CFunc)) { + if (funcp->dpiImportPrototype()) // Declared in __Dpi.h + continue; + if (funcp->dpiExportDispatcher()) // Declared in __Dpi.h + continue; + if (funcp->isMethod() != inClassBody) // Only methods go inside class + continue; + if (funcp->isMethod() && funcp->isLoose()) // Loose methods are declared lazily + continue; + funcsp.push_back(funcp); + } + } + + std::stable_sort(funcsp.begin(), funcsp.end(), [](const AstNode* ap, const AstNode* bp) { + return ap->name() < bp->name(); + }); + + for (const AstCFunc* const funcp : funcsp) { + if (inClassBody) ofp()->putsPrivate(funcp->declPrivate()); + emitCFuncDecl(funcp, modp); + } + } + void emitAll(const AstNodeModule* modp) { + // Include files required by this AstNodeModule + if (const AstClass* const classp = VN_CAST_CONST(modp, Class)) { + if (classp->extendsp()) + puts("#include \"" + + prefixNameProtect(classp->extendsp()->classp()->classOrPackagep()) + + ".h\"\n"); + } + emitModCUse(modp, VUseType::INT_INCLUDE); + + // Forward declarations required by this AstNodeModule + puts("\nclass " + symClassName() + ";\n"); + emitModCUse(modp, VUseType::INT_FWD_CLASS); + + // From `systemc_header + emitTextSection(modp, AstType::atScHdr); + + // Open class body {{{ + if (const AstClass* const classp = VN_CAST_CONST(modp, Class)) { + puts("class "); + puts(prefixNameProtect(modp)); + if (classp->extendsp()) { + puts(" : public "); + puts(prefixNameProtect(classp->extendsp()->classp())); + } + } else { + puts("VL_MODULE(" + prefixNameProtect(modp) + ")"); + } + puts(" {\n"); + ofp()->resetPrivate(); + ofp()->putsPrivate(false); // public: + + // Emit all class body contents + emitCellDecls(modp); + emitEnums(modp); + emitDesignVarDecls(modp); + emitInternalVarDecls(modp); + emitParamDecls(modp); + emitCtorDtorDecls(modp); + emitInternalMethodDecls(modp); + emitFuncDecls(modp, /* inClassBody: */ true); + + // From `systemc_interface + emitTextSection(modp, AstType::atScInt); + + // Close class body + if (!VN_IS(modp, Class)) { + puts("} VL_ATTR_ALIGNED(VL_CACHE_LINE_BYTES);\n"); + } else { + puts("};\n"); + } + // }}} + + // Emit out of class function declarations + puts("\n"); + emitFuncDecls(modp, /* inClassBody: */ false); + } + + explicit EmitCHeader(const AstNodeModule* modp) { + UINFO(5, " Emitting header for " << prefixNameProtect(modp) << endl); + + // Open output file + const string filename = v3Global.opt.makeDir() + "/" + prefixNameProtect(modp) + ".h"; + newCFile(filename, /* slow: */ false, /* source: */ false); + m_ofp = v3Global.opt.systemC() ? new V3OutScFile(filename) : new V3OutCFile(filename); + + ofp()->putsHeader(); + puts("// DESCRIPTION: Verilator output: Design internal header\n"); + puts("// See " + topClassName() + ".h for the primary calling header\n"); + + ofp()->putsGuard(); + + // Include files + puts("\n"); + ofp()->putsIntTopInclude(); + puts("#include \"verilated_heavy.h\"\n"); + if (v3Global.opt.mtasks()) puts("#include \"verilated_threads.h\"\n"); + if (v3Global.opt.savable()) puts("#include \"verilated_save.h\"\n"); + if (v3Global.opt.coverage()) puts("#include \"verilated_cov.h\"\n"); + + emitAll(modp); + + if (const AstClassPackage* const packagep = VN_CAST_CONST(modp, ClassPackage)) { + // Put the non-static class implementation in same h file for speed + emitAll(packagep->classp()); + } + + ofp()->putsEndGuard(); + + // Close output file + VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr); + } + virtual ~EmitCHeader() override = default; + +public: + static void main(const AstNodeModule* modp) { EmitCHeader emitCHeader(modp); } +}; + +//###################################################################### +// EmitC class functions + +void V3EmitC::emitcHeaders() { + UINFO(2, __FUNCTION__ << ": " << endl); + + // Process each module in turn + for (const AstNode* nodep = v3Global.rootp()->modulesp(); nodep; nodep = nodep->nextp()) { + if (VN_IS(nodep, Class)) continue; // Declared with the ClassPackage + EmitCHeader::main(VN_CAST_CONST(nodep, NodeModule)); + } +} diff --git a/src/V3EmitCImp.cpp b/src/V3EmitCImp.cpp new file mode 100644 index 000000000..7b767ab99 --- /dev/null +++ b/src/V3EmitCImp.cpp @@ -0,0 +1,739 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// DESCRIPTION: Verilator: Emit C++ for tree +// +// Code available from: https://verilator.org +// +//************************************************************************* +// +// Copyright 2003-2021 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 +// +//************************************************************************* + +#include "config_build.h" +#include "verilatedos.h" + +#include "V3Global.h" +#include "V3EmitC.h" +#include "V3EmitCFunc.h" + +#include +#include + +//###################################################################### +// Internal EmitC implementation + +class EmitCImp final : EmitCFunc { + // MEMBERS + const AstNodeModule* const m_fileModp; // Files names/headers constructed using this module + const bool m_slow; // Creating __Slow file + + // METHODS + void openNextOutputFile() { + UASSERT(!m_ofp, "Output file already open"); + + m_lazyDecls.reset(); // Need to emit new lazy declarations + + if (v3Global.opt.lintOnly()) { + // Unfortunately we have some lint checks here, so we can't just skip processing. + // We should move them to a different stage. + const string filename = VL_DEV_NULL; + newCFile(filename, /* slow: */ m_slow, /* source: */ true); + m_ofp = new V3OutCFile(filename); + } else { + string filename = v3Global.opt.makeDir() + "/" + prefixNameProtect(m_fileModp); + if (const int filenum = splitFilenumInc()) filename += "__" + cvtToStr(filenum); + if (m_slow) filename += "__Slow"; + filename += ".cpp"; + newCFile(filename, /* slow: */ m_slow, /* source: */ true); + m_ofp = v3Global.opt.systemC() ? new V3OutScFile(filename) : new V3OutCFile(filename); + } + + ofp()->putsHeader(); + puts("// DESCRIPTION: Verilator output: Design implementation internals\n"); + puts("// See " + topClassName() + ".h for the primary calling header\n"); + + // Include files + puts("\n"); + puts("#include \"" + prefixNameProtect(m_fileModp) + ".h\"\n"); + puts("#include \"" + symClassName() + ".h\"\n"); + + if (v3Global.dpi()) { + puts("\n"); + puts("#include \"verilated_dpi.h\"\n"); + } + + emitModCUse(m_fileModp, VUseType::IMP_INCLUDE); + emitModCUse(m_fileModp, VUseType::IMP_FWD_CLASS); + + emitTextSection(m_modp, AstType::atScImpHdr); + } + + void emitParamDefns(const AstNodeModule* modp) { + for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { + if (const AstVar* const varp = VN_CAST_CONST(nodep, Var)) { + if (varp->isParam() && (varp->isUsedParam() || varp->isSigPublic())) { + UASSERT_OBJ(varp->valuep(), nodep, "No init for a param?"); + // These should be static const values, however older MSVC++ did't + // support them; should be ok now under C++11, need to refactor. + if (varp->isWide()) { // Unsupported for output + } else if (varp->isString()) { + puts("const std::string "); + puts(prefixNameProtect(modp) + "::" + protect("var_" + varp->name()) + + "("); + iterateAndNextNull(varp->valuep()); + puts(");\n"); + } else if (!VN_IS(varp->valuep(), Const)) { // Unsupported for output + // putsDecoration("// enum ..... "+varp->nameProtect() + // +"not simple value, see variable above instead"); + } else if (VN_IS(varp->dtypep(), BasicDType) + && VN_CAST(varp->dtypep(), BasicDType) + ->isOpaque()) { // Can't put out e.g. doubles + } else { + puts(varp->isQuad() ? "const QData " : "const IData "); + puts(prefixNameProtect(modp) + "::" + protect("var_" + varp->name()) + + "("); + iterateAndNextNull(varp->valuep()); + puts(");\n"); + } + } + } + } + } + void emitCtorImp(const AstNodeModule* modp) { + const string modName = prefixNameProtect(modp); + + puts("\n"); + m_lazyDecls.emit("void " + modName + "__", protect("_ctor_var_reset"), + "(" + modName + "* vlSelf);"); + puts("\n"); + + puts(modName + "::" + modName + "(const char* _vcname__)\n"); + puts(" : VerilatedModule(_vcname__)\n"); + + ofp()->indentInc(); + for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { + if (const AstVar* const varp = VN_CAST_CONST(nodep, Var)) { + if (const AstBasicDType* const dtypep + = VN_CAST(varp->dtypeSkipRefp(), BasicDType)) { + if (dtypep->keyword().isMTaskState()) { + puts(", "); + puts(varp->nameProtect()); + puts("("); + iterate(varp->valuep()); + puts(")\n"); + } else if (varp->isIO() && varp->isSc()) { + puts(", "); + puts(varp->nameProtect()); + puts("("); + putsQuoted(varp->nameProtect()); + puts(")\n"); + } + } + } + } + ofp()->indentDec(); + + puts(" {\n"); + + putsDecoration("// Reset structure values\n"); + puts(modName + "__" + protect("_ctor_var_reset") + "(this);\n"); + emitTextSection(modp, AstType::atScCtor); + + puts("}\n"); + } + void emitConfigureImp(const AstNodeModule* modp) { + const string modName = prefixNameProtect(modp); + + if (v3Global.opt.coverage()) { + puts("\n"); + m_lazyDecls.emit("void " + modName + "__", protect("_configure_coverage"), + "(" + modName + "* vlSelf, bool first);"); + } + + puts("\nvoid " + modName + "::" + protect("__Vconfigure") + "(" + symClassName() + + "* _vlSymsp, bool first) {\n"); + puts("if (false && first) {} // Prevent unused\n"); + puts("this->vlSymsp = _vlSymsp;\n"); // First, as later stuff needs it. + if (v3Global.opt.coverage()) { + puts(modName + "__" + protect("_configure_coverage") + "(this, first);\n"); + } + puts("}\n"); + splitSizeInc(10); + } + void emitCoverageImp() { + if (v3Global.opt.coverage()) { + puts("\n// Coverage\n"); + // Rather than putting out VL_COVER_INSERT calls directly, we do it via this function + // This gets around gcc slowness constructing all of the template arguments. + puts("void " + prefixNameProtect(m_modp) + "::__vlCoverInsert("); + puts(v3Global.opt.threads() ? "std::atomic" : "uint32_t"); + puts("* countp, bool enable, const char* filenamep, int lineno, int column,\n"); + puts("const char* hierp, const char* pagep, const char* commentp, const char* " + "linescovp) " + "{\n"); + if (v3Global.opt.threads()) { + puts("assert(sizeof(uint32_t) == sizeof(std::atomic));\n"); + puts("uint32_t* count32p = reinterpret_cast(countp);\n"); + } else { + puts("uint32_t* count32p = countp;\n"); + } + // static doesn't need save-restore as is constant + puts("static uint32_t fake_zero_count = 0;\n"); + // Used for second++ instantiation of identical bin + puts("if (!enable) count32p = &fake_zero_count;\n"); + puts("*count32p = 0;\n"); + puts("VL_COVER_INSERT(vlSymsp->_vm_contextp__->coveragep(), count32p,"); + puts(" \"filename\",filenamep,"); + puts(" \"lineno\",lineno,"); + puts(" \"column\",column,\n"); + // Need to move hier into scopes and back out if do this + // puts( "\"hier\",std::string(vlSymsp->name())+hierp,"); + puts("\"hier\",std::string(name())+hierp,"); + puts(" \"page\",pagep,"); + puts(" \"comment\",commentp,"); + puts(" (linescovp[0] ? \"linescov\" : \"\"), linescovp);\n"); + puts("}\n"); + splitSizeInc(10); + } + } + void emitDestructorImp(const AstNodeModule* modp) { + puts("\n"); + puts(prefixNameProtect(modp) + "::~" + prefixNameProtect(modp) + "() {\n"); + emitTextSection(modp, AstType::atScDtor); + puts("}\n"); + splitSizeInc(10); + } + void emitSavableImp(const AstNodeModule* modp) { + if (v3Global.opt.savable()) { + puts("\n// Savable\n"); + for (int de = 0; de < 2; ++de) { + const string classname = de ? "VerilatedDeserialize" : "VerilatedSerialize"; + const string funcname = de ? "__Vdeserialize" : "__Vserialize"; + const string op = de ? ">>" : "<<"; + // NOLINTNEXTLINE(performance-inefficient-string-concatenation) + puts("void " + prefixNameProtect(modp) + "::" + protect(funcname) + "(" + classname + + "& os) {\n"); + // Place a computed checksum to ensure proper structure save/restore formatting + // OK if this hash includes some things we won't dump, since + // just looking for loading the wrong model + VHashSha256 hash; + for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { + if (const AstVar* varp = VN_CAST(nodep, Var)) { + hash.insert(varp->name()); + hash.insert(varp->dtypep()->width()); + } + } + ofp()->printf("vluint64_t __Vcheckval = 0x%" VL_PRI64 "xULL;\n", + static_cast(hash.digestUInt64())); + if (de) { + puts("os.readAssert(__Vcheckval);\n"); + } else { + puts("os << __Vcheckval;\n"); + } + + // Save context + // If multiple models save the same context we'll save it multiple + // times, but is harmless, and doing it otherwise would break + // backwards compatibility. + puts("os " + op + " vlSymsp->_vm_contextp__;\n"); + + // Save all members + for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { + if (const AstVar* varp = VN_CAST(nodep, Var)) { + if (varp->isIO() && modp->isTop() && optSystemC()) { + // System C top I/O doesn't need loading, as the + // lower level subinst code does it. + } else if (varp->isParam()) { + } else if (varp->isStatic() && varp->isConst()) { + } else { + int vects = 0; + AstNodeDType* elementp = varp->dtypeSkipRefp(); + for (AstUnpackArrayDType* arrayp = VN_CAST(elementp, UnpackArrayDType); + arrayp; arrayp = VN_CAST(elementp, UnpackArrayDType)) { + const int vecnum = vects++; + UASSERT_OBJ(arrayp->hi() >= arrayp->lo(), varp, + "Should have swapped msb & lsb earlier."); + const string ivar = string("__Vi") + cvtToStr(vecnum); + puts("for (int __Vi" + cvtToStr(vecnum) + "=" + cvtToStr(0)); + puts("; " + ivar + "<" + cvtToStr(arrayp->elementsConst())); + puts("; ++" + ivar + ") {\n"); + elementp = arrayp->subDTypep()->skipRefp(); + } + const AstBasicDType* const basicp = elementp->basicp(); + // Do not save MTask state, only matters within an evaluation + if (basicp && basicp->keyword().isMTaskState()) continue; + // Want to detect types that are represented as arrays + // (i.e. packed types of more than 64 bits). + if (elementp->isWide() + && !(basicp && basicp->keyword() == AstBasicDTypeKwd::STRING)) { + const int vecnum = vects++; + const string ivar = string("__Vi") + cvtToStr(vecnum); + puts("for (int __Vi" + cvtToStr(vecnum) + "=" + cvtToStr(0)); + puts("; " + ivar + "<" + cvtToStr(elementp->widthWords())); + puts("; ++" + ivar + ") {\n"); + } + puts("os" + op + varp->nameProtect()); + for (int v = 0; v < vects; ++v) puts("[__Vi" + cvtToStr(v) + "]"); + puts(";\n"); + for (int v = 0; v < vects; ++v) puts("}\n"); + } + } + } + + puts("}\n"); + } + } + } + void emitAll(const AstNodeModule* modp) { + if (m_slow) { + // Emit static variable definitions + const string modName = prefixNameProtect(modp); + for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { + if (const AstVar* const varp = VN_CAST_CONST(nodep, Var)) { + if (varp->isStatic()) { + puts(varp->vlArgType(true, false, false, modName)); + puts(";\n"); + } + } + } + + if (!VN_IS(modp, Class)) { + emitParamDefns(modp); + emitCtorImp(modp); + emitConfigureImp(modp); + emitDestructorImp(modp); + } + emitSavableImp(modp); + emitCoverageImp(); + } else { + emitTextSection(modp, AstType::atScImp); + } + + // Emit all AstCFunc + for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { + if (AstCFunc* const funcp = VN_CAST(nodep, CFunc)) iterate(funcp); + } + } + + // VISITORS + virtual void visit(AstCFunc* nodep) override { + // TRACE_* and DPI handled elsewhere + if (nodep->isTrace()) return; + if (nodep->dpiImportPrototype()) return; + if (nodep->dpiExportDispatcher()) return; + if (nodep->slow() != m_slow) return; + + if (splitNeeded()) { + // Splitting file, so using parallel build. + v3Global.useParallelBuild(true); + // Close old file + VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr); + // Open a new file + openNextOutputFile(); + } + + EmitCFunc::visit(nodep); + } + + explicit EmitCImp(const AstNodeModule* modp, bool slow) + : m_fileModp{modp} + , m_slow{slow} { + UINFO(5, " Emitting implementation of " << prefixNameProtect(modp) << endl); + + m_modp = modp; + + openNextOutputFile(); + + emitAll(modp); + + if (const AstClassPackage* const packagep = VN_CAST_CONST(modp, ClassPackage)) { + // Put the non-static class implementation in same C++ files as + // often optimizations are possible when both are seen by the + // compiler together + m_modp = packagep->classp(); + emitAll(packagep->classp()); + } + + // Close output file + VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr); + } + virtual ~EmitCImp() override = default; + +public: + static void main(const AstNodeModule* modp, bool slow) { EmitCImp{modp, slow}; } +}; + +//###################################################################### +// Tracing routines + +class EmitCTrace final : EmitCFunc { + // NODE STATE/TYPES + // Cleared on netlist + // AstNode::user1() -> int. Enum number + AstUser1InUse m_inuser1; + + // MEMBERS + const bool m_slow; // Making slow file + int m_enumNum = 0; // Enumeration number (whole netlist) + + // METHODS + void newOutCFile(int filenum) { + m_lazyDecls.reset(); // Need to emit new lazy declarations + + string filename + = (v3Global.opt.makeDir() + "/" + topClassName() + "_" + protect("_Trace")); + if (filenum) filename += "__" + cvtToStr(filenum); + filename += (m_slow ? "__Slow" : ""); + filename += ".cpp"; + + AstCFile* cfilep = newCFile(filename, m_slow, true /*source*/); + cfilep->support(true); + + if (m_ofp) v3fatalSrc("Previous file not closed"); + if (optSystemC()) { + m_ofp = new V3OutScFile(filename); + } else { + m_ofp = new V3OutCFile(filename); + } + m_ofp->putsHeader(); + m_ofp->puts("// DESCR" + "IPTION: Verilator output: Tracing implementation internals\n"); + + // Includes + puts("#include \"" + v3Global.opt.traceSourceLang() + ".h\"\n"); + puts("#include \"" + symClassName() + ".h\"\n"); + puts("\n"); + } + + bool emitTraceIsScBv(AstTraceInc* nodep) { + const AstVarRef* varrefp = VN_CAST(nodep->declp()->valuep(), VarRef); + if (!varrefp) return false; + AstVar* varp = varrefp->varp(); + return varp->isSc() && varp->isScBv(); + } + + bool emitTraceIsScBigUint(AstTraceInc* nodep) { + const AstVarRef* varrefp = VN_CAST(nodep->declp()->valuep(), VarRef); + if (!varrefp) return false; + AstVar* varp = varrefp->varp(); + return varp->isSc() && varp->isScBigUint(); + } + + bool emitTraceIsScUint(AstTraceInc* nodep) { + const AstVarRef* varrefp = VN_CAST(nodep->declp()->valuep(), VarRef); + if (!varrefp) return false; + AstVar* varp = varrefp->varp(); + return varp->isSc() && varp->isScUint(); + } + + void emitTraceInitOne(AstTraceDecl* nodep, int enumNum) { + if (nodep->dtypep()->basicp()->isDouble()) { + puts("tracep->declDouble"); + } else if (nodep->isWide()) { + puts("tracep->declArray"); + } else if (nodep->isQuad()) { + puts("tracep->declQuad"); + } else if (nodep->bitRange().ranged()) { + puts("tracep->declBus"); + } else { + puts("tracep->declBit"); + } + + puts("(c+" + cvtToStr(nodep->code())); + if (nodep->arrayRange().ranged()) puts("+i*" + cvtToStr(nodep->widthWords())); + puts(","); + if (nodep->isScoped()) puts("Verilated::catName(scopep,"); + putsQuoted(VIdProtect::protectWordsIf(nodep->showname(), nodep->protect())); + if (nodep->isScoped()) puts(",(int)scopet,\" \")"); + // Direction + if (v3Global.opt.traceFormat().fst()) { + puts("," + cvtToStr(enumNum)); + // fstVarDir + if (nodep->declDirection().isInoutish()) { + puts(",FST_VD_INOUT"); + } else if (nodep->declDirection().isWritable()) { + puts(",FST_VD_OUTPUT"); + } else if (nodep->declDirection().isNonOutput()) { + puts(",FST_VD_INPUT"); + } else { + puts(", FST_VD_IMPLICIT"); + } + // + // fstVarType + const AstVarType vartype = nodep->varType(); + const AstBasicDTypeKwd kwd = nodep->declKwd(); + string fstvt; + // Doubles have special decoding properties, so must indicate if a double + if (nodep->dtypep()->basicp()->isDouble()) { + if (vartype == AstVarType::GPARAM || vartype == AstVarType::LPARAM) { + fstvt = "FST_VT_VCD_REAL_PARAMETER"; + } else { + fstvt = "FST_VT_VCD_REAL"; + } + } + // clang-format off + else if (vartype == AstVarType::GPARAM) { fstvt = "FST_VT_VCD_PARAMETER"; } + else if (vartype == AstVarType::LPARAM) { fstvt = "FST_VT_VCD_PARAMETER"; } + else if (vartype == AstVarType::SUPPLY0) { fstvt = "FST_VT_VCD_SUPPLY0"; } + else if (vartype == AstVarType::SUPPLY1) { fstvt = "FST_VT_VCD_SUPPLY1"; } + else if (vartype == AstVarType::TRI0) { fstvt = "FST_VT_VCD_TRI0"; } + else if (vartype == AstVarType::TRI1) { fstvt = "FST_VT_VCD_TRI1"; } + else if (vartype == AstVarType::TRIWIRE) { fstvt = "FST_VT_VCD_TRI"; } + else if (vartype == AstVarType::WIRE) { fstvt = "FST_VT_VCD_WIRE"; } + else if (vartype == AstVarType::PORT) { fstvt = "FST_VT_VCD_WIRE"; } + // + else if (kwd == AstBasicDTypeKwd::INTEGER) { fstvt = "FST_VT_VCD_INTEGER"; } + else if (kwd == AstBasicDTypeKwd::BIT) { fstvt = "FST_VT_SV_BIT"; } + else if (kwd == AstBasicDTypeKwd::LOGIC) { fstvt = "FST_VT_SV_LOGIC"; } + else if (kwd == AstBasicDTypeKwd::INT) { fstvt = "FST_VT_SV_INT"; } + else if (kwd == AstBasicDTypeKwd::SHORTINT) { fstvt = "FST_VT_SV_SHORTINT"; } + else if (kwd == AstBasicDTypeKwd::LONGINT) { fstvt = "FST_VT_SV_LONGINT"; } + else if (kwd == AstBasicDTypeKwd::BYTE) { fstvt = "FST_VT_SV_BYTE"; } + else { fstvt = "FST_VT_SV_BIT"; } + // clang-format on + // + // Not currently supported + // FST_VT_VCD_EVENT + // FST_VT_VCD_PORT + // FST_VT_VCD_SHORTREAL + // FST_VT_VCD_REALTIME + // FST_VT_VCD_SPARRAY + // FST_VT_VCD_TRIAND + // FST_VT_VCD_TRIOR + // FST_VT_VCD_TRIREG + // FST_VT_VCD_WAND + // FST_VT_VCD_WOR + // FST_VT_SV_ENUM + // FST_VT_GEN_STRING + puts("," + fstvt); + } + // Range + if (nodep->arrayRange().ranged()) { + puts(", true,(i+" + cvtToStr(nodep->arrayRange().lo()) + ")"); + } else { + puts(", false,-1"); + } + if (!nodep->dtypep()->basicp()->isDouble() && nodep->bitRange().ranged()) { + puts(", " + cvtToStr(nodep->bitRange().left()) + "," + + cvtToStr(nodep->bitRange().right())); + } + puts(");"); + } + + int emitTraceDeclDType(AstNodeDType* nodep) { + // Return enum number or -1 for none + if (v3Global.opt.traceFormat().fst()) { + // Skip over refs-to-refs, but stop before final ref so can get data type name + // Alternatively back in V3Width we could push enum names from upper typedefs + if (AstEnumDType* enump = VN_CAST(nodep->skipRefToEnump(), EnumDType)) { + int enumNum = enump->user1(); + if (!enumNum) { + enumNum = ++m_enumNum; + enump->user1(enumNum); + int nvals = 0; + puts("{\n"); + puts("const char* " + protect("__VenumItemNames") + "[]\n"); + puts("= {"); + for (AstEnumItem* itemp = enump->itemsp(); itemp; + itemp = VN_CAST(itemp->nextp(), EnumItem)) { + if (++nvals > 1) puts(", "); + putbs("\"" + itemp->prettyName() + "\""); + } + puts("};\n"); + nvals = 0; + puts("const char* " + protect("__VenumItemValues") + "[]\n"); + puts("= {"); + for (AstEnumItem* itemp = enump->itemsp(); itemp; + itemp = VN_CAST(itemp->nextp(), EnumItem)) { + AstConst* constp = VN_CAST(itemp->valuep(), Const); + if (++nvals > 1) puts(", "); + putbs("\"" + constp->num().displayed(nodep, "%0b") + "\""); + } + puts("};\n"); + puts("tracep->declDTypeEnum(" + cvtToStr(enumNum) + ", \"" + + enump->prettyName() + "\", " + cvtToStr(nvals) + ", " + + cvtToStr(enump->widthMin()) + ", " + protect("__VenumItemNames") + ", " + + protect("__VenumItemValues") + ");\n"); + puts("}\n"); + } + return enumNum; + } + } + return -1; + } + + void emitTraceChangeOne(AstTraceInc* nodep, int arrayindex) { + iterateAndNextNull(nodep->precondsp()); + const string func = nodep->full() ? "full" : "chg"; + bool emitWidth = true; + if (nodep->dtypep()->basicp()->isDouble()) { + puts("tracep->" + func + "Double"); + emitWidth = false; + } else if (nodep->isWide() || emitTraceIsScBv(nodep) || emitTraceIsScBigUint(nodep)) { + puts("tracep->" + func + "WData"); + } else if (nodep->isQuad()) { + puts("tracep->" + func + "QData"); + } else if (nodep->declp()->widthMin() > 16) { + puts("tracep->" + func + "IData"); + } else if (nodep->declp()->widthMin() > 8) { + puts("tracep->" + func + "SData"); + } else if (nodep->declp()->widthMin() > 1) { + puts("tracep->" + func + "CData"); + } else { + puts("tracep->" + func + "Bit"); + emitWidth = false; + } + + const uint32_t offset = (arrayindex < 0) ? 0 : (arrayindex * nodep->declp()->widthWords()); + const uint32_t code = nodep->declp()->code() + offset; + puts(v3Global.opt.trueTraceThreads() && !nodep->full() ? "(base+" : "(oldp+"); + puts(cvtToStr(code - nodep->baseCode())); + puts(","); + emitTraceValue(nodep, arrayindex); + if (emitWidth) puts("," + cvtToStr(nodep->declp()->widthMin())); + puts(");\n"); + } + + void emitTraceValue(AstTraceInc* nodep, int arrayindex) { + if (AstVarRef* const varrefp = VN_CAST(nodep->valuep(), VarRef)) { + AstVar* varp = varrefp->varp(); + puts("("); + if (emitTraceIsScBigUint(nodep)) { + puts("(vluint32_t*)"); + } else if (emitTraceIsScBv(nodep)) { + puts("VL_SC_BV_DATAP("); + } + iterate(varrefp); // Put var name out + // Tracing only supports 1D arrays + if (nodep->declp()->arrayRange().ranged()) { + if (arrayindex == -2) { + puts("[i]"); + } else if (arrayindex == -1) { + puts("[0]"); + } else { + puts("[" + cvtToStr(arrayindex) + "]"); + } + } + if (varp->isSc()) puts(".read()"); + if (emitTraceIsScUint(nodep)) { + puts(nodep->isQuad() ? ".to_uint64()" : ".to_uint()"); + } else if (emitTraceIsScBigUint(nodep)) { + puts(".get_raw()"); + } else if (emitTraceIsScBv(nodep)) { + puts(")"); + } + puts(")"); + } else { + puts("("); + iterate(nodep->valuep()); + puts(")"); + } + } + + // VISITORS + using EmitCFunc::visit; // Suppress hidden overloaded virtual function warning + virtual void visit(AstCFunc* nodep) override { + if (!nodep->isTrace()) return; + if (nodep->slow() != m_slow) return; + + if (splitNeeded()) { + // Splitting file, so using parallel build. + v3Global.useParallelBuild(true); + // Close old file + VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr); + // Open a new file + newOutCFile(splitFilenumInc()); + } + + EmitCFunc::visit(nodep); + } + virtual void visit(AstTraceDecl* nodep) override { + const int enumNum = emitTraceDeclDType(nodep->dtypep()); + if (nodep->arrayRange().ranged()) { + puts("{int i; for (i=0; i<" + cvtToStr(nodep->arrayRange().elements()) + "; i++) {\n"); + emitTraceInitOne(nodep, enumNum); + puts("}}\n"); + } else { + emitTraceInitOne(nodep, enumNum); + puts("\n"); + } + } + virtual void visit(AstTraceInc* nodep) override { + if (nodep->declp()->arrayRange().ranged()) { + // It traces faster if we unroll the loop + for (int i = 0; i < nodep->declp()->arrayRange().elements(); i++) { + emitTraceChangeOne(nodep, i); + } + } else { + emitTraceChangeOne(nodep, -1); + } + } + + explicit EmitCTrace(AstNodeModule* modp, bool slow) + : m_slow{slow} { + m_modp = modp; + // Open output file + newOutCFile(splitFilenumInc()); + // Emit functions + for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { + if (AstCFunc* const funcp = VN_CAST(nodep, CFunc)) { iterate(funcp); } + } + // Close output file + VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr); + } + virtual ~EmitCTrace() override = default; + +public: + static void main(AstNodeModule* modp, bool slow) { EmitCTrace{modp, slow}; } +}; + +//###################################################################### +// EmitC class functions + +void V3EmitC::emitcImp() { + UINFO(2, __FUNCTION__ << ": " << endl); + // Set user4p in all CFunc and Var to point to the containing AstNodeModule + AstUser4InUse user4InUse; + const auto setAll = [](AstNodeModule* modp) -> void { + for (AstNode* nodep = VN_CAST(modp, NodeModule)->stmtsp(); nodep; nodep = nodep->nextp()) { + if (VN_IS(nodep, CFunc) || VN_IS(nodep, Var)) nodep->user4p(modp); + } + }; + for (AstNode* modp = v3Global.rootp()->modulesp(); modp; modp = modp->nextp()) { + setAll(VN_CAST(modp, NodeModule)); + } + setAll(v3Global.rootp()->constPoolp()->modp()); + + // Process each module in turn + for (const AstNode* nodep = v3Global.rootp()->modulesp(); nodep; nodep = nodep->nextp()) { + if (VN_IS(nodep, Class)) continue; // Imped with ClassPackage + const AstNodeModule* const modp = VN_CAST_CONST(nodep, NodeModule); + EmitCImp::main(modp, /* slow: */ true); + EmitCImp::main(modp, /* slow: */ false); + } + + // Emit trace routines (currently they can only exist in the top module) + if (v3Global.opt.trace()) { + EmitCTrace::main(v3Global.rootp()->topModulep(), /* slow: */ true); + EmitCTrace::main(v3Global.rootp()->topModulep(), /* slow: */ false); + } +} + +void V3EmitC::emitcFiles() { + UINFO(2, __FUNCTION__ << ": " << endl); + for (AstNodeFile* filep = v3Global.rootp()->filesp(); filep; + filep = VN_CAST(filep->nextp(), NodeFile)) { + AstCFile* cfilep = VN_CAST(filep, CFile); + if (cfilep && cfilep->tblockp()) { + V3OutCFile of(cfilep->name()); + of.puts("// DESCR" + "IPTION: Verilator generated C++\n"); + EmitCFunc visitor(cfilep->tblockp(), &of, true); + } + } +} diff --git a/src/V3EmitCModel.cpp b/src/V3EmitCModel.cpp index 78565b568..2e47ff004 100644 --- a/src/V3EmitCModel.cpp +++ b/src/V3EmitCModel.cpp @@ -585,8 +585,7 @@ class EmitCModel final : public EmitCFunc { if (!m_ofp) { const string filename = v3Global.opt.makeDir() + "/" + topClassName() - + "__Dpi_Export_" + cvtToStr(splitFilenumInc() - 1) - + ".cpp"; + + "__Dpi_Export_" + cvtToStr(splitFilenumInc()) + ".cpp"; newCFile(filename, /* slow: */ false, /* source: */ true); m_ofp = v3Global.opt.systemC() ? new V3OutScFile{filename} : new V3OutCFile{filename}; diff --git a/src/Verilator.cpp b/src/Verilator.cpp index b61275ccc..65b75c2bb 100644 --- a/src/Verilator.cpp +++ b/src/Verilator.cpp @@ -516,12 +516,13 @@ static void process() { V3EmitC::emitcSyms(); V3EmitC::emitcConstPool(); V3EmitC::emitcModel(); + V3EmitC::emitcHeaders(); } else if (v3Global.opt.dpiHdrOnly()) { V3EmitC::emitcSyms(true); } if (!v3Global.opt.xmlOnly() - && !v3Global.opt.dpiHdrOnly()) { // Unfortunately we have some lint checks in emitc. - V3EmitC::emitc(); + && !v3Global.opt.dpiHdrOnly()) { // Unfortunately we have some lint checks in emitcImp. + V3EmitC::emitcImp(); } if (v3Global.opt.xmlOnly() // Check XML when debugging to make sure no missing node types From ddef61d62edc9a54e7427300614bdb4f7baa0208 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Tue, 13 Jul 2021 17:42:17 +0100 Subject: [PATCH 24/84] Internals: Move AstNodeCCall::m_selfPointer to AstCCall Other sub-classes of AstNodeCCall do not need the self pointer. Moving it into the specific sub-class that needs it clarifies V3Descope and Emit. No functional change intended. --- src/V3Ast.h | 4 ---- src/V3AstNodes.cpp | 3 ++- src/V3AstNodes.h | 7 +++++++ src/V3Descope.cpp | 28 ++++++++++++++++++++++------ src/V3EmitCFunc.cpp | 14 ++++++++++---- src/V3EmitCFunc.h | 35 ++++++++++++++++++----------------- 6 files changed, 59 insertions(+), 32 deletions(-) diff --git a/src/V3Ast.h b/src/V3Ast.h index 9a26091ea..1afe34c83 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -2612,7 +2612,6 @@ class AstNodeCCall VL_NOT_FINAL : public AstNodeStmt { // A call of a C++ function, perhaps a AstCFunc or perhaps globally named // Functions are not statements, while tasks are. AstNodeStmt needs isStatement() to deal. AstCFunc* m_funcp; - string m_selfPointer; // Output code object pointer (e.g.: 'this') string m_argTypes; protected: @@ -2638,9 +2637,6 @@ public: virtual bool isPure() const override; virtual bool isOutputter() const override { return !isPure(); } AstCFunc* funcp() const { return m_funcp; } - string selfPointer() const { return m_selfPointer; } - void selfPointer(const string& value) { m_selfPointer = value; } - string selfPointerProtect(bool useSelfForThis) const; void argTypes(const string& str) { m_argTypes = str; } string argTypes() const { return m_argTypes; } // op1p reserved for AstCMethodCall diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index 4ece401fd..52f5b6755 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -121,7 +121,8 @@ const char* AstNodeCCall::broken() const { return nullptr; } bool AstNodeCCall::isPure() const { return funcp()->pure(); } -string AstNodeCCall::selfPointerProtect(bool useSelfForThis) const { + +string AstCCall::selfPointerProtect(bool useSelfForThis) const { const string& sp = useSelfForThis ? VString::replaceWord(selfPointer(), "this", "vlSelf") : selfPointer(); return VIdProtect::protectWordsIf(sp, protect()); diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index 120121d3b..e9f7e8614 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -8880,10 +8880,17 @@ class AstCCall final : public AstNodeCCall { // C++ function call // Parents: Anything above a statement // Children: Args to the function + + string m_selfPointer; // Output code object pointer (e.g.: 'this') + public: AstCCall(FileLine* fl, AstCFunc* funcp, AstNode* argsp = nullptr) : ASTGEN_SUPER_CCall(fl, funcp, argsp) {} ASTNODE_NODE_FUNCS(CCall) + + string selfPointer() const { return m_selfPointer; } + void selfPointer(const string& value) { m_selfPointer = value; } + string selfPointerProtect(bool useSelfForThis) const; }; class AstCMethodCall final : public AstNodeCCall { diff --git a/src/V3Descope.cpp b/src/V3Descope.cpp index fc10d5879..9cb49f44d 100644 --- a/src/V3Descope.cpp +++ b/src/V3Descope.cpp @@ -73,6 +73,7 @@ private: // module. string descopedSelfPointer(const AstScope* scopep) { UASSERT(scopep, "Var/Func not scoped"); + UASSERT(!VN_IS(scopep->modp(), Class), "References to classes handled elsewhere"); // Static functions can't use relative references via 'this->' const bool relativeRefOk = !m_funcp->isStatic(); @@ -83,9 +84,7 @@ private: if (relativeRefOk && scopep == m_scopep) { return "this"; - } else if (VN_IS(scopep->modp(), Class)) { - return "this"; - } else if (!m_modSingleton && relativeRefOk && scopep->aboveScopep() == m_scopep + } else if (relativeRefOk && !m_modSingleton && scopep->aboveScopep() == m_scopep && VN_IS(scopep->modp(), Module)) { // Reference to scope of instance directly under this module, can just "this->cell", // which can potentially be V3Combined, but note this requires one extra pointer @@ -215,20 +214,37 @@ private: UASSERT_OBJ(m_scopep, nodep, "Node not under scope"); const AstVar* const varp = nodep->varScopep()->varp(); const AstScope* const scopep = nodep->varScopep()->scopep(); - if (!varp->isFuncLocal()) { nodep->selfPointer(descopedSelfPointer(scopep)); } + if (varp->isFuncLocal()) { + // Reference to function locals need no self pointer + nodep->selfPointer(""); + } else if (VN_IS(scopep->modp(), Class)) { + // Direct reference to class members are from within the class itself, references from + // outside the class must go via AstMemberSel + nodep->selfPointer("this"); + } else { + nodep->selfPointer(descopedSelfPointer(scopep)); + } nodep->varScopep(nullptr); UINFO(9, " refout " << nodep << endl); } - virtual void visit(AstNodeCCall* nodep) override { + virtual void visit(AstCCall* nodep) override { // UINFO(9, " " << nodep << endl); iterateChildren(nodep); // Convert the hierch name UASSERT_OBJ(m_scopep, nodep, "Node not under scope"); const AstScope* const scopep = nodep->funcp()->scopep(); - nodep->selfPointer(descopedSelfPointer(scopep)); + if (VN_IS(scopep->modp(), Class)) { + // Direct call to class methods are from within the class itself, method calls from + // outside the class must go via AstCMethodCall + nodep->selfPointer("this"); + } else { + nodep->selfPointer(descopedSelfPointer(scopep)); + } // Can't do this, as we may have more calls later // nodep->funcp()->scopep(nullptr); } + virtual void visit(AstCMethodCall* nodep) override { iterateChildren(nodep); } + virtual void visit(AstCNew* nodep) override { iterateChildren(nodep); } virtual void visit(AstCFunc* nodep) override { VL_RESTORER(m_funcp); if (!nodep->user1()) { diff --git a/src/V3EmitCFunc.cpp b/src/V3EmitCFunc.cpp index e27f628a9..5729c51e2 100644 --- a/src/V3EmitCFunc.cpp +++ b/src/V3EmitCFunc.cpp @@ -433,12 +433,12 @@ void EmitCFunc::displayNode(AstNode* nodep, AstScopeName* scopenamep, const stri displayEmit(nodep, isScan); } -void EmitCFunc::emitCCallArgs(AstNodeCCall* nodep) { +void EmitCFunc::emitCCallArgs(const AstNodeCCall* nodep, const string& selfPointer) { + puts("("); bool comma = false; if (nodep->funcp()->isLoose() && !nodep->funcp()->isStatic()) { - UASSERT_OBJ(!nodep->selfPointer().empty(), nodep, - "Call to loose method without self pointer"); - puts(nodep->selfPointerProtect(m_useSelfForThis)); + UASSERT_OBJ(!selfPointer.empty(), nodep, "Call to loose method without self pointer"); + puts(selfPointer); comma = true; } if (!nodep->argTypes().empty()) { @@ -451,6 +451,12 @@ void EmitCFunc::emitCCallArgs(AstNodeCCall* nodep) { iterate(subnodep); comma = true; } + if (VN_IS(nodep->backp(), NodeMath) || VN_IS(nodep->backp(), CReturn)) { + // We should have a separate CCall for math and statement usage, but... + puts(")"); + } else { + puts(");\n"); + } } void EmitCFunc::emitDereference(const string& pointer) { diff --git a/src/V3EmitCFunc.h b/src/V3EmitCFunc.h index 533984eb3..01aaf8dad 100644 --- a/src/V3EmitCFunc.h +++ b/src/V3EmitCFunc.h @@ -172,7 +172,7 @@ public: } void emitOpName(AstNode* nodep, const string& format, AstNode* lhsp, AstNode* rhsp, AstNode* thsp); - void emitCCallArgs(AstNodeCCall* nodep); + void emitCCallArgs(const AstNodeCCall* nodep, const string& selfPointer); void emitDereference(const string& pointer); void emitCvtPackStr(AstNode* nodep); void emitCvtWideArray(AstNode* nodep, AstNode* fromp); @@ -353,7 +353,7 @@ public: } puts(")"); } - virtual void visit(AstNodeCCall* nodep) override { + virtual void visit(AstCCall* nodep) override { const AstCFunc* const funcp = nodep->funcp(); if (AstCMethodCall* ccallp = VN_CAST(nodep, CMethodCall)) { UASSERT_OBJ(!funcp->isLoose(), nodep, "Loose method called via AstCMethodCall"); @@ -382,14 +382,22 @@ public: } puts(funcp->nameProtect()); } - puts("("); - emitCCallArgs(nodep); - if (VN_IS(nodep->backp(), NodeMath) || VN_IS(nodep->backp(), CReturn)) { - // We should have a separate CCall for math and statement usage, but... - puts(")"); - } else { - puts(");\n"); - } + emitCCallArgs(nodep, nodep->selfPointerProtect(m_useSelfForThis)); + } + virtual void visit(AstCMethodCall* nodep) override { + const AstCFunc* const funcp = nodep->funcp(); + UASSERT_OBJ(!funcp->isLoose(), nodep, "Loose method called via AstCMethodCall"); + iterate(nodep->fromp()); + putbs("->"); + puts(funcp->nameProtect()); + emitCCallArgs(nodep, ""); + } + virtual void visit(AstCNew* nodep) override { + puts("std::make_shared<" + prefixNameProtect(nodep->dtypep()) + ">("); + puts("vlSymsp"); // TODO make this part of argsp, and eliminate when unnecessary + if (nodep->argsp()) puts(", "); + iterateAndNextNull(nodep->argsp()); + puts(")"); } virtual void visit(AstCMethodHard* nodep) override { iterate(nodep->fromp()); @@ -1013,13 +1021,6 @@ public: puts(cvtToStr(nodep->fileline()->lineno())); puts(")"); } - virtual void visit(AstCNew* nodep) override { - puts("std::make_shared<" + prefixNameProtect(nodep->dtypep()) + ">("); - puts("vlSymsp"); // TODO make this part of argsp, and eliminate when unnecessary - if (nodep->argsp()) puts(", "); - iterateAndNextNull(nodep->argsp()); - puts(")"); - } virtual void visit(AstNewCopy* nodep) override { puts("std::make_shared<" + prefixNameProtect(nodep->dtypep()) + ">("); puts("*"); // i.e. make into a reference From bac84726e1d179e9051a54dcd6339073780c2dd5 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Tue, 13 Jul 2021 21:17:41 +0100 Subject: [PATCH 25/84] Emit: Clean up some code coverage holes --- src/V3EmitCFunc.h | 26 ++++---------------------- 1 file changed, 4 insertions(+), 22 deletions(-) diff --git a/src/V3EmitCFunc.h b/src/V3EmitCFunc.h index 01aaf8dad..362aa7e3a 100644 --- a/src/V3EmitCFunc.h +++ b/src/V3EmitCFunc.h @@ -114,7 +114,6 @@ public: class EmitCFunc VL_NOT_FINAL : public EmitCBaseVisitor { private: - bool m_suppressSemi; AstVarRef* m_wideTempRefp; // Variable that _WW macros should be setting int m_labelNum; // Next label number int m_splitSize; // # of cfunc nodes placed into output file @@ -338,7 +337,7 @@ public: iterateAndNextNull(nodep->rhsp()); if (paren) puts(")"); if (decind) ofp()->blockDec(); - if (!m_suppressSemi) puts(";\n"); + puts(";\n"); } virtual void visit(AstAlwaysPublic*) override {} virtual void visit(AstAssocSel* nodep) override { @@ -355,13 +354,7 @@ public: } virtual void visit(AstCCall* nodep) override { const AstCFunc* const funcp = nodep->funcp(); - if (AstCMethodCall* ccallp = VN_CAST(nodep, CMethodCall)) { - UASSERT_OBJ(!funcp->isLoose(), nodep, "Loose method called via AstCMethodCall"); - // make this a Ast type for future opt - iterate(ccallp->fromp()); - putbs("->"); - puts(funcp->nameProtect()); - } else if (funcp->dpiImportPrototype()) { + if (funcp->dpiImportPrototype()) { // Calling DPI import puts(funcp->name()); } else if (funcp->isProperMethod() && funcp->isStatic()) { @@ -1208,30 +1201,19 @@ public: m_blkChangeDetVec.push_back(nodep); } - // Just iterate - virtual void visit(AstNetlist* nodep) override { iterateChildren(nodep); } - virtual void visit(AstTopScope* nodep) override { iterateChildren(nodep); } - virtual void visit(AstScope* nodep) override { iterateChildren(nodep); } - // NOPs - virtual void visit(AstTypedef*) override {} - virtual void visit(AstPragma*) override {} - virtual void visit(AstCell*) override {} // Handled outside the Visit class - virtual void visit(AstNodeText*) override {} // Handled outside the Visit class - virtual void visit(AstCFile*) override {} // Handled outside the Visit class - virtual void visit(AstCellInline*) override {} // Handled outside visit (in EmitCSyms) - virtual void visit(AstCUse*) override {} // Handled outside the Visit class // Default virtual void visit(AstNode* nodep) override { puts(string("\n???? // ") + nodep->prettyTypeName() + "\n"); iterateChildren(nodep); + // LCOV_EXCL_START if (!v3Global.opt.lintOnly()) { // An internal problem, so suppress nodep->v3fatalSrc("Unknown node type reached emitter: " << nodep->prettyTypeName()); } + // LCOV_EXCL_STOP } EmitCFunc() : m_lazyDecls(*this) { - m_suppressSemi = false; m_wideTempRefp = nullptr; m_labelNum = 0; m_splitSize = 0; From cf39331d1f48a8438e82bd9a99d6da1db7daf7e2 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Wed, 14 Jul 2021 13:01:03 +0100 Subject: [PATCH 26/84] Emit: Factor out visitor that can emit constant initializers This is needed both by the V3EmitCConstPool emitter and the V3EmitCHeaders emitter. --- src/V3EmitCConstInit.h | 122 +++++++++++++++++++++++++++++++++++++++ src/V3EmitCConstPool.cpp | 78 ++----------------------- src/V3EmitCHeaders.cpp | 6 +- 3 files changed, 130 insertions(+), 76 deletions(-) create mode 100644 src/V3EmitCConstInit.h diff --git a/src/V3EmitCConstInit.h b/src/V3EmitCConstInit.h new file mode 100644 index 000000000..1cabd9f03 --- /dev/null +++ b/src/V3EmitCConstInit.h @@ -0,0 +1,122 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// DESCRIPTION: Verilator: Emit C++ for tree +// +// Code available from: https://verilator.org +// +//************************************************************************* +// +// Copyright 2003-2021 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 +// +//************************************************************************* + +#include "config_build.h" +#include "verilatedos.h" + +#include "V3Ast.h" +#include "V3EmitCBase.h" +#include "V3Error.h" + +#include + +//###################################################################### +// Emitter that can emit constant initializer expressions + +class EmitCConstInit VL_NOT_FINAL : public EmitCBaseVisitor { + // MEMBERS + bool m_inUnpacked = false; + uint32_t m_unpackedWord = 0; + + // METHODS + VL_DEBUG_FUNC; // Declare debug() + +protected: + // VISITORS + virtual void visit(AstInitArray* nodep) override { + const AstUnpackArrayDType* const dtypep + = VN_CAST(nodep->dtypep()->skipRefp(), UnpackArrayDType); + UASSERT_OBJ(dtypep, nodep, "Array initializer has non-array dtype"); + const uint32_t size = dtypep->elementsConst(); + const uint32_t elemBytes = dtypep->subDTypep()->widthTotalBytes(); + const uint32_t tabMod = dtypep->subDTypep()->isString() ? 1 // String + : elemBytes <= 2 ? 8 // CData, SData + : elemBytes <= 4 ? 4 // IData + : elemBytes <= 8 ? 2 // QData + : 1; + VL_RESTORER(m_inUnpacked); + VL_RESTORER(m_unpackedWord); + m_inUnpacked = true; + // Note the double {{ initializer. The first { starts the initializer of the VlUnpacked, + // and the second starts the initializer of m_storage within the VlUnpacked. + puts("{"); + ofp()->putsNoTracking("{"); + puts("\n"); + for (uint32_t n = 0; n < size; ++n) { + m_unpackedWord = n; + if (n) puts(n % tabMod ? ", " : ",\n"); + iterate(nodep->getIndexDefaultedValuep(n)); + } + puts("\n"); + puts("}"); + ofp()->putsNoTracking("}"); + } + + virtual void visit(AstInitItem* nodep) override { // LCOV_EXCL_START + nodep->v3fatal("Handled by AstInitArray"); + } // LCOV_EXCL_STOP + + virtual void visit(AstConst* nodep) override { + const V3Number& num = nodep->num(); + UASSERT_OBJ(!num.isFourState(), nodep, "4-state value in constant pool"); + AstNodeDType* const dtypep = nodep->dtypep(); + if (num.isString()) { + // Note: putsQuoted does not track indentation, so we use this instead + puts("\""); + puts(num.toString()); + puts("\""); + } else if (dtypep->isWide()) { + const uint32_t size = dtypep->widthWords(); + // Note the double {{ initializer. The first { starts the initializer of the VlWide, + // and the second starts the initializer of m_storage within the VlWide. + puts("{"); + ofp()->putsNoTracking("{"); + if (m_inUnpacked) puts(" // VlWide " + cvtToStr(m_unpackedWord)); + puts("\n"); + for (uint32_t n = 0; n < size; ++n) { + if (n) puts(n % 4 ? ", " : ",\n"); + ofp()->printf("0x%08" PRIx32, num.edataWord(n)); + } + puts("\n"); + puts("}"); + ofp()->putsNoTracking("}"); + } else if (dtypep->isDouble()) { + const double dnum = num.toDouble(); + const char* const fmt + = !m_inUnpacked && (static_cast(dnum) == dnum && -1000 < dnum && dnum < 1000) + ? "%3.1f" // Force decimal point + : "%.17e"; // %e always yields a float literal + ofp()->printf(fmt, dnum); + } else if (dtypep->isQuad()) { + const uint64_t qnum = static_cast(num.toUQuad()); + const char* const fmt + = !m_inUnpacked && (qnum < 10) ? ("%" PRIx64 "ULL") : ("0x%016" PRIx64 "ULL"); + ofp()->printf(fmt, qnum); + } else { + const uint32_t unum = num.toUInt(); + const char* const fmt = !m_inUnpacked && (unum < 10) ? ("%" PRIu32 "U") + : (dtypep->widthMin() > 16) ? ("0x%08" PRIx32 "U") + : (dtypep->widthMin() > 8) ? ("0x%04" PRIx32 "U") + : ("0x%02" PRIx32 "U"); + ofp()->printf(fmt, unum); + } + } + + // Default + virtual void visit(AstNode* nodep) override { // LCOV_EXCL_START + nodep->v3fatalSrc("Unknown node type reached EmitCConstInit: " << nodep->prettyTypeName()); + } // LCOV_EXCL_STOP +}; diff --git a/src/V3EmitCConstPool.cpp b/src/V3EmitCConstPool.cpp index 589914a6e..4fa3b813c 100644 --- a/src/V3EmitCConstPool.cpp +++ b/src/V3EmitCConstPool.cpp @@ -18,7 +18,7 @@ #include "verilatedos.h" #include "V3EmitC.h" -#include "V3EmitCBase.h" +#include "V3EmitCConstInit.h" #include "V3File.h" #include "V3Global.h" #include "V3String.h" @@ -30,10 +30,8 @@ //###################################################################### // Const pool emitter -class EmitCConstPool final : EmitCBaseVisitor { +class EmitCConstPool final : public EmitCConstInit { // MEMBERS - bool m_inUnpacked = false; - uint32_t m_unpackedWord = 0; uint32_t m_outFileCount = 0; int m_outFileSize = 0; VDouble0 m_tablesEmitted; @@ -102,77 +100,9 @@ class EmitCConstPool final : EmitCBaseVisitor { } // VISITORS - virtual void visit(AstNode* nodep) override { iterateChildrenConst(nodep); } - - virtual void visit(AstInitArray* nodep) override { - const AstUnpackArrayDType* const dtypep - = VN_CAST(nodep->dtypep()->skipRefp(), UnpackArrayDType); - UASSERT_OBJ(dtypep, nodep, "Array initializer has non-array dtype"); - const uint32_t size = dtypep->elementsConst(); - const uint32_t elemBytes = dtypep->subDTypep()->widthTotalBytes(); - const uint32_t tabMod = dtypep->subDTypep()->isString() ? 1 // String - : elemBytes <= 2 ? 8 // CData, SData - : elemBytes <= 4 ? 4 // IData - : elemBytes <= 8 ? 2 // QData - : 1; - VL_RESTORER(m_inUnpacked); - VL_RESTORER(m_unpackedWord); - m_inUnpacked = true; - // Note the double {{ initializer. The first { starts the initializer of the VlUnpacked, - // and the second starts the initializer of m_storage within the VlUnpacked. - puts("{"); - ofp()->putsNoTracking("{"); - puts("\n"); - for (uint32_t n = 0; n < size; ++n) { - m_unpackedWord = n; - if (n) puts(n % tabMod ? ", " : ",\n"); - iterate(nodep->getIndexDefaultedValuep(n)); - } - puts("\n"); - puts("}"); - ofp()->putsNoTracking("}"); - } - - virtual void visit(AstInitItem* nodep) override { // LCOV_EXCL_START - nodep->v3fatal("Handled by AstInitArray"); - } // LCOV_EXCL_END - virtual void visit(AstConst* nodep) override { - const V3Number& num = nodep->num(); - UASSERT_OBJ(!num.isFourState(), nodep, "4-state value in constant pool"); - AstNodeDType* const dtypep = nodep->dtypep(); - m_outFileSize += 1; - if (num.isString()) { - // Note: putsQuoted does not track indentation, so we use this instead - puts("\""); - puts(num.toString()); - puts("\""); - m_outFileSize += 9; - } else if (dtypep->isWide()) { - const uint32_t size = dtypep->widthWords(); - m_outFileSize += size - 1; - // Note the double {{ initializer. The first { starts the initializer of the VlWide, - // and the second starts the initializer of m_storage within the VlWide. - puts("{"); - ofp()->putsNoTracking("{"); - if (m_inUnpacked) puts(" // VlWide " + cvtToStr(m_unpackedWord)); - puts("\n"); - for (uint32_t n = 0; n < size; ++n) { - if (n) puts(n % 4 ? ", " : ",\n"); - ofp()->printf("0x%08" PRIx32, num.edataWord(n)); - } - puts("\n"); - puts("}"); - ofp()->putsNoTracking("}"); - } else if (dtypep->isQuad()) { - ofp()->printf("0x%016" PRIx64, static_cast(num.toUQuad())); - } else if (dtypep->widthMin() > 16) { - ofp()->printf("0x%08" PRIx32, num.toUInt()); - } else if (dtypep->widthMin() > 8) { - ofp()->printf("0x%04" PRIx32, num.toUInt()); - } else { - ofp()->printf("0x%02" PRIx32, num.toUInt()); - } + m_outFileSize += nodep->num().isString() ? 10 : nodep->isWide() ? nodep->widthWords() : 1; + EmitCConstInit::visit(nodep); } public: diff --git a/src/V3EmitCHeaders.cpp b/src/V3EmitCHeaders.cpp index 7e63d8dc5..6f230ac57 100644 --- a/src/V3EmitCHeaders.cpp +++ b/src/V3EmitCHeaders.cpp @@ -19,7 +19,7 @@ #include "V3Global.h" #include "V3EmitC.h" -#include "V3EmitCFunc.h" +#include "V3EmitCConstInit.h" #include #include @@ -27,8 +27,10 @@ //###################################################################### // Internal EmitC implementation -class EmitCHeader final : public EmitCFunc { +class EmitCHeader final : public EmitCConstInit { // METHODS + VL_DEBUG_FUNC; // Declare debug() + void decorateFirst(bool& first, const string& str) { if (first) { putsDecoration(str); From 30fa5e41be9f40e707c57a7aa78107832fa94b8c Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Mon, 19 Jul 2021 17:00:23 +0100 Subject: [PATCH 27/84] Don't emit trace files with --lint-only --- src/V3EmitCImp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/V3EmitCImp.cpp b/src/V3EmitCImp.cpp index 7b767ab99..78d65c49f 100644 --- a/src/V3EmitCImp.cpp +++ b/src/V3EmitCImp.cpp @@ -718,7 +718,7 @@ void V3EmitC::emitcImp() { } // Emit trace routines (currently they can only exist in the top module) - if (v3Global.opt.trace()) { + if (v3Global.opt.trace() && !v3Global.opt.lintOnly()) { EmitCTrace::main(v3Global.rootp()->topModulep(), /* slow: */ true); EmitCTrace::main(v3Global.rootp()->topModulep(), /* slow: */ false); } From 1166728a8d055420dc49f610de92f92018da5a24 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Mon, 19 Jul 2021 17:46:17 +0100 Subject: [PATCH 28/84] Tests: make t_trace_*two* tests less sensitive to file names No functional change intended --- test_regress/t/t_trace_two_cc.cpp | 7 +------ test_regress/t/t_trace_two_dump_cc.pl | 5 +++++ test_regress/t/t_trace_two_dump_sc.pl | 5 +++++ test_regress/t/t_trace_two_dumpfst_cc.pl | 5 +++++ test_regress/t/t_trace_two_hdr_cc.pl | 5 +++++ test_regress/t/t_trace_two_hdr_sc.pl | 5 +++++ test_regress/t/t_trace_two_hdrfst_cc.pl | 5 +++++ test_regress/t/t_trace_two_port_cc.pl | 5 +++++ test_regress/t/t_trace_two_port_sc.pl | 5 +++++ test_regress/t/t_trace_two_portfst_cc.pl | 5 +++++ test_regress/t/t_trace_two_sc.cpp | 7 +------ 11 files changed, 47 insertions(+), 12 deletions(-) diff --git a/test_regress/t/t_trace_two_cc.cpp b/test_regress/t/t_trace_two_cc.cpp index 02c639418..238877e5f 100644 --- a/test_regress/t/t_trace_two_cc.cpp +++ b/test_regress/t/t_trace_two_cc.cpp @@ -21,12 +21,7 @@ // clang-format on // Compile in place -#include "Vt_trace_two_b.cpp" -#include "Vt_trace_two_b__Syms.cpp" -#include "Vt_trace_two_b___024root.cpp" -#include "Vt_trace_two_b___024root__Slow.cpp" -#include "Vt_trace_two_b__Trace.cpp" -#include "Vt_trace_two_b__Trace__Slow.cpp" +#include "Vt_trace_two_b__ALL.cpp" VM_PREFIX* ap; Vt_trace_two_b* bp; diff --git a/test_regress/t/t_trace_two_dump_cc.pl b/test_regress/t/t_trace_two_dump_cc.pl index 0b20803a9..d4643e039 100755 --- a/test_regress/t/t_trace_two_dump_cc.pl +++ b/test_regress/t/t_trace_two_dump_cc.pl @@ -21,6 +21,11 @@ compile( verilator_flags2 => ['-trace'], ); +run( + logfile => "$Self->{obj_dir}/make_first_ALL.log", + cmd => ["make", "-C", "$Self->{obj_dir}", "-f", "Vt_trace_two_b.mk", "Vt_trace_two_b__ALL.cpp"] + ); + compile( make_main => 0, top_filename => 't_trace_two_a.v', diff --git a/test_regress/t/t_trace_two_dump_sc.pl b/test_regress/t/t_trace_two_dump_sc.pl index 311848fe7..dd789a58b 100755 --- a/test_regress/t/t_trace_two_dump_sc.pl +++ b/test_regress/t/t_trace_two_dump_sc.pl @@ -24,6 +24,11 @@ else { verilator_flags2 => ['-sc -trace'], ); + run( + logfile => "$Self->{obj_dir}/make_first_ALL.log", + cmd => ["make", "-C", "$Self->{obj_dir}", "-f", "Vt_trace_two_b.mk", "Vt_trace_two_b__ALL.cpp"] + ); + compile( make_main => 0, top_filename => 't_trace_two_a.v', diff --git a/test_regress/t/t_trace_two_dumpfst_cc.pl b/test_regress/t/t_trace_two_dumpfst_cc.pl index 37f00db1c..4c1b2c968 100755 --- a/test_regress/t/t_trace_two_dumpfst_cc.pl +++ b/test_regress/t/t_trace_two_dumpfst_cc.pl @@ -21,6 +21,11 @@ compile( verilator_flags2 => ['--trace-fst --trace-threads 1 -DTEST_FST'], ); +run( + logfile => "$Self->{obj_dir}/make_first_ALL.log", + cmd => ["make", "-C", "$Self->{obj_dir}", "-f", "Vt_trace_two_b.mk", "Vt_trace_two_b__ALL.cpp"] + ); + compile( make_main => 0, top_filename => 't_trace_two_a.v', diff --git a/test_regress/t/t_trace_two_hdr_cc.pl b/test_regress/t/t_trace_two_hdr_cc.pl index 352e12450..f0c386bbb 100755 --- a/test_regress/t/t_trace_two_hdr_cc.pl +++ b/test_regress/t/t_trace_two_hdr_cc.pl @@ -21,6 +21,11 @@ compile( verilator_flags2 => ['-trace'], ); +run( + logfile => "$Self->{obj_dir}/make_first_ALL.log", + cmd => ["make", "-C", "$Self->{obj_dir}", "-f", "Vt_trace_two_b.mk", "Vt_trace_two_b__ALL.cpp"] + ); + compile( make_main => 0, top_filename => 't_trace_two_a.v', diff --git a/test_regress/t/t_trace_two_hdr_sc.pl b/test_regress/t/t_trace_two_hdr_sc.pl index b62a58aef..8374fcfee 100755 --- a/test_regress/t/t_trace_two_hdr_sc.pl +++ b/test_regress/t/t_trace_two_hdr_sc.pl @@ -24,6 +24,11 @@ else { verilator_flags2 => ['-sc -trace'], ); + run( + logfile => "$Self->{obj_dir}/make_first_ALL.log", + cmd => ["make", "-C", "$Self->{obj_dir}", "-f", "Vt_trace_two_b.mk", "Vt_trace_two_b__ALL.cpp"] + ); + compile( make_main => 0, top_filename => 't_trace_two_a.v', diff --git a/test_regress/t/t_trace_two_hdrfst_cc.pl b/test_regress/t/t_trace_two_hdrfst_cc.pl index cbdae9a87..af0d8307f 100755 --- a/test_regress/t/t_trace_two_hdrfst_cc.pl +++ b/test_regress/t/t_trace_two_hdrfst_cc.pl @@ -21,6 +21,11 @@ compile( verilator_flags2 => ['--trace-fst --trace-threads 1'], ); +run( + logfile => "$Self->{obj_dir}/make_first_ALL.log", + cmd => ["make", "-C", "$Self->{obj_dir}", "-f", "Vt_trace_two_b.mk", "Vt_trace_two_b__ALL.cpp"] + ); + compile( make_main => 0, top_filename => 't_trace_two_a.v', diff --git a/test_regress/t/t_trace_two_port_cc.pl b/test_regress/t/t_trace_two_port_cc.pl index c35b91acb..7e348f19b 100755 --- a/test_regress/t/t_trace_two_port_cc.pl +++ b/test_regress/t/t_trace_two_port_cc.pl @@ -21,6 +21,11 @@ compile( verilator_flags2 => ['-trace'], ); +run( + logfile => "$Self->{obj_dir}/make_first_ALL.log", + cmd => ["make", "-C", "$Self->{obj_dir}", "-f", "Vt_trace_two_b.mk", "Vt_trace_two_b__ALL.cpp"] + ); + compile( make_main => 0, top_filename => 't_trace_two_a.v', diff --git a/test_regress/t/t_trace_two_port_sc.pl b/test_regress/t/t_trace_two_port_sc.pl index 84676c3a8..e62e64a3b 100755 --- a/test_regress/t/t_trace_two_port_sc.pl +++ b/test_regress/t/t_trace_two_port_sc.pl @@ -24,6 +24,11 @@ else { verilator_flags2 => ['-sc -trace'], ); + run( + logfile => "$Self->{obj_dir}/make_first_ALL.log", + cmd => ["make", "-C", "$Self->{obj_dir}", "-f", "Vt_trace_two_b.mk", "Vt_trace_two_b__ALL.cpp"] + ); + compile( make_main => 0, top_filename => 't_trace_two_a.v', diff --git a/test_regress/t/t_trace_two_portfst_cc.pl b/test_regress/t/t_trace_two_portfst_cc.pl index 689296b42..676bfb508 100755 --- a/test_regress/t/t_trace_two_portfst_cc.pl +++ b/test_regress/t/t_trace_two_portfst_cc.pl @@ -21,6 +21,11 @@ compile( verilator_flags2 => ['--trace-fst --trace-threads 1'], ); +run( + logfile => "$Self->{obj_dir}/make_first_ALL.log", + cmd => ["make", "-C", "$Self->{obj_dir}", "-f", "Vt_trace_two_b.mk", "Vt_trace_two_b__ALL.cpp"] + ); + compile( make_main => 0, top_filename => 't_trace_two_a.v', diff --git a/test_regress/t/t_trace_two_sc.cpp b/test_regress/t/t_trace_two_sc.cpp index 7547853d5..bd4f0ad77 100644 --- a/test_regress/t/t_trace_two_sc.cpp +++ b/test_regress/t/t_trace_two_sc.cpp @@ -17,12 +17,7 @@ // clang-format on // Compile in place -#include "Vt_trace_two_b.cpp" -#include "Vt_trace_two_b___024root.cpp" -#include "Vt_trace_two_b___024root__Slow.cpp" -#include "Vt_trace_two_b__Syms.cpp" -#include "Vt_trace_two_b__Trace.cpp" -#include "Vt_trace_two_b__Trace__Slow.cpp" +#include "Vt_trace_two_b__ALL.cpp" // General headers #include "verilated.h" From da8930a57af87e0e175ca846f3601403af622a1e Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Tue, 20 Jul 2021 08:00:14 -0400 Subject: [PATCH 29/84] Fix multitop to go to stderr (#3070) --- src/V3LinkLevel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/V3LinkLevel.cpp b/src/V3LinkLevel.cpp index 52a0ac057..728073fc5 100644 --- a/src/V3LinkLevel.cpp +++ b/src/V3LinkLevel.cpp @@ -62,7 +62,7 @@ void V3LinkLevel::modSortByLevel() { "--top-module to select top." << V3Error::warnContextNone()); for (AstNode* alsop : tops) { - std::cout << secp->warnMore() << "... Top module " << alsop->prettyNameQ() << endl + std::cerr << secp->warnMore() << "... Top module " << alsop->prettyNameQ() << endl << alsop->warnContextSecondary(); } } From 32e99ba0d942683fb1f0975a54f4fd7b2cd385d8 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Tue, 20 Jul 2021 13:52:53 +0100 Subject: [PATCH 30/84] Emit: Attach VL_ATTR_COLD to definitions as well as declarations. This is required by clang if the definition precedes the declaration (which can happen when building via __ALL.cpp), otherwise we get a warning. --- src/V3EmitCBase.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/V3EmitCBase.cpp b/src/V3EmitCBase.cpp index 0e740bbb2..ce087ff6f 100644 --- a/src/V3EmitCBase.cpp +++ b/src/V3EmitCBase.cpp @@ -82,6 +82,7 @@ string EmitCBaseVisitor::cFuncArgs(const AstCFunc* nodep) { void EmitCBaseVisitor::emitCFuncHeader(const AstCFunc* funcp, const AstNodeModule* modp, bool withScope) { + if (funcp->slow()) puts("VL_ATTR_COLD "); if (!funcp->isConstructor() && !funcp->isDestructor()) { puts(funcp->rtnTypeVoid()); puts(" "); @@ -109,7 +110,6 @@ void EmitCBaseVisitor::emitCFuncDecl(const AstCFunc* funcp, const AstNodeModule* puts("virtual "); } emitCFuncHeader(funcp, modp, /* withScope: */ false); - if (funcp->slow()) puts(" VL_ATTR_COLD"); puts(";\n"); if (!funcp->ifdef().empty()) puts("#endif // " + funcp->ifdef() + "\n"); } From a9c4a96c0f14ae651ebf95f48db676368afb21b2 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Tue, 20 Jul 2021 13:55:38 +0100 Subject: [PATCH 31/84] Internals: const pointer argument --- src/V3EmitCBase.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/V3EmitCBase.h b/src/V3EmitCBase.h index 7204012ff..8de966d6a 100644 --- a/src/V3EmitCBase.h +++ b/src/V3EmitCBase.h @@ -71,7 +71,7 @@ public: return v3Global.opt.prefix(); } - static bool isConstPoolMod(AstNode* modp) { + static bool isConstPoolMod(const AstNode* modp) { return modp == v3Global.rootp()->constPoolp()->modp(); } From d9c893af11601efaee82cb8f18b2dc3d0097b238 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Tue, 20 Jul 2021 14:18:35 +0100 Subject: [PATCH 32/84] Internals: Add VString::startsWith predicate function --- src/V3OptionParser.cpp | 8 +++----- src/V3PreProc.cpp | 14 +++++++------- src/V3String.cpp | 4 ++++ src/V3String.h | 2 ++ 4 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/V3OptionParser.cpp b/src/V3OptionParser.cpp index c6eec718c..f1db000af 100644 --- a/src/V3OptionParser.cpp +++ b/src/V3OptionParser.cpp @@ -120,14 +120,12 @@ V3OptionParser::ActionIfs* V3OptionParser::find(const char* optp) { if (it != m_pimpl->m_options.end()) return it->second.get(); for (auto&& act : m_pimpl->m_options) { if (act.second->isOnOffAllowed()) { // Find starts with "-no" - const char* const nop = std::strncmp(optp, "-no", 3) ? nullptr : (optp + 3); + const char* const nop = VString::startsWith(optp, "-no") ? (optp + 3) : nullptr; if (nop && (act.first == nop || act.first == (string{"-"} + nop))) { return act.second.get(); } } else if (act.second->isPartialMatchAllowed()) { - if (!std::strncmp(optp, act.first.c_str(), act.first.length())) { - return act.second.get(); - } + if (VString::startsWith(optp, act.first)) return act.second.get(); } } return nullptr; @@ -148,7 +146,7 @@ V3OptionParser::ActionIfs& V3OptionParser::add(const std::string& opt, ARG arg) bool V3OptionParser::hasPrefixNo(const char* strp) { UASSERT(strp[0] == '-', strp << " does not start with '-'"); if (strp[1] == '-') ++strp; - return std::strncmp(strp, "-no", 3) == 0; + return VString::startsWith(strp, "-no"); } int V3OptionParser::parse(int idx, int argc, char* argv[]) { diff --git a/src/V3PreProc.cpp b/src/V3PreProc.cpp index 3bf62d303..02dbeeaa4 100644 --- a/src/V3PreProc.cpp +++ b/src/V3PreProc.cpp @@ -395,7 +395,7 @@ string V3PreProcImp::commentCleanup(const string& text) { bool V3PreProcImp::commentTokenMatch(string& cmdr, const char* strg) { int len = strlen(strg); - if (0 == strncmp(cmdr.c_str(), strg, len) && (cmdr[len] == '\0' || isspace(cmdr[len]))) { + if (VString::startsWith(cmdr, strg) && (cmdr[len] == '\0' || isspace(cmdr[len]))) { if (isspace(cmdr[len])) len++; cmdr = cmdr.substr(len); return true; @@ -423,27 +423,27 @@ void V3PreProcImp::comment(const string& text) { bool synth = false; bool vlcomment = false; - if ((cp[0] == 'v' || cp[0] == 'V') && 0 == (strncmp(cp + 1, "erilator", 8))) { + if ((cp[0] == 'v' || cp[0] == 'V') && VString::startsWith(cp + 1, "erilator")) { cp += strlen("verilator"); if (*cp == '_') { fileline()->v3error("Extra underscore in meta-comment;" " use /*verilator {...}*/ not /*verilator_{...}*/"); } vlcomment = true; - } else if (0 == (strncmp(cp, "synopsys", strlen("synopsys")))) { + } else if (VString::startsWith(cp, "synopsys")) { cp += strlen("synopsys"); synth = true; if (*cp == '_') { fileline()->v3error("Extra underscore in meta-comment;" " use /*synopsys {...}*/ not /*synopsys_{...}*/"); } - } else if (0 == (strncmp(cp, "cadence", strlen("cadence")))) { + } else if (VString::startsWith(cp, "cadence")) { cp += strlen("cadence"); synth = true; - } else if (0 == (strncmp(cp, "pragma", strlen("pragma")))) { + } else if (VString::startsWith(cp, "pragma")) { cp += strlen("pragma"); synth = true; - } else if (0 == (strncmp(cp, "ambit synthesis", strlen("ambit synthesis")))) { + } else if (VString::startsWith(cp, "ambit synthesis")) { cp += strlen("ambit synthesis"); synth = true; } else { @@ -1544,7 +1544,7 @@ int V3PreProcImp::getFinalToken(string& buf) { // Track `line const char* bufp = buf.c_str(); while (*bufp == '\n') bufp++; - if ((tok == VP_TEXT || tok == VP_LINE) && 0 == strncmp(bufp, "`line ", 6)) { + if ((tok == VP_TEXT || tok == VP_LINE) && VString::startsWith(bufp, "`line ")) { int enter; m_finFilelinep->lineDirective(bufp, enter /*ref*/); } else { diff --git a/src/V3String.cpp b/src/V3String.cpp index c4f054575..caf5a8283 100644 --- a/src/V3String.cpp +++ b/src/V3String.cpp @@ -174,6 +174,10 @@ string VString::replaceWord(const string& str, const string& from, const string& return result; } +bool VString::startsWith(const string& str, const string& prefix) { + return str.rfind(prefix, 0) == 0; // Faster than .find(_) == 0 +} + //###################################################################### // VHashSha256 diff --git a/src/V3String.h b/src/V3String.h index f3ed2f651..e028561f3 100644 --- a/src/V3String.h +++ b/src/V3String.h @@ -113,6 +113,8 @@ public: // to be a consecutive sequence of the characters [a-zA-Z0-9_]. Sub-words are not replaced. // e.g.: replaceWords("one apple bad_apple", "apple", "banana") -> "one banana bad_apple" static string replaceWord(const string& str, const string& from, const string& to); + // Predicate to check if 'str' starts with 'prefix' + static bool startsWith(const string& str, const string& prefix); }; //###################################################################### From 9273fafbbf61a18e7b72800bfea55671145eb340 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Tue, 20 Jul 2021 16:40:38 +0100 Subject: [PATCH 33/84] Internals: Split parts of V3CUse into V3Common Apart from adding required AstCUse, V3CUse also used to create some standard methods for classes. This is now done in a separate pass V3Common. Note that this is not a performance issue, as V3Common just iterates through each module, which are stored in a simple linked list under the netlist, and does not need to traverse the whole netlist. --- src/Makefile_obj.in | 1 + src/V3CUse.cpp | 78 ---------------------------- src/V3Common.cpp | 120 ++++++++++++++++++++++++++++++++++++++++++++ src/V3Common.h | 30 +++++++++++ src/Verilator.cpp | 13 +++-- 5 files changed, 160 insertions(+), 82 deletions(-) create mode 100644 src/V3Common.cpp create mode 100644 src/V3Common.h diff --git a/src/Makefile_obj.in b/src/Makefile_obj.in index 95e860637..abb5f1a36 100644 --- a/src/Makefile_obj.in +++ b/src/Makefile_obj.in @@ -169,6 +169,7 @@ RAW_OBJS = \ V3Clean.o \ V3Clock.o \ V3Combine.o \ + V3Common.o \ V3Config.o \ V3Const__gen.o \ V3Coverage.o \ diff --git a/src/V3CUse.cpp b/src/V3CUse.cpp index c4da89455..d997256f3 100644 --- a/src/V3CUse.cpp +++ b/src/V3CUse.cpp @@ -44,9 +44,6 @@ private: std::map m_didUse; // What we already used // NODE STATE - // Entire netlist: - // AstClass::user1() -> bool. True if class needs to_string dumper - AstUser1InUse m_inuser1; // AstClass::user2() -> bool. True if iterated AstUser2InUse m_inuser2; @@ -129,85 +126,10 @@ class CUseVisitor final : public AstNVisitor { } } } - void makeVlToString(AstClass* nodep) { - AstCFunc* const funcp - = new AstCFunc{nodep->fileline(), "VL_TO_STRING", nullptr, "std::string"}; - funcp->argTypes("const VlClassRef<" + EmitCBaseVisitor::prefixNameProtect(nodep) - + ">& obj"); - funcp->isMethod(false); - funcp->isConst(false); - funcp->isStatic(false); - funcp->protect(false); - AstNode* const exprp - = new AstCMath{nodep->fileline(), "obj ? obj->to_string() : \"null\"", 0}; - exprp->dtypeSetString(); - funcp->addStmtsp(new AstCReturn{nodep->fileline(), exprp}); - nodep->addStmtp(funcp); - } - void makeToString(AstClass* nodep) { - AstCFunc* const funcp - = new AstCFunc{nodep->fileline(), "to_string", nullptr, "std::string"}; - funcp->isConst(true); - funcp->isStatic(false); - funcp->protect(false); - AstNode* const exprp = new AstCMath{nodep->fileline(), - R"(std::string("'{") + to_string_middle() + "}")", 0}; - exprp->dtypeSetString(); - funcp->addStmtsp(new AstCReturn{nodep->fileline(), exprp}); - nodep->addStmtp(funcp); - } - void makeToStringMiddle(AstClass* nodep) { - AstCFunc* const funcp - = new AstCFunc{nodep->fileline(), "to_string_middle", nullptr, "std::string"}; - funcp->isConst(true); - funcp->isStatic(false); - funcp->protect(false); - funcp->addStmtsp(new AstCStmt{nodep->fileline(), "std::string out;\n"}); - std::string comma; - for (AstNode* itemp = nodep->membersp(); itemp; itemp = itemp->nextp()) { - if (auto* const varp = VN_CAST(itemp, Var)) { - if (!varp->isParam()) { - string stmt = "out += \""; - stmt += comma; - comma = ", "; - stmt += itemp->origNameProtect(); - stmt += ":\" + "; - if (itemp->isWide()) { - stmt += "VL_TO_STRING_W("; - stmt += cvtToStr(itemp->widthWords()); - stmt += ", "; - } else { - stmt += "VL_TO_STRING("; - } - stmt += itemp->nameProtect(); - stmt += ");\n"; - nodep->user1(true); // So what we extend dumps this - funcp->addStmtsp(new AstCStmt{nodep->fileline(), stmt}); - } - } - } - if (nodep->extendsp() && nodep->extendsp()->classp()->user1()) { - string stmt = "out += \""; - if (!comma.empty()) stmt += "\", \"+ "; - // comma = ", "; // Nothing further so not needed - stmt += nodep->extendsp()->dtypep()->nameProtect(); - stmt += "::to_string_middle();\n"; - nodep->user1(true); // So what we extend dumps this - funcp->addStmtsp(new AstCStmt{nodep->fileline(), stmt}); - } - funcp->addStmtsp(new AstCStmt{nodep->fileline(), "return out;\n"}); - nodep->addStmtp(funcp); - } - // VISITORS virtual void visit(AstNodeModule* nodep) override { makeUseCells(nodep); { CUseDTypeVisitor dtypeVisitor{nodep, m_state}; } - if (AstClass* const classp = VN_CAST(nodep, Class)) { - makeVlToString(classp); - makeToString(classp); - makeToStringMiddle(classp); - } } virtual void visit(AstNode*) override {} // All in AstNodeModule diff --git a/src/V3Common.cpp b/src/V3Common.cpp new file mode 100644 index 000000000..0cb564a5d --- /dev/null +++ b/src/V3Common.cpp @@ -0,0 +1,120 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// DESCRIPTION: Verilator: Add common contents to modules +// +// Code available from: https://verilator.org +// +//************************************************************************* +// +// Copyright 2003-2021 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 +// +//************************************************************************* +// V3Common's Transformations: +// +// Each class: +// Create string access functions +// +//************************************************************************* + +#include "config_build.h" +#include "verilatedos.h" + +#include "V3Common.h" +#include "V3Ast.h" +#include "V3Global.h" +#include "V3EmitCBase.h" + +//###################################################################### +// Common component builders + +static void makeVlToString(AstClass* nodep) { + AstCFunc* const funcp + = new AstCFunc{nodep->fileline(), "VL_TO_STRING", nullptr, "std::string"}; + funcp->argTypes("const VlClassRef<" + EmitCBaseVisitor::prefixNameProtect(nodep) + ">& obj"); + funcp->isMethod(false); + funcp->isConst(false); + funcp->isStatic(false); + funcp->protect(false); + AstNode* const exprp = new AstCMath{nodep->fileline(), "obj ? obj->to_string() : \"null\"", 0}; + exprp->dtypeSetString(); + funcp->addStmtsp(new AstCReturn{nodep->fileline(), exprp}); + nodep->addStmtp(funcp); +} +static void makeToString(AstClass* nodep) { + AstCFunc* const funcp = new AstCFunc{nodep->fileline(), "to_string", nullptr, "std::string"}; + funcp->isConst(true); + funcp->isStatic(false); + funcp->protect(false); + AstNode* const exprp + = new AstCMath{nodep->fileline(), R"(std::string("'{") + to_string_middle() + "}")", 0}; + exprp->dtypeSetString(); + funcp->addStmtsp(new AstCReturn{nodep->fileline(), exprp}); + nodep->addStmtp(funcp); +} +static void makeToStringMiddle(AstClass* nodep) { + AstCFunc* const funcp + = new AstCFunc{nodep->fileline(), "to_string_middle", nullptr, "std::string"}; + funcp->isConst(true); + funcp->isStatic(false); + funcp->protect(false); + funcp->addStmtsp(new AstCStmt{nodep->fileline(), "std::string out;\n"}); + std::string comma; + for (AstNode* itemp = nodep->membersp(); itemp; itemp = itemp->nextp()) { + if (auto* const varp = VN_CAST(itemp, Var)) { + if (!varp->isParam()) { + string stmt = "out += \""; + stmt += comma; + comma = ", "; + stmt += itemp->origNameProtect(); + stmt += ":\" + "; + if (itemp->isWide()) { + stmt += "VL_TO_STRING_W("; + stmt += cvtToStr(itemp->widthWords()); + stmt += ", "; + } else { + stmt += "VL_TO_STRING("; + } + stmt += itemp->nameProtect(); + stmt += ");\n"; + nodep->user1(true); // So what we extend dumps this + funcp->addStmtsp(new AstCStmt{nodep->fileline(), stmt}); + } + } + } + if (nodep->extendsp() && nodep->extendsp()->classp()->user1()) { + string stmt = "out += \""; + if (!comma.empty()) stmt += "\", \"+ "; + // comma = ", "; // Nothing further so not needed + stmt += nodep->extendsp()->dtypep()->nameProtect(); + stmt += "::to_string_middle();\n"; + nodep->user1(true); // So what we extend dumps this + funcp->addStmtsp(new AstCStmt{nodep->fileline(), stmt}); + } + funcp->addStmtsp(new AstCStmt{nodep->fileline(), "return out;\n"}); + nodep->addStmtp(funcp); +} + +//###################################################################### +// V3Common class functions + +void V3Common::commonAll() { + UINFO(2, __FUNCTION__ << ": " << endl); + // Create common contents for each module + for (AstNode* nodep = v3Global.rootp()->modulesp(); nodep; nodep = nodep->nextp()) { + if (AstClass* const classp = VN_CAST(nodep, Class)) { + // NODE STATE + // Entire netlist: + // AstClass::user1() -> bool. True if class needs to_string dumper + AstUser1InUse m_inuser1; + // Create ToString methods + makeVlToString(classp); + makeToString(classp); + makeToStringMiddle(classp); + } + } + V3Global::dumpCheckGlobalTree("common", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); +} diff --git a/src/V3Common.h b/src/V3Common.h new file mode 100644 index 000000000..408507e77 --- /dev/null +++ b/src/V3Common.h @@ -0,0 +1,30 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// DESCRIPTION: Verilator: Add common contents to modules +// +// Code available from: https://verilator.org +// +//************************************************************************* +// +// Copyright 2003-2021 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_V3COMMON_H_ +#define VERILATOR_V3COMMON_H_ + +#include "config_build.h" +#include "verilatedos.h" + +//============================================================================ + +class V3Common final { +public: + static void commonAll(); +}; + +#endif // Guard diff --git a/src/Verilator.cpp b/src/Verilator.cpp index 65b75c2bb..28400d9e4 100644 --- a/src/Verilator.cpp +++ b/src/Verilator.cpp @@ -34,6 +34,7 @@ #include "V3Clean.h" #include "V3Clock.h" #include "V3Combine.h" +#include "V3Common.h" #include "V3Const.h" #include "V3Coverage.h" #include "V3CoverageJoin.h" @@ -502,15 +503,19 @@ static void process() { V3Partition::finalize(); } - // Output the text if (!v3Global.opt.lintOnly() && !v3Global.opt.xmlOnly() && !v3Global.opt.dpiHdrOnly()) { - // Create AstCUse to determine what class forward declarations/#includes needed in C - // Must be before V3EmitC - V3CUse::cUseAll(); + // Add common methods/etc to modules + V3Common::commonAll(); // Order variables V3VariableOrder::orderAll(); + // Create AstCUse to determine what class forward declarations/#includes needed in C + V3CUse::cUseAll(); + } + + // Output the text + if (!v3Global.opt.lintOnly() && !v3Global.opt.xmlOnly() && !v3Global.opt.dpiHdrOnly()) { // emitcInlines is first, as it may set needHInlines which other emitters read V3EmitC::emitcInlines(); V3EmitC::emitcSyms(); From e3dd70c3737ebed4731f59b6ef0baea9c7f452c2 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Tue, 20 Jul 2021 19:52:15 +0100 Subject: [PATCH 34/84] Internals: Simplify V3CUse No functional change intended. --- src/V3AstNodes.h | 7 ++- src/V3CUse.cpp | 120 ++++++++++++++--------------------------------- 2 files changed, 39 insertions(+), 88 deletions(-) diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index e9f7e8614..e85401a07 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -9013,8 +9013,8 @@ class AstCUse final : public AstNode { // C++ use of a class or #include; indicates need of forward declaration // Parents: NODEMODULE private: - VUseType m_useType; // What sort of use this is - string m_name; + const VUseType m_useType; // What sort of use this is + const string m_name; public: AstCUse(FileLine* fl, VUseType useType, const string& name) @@ -9022,10 +9022,9 @@ public: , m_useType{useType} , m_name{name} {} ASTNODE_NODE_FUNCS(CUse) - virtual string name() const override { return m_name; } virtual void dump(std::ostream& str = std::cout) const override; + virtual string name() const override { return m_name; } VUseType useType() const { return m_useType; } - void useType(VUseType useType) { m_useType = useType; } }; class AstMTaskBody final : public AstNode { diff --git a/src/V3CUse.cpp b/src/V3CUse.cpp index d997256f3..78a9260c3 100644 --- a/src/V3CUse.cpp +++ b/src/V3CUse.cpp @@ -13,14 +13,12 @@ // SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 // //************************************************************************* -// V3Class's Transformations: +// V3CUse's Transformations: // // Each module: // Each cell: // Create CUse for cell forward declaration -// Each class: -// Create string access functions -// Search for dtypes referencing class, and create CUse for forward declaraion +// Search for dtypes referencing class, and create CUse for forward declaration // //************************************************************************* @@ -30,58 +28,39 @@ #include "V3Global.h" #include "V3CUse.h" #include "V3Ast.h" -#include "V3EmitCBase.h" -#include +#include //###################################################################### -class CUseState final { -private: - // MEMBERS - AstNodeModule* m_modInsertp; // Current module to insert AstCUse under - using UseString = std::pair; - std::map m_didUse; // What we already used - - // NODE STATE - // AstClass::user2() -> bool. True if iterated - AstUser2InUse m_inuser2; - - // METHODS - VL_DEBUG_FUNC; // Declare debug() - -public: - AstCUse* newUse(AstNode* nodep, VUseType useType, const string& name) { - UseString key{useType, name}; - if (m_didUse.find(key) == m_didUse.end()) { - AstCUse* const newp = new AstCUse{nodep->fileline(), useType, name}; - m_modInsertp->addStmtp(newp); - UINFO(8, "Insert " << newp << endl); - m_didUse[key] = newp; - } - return m_didUse[key]; - } - - // CONSTRUCTORS - explicit CUseState(AstNodeModule* nodep) - : m_modInsertp{nodep} {} - virtual ~CUseState() = default; - VL_UNCOPYABLE(CUseState); -}; - // Visit within a module all nodes and data types they reference, finding // any classes so we can make sure they are defined when Verilated code // compiles -class CUseDTypeVisitor final : public AstNVisitor { +class CUseVisitor final : public AstNVisitor { + // NODE STATE + // AstNode::user1() -> bool. True if already visited + AstUser1InUse m_inuser1; + // MEMBERS - CUseState& m_stater; // State for inserter bool m_impOnly = false; // In details needed only for implementation + AstNodeModule* const m_modp; // Current module + std::set> m_didUse; // What we already used + // METHODS + void addNewUse(AstNode* nodep, VUseType useType, const string& name) { + if (m_didUse.emplace(useType, name).second) { + AstCUse* const newp = new AstCUse{nodep->fileline(), useType, name}; + m_modp->addStmtp(newp); + UINFO(8, "Insert " << newp << endl); + } + } + + // VISITORS virtual void visit(AstClassRefDType* nodep) override { - if (nodep->user2SetOnce()) return; // Process once - if (!m_impOnly) m_stater.newUse(nodep, VUseType::INT_FWD_CLASS, nodep->classp()->name()); + if (nodep->user1SetOnce()) return; // Process once + if (!m_impOnly) addNewUse(nodep, VUseType::INT_FWD_CLASS, nodep->classp()->name()); // No class.h, it's inside the class package's h file - m_stater.newUse(nodep, VUseType::IMP_INCLUDE, nodep->classp()->classOrPackagep()->name()); + addNewUse(nodep, VUseType::IMP_INCLUDE, nodep->classp()->classOrPackagep()->name()); // Need to include extends() when we implement, but no need for pointers to know VL_RESTORER(m_impOnly); { @@ -90,54 +69,27 @@ class CUseDTypeVisitor final : public AstNVisitor { } } virtual void visit(AstNodeDType* nodep) override { - if (nodep->user2SetOnce()) return; // Process once + if (nodep->user1SetOnce()) return; // Process once if (nodep->virtRefDTypep()) iterate(nodep->virtRefDTypep()); if (nodep->virtRefDType2p()) iterate(nodep->virtRefDType2p()); } virtual void visit(AstNode* nodep) override { - if (nodep->user2SetOnce()) return; // Process once - if (nodep->dtypep() && !nodep->dtypep()->user2()) iterate(nodep->dtypep()); + if (nodep->user1SetOnce()) return; // Process once + if (nodep->dtypep() && !nodep->dtypep()->user1()) iterate(nodep->dtypep()); + iterateChildren(nodep); + } + virtual void visit(AstCell* nodep) override { + if (nodep->user1SetOnce()) return; // Process once + // Currently no IMP_INCLUDE because we include __Syms which has them all + addNewUse(nodep, VUseType::INT_FWD_CLASS, nodep->modp()->name()); iterateChildren(nodep); } public: // CONSTRUCTORS - explicit CUseDTypeVisitor(AstNodeModule* nodep, CUseState& stater) - : m_stater(stater) { // Need () or GCC 4.8 false warning - iterate(nodep); - } - virtual ~CUseDTypeVisitor() override = default; - VL_UNCOPYABLE(CUseDTypeVisitor); -}; - -class CUseVisitor final : public AstNVisitor { - // MEMBERS - CUseState m_state; // Inserter state - - // METHODS - VL_DEBUG_FUNC; // Declare debug() - - // Module use builders - void makeUseCells(AstNodeModule* nodep) { - for (AstNode* itemp = nodep->stmtsp(); itemp; itemp = itemp->nextp()) { - if (AstCell* const cellp = VN_CAST(itemp, Cell)) { - // Currently no include because we include __Syms which has them all - m_state.newUse(nodep, VUseType::INT_FWD_CLASS, cellp->modp()->name()); - } - } - } - // VISITORS - virtual void visit(AstNodeModule* nodep) override { - makeUseCells(nodep); - { CUseDTypeVisitor dtypeVisitor{nodep, m_state}; } - } - virtual void visit(AstNode*) override {} // All in AstNodeModule - -public: - // CONSTRUCTORS - explicit CUseVisitor(AstNodeModule* nodep) - : m_state{nodep} { - iterate(nodep); + explicit CUseVisitor(AstNodeModule* modp) + : m_modp(modp) { + iterate(modp); } virtual ~CUseVisitor() override = default; VL_UNCOPYABLE(CUseVisitor); @@ -153,7 +105,7 @@ void V3CUse::cUseAll() { modp = VN_CAST(modp->nextp(), NodeModule)) { // Insert under this module; someday we should e.g. make Ast // for each output file and put under that - CUseVisitor visitor{modp}; + CUseVisitor{modp}; } V3Global::dumpCheckGlobalTree("cuse", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } From 42d918c1638a00e164786dccf713dd6db1a74099 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Thu, 22 Jul 2021 13:33:00 +0100 Subject: [PATCH 35/84] Internals: Omit selfPointer from ConstPool references AstVarRefs to constant pool values are special and they have no use for the selfPointer. Removing it simplifies future downstream code. --- src/V3Descope.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/V3Descope.cpp b/src/V3Descope.cpp index 9cb49f44d..0621075d7 100644 --- a/src/V3Descope.cpp +++ b/src/V3Descope.cpp @@ -217,6 +217,9 @@ private: if (varp->isFuncLocal()) { // Reference to function locals need no self pointer nodep->selfPointer(""); + } else if (scopep->modp() == v3Global.rootp()->constPoolp()->modp()) { + // Reference to constant pool value need no self pointer + nodep->selfPointer(""); } else if (VN_IS(scopep->modp(), Class)) { // Direct reference to class members are from within the class itself, references from // outside the class must go via AstMemberSel From 8bb77f86ec8eebbb748a75795e06bd71111679eb Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Thu, 22 Jul 2021 15:53:42 +0100 Subject: [PATCH 36/84] Emit: Factor out parent module access via user4 No functional change. --- src/V3EmitCBase.cpp | 20 +++++++++++++++++--- src/V3EmitCBase.h | 18 ++++++++++++++++++ src/V3EmitCFunc.h | 23 +++++++++++++---------- src/V3EmitCImp.cpp | 13 ++----------- 4 files changed, 50 insertions(+), 24 deletions(-) diff --git a/src/V3EmitCBase.cpp b/src/V3EmitCBase.cpp index ce087ff6f..0632a32e3 100644 --- a/src/V3EmitCBase.cpp +++ b/src/V3EmitCBase.cpp @@ -20,11 +20,26 @@ #include "V3EmitCBase.h" #include "V3Task.h" +//###################################################################### +// EmitCParentModule implementation + +EmitCParentModule::EmitCParentModule() { + const auto setAll = [](AstNodeModule* modp) -> void { + for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { + if (VN_IS(nodep, CFunc) || VN_IS(nodep, Var)) { nodep->user4p(modp); } + } + }; + for (AstNode* modp = v3Global.rootp()->modulesp(); modp; modp = modp->nextp()) { + setAll(VN_CAST(modp, NodeModule)); + } + setAll(v3Global.rootp()->constPoolp()->modp()); +} + //###################################################################### // EmitCBaseVisitor implementation string EmitCBaseVisitor::funcNameProtect(const AstCFunc* nodep, const AstNodeModule* modp) { - modp = modp ? modp : VN_CAST(nodep->user4p(), NodeModule); + modp = modp ? modp : EmitCParentModule::get(nodep); string name; if (nodep->isConstructor()) { name += prefixNameProtect(modp); @@ -54,8 +69,7 @@ string EmitCBaseVisitor::cFuncArgs(const AstCFunc* nodep) { string args; if (nodep->isLoose() && !nodep->isStatic()) { if (nodep->isConst().trueKnown()) args += "const "; - AstNodeModule* modp = VN_CAST(nodep->user4p(), NodeModule); - args += prefixNameProtect(modp); + args += prefixNameProtect(EmitCParentModule::get(nodep)); args += "* vlSelf"; } if (!nodep->argTypes().empty()) { diff --git a/src/V3EmitCBase.h b/src/V3EmitCBase.h index 8de966d6a..0b34a525e 100644 --- a/src/V3EmitCBase.h +++ b/src/V3EmitCBase.h @@ -27,6 +27,24 @@ #include #include +//###################################################################### +// Set user4p in all CFunc and Var to point to the containing AstNodeModule + +class EmitCParentModule final { + // NODE STATE + // AstFunc::user4p() AstNodeModule* Parent module pointer + // AstVar::user4p() AstNodeModule* Parent module pointer + AstUser4InUse user4InUse; + +public: + EmitCParentModule(); + VL_UNCOPYABLE(EmitCParentModule); + + static const AstNodeModule* get(const AstNode* nodep) { + return VN_CAST_CONST(nodep->user4p(), NodeModule); + } +}; + //###################################################################### // Base Visitor class -- holds output file pointer diff --git a/src/V3EmitCFunc.h b/src/V3EmitCFunc.h index 362aa7e3a..cf947674c 100644 --- a/src/V3EmitCFunc.h +++ b/src/V3EmitCFunc.h @@ -53,8 +53,7 @@ class EmitCLazyDecls final : public AstNVisitor { // Already declared manually if (m_emittedManually.count(funcp->nameProtect())) return; // Needs lazy declaration, emit one - m_emitter.emitCFuncDecl(funcp, VN_CAST_CONST(funcp->user4p(), NodeModule), - funcp->dpiImportPrototype()); + m_emitter.emitCFuncDecl(funcp, EmitCParentModule::get(funcp), funcp->dpiImportPrototype()); m_needsBlankLine = true; } @@ -82,7 +81,9 @@ class EmitCLazyDecls final : public AstNVisitor { virtual void visit(AstVarRef* nodep) override { AstVar* const varp = nodep->varp(); // Only constant pool symbols are lazy declared for now ... - if (EmitCBaseVisitor::isConstPoolMod(varp->user4p())) { lazyDeclareConstPoolVar(varp); } + if (EmitCBaseVisitor::isConstPoolMod(EmitCParentModule::get(varp))) { + lazyDeclareConstPoolVar(varp); + } } virtual void visit(AstNode* nodep) override { iterateChildrenConst(nodep); } @@ -354,16 +355,17 @@ public: } virtual void visit(AstCCall* nodep) override { const AstCFunc* const funcp = nodep->funcp(); + const AstNodeModule* const funcModp = EmitCParentModule::get(funcp); if (funcp->dpiImportPrototype()) { // Calling DPI import puts(funcp->name()); } else if (funcp->isProperMethod() && funcp->isStatic()) { // Call static method via the containing class - puts(prefixNameProtect(funcp->user4p()) + "::"); + puts(prefixNameProtect(funcModp) + "::"); puts(funcp->nameProtect()); - } else if (VN_IS(funcp->user4p(), Class) && funcp->user4p() != m_modp) { + } else if (VN_IS(funcModp, Class) && funcModp != m_modp) { // Calling superclass method - puts(prefixNameProtect(funcp->user4p()) + "::"); + puts(prefixNameProtect(funcModp) + "::"); puts(funcp->nameProtect()); } else if (funcp->isLoose()) { // Calling loose method @@ -1100,15 +1102,16 @@ public: // Terminals virtual void visit(AstVarRef* nodep) override { const AstVar* const varp = nodep->varp(); - if (isConstPoolMod(varp->user4p())) { + const AstNodeModule* const varModp = EmitCParentModule::get(varp); + if (isConstPoolMod(varModp)) { // Reference to constant pool variable puts(topClassName() + "__ConstPool__"); } else if (varp->isStatic()) { // Access static variable via the containing class - puts(prefixNameProtect(varp->user4p()) + "::"); - } else if (VN_IS(varp->user4p(), Class) && varp->user4p() != m_modp) { + puts(prefixNameProtect(varModp) + "::"); + } else if (VN_IS(varModp, Class) && varModp != m_modp) { // Superclass member reference - puts(prefixNameProtect(varp->user4p()) + "::"); + puts(prefixNameProtect(varModp) + "::"); } else if (!nodep->selfPointer().empty()) { emitDereference(nodep->selfPointerProtect(m_useSelfForThis)); } diff --git a/src/V3EmitCImp.cpp b/src/V3EmitCImp.cpp index 78d65c49f..dad347971 100644 --- a/src/V3EmitCImp.cpp +++ b/src/V3EmitCImp.cpp @@ -697,17 +697,8 @@ public: void V3EmitC::emitcImp() { UINFO(2, __FUNCTION__ << ": " << endl); - // Set user4p in all CFunc and Var to point to the containing AstNodeModule - AstUser4InUse user4InUse; - const auto setAll = [](AstNodeModule* modp) -> void { - for (AstNode* nodep = VN_CAST(modp, NodeModule)->stmtsp(); nodep; nodep = nodep->nextp()) { - if (VN_IS(nodep, CFunc) || VN_IS(nodep, Var)) nodep->user4p(modp); - } - }; - for (AstNode* modp = v3Global.rootp()->modulesp(); modp; modp = modp->nextp()) { - setAll(VN_CAST(modp, NodeModule)); - } - setAll(v3Global.rootp()->constPoolp()->modp()); + // Make parent module pointers available. + EmitCParentModule emitCParentModule; // Process each module in turn for (const AstNode* nodep = v3Global.rootp()->modulesp(); nodep; nodep = nodep->nextp()) { From ab4063f098d668bf72d3e969b8fa4521d61cc2c2 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Wed, 14 Jul 2021 22:37:37 +0100 Subject: [PATCH 37/84] Emit implementations into separate files based on required headers. This patch partitions AstCFuncs under an AstNodeModule based on which header files they require for their implementation, and emits them into separate files based on the distinct dependency sets. This helps with incremental recompilation of the output C++. --- Changes | 2 + src/V3CUse.cpp | 2 - src/V3EmitCFunc.h | 10 +- src/V3EmitCImp.cpp | 298 +++++++++++++++++---- src/V3EmitCModel.cpp | 8 +- src/V3UniqueNames.h | 39 +++ test_regress/driver.pl | 51 ++++ test_regress/t/t_alw_noreorder.pl | 5 +- test_regress/t/t_alw_reorder.pl | 6 +- test_regress/t/t_c_this.pl | 2 +- test_regress/t/t_flag_comp_limit_parens.pl | 2 +- test_regress/t/t_flag_csplit_off.pl | 2 +- test_regress/t/t_flag_xinitial_unique.pl | 2 +- test_regress/t/t_foreach.pl | 10 +- test_regress/t/t_inst_tree_inl0_pub1.pl | 19 +- test_regress/t/t_optm_if_array.pl | 5 +- test_regress/t/t_optm_redor.pl | 5 +- test_regress/t/t_trace_ena_cc.pl | 4 +- 18 files changed, 378 insertions(+), 94 deletions(-) create mode 100644 src/V3UniqueNames.h diff --git a/Changes b/Changes index ad852bf44..a1151d3b5 100644 --- a/Changes +++ b/Changes @@ -14,6 +14,8 @@ Verilator 4.211 devel **Minor:** * Fix -G to treat simple integer literals as signed (#3060). [Anikin1610] +* Output files are split based on the set of headers required + in order to aid incremental compilation via ccache (#3071). [Geza Lore] Verilator 4.210 2021-07-07 diff --git a/src/V3CUse.cpp b/src/V3CUse.cpp index 78a9260c3..54c2a0566 100644 --- a/src/V3CUse.cpp +++ b/src/V3CUse.cpp @@ -59,8 +59,6 @@ class CUseVisitor final : public AstNVisitor { virtual void visit(AstClassRefDType* nodep) override { if (nodep->user1SetOnce()) return; // Process once if (!m_impOnly) addNewUse(nodep, VUseType::INT_FWD_CLASS, nodep->classp()->name()); - // No class.h, it's inside the class package's h file - addNewUse(nodep, VUseType::IMP_INCLUDE, nodep->classp()->classOrPackagep()->name()); // Need to include extends() when we implement, but no need for pointers to know VL_RESTORER(m_impOnly); { diff --git a/src/V3EmitCFunc.h b/src/V3EmitCFunc.h index cf947674c..eb1aa4de3 100644 --- a/src/V3EmitCFunc.h +++ b/src/V3EmitCFunc.h @@ -118,7 +118,6 @@ private: AstVarRef* m_wideTempRefp; // Variable that _WW macros should be setting int m_labelNum; // Next label number int m_splitSize; // # of cfunc nodes placed into output file - int m_splitFilenum; // File number being created, 0 = primary bool m_inUC = false; // Inside an AstUCStmt or AstUCMath std::vector m_blkChangeDetVec; // All encountered changes in block @@ -133,15 +132,11 @@ public: VL_DEBUG_FUNC; // Declare debug() // ACCESSORS - int splitFilenumInc() { - m_splitSize = 0; - return m_splitFilenum++; - } - int splitSize() const { return m_splitSize; } void splitSizeInc(int count) { m_splitSize += count; } void splitSizeInc(AstNode* nodep) { splitSizeInc(EmitCBaseCounterVisitor(nodep).count()); } + void splitSizeReset() { m_splitSize = 0; } bool splitNeeded() const { - return v3Global.opt.outputSplit() && splitSize() >= v3Global.opt.outputSplit(); + return v3Global.opt.outputSplit() && m_splitSize >= v3Global.opt.outputSplit(); } // METHODS @@ -1220,7 +1215,6 @@ public: m_wideTempRefp = nullptr; m_labelNum = 0; m_splitSize = 0; - m_splitFilenum = 0; } EmitCFunc(AstNode* nodep, V3OutCFile* ofp, bool trackText = false) : EmitCFunc{} { diff --git a/src/V3EmitCImp.cpp b/src/V3EmitCImp.cpp index dad347971..be6bca502 100644 --- a/src/V3EmitCImp.cpp +++ b/src/V3EmitCImp.cpp @@ -19,10 +19,126 @@ #include "V3Global.h" #include "V3EmitC.h" +#include "V3Ast.h" #include "V3EmitCFunc.h" +#include "V3String.h" +#include "V3UniqueNames.h" +#include +#include #include -#include + +//###################################################################### +// Visitor that gathers the headers required by an AstCFunc + +class EmitCGatherDependencies final : AstNVisitor { + // Ordered set, as it is used as a key in another map. + std::set m_dependencies; // Header names to be included in output C++ file + + // METHODS + void addSymsDependency() { m_dependencies.insert(EmitCBaseVisitor::symClassName()); } + void addModDependency(const AstNodeModule* modp) { + if (const AstClass* const classp = VN_CAST_CONST(modp, Class)) { + m_dependencies.insert(EmitCBaseVisitor::prefixNameProtect(classp->classOrPackagep())); + } else { + m_dependencies.insert(EmitCBaseVisitor::prefixNameProtect(modp)); + } + } + void addDTypeDependency(const AstNodeDType* nodep) { + if (const AstClassRefDType* const dtypep = VN_CAST_CONST(nodep, ClassRefDType)) { + m_dependencies.insert( + EmitCBaseVisitor::prefixNameProtect(dtypep->classp()->classOrPackagep())); + } + } + void addSelfDependency(const string& selfPointer, AstNode* nodep) { + if (selfPointer.empty()) { + // No self pointer (e.g.: function locals, const pool values, loose static methods), + // so no dependency + } else if (VString::startsWith(selfPointer, "this")) { + // Dereferencing 'this', we need the definition of this module, which is also the + // module that contains the variable. + addModDependency(EmitCParentModule::get(nodep)); + } else { + // Must be an absolute reference + UASSERT_OBJ(selfPointer.find("vlSymsp") != string::npos, nodep, + "Unknown self pointer: '" << selfPointer << "'"); + // Dereferencing vlSymsp, so we need it's definition... + m_dependencies.insert(EmitCBaseVisitor::symClassName()); + } + } + + // VISITORS + virtual void visit(AstCCall* nodep) override { + addSelfDependency(nodep->selfPointer(), nodep->funcp()); + iterateChildrenConst(nodep); + } + virtual void visit(AstCNew* nodep) override { + addDTypeDependency(nodep->dtypep()); + iterateChildrenConst(nodep); + } + virtual void visit(AstCMethodCall* nodep) override { + addDTypeDependency(nodep->fromp()->dtypep()); + iterateChildrenConst(nodep); + } + virtual void visit(AstNewCopy* nodep) override { + addDTypeDependency(nodep->dtypep()); + iterateChildrenConst(nodep); + } + virtual void visit(AstMemberSel* nodep) override { + addDTypeDependency(nodep->fromp()->dtypep()); + iterateChildrenConst(nodep); + } + virtual void visit(AstNodeVarRef* nodep) override { + addSelfDependency(nodep->selfPointer(), nodep->varp()); + iterateChildrenConst(nodep); + } + virtual void visit(AstCoverDecl* nodep) override { + addSymsDependency(); + iterateChildrenConst(nodep); + } + virtual void visit(AstCoverInc* nodep) override { + addSymsDependency(); + iterateChildrenConst(nodep); + } + virtual void visit(AstDumpCtl* nodep) override { + addSymsDependency(); + iterateChildrenConst(nodep); + } + virtual void visit(AstScopeName* nodep) override { + addSymsDependency(); + iterateChildrenConst(nodep); + } + virtual void visit(AstPrintTimeScale* nodep) override { + addSymsDependency(); + iterateChildrenConst(nodep); + } + virtual void visit(AstTimeFormat* nodep) override { + addSymsDependency(); + iterateChildrenConst(nodep); + } + virtual void visit(AstNodeSimpleText* nodep) override { + if (nodep->text().find("vlSymsp") != string::npos) { + m_dependencies.insert(EmitCBaseVisitor::symClassName()); + } + iterateChildrenConst(nodep); + } + virtual void visit(AstNode* nodep) override { iterateChildrenConst(nodep); } + + // CONSTRUCTOR + explicit EmitCGatherDependencies(AstCFunc* cfuncp) { + // Strictly speaking, for loose methods, we could get away with just a forward + // declaration of the receiver class, but their body very likely includes at least one + // relative reference, so we are probably not loosing much. + addModDependency(EmitCParentModule::get(cfuncp)); + iterate(cfuncp); + } + +public: + static const std::set gather(AstCFunc* cfuncp) { + EmitCGatherDependencies visitor{cfuncp}; + return std::move(visitor.m_dependencies); + } +}; //###################################################################### // Internal EmitC implementation @@ -31,11 +147,15 @@ class EmitCImp final : EmitCFunc { // MEMBERS const AstNodeModule* const m_fileModp; // Files names/headers constructed using this module const bool m_slow; // Creating __Slow file + const std::set* m_requiredHeadersp; // Header files required by output file + std::string m_subFileName; // substring added to output filenames + V3UniqueNames m_uniqueNames; // For generating unique file names // METHODS - void openNextOutputFile() { + void openNextOutputFile(const std::set& headers, const string& subFileName) { UASSERT(!m_ofp, "Output file already open"); + splitSizeReset(); // Reset file size tracking m_lazyDecls.reset(); // Need to emit new lazy declarations if (v3Global.opt.lintOnly()) { @@ -46,7 +166,10 @@ class EmitCImp final : EmitCFunc { m_ofp = new V3OutCFile(filename); } else { string filename = v3Global.opt.makeDir() + "/" + prefixNameProtect(m_fileModp); - if (const int filenum = splitFilenumInc()) filename += "__" + cvtToStr(filenum); + if (!subFileName.empty()) { + filename += "__" + subFileName; + filename = m_uniqueNames.get(filename); + } if (m_slow) filename += "__Slow"; filename += ".cpp"; newCFile(filename, /* slow: */ m_slow, /* source: */ true); @@ -58,21 +181,26 @@ class EmitCImp final : EmitCFunc { puts("// See " + topClassName() + ".h for the primary calling header\n"); // Include files + puts("\n#include \"verilated_heavy.h\"\n"); + if (v3Global.dpi()) puts("#include \"verilated_dpi.h\"\n"); puts("\n"); - puts("#include \"" + prefixNameProtect(m_fileModp) + ".h\"\n"); - puts("#include \"" + symClassName() + ".h\"\n"); - - if (v3Global.dpi()) { - puts("\n"); - puts("#include \"verilated_dpi.h\"\n"); - } - - emitModCUse(m_fileModp, VUseType::IMP_INCLUDE); - emitModCUse(m_fileModp, VUseType::IMP_FWD_CLASS); + for (const string& name : headers) puts("#include \"" + name + ".h\"\n"); emitTextSection(m_modp, AstType::atScImpHdr); } + void emitStaticVarDefns(const AstNodeModule* modp) { + // Emit static variable definitions + const string modName = prefixNameProtect(modp); + for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { + if (const AstVar* const varp = VN_CAST_CONST(nodep, Var)) { + if (varp->isStatic()) { + puts(varp->vlArgType(true, false, false, modName)); + puts(";\n"); + } + } + } + } void emitParamDefns(const AstNodeModule* modp) { for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { if (const AstVar* const varp = VN_CAST_CONST(nodep, Var)) { @@ -168,8 +296,9 @@ class EmitCImp final : EmitCFunc { void emitCoverageImp() { if (v3Global.opt.coverage()) { puts("\n// Coverage\n"); - // Rather than putting out VL_COVER_INSERT calls directly, we do it via this function - // This gets around gcc slowness constructing all of the template arguments. + // Rather than putting out VL_COVER_INSERT calls directly, we do it via this + // function. This gets around gcc slowness constructing all of the template + // arguments. puts("void " + prefixNameProtect(m_modp) + "::__vlCoverInsert("); puts(v3Global.opt.threads() ? "std::atomic" : "uint32_t"); puts("* countp, bool enable, const char* filenamep, int lineno, int column,\n"); @@ -289,19 +418,23 @@ class EmitCImp final : EmitCFunc { } } } - void emitAll(const AstNodeModule* modp) { + // Predicate to check if we actually need to emit anything into the common implementation file. + // Used to avoid creating empty output files. + bool hasCommonImp(const AstNodeModule* modp) const { + // Nothing to emit if no module! + if (!modp) return false; + // We always need the slow file + if (m_slow) return true; + // The fast file is only required when we have ScImp nodes + for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { + if (VN_IS(nodep, ScImp)) return true; + } + return false; + } + // Actually emit common implementation contents for given AstNodeModule + void doCommonImp(const AstNodeModule* modp) { if (m_slow) { - // Emit static variable definitions - const string modName = prefixNameProtect(modp); - for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { - if (const AstVar* const varp = VN_CAST_CONST(nodep, Var)) { - if (varp->isStatic()) { - puts(varp->vlArgType(true, false, false, modName)); - puts(";\n"); - } - } - } - + emitStaticVarDefns(modp); if (!VN_IS(modp, Class)) { emitParamDefns(modp); emitCtorImp(modp); @@ -311,30 +444,86 @@ class EmitCImp final : EmitCFunc { emitSavableImp(modp); emitCoverageImp(); } else { + // From `systemc_implementation emitTextSection(modp, AstType::atScImp); } + } + void emitCommonImp(const AstNodeModule* modp) { + const AstClass* const classp + = VN_IS(modp, ClassPackage) ? VN_CAST_CONST(modp, ClassPackage)->classp() : nullptr; - // Emit all AstCFunc - for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { - if (AstCFunc* const funcp = VN_CAST(nodep, CFunc)) iterate(funcp); + if (hasCommonImp(modp) || hasCommonImp(classp)) { + std::set headers; + headers.insert(prefixNameProtect(m_fileModp)); + headers.insert(symClassName()); + + openNextOutputFile(headers, ""); + + doCommonImp(modp); + if (classp) { + VL_RESTORER(m_modp); + m_modp = classp; + doCommonImp(classp); + } + + VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr); + } + } + void emitCFuncImp(const AstNodeModule* modp) { + // Partition functions based on which module definitions they require, by building a + // map from "AstNodeModules whose definitions are required" -> "functions that need + // them" + std::map, std::vector> depSet2funcps; + + const auto gather = [this, &depSet2funcps](const AstNodeModule* modp) { + for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { + if (AstCFunc* const funcp = VN_CAST(nodep, CFunc)) { + // TRACE_* and DPI handled elsewhere + if (funcp->isTrace()) continue; + if (funcp->dpiImportPrototype()) continue; + if (funcp->dpiExportDispatcher()) continue; + if (funcp->slow() != m_slow) continue; + const auto& depSet = EmitCGatherDependencies::gather(funcp); + depSet2funcps[depSet].push_back(funcp); + } + } + }; + + gather(modp); + if (const AstClassPackage* const packagep = VN_CAST_CONST(modp, ClassPackage)) { + gather(packagep->classp()); + } + + // Emit all functions in each dependency set into separate files + for (const auto& pair : depSet2funcps) { + m_requiredHeadersp = &pair.first; + // Compute the hash of the dependencies, so we can add it to the filenames to + // disambiguate them + V3Hash hash; + for (const string& name : *m_requiredHeadersp) { hash += name; } + m_subFileName = "DepSet_" + cvtToHex(hash.value()); + // Open output file + openNextOutputFile(*m_requiredHeadersp, m_subFileName); + // Emit functions in this dependency set + for (AstCFunc* const funcp : pair.second) { + VL_RESTORER(m_modp); + m_modp = EmitCParentModule::get(funcp); + iterate(funcp); + } + // Close output file + VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr); } } // VISITORS virtual void visit(AstCFunc* nodep) override { - // TRACE_* and DPI handled elsewhere - if (nodep->isTrace()) return; - if (nodep->dpiImportPrototype()) return; - if (nodep->dpiExportDispatcher()) return; - if (nodep->slow() != m_slow) return; - if (splitNeeded()) { // Splitting file, so using parallel build. v3Global.useParallelBuild(true); // Close old file VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr); // Open a new file - openNextOutputFile(); + openNextOutputFile(*m_requiredHeadersp, m_subFileName); } EmitCFunc::visit(nodep); @@ -347,20 +536,16 @@ class EmitCImp final : EmitCFunc { m_modp = modp; - openNextOutputFile(); + // Emit implementation of this module, if this is an AstClassPackage, then put the + // corresponding AstClass implementation in the same file as often optimziations are + // possible when both are seen by the compiler + // TODO: is the above comment still true? - emitAll(modp); + // Emit implementations of common parts + emitCommonImp(modp); - if (const AstClassPackage* const packagep = VN_CAST_CONST(modp, ClassPackage)) { - // Put the non-static class implementation in same C++ files as - // often optimizations are possible when both are seen by the - // compiler together - m_modp = packagep->classp(); - emitAll(packagep->classp()); - } - - // Close output file - VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr); + // Emit implementations of all AstCFunc + emitCFuncImp(modp); } virtual ~EmitCImp() override = default; @@ -380,21 +565,24 @@ class EmitCTrace final : EmitCFunc { // MEMBERS const bool m_slow; // Making slow file int m_enumNum = 0; // Enumeration number (whole netlist) + V3UniqueNames m_uniqueNames; // For generating unique file names // METHODS - void newOutCFile(int filenum) { + void openNextOutputFile() { + UASSERT(!m_ofp, "Output file already open"); + + splitSizeReset(); // Reset file size tracking m_lazyDecls.reset(); // Need to emit new lazy declarations string filename = (v3Global.opt.makeDir() + "/" + topClassName() + "_" + protect("_Trace")); - if (filenum) filename += "__" + cvtToStr(filenum); - filename += (m_slow ? "__Slow" : ""); + filename = m_uniqueNames.get(filename); + if (m_slow) filename += "__Slow"; filename += ".cpp"; AstCFile* cfilep = newCFile(filename, m_slow, true /*source*/); cfilep->support(true); - if (m_ofp) v3fatalSrc("Previous file not closed"); if (optSystemC()) { m_ofp = new V3OutScFile(filename); } else { @@ -647,7 +835,7 @@ class EmitCTrace final : EmitCFunc { // Close old file VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr); // Open a new file - newOutCFile(splitFilenumInc()); + openNextOutputFile(); } EmitCFunc::visit(nodep); @@ -678,7 +866,7 @@ class EmitCTrace final : EmitCFunc { : m_slow{slow} { m_modp = modp; // Open output file - newOutCFile(splitFilenumInc()); + openNextOutputFile(); // Emit functions for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { if (AstCFunc* const funcp = VN_CAST(nodep, CFunc)) { iterate(funcp); } diff --git a/src/V3EmitCModel.cpp b/src/V3EmitCModel.cpp index 2e47ff004..d0c6ccb83 100644 --- a/src/V3EmitCModel.cpp +++ b/src/V3EmitCModel.cpp @@ -20,11 +20,13 @@ #include "V3Global.h" #include "V3EmitC.h" #include "V3EmitCFunc.h" +#include "V3UniqueNames.h" #include #include class EmitCModel final : public EmitCFunc { + V3UniqueNames m_uniqueNames; // For generating unique file names // METHODS VL_DEBUG_FUNC; @@ -584,11 +586,13 @@ class EmitCModel final : public EmitCFunc { } if (!m_ofp) { - const string filename = v3Global.opt.makeDir() + "/" + topClassName() - + "__Dpi_Export_" + cvtToStr(splitFilenumInc()) + ".cpp"; + string filename = v3Global.opt.makeDir() + "/" + topClassName() + "__Dpi_Export"; + filename = m_uniqueNames.get(filename); + filename += ".cpp"; newCFile(filename, /* slow: */ false, /* source: */ true); m_ofp = v3Global.opt.systemC() ? new V3OutScFile{filename} : new V3OutCFile{filename}; + splitSizeReset(); // Reset file size tracking m_lazyDecls.reset(); m_ofp->putsHeader(); puts( diff --git a/src/V3UniqueNames.h b/src/V3UniqueNames.h new file mode 100644 index 000000000..2d3f94560 --- /dev/null +++ b/src/V3UniqueNames.h @@ -0,0 +1,39 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// DESCRIPTION: Verilator: Basic data structure to keep names unique +// +// Code available from: https://verilator.org +// +//************************************************************************* +// +// Copyright 2005-2021 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_V3UNIQUENAMES_H_ +#define VERILATOR_V3UNIQUENAMES_H_ +#include "config_build.h" +#include "verilatedos.h" + +#include +#include + +class V3UniqueNames final { + std::unordered_map m_multiplicity; // Suffix number for given key + +public: + // Return argument, appended with a unique suffix each time we are called with the same + // argument. + std::string get(const std::string& name) { + const unsigned num = m_multiplicity.emplace(name, 0).first->second++; + return name + "__" + cvtToStr(num); + } +}; + +#endif // Guard diff --git a/test_regress/driver.pl b/test_regress/driver.pl index 9c1a56408..bf6efd9bc 100755 --- a/test_regress/driver.pl +++ b/test_regress/driver.pl @@ -2371,6 +2371,33 @@ sub tries { return 2; } +sub glob_all { + my $self = (ref $_[0]? shift : $Self); + my $pattern = shift; + + return glob($pattern); +} + +sub glob_one { + my $self = (ref $_[0]? shift : $Self); + my $pattern = shift; + return if $self->errors || $self->skips || $self->unsupporteds; + + my @files = glob($pattern); + my $n = scalar @files; + if ($n == 0) { + $self->error("glob_one: pattern '$pattern' does not match any files\n"); + } elsif ($n != 1) { + my $msg = "glob_one: pattern '$pattern' matches multiple files:\n"; + foreach my $file (@files) { + $msg .= $file."\n"; + } + $self->error($msg); + } else { + return $files[0]; + } +} + sub file_grep_not { my $self = (ref $_[0]? shift : $Self); my $filename = shift; @@ -2402,6 +2429,30 @@ sub file_grep { } } +sub file_grep_any { + my $self = $Self; + my @filenames = @{$_[0]}; shift; + my $regexp = shift; + my $expvalue = shift; + return if $self->errors || $self->skips || $self->unsupporteds; + + foreach my $filename (@filenames) { + my $contents = $self->file_contents($filename); + return if ($contents eq "_Already_Errored_"); + if ($contents =~ /$regexp/) { + if ($expvalue && $expvalue ne $1) { + $self->error("file_grep: $filename: Got='$1' Expected='$expvalue' in regexp: $regexp\n"); + } + return; + } + } + my $msg = "file_grep_any: Regexp '$regexp' not found in any of the following files:\n"; + foreach my $filename (@filenames) { + $msg .= $filename."\n"; + } + $self->error($msg); +} + my %_File_Contents_Cache; sub file_contents { diff --git a/test_regress/t/t_alw_noreorder.pl b/test_regress/t/t_alw_noreorder.pl index b0543df38..7d6eceb88 100755 --- a/test_regress/t/t_alw_noreorder.pl +++ b/test_regress/t/t_alw_noreorder.pl @@ -19,8 +19,9 @@ file_grep($Self->{stats}, qr/Optimizations, Split always\s+(\d+)/i, 0); # Here we should see some dly vars since reorder is disabled. # (Whereas our twin test, t_alw_reorder, should see no dly vars # since it enables the reorder step.) -file_grep("$Self->{obj_dir}/$Self->{VM_PREFIX}___024root.cpp", qr/dly__t__DOT__v1/i); -file_grep("$Self->{obj_dir}/$Self->{VM_PREFIX}___024root.cpp", qr/dly__t__DOT__v2/i); +my @files = glob_all("$Self->{obj_dir}/$Self->{VM_PREFIX}___024root*.cpp"); +file_grep_any(\@files, qr/dly__t__DOT__v1/i); +file_grep_any(\@files, qr/dly__t__DOT__v2/i); execute( check_finished=>1, diff --git a/test_regress/t/t_alw_reorder.pl b/test_regress/t/t_alw_reorder.pl index 1335722ca..dce2f101f 100755 --- a/test_regress/t/t_alw_reorder.pl +++ b/test_regress/t/t_alw_reorder.pl @@ -18,8 +18,10 @@ file_grep($Self->{stats}, qr/Optimizations, Split always\s+(\d+)/i, 0); # Important: if reorder succeeded, we should see no dly vars. # Equally important: twin test t_alw_noreorder should see dly vars, # is identical to this test except for disabling the reorder step. -foreach my $file ("$Self->{obj_dir}/$Self->{VM_PREFIX}.cpp", - "$Self->{obj_dir}/$Self->{VM_PREFIX}.h") { +foreach my $file ( + glob_all("$Self->{obj_dir}/$Self->{VM_PREFIX}*.h"), + glob_all("$Self->{obj_dir}/$Self->{VM_PREFIX}*.cpp") + ) { file_grep_not($file, qr/dly__t__DOT__v1/i); file_grep_not($file, qr/dly__t__DOT__v2/i); file_grep_not($file, qr/dly__t__DOT__v3/i); diff --git a/test_regress/t/t_c_this.pl b/test_regress/t/t_c_this.pl index 37616efd7..b8edcffb4 100755 --- a/test_regress/t/t_c_this.pl +++ b/test_regress/t/t_c_this.pl @@ -15,7 +15,7 @@ compile(); if ($Self->{vlt_all}) { # The word 'this' (but only the whole word 'this' should have been replaced # in the contents. - my $file = "$Self->{obj_dir}/$Self->{VM_PREFIX}___024root.cpp"; + my $file = glob_one("$Self->{obj_dir}/$Self->{VM_PREFIX}___024root__DepSet_*__0.cpp"); my $text = file_contents($file); error("$file has 'this->clk'") if ($text =~ m/\bthis->clk\b/); error("$file does not have 'xthis'") if ($text !~ m/\bxthis\b/); diff --git a/test_regress/t/t_flag_comp_limit_parens.pl b/test_regress/t/t_flag_comp_limit_parens.pl index 11731a873..51fb7f932 100755 --- a/test_regress/t/t_flag_comp_limit_parens.pl +++ b/test_regress/t/t_flag_comp_limit_parens.pl @@ -18,7 +18,7 @@ execute( check_finished => 1, ); -file_grep("$Self->{obj_dir}/Vt_flag_comp_limit_parens___024root__Slow.cpp", qr/Vdeeptemp/x); +file_grep(glob_one("$Self->{obj_dir}/Vt_flag_comp_limit_parens___024root__DepSet_*__0__Slow.cpp"), qr/Vdeeptemp/x); ok(1); 1; diff --git a/test_regress/t/t_flag_csplit_off.pl b/test_regress/t/t_flag_csplit_off.pl index d29b8f2ef..8e3014653 100755 --- a/test_regress/t/t_flag_csplit_off.pl +++ b/test_regress/t/t_flag_csplit_off.pl @@ -61,7 +61,7 @@ while (1) { sub check_no_splits { foreach my $file (glob("$Self->{obj_dir}/*.cpp")) { $file =~ s/__024root//; - if ($file =~ qr/__\d/) { + if ($file =~ qr/__[1-9]/) { error("Split file found: $file"); } } diff --git a/test_regress/t/t_flag_xinitial_unique.pl b/test_regress/t/t_flag_xinitial_unique.pl index 572880d86..f2451bd3f 100755 --- a/test_regress/t/t_flag_xinitial_unique.pl +++ b/test_regress/t/t_flag_xinitial_unique.pl @@ -18,7 +18,7 @@ execute( check_finished => 1, ); -file_grep("$Self->{obj_dir}/$Self->{VM_PREFIX}___024root__Slow.cpp", qr/VL_RAND_RESET/); +file_grep(glob_one("$Self->{obj_dir}/$Self->{VM_PREFIX}___024root__DepSet_*__0__Slow.cpp"), qr/VL_RAND_RESET/); ok(1); 1; diff --git a/test_regress/t/t_foreach.pl b/test_regress/t/t_foreach.pl index 06fc908f0..5c5b93dd0 100755 --- a/test_regress/t/t_foreach.pl +++ b/test_regress/t/t_foreach.pl @@ -20,14 +20,16 @@ execute( # We expect all loops should be unrolled by verilator, # none of the loop variables should exist in the output: -file_grep_not("$Self->{obj_dir}/$Self->{VM_PREFIX}___024root.cpp", qr/index_/); -file_grep_not("$Self->{obj_dir}/$Self->{VM_PREFIX}___024root__Slow.cpp", qr/index_/); +for my $file (glob_all("$Self->{obj_dir}/$Self->{VM_PREFIX}*.cpp")) { + file_grep_not($file, qr/index_/); +} # Further, we expect that all logic within the loop should # have been evaluated inside the compiler. So there should be # no references to 'sum' in the .cpp. -file_grep_not("$Self->{obj_dir}/$Self->{VM_PREFIX}___024root.cpp", qr/sum/); -file_grep_not("$Self->{obj_dir}/$Self->{VM_PREFIX}___024root__Slow.cpp", qr/sum/); +for my $file (glob_all("$Self->{obj_dir}/$Self->{VM_PREFIX}*.cpp")) { + file_grep_not($file, qr/sum/); +} ok(1); 1; diff --git a/test_regress/t/t_inst_tree_inl0_pub1.pl b/test_regress/t/t_inst_tree_inl0_pub1.pl index 3bf53b1f1..effe5ebbe 100755 --- a/test_regress/t/t_inst_tree_inl0_pub1.pl +++ b/test_regress/t/t_inst_tree_inl0_pub1.pl @@ -23,17 +23,18 @@ sub checkRelativeRefs { my ($mod, $expect_relative) = @_; my $found_relative = 0; - my $file = "$Self->{obj_dir}/V$Self->{name}_${mod}.cpp"; - my $text = file_contents($file); + foreach my $file (glob_all("$Self->{obj_dir}/V$Self->{name}_${mod}*.cpp")) { + my $text = file_contents($file); - if ($text =~ m/this->/ || $text =~ m/vlSelf->/) { - $found_relative = 1; - } + if ($text =~ m/this->/ || $text =~ m/vlSelf->/) { + $found_relative = 1; + } - if ($found_relative != $expect_relative) { - error("$file " . - ($found_relative ? "has" : "does not have") . - " relative variable references."); + if ($found_relative != $expect_relative) { + error("$file " . + ($found_relative ? "has" : "does not have") . + " relative variable references."); + } } } diff --git a/test_regress/t/t_optm_if_array.pl b/test_regress/t/t_optm_if_array.pl index 2b2ca158f..aa9716f06 100755 --- a/test_regress/t/t_optm_if_array.pl +++ b/test_regress/t/t_optm_if_array.pl @@ -17,8 +17,9 @@ execute( check_finished => 1, ); -file_grep_not("$Self->{obj_dir}/$Self->{VM_PREFIX}___024root.cpp", qr/rstn_r/); -file_grep_not("$Self->{obj_dir}/$Self->{VM_PREFIX}___024root__Slow.cpp", qr/rstn_r/); +for my $file (glob_all("$Self->{obj_dir}/$Self->{VM_PREFIX}___024root*.cpp")) { + file_grep_not($file, qr/rstn_r/); +} ok(1); 1; diff --git a/test_regress/t/t_optm_redor.pl b/test_regress/t/t_optm_redor.pl index 2b2ca158f..aa9716f06 100755 --- a/test_regress/t/t_optm_redor.pl +++ b/test_regress/t/t_optm_redor.pl @@ -17,8 +17,9 @@ execute( check_finished => 1, ); -file_grep_not("$Self->{obj_dir}/$Self->{VM_PREFIX}___024root.cpp", qr/rstn_r/); -file_grep_not("$Self->{obj_dir}/$Self->{VM_PREFIX}___024root__Slow.cpp", qr/rstn_r/); +for my $file (glob_all("$Self->{obj_dir}/$Self->{VM_PREFIX}___024root*.cpp")) { + file_grep_not($file, qr/rstn_r/); +} ok(1); 1; diff --git a/test_regress/t/t_trace_ena_cc.pl b/test_regress/t/t_trace_ena_cc.pl index ff3debaf3..ea30e0880 100755 --- a/test_regress/t/t_trace_ena_cc.pl +++ b/test_regress/t/t_trace_ena_cc.pl @@ -21,8 +21,8 @@ execute( ); if ($Self->{vlt_all}) { - file_grep ("$Self->{obj_dir}/V$Self->{name}__Trace__Slow.cpp", qr/c_trace_on\"/x); - file_grep_not ("$Self->{obj_dir}/V$Self->{name}__Trace__Slow.cpp", qr/_trace_off\"/x); + file_grep ("$Self->{obj_dir}/V$Self->{name}__Trace__0__Slow.cpp", qr/c_trace_on\"/x); + file_grep_not ("$Self->{obj_dir}/V$Self->{name}__Trace__0__Slow.cpp", qr/_trace_off\"/x); file_grep ("$Self->{obj_dir}/simx.vcd", qr/\$enddefinitions/x); file_grep_not ("$Self->{obj_dir}/simx.vcd", qr/inside_sub/x); From fa64f8fb32063610d93743948a1f388beaea1327 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Thu, 22 Jul 2021 20:05:54 -0400 Subject: [PATCH 38/84] Internals: C++11 cleanup. No functional change intended. --- src/V3Changed.cpp | 52 ++++++++++++++++++++++---------------------- src/V3EmitCModel.cpp | 2 +- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/V3Changed.cpp b/src/V3Changed.cpp index 62957bc21..062da8eac 100644 --- a/src/V3Changed.cpp +++ b/src/V3Changed.cpp @@ -59,29 +59,29 @@ public: } if (!m_chgFuncp || v3Global.opt.outputSplitCFuncs() < m_numStmts) { m_chgFuncp - = new AstCFunc(m_scopetopp->fileline(), "_change_request_" + cvtToStr(++m_funcNum), - m_scopetopp, "QData"); + = new AstCFunc{m_scopetopp->fileline(), "_change_request_" + cvtToStr(++m_funcNum), + m_scopetopp, "QData"}; m_chgFuncp->isStatic(false); m_chgFuncp->isLoose(true); m_chgFuncp->declPrivate(true); m_scopetopp->addActivep(m_chgFuncp); // Add a top call to it - AstCCall* callp = new AstCCall(m_scopetopp->fileline(), m_chgFuncp); + AstCCall* const callp = new AstCCall{m_scopetopp->fileline(), m_chgFuncp}; if (!m_tlChgFuncp->stmtsp()) { - m_tlChgFuncp->addStmtsp(new AstCReturn(m_scopetopp->fileline(), callp)); + m_tlChgFuncp->addStmtsp(new AstCReturn{m_scopetopp->fileline(), callp}); } else { - AstCReturn* returnp = VN_CAST(m_tlChgFuncp->stmtsp(), CReturn); + AstCReturn* const returnp = VN_CAST(m_tlChgFuncp->stmtsp(), CReturn); UASSERT_OBJ(returnp, m_scopetopp, "Lost CReturn in top change function"); // This is currently using AstLogOr which will shortcut the // evaluation if any function returns true. This is likely what // we want and is similar to the logic already in use inside // V3EmitC, however, it also means that verbose logging may // miss to print change detect variables. - AstNode* newp = new AstCReturn( + AstNode* const newp = new AstCReturn{ m_scopetopp->fileline(), - new AstLogOr(m_scopetopp->fileline(), callp, returnp->lhsp()->unlinkFrBack())); + new AstLogOr{m_scopetopp->fileline(), callp, returnp->lhsp()->unlinkFrBack()}}; returnp->replaceWith(newp); VL_DO_DANGLING(returnp->deleteTree(), returnp); } @@ -123,11 +123,11 @@ private: } m_statep->maybeCreateChgFuncp(); - AstChangeDet* changep = new AstChangeDet(m_vscp->fileline(), m_varEqnp->cloneTree(true), - m_newRvEqnp->cloneTree(true), false); + AstChangeDet* const changep = new AstChangeDet{ + m_vscp->fileline(), m_varEqnp->cloneTree(true), m_newRvEqnp->cloneTree(true), false}; m_statep->m_chgFuncp->addStmtsp(changep); - AstAssign* initp = new AstAssign(m_vscp->fileline(), m_newLvEqnp->cloneTree(true), - m_varEqnp->cloneTree(true)); + AstAssign* const initp = new AstAssign{m_vscp->fileline(), m_newLvEqnp->cloneTree(true), + m_varEqnp->cloneTree(true)}; m_statep->m_chgFuncp->addFinalsp(initp); EmitCBaseCounterVisitor visitor{initp}; m_statep->m_numStmts += visitor.count(); @@ -145,11 +145,11 @@ private: VL_RESTORER(m_newLvEqnp); VL_RESTORER(m_newRvEqnp); { - m_varEqnp = new AstArraySel(nodep->fileline(), m_varEqnp->cloneTree(true), index); + m_varEqnp = new AstArraySel{nodep->fileline(), m_varEqnp->cloneTree(true), index}; m_newLvEqnp - = new AstArraySel(nodep->fileline(), m_newLvEqnp->cloneTree(true), index); + = new AstArraySel{nodep->fileline(), m_newLvEqnp->cloneTree(true), index}; m_newRvEqnp - = new AstArraySel(nodep->fileline(), m_newRvEqnp->cloneTree(true), index); + = new AstArraySel{nodep->fileline(), m_newRvEqnp->cloneTree(true), index}; iterate(nodep->subDTypep()->skipRefp()); @@ -184,22 +184,22 @@ public: m_vscp = vscp; m_detects = 0; { - AstVar* varp = m_vscp->varp(); - string newvarname - = ("__Vchglast__" + m_vscp->scopep()->nameDotless() + "__" + varp->shortName()); + AstVar* const varp = m_vscp->varp(); + string newvarname{"__Vchglast__" + m_vscp->scopep()->nameDotless() + "__" + + varp->shortName()}; // Create: VARREF(_last) // ASSIGN(VARREF(_last), VARREF(var)) // ... // CHANGEDET(VARREF(_last), VARREF(var)) - AstVar* newvarp - = new AstVar(varp->fileline(), AstVarType::MODULETEMP, newvarname, varp); + AstVar* const newvarp + = new AstVar{varp->fileline(), AstVarType::MODULETEMP, newvarname, varp}; m_statep->m_topModp->addStmtp(newvarp); - m_newvscp = new AstVarScope(m_vscp->fileline(), m_statep->m_scopetopp, newvarp); + m_newvscp = new AstVarScope{m_vscp->fileline(), m_statep->m_scopetopp, newvarp}; m_statep->m_scopetopp->addVarp(m_newvscp); - m_varEqnp = new AstVarRef(m_vscp->fileline(), m_vscp, VAccess::READ); - m_newLvEqnp = new AstVarRef(m_vscp->fileline(), m_newvscp, VAccess::WRITE); - m_newRvEqnp = new AstVarRef(m_vscp->fileline(), m_newvscp, VAccess::READ); + m_varEqnp = new AstVarRef{m_vscp->fileline(), m_vscp, VAccess::READ}; + m_newLvEqnp = new AstVarRef{m_vscp->fileline(), m_newvscp, VAccess::WRITE}; + m_newRvEqnp = new AstVarRef{m_vscp->fileline(), m_newvscp, VAccess::READ}; } iterate(vscp->dtypep()->skipRefp()); m_varEqnp->deleteTree(); @@ -243,13 +243,13 @@ private: // Clearing AstNode::user1ClearTree(); // Create the change detection function - AstScope* scopep = nodep->scopep(); + AstScope* const scopep = nodep->scopep(); UASSERT_OBJ(scopep, nodep, "No scope found on top level, perhaps you have no statements?"); m_statep->m_scopetopp = scopep; // Create a wrapper change detection function that calls each change detection function m_statep->m_tlChgFuncp - = new AstCFunc(nodep->fileline(), "_change_request", scopep, "QData"); + = new AstCFunc{nodep->fileline(), "_change_request", scopep, "QData"}; m_statep->m_tlChgFuncp->isStatic(false); m_statep->m_tlChgFuncp->isLoose(true); m_statep->m_tlChgFuncp->declPrivate(true); @@ -258,7 +258,7 @@ private: // to ensure that V3EmitC outputs the necessary code. m_statep->maybeCreateChgFuncp(); m_statep->m_chgFuncp->addStmtsp( - new AstChangeDet(nodep->fileline(), nullptr, nullptr, false)); + new AstChangeDet{nodep->fileline(), nullptr, nullptr, false}); iterateChildren(nodep); } diff --git a/src/V3EmitCModel.cpp b/src/V3EmitCModel.cpp index d0c6ccb83..5b3922e6d 100644 --- a/src/V3EmitCModel.cpp +++ b/src/V3EmitCModel.cpp @@ -274,7 +274,7 @@ class EmitCModel final : public EmitCFunc { const int vecnum = vects++; UASSERT_OBJ(arrayp->hi() >= arrayp->lo(), varp, "Should have swapped msb & lsb earlier."); - const string ivar = string("__Vi") + cvtToStr(vecnum); + const string ivar = std::string{"__Vi"} + cvtToStr(vecnum); puts("for (int __Vi" + cvtToStr(vecnum) + "=" + cvtToStr(arrayp->lo())); puts("; " + ivar + "<=" + cvtToStr(arrayp->hi())); From fc25721e55f8d555fc4bc85620d228fdcc8e128b Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Thu, 22 Jul 2021 20:13:45 -0400 Subject: [PATCH 39/84] Increase instruction count of ChangeDet (#3072) --- src/V3AstNodes.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index e85401a07..55239b529 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -4736,7 +4736,7 @@ public: bool isClockReq() const { return m_clockReq; } virtual bool isGateOptimizable() const override { return false; } virtual bool isPredictOptimizable() const override { return false; } - virtual int instrCount() const override { return widthInstrs(); } + virtual int instrCount() const override { return widthInstrs() * 2; } // xor, or/logor virtual bool same(const AstNode* samep) const override { return true; } }; From 13933743ade09175723e49f3c2df20e4d055adb1 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Thu, 22 Jul 2021 20:50:03 -0400 Subject: [PATCH 40/84] Suppress creating change_request if not needed. --- src/V3AstNodes.h | 3 ++ src/V3Changed.cpp | 37 +++++++++------- src/V3EmitCFunc.cpp | 2 +- src/V3EmitCModel.cpp | 63 ++++++++++++++++------------ test_regress/t/t_protect_ids_key.out | 2 - test_regress/t/t_verilated_debug.out | 6 --- 6 files changed, 63 insertions(+), 50 deletions(-) diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index 55239b529..580e34c08 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -9155,6 +9155,7 @@ private: AstExecGraph* m_execGraphp = nullptr; // Execution MTask graph for threads>1 mode VTimescale m_timeunit; // Global time unit VTimescale m_timeprecision; // Global time precision + bool m_changeRequest = false; // Have _change_request method bool m_timescaleSpecified = false; // Input HDL specified timescale public: AstNetlist(); @@ -9178,6 +9179,8 @@ public: AstNode* miscsp() const { return op3p(); } // op3 = List of dtypes etc void addMiscsp(AstNode* nodep) { addOp3p(nodep); } AstTypeTable* typeTablep() { return m_typeTablep; } + void changeRequest(bool specified) { m_changeRequest = specified; } + bool changeRequest() const { return m_changeRequest; } AstConstPool* constPoolp() { return m_constPoolp; } AstPackage* dollarUnitPkgp() const { return m_dollarUnitPkgp; } AstPackage* dollarUnitPkgAddp() { diff --git a/src/V3Changed.cpp b/src/V3Changed.cpp index 062da8eac..dc3343f87 100644 --- a/src/V3Changed.cpp +++ b/src/V3Changed.cpp @@ -47,11 +47,33 @@ public: AstCFunc* m_tlChgFuncp = nullptr; // Top level change function we're building int m_numStmts = 0; // Number of statements added to m_chgFuncp int m_funcNum = 0; // Number of change functions emitted + bool m_madeTopChg = false; ChangedState() = default; ~ChangedState() = default; void maybeCreateChgFuncp() { + maybeCreateTopChg(); + maybeCreateMidChg(); + } + void maybeCreateTopChg() { + if (m_madeTopChg) return; + m_madeTopChg = true; + v3Global.rootp()->changeRequest(true); + + // Create a wrapper change detection function that calls each change detection function + m_tlChgFuncp + = new AstCFunc{m_scopetopp->fileline(), "_change_request", m_scopetopp, "QData"}; + m_tlChgFuncp->isStatic(false); + m_tlChgFuncp->isLoose(true); + m_tlChgFuncp->declPrivate(true); + m_scopetopp->addActivep(m_tlChgFuncp); + // Each change detection function needs at least one AstChangeDet + // to ensure that V3EmitC outputs the necessary code. + maybeCreateMidChg(); + m_chgFuncp->addStmtsp(new AstChangeDet{m_scopetopp->fileline(), nullptr, nullptr, false}); + } + void maybeCreateMidChg() { // Don't create an extra function call if splitting is disabled if (!v3Global.opt.outputSplitCFuncs()) { m_chgFuncp = m_tlChgFuncp; @@ -242,24 +264,11 @@ private: UINFO(4, " TS " << nodep << endl); // Clearing AstNode::user1ClearTree(); - // Create the change detection function + // Prep for if make change detection function AstScope* const scopep = nodep->scopep(); UASSERT_OBJ(scopep, nodep, "No scope found on top level, perhaps you have no statements?"); m_statep->m_scopetopp = scopep; - // Create a wrapper change detection function that calls each change detection function - m_statep->m_tlChgFuncp - = new AstCFunc{nodep->fileline(), "_change_request", scopep, "QData"}; - m_statep->m_tlChgFuncp->isStatic(false); - m_statep->m_tlChgFuncp->isLoose(true); - m_statep->m_tlChgFuncp->declPrivate(true); - m_statep->m_scopetopp->addActivep(m_statep->m_tlChgFuncp); - // Each change detection function needs at least one AstChangeDet - // to ensure that V3EmitC outputs the necessary code. - m_statep->maybeCreateChgFuncp(); - m_statep->m_chgFuncp->addStmtsp( - new AstChangeDet{nodep->fileline(), nullptr, nullptr, false}); - iterateChildren(nodep); } virtual void visit(AstVarScope* nodep) override { diff --git a/src/V3EmitCFunc.cpp b/src/V3EmitCFunc.cpp index 5729c51e2..ed130c578 100644 --- a/src/V3EmitCFunc.cpp +++ b/src/V3EmitCFunc.cpp @@ -772,7 +772,7 @@ void EmitCFunc::emitChangeDet() { puts("QData __req = false; // Logically a bool\n"); // But not because it results in // faster code bool gotOne = false; - for (AstChangeDet* changep : m_blkChangeDetVec) { + for (AstChangeDet* const changep : m_blkChangeDetVec) { if (changep->lhsp()) { if (!gotOne) { // Not a clocked block puts("__req |= ("); diff --git a/src/V3EmitCModel.cpp b/src/V3EmitCModel.cpp index 5b3922e6d..965cf56c0 100644 --- a/src/V3EmitCModel.cpp +++ b/src/V3EmitCModel.cpp @@ -312,8 +312,10 @@ class EmitCModel final : public EmitCFunc { const string topModNameProtected = prefixNameProtect(modp); putsDecoration("// Evaluate till stable\n"); - puts("int __VclockLoop = 0;\n"); - puts("QData __Vchange = 1;\n"); + if (v3Global.rootp()->changeRequest()) { + puts("int __VclockLoop = 0;\n"); + puts("QData __Vchange = 1;\n"); + } if (v3Global.opt.trace()) puts("vlSymsp->__Vm_activity = true;\n"); puts("do {\n"); puts("VL_DEBUG_IF(VL_DBG_MSGF(\"+ "); @@ -322,29 +324,34 @@ class EmitCModel final : public EmitCFunc { if (initial) puts(topModNameProtected + "__" + protect("_eval_settle") + "(&(vlSymsp->TOP));\n"); puts(topModNameProtected + "__" + protect("_eval") + "(&(vlSymsp->TOP));\n"); - puts("if (VL_UNLIKELY(++__VclockLoop > " + cvtToStr(v3Global.opt.convergeLimit()) - + ")) {\n"); - puts("// About to fail, so enable debug to see what's not settling.\n"); - puts("// Note you must run make with OPT=-DVL_DEBUG for debug prints.\n"); - puts("int __Vsaved_debug = Verilated::debug();\n"); - puts("Verilated::debug(1);\n"); - puts("__Vchange = " + topModNameProtected + "__" + protect("_change_request") - + "(&(vlSymsp->TOP));\n"); - puts("Verilated::debug(__Vsaved_debug);\n"); - puts("VL_FATAL_MT("); - putsQuoted(protect(modp->fileline()->filename())); - puts(", "); - puts(cvtToStr(modp->fileline()->lineno())); - puts(", \"\",\n"); - puts("\"Verilated model didn't "); - if (initial) puts("DC "); - puts("converge\\n\"\n"); - puts("\"- See https://verilator.org/warn/DIDNOTCONVERGE\");\n"); - puts("} else {\n"); - puts("__Vchange = " + topModNameProtected + "__" + protect("_change_request") - + "(&(vlSymsp->TOP));\n"); - puts("}\n"); - puts("} while (VL_UNLIKELY(__Vchange));\n"); + if (v3Global.rootp()->changeRequest()) { + puts("if (VL_UNLIKELY(++__VclockLoop > " + cvtToStr(v3Global.opt.convergeLimit()) + + ")) {\n"); + puts("// About to fail, so enable debug to see what's not settling.\n"); + puts("// Note you must run make with OPT=-DVL_DEBUG for debug prints.\n"); + puts("int __Vsaved_debug = Verilated::debug();\n"); + puts("Verilated::debug(1);\n"); + puts("__Vchange = " + topModNameProtected + "__" + protect("_change_request") + + "(&(vlSymsp->TOP));\n"); + puts("Verilated::debug(__Vsaved_debug);\n"); + puts("VL_FATAL_MT("); + putsQuoted(protect(modp->fileline()->filename())); + puts(", "); + puts(cvtToStr(modp->fileline()->lineno())); + puts(", \"\",\n"); + puts("\"Verilated model didn't "); + if (initial) puts("DC "); + puts("converge\\n\"\n"); + puts("\"- See https://verilator.org/warn/DIDNOTCONVERGE\");\n"); + puts("} else {\n"); + puts("__Vchange = " + topModNameProtected + "__" + protect("_change_request") + + "(&(vlSymsp->TOP));\n"); + puts("}\n"); + } + puts("} while (" + + (v3Global.rootp()->changeRequest() ? std::string{"VL_UNLIKELY(__Vchange)"} + : std::string{"0"}) + + ");\n"); } void emitStandardMethods(AstNodeModule* modp) { @@ -360,8 +367,10 @@ class EmitCModel final : public EmitCFunc { puts("void " + topModNameProtected + "__" + protect("_eval_initial") + selfDecl + ";\n"); puts("void " + topModNameProtected + "__" + protect("_eval_settle") + selfDecl + ";\n"); puts("void " + topModNameProtected + "__" + protect("_eval") + selfDecl + ";\n"); - puts("QData " + topModNameProtected + "__" + protect("_change_request") + selfDecl - + ";\n"); + if (v3Global.rootp()->changeRequest()) { + puts("QData " + topModNameProtected + "__" + protect("_change_request") + selfDecl + + ";\n"); + } puts("#ifdef VL_DEBUG\n"); puts("void " + topModNameProtected + "__" + protect("_eval_debug_assertions") + selfDecl + ";\n"); diff --git a/test_regress/t/t_protect_ids_key.out b/test_regress/t/t_protect_ids_key.out index 7a7d53d4b..4575b30b0 100644 --- a/test_regress/t/t_protect_ids_key.out +++ b/test_regress/t/t_protect_ids_key.out @@ -22,8 +22,6 @@ - - diff --git a/test_regress/t/t_verilated_debug.out b/test_regress/t/t_verilated_debug.out index 5ac844a6b..932ee8483 100644 --- a/test_regress/t/t_verilated_debug.out +++ b/test_regress/t/t_verilated_debug.out @@ -13,18 +13,12 @@ internalsDump: -V{t#,#}+ Initial loop -V{t#,#}+ Vt_verilated_debug___024root___eval_settle -V{t#,#}+ Vt_verilated_debug___024root___eval --V{t#,#}+ Vt_verilated_debug___024root___change_request --V{t#,#}+ Vt_verilated_debug___024root___change_request_1 -V{t#,#}+ Clock loop -V{t#,#}+ Vt_verilated_debug___024root___eval --V{t#,#}+ Vt_verilated_debug___024root___change_request --V{t#,#}+ Vt_verilated_debug___024root___change_request_1 -V{t#,#}+++++TOP Evaluate Vt_verilated_debug::eval_step -V{t#,#}+ Vt_verilated_debug___024root___eval_debug_assertions -V{t#,#}+ Clock loop -V{t#,#}+ Vt_verilated_debug___024root___eval -V{t#,#}+ Vt_verilated_debug___024root___sequent__TOP__2 *-* All Finished *-* --V{t#,#}+ Vt_verilated_debug___024root___change_request --V{t#,#}+ Vt_verilated_debug___024root___change_request_1 -V{t#,#}+ Vt_verilated_debug___024root___final From 9907d211ff5fa408a7eb6387ef0ceaedaeea2d32 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Fri, 23 Jul 2021 12:57:45 +0100 Subject: [PATCH 41/84] Make VlWide and VlUnpacked C++20 compatible aggregates. C++20 requires that aggregate types do not have a user declared constructor, not even an explicitly defaulted one. We need these types to be aggregates for static initialization. Fixes #3076. --- include/verilated_heavy.h | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/include/verilated_heavy.h b/include/verilated_heavy.h index a0f8107bd..150991a19 100644 --- a/include/verilated_heavy.h +++ b/include/verilated_heavy.h @@ -107,15 +107,12 @@ template struct VlWide final { EData m_storage[T_Words]; // Contents of the packed array // CONSTRUCTORS - // cppcheck-suppress uninitVar - VlWide() = default; - ~VlWide() = default; - VlWide(const VlWide&) = default; - VlWide(VlWide&&) = default; + // Default constructors and destructor are used. Note however that C++20 requires that + // aggregate types do not have a user declared constructor, not even an explicitly defaulted + // one. // OPERATOR METHODS - VlWide& operator=(const VlWide&) = default; - VlWide& operator=(VlWide&&) = default; + // Default copy assignment operators are used. operator WDataOutP() { return &m_storage[0]; } // This also allows [] operator WDataInP() const { return &m_storage[0]; } // This also allows [] @@ -818,12 +815,12 @@ template struct VlUnpacked final { T_Value m_storage[T_Depth]; // Contents of the unpacked array // CONSTRUCTORS - VlUnpacked() = default; - ~VlUnpacked() = default; - VlUnpacked(const VlUnpacked&) = default; - VlUnpacked(VlUnpacked&&) = default; - VlUnpacked& operator=(const VlUnpacked&) = default; - VlUnpacked& operator=(VlUnpacked&&) = default; + // Default constructors and destructor are used. Note however that C++20 requires that + // aggregate types do not have a user declared constructor, not even an explicitly defaulted + // one. + + // OPERATOR METHODS + // Default copy assignment operators are used. // METHODS // Raw access From 4ab4c0c8baa14dca1bc926b1b4ff02eb4b4f16a5 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Thu, 22 Jul 2021 18:59:03 +0100 Subject: [PATCH 42/84] Emit parameter values as 'static constexpr' instead of enum All parameters that are required in the output are now emitted as 'static constexpr, except for string or array of strings parameters, which are still emitted as 'static const' (required as std::string is not a literal type, so cannot be constexpr). This simplifies handling of parameters and supports 'real' parameters. --- Changes | 2 ++ src/V3Ast.h | 20 +++++++++++ src/V3AstNodes.cpp | 14 ++++++++ src/V3CCtors.cpp | 3 +- src/V3EmitCFunc.h | 17 ++++++++-- src/V3EmitCHeaders.cpp | 35 ++++++-------------- src/V3EmitCImp.cpp | 42 +++++++++++------------ src/V3EmitCSyms.cpp | 9 ++--- src/V3Param.cpp | 11 +----- test_regress/t/t_param_array7.pl | 21 ++++++++++++ test_regress/t/t_param_array7.v | 57 ++++++++++++++++++++++++++++++++ 11 files changed, 163 insertions(+), 68 deletions(-) create mode 100755 test_regress/t/t_param_array7.pl create mode 100644 test_regress/t/t_param_array7.v diff --git a/Changes b/Changes index a1151d3b5..e4ed2a737 100644 --- a/Changes +++ b/Changes @@ -16,6 +16,8 @@ Verilator 4.211 devel * Fix -G to treat simple integer literals as signed (#3060). [Anikin1610] * Output files are split based on the set of headers required in order to aid incremental compilation via ccache (#3071). [Geza Lore] +* Parameter values are now emitted as 'static constexpr' instead of enum. + C++ direct references to parameters might require updating (#3077). [Geza Lore] Verilator 4.210 2021-07-07 diff --git a/src/V3Ast.h b/src/V3Ast.h index 1afe34c83..eace39dce 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -529,6 +529,25 @@ public: bool isEventValue() const { return m_e == EVENTVALUE; } bool isString() const { return m_e == STRING; } bool isMTaskState() const { return m_e == MTASKSTATE; } + // Does this represent a C++ LiteralType? (can be constexpr) + bool isLiteralType() const { + switch (m_e) { + case BIT: + case BYTE: + case CHANDLE: + case INT: + case INTEGER: + case LOGIC: + case LONGINT: + case DOUBLE: + case SHORTINT: + case SCOPEPTR: + case CHARPTR: + case UINT32: + case UINT64: return true; + default: return false; + } + } }; inline bool operator==(const AstBasicDTypeKwd& lhs, const AstBasicDTypeKwd& rhs) { return lhs.m_e == rhs.m_e; @@ -2436,6 +2455,7 @@ public: return (isString() ? "N" : isWide() ? "W" : isQuad() ? "Q" : "I"); } string cType(const string& name, bool forFunc, bool isRef) const; + bool isLiteralType() const; // Does this represent a C++ LiteralType? (can be constexpr) private: class CTypeRecursed; diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index 52f5b6755..7fbc3be71 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -776,6 +776,20 @@ int AstNodeDType::widthPow2() const { return 1; } +bool AstNodeDType::isLiteralType() const { + if (auto* const dtypep = VN_CAST_CONST(skipRefp(), BasicDType)) { + return dtypep->keyword().isLiteralType(); + } else if (auto* const dtypep = VN_CAST_CONST(skipRefp(), UnpackArrayDType)) { + return dtypep->basicp()->isLiteralType(); + } else if (auto* const dtypep = VN_CAST_CONST(skipRefp(), StructDType)) { + // Currently all structs are packed, later this can be expanded to + // 'forall members _.isLiteralType()' + return dtypep->packed(); + } else { + return false; + } +} + /// What is the base variable (or const) this dereferences? AstNode* AstArraySel::baseFromp(AstNode* nodep, bool overMembers) { // Else AstArraySel etc; search for the base diff --git a/src/V3CCtors.cpp b/src/V3CCtors.cpp index d1b9daba0..40b328966 100644 --- a/src/V3CCtors.cpp +++ b/src/V3CCtors.cpp @@ -183,7 +183,8 @@ void V3CCtors::cctorsAll() { for (AstNode* np = modp->stmtsp(); np; np = np->nextp()) { if (AstVar* const varp = VN_CAST(np, Var)) { - if (!varp->isIfaceParent() && !varp->isIfaceRef() && !varp->noReset()) { + if (!varp->isIfaceParent() && !varp->isIfaceRef() && !varp->noReset() + && !varp->isParam()) { const auto vrefp = new AstVarRef{varp->fileline(), varp, VAccess::WRITE}; var_reset.add(new AstCReset{varp->fileline(), vrefp}); } diff --git a/src/V3EmitCFunc.h b/src/V3EmitCFunc.h index eb1aa4de3..fddf3e348 100644 --- a/src/V3EmitCFunc.h +++ b/src/V3EmitCFunc.h @@ -21,7 +21,7 @@ #include "verilatedos.h" #include "V3Global.h" -#include "V3EmitCBase.h" +#include "V3EmitCConstInit.h" #include #include @@ -113,13 +113,14 @@ public: //###################################################################### // Emit statements and math operators -class EmitCFunc VL_NOT_FINAL : public EmitCBaseVisitor { +class EmitCFunc VL_NOT_FINAL : public EmitCConstInit { private: AstVarRef* m_wideTempRefp; // Variable that _WW macros should be setting int m_labelNum; // Next label number int m_splitSize; // # of cfunc nodes placed into output file bool m_inUC = false; // Inside an AstUCStmt or AstUCMath std::vector m_blkChangeDetVec; // All encountered changes in block + bool m_emitConstInit = false; // Emitting constant initializer protected: EmitCLazyDecls m_lazyDecls; // Visitor for emitting lazy declarations @@ -178,8 +179,16 @@ public: AstNodeDType* dtypep, int depth, const string& suffix); void doubleOrDetect(AstChangeDet* changep, bool& gotOne); void emitChangeDet(); + void emitConstInit(AstNode* initp) { + // We should refactor emit to produce output into a provided buffer, not go through members + // variables. That way we could just invoke the appropriate emitter as needed. + VL_RESTORER(m_emitConstInit); + m_emitConstInit = true; + iterate(initp); + } // VISITORS + using EmitCConstInit::visit; virtual void visit(AstCFunc* nodep) override { VL_RESTORER(m_useSelfForThis); VL_RESTORER(m_cfuncp); @@ -1120,7 +1129,9 @@ public: puts(funcNameProtect(funcp)); } virtual void visit(AstConst* nodep) override { - if (nodep->isWide()) { + if (m_emitConstInit) { + EmitCConstInit::visit(nodep); + } else if (nodep->isWide()) { UASSERT_OBJ(m_wideTempRefp, nodep, "Wide Constant w/ no temp"); emitConstant(nodep, m_wideTempRefp, ""); m_wideTempRefp = nullptr; // We used it, barf if set it a second time diff --git a/src/V3EmitCHeaders.cpp b/src/V3EmitCHeaders.cpp index 6f230ac57..2ef0903c3 100644 --- a/src/V3EmitCHeaders.cpp +++ b/src/V3EmitCHeaders.cpp @@ -103,8 +103,7 @@ class EmitCHeader final : public EmitCConstInit { // Emit variables in consecutive anon and non-anon batches for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { if (const AstVar* const varp = VN_CAST_CONST(nodep, Var)) { - if (varp->isIO() || varp->isSignal() || varp->isClassMember() || varp->isTemp() - || (varp->isParam() && !VN_IS(varp->valuep(), Const))) { + if (varp->isIO() || varp->isSignal() || varp->isClassMember() || varp->isTemp()) { const bool anon = isAnonOk(varp); if (anon != lastAnon) emitCurrentList(); lastAnon = anon; @@ -126,31 +125,19 @@ class EmitCHeader final : public EmitCConstInit { bool first = true; for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { if (const AstVar* const varp = VN_CAST(nodep, Var)) { - if (varp->isParam() && (varp->isUsedParam() || varp->isSigPublic())) { + if (varp->isParam()) { decorateFirst(first, "\n// PARAMETERS\n"); UASSERT_OBJ(varp->valuep(), nodep, "No init for a param?"); - // These should be static const values, however older MSVC++ did't - // support them; should be ok now under C++11, need to refactor. - if (varp->isWide()) { // Unsupported for output - putsDecoration("// enum WData " + varp->nameProtect() + " //wide"); - } else if (varp->isString()) { - puts("static const std::string " + protect("var_" + varp->name()) + ";\n"); - } else if (!VN_IS(varp->valuep(), Const)) { // Unsupported for output - // putsDecoration("// enum ..... "+varp->nameProtect() - // +"not simple value, see variable above instead"); - } else if (VN_IS(varp->dtypep(), BasicDType) - && VN_CAST(varp->dtypep(), BasicDType) - ->isOpaque()) { // Can't put out e.g. doubles - } else { - // enum - puts(varp->isQuad() ? "enum _QData" : "enum _IData"); - puts("" + varp->nameProtect() + " { " + varp->nameProtect() + " = "); - iterateAndNextNull(varp->valuep()); - puts("};\n"); - // var - puts(varp->isQuad() ? "static const QData " : "static const IData "); - puts(protect("var_" + varp->name()) + ";\n"); + // Only C++ LiteralTypes can be constexpr + const bool canBeConstexpr = varp->dtypep()->isLiteralType(); + puts("static "); + puts(canBeConstexpr ? "constexpr " : "const "); + puts(varp->dtypep()->cType(varp->nameProtect(), false, false)); + if (canBeConstexpr) { + puts(" = "); + iterate(varp->valuep()); } + puts(";\n"); } } } diff --git a/src/V3EmitCImp.cpp b/src/V3EmitCImp.cpp index be6bca502..69b5f776e 100644 --- a/src/V3EmitCImp.cpp +++ b/src/V3EmitCImp.cpp @@ -202,35 +202,31 @@ class EmitCImp final : EmitCFunc { } } void emitParamDefns(const AstNodeModule* modp) { + const string modName = prefixNameProtect(modp); + bool first = true; for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { if (const AstVar* const varp = VN_CAST_CONST(nodep, Var)) { - if (varp->isParam() && (varp->isUsedParam() || varp->isSigPublic())) { - UASSERT_OBJ(varp->valuep(), nodep, "No init for a param?"); - // These should be static const values, however older MSVC++ did't - // support them; should be ok now under C++11, need to refactor. - if (varp->isWide()) { // Unsupported for output - } else if (varp->isString()) { - puts("const std::string "); - puts(prefixNameProtect(modp) + "::" + protect("var_" + varp->name()) - + "("); - iterateAndNextNull(varp->valuep()); - puts(");\n"); - } else if (!VN_IS(varp->valuep(), Const)) { // Unsupported for output - // putsDecoration("// enum ..... "+varp->nameProtect() - // +"not simple value, see variable above instead"); - } else if (VN_IS(varp->dtypep(), BasicDType) - && VN_CAST(varp->dtypep(), BasicDType) - ->isOpaque()) { // Can't put out e.g. doubles - } else { - puts(varp->isQuad() ? "const QData " : "const IData "); - puts(prefixNameProtect(modp) + "::" + protect("var_" + varp->name()) - + "("); - iterateAndNextNull(varp->valuep()); - puts(");\n"); + if (varp->isParam()) { + if (first) { + puts("\n"); + putsDecoration("// Parameter definitions for " + modName + "\n"); + first = false; } + UASSERT_OBJ(varp->valuep(), nodep, "No init for a param?"); + // Only C++ LiteralTypes can be constexpr + const bool canBeConstexpr = varp->dtypep()->isLiteralType(); + puts(canBeConstexpr ? "constexpr " : "const "); + const string scopedName = modName + "::" + varp->nameProtect(); + puts(varp->dtypep()->cType(scopedName, false, false)); + if (!canBeConstexpr) { + puts(" = "); + emitConstInit(varp->valuep()); + } + puts(";\n"); } } } + if (!first) puts("\n"); } void emitCtorImp(const AstNodeModule* modp) { const string modName = prefixNameProtect(modp); diff --git a/src/V3EmitCSyms.cpp b/src/V3EmitCSyms.cpp index 335f09426..7cd836c91 100644 --- a/src/V3EmitCSyms.cpp +++ b/src/V3EmitCSyms.cpp @@ -857,13 +857,8 @@ void EmitCSyms::emitSymImp() { putsQuoted(protect(it->second.m_varBasePretty)); std::string varName; - varName += (protectIf(scopep->nameDotless(), scopep->protect()) + "."); - - if (varp->isParam()) { - varName += protect("var_" + varp->name()); - } else { - varName += protect(varp->name()); - } + varName += protectIf(scopep->nameDotless(), scopep->protect()) + "."; + varName += protect(varp->name()); if (varp->isParam()) { if (varp->vlEnumType() == "VLVT_STRING") { diff --git a/src/V3Param.cpp b/src/V3Param.cpp index 9976028bb..3552d6a4a 100644 --- a/src/V3Param.cpp +++ b/src/V3Param.cpp @@ -962,17 +962,8 @@ class ParamVisitor final : public AstNVisitor { V3Const::constifyParamsEdit(nodep); // The variable, not just the var->init() if (!VN_IS(nodep->valuep(), Const) && !VN_IS(nodep->valuep(), Unbounded)) { // Complex init, like an array - // Make a new INITIAL to set the value. - // This allows the normal array/struct handling code to properly - // initialize the parameter. - nodep->addNext(new AstInitial( - nodep->fileline(), - new AstAssign(nodep->fileline(), - new AstVarRef(nodep->fileline(), nodep, VAccess::WRITE), - nodep->valuep()->cloneTree(true)))); if (nodep->isFuncLocal()) { - // We put the initial in wrong place under a function. We - // should move the parameter out of the function and to the + // We should move the parameter out of the function and to the // module, with appropriate dotting, but this confuses LinkDot // (as then name isn't found later), so punt - probably can // treat as static function variable when that is supported. diff --git a/test_regress/t/t_param_array7.pl b/test_regress/t/t_param_array7.pl new file mode 100755 index 000000000..b46d46042 --- /dev/null +++ b/test_regress/t/t_param_array7.pl @@ -0,0 +1,21 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 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 + +scenarios(simulator => 1); + +compile( + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_param_array7.v b/test_regress/t/t_param_array7.v new file mode 100644 index 000000000..72380d0a9 --- /dev/null +++ b/test_regress/t/t_param_array7.v @@ -0,0 +1,57 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2021 by Geza Lore. +// SPDX-License-Identifier: CC0-1.0 + +typedef struct packed { + longint a; + longint b; + longint c; +} s_t; + +module t; + localparam int c0 [4] = '{5, 6, 7, 8}; + localparam bit [255:0] c1 [4] = '{9, 10, 11, 12}; + localparam string c2 [2] = '{"baz", "quux"}; + localparam s_t c3 [2] = '{'{a: 100, b: 200, c: 300}, + '{a: 1000, b: 2000, c: 3000}}; + + a #( + .p0(c0), + .p1(c1), + .p2(c2), + .p3(c3) + ) i_a (); +endmodule + +module a + #( + parameter int p0 [4] = '{1, 2, 3, 4}, + parameter bit [255:0] p1 [4] = '{1, 2, 3, 4}, + parameter string p2 [2] = '{"foo", "bar"}, + parameter s_t p3 [2] = '{'{a: 1, b: 2, c: 3}, + '{a: 1, b: 2, c: 3}} + ); + initial begin + // Go via $c to ensure parameters are emitted + if (p0[$c("0")] != 5) $stop; + if (p0[$c("1")] != 6) $stop; + if (p0[$c("2")] != 7) $stop; + if (p0[$c("3")] != 8) $stop; + if (p1[$c("0")] != 9) $stop; + if (p1[$c("1")] != 10) $stop; + if (p1[$c("2")] != 11) $stop; + if (p1[$c("3")] != 12) $stop; + if (p2[$c("0")] != "baz") $stop; + if (p2[$c("1")] != "quux") $stop; + if (p3[$c("0")].a != 100) $stop; + if (p3[$c("0")].b != 200) $stop; + if (p3[$c("0")].c != 300) $stop; + if (p3[$c("1")].a != 1000) $stop; + if (p3[$c("1")].b != 2000) $stop; + if (p3[$c("1")].c != 3000) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule From 90c917ee836b95bd3ace99da6319d3b46fc07641 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Fri, 23 Jul 2021 17:16:10 +0100 Subject: [PATCH 43/84] Enable now supported tests for string array initializers Fixes #2895. --- Changes | 3 ++- test_regress/t/t_unpacked_str_init.pl | 3 +-- test_regress/t/t_unpacked_str_init2.out | 34 +++++++++++++++++++++++++ test_regress/t/t_unpacked_str_init2.pl | 8 +++--- 4 files changed, 42 insertions(+), 6 deletions(-) create mode 100644 test_regress/t/t_unpacked_str_init2.out diff --git a/Changes b/Changes index e4ed2a737..4b4f51fbd 100644 --- a/Changes +++ b/Changes @@ -13,11 +13,12 @@ Verilator 4.211 devel **Minor:** -* Fix -G to treat simple integer literals as signed (#3060). [Anikin1610] * Output files are split based on the set of headers required in order to aid incremental compilation via ccache (#3071). [Geza Lore] * Parameter values are now emitted as 'static constexpr' instead of enum. C++ direct references to parameters might require updating (#3077). [Geza Lore] +* Fix -G to treat simple integer literals as signed (#3060). [Anikin1610] +* Fix emitted string array initializers (#2895). [Iztok Jeras] Verilator 4.210 2021-07-07 diff --git a/test_regress/t/t_unpacked_str_init.pl b/test_regress/t/t_unpacked_str_init.pl index 235ca326a..baf0745f9 100755 --- a/test_regress/t/t_unpacked_str_init.pl +++ b/test_regress/t/t_unpacked_str_init.pl @@ -10,8 +10,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); -# TODO change to compile() -lint( +compile( ); # No execute, not self-checking diff --git a/test_regress/t/t_unpacked_str_init2.out b/test_regress/t/t_unpacked_str_init2.out new file mode 100644 index 000000000..349ba5028 --- /dev/null +++ b/test_regress/t/t_unpacked_str_init2.out @@ -0,0 +1,34 @@ +REGX: zero +REGX: ra +REGX: sp +REGX: gp +REGX: tp +REGX: t0 +REGX: t1 +REGX: t2 +REGX: s0/fp +REGX: s1 +REGX: a0 +REGX: a1 +REGX: a2 +REGX: a3 +REGX: a4 +REGX: a5 +REGX: a6 +REGX: a7 +REGX: s2 +REGX: s3 +REGX: s4 +REGX: s5 +REGX: s6 +REGX: s7 +REGX: s8 +REGX: s9 +REGX: s10 +REGX: s11 +REGX: t3 +REGX: t4 +REGX: t5 +REGX: t6 +OP: ILLEGAL +*-* All Finished *-* diff --git a/test_regress/t/t_unpacked_str_init2.pl b/test_regress/t/t_unpacked_str_init2.pl index 235ca326a..bd862f553 100755 --- a/test_regress/t/t_unpacked_str_init2.pl +++ b/test_regress/t/t_unpacked_str_init2.pl @@ -10,11 +10,13 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); -# TODO change to compile() -lint( +compile( ); -# No execute, not self-checking +execute( + check_finished => 1, + expect_filename => $Self->{golden_filename} + ); ok(1); 1; From 1de33b9fb763dd57fb62a2a7e50878d30dd2a654 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Thu, 22 Jul 2021 21:09:24 +0100 Subject: [PATCH 44/84] Support localparams in tasks/functions --- Changes | 1 + src/V3Param.cpp | 11 -- src/V3Task.cpp | 54 +++++---- test_regress/t/t_param_in_func.pl | 33 ++++++ test_regress/t/t_param_in_func.v | 130 +++++++++++++++++++++ test_regress/t/t_param_in_func_bad.out | 6 - test_regress/t/t_param_in_func_bad.pl | 19 --- test_regress/t/t_param_in_func_bad.v | 29 ----- test_regress/t/t_param_in_func_noinline.pl | 35 ++++++ 9 files changed, 233 insertions(+), 85 deletions(-) create mode 100755 test_regress/t/t_param_in_func.pl create mode 100644 test_regress/t/t_param_in_func.v delete mode 100644 test_regress/t/t_param_in_func_bad.out delete mode 100755 test_regress/t/t_param_in_func_bad.pl delete mode 100644 test_regress/t/t_param_in_func_bad.v create mode 100755 test_regress/t/t_param_in_func_noinline.pl diff --git a/Changes b/Changes index 4b4f51fbd..e2e7cf0da 100644 --- a/Changes +++ b/Changes @@ -13,6 +13,7 @@ Verilator 4.211 devel **Minor:** +* Support unpackes array localparams in tasks/functions (#3078). [Geza Lore] * Output files are split based on the set of headers required in order to aid incremental compilation via ccache (#3071). [Geza Lore] * Parameter values are now emitted as 'static constexpr' instead of enum. diff --git a/src/V3Param.cpp b/src/V3Param.cpp index 3552d6a4a..733cb33d1 100644 --- a/src/V3Param.cpp +++ b/src/V3Param.cpp @@ -960,17 +960,6 @@ class ParamVisitor final : public AstNVisitor { << " (IEEE 1800-2017 6.20.1): " << nodep->prettyNameQ()); } else { V3Const::constifyParamsEdit(nodep); // The variable, not just the var->init() - if (!VN_IS(nodep->valuep(), Const) - && !VN_IS(nodep->valuep(), Unbounded)) { // Complex init, like an array - if (nodep->isFuncLocal()) { - // We should move the parameter out of the function and to the - // module, with appropriate dotting, but this confuses LinkDot - // (as then name isn't found later), so punt - probably can - // treat as static function variable when that is supported. - nodep->v3warn(E_UNSUPPORTED, - "Unsupported: Parameters in functions with complex assign"); - } - } } } } diff --git a/src/V3Task.cpp b/src/V3Task.cpp index 9b2226468..3bf94dcb0 100644 --- a/src/V3Task.cpp +++ b/src/V3Task.cpp @@ -419,17 +419,22 @@ private: return newvscp; } AstVarScope* createVarScope(AstVar* invarp, const string& name) { - // We could create under either the ref's scope or the ftask's scope. - // It shouldn't matter, as they are only local variables. - // We choose to do it under whichever called this function, which results - // in more cache locality. - AstVar* newvarp = new AstVar(invarp->fileline(), AstVarType::BLOCKTEMP, name, invarp); - newvarp->funcLocal(false); - newvarp->propagateAttrFrom(invarp); - m_modp->addStmtp(newvarp); - AstVarScope* newvscp = new AstVarScope(newvarp->fileline(), m_scopep, newvarp); - m_scopep->addVarp(newvscp); - return newvscp; + if (invarp->isParam() && VN_IS(invarp->valuep(), InitArray)) { + // Move array params in functions into constant pool + return v3Global.rootp()->constPoolp()->findTable(VN_CAST(invarp->valuep(), InitArray)); + } else { + // We could create under either the ref's scope or the ftask's scope. + // It shouldn't matter, as they are only local variables. + // We choose to do it under whichever called this function, which results + // in more cache locality. + AstVar* newvarp = new AstVar{invarp->fileline(), AstVarType::BLOCKTEMP, name, invarp}; + newvarp->funcLocal(false); + newvarp->propagateAttrFrom(invarp); + m_modp->addStmtp(newvarp); + AstVarScope* newvscp = new AstVarScope{newvarp->fileline(), m_scopep, newvarp}; + m_scopep->addVarp(newvscp); + return newvscp; + } } AstNode* createInlinedFTask(AstNodeFTaskRef* refp, const string& namePrefix, @@ -1192,18 +1197,27 @@ private: for (AstNode *nextp, *stmtp = nodep->stmtsp(); stmtp; stmtp = nextp) { nextp = stmtp->nextp(); if (AstVar* portp = VN_CAST(stmtp, Var)) { - if (portp->isIO()) { - // Move it to new function + if (portp->isParam() && VN_IS(portp->valuep(), InitArray)) { + // Move array parameters in functions into constant pool portp->unlinkFrBack(); - portp->funcLocal(true); - cfuncp->addArgsp(portp); + pushDeletep(portp); + AstNode* const tablep = v3Global.rootp()->constPoolp()->findTable( + VN_CAST(portp->valuep(), InitArray)); + portp->user2p(tablep); } else { - // "Normal" variable, mark inside function - portp->funcLocal(true); + if (portp->isIO()) { + // Move it to new function + portp->unlinkFrBack(); + portp->funcLocal(true); + cfuncp->addArgsp(portp); + } else { + // "Normal" variable, mark inside function + portp->funcLocal(true); + } + AstVarScope* newvscp = new AstVarScope{portp->fileline(), m_scopep, portp}; + m_scopep->addVarp(newvscp); + portp->user2p(newvscp); } - AstVarScope* newvscp = new AstVarScope(portp->fileline(), m_scopep, portp); - m_scopep->addVarp(newvscp); - portp->user2p(newvscp); } } diff --git a/test_regress/t/t_param_in_func.pl b/test_regress/t/t_param_in_func.pl new file mode 100755 index 000000000..849ab9a69 --- /dev/null +++ b/test_regress/t/t_param_in_func.pl @@ -0,0 +1,33 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2019 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 + +scenarios(simulator => 1); + +compile( + verilator_flags2 => ["--stats"], + ); + +execute(check_finished => 1); + +# The parameter array should have been put in the constant pool +if ($Self->{vlt_all}) { + file_grep($Self->{stats}, qr/ConstPool, Tables emitted\s+(\d+)/i, 3); +} + +# Shouldn't have any references to the parameter array +foreach my $file ( + glob_all("$Self->{obj_dir}/$Self->{VM_PREFIX}*.h"), + glob_all("$Self->{obj_dir}/$Self->{VM_PREFIX}*.cpp") + ) { + file_grep_not($file, qr/digits/i); +} + +ok(1); +1; diff --git a/test_regress/t/t_param_in_func.v b/test_regress/t/t_param_in_func.v new file mode 100644 index 000000000..62e86b395 --- /dev/null +++ b/test_regress/t/t_param_in_func.v @@ -0,0 +1,130 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2019 by Driss Hafdi. +// SPDX-License-Identifier: CC0-1.0 + +module t (/*AUTOARG*/); + initial begin + if (getUnpacked($c("0")) != "0") $stop; + if (getUnpacked($c("1")) != "1") $stop; + if (getUnpacked($c("2")) != "2") $stop; + if (getUnpacked($c("3")) != "3") $stop; + if (getUnpacked($c("4")) != "4") $stop; + if (getUnpacked($c("5")) != "5") $stop; + if (getUnpacked($c("6")) != "6") $stop; + if (getUnpacked($c("7")) != "7") $stop; + if (getUnpacked($c("8")) != "8") $stop; + if (getUnpacked($c("9")) != "9") $stop; + + if (getPacked($c("0")) != "0") $stop; + if (getPacked($c("1")) != "1") $stop; + if (getPacked($c("2")) != "2") $stop; + if (getPacked($c("3")) != "3") $stop; + if (getPacked($c("4")) != "4") $stop; + if (getPacked($c("5")) != "5") $stop; + if (getPacked($c("6")) != "6") $stop; + if (getPacked($c("7")) != "7") $stop; + if (getPacked($c("8")) != "8") $stop; + if (getPacked($c("9")) != "9") $stop; + + if (getString($c("0")) != "0") $stop; + if (getString($c("1")) != "1") $stop; + if (getString($c("2")) != "2") $stop; + if (getString($c("3")) != "3") $stop; + if (getString($c("4")) != "4") $stop; + if (getString($c("5")) != "5") $stop; + if (getString($c("6")) != "6") $stop; + if (getString($c("7")) != "7") $stop; + if (getString($c("8")) != "8") $stop; + if (getString($c("9")) != "9") $stop; + + if (getStruct($c("0")) != "0") $stop; + if (getStruct($c("1")) != "1") $stop; + if (getStruct($c("2")) != "2") $stop; + if (getStruct($c("3")) != "3") $stop; + if (getStruct($c("4")) != "4") $stop; + if (getStruct($c("5")) != "5") $stop; + if (getStruct($c("6")) != "6") $stop; + if (getStruct($c("7")) != "7") $stop; + if (getStruct($c("8")) != "8") $stop; + if (getStruct($c("9")) != "9") $stop; + + if (getType($c("0")) != "0") $stop; + if (getType($c("1")) != "1") $stop; + if (getType($c("2")) != "2") $stop; + if (getType($c("3")) != "3") $stop; + if (getType($c("4")) != "4") $stop; + if (getType($c("5")) != "5") $stop; + if (getType($c("6")) != "6") $stop; + if (getType($c("7")) != "7") $stop; + if (getType($c("8")) != "8") $stop; + if (getType($c("9")) != "9") $stop; + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule + +function automatic logic [7:0] getUnpacked(logic[3:0] d); +`ifdef NO_INLINE + /* verilator no_inline_task */ +`endif + localparam logic [7:0] digits [10] = + '{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"}; + return digits[d]; +endfunction + +function automatic logic [7:0] getPacked(logic[3:0] d); +`ifdef NO_INLINE + /* verilator no_inline_task */ +`endif + localparam logic [9:0][7:0] digits = + {"9", "8", "7", "6", "5", "4", "3", "2", "1", "0"}; + return digits[d]; +endfunction + +function automatic string getString(logic[3:0] d); +`ifdef NO_INLINE + /* verilator no_inline_task */ +`endif + localparam string digits [10] = + '{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"}; + return digits[d]; +endfunction + +function automatic logic [7:0] getStruct(logic[3:0] d); +`ifdef NO_INLINE + /* verilator no_inline_task */ +`endif + // Silly indirect lookup table because we want to use a struct + typedef struct packed { + logic [7:0] result; + longint index; + } lut_t; + localparam lut_t digits [10] = + '{ + '{result: "1", index: 9}, + '{result: "2", index: 0}, + '{result: "3", index: 1}, + '{result: "4", index: 2}, + '{result: "5", index: 3}, + '{result: "6", index: 4}, + '{result: "7", index: 5}, + '{result: "8", index: 6}, + '{result: "9", index: 7}, + '{result: "0", index: 8} + }; + return digits[4'(digits[d].index)].result; +endfunction + +function automatic logic [7:0] getType(logic[3:0] d); +`ifdef NO_INLINE + /* verilator no_inline_task */ +`endif + localparam type octet_t = logic [7:0]; + localparam octet_t [9:0] digits = + {"9", "8", "7", "6", "5", "4", "3", "2", "1", "0"}; + return digits[d]; +endfunction + diff --git a/test_regress/t/t_param_in_func_bad.out b/test_regress/t/t_param_in_func_bad.out deleted file mode 100644 index 73820ed23..000000000 --- a/test_regress/t/t_param_in_func_bad.out +++ /dev/null @@ -1,6 +0,0 @@ -%Error-UNSUPPORTED: t/t_param_in_func_bad.v:24:26: Unsupported: Parameters in functions with complex assign - : ... In instance t - 24 | localparam logic[7:0] digits[10] - | ^~~~~~ - ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest -%Error: Exiting due to diff --git a/test_regress/t/t_param_in_func_bad.pl b/test_regress/t/t_param_in_func_bad.pl deleted file mode 100755 index 27159da5b..000000000 --- a/test_regress/t/t_param_in_func_bad.pl +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env perl -if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } -# DESCRIPTION: Verilator: Verilog Test driver/expect definition -# -# Copyright 2019 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 - -scenarios(linter => 1); - -lint( - fails => 1, - expect_filename => $Self->{golden_filename}, - ); - -ok(1); -1; diff --git a/test_regress/t/t_param_in_func_bad.v b/test_regress/t/t_param_in_func_bad.v deleted file mode 100644 index 02a451f42..000000000 --- a/test_regress/t/t_param_in_func_bad.v +++ /dev/null @@ -1,29 +0,0 @@ -// DESCRIPTION: Verilator: Verilog Test module -// -// This file ONLY is placed into the Public Domain, for any use, -// without warranty, 2019 by Driss Hafdi. -// SPDX-License-Identifier: CC0-1.0 - -module t (/*AUTOARG*/ - // Inputs - clk - ); - - input clk; - - logic [7:0] digit = getDigit(4'd1); - - initial begin - if (digit != "1") $stop; - $write("*-* All Finished *-*\n"); - $finish; - end -endmodule - -function automatic logic[7:0] getDigit(logic[3:0] d); - localparam logic[7:0] digits[10] - = '{ - "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" - }; - return digits[d]; -endfunction diff --git a/test_regress/t/t_param_in_func_noinline.pl b/test_regress/t/t_param_in_func_noinline.pl new file mode 100755 index 000000000..c089b8b2e --- /dev/null +++ b/test_regress/t/t_param_in_func_noinline.pl @@ -0,0 +1,35 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2019 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 + +scenarios(simulator => 1); + +top_filename("t/t_param_in_func.v"); + +compile( + verilator_flags2 => ["--stats", "+define+NO_INLINE=1"], + ); + +execute(check_finished => 1); + +# The parameter array should have been put in the constant pool +if ($Self->{vlt_all}) { + file_grep($Self->{stats}, qr/ConstPool, Tables emitted\s+(\d+)/i, 3); +} + +# Shouldn't have any references to the parameter array +foreach my $file ( + glob_all("$Self->{obj_dir}/$Self->{VM_PREFIX}*.h"), + glob_all("$Self->{obj_dir}/$Self->{VM_PREFIX}*.cpp") + ) { + file_grep_not($file, qr/digits/i); +} + +ok(1); +1; From 19875d2e85bb6e840515fd0404633da93baf96a1 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Fri, 23 Jul 2021 19:24:25 -0400 Subject: [PATCH 45/84] Increase split count of ChangeDet (#3072). --- src/V3Changed.cpp | 5 ++++- src/V3EmitCBase.h | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/V3Changed.cpp b/src/V3Changed.cpp index dc3343f87..589a681ac 100644 --- a/src/V3Changed.cpp +++ b/src/V3Changed.cpp @@ -151,8 +151,11 @@ private: AstAssign* const initp = new AstAssign{m_vscp->fileline(), m_newLvEqnp->cloneTree(true), m_varEqnp->cloneTree(true)}; m_statep->m_chgFuncp->addFinalsp(initp); + + // Later code will expand words which adds to GCC compile time, + // so add penalty based on word width also EmitCBaseCounterVisitor visitor{initp}; - m_statep->m_numStmts += visitor.count(); + m_statep->m_numStmts += visitor.count() + m_varEqnp->widthWords(); } virtual void visit(AstBasicDType*) override { // diff --git a/src/V3EmitCBase.h b/src/V3EmitCBase.h index 0b34a525e..dcebb4484 100644 --- a/src/V3EmitCBase.h +++ b/src/V3EmitCBase.h @@ -122,7 +122,7 @@ private: int m_count = 0; // Number of statements // VISITORS virtual void visit(AstNode* nodep) override { - m_count++; + ++m_count; iterateChildren(nodep); } From ab13a2ebdc37775cccddbe66a2ef3467cfea8a82 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 24 Jul 2021 08:36:11 -0400 Subject: [PATCH 46/84] Internals: Use C++11 const and initializers. No functional change intended. --- include/verilated.cpp | 205 ++++++++++++++++---------------- include/verilated.h | 4 +- include/verilated_cov.cpp | 56 ++++----- include/verilated_dpi.cpp | 56 ++++----- include/verilated_fst_c.cpp | 10 +- include/verilated_heavy.h | 10 +- include/verilated_imp.h | 48 ++++---- include/verilated_save.cpp | 10 +- include/verilated_threads.cpp | 8 +- include/verilated_threads.h | 6 +- include/verilated_trace.h | 8 +- include/verilated_trace_imp.cpp | 18 +-- include/verilated_vcd_c.cpp | 26 ++-- include/verilated_vcd_sc.h | 4 +- include/verilated_vpi.cpp | 113 +++++++++--------- nodist/dot_importer | 4 +- 16 files changed, 296 insertions(+), 290 deletions(-) diff --git a/include/verilated.cpp b/include/verilated.cpp index 01d31413f..375647d2d 100644 --- a/include/verilated.cpp +++ b/include/verilated.cpp @@ -173,9 +173,9 @@ void vl_stop_maybe(const char* filename, int linenum, const char* hier, bool may void VL_FINISH_MT(const char* filename, int linenum, const char* hier) VL_MT_SAFE { #ifdef VL_THREADED - VerilatedThreadMsgQueue::post(VerilatedMsg([=]() { // + VerilatedThreadMsgQueue::post(VerilatedMsg{[=]() { // vl_finish(filename, linenum, hier); - })); + }}); #else vl_finish(filename, linenum, hier); #endif @@ -183,9 +183,9 @@ void VL_FINISH_MT(const char* filename, int linenum, const char* hier) VL_MT_SAF void VL_STOP_MT(const char* filename, int linenum, const char* hier, bool maybe) VL_MT_SAFE { #ifdef VL_THREADED - VerilatedThreadMsgQueue::post(VerilatedMsg([=]() { // + VerilatedThreadMsgQueue::post(VerilatedMsg{[=]() { // vl_stop_maybe(filename, linenum, hier, maybe); - })); + }}); #else vl_stop_maybe(filename, linenum, hier, maybe); #endif @@ -193,9 +193,9 @@ void VL_STOP_MT(const char* filename, int linenum, const char* hier, bool maybe) void VL_FATAL_MT(const char* filename, int linenum, const char* hier, const char* msg) VL_MT_SAFE { #ifdef VL_THREADED - VerilatedThreadMsgQueue::post(VerilatedMsg([=]() { // + VerilatedThreadMsgQueue::post(VerilatedMsg{[=]() { // vl_fatal(filename, linenum, hier, msg); - })); + }}); #else vl_fatal(filename, linenum, hier, msg); #endif @@ -208,14 +208,14 @@ void VL_FATAL_MT(const char* filename, int linenum, const char* hier, const char std::string _vl_string_vprintf(const char* formatp, va_list ap) VL_MT_SAFE { va_list aq; va_copy(aq, ap); - int len = VL_VSNPRINTF(nullptr, 0, formatp, aq); + size_t len = VL_VSNPRINTF(nullptr, 0, formatp, aq); va_end(aq); if (VL_UNLIKELY(len < 1)) return ""; - char* bufp = new char[len + 1]; + char* const bufp = new char[len + 1]; VL_VSNPRINTF(bufp, len + 1, formatp, ap); - const std::string out = std::string(bufp, len); + const std::string out{bufp, len}; delete[] bufp; return out; } @@ -263,9 +263,9 @@ void VL_PRINTF_MT(const char* formatp, ...) VL_MT_SAFE { va_start(ap, formatp); const std::string out = _vl_string_vprintf(formatp, ap); va_end(ap); - VerilatedThreadMsgQueue::post(VerilatedMsg([=]() { // + VerilatedThreadMsgQueue::post(VerilatedMsg{[=]() { // VL_PRINTF("%s", out.c_str()); - })); + }}); } #endif @@ -276,7 +276,7 @@ static vluint32_t vl_sys_rand32() VL_MT_UNSAFE { // Return random 32-bits using system library. // Used only to construct seed for Verilator's PNRG. static VerilatedMutex s_mutex; - const VerilatedLockGuard lock(s_mutex); // Otherwise rand is unsafe + const VerilatedLockGuard lock{s_mutex}; // Otherwise rand is unsafe #if defined(_WIN32) && !defined(__CYGWIN__) // Windows doesn't have lrand48(), although Cygwin does. return (std::rand() << 16) ^ std::rand(); @@ -742,7 +742,7 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA } else if (!inPct) { // Normal text // Fast-forward to next escape and add to output const char* ep = pos; - while (ep[0] && ep[0] != '%') ep++; + while (ep[0] && ep[0] != '%') ++ep; if (ep != pos) { output.append(pos, ep - pos); pos += ep - pos - 1; @@ -776,7 +776,7 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA output += '%'; break; case 'N': { // "C" string with name of module, add . if needed - const char* cstrp = va_arg(ap, const char*); + const char* const cstrp = va_arg(ap, const char*); if (VL_LIKELY(*cstrp)) { output += cstrp; output += '.'; @@ -784,13 +784,13 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA break; } case 'S': { // "C" string - const char* cstrp = va_arg(ap, const char*); + const char* const cstrp = va_arg(ap, const char*); output += cstrp; break; } case '@': { // Verilog/C++ string va_arg(ap, int); // # bits is ignored - const std::string* cstrp = va_arg(ap, const std::string*); + const std::string* const cstrp = va_arg(ap, const std::string*); std::string padding; if (width > cstrp->size()) padding.append(width - cstrp->size(), ' '); output += left ? (*cstrp + padding) : (padding + *cstrp); @@ -808,7 +808,8 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA const int timeunit = va_arg(ap, int); output += _vl_vsformat_time(t_tmp, d, timeunit, left, width); } else { - std::string fmts(pctp, pos - pctp + 1); + const size_t len = pos - pctp + 1; + std::string fmts{pctp, len}; VL_SNPRINTF(t_tmp, VL_VALUE_STRING_MAX_WIDTH, fmts.c_str(), d); output += t_tmp; } @@ -862,7 +863,7 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA if (VL_SIGN_E(lbits, lwp[VL_WORDS_I(lbits) - 1])) { VlWide neg; VL_NEGATE_W(VL_WORDS_I(lbits), neg, lwp); - append = std::string("-") + VL_DECIMAL_NW(lbits, neg); + append = std::string{"-"} + VL_DECIMAL_NW(lbits, neg); } else { append = VL_DECIMAL_NW(lbits, lwp); } @@ -957,7 +958,7 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA } break; default: { // LCOV_EXCL_START - const std::string msg = std::string("Unknown _vl_vsformat code: ") + pos[0]; + const std::string msg = std::string{"Unknown _vl_vsformat code: "} + pos[0]; VL_FATAL_MT(__FILE__, __LINE__, "", msg.c_str()); break; } // LCOV_EXCL_STOP @@ -1217,7 +1218,7 @@ IData _vl_vsscanf(FILE* fp, // If a fscanf case 'u': { // Read packed 2-value binary data const int bytes = VL_BYTES_I(obits); - char* out = reinterpret_cast(owp); + char* const out = reinterpret_cast(owp); if (!_vl_vsss_read_bin(fp, floc, fromp, fstr, out, bytes)) goto done; const int last = bytes % 4; if (last != 0 @@ -1242,7 +1243,7 @@ IData _vl_vsscanf(FILE* fp, // If a fscanf break; } default: { // LCOV_EXCL_START - const std::string msg = std::string("Unknown _vl_vsscanf code: ") + pos[0]; + const std::string msg = std::string{"Unknown _vl_vsscanf code: "} + pos[0]; VL_FATAL_MT(__FILE__, __LINE__, "", msg.c_str()); break; } // LCOV_EXCL_STOP @@ -1253,19 +1254,19 @@ IData _vl_vsscanf(FILE* fp, // If a fscanf // Reload data if non-wide (if wide, we put it in the right place directly) if (obits == 0) { // Due to inIgnore } else if (obits == -1) { // string - std::string* p = va_arg(ap, std::string*); + std::string* const p = va_arg(ap, std::string*); *p = t_tmp; } else if (obits <= VL_BYTESIZE) { - CData* p = va_arg(ap, CData*); + CData* const p = va_arg(ap, CData*); *p = owp[0]; } else if (obits <= VL_SHORTSIZE) { - SData* p = va_arg(ap, SData*); + SData* const p = va_arg(ap, SData*); *p = owp[0]; } else if (obits <= VL_IDATASIZE) { - IData* p = va_arg(ap, IData*); + IData* const p = va_arg(ap, IData*); *p = owp[0]; } else if (obits <= VL_QUADSIZE) { - QData* p = va_arg(ap, QData*); + QData* const p = va_arg(ap, QData*); *p = VL_SET_QW(owp); } } @@ -1354,7 +1355,7 @@ IData VL_FGETS_NI(std::string& dest, IData fpi) VL_MT_SAFE { IData VL_FERROR_IN(IData, std::string& outputr) VL_MT_SAFE { // We ignore lhs/fpi - IEEE says "most recent error" so probably good enough const IData ret = errno; - outputr = std::string(::std::strerror(ret)); + outputr = std::string{::std::strerror(ret)}; return ret; } @@ -1541,19 +1542,19 @@ IData VL_FREAD_I(int width, int array_lsb, int array_size, void* memp, IData fpi // Shift value in IData entry = read_elements + start - array_lsb; if (width <= 8) { - CData* datap = &(reinterpret_cast(memp))[entry]; + CData* const datap = &(reinterpret_cast(memp))[entry]; if (shift == start_shift) *datap = 0; *datap |= (c << shift) & VL_MASK_I(width); } else if (width <= 16) { - SData* datap = &(reinterpret_cast(memp))[entry]; + SData* const datap = &(reinterpret_cast(memp))[entry]; if (shift == start_shift) *datap = 0; *datap |= (c << shift) & VL_MASK_I(width); } else if (width <= VL_IDATASIZE) { - IData* datap = &(reinterpret_cast(memp))[entry]; + IData* const datap = &(reinterpret_cast(memp))[entry]; if (shift == start_shift) *datap = 0; *datap |= (c << shift) & VL_MASK_I(width); } else if (width <= VL_QUADSIZE) { - QData* datap = &(reinterpret_cast(memp))[entry]; + QData* const datap = &(reinterpret_cast(memp))[entry]; if (shift == start_shift) *datap = 0; *datap |= ((static_cast(c) << static_cast(shift)) & VL_MASK_Q(width)); } else { @@ -1615,7 +1616,7 @@ IData VL_VALUEPLUSARGS_INW(int rbits, const std::string& ld, WDataOutP rwp) VL_M } const std::string& match = Verilated::threadContextp()->impp()->argPlusMatch(prefix.c_str()); - const char* dp = match.c_str() + 1 /*leading + */ + prefix.length(); + const char* const dp = match.c_str() + 1 /*leading + */ + prefix.length(); if (match.empty()) return 0; VL_ZERO_RESET_W(rbits, rwp); @@ -1684,9 +1685,9 @@ IData VL_VALUEPLUSARGS_INN(int, const std::string& ld, std::string& rdr) VL_MT_S } } const std::string& match = Verilated::threadContextp()->impp()->argPlusMatch(prefix.c_str()); - const char* dp = match.c_str() + 1 /*leading + */ + prefix.length(); + const char* const dp = match.c_str() + 1 /*leading + */ + prefix.length(); if (match.empty()) return 0; - rdr = std::string(dp); + rdr = std::string{dp}; return 1; } @@ -1731,17 +1732,17 @@ std::string VL_CVT_PACK_STR_NW(int lwords, const WDataInP lwp) VL_MT_SAFE { int lsb = obits - 1; bool start = true; char* destp = destout; - int len = 0; + size_t len = 0; for (; lsb >= 0; --lsb) { lsb = (lsb / 8) * 8; // Next digit IData charval = VL_BITRSHIFT_W(lwp, lsb) & 0xff; if (!start || charval) { *destp++ = (charval == 0) ? ' ' : charval; - len++; + ++len; start = false; // Drop leading 0s } } - return std::string(destout, len); + return std::string{destout, len}; } std::string VL_PUTC_N(const std::string& lhs, IData rhs, CData ths) VL_PURE { @@ -1924,19 +1925,19 @@ void VlReadMem::setData(void* valuep, const std::string& rhs) { const int value = (c >= 'a' ? (c == 'x' ? VL_RAND_RESET_I(4) : (c - 'a' + 10)) : (c - '0')); if (m_bits <= 8) { - CData* datap = reinterpret_cast(valuep); + CData* const datap = reinterpret_cast(valuep); if (!innum) *datap = 0; *datap = ((*datap << shift) + value) & VL_MASK_I(m_bits); } else if (m_bits <= 16) { - SData* datap = reinterpret_cast(valuep); + SData* const datap = reinterpret_cast(valuep); if (!innum) *datap = 0; *datap = ((*datap << shift) + value) & VL_MASK_I(m_bits); } else if (m_bits <= VL_IDATASIZE) { - IData* datap = reinterpret_cast(valuep); + IData* const datap = reinterpret_cast(valuep); if (!innum) *datap = 0; *datap = ((*datap << shift) + value) & VL_MASK_I(m_bits); } else if (m_bits <= VL_QUADSIZE) { - QData* datap = reinterpret_cast(valuep); + QData* const datap = reinterpret_cast(valuep); if (!innum) *datap = 0; *datap = ((*datap << static_cast(shift)) + static_cast(value)) & VL_MASK_Q(m_bits); @@ -1979,7 +1980,7 @@ void VlWriteMem::print(QData addr, bool addrstamp, const void* valuep) { } m_addr = addr + 1; if (m_bits <= 8) { - const CData* datap = reinterpret_cast(valuep); + const CData* const datap = reinterpret_cast(valuep); if (m_hex) { fprintf(m_fp, memhFormat(m_bits), VL_MASK_I(m_bits) & *datap); fprintf(m_fp, "\n"); @@ -1987,7 +1988,7 @@ void VlWriteMem::print(QData addr, bool addrstamp, const void* valuep) { fprintf(m_fp, "%s\n", formatBinary(m_bits, *datap)); } } else if (m_bits <= 16) { - const SData* datap = reinterpret_cast(valuep); + const SData* const datap = reinterpret_cast(valuep); if (m_hex) { fprintf(m_fp, memhFormat(m_bits), VL_MASK_I(m_bits) & *datap); fprintf(m_fp, "\n"); @@ -1995,7 +1996,7 @@ void VlWriteMem::print(QData addr, bool addrstamp, const void* valuep) { fprintf(m_fp, "%s\n", formatBinary(m_bits, *datap)); } } else if (m_bits <= 32) { - const IData* datap = reinterpret_cast(valuep); + const IData* const datap = reinterpret_cast(valuep); if (m_hex) { fprintf(m_fp, memhFormat(m_bits), VL_MASK_I(m_bits) & *datap); fprintf(m_fp, "\n"); @@ -2003,7 +2004,7 @@ void VlWriteMem::print(QData addr, bool addrstamp, const void* valuep) { fprintf(m_fp, "%s\n", formatBinary(m_bits, *datap)); } } else if (m_bits <= 64) { - const QData* datap = reinterpret_cast(valuep); + const QData* const datap = reinterpret_cast(valuep); const vluint64_t value = VL_MASK_Q(m_bits) & *datap; const vluint32_t lo = value & 0xffffffff; const vluint32_t hi = value >> 32; @@ -2038,7 +2039,7 @@ void VlWriteMem::print(QData addr, bool addrstamp, const void* valuep) { fprintf(m_fp, "%s", formatBinary(32, data)); } } - word_idx--; + --word_idx; first = false; } fprintf(m_fp, "\n"); @@ -2057,7 +2058,7 @@ void VL_READMEM_N(bool hex, // Hex format, else binary ) VL_MT_SAFE { if (start < static_cast(array_lsb)) start = array_lsb; - VlReadMem rmem(hex, bits, filename, start, end); + VlReadMem rmem{hex, bits, filename, start, end}; if (VL_UNLIKELY(!rmem.isOpen())) return; while (true) { QData addr = 0; @@ -2070,16 +2071,16 @@ void VL_READMEM_N(bool hex, // Hex format, else binary } else { QData entry = addr - array_lsb; if (bits <= 8) { - CData* datap = &(reinterpret_cast(memp))[entry]; + CData* const datap = &(reinterpret_cast(memp))[entry]; rmem.setData(datap, value); } else if (bits <= 16) { - SData* datap = &(reinterpret_cast(memp))[entry]; + SData* const datap = &(reinterpret_cast(memp))[entry]; rmem.setData(datap, value); } else if (bits <= VL_IDATASIZE) { - IData* datap = &(reinterpret_cast(memp))[entry]; + IData* const datap = &(reinterpret_cast(memp))[entry]; rmem.setData(datap, value); } else if (bits <= VL_QUADSIZE) { - QData* datap = &(reinterpret_cast(memp))[entry]; + QData* const datap = &(reinterpret_cast(memp))[entry]; rmem.setData(datap, value); } else { WDataOutP datap @@ -2107,22 +2108,22 @@ void VL_WRITEMEM_N(bool hex, // Hex format, else binary if (start < static_cast(array_lsb)) start = array_lsb; if (end > addr_max) end = addr_max; - VlWriteMem wmem(hex, bits, filename, start, end); + VlWriteMem wmem{hex, bits, filename, start, end}; if (VL_UNLIKELY(!wmem.isOpen())) return; for (QData addr = start; addr <= end; ++addr) { const QData row_offset = addr - array_lsb; if (bits <= 8) { - const CData* datap = &(reinterpret_cast(memp))[row_offset]; + const CData* const datap = &(reinterpret_cast(memp))[row_offset]; wmem.print(addr, false, datap); } else if (bits <= 16) { - const SData* datap = &(reinterpret_cast(memp))[row_offset]; + const SData* const datap = &(reinterpret_cast(memp))[row_offset]; wmem.print(addr, false, datap); } else if (bits <= 32) { - const IData* datap = &(reinterpret_cast(memp))[row_offset]; + const IData* const datap = &(reinterpret_cast(memp))[row_offset]; wmem.print(addr, false, datap); } else if (bits <= 64) { - const QData* datap = &(reinterpret_cast(memp))[row_offset]; + const QData* const datap = &(reinterpret_cast(memp))[row_offset]; wmem.print(addr, false, datap); } else { const WDataInP memDatap = reinterpret_cast(memp); @@ -2142,8 +2143,8 @@ int VL_TIME_STR_CONVERT(const char* strp) VL_PURE { if (!strp) return 0; if (*strp++ != '1') return 0; while (*strp == '0') { - scale++; - strp++; + ++scale; + ++strp; } switch (*strp++) { case 's': break; @@ -2273,19 +2274,19 @@ VerilatedContext::Serialized::Serialized() { } void VerilatedContext::assertOn(bool flag) VL_MT_SAFE { - const VerilatedLockGuard lock(m_mutex); + const VerilatedLockGuard lock{m_mutex}; m_s.m_assertOn = flag; } void VerilatedContext::calcUnusedSigs(bool flag) VL_MT_SAFE { - const VerilatedLockGuard lock(m_mutex); + const VerilatedLockGuard lock{m_mutex}; m_s.m_calcUnusedSigs = flag; } void VerilatedContext::dumpfile(const std::string& flag) VL_MT_SAFE_EXCLUDES(m_timeDumpMutex) { - const VerilatedLockGuard lock(m_timeDumpMutex); + const VerilatedLockGuard lock{m_timeDumpMutex}; m_dumpfile = flag; } std::string VerilatedContext::dumpfile() const VL_MT_SAFE_EXCLUDES(m_timeDumpMutex) { - const VerilatedLockGuard lock(m_timeDumpMutex); + const VerilatedLockGuard lock{m_timeDumpMutex}; return m_dumpfile; } std::string VerilatedContext::dumpfileCheck() const VL_MT_SAFE_EXCLUDES(m_timeDumpMutex) { @@ -2297,61 +2298,61 @@ std::string VerilatedContext::dumpfileCheck() const VL_MT_SAFE_EXCLUDES(m_timeDu return out; } void VerilatedContext::errorCount(int val) VL_MT_SAFE { - const VerilatedLockGuard lock(m_mutex); + const VerilatedLockGuard lock{m_mutex}; m_s.m_errorCount = val; } void VerilatedContext::errorCountInc() VL_MT_SAFE { - const VerilatedLockGuard lock(m_mutex); + const VerilatedLockGuard lock{m_mutex}; ++m_s.m_errorCount; } void VerilatedContext::errorLimit(int val) VL_MT_SAFE { - const VerilatedLockGuard lock(m_mutex); + const VerilatedLockGuard lock{m_mutex}; m_s.m_errorLimit = val; } void VerilatedContext::fatalOnError(bool flag) VL_MT_SAFE { - const VerilatedLockGuard lock(m_mutex); + const VerilatedLockGuard lock{m_mutex}; m_s.m_fatalOnError = flag; } void VerilatedContext::fatalOnVpiError(bool flag) VL_MT_SAFE { - const VerilatedLockGuard lock(m_mutex); + const VerilatedLockGuard lock{m_mutex}; m_s.m_fatalOnVpiError = flag; } void VerilatedContext::gotError(bool flag) VL_MT_SAFE { - const VerilatedLockGuard lock(m_mutex); + const VerilatedLockGuard lock{m_mutex}; m_s.m_gotError = flag; } void VerilatedContext::gotFinish(bool flag) VL_MT_SAFE { - const VerilatedLockGuard lock(m_mutex); + const VerilatedLockGuard lock{m_mutex}; m_s.m_gotFinish = flag; } void VerilatedContext::profThreadsStart(vluint64_t flag) VL_MT_SAFE { - const VerilatedLockGuard lock(m_mutex); + const VerilatedLockGuard lock{m_mutex}; m_ns.m_profThreadsStart = flag; } void VerilatedContext::profThreadsWindow(vluint64_t flag) VL_MT_SAFE { - const VerilatedLockGuard lock(m_mutex); + const VerilatedLockGuard lock{m_mutex}; m_ns.m_profThreadsWindow = flag; } void VerilatedContext::profThreadsFilename(const std::string& flag) VL_MT_SAFE { - const VerilatedLockGuard lock(m_mutex); + const VerilatedLockGuard lock{m_mutex}; m_ns.m_profThreadsFilename = flag; } std::string VerilatedContext::profThreadsFilename() const VL_MT_SAFE { - const VerilatedLockGuard lock(m_mutex); + const VerilatedLockGuard lock{m_mutex}; return m_ns.m_profThreadsFilename; } void VerilatedContext::randReset(int val) VL_MT_SAFE { - const VerilatedLockGuard lock(m_mutex); + const VerilatedLockGuard lock{m_mutex}; m_s.m_randReset = val; } void VerilatedContext::timeunit(int value) VL_MT_SAFE { if (value < 0) value = -value; // Stored as 0..15 - const VerilatedLockGuard lock(m_mutex); + const VerilatedLockGuard lock{m_mutex}; m_s.m_timeunit = value; } void VerilatedContext::timeprecision(int value) VL_MT_SAFE { if (value < 0) value = -value; // Stored as 0..15 - const VerilatedLockGuard lock(m_mutex); + const VerilatedLockGuard lock{m_mutex}; m_s.m_timeprecision = value; #ifdef SYSTEMC_VERSION const sc_time sc_res = sc_get_time_resolution(); @@ -2387,13 +2388,13 @@ const char* VerilatedContext::timeprecisionString() const VL_MT_SAFE { } void VerilatedContext::commandArgs(int argc, const char** argv) VL_MT_SAFE_EXCLUDES(m_argMutex) { - const VerilatedLockGuard lock(m_argMutex); + const VerilatedLockGuard lock{m_argMutex}; m_args.m_argVec.clear(); // Empty first, then add impp()->commandArgsAddGuts(argc, argv); } void VerilatedContext::commandArgsAdd(int argc, const char** argv) VL_MT_SAFE_EXCLUDES(m_argMutex) { - const VerilatedLockGuard lock(m_argMutex); + const VerilatedLockGuard lock{m_argMutex}; impp()->commandArgsAddGuts(argc, argv); } const char* VerilatedContext::commandArgsPlusMatch(const char* prefixp) @@ -2428,14 +2429,14 @@ void VerilatedContextImp::commandArgsAddGuts(int argc, const char** argv) VL_REQ m_args.m_argVecLoaded = true; // Can't just test later for empty vector, no arguments is ok } void VerilatedContextImp::commandArgDump() const VL_MT_SAFE_EXCLUDES(m_argMutex) { - const VerilatedLockGuard lock(m_argMutex); + const VerilatedLockGuard lock{m_argMutex}; VL_PRINTF_MT(" Argv:"); for (const auto& i : m_args.m_argVec) VL_PRINTF_MT(" %s", i.c_str()); VL_PRINTF_MT("\n"); } std::string VerilatedContextImp::argPlusMatch(const char* prefixp) VL_MT_SAFE_EXCLUDES(m_argMutex) { - const VerilatedLockGuard lock(m_argMutex); + const VerilatedLockGuard lock{m_argMutex}; // Note prefixp does not include the leading "+" const size_t len = std::strlen(prefixp); if (VL_UNLIKELY(!m_args.m_argVecLoaded)) { @@ -2454,7 +2455,7 @@ std::string VerilatedContextImp::argPlusMatch(const char* prefixp) // Return string representing current argv // Only used by VPI so uses static storage, only supports most recent called context std::pair VerilatedContextImp::argc_argv() VL_MT_SAFE_EXCLUDES(m_argMutex) { - const VerilatedLockGuard lock(m_argMutex); + const VerilatedLockGuard lock{m_argMutex}; static bool s_loaded = false; static int s_argc = 0; static char** s_argvp = nullptr; @@ -2529,7 +2530,7 @@ bool VerilatedContextImp::commandArgVlValue(const std::string& arg, const std::s void VerilatedContext::randSeed(int val) VL_MT_SAFE { // As we have per-thread state, the epoch must be static, // and so the rand seed's mutex must also be static - const VerilatedLockGuard lock(VerilatedContextImp::s().s_randMutex); + const VerilatedLockGuard lock{VerilatedContextImp::s().s_randMutex}; m_s.m_randSeed = val; const vluint64_t newEpoch = VerilatedContextImp::s().s_randSeedEpoch + 1; // Obververs must see new epoch AFTER seed updated @@ -2552,10 +2553,10 @@ vluint64_t VerilatedContextImp::randSeedDefault64() const VL_MT_SAFE { // VerilatedContext:: Methods - scopes void VerilatedContext::scopesDump() const VL_MT_SAFE { - const VerilatedLockGuard lock(m_impdatap->m_nameMutex); + const VerilatedLockGuard lock{m_impdatap->m_nameMutex}; VL_PRINTF_MT(" scopesDump:\n"); for (const auto& i : m_impdatap->m_nameMap) { - const VerilatedScope* scopep = i.second; + const VerilatedScope* const scopep = i.second; scopep->scopeDump(); } VL_PRINTF_MT("\n"); @@ -2563,20 +2564,20 @@ void VerilatedContext::scopesDump() const VL_MT_SAFE { void VerilatedContextImp::scopeInsert(const VerilatedScope* scopep) VL_MT_SAFE { // Slow ok - called once/scope at construction - const VerilatedLockGuard lock(m_impdatap->m_nameMutex); + const VerilatedLockGuard lock{m_impdatap->m_nameMutex}; const auto it = m_impdatap->m_nameMap.find(scopep->name()); if (it == m_impdatap->m_nameMap.end()) m_impdatap->m_nameMap.emplace(scopep->name(), scopep); } void VerilatedContextImp::scopeErase(const VerilatedScope* scopep) VL_MT_SAFE { // Slow ok - called once/scope at destruction - const VerilatedLockGuard lock(m_impdatap->m_nameMutex); + const VerilatedLockGuard lock{m_impdatap->m_nameMutex}; VerilatedImp::userEraseScope(scopep); const auto it = m_impdatap->m_nameMap.find(scopep->name()); if (it != m_impdatap->m_nameMap.end()) m_impdatap->m_nameMap.erase(it); } const VerilatedScope* VerilatedContext::scopeFind(const char* namep) const VL_MT_SAFE { // Thread save only assuming this is called only after model construction completed - const VerilatedLockGuard lock(m_impdatap->m_nameMutex); + const VerilatedLockGuard lock{m_impdatap->m_nameMutex}; // If too slow, can assume this is only VL_MT_SAFE_POSINIT const auto& it = m_impdatap->m_nameMap.find(namep); if (VL_UNLIKELY(it == m_impdatap->m_nameMap.end())) return nullptr; @@ -2670,11 +2671,11 @@ static void runCallbacks(const VoidPCbList& cbs) VL_MT_SAFE { } void Verilated::addFlushCb(VoidPCb cb, void* datap) VL_MT_SAFE { - const VerilatedLockGuard lock(VlCbStatic.s_flushMutex); + const VerilatedLockGuard lock{VlCbStatic.s_flushMutex}; addCb(cb, datap, VlCbStatic.s_flushCbs); } void Verilated::removeFlushCb(VoidPCb cb, void* datap) VL_MT_SAFE { - const VerilatedLockGuard lock(VlCbStatic.s_flushMutex); + const VerilatedLockGuard lock{VlCbStatic.s_flushMutex}; removeCb(cb, datap, VlCbStatic.s_flushCbs); } void Verilated::runFlushCallbacks() VL_MT_SAFE { @@ -2685,7 +2686,7 @@ void Verilated::runFlushCallbacks() VL_MT_SAFE { static int s_recursing = 0; #endif if (!s_recursing++) { - const VerilatedLockGuard lock(VlCbStatic.s_flushMutex); + const VerilatedLockGuard lock{VlCbStatic.s_flushMutex}; runCallbacks(VlCbStatic.s_flushCbs); } --s_recursing; @@ -2698,11 +2699,11 @@ void Verilated::runFlushCallbacks() VL_MT_SAFE { } void Verilated::addExitCb(VoidPCb cb, void* datap) VL_MT_SAFE { - const VerilatedLockGuard lock(VlCbStatic.s_exitMutex); + const VerilatedLockGuard lock{VlCbStatic.s_exitMutex}; addCb(cb, datap, VlCbStatic.s_exitCbs); } void Verilated::removeExitCb(VoidPCb cb, void* datap) VL_MT_SAFE { - const VerilatedLockGuard lock(VlCbStatic.s_exitMutex); + const VerilatedLockGuard lock{VlCbStatic.s_exitMutex}; removeCb(cb, datap, VlCbStatic.s_exitCbs); } void Verilated::runExitCallbacks() VL_MT_SAFE { @@ -2712,7 +2713,7 @@ void Verilated::runExitCallbacks() VL_MT_SAFE { static int s_recursing = 0; #endif if (!s_recursing++) { - const VerilatedLockGuard lock(VlCbStatic.s_exitMutex); + const VerilatedLockGuard lock{VlCbStatic.s_exitMutex}; runCallbacks(VlCbStatic.s_exitCbs); } --s_recursing; @@ -2729,7 +2730,7 @@ void Verilated::nullPointerError(const char* filename, int linenum) VL_MT_SAFE { void Verilated::overWidthError(const char* signame) VL_MT_SAFE { // Slowpath - Called only when signal sets too high of a bit - const std::string msg = (std::string("Testbench C set input '") + signame + const std::string msg = (std::string{"Testbench C set input '"} + signame + "' to value that overflows what the signal's width can fit"); VL_FATAL_MT("unknown", 0, "", msg.c_str()); VL_UNREACHABLE @@ -2846,7 +2847,7 @@ void VerilatedScope::configure(VerilatedSyms* symsp, const char* prefixp, const m_type = type; m_timeunit = timeunit; { - char* namep = new char[std::strlen(prefixp) + std::strlen(suffixp) + 2]; + char* const namep = new char[std::strlen(prefixp) + std::strlen(suffixp) + 2]; char* dp = namep; for (const char* sp = prefixp; *sp;) *dp++ = *sp++; if (*prefixp && *suffixp) *dp++ = '.'; @@ -2886,7 +2887,7 @@ void VerilatedScope::varInsert(int finalize, const char* namep, void* datap, boo // statically construct from that. if (!finalize) return; - if (!m_varsp) m_varsp = new VerilatedVarNameMap(); + if (!m_varsp) m_varsp = new VerilatedVarNameMap; VerilatedVar var(namep, datap, vltype, static_cast(vlflags), dims, isParam); va_list ap; @@ -2903,9 +2904,9 @@ void VerilatedScope::varInsert(int finalize, const char* namep, void* datap, boo } else { // We could have a linked list of ranges, but really this whole thing needs // to be generalized to support structs and unions, etc. - VL_FATAL_MT( - __FILE__, __LINE__, "", - (std::string("Unsupported multi-dimensional public varInsert: ") + namep).c_str()); + std::string msg + = std::string{"Unsupported multi-dimensional public varInsert: "} + namep; + VL_FATAL_MT(__FILE__, __LINE__, "", msg.c_str()); } } va_end(ap); @@ -2925,7 +2926,7 @@ VerilatedVar* VerilatedScope::varFind(const char* namep) const VL_MT_SAFE_POSTIN void* VerilatedScope::exportFindNullError(int funcnum) VL_MT_SAFE { // Slowpath - Called only when find has failed const std::string msg - = (std::string("Testbench C called '") + VerilatedImp::exportName(funcnum) + = (std::string{"Testbench C called '"} + VerilatedImp::exportName(funcnum) + "' but scope wasn't set, perhaps due to dpi import call without " + "'context', or missing svSetScope. See IEEE 1800-2017 35.5.3."); VL_FATAL_MT("unknown", 0, "", msg.c_str()); @@ -2935,7 +2936,7 @@ void* VerilatedScope::exportFindNullError(int funcnum) VL_MT_SAFE { void* VerilatedScope::exportFindError(int funcnum) const { // Slowpath - Called only when find has failed const std::string msg - = (std::string("Testbench C called '") + VerilatedImp::exportName(funcnum) + = (std::string{"Testbench C called '"} + VerilatedImp::exportName(funcnum) + "' but this DPI export function exists only in other scopes, not scope '" + name() + "'"); VL_FATAL_MT("unknown", 0, "", msg.c_str()); @@ -2950,7 +2951,7 @@ void VerilatedScope::scopeDump() const { VerilatedImp::exportName(i)); } } - if (VerilatedVarNameMap* varsp = this->varsp()) { + if (VerilatedVarNameMap* const varsp = this->varsp()) { for (const auto& i : *varsp) VL_PRINTF_MT(" VAR %p: %s\n", &(i.second), i.first); } } diff --git a/include/verilated.h b/include/verilated.h index cb375d27c..f71f13308 100644 --- a/include/verilated.h +++ b/include/verilated.h @@ -1614,14 +1614,14 @@ static inline IData VL_ONEHOT0_W(int words, WDataInP const lwp) VL_MT_SAFE { static inline IData VL_CLOG2_I(IData lhs) VL_PURE { // There are faster algorithms, or fls GCC4 builtins, but rarely used if (VL_UNLIKELY(!lhs)) return 0; - lhs--; + --lhs; int shifts = 0; for (; lhs != 0; ++shifts) lhs = lhs >> 1; return shifts; } static inline IData VL_CLOG2_Q(QData lhs) VL_PURE { if (VL_UNLIKELY(!lhs)) return 0; - lhs--; + --lhs; int shifts = 0; for (; lhs != 0; ++shifts) lhs = lhs >> 1ULL; return shifts; diff --git a/include/verilated_cov.cpp b/include/verilated_cov.cpp index 1fe14ba8d..78757fc4a 100644 --- a/include/verilated_cov.cpp +++ b/include/verilated_cov.cpp @@ -132,7 +132,7 @@ private: int valueIndex(const std::string& value) VL_REQUIRES(m_mutex) { const auto iter = m_valueIndexes.find(value); if (iter != m_valueIndexes.end()) return iter->second; - m_nextIndex++; + ++m_nextIndex; assert(m_nextIndex > 0); // Didn't rollover m_valueIndexes.emplace(value, m_nextIndex); m_indexValues.emplace(m_nextIndex, value); @@ -165,11 +165,11 @@ private: const std::string& value) VL_PURE { std::string name; if (key.length() == 1 && std::isalpha(key[0])) { - name += std::string("\001") + key; + name += std::string{"\001"} + key; } else { - name += std::string("\001") + dequote(key); + name += std::string{"\001"} + dequote(key); } - name += std::string("\002") + dequote(value); + name += std::string{"\002"} + dequote(value); return name; } static std::string combineHier(const std::string& old, const std::string& add) VL_PURE { @@ -180,31 +180,32 @@ private: if (old.empty()) return add; if (add.empty()) return old; - const char* a = old.c_str(); - const char* b = add.c_str(); + const char* const a = old.c_str(); + const char* const b = add.c_str(); // Scan forward to first mismatch const char* apre = a; const char* bpre = b; while (*apre == *bpre) { - apre++; - bpre++; + ++apre; + ++bpre; } // We used to backup and split on only .'s but it seems better to be verbose // and not assume . is the separator - const std::string prefix = std::string(a, apre - a); + const size_t prefix_len = apre - a; + const std::string prefix = std::string{a, prefix_len}; // Scan backward to last mismatch const char* apost = a + std::strlen(a) - 1; const char* bpost = b + std::strlen(b) - 1; while (*apost == *bpost && apost > apre && bpost > bpre) { - apost--; - bpost--; + --apost; + --bpost; } // Forward to . so we have a whole word - std::string suffix = *bpost ? std::string(bpost + 1) : ""; + std::string suffix = *bpost ? std::string{bpost + 1} : ""; std::string out = prefix + "*" + suffix; @@ -253,17 +254,17 @@ public: // PUBLIC METHODS void forcePerInstance(const bool flag) VL_MT_SAFE_EXCLUDES(m_mutex) { Verilated::quiesce(); - const VerilatedLockGuard lock(m_mutex); + const VerilatedLockGuard lock{m_mutex}; m_forcePerInstance = flag; } void clear() VL_MT_SAFE_EXCLUDES(m_mutex) { Verilated::quiesce(); - const VerilatedLockGuard lock(m_mutex); + const VerilatedLockGuard lock{m_mutex}; clearGuts(); } void clearNonMatch(const char* const matchp) VL_MT_SAFE_EXCLUDES(m_mutex) { Verilated::quiesce(); - const VerilatedLockGuard lock(m_mutex); + const VerilatedLockGuard lock{m_mutex}; if (matchp && matchp[0]) { ItemList newlist; for (const auto& itemp : m_items) { @@ -278,24 +279,24 @@ public: } void zero() VL_MT_SAFE_EXCLUDES(m_mutex) { Verilated::quiesce(); - const VerilatedLockGuard lock(m_mutex); + const VerilatedLockGuard lock{m_mutex}; for (const auto& itemp : m_items) itemp->zero(); } // We assume there's always call to i/f/p in that order void inserti(VerilatedCovImpItem* itemp) VL_MT_SAFE_EXCLUDES(m_mutex) { - const VerilatedLockGuard lock(m_mutex); + const VerilatedLockGuard lock{m_mutex}; assert(!m_insertp); m_insertp = itemp; } void insertf(const char* const filenamep, const int lineno) VL_MT_SAFE_EXCLUDES(m_mutex) { - const VerilatedLockGuard lock(m_mutex); + const VerilatedLockGuard lock{m_mutex}; m_insertFilenamep = filenamep; m_insertLineno = lineno; } void insertp(const char* ckeyps[VerilatedCovConst::MAX_KEYS], const char* valps[VerilatedCovConst::MAX_KEYS]) VL_MT_SAFE_EXCLUDES(m_mutex) { - const VerilatedLockGuard lock(m_mutex); + const VerilatedLockGuard lock{m_mutex}; assert(m_insertp); // First two key/vals are filename ckeyps[0] = "filename"; @@ -308,7 +309,8 @@ public: while (const char* foundp = std::strchr(fnstartp, '/')) fnstartp = foundp + 1; const char* fnendp = fnstartp; for (; *fnendp && *fnendp != '.'; fnendp++) {} - std::string page_default = "sp_user/" + std::string(fnstartp, fnendp - fnstartp); + const size_t page_len = fnendp - fnstartp; + const std::string page_default = "sp_user/" + std::string{fnstartp, page_len}; ckeyps[2] = "page"; valps[2] = page_default.c_str(); @@ -337,7 +339,7 @@ public: // cout<<" "<<__FUNCTION__<<" "<m_keys[addKeynum] = valueIndex(key); m_insertp->m_vals[addKeynum] = valueIndex(val); - addKeynum++; + ++addKeynum; if (VL_UNCOVERABLE(!legalKey(key))) { std::string msg = ("%Error: Coverage keys of one character, or letter+digit are illegal: " @@ -353,15 +355,15 @@ public: void write(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) { Verilated::quiesce(); - const VerilatedLockGuard lock(m_mutex); + const VerilatedLockGuard lock{m_mutex}; #ifndef VM_COVERAGE VL_FATAL_MT("", 0, "", "%Error: Called VerilatedCov::write when VM_COVERAGE disabled\n"); #endif selftest(); - std::ofstream os(filename); + std::ofstream os{filename}; if (os.fail()) { - std::string msg = std::string("%Error: Can't write '") + filename + "'"; + std::string msg = std::string{"%Error: Can't write '"} + filename + "'"; VL_FATAL_MT("", 0, "", msg.c_str()); return; } @@ -435,10 +437,10 @@ void VerilatedCovContext::clearNonMatch(const char* matchp) VL_MT_SAFE { void VerilatedCovContext::zero() VL_MT_SAFE { impp()->zero(); } void VerilatedCovContext::write(const char* filenamep) VL_MT_SAFE { impp()->write(filenamep); } void VerilatedCovContext::_inserti(vluint32_t* itemp) VL_MT_SAFE { - impp()->inserti(new VerilatedCoverItemSpec(itemp)); + impp()->inserti(new VerilatedCoverItemSpec{itemp}); } void VerilatedCovContext::_inserti(vluint64_t* itemp) VL_MT_SAFE { - impp()->inserti(new VerilatedCoverItemSpec(itemp)); + impp()->inserti(new VerilatedCoverItemSpec{itemp}); } void VerilatedCovContext::_insertf(const char* filename, int lineno) VL_MT_SAFE { impp()->insertf(filename, lineno); @@ -511,7 +513,7 @@ VerilatedCovContext* VerilatedCov::threadCovp() VL_MT_SAFE { VerilatedCovContext* VerilatedContext::coveragep() VL_MT_SAFE { static VerilatedMutex s_mutex; if (VL_UNLIKELY(!m_coveragep)) { - const VerilatedLockGuard lock(s_mutex); + const VerilatedLockGuard lock{s_mutex}; if (VL_LIKELY(!m_coveragep)) { // Not redundant, prevents race m_coveragep.reset(new VerilatedCovImp); } diff --git a/include/verilated_dpi.cpp b/include/verilated_dpi.cpp index 8ec3fc457..986de1453 100644 --- a/include/verilated_dpi.cpp +++ b/include/verilated_dpi.cpp @@ -184,7 +184,7 @@ static inline const VerilatedDpiOpenVar* _vl_openhandle_varp(const svOpenArrayHa VL_FATAL_MT(__FILE__, __LINE__, "", "%%Error: DPI svOpenArrayHandle function called with nullptr handle"); } - const VerilatedDpiOpenVar* varp = reinterpret_cast(h); + const VerilatedDpiOpenVar* const varp = reinterpret_cast(h); if (VL_UNLIKELY(!varp->magicOk())) { VL_FATAL_MT(__FILE__, __LINE__, "", "%%Error: DPI svOpenArrayHandle function called with non-Verilator handle"); @@ -205,13 +205,13 @@ int svDimensions(const svOpenArrayHandle h) { return _vl_openhandle_varp(h)->udi // Return pointer to open array data, or nullptr if not in IEEE standard C layout void* svGetArrayPtr(const svOpenArrayHandle h) { - const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(h); + const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(h); if (VL_UNLIKELY(!varp->isDpiStdLayout())) return nullptr; return varp->datap(); } // Return size of open array, or 0 if not in IEEE standard C layout int svSizeOfArray(const svOpenArrayHandle h) { - const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(h); + const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(h); if (VL_UNLIKELY(!varp->isDpiStdLayout())) return 0; // Truncate 64 bits to int; DPI is limited to 4GB return static_cast(varp->totalSize()); @@ -264,15 +264,15 @@ static void* _vl_svGetArrElemPtr(const svOpenArrayHandle h, int nargs, int indx1 int indx3) VL_MT_SAFE { const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(h); if (VL_UNLIKELY(!varp->isDpiStdLayout())) return nullptr; - void* datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3); + void* const datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3); return datap; } // Copy to user bit array from simulator open array static void _vl_svGetBitArrElemVecVal(svBitVecVal* d, const svOpenArrayHandle s, int nargs, int indx1, int indx2, int indx3) VL_MT_SAFE { - const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(s); - void* datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3); + const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(s); + void* const datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3); if (VL_UNLIKELY(!datap)) return; switch (varp->vltype()) { // LCOV_EXCL_BR_LINE case VLVT_UINT8: d[0] = *(reinterpret_cast(datap)); return; @@ -299,8 +299,8 @@ static void _vl_svGetBitArrElemVecVal(svBitVecVal* d, const svOpenArrayHandle s, // Copy to user logic array from simulator open array static void _vl_svGetLogicArrElemVecVal(svLogicVecVal* d, const svOpenArrayHandle s, int nargs, int indx1, int indx2, int indx3) VL_MT_SAFE { - const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(s); - void* datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3); + const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(s); + void* const datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3); if (VL_UNLIKELY(!datap)) return; switch (varp->vltype()) { // LCOV_EXCL_BR_LINE case VLVT_UINT8: @@ -342,8 +342,8 @@ static void _vl_svGetLogicArrElemVecVal(svLogicVecVal* d, const svOpenArrayHandl // Copy to simulator open array from from user bit array static void _vl_svPutBitArrElemVecVal(const svOpenArrayHandle d, const svBitVecVal* s, int nargs, int indx1, int indx2, int indx3) VL_MT_SAFE { - const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(d); - void* datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3); + const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(d); + void* const datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3); if (VL_UNLIKELY(!datap)) return; switch (varp->vltype()) { // LCOV_EXCL_BR_LINE case VLVT_UINT8: *(reinterpret_cast(datap)) = s[0]; return; @@ -364,8 +364,8 @@ static void _vl_svPutBitArrElemVecVal(const svOpenArrayHandle d, const svBitVecV // Copy to simulator open array from from user logic array static void _vl_svPutLogicArrElemVecVal(const svOpenArrayHandle d, const svLogicVecVal* s, int nargs, int indx1, int indx2, int indx3) VL_MT_SAFE { - const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(d); - void* datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3); + const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(d); + void* const datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3); if (VL_UNLIKELY(!datap)) return; switch (varp->vltype()) { // LCOV_EXCL_BR_LINE case VLVT_UINT8: *(reinterpret_cast(datap)) = s[0].aval; return; @@ -388,8 +388,8 @@ static void _vl_svPutLogicArrElemVecVal(const svOpenArrayHandle d, const svLogic static svBit _vl_svGetBitArrElem(const svOpenArrayHandle s, int nargs, int indx1, int indx2, int indx3, int indx4) VL_MT_SAFE { // One extra index supported, as need bit number - const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(s); - void* datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3); + const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(s); + void* const datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3); if (VL_UNLIKELY(!datap)) return 0; switch (varp->vltype()) { // LCOV_EXCL_BR_LINE case VLVT_UINT8: return (*(reinterpret_cast(datap))) & 1; @@ -404,8 +404,8 @@ static void _vl_svPutBitArrElem(const svOpenArrayHandle d, svBit value, int narg int indx2, int indx3, int indx4) VL_MT_SAFE { // One extra index supported, as need bit number value &= 1; // Make sure clean - const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(d); - void* datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3); + const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(d); + void* const datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3); if (VL_UNLIKELY(!datap)) return; switch (varp->vltype()) { // LCOV_EXCL_BR_LINE case VLVT_UINT8: *(reinterpret_cast(datap)) = value; return; @@ -420,7 +420,7 @@ static void _vl_svPutBitArrElem(const svOpenArrayHandle d, svBit value, int narg // DPI accessors that simply call above functions void* svGetArrElemPtr(const svOpenArrayHandle h, int indx1, ...) { - const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(h); + const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(h); void* datap; va_list ap; va_start(ap, indx1); @@ -454,7 +454,7 @@ void* svGetArrElemPtr3(const svOpenArrayHandle h, int indx1, int indx2, int indx } void svPutBitArrElemVecVal(const svOpenArrayHandle d, const svBitVecVal* s, int indx1, ...) { - const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(d); + const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(d); va_list ap; va_start(ap, indx1); switch (varp->udims()) { @@ -486,7 +486,7 @@ void svPutBitArrElem3VecVal(const svOpenArrayHandle d, const svBitVecVal* s, int _vl_svPutBitArrElemVecVal(d, s, 3, indx1, indx2, indx3); } void svPutLogicArrElemVecVal(const svOpenArrayHandle d, const svLogicVecVal* s, int indx1, ...) { - const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(d); + const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(d); va_list ap; va_start(ap, indx1); switch (varp->udims()) { @@ -522,7 +522,7 @@ void svPutLogicArrElem3VecVal(const svOpenArrayHandle d, const svLogicVecVal* s, // From simulator storage into user space void svGetBitArrElemVecVal(svBitVecVal* d, const svOpenArrayHandle s, int indx1, ...) { - const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(s); + const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(s); va_list ap; va_start(ap, indx1); switch (varp->udims()) { @@ -553,7 +553,7 @@ void svGetBitArrElem3VecVal(svBitVecVal* d, const svOpenArrayHandle s, int indx1 _vl_svGetBitArrElemVecVal(d, s, 3, indx1, indx2, indx3); } void svGetLogicArrElemVecVal(svLogicVecVal* d, const svOpenArrayHandle s, int indx1, ...) { - const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(s); + const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(s); va_list ap; va_start(ap, indx1); switch (varp->udims()) { @@ -585,7 +585,7 @@ void svGetLogicArrElem3VecVal(svLogicVecVal* d, const svOpenArrayHandle s, int i } svBit svGetBitArrElem(const svOpenArrayHandle s, int indx1, ...) { - const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(s); + const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(s); svBit out; va_list ap; va_start(ap, indx1); @@ -618,7 +618,7 @@ svBit svGetBitArrElem3(const svOpenArrayHandle s, int indx1, int indx2, int indx } svLogic svGetLogicArrElem(const svOpenArrayHandle s, int indx1, ...) { // Verilator doesn't support X/Z so can just call Bit version - const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(s); + const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(s); svBit out; va_list ap; va_start(ap, indx1); @@ -654,7 +654,7 @@ svLogic svGetLogicArrElem3(const svOpenArrayHandle s, int indx1, int indx2, int } void svPutBitArrElem(const svOpenArrayHandle d, svBit value, int indx1, ...) { - const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(d); + const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(d); va_list ap; va_start(ap, indx1); switch (varp->udims()) { @@ -685,7 +685,7 @@ void svPutBitArrElem3(const svOpenArrayHandle d, svBit value, int indx1, int ind } void svPutLogicArrElem(const svOpenArrayHandle d, svLogic value, int indx1, ...) { // Verilator doesn't support X/Z so can just call Bit version - const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(d); + const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(d); va_list ap; va_start(ap, indx1); switch (varp->udims()) { @@ -732,15 +732,15 @@ svScope svGetScope() { } svScope svSetScope(const svScope scope) { - const VerilatedScope* prevScopep = Verilated::dpiScope(); - const VerilatedScope* vscopep = reinterpret_cast(scope); + const VerilatedScope* const prevScopep = Verilated::dpiScope(); + const VerilatedScope* const vscopep = reinterpret_cast(scope); Verilated::dpiScope(vscopep); // NOLINTNEXTLINE(google-readability-casting) return (svScope)(prevScopep); } const char* svGetNameFromScope(const svScope scope) { - const VerilatedScope* vscopep = reinterpret_cast(scope); + const VerilatedScope* const vscopep = reinterpret_cast(scope); return vscopep->name(); } diff --git a/include/verilated_fst_c.cpp b/include/verilated_fst_c.cpp index 9d91029de..4aea35c20 100644 --- a/include/verilated_fst_c.cpp +++ b/include/verilated_fst_c.cpp @@ -98,7 +98,7 @@ VerilatedFst::~VerilatedFst() { } void VerilatedFst::open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) { - const VerilatedLockGuard lock(m_mutex); + const VerilatedLockGuard lock{m_mutex}; m_fst = fstWriterCreate(filename, 1); fstWriterSetPackType(m_fst, FST_WR_PT_LZ4); fstWriterSetTimescaleFromString(m_fst, timeResStr().c_str()); // lintok-begin-on-ref @@ -130,14 +130,14 @@ void VerilatedFst::open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) { } void VerilatedFst::close() VL_MT_SAFE_EXCLUDES(m_mutex) { - const VerilatedLockGuard lock(m_mutex); + const VerilatedLockGuard lock{m_mutex}; VerilatedTrace::closeBase(); fstWriterClose(m_fst); m_fst = nullptr; } void VerilatedFst::flush() VL_MT_SAFE_EXCLUDES(m_mutex) { - const VerilatedLockGuard lock(m_mutex); + const VerilatedLockGuard lock{m_mutex}; VerilatedTrace::flushBase(); fstWriterFlushContext(m_fst); } @@ -161,11 +161,11 @@ void VerilatedFst::declare(vluint32_t code, const char* name, int dtypenum, fstV VerilatedTrace::declCode(code, bits, false); - std::istringstream nameiss(name); + std::istringstream nameiss{name}; std::istream_iterator beg(nameiss); std::istream_iterator end; std::list tokens(beg, end); // Split name - std::string symbol_name(tokens.back()); + std::string symbol_name{tokens.back()}; tokens.pop_back(); // Remove symbol name from hierarchy tokens.insert(tokens.begin(), moduleName()); // Add current module to the hierarchy std::string tmpModName; diff --git a/include/verilated_heavy.h b/include/verilated_heavy.h index 150991a19..598a99400 100644 --- a/include/verilated_heavy.h +++ b/include/verilated_heavy.h @@ -316,7 +316,7 @@ public: }); } void reverse() { std::reverse(m_deque.begin(), m_deque.end()); } - void shuffle() { std::shuffle(m_deque.begin(), m_deque.end(), VlURNG()); } + void shuffle() { std::shuffle(m_deque.begin(), m_deque.end(), VlURNG{}); } VlQueue unique() const { VlQueue out; std::unordered_set saw; @@ -397,12 +397,12 @@ public: // Reduction operators VlQueue min() const { - if (m_deque.empty()) return VlQueue(); + if (m_deque.empty()) return VlQueue{}; const auto it = std::min_element(m_deque.begin(), m_deque.end()); return VlQueue::cons(*it); } VlQueue max() const { - if (m_deque.empty()) return VlQueue(); + if (m_deque.empty()) return VlQueue{}; const auto it = std::max_element(m_deque.begin(), m_deque.end()); return VlQueue::cons(*it); } @@ -774,7 +774,7 @@ std::string VL_TO_STRING(const VlAssocArray& obj) { template void VL_READMEM_N(bool hex, int bits, const std::string& filename, VlAssocArray& obj, QData start, QData end) VL_MT_SAFE { - VlReadMem rmem(hex, bits, filename, start, end); + VlReadMem rmem{hex, bits, filename, start, end}; if (VL_UNLIKELY(!rmem.isOpen())) return; while (true) { QData addr; @@ -790,7 +790,7 @@ void VL_READMEM_N(bool hex, int bits, const std::string& filename, template void VL_WRITEMEM_N(bool hex, int bits, const std::string& filename, const VlAssocArray& obj, QData start, QData end) VL_MT_SAFE { - VlWriteMem wmem(hex, bits, filename, start, end); + VlWriteMem wmem{hex, bits, filename, start, end}; if (VL_UNLIKELY(!wmem.isOpen())) return; for (const auto& i : obj) { const QData addr = i.first; diff --git a/include/verilated_imp.h b/include/verilated_imp.h index b4d13e0e0..fef103f7b 100644 --- a/include/verilated_imp.h +++ b/include/verilated_imp.h @@ -104,7 +104,7 @@ public: // METHODS // Add message to queue (called by producer) void post(const VerilatedMsg& msg) VL_MT_SAFE_EXCLUDES(m_mutex) { - const VerilatedLockGuard lock(m_mutex); + const VerilatedLockGuard lock{m_mutex}; m_queue.insert(msg); // Pass by value to copy the message into queue ++m_depth; } @@ -124,7 +124,7 @@ public: const VerilatedMsg msg = *(it); m_queue.erase(it); m_mutex.unlock(); - m_depth--; // Ok if outside critical section as only this code checks the value + --m_depth; // Ok if outside critical section as only this code checks the value { VL_DEBUG_IF(VL_DBG_MSGF("Executing callback from mtaskId=%d\n", msg.mtaskId());); msg.run(); @@ -256,11 +256,11 @@ public: // But only for verilated*.cpp int timeFormatWidth() const VL_MT_SAFE { return m_s.m_timeFormatWidth; } void timeFormatWidth(int value) VL_MT_SAFE { m_s.m_timeFormatWidth = value; } std::string timeFormatSuffix() const VL_MT_SAFE_EXCLUDES(m_timeDumpMutex) { - const VerilatedLockGuard lock(m_timeDumpMutex); + const VerilatedLockGuard lock{m_timeDumpMutex}; return m_timeFormatSuffix; } void timeFormatSuffix(const std::string& value) VL_MT_SAFE_EXCLUDES(m_timeDumpMutex) { - const VerilatedLockGuard lock(m_timeDumpMutex); + const VerilatedLockGuard lock{m_timeDumpMutex}; m_timeFormatSuffix = value; } @@ -276,7 +276,7 @@ public: // But only for verilated*.cpp public: // But only for verilated*.cpp // METHODS - file IO IData fdNewMcd(const char* filenamep) VL_MT_SAFE_EXCLUDES(m_fdMutex) { - const VerilatedLockGuard lock(m_fdMutex); + const VerilatedLockGuard lock{m_fdMutex}; if (m_fdFreeMct.empty()) return 0; IData idx = m_fdFreeMct.back(); m_fdFreeMct.pop_back(); @@ -285,10 +285,10 @@ public: // But only for verilated*.cpp return (1 << idx); } IData fdNew(const char* filenamep, const char* modep) VL_MT_SAFE_EXCLUDES(m_fdMutex) { - FILE* fp = std::fopen(filenamep, modep); + FILE* const fp = std::fopen(filenamep, modep); if (VL_UNLIKELY(!fp)) return 0; // Bit 31 indicates it's a descriptor not a MCD - const VerilatedLockGuard lock(m_fdMutex); + const VerilatedLockGuard lock{m_fdMutex}; if (m_fdFree.empty()) { // Need to create more space in m_fdps and m_fdFree const std::size_t start = std::max(31UL + 1UL + 3UL, m_fdps.size()); @@ -306,25 +306,25 @@ public: // But only for verilated*.cpp return (idx | (1UL << 31)); // bit 31 indicates not MCD } void fdFlush(IData fdi) VL_MT_SAFE_EXCLUDES(m_fdMutex) { - const VerilatedLockGuard lock(m_fdMutex); + const VerilatedLockGuard lock{m_fdMutex}; const VerilatedFpList fdlist = fdToFpList(fdi); for (const auto& i : fdlist) std::fflush(i); } IData fdSeek(IData fdi, IData offset, IData origin) VL_MT_SAFE_EXCLUDES(m_fdMutex) { - const VerilatedLockGuard lock(m_fdMutex); + const VerilatedLockGuard lock{m_fdMutex}; const VerilatedFpList fdlist = fdToFpList(fdi); if (VL_UNLIKELY(fdlist.size() != 1)) return 0; return static_cast( std::fseek(*fdlist.begin(), static_cast(offset), static_cast(origin))); } IData fdTell(IData fdi) VL_MT_SAFE_EXCLUDES(m_fdMutex) { - const VerilatedLockGuard lock(m_fdMutex); + const VerilatedLockGuard lock{m_fdMutex}; const VerilatedFpList fdlist = fdToFpList(fdi); if (VL_UNLIKELY(fdlist.size() != 1)) return 0; return static_cast(std::ftell(*fdlist.begin())); } void fdWrite(IData fdi, const std::string& output) VL_MT_SAFE_EXCLUDES(m_fdMutex) { - const VerilatedLockGuard lock(m_fdMutex); + const VerilatedLockGuard lock{m_fdMutex}; const VerilatedFpList fdlist = fdToFpList(fdi); for (const auto& i : fdlist) { if (VL_UNLIKELY(!i)) continue; @@ -332,7 +332,7 @@ public: // But only for verilated*.cpp } } void fdClose(IData fdi) VL_MT_SAFE_EXCLUDES(m_fdMutex) { - const VerilatedLockGuard lock(m_fdMutex); + const VerilatedLockGuard lock{m_fdMutex}; if (VL_BITISSET_I(fdi, 31)) { // Non-MCD case IData idx = VL_MASK_I(31) & fdi; @@ -356,7 +356,7 @@ public: // But only for verilated*.cpp } } inline FILE* fdToFp(IData fdi) VL_MT_SAFE_EXCLUDES(m_fdMutex) { - const VerilatedLockGuard lock(m_fdMutex); + const VerilatedLockGuard lock{m_fdMutex}; const VerilatedFpList fdlist = fdToFpList(fdi); if (VL_UNLIKELY(fdlist.size() != 1)) return nullptr; return *fdlist.begin(); @@ -463,7 +463,7 @@ public: // per map overhead * N scopes would take much more space and cache thrashing. // As scopep's are pointers, this implicitly handles multiple Context's static inline void userInsert(const void* scopep, void* userKey, void* userData) VL_MT_SAFE { - const VerilatedLockGuard lock(s().m_userMapMutex); + const VerilatedLockGuard lock{s().m_userMapMutex}; const auto it = s().m_userMap.find(std::make_pair(scopep, userKey)); if (it != s().m_userMap.end()) { it->second = userData; @@ -472,7 +472,7 @@ public: } } static inline void* userFind(const void* scopep, void* userKey) VL_MT_SAFE { - const VerilatedLockGuard lock(s().m_userMapMutex); + const VerilatedLockGuard lock{s().m_userMapMutex}; const auto& it = vlstd::as_const(s().m_userMap).find(std::make_pair(scopep, userKey)); if (VL_UNLIKELY(it == s().m_userMap.end())) return nullptr; return it->second; @@ -482,7 +482,7 @@ public: // But only for verilated.cpp // Symbol table destruction cleans up the entries for each scope. static void userEraseScope(const VerilatedScope* scopep) VL_MT_SAFE { // Slow ok - called once/scope on destruction, so we simply iterate. - const VerilatedLockGuard lock(s().m_userMapMutex); + const VerilatedLockGuard lock{s().m_userMapMutex}; for (auto it = s().m_userMap.begin(); it != s().m_userMap.end();) { if (it->first.first == scopep) { s().m_userMap.erase(it++); @@ -492,7 +492,7 @@ public: // But only for verilated.cpp } } static void userDump() VL_MT_SAFE { - const VerilatedLockGuard lock(s().m_userMapMutex); // Avoid it changing in middle of dump + const VerilatedLockGuard lock{s().m_userMapMutex}; // Avoid it changing in middle of dump bool first = true; for (const auto& i : s().m_userMap) { if (first) { @@ -508,13 +508,13 @@ public: // But only for verilated*.cpp // METHODS - hierarchy static void hierarchyAdd(const VerilatedScope* fromp, const VerilatedScope* top) VL_MT_SAFE { // Slow ok - called at construction for VPI accessible elements - const VerilatedLockGuard lock(s().m_hierMapMutex); + const VerilatedLockGuard lock{s().m_hierMapMutex}; s().m_hierMap[fromp].push_back(top); } static void hierarchyRemove(const VerilatedScope* fromp, const VerilatedScope* top) VL_MT_SAFE { // Slow ok - called at destruction for VPI accessible elements - const VerilatedLockGuard lock(s().m_hierMapMutex); + const VerilatedLockGuard lock{s().m_hierMapMutex}; VerilatedHierarchyMap& map = s().m_hierMap; if (map.find(fromp) == map.end()) return; auto& scopes = map[fromp]; @@ -537,7 +537,7 @@ public: // But only for verilated*.cpp // miss at the cost of a multiply, and all lookups move to slowpath. static int exportInsert(const char* namep) VL_MT_SAFE { // Slow ok - called once/function at creation - const VerilatedLockGuard lock(s().m_exportMutex); + const VerilatedLockGuard lock{s().m_exportMutex}; const auto it = s().m_exportMap.find(namep); if (it == s().m_exportMap.end()) { s().m_exportMap.emplace(namep, s().m_exportNext++); @@ -547,24 +547,24 @@ public: // But only for verilated*.cpp } } static int exportFind(const char* namep) VL_MT_SAFE { - const VerilatedLockGuard lock(s().m_exportMutex); + const VerilatedLockGuard lock{s().m_exportMutex}; const auto& it = s().m_exportMap.find(namep); if (VL_LIKELY(it != s().m_exportMap.end())) return it->second; - std::string msg = (std::string("%Error: Testbench C called ") + namep + std::string msg = (std::string{"%Error: Testbench C called "} + namep + " but no such DPI export function name exists in ANY model"); VL_FATAL_MT("unknown", 0, "", msg.c_str()); return -1; } static const char* exportName(int funcnum) VL_MT_SAFE { // Slowpath; find name for given export; errors only so no map to reverse-map it - const VerilatedLockGuard lock(s().m_exportMutex); + const VerilatedLockGuard lock{s().m_exportMutex}; for (const auto& i : s().m_exportMap) { if (i.second == funcnum) return i.first; } return "*UNKNOWN*"; } static void exportsDump() VL_MT_SAFE { - const VerilatedLockGuard lock(s().m_exportMutex); + const VerilatedLockGuard lock{s().m_exportMutex}; bool first = true; for (const auto& i : s().m_exportMap) { if (first) { diff --git a/include/verilated_save.cpp b/include/verilated_save.cpp index 0c07022ea..2fec9808d 100644 --- a/include/verilated_save.cpp +++ b/include/verilated_save.cpp @@ -93,8 +93,8 @@ void VerilatedDeserialize::header() VL_MT_UNSAFE_ONE { if (VL_UNLIKELY(os.readDiffers(VLTSAVE_HEADER_STR, std::strlen(VLTSAVE_HEADER_STR)))) { const std::string fn = filename(); const std::string msg - = std::string( - "Can't deserialize; file has wrong header signature, or file not found: ") + = std:: + string{"Can't deserialize; file has wrong header signature, or file not found: "} + filename(); VL_FATAL_MT(fn.c_str(), 0, "", msg.c_str()); // Die before we close() as close would infinite loop @@ -112,7 +112,7 @@ void VerilatedDeserialize::trailer() VL_MT_UNSAFE_ONE { if (VL_UNLIKELY(os.readDiffers(VLTSAVE_TRAILER_STR, std::strlen(VLTSAVE_TRAILER_STR)))) { const std::string fn = filename(); const std::string msg - = std::string("Can't deserialize; file has wrong end-of-file signature: ") + = std::string{"Can't deserialize; file has wrong end-of-file signature: "} + filename(); VL_FATAL_MT(fn.c_str(), 0, "", msg.c_str()); // Die before we close() as close would infinite loop @@ -204,7 +204,7 @@ void VerilatedSave::flush() VL_MT_UNSAFE_ONE { if (VL_UNCOVERABLE(errno != EAGAIN && errno != EINTR)) { // LCOV_EXCL_START // write failed, presume error (perhaps out of disk space) - std::string msg = std::string(__FUNCTION__) + ": " + std::strerror(errno); + std::string msg = std::string{__FUNCTION__} + ": " + std::strerror(errno); VL_FATAL_MT("", 0, "", msg.c_str()); close(); break; @@ -235,7 +235,7 @@ void VerilatedRestore::fill() VL_MT_UNSAFE_ONE { if (VL_UNCOVERABLE(errno != EAGAIN && errno != EINTR)) { // LCOV_EXCL_START // write failed, presume error (perhaps out of disk space) - const std::string msg = std::string(__FUNCTION__) + ": " + std::strerror(errno); + const std::string msg = std::string{__FUNCTION__} + ": " + std::strerror(errno); VL_FATAL_MT("", 0, "", msg.c_str()); close(); break; diff --git a/include/verilated_threads.cpp b/include/verilated_threads.cpp index 8d3480540..99e59e444 100644 --- a/include/verilated_threads.cpp +++ b/include/verilated_threads.cpp @@ -104,7 +104,7 @@ VlThreadPool::VlThreadPool(VerilatedContext* contextp, int nThreads, bool profil } // Create'em for (int i = 0; i < nThreads; ++i) { - m_workers.push_back(new VlWorkerThread(this, contextp, profiling)); + m_workers.push_back(new VlWorkerThread{this, contextp, profiling}); } // Set up a profile buffer for the current thread too -- on the // assumption that it's the same thread that calls eval and may be @@ -131,13 +131,13 @@ void VlThreadPool::setupProfilingClientThread() VL_MT_SAFE_EXCLUDES(m_mutex) { // try not to malloc while collecting profiling. t_profilep->reserve(4096); { - const VerilatedLockGuard lk(m_mutex); + const VerilatedLockGuard lock{m_mutex}; m_allProfiles.insert(t_profilep); } } void VlThreadPool::profileAppendAll(const VlProfileRec& rec) VL_MT_SAFE_EXCLUDES(m_mutex) { - const VerilatedLockGuard lk(m_mutex); + const VerilatedLockGuard lock{m_mutex}; for (const auto& profilep : m_allProfiles) { // Every thread's profile trace gets a copy of rec. profilep->emplace_back(rec); @@ -146,7 +146,7 @@ void VlThreadPool::profileAppendAll(const VlProfileRec& rec) VL_MT_SAFE_EXCLUDES void VlThreadPool::profileDump(const char* filenamep, vluint64_t ticksElapsed) VL_MT_SAFE_EXCLUDES(m_mutex) { - const VerilatedLockGuard lk(m_mutex); + const VerilatedLockGuard lock{m_mutex}; VL_DEBUG_IF(VL_DBG_MSGF("+prof+threads writing to '%s'\n", filenamep);); FILE* const fp = std::fopen(filenamep, "w"); diff --git a/include/verilated_threads.h b/include/verilated_threads.h index b6163477b..150168f80 100644 --- a/include/verilated_threads.h +++ b/include/verilated_threads.h @@ -225,10 +225,10 @@ public: } VL_CPU_RELAX(); } - VerilatedLockGuard lk(m_mutex); + VerilatedLockGuard lock{m_mutex}; while (m_ready.empty()) { m_waiting = true; - m_cv.wait(lk); + m_cv.wait(lock); } m_waiting = false; // As noted above this is inefficient if our ready list is ever @@ -242,7 +242,7 @@ public: VL_MT_SAFE_EXCLUDES(m_mutex) { bool notify; { - const VerilatedLockGuard lk(m_mutex); + const VerilatedLockGuard lock{m_mutex}; m_ready.emplace_back(fnp, selfp, evenCycle); m_ready_size.fetch_add(1, std::memory_order_relaxed); notify = m_waiting; diff --git a/include/verilated_trace.h b/include/verilated_trace.h index db3b71efc..6e7d6c59e 100644 --- a/include/verilated_trace.h +++ b/include/verilated_trace.h @@ -52,21 +52,21 @@ private: public: // Put an element at the back of the queue void put(T value) VL_MT_SAFE_EXCLUDES(m_mutex) { - VerilatedLockGuard lock(m_mutex); + const VerilatedLockGuard lock{m_mutex}; m_queue.push_back(value); m_cv.notify_one(); } // Put an element at the front of the queue void put_front(T value) VL_MT_SAFE_EXCLUDES(m_mutex) { - VerilatedLockGuard lock(m_mutex); + const VerilatedLockGuard lock{m_mutex}; m_queue.push_front(value); m_cv.notify_one(); } // Get an element from the front of the queue. Blocks if none available T get() VL_MT_SAFE_EXCLUDES(m_mutex) { - VerilatedLockGuard lock(m_mutex); + VerilatedLockGuard lock{m_mutex}; m_cv.wait(lock, [this]() VL_REQUIRES(m_mutex) { return !m_queue.empty(); }); assert(!m_queue.empty()); T value = m_queue.front(); @@ -76,7 +76,7 @@ public: // Non blocking get bool tryGet(T& result) VL_MT_SAFE_EXCLUDES(m_mutex) { - const VerilatedLockGuard lockGuard(m_mutex); + const VerilatedLockGuard lockGuard{m_mutex}; if (m_queue.empty()) return false; result = m_queue.front(); m_queue.pop_front(); diff --git a/include/verilated_trace_imp.cpp b/include/verilated_trace_imp.cpp index 6a31b6cd0..07e60f7a3 100644 --- a/include/verilated_trace_imp.cpp +++ b/include/verilated_trace_imp.cpp @@ -246,7 +246,7 @@ template <> void VerilatedTrace::closeBase() { shutdownWorker(); while (m_numTraceBuffers) { delete[] m_buffersFromWorker.get(); - m_numTraceBuffers--; + --m_numTraceBuffers; } #endif } @@ -351,7 +351,7 @@ template <> void VerilatedTrace::traceInit() VL_MT_UNSAFE { m_traceBufferSize = nextCode() + numSignals() * 2 + 4; // Start the worker thread - m_workerThread.reset(new std::thread(&VerilatedTrace::workerThreadMain, this)); + m_workerThread.reset(new std::thread{&VerilatedTrace::workerThreadMain, this}); #endif } @@ -398,7 +398,7 @@ void VerilatedTrace::dump(vluint64_t timeui) VL_MT_SAFE_EXCLUDES(m // Not really VL_MT_SAFE but more VL_MT_UNSAFE_ONE. // This does get the mutex, but if multiple threads are trying to dump // chances are the data being dumped will have other problems - const VerilatedLockGuard lock(m_mutex); + const VerilatedLockGuard lock{m_mutex}; if (VL_UNCOVERABLE(m_timeLastDump && timeui <= m_timeLastDump)) { // LCOV_EXCL_START VL_PRINTF_MT("%%Warning: previous dump at t=%" VL_PRI64 "u, requesting t=%" VL_PRI64 "u, dump call ignored\n", @@ -479,9 +479,9 @@ template <> void VerilatedTrace::addCallbackRecord(std::vector& cbVec, CallbackRecord& cbRec) VL_MT_SAFE_EXCLUDES(m_mutex) { - const VerilatedLockGuard lock(m_mutex); + const VerilatedLockGuard lock{m_mutex}; if (VL_UNCOVERABLE(timeLastDump() != 0)) { // LCOV_EXCL_START - std::string msg = (std::string("Internal: ") + __FILE__ + "::" + __FUNCTION__ + std::string msg = (std::string{"Internal: "} + __FILE__ + "::" + __FUNCTION__ + " called with already open file"); VL_FATAL_MT(__FILE__, __LINE__, "", msg.c_str()); } // LCOV_EXCL_STOP @@ -489,19 +489,19 @@ void VerilatedTrace::addCallbackRecord(std::vector } template <> void VerilatedTrace::addInitCb(initCb_t cb, void* userp) VL_MT_SAFE { - CallbackRecord cbr(cb, userp); + CallbackRecord cbr{cb, userp}; addCallbackRecord(m_initCbs, cbr); } template <> void VerilatedTrace::addFullCb(dumpCb_t cb, void* userp) VL_MT_SAFE { - CallbackRecord cbr(cb, userp); + CallbackRecord cbr{cb, userp}; addCallbackRecord(m_fullCbs, cbr); } template <> void VerilatedTrace::addChgCb(dumpCb_t cb, void* userp) VL_MT_SAFE { - CallbackRecord cbr(cb, userp); + CallbackRecord cbr{cb, userp}; addCallbackRecord(m_chgCbs, cbr); } template <> void VerilatedTrace::addCleanupCb(dumpCb_t cb, void* userp) VL_MT_SAFE { - CallbackRecord cbr(cb, userp); + CallbackRecord cbr{cb, userp}; addCallbackRecord(m_cleanupCbs, cbr); } template <> void VerilatedTrace::module(const std::string& name) VL_MT_UNSAFE { diff --git a/include/verilated_vcd_c.cpp b/include/verilated_vcd_c.cpp index 798618643..de7433498 100644 --- a/include/verilated_vcd_c.cpp +++ b/include/verilated_vcd_c.cpp @@ -102,7 +102,7 @@ VerilatedVcd::VerilatedVcd(VerilatedVcdFile* filep) { } void VerilatedVcd::open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) { - const VerilatedLockGuard lock(m_mutex); + const VerilatedLockGuard lock{m_mutex}; if (isOpen()) return; // Set member variables @@ -120,7 +120,7 @@ void VerilatedVcd::open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) { void VerilatedVcd::openNext(bool incFilename) VL_MT_SAFE_EXCLUDES(m_mutex) { // Open next filename in concat sequence, mangle filename if // incFilename is true. - const VerilatedLockGuard lock(m_mutex); + const VerilatedLockGuard lock{m_mutex}; openNextImp(incFilename); } @@ -195,11 +195,11 @@ void VerilatedVcd::makeNameMap() { if (!hiername.empty() && hiername[0] == '\t') nullScope = true; } if (nullScope) { - NameMap* newmapp = new NameMap; + NameMap* const newmapp = new NameMap; for (const auto& i : *m_namemapp) { const std::string& hiername = i.first; const std::string& decl = i.second; - std::string newname = std::string("top"); + std::string newname{"top"}; if (hiername[0] != '\t') newname += ' '; newname += hiername; newmapp->emplace(newname, decl); @@ -243,7 +243,7 @@ void VerilatedVcd::closeErr() { void VerilatedVcd::close() VL_MT_SAFE_EXCLUDES(m_mutex) { // This function is on the flush() call path - const VerilatedLockGuard lock(m_mutex); + const VerilatedLockGuard lock{m_mutex}; if (!isOpen()) return; if (m_evcd) { printStr("$vcdclose "); @@ -257,7 +257,7 @@ void VerilatedVcd::close() VL_MT_SAFE_EXCLUDES(m_mutex) { } void VerilatedVcd::flush() VL_MT_SAFE_EXCLUDES(m_mutex) { - const VerilatedLockGuard lock(m_mutex); + const VerilatedLockGuard lock{m_mutex}; VerilatedTrace::flushBase(); bufferFlush(); } @@ -311,7 +311,7 @@ void VerilatedVcd::bufferFlush() VL_MT_UNSAFE_ONE { // LCOV_EXCL_START // write failed, presume error (perhaps out of disk space) std::string msg - = std::string("VerilatedVcd::bufferFlush: ") + std::strerror(errno); + = std::string{"VerilatedVcd::bufferFlush: "} + std::strerror(errno); VL_FATAL_MT("", 0, "", msg.c_str()); closeErr(); break; @@ -331,7 +331,7 @@ char* VerilatedVcd::writeCode(char* writep, vluint32_t code) { *writep++ = static_cast('!' + code % 94); code /= 94; while (code) { - code--; + --code; *writep++ = static_cast('!' + code % 94); code /= 94; } @@ -393,8 +393,8 @@ void VerilatedVcd::dumpHeader() { // Skip common prefix, it must break at a space or tab for (; *np && (*np == *lp); np++, lp++) {} while (np != hiername && *np && *np != ' ' && *np != '\t') { - np--; - lp--; + --np; + --lp; } // printf("hier %s\n lp=%s\n np=%s\n",hiername,lp,np); @@ -896,9 +896,9 @@ void vcdTestMain(const char* filenamep) { VerilatedVcdC* vcdp = new VerilatedVcdC; vcdp->evcd(true); vcdp->set_time_unit("1ms"); - vcdp->set_time_unit(std::string("1ms")); + vcdp->set_time_unit(std::string{"1ms"}); vcdp->set_time_resolution("1ns"); - vcdp->set_time_resolution(std::string("1ns")); + vcdp->set_time_resolution(std::string{"1ns"}); vcdp->spTrace()->addInitCb(&vcdInit, 0); vcdp->spTrace()->addFullCb(&vcdFull, 0); vcdp->spTrace()->addChgCb(&vcdChange, 0); @@ -927,7 +927,7 @@ void vcdTestMain(const char* filenamep) { vcdp->dump(++timestamp); vcdp->dump(++timestamp); # ifdef VERILATED_VCD_TEST_64BIT - vluint64_t bytesPerDump = 15ULL; + const vluint64_t bytesPerDump = 15ULL; for (vluint64_t i = 0; i < ((1ULL << 32) / bytesPerDump); i++) { v1 = i; vcdp->dump(++timestamp); diff --git a/include/verilated_vcd_sc.h b/include/verilated_vcd_sc.h index de6e6d943..a79a9c61a 100644 --- a/include/verilated_vcd_sc.h +++ b/include/verilated_vcd_sc.h @@ -44,9 +44,9 @@ public: // We want to avoid a depreciated warning, but still be back compatible. // Turning off the message just for this still results in an // annoying "to turn off" message. - const sc_time t1sec(1, SC_SEC); + const sc_time t1sec{1, SC_SEC}; if (t1sec.to_default_time_units() != 0) { - const sc_time tunits(1.0 / t1sec.to_default_time_units(), SC_SEC); + const sc_time tunits{1.0 / t1sec.to_default_time_units(), SC_SEC}; spTrace()->set_time_unit(tunits.to_string()); } spTrace()->set_time_resolution(sc_get_time_resolution().to_string()); diff --git a/include/verilated_vpi.cpp b/include/verilated_vpi.cpp index eb9e254a3..c354d89f1 100644 --- a/include/verilated_vpi.cpp +++ b/include/verilated_vpi.cpp @@ -80,7 +80,7 @@ public: static const size_t chunk = 96; if (VL_UNCOVERABLE(size > chunk)) VL_FATAL_MT(__FILE__, __LINE__, "", "increase chunk"); if (VL_LIKELY(t_freeHead)) { - vluint8_t* newp = t_freeHead; + vluint8_t* const newp = t_freeHead; t_freeHead = *(reinterpret_cast(newp)); *(reinterpret_cast(newp)) = activeMagic(); return newp + 8; @@ -91,7 +91,7 @@ public: return newp + 8; } static void operator delete(void* obj, size_t /*size*/)VL_MT_SAFE { - vluint8_t* oldp = (static_cast(obj)) - 8; + vluint8_t* const oldp = (static_cast(obj)) - 8; if (VL_UNLIKELY(*(reinterpret_cast(oldp)) != activeMagic())) { VL_FATAL_MT(__FILE__, __LINE__, "", "vpi_release_handle() called on same object twice, or on non-Verilator " @@ -128,7 +128,7 @@ class VerilatedVpioTimedCb final : public VerilatedVpio { public: VerilatedVpioTimedCb(vluint64_t id, QData time) - : m_id(id) + : m_id{id} , m_time{time} {} virtual ~VerilatedVpioTimedCb() override = default; static VerilatedVpioTimedCb* castp(vpiHandle h) { @@ -147,7 +147,7 @@ class VerilatedVpioReasonCb final : public VerilatedVpio { public: // cppcheck-suppress uninitVar // m_value VerilatedVpioReasonCb(vluint64_t id, PLI_INT32 reason) - : m_id(id) + : m_id{id} , m_reason{reason} {} virtual ~VerilatedVpioReasonCb() override = default; static VerilatedVpioReasonCb* castp(vpiHandle h) { @@ -200,7 +200,7 @@ public: virtual const char* name() const override { return m_varp->name(); } virtual const char* fullname() const override { static VL_THREAD_LOCAL std::string t_out; - t_out = std::string(m_scopep->name()) + "." + name(); + t_out = std::string{m_scopep->name()} + "." + name(); return t_out.c_str(); } }; @@ -208,7 +208,7 @@ public: class VerilatedVpioParam final : public VerilatedVpioVarBase { public: VerilatedVpioParam(const VerilatedVar* varp, const VerilatedScope* scopep) - : VerilatedVpioVarBase(varp, scopep) {} + : VerilatedVpioVarBase{varp, scopep} {} virtual ~VerilatedVpioParam() override = default; static VerilatedVpioParam* castp(vpiHandle h) { @@ -252,7 +252,7 @@ public: return nullptr; } m_done = true; - return ((new VerilatedVpioRange(m_range))->castVpiHandle()); + return ((new VerilatedVpioRange{m_range})->castVpiHandle()); } }; @@ -286,13 +286,13 @@ protected: public: VerilatedVpioVar(const VerilatedVar* varp, const VerilatedScope* scopep) - : VerilatedVpioVarBase(varp, scopep) { + : VerilatedVpioVarBase{varp, scopep} { m_mask.u32 = VL_MASK_I(varp->packed().elements()); m_entSize = varp->entSize(); m_varDatap = varp->datap(); } explicit VerilatedVpioVar(const VerilatedVpioVar* varp) - : VerilatedVpioVarBase(varp) { + : VerilatedVpioVarBase{varp} { if (varp) { m_mask.u32 = varp->m_mask.u32; m_entSize = varp->m_entSize; @@ -345,7 +345,7 @@ public: static VL_THREAD_LOCAL std::string t_out; char num[25]; VL_SNPRINTF(num, 25, "%d", m_index); - t_out = std::string(scopep()->name()) + "." + name() + "[" + num + "]"; + t_out = std::string{scopep()->name()} + "." + name() + "[" + num + "]"; return t_out.c_str(); } }; @@ -379,7 +379,7 @@ public: delete this; // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle return nullptr; } - return ((new VerilatedVpioVar(&(m_it->second), m_scopep))->castVpiHandle()); + return ((new VerilatedVpioVar{&(m_it->second), m_scopep})->castVpiHandle()); } delete this; // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle return nullptr; // End of list - only one deep @@ -457,7 +457,7 @@ public: return nullptr; } const VerilatedScope* modp = *m_it++; - return (new VerilatedVpioModule(modp))->castVpiHandle(); + return (new VerilatedVpioModule{modp})->castVpiHandle(); } }; @@ -475,9 +475,9 @@ class VerilatedVpiCbHolder final { public: // cppcheck-suppress uninitVar // m_value VerilatedVpiCbHolder(vluint64_t id, const s_cb_data* cbDatap, const VerilatedVpioVar* varop) - : m_id(id) - , m_cbData(*cbDatap) - , m_varo(varop) { + : m_id{id} + , m_cbData{*cbDatap} + , m_varo{varop} { m_value.format = cbDatap->value ? cbDatap->value->format : vpiSuppressVal; m_cbData.value = &m_value; if (varop) { @@ -626,9 +626,10 @@ public: continue; } VerilatedVpiCbHolder& ho = *it++; - if (VerilatedVpioVar* varop = VerilatedVpioVar::castp(ho.cb_datap()->obj)) { - void* newDatap = varop->varDatap(); - void* prevDatap = varop->prevDatap(); // Was malloced when we added the callback + if (VerilatedVpioVar* const varop = VerilatedVpioVar::castp(ho.cb_datap()->obj)) { + void* const newDatap = varop->varDatap(); + void* const prevDatap + = varop->prevDatap(); // Was malloced when we added the callback VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: value_test %s v[0]=%d/%d %p %p\n", varop->fullname(), *((CData*)newDatap), *((CData*)prevDatap), newDatap, prevDatap);); @@ -758,7 +759,7 @@ PLI_INT32 VerilatedVpioReasonCb::dovpi_remove_cb() { VerilatedVpiError* VerilatedVpiImp::error_info() VL_MT_UNSAFE_ONE { VerilatedVpiImp::assertOneCheck(); - if (VL_UNLIKELY(!s().m_errorInfop)) s().m_errorInfop = new VerilatedVpiError(); + if (VL_UNLIKELY(!s().m_errorInfop)) s().m_errorInfop = new VerilatedVpiError; return s().m_errorInfop; } @@ -1108,13 +1109,13 @@ const char* VerilatedVpiError::strFromVpiProp(PLI_INT32 vpiVal) VL_MT_SAFE { #define SELF_CHECK_RESULT_CSTR(got, exp) \ if (0 != std::strcmp((got), (exp))) { \ std::string msg \ - = std::string("%Error: ") + "GOT = '" + got + "'" + " EXP = '" + exp + "'"; \ + = std::string{"%Error: "} + "GOT = '" + got + "'" + " EXP = '" + exp + "'"; \ VL_FATAL_MT(__FILE__, __LINE__, "", msg.c_str()); \ } #define SELF_CHECK_ENUM_STR(fn, enumn) \ do { \ - const char* strVal = VerilatedVpiError::fn(enumn); \ + const char* const strVal = VerilatedVpiError::fn(enumn); \ SELF_CHECK_RESULT_CSTR(strVal, #enumn); \ } while (0) @@ -1358,7 +1359,7 @@ vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) { const VerilatedVpioScope* const voScopep = VerilatedVpioScope::castp(scope); std::string scopeAndName = namep; if (voScopep) { - scopeAndName = std::string(voScopep->fullname()) + "." + namep; + scopeAndName = std::string{voScopep->fullname()} + "." + namep; namep = const_cast(scopeAndName.c_str()); } { @@ -1366,9 +1367,9 @@ vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) { scopep = Verilated::threadContextp()->scopeFind(namep); if (scopep) { // Whole thing found as a scope if (scopep->type() == VerilatedScope::SCOPE_MODULE) { - return (new VerilatedVpioModule(scopep))->castVpiHandle(); + return (new VerilatedVpioModule{scopep})->castVpiHandle(); } else { - return (new VerilatedVpioScope(scopep))->castVpiHandle(); + return (new VerilatedVpioScope{scopep})->castVpiHandle(); } } const char* baseNamep = scopeAndName.c_str(); @@ -1376,7 +1377,8 @@ vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) { const char* const dotp = std::strrchr(namep, '.'); if (VL_LIKELY(dotp)) { baseNamep = dotp + 1; - scopename = std::string(namep, dotp - namep); + const size_t len = dotp - namep; + scopename = std::string{namep, len}; } if (scopename.find('.') == std::string::npos) { @@ -1393,9 +1395,9 @@ vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) { if (!varp) return nullptr; if (varp->isParam()) { - return (new VerilatedVpioParam(varp, scopep))->castVpiHandle(); + return (new VerilatedVpioParam{varp, scopep})->castVpiHandle(); } else { - return (new VerilatedVpioVar(varp, scopep))->castVpiHandle(); + return (new VerilatedVpioVar{varp, scopep})->castVpiHandle(); } } @@ -1405,7 +1407,7 @@ vpiHandle vpi_handle_by_index(vpiHandle object, PLI_INT32 indx) { VerilatedVpiImp::assertOneCheck(); VL_VPI_ERROR_RESET_(); // Memory words are not indexable - VerilatedVpioMemoryWord* vop = VerilatedVpioMemoryWord::castp(object); + VerilatedVpioMemoryWord* const vop = VerilatedVpioMemoryWord::castp(object); if (VL_UNLIKELY(vop)) return nullptr; const VerilatedVpioVar* const varop = VerilatedVpioVar::castp(object); if (VL_LIKELY(varop)) { @@ -1414,15 +1416,15 @@ vpiHandle vpi_handle_by_index(vpiHandle object, PLI_INT32 indx) { if (VL_UNLIKELY(indx > varop->varp()->unpacked().left() || indx < varop->varp()->unpacked().right())) return nullptr; - return (new VerilatedVpioMemoryWord(varop->varp(), varop->scopep(), indx, - indx - varop->varp()->unpacked().right())) + return (new VerilatedVpioMemoryWord{varop->varp(), varop->scopep(), indx, + indx - varop->varp()->unpacked().right()}) ->castVpiHandle(); } if (VL_UNLIKELY(indx < varop->varp()->unpacked().left() || indx > varop->varp()->unpacked().right())) return nullptr; - return (new VerilatedVpioMemoryWord(varop->varp(), varop->scopep(), indx, - indx - varop->varp()->unpacked().left())) + return (new VerilatedVpioMemoryWord{varop->varp(), varop->scopep(), indx, + indx - varop->varp()->unpacked().left()}) ->castVpiHandle(); } VL_VPI_INTERNAL_(__FILE__, __LINE__, "%s : can't resolve handle", __func__); @@ -1437,12 +1439,12 @@ vpiHandle vpi_handle(PLI_INT32 type, vpiHandle object) { VL_VPI_ERROR_RESET_(); switch (type) { case vpiLeftRange: { - if (VerilatedVpioVarBase* vop = VerilatedVpioVarBase::castp(object)) { + if (VerilatedVpioVarBase* const vop = VerilatedVpioVarBase::castp(object)) { if (VL_UNLIKELY(!vop->rangep())) return nullptr; - return (new VerilatedVpioConst(vop->rangep()->left()))->castVpiHandle(); - } else if (VerilatedVpioRange* vop = VerilatedVpioRange::castp(object)) { + return (new VerilatedVpioConst{vop->rangep()->left()})->castVpiHandle(); + } else if (VerilatedVpioRange* const vop = VerilatedVpioRange::castp(object)) { if (VL_UNLIKELY(!vop->rangep())) return nullptr; - return (new VerilatedVpioConst(vop->rangep()->left()))->castVpiHandle(); + return (new VerilatedVpioConst{vop->rangep()->left()})->castVpiHandle(); } VL_VPI_WARNING_(__FILE__, __LINE__, "%s: Unsupported vpiHandle (%p) for type %s, nothing will be returned", @@ -1450,12 +1452,12 @@ vpiHandle vpi_handle(PLI_INT32 type, vpiHandle object) { return nullptr; } case vpiRightRange: { - if (VerilatedVpioVarBase* vop = VerilatedVpioVarBase::castp(object)) { + if (VerilatedVpioVarBase* const vop = VerilatedVpioVarBase::castp(object)) { if (VL_UNLIKELY(!vop->rangep())) return nullptr; - return (new VerilatedVpioConst(vop->rangep()->right()))->castVpiHandle(); - } else if (VerilatedVpioRange* vop = VerilatedVpioRange::castp(object)) { + return (new VerilatedVpioConst{vop->rangep()->right()})->castVpiHandle(); + } else if (VerilatedVpioRange* const vop = VerilatedVpioRange::castp(object)) { if (VL_UNLIKELY(!vop->rangep())) return nullptr; - return (new VerilatedVpioConst(vop->rangep()->right()))->castVpiHandle(); + return (new VerilatedVpioConst{vop->rangep()->right()})->castVpiHandle(); } VL_VPI_WARNING_(__FILE__, __LINE__, "%s: Unsupported vpiHandle (%p) for type %s, nothing will be returned", @@ -1463,19 +1465,20 @@ vpiHandle vpi_handle(PLI_INT32 type, vpiHandle object) { return nullptr; } case vpiIndex: { - VerilatedVpioVar* vop = VerilatedVpioVar::castp(object); + VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object); if (VL_UNLIKELY(!vop)) return nullptr; - return (new VerilatedVpioConst(vop->index()))->castVpiHandle(); + const vlsint32_t val = vop->index(); + return (new VerilatedVpioConst{val})->castVpiHandle(); } case vpiScope: { - VerilatedVpioVarBase* vop = VerilatedVpioVarBase::castp(object); + VerilatedVpioVarBase* const vop = VerilatedVpioVarBase::castp(object); if (VL_UNLIKELY(!vop)) return nullptr; - return (new VerilatedVpioScope(vop->scopep()))->castVpiHandle(); + return (new VerilatedVpioScope{vop->scopep()})->castVpiHandle(); } case vpiParent: { - VerilatedVpioMemoryWord* vop = VerilatedVpioMemoryWord::castp(object); + VerilatedVpioMemoryWord* const vop = VerilatedVpioMemoryWord::castp(object); if (VL_UNLIKELY(!vop)) return nullptr; - return (new VerilatedVpioVar(vop->varp(), vop->scopep()))->castVpiHandle(); + return (new VerilatedVpioVar{vop->varp(), vop->scopep()})->castVpiHandle(); } default: VL_VPI_WARNING_(__FILE__, __LINE__, "%s: Unsupported type %s, nothing will be returned", @@ -1505,7 +1508,7 @@ vpiHandle vpi_iterate(PLI_INT32 type, vpiHandle object) { VerilatedVpiError::strFromVpiMethod(type), vop->fullname(), vop->varp()->dims()); } - return (new VerilatedVpioMemoryWordIter(object, vop->varp()))->castVpiHandle(); + return (new VerilatedVpioMemoryWordIter{object, vop->varp()})->castVpiHandle(); } case vpiRange: { const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object); @@ -1518,12 +1521,12 @@ vpiHandle vpi_iterate(PLI_INT32 type, vpiHandle object) { VerilatedVpiError::strFromVpiMethod(type), vop->fullname(), vop->varp()->dims()); } - return ((new VerilatedVpioRangeIter(vop->rangep()))->castVpiHandle()); + return ((new VerilatedVpioRangeIter{vop->rangep()})->castVpiHandle()); } case vpiReg: { const VerilatedVpioScope* const vop = VerilatedVpioScope::castp(object); if (VL_UNLIKELY(!vop)) return nullptr; - return ((new VerilatedVpioVarIter(vop->scopep()))->castVpiHandle()); + return ((new VerilatedVpioVarIter{vop->scopep()})->castVpiHandle()); } case vpiModule: { const VerilatedVpioModule* const vop = VerilatedVpioModule::castp(object); @@ -1531,7 +1534,7 @@ vpiHandle vpi_iterate(PLI_INT32 type, vpiHandle object) { const VerilatedScope* const modp = vop ? vop->scopep() : nullptr; const auto it = vlstd::as_const(map)->find(const_cast(modp)); if (it == map->end()) return nullptr; - return ((new VerilatedVpioModuleIter(it->second))->castVpiHandle()); + return ((new VerilatedVpioModuleIter{it->second})->castVpiHandle()); } default: VL_VPI_WARNING_(__FILE__, __LINE__, "%s: Unsupported type %s, nothing will be returned", @@ -1543,7 +1546,7 @@ vpiHandle vpi_scan(vpiHandle object) { VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_scan %p\n", object);); VerilatedVpiImp::assertOneCheck(); VL_VPI_ERROR_RESET_(); - VerilatedVpio* vop = VerilatedVpio::castp(object); + VerilatedVpio* const vop = VerilatedVpio::castp(object); if (VL_UNLIKELY(!vop)) return nullptr; return vop->dovpi_scan(); } @@ -1893,13 +1896,13 @@ void vpi_get_value(vpiHandle object, p_vpi_value valuep) { VL_VPI_ERROR_RESET_(); if (VL_UNLIKELY(!valuep)) return; - if (VerilatedVpioVar* vop = VerilatedVpioVar::castp(object)) { + if (VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object)) { vl_get_value(vop->varp(), vop->varDatap(), valuep, vop->fullname()); return; } else if (const VerilatedVpioParam* const vop = VerilatedVpioParam::castp(object)) { vl_get_value(vop->varp(), vop->varDatap(), valuep, vop->fullname()); return; - } else if (VerilatedVpioConst* vop = VerilatedVpioConst::castp(object)) { + } else if (VerilatedVpioConst* const vop = VerilatedVpioConst::castp(object)) { if (valuep->format == vpiIntVal) { valuep->value.integer = vop->num(); return; @@ -2161,7 +2164,7 @@ void vpi_get_time(vpiHandle object, p_vpi_time time_p) { return; } else if (time_p->type == vpiScaledRealTime) { double dtime = VL_TIME_D(); - if (VerilatedVpioScope* vop = VerilatedVpioScope::castp(object)) { + if (VerilatedVpioScope* const vop = VerilatedVpioScope::castp(object)) { const int scalePow10 = Verilated::threadContextp()->timeprecision() - vop->scopep()->timeunit(); const double scale = vl_time_multiplier(scalePow10); // e.g. 0.0001 @@ -2271,7 +2274,7 @@ PLI_INT32 vpi_free_object(vpiHandle object) { PLI_INT32 vpi_release_handle(vpiHandle object) { VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_release_handle %p\n", object);); VerilatedVpiImp::assertOneCheck(); - VerilatedVpio* vop = VerilatedVpio::castp(object); + VerilatedVpio* const vop = VerilatedVpio::castp(object); VL_VPI_ERROR_RESET_(); if (VL_UNLIKELY(!vop)) return 0; VL_DO_DANGLING(delete vop, vop); diff --git a/nodist/dot_importer b/nodist/dot_importer index fb6d6661a..cc9520a27 100755 --- a/nodist/dot_importer +++ b/nodist/dot_importer @@ -66,11 +66,11 @@ def cwrite(filename): fh.write(" auto* gp = &m_graph;\n") for ver in sorted(Vertexes, key=lambda ver: ver['num']): fh.write( - " auto* %s = new V3GraphTestVertex(gp, \"%s\"); if (%s) {}\n" + " auto* %s = new V3GraphTestVertex{gp, \"%s\"}; if (%s) {}\n" % (ver['name'], ver['name'], ver['name'])) fh.write("\n") for edge in Edges: - fh.write(" new V3GraphEdge(gp, %s, %s, %s, %s);\n" % + fh.write(" new V3GraphEdge{gp, %s, %s, %s, %s};\n" % (edge['from'], edge['to'], edge['weight'], "true" if edge['cutable'] else "false")) fh.write("}\n") From 44173b40a1fafdcb2c1476d7ef289f77da74a91a Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 24 Jul 2021 09:14:18 -0400 Subject: [PATCH 47/84] Internals: Remove dead code. No functional change. --- src/V3Ast.h | 2 -- src/V3AstNodes.h | 16 ---------------- 2 files changed, 18 deletions(-) diff --git a/src/V3Ast.h b/src/V3Ast.h index eace39dce..5925d881d 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -1792,8 +1792,6 @@ public: virtual bool isGateOptimizable() const { return true; } // GateDedupable is a slightly larger superset of GateOptimzable (eg, AstNodeIf) virtual bool isGateDedupable() const { return isGateOptimizable(); } - // Needs verilated_heavy.h (uses std::string or some others) - virtual bool isHeavy() const { return false; } // Else creates output or exits, etc, not unconsumed virtual bool isOutputter() const { return false; } // Else a AstTime etc which output can't be predicted from input diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index 580e34c08..987d9594e 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -307,7 +307,6 @@ public: : ASTGEN_SUPER_Class(fl, name) {} ASTNODE_NODE_FUNCS(Class) virtual string verilogKwd() const override { return "class"; } - virtual bool isHeavy() const override { return true; } virtual bool maybePointedTo() const override { return true; } virtual void dump(std::ostream& str) const override; virtual const char* broken() const override { @@ -556,7 +555,6 @@ public: virtual void dumpSmall(std::ostream& str) const override; virtual AstNodeDType* getChildDTypep() const override { return childDTypep(); } virtual AstNodeDType* getChild2DTypep() const override { return keyChildDTypep(); } - virtual bool isHeavy() const override { return true; } // op1 = Range of variable AstNodeDType* childDTypep() const { return VN_CAST(op1p(), NodeDType); } void childDTypep(AstNodeDType* nodep) { setOp1p(nodep); } @@ -653,7 +651,6 @@ public: } virtual string prettyDTypeName() const override; virtual void dumpSmall(std::ostream& str) const override; - virtual bool isHeavy() const override { return true; } virtual AstNodeDType* getChildDTypep() const override { return childDTypep(); } // op1 = Range of variable AstNodeDType* childDTypep() const { return VN_CAST(op1p(), NodeDType); } @@ -881,7 +878,6 @@ public: BROKEN_RTN(dtypep() != this); return nullptr; } - virtual bool isHeavy() const override { return keyword() == AstBasicDTypeKwd::STRING; } AstRange* rangep() const { return VN_CAST(op1p(), Range); } // op1 = Range of variable void rangep(AstRange* nodep) { setNOp1p(nodep); } void setSignedState(const VSigning& signst) { @@ -1141,7 +1137,6 @@ public: } virtual void dumpSmall(std::ostream& str) const override; virtual string prettyDTypeName() const override; - virtual bool isHeavy() const override { return true; } virtual AstNodeDType* getChildDTypep() const override { return childDTypep(); } // op1 = Range of variable AstNodeDType* childDTypep() const { return VN_CAST(op1p(), NodeDType); } @@ -3882,7 +3877,6 @@ public: ASTNODE_NODE_FUNCS(DumpCtl) virtual string verilogKwd() const override { return ctlType().ascii(); } virtual bool isGateOptimizable() const override { return false; } - virtual bool isHeavy() const override { return true; } virtual bool isPredictOptimizable() const override { return false; } virtual bool isOutputter() const override { return true; } virtual bool cleanOut() const { return true; } @@ -4038,7 +4032,6 @@ public: ASTNODE_NODE_FUNCS(FOpen) virtual string verilogKwd() const override { return "$fopen"; } virtual bool isGateOptimizable() const override { return false; } - virtual bool isHeavy() const override { return true; } virtual bool isPredictOptimizable() const override { return false; } virtual bool isPure() const override { return false; } virtual bool isOutputter() const override { return true; } @@ -4060,7 +4053,6 @@ public: ASTNODE_NODE_FUNCS(FOpenMcd) virtual string verilogKwd() const override { return "$fopen"; } virtual bool isGateOptimizable() const override { return false; } - virtual bool isHeavy() const override { return true; } virtual bool isPredictOptimizable() const override { return false; } virtual bool isPure() const override { return false; } virtual bool isOutputter() const override { return true; } @@ -4284,7 +4276,6 @@ public: setNOp4p(msbp); } virtual bool isGateOptimizable() const override { return false; } - virtual bool isHeavy() const override { return true; } virtual bool isPredictOptimizable() const override { return false; } virtual bool isPure() const override { return false; } virtual bool isOutputter() const override { return true; } @@ -4395,7 +4386,6 @@ public: virtual string emitVerilog() override { return "%f$value$plusargs(%l, %k%r)"; } virtual string emitC() override { V3ERROR_NA_RETURN(""); } virtual bool isGateOptimizable() const override { return false; } - virtual bool isHeavy() const override { return true; } virtual bool isPredictOptimizable() const override { return false; } virtual bool cleanOut() const override { return true; } virtual bool same(const AstNode* samep) const override { return true; } @@ -6534,7 +6524,6 @@ public: virtual bool cleanOut() const override { return true; } virtual bool cleanLhs() const override { return true; } virtual bool sizeMattersLhs() const override { return false; } - virtual bool isHeavy() const override { return true; } FmtType format() const { return m_fmt; } }; @@ -8152,7 +8141,6 @@ public: virtual bool sizeMattersLhs() const override { return false; } virtual bool sizeMattersRhs() const override { return false; } virtual bool sizeMattersThs() const override { return false; } - virtual bool isHeavy() const override { return true; } }; class AstGetcN final : public AstNodeBiop { @@ -8178,7 +8166,6 @@ public: virtual bool cleanRhs() const override { return true; } virtual bool sizeMattersLhs() const override { return false; } virtual bool sizeMattersRhs() const override { return false; } - virtual bool isHeavy() const override { return true; } }; class AstGetcRefN final : public AstNodeBiop { @@ -8204,7 +8191,6 @@ public: virtual bool cleanRhs() const override { return true; } virtual bool sizeMattersLhs() const override { return false; } virtual bool sizeMattersRhs() const override { return false; } - virtual bool isHeavy() const override { return true; } }; class AstSubstrN final : public AstNodeTriop { @@ -8230,7 +8216,6 @@ public: virtual bool sizeMattersLhs() const override { return false; } virtual bool sizeMattersRhs() const override { return false; } virtual bool sizeMattersThs() const override { return false; } - virtual bool isHeavy() const override { return true; } }; class AstCompareNN final : public AstNodeBiop { @@ -8263,7 +8248,6 @@ public: virtual bool cleanRhs() const override { return true; } virtual bool sizeMattersLhs() const override { return false; } virtual bool sizeMattersRhs() const override { return false; } - virtual bool isHeavy() const override { return true; } }; class AstFell final : public AstNodeMath { From b90fce55f418d35587383d3bc08ad7029774ffca Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 24 Jul 2021 10:00:33 -0400 Subject: [PATCH 48/84] Includes: Refactor verilated.h and deprecate verilated_heavy.h (#2701). --- Changes | 1 + docs/guide/deprecations.rst | 5 + include/verilated.cpp | 1 - include/verilated.h | 2151 +-------------------------- include/verilated_dpi.h | 1 - include/verilated_funcs.h | 2252 +++++++++++++++++++++++++++++ include/verilated_heavy.h | 960 +----------- include/verilated_imp.h | 1 - include/verilated_save.h | 2 +- include/verilated_syms.h | 2 +- include/verilated_types.h | 897 ++++++++++++ src/V3EmitCConstPool.cpp | 2 +- src/V3EmitCHeaders.cpp | 2 +- src/V3EmitCImp.cpp | 2 +- src/V3EmitCModel.cpp | 2 +- src/V3EmitCSyms.cpp | 2 +- test_regress/t/t_verilated_all.pl | 1 + 17 files changed, 3183 insertions(+), 3101 deletions(-) create mode 100644 include/verilated_funcs.h create mode 100644 include/verilated_types.h diff --git a/Changes b/Changes index e2e7cf0da..40413b760 100644 --- a/Changes +++ b/Changes @@ -18,6 +18,7 @@ Verilator 4.211 devel in order to aid incremental compilation via ccache (#3071). [Geza Lore] * Parameter values are now emitted as 'static constexpr' instead of enum. C++ direct references to parameters might require updating (#3077). [Geza Lore] +* Refactored Verilated include files; include verilated.h not verilated_heavy.h. * Fix -G to treat simple integer literals as signed (#3060). [Anikin1610] * Fix emitted string array initializers (#2895). [Iztok Jeras] diff --git a/docs/guide/deprecations.rst b/docs/guide/deprecations.rst index aa9fcb5df..8d3124175 100644 --- a/docs/guide/deprecations.rst +++ b/docs/guide/deprecations.rst @@ -11,6 +11,11 @@ C++11 compiler support require C++14 or newer compilers for both compiling Verilator and compiling Verilated models no sooner than January 2022. +Verilated_heavy.h + The legacy "verilated_heavy.h" include was replaced with just including + "verilated.h". Verilated_heavy.h is planned for removal no sooner than + July 2022. + Configuration File -msg The :vlopt:`lint_off` "-msg" option has been replaced with the "-rule" option. "-msg" is planned for removal no sooner than January 2021. diff --git a/include/verilated.cpp b/include/verilated.cpp index 375647d2d..e01f32a78 100644 --- a/include/verilated.cpp +++ b/include/verilated.cpp @@ -1347,7 +1347,6 @@ IData VL_FGETS_IXI(int obits, void* destp, IData fpi) VL_MT_SAFE { return got; } -// declared in verilated_heavy.h IData VL_FGETS_NI(std::string& dest, IData fpi) VL_MT_SAFE { return getLine(dest, fpi, std::numeric_limits::max()); } diff --git a/include/verilated.h b/include/verilated.h index f71f13308..1866e34fb 100644 --- a/include/verilated.h +++ b/include/verilated.h @@ -29,6 +29,7 @@ #ifndef VERILATOR_VERILATED_H_ #define VERILATOR_VERILATED_H_ +#define VERILATOR_VERILATED_H_INTERNAL_ // clang-format off #include "verilatedos.h" @@ -36,18 +37,22 @@ # include "verilated_sc.h" // Get SYSTEMC_VERSION and time declarations #endif +#include +#include #include #include #include #include #include #include +#include +#include #include +#include #include +#include #include // avoided to reduce compile time -// avoided and instead in verilated_heavy.h to reduce compile time -// avoided and instead in verilated_heavy.h to reduce compile time #ifdef VL_THREADED # include # include @@ -259,31 +264,7 @@ public: const char* name() const { return m_namep; } ///< Return name of module }; -//========================================================================= -// Declare nets - -#define VL_SIG8(name, msb, lsb) CData name ///< Declare signal, 1-8 bits -#define VL_SIG16(name, msb, lsb) SData name ///< Declare signal, 9-16 bits -#define VL_SIG64(name, msb, lsb) QData name ///< Declare signal, 33-64 bits -#define VL_SIG(name, msb, lsb) IData name ///< Declare signal, 17-32 bits -#define VL_SIGW(name, msb, lsb, words) WData name[words] ///< Declare signal, 65+ bits -#define VL_IN8(name, msb, lsb) CData name ///< Declare input signal, 1-8 bits -#define VL_IN16(name, msb, lsb) SData name ///< Declare input signal, 9-16 bits -#define VL_IN64(name, msb, lsb) QData name ///< Declare input signal, 33-64 bits -#define VL_IN(name, msb, lsb) IData name ///< Declare input signal, 17-32 bits -#define VL_INW(name, msb, lsb, words) WData name[words] ///< Declare input signal, 65+ bits -#define VL_INOUT8(name, msb, lsb) CData name ///< Declare bidir signal, 1-8 bits -#define VL_INOUT16(name, msb, lsb) SData name ///< Declare bidir signal, 9-16 bits -#define VL_INOUT64(name, msb, lsb) QData name ///< Declare bidir signal, 33-64 bits -#define VL_INOUT(name, msb, lsb) IData name ///< Declare bidir signal, 17-32 bits -#define VL_INOUTW(name, msb, lsb, words) WData name[words] ///< Declare bidir signal, 65+ bits -#define VL_OUT8(name, msb, lsb) CData name ///< Declare output signal, 1-8 bits -#define VL_OUT16(name, msb, lsb) SData name ///< Declare output signal, 9-16 bits -#define VL_OUT64(name, msb, lsb) QData name ///< Declare output signal, 33-64bits -#define VL_OUT(name, msb, lsb) IData name ///< Declare output signal, 17-32 bits -#define VL_OUTW(name, msb, lsb, words) WData name[words] ///< Declare output signal, 65+ bits - -///< Declare a module, ala SC_MODULE +/// Declare a module, ala SC_MODULE #define VL_MODULE(modname) class modname VL_NOT_FINAL : public VerilatedModule // Not class final in VL_MODULE, as users might be abstracting our models (--hierarchical) @@ -887,2122 +868,16 @@ inline void VerilatedContext::debug(int val) VL_MT_SAFE { Verilated::debug(val); inline int VerilatedContext::debug() VL_MT_SAFE { return Verilated::debug(); } //========================================================================= -// Extern functions -- User may override -- See verilated.cpp +// Data Types -/// Routine to call for $finish -/// User code may wish to replace this function, to do so, define VL_USER_FINISH. -/// This code does not have to be thread safe. -/// Verilator internal code must call VL_FINISH_MT instead, which eventually calls this. -extern void vl_finish(const char* filename, int linenum, const char* hier); - -/// Routine to call for $stop and non-fatal error -/// User code may wish to replace this function, to do so, define VL_USER_STOP. -/// This code does not have to be thread safe. -/// Verilator internal code must call VL_FINISH_MT instead, which eventually calls this. -extern void vl_stop(const char* filename, int linenum, const char* hier); - -/// Routine to call for a couple of fatal messages -/// User code may wish to replace this function, to do so, define VL_USER_FATAL. -/// This code does not have to be thread safe. -/// Verilator internal code must call VL_FINISH_MT instead, which eventually calls this. -extern void vl_fatal(const char* filename, int linenum, const char* hier, const char* msg); +#include "verilated_types.h" //========================================================================= -// Extern functions -- Slow path +// Functions -/// Multithread safe wrapper for calls to $finish -extern void VL_FINISH_MT(const char* filename, int linenum, const char* hier) VL_MT_SAFE; -/// Multithread safe wrapper for calls to $stop -extern void VL_STOP_MT(const char* filename, int linenum, const char* hier, - bool maybe = true) VL_MT_SAFE; -/// Multithread safe wrapper to call for a couple of fatal messages -extern void VL_FATAL_MT(const char* filename, int linenum, const char* hier, - const char* msg) VL_MT_SAFE; - -// clang-format off -/// Print a string, multithread safe. Eventually VL_PRINTF will get called. -#ifdef VL_THREADED -extern void VL_PRINTF_MT(const char* formatp, ...) VL_ATTR_PRINTF(1) VL_MT_SAFE; -#else -# define VL_PRINTF_MT VL_PRINTF // The following parens will take care of themselves -#endif -// clang-format on - -/// Print a debug message from internals with standard prefix, with printf style format -extern void VL_DBG_MSGF(const char* formatp, ...) VL_ATTR_PRINTF(1) VL_MT_SAFE; - -extern vluint64_t vl_rand64() VL_MT_SAFE; -inline IData VL_RANDOM_I(int obits) VL_MT_SAFE { return vl_rand64() & VL_MASK_I(obits); } -inline QData VL_RANDOM_Q(int obits) VL_MT_SAFE { return vl_rand64() & VL_MASK_Q(obits); } -#ifndef VL_NO_LEGACY -extern WDataOutP VL_RANDOM_W(int obits, WDataOutP outwp); -#endif -extern IData VL_RANDOM_SEEDED_II(int obits, IData seed) VL_MT_SAFE; -inline IData VL_URANDOM_RANGE_I(IData hi, IData lo) { - vluint64_t rnd = vl_rand64(); - if (VL_LIKELY(hi > lo)) { - // Modulus isn't very fast but it's common that hi-low is power-of-two - return (rnd % (hi - lo + 1)) + lo; - } else { - return (rnd % (lo - hi + 1)) + hi; - } -} - -// These are init time only, so slow is fine -/// Random reset a signal of given width -extern IData VL_RAND_RESET_I(int obits); -/// Random reset a signal of given width -extern QData VL_RAND_RESET_Q(int obits); -/// Random reset a signal of given width -extern WDataOutP VL_RAND_RESET_W(int obits, WDataOutP outwp); -/// Zero reset a signal (slow - else use VL_ZERO_W) -extern WDataOutP VL_ZERO_RESET_W(int obits, WDataOutP outwp); - -#if VL_THREADED -/// Return high-precision counter for profiling, or 0x0 if not available -inline QData VL_RDTSC_Q() { - vluint64_t val; - VL_RDTSC(val); - return val; -} -#endif - -extern void VL_PRINTTIMESCALE(const char* namep, const char* timeunitp, - const VerilatedContext* contextp) VL_MT_SAFE; - -extern WDataOutP _vl_moddiv_w(int lbits, WDataOutP owp, WDataInP const lwp, WDataInP const rwp, - bool is_modulus); - -extern IData VL_FGETS_IXI(int obits, void* destp, IData fpi); - -extern void VL_FFLUSH_I(IData fdi); -extern IData VL_FSEEK_I(IData fdi, IData offset, IData origin); -extern IData VL_FTELL_I(IData fdi); -extern void VL_FCLOSE_I(IData fdi); - -extern IData VL_FREAD_I(int width, int array_lsb, int array_size, void* memp, IData fpi, - IData start, IData count); - -extern void VL_WRITEF(const char* formatp, ...); -extern void VL_FWRITEF(IData fpi, const char* formatp, ...); - -extern IData VL_FSCANF_IX(IData fpi, const char* formatp, ...); -extern IData VL_SSCANF_IIX(int lbits, IData ld, const char* formatp, ...); -extern IData VL_SSCANF_IQX(int lbits, QData ld, const char* formatp, ...); -extern IData VL_SSCANF_IWX(int lbits, WDataInP const lwp, const char* formatp, ...); - -extern void VL_SFORMAT_X(int obits, CData& destr, const char* formatp, ...); -extern void VL_SFORMAT_X(int obits, SData& destr, const char* formatp, ...); -extern void VL_SFORMAT_X(int obits, IData& destr, const char* formatp, ...); -extern void VL_SFORMAT_X(int obits, QData& destr, const char* formatp, ...); -extern void VL_SFORMAT_X(int obits, void* destp, const char* formatp, ...); - -extern IData VL_SYSTEM_IW(int lhswords, WDataInP const lhsp); -extern IData VL_SYSTEM_IQ(QData lhs); -inline IData VL_SYSTEM_II(IData lhs) VL_MT_SAFE { return VL_SYSTEM_IQ(lhs); } - -extern IData VL_TESTPLUSARGS_I(const char* formatp); -extern const char* vl_mc_scan_plusargs(const char* prefixp); // PLIish - -//========================================================================= -// Base macros - -// Return true if data[bit] set; not 0/1 return, but 0/non-zero return. -#define VL_BITISSET_I(data, bit) ((data) & (VL_UL(1) << VL_BITBIT_I(bit))) -#define VL_BITISSET_Q(data, bit) ((data) & (1ULL << VL_BITBIT_Q(bit))) -#define VL_BITISSET_E(data, bit) ((data) & (VL_EUL(1) << VL_BITBIT_E(bit))) -#define VL_BITISSET_W(data, bit) ((data)[VL_BITWORD_E(bit)] & (VL_EUL(1) << VL_BITBIT_E(bit))) -#define VL_BITISSETLIMIT_W(data, width, bit) (((bit) < (width)) && VL_BITISSET_W(data, bit)) - -// Shift appropriate word by bit. Does not account for wrapping between two words -#define VL_BITRSHIFT_W(data, bit) ((data)[VL_BITWORD_E(bit)] >> VL_BITBIT_E(bit)) - -// Create two 32-bit words from quadword -// WData is always at least 2 words; does not clean upper bits -#define VL_SET_WQ(owp, data) \ - do { \ - (owp)[0] = static_cast(data); \ - (owp)[1] = static_cast((data) >> VL_EDATASIZE); \ - } while (false) -#define VL_SET_WI(owp, data) \ - do { \ - (owp)[0] = static_cast(data); \ - (owp)[1] = 0; \ - } while (false) -#define VL_SET_QW(lwp) \ - ((static_cast((lwp)[0])) \ - | (static_cast((lwp)[1]) << (static_cast(VL_EDATASIZE)))) -#define VL_SET_QII(ld, rd) ((static_cast(ld) << 32ULL) | static_cast(rd)) - -// Return FILE* from IData -extern FILE* VL_CVT_I_FP(IData lhs) VL_MT_SAFE; - -// clang-format off -// Use a union to avoid cast-to-different-size warnings -// Return void* from QData -static inline void* VL_CVT_Q_VP(QData lhs) VL_PURE { - union { void* fp; QData q; } u; - u.q = lhs; - return u.fp; -} -// Return QData from const void* -static inline QData VL_CVT_VP_Q(const void* fp) VL_PURE { - union { const void* fp; QData q; } u; - u.q = 0; - u.fp = fp; - return u.q; -} -// Return double from QData (bits, not numerically) -static inline double VL_CVT_D_Q(QData lhs) VL_PURE { - union { double d; QData q; } u; - u.q = lhs; - return u.d; -} -// Return QData from double (bits, not numerically) -static inline QData VL_CVT_Q_D(double lhs) VL_PURE { - union { double d; QData q; } u; - u.d = lhs; - return u.q; -} -// clang-format on - -// Return double from lhs (numeric) unsigned -double VL_ITOR_D_W(int lbits, WDataInP const lwp) VL_PURE; -static inline double VL_ITOR_D_I(int, IData lhs) VL_PURE { - return static_cast(static_cast(lhs)); -} -static inline double VL_ITOR_D_Q(int, QData lhs) VL_PURE { - return static_cast(static_cast(lhs)); -} -// Return double from lhs (numeric) signed -double VL_ISTOR_D_W(int lbits, WDataInP const lwp) VL_PURE; -static inline double VL_ISTOR_D_I(int lbits, IData lhs) VL_PURE { - if (lbits == 32) return static_cast(static_cast(lhs)); - WData lwp[VL_WQ_WORDS_E]; - VL_SET_WI(lwp, lhs); - return VL_ISTOR_D_W(lbits, lwp); -} -static inline double VL_ISTOR_D_Q(int lbits, QData lhs) VL_PURE { - if (lbits == 64) return static_cast(static_cast(lhs)); - WData lwp[VL_WQ_WORDS_E]; - VL_SET_WQ(lwp, lhs); - return VL_ISTOR_D_W(lbits, lwp); -} -// Return QData from double (numeric) -static inline IData VL_RTOI_I_D(double lhs) VL_PURE { - return static_cast(VL_TRUNC(lhs)); -} - -// Sign extend such that if MSB set, we get ffff_ffff, else 0s -// (Requires clean input) -#define VL_SIGN_I(nbits, lhs) ((lhs) >> VL_BITBIT_I((nbits)-VL_UL(1))) -#define VL_SIGN_Q(nbits, lhs) ((lhs) >> VL_BITBIT_Q((nbits)-1ULL)) -#define VL_SIGN_E(nbits, lhs) ((lhs) >> VL_BITBIT_E((nbits)-VL_EUL(1))) -#define VL_SIGN_W(nbits, rwp) \ - ((rwp)[VL_BITWORD_E((nbits)-VL_EUL(1))] >> VL_BITBIT_E((nbits)-VL_EUL(1))) -#define VL_SIGNONES_E(nbits, lhs) (-(VL_SIGN_E(nbits, lhs))) - -// Sign bit extended up to MSB, doesn't include unsigned portion -// Optimization bug in GCC 3.3 returns different bitmasks to later states for -static inline IData VL_EXTENDSIGN_I(int lbits, IData lhs) VL_PURE { - return (-((lhs) & (VL_UL(1) << (lbits - 1)))); -} -static inline QData VL_EXTENDSIGN_Q(int lbits, QData lhs) VL_PURE { - return (-((lhs) & (1ULL << (lbits - 1)))); -} - -// Debugging prints -extern void _vl_debug_print_w(int lbits, WDataInP const iwp); - -//========================================================================= -// Pli macros - -extern int VL_TIME_STR_CONVERT(const char* strp) VL_PURE; - -// These are deprecated and used only to establish the default precision/units. -// Use Verilator timescale-override for better control. -// clang-format off -#ifndef VL_TIME_PRECISION -# ifdef VL_TIME_PRECISION_STR -# define VL_TIME_PRECISION VL_TIME_STR_CONVERT(VL_STRINGIFY(VL_TIME_PRECISION_STR)) -# else -# define VL_TIME_PRECISION (-12) ///< Timescale default units if not in Verilog - picoseconds -# endif -#endif -#ifndef VL_TIME_UNIT -# ifdef VL_TIME_UNIT_STR -# define VL_TIME_UNIT VL_TIME_STR_CONVERT(VL_STRINGIFY(VL_TIME_PRECISION_STR)) -# else -# define VL_TIME_UNIT (-12) ///< Timescale default units if not in Verilog - picoseconds -# endif -#endif - -#if defined(SYSTEMC_VERSION) -/// Return current simulation time -// Already defined: extern sc_time sc_time_stamp(); -inline vluint64_t vl_time_stamp64() { return sc_time_stamp().value(); } -#else // Non-SystemC -# if !defined(VL_TIME_CONTEXT) && !defined(VL_NO_LEGACY) -# ifdef VL_TIME_STAMP64 -// vl_time_stamp64() may be optionally defined by the user to return time. -// On MSVC++ weak symbols are not supported so must be declared, or define -// VL_TIME_CONTEXT. -extern vluint64_t vl_time_stamp64() VL_ATTR_WEAK; -# else -// sc_time_stamp() may be optionally defined by the user to return time. -// On MSVC++ weak symbols are not supported so must be declared, or define -// VL_TIME_CONTEXT. -extern double sc_time_stamp() VL_ATTR_WEAK; // Verilator 4.032 and newer -inline vluint64_t vl_time_stamp64() { - // clang9.0.1 requires & although we really do want the weak symbol value - return VL_LIKELY(&sc_time_stamp) ? static_cast(sc_time_stamp()) : 0; -} -# endif -# endif -#endif - -inline vluint64_t VerilatedContext::time() const VL_MT_SAFE { - // When using non-default context, fastest path is return time - if (VL_LIKELY(m_s.m_time)) return m_s.m_time; -#if defined(SYSTEMC_VERSION) || (!defined(VL_TIME_CONTEXT) && !defined(VL_NO_LEGACY)) - // Zero time could mean really at zero, or using callback - // clang9.0.1 requires & although we really do want the weak symbol value - if (VL_LIKELY(&vl_time_stamp64)) { // else is weak symbol that is not defined - return vl_time_stamp64(); - } -#endif - return 0; -} - -#define VL_TIME_Q() (Verilated::threadContextp()->time()) -#define VL_TIME_D() (static_cast(VL_TIME_Q())) - -// Time scaled from 1-per-precision into a module's time units ("Unit"-ed, not "United") -// Optimized assuming scale is always constant. -// Can't use multiply in Q flavor, as might lose precision -#define VL_TIME_UNITED_Q(scale) (VL_TIME_Q() / static_cast(scale)) -#define VL_TIME_UNITED_D(scale) (VL_TIME_D() / static_cast(scale)) - -// Return time precision as multiplier of time units -double vl_time_multiplier(int scale) VL_PURE; -// Return power of 10. e.g. returns 100 if n==2 -vluint64_t vl_time_pow10(int n) VL_PURE; - -#ifdef VL_DEBUG -/// Evaluate statement if Verilated::debug() enabled -# define VL_DEBUG_IF(stmt) \ - do { \ - if (VL_UNLIKELY(Verilated::debug())) {stmt} \ - } while (false) -#else -// We intentionally do not compile the stmt to improve compile speed -# define VL_DEBUG_IF(stmt) do {} while (false) -#endif - -// clang-format on - -//========================================================================= -// Functional macros/routines -// These all take the form -// VL_func_IW(bits, bits, op, op) -// VL_func_WW(bits, bits, out, op, op) -// The I/W indicates if it's a integer or wide for the output and each operand. -// The bits indicate the bit width of the output and each operand. -// If wide output, a temporary storage location is specified. - -//=================================================================== -// SETTING OPERATORS - -// Output clean -// EMIT_RULE: VL_CLEAN: oclean=clean; obits=lbits; -#define VL_CLEAN_II(obits, lbits, lhs) ((lhs)&VL_MASK_I(obits)) -#define VL_CLEAN_QQ(obits, lbits, lhs) ((lhs)&VL_MASK_Q(obits)) - -// EMIT_RULE: VL_ASSIGNCLEAN: oclean=clean; obits==lbits; -#define VL_ASSIGNCLEAN_W(obits, owp, lwp) VL_CLEAN_WW((obits), (obits), (owp), (lwp)) -static inline WDataOutP _vl_clean_inplace_w(int obits, WDataOutP owp) VL_MT_SAFE { - const int words = VL_WORDS_I(obits); - owp[words - 1] &= VL_MASK_E(obits); - return owp; -} -static inline WDataOutP VL_CLEAN_WW(int obits, int, WDataOutP owp, WDataInP const lwp) VL_MT_SAFE { - const int words = VL_WORDS_I(obits); - for (int i = 0; (i < (words - 1)); ++i) owp[i] = lwp[i]; - owp[words - 1] = lwp[words - 1] & VL_MASK_E(obits); - return owp; -} -static inline WDataOutP VL_ZERO_W(int obits, WDataOutP owp) VL_MT_SAFE { - const int words = VL_WORDS_I(obits); - for (int i = 0; i < words; ++i) owp[i] = 0; - return owp; -} -static inline WDataOutP VL_ALLONES_W(int obits, WDataOutP owp) VL_MT_SAFE { - const int words = VL_WORDS_I(obits); - for (int i = 0; i < (words - 1); ++i) owp[i] = ~VL_EUL(0); - owp[words - 1] = VL_MASK_E(obits); - return owp; -} - -// EMIT_RULE: VL_ASSIGN: oclean=rclean; obits==lbits; -// For now, we always have a clean rhs. -// Note: If a ASSIGN isn't clean, use VL_ASSIGNCLEAN instead to do the same thing. -static inline WDataOutP VL_ASSIGN_W(int obits, WDataOutP owp, WDataInP const lwp) VL_MT_SAFE { - const int words = VL_WORDS_I(obits); - for (int i = 0; i < words; ++i) owp[i] = lwp[i]; - return owp; -} - -// EMIT_RULE: VL_ASSIGNBIT: rclean=clean; -static inline void VL_ASSIGNBIT_II(int, int bit, CData& lhsr, IData rhs) VL_PURE { - lhsr = ((lhsr & ~(VL_UL(1) << VL_BITBIT_I(bit))) | (rhs << VL_BITBIT_I(bit))); -} -static inline void VL_ASSIGNBIT_II(int, int bit, SData& lhsr, IData rhs) VL_PURE { - lhsr = ((lhsr & ~(VL_UL(1) << VL_BITBIT_I(bit))) | (rhs << VL_BITBIT_I(bit))); -} -static inline void VL_ASSIGNBIT_II(int, int bit, IData& lhsr, IData rhs) VL_PURE { - lhsr = ((lhsr & ~(VL_UL(1) << VL_BITBIT_I(bit))) | (rhs << VL_BITBIT_I(bit))); -} -static inline void VL_ASSIGNBIT_QI(int, int bit, QData& lhsr, QData rhs) VL_PURE { - lhsr = ((lhsr & ~(1ULL << VL_BITBIT_Q(bit))) | (static_cast(rhs) << VL_BITBIT_Q(bit))); -} -static inline void VL_ASSIGNBIT_WI(int, int bit, WDataOutP owp, IData rhs) VL_MT_SAFE { - EData orig = owp[VL_BITWORD_E(bit)]; - owp[VL_BITWORD_E(bit)] = ((orig & ~(VL_EUL(1) << VL_BITBIT_E(bit))) - | (static_cast(rhs) << VL_BITBIT_E(bit))); -} -// Alternative form that is an instruction faster when rhs is constant one. -static inline void VL_ASSIGNBIT_IO(int, int bit, CData& lhsr, IData) VL_PURE { - lhsr = (lhsr | (VL_UL(1) << VL_BITBIT_I(bit))); -} -static inline void VL_ASSIGNBIT_IO(int, int bit, SData& lhsr, IData) VL_PURE { - lhsr = (lhsr | (VL_UL(1) << VL_BITBIT_I(bit))); -} -static inline void VL_ASSIGNBIT_IO(int, int bit, IData& lhsr, IData) VL_PURE { - lhsr = (lhsr | (VL_UL(1) << VL_BITBIT_I(bit))); -} -static inline void VL_ASSIGNBIT_QO(int, int bit, QData& lhsr, IData) VL_PURE { - lhsr = (lhsr | (1ULL << VL_BITBIT_Q(bit))); -} -static inline void VL_ASSIGNBIT_WO(int, int bit, WDataOutP owp, IData) VL_MT_SAFE { - const EData orig = owp[VL_BITWORD_E(bit)]; - owp[VL_BITWORD_E(bit)] = (orig | (VL_EUL(1) << VL_BITBIT_E(bit))); -} - -//=================================================================== -// SYSTEMC OPERATORS -// Copying verilog format to systemc integers and bit vectors. -// Get a SystemC variable - -#define VL_ASSIGN_ISI(obits, vvar, svar) \ - { (vvar) = VL_CLEAN_II((obits), (obits), (svar).read()); } -#define VL_ASSIGN_QSQ(obits, vvar, svar) \ - { (vvar) = VL_CLEAN_QQ((obits), (obits), (svar).read()); } - -#define VL_ASSIGN_ISW(obits, od, svar) \ - { (od) = ((svar).read().get_word(0)) & VL_MASK_I(obits); } -#define VL_ASSIGN_QSW(obits, od, svar) \ - { \ - (od) = ((static_cast((svar).read().get_word(1))) << VL_IDATASIZE \ - | (svar).read().get_word(0)) \ - & VL_MASK_Q(obits); \ - } -#define VL_ASSIGN_WSW(obits, owp, svar) \ - { \ - const int words = VL_WORDS_I(obits); \ - for (int i = 0; i < words; ++i) (owp)[i] = (svar).read().get_word(i); \ - (owp)[words - 1] &= VL_MASK_E(obits); \ - } - -#define VL_ASSIGN_ISU(obits, vvar, svar) \ - { (vvar) = VL_CLEAN_II((obits), (obits), (svar).read().to_uint()); } -#define VL_ASSIGN_QSU(obits, vvar, svar) \ - { (vvar) = VL_CLEAN_QQ((obits), (obits), (svar).read().to_uint64()); } -#define VL_ASSIGN_WSB(obits, owp, svar) \ - { \ - const int words = VL_WORDS_I(obits); \ - sc_biguint<(obits)> _butemp = (svar).read(); \ - for (int i = 0; i < words; ++i) { \ - int msb = ((i + 1) * VL_IDATASIZE) - 1; \ - msb = (msb >= (obits)) ? ((obits)-1) : msb; \ - (owp)[i] = _butemp.range(msb, i * VL_IDATASIZE).to_uint(); \ - } \ - (owp)[words - 1] &= VL_MASK_E(obits); \ - } - -// Copying verilog format from systemc integers and bit vectors. -// Set a SystemC variable - -#define VL_ASSIGN_SII(obits, svar, vvar) \ - { (svar).write(vvar); } -#define VL_ASSIGN_SQQ(obits, svar, vvar) \ - { (svar).write(vvar); } - -#define VL_ASSIGN_SWI(obits, svar, rd) \ - { \ - sc_bv<(obits)> _bvtemp; \ - _bvtemp.set_word(0, (rd)); \ - (svar).write(_bvtemp); \ - } -#define VL_ASSIGN_SWQ(obits, svar, rd) \ - { \ - sc_bv<(obits)> _bvtemp; \ - _bvtemp.set_word(0, static_cast(rd)); \ - _bvtemp.set_word(1, static_cast((rd) >> VL_IDATASIZE)); \ - (svar).write(_bvtemp); \ - } -#define VL_ASSIGN_SWW(obits, svar, rwp) \ - { \ - sc_bv<(obits)> _bvtemp; \ - for (int i = 0; i < VL_WORDS_I(obits); ++i) _bvtemp.set_word(i, (rwp)[i]); \ - (svar).write(_bvtemp); \ - } - -#define VL_ASSIGN_SUI(obits, svar, rd) \ - { (svar).write(rd); } -#define VL_ASSIGN_SUQ(obits, svar, rd) \ - { (svar).write(rd); } -#define VL_ASSIGN_SBI(obits, svar, rd) \ - { (svar).write(rd); } -#define VL_ASSIGN_SBQ(obits, svar, rd) \ - { (svar).write(rd); } -#define VL_ASSIGN_SBW(obits, svar, rwp) \ - { \ - sc_biguint<(obits)> _butemp; \ - for (int i = 0; i < VL_WORDS_I(obits); ++i) { \ - int msb = ((i + 1) * VL_IDATASIZE) - 1; \ - msb = (msb >= (obits)) ? ((obits)-1) : msb; \ - _butemp.range(msb, i* VL_IDATASIZE) = (rwp)[i]; \ - } \ - (svar).write(_butemp); \ - } - -//=================================================================== -// Extending sizes - -// CAREFUL, we're width changing, so obits!=lbits - -// Right must be clean because otherwise size increase would pick up bad bits -// EMIT_RULE: VL_EXTEND: oclean=clean; rclean==clean; -#define VL_EXTEND_II(obits, lbits, lhs) ((lhs)) -#define VL_EXTEND_QI(obits, lbits, lhs) (static_cast(lhs)) -#define VL_EXTEND_QQ(obits, lbits, lhs) ((lhs)) - -static inline WDataOutP VL_EXTEND_WI(int obits, int, WDataOutP owp, IData ld) VL_MT_SAFE { - // Note for extracts that obits != lbits - owp[0] = ld; - for (int i = 1; i < VL_WORDS_I(obits); ++i) owp[i] = 0; - return owp; -} -static inline WDataOutP VL_EXTEND_WQ(int obits, int, WDataOutP owp, QData ld) VL_MT_SAFE { - VL_SET_WQ(owp, ld); - for (int i = VL_WQ_WORDS_E; i < VL_WORDS_I(obits); ++i) owp[i] = 0; - return owp; -} -static inline WDataOutP VL_EXTEND_WW(int obits, int lbits, WDataOutP owp, - WDataInP const lwp) VL_MT_SAFE { - for (int i = 0; i < VL_WORDS_I(lbits); ++i) owp[i] = lwp[i]; - for (int i = VL_WORDS_I(lbits); i < VL_WORDS_I(obits); ++i) owp[i] = 0; - return owp; -} - -// EMIT_RULE: VL_EXTENDS: oclean=*dirty*; obits=lbits; -// Sign extension; output dirty -static inline IData VL_EXTENDS_II(int, int lbits, IData lhs) VL_PURE { - return VL_EXTENDSIGN_I(lbits, lhs) | lhs; -} -static inline QData VL_EXTENDS_QI(int, int lbits, QData lhs /*Q_as_need_extended*/) VL_PURE { - return VL_EXTENDSIGN_Q(lbits, lhs) | lhs; -} -static inline QData VL_EXTENDS_QQ(int, int lbits, QData lhs) VL_PURE { - return VL_EXTENDSIGN_Q(lbits, lhs) | lhs; -} - -static inline WDataOutP VL_EXTENDS_WI(int obits, int lbits, WDataOutP owp, IData ld) VL_MT_SAFE { - const EData sign = VL_SIGNONES_E(lbits, static_cast(ld)); - owp[0] = ld | (sign & ~VL_MASK_E(lbits)); - for (int i = 1; i < VL_WORDS_I(obits); ++i) owp[i] = sign; - return owp; -} -static inline WDataOutP VL_EXTENDS_WQ(int obits, int lbits, WDataOutP owp, QData ld) VL_MT_SAFE { - VL_SET_WQ(owp, ld); - const EData sign = VL_SIGNONES_E(lbits, owp[1]); - owp[1] |= sign & ~VL_MASK_E(lbits); - for (int i = VL_WQ_WORDS_E; i < VL_WORDS_I(obits); ++i) owp[i] = sign; - return owp; -} -static inline WDataOutP VL_EXTENDS_WW(int obits, int lbits, WDataOutP owp, - WDataInP const lwp) VL_MT_SAFE { - for (int i = 0; i < VL_WORDS_I(lbits) - 1; ++i) owp[i] = lwp[i]; - const int lmsw = VL_WORDS_I(lbits) - 1; - const EData sign = VL_SIGNONES_E(lbits, lwp[lmsw]); - owp[lmsw] = lwp[lmsw] | (sign & ~VL_MASK_E(lbits)); - for (int i = VL_WORDS_I(lbits); i < VL_WORDS_I(obits); ++i) owp[i] = sign; - return owp; -} - -//=================================================================== -// REDUCTION OPERATORS - -// EMIT_RULE: VL_REDAND: oclean=clean; lclean==clean; obits=1; -#define VL_REDAND_II(obits, lbits, lhs) ((lhs) == VL_MASK_I(lbits)) -#define VL_REDAND_IQ(obits, lbits, lhs) ((lhs) == VL_MASK_Q(lbits)) -static inline IData VL_REDAND_IW(int, int lbits, WDataInP const lwp) VL_MT_SAFE { - const int words = VL_WORDS_I(lbits); - EData combine = lwp[0]; - for (int i = 1; i < words - 1; ++i) combine &= lwp[i]; - combine &= ~VL_MASK_E(lbits) | lwp[words - 1]; - return ((~combine) == 0); -} - -// EMIT_RULE: VL_REDOR: oclean=clean; lclean==clean; obits=1; -#define VL_REDOR_I(lhs) ((lhs) != 0) -#define VL_REDOR_Q(lhs) ((lhs) != 0) -static inline IData VL_REDOR_W(int words, WDataInP const lwp) VL_MT_SAFE { - EData equal = 0; - for (int i = 0; i < words; ++i) equal |= lwp[i]; - return (equal != 0); -} - -// EMIT_RULE: VL_REDXOR: oclean=dirty; obits=1; -static inline IData VL_REDXOR_2(IData r) VL_PURE { - // Experiments show VL_REDXOR_2 is faster than __builtin_parityl - r = (r ^ (r >> 1)); - return r; -} -static inline IData VL_REDXOR_4(IData r) VL_PURE { -#if defined(__GNUC__) && (__GNUC__ >= 4) && !defined(VL_NO_BUILTINS) - return __builtin_parityl(r); -#else - r = (r ^ (r >> 1)); - r = (r ^ (r >> 2)); - return r; -#endif -} -static inline IData VL_REDXOR_8(IData r) VL_PURE { -#if defined(__GNUC__) && (__GNUC__ >= 4) && !defined(VL_NO_BUILTINS) - return __builtin_parityl(r); -#else - r = (r ^ (r >> 1)); - r = (r ^ (r >> 2)); - r = (r ^ (r >> 4)); - return r; -#endif -} -static inline IData VL_REDXOR_16(IData r) VL_PURE { -#if defined(__GNUC__) && (__GNUC__ >= 4) && !defined(VL_NO_BUILTINS) - return __builtin_parityl(r); -#else - r = (r ^ (r >> 1)); - r = (r ^ (r >> 2)); - r = (r ^ (r >> 4)); - r = (r ^ (r >> 8)); - return r; -#endif -} -static inline IData VL_REDXOR_32(IData r) VL_PURE { -#if defined(__GNUC__) && (__GNUC__ >= 4) && !defined(VL_NO_BUILTINS) - return __builtin_parityl(r); -#else - r = (r ^ (r >> 1)); - r = (r ^ (r >> 2)); - r = (r ^ (r >> 4)); - r = (r ^ (r >> 8)); - r = (r ^ (r >> 16)); - return r; -#endif -} -static inline IData VL_REDXOR_64(QData r) VL_PURE { -#if defined(__GNUC__) && (__GNUC__ >= 4) && !defined(VL_NO_BUILTINS) - return __builtin_parityll(r); -#else - r = (r ^ (r >> 1)); - r = (r ^ (r >> 2)); - r = (r ^ (r >> 4)); - r = (r ^ (r >> 8)); - r = (r ^ (r >> 16)); - r = (r ^ (r >> 32)); - return static_cast(r); -#endif -} -static inline IData VL_REDXOR_W(int words, WDataInP const lwp) VL_MT_SAFE { - EData r = lwp[0]; - for (int i = 1; i < words; ++i) r ^= lwp[i]; - return VL_REDXOR_32(r); -} - -// EMIT_RULE: VL_COUNTONES_II: oclean = false; lhs clean -static inline IData VL_COUNTONES_I(IData lhs) VL_PURE { - // This is faster than __builtin_popcountl - IData r = lhs - ((lhs >> 1) & 033333333333) - ((lhs >> 2) & 011111111111); - r = (r + (r >> 3)) & 030707070707; - r = (r + (r >> 6)); - r = (r + (r >> 12) + (r >> 24)) & 077; - return r; -} -static inline IData VL_COUNTONES_Q(QData lhs) VL_PURE { - return VL_COUNTONES_I(static_cast(lhs)) + VL_COUNTONES_I(static_cast(lhs >> 32)); -} -#define VL_COUNTONES_E VL_COUNTONES_I -static inline IData VL_COUNTONES_W(int words, WDataInP const lwp) VL_MT_SAFE { - EData r = 0; - for (int i = 0; i < words; ++i) r += VL_COUNTONES_E(lwp[i]); - return r; -} - -// EMIT_RULE: VL_COUNTBITS_II: oclean = false; lhs clean -static inline IData VL_COUNTBITS_I(int lbits, IData lhs, IData ctrl0, IData ctrl1, - IData ctrl2) VL_PURE { - int ctrlSum = (ctrl0 & 0x1) + (ctrl1 & 0x1) + (ctrl2 & 0x1); - if (ctrlSum == 3) { - return VL_COUNTONES_I(lhs); - } else if (ctrlSum == 0) { - IData mask = (lbits == 32) ? -1 : ((1 << lbits) - 1); - return VL_COUNTONES_I(~lhs & mask); - } else { - return (lbits == 32) ? 32 : lbits; - } -} -static inline IData VL_COUNTBITS_Q(int lbits, QData lhs, IData ctrl0, IData ctrl1, - IData ctrl2) VL_PURE { - return VL_COUNTBITS_I(32, static_cast(lhs), ctrl0, ctrl1, ctrl2) - + VL_COUNTBITS_I(lbits - 32, static_cast(lhs >> 32), ctrl0, ctrl1, ctrl2); -} -#define VL_COUNTBITS_E VL_COUNTBITS_I -static inline IData VL_COUNTBITS_W(int lbits, int words, WDataInP const lwp, IData ctrl0, - IData ctrl1, IData ctrl2) VL_MT_SAFE { - EData r = 0; - IData wordLbits = 32; - for (int i = 0; i < words; ++i) { - if (i == words - 1) wordLbits = lbits % 32; - r += VL_COUNTBITS_E(wordLbits, lwp[i], ctrl0, ctrl1, ctrl2); - } - return r; -} - -static inline IData VL_ONEHOT_I(IData lhs) VL_PURE { - return (((lhs & (lhs - 1)) == 0) & (lhs != 0)); -} -static inline IData VL_ONEHOT_Q(QData lhs) VL_PURE { - return (((lhs & (lhs - 1)) == 0) & (lhs != 0)); -} -static inline IData VL_ONEHOT_W(int words, WDataInP const lwp) VL_MT_SAFE { - EData one = 0; - for (int i = 0; (i < words); ++i) { - if (lwp[i]) { - if (one) return 0; - one = 1; - if (lwp[i] & (lwp[i] - 1)) return 0; - } - } - return one; -} - -static inline IData VL_ONEHOT0_I(IData lhs) VL_PURE { return ((lhs & (lhs - 1)) == 0); } -static inline IData VL_ONEHOT0_Q(QData lhs) VL_PURE { return ((lhs & (lhs - 1)) == 0); } -static inline IData VL_ONEHOT0_W(int words, WDataInP const lwp) VL_MT_SAFE { - bool one = false; - for (int i = 0; (i < words); ++i) { - if (lwp[i]) { - if (one) return 0; - one = true; - if (lwp[i] & (lwp[i] - 1)) return 0; - } - } - return 1; -} - -static inline IData VL_CLOG2_I(IData lhs) VL_PURE { - // There are faster algorithms, or fls GCC4 builtins, but rarely used - if (VL_UNLIKELY(!lhs)) return 0; - --lhs; - int shifts = 0; - for (; lhs != 0; ++shifts) lhs = lhs >> 1; - return shifts; -} -static inline IData VL_CLOG2_Q(QData lhs) VL_PURE { - if (VL_UNLIKELY(!lhs)) return 0; - --lhs; - int shifts = 0; - for (; lhs != 0; ++shifts) lhs = lhs >> 1ULL; - return shifts; -} -static inline IData VL_CLOG2_W(int words, WDataInP const lwp) VL_MT_SAFE { - EData adjust = (VL_COUNTONES_W(words, lwp) == 1) ? 0 : 1; - for (int i = words - 1; i >= 0; --i) { - if (VL_UNLIKELY(lwp[i])) { // Shorter worst case if predict not taken - for (int bit = VL_EDATASIZE - 1; bit >= 0; --bit) { - if (VL_UNLIKELY(VL_BITISSET_E(lwp[i], bit))) { - return i * VL_EDATASIZE + bit + adjust; - } - } - // Can't get here - one bit must be set - } - } - return 0; -} - -static inline IData VL_MOSTSETBITP1_W(int words, WDataInP const lwp) VL_MT_SAFE { - // MSB set bit plus one; similar to FLS. 0=value is zero - for (int i = words - 1; i >= 0; --i) { - if (VL_UNLIKELY(lwp[i])) { // Shorter worst case if predict not taken - for (int bit = VL_EDATASIZE - 1; bit >= 0; --bit) { - if (VL_UNLIKELY(VL_BITISSET_E(lwp[i], bit))) return i * VL_EDATASIZE + bit + 1; - } - // Can't get here - one bit must be set - } - } - return 0; -} - -//=================================================================== -// SIMPLE LOGICAL OPERATORS - -// EMIT_RULE: VL_AND: oclean=lclean||rclean; obits=lbits; lbits==rbits; -static inline WDataOutP VL_AND_W(int words, WDataOutP owp, WDataInP const lwp, - WDataInP const rwp) VL_MT_SAFE { - for (int i = 0; (i < words); ++i) owp[i] = (lwp[i] & rwp[i]); - return owp; -} -// EMIT_RULE: VL_OR: oclean=lclean&&rclean; obits=lbits; lbits==rbits; -static inline WDataOutP VL_OR_W(int words, WDataOutP owp, WDataInP const lwp, - WDataInP const rwp) VL_MT_SAFE { - for (int i = 0; (i < words); ++i) owp[i] = (lwp[i] | rwp[i]); - return owp; -} -// EMIT_RULE: VL_CHANGEXOR: oclean=1; obits=32; lbits==rbits; -static inline IData VL_CHANGEXOR_W(int words, WDataInP const lwp, WDataInP const rwp) VL_MT_SAFE { - IData od = 0; - for (int i = 0; (i < words); ++i) od |= (lwp[i] ^ rwp[i]); - return od; -} -// EMIT_RULE: VL_XOR: oclean=lclean&&rclean; obits=lbits; lbits==rbits; -static inline WDataOutP VL_XOR_W(int words, WDataOutP owp, WDataInP const lwp, - WDataInP const rwp) VL_MT_SAFE { - for (int i = 0; (i < words); ++i) owp[i] = (lwp[i] ^ rwp[i]); - return owp; -} -// EMIT_RULE: VL_NOT: oclean=dirty; obits=lbits; -static inline WDataOutP VL_NOT_W(int words, WDataOutP owp, WDataInP const lwp) VL_MT_SAFE { - for (int i = 0; i < words; ++i) owp[i] = ~(lwp[i]); - return owp; -} - -//========================================================================= -// Logical comparisons - -// EMIT_RULE: VL_EQ: oclean=clean; lclean==clean; rclean==clean; obits=1; lbits==rbits; -// EMIT_RULE: VL_NEQ: oclean=clean; lclean==clean; rclean==clean; obits=1; lbits==rbits; -// EMIT_RULE: VL_LT: oclean=clean; lclean==clean; rclean==clean; obits=1; lbits==rbits; -// EMIT_RULE: VL_GT: oclean=clean; lclean==clean; rclean==clean; obits=1; lbits==rbits; -// EMIT_RULE: VL_GTE: oclean=clean; lclean==clean; rclean==clean; obits=1; lbits==rbits; -// EMIT_RULE: VL_LTE: oclean=clean; lclean==clean; rclean==clean; obits=1; lbits==rbits; -#define VL_NEQ_W(words, lwp, rwp) (!VL_EQ_W(words, lwp, rwp)) -#define VL_LT_W(words, lwp, rwp) (_vl_cmp_w(words, lwp, rwp) < 0) -#define VL_LTE_W(words, lwp, rwp) (_vl_cmp_w(words, lwp, rwp) <= 0) -#define VL_GT_W(words, lwp, rwp) (_vl_cmp_w(words, lwp, rwp) > 0) -#define VL_GTE_W(words, lwp, rwp) (_vl_cmp_w(words, lwp, rwp) >= 0) - -// Output clean, AND MUST BE CLEAN -static inline IData VL_EQ_W(int words, WDataInP const lwp, WDataInP const rwp) VL_MT_SAFE { - EData nequal = 0; - for (int i = 0; (i < words); ++i) nequal |= (lwp[i] ^ rwp[i]); - return (nequal == 0); -} - -// Internal usage -static inline int _vl_cmp_w(int words, WDataInP const lwp, WDataInP const rwp) VL_MT_SAFE { - for (int i = words - 1; i >= 0; --i) { - if (lwp[i] > rwp[i]) return 1; - if (lwp[i] < rwp[i]) return -1; - } - return 0; // == -} - -#define VL_LTS_IWW(obits, lbits, rbbits, lwp, rwp) (_vl_cmps_w(lbits, lwp, rwp) < 0) -#define VL_LTES_IWW(obits, lbits, rbits, lwp, rwp) (_vl_cmps_w(lbits, lwp, rwp) <= 0) -#define VL_GTS_IWW(obits, lbits, rbits, lwp, rwp) (_vl_cmps_w(lbits, lwp, rwp) > 0) -#define VL_GTES_IWW(obits, lbits, rbits, lwp, rwp) (_vl_cmps_w(lbits, lwp, rwp) >= 0) - -static inline IData VL_GTS_III(int, int lbits, int, IData lhs, IData rhs) VL_PURE { - // For lbits==32, this becomes just a single instruction, otherwise ~5. - // GCC 3.3.4 sign extension bugs on AMD64 architecture force us to use quad logic - const vlsint64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); // Q for gcc - const vlsint64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); // Q for gcc - return lhs_signed > rhs_signed; -} -static inline IData VL_GTS_IQQ(int, int lbits, int, QData lhs, QData rhs) VL_PURE { - const vlsint64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); - const vlsint64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); - return lhs_signed > rhs_signed; -} - -static inline IData VL_GTES_III(int, int lbits, int, IData lhs, IData rhs) VL_PURE { - const vlsint64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); // Q for gcc - const vlsint64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); // Q for gcc - return lhs_signed >= rhs_signed; -} -static inline IData VL_GTES_IQQ(int, int lbits, int, QData lhs, QData rhs) VL_PURE { - const vlsint64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); - const vlsint64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); - return lhs_signed >= rhs_signed; -} - -static inline IData VL_LTS_III(int, int lbits, int, IData lhs, IData rhs) VL_PURE { - const vlsint64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); // Q for gcc - const vlsint64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); // Q for gcc - return lhs_signed < rhs_signed; -} -static inline IData VL_LTS_IQQ(int, int lbits, int, QData lhs, QData rhs) VL_PURE { - const vlsint64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); - const vlsint64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); - return lhs_signed < rhs_signed; -} - -static inline IData VL_LTES_III(int, int lbits, int, IData lhs, IData rhs) VL_PURE { - const vlsint64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); // Q for gcc - const vlsint64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); // Q for gcc - return lhs_signed <= rhs_signed; -} -static inline IData VL_LTES_IQQ(int, int lbits, int, QData lhs, QData rhs) VL_PURE { - const vlsint64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); - const vlsint64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); - return lhs_signed <= rhs_signed; -} - -static inline int _vl_cmps_w(int lbits, WDataInP const lwp, WDataInP const rwp) VL_MT_SAFE { - const int words = VL_WORDS_I(lbits); - int i = words - 1; - // We need to flip sense if negative comparison - const EData lsign = VL_SIGN_E(lbits, lwp[i]); - const EData rsign = VL_SIGN_E(lbits, rwp[i]); - if (!lsign && rsign) return 1; // + > - - if (lsign && !rsign) return -1; // - < + - for (; i >= 0; --i) { - if (lwp[i] > rwp[i]) return 1; - if (lwp[i] < rwp[i]) return -1; - } - return 0; // == -} - -//========================================================================= -// Math - -// Output NOT clean -static inline WDataOutP VL_NEGATE_W(int words, WDataOutP owp, WDataInP const lwp) VL_MT_SAFE { - EData carry = 1; - for (int i = 0; i < words; ++i) { - owp[i] = ~lwp[i] + carry; - carry = (owp[i] < ~lwp[i]); - } - return owp; -} -static inline void VL_NEGATE_INPLACE_W(int words, WDataOutP owp_lwp) VL_MT_SAFE { - EData carry = 1; - for (int i = 0; i < words; ++i) { - EData word = ~owp_lwp[i] + carry; - carry = (word < ~owp_lwp[i]); - owp_lwp[i] = word; - } -} - -// EMIT_RULE: VL_MUL: oclean=dirty; lclean==clean; rclean==clean; -// EMIT_RULE: VL_DIV: oclean=dirty; lclean==clean; rclean==clean; -// EMIT_RULE: VL_MODDIV: oclean=dirty; lclean==clean; rclean==clean; -#define VL_DIV_III(lbits, lhs, rhs) (((rhs) == 0) ? 0 : (lhs) / (rhs)) -#define VL_DIV_QQQ(lbits, lhs, rhs) (((rhs) == 0) ? 0 : (lhs) / (rhs)) -#define VL_DIV_WWW(lbits, owp, lwp, rwp) (_vl_moddiv_w(lbits, owp, lwp, rwp, 0)) -#define VL_MODDIV_III(lbits, lhs, rhs) (((rhs) == 0) ? 0 : (lhs) % (rhs)) -#define VL_MODDIV_QQQ(lbits, lhs, rhs) (((rhs) == 0) ? 0 : (lhs) % (rhs)) -#define VL_MODDIV_WWW(lbits, owp, lwp, rwp) (_vl_moddiv_w(lbits, owp, lwp, rwp, 1)) - -static inline WDataOutP VL_ADD_W(int words, WDataOutP owp, WDataInP const lwp, - WDataInP const rwp) VL_MT_SAFE { - QData carry = 0; - for (int i = 0; i < words; ++i) { - carry = carry + static_cast(lwp[i]) + static_cast(rwp[i]); - owp[i] = (carry & 0xffffffffULL); - carry = (carry >> 32ULL) & 0xffffffffULL; - } - // Last output word is dirty - return owp; -} - -static inline WDataOutP VL_SUB_W(int words, WDataOutP owp, WDataInP const lwp, - WDataInP const rwp) VL_MT_SAFE { - QData carry = 0; - for (int i = 0; i < words; ++i) { - carry = (carry + static_cast(lwp[i]) - + static_cast(static_cast(~rwp[i]))); - if (i == 0) ++carry; // Negation of rwp - owp[i] = (carry & 0xffffffffULL); - carry = (carry >> 32ULL) & 0xffffffffULL; - } - // Last output word is dirty - return owp; -} - -static inline WDataOutP VL_MUL_W(int words, WDataOutP owp, WDataInP const lwp, - WDataInP const rwp) VL_MT_SAFE { - for (int i = 0; i < words; ++i) owp[i] = 0; - for (int lword = 0; lword < words; ++lword) { - for (int rword = 0; rword < words; ++rword) { - QData mul = static_cast(lwp[lword]) * static_cast(rwp[rword]); - for (int qword = lword + rword; qword < words; ++qword) { - mul += static_cast(owp[qword]); - owp[qword] = (mul & 0xffffffffULL); - mul = (mul >> 32ULL) & 0xffffffffULL; - } - } - } - // Last output word is dirty - return owp; -} - -static inline IData VL_MULS_III(int, int lbits, int, IData lhs, IData rhs) VL_PURE { - const vlsint32_t lhs_signed = VL_EXTENDS_II(32, lbits, lhs); - const vlsint32_t rhs_signed = VL_EXTENDS_II(32, lbits, rhs); - return lhs_signed * rhs_signed; -} -static inline QData VL_MULS_QQQ(int, int lbits, int, QData lhs, QData rhs) VL_PURE { - const vlsint64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); - const vlsint64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); - return lhs_signed * rhs_signed; -} - -static inline WDataOutP VL_MULS_WWW(int, int lbits, int, WDataOutP owp, WDataInP const lwp, - WDataInP const rwp) VL_MT_SAFE { - const int words = VL_WORDS_I(lbits); - // cppcheck-suppress variableScope - WData lwstore[VL_MULS_MAX_WORDS]; // Fixed size, as MSVC++ doesn't allow [words] here - // cppcheck-suppress variableScope - WData rwstore[VL_MULS_MAX_WORDS]; - WDataInP lwusp = lwp; - WDataInP rwusp = rwp; - EData lneg = VL_SIGN_E(lbits, lwp[words - 1]); - if (lneg) { // Negate lhs - lwusp = lwstore; - VL_NEGATE_W(words, lwstore, lwp); - lwstore[words - 1] &= VL_MASK_E(lbits); // Clean it - } - EData rneg = VL_SIGN_E(lbits, rwp[words - 1]); - if (rneg) { // Negate rhs - rwusp = rwstore; - VL_NEGATE_W(words, rwstore, rwp); - rwstore[words - 1] &= VL_MASK_E(lbits); // Clean it - } - VL_MUL_W(words, owp, lwusp, rwusp); - owp[words - 1] &= VL_MASK_E( - lbits); // Clean. Note it's ok for the multiply to overflow into the sign bit - if ((lneg ^ rneg) & 1) { // Negate output (not using NEGATE, as owp==lwp) - QData carry = 0; - for (int i = 0; i < words; ++i) { - carry = carry + static_cast(static_cast(~owp[i])); - if (i == 0) ++carry; // Negation of temp2 - owp[i] = (carry & 0xffffffffULL); - carry = (carry >> 32ULL) & 0xffffffffULL; - } - // Not needed: owp[words-1] |= 1< 0) power = power * power; - if (rhs & (1ULL << i)) out *= power; - } - return out; -} -static inline QData VL_POW_QQQ(int, int, int rbits, QData lhs, QData rhs) VL_PURE { - if (VL_UNLIKELY(rhs == 0)) return 1; - if (VL_UNLIKELY(lhs == 0)) return 0; - QData power = lhs; - QData out = 1ULL; - for (int i = 0; i < rbits; ++i) { - if (i > 0) power = power * power; - if (rhs & (1ULL << i)) out *= power; - } - return out; -} -WDataOutP VL_POW_WWW(int obits, int, int rbits, WDataOutP owp, WDataInP const lwp, - WDataInP const rwp); -WDataOutP VL_POW_WWQ(int obits, int, int rbits, WDataOutP owp, WDataInP const lwp, QData rhs); -QData VL_POW_QQW(int obits, int, int rbits, QData lhs, WDataInP const rwp); - -#define VL_POWSS_IIQ(obits, lbits, rbits, lhs, rhs, lsign, rsign) \ - VL_POWSS_QQQ(obits, lbits, rbits, lhs, rhs, lsign, rsign) -#define VL_POWSS_IIQ(obits, lbits, rbits, lhs, rhs, lsign, rsign) \ - VL_POWSS_QQQ(obits, lbits, rbits, lhs, rhs, lsign, rsign) -#define VL_POWSS_IIW(obits, lbits, rbits, lhs, rwp, lsign, rsign) \ - VL_POWSS_QQW(obits, lbits, rbits, lhs, rwp, lsign, rsign) -#define VL_POWSS_QQI(obits, lbits, rbits, lhs, rhs, lsign, rsign) \ - VL_POWSS_QQQ(obits, lbits, rbits, lhs, rhs, lsign, rsign) -#define VL_POWSS_WWI(obits, lbits, rbits, owp, lwp, rhs, lsign, rsign) \ - VL_POWSS_WWQ(obits, lbits, rbits, owp, lwp, rhs, lsign, rsign) - -static inline IData VL_POWSS_III(int obits, int, int rbits, IData lhs, IData rhs, bool lsign, - bool rsign) VL_MT_SAFE { - if (VL_UNLIKELY(rhs == 0)) return 1; - if (rsign && VL_SIGN_I(rbits, rhs)) { - if (lhs == 0) { - return 0; // "X" - } else if (lhs == 1) { - return 1; - } else if (lsign && lhs == VL_MASK_I(obits)) { // -1 - if (rhs & 1) { - return VL_MASK_I(obits); // -1^odd=-1 - } else { - return 1; // -1^even=1 - } - } - return 0; - } - return VL_POW_III(obits, rbits, rbits, lhs, rhs); -} -static inline QData VL_POWSS_QQQ(int obits, int, int rbits, QData lhs, QData rhs, bool lsign, - bool rsign) VL_MT_SAFE { - if (VL_UNLIKELY(rhs == 0)) return 1; - if (rsign && VL_SIGN_Q(rbits, rhs)) { - if (lhs == 0) { - return 0; // "X" - } else if (lhs == 1) { - return 1; - } else if (lsign && lhs == VL_MASK_Q(obits)) { // -1 - if (rhs & 1) { - return VL_MASK_Q(obits); // -1^odd=-1 - } else { - return 1; // -1^even=1 - } - } - return 0; - } - return VL_POW_QQQ(obits, rbits, rbits, lhs, rhs); -} -WDataOutP VL_POWSS_WWW(int obits, int, int rbits, WDataOutP owp, WDataInP const lwp, - WDataInP const rwp, bool lsign, bool rsign); -WDataOutP VL_POWSS_WWQ(int obits, int, int rbits, WDataOutP owp, WDataInP const lwp, QData rhs, - bool lsign, bool rsign); -QData VL_POWSS_QQW(int obits, int, int rbits, QData lhs, WDataInP const rwp, bool lsign, - bool rsign); - -//=================================================================== -// Concat/replication - -// INTERNAL: Stuff LHS bit 0++ into OUTPUT at specified offset -// ld may be "dirty", output is clean -static inline void _vl_insert_II(int, CData& lhsr, IData ld, int hbit, int lbit, - int rbits) VL_PURE { - const IData cleanmask = VL_MASK_I(rbits); - const IData insmask = (VL_MASK_I(hbit - lbit + 1)) << lbit; - lhsr = (lhsr & ~insmask) | ((ld << lbit) & (insmask & cleanmask)); -} -static inline void _vl_insert_II(int, SData& lhsr, IData ld, int hbit, int lbit, - int rbits) VL_PURE { - const IData cleanmask = VL_MASK_I(rbits); - const IData insmask = (VL_MASK_I(hbit - lbit + 1)) << lbit; - lhsr = (lhsr & ~insmask) | ((ld << lbit) & (insmask & cleanmask)); -} -static inline void _vl_insert_II(int, IData& lhsr, IData ld, int hbit, int lbit, - int rbits) VL_PURE { - const IData cleanmask = VL_MASK_I(rbits); - const IData insmask = (VL_MASK_I(hbit - lbit + 1)) << lbit; - lhsr = (lhsr & ~insmask) | ((ld << lbit) & (insmask & cleanmask)); -} -static inline void _vl_insert_QQ(int, QData& lhsr, QData ld, int hbit, int lbit, - int rbits) VL_PURE { - const QData cleanmask = VL_MASK_Q(rbits); - const QData insmask = (VL_MASK_Q(hbit - lbit + 1)) << lbit; - lhsr = (lhsr & ~insmask) | ((ld << lbit) & (insmask & cleanmask)); -} -static inline void _vl_insert_WI(int, WDataOutP owp, IData ld, int hbit, int lbit, - int rbits = 0) VL_MT_SAFE { - const int hoffset = VL_BITBIT_E(hbit); - const int loffset = VL_BITBIT_E(lbit); - const int roffset = VL_BITBIT_E(rbits); - const int hword = VL_BITWORD_E(hbit); - const int lword = VL_BITWORD_E(lbit); - const int rword = VL_BITWORD_E(rbits); - const EData cleanmask = hword == rword ? VL_MASK_E(roffset) : VL_MASK_E(0); - - if (hoffset == VL_SIZEBITS_E && loffset == 0) { - // Fast and common case, word based insertion - owp[VL_BITWORD_E(lbit)] = ld & cleanmask; - } else { - const EData lde = static_cast(ld); - if (hword == lword) { // know < EData bits because above checks it - // Assignment is contained within one word of destination - const EData insmask = (VL_MASK_E(hoffset - loffset + 1)) << loffset; - owp[lword] = (owp[lword] & ~insmask) | ((lde << loffset) & (insmask & cleanmask)); - } else { - // Assignment crosses a word boundary in destination - const EData hinsmask = (VL_MASK_E(hoffset - 0 + 1)) << 0; - const EData linsmask = (VL_MASK_E((VL_EDATASIZE - 1) - loffset + 1)) << loffset; - const int nbitsonright = VL_EDATASIZE - loffset; // bits that end up in lword - owp[lword] = (owp[lword] & ~linsmask) | ((lde << loffset) & linsmask); - owp[hword] - = (owp[hword] & ~hinsmask) | ((lde >> nbitsonright) & (hinsmask & cleanmask)); - } - } -} - -// INTERNAL: Stuff large LHS bit 0++ into OUTPUT at specified offset -// lwp may be "dirty" -static inline void _vl_insert_WW(int, WDataOutP owp, WDataInP const lwp, int hbit, int lbit, - int rbits = 0) VL_MT_SAFE { - const int hoffset = VL_BITBIT_E(hbit); - const int loffset = VL_BITBIT_E(lbit); - const int roffset = VL_BITBIT_E(rbits); - const int lword = VL_BITWORD_E(lbit); - const int hword = VL_BITWORD_E(hbit); - const int rword = VL_BITWORD_E(rbits); - const int words = VL_WORDS_I(hbit - lbit + 1); - // Cleaning mask, only applied to top word of the assignment. Is a no-op - // if we don't assign to the top word of the destination. - const EData cleanmask = hword == rword ? VL_MASK_E(roffset) : VL_MASK_E(0); - - if (hoffset == VL_SIZEBITS_E && loffset == 0) { - // Fast and common case, word based insertion - for (int i = 0; i < (words - 1); ++i) owp[lword + i] = lwp[i]; - owp[hword] = lwp[words - 1] & cleanmask; - } else if (loffset == 0) { - // Non-32bit, but nicely aligned, so stuff all but the last word - for (int i = 0; i < (words - 1); ++i) owp[lword + i] = lwp[i]; - // Know it's not a full word as above fast case handled it - const EData hinsmask = (VL_MASK_E(hoffset - 0 + 1)); - owp[hword] = (owp[hword] & ~hinsmask) | (lwp[words - 1] & (hinsmask & cleanmask)); - } else { - const EData hinsmask = (VL_MASK_E(hoffset - 0 + 1)) << 0; - const EData linsmask = (VL_MASK_E((VL_EDATASIZE - 1) - loffset + 1)) << loffset; - const int nbitsonright - = VL_EDATASIZE - loffset; // bits that end up in lword (know loffset!=0) - // Middle words - for (int i = 0; i < words; ++i) { - { // Lower word - const int oword = lword + i; - const EData d = lwp[i] << loffset; - const EData od = (owp[oword] & ~linsmask) | (d & linsmask); - if (oword == hword) { - owp[oword] = (owp[oword] & ~hinsmask) | (od & (hinsmask & cleanmask)); - } else { - owp[oword] = od; - } - } - { // Upper word - const int oword = lword + i + 1; - if (oword <= hword) { - const EData d = lwp[i] >> nbitsonright; - const EData od = (d & ~linsmask) | (owp[oword] & linsmask); - if (oword == hword) { - owp[oword] = (owp[oword] & ~hinsmask) | (od & (hinsmask & cleanmask)); - } else { - owp[oword] = od; - } - } - } - } - } -} - -static inline void _vl_insert_WQ(int obits, WDataOutP owp, QData ld, int hbit, int lbit, - int rbits = 0) VL_MT_SAFE { - WData lwp[VL_WQ_WORDS_E]; - VL_SET_WQ(lwp, ld); - _vl_insert_WW(obits, owp, lwp, hbit, lbit, rbits); -} - -// EMIT_RULE: VL_REPLICATE: oclean=clean>width32, dirty<=width32; lclean=clean; rclean==clean; -// RHS MUST BE CLEAN CONSTANT. -#define VL_REPLICATE_IOI(obits, lbits, rbits, ld, rep) (-(ld)) // Iff lbits==1 -#define VL_REPLICATE_QOI(obits, lbits, rbits, ld, rep) (-(static_cast(ld))) // Iff lbits==1 - -static inline IData VL_REPLICATE_III(int, int lbits, int, IData ld, IData rep) VL_PURE { - IData returndata = ld; - for (unsigned i = 1; i < rep; ++i) { - returndata = returndata << lbits; - returndata |= ld; - } - return returndata; -} -static inline QData VL_REPLICATE_QII(int, int lbits, int, IData ld, IData rep) VL_PURE { - QData returndata = ld; - for (unsigned i = 1; i < rep; ++i) { - returndata = returndata << lbits; - returndata |= static_cast(ld); - } - return returndata; -} -static inline WDataOutP VL_REPLICATE_WII(int obits, int lbits, int, WDataOutP owp, IData ld, - IData rep) VL_MT_SAFE { - owp[0] = ld; - for (unsigned i = 1; i < rep; ++i) { - _vl_insert_WI(obits, owp, ld, i * lbits + lbits - 1, i * lbits); - } - return owp; -} -static inline WDataOutP VL_REPLICATE_WQI(int obits, int lbits, int, WDataOutP owp, QData ld, - IData rep) VL_MT_SAFE { - VL_SET_WQ(owp, ld); - for (unsigned i = 1; i < rep; ++i) { - _vl_insert_WQ(obits, owp, ld, i * lbits + lbits - 1, i * lbits); - } - return owp; -} -static inline WDataOutP VL_REPLICATE_WWI(int obits, int lbits, int, WDataOutP owp, - WDataInP const lwp, IData rep) VL_MT_SAFE { - for (int i = 0; i < VL_WORDS_I(lbits); ++i) owp[i] = lwp[i]; - for (unsigned i = 1; i < rep; ++i) { - _vl_insert_WW(obits, owp, lwp, i * lbits + lbits - 1, i * lbits); - } - return owp; -} - -// Left stream operator. Output will always be clean. LHS and RHS must be clean. -// Special "fast" versions for slice sizes that are a power of 2. These use -// shifts and masks to execute faster than the slower for-loop approach where a -// subset of bits is copied in during each iteration. -static inline IData VL_STREAML_FAST_III(int, int lbits, int, IData ld, IData rd_log2) VL_PURE { - // Pre-shift bits in most-significant slice: - // - // If lbits is not a multiple of the slice size (i.e., lbits % rd != 0), - // then we end up with a "gap" in our reversed result. For example, if we - // have a 5-bit Verlilog signal (lbits=5) in an 8-bit C data type: - // - // ld = ---43210 - // - // (where numbers are the Verilog signal bit numbers and '-' is an unused bit). - // Executing the switch statement below with a slice size of two (rd=2, - // rd_log2=1) produces: - // - // ret = 1032-400 - // - // Pre-shifting the bits in the most-significant slice allows us to avoid - // this gap in the shuffled data: - // - // ld_adjusted = --4-3210 - // ret = 10324--- - IData ret = ld; - if (rd_log2) { - const vluint32_t lbitsFloor = lbits & ~VL_MASK_I(rd_log2); // max multiple of rd <= lbits - const vluint32_t lbitsRem = lbits - lbitsFloor; // number of bits in most-sig slice (MSS) - const IData msbMask = VL_MASK_I(lbitsRem) << lbitsFloor; // mask to sel only bits in MSS - ret = (ret & ~msbMask) | ((ret & msbMask) << ((VL_UL(1) << rd_log2) - lbitsRem)); - } - switch (rd_log2) { - case 0: ret = ((ret >> 1) & VL_UL(0x55555555)) | ((ret & VL_UL(0x55555555)) << 1); // FALLTHRU - case 1: ret = ((ret >> 2) & VL_UL(0x33333333)) | ((ret & VL_UL(0x33333333)) << 2); // FALLTHRU - case 2: ret = ((ret >> 4) & VL_UL(0x0f0f0f0f)) | ((ret & VL_UL(0x0f0f0f0f)) << 4); // FALLTHRU - case 3: ret = ((ret >> 8) & VL_UL(0x00ff00ff)) | ((ret & VL_UL(0x00ff00ff)) << 8); // FALLTHRU - case 4: ret = ((ret >> 16) | (ret << 16)); // FALLTHRU - default:; - } - return ret >> (VL_IDATASIZE - lbits); -} - -static inline QData VL_STREAML_FAST_QQI(int, int lbits, int, QData ld, IData rd_log2) VL_PURE { - // Pre-shift bits in most-significant slice (see comment in VL_STREAML_FAST_III) - QData ret = ld; - if (rd_log2) { - const vluint32_t lbitsFloor = lbits & ~VL_MASK_I(rd_log2); - const vluint32_t lbitsRem = lbits - lbitsFloor; - const QData msbMask = VL_MASK_Q(lbitsRem) << lbitsFloor; - ret = (ret & ~msbMask) | ((ret & msbMask) << ((1ULL << rd_log2) - lbitsRem)); - } - switch (rd_log2) { - case 0: - ret = (((ret >> 1) & 0x5555555555555555ULL) - | ((ret & 0x5555555555555555ULL) << 1)); // FALLTHRU - case 1: - ret = (((ret >> 2) & 0x3333333333333333ULL) - | ((ret & 0x3333333333333333ULL) << 2)); // FALLTHRU - case 2: - ret = (((ret >> 4) & 0x0f0f0f0f0f0f0f0fULL) - | ((ret & 0x0f0f0f0f0f0f0f0fULL) << 4)); // FALLTHRU - case 3: - ret = (((ret >> 8) & 0x00ff00ff00ff00ffULL) - | ((ret & 0x00ff00ff00ff00ffULL) << 8)); // FALLTHRU - case 4: - ret = (((ret >> 16) & 0x0000ffff0000ffffULL) - | ((ret & 0x0000ffff0000ffffULL) << 16)); // FALLTHRU - case 5: ret = ((ret >> 32) | (ret << 32)); // FALLTHRU - default:; - } - return ret >> (VL_QUADSIZE - lbits); -} - -// Regular "slow" streaming operators -static inline IData VL_STREAML_III(int, int lbits, int, IData ld, IData rd) VL_PURE { - IData ret = 0; - // Slice size should never exceed the lhs width - const IData mask = VL_MASK_I(rd); - for (int istart = 0; istart < lbits; istart += rd) { - int ostart = lbits - rd - istart; - ostart = ostart > 0 ? ostart : 0; - ret |= ((ld >> istart) & mask) << ostart; - } - return ret; -} - -static inline QData VL_STREAML_QQI(int, int lbits, int, QData ld, IData rd) VL_PURE { - QData ret = 0; - // Slice size should never exceed the lhs width - const QData mask = VL_MASK_Q(rd); - for (int istart = 0; istart < lbits; istart += rd) { - int ostart = lbits - rd - istart; - ostart = ostart > 0 ? ostart : 0; - ret |= ((ld >> istart) & mask) << ostart; - } - return ret; -} - -static inline WDataOutP VL_STREAML_WWI(int, int lbits, int, WDataOutP owp, WDataInP const lwp, - IData rd) VL_MT_SAFE { - VL_ZERO_W(lbits, owp); - // Slice size should never exceed the lhs width - const int ssize = (rd < static_cast(lbits)) ? rd : (static_cast(lbits)); - for (int istart = 0; istart < lbits; istart += rd) { - int ostart = lbits - rd - istart; - ostart = ostart > 0 ? ostart : 0; - for (int sbit = 0; sbit < ssize && sbit < lbits - istart; ++sbit) { - // Extract a single bit from lwp and shift it to the correct - // location for owp. - EData bit = (VL_BITRSHIFT_W(lwp, (istart + sbit)) & 1) << VL_BITBIT_E(ostart + sbit); - owp[VL_BITWORD_E(ostart + sbit)] |= bit; - } - } - return owp; -} - -// Because concats are common and wide, it's valuable to always have a clean output. -// Thus we specify inputs must be clean, so we don't need to clean the output. -// Note the bit shifts are always constants, so the adds in these constify out. -// Casts required, as args may be 8 bit entities, and need to shift to appropriate output size -#define VL_CONCAT_III(obits, lbits, rbits, ld, rd) \ - (static_cast(ld) << (rbits) | static_cast(rd)) -#define VL_CONCAT_QII(obits, lbits, rbits, ld, rd) \ - (static_cast(ld) << (rbits) | static_cast(rd)) -#define VL_CONCAT_QIQ(obits, lbits, rbits, ld, rd) \ - (static_cast(ld) << (rbits) | static_cast(rd)) -#define VL_CONCAT_QQI(obits, lbits, rbits, ld, rd) \ - (static_cast(ld) << (rbits) | static_cast(rd)) -#define VL_CONCAT_QQQ(obits, lbits, rbits, ld, rd) \ - (static_cast(ld) << (rbits) | static_cast(rd)) - -static inline WDataOutP VL_CONCAT_WII(int obits, int lbits, int rbits, WDataOutP owp, IData ld, - IData rd) VL_MT_SAFE { - owp[0] = rd; - for (int i = 1; i < VL_WORDS_I(obits); ++i) owp[i] = 0; - _vl_insert_WI(obits, owp, ld, rbits + lbits - 1, rbits); - return owp; -} -static inline WDataOutP VL_CONCAT_WWI(int obits, int lbits, int rbits, WDataOutP owp, - WDataInP const lwp, IData rd) VL_MT_SAFE { - owp[0] = rd; - for (int i = 1; i < VL_WORDS_I(obits); ++i) owp[i] = 0; - _vl_insert_WW(obits, owp, lwp, rbits + lbits - 1, rbits); - return owp; -} -static inline WDataOutP VL_CONCAT_WIW(int obits, int lbits, int rbits, WDataOutP owp, IData ld, - WDataInP const rwp) VL_MT_SAFE { - for (int i = 0; i < VL_WORDS_I(rbits); ++i) owp[i] = rwp[i]; - for (int i = VL_WORDS_I(rbits); i < VL_WORDS_I(obits); ++i) owp[i] = 0; - _vl_insert_WI(obits, owp, ld, rbits + lbits - 1, rbits); - return owp; -} -static inline WDataOutP VL_CONCAT_WIQ(int obits, int lbits, int rbits, WDataOutP owp, IData ld, - QData rd) VL_MT_SAFE { - VL_SET_WQ(owp, rd); - for (int i = VL_WQ_WORDS_E; i < VL_WORDS_I(obits); ++i) owp[i] = 0; - _vl_insert_WI(obits, owp, ld, rbits + lbits - 1, rbits); - return owp; -} -static inline WDataOutP VL_CONCAT_WQI(int obits, int lbits, int rbits, WDataOutP owp, QData ld, - IData rd) VL_MT_SAFE { - owp[0] = rd; - for (int i = 1; i < VL_WORDS_I(obits); ++i) owp[i] = 0; - _vl_insert_WQ(obits, owp, ld, rbits + lbits - 1, rbits); - return owp; -} -static inline WDataOutP VL_CONCAT_WQQ(int obits, int lbits, int rbits, WDataOutP owp, QData ld, - QData rd) VL_MT_SAFE { - VL_SET_WQ(owp, rd); - for (int i = VL_WQ_WORDS_E; i < VL_WORDS_I(obits); ++i) owp[i] = 0; - _vl_insert_WQ(obits, owp, ld, rbits + lbits - 1, rbits); - return owp; -} -static inline WDataOutP VL_CONCAT_WWQ(int obits, int lbits, int rbits, WDataOutP owp, - WDataInP const lwp, QData rd) VL_MT_SAFE { - VL_SET_WQ(owp, rd); - for (int i = VL_WQ_WORDS_E; i < VL_WORDS_I(obits); ++i) owp[i] = 0; - _vl_insert_WW(obits, owp, lwp, rbits + lbits - 1, rbits); - return owp; -} -static inline WDataOutP VL_CONCAT_WQW(int obits, int lbits, int rbits, WDataOutP owp, QData ld, - WDataInP const rwp) VL_MT_SAFE { - for (int i = 0; i < VL_WORDS_I(rbits); ++i) owp[i] = rwp[i]; - for (int i = VL_WORDS_I(rbits); i < VL_WORDS_I(obits); ++i) owp[i] = 0; - _vl_insert_WQ(obits, owp, ld, rbits + lbits - 1, rbits); - return owp; -} -static inline WDataOutP VL_CONCAT_WWW(int obits, int lbits, int rbits, WDataOutP owp, - WDataInP const lwp, WDataInP const rwp) VL_MT_SAFE { - for (int i = 0; i < VL_WORDS_I(rbits); ++i) owp[i] = rwp[i]; - for (int i = VL_WORDS_I(rbits); i < VL_WORDS_I(obits); ++i) owp[i] = 0; - _vl_insert_WW(obits, owp, lwp, rbits + lbits - 1, rbits); - return owp; -} - -//=================================================================== -// Shifts - -// Static shift, used by internal functions -// The output is the same as the input - it overlaps! -static inline void _vl_shiftl_inplace_w(int obits, WDataOutP iowp, - IData rd /*1 or 4*/) VL_MT_SAFE { - const int words = VL_WORDS_I(obits); - const EData linsmask = VL_MASK_E(rd); - for (int i = words - 1; i >= 1; --i) { - iowp[i] - = ((iowp[i] << rd) & ~linsmask) | ((iowp[i - 1] >> (VL_EDATASIZE - rd)) & linsmask); - } - iowp[0] = ((iowp[0] << rd) & ~linsmask); - iowp[VL_WORDS_I(obits) - 1] &= VL_MASK_E(obits); -} - -// EMIT_RULE: VL_SHIFTL: oclean=lclean; rclean==clean; -// Important: Unlike most other funcs, the shift might well be a computed -// expression. Thus consider this when optimizing. (And perhaps have 2 funcs?) -static inline WDataOutP VL_SHIFTL_WWI(int obits, int, int, WDataOutP owp, WDataInP const lwp, - IData rd) VL_MT_SAFE { - const int word_shift = VL_BITWORD_E(rd); - const int bit_shift = VL_BITBIT_E(rd); - if (rd >= static_cast(obits)) { // rd may be huge with MSB set - for (int i = 0; i < VL_WORDS_I(obits); ++i) owp[i] = 0; - } else if (bit_shift == 0) { // Aligned word shift (<<0,<<32,<<64 etc) - for (int i = 0; i < word_shift; ++i) owp[i] = 0; - for (int i = word_shift; i < VL_WORDS_I(obits); ++i) owp[i] = lwp[i - word_shift]; - } else { - for (int i = 0; i < VL_WORDS_I(obits); ++i) owp[i] = 0; - _vl_insert_WW(obits, owp, lwp, obits - 1, rd); - } - return owp; -} -static inline WDataOutP VL_SHIFTL_WWW(int obits, int lbits, int rbits, WDataOutP owp, - WDataInP const lwp, WDataInP const rwp) VL_MT_SAFE { - for (int i = 1; i < VL_WORDS_I(rbits); ++i) { - if (VL_UNLIKELY(rwp[i])) { // Huge shift 1>>32 or more - return VL_ZERO_W(obits, owp); - } - } - return VL_SHIFTL_WWI(obits, lbits, 32, owp, lwp, rwp[0]); -} -static inline WDataOutP VL_SHIFTL_WWQ(int obits, int lbits, int rbits, WDataOutP owp, - WDataInP const lwp, QData rd) VL_MT_SAFE { - WData rwp[VL_WQ_WORDS_E]; - VL_SET_WQ(rwp, rd); - return VL_SHIFTL_WWW(obits, lbits, rbits, owp, lwp, rwp); -} -static inline IData VL_SHIFTL_IIW(int obits, int, int rbits, IData lhs, - WDataInP const rwp) VL_MT_SAFE { - for (int i = 1; i < VL_WORDS_I(rbits); ++i) { - if (VL_UNLIKELY(rwp[i])) { // Huge shift 1>>32 or more - return 0; - } - } - return VL_CLEAN_II(obits, obits, lhs << rwp[0]); -} -static inline IData VL_SHIFTL_IIQ(int obits, int, int, IData lhs, QData rhs) VL_MT_SAFE { - if (VL_UNLIKELY(rhs >= VL_IDATASIZE)) return 0; - return VL_CLEAN_II(obits, obits, lhs << rhs); -} -static inline QData VL_SHIFTL_QQW(int obits, int, int rbits, QData lhs, - WDataInP const rwp) VL_MT_SAFE { - for (int i = 1; i < VL_WORDS_I(rbits); ++i) { - if (VL_UNLIKELY(rwp[i])) { // Huge shift 1>>32 or more - return 0; - } - } - // Above checks rwp[1]==0 so not needed in below shift - return VL_CLEAN_QQ(obits, obits, lhs << (static_cast(rwp[0]))); -} -static inline QData VL_SHIFTL_QQQ(int obits, int, int, QData lhs, QData rhs) VL_MT_SAFE { - if (VL_UNLIKELY(rhs >= VL_QUADSIZE)) return 0; - return VL_CLEAN_QQ(obits, obits, lhs << rhs); -} - -// EMIT_RULE: VL_SHIFTR: oclean=lclean; rclean==clean; -// Important: Unlike most other funcs, the shift might well be a computed -// expression. Thus consider this when optimizing. (And perhaps have 2 funcs?) -static inline WDataOutP VL_SHIFTR_WWI(int obits, int, int, WDataOutP owp, WDataInP const lwp, - IData rd) VL_MT_SAFE { - const int word_shift = VL_BITWORD_E(rd); // Maybe 0 - const int bit_shift = VL_BITBIT_E(rd); - if (rd >= static_cast(obits)) { // rd may be huge with MSB set - for (int i = 0; i < VL_WORDS_I(obits); ++i) owp[i] = 0; - } else if (bit_shift == 0) { // Aligned word shift (>>0,>>32,>>64 etc) - const int copy_words = (VL_WORDS_I(obits) - word_shift); - for (int i = 0; i < copy_words; ++i) owp[i] = lwp[i + word_shift]; - for (int i = copy_words; i < VL_WORDS_I(obits); ++i) owp[i] = 0; - } else { - const int loffset = rd & VL_SIZEBITS_E; - const int nbitsonright = VL_EDATASIZE - loffset; // bits that end up in lword (know - // loffset!=0) Middle words - const int words = VL_WORDS_I(obits - rd); - for (int i = 0; i < words; ++i) { - owp[i] = lwp[i + word_shift] >> loffset; - const int upperword = i + word_shift + 1; - if (upperword < VL_WORDS_I(obits)) owp[i] |= lwp[upperword] << nbitsonright; - } - for (int i = words; i < VL_WORDS_I(obits); ++i) owp[i] = 0; - } - return owp; -} -static inline WDataOutP VL_SHIFTR_WWW(int obits, int lbits, int rbits, WDataOutP owp, - WDataInP const lwp, WDataInP const rwp) VL_MT_SAFE { - for (int i = 1; i < VL_WORDS_I(rbits); ++i) { - if (VL_UNLIKELY(rwp[i])) { // Huge shift 1>>32 or more - return VL_ZERO_W(obits, owp); - } - } - return VL_SHIFTR_WWI(obits, lbits, 32, owp, lwp, rwp[0]); -} -static inline WDataOutP VL_SHIFTR_WWQ(int obits, int lbits, int rbits, WDataOutP owp, - WDataInP const lwp, QData rd) VL_MT_SAFE { - WData rwp[VL_WQ_WORDS_E]; - VL_SET_WQ(rwp, rd); - return VL_SHIFTR_WWW(obits, lbits, rbits, owp, lwp, rwp); -} - -static inline IData VL_SHIFTR_IIW(int obits, int, int rbits, IData lhs, - WDataInP const rwp) VL_MT_SAFE { - for (int i = 1; i < VL_WORDS_I(rbits); ++i) { - if (VL_UNLIKELY(rwp[i])) { // Huge shift 1>>32 or more - return 0; - } - } - return VL_CLEAN_II(obits, obits, lhs >> rwp[0]); -} -static inline QData VL_SHIFTR_QQW(int obits, int, int rbits, QData lhs, - WDataInP const rwp) VL_MT_SAFE { - for (int i = 1; i < VL_WORDS_I(rbits); ++i) { - if (VL_UNLIKELY(rwp[i])) { // Huge shift 1>>32 or more - return 0; - } - } - // Above checks rwp[1]==0 so not needed in below shift - return VL_CLEAN_QQ(obits, obits, lhs >> (static_cast(rwp[0]))); -} -static inline IData VL_SHIFTR_IIQ(int obits, int, int, IData lhs, QData rhs) VL_MT_SAFE { - if (VL_UNLIKELY(rhs >= VL_IDATASIZE)) return 0; - return VL_CLEAN_QQ(obits, obits, lhs >> rhs); -} -static inline QData VL_SHIFTR_QQQ(int obits, int, int, QData lhs, QData rhs) VL_MT_SAFE { - if (VL_UNLIKELY(rhs >= VL_QUADSIZE)) return 0; - return VL_CLEAN_QQ(obits, obits, lhs >> rhs); -} - -// EMIT_RULE: VL_SHIFTRS: oclean=false; lclean=clean, rclean==clean; -static inline IData VL_SHIFTRS_III(int obits, int lbits, int, IData lhs, IData rhs) VL_PURE { - // Note the C standard does not specify the >> operator as a arithmetic shift! - // IEEE says signed if output signed, but bit position from lbits; - // must use lbits for sign; lbits might != obits, - // an EXTEND(SHIFTRS(...)) can became a SHIFTRS(...) within same 32/64 bit word length - const IData sign = -(lhs >> (lbits - 1)); // ffff_ffff if negative - const IData signext = ~(VL_MASK_I(lbits) >> rhs); // One with bits where we've shifted "past" - return (lhs >> rhs) | (sign & VL_CLEAN_II(obits, obits, signext)); -} -static inline QData VL_SHIFTRS_QQI(int obits, int lbits, int, QData lhs, IData rhs) VL_PURE { - const QData sign = -(lhs >> (lbits - 1)); - const QData signext = ~(VL_MASK_Q(lbits) >> rhs); - return (lhs >> rhs) | (sign & VL_CLEAN_QQ(obits, obits, signext)); -} -static inline IData VL_SHIFTRS_IQI(int obits, int lbits, int rbits, QData lhs, IData rhs) VL_PURE { - return static_cast(VL_SHIFTRS_QQI(obits, lbits, rbits, lhs, rhs)); -} -static inline WDataOutP VL_SHIFTRS_WWI(int obits, int lbits, int, WDataOutP owp, - WDataInP const lwp, IData rd) VL_MT_SAFE { - const int word_shift = VL_BITWORD_E(rd); - const int bit_shift = VL_BITBIT_E(rd); - const int lmsw = VL_WORDS_I(obits) - 1; - const EData sign = VL_SIGNONES_E(lbits, lwp[lmsw]); - if (rd >= static_cast(obits)) { // Shifting past end, sign in all of lbits - for (int i = 0; i <= lmsw; ++i) owp[i] = sign; - owp[lmsw] &= VL_MASK_E(lbits); - } else if (bit_shift == 0) { // Aligned word shift (>>0,>>32,>>64 etc) - const int copy_words = (VL_WORDS_I(obits) - word_shift); - for (int i = 0; i < copy_words; ++i) owp[i] = lwp[i + word_shift]; - if (copy_words >= 0) owp[copy_words - 1] |= ~VL_MASK_E(obits) & sign; - for (int i = copy_words; i < VL_WORDS_I(obits); ++i) owp[i] = sign; - owp[lmsw] &= VL_MASK_E(lbits); - } else { - const int loffset = rd & VL_SIZEBITS_E; - int nbitsonright = VL_EDATASIZE - loffset; // bits that end up in lword (know loffset!=0) - // Middle words - const int words = VL_WORDS_I(obits - rd); - for (int i = 0; i < words; ++i) { - owp[i] = lwp[i + word_shift] >> loffset; - const int upperword = i + word_shift + 1; - if (upperword < VL_WORDS_I(obits)) owp[i] |= lwp[upperword] << nbitsonright; - } - if (words) owp[words - 1] |= sign & ~VL_MASK_E(obits - loffset); - for (int i = words; i < VL_WORDS_I(obits); ++i) owp[i] = sign; - owp[lmsw] &= VL_MASK_E(lbits); - } - return owp; -} -static inline WDataOutP VL_SHIFTRS_WWW(int obits, int lbits, int rbits, WDataOutP owp, - WDataInP const lwp, WDataInP const rwp) VL_MT_SAFE { - EData overshift = 0; // Huge shift 1>>32 or more - for (int i = 1; i < VL_WORDS_I(rbits); ++i) overshift |= rwp[i]; - if (VL_UNLIKELY(overshift || rwp[0] >= obits)) { - const int lmsw = VL_WORDS_I(obits) - 1; - const EData sign = VL_SIGNONES_E(lbits, lwp[lmsw]); - for (int j = 0; j <= lmsw; ++j) owp[j] = sign; - owp[lmsw] &= VL_MASK_E(lbits); - return owp; - } - return VL_SHIFTRS_WWI(obits, lbits, 32, owp, lwp, rwp[0]); -} -static inline WDataOutP VL_SHIFTRS_WWQ(int obits, int lbits, int rbits, WDataOutP owp, - WDataInP const lwp, QData rd) VL_MT_SAFE { - WData rwp[VL_WQ_WORDS_E]; - VL_SET_WQ(rwp, rd); - return VL_SHIFTRS_WWW(obits, lbits, rbits, owp, lwp, rwp); -} -static inline IData VL_SHIFTRS_IIW(int obits, int lbits, int rbits, IData lhs, - WDataInP const rwp) VL_MT_SAFE { - EData overshift = 0; // Huge shift 1>>32 or more - for (int i = 1; i < VL_WORDS_I(rbits); ++i) overshift |= rwp[i]; - if (VL_UNLIKELY(overshift || rwp[0] >= obits)) { - const IData sign = -(lhs >> (lbits - 1)); // ffff_ffff if negative - return VL_CLEAN_II(obits, obits, sign); - } - return VL_SHIFTRS_III(obits, lbits, 32, lhs, rwp[0]); -} -static inline QData VL_SHIFTRS_QQW(int obits, int lbits, int rbits, QData lhs, - WDataInP const rwp) VL_MT_SAFE { - EData overshift = 0; // Huge shift 1>>32 or more - for (int i = 1; i < VL_WORDS_I(rbits); ++i) overshift |= rwp[i]; - if (VL_UNLIKELY(overshift || rwp[0] >= obits)) { - const QData sign = -(lhs >> (lbits - 1)); // ffff_ffff if negative - return VL_CLEAN_QQ(obits, obits, sign); - } - return VL_SHIFTRS_QQI(obits, lbits, 32, lhs, rwp[0]); -} -static inline IData VL_SHIFTRS_IIQ(int obits, int lbits, int rbits, IData lhs, - QData rhs) VL_MT_SAFE { - WData rwp[VL_WQ_WORDS_E]; - VL_SET_WQ(rwp, rhs); - return VL_SHIFTRS_IIW(obits, lbits, rbits, lhs, rwp); -} -static inline QData VL_SHIFTRS_QQQ(int obits, int lbits, int rbits, QData lhs, QData rhs) VL_PURE { - WData rwp[VL_WQ_WORDS_E]; - VL_SET_WQ(rwp, rhs); - return VL_SHIFTRS_QQW(obits, lbits, rbits, lhs, rwp); -} - -//=================================================================== -// Bit selection - -// EMIT_RULE: VL_BITSEL: oclean=dirty; rclean==clean; -#define VL_BITSEL_IIII(obits, lbits, rbits, zbits, lhs, rhs) ((lhs) >> (rhs)) -#define VL_BITSEL_QIII(obits, lbits, rbits, zbits, lhs, rhs) ((lhs) >> (rhs)) -#define VL_BITSEL_QQII(obits, lbits, rbits, zbits, lhs, rhs) ((lhs) >> (rhs)) -#define VL_BITSEL_IQII(obits, lbits, rbits, zbits, lhs, rhs) (static_cast((lhs) >> (rhs))) - -static inline IData VL_BITSEL_IWII(int, int lbits, int, int, WDataInP const lwp, - IData rd) VL_MT_SAFE { - int word = VL_BITWORD_E(rd); - if (VL_UNLIKELY(rd > static_cast(lbits))) { - return ~0; // Spec says you can go outside the range of a array. Don't coredump if so. - // We return all 1's as that's more likely to find bugs (?) than 0's. - } else { - return (lwp[word] >> VL_BITBIT_E(rd)); - } -} - -// EMIT_RULE: VL_RANGE: oclean=lclean; out=dirty -// & MUST BE CLEAN (currently constant) -#define VL_SEL_IIII(obits, lbits, rbits, tbits, lhs, lsb, width) ((lhs) >> (lsb)) -#define VL_SEL_QQII(obits, lbits, rbits, tbits, lhs, lsb, width) ((lhs) >> (lsb)) -#define VL_SEL_IQII(obits, lbits, rbits, tbits, lhs, lsb, width) \ - (static_cast((lhs) >> (lsb))) - -static inline IData VL_SEL_IWII(int, int lbits, int, int, WDataInP const lwp, IData lsb, - IData width) VL_MT_SAFE { - int msb = lsb + width - 1; - if (VL_UNLIKELY(msb >= lbits)) { - return ~0; // Spec says you can go outside the range of a array. Don't coredump if so. - } else if (VL_BITWORD_E(msb) == VL_BITWORD_E(static_cast(lsb))) { - return VL_BITRSHIFT_W(lwp, lsb); - } else { - // 32 bit extraction may span two words - int nbitsfromlow = VL_EDATASIZE - VL_BITBIT_E(lsb); // bits that come from low word - return ((lwp[VL_BITWORD_E(msb)] << nbitsfromlow) | VL_BITRSHIFT_W(lwp, lsb)); - } -} - -static inline QData VL_SEL_QWII(int, int lbits, int, int, WDataInP const lwp, IData lsb, - IData width) VL_MT_SAFE { - const int msb = lsb + width - 1; - if (VL_UNLIKELY(msb > lbits)) { - return ~0; // Spec says you can go outside the range of a array. Don't coredump if so. - } else if (VL_BITWORD_E(msb) == VL_BITWORD_E(static_cast(lsb))) { - return VL_BITRSHIFT_W(lwp, lsb); - } else if (VL_BITWORD_E(msb) == 1 + VL_BITWORD_E(static_cast(lsb))) { - const int nbitsfromlow = VL_EDATASIZE - VL_BITBIT_E(lsb); - const QData hi = (lwp[VL_BITWORD_E(msb)]); - const QData lo = VL_BITRSHIFT_W(lwp, lsb); - return (hi << nbitsfromlow) | lo; - } else { - // 64 bit extraction may span three words - int nbitsfromlow = VL_EDATASIZE - VL_BITBIT_E(lsb); - const QData hi = (lwp[VL_BITWORD_E(msb)]); - const QData mid = (lwp[VL_BITWORD_E(lsb) + 1]); - const QData lo = VL_BITRSHIFT_W(lwp, lsb); - return (hi << (nbitsfromlow + VL_EDATASIZE)) | (mid << nbitsfromlow) | lo; - } -} - -static inline WDataOutP VL_SEL_WWII(int obits, int lbits, int, int, WDataOutP owp, - WDataInP const lwp, IData lsb, IData width) VL_MT_SAFE { - const int msb = lsb + width - 1; - const int word_shift = VL_BITWORD_E(lsb); - if (VL_UNLIKELY(msb > lbits)) { // Outside bounds, - for (int i = 0; i < VL_WORDS_I(obits) - 1; ++i) owp[i] = ~0; - owp[VL_WORDS_I(obits) - 1] = VL_MASK_E(obits); - } else if (VL_BITBIT_E(lsb) == 0) { - // Just a word extract - for (int i = 0; i < VL_WORDS_I(obits); ++i) owp[i] = lwp[i + word_shift]; - } else { - // Not a _vl_insert because the bits come from any bit number and goto bit 0 - const int loffset = lsb & VL_SIZEBITS_E; - const int nbitsfromlow = VL_EDATASIZE - loffset; // bits that end up in lword (know - // loffset!=0) Middle words - const int words = VL_WORDS_I(msb - lsb + 1); - for (int i = 0; i < words; ++i) { - owp[i] = lwp[i + word_shift] >> loffset; - const int upperword = i + word_shift + 1; - if (upperword <= static_cast(VL_BITWORD_E(msb))) { - owp[i] |= lwp[upperword] << nbitsfromlow; - } - } - for (int i = words; i < VL_WORDS_I(obits); ++i) owp[i] = 0; - } - return owp; -} - -//====================================================================== -// Math needing insert/select - -// Return QData from double (numeric) -// EMIT_RULE: VL_RTOIROUND_Q_D: oclean=dirty; lclean==clean/real -static inline QData VL_RTOIROUND_Q_D(int, double lhs) VL_PURE { - // IEEE format: [63]=sign [62:52]=exp+1023 [51:0]=mantissa - // This does not need to support subnormals as they are sub-integral - lhs = VL_ROUND(lhs); - if (lhs == 0.0) return 0; - const QData q = VL_CVT_Q_D(lhs); - const int lsb = static_cast((q >> 52ULL) & VL_MASK_Q(11)) - 1023 - 52; - const vluint64_t mantissa = (q & VL_MASK_Q(52)) | (1ULL << 52); - vluint64_t out = 0; - if (lsb < 0) { - out = mantissa >> -lsb; - } else if (lsb < 64) { - out = mantissa << lsb; - } - if (lhs < 0) out = -out; - return out; -} -static inline IData VL_RTOIROUND_I_D(int bits, double lhs) VL_PURE { - return static_cast(VL_RTOIROUND_Q_D(bits, lhs)); -} -static inline WDataOutP VL_RTOIROUND_W_D(int obits, WDataOutP owp, double lhs) VL_PURE { - // IEEE format: [63]=sign [62:52]=exp+1023 [51:0]=mantissa - // This does not need to support subnormals as they are sub-integral - lhs = VL_ROUND(lhs); - VL_ZERO_W(obits, owp); - if (lhs == 0.0) return owp; - const QData q = VL_CVT_Q_D(lhs); - const int lsb = static_cast((q >> 52ULL) & VL_MASK_Q(11)) - 1023 - 52; - const vluint64_t mantissa = (q & VL_MASK_Q(52)) | (1ULL << 52); - if (lsb < 0) { - VL_SET_WQ(owp, mantissa >> -lsb); - } else if (lsb < obits) { - _vl_insert_WQ(obits, owp, mantissa, lsb + 52, lsb); - } - if (lhs < 0) VL_NEGATE_INPLACE_W(VL_WORDS_I(obits), owp); - return owp; -} - -//====================================================================== -// Range assignments - -// EMIT_RULE: VL_ASSIGNRANGE: rclean=dirty; -static inline void VL_ASSIGNSEL_IIII(int rbits, int obits, int lsb, CData& lhsr, - IData rhs) VL_PURE { - _vl_insert_II(obits, lhsr, rhs, lsb + obits - 1, lsb, rbits); -} -static inline void VL_ASSIGNSEL_IIII(int rbits, int obits, int lsb, SData& lhsr, - IData rhs) VL_PURE { - _vl_insert_II(obits, lhsr, rhs, lsb + obits - 1, lsb, rbits); -} -static inline void VL_ASSIGNSEL_IIII(int rbits, int obits, int lsb, IData& lhsr, - IData rhs) VL_PURE { - _vl_insert_II(obits, lhsr, rhs, lsb + obits - 1, lsb, rbits); -} -static inline void VL_ASSIGNSEL_QIII(int rbits, int obits, int lsb, QData& lhsr, - IData rhs) VL_PURE { - _vl_insert_QQ(obits, lhsr, rhs, lsb + obits - 1, lsb, rbits); -} -static inline void VL_ASSIGNSEL_QQII(int rbits, int obits, int lsb, QData& lhsr, - QData rhs) VL_PURE { - _vl_insert_QQ(obits, lhsr, rhs, lsb + obits - 1, lsb, rbits); -} -static inline void VL_ASSIGNSEL_QIIQ(int rbits, int obits, int lsb, QData& lhsr, - QData rhs) VL_PURE { - _vl_insert_QQ(obits, lhsr, rhs, lsb + obits - 1, lsb, rbits); -} -// static inline void VL_ASSIGNSEL_IIIW(int obits, int lsb, IData& lhsr, WDataInP const rwp) -// VL_MT_SAFE { Illegal, as lhs width >= rhs width -static inline void VL_ASSIGNSEL_WIII(int rbits, int obits, int lsb, WDataOutP owp, - IData rhs) VL_MT_SAFE { - _vl_insert_WI(obits, owp, rhs, lsb + obits - 1, lsb, rbits); -} -static inline void VL_ASSIGNSEL_WIIQ(int rbits, int obits, int lsb, WDataOutP owp, - QData rhs) VL_MT_SAFE { - _vl_insert_WQ(obits, owp, rhs, lsb + obits - 1, lsb, rbits); -} -static inline void VL_ASSIGNSEL_WIIW(int rbits, int obits, int lsb, WDataOutP owp, - WDataInP const rwp) VL_MT_SAFE { - _vl_insert_WW(obits, owp, rwp, lsb + obits - 1, lsb, rbits); -} - -//====================================================================== -// Triops - -static inline WDataOutP VL_COND_WIWW(int obits, int, int, int, WDataOutP owp, int cond, - WDataInP const w1p, WDataInP const w2p) VL_MT_SAFE { - const int words = VL_WORDS_I(obits); - for (int i = 0; i < words; ++i) owp[i] = cond ? w1p[i] : w2p[i]; - return owp; -} - -//====================================================================== -// Constification - -// VL_CONST_W_#X(int obits, WDataOutP owp, IData data0, .... IData data(#-1)) -// Sets wide vector words to specified constant words. -// These macros are used when o might represent more words then are given as constants, -// hence all upper words must be zeroed. -// If changing the number of functions here, also change EMITCINLINES_NUM_CONSTW - -#define VL_C_END_(obits, wordsSet) \ - for (int i = (wordsSet); i < VL_WORDS_I(obits); ++i) o[i] = 0; \ - return o - -// clang-format off -static inline WDataOutP VL_CONST_W_1X(int obits, WDataOutP o, EData d0) VL_MT_SAFE { - o[0] = d0; - VL_C_END_(obits, 1); -} -static inline WDataOutP VL_CONST_W_2X(int obits, WDataOutP o, EData d1, EData d0) VL_MT_SAFE { - o[0] = d0; o[1] = d1; - VL_C_END_(obits, 2); -} -static inline WDataOutP VL_CONST_W_3X(int obits, WDataOutP o, EData d2, EData d1, - EData d0) VL_MT_SAFE { - o[0] = d0; o[1] = d1; o[2] = d2; - VL_C_END_(obits,3); -} -static inline WDataOutP VL_CONST_W_4X(int obits, WDataOutP o, - EData d3, EData d2, EData d1, EData d0) VL_MT_SAFE { - o[0] = d0; o[1] = d1; o[2] = d2; o[3] = d3; - VL_C_END_(obits,4); -} -static inline WDataOutP VL_CONST_W_5X(int obits, WDataOutP o, - EData d4, - EData d3, EData d2, EData d1, EData d0) VL_MT_SAFE { - o[0] = d0; o[1] = d1; o[2] = d2; o[3] = d3; - o[4] = d4; - VL_C_END_(obits,5); -} -static inline WDataOutP VL_CONST_W_6X(int obits, WDataOutP o, - EData d5, EData d4, - EData d3, EData d2, EData d1, EData d0) VL_MT_SAFE { - o[0] = d0; o[1] = d1; o[2] = d2; o[3] = d3; - o[4] = d4; o[5] = d5; - VL_C_END_(obits,6); -} -static inline WDataOutP VL_CONST_W_7X(int obits, WDataOutP o, - EData d6, EData d5, EData d4, - EData d3, EData d2, EData d1, EData d0) VL_MT_SAFE { - o[0] = d0; o[1] = d1; o[2] = d2; o[3] = d3; - o[4] = d4; o[5] = d5; o[6] = d6; - VL_C_END_(obits,7); -} -static inline WDataOutP VL_CONST_W_8X(int obits, WDataOutP o, - EData d7, EData d6, EData d5, EData d4, - EData d3, EData d2, EData d1, EData d0) VL_MT_SAFE { - o[0] = d0; o[1] = d1; o[2] = d2; o[3] = d3; - o[4] = d4; o[5] = d5; o[6] = d6; o[7] = d7; - VL_C_END_(obits,8); -} -// -static inline WDataOutP VL_CONSTHI_W_1X(int obits, int lsb, WDataOutP obase, - EData d0) VL_MT_SAFE { - WDataOutP o = obase + VL_WORDS_I(lsb); - o[0] = d0; - VL_C_END_(obits, VL_WORDS_I(lsb) + 1); -} -static inline WDataOutP VL_CONSTHI_W_2X(int obits, int lsb, WDataOutP obase, - EData d1, EData d0) VL_MT_SAFE { - WDataOutP o = obase + VL_WORDS_I(lsb); - o[0] = d0; o[1] = d1; - VL_C_END_(obits, VL_WORDS_I(lsb) + 2); -} -static inline WDataOutP VL_CONSTHI_W_3X(int obits, int lsb, WDataOutP obase, - EData d2, EData d1, EData d0) VL_MT_SAFE { - WDataOutP o = obase + VL_WORDS_I(lsb); - o[0] = d0; o[1] = d1; o[2] = d2; - VL_C_END_(obits, VL_WORDS_I(lsb) + 3); -} -static inline WDataOutP VL_CONSTHI_W_4X(int obits, int lsb, WDataOutP obase, - EData d3, EData d2, EData d1, EData d0) VL_MT_SAFE { - WDataOutP o = obase + VL_WORDS_I(lsb); - o[0] = d0; o[1] = d1; o[2] = d2; o[3] = d3; - VL_C_END_(obits, VL_WORDS_I(lsb) + 4); -} -static inline WDataOutP VL_CONSTHI_W_5X(int obits, int lsb, WDataOutP obase, - EData d4, - EData d3, EData d2, EData d1, EData d0) VL_MT_SAFE { - WDataOutP o = obase + VL_WORDS_I(lsb); - o[0] = d0; o[1] = d1; o[2] = d2; o[3] = d3; - o[4] = d4; - VL_C_END_(obits, VL_WORDS_I(lsb) + 5); -} -static inline WDataOutP VL_CONSTHI_W_6X(int obits, int lsb, WDataOutP obase, - EData d5, EData d4, - EData d3, EData d2, EData d1, EData d0) VL_MT_SAFE { - WDataOutP o = obase + VL_WORDS_I(lsb); - o[0] = d0; o[1] = d1; o[2] = d2; o[3] = d3; - o[4] = d4; o[5] = d5; - VL_C_END_(obits, VL_WORDS_I(lsb) + 6); -} -static inline WDataOutP VL_CONSTHI_W_7X(int obits, int lsb, WDataOutP obase, - EData d6, EData d5, EData d4, - EData d3, EData d2, EData d1, EData d0) VL_MT_SAFE { - WDataOutP o = obase + VL_WORDS_I(lsb); - o[0] = d0; o[1] = d1; o[2] = d2; o[3] = d3; - o[4] = d4; o[5] = d5; o[6] = d6; - VL_C_END_(obits, VL_WORDS_I(lsb) + 7); -} -static inline WDataOutP VL_CONSTHI_W_8X(int obits, int lsb, WDataOutP obase, - EData d7, EData d6, EData d5, EData d4, - EData d3, EData d2, EData d1, EData d0) VL_MT_SAFE { - WDataOutP o = obase + VL_WORDS_I(lsb); - o[0] = d0; o[1] = d1; o[2] = d2; o[3] = d3; - o[4] = d4; o[5] = d5; o[6] = d6; o[7] = d7; - VL_C_END_(obits, VL_WORDS_I(lsb) + 8); -} - -#undef VL_C_END_ - -// Partial constant, lower words of vector wider than 8*32, starting at bit number lsb -static inline void VL_CONSTLO_W_8X(int lsb, WDataOutP obase, - EData d7, EData d6, EData d5, EData d4, - EData d3, EData d2, EData d1, EData d0) VL_MT_SAFE { - WDataOutP o = obase + VL_WORDS_I(lsb); - o[0] = d0; o[1] = d1; o[2] = d2; o[3] = d3; o[4] = d4; o[5] = d5; o[6] = d6; o[7] = d7; -} -// clang-format on +#include "verilated_funcs.h" //====================================================================== +#undef VERILATOR_VERILATED_H_INTERNAL_ #endif // Guard diff --git a/include/verilated_dpi.h b/include/verilated_dpi.h index d86e741fa..c1638f5d1 100644 --- a/include/verilated_dpi.h +++ b/include/verilated_dpi.h @@ -28,7 +28,6 @@ #include "verilatedos.h" #include "verilated.h" // Also presumably included by caller -#include "verilated_heavy.h" // Also presumably included by caller #include "verilated_sym_props.h" #include "svdpi.h" diff --git a/include/verilated_funcs.h b/include/verilated_funcs.h new file mode 100644 index 000000000..0c11c6551 --- /dev/null +++ b/include/verilated_funcs.h @@ -0,0 +1,2252 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// +// Code available from: https://verilator.org +// +// Copyright 2003-2021 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 +// +//************************************************************************* +/// +/// \file +/// \brief Verilated common functions +/// +/// verilated.h should be included instead of this file. +/// +/// Those macro/function/variable starting or ending in _ are internal, +/// however many of the other function/macros here are also internal. +/// +//************************************************************************* + +#ifndef VERILATOR_VERILATED_FUNCS_H_ +#define VERILATOR_VERILATED_FUNCS_H_ + +#ifndef VERILATOR_VERILATED_H_INTERNAL_ +#error "verilated_funcs.h should only be included by verilated.h" +#endif + +//========================================================================= +// Extern functions -- User may override -- See verilated.cpp + +/// Routine to call for $finish +/// User code may wish to replace this function, to do so, define VL_USER_FINISH. +/// This code does not have to be thread safe. +/// Verilator internal code must call VL_FINISH_MT instead, which eventually calls this. +extern void vl_finish(const char* filename, int linenum, const char* hier); + +/// Routine to call for $stop and non-fatal error +/// User code may wish to replace this function, to do so, define VL_USER_STOP. +/// This code does not have to be thread safe. +/// Verilator internal code must call VL_FINISH_MT instead, which eventually calls this. +extern void vl_stop(const char* filename, int linenum, const char* hier); + +/// Routine to call for a couple of fatal messages +/// User code may wish to replace this function, to do so, define VL_USER_FATAL. +/// This code does not have to be thread safe. +/// Verilator internal code must call VL_FINISH_MT instead, which eventually calls this. +extern void vl_fatal(const char* filename, int linenum, const char* hier, const char* msg); + +//========================================================================= +// Extern functions -- Slow path + +/// Multithread safe wrapper for calls to $finish +extern void VL_FINISH_MT(const char* filename, int linenum, const char* hier) VL_MT_SAFE; +/// Multithread safe wrapper for calls to $stop +extern void VL_STOP_MT(const char* filename, int linenum, const char* hier, + bool maybe = true) VL_MT_SAFE; +/// Multithread safe wrapper to call for a couple of fatal messages +extern void VL_FATAL_MT(const char* filename, int linenum, const char* hier, + const char* msg) VL_MT_SAFE; + +// clang-format off +/// Print a string, multithread safe. Eventually VL_PRINTF will get called. +#ifdef VL_THREADED +extern void VL_PRINTF_MT(const char* formatp, ...) VL_ATTR_PRINTF(1) VL_MT_SAFE; +#else +# define VL_PRINTF_MT VL_PRINTF // The following parens will take care of themselves +#endif +// clang-format on + +/// Print a debug message from internals with standard prefix, with printf style format +extern void VL_DBG_MSGF(const char* formatp, ...) VL_ATTR_PRINTF(1) VL_MT_SAFE; + +inline IData VL_RANDOM_I(int obits) VL_MT_SAFE { return vl_rand64() & VL_MASK_I(obits); } +inline QData VL_RANDOM_Q(int obits) VL_MT_SAFE { return vl_rand64() & VL_MASK_Q(obits); } +#ifndef VL_NO_LEGACY +extern WDataOutP VL_RANDOM_W(int obits, WDataOutP outwp); +#endif +extern IData VL_RANDOM_SEEDED_II(int obits, IData seed) VL_MT_SAFE; +inline IData VL_URANDOM_RANGE_I(IData hi, IData lo) { + vluint64_t rnd = vl_rand64(); + if (VL_LIKELY(hi > lo)) { + // Modulus isn't very fast but it's common that hi-low is power-of-two + return (rnd % (hi - lo + 1)) + lo; + } else { + return (rnd % (lo - hi + 1)) + hi; + } +} + +// These are init time only, so slow is fine +/// Random reset a signal of given width +extern IData VL_RAND_RESET_I(int obits); +/// Random reset a signal of given width +extern QData VL_RAND_RESET_Q(int obits); +/// Random reset a signal of given width +extern WDataOutP VL_RAND_RESET_W(int obits, WDataOutP outwp); +/// Zero reset a signal (slow - else use VL_ZERO_W) +extern WDataOutP VL_ZERO_RESET_W(int obits, WDataOutP outwp); + +#if VL_THREADED +/// Return high-precision counter for profiling, or 0x0 if not available +inline QData VL_RDTSC_Q() { + vluint64_t val; + VL_RDTSC(val); + return val; +} +#endif + +extern void VL_PRINTTIMESCALE(const char* namep, const char* timeunitp, + const VerilatedContext* contextp) VL_MT_SAFE; + +extern WDataOutP _vl_moddiv_w(int lbits, WDataOutP owp, WDataInP const lwp, WDataInP const rwp, + bool is_modulus); + +extern IData VL_FGETS_IXI(int obits, void* destp, IData fpi); + +extern void VL_FFLUSH_I(IData fdi); +extern IData VL_FSEEK_I(IData fdi, IData offset, IData origin); +extern IData VL_FTELL_I(IData fdi); +extern void VL_FCLOSE_I(IData fdi); + +extern IData VL_FREAD_I(int width, int array_lsb, int array_size, void* memp, IData fpi, + IData start, IData count); + +extern void VL_WRITEF(const char* formatp, ...); +extern void VL_FWRITEF(IData fpi, const char* formatp, ...); + +extern IData VL_FSCANF_IX(IData fpi, const char* formatp, ...); +extern IData VL_SSCANF_IIX(int lbits, IData ld, const char* formatp, ...); +extern IData VL_SSCANF_IQX(int lbits, QData ld, const char* formatp, ...); +extern IData VL_SSCANF_IWX(int lbits, WDataInP const lwp, const char* formatp, ...); + +extern void VL_SFORMAT_X(int obits, CData& destr, const char* formatp, ...); +extern void VL_SFORMAT_X(int obits, SData& destr, const char* formatp, ...); +extern void VL_SFORMAT_X(int obits, IData& destr, const char* formatp, ...); +extern void VL_SFORMAT_X(int obits, QData& destr, const char* formatp, ...); +extern void VL_SFORMAT_X(int obits, void* destp, const char* formatp, ...); + +extern IData VL_SYSTEM_IW(int lhswords, WDataInP const lhsp); +extern IData VL_SYSTEM_IQ(QData lhs); +inline IData VL_SYSTEM_II(IData lhs) VL_MT_SAFE { return VL_SYSTEM_IQ(lhs); } + +extern IData VL_TESTPLUSARGS_I(const char* formatp); +extern const char* vl_mc_scan_plusargs(const char* prefixp); // PLIish + +//========================================================================= +// Base macros + +// Return true if data[bit] set; not 0/1 return, but 0/non-zero return. +#define VL_BITISSET_I(data, bit) ((data) & (VL_UL(1) << VL_BITBIT_I(bit))) +#define VL_BITISSET_Q(data, bit) ((data) & (1ULL << VL_BITBIT_Q(bit))) +#define VL_BITISSET_E(data, bit) ((data) & (VL_EUL(1) << VL_BITBIT_E(bit))) +#define VL_BITISSET_W(data, bit) ((data)[VL_BITWORD_E(bit)] & (VL_EUL(1) << VL_BITBIT_E(bit))) +#define VL_BITISSETLIMIT_W(data, width, bit) (((bit) < (width)) && VL_BITISSET_W(data, bit)) + +// Shift appropriate word by bit. Does not account for wrapping between two words +#define VL_BITRSHIFT_W(data, bit) ((data)[VL_BITWORD_E(bit)] >> VL_BITBIT_E(bit)) + +// Create two 32-bit words from quadword +// WData is always at least 2 words; does not clean upper bits +#define VL_SET_WQ(owp, data) \ + do { \ + (owp)[0] = static_cast(data); \ + (owp)[1] = static_cast((data) >> VL_EDATASIZE); \ + } while (false) +#define VL_SET_WI(owp, data) \ + do { \ + (owp)[0] = static_cast(data); \ + (owp)[1] = 0; \ + } while (false) +#define VL_SET_QW(lwp) \ + ((static_cast((lwp)[0])) \ + | (static_cast((lwp)[1]) << (static_cast(VL_EDATASIZE)))) +#define VL_SET_QII(ld, rd) ((static_cast(ld) << 32ULL) | static_cast(rd)) + +// Return FILE* from IData +extern FILE* VL_CVT_I_FP(IData lhs) VL_MT_SAFE; + +// clang-format off +// Use a union to avoid cast-to-different-size warnings +// Return void* from QData +static inline void* VL_CVT_Q_VP(QData lhs) VL_PURE { + union { void* fp; QData q; } u; + u.q = lhs; + return u.fp; +} +// Return QData from const void* +static inline QData VL_CVT_VP_Q(const void* fp) VL_PURE { + union { const void* fp; QData q; } u; + u.q = 0; + u.fp = fp; + return u.q; +} +// Return double from QData (bits, not numerically) +static inline double VL_CVT_D_Q(QData lhs) VL_PURE { + union { double d; QData q; } u; + u.q = lhs; + return u.d; +} +// Return QData from double (bits, not numerically) +static inline QData VL_CVT_Q_D(double lhs) VL_PURE { + union { double d; QData q; } u; + u.d = lhs; + return u.q; +} +// clang-format on + +// Return double from lhs (numeric) unsigned +double VL_ITOR_D_W(int lbits, WDataInP const lwp) VL_PURE; +static inline double VL_ITOR_D_I(int, IData lhs) VL_PURE { + return static_cast(static_cast(lhs)); +} +static inline double VL_ITOR_D_Q(int, QData lhs) VL_PURE { + return static_cast(static_cast(lhs)); +} +// Return double from lhs (numeric) signed +double VL_ISTOR_D_W(int lbits, WDataInP const lwp) VL_PURE; +static inline double VL_ISTOR_D_I(int lbits, IData lhs) VL_PURE { + if (lbits == 32) return static_cast(static_cast(lhs)); + VlWide lwp; + VL_SET_WI(lwp, lhs); + return VL_ISTOR_D_W(lbits, lwp); +} +static inline double VL_ISTOR_D_Q(int lbits, QData lhs) VL_PURE { + if (lbits == 64) return static_cast(static_cast(lhs)); + VlWide lwp; + VL_SET_WQ(lwp, lhs); + return VL_ISTOR_D_W(lbits, lwp); +} +// Return QData from double (numeric) +static inline IData VL_RTOI_I_D(double lhs) VL_PURE { + return static_cast(VL_TRUNC(lhs)); +} + +// Sign extend such that if MSB set, we get ffff_ffff, else 0s +// (Requires clean input) +#define VL_SIGN_I(nbits, lhs) ((lhs) >> VL_BITBIT_I((nbits)-VL_UL(1))) +#define VL_SIGN_Q(nbits, lhs) ((lhs) >> VL_BITBIT_Q((nbits)-1ULL)) +#define VL_SIGN_E(nbits, lhs) ((lhs) >> VL_BITBIT_E((nbits)-VL_EUL(1))) +#define VL_SIGN_W(nbits, rwp) \ + ((rwp)[VL_BITWORD_E((nbits)-VL_EUL(1))] >> VL_BITBIT_E((nbits)-VL_EUL(1))) +#define VL_SIGNONES_E(nbits, lhs) (-(VL_SIGN_E(nbits, lhs))) + +// Sign bit extended up to MSB, doesn't include unsigned portion +// Optimization bug in GCC 3.3 returns different bitmasks to later states for +static inline IData VL_EXTENDSIGN_I(int lbits, IData lhs) VL_PURE { + return (-((lhs) & (VL_UL(1) << (lbits - 1)))); +} +static inline QData VL_EXTENDSIGN_Q(int lbits, QData lhs) VL_PURE { + return (-((lhs) & (1ULL << (lbits - 1)))); +} + +// Debugging prints +extern void _vl_debug_print_w(int lbits, WDataInP const iwp); + +//========================================================================= +// Pli macros + +extern int VL_TIME_STR_CONVERT(const char* strp) VL_PURE; + +// These are deprecated and used only to establish the default precision/units. +// Use Verilator timescale-override for better control. +// clang-format off +#ifndef VL_TIME_PRECISION +# ifdef VL_TIME_PRECISION_STR +# define VL_TIME_PRECISION VL_TIME_STR_CONVERT(VL_STRINGIFY(VL_TIME_PRECISION_STR)) +# else +# define VL_TIME_PRECISION (-12) ///< Timescale default units if not in Verilog - picoseconds +# endif +#endif +#ifndef VL_TIME_UNIT +# ifdef VL_TIME_UNIT_STR +# define VL_TIME_UNIT VL_TIME_STR_CONVERT(VL_STRINGIFY(VL_TIME_PRECISION_STR)) +# else +# define VL_TIME_UNIT (-12) ///< Timescale default units if not in Verilog - picoseconds +# endif +#endif + +#if defined(SYSTEMC_VERSION) +/// Return current simulation time +// Already defined: extern sc_time sc_time_stamp(); +inline vluint64_t vl_time_stamp64() { return sc_time_stamp().value(); } +#else // Non-SystemC +# if !defined(VL_TIME_CONTEXT) && !defined(VL_NO_LEGACY) +# ifdef VL_TIME_STAMP64 +// vl_time_stamp64() may be optionally defined by the user to return time. +// On MSVC++ weak symbols are not supported so must be declared, or define +// VL_TIME_CONTEXT. +extern vluint64_t vl_time_stamp64() VL_ATTR_WEAK; +# else +// sc_time_stamp() may be optionally defined by the user to return time. +// On MSVC++ weak symbols are not supported so must be declared, or define +// VL_TIME_CONTEXT. +extern double sc_time_stamp() VL_ATTR_WEAK; // Verilator 4.032 and newer +inline vluint64_t vl_time_stamp64() { + // clang9.0.1 requires & although we really do want the weak symbol value + return VL_LIKELY(&sc_time_stamp) ? static_cast(sc_time_stamp()) : 0; +} +# endif +# endif +#endif + +inline vluint64_t VerilatedContext::time() const VL_MT_SAFE { + // When using non-default context, fastest path is return time + if (VL_LIKELY(m_s.m_time)) return m_s.m_time; +#if defined(SYSTEMC_VERSION) || (!defined(VL_TIME_CONTEXT) && !defined(VL_NO_LEGACY)) + // Zero time could mean really at zero, or using callback + // clang9.0.1 requires & although we really do want the weak symbol value + if (VL_LIKELY(&vl_time_stamp64)) { // else is weak symbol that is not defined + return vl_time_stamp64(); + } +#endif + return 0; +} + +#define VL_TIME_Q() (Verilated::threadContextp()->time()) +#define VL_TIME_D() (static_cast(VL_TIME_Q())) + +// Time scaled from 1-per-precision into a module's time units ("Unit"-ed, not "United") +// Optimized assuming scale is always constant. +// Can't use multiply in Q flavor, as might lose precision +#define VL_TIME_UNITED_Q(scale) (VL_TIME_Q() / static_cast(scale)) +#define VL_TIME_UNITED_D(scale) (VL_TIME_D() / static_cast(scale)) + +// Return time precision as multiplier of time units +double vl_time_multiplier(int scale) VL_PURE; +// Return power of 10. e.g. returns 100 if n==2 +vluint64_t vl_time_pow10(int n) VL_PURE; + +#ifdef VL_DEBUG +/// Evaluate statement if Verilated::debug() enabled +# define VL_DEBUG_IF(stmt) \ + do { \ + if (VL_UNLIKELY(Verilated::debug())) {stmt} \ + } while (false) +#else +// We intentionally do not compile the stmt to improve compile speed +# define VL_DEBUG_IF(stmt) do {} while (false) +#endif + +// clang-format on + +//========================================================================= +// Functional macros/routines +// These all take the form +// VL_func_IW(bits, bits, op, op) +// VL_func_WW(bits, bits, out, op, op) +// The I/W indicates if it's a integer or wide for the output and each operand. +// The bits indicate the bit width of the output and each operand. +// If wide output, a temporary storage location is specified. + +//=================================================================== +// SETTING OPERATORS + +// Output clean +// EMIT_RULE: VL_CLEAN: oclean=clean; obits=lbits; +#define VL_CLEAN_II(obits, lbits, lhs) ((lhs)&VL_MASK_I(obits)) +#define VL_CLEAN_QQ(obits, lbits, lhs) ((lhs)&VL_MASK_Q(obits)) + +// EMIT_RULE: VL_ASSIGNCLEAN: oclean=clean; obits==lbits; +#define VL_ASSIGNCLEAN_W(obits, owp, lwp) VL_CLEAN_WW((obits), (obits), (owp), (lwp)) +static inline WDataOutP _vl_clean_inplace_w(int obits, WDataOutP owp) VL_MT_SAFE { + const int words = VL_WORDS_I(obits); + owp[words - 1] &= VL_MASK_E(obits); + return owp; +} +static inline WDataOutP VL_CLEAN_WW(int obits, int, WDataOutP owp, WDataInP const lwp) VL_MT_SAFE { + const int words = VL_WORDS_I(obits); + for (int i = 0; (i < (words - 1)); ++i) owp[i] = lwp[i]; + owp[words - 1] = lwp[words - 1] & VL_MASK_E(obits); + return owp; +} +static inline WDataOutP VL_ZERO_W(int obits, WDataOutP owp) VL_MT_SAFE { + const int words = VL_WORDS_I(obits); + for (int i = 0; i < words; ++i) owp[i] = 0; + return owp; +} +static inline WDataOutP VL_ALLONES_W(int obits, WDataOutP owp) VL_MT_SAFE { + const int words = VL_WORDS_I(obits); + for (int i = 0; i < (words - 1); ++i) owp[i] = ~VL_EUL(0); + owp[words - 1] = VL_MASK_E(obits); + return owp; +} + +// EMIT_RULE: VL_ASSIGN: oclean=rclean; obits==lbits; +// For now, we always have a clean rhs. +// Note: If a ASSIGN isn't clean, use VL_ASSIGNCLEAN instead to do the same thing. +static inline WDataOutP VL_ASSIGN_W(int obits, WDataOutP owp, WDataInP const lwp) VL_MT_SAFE { + const int words = VL_WORDS_I(obits); + for (int i = 0; i < words; ++i) owp[i] = lwp[i]; + return owp; +} + +// EMIT_RULE: VL_ASSIGNBIT: rclean=clean; +static inline void VL_ASSIGNBIT_II(int, int bit, CData& lhsr, IData rhs) VL_PURE { + lhsr = ((lhsr & ~(VL_UL(1) << VL_BITBIT_I(bit))) | (rhs << VL_BITBIT_I(bit))); +} +static inline void VL_ASSIGNBIT_II(int, int bit, SData& lhsr, IData rhs) VL_PURE { + lhsr = ((lhsr & ~(VL_UL(1) << VL_BITBIT_I(bit))) | (rhs << VL_BITBIT_I(bit))); +} +static inline void VL_ASSIGNBIT_II(int, int bit, IData& lhsr, IData rhs) VL_PURE { + lhsr = ((lhsr & ~(VL_UL(1) << VL_BITBIT_I(bit))) | (rhs << VL_BITBIT_I(bit))); +} +static inline void VL_ASSIGNBIT_QI(int, int bit, QData& lhsr, QData rhs) VL_PURE { + lhsr = ((lhsr & ~(1ULL << VL_BITBIT_Q(bit))) | (static_cast(rhs) << VL_BITBIT_Q(bit))); +} +static inline void VL_ASSIGNBIT_WI(int, int bit, WDataOutP owp, IData rhs) VL_MT_SAFE { + EData orig = owp[VL_BITWORD_E(bit)]; + owp[VL_BITWORD_E(bit)] = ((orig & ~(VL_EUL(1) << VL_BITBIT_E(bit))) + | (static_cast(rhs) << VL_BITBIT_E(bit))); +} +// Alternative form that is an instruction faster when rhs is constant one. +static inline void VL_ASSIGNBIT_IO(int, int bit, CData& lhsr, IData) VL_PURE { + lhsr = (lhsr | (VL_UL(1) << VL_BITBIT_I(bit))); +} +static inline void VL_ASSIGNBIT_IO(int, int bit, SData& lhsr, IData) VL_PURE { + lhsr = (lhsr | (VL_UL(1) << VL_BITBIT_I(bit))); +} +static inline void VL_ASSIGNBIT_IO(int, int bit, IData& lhsr, IData) VL_PURE { + lhsr = (lhsr | (VL_UL(1) << VL_BITBIT_I(bit))); +} +static inline void VL_ASSIGNBIT_QO(int, int bit, QData& lhsr, IData) VL_PURE { + lhsr = (lhsr | (1ULL << VL_BITBIT_Q(bit))); +} +static inline void VL_ASSIGNBIT_WO(int, int bit, WDataOutP owp, IData) VL_MT_SAFE { + const EData orig = owp[VL_BITWORD_E(bit)]; + owp[VL_BITWORD_E(bit)] = (orig | (VL_EUL(1) << VL_BITBIT_E(bit))); +} + +//=================================================================== +// SYSTEMC OPERATORS +// Copying verilog format to systemc integers and bit vectors. +// Get a SystemC variable + +#define VL_ASSIGN_ISI(obits, vvar, svar) \ + { (vvar) = VL_CLEAN_II((obits), (obits), (svar).read()); } +#define VL_ASSIGN_QSQ(obits, vvar, svar) \ + { (vvar) = VL_CLEAN_QQ((obits), (obits), (svar).read()); } + +#define VL_ASSIGN_ISW(obits, od, svar) \ + { (od) = ((svar).read().get_word(0)) & VL_MASK_I(obits); } +#define VL_ASSIGN_QSW(obits, od, svar) \ + { \ + (od) = ((static_cast((svar).read().get_word(1))) << VL_IDATASIZE \ + | (svar).read().get_word(0)) \ + & VL_MASK_Q(obits); \ + } +#define VL_ASSIGN_WSW(obits, owp, svar) \ + { \ + const int words = VL_WORDS_I(obits); \ + for (int i = 0; i < words; ++i) (owp)[i] = (svar).read().get_word(i); \ + (owp)[words - 1] &= VL_MASK_E(obits); \ + } + +#define VL_ASSIGN_ISU(obits, vvar, svar) \ + { (vvar) = VL_CLEAN_II((obits), (obits), (svar).read().to_uint()); } +#define VL_ASSIGN_QSU(obits, vvar, svar) \ + { (vvar) = VL_CLEAN_QQ((obits), (obits), (svar).read().to_uint64()); } +#define VL_ASSIGN_WSB(obits, owp, svar) \ + { \ + const int words = VL_WORDS_I(obits); \ + sc_biguint<(obits)> _butemp = (svar).read(); \ + for (int i = 0; i < words; ++i) { \ + int msb = ((i + 1) * VL_IDATASIZE) - 1; \ + msb = (msb >= (obits)) ? ((obits)-1) : msb; \ + (owp)[i] = _butemp.range(msb, i * VL_IDATASIZE).to_uint(); \ + } \ + (owp)[words - 1] &= VL_MASK_E(obits); \ + } + +// Copying verilog format from systemc integers and bit vectors. +// Set a SystemC variable + +#define VL_ASSIGN_SII(obits, svar, vvar) \ + { (svar).write(vvar); } +#define VL_ASSIGN_SQQ(obits, svar, vvar) \ + { (svar).write(vvar); } + +#define VL_ASSIGN_SWI(obits, svar, rd) \ + { \ + sc_bv<(obits)> _bvtemp; \ + _bvtemp.set_word(0, (rd)); \ + (svar).write(_bvtemp); \ + } +#define VL_ASSIGN_SWQ(obits, svar, rd) \ + { \ + sc_bv<(obits)> _bvtemp; \ + _bvtemp.set_word(0, static_cast(rd)); \ + _bvtemp.set_word(1, static_cast((rd) >> VL_IDATASIZE)); \ + (svar).write(_bvtemp); \ + } +#define VL_ASSIGN_SWW(obits, svar, rwp) \ + { \ + sc_bv<(obits)> _bvtemp; \ + for (int i = 0; i < VL_WORDS_I(obits); ++i) _bvtemp.set_word(i, (rwp)[i]); \ + (svar).write(_bvtemp); \ + } + +#define VL_ASSIGN_SUI(obits, svar, rd) \ + { (svar).write(rd); } +#define VL_ASSIGN_SUQ(obits, svar, rd) \ + { (svar).write(rd); } +#define VL_ASSIGN_SBI(obits, svar, rd) \ + { (svar).write(rd); } +#define VL_ASSIGN_SBQ(obits, svar, rd) \ + { (svar).write(rd); } +#define VL_ASSIGN_SBW(obits, svar, rwp) \ + { \ + sc_biguint<(obits)> _butemp; \ + for (int i = 0; i < VL_WORDS_I(obits); ++i) { \ + int msb = ((i + 1) * VL_IDATASIZE) - 1; \ + msb = (msb >= (obits)) ? ((obits)-1) : msb; \ + _butemp.range(msb, i* VL_IDATASIZE) = (rwp)[i]; \ + } \ + (svar).write(_butemp); \ + } + +//=================================================================== +// Extending sizes + +// CAREFUL, we're width changing, so obits!=lbits + +// Right must be clean because otherwise size increase would pick up bad bits +// EMIT_RULE: VL_EXTEND: oclean=clean; rclean==clean; +#define VL_EXTEND_II(obits, lbits, lhs) ((lhs)) +#define VL_EXTEND_QI(obits, lbits, lhs) (static_cast(lhs)) +#define VL_EXTEND_QQ(obits, lbits, lhs) ((lhs)) + +static inline WDataOutP VL_EXTEND_WI(int obits, int, WDataOutP owp, IData ld) VL_MT_SAFE { + // Note for extracts that obits != lbits + owp[0] = ld; + for (int i = 1; i < VL_WORDS_I(obits); ++i) owp[i] = 0; + return owp; +} +static inline WDataOutP VL_EXTEND_WQ(int obits, int, WDataOutP owp, QData ld) VL_MT_SAFE { + VL_SET_WQ(owp, ld); + for (int i = VL_WQ_WORDS_E; i < VL_WORDS_I(obits); ++i) owp[i] = 0; + return owp; +} +static inline WDataOutP VL_EXTEND_WW(int obits, int lbits, WDataOutP owp, + WDataInP const lwp) VL_MT_SAFE { + for (int i = 0; i < VL_WORDS_I(lbits); ++i) owp[i] = lwp[i]; + for (int i = VL_WORDS_I(lbits); i < VL_WORDS_I(obits); ++i) owp[i] = 0; + return owp; +} + +// EMIT_RULE: VL_EXTENDS: oclean=*dirty*; obits=lbits; +// Sign extension; output dirty +static inline IData VL_EXTENDS_II(int, int lbits, IData lhs) VL_PURE { + return VL_EXTENDSIGN_I(lbits, lhs) | lhs; +} +static inline QData VL_EXTENDS_QI(int, int lbits, QData lhs /*Q_as_need_extended*/) VL_PURE { + return VL_EXTENDSIGN_Q(lbits, lhs) | lhs; +} +static inline QData VL_EXTENDS_QQ(int, int lbits, QData lhs) VL_PURE { + return VL_EXTENDSIGN_Q(lbits, lhs) | lhs; +} + +static inline WDataOutP VL_EXTENDS_WI(int obits, int lbits, WDataOutP owp, IData ld) VL_MT_SAFE { + const EData sign = VL_SIGNONES_E(lbits, static_cast(ld)); + owp[0] = ld | (sign & ~VL_MASK_E(lbits)); + for (int i = 1; i < VL_WORDS_I(obits); ++i) owp[i] = sign; + return owp; +} +static inline WDataOutP VL_EXTENDS_WQ(int obits, int lbits, WDataOutP owp, QData ld) VL_MT_SAFE { + VL_SET_WQ(owp, ld); + const EData sign = VL_SIGNONES_E(lbits, owp[1]); + owp[1] |= sign & ~VL_MASK_E(lbits); + for (int i = VL_WQ_WORDS_E; i < VL_WORDS_I(obits); ++i) owp[i] = sign; + return owp; +} +static inline WDataOutP VL_EXTENDS_WW(int obits, int lbits, WDataOutP owp, + WDataInP const lwp) VL_MT_SAFE { + for (int i = 0; i < VL_WORDS_I(lbits) - 1; ++i) owp[i] = lwp[i]; + const int lmsw = VL_WORDS_I(lbits) - 1; + const EData sign = VL_SIGNONES_E(lbits, lwp[lmsw]); + owp[lmsw] = lwp[lmsw] | (sign & ~VL_MASK_E(lbits)); + for (int i = VL_WORDS_I(lbits); i < VL_WORDS_I(obits); ++i) owp[i] = sign; + return owp; +} + +//=================================================================== +// REDUCTION OPERATORS + +// EMIT_RULE: VL_REDAND: oclean=clean; lclean==clean; obits=1; +#define VL_REDAND_II(obits, lbits, lhs) ((lhs) == VL_MASK_I(lbits)) +#define VL_REDAND_IQ(obits, lbits, lhs) ((lhs) == VL_MASK_Q(lbits)) +static inline IData VL_REDAND_IW(int, int lbits, WDataInP const lwp) VL_MT_SAFE { + const int words = VL_WORDS_I(lbits); + EData combine = lwp[0]; + for (int i = 1; i < words - 1; ++i) combine &= lwp[i]; + combine &= ~VL_MASK_E(lbits) | lwp[words - 1]; + return ((~combine) == 0); +} + +// EMIT_RULE: VL_REDOR: oclean=clean; lclean==clean; obits=1; +#define VL_REDOR_I(lhs) ((lhs) != 0) +#define VL_REDOR_Q(lhs) ((lhs) != 0) +static inline IData VL_REDOR_W(int words, WDataInP const lwp) VL_MT_SAFE { + EData equal = 0; + for (int i = 0; i < words; ++i) equal |= lwp[i]; + return (equal != 0); +} + +// EMIT_RULE: VL_REDXOR: oclean=dirty; obits=1; +static inline IData VL_REDXOR_2(IData r) VL_PURE { + // Experiments show VL_REDXOR_2 is faster than __builtin_parityl + r = (r ^ (r >> 1)); + return r; +} +static inline IData VL_REDXOR_4(IData r) VL_PURE { +#if defined(__GNUC__) && (__GNUC__ >= 4) && !defined(VL_NO_BUILTINS) + return __builtin_parityl(r); +#else + r = (r ^ (r >> 1)); + r = (r ^ (r >> 2)); + return r; +#endif +} +static inline IData VL_REDXOR_8(IData r) VL_PURE { +#if defined(__GNUC__) && (__GNUC__ >= 4) && !defined(VL_NO_BUILTINS) + return __builtin_parityl(r); +#else + r = (r ^ (r >> 1)); + r = (r ^ (r >> 2)); + r = (r ^ (r >> 4)); + return r; +#endif +} +static inline IData VL_REDXOR_16(IData r) VL_PURE { +#if defined(__GNUC__) && (__GNUC__ >= 4) && !defined(VL_NO_BUILTINS) + return __builtin_parityl(r); +#else + r = (r ^ (r >> 1)); + r = (r ^ (r >> 2)); + r = (r ^ (r >> 4)); + r = (r ^ (r >> 8)); + return r; +#endif +} +static inline IData VL_REDXOR_32(IData r) VL_PURE { +#if defined(__GNUC__) && (__GNUC__ >= 4) && !defined(VL_NO_BUILTINS) + return __builtin_parityl(r); +#else + r = (r ^ (r >> 1)); + r = (r ^ (r >> 2)); + r = (r ^ (r >> 4)); + r = (r ^ (r >> 8)); + r = (r ^ (r >> 16)); + return r; +#endif +} +static inline IData VL_REDXOR_64(QData r) VL_PURE { +#if defined(__GNUC__) && (__GNUC__ >= 4) && !defined(VL_NO_BUILTINS) + return __builtin_parityll(r); +#else + r = (r ^ (r >> 1)); + r = (r ^ (r >> 2)); + r = (r ^ (r >> 4)); + r = (r ^ (r >> 8)); + r = (r ^ (r >> 16)); + r = (r ^ (r >> 32)); + return static_cast(r); +#endif +} +static inline IData VL_REDXOR_W(int words, WDataInP const lwp) VL_MT_SAFE { + EData r = lwp[0]; + for (int i = 1; i < words; ++i) r ^= lwp[i]; + return VL_REDXOR_32(r); +} + +// EMIT_RULE: VL_COUNTONES_II: oclean = false; lhs clean +static inline IData VL_COUNTONES_I(IData lhs) VL_PURE { + // This is faster than __builtin_popcountl + IData r = lhs - ((lhs >> 1) & 033333333333) - ((lhs >> 2) & 011111111111); + r = (r + (r >> 3)) & 030707070707; + r = (r + (r >> 6)); + r = (r + (r >> 12) + (r >> 24)) & 077; + return r; +} +static inline IData VL_COUNTONES_Q(QData lhs) VL_PURE { + return VL_COUNTONES_I(static_cast(lhs)) + VL_COUNTONES_I(static_cast(lhs >> 32)); +} +#define VL_COUNTONES_E VL_COUNTONES_I +static inline IData VL_COUNTONES_W(int words, WDataInP const lwp) VL_MT_SAFE { + EData r = 0; + for (int i = 0; i < words; ++i) r += VL_COUNTONES_E(lwp[i]); + return r; +} + +// EMIT_RULE: VL_COUNTBITS_II: oclean = false; lhs clean +static inline IData VL_COUNTBITS_I(int lbits, IData lhs, IData ctrl0, IData ctrl1, + IData ctrl2) VL_PURE { + int ctrlSum = (ctrl0 & 0x1) + (ctrl1 & 0x1) + (ctrl2 & 0x1); + if (ctrlSum == 3) { + return VL_COUNTONES_I(lhs); + } else if (ctrlSum == 0) { + IData mask = (lbits == 32) ? -1 : ((1 << lbits) - 1); + return VL_COUNTONES_I(~lhs & mask); + } else { + return (lbits == 32) ? 32 : lbits; + } +} +static inline IData VL_COUNTBITS_Q(int lbits, QData lhs, IData ctrl0, IData ctrl1, + IData ctrl2) VL_PURE { + return VL_COUNTBITS_I(32, static_cast(lhs), ctrl0, ctrl1, ctrl2) + + VL_COUNTBITS_I(lbits - 32, static_cast(lhs >> 32), ctrl0, ctrl1, ctrl2); +} +#define VL_COUNTBITS_E VL_COUNTBITS_I +static inline IData VL_COUNTBITS_W(int lbits, int words, WDataInP const lwp, IData ctrl0, + IData ctrl1, IData ctrl2) VL_MT_SAFE { + EData r = 0; + IData wordLbits = 32; + for (int i = 0; i < words; ++i) { + if (i == words - 1) wordLbits = lbits % 32; + r += VL_COUNTBITS_E(wordLbits, lwp[i], ctrl0, ctrl1, ctrl2); + } + return r; +} + +static inline IData VL_ONEHOT_I(IData lhs) VL_PURE { + return (((lhs & (lhs - 1)) == 0) & (lhs != 0)); +} +static inline IData VL_ONEHOT_Q(QData lhs) VL_PURE { + return (((lhs & (lhs - 1)) == 0) & (lhs != 0)); +} +static inline IData VL_ONEHOT_W(int words, WDataInP const lwp) VL_MT_SAFE { + EData one = 0; + for (int i = 0; (i < words); ++i) { + if (lwp[i]) { + if (one) return 0; + one = 1; + if (lwp[i] & (lwp[i] - 1)) return 0; + } + } + return one; +} + +static inline IData VL_ONEHOT0_I(IData lhs) VL_PURE { return ((lhs & (lhs - 1)) == 0); } +static inline IData VL_ONEHOT0_Q(QData lhs) VL_PURE { return ((lhs & (lhs - 1)) == 0); } +static inline IData VL_ONEHOT0_W(int words, WDataInP const lwp) VL_MT_SAFE { + bool one = false; + for (int i = 0; (i < words); ++i) { + if (lwp[i]) { + if (one) return 0; + one = true; + if (lwp[i] & (lwp[i] - 1)) return 0; + } + } + return 1; +} + +static inline IData VL_CLOG2_I(IData lhs) VL_PURE { + // There are faster algorithms, or fls GCC4 builtins, but rarely used + if (VL_UNLIKELY(!lhs)) return 0; + --lhs; + int shifts = 0; + for (; lhs != 0; ++shifts) lhs = lhs >> 1; + return shifts; +} +static inline IData VL_CLOG2_Q(QData lhs) VL_PURE { + if (VL_UNLIKELY(!lhs)) return 0; + --lhs; + int shifts = 0; + for (; lhs != 0; ++shifts) lhs = lhs >> 1ULL; + return shifts; +} +static inline IData VL_CLOG2_W(int words, WDataInP const lwp) VL_MT_SAFE { + EData adjust = (VL_COUNTONES_W(words, lwp) == 1) ? 0 : 1; + for (int i = words - 1; i >= 0; --i) { + if (VL_UNLIKELY(lwp[i])) { // Shorter worst case if predict not taken + for (int bit = VL_EDATASIZE - 1; bit >= 0; --bit) { + if (VL_UNLIKELY(VL_BITISSET_E(lwp[i], bit))) { + return i * VL_EDATASIZE + bit + adjust; + } + } + // Can't get here - one bit must be set + } + } + return 0; +} + +static inline IData VL_MOSTSETBITP1_W(int words, WDataInP const lwp) VL_MT_SAFE { + // MSB set bit plus one; similar to FLS. 0=value is zero + for (int i = words - 1; i >= 0; --i) { + if (VL_UNLIKELY(lwp[i])) { // Shorter worst case if predict not taken + for (int bit = VL_EDATASIZE - 1; bit >= 0; --bit) { + if (VL_UNLIKELY(VL_BITISSET_E(lwp[i], bit))) return i * VL_EDATASIZE + bit + 1; + } + // Can't get here - one bit must be set + } + } + return 0; +} + +//=================================================================== +// SIMPLE LOGICAL OPERATORS + +// EMIT_RULE: VL_AND: oclean=lclean||rclean; obits=lbits; lbits==rbits; +static inline WDataOutP VL_AND_W(int words, WDataOutP owp, WDataInP const lwp, + WDataInP const rwp) VL_MT_SAFE { + for (int i = 0; (i < words); ++i) owp[i] = (lwp[i] & rwp[i]); + return owp; +} +// EMIT_RULE: VL_OR: oclean=lclean&&rclean; obits=lbits; lbits==rbits; +static inline WDataOutP VL_OR_W(int words, WDataOutP owp, WDataInP const lwp, + WDataInP const rwp) VL_MT_SAFE { + for (int i = 0; (i < words); ++i) owp[i] = (lwp[i] | rwp[i]); + return owp; +} +// EMIT_RULE: VL_CHANGEXOR: oclean=1; obits=32; lbits==rbits; +static inline IData VL_CHANGEXOR_W(int words, WDataInP const lwp, WDataInP const rwp) VL_MT_SAFE { + IData od = 0; + for (int i = 0; (i < words); ++i) od |= (lwp[i] ^ rwp[i]); + return od; +} +// EMIT_RULE: VL_XOR: oclean=lclean&&rclean; obits=lbits; lbits==rbits; +static inline WDataOutP VL_XOR_W(int words, WDataOutP owp, WDataInP const lwp, + WDataInP const rwp) VL_MT_SAFE { + for (int i = 0; (i < words); ++i) owp[i] = (lwp[i] ^ rwp[i]); + return owp; +} +// EMIT_RULE: VL_NOT: oclean=dirty; obits=lbits; +static inline WDataOutP VL_NOT_W(int words, WDataOutP owp, WDataInP const lwp) VL_MT_SAFE { + for (int i = 0; i < words; ++i) owp[i] = ~(lwp[i]); + return owp; +} + +//========================================================================= +// Logical comparisons + +// EMIT_RULE: VL_EQ: oclean=clean; lclean==clean; rclean==clean; obits=1; lbits==rbits; +// EMIT_RULE: VL_NEQ: oclean=clean; lclean==clean; rclean==clean; obits=1; lbits==rbits; +// EMIT_RULE: VL_LT: oclean=clean; lclean==clean; rclean==clean; obits=1; lbits==rbits; +// EMIT_RULE: VL_GT: oclean=clean; lclean==clean; rclean==clean; obits=1; lbits==rbits; +// EMIT_RULE: VL_GTE: oclean=clean; lclean==clean; rclean==clean; obits=1; lbits==rbits; +// EMIT_RULE: VL_LTE: oclean=clean; lclean==clean; rclean==clean; obits=1; lbits==rbits; +#define VL_NEQ_W(words, lwp, rwp) (!VL_EQ_W(words, lwp, rwp)) +#define VL_LT_W(words, lwp, rwp) (_vl_cmp_w(words, lwp, rwp) < 0) +#define VL_LTE_W(words, lwp, rwp) (_vl_cmp_w(words, lwp, rwp) <= 0) +#define VL_GT_W(words, lwp, rwp) (_vl_cmp_w(words, lwp, rwp) > 0) +#define VL_GTE_W(words, lwp, rwp) (_vl_cmp_w(words, lwp, rwp) >= 0) + +// Output clean, AND MUST BE CLEAN +static inline IData VL_EQ_W(int words, WDataInP const lwp, WDataInP const rwp) VL_MT_SAFE { + EData nequal = 0; + for (int i = 0; (i < words); ++i) nequal |= (lwp[i] ^ rwp[i]); + return (nequal == 0); +} + +// Internal usage +static inline int _vl_cmp_w(int words, WDataInP const lwp, WDataInP const rwp) VL_MT_SAFE { + for (int i = words - 1; i >= 0; --i) { + if (lwp[i] > rwp[i]) return 1; + if (lwp[i] < rwp[i]) return -1; + } + return 0; // == +} + +#define VL_LTS_IWW(obits, lbits, rbbits, lwp, rwp) (_vl_cmps_w(lbits, lwp, rwp) < 0) +#define VL_LTES_IWW(obits, lbits, rbits, lwp, rwp) (_vl_cmps_w(lbits, lwp, rwp) <= 0) +#define VL_GTS_IWW(obits, lbits, rbits, lwp, rwp) (_vl_cmps_w(lbits, lwp, rwp) > 0) +#define VL_GTES_IWW(obits, lbits, rbits, lwp, rwp) (_vl_cmps_w(lbits, lwp, rwp) >= 0) + +static inline IData VL_GTS_III(int, int lbits, int, IData lhs, IData rhs) VL_PURE { + // For lbits==32, this becomes just a single instruction, otherwise ~5. + // GCC 3.3.4 sign extension bugs on AMD64 architecture force us to use quad logic + const vlsint64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); // Q for gcc + const vlsint64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); // Q for gcc + return lhs_signed > rhs_signed; +} +static inline IData VL_GTS_IQQ(int, int lbits, int, QData lhs, QData rhs) VL_PURE { + const vlsint64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); + const vlsint64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); + return lhs_signed > rhs_signed; +} + +static inline IData VL_GTES_III(int, int lbits, int, IData lhs, IData rhs) VL_PURE { + const vlsint64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); // Q for gcc + const vlsint64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); // Q for gcc + return lhs_signed >= rhs_signed; +} +static inline IData VL_GTES_IQQ(int, int lbits, int, QData lhs, QData rhs) VL_PURE { + const vlsint64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); + const vlsint64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); + return lhs_signed >= rhs_signed; +} + +static inline IData VL_LTS_III(int, int lbits, int, IData lhs, IData rhs) VL_PURE { + const vlsint64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); // Q for gcc + const vlsint64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); // Q for gcc + return lhs_signed < rhs_signed; +} +static inline IData VL_LTS_IQQ(int, int lbits, int, QData lhs, QData rhs) VL_PURE { + const vlsint64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); + const vlsint64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); + return lhs_signed < rhs_signed; +} + +static inline IData VL_LTES_III(int, int lbits, int, IData lhs, IData rhs) VL_PURE { + const vlsint64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); // Q for gcc + const vlsint64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); // Q for gcc + return lhs_signed <= rhs_signed; +} +static inline IData VL_LTES_IQQ(int, int lbits, int, QData lhs, QData rhs) VL_PURE { + const vlsint64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); + const vlsint64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); + return lhs_signed <= rhs_signed; +} + +static inline int _vl_cmps_w(int lbits, WDataInP const lwp, WDataInP const rwp) VL_MT_SAFE { + const int words = VL_WORDS_I(lbits); + int i = words - 1; + // We need to flip sense if negative comparison + const EData lsign = VL_SIGN_E(lbits, lwp[i]); + const EData rsign = VL_SIGN_E(lbits, rwp[i]); + if (!lsign && rsign) return 1; // + > - + if (lsign && !rsign) return -1; // - < + + for (; i >= 0; --i) { + if (lwp[i] > rwp[i]) return 1; + if (lwp[i] < rwp[i]) return -1; + } + return 0; // == +} + +//========================================================================= +// Math + +// Output NOT clean +static inline WDataOutP VL_NEGATE_W(int words, WDataOutP owp, WDataInP const lwp) VL_MT_SAFE { + EData carry = 1; + for (int i = 0; i < words; ++i) { + owp[i] = ~lwp[i] + carry; + carry = (owp[i] < ~lwp[i]); + } + return owp; +} +static inline void VL_NEGATE_INPLACE_W(int words, WDataOutP owp_lwp) VL_MT_SAFE { + EData carry = 1; + for (int i = 0; i < words; ++i) { + EData word = ~owp_lwp[i] + carry; + carry = (word < ~owp_lwp[i]); + owp_lwp[i] = word; + } +} + +// EMIT_RULE: VL_MUL: oclean=dirty; lclean==clean; rclean==clean; +// EMIT_RULE: VL_DIV: oclean=dirty; lclean==clean; rclean==clean; +// EMIT_RULE: VL_MODDIV: oclean=dirty; lclean==clean; rclean==clean; +#define VL_DIV_III(lbits, lhs, rhs) (((rhs) == 0) ? 0 : (lhs) / (rhs)) +#define VL_DIV_QQQ(lbits, lhs, rhs) (((rhs) == 0) ? 0 : (lhs) / (rhs)) +#define VL_DIV_WWW(lbits, owp, lwp, rwp) (_vl_moddiv_w(lbits, owp, lwp, rwp, 0)) +#define VL_MODDIV_III(lbits, lhs, rhs) (((rhs) == 0) ? 0 : (lhs) % (rhs)) +#define VL_MODDIV_QQQ(lbits, lhs, rhs) (((rhs) == 0) ? 0 : (lhs) % (rhs)) +#define VL_MODDIV_WWW(lbits, owp, lwp, rwp) (_vl_moddiv_w(lbits, owp, lwp, rwp, 1)) + +static inline WDataOutP VL_ADD_W(int words, WDataOutP owp, WDataInP const lwp, + WDataInP const rwp) VL_MT_SAFE { + QData carry = 0; + for (int i = 0; i < words; ++i) { + carry = carry + static_cast(lwp[i]) + static_cast(rwp[i]); + owp[i] = (carry & 0xffffffffULL); + carry = (carry >> 32ULL) & 0xffffffffULL; + } + // Last output word is dirty + return owp; +} + +static inline WDataOutP VL_SUB_W(int words, WDataOutP owp, WDataInP const lwp, + WDataInP const rwp) VL_MT_SAFE { + QData carry = 0; + for (int i = 0; i < words; ++i) { + carry = (carry + static_cast(lwp[i]) + + static_cast(static_cast(~rwp[i]))); + if (i == 0) ++carry; // Negation of rwp + owp[i] = (carry & 0xffffffffULL); + carry = (carry >> 32ULL) & 0xffffffffULL; + } + // Last output word is dirty + return owp; +} + +static inline WDataOutP VL_MUL_W(int words, WDataOutP owp, WDataInP const lwp, + WDataInP const rwp) VL_MT_SAFE { + for (int i = 0; i < words; ++i) owp[i] = 0; + for (int lword = 0; lword < words; ++lword) { + for (int rword = 0; rword < words; ++rword) { + QData mul = static_cast(lwp[lword]) * static_cast(rwp[rword]); + for (int qword = lword + rword; qword < words; ++qword) { + mul += static_cast(owp[qword]); + owp[qword] = (mul & 0xffffffffULL); + mul = (mul >> 32ULL) & 0xffffffffULL; + } + } + } + // Last output word is dirty + return owp; +} + +static inline IData VL_MULS_III(int, int lbits, int, IData lhs, IData rhs) VL_PURE { + const vlsint32_t lhs_signed = VL_EXTENDS_II(32, lbits, lhs); + const vlsint32_t rhs_signed = VL_EXTENDS_II(32, lbits, rhs); + return lhs_signed * rhs_signed; +} +static inline QData VL_MULS_QQQ(int, int lbits, int, QData lhs, QData rhs) VL_PURE { + const vlsint64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); + const vlsint64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); + return lhs_signed * rhs_signed; +} + +static inline WDataOutP VL_MULS_WWW(int, int lbits, int, WDataOutP owp, WDataInP const lwp, + WDataInP const rwp) VL_MT_SAFE { + const int words = VL_WORDS_I(lbits); + // cppcheck-suppress variableScope + WData lwstore[VL_MULS_MAX_WORDS]; // Fixed size, as MSVC++ doesn't allow [words] here + // cppcheck-suppress variableScope + WData rwstore[VL_MULS_MAX_WORDS]; + WDataInP lwusp = lwp; + WDataInP rwusp = rwp; + EData lneg = VL_SIGN_E(lbits, lwp[words - 1]); + if (lneg) { // Negate lhs + lwusp = lwstore; + VL_NEGATE_W(words, lwstore, lwp); + lwstore[words - 1] &= VL_MASK_E(lbits); // Clean it + } + EData rneg = VL_SIGN_E(lbits, rwp[words - 1]); + if (rneg) { // Negate rhs + rwusp = rwstore; + VL_NEGATE_W(words, rwstore, rwp); + rwstore[words - 1] &= VL_MASK_E(lbits); // Clean it + } + VL_MUL_W(words, owp, lwusp, rwusp); + owp[words - 1] &= VL_MASK_E( + lbits); // Clean. Note it's ok for the multiply to overflow into the sign bit + if ((lneg ^ rneg) & 1) { // Negate output (not using NEGATE, as owp==lwp) + QData carry = 0; + for (int i = 0; i < words; ++i) { + carry = carry + static_cast(static_cast(~owp[i])); + if (i == 0) ++carry; // Negation of temp2 + owp[i] = (carry & 0xffffffffULL); + carry = (carry >> 32ULL) & 0xffffffffULL; + } + // Not needed: owp[words-1] |= 1< 0) power = power * power; + if (rhs & (1ULL << i)) out *= power; + } + return out; +} +static inline QData VL_POW_QQQ(int, int, int rbits, QData lhs, QData rhs) VL_PURE { + if (VL_UNLIKELY(rhs == 0)) return 1; + if (VL_UNLIKELY(lhs == 0)) return 0; + QData power = lhs; + QData out = 1ULL; + for (int i = 0; i < rbits; ++i) { + if (i > 0) power = power * power; + if (rhs & (1ULL << i)) out *= power; + } + return out; +} +WDataOutP VL_POW_WWW(int obits, int, int rbits, WDataOutP owp, WDataInP const lwp, + WDataInP const rwp); +WDataOutP VL_POW_WWQ(int obits, int, int rbits, WDataOutP owp, WDataInP const lwp, QData rhs); +QData VL_POW_QQW(int obits, int, int rbits, QData lhs, WDataInP const rwp); + +#define VL_POWSS_IIQ(obits, lbits, rbits, lhs, rhs, lsign, rsign) \ + VL_POWSS_QQQ(obits, lbits, rbits, lhs, rhs, lsign, rsign) +#define VL_POWSS_IIQ(obits, lbits, rbits, lhs, rhs, lsign, rsign) \ + VL_POWSS_QQQ(obits, lbits, rbits, lhs, rhs, lsign, rsign) +#define VL_POWSS_IIW(obits, lbits, rbits, lhs, rwp, lsign, rsign) \ + VL_POWSS_QQW(obits, lbits, rbits, lhs, rwp, lsign, rsign) +#define VL_POWSS_QQI(obits, lbits, rbits, lhs, rhs, lsign, rsign) \ + VL_POWSS_QQQ(obits, lbits, rbits, lhs, rhs, lsign, rsign) +#define VL_POWSS_WWI(obits, lbits, rbits, owp, lwp, rhs, lsign, rsign) \ + VL_POWSS_WWQ(obits, lbits, rbits, owp, lwp, rhs, lsign, rsign) + +static inline IData VL_POWSS_III(int obits, int, int rbits, IData lhs, IData rhs, bool lsign, + bool rsign) VL_MT_SAFE { + if (VL_UNLIKELY(rhs == 0)) return 1; + if (rsign && VL_SIGN_I(rbits, rhs)) { + if (lhs == 0) { + return 0; // "X" + } else if (lhs == 1) { + return 1; + } else if (lsign && lhs == VL_MASK_I(obits)) { // -1 + if (rhs & 1) { + return VL_MASK_I(obits); // -1^odd=-1 + } else { + return 1; // -1^even=1 + } + } + return 0; + } + return VL_POW_III(obits, rbits, rbits, lhs, rhs); +} +static inline QData VL_POWSS_QQQ(int obits, int, int rbits, QData lhs, QData rhs, bool lsign, + bool rsign) VL_MT_SAFE { + if (VL_UNLIKELY(rhs == 0)) return 1; + if (rsign && VL_SIGN_Q(rbits, rhs)) { + if (lhs == 0) { + return 0; // "X" + } else if (lhs == 1) { + return 1; + } else if (lsign && lhs == VL_MASK_Q(obits)) { // -1 + if (rhs & 1) { + return VL_MASK_Q(obits); // -1^odd=-1 + } else { + return 1; // -1^even=1 + } + } + return 0; + } + return VL_POW_QQQ(obits, rbits, rbits, lhs, rhs); +} +WDataOutP VL_POWSS_WWW(int obits, int, int rbits, WDataOutP owp, WDataInP const lwp, + WDataInP const rwp, bool lsign, bool rsign); +WDataOutP VL_POWSS_WWQ(int obits, int, int rbits, WDataOutP owp, WDataInP const lwp, QData rhs, + bool lsign, bool rsign); +QData VL_POWSS_QQW(int obits, int, int rbits, QData lhs, WDataInP const rwp, bool lsign, + bool rsign); + +//=================================================================== +// Concat/replication + +// INTERNAL: Stuff LHS bit 0++ into OUTPUT at specified offset +// ld may be "dirty", output is clean +static inline void _vl_insert_II(int, CData& lhsr, IData ld, int hbit, int lbit, + int rbits) VL_PURE { + const IData cleanmask = VL_MASK_I(rbits); + const IData insmask = (VL_MASK_I(hbit - lbit + 1)) << lbit; + lhsr = (lhsr & ~insmask) | ((ld << lbit) & (insmask & cleanmask)); +} +static inline void _vl_insert_II(int, SData& lhsr, IData ld, int hbit, int lbit, + int rbits) VL_PURE { + const IData cleanmask = VL_MASK_I(rbits); + const IData insmask = (VL_MASK_I(hbit - lbit + 1)) << lbit; + lhsr = (lhsr & ~insmask) | ((ld << lbit) & (insmask & cleanmask)); +} +static inline void _vl_insert_II(int, IData& lhsr, IData ld, int hbit, int lbit, + int rbits) VL_PURE { + const IData cleanmask = VL_MASK_I(rbits); + const IData insmask = (VL_MASK_I(hbit - lbit + 1)) << lbit; + lhsr = (lhsr & ~insmask) | ((ld << lbit) & (insmask & cleanmask)); +} +static inline void _vl_insert_QQ(int, QData& lhsr, QData ld, int hbit, int lbit, + int rbits) VL_PURE { + const QData cleanmask = VL_MASK_Q(rbits); + const QData insmask = (VL_MASK_Q(hbit - lbit + 1)) << lbit; + lhsr = (lhsr & ~insmask) | ((ld << lbit) & (insmask & cleanmask)); +} +static inline void _vl_insert_WI(int, WDataOutP owp, IData ld, int hbit, int lbit, + int rbits = 0) VL_MT_SAFE { + const int hoffset = VL_BITBIT_E(hbit); + const int loffset = VL_BITBIT_E(lbit); + const int roffset = VL_BITBIT_E(rbits); + const int hword = VL_BITWORD_E(hbit); + const int lword = VL_BITWORD_E(lbit); + const int rword = VL_BITWORD_E(rbits); + const EData cleanmask = hword == rword ? VL_MASK_E(roffset) : VL_MASK_E(0); + + if (hoffset == VL_SIZEBITS_E && loffset == 0) { + // Fast and common case, word based insertion + owp[VL_BITWORD_E(lbit)] = ld & cleanmask; + } else { + const EData lde = static_cast(ld); + if (hword == lword) { // know < EData bits because above checks it + // Assignment is contained within one word of destination + const EData insmask = (VL_MASK_E(hoffset - loffset + 1)) << loffset; + owp[lword] = (owp[lword] & ~insmask) | ((lde << loffset) & (insmask & cleanmask)); + } else { + // Assignment crosses a word boundary in destination + const EData hinsmask = (VL_MASK_E(hoffset - 0 + 1)) << 0; + const EData linsmask = (VL_MASK_E((VL_EDATASIZE - 1) - loffset + 1)) << loffset; + const int nbitsonright = VL_EDATASIZE - loffset; // bits that end up in lword + owp[lword] = (owp[lword] & ~linsmask) | ((lde << loffset) & linsmask); + owp[hword] + = (owp[hword] & ~hinsmask) | ((lde >> nbitsonright) & (hinsmask & cleanmask)); + } + } +} + +// INTERNAL: Stuff large LHS bit 0++ into OUTPUT at specified offset +// lwp may be "dirty" +static inline void _vl_insert_WW(int, WDataOutP owp, WDataInP const lwp, int hbit, int lbit, + int rbits = 0) VL_MT_SAFE { + const int hoffset = VL_BITBIT_E(hbit); + const int loffset = VL_BITBIT_E(lbit); + const int roffset = VL_BITBIT_E(rbits); + const int lword = VL_BITWORD_E(lbit); + const int hword = VL_BITWORD_E(hbit); + const int rword = VL_BITWORD_E(rbits); + const int words = VL_WORDS_I(hbit - lbit + 1); + // Cleaning mask, only applied to top word of the assignment. Is a no-op + // if we don't assign to the top word of the destination. + const EData cleanmask = hword == rword ? VL_MASK_E(roffset) : VL_MASK_E(0); + + if (hoffset == VL_SIZEBITS_E && loffset == 0) { + // Fast and common case, word based insertion + for (int i = 0; i < (words - 1); ++i) owp[lword + i] = lwp[i]; + owp[hword] = lwp[words - 1] & cleanmask; + } else if (loffset == 0) { + // Non-32bit, but nicely aligned, so stuff all but the last word + for (int i = 0; i < (words - 1); ++i) owp[lword + i] = lwp[i]; + // Know it's not a full word as above fast case handled it + const EData hinsmask = (VL_MASK_E(hoffset - 0 + 1)); + owp[hword] = (owp[hword] & ~hinsmask) | (lwp[words - 1] & (hinsmask & cleanmask)); + } else { + const EData hinsmask = (VL_MASK_E(hoffset - 0 + 1)) << 0; + const EData linsmask = (VL_MASK_E((VL_EDATASIZE - 1) - loffset + 1)) << loffset; + const int nbitsonright + = VL_EDATASIZE - loffset; // bits that end up in lword (know loffset!=0) + // Middle words + for (int i = 0; i < words; ++i) { + { // Lower word + const int oword = lword + i; + const EData d = lwp[i] << loffset; + const EData od = (owp[oword] & ~linsmask) | (d & linsmask); + if (oword == hword) { + owp[oword] = (owp[oword] & ~hinsmask) | (od & (hinsmask & cleanmask)); + } else { + owp[oword] = od; + } + } + { // Upper word + const int oword = lword + i + 1; + if (oword <= hword) { + const EData d = lwp[i] >> nbitsonright; + const EData od = (d & ~linsmask) | (owp[oword] & linsmask); + if (oword == hword) { + owp[oword] = (owp[oword] & ~hinsmask) | (od & (hinsmask & cleanmask)); + } else { + owp[oword] = od; + } + } + } + } + } +} + +static inline void _vl_insert_WQ(int obits, WDataOutP owp, QData ld, int hbit, int lbit, + int rbits = 0) VL_MT_SAFE { + VlWide lwp; + VL_SET_WQ(lwp, ld); + _vl_insert_WW(obits, owp, lwp, hbit, lbit, rbits); +} + +// EMIT_RULE: VL_REPLICATE: oclean=clean>width32, dirty<=width32; lclean=clean; rclean==clean; +// RHS MUST BE CLEAN CONSTANT. +#define VL_REPLICATE_IOI(obits, lbits, rbits, ld, rep) (-(ld)) // Iff lbits==1 +#define VL_REPLICATE_QOI(obits, lbits, rbits, ld, rep) (-(static_cast(ld))) // Iff lbits==1 + +static inline IData VL_REPLICATE_III(int, int lbits, int, IData ld, IData rep) VL_PURE { + IData returndata = ld; + for (unsigned i = 1; i < rep; ++i) { + returndata = returndata << lbits; + returndata |= ld; + } + return returndata; +} +static inline QData VL_REPLICATE_QII(int, int lbits, int, IData ld, IData rep) VL_PURE { + QData returndata = ld; + for (unsigned i = 1; i < rep; ++i) { + returndata = returndata << lbits; + returndata |= static_cast(ld); + } + return returndata; +} +static inline WDataOutP VL_REPLICATE_WII(int obits, int lbits, int, WDataOutP owp, IData ld, + IData rep) VL_MT_SAFE { + owp[0] = ld; + for (unsigned i = 1; i < rep; ++i) { + _vl_insert_WI(obits, owp, ld, i * lbits + lbits - 1, i * lbits); + } + return owp; +} +static inline WDataOutP VL_REPLICATE_WQI(int obits, int lbits, int, WDataOutP owp, QData ld, + IData rep) VL_MT_SAFE { + VL_SET_WQ(owp, ld); + for (unsigned i = 1; i < rep; ++i) { + _vl_insert_WQ(obits, owp, ld, i * lbits + lbits - 1, i * lbits); + } + return owp; +} +static inline WDataOutP VL_REPLICATE_WWI(int obits, int lbits, int, WDataOutP owp, + WDataInP const lwp, IData rep) VL_MT_SAFE { + for (int i = 0; i < VL_WORDS_I(lbits); ++i) owp[i] = lwp[i]; + for (unsigned i = 1; i < rep; ++i) { + _vl_insert_WW(obits, owp, lwp, i * lbits + lbits - 1, i * lbits); + } + return owp; +} + +// Left stream operator. Output will always be clean. LHS and RHS must be clean. +// Special "fast" versions for slice sizes that are a power of 2. These use +// shifts and masks to execute faster than the slower for-loop approach where a +// subset of bits is copied in during each iteration. +static inline IData VL_STREAML_FAST_III(int, int lbits, int, IData ld, IData rd_log2) VL_PURE { + // Pre-shift bits in most-significant slice: + // + // If lbits is not a multiple of the slice size (i.e., lbits % rd != 0), + // then we end up with a "gap" in our reversed result. For example, if we + // have a 5-bit Verlilog signal (lbits=5) in an 8-bit C data type: + // + // ld = ---43210 + // + // (where numbers are the Verilog signal bit numbers and '-' is an unused bit). + // Executing the switch statement below with a slice size of two (rd=2, + // rd_log2=1) produces: + // + // ret = 1032-400 + // + // Pre-shifting the bits in the most-significant slice allows us to avoid + // this gap in the shuffled data: + // + // ld_adjusted = --4-3210 + // ret = 10324--- + IData ret = ld; + if (rd_log2) { + const vluint32_t lbitsFloor = lbits & ~VL_MASK_I(rd_log2); // max multiple of rd <= lbits + const vluint32_t lbitsRem = lbits - lbitsFloor; // number of bits in most-sig slice (MSS) + const IData msbMask = VL_MASK_I(lbitsRem) << lbitsFloor; // mask to sel only bits in MSS + ret = (ret & ~msbMask) | ((ret & msbMask) << ((VL_UL(1) << rd_log2) - lbitsRem)); + } + switch (rd_log2) { + case 0: ret = ((ret >> 1) & VL_UL(0x55555555)) | ((ret & VL_UL(0x55555555)) << 1); // FALLTHRU + case 1: ret = ((ret >> 2) & VL_UL(0x33333333)) | ((ret & VL_UL(0x33333333)) << 2); // FALLTHRU + case 2: ret = ((ret >> 4) & VL_UL(0x0f0f0f0f)) | ((ret & VL_UL(0x0f0f0f0f)) << 4); // FALLTHRU + case 3: ret = ((ret >> 8) & VL_UL(0x00ff00ff)) | ((ret & VL_UL(0x00ff00ff)) << 8); // FALLTHRU + case 4: ret = ((ret >> 16) | (ret << 16)); // FALLTHRU + default:; + } + return ret >> (VL_IDATASIZE - lbits); +} + +static inline QData VL_STREAML_FAST_QQI(int, int lbits, int, QData ld, IData rd_log2) VL_PURE { + // Pre-shift bits in most-significant slice (see comment in VL_STREAML_FAST_III) + QData ret = ld; + if (rd_log2) { + const vluint32_t lbitsFloor = lbits & ~VL_MASK_I(rd_log2); + const vluint32_t lbitsRem = lbits - lbitsFloor; + const QData msbMask = VL_MASK_Q(lbitsRem) << lbitsFloor; + ret = (ret & ~msbMask) | ((ret & msbMask) << ((1ULL << rd_log2) - lbitsRem)); + } + switch (rd_log2) { + case 0: + ret = (((ret >> 1) & 0x5555555555555555ULL) + | ((ret & 0x5555555555555555ULL) << 1)); // FALLTHRU + case 1: + ret = (((ret >> 2) & 0x3333333333333333ULL) + | ((ret & 0x3333333333333333ULL) << 2)); // FALLTHRU + case 2: + ret = (((ret >> 4) & 0x0f0f0f0f0f0f0f0fULL) + | ((ret & 0x0f0f0f0f0f0f0f0fULL) << 4)); // FALLTHRU + case 3: + ret = (((ret >> 8) & 0x00ff00ff00ff00ffULL) + | ((ret & 0x00ff00ff00ff00ffULL) << 8)); // FALLTHRU + case 4: + ret = (((ret >> 16) & 0x0000ffff0000ffffULL) + | ((ret & 0x0000ffff0000ffffULL) << 16)); // FALLTHRU + case 5: ret = ((ret >> 32) | (ret << 32)); // FALLTHRU + default:; + } + return ret >> (VL_QUADSIZE - lbits); +} + +// Regular "slow" streaming operators +static inline IData VL_STREAML_III(int, int lbits, int, IData ld, IData rd) VL_PURE { + IData ret = 0; + // Slice size should never exceed the lhs width + const IData mask = VL_MASK_I(rd); + for (int istart = 0; istart < lbits; istart += rd) { + int ostart = lbits - rd - istart; + ostart = ostart > 0 ? ostart : 0; + ret |= ((ld >> istart) & mask) << ostart; + } + return ret; +} + +static inline QData VL_STREAML_QQI(int, int lbits, int, QData ld, IData rd) VL_PURE { + QData ret = 0; + // Slice size should never exceed the lhs width + const QData mask = VL_MASK_Q(rd); + for (int istart = 0; istart < lbits; istart += rd) { + int ostart = lbits - rd - istart; + ostart = ostart > 0 ? ostart : 0; + ret |= ((ld >> istart) & mask) << ostart; + } + return ret; +} + +static inline WDataOutP VL_STREAML_WWI(int, int lbits, int, WDataOutP owp, WDataInP const lwp, + IData rd) VL_MT_SAFE { + VL_ZERO_W(lbits, owp); + // Slice size should never exceed the lhs width + const int ssize = (rd < static_cast(lbits)) ? rd : (static_cast(lbits)); + for (int istart = 0; istart < lbits; istart += rd) { + int ostart = lbits - rd - istart; + ostart = ostart > 0 ? ostart : 0; + for (int sbit = 0; sbit < ssize && sbit < lbits - istart; ++sbit) { + // Extract a single bit from lwp and shift it to the correct + // location for owp. + EData bit = (VL_BITRSHIFT_W(lwp, (istart + sbit)) & 1) << VL_BITBIT_E(ostart + sbit); + owp[VL_BITWORD_E(ostart + sbit)] |= bit; + } + } + return owp; +} + +// Because concats are common and wide, it's valuable to always have a clean output. +// Thus we specify inputs must be clean, so we don't need to clean the output. +// Note the bit shifts are always constants, so the adds in these constify out. +// Casts required, as args may be 8 bit entities, and need to shift to appropriate output size +#define VL_CONCAT_III(obits, lbits, rbits, ld, rd) \ + (static_cast(ld) << (rbits) | static_cast(rd)) +#define VL_CONCAT_QII(obits, lbits, rbits, ld, rd) \ + (static_cast(ld) << (rbits) | static_cast(rd)) +#define VL_CONCAT_QIQ(obits, lbits, rbits, ld, rd) \ + (static_cast(ld) << (rbits) | static_cast(rd)) +#define VL_CONCAT_QQI(obits, lbits, rbits, ld, rd) \ + (static_cast(ld) << (rbits) | static_cast(rd)) +#define VL_CONCAT_QQQ(obits, lbits, rbits, ld, rd) \ + (static_cast(ld) << (rbits) | static_cast(rd)) + +static inline WDataOutP VL_CONCAT_WII(int obits, int lbits, int rbits, WDataOutP owp, IData ld, + IData rd) VL_MT_SAFE { + owp[0] = rd; + for (int i = 1; i < VL_WORDS_I(obits); ++i) owp[i] = 0; + _vl_insert_WI(obits, owp, ld, rbits + lbits - 1, rbits); + return owp; +} +static inline WDataOutP VL_CONCAT_WWI(int obits, int lbits, int rbits, WDataOutP owp, + WDataInP const lwp, IData rd) VL_MT_SAFE { + owp[0] = rd; + for (int i = 1; i < VL_WORDS_I(obits); ++i) owp[i] = 0; + _vl_insert_WW(obits, owp, lwp, rbits + lbits - 1, rbits); + return owp; +} +static inline WDataOutP VL_CONCAT_WIW(int obits, int lbits, int rbits, WDataOutP owp, IData ld, + WDataInP const rwp) VL_MT_SAFE { + for (int i = 0; i < VL_WORDS_I(rbits); ++i) owp[i] = rwp[i]; + for (int i = VL_WORDS_I(rbits); i < VL_WORDS_I(obits); ++i) owp[i] = 0; + _vl_insert_WI(obits, owp, ld, rbits + lbits - 1, rbits); + return owp; +} +static inline WDataOutP VL_CONCAT_WIQ(int obits, int lbits, int rbits, WDataOutP owp, IData ld, + QData rd) VL_MT_SAFE { + VL_SET_WQ(owp, rd); + for (int i = VL_WQ_WORDS_E; i < VL_WORDS_I(obits); ++i) owp[i] = 0; + _vl_insert_WI(obits, owp, ld, rbits + lbits - 1, rbits); + return owp; +} +static inline WDataOutP VL_CONCAT_WQI(int obits, int lbits, int rbits, WDataOutP owp, QData ld, + IData rd) VL_MT_SAFE { + owp[0] = rd; + for (int i = 1; i < VL_WORDS_I(obits); ++i) owp[i] = 0; + _vl_insert_WQ(obits, owp, ld, rbits + lbits - 1, rbits); + return owp; +} +static inline WDataOutP VL_CONCAT_WQQ(int obits, int lbits, int rbits, WDataOutP owp, QData ld, + QData rd) VL_MT_SAFE { + VL_SET_WQ(owp, rd); + for (int i = VL_WQ_WORDS_E; i < VL_WORDS_I(obits); ++i) owp[i] = 0; + _vl_insert_WQ(obits, owp, ld, rbits + lbits - 1, rbits); + return owp; +} +static inline WDataOutP VL_CONCAT_WWQ(int obits, int lbits, int rbits, WDataOutP owp, + WDataInP const lwp, QData rd) VL_MT_SAFE { + VL_SET_WQ(owp, rd); + for (int i = VL_WQ_WORDS_E; i < VL_WORDS_I(obits); ++i) owp[i] = 0; + _vl_insert_WW(obits, owp, lwp, rbits + lbits - 1, rbits); + return owp; +} +static inline WDataOutP VL_CONCAT_WQW(int obits, int lbits, int rbits, WDataOutP owp, QData ld, + WDataInP const rwp) VL_MT_SAFE { + for (int i = 0; i < VL_WORDS_I(rbits); ++i) owp[i] = rwp[i]; + for (int i = VL_WORDS_I(rbits); i < VL_WORDS_I(obits); ++i) owp[i] = 0; + _vl_insert_WQ(obits, owp, ld, rbits + lbits - 1, rbits); + return owp; +} +static inline WDataOutP VL_CONCAT_WWW(int obits, int lbits, int rbits, WDataOutP owp, + WDataInP const lwp, WDataInP const rwp) VL_MT_SAFE { + for (int i = 0; i < VL_WORDS_I(rbits); ++i) owp[i] = rwp[i]; + for (int i = VL_WORDS_I(rbits); i < VL_WORDS_I(obits); ++i) owp[i] = 0; + _vl_insert_WW(obits, owp, lwp, rbits + lbits - 1, rbits); + return owp; +} + +//=================================================================== +// Shifts + +// Static shift, used by internal functions +// The output is the same as the input - it overlaps! +static inline void _vl_shiftl_inplace_w(int obits, WDataOutP iowp, + IData rd /*1 or 4*/) VL_MT_SAFE { + const int words = VL_WORDS_I(obits); + const EData linsmask = VL_MASK_E(rd); + for (int i = words - 1; i >= 1; --i) { + iowp[i] + = ((iowp[i] << rd) & ~linsmask) | ((iowp[i - 1] >> (VL_EDATASIZE - rd)) & linsmask); + } + iowp[0] = ((iowp[0] << rd) & ~linsmask); + iowp[VL_WORDS_I(obits) - 1] &= VL_MASK_E(obits); +} + +// EMIT_RULE: VL_SHIFTL: oclean=lclean; rclean==clean; +// Important: Unlike most other funcs, the shift might well be a computed +// expression. Thus consider this when optimizing. (And perhaps have 2 funcs?) +static inline WDataOutP VL_SHIFTL_WWI(int obits, int, int, WDataOutP owp, WDataInP const lwp, + IData rd) VL_MT_SAFE { + const int word_shift = VL_BITWORD_E(rd); + const int bit_shift = VL_BITBIT_E(rd); + if (rd >= static_cast(obits)) { // rd may be huge with MSB set + for (int i = 0; i < VL_WORDS_I(obits); ++i) owp[i] = 0; + } else if (bit_shift == 0) { // Aligned word shift (<<0,<<32,<<64 etc) + for (int i = 0; i < word_shift; ++i) owp[i] = 0; + for (int i = word_shift; i < VL_WORDS_I(obits); ++i) owp[i] = lwp[i - word_shift]; + } else { + for (int i = 0; i < VL_WORDS_I(obits); ++i) owp[i] = 0; + _vl_insert_WW(obits, owp, lwp, obits - 1, rd); + } + return owp; +} +static inline WDataOutP VL_SHIFTL_WWW(int obits, int lbits, int rbits, WDataOutP owp, + WDataInP const lwp, WDataInP const rwp) VL_MT_SAFE { + for (int i = 1; i < VL_WORDS_I(rbits); ++i) { + if (VL_UNLIKELY(rwp[i])) { // Huge shift 1>>32 or more + return VL_ZERO_W(obits, owp); + } + } + return VL_SHIFTL_WWI(obits, lbits, 32, owp, lwp, rwp[0]); +} +static inline WDataOutP VL_SHIFTL_WWQ(int obits, int lbits, int rbits, WDataOutP owp, + WDataInP const lwp, QData rd) VL_MT_SAFE { + VlWide rwp; + VL_SET_WQ(rwp, rd); + return VL_SHIFTL_WWW(obits, lbits, rbits, owp, lwp, rwp); +} +static inline IData VL_SHIFTL_IIW(int obits, int, int rbits, IData lhs, + WDataInP const rwp) VL_MT_SAFE { + for (int i = 1; i < VL_WORDS_I(rbits); ++i) { + if (VL_UNLIKELY(rwp[i])) { // Huge shift 1>>32 or more + return 0; + } + } + return VL_CLEAN_II(obits, obits, lhs << rwp[0]); +} +static inline IData VL_SHIFTL_IIQ(int obits, int, int, IData lhs, QData rhs) VL_MT_SAFE { + if (VL_UNLIKELY(rhs >= VL_IDATASIZE)) return 0; + return VL_CLEAN_II(obits, obits, lhs << rhs); +} +static inline QData VL_SHIFTL_QQW(int obits, int, int rbits, QData lhs, + WDataInP const rwp) VL_MT_SAFE { + for (int i = 1; i < VL_WORDS_I(rbits); ++i) { + if (VL_UNLIKELY(rwp[i])) { // Huge shift 1>>32 or more + return 0; + } + } + // Above checks rwp[1]==0 so not needed in below shift + return VL_CLEAN_QQ(obits, obits, lhs << (static_cast(rwp[0]))); +} +static inline QData VL_SHIFTL_QQQ(int obits, int, int, QData lhs, QData rhs) VL_MT_SAFE { + if (VL_UNLIKELY(rhs >= VL_QUADSIZE)) return 0; + return VL_CLEAN_QQ(obits, obits, lhs << rhs); +} + +// EMIT_RULE: VL_SHIFTR: oclean=lclean; rclean==clean; +// Important: Unlike most other funcs, the shift might well be a computed +// expression. Thus consider this when optimizing. (And perhaps have 2 funcs?) +static inline WDataOutP VL_SHIFTR_WWI(int obits, int, int, WDataOutP owp, WDataInP const lwp, + IData rd) VL_MT_SAFE { + const int word_shift = VL_BITWORD_E(rd); // Maybe 0 + const int bit_shift = VL_BITBIT_E(rd); + if (rd >= static_cast(obits)) { // rd may be huge with MSB set + for (int i = 0; i < VL_WORDS_I(obits); ++i) owp[i] = 0; + } else if (bit_shift == 0) { // Aligned word shift (>>0,>>32,>>64 etc) + const int copy_words = (VL_WORDS_I(obits) - word_shift); + for (int i = 0; i < copy_words; ++i) owp[i] = lwp[i + word_shift]; + for (int i = copy_words; i < VL_WORDS_I(obits); ++i) owp[i] = 0; + } else { + const int loffset = rd & VL_SIZEBITS_E; + const int nbitsonright = VL_EDATASIZE - loffset; // bits that end up in lword (know + // loffset!=0) Middle words + const int words = VL_WORDS_I(obits - rd); + for (int i = 0; i < words; ++i) { + owp[i] = lwp[i + word_shift] >> loffset; + const int upperword = i + word_shift + 1; + if (upperword < VL_WORDS_I(obits)) owp[i] |= lwp[upperword] << nbitsonright; + } + for (int i = words; i < VL_WORDS_I(obits); ++i) owp[i] = 0; + } + return owp; +} +static inline WDataOutP VL_SHIFTR_WWW(int obits, int lbits, int rbits, WDataOutP owp, + WDataInP const lwp, WDataInP const rwp) VL_MT_SAFE { + for (int i = 1; i < VL_WORDS_I(rbits); ++i) { + if (VL_UNLIKELY(rwp[i])) { // Huge shift 1>>32 or more + return VL_ZERO_W(obits, owp); + } + } + return VL_SHIFTR_WWI(obits, lbits, 32, owp, lwp, rwp[0]); +} +static inline WDataOutP VL_SHIFTR_WWQ(int obits, int lbits, int rbits, WDataOutP owp, + WDataInP const lwp, QData rd) VL_MT_SAFE { + VlWide rwp; + VL_SET_WQ(rwp, rd); + return VL_SHIFTR_WWW(obits, lbits, rbits, owp, lwp, rwp); +} + +static inline IData VL_SHIFTR_IIW(int obits, int, int rbits, IData lhs, + WDataInP const rwp) VL_MT_SAFE { + for (int i = 1; i < VL_WORDS_I(rbits); ++i) { + if (VL_UNLIKELY(rwp[i])) { // Huge shift 1>>32 or more + return 0; + } + } + return VL_CLEAN_II(obits, obits, lhs >> rwp[0]); +} +static inline QData VL_SHIFTR_QQW(int obits, int, int rbits, QData lhs, + WDataInP const rwp) VL_MT_SAFE { + for (int i = 1; i < VL_WORDS_I(rbits); ++i) { + if (VL_UNLIKELY(rwp[i])) { // Huge shift 1>>32 or more + return 0; + } + } + // Above checks rwp[1]==0 so not needed in below shift + return VL_CLEAN_QQ(obits, obits, lhs >> (static_cast(rwp[0]))); +} +static inline IData VL_SHIFTR_IIQ(int obits, int, int, IData lhs, QData rhs) VL_MT_SAFE { + if (VL_UNLIKELY(rhs >= VL_IDATASIZE)) return 0; + return VL_CLEAN_QQ(obits, obits, lhs >> rhs); +} +static inline QData VL_SHIFTR_QQQ(int obits, int, int, QData lhs, QData rhs) VL_MT_SAFE { + if (VL_UNLIKELY(rhs >= VL_QUADSIZE)) return 0; + return VL_CLEAN_QQ(obits, obits, lhs >> rhs); +} + +// EMIT_RULE: VL_SHIFTRS: oclean=false; lclean=clean, rclean==clean; +static inline IData VL_SHIFTRS_III(int obits, int lbits, int, IData lhs, IData rhs) VL_PURE { + // Note the C standard does not specify the >> operator as a arithmetic shift! + // IEEE says signed if output signed, but bit position from lbits; + // must use lbits for sign; lbits might != obits, + // an EXTEND(SHIFTRS(...)) can became a SHIFTRS(...) within same 32/64 bit word length + const IData sign = -(lhs >> (lbits - 1)); // ffff_ffff if negative + const IData signext = ~(VL_MASK_I(lbits) >> rhs); // One with bits where we've shifted "past" + return (lhs >> rhs) | (sign & VL_CLEAN_II(obits, obits, signext)); +} +static inline QData VL_SHIFTRS_QQI(int obits, int lbits, int, QData lhs, IData rhs) VL_PURE { + const QData sign = -(lhs >> (lbits - 1)); + const QData signext = ~(VL_MASK_Q(lbits) >> rhs); + return (lhs >> rhs) | (sign & VL_CLEAN_QQ(obits, obits, signext)); +} +static inline IData VL_SHIFTRS_IQI(int obits, int lbits, int rbits, QData lhs, IData rhs) VL_PURE { + return static_cast(VL_SHIFTRS_QQI(obits, lbits, rbits, lhs, rhs)); +} +static inline WDataOutP VL_SHIFTRS_WWI(int obits, int lbits, int, WDataOutP owp, + WDataInP const lwp, IData rd) VL_MT_SAFE { + const int word_shift = VL_BITWORD_E(rd); + const int bit_shift = VL_BITBIT_E(rd); + const int lmsw = VL_WORDS_I(obits) - 1; + const EData sign = VL_SIGNONES_E(lbits, lwp[lmsw]); + if (rd >= static_cast(obits)) { // Shifting past end, sign in all of lbits + for (int i = 0; i <= lmsw; ++i) owp[i] = sign; + owp[lmsw] &= VL_MASK_E(lbits); + } else if (bit_shift == 0) { // Aligned word shift (>>0,>>32,>>64 etc) + const int copy_words = (VL_WORDS_I(obits) - word_shift); + for (int i = 0; i < copy_words; ++i) owp[i] = lwp[i + word_shift]; + if (copy_words >= 0) owp[copy_words - 1] |= ~VL_MASK_E(obits) & sign; + for (int i = copy_words; i < VL_WORDS_I(obits); ++i) owp[i] = sign; + owp[lmsw] &= VL_MASK_E(lbits); + } else { + const int loffset = rd & VL_SIZEBITS_E; + int nbitsonright = VL_EDATASIZE - loffset; // bits that end up in lword (know loffset!=0) + // Middle words + const int words = VL_WORDS_I(obits - rd); + for (int i = 0; i < words; ++i) { + owp[i] = lwp[i + word_shift] >> loffset; + const int upperword = i + word_shift + 1; + if (upperword < VL_WORDS_I(obits)) owp[i] |= lwp[upperword] << nbitsonright; + } + if (words) owp[words - 1] |= sign & ~VL_MASK_E(obits - loffset); + for (int i = words; i < VL_WORDS_I(obits); ++i) owp[i] = sign; + owp[lmsw] &= VL_MASK_E(lbits); + } + return owp; +} +static inline WDataOutP VL_SHIFTRS_WWW(int obits, int lbits, int rbits, WDataOutP owp, + WDataInP const lwp, WDataInP const rwp) VL_MT_SAFE { + EData overshift = 0; // Huge shift 1>>32 or more + for (int i = 1; i < VL_WORDS_I(rbits); ++i) overshift |= rwp[i]; + if (VL_UNLIKELY(overshift || rwp[0] >= obits)) { + const int lmsw = VL_WORDS_I(obits) - 1; + const EData sign = VL_SIGNONES_E(lbits, lwp[lmsw]); + for (int j = 0; j <= lmsw; ++j) owp[j] = sign; + owp[lmsw] &= VL_MASK_E(lbits); + return owp; + } + return VL_SHIFTRS_WWI(obits, lbits, 32, owp, lwp, rwp[0]); +} +static inline WDataOutP VL_SHIFTRS_WWQ(int obits, int lbits, int rbits, WDataOutP owp, + WDataInP const lwp, QData rd) VL_MT_SAFE { + VlWide rwp; + VL_SET_WQ(rwp, rd); + return VL_SHIFTRS_WWW(obits, lbits, rbits, owp, lwp, rwp); +} +static inline IData VL_SHIFTRS_IIW(int obits, int lbits, int rbits, IData lhs, + WDataInP const rwp) VL_MT_SAFE { + EData overshift = 0; // Huge shift 1>>32 or more + for (int i = 1; i < VL_WORDS_I(rbits); ++i) overshift |= rwp[i]; + if (VL_UNLIKELY(overshift || rwp[0] >= obits)) { + const IData sign = -(lhs >> (lbits - 1)); // ffff_ffff if negative + return VL_CLEAN_II(obits, obits, sign); + } + return VL_SHIFTRS_III(obits, lbits, 32, lhs, rwp[0]); +} +static inline QData VL_SHIFTRS_QQW(int obits, int lbits, int rbits, QData lhs, + WDataInP const rwp) VL_MT_SAFE { + EData overshift = 0; // Huge shift 1>>32 or more + for (int i = 1; i < VL_WORDS_I(rbits); ++i) overshift |= rwp[i]; + if (VL_UNLIKELY(overshift || rwp[0] >= obits)) { + const QData sign = -(lhs >> (lbits - 1)); // ffff_ffff if negative + return VL_CLEAN_QQ(obits, obits, sign); + } + return VL_SHIFTRS_QQI(obits, lbits, 32, lhs, rwp[0]); +} +static inline IData VL_SHIFTRS_IIQ(int obits, int lbits, int rbits, IData lhs, + QData rhs) VL_MT_SAFE { + VlWide rwp; + VL_SET_WQ(rwp, rhs); + return VL_SHIFTRS_IIW(obits, lbits, rbits, lhs, rwp); +} +static inline QData VL_SHIFTRS_QQQ(int obits, int lbits, int rbits, QData lhs, QData rhs) VL_PURE { + VlWide rwp; + VL_SET_WQ(rwp, rhs); + return VL_SHIFTRS_QQW(obits, lbits, rbits, lhs, rwp); +} + +//=================================================================== +// Bit selection + +// EMIT_RULE: VL_BITSEL: oclean=dirty; rclean==clean; +#define VL_BITSEL_IIII(obits, lbits, rbits, zbits, lhs, rhs) ((lhs) >> (rhs)) +#define VL_BITSEL_QIII(obits, lbits, rbits, zbits, lhs, rhs) ((lhs) >> (rhs)) +#define VL_BITSEL_QQII(obits, lbits, rbits, zbits, lhs, rhs) ((lhs) >> (rhs)) +#define VL_BITSEL_IQII(obits, lbits, rbits, zbits, lhs, rhs) (static_cast((lhs) >> (rhs))) + +static inline IData VL_BITSEL_IWII(int, int lbits, int, int, WDataInP const lwp, + IData rd) VL_MT_SAFE { + int word = VL_BITWORD_E(rd); + if (VL_UNLIKELY(rd > static_cast(lbits))) { + return ~0; // Spec says you can go outside the range of a array. Don't coredump if so. + // We return all 1's as that's more likely to find bugs (?) than 0's. + } else { + return (lwp[word] >> VL_BITBIT_E(rd)); + } +} + +// EMIT_RULE: VL_RANGE: oclean=lclean; out=dirty +// & MUST BE CLEAN (currently constant) +#define VL_SEL_IIII(obits, lbits, rbits, tbits, lhs, lsb, width) ((lhs) >> (lsb)) +#define VL_SEL_QQII(obits, lbits, rbits, tbits, lhs, lsb, width) ((lhs) >> (lsb)) +#define VL_SEL_IQII(obits, lbits, rbits, tbits, lhs, lsb, width) \ + (static_cast((lhs) >> (lsb))) + +static inline IData VL_SEL_IWII(int, int lbits, int, int, WDataInP const lwp, IData lsb, + IData width) VL_MT_SAFE { + int msb = lsb + width - 1; + if (VL_UNLIKELY(msb >= lbits)) { + return ~0; // Spec says you can go outside the range of a array. Don't coredump if so. + } else if (VL_BITWORD_E(msb) == VL_BITWORD_E(static_cast(lsb))) { + return VL_BITRSHIFT_W(lwp, lsb); + } else { + // 32 bit extraction may span two words + int nbitsfromlow = VL_EDATASIZE - VL_BITBIT_E(lsb); // bits that come from low word + return ((lwp[VL_BITWORD_E(msb)] << nbitsfromlow) | VL_BITRSHIFT_W(lwp, lsb)); + } +} + +static inline QData VL_SEL_QWII(int, int lbits, int, int, WDataInP const lwp, IData lsb, + IData width) VL_MT_SAFE { + const int msb = lsb + width - 1; + if (VL_UNLIKELY(msb > lbits)) { + return ~0; // Spec says you can go outside the range of a array. Don't coredump if so. + } else if (VL_BITWORD_E(msb) == VL_BITWORD_E(static_cast(lsb))) { + return VL_BITRSHIFT_W(lwp, lsb); + } else if (VL_BITWORD_E(msb) == 1 + VL_BITWORD_E(static_cast(lsb))) { + const int nbitsfromlow = VL_EDATASIZE - VL_BITBIT_E(lsb); + const QData hi = (lwp[VL_BITWORD_E(msb)]); + const QData lo = VL_BITRSHIFT_W(lwp, lsb); + return (hi << nbitsfromlow) | lo; + } else { + // 64 bit extraction may span three words + int nbitsfromlow = VL_EDATASIZE - VL_BITBIT_E(lsb); + const QData hi = (lwp[VL_BITWORD_E(msb)]); + const QData mid = (lwp[VL_BITWORD_E(lsb) + 1]); + const QData lo = VL_BITRSHIFT_W(lwp, lsb); + return (hi << (nbitsfromlow + VL_EDATASIZE)) | (mid << nbitsfromlow) | lo; + } +} + +static inline WDataOutP VL_SEL_WWII(int obits, int lbits, int, int, WDataOutP owp, + WDataInP const lwp, IData lsb, IData width) VL_MT_SAFE { + const int msb = lsb + width - 1; + const int word_shift = VL_BITWORD_E(lsb); + if (VL_UNLIKELY(msb > lbits)) { // Outside bounds, + for (int i = 0; i < VL_WORDS_I(obits) - 1; ++i) owp[i] = ~0; + owp[VL_WORDS_I(obits) - 1] = VL_MASK_E(obits); + } else if (VL_BITBIT_E(lsb) == 0) { + // Just a word extract + for (int i = 0; i < VL_WORDS_I(obits); ++i) owp[i] = lwp[i + word_shift]; + } else { + // Not a _vl_insert because the bits come from any bit number and goto bit 0 + const int loffset = lsb & VL_SIZEBITS_E; + const int nbitsfromlow = VL_EDATASIZE - loffset; // bits that end up in lword (know + // loffset!=0) Middle words + const int words = VL_WORDS_I(msb - lsb + 1); + for (int i = 0; i < words; ++i) { + owp[i] = lwp[i + word_shift] >> loffset; + const int upperword = i + word_shift + 1; + if (upperword <= static_cast(VL_BITWORD_E(msb))) { + owp[i] |= lwp[upperword] << nbitsfromlow; + } + } + for (int i = words; i < VL_WORDS_I(obits); ++i) owp[i] = 0; + } + return owp; +} + +//====================================================================== +// Math needing insert/select + +// Return QData from double (numeric) +// EMIT_RULE: VL_RTOIROUND_Q_D: oclean=dirty; lclean==clean/real +static inline QData VL_RTOIROUND_Q_D(int, double lhs) VL_PURE { + // IEEE format: [63]=sign [62:52]=exp+1023 [51:0]=mantissa + // This does not need to support subnormals as they are sub-integral + lhs = VL_ROUND(lhs); + if (lhs == 0.0) return 0; + const QData q = VL_CVT_Q_D(lhs); + const int lsb = static_cast((q >> 52ULL) & VL_MASK_Q(11)) - 1023 - 52; + const vluint64_t mantissa = (q & VL_MASK_Q(52)) | (1ULL << 52); + vluint64_t out = 0; + if (lsb < 0) { + out = mantissa >> -lsb; + } else if (lsb < 64) { + out = mantissa << lsb; + } + if (lhs < 0) out = -out; + return out; +} +static inline IData VL_RTOIROUND_I_D(int bits, double lhs) VL_PURE { + return static_cast(VL_RTOIROUND_Q_D(bits, lhs)); +} +static inline WDataOutP VL_RTOIROUND_W_D(int obits, WDataOutP owp, double lhs) VL_PURE { + // IEEE format: [63]=sign [62:52]=exp+1023 [51:0]=mantissa + // This does not need to support subnormals as they are sub-integral + lhs = VL_ROUND(lhs); + VL_ZERO_W(obits, owp); + if (lhs == 0.0) return owp; + const QData q = VL_CVT_Q_D(lhs); + const int lsb = static_cast((q >> 52ULL) & VL_MASK_Q(11)) - 1023 - 52; + const vluint64_t mantissa = (q & VL_MASK_Q(52)) | (1ULL << 52); + if (lsb < 0) { + VL_SET_WQ(owp, mantissa >> -lsb); + } else if (lsb < obits) { + _vl_insert_WQ(obits, owp, mantissa, lsb + 52, lsb); + } + if (lhs < 0) VL_NEGATE_INPLACE_W(VL_WORDS_I(obits), owp); + return owp; +} + +//====================================================================== +// Range assignments + +// EMIT_RULE: VL_ASSIGNRANGE: rclean=dirty; +static inline void VL_ASSIGNSEL_IIII(int rbits, int obits, int lsb, CData& lhsr, + IData rhs) VL_PURE { + _vl_insert_II(obits, lhsr, rhs, lsb + obits - 1, lsb, rbits); +} +static inline void VL_ASSIGNSEL_IIII(int rbits, int obits, int lsb, SData& lhsr, + IData rhs) VL_PURE { + _vl_insert_II(obits, lhsr, rhs, lsb + obits - 1, lsb, rbits); +} +static inline void VL_ASSIGNSEL_IIII(int rbits, int obits, int lsb, IData& lhsr, + IData rhs) VL_PURE { + _vl_insert_II(obits, lhsr, rhs, lsb + obits - 1, lsb, rbits); +} +static inline void VL_ASSIGNSEL_QIII(int rbits, int obits, int lsb, QData& lhsr, + IData rhs) VL_PURE { + _vl_insert_QQ(obits, lhsr, rhs, lsb + obits - 1, lsb, rbits); +} +static inline void VL_ASSIGNSEL_QQII(int rbits, int obits, int lsb, QData& lhsr, + QData rhs) VL_PURE { + _vl_insert_QQ(obits, lhsr, rhs, lsb + obits - 1, lsb, rbits); +} +static inline void VL_ASSIGNSEL_QIIQ(int rbits, int obits, int lsb, QData& lhsr, + QData rhs) VL_PURE { + _vl_insert_QQ(obits, lhsr, rhs, lsb + obits - 1, lsb, rbits); +} +// static inline void VL_ASSIGNSEL_IIIW(int obits, int lsb, IData& lhsr, WDataInP const rwp) +// VL_MT_SAFE { Illegal, as lhs width >= rhs width +static inline void VL_ASSIGNSEL_WIII(int rbits, int obits, int lsb, WDataOutP owp, + IData rhs) VL_MT_SAFE { + _vl_insert_WI(obits, owp, rhs, lsb + obits - 1, lsb, rbits); +} +static inline void VL_ASSIGNSEL_WIIQ(int rbits, int obits, int lsb, WDataOutP owp, + QData rhs) VL_MT_SAFE { + _vl_insert_WQ(obits, owp, rhs, lsb + obits - 1, lsb, rbits); +} +static inline void VL_ASSIGNSEL_WIIW(int rbits, int obits, int lsb, WDataOutP owp, + WDataInP const rwp) VL_MT_SAFE { + _vl_insert_WW(obits, owp, rwp, lsb + obits - 1, lsb, rbits); +} + +//====================================================================== +// Triops + +static inline WDataOutP VL_COND_WIWW(int obits, int, int, int, WDataOutP owp, int cond, + WDataInP const w1p, WDataInP const w2p) VL_MT_SAFE { + const int words = VL_WORDS_I(obits); + for (int i = 0; i < words; ++i) owp[i] = cond ? w1p[i] : w2p[i]; + return owp; +} + +//====================================================================== +// Constification + +// VL_CONST_W_#X(int obits, WDataOutP owp, IData data0, .... IData data(#-1)) +// Sets wide vector words to specified constant words. +// These macros are used when o might represent more words then are given as constants, +// hence all upper words must be zeroed. +// If changing the number of functions here, also change EMITCINLINES_NUM_CONSTW + +#define VL_C_END_(obits, wordsSet) \ + for (int i = (wordsSet); i < VL_WORDS_I(obits); ++i) o[i] = 0; \ + return o + +// clang-format off +static inline WDataOutP VL_CONST_W_1X(int obits, WDataOutP o, EData d0) VL_MT_SAFE { + o[0] = d0; + VL_C_END_(obits, 1); +} +static inline WDataOutP VL_CONST_W_2X(int obits, WDataOutP o, EData d1, EData d0) VL_MT_SAFE { + o[0] = d0; o[1] = d1; + VL_C_END_(obits, 2); +} +static inline WDataOutP VL_CONST_W_3X(int obits, WDataOutP o, EData d2, EData d1, + EData d0) VL_MT_SAFE { + o[0] = d0; o[1] = d1; o[2] = d2; + VL_C_END_(obits,3); +} +static inline WDataOutP VL_CONST_W_4X(int obits, WDataOutP o, + EData d3, EData d2, EData d1, EData d0) VL_MT_SAFE { + o[0] = d0; o[1] = d1; o[2] = d2; o[3] = d3; + VL_C_END_(obits,4); +} +static inline WDataOutP VL_CONST_W_5X(int obits, WDataOutP o, + EData d4, + EData d3, EData d2, EData d1, EData d0) VL_MT_SAFE { + o[0] = d0; o[1] = d1; o[2] = d2; o[3] = d3; + o[4] = d4; + VL_C_END_(obits,5); +} +static inline WDataOutP VL_CONST_W_6X(int obits, WDataOutP o, + EData d5, EData d4, + EData d3, EData d2, EData d1, EData d0) VL_MT_SAFE { + o[0] = d0; o[1] = d1; o[2] = d2; o[3] = d3; + o[4] = d4; o[5] = d5; + VL_C_END_(obits,6); +} +static inline WDataOutP VL_CONST_W_7X(int obits, WDataOutP o, + EData d6, EData d5, EData d4, + EData d3, EData d2, EData d1, EData d0) VL_MT_SAFE { + o[0] = d0; o[1] = d1; o[2] = d2; o[3] = d3; + o[4] = d4; o[5] = d5; o[6] = d6; + VL_C_END_(obits,7); +} +static inline WDataOutP VL_CONST_W_8X(int obits, WDataOutP o, + EData d7, EData d6, EData d5, EData d4, + EData d3, EData d2, EData d1, EData d0) VL_MT_SAFE { + o[0] = d0; o[1] = d1; o[2] = d2; o[3] = d3; + o[4] = d4; o[5] = d5; o[6] = d6; o[7] = d7; + VL_C_END_(obits,8); +} +// +static inline WDataOutP VL_CONSTHI_W_1X(int obits, int lsb, WDataOutP obase, + EData d0) VL_MT_SAFE { + WDataOutP o = obase + VL_WORDS_I(lsb); + o[0] = d0; + VL_C_END_(obits, VL_WORDS_I(lsb) + 1); +} +static inline WDataOutP VL_CONSTHI_W_2X(int obits, int lsb, WDataOutP obase, + EData d1, EData d0) VL_MT_SAFE { + WDataOutP o = obase + VL_WORDS_I(lsb); + o[0] = d0; o[1] = d1; + VL_C_END_(obits, VL_WORDS_I(lsb) + 2); +} +static inline WDataOutP VL_CONSTHI_W_3X(int obits, int lsb, WDataOutP obase, + EData d2, EData d1, EData d0) VL_MT_SAFE { + WDataOutP o = obase + VL_WORDS_I(lsb); + o[0] = d0; o[1] = d1; o[2] = d2; + VL_C_END_(obits, VL_WORDS_I(lsb) + 3); +} +static inline WDataOutP VL_CONSTHI_W_4X(int obits, int lsb, WDataOutP obase, + EData d3, EData d2, EData d1, EData d0) VL_MT_SAFE { + WDataOutP o = obase + VL_WORDS_I(lsb); + o[0] = d0; o[1] = d1; o[2] = d2; o[3] = d3; + VL_C_END_(obits, VL_WORDS_I(lsb) + 4); +} +static inline WDataOutP VL_CONSTHI_W_5X(int obits, int lsb, WDataOutP obase, + EData d4, + EData d3, EData d2, EData d1, EData d0) VL_MT_SAFE { + WDataOutP o = obase + VL_WORDS_I(lsb); + o[0] = d0; o[1] = d1; o[2] = d2; o[3] = d3; + o[4] = d4; + VL_C_END_(obits, VL_WORDS_I(lsb) + 5); +} +static inline WDataOutP VL_CONSTHI_W_6X(int obits, int lsb, WDataOutP obase, + EData d5, EData d4, + EData d3, EData d2, EData d1, EData d0) VL_MT_SAFE { + WDataOutP o = obase + VL_WORDS_I(lsb); + o[0] = d0; o[1] = d1; o[2] = d2; o[3] = d3; + o[4] = d4; o[5] = d5; + VL_C_END_(obits, VL_WORDS_I(lsb) + 6); +} +static inline WDataOutP VL_CONSTHI_W_7X(int obits, int lsb, WDataOutP obase, + EData d6, EData d5, EData d4, + EData d3, EData d2, EData d1, EData d0) VL_MT_SAFE { + WDataOutP o = obase + VL_WORDS_I(lsb); + o[0] = d0; o[1] = d1; o[2] = d2; o[3] = d3; + o[4] = d4; o[5] = d5; o[6] = d6; + VL_C_END_(obits, VL_WORDS_I(lsb) + 7); +} +static inline WDataOutP VL_CONSTHI_W_8X(int obits, int lsb, WDataOutP obase, + EData d7, EData d6, EData d5, EData d4, + EData d3, EData d2, EData d1, EData d0) VL_MT_SAFE { + WDataOutP o = obase + VL_WORDS_I(lsb); + o[0] = d0; o[1] = d1; o[2] = d2; o[3] = d3; + o[4] = d4; o[5] = d5; o[6] = d6; o[7] = d7; + VL_C_END_(obits, VL_WORDS_I(lsb) + 8); +} + +#undef VL_C_END_ + +// Partial constant, lower words of vector wider than 8*32, starting at bit number lsb +static inline void VL_CONSTLO_W_8X(int lsb, WDataOutP obase, + EData d7, EData d6, EData d5, EData d4, + EData d3, EData d2, EData d1, EData d0) VL_MT_SAFE { + WDataOutP o = obase + VL_WORDS_I(lsb); + o[0] = d0; o[1] = d1; o[2] = d2; o[3] = d3; o[4] = d4; o[5] = d5; o[6] = d6; o[7] = d7; +} +// clang-format on + +//====================================================================== +// Strings + +extern std::string VL_PUTC_N(const std::string& lhs, IData rhs, CData ths) VL_PURE; +extern CData VL_GETC_N(const std::string& lhs, IData rhs) VL_PURE; +extern std::string VL_SUBSTR_N(const std::string& lhs, IData rhs, IData ths) VL_PURE; + +inline IData VL_CMP_NN(const std::string& lhs, const std::string& rhs, bool ignoreCase) VL_PURE { + // SystemVerilog does not allow a string variable to contain '\0'. + // So C functions such as strcmp() can correctly compare strings. + if (ignoreCase) { + return VL_STRCASECMP(lhs.c_str(), rhs.c_str()); + } else { + return std::strcmp(lhs.c_str(), rhs.c_str()); + } +} + +extern IData VL_ATOI_N(const std::string& str, int base) VL_PURE; + +extern IData VL_FGETS_NI(std::string& dest, IData fpi); + +//====================================================================== +// Conversion functions + +extern std::string VL_CVT_PACK_STR_NW(int lwords, const WDataInP lwp) VL_MT_SAFE; +inline std::string VL_CVT_PACK_STR_NQ(QData lhs) VL_PURE { + VlWide lw; + VL_SET_WQ(lw, lhs); + return VL_CVT_PACK_STR_NW(VL_WQ_WORDS_E, lw); +} +inline std::string VL_CVT_PACK_STR_NN(const std::string& lhs) VL_PURE { return lhs; } +inline std::string& VL_CVT_PACK_STR_NN(std::string& lhs) VL_PURE { return lhs; } +inline std::string VL_CVT_PACK_STR_NI(IData lhs) VL_PURE { + VlWide lw; + VL_SET_WI(lw, lhs); + return VL_CVT_PACK_STR_NW(1, lw); +} +inline std::string VL_CONCATN_NNN(const std::string& lhs, const std::string& rhs) VL_PURE { + return lhs + rhs; +} +inline std::string VL_REPLICATEN_NNQ(int, int, int, const std::string& lhs, IData rep) VL_PURE { + std::string out; + out.reserve(lhs.length() * rep); + for (unsigned times = 0; times < rep; ++times) out += lhs; + return out; +} +inline std::string VL_REPLICATEN_NNI(int obits, int lbits, int rbits, const std::string& lhs, + IData rep) VL_PURE { + return VL_REPLICATEN_NNQ(obits, lbits, rbits, lhs, rep); +} + +inline IData VL_LEN_IN(const std::string& ld) { return ld.length(); } +extern std::string VL_TOLOWER_NN(const std::string& ld); +extern std::string VL_TOUPPER_NN(const std::string& ld); + +extern IData VL_FERROR_IN(IData fpi, std::string& outputr) VL_MT_SAFE; +extern IData VL_FOPEN_NN(const std::string& filename, const std::string& mode) VL_MT_SAFE; +extern IData VL_FOPEN_MCD_N(const std::string& filename) VL_MT_SAFE; +extern void VL_READMEM_N(bool hex, int bits, QData depth, int array_lsb, + const std::string& filename, void* memp, QData start, + QData end) VL_MT_SAFE; +extern void VL_WRITEMEM_N(bool hex, int bits, QData depth, int array_lsb, + const std::string& filename, const void* memp, QData start, + QData end) VL_MT_SAFE; +extern IData VL_SSCANF_INX(int lbits, const std::string& ld, const char* formatp, ...) VL_MT_SAFE; +extern void VL_SFORMAT_X(int obits_ignored, std::string& output, const char* formatp, + ...) VL_MT_SAFE; +extern std::string VL_SFORMATF_NX(const char* formatp, ...) VL_MT_SAFE; +extern void VL_TIMEFORMAT_IINI(int units, int precision, const std::string& suffix, int width, + VerilatedContext* contextp) VL_MT_SAFE; +extern IData VL_VALUEPLUSARGS_INW(int rbits, const std::string& ld, WDataOutP rwp) VL_MT_SAFE; +inline IData VL_VALUEPLUSARGS_INI(int rbits, const std::string& ld, CData& rdr) VL_MT_SAFE { + VlWide<2> rwp; + IData got = VL_VALUEPLUSARGS_INW(rbits, ld, rwp); + if (got) rdr = rwp[0]; + return got; +} +inline IData VL_VALUEPLUSARGS_INI(int rbits, const std::string& ld, SData& rdr) VL_MT_SAFE { + VlWide<2> rwp; + IData got = VL_VALUEPLUSARGS_INW(rbits, ld, rwp); + if (got) rdr = rwp[0]; + return got; +} +inline IData VL_VALUEPLUSARGS_INI(int rbits, const std::string& ld, IData& rdr) VL_MT_SAFE { + VlWide<2> rwp; + IData got = VL_VALUEPLUSARGS_INW(rbits, ld, rwp); + if (got) rdr = rwp[0]; + return got; +} +inline IData VL_VALUEPLUSARGS_INQ(int rbits, const std::string& ld, QData& rdr) VL_MT_SAFE { + VlWide<2> rwp; + IData got = VL_VALUEPLUSARGS_INW(rbits, ld, rwp); + if (got) rdr = VL_SET_QW(rwp); + return got; +} +inline IData VL_VALUEPLUSARGS_INQ(int rbits, const std::string& ld, double& rdr) VL_MT_SAFE { + VlWide<2> rwp; + IData got = VL_VALUEPLUSARGS_INW(rbits, ld, rwp); + if (got) rdr = VL_CVT_D_Q(VL_SET_QW(rwp)); + return got; +} +extern IData VL_VALUEPLUSARGS_INN(int, const std::string& ld, std::string& rdr) VL_MT_SAFE; + +//====================================================================== + +#endif // Guard diff --git a/include/verilated_heavy.h b/include/verilated_heavy.h index 598a99400..80129772f 100644 --- a/include/verilated_heavy.h +++ b/include/verilated_heavy.h @@ -12,966 +12,20 @@ //************************************************************************* /// /// \file -/// \brief Verilated string and data-type header +/// \brief Verilated old string and data-type header /// -/// This file is included automatically by Verilator at the top of -/// all C++ files it generates. It is used when strings or other -/// heavyweight types are required; these contents are not part of -/// verilated.h to save compile time when such types aren't used. +/// This file is deprecated, and provided for backwards compatibility. +/// Include verilated.h instead. /// //************************************************************************* #ifndef VERILATOR_VERILATED_HEAVY_H_ #define VERILATOR_VERILATED_HEAVY_H_ +#ifdef VL_NO_LEGACY +#error "Include instead of " +#endif + #include "verilated.h" -#include -#include -#include -#include -#include -#include -#include -#include - -//=================================================================== -// String formatters (required by below containers) - -extern std::string VL_TO_STRING(CData lhs); -extern std::string VL_TO_STRING(SData lhs); -extern std::string VL_TO_STRING(IData lhs); -extern std::string VL_TO_STRING(QData lhs); -inline std::string VL_TO_STRING(const std::string& obj) { return "\"" + obj + "\""; } -extern std::string VL_TO_STRING_W(int words, const WDataInP obj); - -//=================================================================== -// Shuffle RNG - -class VlURNG final { -public: - using result_type = size_t; - static constexpr size_t min() { return 0; } - static constexpr size_t max() { return 1ULL << 31; } - size_t operator()() { return VL_MASK_I(31) & VL_RANDOM_I(32); } -}; - -//=================================================================== -// Readmem/Writemem operation classes - -class VlReadMem final { - bool m_hex; // Hex format - int m_bits; // Bit width of values - const std::string& m_filename; // Filename - QData m_end; // End address (as specified by user) - FILE* m_fp; // File handle for filename - QData m_addr; // Next address to read - int m_linenum; // Line number last read from file -public: - VlReadMem(bool hex, int bits, const std::string& filename, QData start, QData end); - ~VlReadMem(); - bool isOpen() const { return m_fp != nullptr; } - int linenum() const { return m_linenum; } - bool get(QData& addrr, std::string& valuer); - void setData(void* valuep, const std::string& rhs); -}; - -class VlWriteMem final { - bool m_hex; // Hex format - int m_bits; // Bit width of values - FILE* m_fp; // File handle for filename - QData m_addr; // Next address to write -public: - VlWriteMem(bool hex, int bits, const std::string& filename, QData start, QData end); - ~VlWriteMem(); - bool isOpen() const { return m_fp != nullptr; } - void print(QData addr, bool addrstamp, const void* valuep); -}; - -//=================================================================== -/// Verilog wide packed bit container. -/// Similar to std::array, but lighter weight, only methods needed -/// by Verilator, to help compile time. -/// -/// A 'struct' as we want this to be an aggregate type that allows -/// static aggregate initialization. Consider data members private. -/// -/// For example a Verilog "bit [94:0]" will become a VlWide<3> because 3*32 -/// bits are needed to hold the 95 bits. The MSB (bit 96) must always be -/// zero in memory, but during intermediate operations in the Verilated -/// internals is unpredictable. - -template struct VlWide final { - // MEMBERS - // This should be the only data member, otherwise generated static initializers need updating - EData m_storage[T_Words]; // Contents of the packed array - - // CONSTRUCTORS - // Default constructors and destructor are used. Note however that C++20 requires that - // aggregate types do not have a user declared constructor, not even an explicitly defaulted - // one. - - // OPERATOR METHODS - // Default copy assignment operators are used. - operator WDataOutP() { return &m_storage[0]; } // This also allows [] - operator WDataInP() const { return &m_storage[0]; } // This also allows [] - - // METHODS - const EData& at(size_t index) const { return m_storage[index]; } - EData& at(size_t index) { return m_storage[index]; } - WData* data() { return &m_storage[0]; } - const WData* data() const { return &m_storage[0]; } - bool operator<(const VlWide& rhs) const { - return VL_LT_W(T_Words, data(), rhs.data()); - } -}; - -// Convert a C array to std::array reference by pointer magic, without copy. -// Data type (second argument) is so the function template can automatically generate. -template -VlWide& VL_CVT_W_A(const WDataInP inp, const VlWide&) { - return *((VlWide*)inp); -} - -template std::string VL_TO_STRING(const VlWide& obj) { - return VL_TO_STRING_W(T_Words, obj.data()); -} - -//=================================================================== -// Verilog queue and dynamic array container -// There are no multithreaded locks on this; the base variable must -// be protected by other means -// -// Bound here is the maximum size() allowed, e.g. 1 + SystemVerilog bound -// For dynamic arrays it is always zero -template class VlQueue final { -private: - // TYPES - using Deque = std::deque; - -public: - using const_iterator = typename Deque::const_iterator; - -private: - // MEMBERS - Deque m_deque; // State of the assoc array - T_Value m_defaultValue; // Default value - -public: - // CONSTRUCTORS - // m_defaultValue isn't defaulted. Caller's constructor must do it. - VlQueue() = default; - ~VlQueue() = default; - VlQueue(const VlQueue&) = default; - VlQueue(VlQueue&&) = default; - VlQueue& operator=(const VlQueue&) = default; - VlQueue& operator=(VlQueue&&) = default; - - // Standard copy constructor works. Verilog: assoca = assocb - // Also must allow conversion from a different T_MaxSize queue - template VlQueue operator=(const VlQueue& rhs) { - m_deque = rhs.privateDeque(); - if (VL_UNLIKELY(T_MaxSize && T_MaxSize < m_deque.size())) m_deque.resize(T_MaxSize - 1); - return *this; - } - - static VlQueue cons(const T_Value& lhs) { - VlQueue out; - out.push_back(lhs); - return out; - } - static VlQueue cons(const T_Value& lhs, const T_Value& rhs) { - VlQueue out; - out.push_back(rhs); - out.push_back(lhs); - return out; - } - static VlQueue cons(const VlQueue& lhs, const T_Value& rhs) { - VlQueue out = lhs; - out.push_front(rhs); - return out; - } - static VlQueue cons(const T_Value& lhs, const VlQueue& rhs) { - VlQueue out = rhs; - out.push_back(lhs); - return out; - } - static VlQueue cons(const VlQueue& lhs, const VlQueue& rhs) { - VlQueue out = rhs; - for (const auto& i : lhs.m_deque) out.push_back(i); - return out; - } - - // METHODS - T_Value& atDefault() { return m_defaultValue; } - const T_Value& atDefault() const { return m_defaultValue; } - const Deque& privateDeque() const { return m_deque; } - - // Size. Verilog: function int size(), or int num() - int size() const { return m_deque.size(); } - // Clear array. Verilog: function void delete([input index]) - void clear() { m_deque.clear(); } - void erase(vlsint32_t index) { - if (VL_LIKELY(index >= 0 && index < m_deque.size())) - m_deque.erase(m_deque.begin() + index); - } - - // Dynamic array new[] becomes a renew() - void renew(size_t size) { - clear(); - m_deque.resize(size, atDefault()); - } - // Dynamic array new[]() becomes a renew_copy() - void renew_copy(size_t size, const VlQueue& rhs) { - if (size == 0) { - clear(); - } else { - *this = rhs; - m_deque.resize(size, atDefault()); - } - } - - // function void q.push_front(value) - void push_front(const T_Value& value) { - m_deque.push_front(value); - if (VL_UNLIKELY(T_MaxSize != 0 && m_deque.size() > T_MaxSize)) m_deque.pop_back(); - } - // function void q.push_back(value) - void push_back(const T_Value& value) { - if (VL_LIKELY(T_MaxSize == 0 || m_deque.size() < T_MaxSize)) m_deque.push_back(value); - } - // function value_t q.pop_front(); - T_Value pop_front() { - if (m_deque.empty()) return m_defaultValue; - T_Value v = m_deque.front(); - m_deque.pop_front(); - return v; - } - // function value_t q.pop_back(); - T_Value pop_back() { - if (m_deque.empty()) return m_defaultValue; - T_Value v = m_deque.back(); - m_deque.pop_back(); - return v; - } - - // Setting. Verilog: assoc[index] = v - // Can't just overload operator[] or provide a "at" reference to set, - // because we need to be able to insert only when the value is set - T_Value& at(vlsint32_t index) { - static T_Value s_throwAway; - // Needs to work for dynamic arrays, so does not use T_MaxSize - if (VL_UNLIKELY(index < 0 || index >= m_deque.size())) { - s_throwAway = atDefault(); - return s_throwAway; - } else { - return m_deque[index]; - } - } - // Accessing. Verilog: v = assoc[index] - const T_Value& at(vlsint32_t index) const { - static T_Value s_throwAway; - // Needs to work for dynamic arrays, so does not use T_MaxSize - if (VL_UNLIKELY(index < 0 || index >= m_deque.size())) { - return atDefault(); - } else { - return m_deque[index]; - } - } - // function void q.insert(index, value); - void insert(vlsint32_t index, const T_Value& value) { - if (VL_UNLIKELY(index < 0 || index >= m_deque.size())) return; - m_deque.insert(m_deque.begin() + index, value); - } - - // Return slice q[lsb:msb] - VlQueue slice(vlsint32_t lsb, vlsint32_t msb) const { - VlQueue out; - if (VL_UNLIKELY(lsb < 0)) lsb = 0; - if (VL_UNLIKELY(lsb >= m_deque.size())) lsb = m_deque.size() - 1; - if (VL_UNLIKELY(msb >= m_deque.size())) msb = m_deque.size() - 1; - for (vlsint32_t i = lsb; i <= msb; ++i) out.push_back(m_deque[i]); - return out; - } - - // For save/restore - const_iterator begin() const { return m_deque.begin(); } - const_iterator end() const { return m_deque.end(); } - - // Methods - void sort() { std::sort(m_deque.begin(), m_deque.end()); } - template void sort(Func with_func) { - // with_func returns arbitrary type to use for the sort comparison - std::sort(m_deque.begin(), m_deque.end(), [=](const T_Value& a, const T_Value& b) { - // index number is meaninless with sort, as it changes - return with_func(0, a) < with_func(0, b); - }); - } - void rsort() { std::sort(m_deque.rbegin(), m_deque.rend()); } - template void rsort(Func with_func) { - // with_func returns arbitrary type to use for the sort comparison - std::sort(m_deque.rbegin(), m_deque.rend(), [=](const T_Value& a, const T_Value& b) { - // index number is meaninless with sort, as it changes - return with_func(0, a) < with_func(0, b); - }); - } - void reverse() { std::reverse(m_deque.begin(), m_deque.end()); } - void shuffle() { std::shuffle(m_deque.begin(), m_deque.end(), VlURNG{}); } - VlQueue unique() const { - VlQueue out; - std::unordered_set saw; - for (const auto& i : m_deque) { - auto it = saw.find(i); - if (it == saw.end()) { - saw.insert(it, i); - out.push_back(i); - } - } - return out; - } - VlQueue unique_index() const { - VlQueue out; - IData index = 0; - std::unordered_set saw; - for (const auto& i : m_deque) { - auto it = saw.find(i); - if (it == saw.end()) { - saw.insert(it, i); - out.push_back(index); - } - ++index; - } - return out; - } - template VlQueue find(Func with_func) const { - VlQueue out; - IData index = 0; - for (const auto& i : m_deque) { - if (with_func(index, i)) out.push_back(i); - ++index; - } - return out; - } - template VlQueue find_index(Func with_func) const { - VlQueue out; - IData index = 0; - for (const auto& i : m_deque) { - if (with_func(index, i)) out.push_back(index); - ++index; - } - return out; - } - template VlQueue find_first(Func with_func) const { - // Can't use std::find_if as need index number - IData index = 0; - for (const auto& i : m_deque) { - if (with_func(index, i)) return VlQueue::cons(i); - ++index; - } - return VlQueue{}; - } - template VlQueue find_first_index(Func with_func) const { - IData index = 0; - for (const auto& i : m_deque) { - if (with_func(index, i)) return VlQueue::cons(index); - ++index; - } - return VlQueue{}; - } - template VlQueue find_last(Func with_func) const { - IData index = m_deque.size() - 1; - for (auto it = m_deque.rbegin(); it != m_deque.rend(); ++it) { - if (with_func(index, *it)) return VlQueue::cons(*it); - --index; - } - return VlQueue{}; - } - template VlQueue find_last_index(Func with_func) const { - IData index = m_deque.size() - 1; - for (auto it = m_deque.rbegin(); it != m_deque.rend(); ++it) { - if (with_func(index, *it)) return VlQueue::cons(index); - --index; - } - return VlQueue{}; - } - - // Reduction operators - VlQueue min() const { - if (m_deque.empty()) return VlQueue{}; - const auto it = std::min_element(m_deque.begin(), m_deque.end()); - return VlQueue::cons(*it); - } - VlQueue max() const { - if (m_deque.empty()) return VlQueue{}; - const auto it = std::max_element(m_deque.begin(), m_deque.end()); - return VlQueue::cons(*it); - } - - T_Value r_sum() const { - T_Value out(0); // Type must have assignment operator - for (const auto& i : m_deque) out += i; - return out; - } - template T_Value r_sum(Func with_func) const { - T_Value out(0); // Type must have assignment operator - IData index = 0; - for (const auto& i : m_deque) out += with_func(index++, i); - return out; - } - T_Value r_product() const { - if (m_deque.empty()) return T_Value(0); - auto it = m_deque.begin(); - T_Value out{*it}; - ++it; - for (; it != m_deque.end(); ++it) out *= *it; - return out; - } - template T_Value r_product(Func with_func) const { - if (m_deque.empty()) return T_Value(0); - auto it = m_deque.begin(); - IData index = 0; - T_Value out{with_func(index, *it)}; - ++it; - ++index; - for (; it != m_deque.end(); ++it) out *= with_func(index++, *it); - return out; - } - T_Value r_and() const { - if (m_deque.empty()) return T_Value(0); - auto it = m_deque.begin(); - T_Value out{*it}; - ++it; - for (; it != m_deque.end(); ++it) out &= *it; - return out; - } - template T_Value r_and(Func with_func) const { - if (m_deque.empty()) return T_Value(0); - auto it = m_deque.begin(); - IData index = 0; - T_Value out{with_func(index, *it)}; - ++it; - ++index; - for (; it != m_deque.end(); ++it) out &= with_func(index, *it); - return out; - } - T_Value r_or() const { - T_Value out(0); // Type must have assignment operator - for (const auto& i : m_deque) out |= i; - return out; - } - template T_Value r_or(Func with_func) const { - T_Value out(0); // Type must have assignment operator - IData index = 0; - for (const auto& i : m_deque) out |= with_func(index++, i); - return out; - } - T_Value r_xor() const { - T_Value out(0); // Type must have assignment operator - for (const auto& i : m_deque) out ^= i; - return out; - } - template T_Value r_xor(Func with_func) const { - T_Value out(0); // Type must have assignment operator - IData index = 0; - for (const auto& i : m_deque) out ^= with_func(index++, i); - return out; - } - - // Dumping. Verilog: str = $sformatf("%p", assoc) - std::string to_string() const { - if (m_deque.empty()) return "'{}"; // No trailing space - std::string out = "'{"; - std::string comma; - for (const auto& i : m_deque) { - out += comma + VL_TO_STRING(i); - comma = ", "; - } - return out + "} "; - } -}; - -template std::string VL_TO_STRING(const VlQueue& obj) { - return obj.to_string(); -} - -//=================================================================== -// Verilog associative array container -// There are no multithreaded locks on this; the base variable must -// be protected by other means -// -template class VlAssocArray final { -private: - // TYPES - using Map = std::map; - -public: - using const_iterator = typename Map::const_iterator; - -private: - // MEMBERS - Map m_map; // State of the assoc array - T_Value m_defaultValue; // Default value - -public: - // CONSTRUCTORS - // m_defaultValue isn't defaulted. Caller's constructor must do it. - VlAssocArray() = default; - ~VlAssocArray() = default; - VlAssocArray(const VlAssocArray&) = default; - VlAssocArray(VlAssocArray&&) = default; - VlAssocArray& operator=(const VlAssocArray&) = default; - VlAssocArray& operator=(VlAssocArray&&) = default; - - // METHODS - T_Value& atDefault() { return m_defaultValue; } - const T_Value& atDefault() const { return m_defaultValue; } - - // Size of array. Verilog: function int size(), or int num() - int size() const { return m_map.size(); } - // Clear array. Verilog: function void delete([input index]) - void clear() { m_map.clear(); } - void erase(const T_Key& index) { m_map.erase(index); } - // Return 0/1 if element exists. Verilog: function int exists(input index) - int exists(const T_Key& index) const { return m_map.find(index) != m_map.end(); } - // Return first element. Verilog: function int first(ref index); - int first(T_Key& indexr) const { - const auto it = m_map.cbegin(); - if (it == m_map.end()) return 0; - indexr = it->first; - return 1; - } - // Return last element. Verilog: function int last(ref index) - int last(T_Key& indexr) const { - const auto it = m_map.crbegin(); - if (it == m_map.rend()) return 0; - indexr = it->first; - return 1; - } - // Return next element. Verilog: function int next(ref index) - int next(T_Key& indexr) const { - auto it = m_map.find(indexr); - if (VL_UNLIKELY(it == m_map.end())) return 0; - ++it; - if (VL_UNLIKELY(it == m_map.end())) return 0; - indexr = it->first; - return 1; - } - // Return prev element. Verilog: function int prev(ref index) - int prev(T_Key& indexr) const { - auto it = m_map.find(indexr); - if (VL_UNLIKELY(it == m_map.end())) return 0; - if (VL_UNLIKELY(it == m_map.begin())) return 0; - --it; - indexr = it->first; - return 1; - } - // Setting. Verilog: assoc[index] = v - // Can't just overload operator[] or provide a "at" reference to set, - // because we need to be able to insert only when the value is set - T_Value& at(const T_Key& index) { - const auto it = m_map.find(index); - if (it == m_map.end()) { - std::pair pit = m_map.emplace(index, m_defaultValue); - return pit.first->second; - } - return it->second; - } - // Accessing. Verilog: v = assoc[index] - const T_Value& at(const T_Key& index) const { - const auto it = m_map.find(index); - if (it == m_map.end()) { - return m_defaultValue; - } else { - return it->second; - } - } - // Setting as a chained operation - VlAssocArray& set(const T_Key& index, const T_Value& value) { - at(index) = value; - return *this; - } - VlAssocArray& setDefault(const T_Value& value) { - atDefault() = value; - return *this; - } - - // For save/restore - const_iterator begin() const { return m_map.begin(); } - const_iterator end() const { return m_map.end(); } - - // Methods - VlQueue unique() const { - VlQueue out; - std::set saw; - for (const auto& i : m_map) { - auto it = saw.find(i.second); - if (it == saw.end()) { - saw.insert(it, i.second); - out.push_back(i.second); - } - } - return out; - } - VlQueue unique_index() const { - VlQueue out; - std::set saw; - for (const auto& i : m_map) { - auto it = saw.find(i.second); - if (it == saw.end()) { - saw.insert(it, i.second); - out.push_back(i.first); - } - } - return out; - } - template VlQueue find(Func with_func) const { - VlQueue out; - for (const auto& i : m_map) - if (with_func(i.first, i.second)) out.push_back(i.second); - return out; - } - template VlQueue find_index(Func with_func) const { - VlQueue out; - for (const auto& i : m_map) - if (with_func(i.first, i.second)) out.push_back(i.first); - return out; - } - template VlQueue find_first(Func with_func) const { - const auto it - = std::find_if(m_map.begin(), m_map.end(), [=](const std::pair& i) { - return with_func(i.first, i.second); - }); - if (it == m_map.end()) return VlQueue{}; - return VlQueue::cons(it->second); - } - template VlQueue find_first_index(Func with_func) const { - const auto it - = std::find_if(m_map.begin(), m_map.end(), [=](const std::pair& i) { - return with_func(i.first, i.second); - }); - if (it == m_map.end()) return VlQueue{}; - return VlQueue::cons(it->first); - } - template VlQueue find_last(Func with_func) const { - const auto it - = std::find_if(m_map.rbegin(), m_map.rend(), [=](const std::pair& i) { - return with_func(i.first, i.second); - }); - if (it == m_map.rend()) return VlQueue{}; - return VlQueue::cons(it->second); - } - template VlQueue find_last_index(Func with_func) const { - const auto it - = std::find_if(m_map.rbegin(), m_map.rend(), [=](const std::pair& i) { - return with_func(i.first, i.second); - }); - if (it == m_map.rend()) return VlQueue{}; - return VlQueue::cons(it->first); - } - - // Reduction operators - VlQueue min() const { - if (m_map.empty()) return VlQueue(); - const auto it = std::min_element( - m_map.begin(), m_map.end(), - [](const std::pair& a, const std::pair& b) { - return a.second < b.second; - }); - return VlQueue::cons(it->second); - } - VlQueue max() const { - if (m_map.empty()) return VlQueue(); - const auto it = std::max_element( - m_map.begin(), m_map.end(), - [](const std::pair& a, const std::pair& b) { - return a.second < b.second; - }); - return VlQueue::cons(it->second); - } - - T_Value r_sum() const { - T_Value out(0); // Type must have assignment operator - for (const auto& i : m_map) out += i.second; - return out; - } - template T_Value r_sum(Func with_func) const { - T_Value out(0); // Type must have assignment operator - for (const auto& i : m_map) out += with_func(i.first, i.second); - return out; - } - T_Value r_product() const { - if (m_map.empty()) return T_Value(0); - auto it = m_map.begin(); - T_Value out{it->second}; - ++it; - for (; it != m_map.end(); ++it) out *= it->second; - return out; - } - template T_Value r_product(Func with_func) const { - if (m_map.empty()) return T_Value(0); - auto it = m_map.begin(); - T_Value out{with_func(it->first, it->second)}; - ++it; - for (; it != m_map.end(); ++it) out *= with_func(it->first, it->second); - return out; - } - T_Value r_and() const { - if (m_map.empty()) return T_Value(0); - auto it = m_map.begin(); - T_Value out{it->second}; - ++it; - for (; it != m_map.end(); ++it) out &= it->second; - return out; - } - template T_Value r_and(Func with_func) const { - if (m_map.empty()) return T_Value(0); - auto it = m_map.begin(); - T_Value out{with_func(it->first, it->second)}; - ++it; - for (; it != m_map.end(); ++it) out &= with_func(it->first, it->second); - return out; - } - T_Value r_or() const { - T_Value out(0); // Type must have assignment operator - for (const auto& i : m_map) out |= i.second; - return out; - } - template T_Value r_or(Func with_func) const { - T_Value out(0); // Type must have assignment operator - for (const auto& i : m_map) out |= with_func(i.first, i.second); - return out; - } - T_Value r_xor() const { - T_Value out(0); // Type must have assignment operator - for (const auto& i : m_map) out ^= i.second; - return out; - } - template T_Value r_xor(Func with_func) const { - T_Value out(0); // Type must have assignment operator - for (const auto& i : m_map) out ^= with_func(i.first, i.second); - return out; - } - - // Dumping. Verilog: str = $sformatf("%p", assoc) - std::string to_string() const { - if (m_map.empty()) return "'{}"; // No trailing space - std::string out = "'{"; - std::string comma; - for (const auto& i : m_map) { - out += comma + VL_TO_STRING(i.first) + ":" + VL_TO_STRING(i.second); - comma = ", "; - } - // Default not printed - maybe random init data - return out + "} "; - } -}; - -template -std::string VL_TO_STRING(const VlAssocArray& obj) { - return obj.to_string(); -} - -template -void VL_READMEM_N(bool hex, int bits, const std::string& filename, - VlAssocArray& obj, QData start, QData end) VL_MT_SAFE { - VlReadMem rmem{hex, bits, filename, start, end}; - if (VL_UNLIKELY(!rmem.isOpen())) return; - while (true) { - QData addr; - std::string data; - if (rmem.get(addr /*ref*/, data /*ref*/)) { - rmem.setData(&(obj.at(addr)), data); - } else { - break; - } - } -} - -template -void VL_WRITEMEM_N(bool hex, int bits, const std::string& filename, - const VlAssocArray& obj, QData start, QData end) VL_MT_SAFE { - VlWriteMem wmem{hex, bits, filename, start, end}; - if (VL_UNLIKELY(!wmem.isOpen())) return; - for (const auto& i : obj) { - const QData addr = i.first; - if (addr >= start && addr <= end) wmem.print(addr, true, &(i.second)); - } -} - -//=================================================================== -/// Verilog unpacked array container -/// For when a standard C++[] array is not sufficient, e.g. an -/// array under a queue, or methods operating on the array. -/// -/// A 'struct' as we want this to be an aggregate type that allows -/// static aggregate initialization. Consider data members private. -/// -/// This class may get exposed to a Verilated Model's top I/O, if the top -/// IO has an unpacked array. - -template struct VlUnpacked final { - // MEMBERS - // This should be the only data member, otherwise generated static initializers need updating - T_Value m_storage[T_Depth]; // Contents of the unpacked array - - // CONSTRUCTORS - // Default constructors and destructor are used. Note however that C++20 requires that - // aggregate types do not have a user declared constructor, not even an explicitly defaulted - // one. - - // OPERATOR METHODS - // Default copy assignment operators are used. - - // METHODS - // Raw access - WData* data() { return &m_storage[0]; } - const WData* data() const { return &m_storage[0]; } - - T_Value& operator[](size_t index) { return m_storage[index]; }; - const T_Value& operator[](size_t index) const { return m_storage[index]; }; - - // Dumping. Verilog: str = $sformatf("%p", assoc) - std::string to_string() const { - std::string out = "'{"; - std::string comma; - for (int i = 0; i < T_Depth; ++i) { - out += comma + VL_TO_STRING(m_storage[i]); - comma = ", "; - } - return out + "} "; - } -}; - -template -std::string VL_TO_STRING(const VlUnpacked& obj) { - return obj.to_string(); -} - -//=================================================================== -// Verilog class reference container -// There are no multithreaded locks on this; the base variable must -// be protected by other means - -#define VlClassRef std::shared_ptr - -template // T typically of type VlClassRef -inline T VL_NULL_CHECK(T t, const char* filename, int linenum) { - if (VL_UNLIKELY(!t)) Verilated::nullPointerError(filename, linenum); - return t; -} - -template -static inline bool VL_CAST_DYNAMIC(VlClassRef in, VlClassRef& outr) { - VlClassRef casted = std::dynamic_pointer_cast(in); - if (VL_LIKELY(casted)) { - outr = casted; - return true; - } else { - return false; - } -} - -//====================================================================== -// Conversion functions - -extern std::string VL_CVT_PACK_STR_NW(int lwords, const WDataInP lwp) VL_MT_SAFE; -inline std::string VL_CVT_PACK_STR_NQ(QData lhs) VL_PURE { - VlWide lw; - VL_SET_WQ(lw, lhs); - return VL_CVT_PACK_STR_NW(VL_WQ_WORDS_E, lw); -} -inline std::string VL_CVT_PACK_STR_NN(const std::string& lhs) VL_PURE { return lhs; } -inline std::string& VL_CVT_PACK_STR_NN(std::string& lhs) VL_PURE { return lhs; } -inline std::string VL_CVT_PACK_STR_NI(IData lhs) VL_PURE { - VlWide lw; - VL_SET_WI(lw, lhs); - return VL_CVT_PACK_STR_NW(1, lw); -} -inline std::string VL_CONCATN_NNN(const std::string& lhs, const std::string& rhs) VL_PURE { - return lhs + rhs; -} -inline std::string VL_REPLICATEN_NNQ(int, int, int, const std::string& lhs, IData rep) VL_PURE { - std::string out; - out.reserve(lhs.length() * rep); - for (unsigned times = 0; times < rep; ++times) out += lhs; - return out; -} -inline std::string VL_REPLICATEN_NNI(int obits, int lbits, int rbits, const std::string& lhs, - IData rep) VL_PURE { - return VL_REPLICATEN_NNQ(obits, lbits, rbits, lhs, rep); -} - -inline IData VL_LEN_IN(const std::string& ld) { return ld.length(); } -extern std::string VL_TOLOWER_NN(const std::string& ld); -extern std::string VL_TOUPPER_NN(const std::string& ld); - -extern IData VL_FERROR_IN(IData fpi, std::string& outputr) VL_MT_SAFE; -extern IData VL_FOPEN_NN(const std::string& filename, const std::string& mode) VL_MT_SAFE; -extern IData VL_FOPEN_MCD_N(const std::string& filename) VL_MT_SAFE; -extern void VL_READMEM_N(bool hex, int bits, QData depth, int array_lsb, - const std::string& filename, void* memp, QData start, - QData end) VL_MT_SAFE; -extern void VL_WRITEMEM_N(bool hex, int bits, QData depth, int array_lsb, - const std::string& filename, const void* memp, QData start, - QData end) VL_MT_SAFE; -extern IData VL_SSCANF_INX(int lbits, const std::string& ld, const char* formatp, ...) VL_MT_SAFE; -extern void VL_SFORMAT_X(int obits_ignored, std::string& output, const char* formatp, - ...) VL_MT_SAFE; -extern std::string VL_SFORMATF_NX(const char* formatp, ...) VL_MT_SAFE; -extern void VL_TIMEFORMAT_IINI(int units, int precision, const std::string& suffix, int width, - VerilatedContext* contextp) VL_MT_SAFE; -extern IData VL_VALUEPLUSARGS_INW(int rbits, const std::string& ld, WDataOutP rwp) VL_MT_SAFE; -inline IData VL_VALUEPLUSARGS_INI(int rbits, const std::string& ld, CData& rdr) VL_MT_SAFE { - VlWide<2> rwp; // WData must always be at least 2 - IData got = VL_VALUEPLUSARGS_INW(rbits, ld, rwp); - if (got) rdr = rwp[0]; - return got; -} -inline IData VL_VALUEPLUSARGS_INI(int rbits, const std::string& ld, SData& rdr) VL_MT_SAFE { - VlWide<2> rwp; // WData must always be at least 2 - IData got = VL_VALUEPLUSARGS_INW(rbits, ld, rwp); - if (got) rdr = rwp[0]; - return got; -} -inline IData VL_VALUEPLUSARGS_INI(int rbits, const std::string& ld, IData& rdr) VL_MT_SAFE { - VlWide<2> rwp; - IData got = VL_VALUEPLUSARGS_INW(rbits, ld, rwp); - if (got) rdr = rwp[0]; - return got; -} -inline IData VL_VALUEPLUSARGS_INQ(int rbits, const std::string& ld, QData& rdr) VL_MT_SAFE { - VlWide<2> rwp; - IData got = VL_VALUEPLUSARGS_INW(rbits, ld, rwp); - if (got) rdr = VL_SET_QW(rwp); - return got; -} -inline IData VL_VALUEPLUSARGS_INQ(int rbits, const std::string& ld, double& rdr) VL_MT_SAFE { - VlWide<2> rwp; - IData got = VL_VALUEPLUSARGS_INW(rbits, ld, rwp); - if (got) rdr = VL_CVT_D_Q(VL_SET_QW(rwp)); - return got; -} -extern IData VL_VALUEPLUSARGS_INN(int, const std::string& ld, std::string& rdr) VL_MT_SAFE; - -//====================================================================== -// Strings - -extern std::string VL_PUTC_N(const std::string& lhs, IData rhs, CData ths) VL_PURE; -extern CData VL_GETC_N(const std::string& lhs, IData rhs) VL_PURE; -extern std::string VL_SUBSTR_N(const std::string& lhs, IData rhs, IData ths) VL_PURE; - -inline IData VL_CMP_NN(const std::string& lhs, const std::string& rhs, bool ignoreCase) VL_PURE { - // SystemVerilog does not allow a string variable to contain '\0'. - // So C functions such as strcmp() can correctly compare strings. - if (ignoreCase) { - return VL_STRCASECMP(lhs.c_str(), rhs.c_str()); - } else { - return std::strcmp(lhs.c_str(), rhs.c_str()); - } -} - -extern IData VL_ATOI_N(const std::string& str, int base) VL_PURE; - -extern IData VL_FGETS_NI(std::string& dest, IData fpi); - #endif // Guard diff --git a/include/verilated_imp.h b/include/verilated_imp.h index fef103f7b..ed17f86e2 100644 --- a/include/verilated_imp.h +++ b/include/verilated_imp.h @@ -30,7 +30,6 @@ #include "verilatedos.h" #include "verilated.h" -#include "verilated_heavy.h" #include "verilated_syms.h" #include diff --git a/include/verilated_save.h b/include/verilated_save.h index 1a6c14e13..f49c15f91 100644 --- a/include/verilated_save.h +++ b/include/verilated_save.h @@ -23,7 +23,7 @@ #define VERILATOR_VERILATED_SAVE_C_H_ #include "verilatedos.h" -#include "verilated_heavy.h" +#include "verilated.h" #include diff --git a/include/verilated_syms.h b/include/verilated_syms.h index 1dc633e6e..160bd31c9 100644 --- a/include/verilated_syms.h +++ b/include/verilated_syms.h @@ -30,7 +30,7 @@ #define VERILATOR_VERILATED_SYMS_H_ #include "verilatedos.h" -#include "verilated_heavy.h" +#include "verilated.h" #include "verilated_sym_props.h" #include diff --git a/include/verilated_types.h b/include/verilated_types.h new file mode 100644 index 000000000..8a0dde96d --- /dev/null +++ b/include/verilated_types.h @@ -0,0 +1,897 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// +// Code available from: https://verilator.org +// +// Copyright 2003-2021 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 +// +//************************************************************************* +/// +/// \file +/// \brief Verilated common data type containers +/// +/// verilated.h should be included instead of this file. +/// +/// Those macro/function/variable starting or ending in _ are internal, +/// however many of the other function/macros here are also internal. +/// +//************************************************************************* + +#ifndef VERILATOR_VERILATED_TYPES_H_ +#define VERILATOR_VERILATED_TYPES_H_ + +#ifndef VERILATOR_VERILATED_H_INTERNAL_ +#error "verilated_types.h should only be included by verilated.h" +#endif + +//=================================================================== +// String formatters (required by below containers) + +extern std::string VL_TO_STRING(CData lhs); +extern std::string VL_TO_STRING(SData lhs); +extern std::string VL_TO_STRING(IData lhs); +extern std::string VL_TO_STRING(QData lhs); +inline std::string VL_TO_STRING(const std::string& obj) { return "\"" + obj + "\""; } +extern std::string VL_TO_STRING_W(int words, const WDataInP obj); + +//========================================================================= +// Declare net data types + +#define VL_SIG8(name, msb, lsb) CData name ///< Declare signal, 1-8 bits +#define VL_SIG16(name, msb, lsb) SData name ///< Declare signal, 9-16 bits +#define VL_SIG64(name, msb, lsb) QData name ///< Declare signal, 33-64 bits +#define VL_SIG(name, msb, lsb) IData name ///< Declare signal, 17-32 bits +#define VL_SIGW(name, msb, lsb, words) WData name[words] ///< Declare signal, 65+ bits +#define VL_IN8(name, msb, lsb) CData name ///< Declare input signal, 1-8 bits +#define VL_IN16(name, msb, lsb) SData name ///< Declare input signal, 9-16 bits +#define VL_IN64(name, msb, lsb) QData name ///< Declare input signal, 33-64 bits +#define VL_IN(name, msb, lsb) IData name ///< Declare input signal, 17-32 bits +#define VL_INW(name, msb, lsb, words) WData name[words] ///< Declare input signal, 65+ bits +#define VL_INOUT8(name, msb, lsb) CData name ///< Declare bidir signal, 1-8 bits +#define VL_INOUT16(name, msb, lsb) SData name ///< Declare bidir signal, 9-16 bits +#define VL_INOUT64(name, msb, lsb) QData name ///< Declare bidir signal, 33-64 bits +#define VL_INOUT(name, msb, lsb) IData name ///< Declare bidir signal, 17-32 bits +#define VL_INOUTW(name, msb, lsb, words) WData name[words] ///< Declare bidir signal, 65+ bits +#define VL_OUT8(name, msb, lsb) CData name ///< Declare output signal, 1-8 bits +#define VL_OUT16(name, msb, lsb) SData name ///< Declare output signal, 9-16 bits +#define VL_OUT64(name, msb, lsb) QData name ///< Declare output signal, 33-64bits +#define VL_OUT(name, msb, lsb) IData name ///< Declare output signal, 17-32 bits +#define VL_OUTW(name, msb, lsb, words) WData name[words] ///< Declare output signal, 65+ bits + +//=================================================================== +// Shuffle RNG + +extern vluint64_t vl_rand64() VL_MT_SAFE; + +class VlURNG final { +public: + using result_type = size_t; + static constexpr size_t min() { return 0; } + static constexpr size_t max() { return 1ULL << 31; } + size_t operator()() { return VL_MASK_I(31) & vl_rand64(); } +}; + +//=================================================================== +// Readmem/Writemem operation classes + +class VlReadMem final { + bool m_hex; // Hex format + int m_bits; // Bit width of values + const std::string& m_filename; // Filename + QData m_end; // End address (as specified by user) + FILE* m_fp; // File handle for filename + QData m_addr; // Next address to read + int m_linenum; // Line number last read from file +public: + VlReadMem(bool hex, int bits, const std::string& filename, QData start, QData end); + ~VlReadMem(); + bool isOpen() const { return m_fp != nullptr; } + int linenum() const { return m_linenum; } + bool get(QData& addrr, std::string& valuer); + void setData(void* valuep, const std::string& rhs); +}; + +class VlWriteMem final { + bool m_hex; // Hex format + int m_bits; // Bit width of values + FILE* m_fp; // File handle for filename + QData m_addr; // Next address to write +public: + VlWriteMem(bool hex, int bits, const std::string& filename, QData start, QData end); + ~VlWriteMem(); + bool isOpen() const { return m_fp != nullptr; } + void print(QData addr, bool addrstamp, const void* valuep); +}; + +//=================================================================== +/// Verilog wide packed bit container. +/// Similar to std::array, but lighter weight, only methods needed +/// by Verilator, to help compile time. +/// +/// A 'struct' as we want this to be an aggregate type that allows +/// static aggregate initialization. Consider data members private. +/// +/// For example a Verilog "bit [94:0]" will become a VlWide<3> because 3*32 +/// bits are needed to hold the 95 bits. The MSB (bit 96) must always be +/// zero in memory, but during intermediate operations in the Verilated +/// internals is unpredictable. + +static int _vl_cmp_w(int words, WDataInP const lwp, WDataInP const rwp) VL_MT_SAFE; + +template struct VlWide final { + // MEMBERS + // This should be the only data member, otherwise generated static initializers need updating + EData m_storage[T_Words]; // Contents of the packed array + + // CONSTRUCTORS + // Default constructors and destructor are used. Note however that C++20 requires that + // aggregate types do not have a user declared constructor, not even an explicitly defaulted + // one. + + // OPERATOR METHODS + // Default copy assignment operators are used. + operator WDataOutP() { return &m_storage[0]; } // This also allows [] + operator WDataInP() const { return &m_storage[0]; } // This also allows [] + + // METHODS + const EData& at(size_t index) const { return m_storage[index]; } + EData& at(size_t index) { return m_storage[index]; } + WData* data() { return &m_storage[0]; } + const WData* data() const { return &m_storage[0]; } + bool operator<(const VlWide& rhs) const { + return _vl_cmp_w(T_Words, data(), rhs.data()) < 0; + } +}; + +// Convert a C array to std::array reference by pointer magic, without copy. +// Data type (second argument) is so the function template can automatically generate. +template +VlWide& VL_CVT_W_A(const WDataInP inp, const VlWide&) { + return *((VlWide*)inp); +} + +template std::string VL_TO_STRING(const VlWide& obj) { + return VL_TO_STRING_W(T_Words, obj.data()); +} + +//=================================================================== +// Verilog queue and dynamic array container +// There are no multithreaded locks on this; the base variable must +// be protected by other means +// +// Bound here is the maximum size() allowed, e.g. 1 + SystemVerilog bound +// For dynamic arrays it is always zero +template class VlQueue final { +private: + // TYPES + using Deque = std::deque; + +public: + using const_iterator = typename Deque::const_iterator; + +private: + // MEMBERS + Deque m_deque; // State of the assoc array + T_Value m_defaultValue; // Default value + +public: + // CONSTRUCTORS + // m_defaultValue isn't defaulted. Caller's constructor must do it. + VlQueue() = default; + ~VlQueue() = default; + VlQueue(const VlQueue&) = default; + VlQueue(VlQueue&&) = default; + VlQueue& operator=(const VlQueue&) = default; + VlQueue& operator=(VlQueue&&) = default; + + // Standard copy constructor works. Verilog: assoca = assocb + // Also must allow conversion from a different T_MaxSize queue + template VlQueue operator=(const VlQueue& rhs) { + m_deque = rhs.privateDeque(); + if (VL_UNLIKELY(T_MaxSize && T_MaxSize < m_deque.size())) m_deque.resize(T_MaxSize - 1); + return *this; + } + + static VlQueue cons(const T_Value& lhs) { + VlQueue out; + out.push_back(lhs); + return out; + } + static VlQueue cons(const T_Value& lhs, const T_Value& rhs) { + VlQueue out; + out.push_back(rhs); + out.push_back(lhs); + return out; + } + static VlQueue cons(const VlQueue& lhs, const T_Value& rhs) { + VlQueue out = lhs; + out.push_front(rhs); + return out; + } + static VlQueue cons(const T_Value& lhs, const VlQueue& rhs) { + VlQueue out = rhs; + out.push_back(lhs); + return out; + } + static VlQueue cons(const VlQueue& lhs, const VlQueue& rhs) { + VlQueue out = rhs; + for (const auto& i : lhs.m_deque) out.push_back(i); + return out; + } + + // METHODS + T_Value& atDefault() { return m_defaultValue; } + const T_Value& atDefault() const { return m_defaultValue; } + const Deque& privateDeque() const { return m_deque; } + + // Size. Verilog: function int size(), or int num() + int size() const { return m_deque.size(); } + // Clear array. Verilog: function void delete([input index]) + void clear() { m_deque.clear(); } + void erase(vlsint32_t index) { + if (VL_LIKELY(index >= 0 && index < m_deque.size())) + m_deque.erase(m_deque.begin() + index); + } + + // Dynamic array new[] becomes a renew() + void renew(size_t size) { + clear(); + m_deque.resize(size, atDefault()); + } + // Dynamic array new[]() becomes a renew_copy() + void renew_copy(size_t size, const VlQueue& rhs) { + if (size == 0) { + clear(); + } else { + *this = rhs; + m_deque.resize(size, atDefault()); + } + } + + // function void q.push_front(value) + void push_front(const T_Value& value) { + m_deque.push_front(value); + if (VL_UNLIKELY(T_MaxSize != 0 && m_deque.size() > T_MaxSize)) m_deque.pop_back(); + } + // function void q.push_back(value) + void push_back(const T_Value& value) { + if (VL_LIKELY(T_MaxSize == 0 || m_deque.size() < T_MaxSize)) m_deque.push_back(value); + } + // function value_t q.pop_front(); + T_Value pop_front() { + if (m_deque.empty()) return m_defaultValue; + T_Value v = m_deque.front(); + m_deque.pop_front(); + return v; + } + // function value_t q.pop_back(); + T_Value pop_back() { + if (m_deque.empty()) return m_defaultValue; + T_Value v = m_deque.back(); + m_deque.pop_back(); + return v; + } + + // Setting. Verilog: assoc[index] = v + // Can't just overload operator[] or provide a "at" reference to set, + // because we need to be able to insert only when the value is set + T_Value& at(vlsint32_t index) { + static T_Value s_throwAway; + // Needs to work for dynamic arrays, so does not use T_MaxSize + if (VL_UNLIKELY(index < 0 || index >= m_deque.size())) { + s_throwAway = atDefault(); + return s_throwAway; + } else { + return m_deque[index]; + } + } + // Accessing. Verilog: v = assoc[index] + const T_Value& at(vlsint32_t index) const { + static T_Value s_throwAway; + // Needs to work for dynamic arrays, so does not use T_MaxSize + if (VL_UNLIKELY(index < 0 || index >= m_deque.size())) { + return atDefault(); + } else { + return m_deque[index]; + } + } + // function void q.insert(index, value); + void insert(vlsint32_t index, const T_Value& value) { + if (VL_UNLIKELY(index < 0 || index >= m_deque.size())) return; + m_deque.insert(m_deque.begin() + index, value); + } + + // Return slice q[lsb:msb] + VlQueue slice(vlsint32_t lsb, vlsint32_t msb) const { + VlQueue out; + if (VL_UNLIKELY(lsb < 0)) lsb = 0; + if (VL_UNLIKELY(lsb >= m_deque.size())) lsb = m_deque.size() - 1; + if (VL_UNLIKELY(msb >= m_deque.size())) msb = m_deque.size() - 1; + for (vlsint32_t i = lsb; i <= msb; ++i) out.push_back(m_deque[i]); + return out; + } + + // For save/restore + const_iterator begin() const { return m_deque.begin(); } + const_iterator end() const { return m_deque.end(); } + + // Methods + void sort() { std::sort(m_deque.begin(), m_deque.end()); } + template void sort(Func with_func) { + // with_func returns arbitrary type to use for the sort comparison + std::sort(m_deque.begin(), m_deque.end(), [=](const T_Value& a, const T_Value& b) { + // index number is meaninless with sort, as it changes + return with_func(0, a) < with_func(0, b); + }); + } + void rsort() { std::sort(m_deque.rbegin(), m_deque.rend()); } + template void rsort(Func with_func) { + // with_func returns arbitrary type to use for the sort comparison + std::sort(m_deque.rbegin(), m_deque.rend(), [=](const T_Value& a, const T_Value& b) { + // index number is meaninless with sort, as it changes + return with_func(0, a) < with_func(0, b); + }); + } + void reverse() { std::reverse(m_deque.begin(), m_deque.end()); } + void shuffle() { std::shuffle(m_deque.begin(), m_deque.end(), VlURNG{}); } + VlQueue unique() const { + VlQueue out; + std::unordered_set saw; + for (const auto& i : m_deque) { + auto it = saw.find(i); + if (it == saw.end()) { + saw.insert(it, i); + out.push_back(i); + } + } + return out; + } + VlQueue unique_index() const { + VlQueue out; + IData index = 0; + std::unordered_set saw; + for (const auto& i : m_deque) { + auto it = saw.find(i); + if (it == saw.end()) { + saw.insert(it, i); + out.push_back(index); + } + ++index; + } + return out; + } + template VlQueue find(Func with_func) const { + VlQueue out; + IData index = 0; + for (const auto& i : m_deque) { + if (with_func(index, i)) out.push_back(i); + ++index; + } + return out; + } + template VlQueue find_index(Func with_func) const { + VlQueue out; + IData index = 0; + for (const auto& i : m_deque) { + if (with_func(index, i)) out.push_back(index); + ++index; + } + return out; + } + template VlQueue find_first(Func with_func) const { + // Can't use std::find_if as need index number + IData index = 0; + for (const auto& i : m_deque) { + if (with_func(index, i)) return VlQueue::cons(i); + ++index; + } + return VlQueue{}; + } + template VlQueue find_first_index(Func with_func) const { + IData index = 0; + for (const auto& i : m_deque) { + if (with_func(index, i)) return VlQueue::cons(index); + ++index; + } + return VlQueue{}; + } + template VlQueue find_last(Func with_func) const { + IData index = m_deque.size() - 1; + for (auto it = m_deque.rbegin(); it != m_deque.rend(); ++it) { + if (with_func(index, *it)) return VlQueue::cons(*it); + --index; + } + return VlQueue{}; + } + template VlQueue find_last_index(Func with_func) const { + IData index = m_deque.size() - 1; + for (auto it = m_deque.rbegin(); it != m_deque.rend(); ++it) { + if (with_func(index, *it)) return VlQueue::cons(index); + --index; + } + return VlQueue{}; + } + + // Reduction operators + VlQueue min() const { + if (m_deque.empty()) return VlQueue{}; + const auto it = std::min_element(m_deque.begin(), m_deque.end()); + return VlQueue::cons(*it); + } + VlQueue max() const { + if (m_deque.empty()) return VlQueue{}; + const auto it = std::max_element(m_deque.begin(), m_deque.end()); + return VlQueue::cons(*it); + } + + T_Value r_sum() const { + T_Value out(0); // Type must have assignment operator + for (const auto& i : m_deque) out += i; + return out; + } + template T_Value r_sum(Func with_func) const { + T_Value out(0); // Type must have assignment operator + IData index = 0; + for (const auto& i : m_deque) out += with_func(index++, i); + return out; + } + T_Value r_product() const { + if (m_deque.empty()) return T_Value(0); + auto it = m_deque.begin(); + T_Value out{*it}; + ++it; + for (; it != m_deque.end(); ++it) out *= *it; + return out; + } + template T_Value r_product(Func with_func) const { + if (m_deque.empty()) return T_Value(0); + auto it = m_deque.begin(); + IData index = 0; + T_Value out{with_func(index, *it)}; + ++it; + ++index; + for (; it != m_deque.end(); ++it) out *= with_func(index++, *it); + return out; + } + T_Value r_and() const { + if (m_deque.empty()) return T_Value(0); + auto it = m_deque.begin(); + T_Value out{*it}; + ++it; + for (; it != m_deque.end(); ++it) out &= *it; + return out; + } + template T_Value r_and(Func with_func) const { + if (m_deque.empty()) return T_Value(0); + auto it = m_deque.begin(); + IData index = 0; + T_Value out{with_func(index, *it)}; + ++it; + ++index; + for (; it != m_deque.end(); ++it) out &= with_func(index, *it); + return out; + } + T_Value r_or() const { + T_Value out(0); // Type must have assignment operator + for (const auto& i : m_deque) out |= i; + return out; + } + template T_Value r_or(Func with_func) const { + T_Value out(0); // Type must have assignment operator + IData index = 0; + for (const auto& i : m_deque) out |= with_func(index++, i); + return out; + } + T_Value r_xor() const { + T_Value out(0); // Type must have assignment operator + for (const auto& i : m_deque) out ^= i; + return out; + } + template T_Value r_xor(Func with_func) const { + T_Value out(0); // Type must have assignment operator + IData index = 0; + for (const auto& i : m_deque) out ^= with_func(index++, i); + return out; + } + + // Dumping. Verilog: str = $sformatf("%p", assoc) + std::string to_string() const { + if (m_deque.empty()) return "'{}"; // No trailing space + std::string out = "'{"; + std::string comma; + for (const auto& i : m_deque) { + out += comma + VL_TO_STRING(i); + comma = ", "; + } + return out + "} "; + } +}; + +template std::string VL_TO_STRING(const VlQueue& obj) { + return obj.to_string(); +} + +//=================================================================== +// Verilog associative array container +// There are no multithreaded locks on this; the base variable must +// be protected by other means +// +template class VlAssocArray final { +private: + // TYPES + using Map = std::map; + +public: + using const_iterator = typename Map::const_iterator; + +private: + // MEMBERS + Map m_map; // State of the assoc array + T_Value m_defaultValue; // Default value + +public: + // CONSTRUCTORS + // m_defaultValue isn't defaulted. Caller's constructor must do it. + VlAssocArray() = default; + ~VlAssocArray() = default; + VlAssocArray(const VlAssocArray&) = default; + VlAssocArray(VlAssocArray&&) = default; + VlAssocArray& operator=(const VlAssocArray&) = default; + VlAssocArray& operator=(VlAssocArray&&) = default; + + // METHODS + T_Value& atDefault() { return m_defaultValue; } + const T_Value& atDefault() const { return m_defaultValue; } + + // Size of array. Verilog: function int size(), or int num() + int size() const { return m_map.size(); } + // Clear array. Verilog: function void delete([input index]) + void clear() { m_map.clear(); } + void erase(const T_Key& index) { m_map.erase(index); } + // Return 0/1 if element exists. Verilog: function int exists(input index) + int exists(const T_Key& index) const { return m_map.find(index) != m_map.end(); } + // Return first element. Verilog: function int first(ref index); + int first(T_Key& indexr) const { + const auto it = m_map.cbegin(); + if (it == m_map.end()) return 0; + indexr = it->first; + return 1; + } + // Return last element. Verilog: function int last(ref index) + int last(T_Key& indexr) const { + const auto it = m_map.crbegin(); + if (it == m_map.rend()) return 0; + indexr = it->first; + return 1; + } + // Return next element. Verilog: function int next(ref index) + int next(T_Key& indexr) const { + auto it = m_map.find(indexr); + if (VL_UNLIKELY(it == m_map.end())) return 0; + ++it; + if (VL_UNLIKELY(it == m_map.end())) return 0; + indexr = it->first; + return 1; + } + // Return prev element. Verilog: function int prev(ref index) + int prev(T_Key& indexr) const { + auto it = m_map.find(indexr); + if (VL_UNLIKELY(it == m_map.end())) return 0; + if (VL_UNLIKELY(it == m_map.begin())) return 0; + --it; + indexr = it->first; + return 1; + } + // Setting. Verilog: assoc[index] = v + // Can't just overload operator[] or provide a "at" reference to set, + // because we need to be able to insert only when the value is set + T_Value& at(const T_Key& index) { + const auto it = m_map.find(index); + if (it == m_map.end()) { + std::pair pit = m_map.emplace(index, m_defaultValue); + return pit.first->second; + } + return it->second; + } + // Accessing. Verilog: v = assoc[index] + const T_Value& at(const T_Key& index) const { + const auto it = m_map.find(index); + if (it == m_map.end()) { + return m_defaultValue; + } else { + return it->second; + } + } + // Setting as a chained operation + VlAssocArray& set(const T_Key& index, const T_Value& value) { + at(index) = value; + return *this; + } + VlAssocArray& setDefault(const T_Value& value) { + atDefault() = value; + return *this; + } + + // For save/restore + const_iterator begin() const { return m_map.begin(); } + const_iterator end() const { return m_map.end(); } + + // Methods + VlQueue unique() const { + VlQueue out; + std::set saw; + for (const auto& i : m_map) { + auto it = saw.find(i.second); + if (it == saw.end()) { + saw.insert(it, i.second); + out.push_back(i.second); + } + } + return out; + } + VlQueue unique_index() const { + VlQueue out; + std::set saw; + for (const auto& i : m_map) { + auto it = saw.find(i.second); + if (it == saw.end()) { + saw.insert(it, i.second); + out.push_back(i.first); + } + } + return out; + } + template VlQueue find(Func with_func) const { + VlQueue out; + for (const auto& i : m_map) + if (with_func(i.first, i.second)) out.push_back(i.second); + return out; + } + template VlQueue find_index(Func with_func) const { + VlQueue out; + for (const auto& i : m_map) + if (with_func(i.first, i.second)) out.push_back(i.first); + return out; + } + template VlQueue find_first(Func with_func) const { + const auto it + = std::find_if(m_map.begin(), m_map.end(), [=](const std::pair& i) { + return with_func(i.first, i.second); + }); + if (it == m_map.end()) return VlQueue{}; + return VlQueue::cons(it->second); + } + template VlQueue find_first_index(Func with_func) const { + const auto it + = std::find_if(m_map.begin(), m_map.end(), [=](const std::pair& i) { + return with_func(i.first, i.second); + }); + if (it == m_map.end()) return VlQueue{}; + return VlQueue::cons(it->first); + } + template VlQueue find_last(Func with_func) const { + const auto it + = std::find_if(m_map.rbegin(), m_map.rend(), [=](const std::pair& i) { + return with_func(i.first, i.second); + }); + if (it == m_map.rend()) return VlQueue{}; + return VlQueue::cons(it->second); + } + template VlQueue find_last_index(Func with_func) const { + const auto it + = std::find_if(m_map.rbegin(), m_map.rend(), [=](const std::pair& i) { + return with_func(i.first, i.second); + }); + if (it == m_map.rend()) return VlQueue{}; + return VlQueue::cons(it->first); + } + + // Reduction operators + VlQueue min() const { + if (m_map.empty()) return VlQueue(); + const auto it = std::min_element( + m_map.begin(), m_map.end(), + [](const std::pair& a, const std::pair& b) { + return a.second < b.second; + }); + return VlQueue::cons(it->second); + } + VlQueue max() const { + if (m_map.empty()) return VlQueue(); + const auto it = std::max_element( + m_map.begin(), m_map.end(), + [](const std::pair& a, const std::pair& b) { + return a.second < b.second; + }); + return VlQueue::cons(it->second); + } + + T_Value r_sum() const { + T_Value out(0); // Type must have assignment operator + for (const auto& i : m_map) out += i.second; + return out; + } + template T_Value r_sum(Func with_func) const { + T_Value out(0); // Type must have assignment operator + for (const auto& i : m_map) out += with_func(i.first, i.second); + return out; + } + T_Value r_product() const { + if (m_map.empty()) return T_Value(0); + auto it = m_map.begin(); + T_Value out{it->second}; + ++it; + for (; it != m_map.end(); ++it) out *= it->second; + return out; + } + template T_Value r_product(Func with_func) const { + if (m_map.empty()) return T_Value(0); + auto it = m_map.begin(); + T_Value out{with_func(it->first, it->second)}; + ++it; + for (; it != m_map.end(); ++it) out *= with_func(it->first, it->second); + return out; + } + T_Value r_and() const { + if (m_map.empty()) return T_Value(0); + auto it = m_map.begin(); + T_Value out{it->second}; + ++it; + for (; it != m_map.end(); ++it) out &= it->second; + return out; + } + template T_Value r_and(Func with_func) const { + if (m_map.empty()) return T_Value(0); + auto it = m_map.begin(); + T_Value out{with_func(it->first, it->second)}; + ++it; + for (; it != m_map.end(); ++it) out &= with_func(it->first, it->second); + return out; + } + T_Value r_or() const { + T_Value out(0); // Type must have assignment operator + for (const auto& i : m_map) out |= i.second; + return out; + } + template T_Value r_or(Func with_func) const { + T_Value out(0); // Type must have assignment operator + for (const auto& i : m_map) out |= with_func(i.first, i.second); + return out; + } + T_Value r_xor() const { + T_Value out(0); // Type must have assignment operator + for (const auto& i : m_map) out ^= i.second; + return out; + } + template T_Value r_xor(Func with_func) const { + T_Value out(0); // Type must have assignment operator + for (const auto& i : m_map) out ^= with_func(i.first, i.second); + return out; + } + + // Dumping. Verilog: str = $sformatf("%p", assoc) + std::string to_string() const { + if (m_map.empty()) return "'{}"; // No trailing space + std::string out = "'{"; + std::string comma; + for (const auto& i : m_map) { + out += comma + VL_TO_STRING(i.first) + ":" + VL_TO_STRING(i.second); + comma = ", "; + } + // Default not printed - maybe random init data + return out + "} "; + } +}; + +template +std::string VL_TO_STRING(const VlAssocArray& obj) { + return obj.to_string(); +} + +template +void VL_READMEM_N(bool hex, int bits, const std::string& filename, + VlAssocArray& obj, QData start, QData end) VL_MT_SAFE { + VlReadMem rmem{hex, bits, filename, start, end}; + if (VL_UNLIKELY(!rmem.isOpen())) return; + while (true) { + QData addr; + std::string data; + if (rmem.get(addr /*ref*/, data /*ref*/)) { + rmem.setData(&(obj.at(addr)), data); + } else { + break; + } + } +} + +template +void VL_WRITEMEM_N(bool hex, int bits, const std::string& filename, + const VlAssocArray& obj, QData start, QData end) VL_MT_SAFE { + VlWriteMem wmem{hex, bits, filename, start, end}; + if (VL_UNLIKELY(!wmem.isOpen())) return; + for (const auto& i : obj) { + const QData addr = i.first; + if (addr >= start && addr <= end) wmem.print(addr, true, &(i.second)); + } +} + +//=================================================================== +/// Verilog unpacked array container +/// For when a standard C++[] array is not sufficient, e.g. an +/// array under a queue, or methods operating on the array. +/// +/// A 'struct' as we want this to be an aggregate type that allows +/// static aggregate initialization. Consider data members private. +/// +/// This class may get exposed to a Verilated Model's top I/O, if the top +/// IO has an unpacked array. + +template struct VlUnpacked final { + // MEMBERS + // This should be the only data member, otherwise generated static initializers need updating + T_Value m_storage[T_Depth]; // Contents of the unpacked array + + // CONSTRUCTORS + // Default constructors and destructor are used. Note however that C++20 requires that + // aggregate types do not have a user declared constructor, not even an explicitly defaulted + // one. + + // OPERATOR METHODS + // Default copy assignment operators are used. + + // METHODS + // Raw access + WData* data() { return &m_storage[0]; } + const WData* data() const { return &m_storage[0]; } + + T_Value& operator[](size_t index) { return m_storage[index]; }; + const T_Value& operator[](size_t index) const { return m_storage[index]; }; + + // Dumping. Verilog: str = $sformatf("%p", assoc) + std::string to_string() const { + std::string out = "'{"; + std::string comma; + for (int i = 0; i < T_Depth; ++i) { + out += comma + VL_TO_STRING(m_storage[i]); + comma = ", "; + } + return out + "} "; + } +}; + +template +std::string VL_TO_STRING(const VlUnpacked& obj) { + return obj.to_string(); +} + +//=================================================================== +// Verilog class reference container +// There are no multithreaded locks on this; the base variable must +// be protected by other means + +#define VlClassRef std::shared_ptr + +template // T typically of type VlClassRef +inline T VL_NULL_CHECK(T t, const char* filename, int linenum) { + if (VL_UNLIKELY(!t)) Verilated::nullPointerError(filename, linenum); + return t; +} + +template +static inline bool VL_CAST_DYNAMIC(VlClassRef in, VlClassRef& outr) { + VlClassRef casted = std::dynamic_pointer_cast(in); + if (VL_LIKELY(casted)) { + outr = casted; + return true; + } else { + return false; + } +} + +//====================================================================== + +#endif // Guard diff --git a/src/V3EmitCConstPool.cpp b/src/V3EmitCConstPool.cpp index 4fa3b813c..abd50bd41 100644 --- a/src/V3EmitCConstPool.cpp +++ b/src/V3EmitCConstPool.cpp @@ -49,7 +49,7 @@ class EmitCConstPool final : public EmitCConstInit { ofp->puts("// DESCRIPTION: Verilator output: Constant pool\n"); ofp->puts("//\n"); ofp->puts("\n"); - ofp->puts("#include \"verilated_heavy.h\"\n"); + ofp->puts("#include \"verilated.h\"\n"); return ofp; } diff --git a/src/V3EmitCHeaders.cpp b/src/V3EmitCHeaders.cpp index 2ef0903c3..74dce02c8 100644 --- a/src/V3EmitCHeaders.cpp +++ b/src/V3EmitCHeaders.cpp @@ -303,7 +303,7 @@ class EmitCHeader final : public EmitCConstInit { // Include files puts("\n"); ofp()->putsIntTopInclude(); - puts("#include \"verilated_heavy.h\"\n"); + puts("#include \"verilated.h\"\n"); if (v3Global.opt.mtasks()) puts("#include \"verilated_threads.h\"\n"); if (v3Global.opt.savable()) puts("#include \"verilated_save.h\"\n"); if (v3Global.opt.coverage()) puts("#include \"verilated_cov.h\"\n"); diff --git a/src/V3EmitCImp.cpp b/src/V3EmitCImp.cpp index 69b5f776e..bc5627cf4 100644 --- a/src/V3EmitCImp.cpp +++ b/src/V3EmitCImp.cpp @@ -181,7 +181,7 @@ class EmitCImp final : EmitCFunc { puts("// See " + topClassName() + ".h for the primary calling header\n"); // Include files - puts("\n#include \"verilated_heavy.h\"\n"); + puts("\n#include \"verilated.h\"\n"); if (v3Global.dpi()) puts("#include \"verilated_dpi.h\"\n"); puts("\n"); for (const string& name : headers) puts("#include \"" + name + ".h\"\n"); diff --git a/src/V3EmitCModel.cpp b/src/V3EmitCModel.cpp index 965cf56c0..140f03140 100644 --- a/src/V3EmitCModel.cpp +++ b/src/V3EmitCModel.cpp @@ -56,7 +56,7 @@ class EmitCModel final : public EmitCFunc { // Include files puts("\n"); ofp()->putsIntTopInclude(); - puts("#include \"verilated_heavy.h\"\n"); + puts("#include \"verilated.h\"\n"); if (v3Global.opt.mtasks()) puts("#include \"verilated_threads.h\"\n"); if (v3Global.opt.savable()) puts("#include \"verilated_save.h\"\n"); if (v3Global.opt.coverage()) puts("#include \"verilated_cov.h\"\n"); diff --git a/src/V3EmitCSyms.cpp b/src/V3EmitCSyms.cpp index 7cd836c91..f86dd7487 100644 --- a/src/V3EmitCSyms.cpp +++ b/src/V3EmitCSyms.cpp @@ -389,7 +389,7 @@ void EmitCSyms::emitSymHdr() { puts("\n"); ofp()->putsIntTopInclude(); - puts("#include \"verilated_heavy.h\"\n"); + puts("#include \"verilated.h\"\n"); if (v3Global.needTraceDumper()) { puts("#include \"" + v3Global.opt.traceSourceLang() + ".h\"\n"); } diff --git a/test_regress/t/t_verilated_all.pl b/test_regress/t/t_verilated_all.pl index 1d8370346..44b2d3a72 100755 --- a/test_regress/t/t_verilated_all.pl +++ b/test_regress/t/t_verilated_all.pl @@ -53,6 +53,7 @@ foreach my $file (sort keys %hit) { if (!$hit{$file} && $file !~ /_sc/ && $file !~ /_fst/ + && $file !~ /_heavy/ && ($file !~ /_thread/ || $Self->cfg_with_threaded)) { error("Include file not covered by t_verilated_all test: ",$file); } From 68aa731ce958de1129331a0b162efe56bdc1358d Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 24 Jul 2021 12:13:19 -0400 Subject: [PATCH 49/84] Fix missing null on blackbox parse. --- src/verilog.y | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/verilog.y b/src/verilog.y index d5bb07096..2f665bfb8 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -1952,7 +1952,8 @@ member_decl_assignment: // Derived from IEEE: variable_decl_assignment // // // IEEE: "[ covergroup_variable_identifier ] '=' class_new // // Pushed into variable_declExpr:class_new - | '=' class_new { nullptr; BBUNSUP($1, "Unsupported: member declaration assignment with new()"); } + | '=' class_new + { $$ = nullptr; BBUNSUP($1, "Unsupported: member declaration assignment with new()"); } ; list_of_variable_decl_assignments: // ==IEEE: list_of_variable_decl_assignments @@ -1975,7 +1976,8 @@ variable_decl_assignment: // ==IEEE: variable_decl_assignment // // // IEEE: "[ covergroup_variable_identifier ] '=' class_new // // Pushed into variable_declExpr:class_new - | '=' class_new { nullptr; BBUNSUP($1, "Unsupported: declaration assignment with new()"); } + | '=' class_new + { $$ = nullptr; BBUNSUP($1, "Unsupported: declaration assignment with new()"); } ; list_of_tf_variable_identifiers: // ==IEEE: list_of_tf_variable_identifiers @@ -2735,8 +2737,7 @@ list_of_defparam_assignments: //== IEEE: list_of_defparam_assignments defparam_assignment: // ==IEEE: defparam_assignment idAny '.' idAny '=' expr { $$ = new AstDefParam($4, *$1, *$3, $5); } | idAny '.' idAny '.' - { $$ = nullptr; - BBUNSUP($4, "Unsupported: defparam with more than one dot"); } + { $$ = nullptr; BBUNSUP($4, "Unsupported: defparam with more than one dot"); } ; //************************************************ @@ -3457,7 +3458,8 @@ for_initialization: // ==IEEE: for_initialization + for_variable_declarat for_initializationItemList: // IEEE: [for_variable_declaration...] for_initializationItem { $$ = $1; } - | for_initializationItemList ',' for_initializationItem { $$ = $1; BBUNSUP($2, "Unsupported: for loop initialization after the first comma"); } + | for_initializationItemList ',' for_initializationItem + { $$ = $1; BBUNSUP($2, "Unsupported: for loop initialization after the first comma"); } ; for_initializationItem: // IEEE: variable_assignment + for_variable_declaration From 275885a79717fc72aebbfad0007763fd3502f886 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 24 Jul 2021 12:21:04 -0400 Subject: [PATCH 50/84] Internals: Fix some long UNSUP lines. No functional change. --- src/verilog.y | 99 ++++++++++++++++++++++++++++++++++----------------- 1 file changed, 66 insertions(+), 33 deletions(-) diff --git a/src/verilog.y b/src/verilog.y index 2f665bfb8..8ce6e77b7 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -1457,10 +1457,12 @@ interface_item: // IEEE: interface_item + non_port_interface_item // // IEEE: generate_region | interface_generate_region { $$ = $1; } | interface_or_generate_item { $$ = $1; } - | program_declaration { $$ = nullptr; BBUNSUP(CRELINE(), "Unsupported: program decls within interface decls"); } + | program_declaration + { $$ = nullptr; BBUNSUP(CRELINE(), "Unsupported: program decls within interface decls"); } // // IEEE 1800-2017: modport_item // // See instead old 2012 position in interface_or_generate_item - | interface_declaration { $$ = nullptr; BBUNSUP(CRELINE(), "Unsupported: interface decls within interface decls"); } + | interface_declaration + { $$ = nullptr; BBUNSUP(CRELINE(), "Unsupported: interface decls within interface decls"); } | timeunits_declaration { $$ = $1; } // // See note in interface_or_generate item | module_common_item { $$ = $1; } @@ -1483,7 +1485,8 @@ interface_or_generate_item: // ==IEEE: interface_or_generate_item anonymous_program: // ==IEEE: anonymous_program // // See the spec - this doesn't change the scope, items still go up "top" - yPROGRAM ';' anonymous_program_itemListE yENDPROGRAM { BBUNSUP($1, "Unsupported: Anonymous programs"); $$ = nullptr; } + yPROGRAM ';' anonymous_program_itemListE yENDPROGRAM + { BBUNSUP($1, "Unsupported: Anonymous programs"); $$ = nullptr; } ; anonymous_program_itemListE: // IEEE: { anonymous_program_item } @@ -1565,9 +1568,12 @@ program_generate_item: // ==IEEE: program_generate_item ; extern_tf_declaration: // ==IEEE: extern_tf_declaration - yEXTERN task_prototype ';' { $$ = nullptr; BBUNSUP($1, "Unsupported: extern task"); } - | yEXTERN function_prototype ';' { $$ = nullptr; BBUNSUP($1, "Unsupported: extern function"); } - | yEXTERN yFORKJOIN task_prototype ';' { $$ = nullptr; BBUNSUP($1, "Unsupported: extern forkjoin"); } + yEXTERN task_prototype ';' + { $$ = nullptr; BBUNSUP($1, "Unsupported: extern task"); } + | yEXTERN function_prototype ';' + { $$ = nullptr; BBUNSUP($1, "Unsupported: extern function"); } + | yEXTERN yFORKJOIN task_prototype ';' + { $$ = nullptr; BBUNSUP($1, "Unsupported: extern forkjoin"); } ; modport_declaration: // ==IEEE: modport_declaration @@ -1598,14 +1604,17 @@ modportPortsDecl: // // IEEE: modport_simple_ports_declaration port_direction modportSimplePort { $$ = new AstModportVarRef($2, *$2, GRAMMARP->m_varIO); } // // IEEE: modport_clocking_declaration - | yCLOCKING idAny/*clocking_identifier*/ { $$ = nullptr; BBUNSUP($1, "Unsupported: Modport clocking"); } + | yCLOCKING idAny/*clocking_identifier*/ + { $$ = nullptr; BBUNSUP($1, "Unsupported: Modport clocking"); } // // IEEE: yIMPORT modport_tf_port // // IEEE: yEXPORT modport_tf_port // // modport_tf_port expanded here | yIMPORT id/*tf_identifier*/ { $$ = new AstModportFTaskRef($2, *$2, false); } | yEXPORT id/*tf_identifier*/ { $$ = new AstModportFTaskRef($2, *$2, true); } - | yIMPORT method_prototype { $$ = nullptr; BBUNSUP($1, "Unsupported: Modport import with prototype"); } - | yEXPORT method_prototype { $$ = nullptr; BBUNSUP($1, "Unsupported: Modport export with prototype"); } + | yIMPORT method_prototype + { $$ = nullptr; BBUNSUP($1, "Unsupported: Modport import with prototype"); } + | yEXPORT method_prototype + { $$ = nullptr; BBUNSUP($1, "Unsupported: Modport export with prototype"); } // Continuations of above after a comma. // // IEEE: modport_simple_ports_declaration | modportSimplePort { $$ = new AstModportVarRef($1,*$1,GRAMMARP->m_varIO); } @@ -1875,9 +1884,11 @@ data_typeNoRef: // ==IEEE: data_type, excluding class_type etc referenc // // declarations which decode '.' modport themselves, so // // instead see data_typeVar | yVIRTUAL__INTERFACE yINTERFACE id/*interface*/ - { $$ = new AstBasicDType($1, AstBasicDTypeKwd::CHANDLE); BBUNSUP($1, "Unsupported: virtual interface"); } + { $$ = new AstBasicDType{$1, AstBasicDTypeKwd::CHANDLE}; + BBUNSUP($1, "Unsupported: virtual interface"); } | yVIRTUAL__anyID id/*interface*/ - { $$ = new AstBasicDType($1, AstBasicDTypeKwd::CHANDLE); BBUNSUP($1, "Unsupported: virtual data type"); } + { $$ = new AstBasicDType{$1, AstBasicDTypeKwd::CHANDLE}; + BBUNSUP($1, "Unsupported: virtual data type"); } | type_reference { $$ = $1; } // // IEEE: class_scope: see data_type above // // IEEE: class_type: see data_type above @@ -2016,8 +2027,10 @@ variable_dimension: // ==IEEE: variable_dimension // // IEEE: associative_dimension (if data_type) // // Can't tell which until see if expr is data type or not | '[' exprOrDataType ']' { $$ = new AstBracketRange($1, $2); } - | yP_BRASTAR ']' { $$ = nullptr; BBUNSUP($1, "Unsupported: [*] wildcard associative arrays"); } - | '[' '*' ']' { $$ = nullptr; BBUNSUP($2, "Unsupported: [*] wildcard associative arrays"); } + | yP_BRASTAR ']' + { $$ = nullptr; BBUNSUP($1, "Unsupported: [*] wildcard associative arrays"); } + | '[' '*' ']' + { $$ = nullptr; BBUNSUP($2, "Unsupported: [*] wildcard associative arrays"); } // // IEEE: queue_dimension // // '[' '$' ']' -- $ is part of expr, see '[' constExpr ']' // // '[' '$' ':' expr ']' -- anyrange:expr:$ @@ -2289,9 +2302,12 @@ non_port_module_item: // ==IEEE: non_port_module_item | module_or_generate_item { $$ = $1; } | specify_block { $$ = $1; } | specparam_declaration { $$ = $1; } - | program_declaration { $$ = nullptr; BBUNSUP(CRELINE(), "Unsupported: program decls within module decls"); } - | module_declaration { $$ = nullptr; BBUNSUP(CRELINE(), "Unsupported: module decls within module decls"); } - | interface_declaration { $$ = nullptr; BBUNSUP(CRELINE(), "Unsupported: interface decls within module decls"); } + | program_declaration + { $$ = nullptr; BBUNSUP(CRELINE(), "Unsupported: program decls within module decls"); } + | module_declaration + { $$ = nullptr; BBUNSUP(CRELINE(), "Unsupported: module decls within module decls"); } + | interface_declaration + { $$ = nullptr; BBUNSUP(CRELINE(), "Unsupported: interface decls within module decls"); } | timeunits_declaration { $$ = $1; } // // Verilator specific | yaSCHDR { $$ = new AstScHdr($1,*$1); } @@ -2327,7 +2343,8 @@ module_common_item: // ==IEEE: module_common_item | bind_directive { $$ = $1; } | continuous_assign { $$ = $1; } // // IEEE: net_alias - | yALIAS variable_lvalue aliasEqList ';' { $$ = nullptr; BBUNSUP($1, "Unsupported: alias statements"); } + | yALIAS variable_lvalue aliasEqList ';' + { $$ = nullptr; BBUNSUP($1, "Unsupported: alias statements"); } | initial_construct { $$ = $1; } | final_construct { $$ = $1; } // // IEEE: always_construct @@ -2360,7 +2377,8 @@ module_or_generate_item_declaration: // ==IEEE: module_or_generate_item_d package_or_generate_item_declaration { $$ = $1; } | genvar_declaration { $$ = $1; } | clocking_declaration { $$ = $1; } - | yDEFAULT yCLOCKING idAny/*new-clocking_identifier*/ ';' { $$ = nullptr; BBUNSUP($1, "Unsupported: default clocking identifier"); } + | yDEFAULT yCLOCKING idAny/*new-clocking_identifier*/ ';' + { $$ = nullptr; BBUNSUP($1, "Unsupported: default clocking identifier"); } //UNSUP yDEFAULT yDISABLE yIFF expr/*expression_or_dist*/ ';' { } ; @@ -2693,7 +2711,8 @@ packed_dimensionList: // IEEE: { packed_dimension } packed_dimension: // ==IEEE: packed_dimension anyrange { $$ = $1; } - | '[' ']' { $$ = nullptr; BBUNSUP($1, "Unsupported: [] dimensions"); } + | '[' ']' + { $$ = nullptr; BBUNSUP($1, "Unsupported: [] dimensions"); } ; //************************************************ @@ -3081,9 +3100,12 @@ statement_item: // IEEE: statement_item // // IEEE: procedural_continuous_assignment | yASSIGN idClassSel '=' delayE expr ';' { $$ = new AstAssign($1,$2,$5); } //UNSUP: delay_or_event_controlE above - | yDEASSIGN variable_lvalue ';' { $$ = nullptr; BBUNSUP($1, "Unsupported: Verilog 1995 deassign"); } - | yFORCE expr '=' expr ';' { $$ = nullptr; BBUNSUP($1, "Unsupported: Verilog 1995 force"); } - | yRELEASE variable_lvalue ';' { $$ = nullptr; BBUNSUP($1, "Unsupported: Verilog 1995 release"); } + | yDEASSIGN variable_lvalue ';' + { $$ = nullptr; BBUNSUP($1, "Unsupported: Verilog 1995 deassign"); } + | yFORCE expr '=' expr ';' + { $$ = nullptr; BBUNSUP($1, "Unsupported: Verilog 1995 force"); } + | yRELEASE variable_lvalue ';' + { $$ = nullptr; BBUNSUP($1, "Unsupported: Verilog 1995 release"); } // // // IEEE: case_statement | unique_priorityE caseStart caseAttrE case_itemListE yENDCASE { $$ = $2; if ($4) $2->addItemsp($4); @@ -3205,7 +3227,8 @@ statement_item: // IEEE: statement_item //UNSUP randsequence_statement { $$ = $1; } // // // IEEE: randcase_statement - | yRANDCASE case_itemList yENDCASE { $$ = nullptr; BBUNSUP($1, "Unsupported: SystemVerilog 2005 randcase statements"); } + | yRANDCASE case_itemList yENDCASE + { $$ = nullptr; BBUNSUP($1, "Unsupported: SystemVerilog 2005 randcase statements"); } // //UNSUP expect_property_statement { $$ = $1; } // @@ -3383,11 +3406,14 @@ caseCondList: // IEEE: part of case_item ; patternNoExpr: // IEEE: pattern **Excluding Expr* - '.' id/*variable*/ { $$ = nullptr; BBUNSUP($1, "Unsupported: '{} tagged patterns"); } - | yP_DOTSTAR { $$ = nullptr; BBUNSUP($1, "Unsupported: '{} tagged patterns"); } + '.' id/*variable*/ + { $$ = nullptr; BBUNSUP($1, "Unsupported: '{} tagged patterns"); } + | yP_DOTSTAR + { $$ = nullptr; BBUNSUP($1, "Unsupported: '{} tagged patterns"); } // // IEEE: "expr" excluded; expand in callers // // "yTAGGED id [expr]" Already part of expr - //UNSUP yTAGGED id/*member_identifier*/ patternNoExpr { $$ = nullptr; BBUNSUP($1, "Unsupported: '{} tagged patterns"); } + //UNSUP yTAGGED id/*member_identifier*/ patternNoExpr + //UNSUP { $$ = nullptr; BBUNSUP($1, "Unsupported: '{} tagged patterns"); } // // "yP_TICKBRA patternList '}'" part of expr under assignment_pattern ; @@ -3563,7 +3589,8 @@ function_subroutine_callNoMethod: // IEEE: function_subroutine_call (as f // // IEEE: randomize_call // // We implement randomize as a normal funcRef, since randomize isn't a keyword // // Note yNULL is already part of expressions, so they come for free - | funcRef yWITH__CUR constraint_block { $$ = $1; BBUNSUP($2, "Unsupported: randomize() 'with' constraint"); } + | funcRef yWITH__CUR constraint_block + { $$ = $1; BBUNSUP($2, "Unsupported: randomize() 'with' constraint"); } | funcRef yWITH__CUR '{' '}' { $$ = new AstWithParse($2, false, $1, nullptr); } ; @@ -4274,7 +4301,8 @@ expr: // IEEE: part of expression/constant_expression/primary // // // IEEE: '(' mintypmax_expression ')' | ~noPar__IGNORE~'(' expr ')' { $$ = $2; } - | ~noPar__IGNORE~'(' expr ':' expr ':' expr ')' { $$ = $2; BBUNSUP($1, "Unsupported: min typ max expressions"); } + | ~noPar__IGNORE~'(' expr ':' expr ':' expr ')' + { $$ = $2; BBUNSUP($1, "Unsupported: min typ max expressions"); } // // PSL rule | '_' '(' expr ')' { $$ = $3; } // Arbitrary Verilog inside PSL // @@ -6160,11 +6188,13 @@ class_item: // ==IEEE: class_item | class_method { $$ = $1; } | class_constraint { $$ = $1; } // - | class_declaration { $$ = nullptr; BBUNSUP($1, "Unsupported: class within class"); } + | class_declaration + { $$ = nullptr; BBUNSUP($1, "Unsupported: class within class"); } | timeunits_declaration { $$ = $1; } //UNSUP covergroup_declaration { $$ = $1; } // // local_parameter_declaration under parameter_declaration - | parameter_declaration ';' { $$ = $1; BBUNSUP($2, "Unsupported: class parameters"); } // 1800-2009 + | parameter_declaration ';' + { $$ = $1; BBUNSUP($2, "Unsupported: class parameters"); } // 1800-2009 | ';' { $$ = nullptr; } // | error ';' { $$ = nullptr; } @@ -6224,8 +6254,10 @@ class_constraint: // ==IEEE: class_constraint constraintStaticE yCONSTRAINT idAny constraint_block { $$ = nullptr; /*UNSUP*/ } // // IEEE: constraint_prototype + constraint_prototype_qualifier | constraintStaticE yCONSTRAINT idAny ';' { $$ = nullptr; } - | yEXTERN constraintStaticE yCONSTRAINT idAny ';' { $$ = nullptr; BBUNSUP($1, "Unsupported: extern constraint"); } - | yPURE constraintStaticE yCONSTRAINT idAny ';' { $$ = nullptr; BBUNSUP($1, "Unsupported: pure constraint"); } + | yEXTERN constraintStaticE yCONSTRAINT idAny ';' + { $$ = nullptr; BBUNSUP($1, "Unsupported: extern constraint"); } + | yPURE constraintStaticE yCONSTRAINT idAny ';' + { $$ = nullptr; BBUNSUP($1, "Unsupported: pure constraint"); } ; constraint_block: // ==IEEE: constraint_block @@ -6239,7 +6271,8 @@ constraint_block_itemList: // IEEE: { constraint_block_item } constraint_block_item: // ==IEEE: constraint_block_item constraint_expression { $$ = $1; } - | ySOLVE solve_before_list yBEFORE solve_before_list ';' { $$ = nullptr; BBUNSUP($2, "Unsupported: solve before"); } + | ySOLVE solve_before_list yBEFORE solve_before_list ';' + { $$ = nullptr; BBUNSUP($2, "Unsupported: solve before"); } ; solve_before_list: // ==IEEE: solve_before_list From 43ecaec9a0b67313f69e8e4f7d524505136574f5 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 24 Jul 2021 12:49:07 -0400 Subject: [PATCH 51/84] Internal C++11 flex cleanups. No functional change intended. --- src/V3PreLex.l | 16 ++++++++-------- src/verilog.l | 14 +++++++------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/V3PreLex.l b/src/V3PreLex.l index 81dcd763f..e32ee7c0c 100644 --- a/src/V3PreLex.l +++ b/src/V3PreLex.l @@ -524,7 +524,7 @@ again: } } if (debug() >= 10) { - cout << "- pp::inputToLex got=" << got << " '" << string(buf, got) << "'" << endl; + cout << "- pp::inputToLex got=" << got << " '" << std::string{buf, got} << "'" << endl; } return got; } @@ -567,7 +567,7 @@ string V3PreLex::endOfStream(bool& againr) { // Final shutdown phase for a stream, we can finally change the // current fileline to the new stream curStreamp()->m_termState = 0; - FileLine* filelinep = curFilelinep(); + FileLine* const filelinep = curFilelinep(); delete curStreamp(); m_streampStack.pop(); // Must work as size>1; EOF is entry 0 if (curStreamp()->m_eof) { @@ -590,7 +590,7 @@ string V3PreLex::endOfStream(bool& againr) { void V3PreLex::initFirstBuffer(FileLine* filelinep) { // Called from constructor to make first buffer // yy_create_buffer also sets yy_fill_buffer=1 so reads from YY_INPUT - VPreStream* streamp = new VPreStream(filelinep, this); + VPreStream* const streamp = new VPreStream{filelinep, this}; streamp->m_eof = true; m_streampStack.push(streamp); // @@ -606,7 +606,7 @@ void V3PreLex::scanNewFile(FileLine* filelinep) { yyerrorf("Recursive `define or other nested inclusion"); curStreamp()->m_eof = true; // Fake it to stop recursion } else { - VPreStream* streamp = new VPreStream(filelinep, this); + VPreStream* const streamp = new VPreStream{filelinep, this}; m_tokFilelinep = curFilelinep(); streamp->m_file = true; scanSwitchStream(streamp); @@ -615,7 +615,7 @@ void V3PreLex::scanNewFile(FileLine* filelinep) { void V3PreLex::scanBytes(const string& str) { // Note buffers also appended in ::scanBytesBack - // Not "m_buffers.push_front(string(strp,len))" as we need a `define + // Not "m_buffers.push_front(string{strp,len})" as we need a `define // to take effect immediately, in the middle of the current buffer // Also we don't use scan_bytes that would set yy_fill_buffer // which would force Flex to bypass our YY_INPUT routine. @@ -625,7 +625,7 @@ void V3PreLex::scanBytes(const string& str) { yyerrorf("Recursive `define or other nested inclusion"); curStreamp()->m_eof = true; // Fake it to stop recursion } else { - VPreStream* streamp = new VPreStream(curFilelinep(), this); + VPreStream* const streamp = new VPreStream{curFilelinep(), this}; streamp->m_buffers.push_front(str); scanSwitchStream(streamp); } @@ -649,7 +649,7 @@ string V3PreLex::currentUnreadChars() { ssize_t left = (yy_n_chars - (yy_c_buf_p - currentBuffer()->yy_ch_buf)); if (left > 0) { // left may be -1 at EOS *(yy_c_buf_p) = (yy_hold_char); - return string(yy_c_buf_p, left); + return std::string(yy_c_buf_p, left); // () narrowing conversion } else { return ""; } @@ -689,7 +689,7 @@ void V3PreLex::dumpStack() { dumpSummary(); std::stack tmpstack = LEXP->m_streampStack; while (!tmpstack.empty()) { - VPreStream* streamp = tmpstack.top(); + const VPreStream* const streamp = tmpstack.top(); cout << "- bufferStack[" << cvtToHex(streamp) << "]: " << " at=" << streamp->m_curFilelinep << " nBuf=" << streamp->m_buffers.size() << " size0=" << (streamp->m_buffers.empty() ? 0 : streamp->m_buffers.front().length()) diff --git a/src/verilog.l b/src/verilog.l index d000c3fad..3c831abea 100644 --- a/src/verilog.l +++ b/src/verilog.l @@ -60,9 +60,9 @@ //====================================================================== static double lexParseDouble(FileLine* fl, const char* textp, size_t length) { - string text = std::string(textp, length); + const string text = std::string{textp, length}; bool success = false; - double d = VString::parseDouble(text, &success); + const double d = VString::parseDouble(text, &success); if (!success) fl->v3error("Syntax error parsing real: '" << textp << "'"); return d; } @@ -608,7 +608,7 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5} /* Default PLI rule */ { - "$"[a-zA-Z_$][a-zA-Z0-9_$]* { string str (yytext, yyleng); + "$"[a-zA-Z_$][a-zA-Z0-9_$]* { const string str (yytext, yyleng); yylval.strp = PARSEP->newString(AstNode::encodeName(str)); FL; return yaD_PLI; } @@ -861,10 +861,10 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5} /* Identifiers and numbers */ { {escid} { FL; yylval.strp = PARSEP->newString - (AstNode::encodeName(string(yytext+1))); // +1 to skip the backslash + (AstNode::encodeName(std::string{yytext+1})); // +1 to skip the backslash return yaID__LEX; } - {id} { FL; yylval.strp = PARSEP->newString(AstNode::encodeName(string(yytext))); + {id} { FL; yylval.strp = PARSEP->newString(AstNode::encodeName(std::string{yytext})); return yaID__LEX; } \"[^\"\\]*\" { FL; yylval.strp = PARSEP->newString(yytext+1, yyleng-2); @@ -875,10 +875,10 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5} /* "# 1'b0" is a delay value so must lex as "#" "1" "'b0" */ if (PARSEP->lexPrevToken()=='#') { int shortlen = 0; - while (isdigit(yytext[shortlen])) shortlen++; + while (isdigit(yytext[shortlen])) ++shortlen; if (shortlen) { // Push rest past numbers for later parse - PARSEP->lexUnputString(yytext+shortlen, yyleng-shortlen); + PARSEP->lexUnputString(yytext + shortlen, yyleng - shortlen); // Return is stuff before the tick yyleng = shortlen; yytext[yyleng] = '\0'; From 18b0f6387db9002c553c940c8ea9b500ea5b1c33 Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Sat, 24 Jul 2021 20:06:06 -0500 Subject: [PATCH 52/84] Add XML ccall, constpool, initarray, and if/while begins (#3080) * EmitXml: Added , , /, wrapped children of and with elements to prevent ambiguity * EmitXml: added signed="true" to signed basicdtypes --- docs/CONTRIBUTORS | 1 + src/V3EmitXml.cpp | 67 +- test_regress/t/t_dist_error_format.pl | 1 + test_regress/t/t_var_port_xml.out | 6 +- test_regress/t/t_var_xref_gen.pl | 3 +- test_regress/t/t_xml_debugcheck.out | 1560 ++++++++++++++++++++++++ test_regress/t/t_xml_debugcheck.pl | 37 + test_regress/t/t_xml_first.out | 2 +- test_regress/t/t_xml_flat.out | 2 +- test_regress/t/t_xml_flat_vlvbound.out | 160 +-- test_regress/t/t_xml_tag.out | 2 +- 11 files changed, 1761 insertions(+), 80 deletions(-) create mode 100644 test_regress/t/t_xml_debugcheck.out create mode 100755 test_regress/t/t_xml_debugcheck.pl diff --git a/docs/CONTRIBUTORS b/docs/CONTRIBUTORS index 47c79e2f9..999f1086a 100644 --- a/docs/CONTRIBUTORS +++ b/docs/CONTRIBUTORS @@ -83,6 +83,7 @@ Sebastien Van Cauwenberghe Sergi Granell Stefan Wallentowitz Stephen Henry +Steven Hugg Tim Snyder Tobias Rosenkranz Tobias Wölfel diff --git a/src/V3EmitXml.cpp b/src/V3EmitXml.cpp index c2facba08..83ff6363d 100644 --- a/src/V3EmitXml.cpp +++ b/src/V3EmitXml.cpp @@ -112,12 +112,67 @@ class EmitXmlFileVisitor final : public AstNVisitor { putsQuoted(nodep->origName()); outputChildrenEnd(nodep, "instance"); } + virtual void visit(AstNodeIf* nodep) override { + outputTag(nodep, "if"); + puts(">\n"); + iterateAndNextNull(nodep->op1p()); + puts("\n"); + iterateAndNextNull(nodep->op2p()); + puts("\n"); + if (nodep->op3p()) { + puts("\n"); + iterateAndNextNull(nodep->op3p()); + puts("\n"); + } + puts("\n"); + } + virtual void visit(AstWhile* nodep) override { + outputTag(nodep, "while"); + puts(">\n"); + puts("\n"); + iterateAndNextNull(nodep->op1p()); + puts("\n"); + if (nodep->op2p()) { + puts("\n"); + iterateAndNextNull(nodep->op2p()); + puts("\n"); + } + if (nodep->op3p()) { + puts("\n"); + iterateAndNextNull(nodep->op3p()); + puts("\n"); + } + if (nodep->op4p()) { + puts("\n"); + iterateAndNextNull(nodep->op4p()); + puts("\n"); + } + puts("\n"); + } virtual void visit(AstNetlist* nodep) override { puts("\n"); iterateChildren(nodep); puts("\n"); } - virtual void visit(AstConstPool*) override {} + virtual void visit(AstConstPool* nodep) override { + if (!v3Global.opt.xmlOnly()) { + puts("\n"); + iterateChildren(nodep); + puts("\n"); + } + } + virtual void visit(AstInitArray* nodep) override { + puts("\n"); + const AstInitArray::KeyItemMap& map = nodep->map(); + for (AstInitArray::KeyItemMap::const_iterator it = map.begin(); it != map.end(); ++it) { + puts("first)); + puts("\">\n"); + iterateChildren(it->second); + puts("\n"); + } + puts("\n"); + } virtual void visit(AstNodeModule* nodep) override { outputTag(nodep, ""); puts(" origName="); @@ -195,6 +250,12 @@ class EmitXmlFileVisitor final : public AstNVisitor { putsQuoted(nodep->dotted()); outputChildrenEnd(nodep, ""); } + virtual void visit(AstNodeCCall* nodep) override { + outputTag(nodep, ""); + puts(" func="); + putsQuoted(nodep->funcp()->name()); + outputChildrenEnd(nodep, ""); + } // Data types virtual void visit(AstBasicDType* nodep) override { @@ -203,6 +264,9 @@ class EmitXmlFileVisitor final : public AstNVisitor { puts(" left=\"" + cvtToStr(nodep->left()) + "\""); puts(" right=\"" + cvtToStr(nodep->right()) + "\""); } + if (nodep->isSigned()) { + puts(" signed=\"true\""); + } puts("/>\n"); } virtual void visit(AstIfaceRefDType* nodep) override { @@ -319,6 +383,7 @@ private: // VISITORS virtual void visit(AstConstPool*) override {} + virtual void visit(AstNodeModule* nodep) override { if (nodep->level() >= 0 && nodep->level() <= 2) { // ==2 because we don't add wrapper when in XML mode diff --git a/test_regress/t/t_dist_error_format.pl b/test_regress/t/t_dist_error_format.pl index 8a30f1f7b..654a7c70f 100755 --- a/test_regress/t/t_dist_error_format.pl +++ b/test_regress/t/t_dist_error_format.pl @@ -40,6 +40,7 @@ sub formats { ++$lineno; $line =~ s/(\$display|\$write).*\".*%(Error|Warning)//; if ($line =~ /(Error|Warning)/ + && $line !~ /^\s* - - - + + + diff --git a/test_regress/t/t_var_xref_gen.pl b/test_regress/t/t_var_xref_gen.pl index 873cde7f6..d862fcf2e 100755 --- a/test_regress/t/t_var_xref_gen.pl +++ b/test_regress/t/t_var_xref_gen.pl @@ -8,11 +8,12 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di # Version 2.0. # SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 -scenarios(simulator => 1); +scenarios(vlt => 1); compile( verilator_flags2 => ["--debug-check"], ); ok(1); + 1; diff --git a/test_regress/t/t_xml_debugcheck.out b/test_regress/t/t_xml_debugcheck.out new file mode 100644 index 000000000..bd15fe808 --- /dev/null +++ b/test_regress/t/t_xml_debugcheck.out @@ -0,0 +1,1560 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test_regress/t/t_xml_debugcheck.pl b/test_regress/t/t_xml_debugcheck.pl new file mode 100755 index 000000000..4770d909d --- /dev/null +++ b/test_regress/t/t_xml_debugcheck.pl @@ -0,0 +1,37 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2012 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 + +scenarios(vlt => 1); + +my $out_filename = "$Self->{obj_dir}/V$Self->{name}.xml"; + +top_filename("t/t_enum_type_methods.v"); + +compile( + verilator_flags2 => ['--debug-check', '--flatten'], + verilator_make_gmake => 0, + make_top_shell => 0, + make_main => 0, + ); + +files_identical("$out_filename", $Self->{golden_filename}); + +# make sure that certain tags are present in --debug-check +# that would not be present in --xml-only +file_grep("$out_filename", qr//x); # for and +file_grep("$out_filename", qr/ signed=/x); # for +file_grep("$out_filename", qr/ func=/x); # for + +ok(1); +1; diff --git a/test_regress/t/t_xml_first.out b/test_regress/t/t_xml_first.out index b00ac18b9..929ff89c1 100644 --- a/test_regress/t/t_xml_first.out +++ b/test_regress/t/t_xml_first.out @@ -79,7 +79,7 @@ - + diff --git a/test_regress/t/t_xml_flat.out b/test_regress/t/t_xml_flat.out index 964c5b791..ea35502fc 100644 --- a/test_regress/t/t_xml_flat.out +++ b/test_regress/t/t_xml_flat.out @@ -108,7 +108,7 @@ - + diff --git a/test_regress/t/t_xml_flat_vlvbound.out b/test_regress/t/t_xml_flat_vlvbound.out index 28a1c9e97..aafd3bcf6 100644 --- a/test_regress/t/t_xml_flat_vlvbound.out +++ b/test_regress/t/t_xml_flat_vlvbound.out @@ -68,43 +68,51 @@ - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + - - - - - - - - - - - - - - + + + + + + + + - - - + + @@ -126,43 +134,51 @@ - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + - - - - - - - - - - - - - - + + + + + + + + - - - + + @@ -189,11 +205,11 @@ - - + + - - + + diff --git a/test_regress/t/t_xml_tag.out b/test_regress/t/t_xml_tag.out index 873eaa1a0..9c4c044e7 100644 --- a/test_regress/t/t_xml_tag.out +++ b/test_regress/t/t_xml_tag.out @@ -55,7 +55,7 @@ - + From 33af14eba89d9f41f93a4574949cdec37e889410 Mon Sep 17 00:00:00 2001 From: github action Date: Sun, 25 Jul 2021 01:07:07 +0000 Subject: [PATCH 53/84] Apply 'make format' --- src/V3EmitXml.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/V3EmitXml.cpp b/src/V3EmitXml.cpp index 83ff6363d..01af7930a 100644 --- a/src/V3EmitXml.cpp +++ b/src/V3EmitXml.cpp @@ -156,9 +156,9 @@ class EmitXmlFileVisitor final : public AstNVisitor { } virtual void visit(AstConstPool* nodep) override { if (!v3Global.opt.xmlOnly()) { - puts("\n"); - iterateChildren(nodep); - puts("\n"); + puts("\n"); + iterateChildren(nodep); + puts("\n"); } } virtual void visit(AstInitArray* nodep) override { @@ -264,9 +264,7 @@ class EmitXmlFileVisitor final : public AstNVisitor { puts(" left=\"" + cvtToStr(nodep->left()) + "\""); puts(" right=\"" + cvtToStr(nodep->right()) + "\""); } - if (nodep->isSigned()) { - puts(" signed=\"true\""); - } + if (nodep->isSigned()) { puts(" signed=\"true\""); } puts("/>\n"); } virtual void visit(AstIfaceRefDType* nodep) override { From b46664c70aedf31edfa3c9971df9bf47f549cc4c Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Sun, 25 Jul 2021 13:32:36 +0100 Subject: [PATCH 54/84] Internals: Use C++11 constexpr for instruction count costs No functional change. --- src/V3Ast.h | 40 +++++++-------- src/V3AstNodes.h | 130 +++++++++++++++++++++++------------------------ 2 files changed, 85 insertions(+), 85 deletions(-) diff --git a/src/V3Ast.h b/src/V3Ast.h index 5925d881d..63d86ae53 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -1510,21 +1510,21 @@ public: static void operator delete(void* obj, size_t size); #endif - // CONSTANT ACCESSORS - static int instrCountBranch() { return 4; } ///< Instruction cycles to branch - static int instrCountDiv() { return 10; } ///< Instruction cycles to divide - static int instrCountDpi() { return 1000; } ///< Instruction cycles to call user function - static int instrCountLd() { return 2; } ///< Instruction cycles to load memory - static int instrCountMul() { return 3; } ///< Instruction cycles to multiply integers - static int instrCountPli() { return 20; } ///< Instruction cycles to call pli routines - static int instrCountDouble() { return 8; } ///< Instruction cycles to convert or do floats - static int instrCountDoubleDiv() { return 40; } ///< Instruction cycles to divide floats - static int instrCountDoubleTrig() { return 200; } ///< Instruction cycles to do trigonomics - static int instrCountString() { return 100; } ///< Instruction cycles to do string ops - /// Instruction cycles to call subroutine - static int instrCountCall() { return instrCountBranch() + 10; } - /// Instruction cycles to determine simulation time - static int instrCountTime() { return instrCountCall() + 5; } + // CONSTANTS + // The following are relative dynamic costs (~ execution cycle count) of various operations. + // They are used by V3InstCount to estimate the relative execution time of code fragments. + static constexpr int INSTR_COUNT_BRANCH = 4; // Branch + static constexpr int INSTR_COUNT_CALL = INSTR_COUNT_BRANCH + 10; // Subroutine call + static constexpr int INSTR_COUNT_LD = 2; // Load memory + static constexpr int INSTR_COUNT_INT_MUL = 3; // Integer multiply + static constexpr int INSTR_COUNT_INT_DIV = 10; // Integer divide + static constexpr int INSTR_COUNT_DBL = 8; // Convert or do float ops + static constexpr int INSTR_COUNT_DBL_DIV = 40; // Double divide + static constexpr int INSTR_COUNT_DBL_TRIG = 200; // Double trigonometric ops + static constexpr int INSTR_COUNT_STR = 100; // String ops + static constexpr int INSTR_COUNT_TIME = INSTR_COUNT_CALL + 5; // Determine simulation time + static constexpr int INSTR_COUNT_PLI = 20; // PLI routines + static constexpr int INSTR_COUNT_DPI = 1000; // DPI import function // ACCESSORS virtual string name() const { return ""; } @@ -2094,7 +2094,7 @@ public: virtual bool sizeMattersLhs() const override { return false; } virtual bool sizeMattersRhs() const override { return false; } virtual bool sizeMattersThs() const override { return false; } - virtual int instrCount() const override { return instrCountBranch(); } + virtual int instrCount() const override { return INSTR_COUNT_BRANCH; } virtual AstNode* cloneType(AstNode* condp, AstNode* expr1p, AstNode* expr2p) = 0; }; @@ -2229,7 +2229,7 @@ public: AstNode* incsp() const { return op3p(); } // op3 = increment statements AstNode* bodysp() const { return op4p(); } // op4 = body of loop virtual bool isGateOptimizable() const override { return false; } - virtual int instrCount() const override { return instrCountBranch(); } + virtual int instrCount() const override { return INSTR_COUNT_BRANCH; } virtual bool same(const AstNode* samep) const override { return true; } }; @@ -2256,7 +2256,7 @@ public: void addElsesp(AstNode* newp) { addOp3p(newp); } virtual bool isGateOptimizable() const override { return false; } virtual bool isGateDedupable() const override { return true; } - virtual int instrCount() const override { return instrCountBranch(); } + virtual int instrCount() const override { return INSTR_COUNT_BRANCH; } virtual bool same(const AstNode* samep) const override { return true; } void branchPred(VBranchPred flag) { m_branchPred = flag; } VBranchPred branchPred() const { return m_branchPred; } @@ -2274,7 +2274,7 @@ protected: public: ASTNODE_BASE_FUNCS(NodeCase) - virtual int instrCount() const override { return instrCountBranch(); } + virtual int instrCount() const override { return INSTR_COUNT_BRANCH; } AstNode* exprp() const { return op1p(); } // op1 = case condition AstCaseItem* itemsp() const { return VN_CAST(op2p(), CaseItem); @@ -2644,7 +2644,7 @@ public: virtual void dump(std::ostream& str = std::cout) const override; virtual void cloneRelink() override; virtual const char* broken() const override; - virtual int instrCount() const override { return instrCountCall(); } + virtual int instrCount() const override { return INSTR_COUNT_CALL; } virtual bool same(const AstNode* samep) const override { const AstNodeCCall* asamep = static_cast(samep); return (funcp() == asamep->funcp() && argTypes() == asamep->argTypes()); diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index 987d9594e..6fb566b7f 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -2400,7 +2400,7 @@ public: } } virtual int instrCount() const override { - return widthInstrs() * (access().isReadOrRW() ? instrCountLd() : 1); + return widthInstrs() * (access().isReadOrRW() ? INSTR_COUNT_LD : 1); } virtual string emitVerilog() override { V3ERROR_NA_RETURN(""); } virtual string emitC() override { V3ERROR_NA_RETURN(""); } @@ -3584,7 +3584,7 @@ public: if (m_dataDeclp && m_dataDeclp->clonep()) m_dataDeclp = m_dataDeclp->clonep(); } virtual void dump(std::ostream& str) const override; - virtual int instrCount() const override { return 1 + 2 * instrCountLd(); } + virtual int instrCount() const override { return 1 + 2 * INSTR_COUNT_LD; } virtual bool maybePointedTo() const override { return true; } void binNum(int flag) { m_binNum = flag; } int binNum() const { return m_binNum; } @@ -3627,7 +3627,7 @@ public: if (m_declp->clonep()) m_declp = m_declp->clonep(); } virtual void dump(std::ostream& str) const override; - virtual int instrCount() const override { return 1 + 2 * instrCountLd(); } + virtual int instrCount() const override { return 1 + 2 * INSTR_COUNT_LD; } virtual bool same(const AstNode* samep) const override { return declp() == static_cast(samep)->declp(); } @@ -3650,7 +3650,7 @@ public: setOp3p(changep); } ASTNODE_NODE_FUNCS(CoverToggle) - virtual int instrCount() const override { return 3 + instrCountBranch() + instrCountLd(); } + virtual int instrCount() const override { return 3 + INSTR_COUNT_BRANCH + INSTR_COUNT_LD; } virtual bool same(const AstNode* samep) const override { return true; } virtual bool isGateOptimizable() const override { return false; } virtual bool isPredictOptimizable() const override { return true; } @@ -3743,7 +3743,7 @@ public: addNOp2p(bodysp); } ASTNODE_NODE_FUNCS(CaseItem) - virtual int instrCount() const override { return widthInstrs() + instrCountBranch(); } + virtual int instrCount() const override { return widthInstrs() + INSTR_COUNT_BRANCH; } AstNode* condsp() const { return op1p(); } // op1 = list of possible matching expressions AstNode* bodysp() const { return op2p(); } // op2 = what to do void condsp(AstNode* nodep) { setOp1p(nodep); } @@ -3787,7 +3787,7 @@ public: } ASTNODE_NODE_FUNCS(SFormatF) virtual string name() const override { return m_text; } - virtual int instrCount() const override { return instrCountPli(); } + virtual int instrCount() const override { return INSTR_COUNT_PLI; } virtual bool hasDType() const override { return true; } virtual bool same(const AstNode* samep) const override { return text() == static_cast(samep)->text(); @@ -3852,7 +3852,7 @@ public: virtual bool same(const AstNode* samep) const override { return displayType() == static_cast(samep)->displayType(); } - virtual int instrCount() const override { return instrCountPli(); } + virtual int instrCount() const override { return INSTR_COUNT_PLI; } AstDisplayType displayType() const { return m_displayType; } void displayType(AstDisplayType type) { m_displayType = type; } // * = Add a newline for $display @@ -3916,7 +3916,7 @@ public: virtual bool same(const AstNode* samep) const override { return displayType() == static_cast(samep)->displayType(); } - virtual int instrCount() const override { return instrCountPli(); } + virtual int instrCount() const override { return INSTR_COUNT_PLI; } AstDisplayType displayType() const { return m_displayType; } void displayType(AstDisplayType type) { m_displayType = type; } void fmtp(AstSFormatF* nodep) { addOp1p(nodep); } // op1 = To-String formatter @@ -3950,7 +3950,7 @@ public: virtual bool isPure() const override { return true; } virtual bool isOutputter() const override { return false; } virtual bool cleanOut() const { return false; } - virtual int instrCount() const override { return instrCountPli(); } + virtual int instrCount() const override { return INSTR_COUNT_PLI; } virtual bool same(const AstNode* samep) const override { return true; } void fmtp(AstSFormatF* nodep) { addOp1p(nodep); } // op1 = To-String formatter AstSFormatF* fmtp() const { return VN_CAST(op1p(), SFormatF); } @@ -3995,7 +3995,7 @@ public: } // Though deleted before opt virtual bool isPure() const override { return false; } // Though deleted before opt virtual bool isOutputter() const override { return true; } // Though deleted before opt - virtual int instrCount() const override { return instrCountPli(); } + virtual int instrCount() const override { return INSTR_COUNT_PLI; } AstNode* exprsp() const { return op1p(); } // op1 = Expressions to output void exprsp(AstNode* nodep) { addOp1p(nodep); } // op1 = Expressions to output }; @@ -4326,7 +4326,7 @@ public: } // Though deleted before opt virtual bool isPure() const override { return false; } // Though deleted before opt virtual bool isOutputter() const override { return true; } // Though deleted before opt - virtual int instrCount() const override { return instrCountPli(); } + virtual int instrCount() const override { return INSTR_COUNT_PLI; } virtual bool same(const AstNode* samep) const override { return m_off == static_cast(samep)->m_off; } @@ -4438,7 +4438,7 @@ public: AstNode* arrayp() const { return op1p(); } // op1 = array and index vars AstNode* bodysp() const { return op4p(); } // op4 = body of loop virtual bool isGateOptimizable() const override { return false; } - virtual int instrCount() const override { return instrCountBranch(); } + virtual int instrCount() const override { return INSTR_COUNT_BRANCH; } virtual bool same(const AstNode* samep) const override { return true; } }; @@ -4455,7 +4455,7 @@ public: virtual bool isGateOptimizable() const override { return false; } // Not relevant - converted to FOR - virtual int instrCount() const override { return instrCountBranch(); } + virtual int instrCount() const override { return INSTR_COUNT_BRANCH; } virtual bool same(const AstNode* samep) const override { return true; } }; @@ -4488,7 +4488,7 @@ public: void addBodysp(AstNode* newp) { addOp3p(newp); } void addIncsp(AstNode* newp) { addOp4p(newp); } virtual bool isGateOptimizable() const override { return false; } - virtual int instrCount() const override { return instrCountBranch(); } + virtual int instrCount() const override { return INSTR_COUNT_BRANCH; } virtual bool same(const AstNode* samep) const override { return true; } // Stop statement searchback here virtual void addBeforeStmt(AstNode* newp, AstNode* belowp) override; @@ -4669,7 +4669,7 @@ public: if (m_labelp->clonep()) m_labelp = m_labelp->clonep(); } virtual void dump(std::ostream& str) const override; - virtual int instrCount() const override { return instrCountBranch(); } + virtual int instrCount() const override { return INSTR_COUNT_BRANCH; } virtual bool same(const AstNode* samep) const override { return labelp() == static_cast(samep)->labelp(); } @@ -5065,7 +5065,7 @@ public: virtual bool isPredictOptimizable() const override { return false; } virtual bool isPure() const override { return false; } virtual bool isOutputter() const override { return true; } - virtual int instrCount() const override { return instrCountPli(); } + virtual int instrCount() const override { return INSTR_COUNT_PLI; } void timeunit(const VTimescale& flag) { m_timeunit = flag; } VTimescale timeunit() const { return m_timeunit; } }; @@ -5164,7 +5164,7 @@ public: virtual bool isPredictOptimizable() const override { return false; } virtual bool isPure() const override { return false; } virtual bool isOutputter() const override { return true; } - virtual int instrCount() const override { return instrCountPli(); } + virtual int instrCount() const override { return INSTR_COUNT_PLI; } AstNode* unitsp() const { return op1p(); } AstNode* precisionp() const { return op2p(); } AstNode* suffixp() const { return op3p(); } @@ -5254,7 +5254,7 @@ public: if (m_declp->clonep()) m_declp = m_declp->clonep(); } virtual void dump(std::ostream& str) const override; - virtual int instrCount() const override { return 10 + 2 * instrCountLd(); } + virtual int instrCount() const override { return 10 + 2 * INSTR_COUNT_LD; } virtual bool hasDType() const override { return true; } virtual bool same(const AstNode* samep) const override { return declp() == static_cast(samep)->declp(); @@ -5432,7 +5432,7 @@ public: virtual bool cleanOut() const override { return true; } virtual bool isGateOptimizable() const override { return false; } virtual bool isPredictOptimizable() const override { return false; } - virtual int instrCount() const override { return instrCountPli(); } + virtual int instrCount() const override { return INSTR_COUNT_PLI; } virtual bool same(const AstNode* samep) const override { return true; } AstNode* seedp() const { return op1p(); } bool reset() const { return m_reset; } @@ -5462,7 +5462,7 @@ public: virtual bool sizeMattersRhs() const override { return false; } virtual bool isGateOptimizable() const override { return false; } virtual bool isPredictOptimizable() const override { return false; } - virtual int instrCount() const override { return instrCountPli(); } + virtual int instrCount() const override { return INSTR_COUNT_PLI; } }; class AstTime final : public AstNodeTermop { @@ -5479,7 +5479,7 @@ public: virtual bool cleanOut() const override { return true; } virtual bool isGateOptimizable() const override { return false; } virtual bool isPredictOptimizable() const override { return false; } - virtual int instrCount() const override { return instrCountTime(); } + virtual int instrCount() const override { return INSTR_COUNT_TIME; } virtual bool same(const AstNode* samep) const override { return true; } virtual void dump(std::ostream& str = std::cout) const override; void timeunit(const VTimescale& flag) { m_timeunit = flag; } @@ -5500,7 +5500,7 @@ public: virtual bool cleanOut() const override { return true; } virtual bool isGateOptimizable() const override { return false; } virtual bool isPredictOptimizable() const override { return false; } - virtual int instrCount() const override { return instrCountTime(); } + virtual int instrCount() const override { return INSTR_COUNT_TIME; } virtual bool same(const AstNode* samep) const override { return true; } virtual void dump(std::ostream& str = std::cout) const override; void timeunit(const VTimescale& flag) { m_timeunit = flag; } @@ -5525,7 +5525,7 @@ public: virtual bool isGateOptimizable() const override { return false; } virtual bool isSubstOptimizable() const override { return false; } virtual bool isPredictOptimizable() const override { return false; } - virtual int instrCount() const override { return instrCountPli(); } + virtual int instrCount() const override { return INSTR_COUNT_PLI; } virtual bool same(const AstNode* samep) const override { return true; } }; @@ -5561,7 +5561,7 @@ public: virtual bool cleanOut() const override { return true; } virtual bool cleanLhs() const override { return false; } virtual bool sizeMattersLhs() const override { return false; } - virtual int instrCount() const override { return instrCountDouble(); } + virtual int instrCount() const override { return INSTR_COUNT_DBL; } virtual bool doubleFlavor() const override { return true; } }; class AstRedAnd final : public AstNodeUniop { @@ -5754,7 +5754,7 @@ public: virtual bool cleanOut() const override { return false; } virtual bool cleanLhs() const override { return false; } // Eliminated before matters virtual bool sizeMattersLhs() const override { return false; } // Eliminated before matters - virtual int instrCount() const override { return instrCountDouble(); } + virtual int instrCount() const override { return INSTR_COUNT_DBL; } }; class AstRToIRoundS final : public AstNodeUniop { // Convert real to integer, with arbitrary sized output (not just "integer" format) @@ -5772,7 +5772,7 @@ public: virtual bool cleanOut() const override { return false; } virtual bool cleanLhs() const override { return false; } virtual bool sizeMattersLhs() const override { return false; } - virtual int instrCount() const override { return instrCountDouble(); } + virtual int instrCount() const override { return INSTR_COUNT_DBL; } }; class AstIToRD final : public AstNodeUniop { // $itor where lhs is unsigned @@ -5788,7 +5788,7 @@ public: virtual bool cleanOut() const override { return false; } virtual bool cleanLhs() const override { return true; } virtual bool sizeMattersLhs() const override { return false; } - virtual int instrCount() const override { return instrCountDouble(); } + virtual int instrCount() const override { return INSTR_COUNT_DBL; } }; class AstISToRD final : public AstNodeUniop { // $itor where lhs is signed @@ -5805,7 +5805,7 @@ public: virtual bool cleanOut() const override { return false; } virtual bool cleanLhs() const override { return true; } virtual bool sizeMattersLhs() const override { return false; } - virtual int instrCount() const override { return instrCountDouble(); } + virtual int instrCount() const override { return INSTR_COUNT_DBL; } }; class AstRealToBits final : public AstNodeUniop { public: @@ -5822,7 +5822,7 @@ public: virtual bool cleanOut() const override { return false; } virtual bool cleanLhs() const override { return false; } // Eliminated before matters virtual bool sizeMattersLhs() const override { return false; } // Eliminated before matters - virtual int instrCount() const override { return instrCountDouble(); } + virtual int instrCount() const override { return INSTR_COUNT_DBL; } }; class AstBitsToRealD final : public AstNodeUniop { public: @@ -5839,7 +5839,7 @@ public: virtual bool cleanOut() const override { return false; } virtual bool cleanLhs() const override { return false; } // Eliminated before matters virtual bool sizeMattersLhs() const override { return false; } // Eliminated before matters - virtual int instrCount() const override { return instrCountDouble(); } + virtual int instrCount() const override { return INSTR_COUNT_DBL; } }; class AstCLog2 final : public AstNodeUniop { @@ -6213,7 +6213,7 @@ public: virtual bool cleanOut() const override { return true; } virtual bool cleanLhs() const override { return false; } virtual bool sizeMattersLhs() const override { return false; } - virtual int instrCount() const override { return instrCountDoubleTrig(); } + virtual int instrCount() const override { return INSTR_COUNT_DBL_TRIG; } virtual bool doubleFlavor() const override { return true; } }; @@ -6551,7 +6551,7 @@ public: virtual bool cleanRhs() const override { return true; } virtual bool sizeMattersLhs() const override { return false; } virtual bool sizeMattersRhs() const override { return false; } - virtual int instrCount() const override { return widthInstrs() + instrCountBranch(); } + virtual int instrCount() const override { return widthInstrs() + INSTR_COUNT_BRANCH; } }; class AstLogAnd final : public AstNodeBiop { public: @@ -6574,7 +6574,7 @@ public: virtual bool cleanRhs() const override { return true; } virtual bool sizeMattersLhs() const override { return false; } virtual bool sizeMattersRhs() const override { return false; } - virtual int instrCount() const override { return widthInstrs() + instrCountBranch(); } + virtual int instrCount() const override { return widthInstrs() + INSTR_COUNT_BRANCH; } }; class AstLogEq final : public AstNodeBiCom { public: @@ -6597,7 +6597,7 @@ public: virtual bool cleanRhs() const override { return true; } virtual bool sizeMattersLhs() const override { return false; } virtual bool sizeMattersRhs() const override { return false; } - virtual int instrCount() const override { return widthInstrs() + instrCountBranch(); } + virtual int instrCount() const override { return widthInstrs() + INSTR_COUNT_BRANCH; } }; class AstLogIf final : public AstNodeBiop { public: @@ -6620,7 +6620,7 @@ public: virtual bool cleanRhs() const override { return true; } virtual bool sizeMattersLhs() const override { return false; } virtual bool sizeMattersRhs() const override { return false; } - virtual int instrCount() const override { return widthInstrs() + instrCountBranch(); } + virtual int instrCount() const override { return widthInstrs() + INSTR_COUNT_BRANCH; } }; class AstOr final : public AstNodeBiComAsv { public: @@ -6733,7 +6733,7 @@ public: virtual bool cleanRhs() const override { return false; } virtual bool sizeMattersLhs() const override { return false; } virtual bool sizeMattersRhs() const override { return false; } - virtual int instrCount() const override { return instrCountDouble(); } + virtual int instrCount() const override { return INSTR_COUNT_DBL; } virtual bool doubleFlavor() const override { return true; } }; class AstEqN final : public AstNodeBiCom { @@ -6757,7 +6757,7 @@ public: virtual bool cleanRhs() const override { return false; } virtual bool sizeMattersLhs() const override { return false; } virtual bool sizeMattersRhs() const override { return false; } - virtual int instrCount() const override { return instrCountString(); } + virtual int instrCount() const override { return INSTR_COUNT_STR; } virtual bool stringFlavor() const override { return true; } }; class AstNeq final : public AstNodeBiCom { @@ -6803,7 +6803,7 @@ public: virtual bool cleanRhs() const override { return false; } virtual bool sizeMattersLhs() const override { return false; } virtual bool sizeMattersRhs() const override { return false; } - virtual int instrCount() const override { return instrCountDouble(); } + virtual int instrCount() const override { return INSTR_COUNT_DBL; } virtual bool doubleFlavor() const override { return true; } }; class AstNeqN final : public AstNodeBiCom { @@ -6827,7 +6827,7 @@ public: virtual bool cleanRhs() const override { return false; } virtual bool sizeMattersLhs() const override { return false; } virtual bool sizeMattersRhs() const override { return false; } - virtual int instrCount() const override { return instrCountString(); } + virtual int instrCount() const override { return INSTR_COUNT_STR; } virtual bool stringFlavor() const override { return true; } }; class AstLt final : public AstNodeBiop { @@ -6873,7 +6873,7 @@ public: virtual bool cleanRhs() const override { return false; } virtual bool sizeMattersLhs() const override { return false; } virtual bool sizeMattersRhs() const override { return false; } - virtual int instrCount() const override { return instrCountDouble(); } + virtual int instrCount() const override { return INSTR_COUNT_DBL; } virtual bool doubleFlavor() const override { return true; } }; class AstLtS final : public AstNodeBiop { @@ -6920,7 +6920,7 @@ public: virtual bool cleanRhs() const override { return false; } virtual bool sizeMattersLhs() const override { return false; } virtual bool sizeMattersRhs() const override { return false; } - virtual int instrCount() const override { return instrCountString(); } + virtual int instrCount() const override { return INSTR_COUNT_STR; } virtual bool stringFlavor() const override { return true; } }; class AstGt final : public AstNodeBiop { @@ -6966,7 +6966,7 @@ public: virtual bool cleanRhs() const override { return false; } virtual bool sizeMattersLhs() const override { return false; } virtual bool sizeMattersRhs() const override { return false; } - virtual int instrCount() const override { return instrCountDouble(); } + virtual int instrCount() const override { return INSTR_COUNT_DBL; } virtual bool doubleFlavor() const override { return true; } }; class AstGtS final : public AstNodeBiop { @@ -7013,7 +7013,7 @@ public: virtual bool cleanRhs() const override { return false; } virtual bool sizeMattersLhs() const override { return false; } virtual bool sizeMattersRhs() const override { return false; } - virtual int instrCount() const override { return instrCountString(); } + virtual int instrCount() const override { return INSTR_COUNT_STR; } virtual bool stringFlavor() const override { return true; } }; class AstGte final : public AstNodeBiop { @@ -7061,7 +7061,7 @@ public: virtual bool cleanRhs() const override { return false; } virtual bool sizeMattersLhs() const override { return false; } virtual bool sizeMattersRhs() const override { return false; } - virtual int instrCount() const override { return instrCountDouble(); } + virtual int instrCount() const override { return INSTR_COUNT_DBL; } virtual bool doubleFlavor() const override { return true; } }; class AstGteS final : public AstNodeBiop { @@ -7108,7 +7108,7 @@ public: virtual bool cleanRhs() const override { return false; } virtual bool sizeMattersLhs() const override { return false; } virtual bool sizeMattersRhs() const override { return false; } - virtual int instrCount() const override { return instrCountString(); } + virtual int instrCount() const override { return INSTR_COUNT_STR; } virtual bool stringFlavor() const override { return true; } }; class AstLte final : public AstNodeBiop { @@ -7156,7 +7156,7 @@ public: virtual bool cleanRhs() const override { return false; } virtual bool sizeMattersLhs() const override { return false; } virtual bool sizeMattersRhs() const override { return false; } - virtual int instrCount() const override { return instrCountDouble(); } + virtual int instrCount() const override { return INSTR_COUNT_DBL; } virtual bool doubleFlavor() const override { return true; } }; class AstLteS final : public AstNodeBiop { @@ -7203,7 +7203,7 @@ public: virtual bool cleanRhs() const override { return false; } virtual bool sizeMattersLhs() const override { return false; } virtual bool sizeMattersRhs() const override { return false; } - virtual int instrCount() const override { return instrCountString(); } + virtual int instrCount() const override { return INSTR_COUNT_STR; } virtual bool stringFlavor() const override { return true; } }; class AstShiftL final : public AstNodeBiop { @@ -7324,7 +7324,7 @@ public: virtual bool cleanRhs() const override { return false; } virtual bool sizeMattersLhs() const override { return false; } virtual bool sizeMattersRhs() const override { return false; } - virtual int instrCount() const override { return instrCountDouble(); } + virtual int instrCount() const override { return INSTR_COUNT_DBL; } virtual bool doubleFlavor() const override { return true; } }; class AstSub final : public AstNodeBiop { @@ -7370,7 +7370,7 @@ public: virtual bool cleanRhs() const override { return false; } virtual bool sizeMattersLhs() const override { return false; } virtual bool sizeMattersRhs() const override { return false; } - virtual int instrCount() const override { return instrCountDouble(); } + virtual int instrCount() const override { return INSTR_COUNT_DBL; } virtual bool doubleFlavor() const override { return true; } }; class AstMul final : public AstNodeBiComAsv { @@ -7394,7 +7394,7 @@ public: virtual bool cleanRhs() const override { return true; } virtual bool sizeMattersLhs() const override { return true; } virtual bool sizeMattersRhs() const override { return true; } - virtual int instrCount() const override { return widthInstrs() * instrCountMul(); } + virtual int instrCount() const override { return widthInstrs() * INSTR_COUNT_INT_MUL; } }; class AstMulD final : public AstNodeBiComAsv { public: @@ -7417,7 +7417,7 @@ public: virtual bool cleanRhs() const override { return false; } virtual bool sizeMattersLhs() const override { return true; } virtual bool sizeMattersRhs() const override { return true; } - virtual int instrCount() const override { return instrCountDouble(); } + virtual int instrCount() const override { return INSTR_COUNT_DBL; } virtual bool doubleFlavor() const override { return true; } }; class AstMulS final : public AstNodeBiComAsv { @@ -7442,7 +7442,7 @@ public: virtual bool cleanRhs() const override { return true; } virtual bool sizeMattersLhs() const override { return true; } virtual bool sizeMattersRhs() const override { return true; } - virtual int instrCount() const override { return widthInstrs() * instrCountMul(); } + virtual int instrCount() const override { return widthInstrs() * INSTR_COUNT_INT_MUL; } virtual bool signedFlavor() const override { return true; } }; class AstDiv final : public AstNodeBiop { @@ -7465,7 +7465,7 @@ public: virtual bool cleanRhs() const override { return true; } virtual bool sizeMattersLhs() const override { return true; } virtual bool sizeMattersRhs() const override { return true; } - virtual int instrCount() const override { return widthInstrs() * instrCountDiv(); } + virtual int instrCount() const override { return widthInstrs() * INSTR_COUNT_INT_DIV; } }; class AstDivD final : public AstNodeBiop { public: @@ -7488,7 +7488,7 @@ public: virtual bool cleanRhs() const override { return false; } virtual bool sizeMattersLhs() const override { return false; } virtual bool sizeMattersRhs() const override { return false; } - virtual int instrCount() const override { return instrCountDoubleDiv(); } + virtual int instrCount() const override { return INSTR_COUNT_DBL_DIV; } virtual bool doubleFlavor() const override { return true; } }; class AstDivS final : public AstNodeBiop { @@ -7511,7 +7511,7 @@ public: virtual bool cleanRhs() const override { return true; } virtual bool sizeMattersLhs() const override { return true; } virtual bool sizeMattersRhs() const override { return true; } - virtual int instrCount() const override { return widthInstrs() * instrCountDiv(); } + virtual int instrCount() const override { return widthInstrs() * INSTR_COUNT_INT_DIV; } virtual bool signedFlavor() const override { return true; } }; class AstModDiv final : public AstNodeBiop { @@ -7534,7 +7534,7 @@ public: virtual bool cleanRhs() const override { return true; } virtual bool sizeMattersLhs() const override { return true; } virtual bool sizeMattersRhs() const override { return true; } - virtual int instrCount() const override { return widthInstrs() * instrCountDiv(); } + virtual int instrCount() const override { return widthInstrs() * INSTR_COUNT_INT_DIV; } }; class AstModDivS final : public AstNodeBiop { public: @@ -7556,7 +7556,7 @@ public: virtual bool cleanRhs() const override { return true; } virtual bool sizeMattersLhs() const override { return true; } virtual bool sizeMattersRhs() const override { return true; } - virtual int instrCount() const override { return widthInstrs() * instrCountDiv(); } + virtual int instrCount() const override { return widthInstrs() * INSTR_COUNT_INT_DIV; } virtual bool signedFlavor() const override { return true; } }; class AstPow final : public AstNodeBiop { @@ -7580,7 +7580,7 @@ public: virtual bool cleanRhs() const override { return true; } virtual bool sizeMattersLhs() const override { return true; } virtual bool sizeMattersRhs() const override { return false; } - virtual int instrCount() const override { return widthInstrs() * instrCountMul() * 10; } + virtual int instrCount() const override { return widthInstrs() * INSTR_COUNT_INT_MUL * 10; } }; class AstPowD final : public AstNodeBiop { public: @@ -7602,7 +7602,7 @@ public: virtual bool cleanRhs() const override { return false; } virtual bool sizeMattersLhs() const override { return false; } virtual bool sizeMattersRhs() const override { return false; } - virtual int instrCount() const override { return instrCountDoubleDiv() * 5; } + virtual int instrCount() const override { return INSTR_COUNT_DBL_DIV * 5; } virtual bool doubleFlavor() const override { return true; } }; class AstPowSU final : public AstNodeBiop { @@ -7628,7 +7628,7 @@ public: virtual bool cleanRhs() const override { return true; } virtual bool sizeMattersLhs() const override { return true; } virtual bool sizeMattersRhs() const override { return false; } - virtual int instrCount() const override { return widthInstrs() * instrCountMul() * 10; } + virtual int instrCount() const override { return widthInstrs() * INSTR_COUNT_INT_MUL * 10; } virtual bool signedFlavor() const override { return true; } }; class AstPowSS final : public AstNodeBiop { @@ -7654,7 +7654,7 @@ public: virtual bool cleanRhs() const override { return true; } virtual bool sizeMattersLhs() const override { return true; } virtual bool sizeMattersRhs() const override { return false; } - virtual int instrCount() const override { return widthInstrs() * instrCountMul() * 10; } + virtual int instrCount() const override { return widthInstrs() * INSTR_COUNT_INT_MUL * 10; } virtual bool signedFlavor() const override { return true; } }; class AstPowUS final : public AstNodeBiop { @@ -7680,7 +7680,7 @@ public: virtual bool cleanRhs() const override { return true; } virtual bool sizeMattersLhs() const override { return true; } virtual bool sizeMattersRhs() const override { return false; } - virtual int instrCount() const override { return widthInstrs() * instrCountMul() * 10; } + virtual int instrCount() const override { return widthInstrs() * INSTR_COUNT_INT_MUL * 10; } virtual bool signedFlavor() const override { return true; } }; class AstPreAdd final : public AstNodeTriop { @@ -7921,7 +7921,7 @@ public: virtual bool cleanRhs() const override { return true; } virtual bool sizeMattersLhs() const override { return false; } virtual bool sizeMattersRhs() const override { return false; } - virtual int instrCount() const override { return instrCountString(); } + virtual int instrCount() const override { return INSTR_COUNT_STR; } virtual bool stringFlavor() const override { return true; } }; class AstReplicate final : public AstNodeBiop { @@ -8084,7 +8084,7 @@ public: virtual bool cleanRhs() const override { return false; } virtual bool sizeMattersLhs() const override { return false; } virtual bool sizeMattersRhs() const override { return false; } - virtual int instrCount() const override { return instrCountDoubleTrig(); } + virtual int instrCount() const override { return INSTR_COUNT_DBL_TRIG; } virtual bool doubleFlavor() const override { return true; } }; @@ -8788,7 +8788,7 @@ public: } // virtual void name(const string& name) override { m_name = name; } - virtual int instrCount() const override { return dpiImportPrototype() ? instrCountDpi() : 0; } + virtual int instrCount() const override { return dpiImportPrototype() ? INSTR_COUNT_DPI : 0; } VBoolOrUnknown isConst() const { return m_isConst; } void isConst(bool flag) { m_isConst.setTrueOrFalse(flag); } void isConst(VBoolOrUnknown flag) { m_isConst = flag; } From f937e3282f3e8487ec1e1287384fcdd14afd6004 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sun, 25 Jul 2021 11:20:19 -0400 Subject: [PATCH 55/84] Tests: Ignore DepSet hash numbers. (#3083) --- test_regress/driver.pl | 3 ++- test_regress/t/t_xml_debugcheck.out | 10 +++++----- test_regress/t/t_xml_debugcheck.pl | 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/test_regress/driver.pl b/test_regress/driver.pl index bf6efd9bc..9073406bf 100755 --- a/test_regress/driver.pl +++ b/test_regress/driver.pl @@ -2185,13 +2185,14 @@ sub files_identical { s/^-V\{t[0-9]+,[0-9]+\}/-V{t#,#}/; # --vlt vs --vltmt run differences $_; } @l1; - for (my $l=0; $l<=$#l1; ++$l) { + for (my $l = 0; $l <= $#l1; ++$l) { # Don't put control chars into our source repository $l1[$l] =~ s/\r/<#013>/mig; $l1[$l] =~ s/Command Failed[^\n]+/Command Failed/mig; $l1[$l] =~ s/Version: Verilator[^\n]+/Version: Verilator ###/mig; $l1[$l] =~ s/CPU Time: +[0-9.]+ seconds[^\n]+/CPU Time: ###/mig; $l1[$l] =~ s/\?v=[0-9.]+/?v=latest/mig; # warning URL + $l1[$l] =~ s/DepSet_[a-f0-9]+_/DepSet_#_/mg; if ($l1[$l] =~ s/Exiting due to.*/Exiting due to/mig) { splice @l1, $l+1; # Trunc rest last; diff --git a/test_regress/t/t_xml_debugcheck.out b/test_regress/t/t_xml_debugcheck.out index bd15fe808..2a62297bf 100644 --- a/test_regress/t/t_xml_debugcheck.out +++ b/test_regress/t/t_xml_debugcheck.out @@ -1501,12 +1501,12 @@ - - - - + + + + - + diff --git a/test_regress/t/t_xml_debugcheck.pl b/test_regress/t/t_xml_debugcheck.pl index 4770d909d..392d9a60c 100755 --- a/test_regress/t/t_xml_debugcheck.pl +++ b/test_regress/t/t_xml_debugcheck.pl @@ -21,7 +21,7 @@ compile( make_main => 0, ); -files_identical("$out_filename", $Self->{golden_filename}); +files_identical("$out_filename", $Self->{golden_filename}, 'logfile'); # make sure that certain tags are present in --debug-check # that would not be present in --xml-only From deebfa3239fa0527682d2a5512c71776b2229406 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sun, 25 Jul 2021 11:22:25 -0400 Subject: [PATCH 56/84] Tests: Remove Ubuntu 16.04 as github removes in a month. --- .github/workflows/build.yml | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2575fb44f..4cced10dc 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -29,7 +29,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-20.04, ubuntu-18.04, ubuntu-16.04] + os: [ubuntu-20.04, ubuntu-18.04] compiler: - { cc: clang, cxx: clang++ } - { cc: gcc, cxx: g++ } @@ -37,11 +37,9 @@ jobs: exclude: # Build pull requests only with ubuntu-20.04 and without m32 - os: ${{ github.event_name == 'pull_request' && 'ubuntu-18.04' || 'do-not-exclude' }} - - os: ${{ github.event_name == 'pull_request' && 'ubuntu-16.04' || 'do-not-exclude' }} - m32: ${{ github.event_name == 'pull_request' && 1 || 'do-not-exclude' }} # Build -m32 only on ubuntu-20.04 - {os: ubuntu-18.04, m32: 1} - - {os: ubuntu-16.04, m32: 1} runs-on: ${{ matrix.os }} name: Build | ${{ matrix.os }} | ${{ matrix.compiler.cc }} ${{ matrix.m32 && '| -m32' || '' }} env: @@ -92,7 +90,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-20.04, ubuntu-18.04, ubuntu-16.04] + os: [ubuntu-20.04, ubuntu-18.04] compiler: - { cc: clang, cxx: clang++ } - { cc: gcc, cxx: g++ } @@ -101,11 +99,9 @@ jobs: exclude: # Build pull requests only with ubuntu-20.04 and without m32 - os: ${{ github.event_name == 'pull_request' && 'ubuntu-18.04' || 'do-not-exclude' }} - - os: ${{ github.event_name == 'pull_request' && 'ubuntu-16.04' || 'do-not-exclude' }} - m32: ${{ github.event_name == 'pull_request' && 1 || 'do-not-exclude' }} # Build -m32 only on ubuntu-20.04 - {os: ubuntu-18.04, m32: 1} - - {os: ubuntu-16.04, m32: 1} runs-on: ${{ matrix.os }} name: Test | ${{ matrix.os }} | ${{ matrix.compiler.cc }} | ${{ matrix.suite }} ${{ matrix.m32 && '| -m32' || '' }} env: From cdeb6e792f4beb1faa96523b6f37f69d23b5e4fd Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Sun, 25 Jul 2021 16:10:55 +0100 Subject: [PATCH 57/84] Add --instr-count-dpi option, change default to 200 This replaces the former static AstNode::INSTR_COUNT_DPI, and makes it user adjustable to fit the design. Fixes #3068. --- Changes | 4 +++- bin/verilator | 1 + docs/guide/exe_verilator.rst | 10 ++++++++++ docs/guide/verilating.rst | 6 ++++++ src/V3Ast.h | 1 - src/V3AstNodes.h | 4 +++- src/V3Options.cpp | 4 ++++ src/V3Options.h | 2 ++ test_regress/t/t_flag_instr_count_dpi_bad.pl | 20 ++++++++++++++++++++ 9 files changed, 49 insertions(+), 3 deletions(-) create mode 100755 test_regress/t/t_flag_instr_count_dpi_bad.pl diff --git a/Changes b/Changes index 40413b760..a839034b2 100644 --- a/Changes +++ b/Changes @@ -13,7 +13,9 @@ Verilator 4.211 devel **Minor:** -* Support unpackes array localparams in tasks/functions (#3078). [Geza Lore] +* Support unpacked array localparams in tasks/functions (#3078). [Geza Lore] +* Add --instr-count-dpi to tune assumed DPI import cost for multithreaded + model scheduling. Default value changed to 200 (#3068). [Yinan Xu] * Output files are split based on the set of headers required in order to aid incremental compilation via ccache (#3071). [Geza Lore] * Parameter values are now emitted as 'static constexpr' instead of enum. diff --git a/bin/verilator b/bin/verilator index b8d272440..0a9ceaf71 100755 --- a/bin/verilator +++ b/bin/verilator @@ -333,6 +333,7 @@ detailed descriptions of these arguments. --if-depth Tune IFDEPTH warning +incdir+ Directory to search for includes --inline-mult Tune module inlining + --instr-count-dpi Assumed dynamic instruction count of DPI imports -LDFLAGS Linker pre-object arguments for makefile --l2-name Verilog scope name of the top module --language Default language standard to parse diff --git a/docs/guide/exe_verilator.rst b/docs/guide/exe_verilator.rst index f92024741..c12214f0f 100644 --- a/docs/guide/exe_verilator.rst +++ b/docs/guide/exe_verilator.rst @@ -536,6 +536,14 @@ Summary: times, but potentially faster simulation speed. This setting is ignored for very small modules; they will always be inlined, if allowed. +.. option:: --instr-count-dpi + + Assumed dynamic instruction count of the average DPI import. This is used + by the partitioning algorithm when creating a multithread model. The + default value is 200. Adjusting this to an appropriate value can yield + performance improvements in multithreaded models. Ignored when creating a + single threaded model. + .. option:: -j [] Specify the level of parallelism for :vlopt:`--build`. The must @@ -1044,6 +1052,8 @@ Summary: Verilator assumes DPI pure imports are threadsafe, but non-pure DPI imports are not. + See also :vlopt:`--instr-count-dpi` option. + .. option:: --threads-max-mtasks Rarely needed. When using :vlopt:`--threads`, specify the number of diff --git a/docs/guide/verilating.rst b/docs/guide/verilating.rst index d1231a314..b33dd4acd 100644 --- a/docs/guide/verilating.rst +++ b/docs/guide/verilating.rst @@ -216,6 +216,12 @@ be done by a "main thread". In most cases the eval thread and main thread are the same thread (i.e. the user's top C++ testbench runs on a single thread), but this is not required. +When making frequent use of DPI imported functions in a multi-threaded +model, it may be beneficial to performance to adjust the +:vlopt:`--instr-count-dpi` option based on some experimentation. This +influences the partitioning of the model by adjusting the assumed execution +time of DPI imports. + The :vlopt:`--trace-threads` options can be used to produce trace dumps using multiple threads. If :vlopt:`--trace-threads` is set without :vlopt:`--threads`, then :vlopt:`--trace-threads` will imply diff --git a/src/V3Ast.h b/src/V3Ast.h index 63d86ae53..13e4f5020 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -1524,7 +1524,6 @@ public: static constexpr int INSTR_COUNT_STR = 100; // String ops static constexpr int INSTR_COUNT_TIME = INSTR_COUNT_CALL + 5; // Determine simulation time static constexpr int INSTR_COUNT_PLI = 20; // PLI routines - static constexpr int INSTR_COUNT_DPI = 1000; // DPI import function // ACCESSORS virtual string name() const { return ""; } diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index 6fb566b7f..cdf54bd7a 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -8788,7 +8788,9 @@ public: } // virtual void name(const string& name) override { m_name = name; } - virtual int instrCount() const override { return dpiImportPrototype() ? INSTR_COUNT_DPI : 0; } + virtual int instrCount() const override { + return dpiImportPrototype() ? v3Global.opt.instrCountDpi() : 0; + } VBoolOrUnknown isConst() const { return m_isConst; } void isConst(bool flag) { m_isConst.setTrueOrFalse(flag); } void isConst(VBoolOrUnknown flag) { m_isConst = flag; } diff --git a/src/V3Options.cpp b/src/V3Options.cpp index 6677c795c..6b4fc0207 100644 --- a/src/V3Options.cpp +++ b/src/V3Options.cpp @@ -1101,6 +1101,10 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char DECL_OPTION("-if-depth", Set, &m_ifDepth); DECL_OPTION("-ignc", OnOff, &m_ignc); DECL_OPTION("-inline-mult", Set, &m_inlineMult); + DECL_OPTION("-instr-count-dpi", CbVal, [this, fl](int val) { + m_instrCountDpi = val; + if (m_instrCountDpi < 0) fl->v3fatal("--instr-count-dpi must be non-negative: " << val); + }); DECL_OPTION("-LDFLAGS", CbVal, callStrSetter(&V3Options::addLdLibs)); const auto setLang = [this, fl](const char* valp) { diff --git a/src/V3Options.h b/src/V3Options.h index 6d42ccabe..acea42e08 100644 --- a/src/V3Options.h +++ b/src/V3Options.h @@ -289,6 +289,7 @@ private: int m_gateStmts = 100; // main switch: --gate-stmts int m_ifDepth = 0; // main switch: --if-depth int m_inlineMult = 2000; // main switch: --inline-mult + int m_instrCountDpi = 200; // main switch: --instr-count-dpi VOptionBool m_makeDepend; // main switch: -MMD int m_maxNumWidth = 65536; // main switch: --max-num-width int m_moduleRecursion = 100; // main switch: --module-recursion-depth @@ -489,6 +490,7 @@ public: int gateStmts() const { return m_gateStmts; } int ifDepth() const { return m_ifDepth; } int inlineMult() const { return m_inlineMult; } + int instrCountDpi() const { return m_instrCountDpi; } VOptionBool makeDepend() const { return m_makeDepend; } int maxNumWidth() const { return m_maxNumWidth; } int moduleRecursionDepth() const { return m_moduleRecursion; } diff --git a/test_regress/t/t_flag_instr_count_dpi_bad.pl b/test_regress/t/t_flag_instr_count_dpi_bad.pl new file mode 100755 index 000000000..33b76e374 --- /dev/null +++ b/test_regress/t/t_flag_instr_count_dpi_bad.pl @@ -0,0 +1,20 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 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 + +scenarios(vlt => 1); + +compile( + verilator_flags2 => ["--instr-count-dpi -1"], + fails => 1, + expect => "%Error: --instr-count-dpi must be non-negative: -1" + ); + +ok(1); +1; From b8e804f05bd3262a757a7a84b4e03652e02781dd Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sun, 25 Jul 2021 13:38:27 -0400 Subject: [PATCH 58/84] Internals: Some clang-tidy cleanups. No functional change intended. --- include/verilated.cpp | 12 ++++++------ include/verilated_cov.cpp | 5 +++-- include/verilated_dpi.cpp | 4 ++-- include/verilated_fst_c.cpp | 30 ++++++++++++++++-------------- include/verilated_imp.h | 2 +- include/verilated_threads.cpp | 3 ++- include/verilated_vcd_c.cpp | 11 ++++++----- include/verilated_vpi.cpp | 23 +++++++++++++---------- src/V3Active.cpp | 6 +++--- src/V3Ast.h | 2 +- src/V3Branch.cpp | 4 ++-- src/V3Const.cpp | 19 +++++++++++-------- src/V3EmitCConstInit.h | 4 ++-- src/V3EmitCFunc.h | 2 +- src/V3EmitCMain.cpp | 2 ++ src/V3LinkInc.cpp | 1 - src/V3MergeCond.cpp | 6 +++--- src/V3Number.cpp | 8 ++++---- src/V3Param.cpp | 2 +- src/V3Simulate.h | 1 + src/V3SplitVar.cpp | 3 ++- src/V3Stats.h | 6 ------ src/V3TSP.cpp | 2 +- src/V3Task.cpp | 8 ++++---- 24 files changed, 87 insertions(+), 79 deletions(-) diff --git a/include/verilated.cpp b/include/verilated.cpp index e01f32a78..e7320e50d 100644 --- a/include/verilated.cpp +++ b/include/verilated.cpp @@ -215,7 +215,7 @@ std::string _vl_string_vprintf(const char* formatp, va_list ap) VL_MT_SAFE { char* const bufp = new char[len + 1]; VL_VSNPRINTF(bufp, len + 1, formatp, ap); - const std::string out{bufp, len}; + std::string out{bufp, len}; // Not const to allow move optimization delete[] bufp; return out; } @@ -970,14 +970,14 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA } static inline bool _vl_vsss_eof(FILE* fp, int floc) VL_MT_SAFE { - if (fp) { + if (VL_LIKELY(fp)) { return std::feof(fp) ? true : false; // true : false to prevent MSVC++ warning } else { return floc < 0; } } static inline void _vl_vsss_advance(FILE* fp, int& floc) VL_MT_SAFE { - if (fp) { + if (VL_LIKELY(fp)) { std::fgetc(fp); } else { floc -= 8; @@ -986,7 +986,7 @@ static inline void _vl_vsss_advance(FILE* fp, int& floc) VL_MT_SAFE { static inline int _vl_vsss_peek(FILE* fp, int& floc, const WDataInP fromp, const std::string& fstr) VL_MT_SAFE { // Get a character without advancing - if (fp) { + if (VL_LIKELY(fp)) { const int data = std::fgetc(fp); if (data == EOF) return EOF; ungetc(data, fp); @@ -2259,7 +2259,7 @@ VerilatedContext::VerilatedContext() Verilated::threadContextp(this); m_ns.m_profThreadsFilename = "profile_threads.dat"; m_fdps.resize(31); - std::fill(m_fdps.begin(), m_fdps.end(), (FILE*)0); + std::fill(m_fdps.begin(), m_fdps.end(), static_cast(nullptr)); m_fdFreeMct.resize(30); for (std::size_t i = 0, id = 1; i < m_fdFreeMct.size(); ++i, ++id) m_fdFreeMct[i] = id; } @@ -2422,7 +2422,7 @@ void VerilatedContext::internalsDump() const VL_MT_SAFE { void VerilatedContextImp::commandArgsAddGuts(int argc, const char** argv) VL_REQUIRES(m_argMutex) { if (!m_args.m_argVecLoaded) m_args.m_argVec.clear(); for (int i = 0; i < argc; ++i) { - m_args.m_argVec.push_back(argv[i]); + m_args.m_argVec.emplace_back(argv[i]); commandArgVl(argv[i]); } m_args.m_argVecLoaded = true; // Can't just test later for empty vector, no arguments is ok diff --git a/include/verilated_cov.cpp b/include/verilated_cov.cpp index 78757fc4a..9f04c5429 100644 --- a/include/verilated_cov.cpp +++ b/include/verilated_cov.cpp @@ -143,8 +143,9 @@ private: std::string rtn; for (const char* pos = text.c_str(); *pos; ++pos) { if (!std::isprint(*pos) || *pos == '%' || *pos == '"') { - char hex[10]; - VL_SNPRINTF(hex, 10, "%%%02X", pos[0]); + constexpr size_t LEN_MAX_HEX = 20; + char hex[LEN_MAX_HEX]; + VL_SNPRINTF(hex, LEN_MAX_HEX, "%%%02X", pos[0]); rtn += hex; } else { rtn += *pos; diff --git a/include/verilated_dpi.cpp b/include/verilated_dpi.cpp index 986de1453..69fbada12 100644 --- a/include/verilated_dpi.cpp +++ b/include/verilated_dpi.cpp @@ -386,7 +386,7 @@ static void _vl_svPutLogicArrElemVecVal(const svOpenArrayHandle d, const svLogic // Return bit from simulator open array static svBit _vl_svGetBitArrElem(const svOpenArrayHandle s, int nargs, int indx1, int indx2, - int indx3, int indx4) VL_MT_SAFE { + int indx3, int) VL_MT_SAFE { // One extra index supported, as need bit number const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(s); void* const datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3); @@ -401,7 +401,7 @@ static svBit _vl_svGetBitArrElem(const svOpenArrayHandle s, int nargs, int indx1 } // Update simulator open array from bit static void _vl_svPutBitArrElem(const svOpenArrayHandle d, svBit value, int nargs, int indx1, - int indx2, int indx3, int indx4) VL_MT_SAFE { + int indx2, int indx3, int) VL_MT_SAFE { // One extra index supported, as need bit number value &= 1; // Make sure clean const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(d); diff --git a/include/verilated_fst_c.cpp b/include/verilated_fst_c.cpp index 4aea35c20..933f8d88e 100644 --- a/include/verilated_fst_c.cpp +++ b/include/verilated_fst_c.cpp @@ -53,29 +53,31 @@ //============================================================================= // Check that vltscope_t matches fstScopeType -static_assert((int)FST_ST_VCD_MODULE == (int)VLT_TRACE_SCOPE_MODULE, + +static_assert(static_cast(FST_ST_VCD_MODULE) == static_cast(VLT_TRACE_SCOPE_MODULE), "VLT_TRACE_SCOPE_MODULE mismatches"); -static_assert((int)FST_ST_VCD_TASK == (int)VLT_TRACE_SCOPE_TASK, +static_assert(static_cast(FST_ST_VCD_TASK) == static_cast(VLT_TRACE_SCOPE_TASK), "VLT_TRACE_SCOPE_TASK mismatches"); -static_assert((int)FST_ST_VCD_FUNCTION == (int)VLT_TRACE_SCOPE_FUNCTION, +static_assert(static_cast(FST_ST_VCD_FUNCTION) == static_cast(VLT_TRACE_SCOPE_FUNCTION), "VLT_TRACE_SCOPE_FUNCTION mismatches"); -static_assert((int)FST_ST_VCD_BEGIN == (int)VLT_TRACE_SCOPE_BEGIN, +static_assert(static_cast(FST_ST_VCD_BEGIN) == static_cast(VLT_TRACE_SCOPE_BEGIN), "VLT_TRACE_SCOPE_BEGIN mismatches"); -static_assert((int)FST_ST_VCD_FORK == (int)VLT_TRACE_SCOPE_FORK, +static_assert(static_cast(FST_ST_VCD_FORK) == static_cast(VLT_TRACE_SCOPE_FORK), "VLT_TRACE_SCOPE_FORK mismatches"); -static_assert((int)FST_ST_VCD_GENERATE == (int)VLT_TRACE_SCOPE_GENERATE, +static_assert(static_cast(FST_ST_VCD_GENERATE) == static_cast(VLT_TRACE_SCOPE_GENERATE), "VLT_TRACE_SCOPE_GENERATE mismatches"); -static_assert((int)FST_ST_VCD_STRUCT == (int)VLT_TRACE_SCOPE_STRUCT, +static_assert(static_cast(FST_ST_VCD_STRUCT) == static_cast(VLT_TRACE_SCOPE_STRUCT), "VLT_TRACE_SCOPE_STRUCT mismatches"); -static_assert((int)FST_ST_VCD_UNION == (int)VLT_TRACE_SCOPE_UNION, +static_assert(static_cast(FST_ST_VCD_UNION) == static_cast(VLT_TRACE_SCOPE_UNION), "VLT_TRACE_SCOPE_UNION mismatches"); -static_assert((int)FST_ST_VCD_CLASS == (int)VLT_TRACE_SCOPE_CLASS, +static_assert(static_cast(FST_ST_VCD_CLASS) == static_cast(VLT_TRACE_SCOPE_CLASS), "VLT_TRACE_SCOPE_CLASS mismatches"); -static_assert((int)FST_ST_VCD_INTERFACE == (int)VLT_TRACE_SCOPE_INTERFACE, +static_assert(static_cast(FST_ST_VCD_INTERFACE) + == static_cast(VLT_TRACE_SCOPE_INTERFACE), "VLT_TRACE_SCOPE_INTERFACE mismatches"); -static_assert((int)FST_ST_VCD_PACKAGE == (int)VLT_TRACE_SCOPE_PACKAGE, +static_assert(static_cast(FST_ST_VCD_PACKAGE) == static_cast(VLT_TRACE_SCOPE_PACKAGE), "VLT_TRACE_SCOPE_PACKAGE mismatches"); -static_assert((int)FST_ST_VCD_PROGRAM == (int)VLT_TRACE_SCOPE_PROGRAM, +static_assert(static_cast(FST_ST_VCD_PROGRAM) == static_cast(VLT_TRACE_SCOPE_PROGRAM), "VLT_TRACE_SCOPE_PROGRAM mismatches"); //============================================================================= @@ -191,8 +193,8 @@ void VerilatedFst::declare(vluint32_t code, const char* name, int dtypenum, fstV tmpModName = *new_it; tmpModName.pop_back(); // If the scope ends with a non-ascii character, it will be 0x80 + fstScopeType - fstWriterSetScope(m_fst, (fstScopeType)(new_it->back() & 0x7f), tmpModName.c_str(), - nullptr); + fstWriterSetScope(m_fst, static_cast(new_it->back() & 0x7f), + tmpModName.c_str(), nullptr); } else fstWriterSetScope(m_fst, FST_ST_VCD_SCOPE, new_it->c_str(), nullptr); diff --git a/include/verilated_imp.h b/include/verilated_imp.h index ed17f86e2..4c30cbafc 100644 --- a/include/verilated_imp.h +++ b/include/verilated_imp.h @@ -178,7 +178,7 @@ public: // FILE* list constructed from a file-descriptor class VerilatedFpList final { - FILE* m_fp[31]; + FILE* m_fp[31] = {}; std::size_t m_sz = 0; public: diff --git a/include/verilated_threads.cpp b/include/verilated_threads.cpp index 99e59e444..606985494 100644 --- a/include/verilated_threads.cpp +++ b/include/verilated_threads.cpp @@ -48,7 +48,8 @@ VlMTaskVertex::VlMTaskVertex(vluint32_t upstreamDepCount) // VlWorkerThread VlWorkerThread::VlWorkerThread(VlThreadPool* poolp, VerilatedContext* contextp, bool profiling) - : m_poolp{poolp} + : m_ready_size{0} + , m_poolp{poolp} , m_profiling{profiling} // Must init this last -- after setting up fields that it might read: , m_exiting{false} , m_cthread{startWorker, this} diff --git a/include/verilated_vcd_c.cpp b/include/verilated_vcd_c.cpp index de7433498..2630967f3 100644 --- a/include/verilated_vcd_c.cpp +++ b/include/verilated_vcd_c.cpp @@ -271,8 +271,9 @@ void VerilatedVcd::printStr(const char* str) { } void VerilatedVcd::printQuad(vluint64_t n) { - char buf[100]; - VL_SNPRINTF(buf, 100, "%" VL_PRI64 "u", n); + constexpr size_t LEN_STR_QUAD = 40; + char buf[LEN_STR_QUAD]; + VL_SNPRINTF(buf, LEN_STR_QUAD, "%" VL_PRI64 "u", n); printStr(buf); } @@ -355,9 +356,9 @@ void VerilatedVcd::dumpHeader() { const time_t tick = time(nullptr); tm ticktm; VL_LOCALTIME_R(&tick, &ticktm); - constexpr int bufsize = 50; - char buf[bufsize]; - std::strftime(buf, bufsize, "%c", &ticktm); + constexpr size_t LEN_BUF = 50; + char buf[LEN_BUF]; + std::strftime(buf, LEN_BUF, "%c", &ticktm); printStr(buf); } printStr(" $end\n"); diff --git a/include/verilated_vpi.cpp b/include/verilated_vpi.cpp index c354d89f1..9c6862158 100644 --- a/include/verilated_vpi.cpp +++ b/include/verilated_vpi.cpp @@ -343,8 +343,9 @@ public: virtual const VerilatedRange* rangep() const override { return &(varp()->packed()); } virtual const char* fullname() const override { static VL_THREAD_LOCAL std::string t_out; - char num[25]; - VL_SNPRINTF(num, 25, "%d", m_index); + constexpr size_t LEN_MAX_INDEX = 25; + char num[LEN_MAX_INDEX]; + VL_SNPRINTF(num, LEN_MAX_INDEX, "%d", m_index); t_out = std::string{scopep()->name()} + "." + name() + "[" + num + "]"; return t_out.c_str(); } @@ -484,7 +485,7 @@ public: m_cbData.obj = m_varo.castVpiHandle(); m_varo.createPrevDatap(); } else { - m_cbData.obj = NULL; + m_cbData.obj = nullptr; } } ~VerilatedVpiCbHolder() = default; @@ -631,12 +632,13 @@ public: void* const prevDatap = varop->prevDatap(); // Was malloced when we added the callback VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: value_test %s v[0]=%d/%d %p %p\n", - varop->fullname(), *((CData*)newDatap), - *((CData*)prevDatap), newDatap, prevDatap);); + varop->fullname(), *(static_cast(newDatap)), + *(static_cast(prevDatap)), newDatap, + prevDatap);); if (std::memcmp(prevDatap, newDatap, varop->entSize()) != 0) { - VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: value_callback %" VL_PRI64 - "d %s v[0]=%d\n", - ho.id(), varop->fullname(), *((CData*)newDatap));); + VL_DEBUG_IF_PLI( + VL_DBG_MSGF("- vpi: value_callback %" VL_PRI64 "d %s v[0]=%d\n", ho.id(), + varop->fullname(), *(static_cast(newDatap)));); update.insert(varop); vpi_get_value(ho.cb_datap()->obj, ho.cb_datap()->value); (ho.cb_rtnp())(ho.cb_datap()); @@ -707,7 +709,8 @@ public: va_end(args); m_errorInfo.state = vpiPLI; t_filehold = file; - setError((PLI_BYTE8*)m_buff, nullptr, const_cast(t_filehold.c_str()), line); + setError(static_cast(m_buff), nullptr, + const_cast(t_filehold.c_str()), line); } p_vpi_error_info getError() { if (m_flag) return &m_errorInfo; @@ -1109,7 +1112,7 @@ const char* VerilatedVpiError::strFromVpiProp(PLI_INT32 vpiVal) VL_MT_SAFE { #define SELF_CHECK_RESULT_CSTR(got, exp) \ if (0 != std::strcmp((got), (exp))) { \ std::string msg \ - = std::string{"%Error: "} + "GOT = '" + got + "'" + " EXP = '" + exp + "'"; \ + = std::string{"%Error: "} + "GOT = '" + (got) + "'" + " EXP = '" + (exp) + "'"; \ VL_FATAL_MT(__FILE__, __LINE__, "", msg.c_str()); \ } diff --git a/src/V3Active.cpp b/src/V3Active.cpp index d00dce620..76adf0e25 100644 --- a/src/V3Active.cpp +++ b/src/V3Active.cpp @@ -67,8 +67,8 @@ public: : V3GraphVertex{graphp} , m_name{name} , m_type{type} {} - virtual string name() const { return m_name + " " + typestr(); } - virtual string dotColor() const { return user() ? "green" : "black"; } + virtual string name() const override { return m_name + " " + typestr(); } + virtual string dotColor() const override { return user() ? "green" : "black"; } virtual int type() const { return m_type; } }; @@ -120,7 +120,7 @@ protected: public: LatchDetectGraph() { clear(); } - ~LatchDetectGraph() { clear(); } + virtual ~LatchDetectGraph() override { clear(); } // ACCESSORS LatchDetectGraphVertex* currentp() { return m_curVertexp; } void currentp(LatchDetectGraphVertex* vertex) { m_curVertexp = vertex; } diff --git a/src/V3Ast.h b/src/V3Ast.h index 13e4f5020..e00b6f34f 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -1716,7 +1716,7 @@ public: VSigning numeric) const; AstNodeDType* findBitRangeDType(const VNumRange& range, int widthMin, VSigning numeric) const; AstNodeDType* findBasicDType(AstBasicDTypeKwd kwd) const; - AstBasicDType* findInsertSameDType(AstBasicDType* nodep); + static AstBasicDType* findInsertSameDType(AstBasicDType* nodep); // METHODS - dump and error void v3errorEnd(std::ostringstream& str) const; diff --git a/src/V3Branch.cpp b/src/V3Branch.cpp index ff3413815..ae8a6f1d8 100644 --- a/src/V3Branch.cpp +++ b/src/V3Branch.cpp @@ -43,8 +43,8 @@ private: AstUser1InUse m_inuser1; // STATE - int m_likely; // Excuses for branch likely taken - int m_unlikely; // Excuses for branch likely not taken + int m_likely = false; // Excuses for branch likely taken + int m_unlikely = false; // Excuses for branch likely not taken std::vector m_cfuncsp; // List of all tasks // METHODS diff --git a/src/V3Const.cpp b/src/V3Const.cpp index 858a043fa..1a2148fc9 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -164,10 +164,11 @@ class ConstBitOpTreeVisitor final : public AstNVisitor { "bit:" << bit << " newWidth:" << newWidth); m_bitPolarity.setAllBitsX(); for (int i = 0; i < oldPol.width(); ++i) { - if (oldPol.bitIs0(i)) + if (oldPol.bitIs0(i)) { m_bitPolarity.setBit(i, '0'); - else if (oldPol.bitIs1(i)) + } else if (oldPol.bitIs1(i)) { m_bitPolarity.setBit(i, '1'); + } } } UASSERT_OBJ(bit < m_bitPolarity.width(), m_refp, @@ -306,19 +307,20 @@ class ConstBitOpTreeVisitor final : public AstNVisitor { } bool ok = !m_failed; - if (expectConst) + if (expectConst) { ok &= !info.m_refp && info.m_constp; - else + } else { ok &= info.m_refp && !info.m_constp; + } return ok ? info : LeafInfo{}; } AstNode* combineTree(AstNode* lhsp, AstNode* rhsp) { if (!lhsp) return rhsp; - if (isAndTree()) + if (isAndTree()) { return new AstAnd(m_rootp->fileline(), lhsp, rhsp); - else if (isOrTree()) + } else if (isOrTree()) { return new AstOr(m_rootp->fileline(), lhsp, rhsp); - else { + } else { UASSERT_OBJ(isXorTree(), m_rootp, "must be either Xor or RedXor"); return new AstXor(m_rootp->fileline(), lhsp, rhsp); } @@ -836,7 +838,8 @@ private: } else if (AstShiftL* const shiftp = VN_CAST(nodep->rhsp(), ShiftL)) { if (const AstConst* scp = VN_CAST_CONST(shiftp->rhsp(), Const)) { // Check if mask is full over the non-zero bits - V3Number maskLo(nodep, nodep->width()), maskHi(nodep, nodep->width()); + V3Number maskLo(nodep, nodep->width()); + V3Number maskHi(nodep, nodep->width()); maskLo.setMask(nodep->width() - scp->num().toUInt()); maskHi.opShiftL(maskLo, scp->num()); return checkMask(maskHi); diff --git a/src/V3EmitCConstInit.h b/src/V3EmitCConstInit.h index 1cabd9f03..386926b63 100644 --- a/src/V3EmitCConstInit.h +++ b/src/V3EmitCConstInit.h @@ -57,7 +57,7 @@ protected: puts("\n"); for (uint32_t n = 0; n < size; ++n) { m_unpackedWord = n; - if (n) puts(n % tabMod ? ", " : ",\n"); + if (n) puts((n % tabMod) ? ", " : ",\n"); iterate(nodep->getIndexDefaultedValuep(n)); } puts("\n"); @@ -87,7 +87,7 @@ protected: if (m_inUnpacked) puts(" // VlWide " + cvtToStr(m_unpackedWord)); puts("\n"); for (uint32_t n = 0; n < size; ++n) { - if (n) puts(n % 4 ? ", " : ",\n"); + if (n) puts((n % 4) ? ", " : ",\n"); ofp()->printf("0x%08" PRIx32, num.edataWord(n)); } puts("\n"); diff --git a/src/V3EmitCFunc.h b/src/V3EmitCFunc.h index fddf3e348..03c2b30b7 100644 --- a/src/V3EmitCFunc.h +++ b/src/V3EmitCFunc.h @@ -91,7 +91,7 @@ class EmitCLazyDecls final : public AstNVisitor { VL_DEBUG_FUNC; public: - EmitCLazyDecls(EmitCBaseVisitor& emitter) + explicit EmitCLazyDecls(EmitCBaseVisitor& emitter) : m_emitter(emitter) {} void emit(AstNode* nodep) { m_needsBlankLine = false; diff --git a/src/V3EmitCMain.cpp b/src/V3EmitCMain.cpp index e4ace105f..b0475fe40 100644 --- a/src/V3EmitCMain.cpp +++ b/src/V3EmitCMain.cpp @@ -93,6 +93,8 @@ private: puts("topp->final();\n"); puts("return 0;\n"); puts("}\n"); + + m_ofp = nullptr; } }; diff --git a/src/V3LinkInc.cpp b/src/V3LinkInc.cpp index fc949abcd..2317218e7 100644 --- a/src/V3LinkInc.cpp +++ b/src/V3LinkInc.cpp @@ -62,7 +62,6 @@ private: AstNode* m_insStmtp = nullptr; // Where to insert statement bool m_unsupportedHere = false; // Used to detect where it's not supported yet -private: void insertBeforeStmt(AstNode* nodep, AstNode* newp) { // Return node that must be visited, if any // See also AstNode::addBeforeStmt; this predates that function diff --git a/src/V3MergeCond.cpp b/src/V3MergeCond.cpp index 32c7d6da0..62ef02196 100644 --- a/src/V3MergeCond.cpp +++ b/src/V3MergeCond.cpp @@ -109,7 +109,7 @@ private: public: // Remove marks from AstVars (clear user1) - void clear() { AstNode::user1ClearTree(); } + static void clear() { AstNode::user1ClearTree(); } // Mark all AstVars referenced by setting user1 void mark(AstNode* node) { iterate(node); } @@ -142,7 +142,7 @@ private: // it is in a supported position, which are: // - RHS is the Cond // - RHS is And(Const, Cond). This And is inserted often by V3Clean. - AstNodeCond* extractCond(AstNode* rhsp) { + static AstNodeCond* extractCond(AstNode* rhsp) { if (AstNodeCond* const condp = VN_CAST(rhsp, NodeCond)) { return condp; } else if (AstAnd* const andp = VN_CAST(rhsp, And)) { @@ -160,7 +160,7 @@ private: // wide, but working that out here is a bit difficult. As this masking is // rarely required (only when trying to merge a "cond & value" with an // earlier ternary), we will just always mask it for safety. - AstNode* maskLsb(AstNode* nodep) { + static AstNode* maskLsb(AstNode* nodep) { AstNode* const maskp = new AstConst(nodep->fileline(), AstConst::BitTrue()); return new AstAnd(nodep->fileline(), nodep, maskp); } diff --git a/src/V3Number.cpp b/src/V3Number.cpp index fe0197718..64d5a8fe2 100644 --- a/src/V3Number.cpp +++ b/src/V3Number.cpp @@ -422,22 +422,22 @@ V3Number& V3Number::setSingleBits(char value) { } V3Number& V3Number::setAllBits0() { - for (int i = 0; i < words(); i++) { m_value[i] = {0, 0}; } + for (int i = 0; i < words(); i++) m_value[i] = {0, 0}; return *this; } V3Number& V3Number::setAllBits1() { - for (int i = 0; i < words(); i++) { m_value[i] = {~0u, 0}; } + for (int i = 0; i < words(); i++) m_value[i] = {~0U, 0}; opCleanThis(); return *this; } V3Number& V3Number::setAllBitsX() { // Use setAllBitsXRemoved if calling this based on a non-X/Z input value such as divide by zero - for (int i = 0; i < words(); i++) { m_value[i] = {~0u, ~0u}; } + for (int i = 0; i < words(); i++) m_value[i] = {~0U, ~0U}; opCleanThis(); return *this; } V3Number& V3Number::setAllBitsZ() { - for (int i = 0; i < words(); i++) { m_value[i] = {0, ~0u}; } + for (int i = 0; i < words(); i++) m_value[i] = {0, ~0U}; opCleanThis(); return *this; } diff --git a/src/V3Param.cpp b/src/V3Param.cpp index 733cb33d1..56175a280 100644 --- a/src/V3Param.cpp +++ b/src/V3Param.cpp @@ -268,7 +268,7 @@ class ParamProcessor final { // METHODS VL_DEBUG_FUNC; // Declare debug() - void makeSmallNames(AstNodeModule* modp) { + static void makeSmallNames(AstNodeModule* modp) { std::vector usedLetter; usedLetter.resize(256); // Pass 1, assign first letter to each gparam's name diff --git a/src/V3Simulate.h b/src/V3Simulate.h index 4fe3444ab..0ded54fe8 100644 --- a/src/V3Simulate.h +++ b/src/V3Simulate.h @@ -991,6 +991,7 @@ private: } } SimStackNode stackNode(nodep, &tconnects); + // cppcheck-suppress danglingLifetime m_callStack.push_back(&stackNode); // Clear output variable if (auto* const basicp = VN_CAST(funcp->fvarp(), Var)->basicp()) { diff --git a/src/V3SplitVar.cpp b/src/V3SplitVar.cpp index d47b198d6..b06f0ec76 100644 --- a/src/V3SplitVar.cpp +++ b/src/V3SplitVar.cpp @@ -1231,9 +1231,10 @@ public: } else { reason = "its type is unknown"; // LCOV_EXCL_LINE } - if (reason) + if (reason) { UINFO(5, "Check " << nodep->prettyNameQ() << " cannot split because" << reason << endl); + } return reason; } VL_DEBUG_FUNC; // Declare debug() diff --git a/src/V3Stats.h b/src/V3Stats.h index 1b6c785c7..b340cf0a9 100644 --- a/src/V3Stats.h +++ b/src/V3Stats.h @@ -24,12 +24,6 @@ class AstNetlist; -#define STAT_ADD_UINFO(level, text, value) \ - do { \ - UINFO((level), "addStat " << text << " " << value << endl); \ - V3Stats::addStat(text, value); \ - } while (0) - //============================================================================ class VDouble0 final { diff --git a/src/V3TSP.cpp b/src/V3TSP.cpp index eb5a53a64..4ea0ae3a4 100644 --- a/src/V3TSP.cpp +++ b/src/V3TSP.cpp @@ -499,7 +499,7 @@ public: : m_xpos{xpos} , m_ypos{ypos} , m_serial{++s_serialNext} {} - ~TspTestState() = default; + virtual ~TspTestState() override = default; virtual int cost(const TspStateBase* otherp) const override { return cost(dynamic_cast(otherp)); } diff --git a/src/V3Task.cpp b/src/V3Task.cpp index 3bf94dcb0..7dce81e29 100644 --- a/src/V3Task.cpp +++ b/src/V3Task.cpp @@ -689,13 +689,13 @@ private: return dpiproto; } - AstNode* createDpiTemp(AstVar* portp, const string& suffix) { + static AstNode* createDpiTemp(AstVar* portp, const string& suffix) { const string stmt = portp->dpiTmpVarType(portp->name() + suffix) + ";\n"; return new AstCStmt(portp->fileline(), stmt); } - AstNode* createAssignInternalToDpi(AstVar* portp, bool isPtr, const string& frSuffix, - const string& toSuffix) { + static AstNode* createAssignInternalToDpi(AstVar* portp, bool isPtr, const string& frSuffix, + const string& toSuffix) { const string stmt = V3Task::assignInternalToDpi(portp, isPtr, frSuffix, toSuffix); return new AstCStmt(portp->fileline(), stmt); } @@ -948,7 +948,7 @@ private: } } - void makePortList(AstNodeFTask* nodep, AstCFunc* dpip) { + static void makePortList(AstNodeFTask* nodep, AstCFunc* dpip) { // Copy nodep's list of function I/O to the new dpip c function for (AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp = stmtp->nextp()) { if (AstVar* portp = VN_CAST(stmtp, Var)) { From 3ec3c2c268b767a0051849b5bf60a1251eb58c11 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Mon, 26 Jul 2021 18:30:25 +0100 Subject: [PATCH 59/84] Internals: Cleanup V3Expand - Use C++ initialization syntax - Add const - Make members static where appropriate - Factor out repeated _->fileline() sub-expressions No functional change. --- src/V3Expand.cpp | 578 ++++++++++++++++++++++------------------------- 1 file changed, 272 insertions(+), 306 deletions(-) diff --git a/src/V3Expand.cpp b/src/V3Expand.cpp index 8696c02fa..d62930a47 100644 --- a/src/V3Expand.cpp +++ b/src/V3Expand.cpp @@ -64,49 +64,49 @@ private: } } - int longOrQuadWidth(AstNode* nodep) { + static int longOrQuadWidth(AstNode* nodep) { return (nodep->width() + (VL_EDATASIZE - 1)) & ~(VL_EDATASIZE - 1); } - V3Number notWideMask(AstNode* nodep) { + static V3Number notWideMask(AstNode* nodep) { return V3Number(nodep, VL_EDATASIZE, ~VL_MASK_E(nodep->widthMin())); } - V3Number wordMask(AstNode* nodep) { + static V3Number wordMask(AstNode* nodep) { if (nodep->isWide()) { - return V3Number(nodep, VL_EDATASIZE, VL_MASK_E(nodep->widthMin())); + return V3Number{nodep, VL_EDATASIZE, VL_MASK_E(nodep->widthMin())}; } else { - V3Number mask(nodep, longOrQuadWidth(nodep)); + V3Number mask{nodep, longOrQuadWidth(nodep)}; mask.setMask(nodep->widthMin()); return mask; } } - void insertBefore(AstNode* placep, AstNode* newp) { + static void insertBefore(AstNode* placep, AstNode* newp) { newp->user1(1); // Already processed, don't need to re-iterate AstNRelinker linker; placep->unlinkFrBack(&linker); newp->addNext(placep); linker.relink(newp); } - void replaceWithDelete(AstNode* nodep, AstNode* newp) { + static void replaceWithDelete(AstNode* nodep, AstNode* newp) { newp->user1(1); // Already processed, don't need to re-iterate nodep->replaceWith(newp); VL_DO_DANGLING(nodep->deleteTree(), nodep); } - AstNode* newWordAssign(AstNodeAssign* placep, int word, AstNode* lhsp, AstNode* rhsp) { - AstAssign* newp = new AstAssign(placep->fileline(), - new AstWordSel(placep->fileline(), lhsp->cloneTree(true), - new AstConst(placep->fileline(), word)), - rhsp); - return newp; + static AstNode* newWordAssign(AstNodeAssign* placep, int word, AstNode* lhsp, AstNode* rhsp) { + FileLine* const fl = placep->fileline(); + return new AstAssign{fl, + new AstWordSel{fl, lhsp->cloneTree(true), + new AstConst{fl, static_cast(word)}}, + rhsp}; } - void addWordAssign(AstNodeAssign* placep, int word, AstNode* lhsp, AstNode* rhsp) { + static void addWordAssign(AstNodeAssign* placep, int word, AstNode* lhsp, AstNode* rhsp) { insertBefore(placep, newWordAssign(placep, word, lhsp, rhsp)); } - void addWordAssign(AstNodeAssign* placep, int word, AstNode* rhsp) { + static void addWordAssign(AstNodeAssign* placep, int word, AstNode* rhsp) { addWordAssign(placep, word, placep->lhsp(), rhsp); } - void fixCloneLvalue(AstNode* nodep) { + static void fixCloneLvalue(AstNode* nodep) { // In AstSel transforms, we call clone() on VarRefs that were lvalues, // but are now being used on the RHS of the assignment if (VN_IS(nodep, VarRef)) VN_CAST(nodep, VarRef)->access(VAccess::READ); @@ -117,84 +117,87 @@ private: if (nodep->op4p()) fixCloneLvalue(nodep->op4p()); } - AstNode* newAstWordSelClone(AstNode* nodep, int word) { + static AstNode* newAstWordSelClone(AstNode* nodep, int word) { // Get the specified word number from a wide array // Or, if it's a long/quad, do appropriate conversion to wide // Concat may pass negative word numbers, that means it wants a zero + FileLine* const fl = nodep->fileline(); if (nodep->isWide() && word >= 0 && word < nodep->widthWords()) { - return new AstWordSel(nodep->fileline(), nodep->cloneTree(true), - new AstConst(nodep->fileline(), word)); + return new AstWordSel{fl, nodep->cloneTree(true), + new AstConst{fl, static_cast(word)}}; } else if (nodep->isQuad() && word == 0) { - AstNode* quadfromp = nodep->cloneTree(true); + AstNode* const quadfromp = nodep->cloneTree(true); quadfromp->dtypeSetBitUnsized(VL_QUADSIZE, quadfromp->widthMin(), VSigning::UNSIGNED); - return new AstCCast(nodep->fileline(), quadfromp, VL_EDATASIZE); + return new AstCCast{fl, quadfromp, VL_EDATASIZE}; } else if (nodep->isQuad() && word == 1) { - AstNode* quadfromp = nodep->cloneTree(true); + AstNode* const quadfromp = nodep->cloneTree(true); quadfromp->dtypeSetBitUnsized(VL_QUADSIZE, quadfromp->widthMin(), VSigning::UNSIGNED); - return new AstCCast(nodep->fileline(), - new AstShiftR(nodep->fileline(), quadfromp, - new AstConst(nodep->fileline(), VL_EDATASIZE), - VL_EDATASIZE), - VL_EDATASIZE); + return new AstCCast{ + fl, new AstShiftR{fl, quadfromp, new AstConst{fl, VL_EDATASIZE}, VL_EDATASIZE}, + VL_EDATASIZE}; } else if (!nodep->isWide() && !nodep->isQuad() && word == 0) { return nodep->cloneTree(true); } else { // Out of bounds - return new AstConst(nodep->fileline(), 0); + return new AstConst{fl, 0}; } } - AstNode* newWordGrabShift(FileLine* fl, int word, AstNode* lhsp, int shift) { + static AstNode* newWordGrabShift(FileLine* fl, int word, AstNode* lhsp, int shift) { // Extract the expression to grab the value for the specified word, if it's the shift // of shift bits from lhsp AstNode* newp; // Negative word numbers requested for lhs when it's "before" what we want. // We get a 0 then. const int othword = word - shift / VL_EDATASIZE; - AstNode* llowp = newAstWordSelClone(lhsp, othword); + AstNode* const llowp = newAstWordSelClone(lhsp, othword); if (const int loffset = VL_BITBIT_E(shift)) { - AstNode* lhip = newAstWordSelClone(lhsp, othword - 1); + AstNode* const lhip = newAstWordSelClone(lhsp, othword - 1); const int nbitsonright = VL_EDATASIZE - loffset; // bits that end up in lword - newp = new AstOr( + newp = new AstOr{ fl, - new AstAnd(fl, new AstConst(fl, AstConst::SizedEData(), VL_MASK_E(loffset)), - new AstShiftR(fl, lhip, new AstConst(fl, nbitsonright), VL_EDATASIZE)), - new AstAnd(fl, new AstConst(fl, AstConst::SizedEData(), ~VL_MASK_E(loffset)), - new AstShiftL(fl, llowp, new AstConst(fl, loffset), VL_EDATASIZE))); + new AstAnd{fl, new AstConst{fl, AstConst::SizedEData(), VL_MASK_E(loffset)}, + new AstShiftR{fl, lhip, + new AstConst{fl, static_cast(nbitsonright)}, + VL_EDATASIZE}}, + new AstAnd{fl, new AstConst{fl, AstConst::SizedEData(), ~VL_MASK_E(loffset)}, + new AstShiftL{fl, llowp, + new AstConst{fl, static_cast(loffset)}, + VL_EDATASIZE}}}; } else { newp = llowp; } return newp; } - AstNode* newWordSel(FileLine* fl, AstNode* fromp, AstNode* lsbp, int wordAdder) { + static AstNode* newWordSel(FileLine* fl, AstNode* fromp, AstNode* lsbp, + uint32_t wordOffset = 0) { // Return equation to get the VL_BITWORD of a constant or non-constant UASSERT_OBJ(fromp->isWide(), fromp, "Only need AstWordSel on wide from's"); - if (wordAdder >= fromp->widthWords()) { + if (wordOffset >= static_cast(fromp->widthWords())) { // e.g. "logic [95:0] var[0]; logic [0] sel; out = var[sel];" // Squash before C++ to avoid getting a C++ compiler warning // (even though code would be unreachable as presumably a // AstCondBound is protecting above this node. - return new AstConst(fl, AstConst::SizedEData(), 0); + return new AstConst{fl, AstConst::SizedEData(), 0}; } else { AstNode* wordp; + FileLine* const lfl = lsbp->fileline(); if (VN_IS(lsbp, Const)) { - wordp = new AstConst(lsbp->fileline(), - wordAdder + VL_BITWORD_E(VN_CAST(lsbp, Const)->toUInt())); + wordp + = new AstConst{lfl, wordOffset + VL_BITWORD_E(VN_CAST(lsbp, Const)->toUInt())}; } else { - wordp = new AstShiftR(lsbp->fileline(), lsbp->cloneTree(true), - new AstConst(lsbp->fileline(), VL_EDATASIZE_LOG2), - VL_EDATASIZE); - if (wordAdder != 0) { - wordp = new AstAdd(lsbp->fileline(), - // This is indexing a arraysel, so a 32 bit constant is fine - new AstConst(lsbp->fileline(), wordAdder), wordp); + wordp = new AstShiftR{lfl, lsbp->cloneTree(true), + new AstConst{lfl, VL_EDATASIZE_LOG2}, VL_EDATASIZE}; + if (wordOffset + != 0) { // This is indexing a arraysel, so a 32 bit constant is fine + wordp = new AstAdd{lfl, new AstConst{lfl, wordOffset}, wordp}; } } - return new AstWordSel(fl, fromp, wordp); + return new AstWordSel{fl, fromp, wordp}; } } - AstNode* dropCondBound(AstNode* nodep) { + static AstNode* dropCondBound(AstNode* nodep) { // Experimental only... // If there's a CONDBOUND safety to keep arrays in bounds, // we're going to AND it to a value that always fits inside a @@ -205,13 +208,14 @@ private: return nodep; } - AstNode* newSelBitBit(AstNode* lsbp) { + static AstNode* newSelBitBit(AstNode* lsbp) { // Return equation to get the VL_BITBIT of a constant or non-constant + FileLine* const fl = lsbp->fileline(); if (VN_IS(lsbp, Const)) { - return new AstConst(lsbp->fileline(), VL_BITBIT_E(VN_CAST(lsbp, Const)->toUInt())); + return new AstConst{fl, VL_BITBIT_E(VN_CAST(lsbp, Const)->toUInt())}; } else { - return new AstAnd(lsbp->fileline(), new AstConst(lsbp->fileline(), VL_EDATASIZE - 1), - dropCondBound(lsbp)->cloneTree(true)); + return new AstAnd{fl, new AstConst{fl, VL_EDATASIZE - 1}, + dropCondBound(lsbp)->cloneTree(true)}; } } @@ -225,10 +229,10 @@ private: rhsp->v3warn(E_UNSUPPORTED, // LCOV_EXCL_LINE // impossible? "Unsupported: 4-state numbers in this context"); } - for (int w = 0; w < nodep->widthWords(); w++) { - addWordAssign( - nodep, w, - new AstConst(nodep->fileline(), AstConst::SizedEData(), rhsp->num().edataWord(w))); + FileLine* const fl = nodep->fileline(); + for (int w = 0; w < nodep->widthWords(); ++w) { + addWordAssign(nodep, w, + new AstConst{fl, AstConst::SizedEData(), rhsp->num().edataWord(w)}); } return true; } @@ -236,7 +240,7 @@ private: bool expandWide(AstNodeAssign* nodep, AstVarRef* rhsp) { UINFO(8, " Wordize ASSIGN(VARREF) " << nodep << endl); if (!doExpand(nodep)) return false; - for (int w = 0; w < nodep->widthWords(); w++) { + for (int w = 0; w < nodep->widthWords(); ++w) { addWordAssign(nodep, w, newAstWordSelClone(rhsp, w)); } return true; @@ -246,7 +250,7 @@ private: UASSERT_OBJ(!VN_IS(nodep->dtypep()->skipRefp(), UnpackArrayDType), nodep, "ArraySel with unpacked arrays should have been removed in V3Slice"); if (!doExpand(nodep)) return false; - for (int w = 0; w < nodep->widthWords(); w++) { + for (int w = 0; w < nodep->widthWords(); ++w) { addWordAssign(nodep, w, newAstWordSelClone(rhsp, w)); } return true; @@ -255,9 +259,9 @@ private: UINFO(8, " Wordize ASSIGN(NOT) " << nodep << endl); // -> {for each_word{ ASSIGN(WORDSEL(wide,#),NOT(WORDSEL(lhs,#))) }} if (!doExpand(nodep)) return false; - for (int w = 0; w < nodep->widthWords(); w++) { - addWordAssign(nodep, w, - new AstNot(rhsp->fileline(), newAstWordSelClone(rhsp->lhsp(), w))); + FileLine* const fl = rhsp->fileline(); + for (int w = 0; w < nodep->widthWords(); ++w) { + addWordAssign(nodep, w, new AstNot{fl, newAstWordSelClone(rhsp->lhsp(), w)}); } return true; } @@ -265,30 +269,33 @@ private: bool expandWide(AstNodeAssign* nodep, AstAnd* rhsp) { UINFO(8, " Wordize ASSIGN(AND) " << nodep << endl); if (!doExpand(nodep)) return false; - for (int w = 0; w < nodep->widthWords(); w++) { + FileLine* const fl = nodep->fileline(); + for (int w = 0; w < nodep->widthWords(); ++w) { addWordAssign(nodep, w, - new AstAnd(nodep->fileline(), newAstWordSelClone(rhsp->lhsp(), w), - newAstWordSelClone(rhsp->rhsp(), w))); + new AstAnd{fl, newAstWordSelClone(rhsp->lhsp(), w), + newAstWordSelClone(rhsp->rhsp(), w)}); } return true; } bool expandWide(AstNodeAssign* nodep, AstOr* rhsp) { UINFO(8, " Wordize ASSIGN(OR) " << nodep << endl); if (!doExpand(nodep)) return false; - for (int w = 0; w < nodep->widthWords(); w++) { + FileLine* const fl = nodep->fileline(); + for (int w = 0; w < nodep->widthWords(); ++w) { addWordAssign(nodep, w, - new AstOr(nodep->fileline(), newAstWordSelClone(rhsp->lhsp(), w), - newAstWordSelClone(rhsp->rhsp(), w))); + new AstOr{fl, newAstWordSelClone(rhsp->lhsp(), w), + newAstWordSelClone(rhsp->rhsp(), w)}); } return true; } bool expandWide(AstNodeAssign* nodep, AstXor* rhsp) { UINFO(8, " Wordize ASSIGN(XOR) " << nodep << endl); if (!doExpand(nodep)) return false; - for (int w = 0; w < nodep->widthWords(); w++) { + FileLine* const fl = nodep->fileline(); + for (int w = 0; w < nodep->widthWords(); ++w) { addWordAssign(nodep, w, - new AstXor(nodep->fileline(), newAstWordSelClone(rhsp->lhsp(), w), - newAstWordSelClone(rhsp->rhsp(), w))); + new AstXor{fl, newAstWordSelClone(rhsp->lhsp(), w), + newAstWordSelClone(rhsp->rhsp(), w)}); } return true; } @@ -296,11 +303,12 @@ private: bool expandWide(AstNodeAssign* nodep, AstNodeCond* rhsp) { UINFO(8, " Wordize ASSIGN(COND) " << nodep << endl); if (!doExpand(nodep)) return false; - for (int w = 0; w < nodep->widthWords(); w++) { + FileLine* const fl = nodep->fileline(); + for (int w = 0; w < nodep->widthWords(); ++w) { addWordAssign(nodep, w, - new AstCond(nodep->fileline(), rhsp->condp()->cloneTree(true), + new AstCond{fl, rhsp->condp()->cloneTree(true), newAstWordSelClone(rhsp->expr1p(), w), - newAstWordSelClone(rhsp->expr2p(), w))); + newAstWordSelClone(rhsp->expr2p(), w)}); } return true; } @@ -312,7 +320,7 @@ private: if (nodep->isWide()) { // See under ASSIGN(EXTEND) } else { - AstNode* lhsp = nodep->lhsp()->unlinkFrBack(); + AstNode* const lhsp = nodep->lhsp()->unlinkFrBack(); AstNode* newp = lhsp; if (nodep->isQuad()) { if (lhsp->isQuad()) { @@ -321,7 +329,7 @@ private: nodep->v3fatalSrc("extending larger thing into smaller?"); } else { UINFO(8, " EXTEND(q<-l) " << nodep << endl); - newp = new AstCCast(nodep->fileline(), lhsp, nodep); + newp = new AstCCast{nodep->fileline(), lhsp, nodep}; } } else { // Long UASSERT_OBJ(!(lhsp->isQuad() || lhsp->isWide()), nodep, @@ -348,97 +356,83 @@ private: // Selection amounts // Check for constant shifts & save some constification work later. // Grab lowest bit(s) - AstNode* lowwordp = newWordSel(nodep->fromp()->fileline(), - nodep->fromp()->cloneTree(true), nodep->lsbp(), 0); + FileLine* const nfl = nodep->fileline(); + FileLine* const lfl = nodep->lsbp()->fileline(); + FileLine* const ffl = nodep->fromp()->fileline(); + AstNode* lowwordp = newWordSel(ffl, nodep->fromp()->cloneTree(true), nodep->lsbp()); if (nodep->isQuad() && !lowwordp->isQuad()) { - lowwordp = new AstCCast(nodep->fileline(), lowwordp, nodep); + lowwordp = new AstCCast{nfl, lowwordp, nodep}; } - AstNode* lowp = new AstShiftR(nodep->fileline(), lowwordp, newSelBitBit(nodep->lsbp()), - nodep->width()); + AstNode* const lowp + = new AstShiftR{nfl, lowwordp, newSelBitBit(nodep->lsbp()), nodep->width()}; // If > 1 bit, we might be crossing the word boundary AstNode* midp = nullptr; - V3Number zero(nodep, longOrQuadWidth(nodep)); if (nodep->widthConst() > 1) { const uint32_t midMsbOffset = std::min(nodep->widthConst(), VL_EDATASIZE) - 1; - AstNode* const midMsbp - = new AstAdd(nodep->lsbp()->fileline(), - new AstConst(nodep->lsbp()->fileline(), midMsbOffset), - nodep->lsbp()->cloneTree(false)); + AstNode* const midMsbp = new AstAdd{lfl, new AstConst{lfl, midMsbOffset}, + nodep->lsbp()->cloneTree(false)}; AstNode* midwordp = // SEL(from,[midwordnum]) - newWordSel(nodep->fromp()->fileline(), nodep->fromp()->cloneTree(true), - midMsbp, 0); + newWordSel(ffl, nodep->fromp()->cloneTree(true), midMsbp, 0); // newWordSel clones the index, so delete it VL_DO_DANGLING(midMsbp->deleteTree(), midMsbp); if (nodep->isQuad() && !midwordp->isQuad()) { - midwordp = new AstCCast(nodep->fileline(), midwordp, nodep); + midwordp = new AstCCast{nfl, midwordp, nodep}; } - AstNode* const midshiftp - = new AstSub(nodep->lsbp()->fileline(), - new AstConst(nodep->lsbp()->fileline(), VL_EDATASIZE), - newSelBitBit(nodep->lsbp())); + AstNode* const midshiftp = new AstSub{lfl, new AstConst{lfl, VL_EDATASIZE}, + newSelBitBit(nodep->lsbp())}; // If we're selecting bit zero, then all 32 bits in the mid word // get shifted << by 32 bits, so ignore them. - midp = new AstCond( - nodep->fileline(), + const V3Number zero{nodep, longOrQuadWidth(nodep)}; + midp = new AstCond{ + nfl, // lsb % VL_EDATASIZE == 0 ? - new AstEq(nodep->fileline(), new AstConst(nodep->fileline(), 0), - newSelBitBit(nodep->lsbp())), + new AstEq{nfl, new AstConst{nfl, 0}, newSelBitBit(nodep->lsbp())}, // 0 : - new AstConst(nodep->fileline(), zero), + new AstConst{nfl, zero}, // midword >> (VL_EDATASIZE - (lbs % VL_EDATASIZE)) - new AstShiftL(nodep->fileline(), midwordp, midshiftp, nodep->width())); + new AstShiftL{nfl, midwordp, midshiftp, nodep->width()}}; } // If > 32 bits, we might be crossing the second word boundary AstNode* hip = nullptr; if (nodep->widthConst() > VL_EDATASIZE) { const uint32_t hiMsbOffset = nodep->widthConst() - 1; - AstNode* const hiMsbp - = new AstAdd(nodep->lsbp()->fileline(), - new AstConst(nodep->lsbp()->fileline(), hiMsbOffset), - nodep->lsbp()->cloneTree(false)); + AstNode* const hiMsbp = new AstAdd{lfl, new AstConst{lfl, hiMsbOffset}, + nodep->lsbp()->cloneTree(false)}; AstNode* hiwordp = // SEL(from,[hiwordnum]) - newWordSel(nodep->fromp()->fileline(), nodep->fromp()->cloneTree(true), hiMsbp, - 0); + newWordSel(ffl, nodep->fromp()->cloneTree(true), hiMsbp); // newWordSel clones the index, so delete it VL_DO_DANGLING(hiMsbp->deleteTree(), hiMsbp); if (nodep->isQuad() && !hiwordp->isQuad()) { - hiwordp = new AstCCast(nodep->fileline(), hiwordp, nodep); + hiwordp = new AstCCast{nfl, hiwordp, nodep}; } - AstNode* const hishiftp - = new AstCond(nodep->fileline(), - // lsb % VL_EDATASIZE == 0 ? - new AstEq(nodep->fileline(), new AstConst(nodep->fileline(), 0), - newSelBitBit(nodep->lsbp())), - // VL_EDATASIZE : - new AstConst(nodep->lsbp()->fileline(), VL_EDATASIZE), - // 64 - (lbs % VL_EDATASIZE) - new AstSub(nodep->lsbp()->fileline(), - new AstConst(nodep->lsbp()->fileline(), 64), - newSelBitBit(nodep->lsbp()))); - hip = new AstShiftL(nodep->fileline(), hiwordp, hishiftp, nodep->width()); + AstNode* const hishiftp = new AstCond{ + nfl, + // lsb % VL_EDATASIZE == 0 ? + new AstEq{nfl, new AstConst{nfl, 0}, newSelBitBit(nodep->lsbp())}, + // VL_EDATASIZE : + new AstConst{lfl, VL_EDATASIZE}, + // 64 - (lbs % VL_EDATASIZE) + new AstSub{lfl, new AstConst{lfl, 64}, newSelBitBit(nodep->lsbp())}}; + hip = new AstShiftL{nfl, hiwordp, hishiftp, nodep->width()}; } AstNode* newp = lowp; - if (midp) newp = new AstOr(nodep->fileline(), midp, newp); - if (hip) newp = new AstOr(nodep->fileline(), hip, newp); + if (midp) newp = new AstOr{nfl, midp, newp}; + if (hip) newp = new AstOr{nfl, hip, newp}; newp->dtypeFrom(nodep); VL_DO_DANGLING(replaceWithDelete(nodep, newp), nodep); } else { // Long/Quad from Long/Quad UINFO(8, " SEL->SHIFT " << nodep << endl); + FileLine* const fl = nodep->fileline(); AstNode* fromp = nodep->fromp()->unlinkFrBack(); - AstNode* lsbp = nodep->lsbp()->unlinkFrBack(); - if (nodep->isQuad() && !fromp->isQuad()) { - fromp = new AstCCast(nodep->fileline(), fromp, nodep); - } - AstNode* newp = new AstShiftR( - nodep->fileline(), fromp, dropCondBound(lsbp), - fromp->width()); // {large}>>32 requires 64-bit shift operation; then cast + AstNode* const lsbp = nodep->lsbp()->unlinkFrBack(); + if (nodep->isQuad() && !fromp->isQuad()) { fromp = new AstCCast{fl, fromp, nodep}; } + // {large}>>32 requires 64-bit shift operation; then cast + AstNode* newp = new AstShiftR{fl, fromp, dropCondBound(lsbp), fromp->width()}; newp->dtypeFrom(fromp); - if (!nodep->isQuad() && fromp->isQuad()) { - newp = new AstCCast(newp->fileline(), newp, nodep); - } + if (!nodep->isQuad() && fromp->isQuad()) { newp = new AstCCast{fl, newp, nodep}; } newp->dtypeFrom(nodep); VL_DO_DANGLING(replaceWithDelete(nodep, newp), nodep); } @@ -450,34 +444,33 @@ private: if (VN_IS(rhsp->lsbp(), Const) && VL_BITBIT_E(rhsp->lsbConst()) == 0) { const int lsb = rhsp->lsbConst(); UINFO(8, " Wordize ASSIGN(SEL,align) " << nodep << endl); - for (int w = 0; w < nodep->widthWords(); w++) { + for (int w = 0; w < nodep->widthWords(); ++w) { addWordAssign(nodep, w, newAstWordSelClone(rhsp->fromp(), w + VL_BITWORD_E(lsb))); } return true; } else { UINFO(8, " Wordize ASSIGN(EXTRACT,misalign) " << nodep << endl); - for (int w = 0; w < nodep->widthWords(); w++) { + FileLine* const nfl = nodep->fileline(); + FileLine* const rfl = rhsp->fileline(); + FileLine* const ffl = rhsp->fromp()->fileline(); + FileLine* const lfl = rhsp->lsbp()->fileline(); + for (int w = 0; w < nodep->widthWords(); ++w) { // Grab lowest bits - AstNode* lowwordp = newWordSel(rhsp->fileline(), rhsp->fromp()->cloneTree(true), - rhsp->lsbp(), w); - AstNode* lowp = new AstShiftR(rhsp->fileline(), lowwordp, - newSelBitBit(rhsp->lsbp()), VL_EDATASIZE); + AstNode* const lowwordp + = newWordSel(rfl, rhsp->fromp()->cloneTree(true), rhsp->lsbp(), w); + AstNode* const lowp + = new AstShiftR{rfl, lowwordp, newSelBitBit(rhsp->lsbp()), VL_EDATASIZE}; // Upper bits - V3Number zero(nodep, VL_EDATASIZE, 0); - AstNode* midwordp = // SEL(from,[1+wordnum]) - newWordSel(rhsp->fromp()->fileline(), rhsp->fromp()->cloneTree(true), - rhsp->lsbp(), w + 1); - AstNode* midshiftp = new AstSub( - rhsp->lsbp()->fileline(), new AstConst(rhsp->lsbp()->fileline(), VL_EDATASIZE), - newSelBitBit(rhsp->lsbp())); - AstNode* midmayp - = new AstShiftL(rhsp->fileline(), midwordp, midshiftp, VL_EDATASIZE); - AstNode* midp - = new AstCond(rhsp->fileline(), - new AstEq(rhsp->fileline(), new AstConst(rhsp->fileline(), 0), - newSelBitBit(rhsp->lsbp())), - new AstConst(rhsp->fileline(), zero), midmayp); - AstNode* newp = new AstOr(nodep->fileline(), midp, lowp); + const V3Number zero{nodep, VL_EDATASIZE, 0}; + AstNode* const midwordp = // SEL(from,[1+wordnum]) + newWordSel(ffl, rhsp->fromp()->cloneTree(true), rhsp->lsbp(), w + 1); + AstNode* const midshiftp + = new AstSub{lfl, new AstConst{lfl, VL_EDATASIZE}, newSelBitBit(rhsp->lsbp())}; + AstNode* const midmayp = new AstShiftL{rfl, midwordp, midshiftp, VL_EDATASIZE}; + AstNode* const midp = new AstCond{ + rfl, new AstEq{rfl, new AstConst{rfl, 0}, newSelBitBit(rhsp->lsbp())}, + new AstConst{rfl, zero}, midmayp}; + AstNode* const newp = new AstOr{nfl, midp, lowp}; addWordAssign(nodep, w, newp); } return true; @@ -491,106 +484,95 @@ private: // rhsp: may be allones and can remove AND NOT gate // lsbp: constant or variable // Yuk. + FileLine* const nfl = nodep->fileline(); + FileLine* const lfl = lhsp->fileline(); const bool destwide = lhsp->fromp()->isWide(); const bool ones = nodep->rhsp()->isAllOnesV(); if (VN_IS(lhsp->lsbp(), Const)) { // The code should work without this constant test, but it won't // constify as nicely as we'd like. AstNode* rhsp = nodep->rhsp()->unlinkFrBack(); - AstNode* destp = lhsp->fromp()->unlinkFrBack(); + AstNode* const destp = lhsp->fromp()->unlinkFrBack(); const int lsb = lhsp->lsbConst(); const int msb = lhsp->msbConst(); - V3Number maskset(nodep, destp->widthMin()); + V3Number maskset{nodep, destp->widthMin()}; for (int bit = lsb; bit < (msb + 1); bit++) maskset.setBit(bit, 1); - V3Number maskold(nodep, destp->widthMin()); + V3Number maskold{nodep, destp->widthMin()}; maskold.opNot(maskset); if (destwide) { UINFO(8, " ASSIGNSEL(const,wide) " << nodep << endl); - for (int w = 0; w < destp->widthWords(); w++) { + for (int w = 0; w < destp->widthWords(); ++w) { if (w >= VL_BITWORD_E(lsb) && w <= VL_BITWORD_E(msb)) { // else we would just be setting it to the same exact value AstNode* oldvalp = newAstWordSelClone(destp, w); fixCloneLvalue(oldvalp); if (!ones) { - oldvalp - = new AstAnd(lhsp->fileline(), - new AstConst(lhsp->fileline(), AstConst::SizedEData(), - maskold.edataWord(w)), - oldvalp); + oldvalp = new AstAnd{ + lfl, + new AstConst{lfl, AstConst::SizedEData(), maskold.edataWord(w)}, + oldvalp}; } // Appropriate word of new value to insert: - AstNode* newp = newWordGrabShift(lhsp->fileline(), w, rhsp, lsb); + AstNode* newp = newWordGrabShift(lfl, w, rhsp, lsb); // Apply cleaning at the top word of the destination // (no cleaning to do if dst's width is a whole number // of words). if (w == destp->widthWords() - 1 && VL_BITBIT_E(destp->widthMin()) != 0) { - V3Number cleanmask(nodep, VL_EDATASIZE); + V3Number cleanmask{nodep, VL_EDATASIZE}; cleanmask.setMask(VL_BITBIT_E(destp->widthMin())); - newp = new AstAnd(lhsp->fileline(), newp, - new AstConst(lhsp->fileline(), cleanmask)); + newp = new AstAnd{lfl, newp, new AstConst{lfl, cleanmask}}; } - addWordAssign(nodep, w, destp, new AstOr(lhsp->fileline(), oldvalp, newp)); + addWordAssign(nodep, w, destp, new AstOr{lfl, oldvalp, newp}); } } VL_DO_DANGLING(rhsp->deleteTree(), rhsp); VL_DO_DANGLING(destp->deleteTree(), destp); } else { UINFO(8, " ASSIGNSEL(const,narrow) " << nodep << endl); - if (destp->isQuad() && !rhsp->isQuad()) { - rhsp = new AstCCast(nodep->fileline(), rhsp, nodep); - } + if (destp->isQuad() && !rhsp->isQuad()) { rhsp = new AstCCast{nfl, rhsp, nodep}; } AstNode* oldvalp = destp->cloneTree(true); fixCloneLvalue(oldvalp); - if (!ones) { - oldvalp = new AstAnd(lhsp->fileline(), new AstConst(lhsp->fileline(), maskold), - oldvalp); - } + if (!ones) { oldvalp = new AstAnd{lfl, new AstConst{lfl, maskold}, oldvalp}; } // The bit-select can refer to bits outside the width of nodep // which we aren't allowed to assign to. This is a mask of the // valid range of nodep which we apply to the new shifted RHS. - V3Number cleanmask(nodep, destp->widthMin()); + V3Number cleanmask{nodep, destp->widthMin()}; cleanmask.setMask(destp->widthMin()); - AstNode* shifted = new AstShiftL( - lhsp->fileline(), rhsp, new AstConst(lhsp->fileline(), lsb), destp->width()); - AstNode* cleaned = new AstAnd(lhsp->fileline(), shifted, - new AstConst(lhsp->fileline(), cleanmask)); - AstNode* newp = new AstOr(lhsp->fileline(), oldvalp, cleaned); - newp = new AstAssign(nodep->fileline(), destp, newp); + AstNode* const shifted = new AstShiftL{ + lfl, rhsp, new AstConst{lfl, static_cast(lsb)}, destp->width()}; + AstNode* const cleaned = new AstAnd{lfl, shifted, new AstConst{lfl, cleanmask}}; + AstNode* const newp = new AstAssign{nfl, destp, new AstOr{lfl, oldvalp, cleaned}}; insertBefore(nodep, newp); } return true; } else { // non-const select offset if (destwide && lhsp->widthConst() == 1) { UINFO(8, " ASSIGNSEL(varlsb,wide,1bit) " << nodep << endl); - AstNode* rhsp = nodep->rhsp()->unlinkFrBack(); - AstNode* destp = lhsp->fromp()->unlinkFrBack(); - AstNode* oldvalp - = newWordSel(lhsp->fileline(), destp->cloneTree(true), lhsp->lsbp(), 0); + AstNode* const rhsp = nodep->rhsp()->unlinkFrBack(); + AstNode* const destp = lhsp->fromp()->unlinkFrBack(); + AstNode* oldvalp = newWordSel(lfl, destp->cloneTree(true), lhsp->lsbp()); fixCloneLvalue(oldvalp); if (!ones) { - oldvalp = new AstAnd( - lhsp->fileline(), - new AstNot( - lhsp->fileline(), - new AstShiftL(lhsp->fileline(), new AstConst(nodep->fileline(), 1), - // newSelBitBit may exceed the MSB of this variable. - // That's ok as we'd just AND with a larger value, - // but oldval would clip the upper bits to sanity - newSelBitBit(lhsp->lsbp()), VL_EDATASIZE)), - oldvalp); + oldvalp = new AstAnd{ + lfl, + new AstNot{ + lfl, new AstShiftL{lfl, new AstConst{nfl, 1}, + // newSelBitBit may exceed the MSB of this variable. + // That's ok as we'd just AND with a larger value, + // but oldval would clip the upper bits to sanity + newSelBitBit(lhsp->lsbp()), VL_EDATASIZE}}, + oldvalp}; } // Restrict the shift amount to 0-31, see bug804. - AstNode* shiftp = new AstAnd(nodep->fileline(), lhsp->lsbp()->cloneTree(true), - new AstConst(nodep->fileline(), VL_EDATASIZE - 1)); - AstNode* newp - = new AstOr(lhsp->fileline(), oldvalp, - new AstShiftL(lhsp->fileline(), rhsp, shiftp, VL_EDATASIZE)); - newp = new AstAssign(nodep->fileline(), - newWordSel(nodep->fileline(), destp, lhsp->lsbp(), 0), newp); + AstNode* const shiftp = new AstAnd{nfl, lhsp->lsbp()->cloneTree(true), + new AstConst{nfl, VL_EDATASIZE - 1}}; + AstNode* const newp = new AstAssign{ + nfl, newWordSel(nfl, destp, lhsp->lsbp()), + new AstOr{lfl, oldvalp, new AstShiftL{lfl, rhsp, shiftp, VL_EDATASIZE}}}; insertBefore(nodep, newp); return true; } else if (destwide) { @@ -609,40 +591,35 @@ private: UINFO(8, " ASSIGNSEL(varlsb,narrow) " << nodep << endl); // nodep->dumpTree(cout, "- old: "); AstNode* rhsp = nodep->rhsp()->unlinkFrBack(); - AstNode* destp = lhsp->fromp()->unlinkFrBack(); + AstNode* const destp = lhsp->fromp()->unlinkFrBack(); AstNode* oldvalp = destp->cloneTree(true); fixCloneLvalue(oldvalp); - V3Number maskwidth(nodep, destp->widthMin()); + V3Number maskwidth{nodep, destp->widthMin()}; for (int bit = 0; bit < lhsp->widthConst(); bit++) maskwidth.setBit(bit, 1); - if (destp->isQuad() && !rhsp->isQuad()) { - rhsp = new AstCCast(nodep->fileline(), rhsp, nodep); - } + if (destp->isQuad() && !rhsp->isQuad()) { rhsp = new AstCCast{nfl, rhsp, nodep}; } if (!ones) { - oldvalp = new AstAnd( - lhsp->fileline(), - new AstNot(lhsp->fileline(), - new AstShiftL(lhsp->fileline(), - new AstConst(nodep->fileline(), maskwidth), - lhsp->lsbp()->cloneTree(true), destp->width())), - oldvalp); + oldvalp = new AstAnd{ + lfl, + new AstNot{lfl, + new AstShiftL{lfl, new AstConst{nfl, maskwidth}, + lhsp->lsbp()->cloneTree(true), destp->width()}}, + oldvalp}; } - AstNode* newp = new AstShiftL(lhsp->fileline(), rhsp, - lhsp->lsbp()->cloneTree(true), destp->width()); + AstNode* newp + = new AstShiftL{lfl, rhsp, lhsp->lsbp()->cloneTree(true), destp->width()}; // Apply cleaning to the new value being inserted. Mask is // slightly wider than necessary to avoid an AND with all ones // being optimized out. No need to clean if destp is // quad-sized as there are no extra bits to contaminate if (destp->widthMin() != 64) { - V3Number cleanmask(nodep, destp->widthMin() + 1); + V3Number cleanmask{nodep, destp->widthMin() + 1}; cleanmask.setMask(destp->widthMin()); - newp = new AstAnd(lhsp->fileline(), newp, - new AstConst(lhsp->fileline(), cleanmask)); + newp = new AstAnd{lfl, newp, new AstConst{lfl, cleanmask}}; } - newp = new AstAssign(nodep->fileline(), destp, - new AstOr(lhsp->fileline(), oldvalp, newp)); + newp = new AstAssign{nfl, destp, new AstOr{lfl, oldvalp, newp}}; // newp->dumpTree(cout, "- new: "); insertBefore(nodep, newp); return true; @@ -657,20 +634,14 @@ private: // See under ASSIGN(WIDE) } else { UINFO(8, " CONCAT " << nodep << endl); + FileLine* const fl = nodep->fileline(); AstNode* lhsp = nodep->lhsp()->unlinkFrBack(); AstNode* rhsp = nodep->rhsp()->unlinkFrBack(); - const int rhsshift = rhsp->widthMin(); - if (nodep->isQuad() && !lhsp->isQuad()) { - lhsp = new AstCCast(nodep->fileline(), lhsp, nodep); - } - if (nodep->isQuad() && !rhsp->isQuad()) { - rhsp = new AstCCast(nodep->fileline(), rhsp, nodep); - } - AstNode* newp = new AstOr(nodep->fileline(), - new AstShiftL(nodep->fileline(), lhsp, - new AstConst(nodep->fileline(), rhsshift), - nodep->width()), - rhsp); + const uint32_t rhsshift = rhsp->widthMin(); + if (nodep->isQuad() && !lhsp->isQuad()) { lhsp = new AstCCast{fl, lhsp, nodep}; } + if (nodep->isQuad() && !rhsp->isQuad()) { rhsp = new AstCCast{fl, rhsp, nodep}; } + AstNode* const newp = new AstOr{ + fl, new AstShiftL{fl, lhsp, new AstConst{fl, rhsshift}, nodep->width()}, rhsp}; newp->dtypeFrom(nodep); // Unsigned VL_DO_DANGLING(replaceWithDelete(nodep, newp), nodep); } @@ -678,17 +649,17 @@ private: bool expandWide(AstNodeAssign* nodep, AstConcat* rhsp) { UINFO(8, " Wordize ASSIGN(CONCAT) " << nodep << endl); if (!doExpand(rhsp)) return false; + FileLine* const fl = rhsp->fileline(); // Lhs or Rhs may be word, long, or quad. // newAstWordSelClone nicely abstracts the difference. const int rhsshift = rhsp->rhsp()->widthMin(); // Sometimes doing the words backwards is preferable. // When we have x={x,foo} backwards is better, when x={foo,x} forward is better // However V3Subst tends to rip this up, so not worth optimizing now. - for (int w = 0; w < rhsp->widthWords(); w++) { + for (int w = 0; w < rhsp->widthWords(); ++w) { addWordAssign(nodep, w, - new AstOr(rhsp->fileline(), - newWordGrabShift(rhsp->fileline(), w, rhsp->lhsp(), rhsshift), - newAstWordSelClone(rhsp->rhsp(), w))); + new AstOr{fl, newWordGrabShift(fl, w, rhsp->lhsp(), rhsshift), + newAstWordSelClone(rhsp->rhsp(), w)}); } return true; } @@ -699,29 +670,29 @@ private: if (nodep->isWide()) { // See under ASSIGN(WIDE) } else { + FileLine* const fl = nodep->fileline(); AstNode* lhsp = nodep->lhsp()->unlinkFrBack(); AstNode* newp; const int lhswidth = lhsp->widthMin(); if (lhswidth == 1) { UINFO(8, " REPLICATE(w1) " << nodep << endl); - newp = new AstNegate(nodep->fileline(), lhsp); + newp = new AstNegate{fl, lhsp}; } else { UINFO(8, " REPLICATE " << nodep << endl); const AstConst* constp = VN_CAST(nodep->rhsp(), Const); UASSERT_OBJ(constp, nodep, "Replication value isn't a constant. Checked earlier!"); uint32_t times = constp->toUInt(); - if (nodep->isQuad() && !lhsp->isQuad()) { - lhsp = new AstCCast(nodep->fileline(), lhsp, nodep); - } + if (nodep->isQuad() && !lhsp->isQuad()) { lhsp = new AstCCast{fl, lhsp, nodep}; } newp = lhsp->cloneTree(true); for (unsigned repnum = 1; repnum < times; repnum++) { const int rhsshift = repnum * lhswidth; - newp = new AstOr(nodep->fileline(), - new AstShiftL(nodep->fileline(), lhsp->cloneTree(true), - new AstConst(nodep->fileline(), rhsshift), - nodep->width()), - newp); + newp = new AstOr{ + fl, + new AstShiftL{fl, lhsp->cloneTree(true), + new AstConst{fl, static_cast(rhsshift)}, + nodep->width()}, + newp}; newp->dtypeFrom(nodep); // Unsigned } VL_DO_DANGLING(lhsp->deleteTree(), lhsp); // Never used @@ -733,23 +704,23 @@ private: bool expandWide(AstNodeAssign* nodep, AstReplicate* rhsp) { UINFO(8, " Wordize ASSIGN(REPLICATE) " << nodep << endl); if (!doExpand(rhsp)) return false; - AstNode* lhsp = rhsp->lhsp(); + FileLine* const fl = nodep->fileline(); + AstNode* const lhsp = rhsp->lhsp(); const int lhswidth = lhsp->widthMin(); - const AstConst* constp = VN_CAST(rhsp->rhsp(), Const); + const AstConst* const constp = VN_CAST(rhsp->rhsp(), Const); UASSERT_OBJ(constp, rhsp, "Replication value isn't a constant. Checked earlier!"); - uint32_t times = constp->toUInt(); - for (int w = 0; w < rhsp->widthWords(); w++) { + const uint32_t times = constp->toUInt(); + for (int w = 0; w < rhsp->widthWords(); ++w) { AstNode* newp; if (lhswidth == 1) { - newp = new AstNegate(nodep->fileline(), lhsp->cloneTree(true)); - newp->dtypeSetLogicSized(VL_EDATASIZE, - VSigning::UNSIGNED); // Replicate always unsigned + newp = new AstNegate{fl, lhsp->cloneTree(true)}; + // Replicate always unsigned + newp->dtypeSetLogicSized(VL_EDATASIZE, VSigning::UNSIGNED); } else { newp = newAstWordSelClone(lhsp, w); + FileLine* const rfl = rhsp->fileline(); for (unsigned repnum = 1; repnum < times; repnum++) { - newp = new AstOr( - nodep->fileline(), - newWordGrabShift(rhsp->fileline(), w, lhsp, lhswidth * repnum), newp); + newp = new AstOr{fl, newWordGrabShift(rfl, w, lhsp, lhswidth * repnum), newp}; } } addWordAssign(nodep, w, newp); @@ -762,11 +733,12 @@ private: iterateChildren(nodep); UINFO(8, " Wordize ChangeXor " << nodep << endl); // -> (0=={or{for each_word{WORDSEL(lhs,#)^WORDSEL(rhs,#)}}} + FileLine* const fl = nodep->fileline(); AstNode* newp = nullptr; - for (int w = 0; w < nodep->lhsp()->widthWords(); w++) { - AstNode* eqp = new AstXor(nodep->fileline(), newAstWordSelClone(nodep->lhsp(), w), - newAstWordSelClone(nodep->rhsp(), w)); - newp = (newp == nullptr) ? eqp : (new AstOr(nodep->fileline(), newp, eqp)); + for (int w = 0; w < nodep->lhsp()->widthWords(); ++w) { + AstNode* const eqp = new AstXor{fl, newAstWordSelClone(nodep->lhsp(), w), + newAstWordSelClone(nodep->rhsp(), w)}; + newp = newp ? new AstOr{fl, newp, eqp} : eqp; } VL_DO_DANGLING(replaceWithDelete(nodep, newp), nodep); } @@ -777,19 +749,17 @@ private: if (nodep->lhsp()->isWide()) { UINFO(8, " Wordize EQ/NEQ " << nodep << endl); // -> (0=={or{for each_word{WORDSEL(lhs,#)^WORDSEL(rhs,#)}}} + FileLine* const fl = nodep->fileline(); AstNode* newp = nullptr; - for (int w = 0; w < nodep->lhsp()->widthWords(); w++) { - AstNode* eqp = new AstXor(nodep->fileline(), newAstWordSelClone(nodep->lhsp(), w), - newAstWordSelClone(nodep->rhsp(), w)); - newp = (newp == nullptr) ? eqp : (new AstOr(nodep->fileline(), newp, eqp)); + for (int w = 0; w < nodep->lhsp()->widthWords(); ++w) { + AstNode* const eqp = new AstXor{fl, newAstWordSelClone(nodep->lhsp(), w), + newAstWordSelClone(nodep->rhsp(), w)}; + newp = newp ? new AstOr{fl, newp, eqp} : eqp; } if (VN_IS(nodep, Neq)) { - newp - = new AstNeq(nodep->fileline(), - new AstConst(nodep->fileline(), AstConst::SizedEData(), 0), newp); + newp = new AstNeq{fl, new AstConst{fl, AstConst::SizedEData(), 0}, newp}; } else { - newp = new AstEq(nodep->fileline(), - new AstConst(nodep->fileline(), AstConst::SizedEData(), 0), newp); + newp = new AstEq{fl, new AstConst{fl, AstConst::SizedEData(), 0}, newp}; } VL_DO_DANGLING(replaceWithDelete(nodep, newp), nodep); } @@ -800,57 +770,52 @@ private: virtual void visit(AstRedOr* nodep) override { if (nodep->user1SetOnce()) return; // Process once iterateChildren(nodep); + FileLine* const fl = nodep->fileline(); if (nodep->lhsp()->isWide()) { UINFO(8, " Wordize REDOR " << nodep << endl); // -> (0!={or{for each_word{WORDSEL(lhs,#)}}} AstNode* newp = nullptr; - for (int w = 0; w < nodep->lhsp()->widthWords(); w++) { - AstNode* eqp = newAstWordSelClone(nodep->lhsp(), w); - newp = (newp == nullptr) ? eqp : (new AstOr(nodep->fileline(), newp, eqp)); + for (int w = 0; w < nodep->lhsp()->widthWords(); ++w) { + AstNode* const eqp = newAstWordSelClone(nodep->lhsp(), w); + newp = newp ? new AstOr{fl, newp, eqp} : eqp; } - newp = new AstNeq(nodep->fileline(), - new AstConst(nodep->fileline(), AstConst::SizedEData(), 0), newp); + newp = new AstNeq{fl, new AstConst{fl, AstConst::SizedEData(), 0}, newp}; VL_DO_DANGLING(replaceWithDelete(nodep, newp), nodep); } else { UINFO(8, " REDOR->EQ " << nodep << endl); - AstNode* lhsp = nodep->lhsp()->unlinkFrBack(); - AstNode* newp = new AstNeq(nodep->fileline(), - new AstConst(nodep->fileline(), AstConst::WidthedValue(), - longOrQuadWidth(nodep), 0), - lhsp); + AstNode* const lhsp = nodep->lhsp()->unlinkFrBack(); + AstNode* const newp = new AstNeq{ + fl, new AstConst{fl, AstConst::WidthedValue(), longOrQuadWidth(nodep), 0}, lhsp}; VL_DO_DANGLING(replaceWithDelete(nodep, newp), nodep); } } virtual void visit(AstRedAnd* nodep) override { if (nodep->user1SetOnce()) return; // Process once iterateChildren(nodep); + FileLine* const fl = nodep->fileline(); if (nodep->lhsp()->isWide()) { UINFO(8, " Wordize REDAND " << nodep << endl); // -> (0!={and{for each_word{WORDSEL(lhs,#)}}} AstNode* newp = nullptr; - for (int w = 0; w < nodep->lhsp()->widthWords(); w++) { + for (int w = 0; w < nodep->lhsp()->widthWords(); ++w) { AstNode* eqp = newAstWordSelClone(nodep->lhsp(), w); if (w == nodep->lhsp()->widthWords() - 1) { // Rather than doing a (slowish) ==##, we OR in the // bits that aren't part of the mask - eqp = new AstOr(nodep->fileline(), - new AstConst(nodep->fileline(), notWideMask(nodep->lhsp())), + eqp = new AstOr{fl, new AstConst{fl, notWideMask(nodep->lhsp())}, // Bug in cppcheck // cppcheck-suppress memleak - eqp); + eqp}; } - newp = (newp == nullptr) ? eqp : (new AstAnd(nodep->fileline(), newp, eqp)); + newp = newp ? new AstAnd{fl, newp, eqp} : eqp; } - newp = new AstEq( - nodep->fileline(), - new AstConst(nodep->fileline(), AstConst::SizedEData(), VL_MASK_E(VL_EDATASIZE)), - newp); + newp = new AstEq{fl, new AstConst{fl, AstConst::SizedEData(), VL_MASK_E(VL_EDATASIZE)}, + newp}; VL_DO_DANGLING(replaceWithDelete(nodep, newp), nodep); } else { UINFO(8, " REDAND->EQ " << nodep << endl); - AstNode* lhsp = nodep->lhsp()->unlinkFrBack(); - AstNode* newp = new AstEq(nodep->fileline(), - new AstConst(nodep->fileline(), wordMask(lhsp)), lhsp); + AstNode* const lhsp = nodep->lhsp()->unlinkFrBack(); + AstNode* const newp = new AstEq{fl, new AstConst{fl, wordMask(lhsp)}, lhsp}; VL_DO_DANGLING(replaceWithDelete(nodep, newp), nodep); } } @@ -860,12 +825,13 @@ private: if (nodep->lhsp()->isWide()) { UINFO(8, " Wordize REDXOR " << nodep << endl); // -> (0!={redxor{for each_word{XOR(WORDSEL(lhs,#))}}} + FileLine* const fl = nodep->fileline(); AstNode* newp = nullptr; - for (int w = 0; w < nodep->lhsp()->widthWords(); w++) { - AstNode* eqp = newAstWordSelClone(nodep->lhsp(), w); - newp = (newp == nullptr) ? eqp : (new AstXor(nodep->fileline(), newp, eqp)); + for (int w = 0; w < nodep->lhsp()->widthWords(); ++w) { + AstNode* const eqp = newAstWordSelClone(nodep->lhsp(), w); + newp = newp ? new AstXor{fl, newp, eqp} : eqp; } - newp = new AstRedXor(nodep->fileline(), newp); + newp = new AstRedXor{fl, newp}; UINFO(8, " Wordize REDXORnew " << newp << endl); VL_DO_DANGLING(replaceWithDelete(nodep, newp), nodep); } @@ -892,30 +858,30 @@ private: && ((VN_IS(nodep->lhsp(), VarRef) || VN_IS(nodep->lhsp(), ArraySel))) && !AstVar::scVarRecurse(nodep->lhsp()) // Need special function for SC && !AstVar::scVarRecurse(nodep->rhsp())) { - if (AstConst* rhsp = VN_CAST(nodep->rhsp(), Const)) { + if (AstConst* const rhsp = VN_CAST(nodep->rhsp(), Const)) { did = expandWide(nodep, rhsp); - } else if (AstVarRef* rhsp = VN_CAST(nodep->rhsp(), VarRef)) { + } else if (AstVarRef* const rhsp = VN_CAST(nodep->rhsp(), VarRef)) { did = expandWide(nodep, rhsp); - } else if (AstSel* rhsp = VN_CAST(nodep->rhsp(), Sel)) { + } else if (AstSel* const rhsp = VN_CAST(nodep->rhsp(), Sel)) { did = expandWide(nodep, rhsp); - } else if (AstArraySel* rhsp = VN_CAST(nodep->rhsp(), ArraySel)) { + } else if (AstArraySel* const rhsp = VN_CAST(nodep->rhsp(), ArraySel)) { did = expandWide(nodep, rhsp); - } else if (AstConcat* rhsp = VN_CAST(nodep->rhsp(), Concat)) { + } else if (AstConcat* const rhsp = VN_CAST(nodep->rhsp(), Concat)) { did = expandWide(nodep, rhsp); - } else if (AstReplicate* rhsp = VN_CAST(nodep->rhsp(), Replicate)) { + } else if (AstReplicate* const rhsp = VN_CAST(nodep->rhsp(), Replicate)) { did = expandWide(nodep, rhsp); - } else if (AstAnd* rhsp = VN_CAST(nodep->rhsp(), And)) { + } else if (AstAnd* const rhsp = VN_CAST(nodep->rhsp(), And)) { did = expandWide(nodep, rhsp); - } else if (AstOr* rhsp = VN_CAST(nodep->rhsp(), Or)) { + } else if (AstOr* const rhsp = VN_CAST(nodep->rhsp(), Or)) { did = expandWide(nodep, rhsp); - } else if (AstNot* rhsp = VN_CAST(nodep->rhsp(), Not)) { + } else if (AstNot* const rhsp = VN_CAST(nodep->rhsp(), Not)) { did = expandWide(nodep, rhsp); - } else if (AstXor* rhsp = VN_CAST(nodep->rhsp(), Xor)) { + } else if (AstXor* const rhsp = VN_CAST(nodep->rhsp(), Xor)) { did = expandWide(nodep, rhsp); - } else if (AstNodeCond* rhsp = VN_CAST(nodep->rhsp(), NodeCond)) { + } else if (AstNodeCond* const rhsp = VN_CAST(nodep->rhsp(), NodeCond)) { did = expandWide(nodep, rhsp); } - } else if (AstSel* lhsp = VN_CAST(nodep->lhsp(), Sel)) { + } else if (AstSel* const lhsp = VN_CAST(nodep->lhsp(), Sel)) { did = expandLhs(nodep, lhsp); } // Cleanup common code From 6bad0e14ce76238a4f05b67d0d536fb978e8ee8f Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Thu, 29 Jul 2021 08:40:41 -0400 Subject: [PATCH 60/84] Support timeunit/timeprecision in $unit. --- Changes | 1 + src/V3LinkLevel.cpp | 6 ++++++ src/V3ParseImp.cpp | 6 ++++-- src/V3ParseImp.h | 12 ++++++++++++ src/verilog.y | 20 ++++--------------- test_regress/t/t_timescale_unit.out | 4 ++++ test_regress/t/t_timescale_unit.pl | 23 ++++++++++++++++++++++ test_regress/t/t_timescale_unit.v | 30 +++++++++++++++++++++++++++++ 8 files changed, 84 insertions(+), 18 deletions(-) create mode 100644 test_regress/t/t_timescale_unit.out create mode 100755 test_regress/t/t_timescale_unit.pl create mode 100644 test_regress/t/t_timescale_unit.v diff --git a/Changes b/Changes index a839034b2..158ecf997 100644 --- a/Changes +++ b/Changes @@ -14,6 +14,7 @@ Verilator 4.211 devel **Minor:** * Support unpacked array localparams in tasks/functions (#3078). [Geza Lore] +* Support timeunit/timeprecision in $unit. * Add --instr-count-dpi to tune assumed DPI import cost for multithreaded model scheduling. Default value changed to 200 (#3068). [Yinan Xu] * Output files are split based on the set of headers required diff --git a/src/V3LinkLevel.cpp b/src/V3LinkLevel.cpp index 728073fc5..d83627ef3 100644 --- a/src/V3LinkLevel.cpp +++ b/src/V3LinkLevel.cpp @@ -100,10 +100,16 @@ void V3LinkLevel::timescaling(const ModVec& mods) { if (unit.isNone()) unit = VTimescale(VTimescale::TS_DEFAULT); v3Global.rootp()->timeunit(unit); + bool dunitTimed = false; // $unit had a timeunit + if (const AstPackage* const upkgp = v3Global.rootp()->dollarUnitPkgp()) { + if (!upkgp->timeunit().isNone()) dunitTimed = true; + } + for (AstNodeModule* nodep : mods) { if (!v3Global.opt.timeOverrideUnit().isNone()) nodep->timeunit(unit); if (nodep->timeunit().isNone()) { if (modTimedp // Got previous + && !dunitTimed && ( // unit doesn't already include an override v3Global.opt.timeOverrideUnit().isNone() && v3Global.opt.timeDefaultUnit().isNone()) diff --git a/src/V3ParseImp.cpp b/src/V3ParseImp.cpp index 9a6272e45..f1c042aa8 100644 --- a/src/V3ParseImp.cpp +++ b/src/V3ParseImp.cpp @@ -108,10 +108,12 @@ void V3ParseImp::timescaleMod(FileLine* fl, AstNodeModule* modp, bool unitSet, d } } if (!unit.isNone()) { + unit = v3Global.opt.timeComputeUnit(unit); if (modp) { - modp->timeunit(v3Global.opt.timeComputeUnit(unit)); + modp->timeunit(unit); } else { - fl->v3error("timeunit/timeprecision not under a module"); + v3Global.rootp()->timeunit(unit); + unitPackage(fl)->timeunit(unit); } } v3Global.rootp()->timeprecisionMerge(fl, prec); diff --git a/src/V3ParseImp.h b/src/V3ParseImp.h index a9477921c..fd6422dce 100644 --- a/src/V3ParseImp.h +++ b/src/V3ParseImp.h @@ -301,6 +301,18 @@ public: //==== Symbol tables V3ParseSym* symp() { return m_symp; } + AstPackage* unitPackage(FileLine* fl) { + // Find one made earlier? + VSymEnt* const rootSymp = symp()->symRootp()->findIdFlat(AstPackage::dollarUnitName()); + AstPackage* pkgp; + if (!rootSymp) { + pkgp = parsep()->rootp()->dollarUnitPkgAddp(); + symp()->reinsert(pkgp, symp()->symRootp()); // Don't push/pop scope as they're global + } else { + pkgp = VN_CAST(rootSymp->nodep(), Package); + } + return pkgp; + } public: // CONSTRUCTORS diff --git a/src/verilog.y b/src/verilog.y index 8ce6e77b7..21015f440 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -148,18 +148,6 @@ public: m_pinNum = m_pinStack.top(); m_pinStack.pop(); } - AstPackage* unitPackage(FileLine* fl) { - // Find one made earlier? - VSymEnt* symp = SYMP->symRootp()->findIdFlat(AstPackage::dollarUnitName()); - AstPackage* pkgp; - if (!symp) { - pkgp = PARSEP->rootp()->dollarUnitPkgAddp(); - SYMP->reinsert(pkgp, SYMP->symRootp()); // Don't push/pop scope as they're global - } else { - pkgp = VN_CAST(symp->nodep(), Package); - } - return pkgp; - } AstNodeDType* addRange(AstBasicDType* dtypep, AstNodeRange* rangesp, bool isPacked) { // If dtypep isn't basic, don't use this, call createArray() instead if (!rangesp) { @@ -1043,8 +1031,8 @@ description: // ==IEEE: description | interface_declaration { } | program_declaration { } | package_declaration { } - | package_item { if ($1) GRAMMARP->unitPackage($1->fileline())->addStmtp($1); } - | bind_directive { if ($1) GRAMMARP->unitPackage($1->fileline())->addStmtp($1); } + | package_item { if ($1) PARSEP->unitPackage($1->fileline())->addStmtp($1); } + | bind_directive { if ($1) PARSEP->unitPackage($1->fileline())->addStmtp($1); } // unsupported // IEEE: config_declaration // // Verilator only | yaT_RESETALL { } // Else, under design, and illegal based on IEEE 22.3 @@ -6157,7 +6145,7 @@ dollarUnitNextId: // $unit // // if not needed must use packageClassScopeNoId // // Must call nextId without any additional tokens following yD_UNIT - { $$ = new AstClassOrPackageRef($1, "$unit", GRAMMARP->unitPackage($1), nullptr); + { $$ = new AstClassOrPackageRef{$1, "$unit", PARSEP->unitPackage($1), nullptr}; SYMP->nextId(PARSEP->rootp()); } ; @@ -6166,7 +6154,7 @@ localNextId: // local // // if not needed must use packageClassScopeNoId // // Must call nextId without any additional tokens following yLOCAL__COLONCOLON - { $$ = new AstClassOrPackageRef($1, "local::", GRAMMARP->unitPackage($1), nullptr); + { $$ = new AstClassOrPackageRef{$1, "local::", PARSEP->unitPackage($1), nullptr}; SYMP->nextId(PARSEP->rootp()); BBUNSUP($1, "Unsupported: Randomize 'local::'"); } ; diff --git a/test_regress/t/t_timescale_unit.out b/test_regress/t/t_timescale_unit.out new file mode 100644 index 000000000..6f3bba3f0 --- /dev/null +++ b/test_regress/t/t_timescale_unit.out @@ -0,0 +1,4 @@ +Time scale of __024unit is 10ps / 10ps +Time scale of from_unit is 10ps / 10ps +Time scale of t is 100ps / 10ps +*-* All Finished *-* diff --git a/test_regress/t/t_timescale_unit.pl b/test_regress/t/t_timescale_unit.pl new file mode 100755 index 000000000..88b7809fc --- /dev/null +++ b/test_regress/t/t_timescale_unit.pl @@ -0,0 +1,23 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 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 + +scenarios(simulator => 1); + +compile( + ); + +execute( + check_finished => 1, + expect_filename => $Self->{golden_filename}, + ); + +ok(1); + +1; diff --git a/test_regress/t/t_timescale_unit.v b/test_regress/t/t_timescale_unit.v new file mode 100644 index 000000000..71825185c --- /dev/null +++ b/test_regress/t/t_timescale_unit.v @@ -0,0 +1,30 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under The Creative Commons Public Domain, for +// any use, without warranty, 2021 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +timeunit 10ps; +timeprecision 10ps; + +task show; + $printtimescale; +endtask + +module from_unit; + task show; + $printtimescale; + endtask +endmodule + +module t; + from_unit from_unit(); + timeunit 100ps; + initial begin + show(); + from_unit.show(); + $printtimescale; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule From 0f4daa12e4b5ec0680bd3df00af503ef7be6e06f Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Thu, 29 Jul 2021 09:00:10 -0400 Subject: [PATCH 61/84] Ignore sv-test's `pragma protect end --- src/V3PreLex.l | 11 +- src/verilog.y | 2 +- test_regress/t/t_preproc.out | 307 +++++++++++++------------- test_regress/t/t_preproc.v | 4 + test_regress/t/t_preproc_comments.out | 301 ++++++++++++------------- 5 files changed, 321 insertions(+), 304 deletions(-) diff --git a/src/V3PreLex.l b/src/V3PreLex.l index e32ee7c0c..bb591ba9e 100644 --- a/src/V3PreLex.l +++ b/src/V3PreLex.l @@ -159,7 +159,7 @@ bom [\357\273\277] LEXP->curFilelinep()->v3warn(PROTECTED, "A '`pragma protected data_block' encrypted section was detected and will be skipped."); BEGIN(ENCBASE64); return VP_TEXT; } -("begin_protected"|"end_protected")[\n\r] { FL_FWDC; linenoInc(); BEGIN(INITIAL); return VP_TEXT; } +("begin_protected"|"end_protected"|"end")[\n\r] { FL_FWDC; linenoInc(); BEGIN(INITIAL); return VP_TEXT; } "version="[^\n\r]*[\n\r] { FL_FWDC; linenoInc(); @@ -175,6 +175,10 @@ bom [\357\273\277] linenoInc(); BEGIN(INITIAL); return VP_TEXT; } + /* end of `pragma protect */ +{wsn}+ { FL_FWDC; return VP_TEXT; } +[\n\r] { FL_FWDC; linenoInc(); BEGIN(INITIAL); return VP_TEXT; } + /* catch-all for unknown '`pragma protect' rules */ . { yyless(0); BEGIN(PRAGMAPRTERR); @@ -202,7 +206,7 @@ bom [\357\273\277] /* Catch only empty `pragma lines */ "`pragma"{wsn}*[\n\r] { - yyless(yyleng-1); FL_FWDC; + yyless(yyleng - 1); FL_FWDC; if (v3Global.opt.pedantic()) { LEXP->curFilelinep()->v3warn(BADSTDPRAGMA, "`pragma is missing a pragma_expression."); } @@ -210,12 +214,11 @@ bom [\357\273\277] /* catch all other nonempty `pragma */ "`pragma"{wsn}*[^\n\r] { - yyless(yyleng-1); FL_FWDC; + yyless(yyleng - 1); FL_FWDC; if (!v3Global.opt.preprocOnly()) BEGIN(PRAGMA); return VP_TEXT; } "protect"{wsn}* { FL_FWDC; BEGIN(PRAGMAPRT); return VP_TEXT;} - /* catch-all for unknown `pragma rules */ . { yyless(0); BEGIN(PRAGMAERR); diff --git a/src/verilog.y b/src/verilog.y index 21015f440..a5ec944e3 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -4779,7 +4779,7 @@ gateUnsupPinList: ; gatePinExpr: - expr { $$ = GRAMMARP ->createGatePin($1); } + expr { $$ = GRAMMARP->createGatePin($1); } ; // This list is also hardcoded in VParseLex.l diff --git a/test_regress/t/t_preproc.out b/test_regress/t/t_preproc.out index 376c8eb4b..d3d6e5c83 100644 --- a/test_regress/t/t_preproc.out +++ b/test_regress/t/t_preproc.out @@ -325,9 +325,14 @@ ZCBXb3JrIGFzIG== `pragma protect end_protected `line 211 "t/t_preproc.v" 0 + +`pragma protect +`pragma protect end + +`line 215 "t/t_preproc.v" 0 endmodule -`line 213 "t/t_preproc.v" 0 +`line 217 "t/t_preproc.v" 0 @@ -338,17 +343,17 @@ endmodule -`line 223 "t/t_preproc.v" 0 +`line 227 "t/t_preproc.v" 0 begin addr <= (({regs[6], regs[7]} + 1)); rd <= 1; end and begin addr <= (({regs[6], regs[7]})); wdata <= (rdata); wr <= 1; end begin addr <= ({regs[6], regs[7]} + 1); rd <= 1; end begin addr <= ({regs[6], regs[7]}); wdata <= (rdata); wr <= 1; end more -`line 227 "t/t_preproc.v" 0 +`line 231 "t/t_preproc.v" 0 -`line 230 "t/t_preproc.v" 0 +`line 234 "t/t_preproc.v" 0 `line 1 "t/t_preproc_inc4.vh" 1 `line 2 "t/t_preproc_inc4.vh" 0 @@ -360,57 +365,57 @@ begin addr <= ({regs[6], regs[7]}); wdata <= (rdata); wr <= 1; end more `line 8 "t/t_preproc_inc4.vh" 2 -`line 230 "t/t_preproc.v" 0 - -`line 231 "t/t_preproc.v" 0 - - - `line 234 "t/t_preproc.v" 0 - -`line 236 "t/t_preproc.v" 0 +`line 235 "t/t_preproc.v" 0 +`line 238 "t/t_preproc.v" 0 + `line 240 "t/t_preproc.v" 0 + + + +`line 244 "t/t_preproc.v" 0 + -`line 243 "t/t_preproc.v" 0 +`line 247 "t/t_preproc.v" 0 $blah("ab,cd","e,f"); $blah(this.logfile,vec); $blah(this.logfile,vec[1,2,3]); $blah(this.logfile,{blah.name(), " is not foo"}); -`line 249 "t/t_preproc.v" 0 +`line 253 "t/t_preproc.v" 0 -`line 252 "t/t_preproc.v" 0 +`line 256 "t/t_preproc.v" 0 `pragma foo = 1 `default_nettype none `default_nettype uwire -`line 256 "t/t_preproc.v" 0 +`line 260 "t/t_preproc.v" 0 -`line 259 "t/t_preproc.v" 0 +`line 263 "t/t_preproc.v" 0 -`line 263 "t/t_preproc.v" 0 -Line_Preproc_Check 263 +`line 267 "t/t_preproc.v" 0 +Line_Preproc_Check 267 -`line 265 "t/t_preproc.v" 0 +`line 269 "t/t_preproc.v" 0 -`line 268 "t/t_preproc.v" 0 +`line 272 "t/t_preproc.v" 0 @@ -418,15 +423,15 @@ Line_Preproc_Check 263 -`line 275 "t/t_preproc.v" 0 +`line 279 "t/t_preproc.v" 0 (x,y) -Line_Preproc_Check 276 +Line_Preproc_Check 280 -`line 278 "t/t_preproc.v" 0 +`line 282 "t/t_preproc.v" 0 -`line 281 "t/t_preproc.v" 0 +`line 285 "t/t_preproc.v" 0 @@ -435,17 +440,17 @@ beginend beginend "beginend" -`line 289 "t/t_preproc.v" 0 +`line 293 "t/t_preproc.v" 0 `\esc`def -`line 295 "t/t_preproc.v" 0 +`line 299 "t/t_preproc.v" 0 Not a \`define -`line 297 "t/t_preproc.v" 0 +`line 301 "t/t_preproc.v" 0 @@ -454,23 +459,23 @@ Not a \`define x,y)--bee submacro has comma paren -`line 305 "t/t_preproc.v" 0 +`line 309 "t/t_preproc.v" 0 $display("10 %d %d", $bits(foo), 10); -`line 310 "t/t_preproc.v" 0 +`line 314 "t/t_preproc.v" 0 -`line 315 "t/t_preproc.v" 0 +`line 319 "t/t_preproc.v" 0 -`line 318 "t/t_preproc.v" 0 +`line 322 "t/t_preproc.v" 0 @@ -485,30 +490,30 @@ $display("10 %d %d", $bits(foo), 10); -`line 332 "t/t_preproc.v" 0 +`line 336 "t/t_preproc.v" 0 -`line 332 "t/t_preproc.v" 0 +`line 336 "t/t_preproc.v" 0 -`line 332 "t/t_preproc.v" 0 +`line 336 "t/t_preproc.v" 0 -`line 332 "t/t_preproc.v" 0 +`line 336 "t/t_preproc.v" 0 -`line 332 "t/t_preproc.v" 0 +`line 336 "t/t_preproc.v" 0 -`line 332 "t/t_preproc.v" 0 +`line 336 "t/t_preproc.v" 0 -`line 332 "t/t_preproc.v" 0 +`line 336 "t/t_preproc.v" 0 -`line 332 "t/t_preproc.v" 0 +`line 336 "t/t_preproc.v" 0 -`line 332 "t/t_preproc.v" 0 +`line 336 "t/t_preproc.v" 0 -`line 332 "t/t_preproc.v" 0 +`line 336 "t/t_preproc.v" 0 assign a3 = ~b3 ; -`line 332 "t/t_preproc.v" 0 +`line 336 "t/t_preproc.v" 0 -`line 334 "t/t_preproc.v" 0 +`line 338 "t/t_preproc.v" 0 \ @@ -519,56 +524,56 @@ $display("10 %d %d", $bits(foo), 10); -`line 343 "t/t_preproc.v" 0 +`line 347 "t/t_preproc.v" 0 -`line 343 "t/t_preproc.v" 0 +`line 347 "t/t_preproc.v" 0 -`line 343 "t/t_preproc.v" 0 +`line 347 "t/t_preproc.v" 0 def i -`line 345 "t/t_preproc.v" 0 +`line 349 "t/t_preproc.v" 0 -`line 347 "t/t_preproc.v" 0 - - - - - `line 351 "t/t_preproc.v" 0 + + + + +`line 355 "t/t_preproc.v" 0 + -`line 357 "t/t_preproc.v" 0 +`line 361 "t/t_preproc.v" 0 1 /*verilator NOT IN DEFINE*/ (nodef) 2 /*verilator PART OF DEFINE*/ (hasdef) 3 -`line 359 "t/t_preproc.v" 0 +`line 363 "t/t_preproc.v" 0 /*verilator NOT PART OF DEFINE*/ (nodef) -`line 360 "t/t_preproc.v" 0 +`line 364 "t/t_preproc.v" 0 4 -`line 360 "t/t_preproc.v" 0 +`line 364 "t/t_preproc.v" 0 /*verilator PART OF DEFINE*/ (nodef) -`line 361 "t/t_preproc.v" 0 +`line 365 "t/t_preproc.v" 0 5 also in -`line 361 "t/t_preproc.v" 0 +`line 365 "t/t_preproc.v" 0 also3 (nodef) HAS a NEW -`line 364 "t/t_preproc.v" 0 +`line 368 "t/t_preproc.v" 0 LINE -`line 366 "t/t_preproc.v" 0 +`line 370 "t/t_preproc.v" 0 -`line 368 "t/t_preproc.v" 0 +`line 372 "t/t_preproc.v" 0 @@ -582,11 +587,11 @@ LINE -`line 381 "t/t_preproc.v" 0 +`line 385 "t/t_preproc.v" 0 -`line 384 "t/t_preproc.v" 0 +`line 388 "t/t_preproc.v" 0 EXP: clxx_scen clxx_scen EXP: clxx_scen @@ -594,44 +599,44 @@ EXP: clxx_scen EXP: do if (start("verilog/inc1.v", 25)) begin message({"Blah-", "clx_scen", " end"}); end while(0); -`line 390 "t/t_preproc.v" 0 +`line 394 "t/t_preproc.v" 0 do -`line 390 "t/t_preproc.v" 0 +`line 394 "t/t_preproc.v" 0 -`line 390 "t/t_preproc.v" 0 +`line 394 "t/t_preproc.v" 0 -`line 390 "t/t_preproc.v" 0 +`line 394 "t/t_preproc.v" 0 -`line 390 "t/t_preproc.v" 0 - -`line 390 "t/t_preproc.v" 0 - if (start("t/t_preproc.v", 390)) begin -`line 390 "t/t_preproc.v" 0 - -`line 390 "t/t_preproc.v" 0 - message({"Blah-", "clx_scen", " end"}); -`line 390 "t/t_preproc.v" 0 - end -`line 390 "t/t_preproc.v" 0 - -`line 390 "t/t_preproc.v" 0 - while(0); - -`line 392 "t/t_preproc.v" 0 - - `line 394 "t/t_preproc.v" 0 +`line 394 "t/t_preproc.v" 0 + if (start("t/t_preproc.v", 394)) begin +`line 394 "t/t_preproc.v" 0 + +`line 394 "t/t_preproc.v" 0 + message({"Blah-", "clx_scen", " end"}); +`line 394 "t/t_preproc.v" 0 + end +`line 394 "t/t_preproc.v" 0 + +`line 394 "t/t_preproc.v" 0 + while(0); - - +`line 396 "t/t_preproc.v" 0 + `line 398 "t/t_preproc.v" 0 + + + + + +`line 402 "t/t_preproc.v" 0 -`line 398 "t/t_preproc.v" 0 +`line 402 "t/t_preproc.v" 0 -`line 399 "t/t_preproc.v" 0 +`line 403 "t/t_preproc.v" 0 EXP: This is fooed @@ -639,7 +644,7 @@ This is fooed EXP: This is fooed_2 This is fooed_2 -`line 406 "t/t_preproc.v" 0 +`line 410 "t/t_preproc.v" 0 np @@ -651,11 +656,11 @@ np -`line 417 "t/t_preproc.v" 0 +`line 421 "t/t_preproc.v" 0 -`line 420 "t/t_preproc.v" 0 +`line 424 "t/t_preproc.v" 0 @@ -664,12 +669,12 @@ np -`line 428 "t/t_preproc.v" 0 - - - - `line 432 "t/t_preproc.v" 0 + + + + +`line 436 "t/t_preproc.v" 0 hello3hello3hello3 hello4hello4hello4hello4 @@ -677,7 +682,7 @@ hello4hello4hello4hello4 -`line 438 "t/t_preproc.v" 0 +`line 442 "t/t_preproc.v" 0 `line 1 "t/t_preproc_inc4.vh" 1 `line 2 "t/t_preproc_inc4.vh" 0 @@ -689,9 +694,9 @@ hello4hello4hello4hello4 `line 8 "t/t_preproc_inc4.vh" 2 -`line 438 "t/t_preproc.v" 0 +`line 442 "t/t_preproc.v" 0 -`line 439 "t/t_preproc.v" 0 +`line 443 "t/t_preproc.v" 0 @@ -701,28 +706,28 @@ hello4hello4hello4hello4 -`line 447 "t/t_preproc.v" 0 +`line 451 "t/t_preproc.v" 0 -Line_Preproc_Check 451 +Line_Preproc_Check 455 -Line_Preproc_Check 457 +Line_Preproc_Check 461 "FOO \ BAR " "arg_line1 \ arg_line2" "FOO \ BAR " -`line 460 "t/t_preproc.v" 0 -Line_Preproc_Check 460 +`line 464 "t/t_preproc.v" 0 +Line_Preproc_Check 464 -`line 464 "t/t_preproc.v" 0 +`line 468 "t/t_preproc.v" 0 @@ -733,14 +738,14 @@ abc -`line 474 "t/t_preproc.v" 0 +`line 478 "t/t_preproc.v" 0 EXP: sonet_frame sonet_frame -`line 480 "t/t_preproc.v" 0 +`line 484 "t/t_preproc.v" 0 EXP: sonet_frame @@ -751,7 +756,7 @@ sonet_frame EXP: sonet_frame sonet_frame -`line 490 "t/t_preproc.v" 0 +`line 494 "t/t_preproc.v" 0 @@ -759,13 +764,13 @@ EXP: module zzz ; endmodule module zzz ; endmodule module zzz ; endmodule -`line 497 "t/t_preproc.v" 0 +`line 501 "t/t_preproc.v" 0 EXP: module a_b ; endmodule module a_b ; endmodule module a_b ; endmodule -`line 502 "t/t_preproc.v" 0 +`line 506 "t/t_preproc.v" 0 integer foo; @@ -779,7 +784,7 @@ module t; initial begin : \`LEX_CAT(a[0],_assignment) -`line 514 "t/t_preproc.v" 0 +`line 518 "t/t_preproc.v" 0 $write("GOT%%m='%m' EXP='%s'\n", "t.\\`LEX_CAT(a[0],_assignment) "); end @@ -788,7 +793,7 @@ module t; initial begin : \a[0]_assignment_a[1] -`line 521 "t/t_preproc.v" 0 +`line 525 "t/t_preproc.v" 0 $write("GOT%%m='%m' EXP='%s'\n", "t.\\a[0]_assignment_a[1] "); end @@ -804,7 +809,7 @@ module t; initial begin : \`CAT(ff,bb) -`line 535 "t/t_preproc.v" 0 +`line 539 "t/t_preproc.v" 0 $write("GOT%%m='%m' EXP='%s'\n", "t.\\`CAT(ff,bb) "); end @@ -812,7 +817,7 @@ module t; initial begin : \`zzz -`line 541 "t/t_preproc.v" 0 +`line 545 "t/t_preproc.v" 0 $write("GOT%%m='%m' EXP='%s'\n", "t.\\`zzz "); end @@ -821,11 +826,11 @@ module t; initial begin : \`FOO -`line 548 "t/t_preproc.v" 0 +`line 552 "t/t_preproc.v" 0 $write("GOT%%m='%m' OTHER_EXP='%s'\n OUR_EXP='%s'", "t.bar ","t.\\`FOO "); end initial begin : \xx`FOO -`line 550 "t/t_preproc.v" 0 +`line 554 "t/t_preproc.v" 0 $write("GOT%%m='%m' EXP='%s'\n", "t.\\xx`FOO "); end @@ -858,27 +863,27 @@ module t; initial -`line 581 "t/t_preproc.v" 0 +`line 585 "t/t_preproc.v" 0 $display("%s%s","a1","b2c3\n"); endmodule -`line 584 "t/t_preproc.v" 0 +`line 588 "t/t_preproc.v" 0 -`line 587 "t/t_preproc.v" 0 +`line 591 "t/t_preproc.v" 0 $display("RAM0"); $display("CPU"); -`line 592 "t/t_preproc.v" 0 +`line 596 "t/t_preproc.v" 0 -`line 597 "t/t_preproc.v" 0 +`line 601 "t/t_preproc.v" 0 XXE_FAMILY = XXE_ @@ -886,7 +891,7 @@ XXE_FAMILY = XXE_ $display("XXE_ is defined"); -`line 604 "t/t_preproc.v" 0 +`line 608 "t/t_preproc.v" 0 XYE_FAMILY = XYE_ @@ -894,7 +899,7 @@ XYE_FAMILY = XYE_ $display("XYE_ is defined"); -`line 611 "t/t_preproc.v" 0 +`line 615 "t/t_preproc.v" 0 XXS_FAMILY = XXS_some @@ -902,7 +907,7 @@ XXS_FAMILY = XXS_some $display("XXS_some is defined"); -`line 618 "t/t_preproc.v" 0 +`line 622 "t/t_preproc.v" 0 XYS_FAMILY = XYS_foo @@ -910,10 +915,10 @@ XYS_FAMILY = XYS_foo $display("XYS_foo is defined"); -`line 625 "t/t_preproc.v" 0 +`line 629 "t/t_preproc.v" 0 -`line 627 "t/t_preproc.v" 0 +`line 631 "t/t_preproc.v" 0 @@ -922,7 +927,7 @@ XYS_FAMILY = XYS_foo -`line 635 "t/t_preproc.v" 0 +`line 639 "t/t_preproc.v" 0 @@ -930,7 +935,7 @@ XYS_FAMILY = XYS_foo -`line 642 "t/t_preproc.v" 0 +`line 646 "t/t_preproc.v" 0 @@ -938,7 +943,7 @@ XYS_FAMILY = XYS_foo -`line 649 "t/t_preproc.v" 0 +`line 653 "t/t_preproc.v" 0 @@ -946,26 +951,26 @@ XYS_FAMILY = XYS_foo -`line 656 "t/t_preproc.v" 0 - - -`line 658 "t/t_preproc.v" 0 - - `line 660 "t/t_preproc.v" 0 - - -(.mySig (myInterface.pa5), + + +`line 662 "t/t_preproc.v" 0 + `line 664 "t/t_preproc.v" 0 +(.mySig (myInterface.pa5), -`line 667 "t/t_preproc.v" 0 +`line 668 "t/t_preproc.v" 0 + + + +`line 671 "t/t_preproc.v" 0 `dbg_hdl(UVM_LOW, ("Functional coverage enabled: paramgrp")); -`line 670 "t/t_preproc.v" 0 +`line 674 "t/t_preproc.v" 0 @@ -974,28 +979,28 @@ XYS_FAMILY = XYS_foo -`line 678 "t/t_preproc.v" 0 +`line 682 "t/t_preproc.v" 0 module pcc2_cfg; generate -`line 680 "t/t_preproc.v" 0 +`line 684 "t/t_preproc.v" 0 covergroup a @(posedge b); -`line 680 "t/t_preproc.v" 0 +`line 684 "t/t_preproc.v" 0 c: coverpoint d iff ((c) === 1'b1); endgroup -`line 680 "t/t_preproc.v" 0 +`line 684 "t/t_preproc.v" 0 a u_a; -`line 680 "t/t_preproc.v" 0 +`line 684 "t/t_preproc.v" 0 initial do begin $display ("DEBUG : %s [%m]", $sformatf ("Functional coverage enabled: u_a")); end while(0); endgenerate endmodule -`line 684 "t/t_preproc.v" 0 +`line 688 "t/t_preproc.v" 0 "`NOT_DEFINED_STR" -`line 689 "t/t_preproc.v" 0 +`line 693 "t/t_preproc.v" 0 @@ -1018,4 +1023,4 @@ predef 2 2 -`line 711 "t/t_preproc.v" 2 +`line 715 "t/t_preproc.v" 2 diff --git a/test_regress/t/t_preproc.v b/test_regress/t/t_preproc.v index a52ce02c1..50f18e53d 100644 --- a/test_regress/t/t_preproc.v +++ b/test_regress/t/t_preproc.v @@ -208,6 +208,10 @@ ZCBXb3JrIGFzIG== `pragma protect end_protected +// encoding envelope +`pragma protect +`pragma protect end + endmodule //====================================================================== diff --git a/test_regress/t/t_preproc_comments.out b/test_regress/t/t_preproc_comments.out index 1d2795895..881de7e8d 100644 --- a/test_regress/t/t_preproc_comments.out +++ b/test_regress/t/t_preproc_comments.out @@ -325,9 +325,14 @@ ZCBXb3JrIGFzIG== `pragma protect end_protected `line 211 "t/t_preproc.v" 0 +// encoding envelope +`pragma protect +`pragma protect end + +`line 215 "t/t_preproc.v" 0 endmodule -`line 213 "t/t_preproc.v" 0 +`line 217 "t/t_preproc.v" 0 //====================================================================== // macro call with define that has comma @@ -338,17 +343,17 @@ endmodule -`line 223 "t/t_preproc.v" 0 +`line 227 "t/t_preproc.v" 0 begin addr <= (({regs[6], regs[7]} + 1)); rd <= 1; end and begin addr <= (({regs[6], regs[7]})); wdata <= (rdata); wr <= 1; end begin addr <= ({regs[6], regs[7]} + 1); rd <= 1; end begin addr <= ({regs[6], regs[7]}); wdata <= (rdata); wr <= 1; end more -`line 227 "t/t_preproc.v" 0 +`line 231 "t/t_preproc.v" 0 //====================================================================== // include of parameterized file -`line 230 "t/t_preproc.v" 0 +`line 234 "t/t_preproc.v" 0 `line 1 "t/t_preproc_inc4.vh" 1 // DESCRIPTION: Verilog::Preproc: Example source code `line 2 "t/t_preproc_inc4.vh" 0 @@ -360,57 +365,57 @@ begin addr <= ({regs[6], regs[7]}); wdata <= (rdata); wr <= 1; end more `line 8 "t/t_preproc_inc4.vh" 2 -`line 230 "t/t_preproc.v" 0 - -`line 231 "t/t_preproc.v" 0 - - - `line 234 "t/t_preproc.v" 0 - -`line 236 "t/t_preproc.v" 0 +`line 235 "t/t_preproc.v" 0 +`line 238 "t/t_preproc.v" 0 + `line 240 "t/t_preproc.v" 0 + + + + +`line 244 "t/t_preproc.v" 0 //====================================================================== // macro call with , in {} -`line 243 "t/t_preproc.v" 0 +`line 247 "t/t_preproc.v" 0 $blah("ab,cd","e,f"); $blah(this.logfile,vec); $blah(this.logfile,vec[1,2,3]); $blah(this.logfile,{blah.name(), " is not foo"}); -`line 249 "t/t_preproc.v" 0 +`line 253 "t/t_preproc.v" 0 //====================================================================== // pragma/default net type -`line 252 "t/t_preproc.v" 0 +`line 256 "t/t_preproc.v" 0 `pragma foo = 1 `default_nettype none `default_nettype uwire -`line 256 "t/t_preproc.v" 0 +`line 260 "t/t_preproc.v" 0 //====================================================================== // Ifdef -`line 259 "t/t_preproc.v" 0 +`line 263 "t/t_preproc.v" 0 -`line 263 "t/t_preproc.v" 0 -Line_Preproc_Check 263 +`line 267 "t/t_preproc.v" 0 +Line_Preproc_Check 267 -`line 265 "t/t_preproc.v" 0 +`line 269 "t/t_preproc.v" 0 //====================================================================== // bug84 -`line 268 "t/t_preproc.v" 0 +`line 272 "t/t_preproc.v" 0 // Hello, comments MIGHT not be legal /*more,,)cmts*/ // But newlines ARE legal... who speced THAT? @@ -418,15 +423,15 @@ Line_Preproc_Check 263 -`line 275 "t/t_preproc.v" 0 +`line 279 "t/t_preproc.v" 0 (//Here x,y //Too) -Line_Preproc_Check 276 +Line_Preproc_Check 280 -`line 278 "t/t_preproc.v" 0 +`line 282 "t/t_preproc.v" 0 //====================================================================== // defines split arguments -`line 281 "t/t_preproc.v" 0 +`line 285 "t/t_preproc.v" 0 @@ -435,17 +440,17 @@ beginend // 2001 spec doesn't require two tokens, so "beginend" ok beginend // 2001 spec doesn't require two tokens, so "beginend" ok "beginend" // No space "beginend" -`line 289 "t/t_preproc.v" 0 +`line 293 "t/t_preproc.v" 0 //====================================================================== // bug106 `\esc`def -`line 295 "t/t_preproc.v" 0 +`line 299 "t/t_preproc.v" 0 Not a \`define -`line 297 "t/t_preproc.v" 0 +`line 301 "t/t_preproc.v" 0 //====================================================================== // misparsed comma in submacro @@ -454,23 +459,23 @@ Not a \`define x,y)--bee submacro has comma paren -`line 305 "t/t_preproc.v" 0 +`line 309 "t/t_preproc.v" 0 //====================================================================== // bug191 $display("10 %d %d", $bits(foo), 10); -`line 310 "t/t_preproc.v" 0 +`line 314 "t/t_preproc.v" 0 //====================================================================== // 1800-2009 -`line 315 "t/t_preproc.v" 0 +`line 319 "t/t_preproc.v" 0 -`line 318 "t/t_preproc.v" 0 +`line 322 "t/t_preproc.v" 0 //====================================================================== // bug202 @@ -485,34 +490,34 @@ $display("10 %d %d", $bits(foo), 10); -`line 332 "t/t_preproc.v" 0 +`line 336 "t/t_preproc.v" 0 -`line 332 "t/t_preproc.v" 0 +`line 336 "t/t_preproc.v" 0 -`line 332 "t/t_preproc.v" 0 +`line 336 "t/t_preproc.v" 0 -`line 332 "t/t_preproc.v" 0 +`line 336 "t/t_preproc.v" 0 -`line 332 "t/t_preproc.v" 0 +`line 336 "t/t_preproc.v" 0 -`line 332 "t/t_preproc.v" 0 +`line 336 "t/t_preproc.v" 0 -`line 332 "t/t_preproc.v" 0 +`line 336 "t/t_preproc.v" 0 -`line 332 "t/t_preproc.v" 0 +`line 336 "t/t_preproc.v" 0 -`line 332 "t/t_preproc.v" 0 +`line 336 "t/t_preproc.v" 0 -`line 332 "t/t_preproc.v" 0 +`line 336 "t/t_preproc.v" 0 assign a3 = ~b3 ; -`line 332 "t/t_preproc.v" 0 +`line 336 "t/t_preproc.v" 0 -`line 334 "t/t_preproc.v" 0 +`line 338 "t/t_preproc.v" 0 /* multi \ line1*/ \ -`line 336 "t/t_preproc.v" 0 +`line 340 "t/t_preproc.v" 0 /*multi \ line2*/ @@ -521,59 +526,59 @@ $display("10 %d %d", $bits(foo), 10); -`line 343 "t/t_preproc.v" 0 +`line 347 "t/t_preproc.v" 0 -`line 343 "t/t_preproc.v" 0 +`line 347 "t/t_preproc.v" 0 -`line 343 "t/t_preproc.v" 0 +`line 347 "t/t_preproc.v" 0 /* multi line 3*/ -`line 343 "t/t_preproc.v" 0 +`line 347 "t/t_preproc.v" 0 def i -`line 345 "t/t_preproc.v" 0 +`line 349 "t/t_preproc.v" 0 //====================================================================== -`line 347 "t/t_preproc.v" 0 - - - - - `line 351 "t/t_preproc.v" 0 + + + + +`line 355 "t/t_preproc.v" 0 + -`line 357 "t/t_preproc.v" 0 +`line 361 "t/t_preproc.v" 0 1 // verilator NOT IN DEFINE (nodef) 2 /* verilator PART OF DEFINE */ (hasdef) 3 -`line 359 "t/t_preproc.v" 0 +`line 363 "t/t_preproc.v" 0 /* verilator NOT PART OF DEFINE */ (nodef) -`line 360 "t/t_preproc.v" 0 +`line 364 "t/t_preproc.v" 0 4 -`line 360 "t/t_preproc.v" 0 +`line 364 "t/t_preproc.v" 0 /* verilator PART OF DEFINE */ (nodef) -`line 361 "t/t_preproc.v" 0 +`line 365 "t/t_preproc.v" 0 5 also in -`line 361 "t/t_preproc.v" 0 +`line 365 "t/t_preproc.v" 0 also3 // CMT NOT (nodef) HAS a NEW -`line 364 "t/t_preproc.v" 0 +`line 368 "t/t_preproc.v" 0 LINE -`line 366 "t/t_preproc.v" 0 +`line 370 "t/t_preproc.v" 0 //====================================================================== -`line 368 "t/t_preproc.v" 0 +`line 372 "t/t_preproc.v" 0 @@ -587,11 +592,11 @@ LINE -`line 381 "t/t_preproc.v" 0 +`line 385 "t/t_preproc.v" 0 -`line 384 "t/t_preproc.v" 0 +`line 388 "t/t_preproc.v" 0 EXP: clxx_scen clxx_scen EXP: clxx_scen @@ -599,44 +604,44 @@ EXP: clxx_scen EXP: do if (start("verilog/inc1.v", 25)) begin message({"Blah-", "clx_scen", " end"}); end while(0); -`line 390 "t/t_preproc.v" 0 +`line 394 "t/t_preproc.v" 0 do -`line 390 "t/t_preproc.v" 0 +`line 394 "t/t_preproc.v" 0 /* synopsys translate_off */ -`line 390 "t/t_preproc.v" 0 +`line 394 "t/t_preproc.v" 0 -`line 390 "t/t_preproc.v" 0 +`line 394 "t/t_preproc.v" 0 -`line 390 "t/t_preproc.v" 0 - -`line 390 "t/t_preproc.v" 0 - if (start("t/t_preproc.v", 390)) begin -`line 390 "t/t_preproc.v" 0 - -`line 390 "t/t_preproc.v" 0 - message({"Blah-", "clx_scen", " end"}); -`line 390 "t/t_preproc.v" 0 - end -`line 390 "t/t_preproc.v" 0 - /* synopsys translate_on */ -`line 390 "t/t_preproc.v" 0 - while(0); - -`line 392 "t/t_preproc.v" 0 -//====================================================================== - `line 394 "t/t_preproc.v" 0 +`line 394 "t/t_preproc.v" 0 + if (start("t/t_preproc.v", 394)) begin +`line 394 "t/t_preproc.v" 0 + +`line 394 "t/t_preproc.v" 0 + message({"Blah-", "clx_scen", " end"}); +`line 394 "t/t_preproc.v" 0 + end +`line 394 "t/t_preproc.v" 0 + /* synopsys translate_on */ +`line 394 "t/t_preproc.v" 0 + while(0); - - +`line 396 "t/t_preproc.v" 0 +//====================================================================== `line 398 "t/t_preproc.v" 0 + + + + + +`line 402 "t/t_preproc.v" 0 -`line 398 "t/t_preproc.v" 0 +`line 402 "t/t_preproc.v" 0 -`line 399 "t/t_preproc.v" 0 +`line 403 "t/t_preproc.v" 0 //`ifndef def_fooed_2 `error "No def_fooed_2" `endif EXP: This is fooed @@ -644,7 +649,7 @@ This is fooed EXP: This is fooed_2 This is fooed_2 -`line 406 "t/t_preproc.v" 0 +`line 410 "t/t_preproc.v" 0 //====================================================================== np @@ -656,11 +661,11 @@ np -`line 417 "t/t_preproc.v" 0 +`line 421 "t/t_preproc.v" 0 -`line 420 "t/t_preproc.v" 0 +`line 424 "t/t_preproc.v" 0 //====================================================================== // Metaprogramming @@ -669,12 +674,12 @@ np -`line 428 "t/t_preproc.v" 0 +`line 432 "t/t_preproc.v" 0 -`line 432 "t/t_preproc.v" 0 +`line 436 "t/t_preproc.v" 0 hello3hello3hello3 hello4hello4hello4hello4 //====================================================================== @@ -682,7 +687,7 @@ hello4hello4hello4hello4 -`line 438 "t/t_preproc.v" 0 +`line 442 "t/t_preproc.v" 0 `line 1 "t/t_preproc_inc4.vh" 1 // DESCRIPTION: Verilog::Preproc: Example source code `line 2 "t/t_preproc_inc4.vh" 0 @@ -694,9 +699,9 @@ hello4hello4hello4hello4 `line 8 "t/t_preproc_inc4.vh" 2 -`line 438 "t/t_preproc.v" 0 +`line 442 "t/t_preproc.v" 0 -`line 439 "t/t_preproc.v" 0 +`line 443 "t/t_preproc.v" 0 //====================================================================== // Defines doing defines @@ -706,28 +711,28 @@ hello4hello4hello4hello4 -`line 447 "t/t_preproc.v" 0 +`line 451 "t/t_preproc.v" 0 -Line_Preproc_Check 451 +Line_Preproc_Check 455 //====================================================================== // Quoted multiline - track line numbers, and ensure \\n gets propagated -Line_Preproc_Check 457 +Line_Preproc_Check 461 "FOO \ BAR " "arg_line1 \ arg_line2" "FOO \ BAR " -`line 460 "t/t_preproc.v" 0 -Line_Preproc_Check 460 +`line 464 "t/t_preproc.v" 0 +Line_Preproc_Check 464 //====================================================================== // bug283 -`line 464 "t/t_preproc.v" 0 +`line 468 "t/t_preproc.v" 0 @@ -738,14 +743,14 @@ abc -`line 474 "t/t_preproc.v" 0 +`line 478 "t/t_preproc.v" 0 EXP: sonet_frame sonet_frame -`line 480 "t/t_preproc.v" 0 +`line 484 "t/t_preproc.v" 0 EXP: sonet_frame @@ -756,7 +761,7 @@ sonet_frame EXP: sonet_frame sonet_frame -`line 490 "t/t_preproc.v" 0 +`line 494 "t/t_preproc.v" 0 // The existance of non-existance of a base define can make a difference @@ -764,13 +769,13 @@ EXP: module zzz ; endmodule module zzz ; endmodule module zzz ; endmodule -`line 497 "t/t_preproc.v" 0 +`line 501 "t/t_preproc.v" 0 EXP: module a_b ; endmodule module a_b ; endmodule module a_b ; endmodule -`line 502 "t/t_preproc.v" 0 +`line 506 "t/t_preproc.v" 0 //====================================================================== // bug311 integer/*NEED_SPACE*/ foo; @@ -784,7 +789,7 @@ module t; initial begin : \`LEX_CAT(a[0],_assignment) -`line 514 "t/t_preproc.v" 0 +`line 518 "t/t_preproc.v" 0 $write("GOT%%m='%m' EXP='%s'\n", "t.\\`LEX_CAT(a[0],_assignment) "); end //----- // SHOULD(simulator-dependant): Backslash doesn't prevent arguments from @@ -793,7 +798,7 @@ module t; initial begin : \a[0]_assignment_a[1] -`line 521 "t/t_preproc.v" 0 +`line 525 "t/t_preproc.v" 0 $write("GOT%%m='%m' EXP='%s'\n", "t.\\a[0]_assignment_a[1] "); end //----- @@ -809,7 +814,7 @@ module t; // Similar to above; \ does not allow expansion after substitution initial begin : \`CAT(ff,bb) -`line 535 "t/t_preproc.v" 0 +`line 539 "t/t_preproc.v" 0 $write("GOT%%m='%m' EXP='%s'\n", "t.\\`CAT(ff,bb) "); end //----- @@ -817,7 +822,7 @@ module t; // MUST: Unknown macro with backslash escape stays as escaped symbol name initial begin : \`zzz -`line 541 "t/t_preproc.v" 0 +`line 545 "t/t_preproc.v" 0 $write("GOT%%m='%m' EXP='%s'\n", "t.\\`zzz "); end //----- @@ -826,11 +831,11 @@ module t; // SHOULD(simulator-dependant): Known macro with backslash escape expands initial begin : \`FOO -`line 548 "t/t_preproc.v" 0 +`line 552 "t/t_preproc.v" 0 $write("GOT%%m='%m' OTHER_EXP='%s'\n OUR_EXP='%s'", "t.bar ","t.\\`FOO "); end // SHOULD(simulator-dependant): Prefix breaks the above initial begin : \xx`FOO -`line 550 "t/t_preproc.v" 0 +`line 554 "t/t_preproc.v" 0 $write("GOT%%m='%m' EXP='%s'\n", "t.\\xx`FOO "); end //----- @@ -863,27 +868,27 @@ module t; initial -`line 581 "t/t_preproc.v" 0 +`line 585 "t/t_preproc.v" 0 $display("%s%s","a1","b2c3\n"); endmodule -`line 584 "t/t_preproc.v" 0 +`line 588 "t/t_preproc.v" 0 //====================================================================== //bug1225 -`line 587 "t/t_preproc.v" 0 +`line 591 "t/t_preproc.v" 0 $display("RAM0"); $display("CPU"); -`line 592 "t/t_preproc.v" 0 +`line 596 "t/t_preproc.v" 0 -`line 597 "t/t_preproc.v" 0 +`line 601 "t/t_preproc.v" 0 XXE_FAMILY = XXE_ @@ -891,7 +896,7 @@ XXE_FAMILY = XXE_ $display("XXE_ is defined"); -`line 604 "t/t_preproc.v" 0 +`line 608 "t/t_preproc.v" 0 XYE_FAMILY = XYE_ @@ -899,7 +904,7 @@ XYE_FAMILY = XYE_ $display("XYE_ is defined"); -`line 611 "t/t_preproc.v" 0 +`line 615 "t/t_preproc.v" 0 XXS_FAMILY = XXS_some @@ -907,7 +912,7 @@ XXS_FAMILY = XXS_some $display("XXS_some is defined"); -`line 618 "t/t_preproc.v" 0 +`line 622 "t/t_preproc.v" 0 XYS_FAMILY = XYS_foo @@ -915,10 +920,10 @@ XYS_FAMILY = XYS_foo $display("XYS_foo is defined"); -`line 625 "t/t_preproc.v" 0 +`line 629 "t/t_preproc.v" 0 //==== -`line 627 "t/t_preproc.v" 0 +`line 631 "t/t_preproc.v" 0 @@ -927,7 +932,7 @@ XYS_FAMILY = XYS_foo -`line 635 "t/t_preproc.v" 0 +`line 639 "t/t_preproc.v" 0 @@ -935,7 +940,7 @@ XYS_FAMILY = XYS_foo -`line 642 "t/t_preproc.v" 0 +`line 646 "t/t_preproc.v" 0 @@ -943,7 +948,7 @@ XYS_FAMILY = XYS_foo -`line 649 "t/t_preproc.v" 0 +`line 653 "t/t_preproc.v" 0 @@ -951,26 +956,26 @@ XYS_FAMILY = XYS_foo -`line 656 "t/t_preproc.v" 0 - - -`line 658 "t/t_preproc.v" 0 - // NEVER - `line 660 "t/t_preproc.v" 0 + + +`line 662 "t/t_preproc.v" 0 + // NEVER + +`line 664 "t/t_preproc.v" 0 //bug1227 (.mySig (myInterface.pa5), -`line 664 "t/t_preproc.v" 0 +`line 668 "t/t_preproc.v" 0 //====================================================================== // Stringify bug -`line 667 "t/t_preproc.v" 0 +`line 671 "t/t_preproc.v" 0 `dbg_hdl(UVM_LOW, ("Functional coverage enabled: paramgrp")); -`line 670 "t/t_preproc.v" 0 +`line 674 "t/t_preproc.v" 0 @@ -979,28 +984,28 @@ XYS_FAMILY = XYS_foo -`line 678 "t/t_preproc.v" 0 +`line 682 "t/t_preproc.v" 0 module pcc2_cfg; generate -`line 680 "t/t_preproc.v" 0 +`line 684 "t/t_preproc.v" 0 covergroup a @(posedge b); -`line 680 "t/t_preproc.v" 0 +`line 684 "t/t_preproc.v" 0 c: coverpoint d iff ((c) === 1'b1); endgroup -`line 680 "t/t_preproc.v" 0 +`line 684 "t/t_preproc.v" 0 a u_a; -`line 680 "t/t_preproc.v" 0 +`line 684 "t/t_preproc.v" 0 initial do begin $display ("DEBUG : %s [%m]", $sformatf ("Functional coverage enabled: u_a")); end while(0); endgenerate endmodule -`line 684 "t/t_preproc.v" 0 +`line 688 "t/t_preproc.v" 0 //====================================================================== // Verilog-Perl bug1668 "`NOT_DEFINED_STR" -`line 689 "t/t_preproc.v" 0 +`line 693 "t/t_preproc.v" 0 //====================================================================== // IEEE mandated predefines // undefineall should have no effect on these @@ -1023,4 +1028,4 @@ predef 2 2 // After `undefineall above, for testing --dump-defines -`line 711 "t/t_preproc.v" 2 +`line 715 "t/t_preproc.v" 2 From 519cc7d61c948920648ec4ebf6c5d819aa5608c3 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Mon, 2 Aug 2021 13:51:18 +0100 Subject: [PATCH 62/84] Internals: Remove code duplication from V3Order No functional change --- src/V3Order.cpp | 32 +++++++------------------------- 1 file changed, 7 insertions(+), 25 deletions(-) diff --git a/src/V3Order.cpp b/src/V3Order.cpp index 0ea3e82c0..93ad38651 100644 --- a/src/V3Order.cpp +++ b/src/V3Order.cpp @@ -259,41 +259,23 @@ private: bool m_newClkMarked; // Flag for deciding whether a new run is needed bool m_inAss = false; // Currently inside of a assignment int m_childClkWidth = 0; // If in hasClk, width of clock signal in child - int m_rightClkWidth = 0; // Clk width on the RHS // METHODS VL_DEBUG_FUNC; // Declare debug() virtual void visit(AstNodeAssign* nodep) override { m_hasClk = false; - if (AstVarRef* varrefp = VN_CAST(nodep->rhsp(), VarRef)) { - this->visit(varrefp); - m_rightClkWidth = varrefp->width(); - if (varrefp->varp()->attrClocker() == VVarAttrClocker::CLOCKER_YES) { - if (m_inClocked) { - varrefp->v3warn( - CLKDATA, "Clock used as data (on rhs of assignment) in sequential block " - << varrefp->prettyNameQ()); - } else { - m_hasClk = true; - UINFO(5, "node is already marked as clocker " << varrefp << endl); - } - } - } else { - m_inAss = true; - m_childClkWidth = 0; - iterateAndNextNull(nodep->rhsp()); - m_rightClkWidth = m_childClkWidth; - m_inAss = false; - } - - // do the marking + m_inAss = true; + m_childClkWidth = 0; + iterateAndNextNull(nodep->rhsp()); + m_inAss = false; if (m_hasClk) { - if (nodep->lhsp()->width() > m_rightClkWidth) { + // do the marking + if (nodep->lhsp()->width() > m_childClkWidth) { nodep->v3warn(CLKDATA, "Clock is assigned to part of data signal " << nodep->lhsp()->prettyNameQ()); UINFO(4, "CLKDATA: lhs with width " << nodep->lhsp()->width() << endl); - UINFO(4, " but rhs clock with width " << m_rightClkWidth << endl); + UINFO(4, " but rhs clock with width " << m_childClkWidth << endl); return; // skip the marking } From 2c8456ea75237453e99097af3441b2254a8c894c Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Mon, 2 Aug 2021 15:33:31 +0100 Subject: [PATCH 63/84] Internals: Cleanup V3SplitVar - Use C++11 initialization syntax - Use C++11 for loops - Add const - Factor out repeated _->fileline() sub-expressions - Factor out issuing warning message No functional change. --- src/V3SplitVar.cpp | 365 +++++++++++++-------------- test_regress/t/t_split_var_1_bad.out | 16 +- 2 files changed, 189 insertions(+), 192 deletions(-) diff --git a/src/V3SplitVar.cpp b/src/V3SplitVar.cpp index b06f0ec76..2f96ecbdd 100644 --- a/src/V3SplitVar.cpp +++ b/src/V3SplitVar.cpp @@ -127,14 +127,12 @@ struct SplitVarImpl { static AstNodeAssign* newAssign(FileLine* fileline, AstNode* lhsp, AstNode* rhsp, const AstVar* varp) { if (varp->isFuncLocal() || varp->isFuncReturn()) { - return new AstAssign(fileline, lhsp, rhsp); + return new AstAssign{fileline, lhsp, rhsp}; } else { - return new AstAssignW(fileline, lhsp, rhsp); + return new AstAssignW{fileline, lhsp, rhsp}; } } - static const char* const notSplitMsg; - // These check functions return valid pointer to the reason text if a variable cannot be split. // Check if a var type can be split @@ -152,10 +150,12 @@ struct SplitVarImpl { return nullptr; } - static const char* cannotSplitConnectedPortReason(AstPin* pinp) { - AstVar* varp = pinp->modVarp(); + static const char* cannotSplitConnectedPortReason(const AstPin* pinp) { + const AstVar* const varp = pinp->modVarp(); if (!varp) return "it is not connected"; - if (const char* reason = cannotSplitVarDirectionReason(varp->direction())) return reason; + if (const char* const reason = cannotSplitVarDirectionReason(varp->direction())) { + return reason; + } return nullptr; } @@ -167,11 +167,15 @@ struct SplitVarImpl { } static const char* cannotSplitVarCommonReason(const AstVar* varp) { - if (AstNodeFTask* taskp = VN_CAST(varp->backp(), NodeFTask)) { - if (const char* reason = cannotSplitTaskReason(taskp)) return reason; + if (const AstNodeFTask* const taskp = VN_CAST(varp->backp(), NodeFTask)) { + if (const char* const reason = cannotSplitTaskReason(taskp)) return reason; + } + if (const char* const reason = cannotSplitVarTypeReason(varp->varType())) { + return reason; + } + if (const char* const reason = cannotSplitVarDirectionReason(varp->direction())) { + return reason; } - if (const char* reason = cannotSplitVarTypeReason(varp->varType())) return reason; - if (const char* reason = cannotSplitVarDirectionReason(varp->direction())) return reason; if (varp->isSigPublic()) return "it is public"; if (varp->isUsedLoopIdx()) return "it is used as a loop variable"; return nullptr; @@ -185,7 +189,7 @@ struct SplitVarImpl { stmtp->unlinkFrBack(); // Insert begin-end because temp value may be inserted to this block later. const std::string name = "__VsplitVarBlk" + cvtToStr(modp->varNumGetInc()); - ap->addStmtp(new AstBegin(ap->fileline(), name, stmtp)); + ap->addStmtp(new AstBegin{ap->fileline(), name, stmtp}); } } @@ -193,28 +197,34 @@ struct SplitVarImpl { if (initp->isJustOneBodyStmt() && initp->bodysp() == stmtp) { stmtp->unlinkFrBack(); // Insert begin-end because temp value may be inserted to this block later. + FileLine* const fl = initp->fileline(); const std::string name = "__VsplitVarBlk" + cvtToStr(modp->varNumGetInc()); - initp->replaceWith( - new AstInitial(initp->fileline(), new AstBegin(initp->fileline(), name, stmtp))); + initp->replaceWith(new AstInitial{fl, new AstBegin{fl, name, stmtp}}); VL_DO_DANGLING(initp->deleteTree(), initp); } } static void insertBeginIfNecessary(AstNodeStmt* stmtp, AstNodeModule* modp) { - AstNode* backp = stmtp->backp(); - if (AstAlways* ap = VN_CAST(backp, Always)) { + AstNode* const backp = stmtp->backp(); + if (AstAlways* const ap = VN_CAST(backp, Always)) { insertBeginCore(ap, stmtp, modp); - } else if (AstAlwaysPublic* ap = VN_CAST(backp, AlwaysPublic)) { + } else if (AstAlwaysPublic* const ap = VN_CAST(backp, AlwaysPublic)) { insertBeginCore(ap, stmtp, modp); - } else if (AstInitial* ap = VN_CAST(backp, Initial)) { + } else if (AstInitial* const ap = VN_CAST(backp, Initial)) { insertBeginCore(ap, stmtp, modp); } } }; // SplitVarImpl -const char* const SplitVarImpl::notSplitMsg - = " has split_var metacomment but will not be split because "; +//###################################################################### +// Utilities required in wharious placs + +static void warnNoSplit(const AstVar* varp, const AstNode* wherep, const char* reasonp) { + wherep->v3warn(SPLITVAR, varp->prettyNameQ() + << " has split_var metacomment but will not be split because " + << reasonp << ".\n"); +} //###################################################################### // Split Unpacked Variables @@ -287,12 +297,11 @@ class UnpackRefMap final { public: using MapType = std::map, AstNodeComparator>; using MapIt = MapType::iterator; - using SetIt = MapType::value_type::second_type::iterator; private: MapType m_map; bool addCore(AstVarRef* refp, const UnpackRef& ref) { - AstVar* varp = refp->varp(); + AstVar* const varp = refp->varp(); UASSERT_OBJ(varp->attrSplitVar(), varp, " no split_var metacomment"); const MapIt it = m_map.find(varp); if (it == m_map.end()) return false; // Not registered @@ -358,23 +367,23 @@ public: v.iterate(nodep); } void visit(AstNVisitor* visitor) { - for (const auto& varp : m_vars) visitor->iterate(varp); - for (auto it = m_sels.begin(), it_end = m_sels.end(); it != it_end; ++it) { + for (AstVar* const varp : m_vars) visitor->iterate(varp); + for (AstSel* const selp : m_sels) { // If m_refs includes VarRef from ArraySel, remove it // because the VarRef would not be visited in SplitPackedVarVisitor::visit(AstSel*). - if (AstVarRef* refp = VN_CAST((*it)->fromp(), VarRef)) { + if (AstVarRef* const refp = VN_CAST(selp->fromp(), VarRef)) { m_refs.erase(refp); - } else if (AstVarRef* refp = VN_CAST((*it)->lsbp(), VarRef)) { + } else if (AstVarRef* const refp = VN_CAST(selp->lsbp(), VarRef)) { m_refs.erase(refp); - } else if (AstVarRef* refp = VN_CAST((*it)->widthp(), VarRef)) { + } else if (AstVarRef* const refp = VN_CAST(selp->widthp(), VarRef)) { m_refs.erase(refp); } - UASSERT_OBJ(reinterpret_cast((*it)->op1p()) != 1, *it, "stale"); - visitor->iterate(*it); + UASSERT_OBJ(reinterpret_cast(selp->op1p()) != 1, selp, "stale"); + visitor->iterate(selp); } - for (auto it = m_refs.begin(), it_end = m_refs.end(); it != it_end; ++it) { - UASSERT_OBJ(reinterpret_cast((*it)->op1p()) != 1, *it, "stale"); - visitor->iterate(*it); + for (AstVarRef* const vrefp : m_refs) { + UASSERT_OBJ(reinterpret_cast(vrefp->op1p()) != 1, vrefp, "stale"); + visitor->iterate(vrefp); } } }; @@ -394,13 +403,14 @@ class SplitUnpackedVarVisitor final : public AstNVisitor, public SplitVarImpl { SplitVarRefsMap m_refsForPackedSplit; static AstVarRef* isTargetVref(AstNode* nodep) { - if (AstVarRef* refp = VN_CAST(nodep, VarRef)) { + if (AstVarRef* const refp = VN_CAST(nodep, VarRef)) { if (refp->varp()->attrSplitVar()) return refp; } return nullptr; } - static int outerMostSizeOfUnpackedArray(AstVar* nodep) { - AstUnpackArrayDType* dtypep = VN_CAST(nodep->dtypep()->skipRefp(), UnpackArrayDType); + static int outerMostSizeOfUnpackedArray(const AstVar* nodep) { + const AstUnpackArrayDType* const dtypep + = VN_CAST_CONST(nodep->dtypep()->skipRefp(), UnpackArrayDType); UASSERT_OBJ(dtypep, nodep, "Must be unapcked array"); return dtypep->elementsConst(); } @@ -425,13 +435,13 @@ class SplitUnpackedVarVisitor final : public AstNVisitor, public SplitVarImpl { AstNVisitor::pushDeletep(nodep); } AstVar* newVar(FileLine* fl, AstVarType type, const std::string& name, AstNodeDType* dtp) { - AstVar* varp = new AstVar(fl, type, name, dtp); + AstVar* const varp = new AstVar{fl, type, name, dtp}; UASSERT_OBJ(m_modp, varp, "Must not nullptr"); m_refsForPackedSplit[m_modp].add(varp); return varp; } AstVarRef* newVarRef(FileLine* fl, AstVar* varp, const VAccess& access) { - AstVarRef* refp = new AstVarRef(fl, varp, access); + AstVarRef* const refp = new AstVarRef{fl, varp, access}; UASSERT_OBJ(m_modp, refp, "Must not nullptr"); m_refsForPackedSplit[m_modp].add(refp); return refp; @@ -473,7 +483,7 @@ class SplitUnpackedVarVisitor final : public AstNVisitor, public SplitVarImpl { VL_RESTORER(m_contextp); { m_contextp = nodep; - AstNodeFTask* ftaskp = nodep->taskp(); + AstNodeFTask* const ftaskp = nodep->taskp(); UASSERT_OBJ(ftaskp, nodep, "Unlinked"); // Iterate arguments of a function/task. for (AstNode *argp = nodep->pinsp(), *paramp = ftaskp->stmtsp(); argp; @@ -495,12 +505,9 @@ class SplitUnpackedVarVisitor final : public AstNVisitor, public SplitVarImpl { m_foundTargetVar.clear(); iterate(argp); if (reason) { - for (VarSet::iterator it = m_foundTargetVar.begin(), - it_end = m_foundTargetVar.end(); - it != it_end; ++it) { - argp->v3warn(SPLITVAR, (*it)->prettyNameQ() - << notSplitMsg << reason << ".\n"); - m_refs.remove(*it); + for (AstVar* const varp : m_foundTargetVar) { + warnNoSplit(varp, argp, reason); + m_refs.remove(varp); } } m_foundTargetVar.clear(); @@ -509,15 +516,14 @@ class SplitUnpackedVarVisitor final : public AstNVisitor, public SplitVarImpl { } virtual void visit(AstPin* nodep) override { UINFO(5, nodep->modVarp()->prettyNameQ() << " pin \n"); - AstNode* exprp = nodep->exprp(); + AstNode* const exprp = nodep->exprp(); if (!exprp) return; // Not connected pin m_foundTargetVar.clear(); iterate(exprp); if (const char* reason = cannotSplitConnectedPortReason(nodep)) { - for (VarSet::iterator it = m_foundTargetVar.begin(), it_end = m_foundTargetVar.end(); - it != it_end; ++it) { - nodep->v3warn(SPLITVAR, (*it)->prettyNameQ() << notSplitMsg << reason << ".\n"); - m_refs.remove(*it); + for (AstVar* const varp : m_foundTargetVar) { + warnNoSplit(varp, nodep, reason); + m_refs.remove(varp); } m_foundTargetVar.clear(); } @@ -550,22 +556,18 @@ class SplitUnpackedVarVisitor final : public AstNVisitor, public SplitVarImpl { iterateChildren(nodep); } virtual void visit(AstArraySel* nodep) override { - if (AstVarRef* refp = isTargetVref(nodep->fromp())) { - AstConst* indexp = VN_CAST(nodep->bitp(), Const); + if (AstVarRef* const refp = isTargetVref(nodep->fromp())) { + AstConst* const indexp = VN_CAST(nodep->bitp(), Const); if (indexp) { // OK UINFO(4, "add " << nodep << " for " << refp->varp()->prettyName() << "\n"); if (indexp->toSInt() < outerMostSizeOfUnpackedArray(refp->varp())) { m_refs.tryAdd(m_contextp, refp, nodep, indexp->toSInt(), m_inFTask); } else { - nodep->bitp()->v3warn(SPLITVAR, refp->prettyNameQ() - << notSplitMsg - << "index is out of range.\n"); + warnNoSplit(refp->varp(), nodep->bitp(), "index is out of range"); m_refs.remove(refp->varp()); } } else { - nodep->bitp()->v3warn(SPLITVAR, refp->prettyNameQ() - << notSplitMsg - << "index cannot be determined statically.\n"); + warnNoSplit(refp->varp(), nodep->bitp(), "index cannot be determined statically"); m_refs.remove(refp->varp()); iterate(nodep->bitp()); } @@ -574,8 +576,8 @@ class SplitUnpackedVarVisitor final : public AstNVisitor, public SplitVarImpl { } } virtual void visit(AstSliceSel* nodep) override { - if (AstVarRef* refp = isTargetVref(nodep->fromp())) { - AstUnpackArrayDType* dtypep + if (AstVarRef* const refp = isTargetVref(nodep->fromp())) { + const AstUnpackArrayDType* const dtypep = VN_CAST(refp->varp()->dtypep()->skipRefp(), UnpackArrayDType); // declRange() of AstSliceSel is shifted by dtypep->declRange().lo() in V3WidthSel.cpp // restore the original decl range here. @@ -592,7 +594,7 @@ class SplitUnpackedVarVisitor final : public AstNVisitor, public SplitVarImpl { } } static AstNode* toInsertPoint(AstNode* insertp) { - if (AstNodeStmt* stmtp = VN_CAST(insertp, NodeStmt)) { + if (AstNodeStmt* const stmtp = VN_CAST(insertp, NodeStmt)) { if (!stmtp->isStatement()) insertp = stmtp->backp(); } return insertp; @@ -600,15 +602,16 @@ class SplitUnpackedVarVisitor final : public AstNVisitor, public SplitVarImpl { AstVarRef* createTempVar(AstNode* context, AstNode* nodep, AstUnpackArrayDType* dtypep, const std::string& name_prefix, std::vector& vars, int start_idx, bool lvalue, bool ftask) { + FileLine* const fl = nodep->fileline(); const std::string name = "__VsplitVar" + cvtToStr(m_modp->varNumGetInc()) + "__" + name_prefix; - AstNodeAssign* assignp = VN_CAST(context, NodeAssign); + AstNodeAssign* const assignp = VN_CAST(context, NodeAssign); if (assignp) { // "always_comb a = b;" to "always_comb begin a = b; end" so that local // variable can be added. insertBeginIfNecessary(assignp, m_modp); } - AstVar* varp = newVar(nodep->fileline(), AstVarType::VAR, name, dtypep); + AstVar* const varp = newVar(fl, AstVarType::VAR, name, dtypep); // Variable will be registered in the caller side. UINFO(3, varp->prettyNameQ() << " is created lsb:" << dtypep->lo() << " msb:" << dtypep->hi() << "\n"); @@ -617,18 +620,17 @@ class SplitUnpackedVarVisitor final : public AstNVisitor, public SplitVarImpl { = (context && VN_IS(context, NodeFTaskRef)) || (assignp && VN_IS(assignp, Assign)); for (int i = 0; i < dtypep->elementsConst(); ++i) { - AstNode* lhsp = newVarRef(nodep->fileline(), vars.at(start_idx + i), - lvalue ? VAccess::WRITE : VAccess::READ); - AstNode* rhsp = new AstArraySel( - nodep->fileline(), - newVarRef(nodep->fileline(), varp, !lvalue ? VAccess::WRITE : VAccess::READ), i); + AstNode* lhsp + = newVarRef(fl, vars.at(start_idx + i), lvalue ? VAccess::WRITE : VAccess::READ); + AstNode* rhsp = new AstArraySel{ + fl, newVarRef(fl, varp, !lvalue ? VAccess::WRITE : VAccess::READ), i}; AstNode* refp = lhsp; UINFO(9, "Creating assign idx:" << i << " + " << start_idx << "\n"); if (!lvalue) std::swap(lhsp, rhsp); AstNode* newassignp; if (use_simple_assign) { - AstNode* insertp = toInsertPoint(context); - newassignp = new AstAssign(nodep->fileline(), lhsp, rhsp); + AstNode* const insertp = toInsertPoint(context); + newassignp = new AstAssign{fl, lhsp, rhsp}; if (lvalue) { // If varp is LHS, this assignment must appear after the original // assignment(context). @@ -638,28 +640,28 @@ class SplitUnpackedVarVisitor final : public AstNVisitor, public SplitVarImpl { insertp->addHereThisAsNext(newassignp); } } else { - newassignp = new AstAssignW(nodep->fileline(), lhsp, rhsp); + newassignp = new AstAssignW{fl, lhsp, rhsp}; // Continuous assignment must be in module context. varp->addNextHere(newassignp); } UASSERT_OBJ(!m_contextp, m_contextp, "must be null"); setContextAndIterate(newassignp, refp); } - return newVarRef(nodep->fileline(), varp, lvalue ? VAccess::WRITE : VAccess::READ); + return newVarRef(fl, varp, lvalue ? VAccess::WRITE : VAccess::READ); } void connectPort(AstVar* varp, std::vector& vars, AstNode* insertp) { UASSERT_OBJ(varp->isIO(), varp, "must be port"); insertp = insertp ? toInsertPoint(insertp) : nullptr; const bool lvalue = varp->direction().isWritable(); + FileLine* const fl = varp->fileline(); for (size_t i = 0; i < vars.size(); ++i) { - AstNode* nodes[] = { - new AstArraySel( - varp->fileline(), - newVarRef(varp->fileline(), varp, lvalue ? VAccess::WRITE : VAccess::READ), i), - newVarRef(varp->fileline(), vars.at(i), !lvalue ? VAccess::WRITE : VAccess::READ)}; - AstNode* lhsp = nodes[lvalue ? 0 : 1]; - AstNode* rhsp = nodes[lvalue ? 1 : 0]; - AstNodeAssign* assignp = newAssign(varp->fileline(), lhsp, rhsp, varp); + AstNode* const nodes[] = { + new AstArraySel{fl, newVarRef(fl, varp, lvalue ? VAccess::WRITE : VAccess::READ), + static_cast(i)}, + newVarRef(fl, vars.at(i), !lvalue ? VAccess::WRITE : VAccess::READ)}; + AstNode* const lhsp = nodes[lvalue ? 0 : 1]; + AstNode* const rhsp = nodes[lvalue ? 1 : 0]; + AstNodeAssign* const assignp = newAssign(fl, lhsp, rhsp, varp); if (insertp) { if (lvalue) { // Just after writing to the temporary variable insertp->addNextHere(assignp); @@ -675,14 +677,15 @@ class SplitUnpackedVarVisitor final : public AstNVisitor, public SplitVarImpl { } size_t collapse(UnpackRefMap& refs) { size_t numSplit = 0; - for (UnpackRefMap::MapIt it = refs.begin(), it_end = refs.end(); it != it_end; ++it) { - UINFO(3, "In module " << m_modp->name() << " var " << it->first->prettyNameQ() - << " which has " << it->second.size() + for (const auto& pair : refs) { + UINFO(3, "In module " << m_modp->name() << " var " << pair.first->prettyNameQ() + << " which has " << pair.second.size() << " refs will be split.\n"); - AstVar* varp = it->first; + AstVar* const varp = pair.first; AstNode* insertp = varp; - AstUnpackArrayDType* dtypep = VN_CAST(varp->dtypep()->skipRefp(), UnpackArrayDType); - AstNodeDType* subTypep = dtypep->subDTypep(); + const AstUnpackArrayDType* const dtypep + = VN_CAST(varp->dtypep()->skipRefp(), UnpackArrayDType); + AstNodeDType* const subTypep = dtypep->subDTypep(); const bool needNext = VN_IS(subTypep, UnpackArrayDType); // Still unpacked array. std::vector vars; // Add the split variables @@ -701,39 +704,38 @@ class SplitUnpackedVarVisitor final : public AstNVisitor, public SplitVarImpl { vars.push_back(newp); setContextAndIterate(nullptr, newp); } - for (UnpackRefMap::SetIt sit = it->second.begin(), sit_end = it->second.end(); - sit != sit_end; ++sit) { + for (const UnpackRef& ref : pair.second) { AstNode* newp = nullptr; - if (sit->isSingleRef()) { - newp = newVarRef(sit->nodep()->fileline(), vars.at(sit->index()), - sit->access()); + if (ref.isSingleRef()) { + newp = newVarRef(ref.nodep()->fileline(), vars.at(ref.index()), ref.access()); } else { - AstVarRef* refp = VN_CAST(sit->nodep(), VarRef); + AstVarRef* refp = VN_CAST(ref.nodep(), VarRef); AstUnpackArrayDType* adtypep; int lsb = 0; if (refp) { adtypep = VN_CAST(refp->dtypep()->skipRefp(), UnpackArrayDType); } else { - AstSliceSel* selp = VN_CAST(sit->nodep(), SliceSel); - UASSERT_OBJ(selp, sit->nodep(), "Unexpected op is registered"); + AstSliceSel* selp = VN_CAST(ref.nodep(), SliceSel); + UASSERT_OBJ(selp, ref.nodep(), "Unexpected op is registered"); refp = VN_CAST(selp->fromp(), VarRef); UASSERT_OBJ(refp, selp, "Unexpected op is registered"); adtypep = VN_CAST(selp->dtypep()->skipRefp(), UnpackArrayDType); lsb = adtypep->lo(); } - AstVarRef* newrefp = createTempVar(sit->context(), refp, adtypep, varp->name(), - vars, lsb, refp->access(), sit->ftask()); + AstVarRef* const newrefp + = createTempVar(ref.context(), refp, adtypep, varp->name(), vars, lsb, + refp->access(), ref.ftask()); newp = newrefp; refp->varp()->addNextHere(newrefp->varp()); UINFO(3, "Create " << newrefp->varp()->prettyNameQ() << " for " << refp << "\n"); } - sit->nodep()->replaceWith(newp); - pushDeletep(sit->nodep()); - setContextAndIterate(sit->context(), newp->backp()); + ref.nodep()->replaceWith(newp); + pushDeletep(ref.nodep()); + setContextAndIterate(ref.context(), newp->backp()); // AstAssign is used. So assignment is necessary for each reference. if (varp->isIO() && (varp->isFuncLocal() || varp->isFuncReturn())) - connectPort(varp, vars, sit->context()); + connectPort(varp, vars, ref.context()); } if (varp->isIO()) { // AssignW will be created, so just once @@ -850,7 +852,9 @@ public: // If this is AstVarRef and referred in the sensitivity list of always@, // return the sensitivity item AstSenItem* backSenItemp() const { - if (AstVarRef* refp = VN_CAST(m_nodep, VarRef)) return VN_CAST(refp->backp(), SenItem); + if (AstVarRef* const refp = VN_CAST(m_nodep, VarRef)) { + return VN_CAST(refp->backp(), SenItem); + } return nullptr; } }; @@ -872,9 +876,7 @@ class PackedVarRef final { for (size_t i = 0; i < refs.size(); ++i) { nodes.emplace(refs[i].nodep(), i); } std::vector vect; vect.reserve(nodes.size()); - for (auto it = nodes.cbegin(), it_end = nodes.cend(); it != it_end; ++it) { - vect.push_back(refs[it->second]); - } + for (const auto& pair : nodes) vect.push_back(refs[pair.second]); refs.swap(vect); } @@ -910,16 +912,16 @@ public: std::vector plan; std::vector> points; // points.reserve(m_lhs.size() * 2 + 2); // 2 points will be added per one PackedVarRefEntry - for (const_iterator it = m_lhs.begin(), itend = m_lhs.end(); it != itend; ++it) { - points.emplace_back(std::make_pair(it->lsb(), false)); // Start of a region - points.emplace_back(std::make_pair(it->msb() + 1, true)); // End of a region + for (const PackedVarRefEntry& ref : m_lhs) { + points.emplace_back(std::make_pair(ref.lsb(), false)); // Start of a region + points.emplace_back(std::make_pair(ref.msb() + 1, true)); // End of a region } if (skipUnused && !m_rhs.empty()) { // Range to be read must be kept, so add points here int lsb = m_basicp->hi() + 1; int msb = m_basicp->lo() - 1; - for (size_t i = 0; i < m_rhs.size(); ++i) { - lsb = std::min(lsb, m_rhs[i].lsb()); - msb = std::max(msb, m_rhs[i].msb()); + for (const PackedVarRefEntry& ref : m_rhs) { + lsb = std::min(lsb, ref.lsb()); + msb = std::max(msb, ref.msb()); } UASSERT_OBJ(lsb <= msb, m_basicp, "lsb:" << lsb << " msb:" << msb << " are wrong"); points.emplace_back(std::make_pair(lsb, false)); @@ -960,8 +962,8 @@ class SplitPackedVarVisitor final : public AstNVisitor, public SplitVarImpl { } virtual void visit(AstVar* nodep) override { if (!nodep->attrSplitVar()) return; // Nothing to do - if (const char* reason = cannotSplitReason(nodep, true)) { - nodep->v3warn(SPLITVAR, nodep->prettyNameQ() << notSplitMsg << reason); + if (const char* const reason = cannotSplitReason(nodep, true)) { + warnNoSplit(nodep, nodep, reason); nodep->attrSplitVar(false); } else { // Finally find a good candidate const bool inserted = m_refs.insert(std::make_pair(nodep, PackedVarRef(nodep))).second; @@ -969,7 +971,7 @@ class SplitPackedVarVisitor final : public AstNVisitor, public SplitVarImpl { } } virtual void visit(AstVarRef* nodep) override { - AstVar* varp = nodep->varp(); + AstVar* const varp = nodep->varp(); visit(varp); const auto refit = m_refs.find(varp); if (refit == m_refs.end()) return; // variable without split_var metacomment @@ -983,13 +985,13 @@ class SplitPackedVarVisitor final : public AstNVisitor, public SplitVarImpl { << " Entire bit of [" << basicp->lo() << "+:" << varp->width() << "] \n"); } virtual void visit(AstSel* nodep) override { - AstVarRef* vrefp = VN_CAST(nodep->fromp(), VarRef); + AstVarRef* const vrefp = VN_CAST(nodep->fromp(), VarRef); if (!vrefp) { iterateChildren(nodep); return; } - AstVar* varp = vrefp->varp(); + AstVar* const varp = vrefp->varp(); const auto refit = m_refs.find(varp); if (refit == m_refs.end()) { iterateChildren(nodep); @@ -1009,9 +1011,7 @@ class SplitPackedVarVisitor final : public AstNVisitor, public SplitVarImpl { << " [" << consts[0]->toSInt() << ":+" << consts[1]->toSInt() << "] lsb:" << refit->second.basicp()->lo() << "\n"); } else { - nodep->v3warn(SPLITVAR, vrefp->prettyNameQ() - << notSplitMsg - << "its bit range cannot be determined statically."); + warnNoSplit(vrefp->varp(), nodep, "its bit range cannot be determined statically"); if (!consts[0]) { UINFO(4, "LSB " << nodep->lsbp() << " is expected to be constant, but not\n"); } @@ -1028,7 +1028,8 @@ class SplitPackedVarVisitor final : public AstNVisitor, public SplitVarImpl { // Extract necessary bit range from a newly created variable to meet ref static AstNode* extractBits(const PackedVarRefEntry& ref, const SplitNewVar& var, const VAccess access) { - AstVarRef* refp = new AstVarRef(ref.nodep()->fileline(), var.varp(), access); + FileLine* const fl = ref.nodep()->fileline(); + AstVarRef* const refp = new AstVarRef{fl, var.varp(), access}; if (ref.lsb() <= var.lsb() && var.msb() <= ref.msb()) { // Use the entire bits return refp; } else { // Use slice @@ -1038,27 +1039,25 @@ class SplitPackedVarVisitor final : public AstNVisitor, public SplitVarImpl { UINFO(4, var.varp()->prettyNameQ() << "[" << msb << ":" << lsb << "] used for " << ref.nodep()->prettyNameQ() << '\n'); // LSB of varp is always 0. "lsb - var.lsb()" means this. see also SplitNewVar - AstSel* selp = new AstSel(ref.nodep()->fileline(), refp, lsb - var.lsb(), bitwidth); - return selp; + return new AstSel{fl, refp, lsb - var.lsb(), bitwidth}; } } static void connectPortAndVar(const std::vector& vars, AstVar* portp, AstNode* insertp) { for (; insertp; insertp = insertp->backp()) { - if (AstNodeStmt* stmtp = VN_CAST(insertp, NodeStmt)) { + if (const AstNodeStmt* const stmtp = VN_CAST(insertp, NodeStmt)) { if (stmtp->isStatement()) break; } } const bool in = portp->isReadOnly(); - for (size_t i = 0; i < vars.size(); ++i) { - AstNode* rhsp = new AstSel( - portp->fileline(), - new AstVarRef(portp->fileline(), portp, !in ? VAccess::WRITE : VAccess::READ), - vars[i].lsb(), vars[i].bitwidth()); - AstNode* lhsp = new AstVarRef(portp->fileline(), vars[i].varp(), - in ? VAccess::WRITE : VAccess::READ); + FileLine* const fl = portp->fileline(); + for (const SplitNewVar& var : vars) { + AstNode* rhsp + = new AstSel{fl, new AstVarRef{fl, portp, !in ? VAccess::WRITE : VAccess::READ}, + var.lsb(), var.bitwidth()}; + AstNode* lhsp = new AstVarRef{fl, var.varp(), in ? VAccess::WRITE : VAccess::READ}; if (!in) std::swap(lhsp, rhsp); - AstNodeAssign* assignp = newAssign(portp->fileline(), lhsp, rhsp, portp); + AstNodeAssign* const assignp = newAssign(fl, lhsp, rhsp, portp); if (insertp) { if (in) { insertp->addHereThisAsNext(assignp); @@ -1066,15 +1065,14 @@ class SplitPackedVarVisitor final : public AstNVisitor, public SplitVarImpl { insertp->addNextHere(assignp); } } else { - vars[i].varp()->addNextHere(assignp); + var.varp()->addNextHere(assignp); } } } void createVars(AstVar* varp, const AstBasicDType* basicp, std::vector& vars) { - for (size_t i = 0; i < vars.size(); ++i) { - SplitNewVar* newvarp = &vars[i]; - int left = newvarp->msb(); - int right = newvarp->lsb(); + for (SplitNewVar& newvar : vars) { + int left = newvar.msb(); + int right = newvar.lsb(); if (basicp->littleEndian()) std::swap(left, right); const std::string name = (left == right) @@ -1085,93 +1083,93 @@ class SplitPackedVarVisitor final : public AstNVisitor, public SplitVarImpl { AstBasicDType* dtypep; switch (basicp->keyword()) { case AstBasicDTypeKwd::BIT: - dtypep = new AstBasicDType(varp->subDTypep()->fileline(), VFlagBitPacked(), - newvarp->bitwidth()); + dtypep = new AstBasicDType{varp->subDTypep()->fileline(), VFlagBitPacked(), + newvar.bitwidth()}; break; case AstBasicDTypeKwd::LOGIC: - dtypep = new AstBasicDType(varp->subDTypep()->fileline(), VFlagLogicPacked(), - newvarp->bitwidth()); + dtypep = new AstBasicDType{varp->subDTypep()->fileline(), VFlagLogicPacked(), + newvar.bitwidth()}; break; default: UASSERT_OBJ(false, basicp, "Only bit and logic are allowed"); } - dtypep->rangep(new AstRange{varp->fileline(), VNumRange{newvarp->msb(), newvarp->lsb(), - basicp->littleEndian()}}); - newvarp->varp(new AstVar(varp->fileline(), AstVarType::VAR, name, dtypep)); - newvarp->varp()->propagateAttrFrom(varp); - newvarp->varp()->funcLocal(varp->isFuncLocal() || varp->isFuncReturn()); + dtypep->rangep(new AstRange{ + varp->fileline(), VNumRange{newvar.msb(), newvar.lsb(), basicp->littleEndian()}}); + newvar.varp(new AstVar{varp->fileline(), AstVarType::VAR, name, dtypep}); + newvar.varp()->propagateAttrFrom(varp); + newvar.varp()->funcLocal(varp->isFuncLocal() || varp->isFuncReturn()); // Enable this line to trace split variable directly: - // newvarp->varp()->trace(varp->isTrace()); + // newvar.varp()->trace(varp->isTrace()); m_netp->typeTablep()->addTypesp(dtypep); - varp->addNextHere(newvarp->varp()); - UINFO(4, newvarp->varp()->prettyNameQ() + varp->addNextHere(newvar.varp()); + UINFO(4, newvar.varp()->prettyNameQ() << " is added for " << varp->prettyNameQ() << '\n'); } } static void updateReferences(AstVar* varp, PackedVarRef& ref, const std::vector& vars) { - for (int lvalue = 0; lvalue <= 1; ++lvalue) { // Refer the new split variables + for (const bool lvalue : {false, true}) { // Refer the new split variables std::vector& refs = lvalue ? ref.lhs() : ref.rhs(); - for (PackedVarRef::iterator refit = refs.begin(), refitend = refs.end(); - refit != refitend; ++refit) { - auto varit = std::upper_bound(vars.begin(), vars.end(), refit->lsb(), - SplitNewVar::Match()); - UASSERT_OBJ(varit != vars.end(), refit->nodep(), "Not found"); - UASSERT(!(varit->msb() < refit->lsb() || refit->msb() < varit->lsb()), + for (PackedVarRefEntry& ref : refs) { + auto varit + = std::upper_bound(vars.begin(), vars.end(), ref.lsb(), SplitNewVar::Match()); + UASSERT_OBJ(varit != vars.end(), ref.nodep(), "Not found"); + UASSERT(!(varit->msb() < ref.lsb() || ref.msb() < varit->lsb()), "wrong search result"); AstNode* prevp; bool inSentitivityList = false; - if (AstSenItem* senitemp = refit->backSenItemp()) { - AstNode* oldsenrefp = senitemp->sensp(); + if (AstSenItem* const senitemp = ref.backSenItemp()) { + AstNode* const oldsenrefp = senitemp->sensp(); oldsenrefp->replaceWith( - new AstVarRef(senitemp->fileline(), varit->varp(), VAccess::READ)); + new AstVarRef{senitemp->fileline(), varit->varp(), VAccess::READ}); VL_DO_DANGLING(oldsenrefp->deleteTree(), oldsenrefp); prevp = senitemp; inSentitivityList = true; } else { - prevp = extractBits(*refit, *varit, lvalue ? VAccess::WRITE : VAccess::READ); + prevp = extractBits(ref, *varit, lvalue ? VAccess::WRITE : VAccess::READ); } - for (int residue = refit->msb() - varit->msb(); residue > 0; + for (int residue = ref.msb() - varit->msb(); residue > 0; residue -= varit->bitwidth()) { ++varit; - UASSERT_OBJ(varit != vars.end(), refit->nodep(), "not enough split variables"); - if (AstSenItem* senitemp = VN_CAST(prevp, SenItem)) { - prevp = new AstSenItem( + UASSERT_OBJ(varit != vars.end(), ref.nodep(), "not enough split variables"); + if (AstSenItem* const senitemp = VN_CAST(prevp, SenItem)) { + prevp = new AstSenItem{ senitemp->fileline(), senitemp->edgeType(), - new AstVarRef(senitemp->fileline(), varit->varp(), VAccess::READ)); + new AstVarRef{senitemp->fileline(), varit->varp(), VAccess::READ}}; senitemp->addNextHere(prevp); } else { - AstNode* bitsp - = extractBits(*refit, *varit, lvalue ? VAccess::WRITE : VAccess::READ); - prevp = new AstConcat(refit->nodep()->fileline(), bitsp, prevp); + AstNode* const bitsp + = extractBits(ref, *varit, lvalue ? VAccess::WRITE : VAccess::READ); + prevp = new AstConcat{ref.nodep()->fileline(), bitsp, prevp}; } } // If varp is an argument of task/func, need to update temporary var // everytime the var is updated. See also another call of connectPortAndVar() in // split() if (varp->isIO() && (varp->isFuncLocal() || varp->isFuncReturn())) - connectPortAndVar(vars, varp, refit->nodep()); - if (!inSentitivityList) refit->replaceNodeWith(prevp); - UASSERT_OBJ(varit->msb() >= refit->msb(), varit->varp(), "Out of range"); + connectPortAndVar(vars, varp, ref.nodep()); + if (!inSentitivityList) ref.replaceNodeWith(prevp); + UASSERT_OBJ(varit->msb() >= ref.msb(), varit->varp(), "Out of range"); } } } // Do the actual splitting operation void split() { - for (auto it = m_refs.begin(), it_end = m_refs.end(); it != it_end; ++it) { - it->second.dedup(); - AstVar* varp = it->first; + for (auto& pair : m_refs) { + AstVar* const varp = pair.first; + PackedVarRef& ref = pair.second; + ref.dedup(); UINFO(3, "In module " << m_modp->name() << " var " << varp->prettyNameQ() - << " which has " << it->second.lhs().size() << " lhs refs and " - << it->second.rhs().size() << " rhs refs will be split.\n"); + << " which has " << ref.lhs().size() << " lhs refs and " + << ref.rhs().size() << " rhs refs will be split.\n"); std::vector vars - = it->second.splitPlan(!varp->isTrace()); // If traced, all bit must be kept + = ref.splitPlan(!varp->isTrace()); // If traced, all bit must be kept if (vars.empty()) continue; if (vars.size() == 1 && vars.front().bitwidth() == varp->width()) continue; // No split - createVars(varp, it->second.basicp(), vars); // Add the split variables + createVars(varp, ref.basicp(), vars); // Add the split variables - updateReferences(varp, it->second, vars); + updateReferences(varp, ref, vars); if (varp->isIO()) { // port cannot be deleted // If varp is a port of a module, single AssignW is sufficient @@ -1179,16 +1177,15 @@ class SplitPackedVarVisitor final : public AstNVisitor, public SplitVarImpl { connectPortAndVar(vars, varp, nullptr); } else if (varp->isTrace()) { // Let's reuse the original variable for tracing - AstNode* rhsp = new AstVarRef(vars.front().varp()->fileline(), vars.front().varp(), - VAccess::READ); + AstNode* rhsp = new AstVarRef{vars.front().varp()->fileline(), vars.front().varp(), + VAccess::READ}; + FileLine* const fl = varp->fileline(); for (size_t i = 1; i < vars.size(); ++i) { - rhsp = new AstConcat( - varp->fileline(), - new AstVarRef(varp->fileline(), vars[i].varp(), VAccess::READ), rhsp); + rhsp = new AstConcat{fl, new AstVarRef{fl, vars[i].varp(), VAccess::READ}, + rhsp}; } - varp->addNextHere(newAssign(varp->fileline(), - new AstVarRef(varp->fileline(), varp, VAccess::WRITE), - rhsp, varp)); + varp->addNextHere( + newAssign(fl, new AstVarRef{fl, varp, VAccess::WRITE}, rhsp, varp)); } else { // the original variable is not used anymore. VL_DO_DANGLING(varp->unlinkFrBack()->deleteTree(), varp); } diff --git a/test_regress/t/t_split_var_1_bad.out b/test_regress/t/t_split_var_1_bad.out index 40dab2d0e..6249c2943 100644 --- a/test_regress/t/t_split_var_1_bad.out +++ b/test_regress/t/t_split_var_1_bad.out @@ -35,35 +35,35 @@ : ... In instance t.i_sub3 90 | assign outwires[12] = inwires[13]; | ^~ -%Warning-SPLITVAR: t/t_split_var_1_bad.v:17:9: 'should_show_warning0' has split_var metacomment but will not be split because it is not an aggregate type of bit nor logic +%Warning-SPLITVAR: t/t_split_var_1_bad.v:17:9: 'should_show_warning0' has split_var metacomment but will not be split because it is not an aggregate type of bit nor logic. : ... In instance t 17 | real should_show_warning0 /*verilator split_var*/ ; | ^~~~~~~~~~~~~~~~~~~~ -%Warning-SPLITVAR: t/t_split_var_1_bad.v:18:11: 'should_show_warning1' has split_var metacomment but will not be split because it is not an aggregate type of bit nor logic +%Warning-SPLITVAR: t/t_split_var_1_bad.v:18:11: 'should_show_warning1' has split_var metacomment but will not be split because it is not an aggregate type of bit nor logic. : ... In instance t 18 | string should_show_warning1 /*verilator split_var*/ ; | ^~~~~~~~~~~~~~~~~~~~ -%Warning-SPLITVAR: t/t_split_var_1_bad.v:19:11: 'should_show_warning2' has split_var metacomment but will not be split because its bitwidth is 1 +%Warning-SPLITVAR: t/t_split_var_1_bad.v:19:11: 'should_show_warning2' has split_var metacomment but will not be split because its bitwidth is 1. : ... In instance t 19 | wire should_show_warning2 /*verilator split_var*/ ; | ^~~~~~~~~~~~~~~~~~~~ -%Warning-SPLITVAR: t/t_split_var_1_bad.v:23:16: 'public_signal' has split_var metacomment but will not be split because it is public +%Warning-SPLITVAR: t/t_split_var_1_bad.v:23:16: 'public_signal' has split_var metacomment but will not be split because it is public. : ... In instance t 23 | logic [1:0] public_signal /*verilator public*/ /*verilator split_var*/ ; | ^~~~~~~~~~~~~ -%Warning-SPLITVAR: t/t_split_var_1_bad.v:31:44: 'inout_port' has split_var metacomment but will not be split because it is an inout port +%Warning-SPLITVAR: t/t_split_var_1_bad.v:31:44: 'inout_port' has split_var metacomment but will not be split because it is an inout port. : ... In instance t 31 | function int bad_func(inout logic [3:0] inout_port /*verilator split_var*/ , | ^~~~~~~~~~ -%Warning-SPLITVAR: t/t_split_var_1_bad.v:32:42: 'ref_port' has split_var metacomment but will not be split because it is a ref argument +%Warning-SPLITVAR: t/t_split_var_1_bad.v:32:42: 'ref_port' has split_var metacomment but will not be split because it is a ref argument. : ... In instance t 32 | ref logic [7:0] ref_port /*verilator split_var*/ ); | ^~~~~~~~ -%Warning-SPLITVAR: t/t_split_var_1_bad.v:37:19: 'loop_idx' has split_var metacomment but will not be split because it is used as a loop variable +%Warning-SPLITVAR: t/t_split_var_1_bad.v:37:19: 'loop_idx' has split_var metacomment but will not be split because it is used as a loop variable. : ... In instance t 37 | logic [7:0] loop_idx /*verilator split_var*/ ; | ^~~~~~~~ -%Warning-SPLITVAR: t/t_split_var_1_bad.v:62:11: 'cannot_split_genvar' has split_var metacomment but will not be split because it is not an aggregate type of bit nor logic +%Warning-SPLITVAR: t/t_split_var_1_bad.v:62:11: 'cannot_split_genvar' has split_var metacomment but will not be split because it is not an aggregate type of bit nor logic. : ... In instance t.i_sub1 62 | genvar cannot_split_genvar /*verilator split_var*/ ; | ^~~~~~~~~~~~~~~~~~~ From bc3e24c8cd145116b6b3b543838dc2fb565f2195 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Wed, 4 Aug 2021 11:48:32 +0100 Subject: [PATCH 64/84] Use c++XX instead of gnu++XX language standards. The GNU extensions to C/C++ are not required to build Verilator. Note this change does not affect __attribute__, __builtin_* and intrinsics, which are still available when using GCC or Clang with the standard language options. --- configure.ac | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/configure.ac b/configure.ac index 246f6e162..85faa4e90 100644 --- a/configure.ac +++ b/configure.ac @@ -346,14 +346,9 @@ AC_SUBST(CFG_CXXFLAGS_PROFILE) # Flag to select newest language standard supported # Macros work such that first option that passes is the one we take -# Currently enabled gnu++14/c++14 due to packaged SystemC dependency -# gnu++17 is the newest that Verilator supports -# std++03 is the oldest that Verilator supports -#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++20) -#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++17) -_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++14) -_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++11) -_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++03) +# Currently enabled c++14 due to packaged SystemC dependency +# c++17 is the newest that Verilator supports +# c++03 is the oldest that Verilator supports #_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=c++20) #_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=c++17) _MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=c++14) @@ -367,11 +362,6 @@ _MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=c++11) _MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=c++14) #_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=c++17) #_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=c++20) -_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=gnu++03) -_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=gnu++11) -_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=gnu++14) -#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=gnu++17) -#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_OLDEST,-std=gnu++20) AC_SUBST(CFG_CXXFLAGS_STD_OLDEST) # Flags for compiling Verilator internals including parser, and Verilated files From 5adc856950fb575f475e81496722aa47c5397666 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Wed, 11 Aug 2021 16:22:52 +0100 Subject: [PATCH 65/84] Tests: ignore all hashes in files_identical Also add 'h' prefix to all printed hashes, to reduce ambiguity. No functional change. --- src/V3AstNodes.cpp | 16 ++++++++-------- src/V3EmitCImp.cpp | 2 +- src/V3Hash.cpp | 9 ++++++++- src/V3Hash.h | 1 + test_regress/driver.pl | 2 +- test_regress/t/t_xml_debugcheck.out | 10 +++++----- 6 files changed, 24 insertions(+), 16 deletions(-) diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index 7fbc3be71..661c40fca 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -1065,8 +1065,8 @@ AstVarScope* AstConstPool::findTable(AstInitArray* initp) { UASSERT_OBJ(VN_IS(valuep, Const), valuep, "Const pool table entry must be Const"); } // Try to find an existing table with the same content - const uint32_t hash = V3Hasher::uncachedHash(initp).value(); - const auto& er = m_tables.equal_range(hash); + const V3Hash hash = V3Hasher::uncachedHash(initp); + const auto& er = m_tables.equal_range(hash.value()); for (auto it = er.first; it != er.second; ++it) { AstVarScope* const varScopep = it->second; const AstInitArray* const init2p = VN_CAST(varScopep->varp()->valuep(), InitArray); @@ -1076,11 +1076,11 @@ AstVarScope* AstConstPool::findTable(AstInitArray* initp) { } // No such table yet, create it. string name = "TABLE_"; - name += cvtToHex(hash); + name += hash.toString(); name += "_"; name += cvtToStr(std::distance(er.first, er.second)); AstVarScope* const varScopep = createNewEntry(name, initp); - m_tables.emplace(hash, varScopep); + m_tables.emplace(hash.value(), varScopep); return varScopep; } @@ -1094,8 +1094,8 @@ static bool sameInit(const AstConst* ap, const AstConst* bp) { AstVarScope* AstConstPool::findConst(AstConst* initp, bool mergeDType) { // Try to find an existing constant with the same value - const uint32_t hash = initp->num().toHash().value(); - const auto& er = m_consts.equal_range(hash); + const V3Hash hash = initp->num().toHash(); + const auto& er = m_consts.equal_range(hash.value()); for (auto it = er.first; it != er.second; ++it) { AstVarScope* const varScopep = it->second; const AstConst* const init2p = VN_CAST(varScopep->varp()->valuep(), Const); @@ -1106,11 +1106,11 @@ AstVarScope* AstConstPool::findConst(AstConst* initp, bool mergeDType) { } // No such constant yet, create it. string name = "CONST_"; - name += cvtToHex(hash); + name += hash.toString(); name += "_"; name += cvtToStr(std::distance(er.first, er.second)); AstVarScope* const varScopep = createNewEntry(name, initp); - m_consts.emplace(hash, varScopep); + m_consts.emplace(hash.value(), varScopep); return varScopep; } diff --git a/src/V3EmitCImp.cpp b/src/V3EmitCImp.cpp index bc5627cf4..03b84a273 100644 --- a/src/V3EmitCImp.cpp +++ b/src/V3EmitCImp.cpp @@ -497,7 +497,7 @@ class EmitCImp final : EmitCFunc { // disambiguate them V3Hash hash; for (const string& name : *m_requiredHeadersp) { hash += name; } - m_subFileName = "DepSet_" + cvtToHex(hash.value()); + m_subFileName = "DepSet_" + hash.toString(); // Open output file openNextOutputFile(*m_requiredHeadersp, m_subFileName); // Emit functions in this dependency set diff --git a/src/V3Hash.cpp b/src/V3Hash.cpp index 35d924771..65059102a 100644 --- a/src/V3Hash.cpp +++ b/src/V3Hash.cpp @@ -19,10 +19,17 @@ #include #include #include +#include V3Hash::V3Hash(const std::string& val) : m_value{static_cast(std::hash{}(val))} {} std::ostream& operator<<(std::ostream& os, const V3Hash& rhs) { - return os << std::hex << std::setw(8) << std::setfill('0') << rhs.value(); + return os << 'h' << std::hex << std::setw(8) << std::setfill('0') << rhs.value(); +} + +std::string V3Hash::toString() const { + std::ostringstream os; + os << *this; + return os.str(); } diff --git a/src/V3Hash.h b/src/V3Hash.h index 06223567c..ef00d0a83 100644 --- a/src/V3Hash.h +++ b/src/V3Hash.h @@ -46,6 +46,7 @@ public: // METHODS uint32_t value() const { return m_value; } + std::string toString() const; // OPERATORS // Comparisons diff --git a/test_regress/driver.pl b/test_regress/driver.pl index 9073406bf..240049350 100755 --- a/test_regress/driver.pl +++ b/test_regress/driver.pl @@ -2192,7 +2192,7 @@ sub files_identical { $l1[$l] =~ s/Version: Verilator[^\n]+/Version: Verilator ###/mig; $l1[$l] =~ s/CPU Time: +[0-9.]+ seconds[^\n]+/CPU Time: ###/mig; $l1[$l] =~ s/\?v=[0-9.]+/?v=latest/mig; # warning URL - $l1[$l] =~ s/DepSet_[a-f0-9]+_/DepSet_#_/mg; + $l1[$l] =~ s/_h[0-9a-f]{8}_/_h########_/mg; if ($l1[$l] =~ s/Exiting due to.*/Exiting due to/mig) { splice @l1, $l+1; # Trunc rest last; diff --git a/test_regress/t/t_xml_debugcheck.out b/test_regress/t/t_xml_debugcheck.out index 2a62297bf..585eb915a 100644 --- a/test_regress/t/t_xml_debugcheck.out +++ b/test_regress/t/t_xml_debugcheck.out @@ -1501,12 +1501,12 @@ - - - - + + + + - + From 00fe36f44cd7ea2548c1eb5a52f956562e9a99da Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Wed, 11 Aug 2021 14:30:00 +0100 Subject: [PATCH 66/84] Name temporary variables based on hash of related node. This improves output stability by removing sequence numbers and hence can improve ccache hit rate. No functional change intended. --- src/V3Ast.h | 2 -- src/V3Const.cpp | 41 ++++++++++++++++++----------- src/V3Depth.cpp | 24 +++++++---------- src/V3Premit.cpp | 21 +++++++-------- src/V3SplitVar.cpp | 25 +++++++++++------- src/V3UniqueNames.h | 31 +++++++++++++++++++--- src/V3Unknown.cpp | 31 ++++++++++++++-------- test_regress/t/t_xml_debugcheck.out | 24 ++++++++--------- 8 files changed, 121 insertions(+), 78 deletions(-) diff --git a/src/V3Ast.h b/src/V3Ast.h index e00b6f34f..47b580c50 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -2849,7 +2849,6 @@ private: bool m_recursive : 1; // Recursive module bool m_recursiveClone : 1; // If recursive, what module it clones, otherwise nullptr int m_level = 0; // 1=top module, 2=cell off top module, ... - int m_varNum = 0; // Incrementing variable number int m_typeNum = 0; // Incrementing implicit type number VLifetime m_lifetime; // Lifetime VTimescale m_timeunit; // Global time unit @@ -2890,7 +2889,6 @@ public: void level(int level) { m_level = level; } int level() const { return m_level; } bool isTop() const { return level() == 1; } - int varNumGetInc() { return ++m_varNum; } int typeNumGetInc() { return ++m_typeNum; } void modPublic(bool flag) { m_modPublic = flag; } bool modPublic() const { return m_modPublic; } diff --git a/src/V3Const.cpp b/src/V3Const.cpp index 1a2148fc9..ab2bfc58c 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -30,6 +30,7 @@ #include "V3Width.h" #include "V3Simulate.h" #include "V3Stats.h" +#include "V3UniqueNames.h" #include @@ -601,6 +602,9 @@ private: AstNode* m_scopep = nullptr; // Current scope AstAttrOf* m_attrp = nullptr; // Current attribute VDouble0 m_statBitOpReduction; // Ops reduced in ConstBitOpTreeVisitor + const bool m_globalPass; // ConstVisitor invoked as a global pass + static uint32_t s_globalPassNum; // Counts number of times ConstVisitor invoked as global pass + V3UniqueNames m_concswapNames; // For generating unique temporary variable names // METHODS VL_DEBUG_FUNC; // Declare debug() @@ -1762,14 +1766,16 @@ private: newp = AstNode::addNext(newp, asn2ap); } else { UASSERT_OBJ(m_modp, nodep, "Not under module"); + UASSERT_OBJ(m_globalPass, nodep, + "Should not reach here when not invoked on whole AstNetlist"); // We could create just one temp variable, but we'll get better optimization // if we make one per term. - string name1 = (string("__Vconcswap") + cvtToStr(m_modp->varNumGetInc())); - string name2 = (string("__Vconcswap") + cvtToStr(m_modp->varNumGetInc())); - AstVar* temp1p = new AstVar(sel1p->fileline(), AstVarType::BLOCKTEMP, name1, - VFlagLogicPacked(), msb1 - lsb1 + 1); - AstVar* temp2p = new AstVar(sel2p->fileline(), AstVarType::BLOCKTEMP, name2, - VFlagLogicPacked(), msb2 - lsb2 + 1); + AstVar* const temp1p + = new AstVar(sel1p->fileline(), AstVarType::BLOCKTEMP, + m_concswapNames.get(sel1p), VFlagLogicPacked(), msb1 - lsb1 + 1); + AstVar* const temp2p + = new AstVar(sel2p->fileline(), AstVarType::BLOCKTEMP, + m_concswapNames.get(sel2p), VFlagLogicPacked(), msb2 - lsb2 + 1); m_modp->addStmtp(temp1p); m_modp->addStmtp(temp2p); AstNodeAssign* asn1ap @@ -1931,6 +1937,7 @@ private: VL_RESTORER(m_modp); { m_modp = nodep; + m_concswapNames.reset(); iterateChildren(nodep); } } @@ -3197,7 +3204,9 @@ public: }; // CONSTRUCTORS - explicit ConstVisitor(ProcMode pmode) { + ConstVisitor(ProcMode pmode, bool globalPass) + : m_globalPass{globalPass} + , m_concswapNames{globalPass ? ("__Vconcswap_" + cvtToStr(s_globalPassNum++)) : ""} { // clang-format off switch (pmode) { case PROC_PARAMS: m_doV = true; m_doNConst = true; m_params = true; @@ -3225,6 +3234,8 @@ public: } }; +uint32_t ConstVisitor::s_globalPassNum = 0; + //###################################################################### // Const class functions @@ -3237,7 +3248,7 @@ AstNode* V3Const::constifyParamsEdit(AstNode* nodep) { // Make sure we've sized everything first nodep = V3Width::widthParamsEdit(nodep); - ConstVisitor visitor{ConstVisitor::PROC_PARAMS}; + ConstVisitor visitor{ConstVisitor::PROC_PARAMS, /* globalPass: */ false}; if (AstVar* varp = VN_CAST(nodep, Var)) { // If a var wants to be constified, it's really a param, and // we want the value to be constant. We aren't passed just the @@ -3267,7 +3278,7 @@ AstNode* V3Const::constifyGenerateParamsEdit(AstNode* nodep) { // Make sure we've sized everything first nodep = V3Width::widthGenerateParamsEdit(nodep); - ConstVisitor visitor{ConstVisitor::PROC_GENERATE}; + ConstVisitor visitor{ConstVisitor::PROC_GENERATE, /* globalPass: */ false}; if (AstVar* varp = VN_CAST(nodep, Var)) { // If a var wants to be constified, it's really a param, and // we want the value to be constant. We aren't passed just the @@ -3285,7 +3296,7 @@ void V3Const::constifyAllLint(AstNetlist* nodep) { // Only call from Verilator.cpp, as it uses user#'s UINFO(2, __FUNCTION__ << ": " << endl); { - ConstVisitor visitor{ConstVisitor::PROC_V_WARN}; + ConstVisitor visitor{ConstVisitor::PROC_V_WARN, /* globalPass: */ true}; (void)visitor.mainAcceptEdit(nodep); } // Destruct before checking V3Global::dumpCheckGlobalTree("const", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); @@ -3294,14 +3305,14 @@ void V3Const::constifyAllLint(AstNetlist* nodep) { void V3Const::constifyCpp(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); { - ConstVisitor visitor{ConstVisitor::PROC_CPP}; + ConstVisitor visitor{ConstVisitor::PROC_CPP, /* globalPass: */ true}; (void)visitor.mainAcceptEdit(nodep); } // Destruct before checking V3Global::dumpCheckGlobalTree("const_cpp", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } AstNode* V3Const::constifyEdit(AstNode* nodep) { - ConstVisitor visitor{ConstVisitor::PROC_V_NOWARN}; + ConstVisitor visitor{ConstVisitor::PROC_V_NOWARN, /* globalPass: */ false}; nodep = visitor.mainAcceptEdit(nodep); return nodep; } @@ -3312,7 +3323,7 @@ void V3Const::constifyAllLive(AstNetlist* nodep) { // IE doesn't prune dead statements, as we need to do some usability checks after this UINFO(2, __FUNCTION__ << ": " << endl); { - ConstVisitor visitor{ConstVisitor::PROC_LIVE}; + ConstVisitor visitor{ConstVisitor::PROC_LIVE, /* globalPass: */ true}; (void)visitor.mainAcceptEdit(nodep); } // Destruct before checking V3Global::dumpCheckGlobalTree("const", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); @@ -3322,14 +3333,14 @@ void V3Const::constifyAll(AstNetlist* nodep) { // Only call from Verilator.cpp, as it uses user#'s UINFO(2, __FUNCTION__ << ": " << endl); { - ConstVisitor visitor{ConstVisitor::PROC_V_EXPENSIVE}; + ConstVisitor visitor{ConstVisitor::PROC_V_EXPENSIVE, /* globalPass: */ true}; (void)visitor.mainAcceptEdit(nodep); } // Destruct before checking V3Global::dumpCheckGlobalTree("const", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } AstNode* V3Const::constifyExpensiveEdit(AstNode* nodep) { - ConstVisitor visitor{ConstVisitor::PROC_V_EXPENSIVE}; + ConstVisitor visitor{ConstVisitor::PROC_V_EXPENSIVE, /* globalPass: */ false}; nodep = visitor.mainAcceptEdit(nodep); return nodep; } diff --git a/src/V3Depth.cpp b/src/V3Depth.cpp index 05110d509..72fc81d6d 100644 --- a/src/V3Depth.cpp +++ b/src/V3Depth.cpp @@ -29,6 +29,7 @@ #include "V3Global.h" #include "V3Depth.h" #include "V3Ast.h" +#include "V3UniqueNames.h" #include @@ -39,11 +40,11 @@ private: // NODE STATE // STATE - AstNodeModule* m_modp = nullptr; // Current module AstCFunc* m_cfuncp = nullptr; // Current block AstNode* m_stmtp = nullptr; // Current statement int m_depth = 0; // How deep in an expression int m_maxdepth = 0; // Maximum depth in an expression + V3UniqueNames m_tempNames; // For generating unique temporary variable names // METHODS VL_DEBUG_FUNC; // Declare debug() @@ -51,10 +52,8 @@ private: void createDeepTemp(AstNode* nodep) { UINFO(6, " Deep " << nodep << endl); // if (debug() >= 9) nodep->dumpTree(cout, "deep:"); - - const string newvarname = (string("__Vdeeptemp") + cvtToStr(m_modp->varNumGetInc())); - AstVar* const varp - = new AstVar{nodep->fileline(), AstVarType::STMTTEMP, newvarname, nodep->dtypep()}; + AstVar* const varp = new AstVar{nodep->fileline(), AstVarType::STMTTEMP, + m_tempNames.get(nodep), nodep->dtypep()}; UASSERT_OBJ(m_cfuncp, nodep, "Deep expression not under a function"); m_cfuncp->addInitsp(varp); // Replace node tree with reference to var @@ -70,21 +69,13 @@ private: } // VISITORS - virtual void visit(AstNodeModule* nodep) override { - UINFO(4, " MOD " << nodep << endl); - VL_RESTORER(m_modp); - { - m_modp = nodep; - m_cfuncp = nullptr; - iterateChildren(nodep); - } - } virtual void visit(AstCFunc* nodep) override { VL_RESTORER(m_cfuncp); { m_cfuncp = nodep; m_depth = 0; m_maxdepth = 0; + m_tempNames.reset(); iterateChildren(nodep); } } @@ -149,7 +140,10 @@ private: public: // CONSTRUCTORS - explicit DepthVisitor(AstNetlist* nodep) { iterate(nodep); } + explicit DepthVisitor(AstNetlist* nodep) + : m_tempNames{"__Vdeeptemp"} { + iterate(nodep); + } virtual ~DepthVisitor() override = default; }; diff --git a/src/V3Premit.cpp b/src/V3Premit.cpp index 948155f20..e665628b2 100644 --- a/src/V3Premit.cpp +++ b/src/V3Premit.cpp @@ -30,8 +30,8 @@ #include "V3Global.h" #include "V3Premit.h" #include "V3Ast.h" -#include "V3DupFinder.h" #include "V3Stats.h" +#include "V3UniqueNames.h" #include @@ -92,17 +92,17 @@ private: // AstNodeMath::user() -> bool. True if iterated already // AstShiftL::user2() -> bool. True if converted to conditional // AstShiftR::user2() -> bool. True if converted to conditional - // *::user4() -> See PremitAssignVisitor + // *::user3() -> See PremitAssignVisitor AstUser1InUse m_inuser1; AstUser2InUse m_inuser2; // STATE - AstNodeModule* m_modp = nullptr; // Current module AstCFunc* m_cfuncp = nullptr; // Current block AstNode* m_stmtp = nullptr; // Current statement AstWhile* m_inWhilep = nullptr; // Inside while loop, special statement additions AstTraceInc* m_inTracep = nullptr; // Inside while loop, special statement additions bool m_assignLhs = false; // Inside assignment lhs, don't breakup extracts + V3UniqueNames m_tempNames; // For generating unique temporary variable names VDouble0 m_extractedToConstPool; // Statistic tracking @@ -184,9 +184,8 @@ private: nodep->deleteTree(); ++m_extractedToConstPool; } else { - // Keep as local temporary - const string name = string("__Vtemp") + cvtToStr(m_modp->varNumGetInc()); - varp = new AstVar(fl, AstVarType::STMTTEMP, name, nodep->dtypep()); + // Keep as local temporary. Name based on hash of node for output stability. + varp = new AstVar(fl, AstVarType::STMTTEMP, m_tempNames.get(nodep), nodep->dtypep()); m_cfuncp->addInitsp(varp); // Put assignment before the referencing statement insertBeforeStmt(new AstAssign(fl, new AstVarRef(fl, varp, VAccess::WRITE), nodep)); @@ -202,16 +201,13 @@ private: // VISITORS virtual void visit(AstNodeModule* nodep) override { UINFO(4, " MOD " << nodep << endl); - UASSERT_OBJ(m_modp == nullptr, nodep, "Nested modules ?"); - m_modp = nodep; - m_cfuncp = nullptr; iterateChildren(nodep); - m_modp = nullptr; } virtual void visit(AstCFunc* nodep) override { VL_RESTORER(m_cfuncp); { m_cfuncp = nodep; + m_tempNames.reset(); iterateChildren(nodep); } } @@ -412,7 +408,10 @@ private: public: // CONSTRUCTORS - explicit PremitVisitor(AstNetlist* nodep) { iterate(nodep); } + explicit PremitVisitor(AstNetlist* nodep) + : m_tempNames{"__Vtemp"} { + iterate(nodep); + } virtual ~PremitVisitor() { V3Stats::addStat("Optimizations, Prelim extracted value to ConstPool", m_extractedToConstPool); diff --git a/src/V3SplitVar.cpp b/src/V3SplitVar.cpp index 2f96ecbdd..71818e975 100644 --- a/src/V3SplitVar.cpp +++ b/src/V3SplitVar.cpp @@ -117,6 +117,7 @@ #include "V3Global.h" #include "V3SplitVar.h" #include "V3Stats.h" +#include "V3UniqueNames.h" #include // sort #include @@ -124,6 +125,10 @@ #include struct SplitVarImpl { + // NODE STATE + // AstNodeModule::user1() -> Block number counter for generating unique names + AstUser1InUse m_user1InUse; // Only used in SplitUnpackedVarVisitor + static AstNodeAssign* newAssign(FileLine* fileline, AstNode* lhsp, AstNode* rhsp, const AstVar* varp) { if (varp->isFuncLocal() || varp->isFuncReturn()) { @@ -184,27 +189,27 @@ struct SplitVarImpl { static const char* cannotSplitPackedVarReason(const AstVar* varp); template - static void insertBeginCore(T_ALWAYSLIKE* ap, AstNodeStmt* stmtp, AstNodeModule* modp) { + void insertBeginCore(T_ALWAYSLIKE* ap, AstNodeStmt* stmtp, AstNodeModule* modp) { if (ap->isJustOneBodyStmt() && ap->bodysp() == stmtp) { stmtp->unlinkFrBack(); // Insert begin-end because temp value may be inserted to this block later. - const std::string name = "__VsplitVarBlk" + cvtToStr(modp->varNumGetInc()); + const std::string name = "__VsplitVarBlk" + cvtToStr(modp->user1Inc(1)); ap->addStmtp(new AstBegin{ap->fileline(), name, stmtp}); } } - static void insertBeginCore(AstInitial* initp, AstNodeStmt* stmtp, AstNodeModule* modp) { + void insertBeginCore(AstInitial* initp, AstNodeStmt* stmtp, AstNodeModule* modp) { if (initp->isJustOneBodyStmt() && initp->bodysp() == stmtp) { stmtp->unlinkFrBack(); // Insert begin-end because temp value may be inserted to this block later. FileLine* const fl = initp->fileline(); - const std::string name = "__VsplitVarBlk" + cvtToStr(modp->varNumGetInc()); + const std::string name = "__VsplitVarBlk" + cvtToStr(modp->user1Inc(1)); initp->replaceWith(new AstInitial{fl, new AstBegin{fl, name, stmtp}}); VL_DO_DANGLING(initp->deleteTree(), initp); } } - static void insertBeginIfNecessary(AstNodeStmt* stmtp, AstNodeModule* modp) { + void insertBeginIfNecessary(AstNodeStmt* stmtp, AstNodeModule* modp) { AstNode* const backp = stmtp->backp(); if (AstAlways* const ap = VN_CAST(backp, Always)) { insertBeginCore(ap, stmtp, modp); @@ -401,6 +406,7 @@ class SplitUnpackedVarVisitor final : public AstNVisitor, public SplitVarImpl { size_t m_numSplit = 0; // List for SplitPackedVarVisitor SplitVarRefsMap m_refsForPackedSplit; + V3UniqueNames m_tempNames; // For generating unique temporary variable names static AstVarRef* isTargetVref(AstNode* nodep) { if (AstVarRef* const refp = VN_CAST(nodep, VarRef)) { @@ -457,6 +463,7 @@ class SplitUnpackedVarVisitor final : public AstNVisitor, public SplitVarImpl { UASSERT_OBJ(!m_modp, m_modp, "Nested module declaration"); UASSERT_OBJ(m_refs.empty(), nodep, "The last module didn't finish split()"); m_modp = nodep; + m_tempNames.reset(); iterateChildren(nodep); split(); m_modp = nullptr; @@ -593,7 +600,7 @@ class SplitUnpackedVarVisitor final : public AstNVisitor, public SplitVarImpl { iterateChildren(nodep); } } - static AstNode* toInsertPoint(AstNode* insertp) { + AstNode* toInsertPoint(AstNode* insertp) { if (AstNodeStmt* const stmtp = VN_CAST(insertp, NodeStmt)) { if (!stmtp->isStatement()) insertp = stmtp->backp(); } @@ -603,8 +610,7 @@ class SplitUnpackedVarVisitor final : public AstNVisitor, public SplitVarImpl { const std::string& name_prefix, std::vector& vars, int start_idx, bool lvalue, bool ftask) { FileLine* const fl = nodep->fileline(); - const std::string name - = "__VsplitVar" + cvtToStr(m_modp->varNumGetInc()) + "__" + name_prefix; + const std::string name = m_tempNames.get(nodep) + "__" + name_prefix; AstNodeAssign* const assignp = VN_CAST(context, NodeAssign); if (assignp) { // "always_comb a = b;" to "always_comb begin a = b; end" so that local @@ -765,7 +771,8 @@ class SplitUnpackedVarVisitor final : public AstNVisitor, public SplitVarImpl { public: explicit SplitUnpackedVarVisitor(AstNetlist* nodep) - : m_refs{} { + : m_refs{} + , m_tempNames{"__VsplitVar"} { iterate(nodep); } ~SplitUnpackedVarVisitor() override { diff --git a/src/V3UniqueNames.h b/src/V3UniqueNames.h index 2d3f94560..f33c86a12 100644 --- a/src/V3UniqueNames.h +++ b/src/V3UniqueNames.h @@ -18,22 +18,47 @@ #ifndef VERILATOR_V3UNIQUENAMES_H_ #define VERILATOR_V3UNIQUENAMES_H_ + #include "config_build.h" #include "verilatedos.h" +#include "V3Hasher.h" + #include #include class V3UniqueNames final { + const std::string m_prefix; // Prefix to attach to all names + std::unordered_map m_multiplicity; // Suffix number for given key public: - // Return argument, appended with a unique suffix each time we are called with the same - // argument. + V3UniqueNames() + : m_prefix{""} {} + explicit V3UniqueNames(const std::string& prefix) + : m_prefix{prefix} {} + + // Return argument, prepended with the prefix if any, then appended with a unique suffix each + // time we are called with the same argument. std::string get(const std::string& name) { const unsigned num = m_multiplicity.emplace(name, 0).first->second++; - return name + "__" + cvtToStr(num); + std::string result; + if (!m_prefix.empty()) { + result += m_prefix; + result += "_"; + } + result += name; + result += "__"; + result += cvtToStr(num); + return result; } + + // Return hash of node as string, prepended with the prefix if any, appended with a unique + // suffix each time we are called with a node that hashes to the same value. + std::string get(const AstNode* nodep) { return get(V3Hasher::uncachedHash(nodep).toString()); } + + // Reset to initial state (as if just constructed) + void reset() { m_multiplicity.clear(); } }; #endif // Guard diff --git a/src/V3Unknown.cpp b/src/V3Unknown.cpp index 5c86e25ed..5ac9b5763 100644 --- a/src/V3Unknown.cpp +++ b/src/V3Unknown.cpp @@ -36,6 +36,7 @@ #include "V3Ast.h" #include "V3Const.h" #include "V3Stats.h" +#include "V3UniqueNames.h" #include @@ -57,6 +58,8 @@ private: AstAssignDly* m_assigndlyp = nullptr; // Current assignment bool m_constXCvt = false; // Convert X's VDouble0 m_statUnkVars; // Statistic tracking + V3UniqueNames m_lvboundNames; // For generating unique temporary variable names + V3UniqueNames m_xrandNames; // For generating unique temporary variable names // METHODS VL_DEBUG_FUNC; // Declare debug() @@ -113,11 +116,10 @@ private: UINFO(4, "Edit BOUNDLVALUE " << newp << endl); replaceHandle.relink(newp); } else { - const string name = (string("__Vlvbound") + cvtToStr(m_modp->varNumGetInc())); - AstVar* varp = new AstVar(fl, AstVarType::MODULETEMP, name, prep->dtypep()); + AstVar* const varp + = new AstVar(fl, AstVarType::MODULETEMP, m_lvboundNames.get(prep), prep->dtypep()); m_modp->addStmtp(varp); - - AstNode* abovep = prep->backp(); // Grab above point before lose it w/ next replace + AstNode* const abovep = prep->backp(); // Grab above point before we replace 'prep' prep->replaceWith(new AstVarRef(fl, varp, VAccess::WRITE)); AstIf* newp = new AstIf( fl, condp, @@ -142,6 +144,8 @@ private: { m_modp = nodep; m_constXCvt = true; + m_lvboundNames.reset(); + m_xrandNames.reset(); iterateChildren(nodep); } } @@ -322,15 +326,16 @@ private: // Make a Vxrand variable // We use the special XTEMP type so it doesn't break pure functions UASSERT_OBJ(m_modp, nodep, "X number not under module"); - const string newvarname = (string("__Vxrand") + cvtToStr(m_modp->varNumGetInc())); - AstVar* newvarp = new AstVar(nodep->fileline(), AstVarType::XTEMP, newvarname, - VFlagLogicPacked(), nodep->width()); + AstVar* const newvarp + = new AstVar(nodep->fileline(), AstVarType::XTEMP, m_xrandNames.get(nodep), + VFlagLogicPacked(), nodep->width()); ++m_statUnkVars; AstNRelinker replaceHandle; nodep->unlinkFrBack(&replaceHandle); - AstNodeVarRef* newref1p = new AstVarRef(nodep->fileline(), newvarp, VAccess::READ); + AstNodeVarRef* const newref1p + = new AstVarRef(nodep->fileline(), newvarp, VAccess::READ); replaceHandle.relink(newref1p); // Replace const with varref - AstInitial* newinitp = new AstInitial( + AstInitial* const newinitp = new AstInitial( nodep->fileline(), new AstAssign( nodep->fileline(), @@ -342,7 +347,7 @@ private: nodep->dtypep(), true))))); // Add inits in front of other statement. // In the future, we should stuff the initp into the module's constructor. - AstNode* afterp = m_modp->stmtsp()->unlinkFrBackWithNext(); + AstNode* const afterp = m_modp->stmtsp()->unlinkFrBackWithNext(); m_modp->addStmtp(newvarp); m_modp->addStmtp(newinitp); m_modp->addStmtp(afterp); @@ -476,7 +481,11 @@ private: public: // CONSTRUCTORS - explicit UnknownVisitor(AstNetlist* nodep) { iterate(nodep); } + explicit UnknownVisitor(AstNetlist* nodep) + : m_lvboundNames{"__Vlvbound"} + , m_xrandNames{"__Vxrand"} { + iterate(nodep); + } virtual ~UnknownVisitor() override { // V3Stats::addStat("Unknowns, variables created", m_statUnkVars); } diff --git a/test_regress/t/t_xml_debugcheck.out b/test_regress/t/t_xml_debugcheck.out index 585eb915a..3b62af357 100644 --- a/test_regress/t/t_xml_debugcheck.out +++ b/test_regress/t/t_xml_debugcheck.out @@ -31,9 +31,9 @@ - - - + + + @@ -93,11 +93,11 @@ - + - + @@ -343,11 +343,11 @@ - + - + @@ -593,11 +593,11 @@ - + - + @@ -845,7 +845,7 @@ - + @@ -1298,11 +1298,11 @@ - + - + From d1836b7b6fb4f7997c1cc0061219d1104a96ac1b Mon Sep 17 00:00:00 2001 From: Pieter Kapsenberg Date: Thu, 12 Aug 2021 10:40:44 -0700 Subject: [PATCH 67/84] Traces show array instances using brackets instead of parens (#3092) (#3095) --- include/verilated_fst_c.cpp | 2 +- include/verilated_vcd_c.cpp | 6 +- src/V3TraceDecl.cpp | 4 +- test_regress/t/t_split_var_2_trace.out | 1588 ++++++++--------- test_regress/t/t_trace_complex.out | 35 +- test_regress/t/t_trace_complex.pl | 8 +- test_regress/t/t_trace_complex_fst.out | 35 +- test_regress/t/t_trace_complex_fst_sc.out | 35 +- test_regress/t/t_trace_complex_old_api.pl | 8 +- test_regress/t/t_trace_complex_params.out | 35 +- test_regress/t/t_trace_complex_params_fst.out | 35 +- .../t/t_trace_complex_params_fst_sc.out | 35 +- test_regress/t/t_trace_complex_portable.pl | 8 +- test_regress/t/t_trace_complex_structs.out | 57 +- test_regress/t/t_trace_complex_structs.pl | 8 +- .../t/t_trace_complex_structs_fst.out | 44 +- .../t/t_trace_complex_structs_fst_sc.out | 44 +- test_regress/t/t_trace_complex_threads_1.pl | 8 +- test_regress/t/t_trace_fst.out | 428 ++--- test_regress/t/t_trace_fst_cmake.out | 428 ++--- test_regress/t/t_trace_fst_sc.out | 8 +- test_regress/t/t_trace_fst_sc_cmake.out | 8 +- test_regress/t/t_trace_packed_struct_fst.out | 20 +- .../t/t_trace_packed_struct_fst_sc.out | 8 +- test_regress/t/t_trace_public.out | 111 +- 25 files changed, 1501 insertions(+), 1505 deletions(-) diff --git a/include/verilated_fst_c.cpp b/include/verilated_fst_c.cpp index 933f8d88e..73275f9c7 100644 --- a/include/verilated_fst_c.cpp +++ b/include/verilated_fst_c.cpp @@ -204,7 +204,7 @@ void VerilatedFst::declare(vluint32_t code, const char* name, int dtypenum, fstV std::stringstream name_ss; name_ss << symbol_name; - if (array) name_ss << "(" << arraynum << ")"; + if (array) name_ss << "[" << arraynum << "]"; std::string name_str = name_ss.str(); if (dtypenum > 0) { diff --git a/include/verilated_vcd_c.cpp b/include/verilated_vcd_c.cpp index 2630967f3..1a21bdbf2 100644 --- a/include/verilated_vcd_c.cpp +++ b/include/verilated_vcd_c.cpp @@ -431,9 +431,9 @@ void VerilatedVcd::dumpHeader() { for (; *np && *np != ' ' && *np != '\t'; np++) { if (*np == '[') { - printStr("("); + printStr("["); } else if (*np == ']') { - printStr(")"); + printStr("]"); } else if (!(*np & '\x80')) { *m_writep++ = *np; } @@ -532,7 +532,7 @@ void VerilatedVcd::declare(vluint32_t code, const char* name, const char* wirep, decl += " "; decl += basename; if (array) { - VL_SNPRINTF(buf, bufsize, "(%d)", arraynum); + VL_SNPRINTF(buf, bufsize, "[%d]", arraynum); decl += buf; hiername += buf; } diff --git a/src/V3TraceDecl.cpp b/src/V3TraceDecl.cpp index 3bbe20f31..066aa51ce 100644 --- a/src/V3TraceDecl.cpp +++ b/src/V3TraceDecl.cpp @@ -253,7 +253,7 @@ private: VL_RESTORER(m_traShowname); VL_RESTORER(m_traValuep); { - m_traShowname += string("(") + cvtToStr(i) + string(")"); + m_traShowname += string("[") + cvtToStr(i) + string("]"); m_traValuep = new AstArraySel( nodep->fileline(), m_traValuep->cloneTree(true), i - nodep->lo()); @@ -278,7 +278,7 @@ private: VL_RESTORER(m_traShowname); VL_RESTORER(m_traValuep); { - m_traShowname += string("(") + cvtToStr(i) + string(")"); + m_traShowname += string("[") + cvtToStr(i) + string("]"); m_traValuep = new AstSel(nodep->fileline(), m_traValuep->cloneTree(true), (i - nodep->lo()) * subtypep->width(), subtypep->width()); diff --git a/test_regress/t/t_split_var_2_trace.out b/test_regress/t/t_split_var_2_trace.out index 7bbee9244..f03a9303f 100644 --- a/test_regress/t/t_split_var_2_trace.out +++ b/test_regress/t/t_split_var_2_trace.out @@ -1,28 +1,27 @@ $version Generated by VerilatedVcd $end -$date Mon Aug 24 21:54:18 2020 - $end -$timescale 1ps $end +$date Wed Aug 11 12:35:42 2021 $end +$timescale 1ps $end $scope module top $end - $var wire 1 U" clk $end + $var wire 1 T" clk $end $scope module t $end - $var wire 32 V" DEPTH [31:0] $end - $var wire 32 X" NUMSUB [31:0] $end - $var wire 32 W" WIDTH [31:0] $end - $var wire 1 U" clk $end - $var wire 64 Z" expc [63:0] $end - $var wire 8 Y" in [7:0] $end - $var wire 8 x! out(0) [7:0] $end - $var wire 8 y! out(1) [7:0] $end - $var wire 8 z! out(2) [7:0] $end - $var wire 8 {! out(3) [7:0] $end - $var wire 8 |! out(4) [7:0] $end - $var wire 8 }! out(5) [7:0] $end - $var wire 8 ~! out(6) [7:0] $end - $var wire 8 !" out(7) [7:0] $end - $var wire 8 "" out(8) [7:0] $end + $var wire 32 U" DEPTH [31:0] $end + $var wire 32 W" NUMSUB [31:0] $end + $var wire 32 V" WIDTH [31:0] $end + $var wire 1 T" clk $end + $var wire 64 Y" expc [63:0] $end + $var wire 8 X" in [7:0] $end + $var wire 8 w! out[0] [7:0] $end + $var wire 8 x! out[1] [7:0] $end + $var wire 8 y! out[2] [7:0] $end + $var wire 8 z! out[3] [7:0] $end + $var wire 8 {! out[4] [7:0] $end + $var wire 8 |! out[5] [7:0] $end + $var wire 8 }! out[6] [7:0] $end + $var wire 8 ~! out[7] [7:0] $end + $var wire 8 !" out[8] [7:0] $end $var wire 3 - shift [2:0] $end - $var wire 8 $" through_tmp [7:0] $end + $var wire 8 #" through_tmp [7:0] $end $scope module always_block $end $var wire 1 . failed $end $scope module unnamedblk1 $end @@ -30,43 +29,43 @@ $timescale 1ps $end $upscope $end $upscope $end $scope module delay0 $end - $var wire 32 u! c [31:0] $end - $var wire 1 U" clk $end - $var wire 1 z" unpack_sig0(10) $end - $var wire 1 {" unpack_sig0(11) $end - $var wire 1 |" unpack_sig0(12) $end - $var wire 1 e! unpack_sig0(13) $end - $var wire 1 f! unpack_sig0(14) $end - $var wire 1 g! unpack_sig0(15) $end - $var wire 1 h! unpack_sig0(16) $end - $var wire 1 i! unpack_sig1(13) $end - $var wire 1 j! unpack_sig1(14) $end - $var wire 1 k! unpack_sig1(15) $end - $var wire 1 l! unpack_sig1(16) $end - $var wire 1 }" unpack_sig2(10) $end - $var wire 1 ~" unpack_sig2(11) $end - $var wire 1 !# unpack_sig2(12) $end - $var wire 1 m! unpack_sig2(13) $end - $var wire 1 n! unpack_sig2(14) $end - $var wire 1 o! unpack_sig2(15) $end - $var wire 1 p! unpack_sig2(16) $end - $var wire 1 q! unpack_sig3(13) $end - $var wire 1 r! unpack_sig3(14) $end - $var wire 1 s! unpack_sig3(15) $end - $var wire 1 t! unpack_sig3(16) $end + $var wire 32 t! c [31:0] $end + $var wire 1 T" clk $end + $var wire 1 y" unpack_sig0(10) $end + $var wire 1 z" unpack_sig0(11) $end + $var wire 1 {" unpack_sig0(12) $end + $var wire 1 d! unpack_sig0(13) $end + $var wire 1 e! unpack_sig0(14) $end + $var wire 1 f! unpack_sig0(15) $end + $var wire 1 g! unpack_sig0(16) $end + $var wire 1 h! unpack_sig1(13) $end + $var wire 1 i! unpack_sig1(14) $end + $var wire 1 j! unpack_sig1(15) $end + $var wire 1 k! unpack_sig1(16) $end + $var wire 1 |" unpack_sig2(10) $end + $var wire 1 }" unpack_sig2(11) $end + $var wire 1 ~" unpack_sig2(12) $end + $var wire 1 l! unpack_sig2(13) $end + $var wire 1 m! unpack_sig2(14) $end + $var wire 1 n! unpack_sig2(15) $end + $var wire 1 o! unpack_sig2(16) $end + $var wire 1 p! unpack_sig3(13) $end + $var wire 1 q! unpack_sig3(14) $end + $var wire 1 r! unpack_sig3(15) $end + $var wire 1 s! unpack_sig3(16) $end $upscope $end $scope module i_t_array_rev $end $var wire 1 ' arrd(0) $end $var wire 1 ( arrd(1) $end - $var wire 1 U" clk $end - $var wire 32 "# cyc [31:0] $end - $var wire 1 v! localbkw(0) $end - $var wire 1 w! localbkw(1) $end + $var wire 1 T" clk $end + $var wire 32 !# cyc [31:0] $end + $var wire 1 u! localbkw(0) $end + $var wire 1 v! localbkw(1) $end $var wire 1 ) y0 $end $var wire 1 * y1 $end $scope module arr_rev_u $end - $var wire 1 + arrbkw(0) $end - $var wire 1 , arrbkw(1) $end + $var wire 1 + arrbkw[0] $end + $var wire 1 , arrbkw[1] $end $var wire 1 ) y0 $end $var wire 1 * y1 $end $upscope $end @@ -78,312 +77,312 @@ $timescale 1ps $end $var wire 32 & var3 [30:-1] $end $upscope $end $scope module shifter0 $end - $var wire 32 V" DEPTH [31:0] $end - $var wire 32 \" OFFSET [31:0] $end - $var wire 32 W" WIDTH [31:0] $end - $var wire 8 Y" in [7:0] $end + $var wire 32 U" DEPTH [31:0] $end + $var wire 32 [" OFFSET [31:0] $end + $var wire 32 V" WIDTH [31:0] $end + $var wire 8 X" in [7:0] $end $var wire 8 0 out [7:0] $end $var wire 3 - shift [2:0] $end $var wire 8 2 tmp(-1) [7:0] $end $var wire 8 1 tmp(-2) [7:0] $end - $var wire 8 Y" tmp(-3) [7:0] $end - $var wire 8 3 tmp(0) [7:0] $end + $var wire 8 X" tmp(-3) [7:0] $end + $var wire 8 0 tmp(0) [7:0] $end $upscope $end $scope module shifter1 $end - $var wire 32 V" DEPTH [31:0] $end - $var wire 32 \" OFFSET [31:0] $end - $var wire 32 W" WIDTH [31:0] $end - $var wire 8 Y" in [7:0] $end - $var wire 8 4 out [7:0] $end + $var wire 32 U" DEPTH [31:0] $end + $var wire 32 [" OFFSET [31:0] $end + $var wire 32 V" WIDTH [31:0] $end + $var wire 8 X" in [7:0] $end + $var wire 8 3 out [7:0] $end $var wire 3 - shift [2:0] $end - $var wire 8 5 tmp(-1) [7:0] $end + $var wire 8 4 tmp(-1) [7:0] $end $var wire 8 1 tmp(-2) [7:0] $end - $var wire 8 Y" tmp(-3) [7:0] $end - $var wire 8 4 tmp(0) [7:0] $end + $var wire 8 X" tmp(-3) [7:0] $end + $var wire 8 3 tmp(0) [7:0] $end $upscope $end $scope module shifter2 $end - $var wire 32 V" DEPTH [31:0] $end - $var wire 32 ]" OFFSET [31:0] $end - $var wire 32 W" WIDTH [31:0] $end - $var wire 8 Y" in [7:0] $end - $var wire 8 6 out [7:0] $end + $var wire 32 U" DEPTH [31:0] $end + $var wire 32 \" OFFSET [31:0] $end + $var wire 32 V" WIDTH [31:0] $end + $var wire 8 X" in [7:0] $end + $var wire 8 5 out [7:0] $end $var wire 3 - shift [2:0] $end - $var wire 8 Y" tmp(1) [7:0] $end - $var wire 8 7 tmp(2) [7:0] $end - $var wire 8 8 tmp(3) [7:0] $end - $var wire 8 6 tmp(4) [7:0] $end + $var wire 8 X" tmp(1) [7:0] $end + $var wire 8 6 tmp(2) [7:0] $end + $var wire 8 7 tmp(3) [7:0] $end + $var wire 8 5 tmp(4) [7:0] $end $upscope $end $scope module shifter3 $end - $var wire 32 V" DEPTH [31:0] $end - $var wire 32 V" N [31:0] $end - $var wire 32 ]" OFFSET [31:0] $end - $var wire 32 W" WIDTH [31:0] $end - $var wire 8 Y" in [7:0] $end - $var wire 8 %" out [7:0] $end + $var wire 32 U" DEPTH [31:0] $end + $var wire 32 U" N [31:0] $end + $var wire 32 \" OFFSET [31:0] $end + $var wire 32 V" WIDTH [31:0] $end + $var wire 8 X" in [7:0] $end + $var wire 8 $" out [7:0] $end $var wire 3 - shift [2:0] $end - $var wire 8 Y" tmp0(1)(1) [7:0] $end - $var wire 8 Y" tmp0(1)(2) [7:0] $end - $var wire 8 Y" tmp0(1)(3) [7:0] $end + $var wire 8 X" tmp0(1)(1) [7:0] $end + $var wire 8 X" tmp0(1)(2) [7:0] $end + $var wire 8 X" tmp0(1)(3) [7:0] $end $var wire 8 1 tmp0(2)(1) [7:0] $end $var wire 8 1 tmp0(2)(2) [7:0] $end $var wire 8 1 tmp0(2)(3) [7:0] $end - $var wire 8 9 tmp0(3)(1) [7:0] $end - $var wire 8 : tmp0(3)(2) [7:0] $end - $var wire 8 ; tmp0(3)(3) [7:0] $end - $var wire 8 < tmp0(4)(1) [7:0] $end - $var wire 8 = tmp0(4)(2) [7:0] $end - $var wire 8 > tmp0(4)(3) [7:0] $end - $var wire 8 ? tmp1(1)(1) [7:0] $end - $var wire 8 @ tmp1(1)(2) [7:0] $end - $var wire 8 A tmp1(1)(3) [7:0] $end - $var wire 8 B tmp1(2)(1) [7:0] $end - $var wire 8 C tmp1(2)(2) [7:0] $end - $var wire 8 D tmp1(2)(3) [7:0] $end - $var wire 8 E tmp1(3)(1) [7:0] $end - $var wire 8 F tmp1(3)(2) [7:0] $end - $var wire 8 G tmp1(3)(3) [7:0] $end - $var wire 8 H tmp1(4)(1) [7:0] $end - $var wire 8 I tmp1(4)(2) [7:0] $end - $var wire 8 J tmp1(4)(3) [7:0] $end - $var wire 8 M! tmp10(1)(1) [7:0] $end - $var wire 8 N! tmp10(1)(2) [7:0] $end - $var wire 8 O! tmp10(1)(3) [7:0] $end - $var wire 8 P! tmp10(2)(1) [7:0] $end - $var wire 8 Q! tmp10(2)(2) [7:0] $end - $var wire 8 R! tmp10(2)(3) [7:0] $end - $var wire 8 S! tmp10(3)(1) [7:0] $end - $var wire 8 T! tmp10(3)(2) [7:0] $end - $var wire 8 U! tmp10(3)(3) [7:0] $end - $var wire 8 V! tmp10(4)(1) [7:0] $end - $var wire 8 W! tmp10(4)(2) [7:0] $end - $var wire 8 X! tmp10(4)(3) [7:0] $end - $var wire 8 &" tmp12(-1)(1)(1) [7:0] $end - $var wire 8 '" tmp12(-1)(1)(2) [7:0] $end - $var wire 8 (" tmp12(-1)(1)(3) [7:0] $end - $var wire 8 )" tmp12(-1)(2)(1) [7:0] $end - $var wire 8 *" tmp12(-1)(2)(2) [7:0] $end - $var wire 8 +" tmp12(-1)(2)(3) [7:0] $end - $var wire 8 ," tmp12(-1)(3)(1) [7:0] $end - $var wire 8 -" tmp12(-1)(3)(2) [7:0] $end - $var wire 8 ." tmp12(-1)(3)(3) [7:0] $end - $var wire 8 %" tmp12(-1)(4)(1) [7:0] $end - $var wire 8 /" tmp12(-1)(4)(2) [7:0] $end - $var wire 8 0" tmp12(-1)(4)(3) [7:0] $end - $var wire 8 1" tmp12(0)(1)(1) [7:0] $end - $var wire 8 2" tmp12(0)(1)(2) [7:0] $end - $var wire 8 3" tmp12(0)(1)(3) [7:0] $end - $var wire 8 4" tmp12(0)(2)(1) [7:0] $end - $var wire 8 5" tmp12(0)(2)(2) [7:0] $end - $var wire 8 6" tmp12(0)(2)(3) [7:0] $end - $var wire 8 7" tmp12(0)(3)(1) [7:0] $end - $var wire 8 8" tmp12(0)(3)(2) [7:0] $end - $var wire 8 9" tmp12(0)(3)(3) [7:0] $end - $var wire 8 :" tmp12(0)(4)(1) [7:0] $end - $var wire 8 ;" tmp12(0)(4)(2) [7:0] $end - $var wire 8 <" tmp12(0)(4)(3) [7:0] $end - $var wire 8 j" tmp13(1)(1) [7:0] $end - $var wire 8 k" tmp13(1)(2) [7:0] $end - $var wire 8 l" tmp13(1)(3) [7:0] $end - $var wire 8 m" tmp13(2)(1) [7:0] $end - $var wire 8 n" tmp13(2)(2) [7:0] $end - $var wire 8 o" tmp13(2)(3) [7:0] $end - $var wire 8 p" tmp13(3)(1) [7:0] $end - $var wire 8 q" tmp13(3)(2) [7:0] $end - $var wire 8 r" tmp13(3)(3) [7:0] $end - $var wire 8 s" tmp13(4)(1) [7:0] $end - $var wire 8 t" tmp13(4)(2) [7:0] $end - $var wire 8 u" tmp13(4)(3) [7:0] $end - $var wire 8 K tmp2(1)(1) [7:0] $end - $var wire 8 L tmp2(1)(2) [7:0] $end - $var wire 8 M tmp2(1)(3) [7:0] $end - $var wire 8 N tmp2(2)(1) [7:0] $end - $var wire 8 O tmp2(2)(2) [7:0] $end - $var wire 8 P tmp2(2)(3) [7:0] $end - $var wire 8 Q tmp2(3)(1) [7:0] $end - $var wire 8 R tmp2(3)(2) [7:0] $end - $var wire 8 S tmp2(3)(3) [7:0] $end - $var wire 8 T tmp2(4)(1) [7:0] $end - $var wire 8 U tmp2(4)(2) [7:0] $end - $var wire 8 V tmp2(4)(3) [7:0] $end - $var wire 8 W tmp3(1)(1) [7:0] $end - $var wire 8 X tmp3(1)(2) [7:0] $end - $var wire 8 Y tmp3(1)(3) [7:0] $end - $var wire 8 Z tmp3(2)(1) [7:0] $end - $var wire 8 [ tmp3(2)(2) [7:0] $end - $var wire 8 \ tmp3(2)(3) [7:0] $end - $var wire 8 ] tmp3(3)(1) [7:0] $end - $var wire 8 ^ tmp3(3)(2) [7:0] $end - $var wire 8 _ tmp3(3)(3) [7:0] $end - $var wire 8 ` tmp3(4)(1) [7:0] $end - $var wire 8 a tmp3(4)(2) [7:0] $end - $var wire 8 b tmp3(4)(3) [7:0] $end - $var wire 8 c tmp4(1)(1) [7:0] $end - $var wire 8 d tmp4(1)(2) [7:0] $end - $var wire 8 e tmp4(1)(3) [7:0] $end - $var wire 8 f tmp4(2)(1) [7:0] $end - $var wire 8 g tmp4(2)(2) [7:0] $end - $var wire 8 h tmp4(2)(3) [7:0] $end - $var wire 8 i tmp4(3)(1) [7:0] $end - $var wire 8 j tmp4(3)(2) [7:0] $end - $var wire 8 k tmp4(3)(3) [7:0] $end - $var wire 8 l tmp4(4)(1) [7:0] $end - $var wire 8 m tmp4(4)(2) [7:0] $end - $var wire 8 n tmp4(4)(3) [7:0] $end - $var wire 8 o tmp5(1)(1) [7:0] $end - $var wire 8 p tmp5(1)(2) [7:0] $end - $var wire 8 q tmp5(1)(3) [7:0] $end - $var wire 8 r tmp5(2)(1) [7:0] $end - $var wire 8 s tmp5(2)(2) [7:0] $end - $var wire 8 t tmp5(2)(3) [7:0] $end - $var wire 8 u tmp5(3)(1) [7:0] $end - $var wire 8 v tmp5(3)(2) [7:0] $end - $var wire 8 w tmp5(3)(3) [7:0] $end - $var wire 8 x tmp5(4)(1) [7:0] $end - $var wire 8 y tmp5(4)(2) [7:0] $end - $var wire 8 z tmp5(4)(3) [7:0] $end - $var wire 8 { tmp6(1)(1) [7:0] $end - $var wire 8 | tmp6(1)(2) [7:0] $end - $var wire 8 } tmp6(1)(3) [7:0] $end - $var wire 8 ~ tmp6(2)(1) [7:0] $end - $var wire 8 !! tmp6(2)(2) [7:0] $end - $var wire 8 "! tmp6(2)(3) [7:0] $end - $var wire 8 #! tmp6(3)(1) [7:0] $end - $var wire 8 $! tmp6(3)(2) [7:0] $end - $var wire 8 %! tmp6(3)(3) [7:0] $end - $var wire 8 &! tmp6(4)(1) [7:0] $end - $var wire 8 '! tmp6(4)(2) [7:0] $end - $var wire 8 (! tmp6(4)(3) [7:0] $end - $var wire 8 )! tmp7(2)(1) [7:0] $end - $var wire 8 *! tmp7(2)(2) [7:0] $end - $var wire 8 +! tmp7(2)(3) [7:0] $end - $var wire 8 ,! tmp7(3)(1) [7:0] $end - $var wire 8 -! tmp7(3)(2) [7:0] $end - $var wire 8 .! tmp7(3)(3) [7:0] $end - $var wire 8 /! tmp7(4)(1) [7:0] $end - $var wire 8 0! tmp7(4)(2) [7:0] $end - $var wire 8 1! tmp7(4)(3) [7:0] $end - $var wire 8 2! tmp7(5)(1) [7:0] $end - $var wire 8 3! tmp7(5)(2) [7:0] $end - $var wire 8 4! tmp7(5)(3) [7:0] $end - $var wire 8 ^" tmp8(0)(1) [7:0] $end - $var wire 8 _" tmp8(0)(2) [7:0] $end - $var wire 8 `" tmp8(0)(3) [7:0] $end - $var wire 8 a" tmp8(1)(1) [7:0] $end - $var wire 8 b" tmp8(1)(2) [7:0] $end - $var wire 8 c" tmp8(1)(3) [7:0] $end - $var wire 8 5! tmp8(2)(1) [7:0] $end - $var wire 8 6! tmp8(2)(2) [7:0] $end - $var wire 8 7! tmp8(2)(3) [7:0] $end - $var wire 8 8! tmp8(3)(1) [7:0] $end - $var wire 8 9! tmp8(3)(2) [7:0] $end - $var wire 8 :! tmp8(3)(3) [7:0] $end - $var wire 8 ;! tmp8(4)(1) [7:0] $end - $var wire 8 ! tmp8(5)(1) [7:0] $end - $var wire 8 ?! tmp8(5)(2) [7:0] $end - $var wire 8 @! tmp8(5)(3) [7:0] $end - $var wire 8 d" tmp8(6)(1) [7:0] $end - $var wire 8 e" tmp8(6)(2) [7:0] $end - $var wire 8 f" tmp8(6)(3) [7:0] $end - $var wire 8 g" tmp8(7)(1) [7:0] $end - $var wire 8 h" tmp8(7)(2) [7:0] $end - $var wire 8 i" tmp8(7)(3) [7:0] $end - $var wire 8 A! tmp9(4)(1) [7:0] $end - $var wire 8 B! tmp9(4)(2) [7:0] $end - $var wire 8 C! tmp9(4)(3) [7:0] $end - $var wire 8 D! tmp9(5)(1) [7:0] $end - $var wire 8 E! tmp9(5)(2) [7:0] $end - $var wire 8 F! tmp9(5)(3) [7:0] $end - $var wire 8 G! tmp9(6)(1) [7:0] $end - $var wire 8 H! tmp9(6)(2) [7:0] $end - $var wire 8 I! tmp9(6)(3) [7:0] $end - $var wire 8 J! tmp9(7)(1) [7:0] $end - $var wire 8 K! tmp9(7)(2) [7:0] $end - $var wire 8 L! tmp9(7)(3) [7:0] $end + $var wire 8 8 tmp0(3)(1) [7:0] $end + $var wire 8 9 tmp0(3)(2) [7:0] $end + $var wire 8 : tmp0(3)(3) [7:0] $end + $var wire 8 ; tmp0(4)(1) [7:0] $end + $var wire 8 < tmp0(4)(2) [7:0] $end + $var wire 8 = tmp0(4)(3) [7:0] $end + $var wire 8 > tmp1(1)(1) [7:0] $end + $var wire 8 ? tmp1(1)(2) [7:0] $end + $var wire 8 @ tmp1(1)(3) [7:0] $end + $var wire 8 A tmp1(2)(1) [7:0] $end + $var wire 8 B tmp1(2)(2) [7:0] $end + $var wire 8 C tmp1(2)(3) [7:0] $end + $var wire 8 D tmp1(3)(1) [7:0] $end + $var wire 8 E tmp1(3)(2) [7:0] $end + $var wire 8 F tmp1(3)(3) [7:0] $end + $var wire 8 G tmp1(4)(1) [7:0] $end + $var wire 8 H tmp1(4)(2) [7:0] $end + $var wire 8 I tmp1(4)(3) [7:0] $end + $var wire 8 L! tmp10(1)(1) [7:0] $end + $var wire 8 M! tmp10(1)(2) [7:0] $end + $var wire 8 N! tmp10(1)(3) [7:0] $end + $var wire 8 O! tmp10(2)(1) [7:0] $end + $var wire 8 P! tmp10(2)(2) [7:0] $end + $var wire 8 Q! tmp10(2)(3) [7:0] $end + $var wire 8 R! tmp10(3)(1) [7:0] $end + $var wire 8 S! tmp10(3)(2) [7:0] $end + $var wire 8 T! tmp10(3)(3) [7:0] $end + $var wire 8 U! tmp10(4)(1) [7:0] $end + $var wire 8 V! tmp10(4)(2) [7:0] $end + $var wire 8 W! tmp10(4)(3) [7:0] $end + $var wire 8 %" tmp12(-1)(1)(1) [7:0] $end + $var wire 8 &" tmp12(-1)(1)(2) [7:0] $end + $var wire 8 '" tmp12(-1)(1)(3) [7:0] $end + $var wire 8 (" tmp12(-1)(2)(1) [7:0] $end + $var wire 8 )" tmp12(-1)(2)(2) [7:0] $end + $var wire 8 *" tmp12(-1)(2)(3) [7:0] $end + $var wire 8 +" tmp12(-1)(3)(1) [7:0] $end + $var wire 8 ," tmp12(-1)(3)(2) [7:0] $end + $var wire 8 -" tmp12(-1)(3)(3) [7:0] $end + $var wire 8 $" tmp12(-1)(4)(1) [7:0] $end + $var wire 8 ." tmp12(-1)(4)(2) [7:0] $end + $var wire 8 /" tmp12(-1)(4)(3) [7:0] $end + $var wire 8 0" tmp12(0)(1)(1) [7:0] $end + $var wire 8 1" tmp12(0)(1)(2) [7:0] $end + $var wire 8 2" tmp12(0)(1)(3) [7:0] $end + $var wire 8 3" tmp12(0)(2)(1) [7:0] $end + $var wire 8 4" tmp12(0)(2)(2) [7:0] $end + $var wire 8 5" tmp12(0)(2)(3) [7:0] $end + $var wire 8 6" tmp12(0)(3)(1) [7:0] $end + $var wire 8 7" tmp12(0)(3)(2) [7:0] $end + $var wire 8 8" tmp12(0)(3)(3) [7:0] $end + $var wire 8 9" tmp12(0)(4)(1) [7:0] $end + $var wire 8 :" tmp12(0)(4)(2) [7:0] $end + $var wire 8 ;" tmp12(0)(4)(3) [7:0] $end + $var wire 8 i" tmp13(1)(1) [7:0] $end + $var wire 8 j" tmp13(1)(2) [7:0] $end + $var wire 8 k" tmp13(1)(3) [7:0] $end + $var wire 8 l" tmp13(2)(1) [7:0] $end + $var wire 8 m" tmp13(2)(2) [7:0] $end + $var wire 8 n" tmp13(2)(3) [7:0] $end + $var wire 8 o" tmp13(3)(1) [7:0] $end + $var wire 8 p" tmp13(3)(2) [7:0] $end + $var wire 8 q" tmp13(3)(3) [7:0] $end + $var wire 8 r" tmp13(4)(1) [7:0] $end + $var wire 8 s" tmp13(4)(2) [7:0] $end + $var wire 8 t" tmp13(4)(3) [7:0] $end + $var wire 8 J tmp2[1][1] [7:0] $end + $var wire 8 K tmp2[1][2] [7:0] $end + $var wire 8 L tmp2[1][3] [7:0] $end + $var wire 8 M tmp2[2][1] [7:0] $end + $var wire 8 N tmp2[2][2] [7:0] $end + $var wire 8 O tmp2[2][3] [7:0] $end + $var wire 8 P tmp2[3][1] [7:0] $end + $var wire 8 Q tmp2[3][2] [7:0] $end + $var wire 8 R tmp2[3][3] [7:0] $end + $var wire 8 S tmp2[4][1] [7:0] $end + $var wire 8 T tmp2[4][2] [7:0] $end + $var wire 8 U tmp2[4][3] [7:0] $end + $var wire 8 V tmp3(1)(1) [7:0] $end + $var wire 8 W tmp3(1)(2) [7:0] $end + $var wire 8 X tmp3(1)(3) [7:0] $end + $var wire 8 Y tmp3(2)(1) [7:0] $end + $var wire 8 Z tmp3(2)(2) [7:0] $end + $var wire 8 [ tmp3(2)(3) [7:0] $end + $var wire 8 \ tmp3(3)(1) [7:0] $end + $var wire 8 ] tmp3(3)(2) [7:0] $end + $var wire 8 ^ tmp3(3)(3) [7:0] $end + $var wire 8 _ tmp3(4)(1) [7:0] $end + $var wire 8 ` tmp3(4)(2) [7:0] $end + $var wire 8 a tmp3(4)(3) [7:0] $end + $var wire 8 b tmp4(1)(1) [7:0] $end + $var wire 8 c tmp4(1)(2) [7:0] $end + $var wire 8 d tmp4(1)(3) [7:0] $end + $var wire 8 e tmp4(2)(1) [7:0] $end + $var wire 8 f tmp4(2)(2) [7:0] $end + $var wire 8 g tmp4(2)(3) [7:0] $end + $var wire 8 h tmp4(3)(1) [7:0] $end + $var wire 8 i tmp4(3)(2) [7:0] $end + $var wire 8 j tmp4(3)(3) [7:0] $end + $var wire 8 k tmp4(4)(1) [7:0] $end + $var wire 8 l tmp4(4)(2) [7:0] $end + $var wire 8 m tmp4(4)(3) [7:0] $end + $var wire 8 n tmp5[1][1] [7:0] $end + $var wire 8 o tmp5[1][2] [7:0] $end + $var wire 8 p tmp5[1][3] [7:0] $end + $var wire 8 q tmp5[2][1] [7:0] $end + $var wire 8 r tmp5[2][2] [7:0] $end + $var wire 8 s tmp5[2][3] [7:0] $end + $var wire 8 t tmp5[3][1] [7:0] $end + $var wire 8 u tmp5[3][2] [7:0] $end + $var wire 8 v tmp5[3][3] [7:0] $end + $var wire 8 w tmp5[4][1] [7:0] $end + $var wire 8 x tmp5[4][2] [7:0] $end + $var wire 8 y tmp5[4][3] [7:0] $end + $var wire 8 z tmp6(1)(1) [7:0] $end + $var wire 8 { tmp6(1)(2) [7:0] $end + $var wire 8 | tmp6(1)(3) [7:0] $end + $var wire 8 } tmp6(2)(1) [7:0] $end + $var wire 8 ~ tmp6(2)(2) [7:0] $end + $var wire 8 !! tmp6(2)(3) [7:0] $end + $var wire 8 "! tmp6(3)(1) [7:0] $end + $var wire 8 #! tmp6(3)(2) [7:0] $end + $var wire 8 $! tmp6(3)(3) [7:0] $end + $var wire 8 %! tmp6(4)(1) [7:0] $end + $var wire 8 &! tmp6(4)(2) [7:0] $end + $var wire 8 '! tmp6(4)(3) [7:0] $end + $var wire 8 (! tmp7(2)(1) [7:0] $end + $var wire 8 )! tmp7(2)(2) [7:0] $end + $var wire 8 *! tmp7(2)(3) [7:0] $end + $var wire 8 +! tmp7(3)(1) [7:0] $end + $var wire 8 ,! tmp7(3)(2) [7:0] $end + $var wire 8 -! tmp7(3)(3) [7:0] $end + $var wire 8 .! tmp7(4)(1) [7:0] $end + $var wire 8 /! tmp7(4)(2) [7:0] $end + $var wire 8 0! tmp7(4)(3) [7:0] $end + $var wire 8 1! tmp7(5)(1) [7:0] $end + $var wire 8 2! tmp7(5)(2) [7:0] $end + $var wire 8 3! tmp7(5)(3) [7:0] $end + $var wire 8 ]" tmp8(0)(1) [7:0] $end + $var wire 8 ^" tmp8(0)(2) [7:0] $end + $var wire 8 _" tmp8(0)(3) [7:0] $end + $var wire 8 `" tmp8(1)(1) [7:0] $end + $var wire 8 a" tmp8(1)(2) [7:0] $end + $var wire 8 b" tmp8(1)(3) [7:0] $end + $var wire 8 4! tmp8(2)(1) [7:0] $end + $var wire 8 5! tmp8(2)(2) [7:0] $end + $var wire 8 6! tmp8(2)(3) [7:0] $end + $var wire 8 7! tmp8(3)(1) [7:0] $end + $var wire 8 8! tmp8(3)(2) [7:0] $end + $var wire 8 9! tmp8(3)(3) [7:0] $end + $var wire 8 :! tmp8(4)(1) [7:0] $end + $var wire 8 ;! tmp8(4)(2) [7:0] $end + $var wire 8 ! tmp8(5)(2) [7:0] $end + $var wire 8 ?! tmp8(5)(3) [7:0] $end + $var wire 8 c" tmp8(6)(1) [7:0] $end + $var wire 8 d" tmp8(6)(2) [7:0] $end + $var wire 8 e" tmp8(6)(3) [7:0] $end + $var wire 8 f" tmp8(7)(1) [7:0] $end + $var wire 8 g" tmp8(7)(2) [7:0] $end + $var wire 8 h" tmp8(7)(3) [7:0] $end + $var wire 8 @! tmp9(4)(1) [7:0] $end + $var wire 8 A! tmp9(4)(2) [7:0] $end + $var wire 8 B! tmp9(4)(3) [7:0] $end + $var wire 8 C! tmp9(5)(1) [7:0] $end + $var wire 8 D! tmp9(5)(2) [7:0] $end + $var wire 8 E! tmp9(5)(3) [7:0] $end + $var wire 8 F! tmp9(6)(1) [7:0] $end + $var wire 8 G! tmp9(6)(2) [7:0] $end + $var wire 8 H! tmp9(6)(3) [7:0] $end + $var wire 8 I! tmp9(7)(1) [7:0] $end + $var wire 8 J! tmp9(7)(2) [7:0] $end + $var wire 8 K! tmp9(7)(3) [7:0] $end $upscope $end $scope module shifter4 $end - $var wire 32 V" DEPTH [31:0] $end - $var wire 32 v" OFFSET [31:0] $end - $var wire 32 W" WIDTH [31:0] $end - $var wire 8 Y" in [7:0] $end - $var wire 8 Y! out [7:0] $end - $var wire 24 x" pad [23:0] $end + $var wire 32 U" DEPTH [31:0] $end + $var wire 32 u" OFFSET [31:0] $end + $var wire 32 V" WIDTH [31:0] $end + $var wire 8 X" in [7:0] $end + $var wire 8 X! out [7:0] $end + $var wire 24 w" pad [23:0] $end $var wire 3 - shift [2:0] $end - $var wire 32 w" tmp(2) [31:0] $end - $var wire 32 Z! tmp(3) [31:0] $end - $var wire 32 [! tmp(4) [31:0] $end - $var wire 32 \! tmp(5) [31:0] $end + $var wire 32 v" tmp(2) [31:0] $end + $var wire 32 Y! tmp(3) [31:0] $end + $var wire 32 Z! tmp(4) [31:0] $end + $var wire 32 [! tmp(5) [31:0] $end $upscope $end $scope module shifter5 $end - $var wire 32 V" DEPTH [31:0] $end - $var wire 32 y" OFFSET [31:0] $end - $var wire 32 W" WIDTH [31:0] $end - $var wire 8 Y" in [7:0] $end - $var wire 8 ]! out [7:0] $end + $var wire 32 U" DEPTH [31:0] $end + $var wire 32 x" OFFSET [31:0] $end + $var wire 32 V" WIDTH [31:0] $end + $var wire 8 X" in [7:0] $end + $var wire 8 \! out [7:0] $end $var wire 3 - shift [2:0] $end - $var wire 32 ^! tmp [31:0] $end + $var wire 32 ]! tmp [31:0] $end $upscope $end $scope module shifter6 $end - $var wire 32 V" DEPTH [31:0] $end - $var wire 32 y" OFFSET [31:0] $end - $var wire 32 W" WIDTH [31:0] $end - $var wire 8 Y" in [7:0] $end - $var wire 8 _! out [7:0] $end + $var wire 32 U" DEPTH [31:0] $end + $var wire 32 x" OFFSET [31:0] $end + $var wire 32 V" WIDTH [31:0] $end + $var wire 8 X" in [7:0] $end + $var wire 8 ^! out [7:0] $end $var wire 3 - shift [2:0] $end - $var wire 32 `! tmp [31:0] $end + $var wire 32 _! tmp [31:0] $end $upscope $end $scope module shifter7 $end - $var wire 32 V" DEPTH [31:0] $end - $var wire 32 W" WIDTH [31:0] $end - $var wire 8 Y" in [7:0] $end - $var wire 8 a! out [7:0] $end + $var wire 32 U" DEPTH [31:0] $end + $var wire 32 V" WIDTH [31:0] $end + $var wire 8 X" in [7:0] $end + $var wire 8 `! out [7:0] $end $var wire 3 - shift [2:0] $end - $var wire 32 b! tmp [31:0] $end + $var wire 32 a! tmp [31:0] $end $upscope $end $scope module shifter8 $end - $var wire 32 V" DEPTH [31:0] $end - $var wire 32 W" WIDTH [31:0] $end - $var wire 8 Y" in [7:0] $end - $var wire 8 c! out [7:0] $end + $var wire 32 U" DEPTH [31:0] $end + $var wire 32 V" WIDTH [31:0] $end + $var wire 8 X" in [7:0] $end + $var wire 8 b! out [7:0] $end $var wire 3 - shift [2:0] $end - $var wire 32 d! tmp [0:31] $end + $var wire 32 c! tmp [0:31] $end $upscope $end $scope module though0 $end - $var wire 32 W" WIDTH [31:0] $end - $var wire 8 #" in [7:0] $end - $var wire 8 $" out [7:0] $end - $var wire 1 =" unpack_tmp(0) $end - $var wire 1 >" unpack_tmp(1) $end - $var wire 1 ?" unpack_tmp(2) $end - $var wire 1 @" unpack_tmp(3) $end - $var wire 1 A" unpack_tmp(4) $end - $var wire 1 B" unpack_tmp(5) $end - $var wire 1 C" unpack_tmp(6) $end - $var wire 1 D" unpack_tmp(7) $end + $var wire 32 V" WIDTH [31:0] $end + $var wire 8 "" in [7:0] $end + $var wire 8 #" out [7:0] $end + $var wire 1 <" unpack_tmp(0) $end + $var wire 1 =" unpack_tmp(1) $end + $var wire 1 >" unpack_tmp(2) $end + $var wire 1 ?" unpack_tmp(3) $end + $var wire 1 @" unpack_tmp(4) $end + $var wire 1 A" unpack_tmp(5) $end + $var wire 1 B" unpack_tmp(6) $end + $var wire 1 C" unpack_tmp(7) $end $scope module i_pack2unpack $end - $var wire 32 W" WIDTH [31:0] $end - $var wire 8 #" in [7:0] $end - $var wire 1 E" out(0) $end - $var wire 1 F" out(1) $end - $var wire 1 G" out(2) $end - $var wire 1 H" out(3) $end - $var wire 1 I" out(4) $end - $var wire 1 J" out(5) $end - $var wire 1 K" out(6) $end - $var wire 1 L" out(7) $end + $var wire 32 V" WIDTH [31:0] $end + $var wire 8 "" in [7:0] $end + $var wire 1 D" out[0] $end + $var wire 1 E" out[1] $end + $var wire 1 F" out[2] $end + $var wire 1 G" out[3] $end + $var wire 1 H" out[4] $end + $var wire 1 I" out[5] $end + $var wire 1 J" out[6] $end + $var wire 1 K" out[7] $end $upscope $end $scope module i_unpack2pack $end - $var wire 32 W" WIDTH [31:0] $end - $var wire 1 M" in(0) $end - $var wire 1 N" in(1) $end - $var wire 1 O" in(2) $end - $var wire 1 P" in(3) $end - $var wire 1 Q" in(4) $end - $var wire 1 R" in(5) $end - $var wire 1 S" in(6) $end - $var wire 1 T" in(7) $end - $var wire 8 $" out [7:0] $end + $var wire 32 V" WIDTH [31:0] $end + $var wire 1 L" in[0] $end + $var wire 1 M" in[1] $end + $var wire 1 N" in[2] $end + $var wire 1 O" in[3] $end + $var wire 1 P" in[4] $end + $var wire 1 Q" in[5] $end + $var wire 1 R" in[6] $end + $var wire 1 S" in[7] $end + $var wire 8 #" out [7:0] $end $upscope $end $upscope $end $upscope $end @@ -540,18 +539,18 @@ b10001110 U! b10001110 V! b10001110 W! b10001110 X! -b10001110 Y! +b00000000000000000000000010001110 Y! b00000000000000000000000010001110 Z! b00000000000000000000000010001110 [! -b00000000000000000000000010001110 \! -b10001110 ]! -b10001110100011101000111010001110 ^! -b10001110 _! -b10001110100011101000111010001110 `! -b10001110 a! -b10001110100011101000111010001110 b! -b10001110 c! -b10001110100011101000111010001110 d! +b10001110 \! +b10001110100011101000111010001110 ]! +b10001110 ^! +b10001110100011101000111010001110 _! +b10001110 `! +b10001110100011101000111010001110 a! +b10001110 b! +b10001110100011101000111010001110 c! +0d! 0e! 0f! 0g! @@ -567,10 +566,10 @@ b10001110100011101000111010001110 d! 0q! 0r! 0s! -0t! -b00000000000000000000000000000000 u! +b00000000000000000000000000000000 t! +0u! 0v! -0w! +b10001110 w! b10001110 x! b10001110 y! b10001110 z! @@ -605,39 +604,39 @@ b10001110 8" b10001110 9" b10001110 :" b10001110 ;" -b10001110 <" -1=" +1<" +0=" 0>" 0?" -0@" +1@" 1A" 1B" -1C" +0C" 0D" -0E" +1E" 1F" 1G" -1H" +0H" 0I" 0J" -0K" -1L" -0M" +1K" +0L" +1M" 1N" 1O" -1P" +0P" 0Q" 0R" -0S" -1T" -0U" -b00000000000000000000000000000011 V" -b00000000000000000000000000001000 W" -b00000000000000000000000000001001 X" -b10001110 Y" -b1000111001000111101000111101000111101000011101000011101000011101 Z" -b11111111111111111111111111111101 \" -b00000000000000000000000000000001 ]" +1S" +0T" +b00000000000000000000000000000011 U" +b00000000000000000000000000001000 V" +b00000000000000000000000000001001 W" +b10001110 X" +b1000111001000111101000111101000111101000011101000011101000011101 Y" +b11111111111111111111111111111101 [" +b00000000000000000000000000000001 \" +b00000000 ]" b00000000 ^" b00000000 _" b00000000 `" @@ -661,18 +660,17 @@ b00000000 q" b00000000 r" b00000000 s" b00000000 t" -b00000000 u" -b00000000000000000000000000000010 v" -b00000000000000000000000010001110 w" -b000000000000000000000000 x" -b11111111111111111111111111111110 y" +b00000000000000000000000000000010 u" +b00000000000000000000000010001110 v" +b000000000000000000000000 w" +b11111111111111111111111111111110 x" +0y" 0z" 0{" 0|" 0}" 0~" -0!# -b00000000000000000000000000000000 "# +b00000000000000000000000000000000 !# #10 b001 - b00000000000000000000000000001001 / @@ -690,7 +688,7 @@ b01000111 : b01000111 ; b01000111 < b01000111 = -b01000111 > +b01000111 A b01000111 B b01000111 C b01000111 D @@ -699,7 +697,7 @@ b01000111 F b01000111 G b01000111 H b01000111 I -b01000111 J +b01000111 M b01000111 N b01000111 O b01000111 P @@ -708,7 +706,7 @@ b01000111 R b01000111 S b01000111 T b01000111 U -b01000111 V +b01000111 Y b01000111 Z b01000111 [ b01000111 \ @@ -717,7 +715,7 @@ b01000111 ^ b01000111 _ b01000111 ` b01000111 a -b01000111 b +b01000111 e b01000111 f b01000111 g b01000111 h @@ -726,7 +724,7 @@ b01000111 j b01000111 k b01000111 l b01000111 m -b01000111 n +b01000111 q b01000111 r b01000111 s b01000111 t @@ -735,7 +733,7 @@ b01000111 v b01000111 w b01000111 x b01000111 y -b01000111 z +b01000111 } b01000111 ~ b01000111 !! b01000111 "! @@ -744,7 +742,7 @@ b01000111 $! b01000111 %! b01000111 &! b01000111 '! -b01000111 (! +b01000111 +! b01000111 ,! b01000111 -! b01000111 .! @@ -753,7 +751,7 @@ b01000111 0! b01000111 1! b01000111 2! b01000111 3! -b01000111 4! +b01000111 7! b01000111 8! b01000111 9! b01000111 :! @@ -762,7 +760,7 @@ b01000111 ! b01000111 ?! -b01000111 @! +b01000111 C! b01000111 D! b01000111 E! b01000111 F! @@ -771,7 +769,7 @@ b01000111 H! b01000111 I! b01000111 J! b01000111 K! -b01000111 L! +b01000111 O! b01000111 P! b01000111 Q! b01000111 R! @@ -781,24 +779,24 @@ b01000111 U! b01000111 V! b01000111 W! b01000111 X! -b01000111 Y! +b00000000000000000000000001000111 Y! b00000000000000000000000001000111 Z! b00000000000000000000000001000111 [! -b00000000000000000000000001000111 \! -b01000111 ]! -b10001110010001110100011101000111 ^! -b01000111 _! -b10001110010001110100011101000111 `! -b01000111 a! -b10001110010001110100011101000111 b! -b01000111 c! -b10001110010001110100011101000111 d! -1e! -1i! -1m! -1q! -b00000000000000000000000000000001 u! -1w! +b01000111 \! +b10001110010001110100011101000111 ]! +b01000111 ^! +b10001110010001110100011101000111 _! +b01000111 `! +b10001110010001110100011101000111 a! +b01000111 b! +b10001110010001110100011101000111 c! +1d! +1h! +1l! +1p! +b00000000000000000000000000000001 t! +1v! +b01000111 w! b01000111 x! b01000111 y! b01000111 z! @@ -810,7 +808,7 @@ b01000111 !" b01000111 "" b01000111 #" b01000111 $" -b01000111 %" +b01000111 (" b01000111 )" b01000111 *" b01000111 +" @@ -818,7 +816,7 @@ b01000111 ," b01000111 -" b01000111 ." b01000111 /" -b01000111 0" +b01000111 3" b01000111 4" b01000111 5" b01000111 6" @@ -827,22 +825,21 @@ b01000111 8" b01000111 9" b01000111 :" b01000111 ;" -b01000111 <" -0=" -1>" -0A" +0<" +1=" +0@" +1C" 1D" -1E" -0H" -1K" -0L" -1M" -0P" -1S" -0T" -1U" +0G" +1J" +0K" +1L" +0O" +1R" +0S" +1T" #15 -0U" +0T" #20 b010 - b10100011 0 @@ -851,122 +848,122 @@ b10100011 2 b10100011 3 b10100011 4 b10100011 5 -b10100011 6 -b10001110 7 +b10001110 6 +b10100011 7 b10100011 8 b10100011 9 b10100011 : b10100011 ; b10100011 < b10100011 = -b10100011 > +b10001110 A b10001110 B b10001110 C -b10001110 D +b10100011 D b10100011 E b10100011 F b10100011 G b10100011 H b10100011 I -b10100011 J +b10001110 M b10001110 N b10001110 O -b10001110 P +b10100011 P b10100011 Q b10100011 R b10100011 S b10100011 T b10100011 U -b10100011 V +b10001110 Y b10001110 Z b10001110 [ -b10001110 \ +b10100011 \ b10100011 ] b10100011 ^ b10100011 _ b10100011 ` b10100011 a -b10100011 b +b10001110 e b10001110 f b10001110 g -b10001110 h +b10100011 h b10100011 i b10100011 j b10100011 k b10100011 l b10100011 m -b10100011 n +b10001110 q b10001110 r b10001110 s -b10001110 t +b10100011 t b10100011 u b10100011 v b10100011 w b10100011 x b10100011 y -b10100011 z +b10001110 } b10001110 ~ b10001110 !! -b10001110 "! +b10100011 "! b10100011 #! b10100011 $! b10100011 %! b10100011 &! b10100011 '! -b10100011 (! +b10001110 +! b10001110 ,! b10001110 -! -b10001110 .! +b10100011 .! b10100011 /! b10100011 0! b10100011 1! b10100011 2! b10100011 3! -b10100011 4! +b10001110 7! b10001110 8! b10001110 9! -b10001110 :! +b10100011 :! b10100011 ;! b10100011 ! b10100011 ?! -b10100011 @! +b10001110 C! b10001110 D! b10001110 E! -b10001110 F! +b10100011 F! b10100011 G! b10100011 H! b10100011 I! b10100011 J! b10100011 K! -b10100011 L! +b10001110 O! b10001110 P! b10001110 Q! -b10001110 R! +b10100011 R! b10100011 S! b10100011 T! b10100011 U! b10100011 V! b10100011 W! b10100011 X! -b10100011 Y! -b00000000000000000000000010001110 Z! +b00000000000000000000000010001110 Y! +b00000000000000000000000010100011 Z! b00000000000000000000000010100011 [! -b00000000000000000000000010100011 \! -b10100011 ]! -b10001110100011101010001110100011 ^! -b10100011 _! -b10001110100011101010001110100011 `! -b10100011 a! -b10001110100011101010001110100011 b! -b10100011 c! -b10001110100011101010001110100011 d! -1f! -1j! -1n! -1r! -b00000000000000000000000000000010 u! +b10100011 \! +b10001110100011101010001110100011 ]! +b10100011 ^! +b10001110100011101010001110100011 _! +b10100011 `! +b10001110100011101010001110100011 a! +b10100011 b! +b10001110100011101010001110100011 c! +1e! +1i! +1m! +1q! +b00000000000000000000000000000010 t! +b10100011 w! b10100011 x! b10100011 y! b10100011 z! @@ -978,39 +975,38 @@ b10100011 !" b10100011 "" b10100011 #" b10100011 $" -b10100011 %" +b10001110 (" b10001110 )" b10001110 *" -b10001110 +" +b10100011 +" b10100011 ," b10100011 -" b10100011 ." b10100011 /" -b10100011 0" +b10001110 3" b10001110 4" b10001110 5" -b10001110 6" +b10100011 6" b10100011 7" b10100011 8" b10100011 9" b10100011 :" b10100011 ;" -b10100011 <" -1=" -0>" -1?" -0B" -0G" -1J" -0K" -1L" -0O" -1R" -0S" +1<" +0=" +1>" +0A" +0F" +1I" +0J" +1K" +0N" +1Q" +0R" +1S" 1T" -1U" #25 -0U" +0T" #30 b011 - b11010001 0 @@ -1019,122 +1015,122 @@ b11010001 2 b11010001 3 b11010001 4 b11010001 5 -b11010001 6 -b01000111 7 +b01000111 6 +b11010001 7 b11010001 8 b11010001 9 b11010001 : b11010001 ; b11010001 < b11010001 = -b11010001 > +b01000111 A b01000111 B b01000111 C -b01000111 D +b11010001 D b11010001 E b11010001 F b11010001 G b11010001 H b11010001 I -b11010001 J +b01000111 M b01000111 N b01000111 O -b01000111 P +b11010001 P b11010001 Q b11010001 R b11010001 S b11010001 T b11010001 U -b11010001 V +b01000111 Y b01000111 Z b01000111 [ -b01000111 \ +b11010001 \ b11010001 ] b11010001 ^ b11010001 _ b11010001 ` b11010001 a -b11010001 b +b01000111 e b01000111 f b01000111 g -b01000111 h +b11010001 h b11010001 i b11010001 j b11010001 k b11010001 l b11010001 m -b11010001 n +b01000111 q b01000111 r b01000111 s -b01000111 t +b11010001 t b11010001 u b11010001 v b11010001 w b11010001 x b11010001 y -b11010001 z +b01000111 } b01000111 ~ b01000111 !! -b01000111 "! +b11010001 "! b11010001 #! b11010001 $! b11010001 %! b11010001 &! b11010001 '! -b11010001 (! +b01000111 +! b01000111 ,! b01000111 -! -b01000111 .! +b11010001 .! b11010001 /! b11010001 0! b11010001 1! b11010001 2! b11010001 3! -b11010001 4! +b01000111 7! b01000111 8! b01000111 9! -b01000111 :! +b11010001 :! b11010001 ;! b11010001 ! b11010001 ?! -b11010001 @! +b01000111 C! b01000111 D! b01000111 E! -b01000111 F! +b11010001 F! b11010001 G! b11010001 H! b11010001 I! b11010001 J! b11010001 K! -b11010001 L! +b01000111 O! b01000111 P! b01000111 Q! -b01000111 R! +b11010001 R! b11010001 S! b11010001 T! b11010001 U! b11010001 V! b11010001 W! b11010001 X! -b11010001 Y! -b00000000000000000000000001000111 Z! +b00000000000000000000000001000111 Y! +b00000000000000000000000011010001 Z! b00000000000000000000000011010001 [! -b00000000000000000000000011010001 \! -b11010001 ]! -b10001110010001111101000111010001 ^! -b11010001 _! -b10001110010001111101000111010001 `! -b11010001 a! -b10001110010001111101000111010001 b! -b11010001 c! -b10001110010001111101000111010001 d! -1g! -1k! -1o! -1s! -b00000000000000000000000000000011 u! +b11010001 \! +b10001110010001111101000111010001 ]! +b11010001 ^! +b10001110010001111101000111010001 _! +b11010001 `! +b10001110010001111101000111010001 a! +b11010001 b! +b10001110010001111101000111010001 c! +1f! +1j! +1n! +1r! +b00000000000000000000000000000011 t! +b11010001 w! b11010001 x! b11010001 y! b11010001 z! @@ -1146,163 +1142,162 @@ b11010001 !" b11010001 "" b11010001 #" b11010001 $" -b11010001 %" +b01000111 (" b01000111 )" b01000111 *" -b01000111 +" +b11010001 +" b11010001 ," b11010001 -" b11010001 ." b11010001 /" -b11010001 0" +b01000111 3" b01000111 4" b01000111 5" -b01000111 6" +b11010001 6" b11010001 7" b11010001 8" b11010001 9" b11010001 :" b11010001 ;" -b11010001 <" -1>" -0?" -1@" -0C" -0F" -1I" -0J" -1K" -0N" -1Q" -0R" -1S" -1U" +1=" +0>" +1?" +0B" +0E" +1H" +0I" +1J" +0M" +1P" +0Q" +1R" +1T" #35 -0U" +0T" #40 b100 - b11101000 0 b10001110 1 b10001110 2 b11101000 3 -b11101000 4 -b10001110 5 -b11101000 6 +b10001110 4 +b11101000 5 +b10001110 6 b10001110 7 b10001110 8 b10001110 9 b10001110 : -b10001110 ; +b11101000 ; b11101000 < b11101000 = -b11101000 > +b10001110 A b10001110 B b10001110 C b10001110 D b10001110 E b10001110 F -b10001110 G +b11101000 G b11101000 H b11101000 I -b11101000 J +b10001110 M b10001110 N b10001110 O b10001110 P b10001110 Q b10001110 R -b10001110 S +b11101000 S b11101000 T b11101000 U -b11101000 V +b10001110 Y b10001110 Z b10001110 [ b10001110 \ b10001110 ] b10001110 ^ -b10001110 _ +b11101000 _ b11101000 ` b11101000 a -b11101000 b +b10001110 e b10001110 f b10001110 g b10001110 h b10001110 i b10001110 j -b10001110 k +b11101000 k b11101000 l b11101000 m -b11101000 n +b10001110 q b10001110 r b10001110 s b10001110 t b10001110 u b10001110 v -b10001110 w +b11101000 w b11101000 x b11101000 y -b11101000 z +b10001110 } b10001110 ~ b10001110 !! b10001110 "! b10001110 #! b10001110 $! -b10001110 %! +b11101000 %! b11101000 &! b11101000 '! -b11101000 (! +b10001110 +! b10001110 ,! b10001110 -! b10001110 .! b10001110 /! b10001110 0! -b10001110 1! +b11101000 1! b11101000 2! b11101000 3! -b11101000 4! +b10001110 7! b10001110 8! b10001110 9! b10001110 :! b10001110 ;! b10001110 ! b11101000 ?! -b11101000 @! +b10001110 C! b10001110 D! b10001110 E! b10001110 F! b10001110 G! b10001110 H! -b10001110 I! +b11101000 I! b11101000 J! b11101000 K! -b11101000 L! +b10001110 O! b10001110 P! b10001110 Q! b10001110 R! b10001110 S! b10001110 T! -b10001110 U! +b11101000 U! b11101000 V! b11101000 W! b11101000 X! -b11101000 Y! +b00000000000000000000000010001110 Y! b00000000000000000000000010001110 Z! -b00000000000000000000000010001110 [! -b00000000000000000000000011101000 \! -b11101000 ]! -b10001110100011101000111011101000 ^! -b11101000 _! -b10001110100011101000111011101000 `! -b11101000 a! -b10001110100011101000111011101000 b! -b11101000 c! -b10001110100011101000111011101000 d! -1h! -1l! -1p! -1t! -b00000000000000000000000000000100 u! +b00000000000000000000000011101000 [! +b11101000 \! +b10001110100011101000111011101000 ]! +b11101000 ^! +b10001110100011101000111011101000 _! +b11101000 `! +b10001110100011101000111011101000 a! +b11101000 b! +b10001110100011101000111011101000 c! +1g! +1k! +1o! +1s! +b00000000000000000000000000000100 t! +b11101000 w! b11101000 x! b11101000 y! b11101000 z! @@ -1314,159 +1309,158 @@ b11101000 !" b11101000 "" b11101000 #" b11101000 $" -b11101000 %" +b10001110 (" b10001110 )" b10001110 *" b10001110 +" b10001110 ," b10001110 -" -b10001110 ." +b11101000 ." b11101000 /" -b11101000 0" +b10001110 3" b10001110 4" b10001110 5" b10001110 6" b10001110 7" b10001110 8" -b10001110 9" +b11101000 9" b11101000 :" b11101000 ;" -b11101000 <" -1?" -0@" -1A" +1>" +0?" +1@" +0C" 0D" -0E" -1H" -0I" -1J" -0M" -1P" -0Q" -1R" -1U" +1G" +0H" +1I" +0L" +1O" +0P" +1Q" +1T" #45 -0U" +0T" #50 b101 - b01110100 0 b01000111 1 b01000111 2 b01110100 3 -b01110100 4 -b01000111 5 -b01110100 6 +b01000111 4 +b01110100 5 +b01000111 6 b01000111 7 b01000111 8 b01000111 9 b01000111 : -b01000111 ; +b01110100 ; b01110100 < b01110100 = -b01110100 > +b01000111 A b01000111 B b01000111 C b01000111 D b01000111 E b01000111 F -b01000111 G +b01110100 G b01110100 H b01110100 I -b01110100 J +b01000111 M b01000111 N b01000111 O b01000111 P b01000111 Q b01000111 R -b01000111 S +b01110100 S b01110100 T b01110100 U -b01110100 V +b01000111 Y b01000111 Z b01000111 [ b01000111 \ b01000111 ] b01000111 ^ -b01000111 _ +b01110100 _ b01110100 ` b01110100 a -b01110100 b +b01000111 e b01000111 f b01000111 g b01000111 h b01000111 i b01000111 j -b01000111 k +b01110100 k b01110100 l b01110100 m -b01110100 n +b01000111 q b01000111 r b01000111 s b01000111 t b01000111 u b01000111 v -b01000111 w +b01110100 w b01110100 x b01110100 y -b01110100 z +b01000111 } b01000111 ~ b01000111 !! b01000111 "! b01000111 #! b01000111 $! -b01000111 %! +b01110100 %! b01110100 &! b01110100 '! -b01110100 (! +b01000111 +! b01000111 ,! b01000111 -! b01000111 .! b01000111 /! b01000111 0! -b01000111 1! +b01110100 1! b01110100 2! b01110100 3! -b01110100 4! +b01000111 7! b01000111 8! b01000111 9! b01000111 :! b01000111 ;! b01000111 ! b01110100 ?! -b01110100 @! +b01000111 C! b01000111 D! b01000111 E! b01000111 F! b01000111 G! b01000111 H! -b01000111 I! +b01110100 I! b01110100 J! b01110100 K! -b01110100 L! +b01000111 O! b01000111 P! b01000111 Q! b01000111 R! b01000111 S! b01000111 T! -b01000111 U! +b01110100 U! b01110100 V! b01110100 W! b01110100 X! -b01110100 Y! +b00000000000000000000000001000111 Y! b00000000000000000000000001000111 Z! -b00000000000000000000000001000111 [! -b00000000000000000000000001110100 \! -b01110100 ]! -b10001110010001110100011101110100 ^! -b01110100 _! -b10001110010001110100011101110100 `! -b01110100 a! -b10001110010001110100011101110100 b! -b01110100 c! -b10001110010001110100011101110100 d! -b00000000000000000000000000000101 u! +b00000000000000000000000001110100 [! +b01110100 \! +b10001110010001110100011101110100 ]! +b01110100 ^! +b10001110010001110100011101110100 _! +b01110100 `! +b10001110010001110100011101110100 a! +b01110100 b! +b10001110010001110100011101110100 c! +b00000000000000000000000000000101 t! +b01110100 w! b01110100 x! b01110100 y! b01110100 z! @@ -1478,159 +1472,158 @@ b01110100 !" b01110100 "" b01110100 #" b01110100 $" -b01110100 %" +b01000111 (" b01000111 )" b01000111 *" b01000111 +" b01000111 ," b01000111 -" -b01000111 ." +b01110100 ." b01110100 /" -b01110100 0" +b01000111 3" b01000111 4" b01000111 5" b01000111 6" b01000111 7" b01000111 8" -b01000111 9" +b01110100 9" b01110100 :" b01110100 ;" -b01110100 <" -0=" -1@" -0A" -1B" -1G" -0H" -1I" -0L" -1O" -0P" -1Q" -0T" -1U" +0<" +1?" +0@" +1A" +1F" +0G" +1H" +0K" +1N" +0O" +1P" +0S" +1T" #55 -0U" +0T" #60 b110 - b00111010 0 b10001110 1 b10100011 2 b00111010 3 -b00111010 4 -b10100011 5 -b00111010 6 -b10001110 7 +b10100011 4 +b00111010 5 +b10001110 6 +b10100011 7 b10100011 8 b10100011 9 b10100011 : -b10100011 ; +b00111010 ; b00111010 < b00111010 = -b00111010 > +b10001110 A b10001110 B b10001110 C -b10001110 D +b10100011 D b10100011 E b10100011 F -b10100011 G +b00111010 G b00111010 H b00111010 I -b00111010 J +b10001110 M b10001110 N b10001110 O -b10001110 P +b10100011 P b10100011 Q b10100011 R -b10100011 S +b00111010 S b00111010 T b00111010 U -b00111010 V +b10001110 Y b10001110 Z b10001110 [ -b10001110 \ +b10100011 \ b10100011 ] b10100011 ^ -b10100011 _ +b00111010 _ b00111010 ` b00111010 a -b00111010 b +b10001110 e b10001110 f b10001110 g -b10001110 h +b10100011 h b10100011 i b10100011 j -b10100011 k +b00111010 k b00111010 l b00111010 m -b00111010 n +b10001110 q b10001110 r b10001110 s -b10001110 t +b10100011 t b10100011 u b10100011 v -b10100011 w +b00111010 w b00111010 x b00111010 y -b00111010 z +b10001110 } b10001110 ~ b10001110 !! -b10001110 "! +b10100011 "! b10100011 #! b10100011 $! -b10100011 %! +b00111010 %! b00111010 &! b00111010 '! -b00111010 (! +b10001110 +! b10001110 ,! b10001110 -! -b10001110 .! +b10100011 .! b10100011 /! b10100011 0! -b10100011 1! +b00111010 1! b00111010 2! b00111010 3! -b00111010 4! +b10001110 7! b10001110 8! b10001110 9! -b10001110 :! +b10100011 :! b10100011 ;! b10100011 ! b00111010 ?! -b00111010 @! +b10001110 C! b10001110 D! b10001110 E! -b10001110 F! +b10100011 F! b10100011 G! b10100011 H! -b10100011 I! +b00111010 I! b00111010 J! b00111010 K! -b00111010 L! +b10001110 O! b10001110 P! b10001110 Q! -b10001110 R! +b10100011 R! b10100011 S! b10100011 T! -b10100011 U! +b00111010 U! b00111010 V! b00111010 W! b00111010 X! -b00111010 Y! -b00000000000000000000000010001110 Z! -b00000000000000000000000010100011 [! -b00000000000000000000000000111010 \! -b00111010 ]! -b10001110100011101010001100111010 ^! -b00111010 _! -b10001110100011101010001100111010 `! -b00111010 a! -b10001110100011101010001100111010 b! -b00111010 c! -b10001110100011101010001100111010 d! -b00000000000000000000000000000110 u! +b00000000000000000000000010001110 Y! +b00000000000000000000000010100011 Z! +b00000000000000000000000000111010 [! +b00111010 \! +b10001110100011101010001100111010 ]! +b00111010 ^! +b10001110100011101010001100111010 _! +b00111010 `! +b10001110100011101010001100111010 a! +b00111010 b! +b10001110100011101010001100111010 c! +b00000000000000000000000000000110 t! +b00111010 w! b00111010 x! b00111010 y! b00111010 z! @@ -1642,159 +1635,158 @@ b00111010 !" b00111010 "" b00111010 #" b00111010 $" -b00111010 %" +b10001110 (" b10001110 )" b10001110 *" -b10001110 +" +b10100011 +" b10100011 ," b10100011 -" -b10100011 ." +b00111010 ." b00111010 /" -b00111010 0" +b10001110 3" b10001110 4" b10001110 5" -b10001110 6" +b10100011 6" b10100011 7" b10100011 8" -b10100011 9" +b00111010 9" b00111010 :" b00111010 ;" -b00111010 <" -0>" -1A" -0B" -1C" -1F" -0G" -1H" -0K" -1N" -0O" -1P" -0S" -1U" +0=" +1@" +0A" +1B" +1E" +0F" +1G" +0J" +1M" +0N" +1O" +0R" +1T" #65 -0U" +0T" #70 b111 - b00011101 0 b01000111 1 b11010001 2 b00011101 3 -b00011101 4 -b11010001 5 -b00011101 6 -b01000111 7 +b11010001 4 +b00011101 5 +b01000111 6 +b11010001 7 b11010001 8 b11010001 9 b11010001 : -b11010001 ; +b00011101 ; b00011101 < b00011101 = -b00011101 > +b01000111 A b01000111 B b01000111 C -b01000111 D +b11010001 D b11010001 E b11010001 F -b11010001 G +b00011101 G b00011101 H b00011101 I -b00011101 J +b01000111 M b01000111 N b01000111 O -b01000111 P +b11010001 P b11010001 Q b11010001 R -b11010001 S +b00011101 S b00011101 T b00011101 U -b00011101 V +b01000111 Y b01000111 Z b01000111 [ -b01000111 \ +b11010001 \ b11010001 ] b11010001 ^ -b11010001 _ +b00011101 _ b00011101 ` b00011101 a -b00011101 b +b01000111 e b01000111 f b01000111 g -b01000111 h +b11010001 h b11010001 i b11010001 j -b11010001 k +b00011101 k b00011101 l b00011101 m -b00011101 n +b01000111 q b01000111 r b01000111 s -b01000111 t +b11010001 t b11010001 u b11010001 v -b11010001 w +b00011101 w b00011101 x b00011101 y -b00011101 z +b01000111 } b01000111 ~ b01000111 !! -b01000111 "! +b11010001 "! b11010001 #! b11010001 $! -b11010001 %! +b00011101 %! b00011101 &! b00011101 '! -b00011101 (! +b01000111 +! b01000111 ,! b01000111 -! -b01000111 .! +b11010001 .! b11010001 /! b11010001 0! -b11010001 1! +b00011101 1! b00011101 2! b00011101 3! -b00011101 4! +b01000111 7! b01000111 8! b01000111 9! -b01000111 :! +b11010001 :! b11010001 ;! b11010001 ! b00011101 ?! -b00011101 @! +b01000111 C! b01000111 D! b01000111 E! -b01000111 F! +b11010001 F! b11010001 G! b11010001 H! -b11010001 I! +b00011101 I! b00011101 J! b00011101 K! -b00011101 L! +b01000111 O! b01000111 P! b01000111 Q! -b01000111 R! +b11010001 R! b11010001 S! b11010001 T! -b11010001 U! +b00011101 U! b00011101 V! b00011101 W! b00011101 X! -b00011101 Y! -b00000000000000000000000001000111 Z! -b00000000000000000000000011010001 [! -b00000000000000000000000000011101 \! -b00011101 ]! -b10001110010001111101000100011101 ^! -b00011101 _! -b10001110010001111101000100011101 `! -b00011101 a! -b10001110010001111101000100011101 b! -b00011101 c! -b10001110010001111101000100011101 d! -b00000000000000000000000000000111 u! +b00000000000000000000000001000111 Y! +b00000000000000000000000011010001 Z! +b00000000000000000000000000011101 [! +b00011101 \! +b10001110010001111101000100011101 ]! +b00011101 ^! +b10001110010001111101000100011101 _! +b00011101 `! +b10001110010001111101000100011101 a! +b00011101 b! +b10001110010001111101000100011101 c! +b00000000000000000000000000000111 t! +b00011101 w! b00011101 x! b00011101 y! b00011101 z! @@ -1806,39 +1798,38 @@ b00011101 !" b00011101 "" b00011101 #" b00011101 $" -b00011101 %" +b01000111 (" b01000111 )" b01000111 *" -b01000111 +" +b11010001 +" b11010001 ," b11010001 -" -b11010001 ." +b00011101 ." b00011101 /" -b00011101 0" +b01000111 3" b01000111 4" b01000111 5" -b01000111 6" +b11010001 6" b11010001 7" b11010001 8" -b11010001 9" +b00011101 9" b00011101 :" b00011101 ;" -b00011101 <" -0?" -1B" -0C" +0>" +1A" +0B" +1C" 1D" -1E" -0F" -1G" -0J" -1M" -0N" -1O" -0R" -1U" +0E" +1F" +0I" +1L" +0M" +1N" +0Q" +1T" #75 -0U" +0T" #80 b000 - b10001110 0 @@ -1855,7 +1846,7 @@ b10001110 : b10001110 ; b10001110 < b10001110 = -b10001110 > +b10001110 A b10001110 B b10001110 C b10001110 D @@ -1864,7 +1855,7 @@ b10001110 F b10001110 G b10001110 H b10001110 I -b10001110 J +b10001110 M b10001110 N b10001110 O b10001110 P @@ -1873,7 +1864,7 @@ b10001110 R b10001110 S b10001110 T b10001110 U -b10001110 V +b10001110 Y b10001110 Z b10001110 [ b10001110 \ @@ -1882,7 +1873,7 @@ b10001110 ^ b10001110 _ b10001110 ` b10001110 a -b10001110 b +b10001110 e b10001110 f b10001110 g b10001110 h @@ -1891,7 +1882,7 @@ b10001110 j b10001110 k b10001110 l b10001110 m -b10001110 n +b10001110 q b10001110 r b10001110 s b10001110 t @@ -1900,7 +1891,7 @@ b10001110 v b10001110 w b10001110 x b10001110 y -b10001110 z +b10001110 } b10001110 ~ b10001110 !! b10001110 "! @@ -1909,7 +1900,7 @@ b10001110 $! b10001110 %! b10001110 &! b10001110 '! -b10001110 (! +b10001110 +! b10001110 ,! b10001110 -! b10001110 .! @@ -1918,7 +1909,7 @@ b10001110 0! b10001110 1! b10001110 2! b10001110 3! -b10001110 4! +b10001110 7! b10001110 8! b10001110 9! b10001110 :! @@ -1927,7 +1918,7 @@ b10001110 ! b10001110 ?! -b10001110 @! +b10001110 C! b10001110 D! b10001110 E! b10001110 F! @@ -1936,7 +1927,7 @@ b10001110 H! b10001110 I! b10001110 J! b10001110 K! -b10001110 L! +b10001110 O! b10001110 P! b10001110 Q! b10001110 R! @@ -1946,19 +1937,19 @@ b10001110 U! b10001110 V! b10001110 W! b10001110 X! -b10001110 Y! +b00000000000000000000000010001110 Y! b00000000000000000000000010001110 Z! b00000000000000000000000010001110 [! -b00000000000000000000000010001110 \! -b10001110 ]! -b10001110100011101000111010001110 ^! -b10001110 _! -b10001110100011101000111010001110 `! -b10001110 a! -b10001110100011101000111010001110 b! -b10001110 c! -b10001110100011101000111010001110 d! -b00000000000000000000000000001000 u! +b10001110 \! +b10001110100011101000111010001110 ]! +b10001110 ^! +b10001110100011101000111010001110 _! +b10001110 `! +b10001110100011101000111010001110 a! +b10001110 b! +b10001110100011101000111010001110 c! +b00000000000000000000000000001000 t! +b10001110 w! b10001110 x! b10001110 y! b10001110 z! @@ -1970,7 +1961,7 @@ b10001110 !" b10001110 "" b10001110 #" b10001110 $" -b10001110 %" +b10001110 (" b10001110 )" b10001110 *" b10001110 +" @@ -1978,7 +1969,7 @@ b10001110 ," b10001110 -" b10001110 ." b10001110 /" -b10001110 0" +b10001110 3" b10001110 4" b10001110 5" b10001110 6" @@ -1987,17 +1978,16 @@ b10001110 8" b10001110 9" b10001110 :" b10001110 ;" -b10001110 <" -1=" -0@" -1C" +1<" +0?" +1B" +0C" 0D" -0E" -1F" -0I" -1L" -0M" -1N" -0Q" +1E" +0H" +1K" +0L" +1M" +0P" +1S" 1T" -1U" diff --git a/test_regress/t/t_trace_complex.out b/test_regress/t/t_trace_complex.out index 74c7eb8c7..62c142c2b 100644 --- a/test_regress/t/t_trace_complex.out +++ b/test_regress/t/t_trace_complex.out @@ -1,7 +1,6 @@ $version Generated by VerilatedVcd $end -$date Mon Nov 16 17:48:27 2020 - $end -$timescale 1ps $end +$date Wed Aug 11 12:40:46 2021 $end +$timescale 1ps $end $scope module top $end $var wire 1 = clk $end @@ -12,24 +11,24 @@ $timescale 1ps $end $var wire 1 G LONGSTART_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_LONGEND $end $var wire 1 = clk $end $var wire 32 $ cyc [31:0] $end - $var wire 8 E unpacked_array(-1) [7:0] $end - $var wire 8 D unpacked_array(-2) [7:0] $end - $var wire 8 F unpacked_array(0) [7:0] $end - $var real 64 1 v_arr_real(0) $end - $var real 64 3 v_arr_real(1) $end + $var wire 8 E unpacked_array[-1] [7:0] $end + $var wire 8 D unpacked_array[-2] [7:0] $end + $var wire 8 F unpacked_array[0] [7:0] $end + $var real 64 1 v_arr_real[0] $end + $var real 64 3 v_arr_real[1] $end $var wire 2 ( v_arrp [2:1] $end $var wire 4 ) v_arrp_arrp [3:0] $end $var wire 4 * v_arrp_strp [3:0] $end - $var wire 1 > v_arru(1) $end - $var wire 1 ? v_arru(2) $end - $var wire 2 + v_arru_arrp(3) [2:1] $end - $var wire 2 , v_arru_arrp(4) [2:1] $end - $var wire 1 @ v_arru_arru(3)(1) $end - $var wire 1 A v_arru_arru(3)(2) $end - $var wire 1 B v_arru_arru(4)(1) $end - $var wire 1 C v_arru_arru(4)(2) $end - $var wire 2 - v_arru_strp(3) [1:0] $end - $var wire 2 . v_arru_strp(4) [1:0] $end + $var wire 1 > v_arru[1] $end + $var wire 1 ? v_arru[2] $end + $var wire 2 + v_arru_arrp[3] [2:1] $end + $var wire 2 , v_arru_arrp[4] [2:1] $end + $var wire 1 @ v_arru_arru[3][1] $end + $var wire 1 A v_arru_arru[3][2] $end + $var wire 1 B v_arru_arru[4][1] $end + $var wire 1 C v_arru_arru[4][2] $end + $var wire 2 - v_arru_strp[3] [1:0] $end + $var wire 2 . v_arru_strp[4] [1:0] $end $var wire 3 9 v_enumb [2:0] $end $var wire 6 : v_enumb2_str [5:0] $end $var wire 32 7 v_enumed [31:0] $end diff --git a/test_regress/t/t_trace_complex.pl b/test_regress/t/t_trace_complex.pl index 49c306eb5..8400cbf09 100755 --- a/test_regress/t/t_trace_complex.pl +++ b/test_regress/t/t_trace_complex.pl @@ -23,10 +23,10 @@ file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_strp_strp /); file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arrp /); file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arrp_arrp /); file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arrp_strp /); -file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru\(/); -file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru_arru\(/); -file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru_arrp\(/); -file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru_strp\(/); +file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru\[/); +file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru_arru\[/); +file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru_arrp\[/); +file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru_strp\[/); vcd_identical ("$Self->{obj_dir}/simx.vcd", $Self->{golden_filename}); diff --git a/test_regress/t/t_trace_complex_fst.out b/test_regress/t/t_trace_complex_fst.out index d57c690c6..3fbfab803 100644 --- a/test_regress/t/t_trace_complex_fst.out +++ b/test_regress/t/t_trace_complex_fst.out @@ -1,5 +1,5 @@ $date - Mon Nov 16 17:51:08 2020 + Wed Aug 11 12:40:48 2021 $end $version @@ -19,19 +19,19 @@ $var logic 2 % v_unip_strp $end $var logic 2 & v_arrp $end $var logic 4 ' v_arrp_arrp $end $var logic 4 ( v_arrp_strp $end -$var logic 1 ) v_arru(1) $end -$var logic 1 * v_arru(2) $end -$var logic 1 + v_arru_arru(3)(1) $end -$var logic 1 , v_arru_arru(3)(2) $end -$var logic 1 - v_arru_arru(4)(1) $end -$var logic 1 . v_arru_arru(4)(2) $end -$var logic 2 / v_arru_arrp(3) $end -$var logic 2 0 v_arru_arrp(4) $end -$var logic 2 1 v_arru_strp(3) $end -$var logic 2 2 v_arru_strp(4) $end +$var logic 1 ) v_arru[1] $end +$var logic 1 * v_arru[2] $end +$var logic 1 + v_arru_arru[3][1] $end +$var logic 1 , v_arru_arru[3][2] $end +$var logic 1 - v_arru_arru[4][1] $end +$var logic 1 . v_arru_arru[4][2] $end +$var logic 2 / v_arru_arrp[3] $end +$var logic 2 0 v_arru_arrp[4] $end +$var logic 2 1 v_arru_strp[3] $end +$var logic 2 2 v_arru_strp[4] $end $var real 64 3 v_real $end -$var real 64 4 v_arr_real(0) $end -$var real 64 5 v_arr_real(1) $end +$var real 64 4 v_arr_real[0] $end +$var real 64 5 v_arr_real[1] $end $var logic 64 6 v_str32x2 $end $attrbegin misc 07 t.enumed_t 4 ZERO ONE TWO THREE 00000000000000000000000000000000 00000000000000000000000000000001 00000000000000000000000000000010 00000000000000000000000000000011 1 $end $attrbegin misc 07 "" 1 $end @@ -42,9 +42,9 @@ $attrbegin misc 07 t.enumb_t 4 BZERO BONE BTWO BTHREE 000 001 010 011 2 $end $attrbegin misc 07 "" 2 $end $var logic 3 9 v_enumb $end $var logic 6 : v_enumb2_str $end -$var logic 8 ; unpacked_array(-2) $end -$var logic 8 < unpacked_array(-1) $end -$var logic 8 = unpacked_array(0) $end +$var logic 8 ; unpacked_array[-2] $end +$var logic 8 < unpacked_array[-1] $end +$var logic 8 = unpacked_array[0] $end $var bit 1 > LONGSTART_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_LONGEND $end $scope module unnamedblk1 $end $var integer 32 ? b $end @@ -67,8 +67,8 @@ $var bit 1 D global_bit $end $upscope $end $upscope $end $enddefinitions $end -$dumpvars #0 +$dumpvars 1D b00000000000000000000000000000100 C b00000000000000000000000000000011 B @@ -105,6 +105,7 @@ b0000 $ b00 # b00000000000000000000000000000000 " 0! +$end #10 1! b00000000000000000000000000000001 " diff --git a/test_regress/t/t_trace_complex_fst_sc.out b/test_regress/t/t_trace_complex_fst_sc.out index f3da1ace4..f56182246 100644 --- a/test_regress/t/t_trace_complex_fst_sc.out +++ b/test_regress/t/t_trace_complex_fst_sc.out @@ -1,5 +1,5 @@ $date - Mon Apr 19 17:05:53 2021 + Wed Aug 11 12:40:52 2021 $end $version @@ -18,19 +18,19 @@ $var logic 2 % v_unip_strp $end $var logic 2 & v_arrp $end $var logic 4 ' v_arrp_arrp $end $var logic 4 ( v_arrp_strp $end -$var logic 1 ) v_arru(1) $end -$var logic 1 * v_arru(2) $end -$var logic 1 + v_arru_arru(3)(1) $end -$var logic 1 , v_arru_arru(3)(2) $end -$var logic 1 - v_arru_arru(4)(1) $end -$var logic 1 . v_arru_arru(4)(2) $end -$var logic 2 / v_arru_arrp(3) $end -$var logic 2 0 v_arru_arrp(4) $end -$var logic 2 1 v_arru_strp(3) $end -$var logic 2 2 v_arru_strp(4) $end +$var logic 1 ) v_arru[1] $end +$var logic 1 * v_arru[2] $end +$var logic 1 + v_arru_arru[3][1] $end +$var logic 1 , v_arru_arru[3][2] $end +$var logic 1 - v_arru_arru[4][1] $end +$var logic 1 . v_arru_arru[4][2] $end +$var logic 2 / v_arru_arrp[3] $end +$var logic 2 0 v_arru_arrp[4] $end +$var logic 2 1 v_arru_strp[3] $end +$var logic 2 2 v_arru_strp[4] $end $var real 64 3 v_real $end -$var real 64 4 v_arr_real(0) $end -$var real 64 5 v_arr_real(1) $end +$var real 64 4 v_arr_real[0] $end +$var real 64 5 v_arr_real[1] $end $var logic 64 6 v_str32x2 $end $attrbegin misc 07 t.enumed_t 4 ZERO ONE TWO THREE 00000000000000000000000000000000 00000000000000000000000000000001 00000000000000000000000000000010 00000000000000000000000000000011 1 $end $attrbegin misc 07 "" 1 $end @@ -41,9 +41,9 @@ $attrbegin misc 07 t.enumb_t 4 BZERO BONE BTWO BTHREE 000 001 010 011 2 $end $attrbegin misc 07 "" 2 $end $var logic 3 9 v_enumb $end $var logic 6 : v_enumb2_str $end -$var logic 8 ; unpacked_array(-2) $end -$var logic 8 < unpacked_array(-1) $end -$var logic 8 = unpacked_array(0) $end +$var logic 8 ; unpacked_array[-2] $end +$var logic 8 < unpacked_array[-1] $end +$var logic 8 = unpacked_array[0] $end $var bit 1 > LONGSTART_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_LONGEND $end $scope module unnamedblk1 $end $var integer 32 ? b $end @@ -66,8 +66,8 @@ $var bit 1 D global_bit $end $upscope $end $upscope $end $enddefinitions $end -$dumpvars #0 +$dumpvars 1D b00000000000000000000000000000100 C b00000000000000000000000000000011 B @@ -104,6 +104,7 @@ b0000 $ b00 # b00000000000000000000000000000000 " 0! +$end #10 1! b00000000000000000000000000000001 " diff --git a/test_regress/t/t_trace_complex_old_api.pl b/test_regress/t/t_trace_complex_old_api.pl index bdf9a0dec..4150e31af 100755 --- a/test_regress/t/t_trace_complex_old_api.pl +++ b/test_regress/t/t_trace_complex_old_api.pl @@ -28,10 +28,10 @@ file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_strp_strp /); file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arrp /); file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arrp_arrp /); file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arrp_strp /); -file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru\(/); -file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru_arru\(/); -file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru_arrp\(/); -file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru_strp\(/); +file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru\[/); +file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru_arru\[/); +file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru_arrp\[/); +file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru_strp\[/); vcd_identical("$Self->{obj_dir}/simx.vcd", $Self->{golden_filename}); diff --git a/test_regress/t/t_trace_complex_params.out b/test_regress/t/t_trace_complex_params.out index 8d2cd1890..4fd754aed 100644 --- a/test_regress/t/t_trace_complex_params.out +++ b/test_regress/t/t_trace_complex_params.out @@ -1,7 +1,6 @@ $version Generated by VerilatedVcd $end -$date Mon Nov 16 17:51:08 2020 - $end -$timescale 1ps $end +$date Wed Aug 11 12:41:11 2021 $end +$timescale 1ps $end $scope module top $end $var wire 1 = clk $end @@ -12,24 +11,24 @@ $timescale 1ps $end $var wire 1 G LONGSTART_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_LONGEND $end $var wire 1 = clk $end $var wire 32 $ cyc [31:0] $end - $var wire 8 E unpacked_array(-1) [7:0] $end - $var wire 8 D unpacked_array(-2) [7:0] $end - $var wire 8 F unpacked_array(0) [7:0] $end - $var real 64 1 v_arr_real(0) $end - $var real 64 3 v_arr_real(1) $end + $var wire 8 E unpacked_array[-1] [7:0] $end + $var wire 8 D unpacked_array[-2] [7:0] $end + $var wire 8 F unpacked_array[0] [7:0] $end + $var real 64 1 v_arr_real[0] $end + $var real 64 3 v_arr_real[1] $end $var wire 2 ( v_arrp [2:1] $end $var wire 4 ) v_arrp_arrp [3:0] $end $var wire 4 * v_arrp_strp [3:0] $end - $var wire 1 > v_arru(1) $end - $var wire 1 ? v_arru(2) $end - $var wire 2 + v_arru_arrp(3) [2:1] $end - $var wire 2 , v_arru_arrp(4) [2:1] $end - $var wire 1 @ v_arru_arru(3)(1) $end - $var wire 1 A v_arru_arru(3)(2) $end - $var wire 1 B v_arru_arru(4)(1) $end - $var wire 1 C v_arru_arru(4)(2) $end - $var wire 2 - v_arru_strp(3) [1:0] $end - $var wire 2 . v_arru_strp(4) [1:0] $end + $var wire 1 > v_arru[1] $end + $var wire 1 ? v_arru[2] $end + $var wire 2 + v_arru_arrp[3] [2:1] $end + $var wire 2 , v_arru_arrp[4] [2:1] $end + $var wire 1 @ v_arru_arru[3][1] $end + $var wire 1 A v_arru_arru[3][2] $end + $var wire 1 B v_arru_arru[4][1] $end + $var wire 1 C v_arru_arru[4][2] $end + $var wire 2 - v_arru_strp[3] [1:0] $end + $var wire 2 . v_arru_strp[4] [1:0] $end $var wire 3 9 v_enumb [2:0] $end $var wire 6 : v_enumb2_str [5:0] $end $var wire 32 7 v_enumed [31:0] $end diff --git a/test_regress/t/t_trace_complex_params_fst.out b/test_regress/t/t_trace_complex_params_fst.out index d57c690c6..1825f3bd9 100644 --- a/test_regress/t/t_trace_complex_params_fst.out +++ b/test_regress/t/t_trace_complex_params_fst.out @@ -1,5 +1,5 @@ $date - Mon Nov 16 17:51:08 2020 + Wed Aug 11 12:41:14 2021 $end $version @@ -19,19 +19,19 @@ $var logic 2 % v_unip_strp $end $var logic 2 & v_arrp $end $var logic 4 ' v_arrp_arrp $end $var logic 4 ( v_arrp_strp $end -$var logic 1 ) v_arru(1) $end -$var logic 1 * v_arru(2) $end -$var logic 1 + v_arru_arru(3)(1) $end -$var logic 1 , v_arru_arru(3)(2) $end -$var logic 1 - v_arru_arru(4)(1) $end -$var logic 1 . v_arru_arru(4)(2) $end -$var logic 2 / v_arru_arrp(3) $end -$var logic 2 0 v_arru_arrp(4) $end -$var logic 2 1 v_arru_strp(3) $end -$var logic 2 2 v_arru_strp(4) $end +$var logic 1 ) v_arru[1] $end +$var logic 1 * v_arru[2] $end +$var logic 1 + v_arru_arru[3][1] $end +$var logic 1 , v_arru_arru[3][2] $end +$var logic 1 - v_arru_arru[4][1] $end +$var logic 1 . v_arru_arru[4][2] $end +$var logic 2 / v_arru_arrp[3] $end +$var logic 2 0 v_arru_arrp[4] $end +$var logic 2 1 v_arru_strp[3] $end +$var logic 2 2 v_arru_strp[4] $end $var real 64 3 v_real $end -$var real 64 4 v_arr_real(0) $end -$var real 64 5 v_arr_real(1) $end +$var real 64 4 v_arr_real[0] $end +$var real 64 5 v_arr_real[1] $end $var logic 64 6 v_str32x2 $end $attrbegin misc 07 t.enumed_t 4 ZERO ONE TWO THREE 00000000000000000000000000000000 00000000000000000000000000000001 00000000000000000000000000000010 00000000000000000000000000000011 1 $end $attrbegin misc 07 "" 1 $end @@ -42,9 +42,9 @@ $attrbegin misc 07 t.enumb_t 4 BZERO BONE BTWO BTHREE 000 001 010 011 2 $end $attrbegin misc 07 "" 2 $end $var logic 3 9 v_enumb $end $var logic 6 : v_enumb2_str $end -$var logic 8 ; unpacked_array(-2) $end -$var logic 8 < unpacked_array(-1) $end -$var logic 8 = unpacked_array(0) $end +$var logic 8 ; unpacked_array[-2] $end +$var logic 8 < unpacked_array[-1] $end +$var logic 8 = unpacked_array[0] $end $var bit 1 > LONGSTART_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_LONGEND $end $scope module unnamedblk1 $end $var integer 32 ? b $end @@ -67,8 +67,8 @@ $var bit 1 D global_bit $end $upscope $end $upscope $end $enddefinitions $end -$dumpvars #0 +$dumpvars 1D b00000000000000000000000000000100 C b00000000000000000000000000000011 B @@ -105,6 +105,7 @@ b0000 $ b00 # b00000000000000000000000000000000 " 0! +$end #10 1! b00000000000000000000000000000001 " diff --git a/test_regress/t/t_trace_complex_params_fst_sc.out b/test_regress/t/t_trace_complex_params_fst_sc.out index b5e67d184..fd56fca23 100644 --- a/test_regress/t/t_trace_complex_params_fst_sc.out +++ b/test_regress/t/t_trace_complex_params_fst_sc.out @@ -1,5 +1,5 @@ $date - Mon Apr 19 17:07:10 2021 + Wed Aug 11 12:41:17 2021 $end $version @@ -18,19 +18,19 @@ $var logic 2 % v_unip_strp $end $var logic 2 & v_arrp $end $var logic 4 ' v_arrp_arrp $end $var logic 4 ( v_arrp_strp $end -$var logic 1 ) v_arru(1) $end -$var logic 1 * v_arru(2) $end -$var logic 1 + v_arru_arru(3)(1) $end -$var logic 1 , v_arru_arru(3)(2) $end -$var logic 1 - v_arru_arru(4)(1) $end -$var logic 1 . v_arru_arru(4)(2) $end -$var logic 2 / v_arru_arrp(3) $end -$var logic 2 0 v_arru_arrp(4) $end -$var logic 2 1 v_arru_strp(3) $end -$var logic 2 2 v_arru_strp(4) $end +$var logic 1 ) v_arru[1] $end +$var logic 1 * v_arru[2] $end +$var logic 1 + v_arru_arru[3][1] $end +$var logic 1 , v_arru_arru[3][2] $end +$var logic 1 - v_arru_arru[4][1] $end +$var logic 1 . v_arru_arru[4][2] $end +$var logic 2 / v_arru_arrp[3] $end +$var logic 2 0 v_arru_arrp[4] $end +$var logic 2 1 v_arru_strp[3] $end +$var logic 2 2 v_arru_strp[4] $end $var real 64 3 v_real $end -$var real 64 4 v_arr_real(0) $end -$var real 64 5 v_arr_real(1) $end +$var real 64 4 v_arr_real[0] $end +$var real 64 5 v_arr_real[1] $end $var logic 64 6 v_str32x2 $end $attrbegin misc 07 t.enumed_t 4 ZERO ONE TWO THREE 00000000000000000000000000000000 00000000000000000000000000000001 00000000000000000000000000000010 00000000000000000000000000000011 1 $end $attrbegin misc 07 "" 1 $end @@ -41,9 +41,9 @@ $attrbegin misc 07 t.enumb_t 4 BZERO BONE BTWO BTHREE 000 001 010 011 2 $end $attrbegin misc 07 "" 2 $end $var logic 3 9 v_enumb $end $var logic 6 : v_enumb2_str $end -$var logic 8 ; unpacked_array(-2) $end -$var logic 8 < unpacked_array(-1) $end -$var logic 8 = unpacked_array(0) $end +$var logic 8 ; unpacked_array[-2] $end +$var logic 8 < unpacked_array[-1] $end +$var logic 8 = unpacked_array[0] $end $var bit 1 > LONGSTART_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_LONGEND $end $scope module unnamedblk1 $end $var integer 32 ? b $end @@ -66,8 +66,8 @@ $var bit 1 D global_bit $end $upscope $end $upscope $end $enddefinitions $end -$dumpvars #0 +$dumpvars 1D b00000000000000000000000000000100 C b00000000000000000000000000000011 B @@ -104,6 +104,7 @@ b0000 $ b00 # b00000000000000000000000000000000 " 0! +$end #10 1! b00000000000000000000000000000001 " diff --git a/test_regress/t/t_trace_complex_portable.pl b/test_regress/t/t_trace_complex_portable.pl index ec7e5462e..56c794042 100755 --- a/test_regress/t/t_trace_complex_portable.pl +++ b/test_regress/t/t_trace_complex_portable.pl @@ -28,10 +28,10 @@ file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_strp_strp /); file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arrp /); file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arrp_arrp /); file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arrp_strp /); -file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru\(/); -file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru_arru\(/); -file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru_arrp\(/); -file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru_strp\(/); +file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru\[/); +file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru_arru\[/); +file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru_arrp\[/); +file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru_strp\[/); vcd_identical("$Self->{obj_dir}/simx.vcd", $Self->{golden_filename}); diff --git a/test_regress/t/t_trace_complex_structs.out b/test_regress/t/t_trace_complex_structs.out index 6fc0af14a..c421a6293 100644 --- a/test_regress/t/t_trace_complex_structs.out +++ b/test_regress/t/t_trace_complex_structs.out @@ -1,7 +1,6 @@ $version Generated by VerilatedVcd $end -$date Mon Nov 16 17:51:09 2020 - $end -$timescale 1ps $end +$date Wed Aug 11 12:41:22 2021 $end +$timescale 1ps $end $scope module top $end $var wire 1 I clk $end @@ -12,22 +11,22 @@ $timescale 1ps $end $var wire 1 S LONGSTART_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_LONGEND $end $var wire 1 I clk $end $var wire 32 $ cyc [31:0] $end - $var wire 8 Q unpacked_array(-1) [7:0] $end - $var wire 8 P unpacked_array(-2) [7:0] $end - $var wire 8 R unpacked_array(0) [7:0] $end - $var real 64 < v_arr_real(0) $end - $var real 64 > v_arr_real(1) $end + $var wire 8 Q unpacked_array[-1] [7:0] $end + $var wire 8 P unpacked_array[-2] [7:0] $end + $var wire 8 R unpacked_array[0] [7:0] $end + $var real 64 < v_arr_real[0] $end + $var real 64 > v_arr_real[1] $end $var wire 2 - v_arrp [2:1] $end - $var wire 2 . v_arrp_arrp(3) [2:1] $end - $var wire 2 / v_arrp_arrp(4) [2:1] $end - $var wire 1 J v_arru(1) $end - $var wire 1 K v_arru(2) $end - $var wire 2 4 v_arru_arrp(3) [2:1] $end - $var wire 2 5 v_arru_arrp(4) [2:1] $end - $var wire 1 L v_arru_arru(3)(1) $end - $var wire 1 M v_arru_arru(3)(2) $end - $var wire 1 N v_arru_arru(4)(1) $end - $var wire 1 O v_arru_arru(4)(2) $end + $var wire 2 . v_arrp_arrp[3] [2:1] $end + $var wire 2 / v_arrp_arrp[4] [2:1] $end + $var wire 1 J v_arru[1] $end + $var wire 1 K v_arru[2] $end + $var wire 2 4 v_arru_arrp[3] [2:1] $end + $var wire 2 5 v_arru_arrp[4] [2:1] $end + $var wire 1 L v_arru_arru[3][1] $end + $var wire 1 M v_arru_arru[3][2] $end + $var wire 1 N v_arru_arru[4][1] $end + $var wire 1 O v_arru_arru[4][2] $end $var wire 3 D v_enumb [2:0] $end $var wire 32 B v_enumed [31:0] $end $var wire 32 C v_enumed2 [31:0] $end @@ -38,19 +37,19 @@ $timescale 1ps $end $var wire 32 H a [31:0] $end $upscope $end $upscope $end - $scope struct v_arrp_strp(3) $end + $scope struct v_arrp_strp[3] $end $var wire 1 1 b0 $end $var wire 1 0 b1 $end $upscope $end - $scope struct v_arrp_strp(4) $end + $scope struct v_arrp_strp[4] $end $var wire 1 3 b0 $end $var wire 1 2 b1 $end $upscope $end - $scope struct v_arru_strp(3) $end + $scope struct v_arru_strp[3] $end $var wire 1 7 b0 $end $var wire 1 6 b1 $end $upscope $end - $scope struct v_arru_strp(4) $end + $scope struct v_arru_strp[4] $end $var wire 1 9 b0 $end $var wire 1 8 b1 $end $upscope $end @@ -58,16 +57,12 @@ $timescale 1ps $end $var wire 3 E a [2:0] $end $var wire 3 F b [2:0] $end $upscope $end - $scope struct v_str32x2(0) $end + $scope struct v_str32x2[0] $end $var wire 32 @ data [31:0] $end $upscope $end - $scope struct v_str32x2(1) $end + $scope struct v_str32x2[1] $end $var wire 32 A data [31:0] $end $upscope $end - $scope struct v_strp $end - $var wire 1 & b0 $end - $var wire 1 % b1 $end - $upscope $end $scope struct v_strp_strp $end $scope struct x0 $end $var wire 1 * b0 $end @@ -78,7 +73,11 @@ $timescale 1ps $end $var wire 1 ' b1 $end $upscope $end $upscope $end - $scope struct v_unip_strp $end + $scope struct v_strp $end + $var wire 1 & b0 $end + $var wire 1 % b1 $end + $upscope $end + $scope union v_unip_strp $end $scope struct x0 $end $var wire 1 , b0 $end $var wire 1 + b1 $end diff --git a/test_regress/t/t_trace_complex_structs.pl b/test_regress/t/t_trace_complex_structs.pl index 6234bcdde..dbbf55c04 100755 --- a/test_regress/t/t_trace_complex_structs.pl +++ b/test_regress/t/t_trace_complex_structs.pl @@ -25,10 +25,10 @@ file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_strp_strp /); file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arrp /); file_grep_not ("$Self->{obj_dir}/simx.vcd", qr/ v_arrp_arrp /); file_grep_not ("$Self->{obj_dir}/simx.vcd", qr/ v_arrp_strp /); -file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru\(/); -file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru_arru\(/); -file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru_arrp\(/); -file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru_strp\(/); +file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru\[/); +file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru_arru\[/); +file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru_arrp\[/); +file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru_strp\[/); vcd_identical ("$Self->{obj_dir}/simx.vcd", $Self->{golden_filename}); diff --git a/test_regress/t/t_trace_complex_structs_fst.out b/test_regress/t/t_trace_complex_structs_fst.out index 48f9b2fed..8ba8df6f3 100644 --- a/test_regress/t/t_trace_complex_structs_fst.out +++ b/test_regress/t/t_trace_complex_structs_fst.out @@ -1,5 +1,5 @@ $date - Mon Nov 16 17:51:13 2020 + Wed Aug 11 12:41:25 2021 $end $version @@ -38,39 +38,39 @@ $var logic 1 * b0 $end $upscope $end $upscope $end $var logic 2 + v_arrp $end -$var logic 2 , v_arrp_arrp(3) $end -$var logic 2 - v_arrp_arrp(4) $end -$scope struct v_arrp_strp(3) $end +$var logic 2 , v_arrp_arrp[3] $end +$var logic 2 - v_arrp_arrp[4] $end +$scope struct v_arrp_strp[3] $end $var logic 1 . b1 $end $var logic 1 / b0 $end $upscope $end -$scope struct v_arrp_strp(4) $end +$scope struct v_arrp_strp[4] $end $var logic 1 0 b1 $end $var logic 1 1 b0 $end $upscope $end -$var logic 1 2 v_arru(1) $end -$var logic 1 3 v_arru(2) $end -$var logic 1 4 v_arru_arru(3)(1) $end -$var logic 1 5 v_arru_arru(3)(2) $end -$var logic 1 6 v_arru_arru(4)(1) $end -$var logic 1 7 v_arru_arru(4)(2) $end -$var logic 2 8 v_arru_arrp(3) $end -$var logic 2 9 v_arru_arrp(4) $end -$scope struct v_arru_strp(3) $end +$var logic 1 2 v_arru[1] $end +$var logic 1 3 v_arru[2] $end +$var logic 1 4 v_arru_arru[3][1] $end +$var logic 1 5 v_arru_arru[3][2] $end +$var logic 1 6 v_arru_arru[4][1] $end +$var logic 1 7 v_arru_arru[4][2] $end +$var logic 2 8 v_arru_arrp[3] $end +$var logic 2 9 v_arru_arrp[4] $end +$scope struct v_arru_strp[3] $end $var logic 1 : b1 $end $var logic 1 ; b0 $end $upscope $end -$scope struct v_arru_strp(4) $end +$scope struct v_arru_strp[4] $end $var logic 1 < b1 $end $var logic 1 = b0 $end $upscope $end $var real 64 > v_real $end -$var real 64 ? v_arr_real(0) $end -$var real 64 @ v_arr_real(1) $end -$scope struct v_str32x2(0) $end +$var real 64 ? v_arr_real[0] $end +$var real 64 @ v_arr_real[1] $end +$scope struct v_str32x2[0] $end $var logic 32 A data $end $upscope $end -$scope struct v_str32x2(1) $end +$scope struct v_str32x2[1] $end $var logic 32 B data $end $attrbegin misc 07 t.enumed_t 4 ZERO ONE TWO THREE 00000000000000000000000000000000 00000000000000000000000000000001 00000000000000000000000000000010 00000000000000000000000000000011 1 $end $upscope $end @@ -87,9 +87,9 @@ $var logic 3 F a $end $attrbegin misc 07 "" 2 $end $var logic 3 G b $end $upscope $end -$var logic 8 H unpacked_array(-2) $end -$var logic 8 I unpacked_array(-1) $end -$var logic 8 J unpacked_array(0) $end +$var logic 8 H unpacked_array[-2] $end +$var logic 8 I unpacked_array[-1] $end +$var logic 8 J unpacked_array[0] $end $var bit 1 K LONGSTART_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_LONGEND $end $scope module unnamedblk1 $end $var integer 32 L b $end diff --git a/test_regress/t/t_trace_complex_structs_fst_sc.out b/test_regress/t/t_trace_complex_structs_fst_sc.out index d666dae4d..515dd4625 100644 --- a/test_regress/t/t_trace_complex_structs_fst_sc.out +++ b/test_regress/t/t_trace_complex_structs_fst_sc.out @@ -1,5 +1,5 @@ $date - Thu Apr 1 15:22:14 2021 + Wed Aug 11 12:41:29 2021 $end $version @@ -37,39 +37,39 @@ $var logic 1 * b0 $end $upscope $end $upscope $end $var logic 2 + v_arrp $end -$var logic 2 , v_arrp_arrp(3) $end -$var logic 2 - v_arrp_arrp(4) $end -$scope struct v_arrp_strp(3) $end +$var logic 2 , v_arrp_arrp[3] $end +$var logic 2 - v_arrp_arrp[4] $end +$scope struct v_arrp_strp[3] $end $var logic 1 . b1 $end $var logic 1 / b0 $end $upscope $end -$scope struct v_arrp_strp(4) $end +$scope struct v_arrp_strp[4] $end $var logic 1 0 b1 $end $var logic 1 1 b0 $end $upscope $end -$var logic 1 2 v_arru(1) $end -$var logic 1 3 v_arru(2) $end -$var logic 1 4 v_arru_arru(3)(1) $end -$var logic 1 5 v_arru_arru(3)(2) $end -$var logic 1 6 v_arru_arru(4)(1) $end -$var logic 1 7 v_arru_arru(4)(2) $end -$var logic 2 8 v_arru_arrp(3) $end -$var logic 2 9 v_arru_arrp(4) $end -$scope struct v_arru_strp(3) $end +$var logic 1 2 v_arru[1] $end +$var logic 1 3 v_arru[2] $end +$var logic 1 4 v_arru_arru[3][1] $end +$var logic 1 5 v_arru_arru[3][2] $end +$var logic 1 6 v_arru_arru[4][1] $end +$var logic 1 7 v_arru_arru[4][2] $end +$var logic 2 8 v_arru_arrp[3] $end +$var logic 2 9 v_arru_arrp[4] $end +$scope struct v_arru_strp[3] $end $var logic 1 : b1 $end $var logic 1 ; b0 $end $upscope $end -$scope struct v_arru_strp(4) $end +$scope struct v_arru_strp[4] $end $var logic 1 < b1 $end $var logic 1 = b0 $end $upscope $end $var real 64 > v_real $end -$var real 64 ? v_arr_real(0) $end -$var real 64 @ v_arr_real(1) $end -$scope struct v_str32x2(0) $end +$var real 64 ? v_arr_real[0] $end +$var real 64 @ v_arr_real[1] $end +$scope struct v_str32x2[0] $end $var logic 32 A data $end $upscope $end -$scope struct v_str32x2(1) $end +$scope struct v_str32x2[1] $end $var logic 32 B data $end $attrbegin misc 07 t.enumed_t 4 ZERO ONE TWO THREE 00000000000000000000000000000000 00000000000000000000000000000001 00000000000000000000000000000010 00000000000000000000000000000011 1 $end $upscope $end @@ -86,9 +86,9 @@ $var logic 3 F a $end $attrbegin misc 07 "" 2 $end $var logic 3 G b $end $upscope $end -$var logic 8 H unpacked_array(-2) $end -$var logic 8 I unpacked_array(-1) $end -$var logic 8 J unpacked_array(0) $end +$var logic 8 H unpacked_array[-2] $end +$var logic 8 I unpacked_array[-1] $end +$var logic 8 J unpacked_array[0] $end $var bit 1 K LONGSTART_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_LONGEND $end $scope module unnamedblk1 $end $var integer 32 L b $end diff --git a/test_regress/t/t_trace_complex_threads_1.pl b/test_regress/t/t_trace_complex_threads_1.pl index 3c7085576..9a6e6d621 100755 --- a/test_regress/t/t_trace_complex_threads_1.pl +++ b/test_regress/t/t_trace_complex_threads_1.pl @@ -26,10 +26,10 @@ file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_strp_strp /); file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arrp /); file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arrp_arrp /); file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arrp_strp /); -file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru\(/); -file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru_arru\(/); -file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru_arrp\(/); -file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru_strp\(/); +file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru\[/); +file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru_arru\[/); +file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru_arrp\[/); +file_grep ("$Self->{obj_dir}/simx.vcd", qr/ v_arru_strp\[/); vcd_identical ("$Self->{obj_dir}/simx.vcd", $Self->{golden_filename}); diff --git a/test_regress/t/t_trace_fst.out b/test_regress/t/t_trace_fst.out index dde4f07e0..16e4dc7d7 100644 --- a/test_regress/t/t_trace_fst.out +++ b/test_regress/t/t_trace_fst.out @@ -1,5 +1,5 @@ $date - Sun Apr 19 04:15:36 2020 + Tue Aug 10 15:48:30 2021 $end $version @@ -39,9 +39,9 @@ $var wire 1 ! clk $end $var wire 1 $ rstn $end $var wire 5 " state $end $var logic 5 6 state_w $end -$var logic 5 7 state_array(0) $end -$var logic 5 8 state_array(1) $end -$var logic 5 9 state_array(2) $end +$var logic 5 7 state_array[0] $end +$var logic 5 8 state_array[1] $end +$var logic 5 9 state_array[2] $end $scope module unnamedblk2 $end $var int 32 : i $end $upscope $end @@ -52,43 +52,45 @@ $upscope $end $upscope $end $upscope $end $enddefinitions $end +#0 $dumpvars -0! -b00000 " -b00000000000000000000000000000000 # -0$ -r1.23 % -r4.56 & -b00000000000000000000000000000000 ' -0( -0) -b00000000000000000000000000000000 * -b0000000000000000 + -b0000000000000000000000000000000000000000000000000000000000000000 , -b00000000 - -b00000000000000000000000001111011 . -b00000000000000000000000111001000 / -00 -11 -02 -13 -04 -05 -b00000 6 -b00000 7 -b00000 8 -b00000 9 -b00000000000000000000000000000000 : b00000000000000000000000000000000 ; +b00000000000000000000000000000000 : +b00000 9 +b00000 8 +b00000 7 +b00000 6 +05 +04 +13 +02 +11 +00 +b00000000000000000000000111001000 / +b00000000000000000000000001111011 . +b00000000 - +b0000000000000000000000000000000000000000000000000000000000000000 , +b0000000000000000 + +b00000000000000000000000000000000 * +0) +0( +b00000000000000000000000000000000 ' +r4.56 & +r1.23 % +0$ +b00000000000000000000000000000000 # +b00000 " +0! +$end #10 -b00000000000000000000000000000011 ; -b00001 9 -b00001 8 -b00001 7 -b10100 6 -b00000000000000000000000000000001 # -b00001 " 1! +b00001 " +b00000000000000000000000000000001 # +b10100 6 +b00001 7 +b00001 8 +b00001 9 +b00000000000000000000000000000011 ; #15 0! #20 @@ -145,15 +147,15 @@ b00000000000000000000000000001011 # #120 1! b00000000000000000000000000001100 # -b01010 6 b10100 9 +b01010 6 b00000000000000000000000000000010 : #125 0! #130 1! -b01010 9 b00101 6 +b01010 9 b00000000000000000000000000001101 # b10100 8 #135 @@ -162,18 +164,18 @@ b10100 8 1! b01010 8 b00000000000000000000000000001110 # -b10110 6 b00101 9 -b10100 " +b10110 6 b10100 7 +b10100 " #145 0! #150 1! -b01010 7 b01010 " -b10110 9 +b01010 7 b01011 6 +b10110 9 b00000000000000000000000000001111 # b00101 8 #155 @@ -182,18 +184,18 @@ b00101 8 1! b10110 8 b00000000000000000000000000010000 # -b10001 6 b01011 9 -b00101 " +b10001 6 b00101 7 +b00101 " #165 0! #170 1! -b10110 7 b10110 " -b10001 9 +b10110 7 b11100 6 +b10001 9 b00000000000000000000000000010001 # b01011 8 #175 @@ -202,18 +204,18 @@ b01011 8 1! b10001 8 b00000000000000000000000000010010 # -b01110 6 b11100 9 -b01011 " +b01110 6 b01011 7 +b01011 " #185 0! #190 1! -b10001 7 b10001 " -b01110 9 +b10001 7 b00111 6 +b01110 9 b00000000000000000000000000010011 # b11100 8 #195 @@ -222,18 +224,18 @@ b11100 8 1! b01110 8 b00000000000000000000000000010100 # -b10111 6 b00111 9 -b11100 " +b10111 6 b11100 7 +b11100 " #205 0! #210 1! -b01110 7 b01110 " -b10111 9 +b01110 7 b11111 6 +b10111 9 b00000000000000000000000000010101 # b00111 8 #215 @@ -242,18 +244,18 @@ b00111 8 1! b10111 8 b00000000000000000000000000010110 # -b11011 6 b11111 9 -b00111 " +b11011 6 b00111 7 +b00111 " #225 0! #230 1! -b10111 7 b10111 " -b11011 9 +b10111 7 b11001 6 +b11011 9 b00000000000000000000000000010111 # b11111 8 #235 @@ -262,18 +264,18 @@ b11111 8 1! b11011 8 b00000000000000000000000000011000 # -b11000 6 b11001 9 -b11111 " +b11000 6 b11111 7 +b11111 " #245 0! #250 1! -b11011 7 b11011 " -b11000 9 +b11011 7 b01100 6 +b11000 9 b00000000000000000000000000011001 # b11001 8 #255 @@ -282,18 +284,18 @@ b11001 8 1! b11000 8 b00000000000000000000000000011010 # -b00110 6 b01100 9 -b11001 " +b00110 6 b11001 7 +b11001 " #265 0! #270 1! -b11000 7 b11000 " -b00110 9 +b11000 7 b00011 6 +b00110 9 b00000000000000000000000000011011 # b01100 8 #275 @@ -302,18 +304,18 @@ b01100 8 1! b00110 8 b00000000000000000000000000011100 # -b10101 6 b00011 9 -b01100 " +b10101 6 b01100 7 +b01100 " #285 0! #290 1! -b00110 7 b00110 " -b10101 9 +b00110 7 b11110 6 +b10101 9 b00000000000000000000000000011101 # b00011 8 #295 @@ -322,18 +324,18 @@ b00011 8 1! b10101 8 b00000000000000000000000000011110 # -b01111 6 b11110 9 -b00011 " +b01111 6 b00011 7 +b00011 " #305 0! #310 1! -b10101 7 b10101 " -b01111 9 +b10101 7 b10011 6 +b01111 9 b00000000000000000000000000011111 # b11110 8 #315 @@ -342,18 +344,18 @@ b11110 8 1! b01111 8 b00000000000000000000000000100000 # -b11101 6 b10011 9 -b11110 " +b11101 6 b11110 7 +b11110 " #325 0! #330 1! -b01111 7 b01111 " -b11101 9 +b01111 7 b11010 6 +b11101 9 b00000000000000000000000000100001 # b10011 8 #335 @@ -362,18 +364,18 @@ b10011 8 1! b11101 8 b00000000000000000000000000100010 # -b01101 6 b11010 9 -b10011 " +b01101 6 b10011 7 +b10011 " #345 0! #350 1! -b11101 7 b11101 " -b01101 9 +b11101 7 b10010 6 +b01101 9 b00000000000000000000000000100011 # b11010 8 #355 @@ -382,18 +384,18 @@ b11010 8 1! b01101 8 b00000000000000000000000000100100 # -b01001 6 b10010 9 -b11010 " +b01001 6 b11010 7 +b11010 " #365 0! #370 1! -b01101 7 b01101 " -b01001 9 +b01101 7 b10000 6 +b01001 9 b00000000000000000000000000100101 # b10010 8 #375 @@ -402,18 +404,18 @@ b10010 8 1! b01001 8 b00000000000000000000000000100110 # -b01000 6 b10000 9 -b10010 " +b01000 6 b10010 7 +b10010 " #385 0! #390 1! -b01001 7 b01001 " -b01000 9 +b01001 7 b00100 6 +b01000 9 b00000000000000000000000000100111 # b10000 8 #395 @@ -422,18 +424,18 @@ b10000 8 1! b01000 8 b00000000000000000000000000101000 # -b00010 6 b00100 9 -b10000 " +b00010 6 b10000 7 +b10000 " #405 0! #410 1! -b01000 7 b01000 " -b00010 9 +b01000 7 b00001 6 +b00010 9 b00000000000000000000000000101001 # b00100 8 #415 @@ -442,18 +444,18 @@ b00100 8 1! b00010 8 b00000000000000000000000000101010 # -b10100 6 b00001 9 -b00100 " +b10100 6 b00100 7 +b00100 " #425 0! #430 1! -b00010 7 b00010 " -b10100 9 +b00010 7 b01010 6 +b10100 9 b00000000000000000000000000101011 # b00001 8 #435 @@ -462,18 +464,18 @@ b00001 8 1! b10100 8 b00000000000000000000000000101100 # -b00101 6 b01010 9 -b00001 " +b00101 6 b00001 7 +b00001 " #445 0! #450 1! -b10100 7 b10100 " -b00101 9 +b10100 7 b10110 6 +b00101 9 b00000000000000000000000000101101 # b01010 8 #455 @@ -482,18 +484,18 @@ b01010 8 1! b00101 8 b00000000000000000000000000101110 # -b01011 6 b10110 9 -b01010 " +b01011 6 b01010 7 +b01010 " #465 0! #470 1! -b00101 7 b00101 " -b01011 9 +b00101 7 b10001 6 +b01011 9 b00000000000000000000000000101111 # b10110 8 #475 @@ -502,18 +504,18 @@ b10110 8 1! b01011 8 b00000000000000000000000000110000 # -b11100 6 b10001 9 -b10110 " +b11100 6 b10110 7 +b10110 " #485 0! #490 1! -b01011 7 b01011 " -b11100 9 +b01011 7 b01110 6 +b11100 9 b00000000000000000000000000110001 # b10001 8 #495 @@ -522,18 +524,18 @@ b10001 8 1! b11100 8 b00000000000000000000000000110010 # -b00111 6 b01110 9 -b10001 " +b00111 6 b10001 7 +b10001 " #505 0! #510 1! -b11100 7 b11100 " -b00111 9 +b11100 7 b10111 6 +b00111 9 b00000000000000000000000000110011 # b01110 8 #515 @@ -542,18 +544,18 @@ b01110 8 1! b00111 8 b00000000000000000000000000110100 # -b11111 6 b10111 9 -b01110 " +b11111 6 b01110 7 +b01110 " #525 0! #530 1! -b00111 7 b00111 " -b11111 9 +b00111 7 b11011 6 +b11111 9 b00000000000000000000000000110101 # b10111 8 #535 @@ -562,18 +564,18 @@ b10111 8 1! b11111 8 b00000000000000000000000000110110 # -b11001 6 b11011 9 -b10111 " +b11001 6 b10111 7 +b10111 " #545 0! #550 1! -b11111 7 b11111 " -b11001 9 +b11111 7 b11000 6 +b11001 9 b00000000000000000000000000110111 # b11011 8 #555 @@ -582,18 +584,18 @@ b11011 8 1! b11001 8 b00000000000000000000000000111000 # -b01100 6 b11000 9 -b11011 " +b01100 6 b11011 7 +b11011 " #565 0! #570 1! -b11001 7 b11001 " -b01100 9 +b11001 7 b00110 6 +b01100 9 b00000000000000000000000000111001 # b11000 8 #575 @@ -602,18 +604,18 @@ b11000 8 1! b01100 8 b00000000000000000000000000111010 # -b00011 6 b00110 9 -b11000 " +b00011 6 b11000 7 +b11000 " #585 0! #590 1! -b01100 7 b01100 " -b00011 9 +b01100 7 b10101 6 +b00011 9 b00000000000000000000000000111011 # b00110 8 #595 @@ -622,18 +624,18 @@ b00110 8 1! b00011 8 b00000000000000000000000000111100 # -b11110 6 b10101 9 -b00110 " +b11110 6 b00110 7 +b00110 " #605 0! #610 1! -b00011 7 b00011 " -b11110 9 +b00011 7 b01111 6 +b11110 9 b00000000000000000000000000111101 # b10101 8 #615 @@ -642,18 +644,18 @@ b10101 8 1! b11110 8 b00000000000000000000000000111110 # -b10011 6 b01111 9 -b10101 " +b10011 6 b10101 7 +b10101 " #625 0! #630 1! -b11110 7 b11110 " -b10011 9 +b11110 7 b11101 6 +b10011 9 b00000000000000000000000000111111 # b01111 8 #635 @@ -662,18 +664,18 @@ b01111 8 1! b10011 8 b00000000000000000000000001000000 # -b11010 6 b11101 9 -b01111 " +b11010 6 b01111 7 +b01111 " #645 0! #650 1! -b10011 7 b10011 " -b11010 9 +b10011 7 b01101 6 +b11010 9 b00000000000000000000000001000001 # b11101 8 #655 @@ -682,18 +684,18 @@ b11101 8 1! b11010 8 b00000000000000000000000001000010 # -b10010 6 b01101 9 -b11101 " +b10010 6 b11101 7 +b11101 " #665 0! #670 1! -b11010 7 b11010 " -b10010 9 +b11010 7 b01001 6 +b10010 9 b00000000000000000000000001000011 # b01101 8 #675 @@ -702,18 +704,18 @@ b01101 8 1! b10010 8 b00000000000000000000000001000100 # -b10000 6 b01001 9 -b01101 " +b10000 6 b01101 7 +b01101 " #685 0! #690 1! -b10010 7 b10010 " -b10000 9 +b10010 7 b01000 6 +b10000 9 b00000000000000000000000001000101 # b01001 8 #695 @@ -722,18 +724,18 @@ b01001 8 1! b10000 8 b00000000000000000000000001000110 # -b00100 6 b01000 9 -b01001 " +b00100 6 b01001 7 +b01001 " #705 0! #710 1! -b10000 7 b10000 " -b00100 9 +b10000 7 b00010 6 +b00100 9 b00000000000000000000000001000111 # b01000 8 #715 @@ -742,18 +744,18 @@ b01000 8 1! b00100 8 b00000000000000000000000001001000 # -b00001 6 b00010 9 -b01000 " +b00001 6 b01000 7 +b01000 " #725 0! #730 1! -b00100 7 b00100 " -b00001 9 +b00100 7 b10100 6 +b00001 9 b00000000000000000000000001001001 # b00010 8 #735 @@ -762,18 +764,18 @@ b00010 8 1! b00001 8 b00000000000000000000000001001010 # -b01010 6 b10100 9 -b00010 " +b01010 6 b00010 7 +b00010 " #745 0! #750 1! -b00001 7 b00001 " -b01010 9 +b00001 7 b00101 6 +b01010 9 b00000000000000000000000001001011 # b10100 8 #755 @@ -782,18 +784,18 @@ b10100 8 1! b01010 8 b00000000000000000000000001001100 # -b10110 6 b00101 9 -b10100 " +b10110 6 b10100 7 +b10100 " #765 0! #770 1! -b01010 7 b01010 " -b10110 9 +b01010 7 b01011 6 +b10110 9 b00000000000000000000000001001101 # b00101 8 #775 @@ -802,18 +804,18 @@ b00101 8 1! b10110 8 b00000000000000000000000001001110 # -b10001 6 b01011 9 -b00101 " +b10001 6 b00101 7 +b00101 " #785 0! #790 1! -b10110 7 b10110 " -b10001 9 +b10110 7 b11100 6 +b10001 9 b00000000000000000000000001001111 # b01011 8 #795 @@ -822,18 +824,18 @@ b01011 8 1! b10001 8 b00000000000000000000000001010000 # -b01110 6 b11100 9 -b01011 " +b01110 6 b01011 7 +b01011 " #805 0! #810 1! -b10001 7 b10001 " -b01110 9 +b10001 7 b00111 6 +b01110 9 b00000000000000000000000001010001 # b11100 8 #815 @@ -842,18 +844,18 @@ b11100 8 1! b01110 8 b00000000000000000000000001010010 # -b10111 6 b00111 9 -b11100 " +b10111 6 b11100 7 +b11100 " #825 0! #830 1! -b01110 7 b01110 " -b10111 9 +b01110 7 b11111 6 +b10111 9 b00000000000000000000000001010011 # b00111 8 #835 @@ -862,18 +864,18 @@ b00111 8 1! b10111 8 b00000000000000000000000001010100 # -b11011 6 b11111 9 -b00111 " +b11011 6 b00111 7 +b00111 " #845 0! #850 1! -b10111 7 b10111 " -b11011 9 +b10111 7 b11001 6 +b11011 9 b00000000000000000000000001010101 # b11111 8 #855 @@ -882,18 +884,18 @@ b11111 8 1! b11011 8 b00000000000000000000000001010110 # -b11000 6 b11001 9 -b11111 " +b11000 6 b11111 7 +b11111 " #865 0! #870 1! -b11011 7 b11011 " -b11000 9 +b11011 7 b01100 6 +b11000 9 b00000000000000000000000001010111 # b11001 8 #875 @@ -902,18 +904,18 @@ b11001 8 1! b11000 8 b00000000000000000000000001011000 # -b00110 6 b01100 9 -b11001 " +b00110 6 b11001 7 +b11001 " #885 0! #890 1! -b11000 7 b11000 " -b00110 9 +b11000 7 b00011 6 +b00110 9 b00000000000000000000000001011001 # b01100 8 #895 @@ -922,18 +924,18 @@ b01100 8 1! b00110 8 b00000000000000000000000001011010 # -b10101 6 b00011 9 -b01100 " +b10101 6 b01100 7 +b01100 " #905 0! #910 1! -b00110 7 b00110 " -b10101 9 +b00110 7 b11110 6 +b10101 9 b00000000000000000000000001011011 # b00011 8 #915 @@ -942,18 +944,18 @@ b00011 8 1! b10101 8 b00000000000000000000000001011100 # -b01111 6 b11110 9 -b00011 " +b01111 6 b00011 7 +b00011 " #925 0! #930 1! -b10101 7 b10101 " -b01111 9 +b10101 7 b10011 6 +b01111 9 b00000000000000000000000001011101 # b11110 8 #935 @@ -962,18 +964,18 @@ b11110 8 1! b01111 8 b00000000000000000000000001011110 # -b11101 6 b10011 9 -b11110 " +b11101 6 b11110 7 +b11110 " #945 0! #950 1! -b01111 7 b01111 " -b11101 9 +b01111 7 b11010 6 +b11101 9 b00000000000000000000000001011111 # b10011 8 #955 @@ -982,18 +984,18 @@ b10011 8 1! b11101 8 b00000000000000000000000001100000 # -b01101 6 b11010 9 -b10011 " +b01101 6 b10011 7 +b10011 " #965 0! #970 1! -b11101 7 b11101 " -b01101 9 +b11101 7 b10010 6 +b01101 9 b00000000000000000000000001100001 # b11010 8 #975 @@ -1002,18 +1004,18 @@ b11010 8 1! b01101 8 b00000000000000000000000001100010 # -b01001 6 b10010 9 -b11010 " +b01001 6 b11010 7 +b11010 " #985 0! #990 1! -b01101 7 b01101 " -b01001 9 +b01101 7 b10000 6 +b01001 9 b00000000000000000000000001100011 # b10010 8 #995 @@ -1022,7 +1024,7 @@ b10010 8 1! b01001 8 b00000000000000000000000001100100 # -b01000 6 b10000 9 -b10010 " +b01000 6 b10010 7 +b10010 " diff --git a/test_regress/t/t_trace_fst_cmake.out b/test_regress/t/t_trace_fst_cmake.out index dde4f07e0..d3e911409 100644 --- a/test_regress/t/t_trace_fst_cmake.out +++ b/test_regress/t/t_trace_fst_cmake.out @@ -1,5 +1,5 @@ $date - Sun Apr 19 04:15:36 2020 + Tue Aug 10 15:48:40 2021 $end $version @@ -39,9 +39,9 @@ $var wire 1 ! clk $end $var wire 1 $ rstn $end $var wire 5 " state $end $var logic 5 6 state_w $end -$var logic 5 7 state_array(0) $end -$var logic 5 8 state_array(1) $end -$var logic 5 9 state_array(2) $end +$var logic 5 7 state_array[0] $end +$var logic 5 8 state_array[1] $end +$var logic 5 9 state_array[2] $end $scope module unnamedblk2 $end $var int 32 : i $end $upscope $end @@ -52,43 +52,45 @@ $upscope $end $upscope $end $upscope $end $enddefinitions $end +#0 $dumpvars -0! -b00000 " -b00000000000000000000000000000000 # -0$ -r1.23 % -r4.56 & -b00000000000000000000000000000000 ' -0( -0) -b00000000000000000000000000000000 * -b0000000000000000 + -b0000000000000000000000000000000000000000000000000000000000000000 , -b00000000 - -b00000000000000000000000001111011 . -b00000000000000000000000111001000 / -00 -11 -02 -13 -04 -05 -b00000 6 -b00000 7 -b00000 8 -b00000 9 -b00000000000000000000000000000000 : b00000000000000000000000000000000 ; +b00000000000000000000000000000000 : +b00000 9 +b00000 8 +b00000 7 +b00000 6 +05 +04 +13 +02 +11 +00 +b00000000000000000000000111001000 / +b00000000000000000000000001111011 . +b00000000 - +b0000000000000000000000000000000000000000000000000000000000000000 , +b0000000000000000 + +b00000000000000000000000000000000 * +0) +0( +b00000000000000000000000000000000 ' +r4.56 & +r1.23 % +0$ +b00000000000000000000000000000000 # +b00000 " +0! +$end #10 -b00000000000000000000000000000011 ; -b00001 9 -b00001 8 -b00001 7 -b10100 6 -b00000000000000000000000000000001 # -b00001 " 1! +b00001 " +b00000000000000000000000000000001 # +b10100 6 +b00001 7 +b00001 8 +b00001 9 +b00000000000000000000000000000011 ; #15 0! #20 @@ -145,15 +147,15 @@ b00000000000000000000000000001011 # #120 1! b00000000000000000000000000001100 # -b01010 6 b10100 9 +b01010 6 b00000000000000000000000000000010 : #125 0! #130 1! -b01010 9 b00101 6 +b01010 9 b00000000000000000000000000001101 # b10100 8 #135 @@ -162,18 +164,18 @@ b10100 8 1! b01010 8 b00000000000000000000000000001110 # -b10110 6 b00101 9 -b10100 " +b10110 6 b10100 7 +b10100 " #145 0! #150 1! -b01010 7 b01010 " -b10110 9 +b01010 7 b01011 6 +b10110 9 b00000000000000000000000000001111 # b00101 8 #155 @@ -182,18 +184,18 @@ b00101 8 1! b10110 8 b00000000000000000000000000010000 # -b10001 6 b01011 9 -b00101 " +b10001 6 b00101 7 +b00101 " #165 0! #170 1! -b10110 7 b10110 " -b10001 9 +b10110 7 b11100 6 +b10001 9 b00000000000000000000000000010001 # b01011 8 #175 @@ -202,18 +204,18 @@ b01011 8 1! b10001 8 b00000000000000000000000000010010 # -b01110 6 b11100 9 -b01011 " +b01110 6 b01011 7 +b01011 " #185 0! #190 1! -b10001 7 b10001 " -b01110 9 +b10001 7 b00111 6 +b01110 9 b00000000000000000000000000010011 # b11100 8 #195 @@ -222,18 +224,18 @@ b11100 8 1! b01110 8 b00000000000000000000000000010100 # -b10111 6 b00111 9 -b11100 " +b10111 6 b11100 7 +b11100 " #205 0! #210 1! -b01110 7 b01110 " -b10111 9 +b01110 7 b11111 6 +b10111 9 b00000000000000000000000000010101 # b00111 8 #215 @@ -242,18 +244,18 @@ b00111 8 1! b10111 8 b00000000000000000000000000010110 # -b11011 6 b11111 9 -b00111 " +b11011 6 b00111 7 +b00111 " #225 0! #230 1! -b10111 7 b10111 " -b11011 9 +b10111 7 b11001 6 +b11011 9 b00000000000000000000000000010111 # b11111 8 #235 @@ -262,18 +264,18 @@ b11111 8 1! b11011 8 b00000000000000000000000000011000 # -b11000 6 b11001 9 -b11111 " +b11000 6 b11111 7 +b11111 " #245 0! #250 1! -b11011 7 b11011 " -b11000 9 +b11011 7 b01100 6 +b11000 9 b00000000000000000000000000011001 # b11001 8 #255 @@ -282,18 +284,18 @@ b11001 8 1! b11000 8 b00000000000000000000000000011010 # -b00110 6 b01100 9 -b11001 " +b00110 6 b11001 7 +b11001 " #265 0! #270 1! -b11000 7 b11000 " -b00110 9 +b11000 7 b00011 6 +b00110 9 b00000000000000000000000000011011 # b01100 8 #275 @@ -302,18 +304,18 @@ b01100 8 1! b00110 8 b00000000000000000000000000011100 # -b10101 6 b00011 9 -b01100 " +b10101 6 b01100 7 +b01100 " #285 0! #290 1! -b00110 7 b00110 " -b10101 9 +b00110 7 b11110 6 +b10101 9 b00000000000000000000000000011101 # b00011 8 #295 @@ -322,18 +324,18 @@ b00011 8 1! b10101 8 b00000000000000000000000000011110 # -b01111 6 b11110 9 -b00011 " +b01111 6 b00011 7 +b00011 " #305 0! #310 1! -b10101 7 b10101 " -b01111 9 +b10101 7 b10011 6 +b01111 9 b00000000000000000000000000011111 # b11110 8 #315 @@ -342,18 +344,18 @@ b11110 8 1! b01111 8 b00000000000000000000000000100000 # -b11101 6 b10011 9 -b11110 " +b11101 6 b11110 7 +b11110 " #325 0! #330 1! -b01111 7 b01111 " -b11101 9 +b01111 7 b11010 6 +b11101 9 b00000000000000000000000000100001 # b10011 8 #335 @@ -362,18 +364,18 @@ b10011 8 1! b11101 8 b00000000000000000000000000100010 # -b01101 6 b11010 9 -b10011 " +b01101 6 b10011 7 +b10011 " #345 0! #350 1! -b11101 7 b11101 " -b01101 9 +b11101 7 b10010 6 +b01101 9 b00000000000000000000000000100011 # b11010 8 #355 @@ -382,18 +384,18 @@ b11010 8 1! b01101 8 b00000000000000000000000000100100 # -b01001 6 b10010 9 -b11010 " +b01001 6 b11010 7 +b11010 " #365 0! #370 1! -b01101 7 b01101 " -b01001 9 +b01101 7 b10000 6 +b01001 9 b00000000000000000000000000100101 # b10010 8 #375 @@ -402,18 +404,18 @@ b10010 8 1! b01001 8 b00000000000000000000000000100110 # -b01000 6 b10000 9 -b10010 " +b01000 6 b10010 7 +b10010 " #385 0! #390 1! -b01001 7 b01001 " -b01000 9 +b01001 7 b00100 6 +b01000 9 b00000000000000000000000000100111 # b10000 8 #395 @@ -422,18 +424,18 @@ b10000 8 1! b01000 8 b00000000000000000000000000101000 # -b00010 6 b00100 9 -b10000 " +b00010 6 b10000 7 +b10000 " #405 0! #410 1! -b01000 7 b01000 " -b00010 9 +b01000 7 b00001 6 +b00010 9 b00000000000000000000000000101001 # b00100 8 #415 @@ -442,18 +444,18 @@ b00100 8 1! b00010 8 b00000000000000000000000000101010 # -b10100 6 b00001 9 -b00100 " +b10100 6 b00100 7 +b00100 " #425 0! #430 1! -b00010 7 b00010 " -b10100 9 +b00010 7 b01010 6 +b10100 9 b00000000000000000000000000101011 # b00001 8 #435 @@ -462,18 +464,18 @@ b00001 8 1! b10100 8 b00000000000000000000000000101100 # -b00101 6 b01010 9 -b00001 " +b00101 6 b00001 7 +b00001 " #445 0! #450 1! -b10100 7 b10100 " -b00101 9 +b10100 7 b10110 6 +b00101 9 b00000000000000000000000000101101 # b01010 8 #455 @@ -482,18 +484,18 @@ b01010 8 1! b00101 8 b00000000000000000000000000101110 # -b01011 6 b10110 9 -b01010 " +b01011 6 b01010 7 +b01010 " #465 0! #470 1! -b00101 7 b00101 " -b01011 9 +b00101 7 b10001 6 +b01011 9 b00000000000000000000000000101111 # b10110 8 #475 @@ -502,18 +504,18 @@ b10110 8 1! b01011 8 b00000000000000000000000000110000 # -b11100 6 b10001 9 -b10110 " +b11100 6 b10110 7 +b10110 " #485 0! #490 1! -b01011 7 b01011 " -b11100 9 +b01011 7 b01110 6 +b11100 9 b00000000000000000000000000110001 # b10001 8 #495 @@ -522,18 +524,18 @@ b10001 8 1! b11100 8 b00000000000000000000000000110010 # -b00111 6 b01110 9 -b10001 " +b00111 6 b10001 7 +b10001 " #505 0! #510 1! -b11100 7 b11100 " -b00111 9 +b11100 7 b10111 6 +b00111 9 b00000000000000000000000000110011 # b01110 8 #515 @@ -542,18 +544,18 @@ b01110 8 1! b00111 8 b00000000000000000000000000110100 # -b11111 6 b10111 9 -b01110 " +b11111 6 b01110 7 +b01110 " #525 0! #530 1! -b00111 7 b00111 " -b11111 9 +b00111 7 b11011 6 +b11111 9 b00000000000000000000000000110101 # b10111 8 #535 @@ -562,18 +564,18 @@ b10111 8 1! b11111 8 b00000000000000000000000000110110 # -b11001 6 b11011 9 -b10111 " +b11001 6 b10111 7 +b10111 " #545 0! #550 1! -b11111 7 b11111 " -b11001 9 +b11111 7 b11000 6 +b11001 9 b00000000000000000000000000110111 # b11011 8 #555 @@ -582,18 +584,18 @@ b11011 8 1! b11001 8 b00000000000000000000000000111000 # -b01100 6 b11000 9 -b11011 " +b01100 6 b11011 7 +b11011 " #565 0! #570 1! -b11001 7 b11001 " -b01100 9 +b11001 7 b00110 6 +b01100 9 b00000000000000000000000000111001 # b11000 8 #575 @@ -602,18 +604,18 @@ b11000 8 1! b01100 8 b00000000000000000000000000111010 # -b00011 6 b00110 9 -b11000 " +b00011 6 b11000 7 +b11000 " #585 0! #590 1! -b01100 7 b01100 " -b00011 9 +b01100 7 b10101 6 +b00011 9 b00000000000000000000000000111011 # b00110 8 #595 @@ -622,18 +624,18 @@ b00110 8 1! b00011 8 b00000000000000000000000000111100 # -b11110 6 b10101 9 -b00110 " +b11110 6 b00110 7 +b00110 " #605 0! #610 1! -b00011 7 b00011 " -b11110 9 +b00011 7 b01111 6 +b11110 9 b00000000000000000000000000111101 # b10101 8 #615 @@ -642,18 +644,18 @@ b10101 8 1! b11110 8 b00000000000000000000000000111110 # -b10011 6 b01111 9 -b10101 " +b10011 6 b10101 7 +b10101 " #625 0! #630 1! -b11110 7 b11110 " -b10011 9 +b11110 7 b11101 6 +b10011 9 b00000000000000000000000000111111 # b01111 8 #635 @@ -662,18 +664,18 @@ b01111 8 1! b10011 8 b00000000000000000000000001000000 # -b11010 6 b11101 9 -b01111 " +b11010 6 b01111 7 +b01111 " #645 0! #650 1! -b10011 7 b10011 " -b11010 9 +b10011 7 b01101 6 +b11010 9 b00000000000000000000000001000001 # b11101 8 #655 @@ -682,18 +684,18 @@ b11101 8 1! b11010 8 b00000000000000000000000001000010 # -b10010 6 b01101 9 -b11101 " +b10010 6 b11101 7 +b11101 " #665 0! #670 1! -b11010 7 b11010 " -b10010 9 +b11010 7 b01001 6 +b10010 9 b00000000000000000000000001000011 # b01101 8 #675 @@ -702,18 +704,18 @@ b01101 8 1! b10010 8 b00000000000000000000000001000100 # -b10000 6 b01001 9 -b01101 " +b10000 6 b01101 7 +b01101 " #685 0! #690 1! -b10010 7 b10010 " -b10000 9 +b10010 7 b01000 6 +b10000 9 b00000000000000000000000001000101 # b01001 8 #695 @@ -722,18 +724,18 @@ b01001 8 1! b10000 8 b00000000000000000000000001000110 # -b00100 6 b01000 9 -b01001 " +b00100 6 b01001 7 +b01001 " #705 0! #710 1! -b10000 7 b10000 " -b00100 9 +b10000 7 b00010 6 +b00100 9 b00000000000000000000000001000111 # b01000 8 #715 @@ -742,18 +744,18 @@ b01000 8 1! b00100 8 b00000000000000000000000001001000 # -b00001 6 b00010 9 -b01000 " +b00001 6 b01000 7 +b01000 " #725 0! #730 1! -b00100 7 b00100 " -b00001 9 +b00100 7 b10100 6 +b00001 9 b00000000000000000000000001001001 # b00010 8 #735 @@ -762,18 +764,18 @@ b00010 8 1! b00001 8 b00000000000000000000000001001010 # -b01010 6 b10100 9 -b00010 " +b01010 6 b00010 7 +b00010 " #745 0! #750 1! -b00001 7 b00001 " -b01010 9 +b00001 7 b00101 6 +b01010 9 b00000000000000000000000001001011 # b10100 8 #755 @@ -782,18 +784,18 @@ b10100 8 1! b01010 8 b00000000000000000000000001001100 # -b10110 6 b00101 9 -b10100 " +b10110 6 b10100 7 +b10100 " #765 0! #770 1! -b01010 7 b01010 " -b10110 9 +b01010 7 b01011 6 +b10110 9 b00000000000000000000000001001101 # b00101 8 #775 @@ -802,18 +804,18 @@ b00101 8 1! b10110 8 b00000000000000000000000001001110 # -b10001 6 b01011 9 -b00101 " +b10001 6 b00101 7 +b00101 " #785 0! #790 1! -b10110 7 b10110 " -b10001 9 +b10110 7 b11100 6 +b10001 9 b00000000000000000000000001001111 # b01011 8 #795 @@ -822,18 +824,18 @@ b01011 8 1! b10001 8 b00000000000000000000000001010000 # -b01110 6 b11100 9 -b01011 " +b01110 6 b01011 7 +b01011 " #805 0! #810 1! -b10001 7 b10001 " -b01110 9 +b10001 7 b00111 6 +b01110 9 b00000000000000000000000001010001 # b11100 8 #815 @@ -842,18 +844,18 @@ b11100 8 1! b01110 8 b00000000000000000000000001010010 # -b10111 6 b00111 9 -b11100 " +b10111 6 b11100 7 +b11100 " #825 0! #830 1! -b01110 7 b01110 " -b10111 9 +b01110 7 b11111 6 +b10111 9 b00000000000000000000000001010011 # b00111 8 #835 @@ -862,18 +864,18 @@ b00111 8 1! b10111 8 b00000000000000000000000001010100 # -b11011 6 b11111 9 -b00111 " +b11011 6 b00111 7 +b00111 " #845 0! #850 1! -b10111 7 b10111 " -b11011 9 +b10111 7 b11001 6 +b11011 9 b00000000000000000000000001010101 # b11111 8 #855 @@ -882,18 +884,18 @@ b11111 8 1! b11011 8 b00000000000000000000000001010110 # -b11000 6 b11001 9 -b11111 " +b11000 6 b11111 7 +b11111 " #865 0! #870 1! -b11011 7 b11011 " -b11000 9 +b11011 7 b01100 6 +b11000 9 b00000000000000000000000001010111 # b11001 8 #875 @@ -902,18 +904,18 @@ b11001 8 1! b11000 8 b00000000000000000000000001011000 # -b00110 6 b01100 9 -b11001 " +b00110 6 b11001 7 +b11001 " #885 0! #890 1! -b11000 7 b11000 " -b00110 9 +b11000 7 b00011 6 +b00110 9 b00000000000000000000000001011001 # b01100 8 #895 @@ -922,18 +924,18 @@ b01100 8 1! b00110 8 b00000000000000000000000001011010 # -b10101 6 b00011 9 -b01100 " +b10101 6 b01100 7 +b01100 " #905 0! #910 1! -b00110 7 b00110 " -b10101 9 +b00110 7 b11110 6 +b10101 9 b00000000000000000000000001011011 # b00011 8 #915 @@ -942,18 +944,18 @@ b00011 8 1! b10101 8 b00000000000000000000000001011100 # -b01111 6 b11110 9 -b00011 " +b01111 6 b00011 7 +b00011 " #925 0! #930 1! -b10101 7 b10101 " -b01111 9 +b10101 7 b10011 6 +b01111 9 b00000000000000000000000001011101 # b11110 8 #935 @@ -962,18 +964,18 @@ b11110 8 1! b01111 8 b00000000000000000000000001011110 # -b11101 6 b10011 9 -b11110 " +b11101 6 b11110 7 +b11110 " #945 0! #950 1! -b01111 7 b01111 " -b11101 9 +b01111 7 b11010 6 +b11101 9 b00000000000000000000000001011111 # b10011 8 #955 @@ -982,18 +984,18 @@ b10011 8 1! b11101 8 b00000000000000000000000001100000 # -b01101 6 b11010 9 -b10011 " +b01101 6 b10011 7 +b10011 " #965 0! #970 1! -b11101 7 b11101 " -b01101 9 +b11101 7 b10010 6 +b01101 9 b00000000000000000000000001100001 # b11010 8 #975 @@ -1002,18 +1004,18 @@ b11010 8 1! b01101 8 b00000000000000000000000001100010 # -b01001 6 b10010 9 -b11010 " +b01001 6 b11010 7 +b11010 " #985 0! #990 1! -b01101 7 b01101 " -b01001 9 +b01101 7 b10000 6 +b01001 9 b00000000000000000000000001100011 # b10010 8 #995 @@ -1022,7 +1024,7 @@ b10010 8 1! b01001 8 b00000000000000000000000001100100 # -b01000 6 b10000 9 -b10010 " +b01000 6 b10010 7 +b10010 " diff --git a/test_regress/t/t_trace_fst_sc.out b/test_regress/t/t_trace_fst_sc.out index 3fe026e70..28581fdda 100644 --- a/test_regress/t/t_trace_fst_sc.out +++ b/test_regress/t/t_trace_fst_sc.out @@ -1,5 +1,5 @@ $date - Thu Apr 1 14:28:55 2021 + Wed Aug 11 00:05:19 2021 $end $version @@ -37,9 +37,9 @@ $var wire 1 ! clk $end $var wire 1 # rstn $end $var wire 5 5 state $end $var logic 5 6 state_w $end -$var logic 5 7 state_array(0) $end -$var logic 5 8 state_array(1) $end -$var logic 5 9 state_array(2) $end +$var logic 5 7 state_array[0] $end +$var logic 5 8 state_array[1] $end +$var logic 5 9 state_array[2] $end $scope module unnamedblk2 $end $var int 32 : i $end $upscope $end diff --git a/test_regress/t/t_trace_fst_sc_cmake.out b/test_regress/t/t_trace_fst_sc_cmake.out index 3fe026e70..79ed28e84 100644 --- a/test_regress/t/t_trace_fst_sc_cmake.out +++ b/test_regress/t/t_trace_fst_sc_cmake.out @@ -1,5 +1,5 @@ $date - Thu Apr 1 14:28:55 2021 + Wed Aug 11 02:14:06 2021 $end $version @@ -37,9 +37,9 @@ $var wire 1 ! clk $end $var wire 1 # rstn $end $var wire 5 5 state $end $var logic 5 6 state_w $end -$var logic 5 7 state_array(0) $end -$var logic 5 8 state_array(1) $end -$var logic 5 9 state_array(2) $end +$var logic 5 7 state_array[0] $end +$var logic 5 8 state_array[1] $end +$var logic 5 9 state_array[2] $end $scope module unnamedblk2 $end $var int 32 : i $end $upscope $end diff --git a/test_regress/t/t_trace_packed_struct_fst.out b/test_regress/t/t_trace_packed_struct_fst.out index ac5cfac00..05ed7013f 100644 --- a/test_regress/t/t_trace_packed_struct_fst.out +++ b/test_regress/t/t_trace_packed_struct_fst.out @@ -1,5 +1,5 @@ $date - Sun Apr 19 04:15:38 2020 + Wed Aug 11 12:42:37 2021 $end $version @@ -13,21 +13,23 @@ $var wire 1 ! clk $end $scope module t $end $var wire 1 ! clk $end $var int 32 " cnt $end -$var parameter 96 # v(0) $end -$var parameter 96 $ v(1) $end -$var parameter 96 % v(2) $end +$var parameter 96 # v[0] $end +$var parameter 96 $ v[1] $end +$var parameter 96 % v[2] $end $upscope $end $upscope $end $enddefinitions $end +#0 $dumpvars -0! -b00000000000000000000000000000000 " -b001100000000000000000000000000100011000000000000000000000000000100110000000000000000000000000000 # -b001000000000000000000000000000100010000000000000000000000000000100100000000000000000000000000000 $ b000100000000000000000000000000100001000000000000000000000000000100010000000000000000000000000000 % +b001000000000000000000000000000100010000000000000000000000000000100100000000000000000000000000000 $ +b001100000000000000000000000000100011000000000000000000000000000100110000000000000000000000000000 # +b00000000000000000000000000000000 " +0! +$end #10 -b00000000000000000000000000000001 " 1! +b00000000000000000000000000000001 " #15 0! #20 diff --git a/test_regress/t/t_trace_packed_struct_fst_sc.out b/test_regress/t/t_trace_packed_struct_fst_sc.out index 7c9ef71f8..c42a1574d 100644 --- a/test_regress/t/t_trace_packed_struct_fst_sc.out +++ b/test_regress/t/t_trace_packed_struct_fst_sc.out @@ -1,5 +1,5 @@ $date - Thu Apr 1 15:33:45 2021 + Wed Aug 11 12:42:40 2021 $end $version @@ -12,9 +12,9 @@ $scope module top $end $scope module t $end $var wire 1 ! clk $end $var int 32 " cnt $end -$var parameter 96 # v(0) $end -$var parameter 96 $ v(1) $end -$var parameter 96 % v(2) $end +$var parameter 96 # v[0] $end +$var parameter 96 $ v[1] $end +$var parameter 96 % v[2] $end $upscope $end $upscope $end $enddefinitions $end diff --git a/test_regress/t/t_trace_public.out b/test_regress/t/t_trace_public.out index 01536b3b7..e9ef906b0 100644 --- a/test_regress/t/t_trace_public.out +++ b/test_regress/t/t_trace_public.out @@ -1,31 +1,30 @@ -$version Generated by SpTraceVcd $end -$date Tue Nov 3 09:34:23 2009 - $end +$version Generated by VerilatedVcd $end +$date Tue Aug 10 15:49:51 2021 $end $timescale 1ps $end $scope module top $end - $var wire 1 6 CLK $end - $var wire 1 7 RESET $end + $var wire 1 5 CLK $end + $var wire 1 6 RESET $end $scope module t $end - $var wire 1 6 CLK $end + $var wire 1 5 CLK $end $var wire 1 # RESET $end - $var wire 32 $ val [31:0] $end - $var wire 2 3 vec(3) [2:1] $end - $var wire 2 4 vec(4) [2:1] $end + $var wire 32 & val [31:0] $end + $var wire 2 $ vec[3] [2:1] $end + $var wire 2 % vec[4] [2:1] $end $scope module glbl $end - $var wire 1 5 GSR $end + $var wire 1 7 GSR $end $upscope $end $scope module little $end - $var wire 1 6 clk $end - $var wire 128 / i128 [63:190] $end - $var wire 49 - i48 [1:49] $end - $var wire 8 , i8 [0:7] $end + $var wire 1 5 clk $end + $var wire 128 1 i128 [63:190] $end + $var wire 49 / i48 [1:49] $end + $var wire 8 . i8 [0:7] $end $upscope $end $scope module neg $end - $var wire 1 6 clk $end - $var wire 128 ( i128 [63:-64] $end - $var wire 48 & i48 [-1:-48] $end - $var wire 8 % i8 [0:-7] $end + $var wire 1 5 clk $end + $var wire 128 * i128 [63:-64] $end + $var wire 48 ( i48 [-1:-48] $end + $var wire 8 ' i8 [0:-7] $end $upscope $end $upscope $end $upscope $end @@ -34,63 +33,63 @@ $enddefinitions $end #0 1# -b00000000000000000000000000000000 $ -b00000000 % -b000000000000000000000000000000000000000000000000 & -b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 ( -b00000000 , -b0000000000000000000000000000000000000000000000000 - -b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 / -b00 3 -b00 4 -15 +b00 $ +b00 % +b00000000000000000000000000000000 & +b00000000 ' +b000000000000000000000000000000000000000000000000 ( +b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 * +b00000000 . +b0000000000000000000000000000000000000000000000000 / +b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 1 +05 +16 17 -06 #1 #2 #3 -b11111111 % -b111111111111111111111111111111111111111111111111 & -b11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 ( -b11111111 , -b1111111111111111111111111111111111111111111111111 - -b11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 / -16 +b11111111 ' +b111111111111111111111111111111111111111111111111 ( +b11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 * +b11111111 . +b1111111111111111111111111111111111111111111111111 / +b11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 1 +15 #4 #5 #6 -06 -#7 05 +#7 +07 #8 #9 0# -b00000000 % -b000000000000000000000000000000000000000000000000 & -b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 ( -b00000000 , -b0000000000000000000000000000000000000000000000000 - -b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 / -07 -16 +b00000000 ' +b000000000000000000000000000000000000000000000000 ( +b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 * +b00000000 . +b0000000000000000000000000000000000000000000000000 / +b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 1 +15 +06 #10 #11 #12 -06 +05 #13 #14 #15 -b00000000000000000000000000000001 $ -b11111111 % -b111111111111111111111111111111111111111111111111 & -b11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 ( -b11111111 , -b1111111111111111111111111111111111111111111111111 - -b11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 / -16 +b00000000000000000000000000000001 & +b11111111 ' +b111111111111111111111111111111111111111111111111 ( +b11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 * +b11111111 . +b1111111111111111111111111111111111111111111111111 / +b11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 1 +15 #16 #17 #18 -06 +05 #19 #20 From 536bdf506e78fcb4caddb522dadb2af7ca07a8c2 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Thu, 12 Aug 2021 21:43:32 +0100 Subject: [PATCH 68/84] Fix re-evaluation of logic dependent on state set in DPI exports (#3091). Verilator should now correctly re-evaluate any logic that depends on state set in a DPI exported function, including if the DPI export is called outside eval, or if the DPI export is called from a DPI import. Whenever the design contains a DPI exported function that sets a non-local variable, we create a global __Vdpi_export_trigger flag, that is set in the body of the DPI export, and make all variables set in any DPI exported functions dependent on this flag (this ensures correct ordering and change detection on state set in DPI exports when needed). The DPI export trigger flag is cleared at the end of eval, which ensured calls to DPI exports outside of eval are detected. Additionally the ordering is modifies to assume that any call to a 'context' DPI import might call DPI exports by adding an edge to the ordering graph from the logic vertex containing the call to the DPI import to the DPI export trigger variable vertex (note the standard does not allow calls to DPI exports from DPI imports that were not imported with 'context', so we do not enforce ordering on those). --- Changes | 1 + src/V3AstNodes.cpp | 1 + src/V3AstNodes.h | 19 ++++++ src/V3Changed.cpp | 4 ++ src/V3Clock.cpp | 8 +++ src/V3Descope.cpp | 4 ++ src/V3GenClk.cpp | 13 ++++ src/V3Order.cpp | 34 ++++++++-- src/V3Task.cpp | 88 +++++++++++++++++++++++++ test_regress/t/t_dpi_qw.pl | 2 +- test_regress/t/t_order_dpi_export_1.cpp | 37 +++++++++++ test_regress/t/t_order_dpi_export_1.pl | 24 +++++++ test_regress/t/t_order_dpi_export_1.v | 36 ++++++++++ test_regress/t/t_order_dpi_export_2.cpp | 38 +++++++++++ test_regress/t/t_order_dpi_export_2.pl | 24 +++++++ test_regress/t/t_order_dpi_export_2.v | 43 ++++++++++++ test_regress/t/t_order_dpi_export_3.cpp | 40 +++++++++++ test_regress/t/t_order_dpi_export_3.pl | 24 +++++++ test_regress/t/t_order_dpi_export_3.v | 56 ++++++++++++++++ test_regress/t/t_order_dpi_export_4.cpp | 40 +++++++++++ test_regress/t/t_order_dpi_export_4.pl | 24 +++++++ test_regress/t/t_order_dpi_export_4.v | 58 ++++++++++++++++ test_regress/t/t_order_dpi_export_5.cpp | 39 +++++++++++ test_regress/t/t_order_dpi_export_5.pl | 24 +++++++ test_regress/t/t_order_dpi_export_5.v | 46 +++++++++++++ 25 files changed, 722 insertions(+), 5 deletions(-) create mode 100644 test_regress/t/t_order_dpi_export_1.cpp create mode 100755 test_regress/t/t_order_dpi_export_1.pl create mode 100644 test_regress/t/t_order_dpi_export_1.v create mode 100644 test_regress/t/t_order_dpi_export_2.cpp create mode 100755 test_regress/t/t_order_dpi_export_2.pl create mode 100644 test_regress/t/t_order_dpi_export_2.v create mode 100644 test_regress/t/t_order_dpi_export_3.cpp create mode 100755 test_regress/t/t_order_dpi_export_3.pl create mode 100644 test_regress/t/t_order_dpi_export_3.v create mode 100644 test_regress/t/t_order_dpi_export_4.cpp create mode 100755 test_regress/t/t_order_dpi_export_4.pl create mode 100644 test_regress/t/t_order_dpi_export_4.v create mode 100644 test_regress/t/t_order_dpi_export_5.cpp create mode 100755 test_regress/t/t_order_dpi_export_5.pl create mode 100644 test_regress/t/t_order_dpi_export_5.v diff --git a/Changes b/Changes index 158ecf997..aa60688ff 100644 --- a/Changes +++ b/Changes @@ -13,6 +13,7 @@ Verilator 4.211 devel **Minor:** +* Fix re-evaluation of logic dependent on state set in DPI exports (#3091). [Geza Lore] * Support unpacked array localparams in tasks/functions (#3078). [Geza Lore] * Support timeunit/timeprecision in $unit. * Add --instr-count-dpi to tune assumed DPI import cost for multithreaded diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index 661c40fca..8ce2682d0 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -1842,6 +1842,7 @@ void AstCFunc::dump(std::ostream& str) const { if (dpiExportImpl()) str << " [DPIEI]"; if (dpiImportPrototype()) str << " [DPIIP]"; if (dpiImportWrapper()) str << " [DPIIW]"; + if (dpiContext()) str << " [DPICTX]"; if (isConstructor()) str << " [CTOR]"; if (isDestructor()) str << " [DTOR]"; if (isVirtual()) str << " [VIRT]"; diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index cdf54bd7a..d1706bbdd 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -3479,6 +3479,17 @@ public: virtual bool brokeLhsMustBeLvalue() const override { return true; } }; +class AstDpiExportUpdated final : public AstNodeStmt { + // Denotes that the referenced variable may have been updated via a DPI Export +public: + AstDpiExportUpdated(FileLine* fl, AstVarScope* varScopep) + : ASTGEN_SUPER_DpiExportUpdated(fl) { + addOp1p(new AstVarRef{fl, varScopep, VAccess::WRITE}); + } + ASTNODE_NODE_FUNCS(DpiExportUpdated) + AstVarScope* varScopep() const { return VN_CAST(op1p(), VarRef)->varScopep(); } +}; + class AstExprStmt final : public AstNodeMath { // Perform a statement, often assignment inside an expression/math node, // the parent gets passed the 'resultp()'. @@ -8744,6 +8755,7 @@ private: bool m_dpiExportImpl : 1; // DPI export implementation (called from DPI dispatcher via lookup) bool m_dpiImportPrototype : 1; // This is the DPI import prototype (i.e.: provided by user) bool m_dpiImportWrapper : 1; // Wrapper for invoking DPI import prototype from generated code + bool m_dpiContext : 1; // Declared as 'context' DPI import/export function public: AstCFunc(FileLine* fl, const string& name, AstScope* scopep, const string& rtnType = "") : ASTGEN_SUPER_CFunc(fl) { @@ -8770,6 +8782,7 @@ public: m_dpiExportImpl = false; m_dpiImportPrototype = false; m_dpiImportWrapper = false; + m_dpiContext = false; } ASTNODE_NODE_FUNCS(CFunc) virtual string name() const override { return m_name; } @@ -8845,6 +8858,8 @@ public: void dpiImportPrototype(bool flag) { m_dpiImportPrototype = flag; } bool dpiImportWrapper() const { return m_dpiImportWrapper; } void dpiImportWrapper(bool flag) { m_dpiImportWrapper = flag; } + bool dpiContext() const { return m_dpiContext; } + void dpiContext(bool flag) { m_dpiContext = flag; } // // If adding node accessors, see below emptyBody AstNode* argsp() const { return op1p(); } @@ -9139,6 +9154,7 @@ private: AstPackage* m_dollarUnitPkgp = nullptr; // $unit AstCFunc* m_evalp = nullptr; // The '_eval' function AstExecGraph* m_execGraphp = nullptr; // Execution MTask graph for threads>1 mode + AstVarScope* m_dpiExportTriggerp = nullptr; // The DPI export trigger variable VTimescale m_timeunit; // Global time unit VTimescale m_timeprecision; // Global time precision bool m_changeRequest = false; // Have _change_request method @@ -9149,6 +9165,7 @@ public: virtual const char* broken() const override { BROKEN_RTN(m_dollarUnitPkgp && !m_dollarUnitPkgp->brokeExists()); BROKEN_RTN(m_evalp && !m_evalp->brokeExists()); + BROKEN_RTN(m_dpiExportTriggerp && !m_dpiExportTriggerp->brokeExists()); return nullptr; } virtual string name() const override { return "$root"; } @@ -9184,6 +9201,8 @@ public: void evalp(AstCFunc* evalp) { m_evalp = evalp; } AstExecGraph* execGraphp() const { return m_execGraphp; } void execGraphp(AstExecGraph* graphp) { m_execGraphp = graphp; } + AstVarScope* dpiExportTriggerp() const { return m_dpiExportTriggerp; } + void dpiExportTriggerp(AstVarScope* varScopep) { m_dpiExportTriggerp = varScopep; } VTimescale timeunit() const { return m_timeunit; } void timeunit(const VTimescale& value) { m_timeunit = value; } VTimescale timeprecision() const { return m_timeprecision; } diff --git a/src/V3Changed.cpp b/src/V3Changed.cpp index 589a681ac..aac14f59a 100644 --- a/src/V3Changed.cpp +++ b/src/V3Changed.cpp @@ -205,6 +205,10 @@ private: public: // CONSTRUCTORS ChangedInsertVisitor(AstVarScope* vscp, ChangedState* statep) { + // DPI export trigger should never need change detect. See similar assertions in V3Order + // (OrderVisitor::nodeMarkCircular), and V3GenClk (GenClkRenameVisitor::genInpClk). + UASSERT_OBJ(vscp != v3Global.rootp()->dpiExportTriggerp(), vscp, + "DPI export trigger should not need change detect"); m_statep = statep; m_vscp = vscp; m_detects = 0; diff --git a/src/V3Clock.cpp b/src/V3Clock.cpp index 6bb9a461c..7f941851d 100644 --- a/src/V3Clock.cpp +++ b/src/V3Clock.cpp @@ -257,6 +257,14 @@ private: // Process the activates iterateChildren(nodep); UINFO(4, " TOPSCOPE iter done " << nodep << endl); + // Clear the DPI export trigger flag at the end of eval + if (AstVarScope* const dpiExportTriggerp = v3Global.rootp()->dpiExportTriggerp()) { + FileLine* const fl = dpiExportTriggerp->fileline(); + AstAssign* const assignp + = new AstAssign{fl, new AstVarRef{fl, dpiExportTriggerp, VAccess::WRITE}, + new AstConst{fl, AstConst::BitFalse{}}}; + m_evalFuncp->addFinalsp(assignp); + } // Split large functions splitCheck(m_evalFuncp); splitCheck(m_initFuncp); diff --git a/src/V3Descope.cpp b/src/V3Descope.cpp index 0621075d7..8ab4dccf5 100644 --- a/src/V3Descope.cpp +++ b/src/V3Descope.cpp @@ -182,6 +182,10 @@ private: } // VISITORS + virtual void visit(AstNetlist* nodep) override { + nodep->dpiExportTriggerp(nullptr); + iterateChildren(nodep); + } virtual void visit(AstNodeModule* nodep) override { VL_RESTORER(m_modp); { diff --git a/src/V3GenClk.cpp b/src/V3GenClk.cpp index 48c39b63d..1845a95b3 100644 --- a/src/V3GenClk.cpp +++ b/src/V3GenClk.cpp @@ -56,6 +56,19 @@ private: if (vscp->user2p()) { return VN_CAST(vscp->user2p(), VarScope); } else { + // In order to create a __VinpClk* for a signal, it needs to be marked circular. + // The DPI export trigger is never marked circular by V3Order (see comments in + // OrderVisitor::nodeMarkCircular). The only other place where one might mark + // a node circular is in this pass (V3GenClk), if the signal is assigned but was + // previously used as a clock. The DPI export trigger is only ever assigned in + // a DPI export called from outside eval, or from a DPI import, which are not + // discovered by GenClkReadVisitor (note that impure tasks - i.e.: those setting + // non-local variables - cannot be no-inline, see V3Task), hence the DPI export + // trigger should never be marked circular. Note that ordering should still be + // correct as there will be a change detect on any signals set from a DPI export + // that might have dependents scheduled earlier. + UASSERT_OBJ(vscp != v3Global.rootp()->dpiExportTriggerp(), vscp, + "DPI export trigger should not need __VinpClk"); AstVar* varp = vscp->varp(); string newvarname = "__VinpClk__" + vscp->scopep()->nameDotless() + "__" + varp->name(); diff --git a/src/V3Order.cpp b/src/V3Order.cpp index 93ad38651..0cb9cf4d8 100644 --- a/src/V3Order.cpp +++ b/src/V3Order.cpp @@ -645,6 +645,7 @@ private: AstSenTree* m_comboDomainp = nullptr; // Combo activation tree AstSenTree* m_deleteDomainp = nullptr; // Delete this from tree OrderInputsVertex* m_inputsVxp = nullptr; // Top level vertex all inputs point from + OrderVarVertex* m_dpiExportTriggerVxp = nullptr; // DPI Export trigger condition vertex OrderLogicVertex* m_logicVxp = nullptr; // Current statement being tracked, nullptr=ignored AstTopScope* m_topScopep = nullptr; // Current top scope being processed AstScope* m_scopetopp = nullptr; // Scope under TOPSCOPE @@ -756,6 +757,10 @@ private: } void nodeMarkCircular(OrderVarVertex* vertexp, OrderEdge* edgep) { + // To be marked circular requires being a clock assigned in a delayed assignment, or + // having a cutable in or out edge, none of which is true for the DPI export trigger. + UASSERT(vertexp != m_dpiExportTriggerVxp, + "DPI expor trigger should not be marked circular"); AstVarScope* nodep = vertexp->varScp(); OrderLogicVertex* fromLVtxp = nullptr; OrderLogicVertex* toLVtxp = nullptr; @@ -953,6 +958,9 @@ private: // Base vertices m_activeSenVxp = nullptr; m_inputsVxp = new OrderInputsVertex(&m_graph, nullptr); + if (AstVarScope* const dpiExportTrigger = v3Global.rootp()->dpiExportTriggerp()) { + m_dpiExportTriggerVxp = newVarUserVertex(dpiExportTrigger, WV_STD); + } // iterateChildren(nodep); // Done topscope, erase extra user information @@ -1130,6 +1138,24 @@ private: } } } + virtual void visit(AstDpiExportUpdated* nodep) override { + // This is under an AstAlways, sensitive to a change in the DPI export trigger. We just + // need to add an edge to the enclosing logic vertex (the vertex for the AstAlways). + OrderVarVertex* const varVxp = newVarUserVertex(nodep->varScopep(), WV_STD); + new OrderComboCutEdge(&m_graph, m_logicVxp, varVxp); + // Only used for ordering, so we can get rid of it here + nodep->unlinkFrBack(); + VL_DO_DANGLING(pushDeletep(nodep), nodep); + } + virtual void visit(AstCCall* nodep) override { + // Calls to 'context' imported DPI function may call DPI exported functions + if (m_dpiExportTriggerVxp && nodep->funcp()->dpiImportWrapper() + && nodep->funcp()->dpiContext()) { + UASSERT_OBJ(m_logicVxp, nodep, "Call not under logic"); + new OrderEdge(&m_graph, m_logicVxp, m_dpiExportTriggerVxp, WEIGHT_NORMAL); + } + iterateChildren(nodep); + } virtual void visit(AstSenTree* nodep) override { // Having a node derived from the sentree isn't required for // correctness, it merely makes the graph better connected @@ -1187,10 +1213,10 @@ private: iterateNewStmt(nodep); } virtual void visit(AstCFunc*) override { - // Ignore for now - // We should detect what variables are set in the function, and make - // settlement code for them, then set a global flag, so we call "settle" - // on the next evaluation loop. + // Calls to DPI exports handled with AstCCall. /* verlator public */ functions are + // ignored for now (and hence potentially mis-ordered), but could use the same or + // similar mechanism as DPI exports. Every other impure function (including those + // that may set a non-local variable) must have been inlined in V3Task. } //-------------------- virtual void visit(AstNode* nodep) override { iterateChildren(nodep); } diff --git a/src/V3Task.cpp b/src/V3Task.cpp index 7dce81e29..a4bcff21d 100644 --- a/src/V3Task.cpp +++ b/src/V3Task.cpp @@ -364,6 +364,39 @@ struct TaskDpiUtils { } }; +//###################################################################### +// Gather non-local variables written by an AstCFunc + +class TaskGatherWrittenVisitor final : public AstNVisitor { + // NODE STATE + // AstVarScope::user5 -> Already considered variable + AstUser5InUse m_user5InUse; + + std::vector m_writtenVariables; // Variables written + + virtual void visit(AstVarRef* nodep) override { + if (nodep->access().isReadOnly()) return; // Ignore read reference + AstVarScope* const varScopep = nodep->varScopep(); + if (varScopep->user5()) return; // Ignore already processed variable + varScopep->user5(true); // Mark as already processed + // Note: We are ignoring function locals as they should not be referenced anywhere outside + // of the enclosing AstCFunc, and therefore they are irrelevant for code ordering. This is + // simply an optimization to avoid adding useless nodes to the ordering graph in V3Order. + if (varScopep->varp()->isFuncLocal()) return; + m_writtenVariables.push_back(varScopep); + } + virtual void visit(AstNode* nodep) override { iterateChildrenConst(nodep); } + + explicit TaskGatherWrittenVisitor(AstNode* nodep) { iterate(nodep); } + +public: + // Gather all written non-local variables + static const std::vector gather(AstCFunc* funcp) { + TaskGatherWrittenVisitor visitor{funcp}; + return std::move(visitor.m_writtenVariables); + } +}; + //###################################################################### // Task state, as a visitor of each AstNode @@ -768,6 +801,7 @@ private: AstCFunc* const funcp = new AstCFunc(nodep->fileline(), nodep->cname(), m_scopep, (rtnvarp ? rtnvarp->dpiArgType(true, true) : "")); funcp->dpiExportDispatcher(true); + funcp->dpiContext(nodep->dpiContext()); funcp->dontCombine(true); funcp->entryPoint(true); funcp->isStatic(true); @@ -899,6 +933,7 @@ private: : ""; AstCFunc* const funcp = new AstCFunc(nodep->fileline(), nodep->cname(), m_scopep, rtnType); funcp->dpiImportPrototype(true); + funcp->dpiContext(nodep->dpiContext()); funcp->dontCombine(true); funcp->entryPoint(false); funcp->isMethod(false); @@ -1062,6 +1097,22 @@ private: } } + AstVarScope* makeDpiExporTrigger() { + AstVarScope* dpiExportTriggerp = v3Global.rootp()->dpiExportTriggerp(); + if (!dpiExportTriggerp) { + // Create the global DPI export trigger flag the first time we encounter a DPI export. + // This flag is set any time a DPI export is invoked, and cleared at the end of eval. + FileLine* const fl = m_topScopep->fileline(); + AstVar* const varp + = new AstVar{fl, AstVarType::VAR, "__Vdpi_export_trigger", VFlagBitPacked{}, 1}; + m_topScopep->scopep()->modp()->addStmtp(varp); + dpiExportTriggerp = new AstVarScope{fl, m_topScopep->scopep(), varp}; + m_topScopep->scopep()->addVarp(dpiExportTriggerp); + v3Global.rootp()->dpiExportTriggerp(dpiExportTriggerp); + } + return dpiExportTriggerp; + } + AstCFunc* makeUserFunc(AstNodeFTask* nodep, bool ftaskNoInline) { // Given a already cloned node, make a public C function, or a non-inline C function // Probably some of this work should be done later, but... @@ -1151,6 +1202,7 @@ private: cfuncp->funcPublic(nodep->taskPublic()); cfuncp->dpiExportImpl(nodep->dpiExport()); cfuncp->dpiImportWrapper(nodep->dpiImport()); + cfuncp->dpiContext(nodep->dpiContext()); if (nodep->dpiImport() || nodep->dpiExport()) { cfuncp->isStatic(true); cfuncp->isLoose(true); @@ -1248,6 +1300,42 @@ private: tempp->stmtsp()->unlinkFrBackWithNext(); VL_DO_DANGLING(tempp->deleteTree(), tempp); } + + if (cfuncp->dpiExportImpl()) { + // Mark all non-local variables written by the DPI exported function as being updated + // by DPI exports. This ensures correct ordering and change detection later. + const std::vector writtenps = TaskGatherWrittenVisitor::gather(cfuncp); + if (!writtenps.empty()) { + AstVarScope* const dpiExportTriggerp = makeDpiExporTrigger(); + FileLine* const fl = cfuncp->fileline(); + + // Set DPI export trigger flag every time the DPI export is called. + AstAssign* const assignp + = new AstAssign{fl, new AstVarRef{fl, dpiExportTriggerp, VAccess::WRITE}, + new AstConst{fl, AstConst::BitTrue{}}}; + // Add as first statement (to avoid issues with early returns) to exported function + if (cfuncp->stmtsp()) { + cfuncp->stmtsp()->addHereThisAsNext(assignp); + } else { + cfuncp->addStmtsp(assignp); + } + + // Add an always block sensitive to the DPI export trigger flag, and add an + // AstDpiExportUpdated node under it for each variable that are writen by the + // exported function. + AstAlways* const alwaysp = new AstAlways{ + fl, VAlwaysKwd::ALWAYS, + new AstSenTree{ + fl, new AstSenItem{fl, VEdgeType::ET_HIGHEDGE, + new AstVarRef{fl, dpiExportTriggerp, VAccess::READ}}}, + nullptr}; + for (AstVarScope* const varScopep : writtenps) { + alwaysp->addStmtp(new AstDpiExportUpdated{fl, varScopep}); + } + m_scopep->addActivep(alwaysp); + } + } + // Delete rest of cloned task and return new func VL_DO_DANGLING(pushDeletep(nodep), nodep); if (debug() >= 9) cfuncp->dumpTree(cout, "-userFunc: "); diff --git a/test_regress/t/t_dpi_qw.pl b/test_regress/t/t_dpi_qw.pl index 74e3bae58..f5c98cf48 100755 --- a/test_regress/t/t_dpi_qw.pl +++ b/test_regress/t/t_dpi_qw.pl @@ -12,7 +12,7 @@ scenarios(simulator => 1); compile( v_flags2 => ["t/t_dpi_qw_c.cpp"], - verilator_flags2 => ["-Wall -Wno-DECLFILENAME -no-l2name"], + verilator_flags2 => ["-Wall -Wno-DECLFILENAME -Wno-UNOPTFLAT -no-l2name"], ); execute( diff --git a/test_regress/t/t_order_dpi_export_1.cpp b/test_regress/t/t_order_dpi_export_1.cpp new file mode 100644 index 000000000..1dcec5c1e --- /dev/null +++ b/test_regress/t/t_order_dpi_export_1.cpp @@ -0,0 +1,37 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// +// Copyright 2021 by Geza Lore. 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 +// +//************************************************************************* + +#include + +#include +#include + +int main(int argc, char* argv[]) { + Vt_order_dpi_export_1* const tb = new Vt_order_dpi_export_1; + tb->contextp()->commandArgs(argc, argv); + bool clk = true; + + while (!tb->contextp()->gotFinish()) { + // Timeout + if (tb->contextp()->time() > 100000) break; + // Toggle and set clock + svSetScope(svGetScopeFromName("TOP.testbench")); + clk = !clk; + set_clk(clk); + // Eval + tb->eval(); + // Advance time + tb->contextp()->timeInc(500); + } + + delete tb; + return 0; +} diff --git a/test_regress/t/t_order_dpi_export_1.pl b/test_regress/t/t_order_dpi_export_1.pl new file mode 100755 index 000000000..21b3c76b6 --- /dev/null +++ b/test_regress/t/t_order_dpi_export_1.pl @@ -0,0 +1,24 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 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 + +scenarios(vlt_all => 1); + +compile( + make_top_shell => 0, + make_main => 0, + verilator_flags2 => ["--exe","$Self->{t_dir}/$Self->{name}.cpp"], + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_order_dpi_export_1.v b/test_regress/t/t_order_dpi_export_1.v new file mode 100644 index 000000000..e71069e06 --- /dev/null +++ b/test_regress/t/t_order_dpi_export_1.v @@ -0,0 +1,36 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// Copyright 2021 by Geza Lore. 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 + +module testbench; + + logic clk; + + export "DPI-C" function set_clk; + function void set_clk(bit val); + clk = val; + endfunction; + + // Downstream signal dependent on clk demonstrates scheduling issue. + // The '$c("1") &' simply ensures that dependent_clk does not get + // replaced with clk early and hence hiding the issue + wire dependent_clk = $c1("1") & clk; + + int n = 0; + + always @(posedge dependent_clk) begin + $display("t=%t n=%d", $time, n); + if ($time != (2*n+1) * 500) $stop; + if (n == 20) begin + $write("*-* All Finished *-*\n"); + $finish; + end + n += 1; + end + +endmodule + diff --git a/test_regress/t/t_order_dpi_export_2.cpp b/test_regress/t/t_order_dpi_export_2.cpp new file mode 100644 index 000000000..3ba47b0d3 --- /dev/null +++ b/test_regress/t/t_order_dpi_export_2.cpp @@ -0,0 +1,38 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// +// Copyright 2021 by Geza Lore. 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 +// +//************************************************************************* + +#include + +#include +#include + +void toggle_other_clk(svBit val) { set_other_clk(val); } + +int main(int argc, char* argv[]) { + Vt_order_dpi_export_2* const tb = new Vt_order_dpi_export_2; + tb->contextp()->commandArgs(argc, argv); + bool clk = true; + + while (!tb->contextp()->gotFinish()) { + // Timeout + if (tb->contextp()->time() > 100000) break; + // Toggle and set main clock + clk = !clk; + tb->clk = clk; + // Eval + tb->eval(); + // Advance time + tb->contextp()->timeInc(500); + } + + delete tb; + return 0; +} diff --git a/test_regress/t/t_order_dpi_export_2.pl b/test_regress/t/t_order_dpi_export_2.pl new file mode 100755 index 000000000..21b3c76b6 --- /dev/null +++ b/test_regress/t/t_order_dpi_export_2.pl @@ -0,0 +1,24 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 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 + +scenarios(vlt_all => 1); + +compile( + make_top_shell => 0, + make_main => 0, + verilator_flags2 => ["--exe","$Self->{t_dir}/$Self->{name}.cpp"], + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_order_dpi_export_2.v b/test_regress/t/t_order_dpi_export_2.v new file mode 100644 index 000000000..a5c1f40a1 --- /dev/null +++ b/test_regress/t/t_order_dpi_export_2.v @@ -0,0 +1,43 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// Copyright 2021 by Geza Lore. 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 + +module testbench( + /*AUTOARG*/ + // Inputs + clk + ); + + input clk; // Top level input clock + logic other_clk; // Dependent clock set via DPI + + export "DPI-C" function set_other_clk; + function void set_other_clk(bit val); + other_clk = val; + endfunction; + + bit even_other = 1; + import "DPI-C" context function void toggle_other_clk(bit val); + always @(posedge clk) begin + even_other <= ~even_other; + toggle_other_clk(even_other); + end + + int n = 0; + + always @(posedge other_clk) begin + $display("t=%t n=%d", $time, n); + if ($time != (4*n+1) * 500) $stop; + if (n == 20) begin + $write("*-* All Finished *-*\n"); + $finish; + end + n += 1; + end + +endmodule + diff --git a/test_regress/t/t_order_dpi_export_3.cpp b/test_regress/t/t_order_dpi_export_3.cpp new file mode 100644 index 000000000..2855845c1 --- /dev/null +++ b/test_regress/t/t_order_dpi_export_3.cpp @@ -0,0 +1,40 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// +// Copyright 2021 by Geza Lore. 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 +// +//************************************************************************* + +#include + +#include +#include + +void toggle_other_clk(svBit val) { set_other_clk(val); } + +void toggle_third_clk(svBit val) { set_third_clk(val); } + +int main(int argc, char* argv[]) { + Vt_order_dpi_export_3* const tb = new Vt_order_dpi_export_3; + tb->contextp()->commandArgs(argc, argv); + bool clk = true; + + while (!tb->contextp()->gotFinish()) { + // Timeout + if (tb->contextp()->time() > 100000) break; + // Toggle and set main clock + clk = !clk; + tb->clk = clk; + // Eval + tb->eval(); + // Advance time + tb->contextp()->timeInc(500); + } + + delete tb; + return 0; +} diff --git a/test_regress/t/t_order_dpi_export_3.pl b/test_regress/t/t_order_dpi_export_3.pl new file mode 100755 index 000000000..21b3c76b6 --- /dev/null +++ b/test_regress/t/t_order_dpi_export_3.pl @@ -0,0 +1,24 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 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 + +scenarios(vlt_all => 1); + +compile( + make_top_shell => 0, + make_main => 0, + verilator_flags2 => ["--exe","$Self->{t_dir}/$Self->{name}.cpp"], + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_order_dpi_export_3.v b/test_regress/t/t_order_dpi_export_3.v new file mode 100644 index 000000000..8083bae97 --- /dev/null +++ b/test_regress/t/t_order_dpi_export_3.v @@ -0,0 +1,56 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// Copyright 2021 by Geza Lore. 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 + +module testbench( + /*AUTOARG*/ + // Inputs + clk + ); + + input clk; // Top level input clock + logic other_clk; // Dependent clock set via DPI + logic third_clk; // Additional dependent clock set via DPI + + export "DPI-C" function set_other_clk; + function void set_other_clk(bit val); + other_clk = val; + endfunction; + + export "DPI-C" function set_third_clk; + function void set_third_clk(bit val); + third_clk = val; + endfunction; + + bit even_other = 1; + import "DPI-C" context function void toggle_other_clk(bit val); + always @(posedge clk) begin + even_other <= ~even_other; + toggle_other_clk(even_other); + end + + bit even_third = 1; + import "DPI-C" context function void toggle_third_clk(bit val); + always @(posedge other_clk) begin + even_third <= ~even_third; + toggle_third_clk(even_third); + end + + int n = 0; + + always @(posedge third_clk) begin + $display("t=%d n=%d", $time, n); + if ($time != (8*n+1) * 500) $stop; + if (n == 20) begin + $write("*-* All Finished *-*\n"); + $finish; + end + n += 1; + end + +endmodule + diff --git a/test_regress/t/t_order_dpi_export_4.cpp b/test_regress/t/t_order_dpi_export_4.cpp new file mode 100644 index 000000000..e4ecd5c89 --- /dev/null +++ b/test_regress/t/t_order_dpi_export_4.cpp @@ -0,0 +1,40 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// +// Copyright 2021 by Geza Lore. 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 +// +//************************************************************************* + +#include + +#include +#include + +void toggle_other_clk(svBit val) { set_other_clk(val); } + +void toggle_third_clk(svBit val) { set_third_clk(val); } + +int main(int argc, char* argv[]) { + Vt_order_dpi_export_4* const tb = new Vt_order_dpi_export_4; + tb->contextp()->commandArgs(argc, argv); + bool clk = true; + + while (!tb->contextp()->gotFinish()) { + // Timeout + if (tb->contextp()->time() > 100000) break; + // Toggle and set main clock + clk = !clk; + tb->clk = clk; + // Eval + tb->eval(); + // Advance time + tb->contextp()->timeInc(500); + } + + delete tb; + return 0; +} diff --git a/test_regress/t/t_order_dpi_export_4.pl b/test_regress/t/t_order_dpi_export_4.pl new file mode 100755 index 000000000..21b3c76b6 --- /dev/null +++ b/test_regress/t/t_order_dpi_export_4.pl @@ -0,0 +1,24 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 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 + +scenarios(vlt_all => 1); + +compile( + make_top_shell => 0, + make_main => 0, + verilator_flags2 => ["--exe","$Self->{t_dir}/$Self->{name}.cpp"], + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_order_dpi_export_4.v b/test_regress/t/t_order_dpi_export_4.v new file mode 100644 index 000000000..73600c1c1 --- /dev/null +++ b/test_regress/t/t_order_dpi_export_4.v @@ -0,0 +1,58 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// Copyright 2021 by Geza Lore. 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 + +module testbench( + /*AUTOARG*/ + // Inputs + clk + ); + + input clk; // Top level input clock + logic other_clk; // Dependent clock set via DPI + logic third_clk; // Additional dependent clock set via DPI + + export "DPI-C" function set_other_clk; + function void set_other_clk(bit val); + other_clk = val; + endfunction; + + export "DPI-C" function set_third_clk; + function void set_third_clk(bit val); + third_clk = val; + endfunction; + + bit even_other = 1; + import "DPI-C" context function void toggle_other_clk(bit val); + always @(posedge clk) begin + even_other <= ~even_other; + toggle_other_clk(even_other); + end + + bit even_third = 1; + import "DPI-C" context function void toggle_third_clk(bit val); + always @(posedge other_clk) begin + even_third <= ~even_third; + toggle_third_clk(even_third); + end + + int n = 0; + + wire final_clk = $c1("1") & third_clk; + + always @(posedge final_clk) begin + $display("t=%d n=%d", $time, n); + if ($time != (8*n+1) * 500) $stop; + if (n == 20) begin + $write("*-* All Finished *-*\n"); + $finish; + end + n += 1; + end + +endmodule + diff --git a/test_regress/t/t_order_dpi_export_5.cpp b/test_regress/t/t_order_dpi_export_5.cpp new file mode 100644 index 000000000..c14bf9ef3 --- /dev/null +++ b/test_regress/t/t_order_dpi_export_5.cpp @@ -0,0 +1,39 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// +// Copyright 2021 by Geza Lore. 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 +// +//************************************************************************* + +#include + +#include +#include + +int main(int argc, char* argv[]) { + Vt_order_dpi_export_5* const tb = new Vt_order_dpi_export_5; + tb->contextp()->commandArgs(argc, argv); + bool clk = true; + + while (!tb->contextp()->gotFinish()) { + // Timeout + if (tb->contextp()->time() > 100000) break; + // Toggle and set main clock + clk = !clk; + tb->clk = clk; + // Reset counter at falling clock edge, once it reached value 4 + svSetScope(svGetScopeFromName("TOP.testbench")); + if (get_cnt() == 4 && !clk) set_cnt(0); + // Eval + tb->eval(); + // Advance time + tb->contextp()->timeInc(500); + } + + delete tb; + return 0; +} diff --git a/test_regress/t/t_order_dpi_export_5.pl b/test_regress/t/t_order_dpi_export_5.pl new file mode 100755 index 000000000..21b3c76b6 --- /dev/null +++ b/test_regress/t/t_order_dpi_export_5.pl @@ -0,0 +1,24 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 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 + +scenarios(vlt_all => 1); + +compile( + make_top_shell => 0, + make_main => 0, + verilator_flags2 => ["--exe","$Self->{t_dir}/$Self->{name}.cpp"], + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_order_dpi_export_5.v b/test_regress/t/t_order_dpi_export_5.v new file mode 100644 index 000000000..744c64ca8 --- /dev/null +++ b/test_regress/t/t_order_dpi_export_5.v @@ -0,0 +1,46 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// Copyright 2021 by Geza Lore. 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 + +module testbench( + /*AUTOARG*/ + // Inputs + clk + ); + + input clk; + + int cnt = 0; + export "DPI-C" function set_cnt; + function void set_cnt(int val); + cnt = val; + endfunction; + export "DPI-C" function get_cnt; + function int get_cnt(); + return cnt; + endfunction; + + always @(posedge clk) cnt += 1; + + // Downstream combinational signal dependent on both input clock and + // DPI export. + wire dependent_clk = cnt == 2; + + int n = 0; + + always @(posedge dependent_clk) begin + $display("t=%t n=%d", $time, n); + if ($time != (8*n+3) * 500) $stop; + if (n == 20) begin + $write("*-* All Finished *-*\n"); + $finish; + end + n += 1; + end + +endmodule + From 4ec8bc358996ab7dddc89244e7d99f54e14a802b Mon Sep 17 00:00:00 2001 From: Yutetsu TAKATSUKASA Date: Sat, 14 Aug 2021 20:28:47 +0900 Subject: [PATCH 69/84] Fix op count and wrong optimization in bitop tree (#3099) - Fix op calculation. - Fix wrong optimization revealed by changing the op calculation: Not(CCast(And(a, b))) must be frozen. --- src/V3Const.cpp | 13 +++++++++---- test_regress/t/t_const_opt_red.pl | 2 +- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/V3Const.cpp b/src/V3Const.cpp index ab2bfc58c..dcf19c421 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -344,7 +344,10 @@ class ConstBitOpTreeVisitor final : public AstNVisitor { virtual void visit(AstNot* nodep) override { CONST_BITOP_RETURN_IF(nodep->widthMin() != 1, nodep); AstNode* lhsp = nodep->lhsp(); - CONST_BITOP_RETURN_IF(VN_IS(lhsp, And) || VN_IS(lhsp, Or) || VN_IS(lhsp, Const), lhsp); + if (AstCCast* castp = VN_CAST(lhsp, CCast)) lhsp = castp->lhsp(); + CONST_BITOP_RETURN_IF(!VN_IS(lhsp, VarRef) && !VN_IS(lhsp, Xor) && !VN_IS(lhsp, RedXor) + && !VN_IS(lhsp, ShiftR), + lhsp); incrOps(nodep, __LINE__); m_polarity = !m_polarity; iterateChildren(nodep); @@ -522,17 +525,19 @@ public: if (visitor.m_failed || visitor.m_varInfos.size() == 1) return nullptr; // Two ops for each varInfo. (And and Eq) - const int vars = visitor.m_varInfos.size() - 1; + int vars = 0; int constTerms = 0; for (auto&& v : visitor.m_varInfos) { - if (v && v->hasConstantResult()) ++constTerms; + if (!v) continue; + ++vars; + if (v->hasConstantResult()) ++constTerms; } // Expected number of ops after this simplification // e.g. (comp0 == (mask0 & var0)) & (comp1 == (mask1 & var1)) & .... // e.g. redXor(mask1 & var0) ^ redXor(mask1 & var1) // 2 ops per variables, numVars - 1 ops among variables int expOps = 2 * (vars - constTerms) + vars - 1; - expOps += 2 * visitor.m_frozenNodes.size(); + expOps += visitor.m_frozenNodes.size(); if (visitor.isXorTree()) { ++expOps; // AstRedXor::cleanOut() == false, so need 1 & redXor if (!visitor.m_polarity) ++expOps; // comparison with 0 diff --git a/test_regress/t/t_const_opt_red.pl b/test_regress/t/t_const_opt_red.pl index d2a4c777b..b3fb2c994 100755 --- a/test_regress/t/t_const_opt_red.pl +++ b/test_regress/t/t_const_opt_red.pl @@ -19,7 +19,7 @@ execute( ); if ($Self->{vlt}) { - file_grep($Self->{stats}, qr/Optimizations, Const bit op reduction\s+(\d+)/i, 149); + file_grep($Self->{stats}, qr/Optimizations, Const bit op reduction\s+(\d+)/i, 155); } ok(1); From c69ddc46f87ac3424fe3770e6fec5c751cf972a5 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Sat, 14 Aug 2021 21:09:01 +0100 Subject: [PATCH 70/84] Fix bitop tree optimization dropping necessary cleaning AND (#3097) Fixes #3096. --- Changes | 1 + src/V3Const.cpp | 38 ++++++++++++-------- test_regress/t/t_const_bitoptree_bug3096.cpp | 29 +++++++++++++++ test_regress/t/t_const_bitoptree_bug3096.pl | 22 ++++++++++++ test_regress/t/t_const_bitoptree_bug3096.v | 24 +++++++++++++ 5 files changed, 100 insertions(+), 14 deletions(-) create mode 100644 test_regress/t/t_const_bitoptree_bug3096.cpp create mode 100755 test_regress/t/t_const_bitoptree_bug3096.pl create mode 100644 test_regress/t/t_const_bitoptree_bug3096.v diff --git a/Changes b/Changes index aa60688ff..d56469b13 100644 --- a/Changes +++ b/Changes @@ -25,6 +25,7 @@ Verilator 4.211 devel * Refactored Verilated include files; include verilated.h not verilated_heavy.h. * Fix -G to treat simple integer literals as signed (#3060). [Anikin1610] * Fix emitted string array initializers (#2895). [Iztok Jeras] +* Fix bitop tree optimization dropping necessary & operator (#3096). [Flavien Solt] Verilator 4.210 2021-07-07 diff --git a/src/V3Const.cpp b/src/V3Const.cpp index dcf19c421..530a7936d 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -858,12 +858,23 @@ private: } bool matchBitOpTree(AstNode* nodep) { + if (nodep->widthMin() != 1) return false; if (!v3Global.opt.oConstBitOpTree()) return false; + string debugPrefix; + if (debug() >= 9) { // LCOV_EXCL_START + static int c = 0; + debugPrefix = "matchBitOpTree["; + debugPrefix += cvtToStr(++c); + debugPrefix += "] "; + nodep->dumpTree(debugPrefix + "INPUT: "); + } // LCOV_EXCL_STOP + AstNode* newp = nullptr; bool tried = false; - if (AstAnd* andp = VN_CAST(nodep, And)) { // 1 & BitOpTree - if (AstConst* bitMaskp = VN_CAST(andp->lhsp(), Const)) { + if (AstAnd* const andp = VN_CAST(nodep, And)) { // 1 & BitOpTree + AstConst* const bitMaskp = VN_CAST(andp->lhsp(), Const); + if (bitMaskp && andp->rhsp()->widthMin() == 1) { if (bitMaskp->num().toUQuad() != 1) return false; newp = ConstBitOpTreeVisitor::simplify(andp->rhsp(), 1, m_statBitOpReduction); tried = true; @@ -876,17 +887,16 @@ private: } if (newp) { UINFO(4, "Transformed leaf of bit tree to " << newp << std::endl); - if (debug() >= 9) { // LCOV_EXCL_START - static int c = 0; - std::cout << "Call matchBitOpTree[" << c << "]\n"; - nodep->dumpTree(std::cout); - std::cout << "\nResult:\n"; - newp->dumpTree(std::cout); - ++c; - } // LCOV_EXCL_STOP nodep->replaceWith(newp); VL_DO_DANGLING(nodep->deleteTree(), nodep); } + if (debug() >= 9) { // LCOV_EXCL_START + if (newp) { + newp->dumpTree(debugPrefix + "RESULT: "); + } else { + cout << debugPrefix << "not replaced" << endl; + } + } // LCOV_EXCL_STOP return newp; } static bool operandShiftSame(const AstNode* nodep) { @@ -3101,7 +3111,7 @@ private: TREEOPV("AstRedOr {$lhsp.castExtend}", "AstRedOr {$lhsp->castExtend()->lhsp()}"); TREEOPV("AstRedXor{$lhsp.castExtend}", "AstRedXor{$lhsp->castExtend()->lhsp()}"); TREEOP ("AstRedXor{$lhsp.castXor, VN_IS(VN_CAST($lhsp,,Xor)->lhsp(),,Const)}", "AstXor{AstRedXor{$lhsp->castXor()->lhsp()}, AstRedXor{$lhsp->castXor()->rhsp()}}"); // ^(const ^ a) => (^const)^(^a) - TREEOPC("AstAnd {nodep->widthMin() == 1, $lhsp.castConst, $rhsp.castRedXor, matchBitOpTree(nodep)}", "DONE"); + TREEOPC("AstAnd {$lhsp.castConst, $rhsp.castRedXor, matchBitOpTree(nodep)}", "DONE"); TREEOPV("AstOneHot{$lhsp.width1}", "replaceWLhs(nodep)"); TREEOPV("AstOneHot0{$lhsp.width1}", "replaceNum(nodep,1)"); // Binary AND/OR is faster than logical and/or (usually) @@ -3125,9 +3135,9 @@ private: TREEOP ("AstAnd {operandShiftSame(nodep)}", "replaceShiftSame(nodep)"); TREEOP ("AstOr {operandShiftSame(nodep)}", "replaceShiftSame(nodep)"); TREEOP ("AstXor {operandShiftSame(nodep)}", "replaceShiftSame(nodep)"); - TREEOPC("AstAnd {nodep->widthMin() == 1, matchBitOpTree(nodep)}", "DONE"); - TREEOPC("AstOr {nodep->widthMin() == 1, matchBitOpTree(nodep)}", "DONE"); - TREEOPC("AstXor {nodep->widthMin() == 1, matchBitOpTree(nodep)}", "DONE"); + TREEOPC("AstAnd {matchBitOpTree(nodep)}", "DONE"); + TREEOPC("AstOr {matchBitOpTree(nodep)}", "DONE"); + TREEOPC("AstXor {matchBitOpTree(nodep)}", "DONE"); // Note can't simplify a extend{extends}, extends{extend}, as the sign // bits end up in the wrong places TREEOPV("AstExtend {$lhsp.castExtend}", "replaceExtend(nodep, VN_CAST(nodep->lhsp(), Extend)->lhsp())"); diff --git a/test_regress/t/t_const_bitoptree_bug3096.cpp b/test_regress/t/t_const_bitoptree_bug3096.cpp new file mode 100644 index 000000000..27a3ca385 --- /dev/null +++ b/test_regress/t/t_const_bitoptree_bug3096.cpp @@ -0,0 +1,29 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// +// Copyright 2021 by Geza Lore. 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 +// +//************************************************************************* + +#include +#include + +#include + +int main(int argc, char* argv[]) { + Vt_const_bitoptree_bug3096* const tb = new Vt_const_bitoptree_bug3096; + + tb->instr_i = 0x08c0006f; + tb->eval(); + + std::cout << "tb->illegal_instr_o: " << static_cast(tb->illegal_instr_o) << std::endl + << std::flush; + assert(tb->illegal_instr_o == 0); + + delete tb; + return 0; +} diff --git a/test_regress/t/t_const_bitoptree_bug3096.pl b/test_regress/t/t_const_bitoptree_bug3096.pl new file mode 100755 index 000000000..16e215620 --- /dev/null +++ b/test_regress/t/t_const_bitoptree_bug3096.pl @@ -0,0 +1,22 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2015 by Todd Strader. 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 + +scenarios(vlt_all => 1); + +compile( + make_top_shell => 0, + make_main => 0, + v_flags2 => ["--exe $Self->{t_dir}/$Self->{name}.cpp"], + ); + +execute(); + +ok(1); +1; diff --git a/test_regress/t/t_const_bitoptree_bug3096.v b/test_regress/t/t_const_bitoptree_bug3096.v new file mode 100644 index 000000000..2f3e42f97 --- /dev/null +++ b/test_regress/t/t_const_bitoptree_bug3096.v @@ -0,0 +1,24 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// Copyright 2021 by Geza Lore. 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 + +// From issue #3096 + +module decoder( + input wire [31:0] instr_i, + // Making 'a' an output preserves it as a sub-expression and causes a missing clean + output wire a, + output wire illegal_instr_o + ); + /* verilator lint_off WIDTH */ + wire b = ! instr_i[12:5]; + wire c = ! instr_i[1:0]; + wire d = ! instr_i[15:13]; + /* verilator lint_on WIDTH */ + assign a = d ? b : 1'h1; + assign illegal_instr_o = c ? a : 1'h0; +endmodule From 8f527a0c6e0c93d2914b0f9b05e47e05c48af93f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ivan=20Vnu=C4=8Dec?= <50591550+IvanVnucec@users.noreply.github.com> Date: Wed, 18 Aug 2021 21:19:02 +0200 Subject: [PATCH 71/84] Commentary (#3102) Actually you can see `https://github.com/verilator/verilator` URL in your browser. --- docs/CONTRIBUTORS | 1 + docs/guide/install.rst | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/CONTRIBUTORS b/docs/CONTRIBUTORS index 999f1086a..bead98916 100644 --- a/docs/CONTRIBUTORS +++ b/docs/CONTRIBUTORS @@ -30,6 +30,7 @@ Harald Heckmann Howard Su Huang Rui HyungKi Jeong +Ivan Vnučec Iztok Jeras James Hanlon James Hutchinson diff --git a/docs/guide/install.rst b/docs/guide/install.rst index ab075cf7d..5b95f23e4 100644 --- a/docs/guide/install.rst +++ b/docs/guide/install.rst @@ -46,7 +46,6 @@ In brief, to install from git: #sudo apt-get install zlibc zlib1g zlib1g-dev # Ubuntu only (ignore if gives error) git clone https://github.com/verilator/verilator # Only first time - ## Note the URL above is not a page you can see with a browser, it's for git only # Every time you need to build: unsetenv VERILATOR_ROOT # For csh; ignore error if on bash From fffc63970de97298454dcd0eb3fa451753a23399 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Wed, 18 Aug 2021 10:54:45 +0100 Subject: [PATCH 72/84] Use narrowest RedXor --- src/V3Cast.cpp | 7 ++++--- src/V3EmitCFunc.h | 7 ++++++- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/V3Cast.cpp b/src/V3Cast.cpp index 0406149c8..de912c72e 100644 --- a/src/V3Cast.cpp +++ b/src/V3Cast.cpp @@ -155,9 +155,10 @@ private: } } virtual void visit(AstVarRef* nodep) override { - if (nodep->access().isReadOnly() && !VN_IS(nodep->backp(), CCast) - && VN_IS(nodep->backp(), NodeMath) && !VN_IS(nodep->backp(), ArraySel) - && nodep->backp()->width() && castSize(nodep) != castSize(nodep->varp())) { + AstNode* const backp = nodep->backp(); + if (nodep->access().isReadOnly() && !VN_IS(backp, CCast) && VN_IS(backp, NodeMath) + && !VN_IS(backp, ArraySel) && !VN_IS(backp, RedXor) && backp->width() + && castSize(nodep) != castSize(nodep->varp())) { // Cast vars to IData first, else below has upper bits wrongly set // CData x=3; out = (QData)(x<<30); insertCast(nodep, castSize(nodep)); diff --git a/src/V3EmitCFunc.h b/src/V3EmitCFunc.h index 03c2b30b7..144307b88 100644 --- a/src/V3EmitCFunc.h +++ b/src/V3EmitCFunc.h @@ -975,8 +975,13 @@ public: if (nodep->lhsp()->isWide()) { visit(VN_CAST(nodep, NodeUniop)); } else { + AstVarRef* const vrefp = VN_CAST(nodep->lhsp(), VarRef); + const int widthPow2 = vrefp ? vrefp->varp()->dtypep()->widthPow2() + : nodep->lhsp()->dtypep()->widthPow2(); + UASSERT_OBJ(widthPow2 > 1, nodep, + "Reduction over single bit value should have been folded"); putbs("VL_REDXOR_"); - puts(cvtToStr(nodep->lhsp()->dtypep()->widthPow2())); + puts(cvtToStr(widthPow2)); puts("("); iterateAndNextNull(nodep->lhsp()); puts(")"); From 8681861be9aa167cecbb267b10881dc8d87e1b83 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Wed, 18 Aug 2021 19:15:02 +0100 Subject: [PATCH 73/84] Improve bitop tree optimization - Remove redundant casting - Cheaper final XOR parity flip (~/^1 instead of != 0) - Support XOR reduction of ~XOR nodes - Don't add redundant masking of terms - Support unmasked terms - Add cheaper implementation for single bit only terms - Ensure result is clean under all circumstances (this fixes current bugs) --- src/V3Const.cpp | 684 ++++++++++++++++++++---------- src/V3Number.cpp | 15 + src/V3Number.h | 8 +- test_regress/t/t_const_opt_cov.pl | 2 +- test_regress/t/t_const_opt_red.pl | 2 +- test_regress/t/t_gate_ormux.pl | 2 +- 6 files changed, 472 insertions(+), 241 deletions(-) diff --git a/src/V3Const.cpp b/src/V3Const.cpp index 530a7936d..daec651bd 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -33,6 +33,7 @@ #include "V3UniqueNames.h" #include +#include //###################################################################### // Utilities @@ -77,18 +78,65 @@ public: bool found() const { return m_found; } }; +static bool isConst(const AstNode* nodep, uint64_t v) { + const AstConst* const constp = VN_CAST_CONST(nodep, Const); + return constp && constp->toUQuad() == v; +} + +template +static typename std::enable_if::value, bool>::type isPow2(T val) { + return (val & (val - 1)) == 0; +} + +static int countTrailingZeroes(uint64_t val) { + UASSERT(val, "countTrailingZeroes argument must be non-zero"); +#if defined(__GNUC__) && !defined(VL_NO_BUILTINS) + return __builtin_ctzll(val); +#else + int bit = 0; + val = ~val; + while (val & 1) { + ++bit; + val >>= 1; + } + return bit; +#endif +} + // This visitor can be used in the post-expanded Ast from V3Expand, where the Ast satisfies: // - Constants are 64 bit at most (because words are accessed via AstWordSel) // - Variables are scoped. class ConstBitOpTreeVisitor final : public AstNVisitor { + // NODE STATE + // AstVarRef::user4u -> Base index of m_varInfos that points VarInfo + // AstVarScope::user4u -> Same as AstVarRef::user4 + AstUser4InUse m_inuser4; + // TYPES + // Holds a node to be added as a term in the reduction tree, it's equivalent op count, and a + // bool indicating if the term is clean (0/1 value, or if the top bits might be dirty) + using ResultTerm = std::tuple; + struct LeafInfo final { // Leaf node (either AstConst or AstVarRef) bool m_polarity = true; int m_lsb = 0; int m_wordIdx = -1; // -1 means AstWordSel is not used. AstVarRef* m_refp = nullptr; AstConst* m_constp = nullptr; + + int width() const { + UASSERT(m_refp, "m_refp should be set"); + const int width = m_refp->varp()->widthMin(); + if (!m_refp->isWide()) { + UASSERT(m_wordIdx == -1, "Bad word index into non-wide"); + return width; + } else { + UASSERT(m_wordIdx >= 0, "Bad word index into wide"); + const int bitsInMSW = VL_BITBIT_E(width) ? VL_BITBIT_E(width) : VL_EDATASIZE; + return m_wordIdx == m_refp->widthWords() - 1 ? bitsInMSW : VL_EDATASIZE; + } + } }; struct BitPolarityEntry final { // Found bit polarity during iterate() @@ -106,7 +154,7 @@ class ConstBitOpTreeVisitor final : public AstNVisitor { ConstBitOpTreeVisitor& m_visitor; const size_t m_polaritiesSize; const size_t m_frozenSize; - const int m_ops; + const unsigned m_ops; const bool m_polarity; bool m_restore; @@ -138,95 +186,145 @@ class ConstBitOpTreeVisitor final : public AstNVisitor { // Collect information for each Variable to transform as below class VarInfo final { // MEMBERS - int m_constResult = -1; // -1: result is not constant, 0 or 1: result of this tree - ConstBitOpTreeVisitor* m_parentp; // ConstBitOpTreeVisitor that holds this VarInfo - AstVarRef* m_refp; // Points the variable that this VarInfo covers + int m_knownResult = -1; // -1: result is not known, 0 or 1: result of this tree + ConstBitOpTreeVisitor* const m_parentp; // ConstBitOpTreeVisitor that holds this VarInfo + AstVarRef* const m_refp; // Points the variable that this VarInfo covers + const int m_width; // Width of term this VarInfo refers to V3Number m_bitPolarity; // Coefficient of each bit public: // METHODS - bool hasConstantResult() const { return m_constResult >= 0; } + bool hasConstResult() const { return m_knownResult >= 0 || m_bitPolarity.isAllX(); } + // The constant result. Only valid if hasConstResult() returned true. + bool getConstResult() const { + // Note that this condition covers m_knownResult == -1 but m_bitPolarity.isAllX(), + // in which case the result is 0 + return m_knownResult == 1; + } + const AstVarRef* refp() const { return m_refp; } bool sameVarAs(const AstNodeVarRef* otherp) const { return m_refp->same(otherp); } void setPolarity(bool compBit, int bit) { - UASSERT_OBJ(!hasConstantResult(), m_refp, "Already has result of " << m_constResult); - UASSERT_OBJ(bit < VL_QUADSIZE, m_refp, - "bit:" << bit << " is too big after V3Expand" - << " back:" << m_refp->backp()); - if (bit >= m_bitPolarity.width()) { // Need to expand m_bitPolarity - const V3Number oldPol = std::move(m_bitPolarity); - // oldPol.width() is 8, 16, or 32 because this visitor is called after V3Expand - // newWidth is increased by 2x because - // - CCast will cast to such bitwidth anyway - // - can avoid frequent expansion - int newWidth = oldPol.width(); - while (bit >= newWidth) newWidth *= 2; - m_bitPolarity = V3Number{m_refp, newWidth}; - UASSERT_OBJ(newWidth == 16 || newWidth == 32 || newWidth == 64, m_refp, - "bit:" << bit << " newWidth:" << newWidth); - m_bitPolarity.setAllBitsX(); - for (int i = 0; i < oldPol.width(); ++i) { - if (oldPol.bitIs0(i)) { - m_bitPolarity.setBit(i, '0'); - } else if (oldPol.bitIs1(i)) { - m_bitPolarity.setBit(i, '1'); - } - } - } - UASSERT_OBJ(bit < m_bitPolarity.width(), m_refp, - "bit:" << bit << " width:" << m_bitPolarity.width() << m_refp); - if (m_bitPolarity.bitIsX(bit)) { // The bit is not yet set + // Ignore if already determined a known reduction + if (m_knownResult >= 0) return; + UASSERT_OBJ(bit < m_width, m_refp, + "Bit index out of range: " << bit << " width: " << m_width); + if (m_bitPolarity.bitIsX(bit)) { // The bit is not yet marked with either polarity m_bitPolarity.setBit(bit, compBit); - } else { // Priviously set the bit + } else { // The bit has already been marked with some polarity const bool sameFlag = m_bitPolarity.bitIs1(bit) == compBit; if (m_parentp->isXorTree()) { - // ^{x[0], ~x[0], x[2], x[3]} === ~^{x[2], x[3]} - UASSERT_OBJ(sameFlag, m_refp, "Only true is set in Xor tree"); + UASSERT_OBJ(compBit && sameFlag, m_refp, "Only true is set in Xor tree"); + // a ^ a ^ b == b so we can ignore a m_bitPolarity.setBit(bit, 'x'); } else { // And, Or - // Can ignore this nodep as the bit is already registered + // Can ignore this nodep as the bit is already marked with the same polarity if (sameFlag) return; // a & a == a, b | b == b - // Otherwise result is constant - m_constResult = m_parentp->isAndTree() ? 0 : 1; + // Otherwise result is constant (a & ~a == 0) or (a | ~a == 1) + m_knownResult = m_parentp->isAndTree() ? 0 : 1; m_bitPolarity.setAllBitsX(); // The variable is not referred anymore } } } - AstNode* getResult() const { - FileLine* fl = m_refp->fileline(); + + // Return reduction term for this VarInfo, together with the number of ops in the term, + // and a boolean indicating if the term is clean (1-bit vs multi-bit value) + ResultTerm getResultTerm() const { + UASSERT(!hasConstResult(), "getTerm on reduction that yields constant"); + FileLine* const fl = m_refp->fileline(); + + // Get the term we are referencing (the WordSel, if wide, otherwise just the VarRef) AstNode* srcp = VN_CAST(m_refp->backp(), WordSel); if (!srcp) srcp = m_refp; - const int width = m_bitPolarity.width(); + srcp = srcp->cloneTree(false); - if (hasConstantResult()) - return new AstConst{fl, - V3Number{srcp, width, static_cast(m_constResult)}}; + // Signed variables might have redundant sign bits that need masking. + const bool hasRedundantSignBits + = m_refp->varp()->dtypep()->isSigned() + && (m_refp->isWide() ? (m_width != VL_EDATASIZE) + : (m_width < 8 || !isPow2(m_width))); - AstConst* maskValuep = new AstConst{fl, V3Number{srcp, width, 0}}; - maskValuep->num().opBitsNonX(m_bitPolarity); // 'x' -> 0, 0->1, 1->1 - // Let AstConst be in lhs as it is the common convention - AstAnd* maskedp = new AstAnd{fl, maskValuep, srcp->cloneTree(false)}; - AstNode* resultp; - if (m_parentp->isXorTree()) { - resultp = new AstRedXor{fl, maskedp}; - resultp->dtypep()->widthForce(width, 1); + // Get the mask that selects the bits that are relevant in this term + V3Number maskNum{srcp, m_width, 0}; + maskNum.opBitsNonX(m_bitPolarity); // 'x' -> 0, 0->1, 1->1 + const uint64_t maskVal = maskNum.toUQuad(); + UASSERT(maskVal != 0, "Should have been recognized as having const 0 result"); + + // Parts of the return value + AstNode* resultp = srcp; // The tree for this term + unsigned ops = 0; // Number of ops in this term + bool clean = false; // Whether the term is clean (has value 0 or 1) + + if (isPow2(maskVal)) { + // If we only want a single bit, shift it out instead of a masked compare. Shifts + // don't go through the flags register on x86 and are hence faster. This is also + // always fewer or same ops as mask and compare, but with shorter instructions on + // x86. + + // Find the index of the bit we want. + const int bit = countTrailingZeroes(maskVal); + // If we want something other than the bottom bit, shift it out + if (bit != 0) { + resultp = new AstShiftR{fl, resultp, + new AstConst{fl, static_cast(bit)}, m_width}; + ++ops; + } + // Negate it if necessary + const bool negate = m_bitPolarity.bitIs0(bit); + if (negate) { + resultp = new AstNot{fl, resultp}; + ++ops; + } + // Clean if MSB of unsigned value, and not negated + clean = (bit == m_width - 1) && !hasRedundantSignBits && !negate; } else { - AstConst* compValuep = maskValuep->cloneTree(false); - compValuep->num().opBitsOne(m_bitPolarity); // 'x'->0, 0->0, 1->1 - if (m_parentp->isAndTree()) { - resultp = new AstEq{fl, compValuep, maskedp}; + // We want multiple bits. Go ahead and extract them. + + // Check if masking is required, and if so apply it + const bool needsMasking = maskVal != VL_MASK_Q(m_width) || hasRedundantSignBits; + if (needsMasking) { + resultp = new AstAnd{fl, new AstConst{fl, maskNum}, resultp}; + ++ops; + } + + // Create the sub-expression for this term + if (m_parentp->isXorTree()) { + if (needsMasking) { + // Reduce the masked term to the minimum known width, + // to use the smallest RedXor formula + const int widthMin = maskNum.widthMin(); + resultp->dtypeChgWidth(widthMin, widthMin); + } + resultp = new AstRedXor{fl, resultp}; + ++ops; + clean = false; + // VL_REDXOR_* returns IData, set width accordingly to avoid unnecessary casts + resultp->dtypeChgWidth(VL_IDATASIZE, 1); + } else if (m_parentp->isAndTree()) { + V3Number compNum{srcp, m_width, 0}; + compNum.opBitsOne(m_bitPolarity); // 'x'->0, 0->0, 1->1 + resultp = new AstEq{fl, new AstConst{fl, compNum}, resultp}; + ++ops; + clean = true; } else { // Or - compValuep->num().opXor(V3Number{compValuep->num()}, maskValuep->num()); - resultp = new AstNeq{fl, compValuep, maskedp}; + V3Number compNum{srcp, m_width, 0}; + compNum.opBitsOne(m_bitPolarity); // 'x'->0, 0->0, 1->1 + compNum.opXor(V3Number{compNum}, maskNum); + resultp = new AstNeq{fl, new AstConst{fl, compNum}, resultp}; + ++ops; + clean = true; } } - return resultp; + + return ResultTerm{resultp, ops, clean}; } + public: // CONSTRUCTORS - VarInfo(ConstBitOpTreeVisitor* parent, AstVarRef* refp) + VarInfo(ConstBitOpTreeVisitor* parent, AstVarRef* refp, int width) : m_parentp{parent} , m_refp{refp} - , m_bitPolarity{refp, refp->isWide() ? VL_EDATASIZE : refp->width()} { + , m_width{width} + , m_bitPolarity{refp, m_width} { m_bitPolarity.setAllBitsX(); } }; @@ -234,21 +332,15 @@ class ConstBitOpTreeVisitor final : public AstNVisitor { // MEMBERS bool m_failed = false; bool m_polarity = true; // Flip when AstNot comes - int m_ops = 0; // Number of operations such as And, Or, Xor, Sel... + unsigned m_ops; // Number of operations such as And, Or, Xor, Sel... int m_lsb = 0; // Current LSB LeafInfo* m_leafp = nullptr; // AstConst or AstVarRef that currently looking for - AstNode* m_rootp; // Root of this AST subtree - AstNode* m_curOpp = nullptr; // The node that should be added to m_frozenNodes + AstNode* const m_rootp; // Root of this AST subtree - AstUser4InUse m_inuser4; std::vector m_frozenNodes; // Nodes that cannot be optimized std::vector m_bitPolarities; // Polarity of bits found during iterate() std::vector> m_varInfos; // VarInfo for each variable, [0] is nullptr - // NODE STATE - // AstVarRef::user4u -> Base index of m_varInfos that points VarInfo - // AstVarScope::user4u -> Same as AstVarRef::user4 - // METHODS VL_DEBUG_FUNC; // Declare debug() @@ -266,8 +358,8 @@ class ConstBitOpTreeVisitor final : public AstNVisitor { UINFO(9, "cannot optimize " << m_rootp << " reason:" << reason << " called from line:" << line << " when checking:" << nodep << std::endl); // if (debug() >= 9) m_rootp->dumpTree(std::cout << "Root node:\n"); + m_failed = true; } - m_failed |= fail; return m_failed; } void incrOps(const AstNode* nodep, int line) { @@ -289,7 +381,7 @@ class ConstBitOpTreeVisitor final : public AstNVisitor { const size_t idx = baseIdx + std::max(0, ref.m_wordIdx); VarInfo* varInfop = m_varInfos[idx].get(); if (!varInfop) { - varInfop = new VarInfo{this, ref.m_refp}; + varInfop = new VarInfo{this, ref.m_refp, ref.width()}; m_varInfos[idx].reset(varInfop); } else { if (!varInfop->sameVarAs(ref.m_refp)) @@ -315,17 +407,6 @@ class ConstBitOpTreeVisitor final : public AstNVisitor { } return ok ? info : LeafInfo{}; } - AstNode* combineTree(AstNode* lhsp, AstNode* rhsp) { - if (!lhsp) return rhsp; - if (isAndTree()) { - return new AstAnd(m_rootp->fileline(), lhsp, rhsp); - } else if (isOrTree()) { - return new AstOr(m_rootp->fileline(), lhsp, rhsp); - } else { - UASSERT_OBJ(isXorTree(), m_rootp, "must be either Xor or RedXor"); - return new AstXor(m_rootp->fileline(), lhsp, rhsp); - } - } // VISITORS virtual void visit(AstNode* nodep) override { @@ -376,134 +457,164 @@ class ConstBitOpTreeVisitor final : public AstNVisitor { m_leafp->m_lsb = m_lsb; } - virtual void visit(AstRedXor* nodep) override { // Expect '^(mask & v)' + virtual void visit(AstRedXor* nodep) override { Restorer restorer{*this}; CONST_BITOP_RETURN_IF(!VN_IS(m_rootp, Xor), nodep); - AstAnd* andp = VN_CAST(nodep->lhsp(), And); - CONST_BITOP_RETURN_IF(!andp, nodep->lhsp()); + AstNode* lhsp = nodep->lhsp(); + if (AstCCast* const castp = VN_CAST(lhsp, CCast)) lhsp = castp->lhsp(); + if (AstAnd* const andp = VN_CAST(lhsp, And)) { // '^(mask & leaf)' + CONST_BITOP_RETURN_IF(!andp, lhsp); - const auto mask = findLeaf(andp->lhsp(), true); - CONST_BITOP_RETURN_IF(!mask.m_constp || mask.m_lsb != 0, andp->lhsp()); + const LeafInfo& mask = findLeaf(andp->lhsp(), true); + CONST_BITOP_RETURN_IF(!mask.m_constp || mask.m_lsb != 0, andp->lhsp()); - const LeafInfo leaf = findLeaf(andp->rhsp(), false); - CONST_BITOP_RETURN_IF(!leaf.m_refp, andp->rhsp()); + const LeafInfo& ref = findLeaf(andp->rhsp(), false); + CONST_BITOP_RETURN_IF(!ref.m_refp, andp->rhsp()); - restorer.disableRestore(); // Now all subtree succeeded + restorer.disableRestore(); // Now all subtree succeeded - incrOps(nodep, __LINE__); - incrOps(andp, __LINE__); - const V3Number& maskNum = mask.m_constp->num(); - for (int i = 0; i < maskNum.width(); ++i) { - // Set true, m_treePolarity takes care of the entire parity - if (maskNum.bitIs1(i)) m_bitPolarities.emplace_back(leaf, true, i + leaf.m_lsb); + const V3Number& maskNum = mask.m_constp->num(); + + incrOps(nodep, __LINE__); + incrOps(andp, __LINE__); + + // Mark all bits checked in this reduction + const int maxBitIdx = std::min(ref.m_lsb + maskNum.width(), ref.width()); + for (int bitIdx = ref.m_lsb; bitIdx < maxBitIdx; ++bitIdx) { + const int maskIdx = bitIdx - ref.m_lsb; + if (maskNum.bitIs0(maskIdx)) continue; + // Set true, m_polarity takes care of the entire parity + m_bitPolarities.emplace_back(ref, true, bitIdx); + } + } else { // '^leaf' + const LeafInfo& ref = findLeaf(lhsp, false); + CONST_BITOP_RETURN_IF(!ref.m_refp, lhsp); + + restorer.disableRestore(); // Now all checks passed + + incrOps(nodep, __LINE__); + + // Mark all bits checked by this comparison + for (int bitIdx = ref.m_lsb; bitIdx < ref.width(); ++bitIdx) { + m_bitPolarities.emplace_back(ref, true, bitIdx); + } } } virtual void visit(AstNodeBiop* nodep) override { - const auto isConst = [](AstNode* nodep, vluint64_t v) -> bool { - AstConst* constp = VN_CAST(nodep, Const); - return constp && constp->toUQuad() == v; - }; - if (nodep->type() == m_rootp->type()) { // And, Or, Xor - CONST_BITOP_RETURN_IF(!m_polarity && isXorTree(), nodep); - incrOps(nodep, __LINE__); - VL_RESTORER(m_curOpp); - VL_RESTORER(m_leafp); - - for (const bool& right : {false, true}) { - Restorer restorer{*this}; - LeafInfo leafInfo; - m_leafp = &leafInfo; - m_curOpp = right ? nodep->rhsp() : nodep->lhsp(); - const bool origFailed = m_failed; - iterate(m_curOpp); - if (leafInfo.m_constp || m_failed) { - // Rvert changes in leaf - restorer.restoreNow(); - m_frozenNodes.push_back(m_curOpp); - m_failed = origFailed; - continue; - } - restorer.disableRestore(); // Now all checks passed - if (leafInfo.m_refp) - m_bitPolarities.emplace_back(leafInfo, isXorTree() || leafInfo.m_polarity, - leafInfo.m_lsb); - } - return; - } else if (VN_IS(m_rootp, Xor) && VN_IS(nodep, Eq) && isConst(nodep->lhsp(), 0) - && VN_IS(nodep->rhsp(), And)) { // 0 == (1 & RedXor) - Restorer restorer{*this}; - AstAnd* andp = static_cast(nodep->rhsp()); // already checked above - CONST_BITOP_RETURN_IF(!isConst(andp->lhsp(), 1), andp->lhsp()); - AstRedXor* redXorp = VN_CAST(andp->rhsp(), RedXor); - CONST_BITOP_RETURN_IF(!redXorp, andp->rhsp()); - incrOps(nodep, __LINE__); - incrOps(andp, __LINE__); - m_polarity = !m_polarity; - iterate(redXorp); - CONST_BITOP_RETURN_IF(m_failed, redXorp); - - restorer.disableRestore(); // Now all checks passed - return; - } else if (VN_IS(m_rootp, Xor) && VN_IS(nodep, And) && isConst(nodep->lhsp(), 1) - && (VN_IS(nodep->rhsp(), Xor) - || VN_IS(nodep->rhsp(), RedXor))) { // 1 & (v[3] ^ v[2]) + if (VN_IS(nodep, And) && isConst(nodep->lhsp(), 1)) { // 1 & _ + // Always reach past a plain making AND Restorer restorer{*this}; incrOps(nodep, __LINE__); iterate(nodep->rhsp()); CONST_BITOP_RETURN_IF(m_failed, nodep->rhsp()); restorer.disableRestore(); // Now all checks passed - return; + } else if (nodep->type() == m_rootp->type()) { // And, Or, Xor + incrOps(nodep, __LINE__); + VL_RESTORER(m_leafp); + + for (const bool right : {false, true}) { + Restorer restorer{*this}; + LeafInfo leafInfo; + m_leafp = &leafInfo; + AstNode* opp = right ? nodep->rhsp() : nodep->lhsp(); + const bool origFailed = m_failed; + iterate(opp); + if (leafInfo.m_constp || m_failed) { + // Revert changes in leaf + restorer.restoreNow(); + // Reach past a cast then add to frozen nodes to be added to final reduction + if (AstCCast* const castp = VN_CAST(opp, CCast)) opp = castp->lhsp(); + m_frozenNodes.push_back(opp); + m_failed = origFailed; + continue; + } + restorer.disableRestore(); // Now all checks passed + if (leafInfo.m_refp) { + // The conditional on the lsb being in range is necessary for some degenerate + // case, e.g.: (IData)((QData)wide[0] >> 32), or <1-bit-var> >> 1, which is + // just zero + if (leafInfo.m_lsb < leafInfo.width()) { + m_bitPolarities.emplace_back(leafInfo, isXorTree() || leafInfo.m_polarity, + leafInfo.m_lsb); + } else if (isAndTree()) { + // If there is a constant 0 term in an And tree, we must include it. Fudge + // this by adding a bit with both polarities, which will simplify to zero + m_bitPolarities.emplace_back(leafInfo, true, 0); + m_bitPolarities.emplace_back(leafInfo, false, 0); + } + } + } } else if ((isAndTree() && VN_IS(nodep, Eq)) || (isOrTree() && VN_IS(nodep, Neq))) { Restorer restorer{*this}; CONST_BITOP_RETURN_IF(!m_polarity, nodep); + AstNode* lhsp = nodep->lhsp(); + if (AstCCast* const castp = VN_CAST(lhsp, CCast)) lhsp = castp->lhsp(); + AstConst* const constp = VN_CAST(lhsp, Const); + CONST_BITOP_RETURN_IF(!constp, nodep->lhsp()); + const bool maskFlip = isOrTree(); - const LeafInfo comp = findLeaf(nodep->lhsp(), true); - CONST_BITOP_RETURN_IF(!comp.m_constp || comp.m_lsb != 0, nodep->lhsp()); + const V3Number& compNum = constp->num(); - AstAnd* andp = VN_CAST(nodep->rhsp(), And); // comp == (mask & v) - CONST_BITOP_RETURN_IF(!andp, nodep->rhsp()); + if (AstAnd* const andp = VN_CAST(nodep->rhsp(), And)) { // comp == (mask & v) + const LeafInfo& mask = findLeaf(andp->lhsp(), true); + CONST_BITOP_RETURN_IF(!mask.m_constp || mask.m_lsb != 0, andp->lhsp()); - const LeafInfo mask = findLeaf(andp->lhsp(), true); - CONST_BITOP_RETURN_IF(!mask.m_constp || mask.m_lsb != 0, andp->lhsp()); + const LeafInfo& ref = findLeaf(andp->rhsp(), false); + CONST_BITOP_RETURN_IF(!ref.m_refp, andp->rhsp()); - const LeafInfo ref = findLeaf(andp->rhsp(), false); - CONST_BITOP_RETURN_IF(!ref.m_refp, andp->rhsp()); + restorer.disableRestore(); // Now all checks passed - restorer.disableRestore(); // Now all checks passed + const V3Number& maskNum = mask.m_constp->num(); - const V3Number maskNum = mask.m_constp->num(); - const V3Number compNum = comp.m_constp->num(); - for (int i = 0; i < maskNum.width(); ++i) { - const int bit = i + ref.m_lsb; - if (maskNum.bitIs0(i)) continue; - m_bitPolarities.emplace_back(ref, compNum.bitIs1(i) != maskFlip, bit); + incrOps(nodep, __LINE__); + incrOps(andp, __LINE__); + + // Mark all bits checked by this comparison + const int maxBitIdx = std::min(ref.m_lsb + compNum.width(), ref.width()); + for (int bitIdx = ref.m_lsb; bitIdx < maxBitIdx; ++bitIdx) { + const int maskIdx = bitIdx - ref.m_lsb; + if (maskNum.bitIs0(maskIdx)) continue; + const bool polarity = compNum.bitIs1(maskIdx) != maskFlip; + m_bitPolarities.emplace_back(ref, polarity, bitIdx); + } + } else { // comp == v + const LeafInfo& ref = findLeaf(nodep->rhsp(), false); + CONST_BITOP_RETURN_IF(!ref.m_refp, nodep->rhsp()); + + restorer.disableRestore(); // Now all checks passed + + incrOps(nodep, __LINE__); + + // Mark all bits checked by this comparison + const int maxBitIdx = std::min(ref.m_lsb + compNum.width(), ref.width()); + for (int bitIdx = ref.m_lsb; bitIdx < maxBitIdx; ++bitIdx) { + const int maskIdx = bitIdx - ref.m_lsb; + const bool polarity = compNum.bitIs1(maskIdx) != maskFlip; + m_bitPolarities.emplace_back(ref, polarity, bitIdx); + } } - incrOps(nodep, __LINE__); - incrOps(andp, __LINE__); - return; + } else { + CONST_BITOP_SET_FAILED("Mixture of different ops cannot be optimized", nodep); } - CONST_BITOP_SET_FAILED("Mixture of different ops cannot be optimized", nodep); } // CONSTRUCTORS - ConstBitOpTreeVisitor(AstNode* nodep, int ops) - : m_ops{ops} + ConstBitOpTreeVisitor(AstNode* nodep, unsigned externalOps) + : m_ops{externalOps} , m_rootp{nodep} { // Fill nullptr at [0] because AstVarScope::user4 is 0 by default m_varInfos.push_back(nullptr); CONST_BITOP_RETURN_IF(!isAndTree() && !isOrTree() && !isXorTree(), nodep); - AstNode::user4ClearTree(); - if (AstNodeBiop* biopp = VN_CAST(nodep, NodeBiop)) { + if (AstNodeBiop* const biopp = VN_CAST(nodep, NodeBiop)) { iterate(biopp); } else { + UASSERT_OBJ(VN_IS(nodep, RedXor), nodep, "Must be RedXor"); incrOps(nodep, __LINE__); iterateChildren(nodep); } for (auto&& entry : m_bitPolarities) { - VarInfo& info = getVarInfo(entry.m_info); - if (info.hasConstantResult()) continue; - info.setPolarity(entry.m_polarity, entry.m_bit); + getVarInfo(entry.m_info).setPolarity(entry.m_polarity, entry.m_bit); } UASSERT_OBJ(isXorTree() || m_polarity, nodep, "must be the original polarity"); } @@ -520,60 +631,173 @@ public: // (3'b000 != (3'b011 & v)) | v[2] => 3'b000 != (3'b111 & v) // Reduction ops are transformed in the same way. // &{v[0], v[1]} => 2'b11 == (2'b11 & v) - static AstNode* simplify(AstNode* nodep, int ops, VDouble0& reduction) { - ConstBitOpTreeVisitor visitor{nodep, ops}; + static AstNode* simplify(AstNode* nodep, int resultWidth, unsigned externalOps, + VDouble0& reduction) { + UASSERT_OBJ(1 <= resultWidth && resultWidth <= 64, nodep, "resultWidth out of range"); + + // Walk tree, gathering all terms referenced in expression + ConstBitOpTreeVisitor visitor{nodep, externalOps}; + + // If failed on root node is not optimizable, or there are no variable terms, then done if (visitor.m_failed || visitor.m_varInfos.size() == 1) return nullptr; - // Two ops for each varInfo. (And and Eq) - int vars = 0; - int constTerms = 0; + // FileLine used for constructing all new nodes in this function + FileLine* const fl = nodep->fileline(); + + // Get partial result each term referenced, count total number of ops and keep track of + // whether we have clean/dirty terms. visitor.m_varInfos appears in deterministic order, + // so the optimized tree is deterministic as well. + + std::vector termps; + termps.reserve(visitor.m_varInfos.size() - 1); + unsigned resultOps = 0; + bool hasCleanTerm = false; + bool hasDirtyTerm = false; + for (auto&& v : visitor.m_varInfos) { - if (!v) continue; - ++vars; - if (v->hasConstantResult()) ++constTerms; - } - // Expected number of ops after this simplification - // e.g. (comp0 == (mask0 & var0)) & (comp1 == (mask1 & var1)) & .... - // e.g. redXor(mask1 & var0) ^ redXor(mask1 & var1) - // 2 ops per variables, numVars - 1 ops among variables - int expOps = 2 * (vars - constTerms) + vars - 1; - expOps += visitor.m_frozenNodes.size(); - if (visitor.isXorTree()) { - ++expOps; // AstRedXor::cleanOut() == false, so need 1 & redXor - if (!visitor.m_polarity) ++expOps; // comparison with 0 - } - if (visitor.m_ops <= expOps) return nullptr; // Unless benefitial, return - - reduction += visitor.m_ops - expOps; - - AstNode* resultp = nullptr; - // VarInfo in visitor.m_varInfos appears in deterministic order, - // so the optimized AST is deterministic too. - for (auto&& varinfop : visitor.m_varInfos) { - if (!varinfop) continue; - AstNode* partialresultp = varinfop->getResult(); - resultp = visitor.combineTree(resultp, partialresultp); - } - AstNode* frozensp = nullptr; - for (AstNode* frozenp : visitor.m_frozenNodes) { - frozenp->unlinkFrBack(); - frozensp = visitor.combineTree(frozensp, frozenp); - } - if (frozensp) resultp = visitor.combineTree(resultp, frozensp); - - if (visitor.isXorTree()) { - // VL_REDXOR_N functions don't guarantee to return only 0/1 - const int width = resultp->width(); - FileLine* fl = nodep->fileline(); - resultp = new AstAnd{fl, new AstConst{fl, V3Number{nodep, width, 1}}, resultp}; - if (!visitor.m_polarity) { - resultp = new AstEq{fl, new AstConst{fl, V3Number{nodep, width, 0}}, resultp}; - resultp->dtypep()->widthForce(1, 1); + if (!v) continue; // Skip nullptr at m_varInfos[0] + if (v->hasConstResult()) { + // If a constant term is known, we can either drop it or the whole tree is constant + AstNode* resultp = nullptr; + if (v->getConstResult()) { + UASSERT_OBJ(visitor.isOrTree(), nodep, + "Only OR tree can yield known 1 result"); + UINFO(9, "OR tree with const 1 term: " << v->refp() << endl); + // Known 1 bit in OR tree, whole result is 1 + resultp = new AstConst{fl, AstConst::BitTrue{}}; + } else if (visitor.isAndTree()) { + UINFO(9, "AND tree with const 0 term: " << v->refp() << endl); + // Known 0 bit in AND tree, whole result is 0 + resultp = new AstConst{fl, AstConst::BitFalse{}}; + } else { + // Known 0 bit in OR or XOR tree. Ignore it. + continue; + } + // Set width and widthMin precisely + resultp->dtypeChgWidth(resultWidth, 1); + for (AstNode* const termp : termps) termp->deleteTree(); + return resultp; + } + const ResultTerm result = v->getResultTerm(); + termps.push_back(std::get<0>(result)); + resultOps += std::get<1>(result); + if (std::get<2>(result)) { + hasCleanTerm = true; + UINFO(9, "Clean term: " << termps.back() << endl); + } else { + hasDirtyTerm = true; + UINFO(9, "Dirty term: " << termps.back() << endl); } } - if (resultp->width() != nodep->width()) { - resultp = new AstCCast{resultp->fileline(), resultp, nodep}; + + // Check if frozen terms are clean or not + for (AstNode* const termp : visitor.m_frozenNodes) { + // Comparison operators are clean + if (VN_IS(termp, Eq) || VN_IS(termp, Neq) || VN_IS(termp, Lt) || VN_IS(termp, Lte) + || VN_IS(termp, Gt) || VN_IS(termp, Gte)) { + hasCleanTerm = true; + } else { + // Otherwise, conservatively assume the frozen term is dirty + hasDirtyTerm = true; + UINFO(9, "Dirty frozen term: " << termp << endl); + } } + + // Figure out if a final negation is required + const bool needsFlip = visitor.isXorTree() && !visitor.m_polarity; + + // Figure out if the final tree needs cleaning + const bool needsCleaning = visitor.isAndTree() ? !hasCleanTerm : hasDirtyTerm; + + // Add size of reduction tree to op count + resultOps += termps.size() + visitor.m_frozenNodes.size() - 1; + // Add final polarity flip in Xor tree + if (needsFlip) ++resultOps; + // Add final cleaning AND + if (needsCleaning) ++resultOps; + + if (debug() >= 9) { // LCOV_EXCL_START + cout << "Bitop tree considered: " << endl; + for (AstNode* const termp : termps) termp->dumpTree("Reduced term: "); + for (AstNode* const termp : visitor.m_frozenNodes) termp->dumpTree("Frozen term: "); + cout << "Needs flipping: " << needsFlip << endl; + cout << "Needs cleaning: " << needsCleaning << endl; + cout << "Size: " << resultOps << " input size: " << visitor.m_ops << endl; + } // LCOV_EXCL_END + + // Sometimes we have no terms left after ignoring redundant terms + // (all of which were zeroes) + if (termps.empty() && visitor.m_frozenNodes.empty()) { + reduction += visitor.m_ops; + AstNode* const resultp = needsFlip ? new AstConst{fl, AstConst::BitTrue{}} + : new AstConst{fl, AstConst::BitFalse{}}; + resultp->dtypeChgWidth(resultWidth, 1); + return resultp; + } + + // Only substitute the result if beneficial as determined by operation count + if (visitor.m_ops <= resultOps) { + for (AstNode* const termp : termps) termp->deleteTree(); + return nullptr; + } + + // Update statistics + reduction += visitor.m_ops - resultOps; + + // Reduction op to combine terms + const auto reduce = [&visitor, fl](AstNode* lhsp, AstNode* rhsp) -> AstNode* { + if (!lhsp) return rhsp; + if (visitor.isAndTree()) { + return new AstAnd{fl, lhsp, rhsp}; + } else if (visitor.isOrTree()) { + return new AstOr{fl, lhsp, rhsp}; + } else { + return new AstXor{fl, lhsp, rhsp}; + } + }; + + // Compute result by reducing all terms + AstNode* resultp = nullptr; + for (AstNode* const termp : termps) { // + resultp = reduce(resultp, termp); + } + // Add any frozen terms to the reduction + for (AstNode* const frozenp : visitor.m_frozenNodes) { + resultp = reduce(resultp, frozenp->unlinkFrBack()); + } + + // Set width of masks to expected result width. This is required to prevent later removal + // of the masking node e.g. by the "AND with all ones" rule. If the result width happens + // to be 1, we still need to ensure the AstAnd is not dropped, so use a wider maks in this + // special case. + const int maskWidth = resultWidth == 1 ? VL_IDATASIZE : resultWidth; + + // Apply final polarity flip + if (needsFlip) { + if (needsCleaning) { + // Cleaning will be added below. Use a NOT which is a byte shorter on x86 + resultp = new AstNot{fl, resultp}; + } else { + // Keep result clean by using XOR(1, _) + AstConst* const maskp = new AstConst{fl, AstConst::WidthedValue{}, maskWidth, 1}; + resultp = new AstXor{fl, maskp, resultp}; + } + } + + // Apply final cleaning + if (needsCleaning) { + AstConst* const maskp = new AstConst{fl, AstConst::WidthedValue{}, maskWidth, 1}; + resultp = new AstAnd{fl, maskp, resultp}; + } + + // Cast back to original size if required + if (resultp->width() != resultWidth) { + resultp = new AstCCast{fl, resultp, resultWidth, 1}; + } + + // Set width and widthMin precisely + resultp->dtypeChgWidth(resultWidth, 1); + return resultp; } }; @@ -871,25 +1095,20 @@ private: } // LCOV_EXCL_STOP AstNode* newp = nullptr; - bool tried = false; - if (AstAnd* const andp = VN_CAST(nodep, And)) { // 1 & BitOpTree - AstConst* const bitMaskp = VN_CAST(andp->lhsp(), Const); - if (bitMaskp && andp->rhsp()->widthMin() == 1) { - if (bitMaskp->num().toUQuad() != 1) return false; - newp = ConstBitOpTreeVisitor::simplify(andp->rhsp(), 1, m_statBitOpReduction); - tried = true; - } - } - if (!tried) { - // (comp == BitOpTree) & BitOpTree - // (comp != BitOpTree) | BitOpTree - newp = ConstBitOpTreeVisitor::simplify(nodep, 0, m_statBitOpReduction); + const AstAnd* const andp = VN_CAST(nodep, And); + const int width = nodep->width(); + if (andp && isConst(andp->lhsp(), 1)) { // 1 & BitOpTree + newp = ConstBitOpTreeVisitor::simplify(andp->rhsp(), width, 1, m_statBitOpReduction); + } else { // BitOpTree + newp = ConstBitOpTreeVisitor::simplify(nodep, width, 0, m_statBitOpReduction); } + if (newp) { UINFO(4, "Transformed leaf of bit tree to " << newp << std::endl); nodep->replaceWith(newp); VL_DO_DANGLING(nodep->deleteTree(), nodep); } + if (debug() >= 9) { // LCOV_EXCL_START if (newp) { newp->dumpTree(debugPrefix + "RESULT: "); @@ -897,6 +1116,7 @@ private: cout << debugPrefix << "not replaced" << endl; } } // LCOV_EXCL_STOP + return newp; } static bool operandShiftSame(const AstNode* nodep) { diff --git a/src/V3Number.cpp b/src/V3Number.cpp index 64d5a8fe2..595830d27 100644 --- a/src/V3Number.cpp +++ b/src/V3Number.cpp @@ -895,6 +895,21 @@ uint8_t V3Number::dataByte(int byte) const { return (edataWord(byte / (VL_EDATASIZE / 8)) >> ((byte * 8) % VL_EDATASIZE)) & 0xff; } +bool V3Number::isAllZ() const { + for (int i = 0; i < width(); i++) { + if (!bitIsZ(i)) return false; + } + return true; +} +bool V3Number::isAllX() const { + uint32_t mask = hiWordMask(); + for (int i = words() - 1; i >= 0; --i) { + const ValueAndX v = m_value[i]; + if ((v.m_value & v.m_valueX) ^ mask) return false; + mask = ~0U; + } + return true; +} bool V3Number::isEqZero() const { for (int i = 0; i < words(); i++) { const ValueAndX v = m_value[i]; diff --git a/src/V3Number.h b/src/V3Number.h index 961b9c414..09b15e67b 100644 --- a/src/V3Number.h +++ b/src/V3Number.h @@ -319,12 +319,8 @@ public: } return false; } - bool isAllZ() const { - for (int i = 0; i < width(); i++) { - if (!bitIsZ(i)) return false; - } - return true; - } + bool isAllZ() const; + bool isAllX() const; bool isEqZero() const; bool isNeqZero() const; bool isBitsZero(int msb, int lsb) const; diff --git a/test_regress/t/t_const_opt_cov.pl b/test_regress/t/t_const_opt_cov.pl index 0a72e4f4f..a052e9321 100755 --- a/test_regress/t/t_const_opt_cov.pl +++ b/test_regress/t/t_const_opt_cov.pl @@ -19,7 +19,7 @@ execute( ); if ($Self->{vlt}) { - file_grep($Self->{stats}, qr/Optimizations, Const bit op reduction\s+(\d+)/i, 2); + file_grep($Self->{stats}, qr/Optimizations, Const bit op reduction\s+(\d+)/i, 144); } ok(1); diff --git a/test_regress/t/t_const_opt_red.pl b/test_regress/t/t_const_opt_red.pl index b3fb2c994..666e5d478 100755 --- a/test_regress/t/t_const_opt_red.pl +++ b/test_regress/t/t_const_opt_red.pl @@ -19,7 +19,7 @@ execute( ); if ($Self->{vlt}) { - file_grep($Self->{stats}, qr/Optimizations, Const bit op reduction\s+(\d+)/i, 155); + file_grep($Self->{stats}, qr/Optimizations, Const bit op reduction\s+(\d+)/i, 158); } ok(1); diff --git a/test_regress/t/t_gate_ormux.pl b/test_regress/t/t_gate_ormux.pl index 996e05170..e0cf23b95 100755 --- a/test_regress/t/t_gate_ormux.pl +++ b/test_regress/t/t_gate_ormux.pl @@ -19,7 +19,7 @@ compile( ); if ($Self->{vlt}) { - file_grep($Self->{stats}, qr/Optimizations, Const bit op reduction\s+(\d+)/i, 994); + file_grep($Self->{stats}, qr/Optimizations, Const bit op reduction\s+(\d+)/i, 1058); } execute( From 34a0bb448ef9fe3b52aea76546e2357242405f55 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Thu, 19 Aug 2021 13:56:23 +0100 Subject: [PATCH 74/84] Improve V3MergeCond - Merge AstNodeIf nodes as well (not just assignment from AstCond) - Merge merged results recursively (optimizes nested conditionals/ifs) - Only checking mergeability once per node. - Don't add redundant masking - Duplicate cheap statements in both branches, if doing so yields a larger merge - Include reduced nodes before the starting conditional in the merge --- src/V3MergeCond.cpp | 372 ++++++++++++++++++++++++------- test_regress/t/t_optm_if_cond.pl | 2 +- 2 files changed, 287 insertions(+), 87 deletions(-) diff --git a/src/V3MergeCond.cpp b/src/V3MergeCond.cpp index 62ef02196..57b5dc511 100644 --- a/src/V3MergeCond.cpp +++ b/src/V3MergeCond.cpp @@ -40,6 +40,8 @@ // 'lhs = cond & value' is actually 'lhs = cond ? value : 1'd0' // 'lhs = cond' is actually 'lhs = cond ? 1'd1 : 1'd0'. // +// Also merges consecutive AstNodeIf statements with the same condition. +// //************************************************************************* #include "config_build.h" @@ -52,36 +54,38 @@ //###################################################################### +enum class Mergeable { + YES, // Tree can be merged + NO_COND_ASSIGN, // Tree cannot be merged because it contains an assignment to a condition + NO_IMPURE // Tree cannot be merged because it contains an impure node +}; + class CheckMergeableVisitor final : public AstNVisitor { private: // STATE - bool m_mergeable - = false; // State tracking whether tree being processed is a mergeable condition + bool m_condAssign = false; // Does this tree contain an assignment to a condition variable?? + bool m_impure = false; // Does this tree contain an impure node? // METHODS VL_DEBUG_FUNC; // Declare debug() - void clearMergeable(const AstNode* nodep, const char* reason) { - UASSERT_OBJ(m_mergeable, nodep, "Should have short-circuited traversal"); - m_mergeable = false; - UINFO(9, "Clearing mergeable on " << nodep << " due to " << reason << endl); - } - // VISITORS virtual void visit(AstNode* nodep) override { - if (!m_mergeable) return; + if (m_impure) return; // Clear if node is impure if (!nodep->isPure()) { - clearMergeable(nodep, "impure"); + UINFO(9, "Not mergeable due to impure node" << nodep << endl); + m_impure = true; return; } iterateChildrenConst(nodep); } virtual void visit(AstVarRef* nodep) override { - if (!m_mergeable) return; + if (m_impure || m_condAssign) return; // Clear if it's an LValue referencing a marked variable if (nodep->access().isWriteOrRW() && nodep->varp()->user1()) { - clearMergeable(nodep, "might modify condition"); + UINFO(9, "Not mergeable due assignment to condition" << nodep << endl); + m_condAssign = true; } } @@ -91,10 +95,17 @@ public: // Return false if this node should not be merged at all because: // - It contains an impure expression // - It contains an LValue referencing the condition - bool operator()(const AstNodeAssign* node) { - m_mergeable = true; - iterateChildrenConst(const_cast(node)); - return m_mergeable; + Mergeable operator()(const AstNode* node) { + m_condAssign = false; + m_impure = false; + iterateChildrenConst(const_cast(node)); + if (m_impure) { // Impure is stronger than cond assign + return Mergeable::NO_IMPURE; + } else if (m_condAssign) { + return Mergeable::NO_COND_ASSIGN; + } else { + return Mergeable::YES; + } } }; @@ -118,8 +129,10 @@ public: class MergeCondVisitor final : public AstNVisitor { private: // NODE STATE - // AstVar::user1 -> Flag set for variables referenced by m_mgCondp + // AstVar::user1 -> Flag set for variables referenced by m_mgCondp + // AstNode::user2 -> Flag marking node as included in merge because cheap to duplicate AstUser1InUse m_user1InUse; + AstUser2InUse m_user2InUse; // STATE VDouble0 m_statMerges; // Statistic tracking @@ -153,16 +166,57 @@ private: return nullptr; } - // Apply (_ & 1'b1). This is necessary because this pass is after V3Clean, - // and sometimes we have an AstAnd with a 1-bit condition on one side, but - // a more than 1-bit value on the other side, so we need to keep only the - // LSB. Ideally we would only do this iff the node is known not to be 1-bit - // wide, but working that out here is a bit difficult. As this masking is - // rarely required (only when trying to merge a "cond & value" with an - // earlier ternary), we will just always mask it for safety. + // Predicate to check if an expression yields only 0 or 1 (i.e.: a 1-bit value) + static bool yieldsOneOrZero(const AstNode* nodep) { + UASSERT_OBJ(!nodep->isWide(), nodep, "Cannot handle wide nodes"); + if (const AstConst* const constp = VN_CAST_CONST(nodep, Const)) { + return constp->num().toUQuad() <= 1; + } + if (const AstVarRef* const vrefp = VN_CAST_CONST(nodep, VarRef)) { + AstVar* const varp = vrefp->varp(); + return varp->widthMin() == 1 && !varp->dtypep()->isSigned(); + } + if (const AstShiftR* const shiftp = VN_CAST_CONST(nodep, ShiftR)) { + // Shift right by width - 1 or more + if (const AstConst* const constp = VN_CAST_CONST(shiftp->rhsp(), Const)) { + const AstVarRef* const vrefp = VN_CAST_CONST(shiftp->lhsp(), VarRef); + const int width = vrefp && !vrefp->varp()->dtypep()->isSigned() + ? vrefp->varp()->widthMin() + : shiftp->width(); + if (constp->toSInt() >= width - 1) return true; + } + return false; + } + if (VN_IS(nodep, Eq) || VN_IS(nodep, Neq) || VN_IS(nodep, Lt) || VN_IS(nodep, Lte) + || VN_IS(nodep, Gt) || VN_IS(nodep, Gte)) { + return true; + } + if (const AstNodeBiop* const biopp = VN_CAST_CONST(nodep, NodeBiop)) { + if (VN_IS(nodep, And)) + return yieldsOneOrZero(biopp->lhsp()) || yieldsOneOrZero(biopp->rhsp()); + if (VN_IS(nodep, Or) || VN_IS(nodep, Xor)) + return yieldsOneOrZero(biopp->lhsp()) && yieldsOneOrZero(biopp->rhsp()); + return false; + } + if (const AstNodeCond* const condp = VN_CAST_CONST(nodep, NodeCond)) { + return yieldsOneOrZero(condp->expr1p()) && yieldsOneOrZero(condp->expr2p()); + } + if (const AstCCast* const castp = VN_CAST_CONST(nodep, CCast)) { + // Cast never sign extends + return yieldsOneOrZero(castp->lhsp()); + } + return false; + } + + // Apply (1'b1 & _) cleaning mask if necessary. This is required because this pass is after + // V3Clean, and sometimes we have an AstAnd with a 1-bit condition on one side, but a more + // than 1-bit value on the other side, so we need to keep only the LSB. static AstNode* maskLsb(AstNode* nodep) { + if (yieldsOneOrZero(nodep)) return nodep; + // Otherwise apply masking AstNode* const maskp = new AstConst(nodep->fileline(), AstConst::BitTrue()); - return new AstAnd(nodep->fileline(), nodep, maskp); + // Mask on left, as conventional + return new AstAnd(nodep->fileline(), maskp, nodep); } // Fold the RHS expression assuming the given condition state. Unlink bits @@ -191,23 +245,47 @@ private: return condTrue ? maskLsb(andp->lhsp()->unlinkFrBack()) : new AstConst{rhsp->fileline(), AstConst::BitFalse()}; } + } else if (VN_IS(rhsp, WordSel) || VN_IS(rhsp, VarRef) || VN_IS(rhsp, Const)) { + return rhsp->cloneTree(false); } + rhsp->dumpTree("Don't know how to fold expression: "); rhsp->v3fatalSrc("Don't know how to fold expression"); } - void mergeEnd() { - UASSERT(m_mgFirstp, "mergeEnd without list"); + void mergeEnd(int lineno) { + UASSERT(m_mgFirstp, "mergeEnd without list " << lineno); + // We might want to recursively merge an AstIf. We stash it in this variable. + AstNodeIf* recursivep = nullptr; + // Drop leading cheap nodes. These were only added in the hope of finding + // an earlier reduced form, but we failed to do so. + while (m_mgFirstp->user2() && m_mgFirstp != m_mgLastp) { + AstNode* const backp = m_mgFirstp; + m_mgFirstp = m_mgFirstp->nextp(); + --m_listLenght; + UASSERT_OBJ(m_mgFirstp && m_mgFirstp->backp() == backp, m_mgLastp, + "The list should have a non-cheap element"); + } + // Drop trailing cheap nodes. These were only added in the hope of finding + // a later conditional to merge, but we failed to do so. + while (m_mgLastp->user2() && m_mgFirstp != m_mgLastp) { + AstNode* const nextp = m_mgLastp; + m_mgLastp = m_mgLastp->backp(); + --m_listLenght; + UASSERT_OBJ(m_mgLastp && m_mgLastp->nextp() == nextp, m_mgFirstp, + "Cheap assignment should not be at the front of the list"); + } // Merge if list is longer than one node if (m_mgFirstp != m_mgLastp) { UINFO(6, "MergeCond - First: " << m_mgFirstp << " Last: " << m_mgLastp << endl); ++m_statMerges; if (m_listLenght > m_statLongestList) m_statLongestList = m_listLenght; + // We need a copy of the condition in the new equivalent 'if' statement, + // and we also need to keep track of it for comparisons later. + m_mgCondp = m_mgCondp->cloneTree(false); // Create equivalent 'if' statement and insert it before the first node - AstIf* const ifp - = new AstIf(m_mgCondp->fileline(), m_mgCondp->unlinkFrBack(), nullptr, nullptr); - m_mgFirstp->replaceWith(ifp); - ifp->addNextHere(m_mgFirstp); + AstIf* const resultp = new AstIf(m_mgCondp->fileline(), m_mgCondp, nullptr, nullptr); + m_mgFirstp->addHereThisAsNext(resultp); // Unzip the list and insert under branches AstNode* nextp = m_mgFirstp; do { @@ -222,18 +300,37 @@ private: } // Count ++m_statMergedItems; - // Unlink RHS and clone to get the 2 assignments (reusing currp) - AstNodeAssign* const thenp = VN_CAST(currp, NodeAssign); - AstNode* const rhsp = thenp->rhsp()->unlinkFrBack(); - AstNodeAssign* const elsep = thenp->cloneTree(false); - // Construct the new RHSs and add to branches - thenp->rhsp(foldAndUnlink(rhsp, true)); - elsep->rhsp(foldAndUnlink(rhsp, false)); - ifp->addIfsp(thenp); - ifp->addElsesp(elsep); - // Cleanup - VL_DO_DANGLING(rhsp->deleteTree(), rhsp); + if (AstNodeAssign* const assignp = VN_CAST(currp, NodeAssign)) { + // Unlink RHS and clone to get the 2 assignments (reusing assignp) + AstNode* const rhsp = assignp->rhsp()->unlinkFrBack(); + AstNodeAssign* const thenp = assignp; + AstNodeAssign* const elsep = assignp->cloneTree(false); + // Construct the new RHSs and add to branches + thenp->rhsp(foldAndUnlink(rhsp, true)); + elsep->rhsp(foldAndUnlink(rhsp, false)); + resultp->addIfsp(thenp); + resultp->addElsesp(elsep); + // Cleanup + VL_DO_DANGLING(rhsp->deleteTree(), rhsp); + } else { + AstNodeIf* const ifp = VN_CAST(currp, NodeIf); + UASSERT_OBJ(ifp, currp, "Must be AstNodeIf"); + // Move branch contents under new if + if (AstNode* const listp = ifp->ifsp()) { + resultp->addIfsp(listp->unlinkFrBackWithNext()); + } + if (AstNode* const listp = ifp->elsesp()) { + resultp->addElsesp(listp->unlinkFrBackWithNext()); + } + // Cleanup + VL_DO_DANGLING(ifp->deleteTree(), ifp); + } } while (nextp); + // Recursively merge the resulting AstIf + recursivep = resultp; + } else if (AstNodeIf* const ifp = VN_CAST(m_mgFirstp, NodeIf)) { + // There was nothing to merge this AstNodeIf with, but try to merge it's branches + recursivep = ifp; } // Reset state m_mgFirstp = nullptr; @@ -241,82 +338,185 @@ private: m_mgLastp = nullptr; m_mgNextp = nullptr; m_markVars.clear(); + AstNode::user2ClearTree(); + // Merge recursively within the branches + if (recursivep) { + iterateAndNextNull(recursivep->ifsp()); + // Close list, if there is one at the end of the then branch + if (m_mgFirstp) mergeEnd(__LINE__); + iterateAndNextNull(recursivep->elsesp()); + // Close list, if there is one at the end of the else branch + if (m_mgFirstp) mergeEnd(__LINE__); + } } - void addToList(AstNode* nodep, AstNode* condp) { + // Check if the node can be simplified if included under the if + bool isSimplifiableNode(AstNode* nodep) { + UASSERT_OBJ(m_mgFirstp, nodep, "Cannot check with empty list"); + if (AstNodeAssign* const assignp = VN_CAST(nodep, NodeAssign)) { + // If it's an assignment to a 1-bit signal, try reduced forms + if (assignp->lhsp()->widthMin() == 1) { + // Is it a 'lhs = cond & value' or 'lhs = value & cond'? + if (AstAnd* const andp = VN_CAST(assignp->rhsp(), And)) { + if (andp->lhsp()->sameTree(m_mgCondp) || andp->rhsp()->sameTree(m_mgCondp)) { + return true; + } + } + // Is it simply 'lhs = cond'? + if (assignp->rhsp()->sameTree(m_mgCondp)) return true; + } + } + return false; + } + + // Check if this node is cheap enough that duplicating it in two branches of an + // AstIf and is hence not likely to cause a performance degradation if doing so. + bool isCheapNode(AstNode* nodep) const { + if (VN_IS(nodep, Comment)) return true; + if (AstNodeAssign* const assignp = VN_CAST(nodep, NodeAssign)) { + // Check LHS + AstNode* lhsp = assignp->lhsp(); + while (AstWordSel* const wselp = VN_CAST(lhsp, WordSel)) { + // WordSel index is not constant, so might be expensive + if (!VN_IS(wselp->bitp(), Const)) return false; + lhsp = wselp->fromp(); + } + // LHS is not a VarRef, so might be expensive + if (!VN_IS(lhsp, VarRef)) return false; + + // Check RHS + AstNode* rhsp = assignp->rhsp(); + while (AstWordSel* const wselp = VN_CAST(rhsp, WordSel)) { + // WordSel index is not constant, so might be expensive + if (!VN_IS(wselp->bitp(), Const)) return false; + rhsp = wselp->fromp(); + } + // RHS is not a VarRef or Constant so might be expensive + if (!VN_IS(rhsp, VarRef) && !VN_IS(rhsp, Const)) return false; + + // Otherwise it is a cheap assignment + return true; + } + return false; + } + + void addToList(AstNode* nodep, AstNode* condp, int line) { // Set up head of new list if node is first in list if (!m_mgFirstp) { - UASSERT_OBJ(condp, nodep, "Cannot start new list without condition"); + UASSERT_OBJ(condp, nodep, "Cannot start new list without condition " << line); m_mgFirstp = nodep; m_mgCondp = condp; m_listLenght = 0; m_markVars.mark(condp); + // Add any preceding nodes to the list that would allow us to extend the merge range + for (;;) { + AstNode* const backp = m_mgFirstp->backp(); + if (!backp || backp->nextp() != m_mgFirstp) break; // Don't move up the tree + if (m_checkMergeable(backp) != Mergeable::YES) break; + if (isSimplifiableNode(backp)) { + ++m_listLenght; + m_mgFirstp = backp; + } else if (isCheapNode(backp)) { + backp->user2(1); + ++m_listLenght; + m_mgFirstp = backp; + } else { + break; + } + } } // Add node ++m_listLenght; // Track end of list m_mgLastp = nodep; - // Set up expected next node in list. Skip over any comments, (inserted - // by V3Order before always blocks) + // Set up expected next node in list. m_mgNextp = nodep->nextp(); - while (m_mgNextp && VN_IS(m_mgNextp, Comment)) { m_mgNextp = m_mgNextp->nextp(); } // If last under parent, done with current list - if (!m_mgNextp) mergeEnd(); + if (!m_mgNextp) mergeEnd(__LINE__); + } + + // If this node is the next expected node and is helpful to add to the list, do so, + // otherwise end the current merge. Return ture if added, false if ended merge. + bool addIfHelpfulElseEndMerge(AstNode* nodep) { + UASSERT_OBJ(m_mgFirstp, nodep, "List must be open"); + if (m_mgNextp == nodep) { + if (isSimplifiableNode(nodep)) { + addToList(nodep, nullptr, __LINE__); + return true; + } + if (isCheapNode(nodep)) { + nodep->user2(1); + addToList(nodep, nullptr, __LINE__); + return true; + } + } + // Not added to list, so we are done with the current list + mergeEnd(__LINE__); + return false; + } + + bool checkOrMakeMergeable(AstNode* nodep) { + const Mergeable reason = m_checkMergeable(nodep); + // If meregeable, we are done + if (reason == Mergeable::YES) return true; + // Node not mergeable. + // If no current list, then this node is just special, move on. + if (!m_mgFirstp) return false; + // Otherwise finish current list + mergeEnd(__LINE__); + // If a tree was not mergeable due to an assignment to a condition, + // then finishing the current list makes it mergeable again. + return reason == Mergeable::NO_COND_ASSIGN; + } + + void mergeEndIfIncompatible(AstNode* nodep, AstNode* condp) { + if (m_mgFirstp && (m_mgNextp != nodep || !condp->sameTree(m_mgCondp))) { + // Node in different list, or has different condition. Finish current list. + mergeEnd(__LINE__); + } } // VISITORS virtual void visit(AstNodeAssign* nodep) override { AstNode* const rhsp = nodep->rhsp(); if (AstNodeCond* const condp = extractCond(rhsp)) { - if (!m_checkMergeable(nodep)) { - // Node not mergeable. - // If no current list, then this node is just special, move on. - if (!m_mgFirstp) return; - // Otherwise finish current list - mergeEnd(); - // Finishing the list might make the node mergeable again, e.g. - // if the reason we could not merge was due to the condition - // being assigned, so check again and stop only if still no. - if (!m_checkMergeable(nodep)) return; - } - if (m_mgFirstp && (m_mgNextp != nodep || !condp->condp()->sameTree(m_mgCondp))) { - // Node in different list, or has different condition. - // Finish current list, addToList will start a new one. - mergeEnd(); - } + // Check if mergeable + if (!checkOrMakeMergeable(nodep)) return; + // Close potentially incompatible pending merge + mergeEndIfIncompatible(nodep, condp->condp()); // Add current node - addToList(nodep, condp->condp()); + addToList(nodep, condp->condp(), __LINE__); } else if (m_mgFirstp) { - // RHS is not a conditional, but we already started a list. - // If it's a 1-bit signal, and a mergeable assignment, try reduced forms - if (m_mgNextp == nodep && rhsp->widthMin() == 1 && m_checkMergeable(nodep)) { - // Is it a 'lhs = cond & value' or 'lhs = value & cond'? - if (AstAnd* const andp = VN_CAST(rhsp, And)) { - if (andp->lhsp()->sameTree(m_mgCondp) || andp->rhsp()->sameTree(m_mgCondp)) { - addToList(nodep, nullptr); - return; - } - } - // Is it simply 'lhs = cond'? - if (rhsp->sameTree(m_mgCondp)) { - addToList(nodep, nullptr); - return; - } - } - // Not added to list, so we are done with the current list - mergeEnd(); + addIfHelpfulElseEndMerge(nodep); } } - virtual void visit(AstComment*) override {} // Skip over comments + + virtual void visit(AstNodeIf* nodep) override { + // Check if mergeable + if (!checkOrMakeMergeable(nodep)) { + // If not mergeable, try to merge the branches + iterateAndNextNull(nodep->ifsp()); + iterateAndNextNull(nodep->elsesp()); + return; + } + // Close potentially incompatible pending merge + mergeEndIfIncompatible(nodep, nodep->condp()); + // Add current node + addToList(nodep, nodep->condp(), __LINE__); + } + // For speed, only iterate what is necessary. virtual void visit(AstNetlist* nodep) override { iterateAndNextNull(nodep->modulesp()); } virtual void visit(AstNodeModule* nodep) override { iterateAndNextNull(nodep->stmtsp()); } virtual void visit(AstCFunc* nodep) override { iterateChildren(nodep); // Close list, if there is one at the end of the function - if (m_mgFirstp) mergeEnd(); + if (m_mgFirstp) mergeEnd(__LINE__); + } + virtual void visit(AstNodeStmt* nodep) override { + if (m_mgFirstp && addIfHelpfulElseEndMerge(nodep)) return; + iterateChildren(nodep); } - virtual void visit(AstNodeStmt* nodep) override { iterateChildren(nodep); } virtual void visit(AstNode* nodep) override {} public: diff --git a/test_regress/t/t_optm_if_cond.pl b/test_regress/t/t_optm_if_cond.pl index 3c5833594..b67f09305 100755 --- a/test_regress/t/t_optm_if_cond.pl +++ b/test_regress/t/t_optm_if_cond.pl @@ -11,7 +11,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(vlt => 1); compile( - verilator_flags2 => ['--stats'], + verilator_flags2 => ['--stats', "-Ow"], ); if ($Self->{vlt_all}) { From f9806595f2c7a5de32ba3d05750b272ed20edb0d Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 21 Aug 2021 10:33:20 -0400 Subject: [PATCH 75/84] Add error when constant function under a generate (#3103). --- Changes | 1 + src/V3Ast.h | 4 ++++ src/V3LinkResolve.cpp | 13 +++++++++++++ src/V3Simulate.h | 8 ++++++++ test_regress/t/t_lint_const_func_dpi_bad.out | 11 +++++++++++ test_regress/t/t_lint_const_func_dpi_bad.pl | 19 +++++++++++++++++++ test_regress/t/t_lint_const_func_dpi_bad.v | 10 ++++++++++ test_regress/t/t_lint_const_func_gen_bad.out | 11 +++++++++++ test_regress/t/t_lint_const_func_gen_bad.pl | 19 +++++++++++++++++++ test_regress/t/t_lint_const_func_gen_bad.v | 17 +++++++++++++++++ 10 files changed, 113 insertions(+) create mode 100644 test_regress/t/t_lint_const_func_dpi_bad.out create mode 100755 test_regress/t/t_lint_const_func_dpi_bad.pl create mode 100644 test_regress/t/t_lint_const_func_dpi_bad.v create mode 100644 test_regress/t/t_lint_const_func_gen_bad.out create mode 100755 test_regress/t/t_lint_const_func_gen_bad.pl create mode 100644 test_regress/t/t_lint_const_func_gen_bad.v diff --git a/Changes b/Changes index d56469b13..e7ddcc712 100644 --- a/Changes +++ b/Changes @@ -23,6 +23,7 @@ Verilator 4.211 devel * Parameter values are now emitted as 'static constexpr' instead of enum. C++ direct references to parameters might require updating (#3077). [Geza Lore] * Refactored Verilated include files; include verilated.h not verilated_heavy.h. +* Add error when constant function under a generate (#3103). [Don Owen] * Fix -G to treat simple integer literals as signed (#3060). [Anikin1610] * Fix emitted string array initializers (#2895). [Iztok Jeras] * Fix bitop tree optimization dropping necessary & operator (#3096). [Flavien Solt] diff --git a/src/V3Ast.h b/src/V3Ast.h index 47b580c50..907f716f3 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -2682,6 +2682,7 @@ private: bool m_isHideProtected : 1; // Verilog protected bool m_pure : 1; // DPI import pure (vs. virtual pure) bool m_pureVirtual : 1; // Pure virtual + bool m_underGenerate : 1; // Under generate (for warning) bool m_virtual : 1; // Virtual method in class VLifetime m_lifetime; // Lifetime protected: @@ -2704,6 +2705,7 @@ protected: , m_isHideProtected{false} , m_pure{false} , m_pureVirtual{false} + , m_underGenerate{false} , m_virtual{false} { addNOp3p(stmtsp); cname(name); // Might be overridden by dpi import/export @@ -2770,6 +2772,8 @@ public: bool pure() const { return m_pure; } void pureVirtual(bool flag) { m_pureVirtual = flag; } bool pureVirtual() const { return m_pureVirtual; } + void underGenerate(bool flag) { m_underGenerate = flag; } + bool underGenerate() const { return m_underGenerate; } void isVirtual(bool flag) { m_virtual = flag; } bool isVirtual() const { return m_virtual; } void lifetime(const VLifetime& flag) { m_lifetime = flag; } diff --git a/src/V3LinkResolve.cpp b/src/V3LinkResolve.cpp index 6c0b2c02a..70067cd79 100644 --- a/src/V3LinkResolve.cpp +++ b/src/V3LinkResolve.cpp @@ -52,6 +52,7 @@ private: AstNodeFTask* m_ftaskp = nullptr; // Function or task we're inside AstNodeCoverOrAssert* m_assertp = nullptr; // Current assertion int m_senitemCvtNum = 0; // Temporary signal counter + bool m_underGenerate = false; // Under GenFor/GenIf // METHODS VL_DEBUG_FUNC; // Declare debug() @@ -111,6 +112,7 @@ private: virtual void visit(AstNodeFTask* nodep) override { // NodeTask: Remember its name for later resolution + if (m_underGenerate) nodep->underGenerate(true); // Remember the existing symbol table scope if (m_classp) { if (nodep->name() == "pre_randomize" || nodep->name() == "post_randomize") { @@ -520,6 +522,17 @@ private: // virtual void visit(AstModport* nodep) override { ... } // We keep Modport's themselves around for XML dump purposes + virtual void visit(AstGenFor* nodep) override { + VL_RESTORER(m_underGenerate); + m_underGenerate = true; + iterateChildren(nodep); + } + virtual void visit(AstGenIf* nodep) override { + VL_RESTORER(m_underGenerate); + m_underGenerate = true; + iterateChildren(nodep); + } + virtual void visit(AstNode* nodep) override { iterateChildren(nodep); } public: diff --git a/src/V3Simulate.h b/src/V3Simulate.h index 0ded54fe8..96967643a 100644 --- a/src/V3Simulate.h +++ b/src/V3Simulate.h @@ -484,8 +484,16 @@ private: return; } if (nodep->dpiImport()) { + if (m_params) { + nodep->v3error("Constant function may not be DPI import (IEEE 1800-2017 13.4.3)"); + } clearOptimizable(nodep, "DPI import functions aren't simulatable"); } + if (nodep->underGenerate()) { + nodep->v3error( + "Constant function may not be declared under generate (IEEE 1800-2017 13.4.3)"); + clearOptimizable(nodep, "Constant function called under generate"); + } checkNodeInfo(nodep); iterateChildren(nodep); } diff --git a/test_regress/t/t_lint_const_func_dpi_bad.out b/test_regress/t/t_lint_const_func_dpi_bad.out new file mode 100644 index 000000000..6ac7d0ab6 --- /dev/null +++ b/test_regress/t/t_lint_const_func_dpi_bad.out @@ -0,0 +1,11 @@ +%Error: t/t_lint_const_func_dpi_bad.v:8:32: Constant function may not be DPI import (IEEE 1800-2017 13.4.3) + : ... In instance t + 8 | import "DPI-C" function int dpiFunc(); + | ^~~~~~~ +%Error: t/t_lint_const_func_dpi_bad.v:9:23: Expecting expression to be constant, but can't determine constant for FUNCREF 'dpiFunc' + : ... In instance t + t/t_lint_const_func_dpi_bad.v:8:32: ... Location of non-constant FUNC 'dpiFunc': DPI import functions aren't simulatable + t/t_lint_const_func_dpi_bad.v:9:23: ... Called from dpiFunc() with parameters: + 9 | localparam PARAM = dpiFunc(); + | ^~~~~~~ +%Error: Exiting due to diff --git a/test_regress/t/t_lint_const_func_dpi_bad.pl b/test_regress/t/t_lint_const_func_dpi_bad.pl new file mode 100755 index 000000000..b5861b2ab --- /dev/null +++ b/test_regress/t/t_lint_const_func_dpi_bad.pl @@ -0,0 +1,19 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2008 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 + +scenarios(vlt => 1); + +lint( + fails => 1, + expect_filename => $Self->{golden_filename}, + ); + +ok(1); +1; diff --git a/test_regress/t/t_lint_const_func_dpi_bad.v b/test_regress/t/t_lint_const_func_dpi_bad.v new file mode 100644 index 000000000..84632841b --- /dev/null +++ b/test_regress/t/t_lint_const_func_dpi_bad.v @@ -0,0 +1,10 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2021 by Donald Owen. +// SPDX-License-Identifier: CC0-1.0 + +module t (); + import "DPI-C" function int dpiFunc(); + localparam PARAM = dpiFunc(); +endmodule diff --git a/test_regress/t/t_lint_const_func_gen_bad.out b/test_regress/t/t_lint_const_func_gen_bad.out new file mode 100644 index 000000000..61bd870a8 --- /dev/null +++ b/test_regress/t/t_lint_const_func_gen_bad.out @@ -0,0 +1,11 @@ +%Error: t/t_lint_const_func_gen_bad.v:11:30: Constant function may not be declared under generate (IEEE 1800-2017 13.4.3) + : ... In instance t + 11 | function automatic bit constFunc(); + | ^~~~~~~~~ +%Error: t/t_lint_const_func_gen_bad.v:15:26: Expecting expression to be constant, but can't determine constant for FUNCREF 'constFunc' + : ... In instance t + t/t_lint_const_func_gen_bad.v:11:30: ... Location of non-constant FUNC 'constFunc': Constant function called under generate + t/t_lint_const_func_gen_bad.v:15:26: ... Called from constFunc() with parameters: + 15 | localparam PARAM = constFunc(); + | ^~~~~~~~~ +%Error: Exiting due to diff --git a/test_regress/t/t_lint_const_func_gen_bad.pl b/test_regress/t/t_lint_const_func_gen_bad.pl new file mode 100755 index 000000000..b5861b2ab --- /dev/null +++ b/test_regress/t/t_lint_const_func_gen_bad.pl @@ -0,0 +1,19 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2008 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 + +scenarios(vlt => 1); + +lint( + fails => 1, + expect_filename => $Self->{golden_filename}, + ); + +ok(1); +1; diff --git a/test_regress/t/t_lint_const_func_gen_bad.v b/test_regress/t/t_lint_const_func_gen_bad.v new file mode 100644 index 000000000..d2ac25a4d --- /dev/null +++ b/test_regress/t/t_lint_const_func_gen_bad.v @@ -0,0 +1,17 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2021 by Donald Owen. +// SPDX-License-Identifier: CC0-1.0 + +module t (); + if (1) begin: GenConstFunc + // IEEE 1800-2017 13.4.3, constant functions shall not be declared inside a + //generate block + function automatic bit constFunc(); + constFunc = 1'b1; + endfunction + + localparam PARAM = constFunc(); + end +endmodule From c3d64d9743e4e3d2d025f82d033d300b3b1fde80 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Mon, 23 Aug 2021 20:13:09 -0400 Subject: [PATCH 76/84] Fix internal error on wide -x-initial unique (#3106). --- Changes | 1 + src/V3Premit.cpp | 4 ++++ test_regress/t/t_flag_xinitial_unique.v | 5 ++++- 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/Changes b/Changes index e7ddcc712..f3f250292 100644 --- a/Changes +++ b/Changes @@ -27,6 +27,7 @@ Verilator 4.211 devel * Fix -G to treat simple integer literals as signed (#3060). [Anikin1610] * Fix emitted string array initializers (#2895). [Iztok Jeras] * Fix bitop tree optimization dropping necessary & operator (#3096). [Flavien Solt] +* Fix internal error on wide -x-initial unique (#3106). [Alexandre Joannou] Verilator 4.210 2021-07-07 diff --git a/src/V3Premit.cpp b/src/V3Premit.cpp index e665628b2..1c680382d 100644 --- a/src/V3Premit.cpp +++ b/src/V3Premit.cpp @@ -324,6 +324,10 @@ private: iterateChildren(nodep); checkNode(nodep); } + virtual void visit(AstRand* nodep) override { + iterateChildren(nodep); + checkNode(nodep); + } virtual void visit(AstUCFunc* nodep) override { iterateChildren(nodep); checkNode(nodep); diff --git a/test_regress/t/t_flag_xinitial_unique.v b/test_regress/t/t_flag_xinitial_unique.v index 04321788a..aff70e92c 100644 --- a/test_regress/t/t_flag_xinitial_unique.v +++ b/test_regress/t/t_flag_xinitial_unique.v @@ -6,10 +6,13 @@ module t (/*AUTOARG*/ // Outputs - value + value, value2 ); output reg [63:0] value; + output wire [64:0] value2; + + assign value2 = {8'bx, 57'h12}; initial begin $write("*-* All Finished *-*\n"); From 959793cde36254992925d94d7be8200b59449148 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Mon, 23 Aug 2021 21:13:33 -0400 Subject: [PATCH 77/84] Internals: Cleanup VL_VALUE_STRING_MAX widths (#3050). --- include/verilated.cpp | 6 +++--- include/verilated_vpi.cpp | 36 ++++++++++++++++++------------------ include/verilatedos.h | 3 ++- src/V3EmitCFunc.h | 4 ++-- 4 files changed, 25 insertions(+), 24 deletions(-) diff --git a/include/verilated.cpp b/include/verilated.cpp index e7320e50d..f86f04852 100644 --- a/include/verilated.cpp +++ b/include/verilated.cpp @@ -1338,7 +1338,7 @@ IData VL_FGETS_IXI(int obits, void* destp, IData fpi) VL_MT_SAFE { if (VL_UNLIKELY(str.empty())) return 0; - // V3Emit has static check that bytes < VL_TO_STRING_MAX_WORDS, but be safe + // V3Emit has static check that bytes < VL_VALUE_STRING_MAX_WORDS, but be safe if (VL_UNCOVERABLE(bytes < str.size())) { VL_FATAL_MT(__FILE__, __LINE__, "", "Internal: fgets buffer overrun"); // LCOV_EXCL_LINE } @@ -1579,7 +1579,7 @@ IData VL_SYSTEM_IQ(QData lhs) VL_MT_SAFE { return VL_SYSTEM_IW(VL_WQ_WORDS_E, lhsw); } IData VL_SYSTEM_IW(int lhswords, const WDataInP lhsp) VL_MT_SAFE { - char filenamez[VL_TO_STRING_MAX_WORDS * VL_EDATASIZE + 1]; + char filenamez[VL_VALUE_STRING_MAX_CHARS + 1]; _vl_vint_to_string(lhswords * VL_EDATASIZE, filenamez, lhsp); const int code = std::system(filenamez); // Yes, std::system() is threadsafe return code >> 8; // Want exit status @@ -1726,7 +1726,7 @@ std::string VL_TOUPPER_NN(const std::string& ld) VL_MT_SAFE { std::string VL_CVT_PACK_STR_NW(int lwords, const WDataInP lwp) VL_MT_SAFE { // See also _vl_vint_to_string - char destout[VL_TO_STRING_MAX_WORDS * VL_EDATASIZE + 1]; + char destout[VL_VALUE_STRING_MAX_CHARS + 1]; int obits = lwords * VL_EDATASIZE; int lsb = obits - 1; bool start = true; diff --git a/include/verilated_vpi.cpp b/include/verilated_vpi.cpp index 9c6862158..5b7bbfe5c 100644 --- a/include/verilated_vpi.cpp +++ b/include/verilated_vpi.cpp @@ -1695,7 +1695,7 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep, const char* fullname) { if (!vl_check_format(varp, valuep, fullname, true)) return; // Maximum required size is for binary string, one byte per bit plus null termination - static VL_THREAD_LOCAL char t_outStr[1 + VL_MULS_MAX_WORDS * 32]; + static VL_THREAD_LOCAL char t_outStr[VL_VALUE_STRING_MAX_WORDS * VL_EDATASIZE + 1]; // cppcheck-suppress variableScope static VL_THREAD_LOCAL int t_outStrSz = sizeof(t_outStr) - 1; // We used to presume vpiValue.format = vpiIntVal or if single bit vpiScalarVal @@ -1703,7 +1703,7 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep, if (valuep->format == vpiVectorVal) { // Vector pointer must come from our memory pool // It only needs to persist until the next vpi_get_value - static VL_THREAD_LOCAL t_vpi_vecval t_out[VL_MULS_MAX_WORDS * 2]; + static VL_THREAD_LOCAL t_vpi_vecval t_out[VL_VALUE_STRING_MAX_WORDS * 2]; valuep->value.vector = t_out; if (varp->vltype() == VLVT_UINT8) { t_out[0].aval = *(reinterpret_cast(varDatap)); @@ -1726,10 +1726,10 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep, return; } else if (varp->vltype() == VLVT_WDATA) { int words = VL_WORDS_I(varp->packed().elements()); - if (VL_UNCOVERABLE(words >= VL_MULS_MAX_WORDS)) { - VL_FATAL_MT( - __FILE__, __LINE__, "", - "vpi_get_value with more than VL_MULS_MAX_WORDS; increase and recompile"); + if (VL_UNCOVERABLE(words >= VL_VALUE_STRING_MAX_WORDS)) { + VL_FATAL_MT(__FILE__, __LINE__, "", + "vpi_get_value with more than VL_VALUE_STRING_MAX_WORDS; increase and " + "recompile"); } const WDataInP datap = (reinterpret_cast(varDatap)); for (int i = 0; i < words; ++i) { @@ -1749,9 +1749,9 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep, VL_VPI_WARNING_( __FILE__, __LINE__, "%s: Truncating string value of %s for %s" - " as buffer size (%d, VL_MULS_MAX_WORDS=%d) is less than required (%d)", + " as buffer size (%d, VL_VALUE_STRING_MAX_WORDS=%d) is less than required (%d)", __func__, VerilatedVpiError::strFromVpiVal(valuep->format), fullname, t_outStrSz, - VL_MULS_MAX_WORDS, bits); + VL_VALUE_STRING_MAX_WORDS, bits); } for (i = 0; i < bits; ++i) { char val = (datap[i >> 3] >> (i & 7)) & 1; @@ -1770,9 +1770,9 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep, VL_VPI_WARNING_( __FILE__, __LINE__, "%s: Truncating string value of %s for %s" - " as buffer size (%d, VL_MULS_MAX_WORDS=%d) is less than required (%d)", + " as buffer size (%d, VL_VALUE_STRING_MAX_WORDS=%d) is less than required (%d)", __func__, VerilatedVpiError::strFromVpiVal(valuep->format), fullname, t_outStrSz, - VL_MULS_MAX_WORDS, chars); + VL_VALUE_STRING_MAX_WORDS, chars); chars = t_outStrSz; } for (i = 0; i < chars; ++i) { @@ -1828,9 +1828,9 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep, VL_VPI_WARNING_( __FILE__, __LINE__, "%s: Truncating string value of %s for %s" - " as buffer size (%d, VL_MULS_MAX_WORDS=%d) is less than required (%d)", + " as buffer size (%d, VL_VALUE_STRING_MAX_WORDS=%d) is less than required (%d)", __func__, VerilatedVpiError::strFromVpiVal(valuep->format), fullname, t_outStrSz, - VL_MULS_MAX_WORDS, chars); + VL_VALUE_STRING_MAX_WORDS, chars); chars = t_outStrSz; } for (i = 0; i < chars; ++i) { @@ -1859,12 +1859,12 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep, int i; if (bytes > t_outStrSz) { // limit maximum size of output to size of buffer to prevent overrun. - VL_VPI_WARNING_( - __FILE__, __LINE__, - "%s: Truncating string value of %s for %s" - " as buffer size (%d, VL_MULS_MAX_WORDS=%d) is less than required (%d)", - __func__, VerilatedVpiError::strFromVpiVal(valuep->format), fullname, - t_outStrSz, VL_MULS_MAX_WORDS, bytes); + VL_VPI_WARNING_(__FILE__, __LINE__, + "%s: Truncating string value of %s for %s" + " as buffer size (%d, VL_VALUE_STRING_MAX_WORDS=%d) is less than " + "required (%d)", + __func__, VerilatedVpiError::strFromVpiVal(valuep->format), + fullname, t_outStrSz, VL_VALUE_STRING_MAX_WORDS, bytes); bytes = t_outStrSz; } for (i = 0; i < bytes; ++i) { diff --git a/include/verilatedos.h b/include/verilatedos.h index 06d07d3a6..1f64bf63c 100644 --- a/include/verilatedos.h +++ b/include/verilatedos.h @@ -421,7 +421,8 @@ typedef unsigned long long vluint64_t; ///< 64-bit unsigned type // Verilated function size macros #define VL_MULS_MAX_WORDS 16 ///< Max size in words of MULS operation -#define VL_TO_STRING_MAX_WORDS 64 ///< Max size in words of String conversion operation +#define VL_VALUE_STRING_MAX_WORDS 64 ///< Max size in words of String conversion operation +#define VL_VALUE_STRING_MAX_CHARS (VL_VALUE_STRING_MAX_WORDS * VL_EDATASIZE / VL_BYTESIZE) //========================================================================= // Base macros diff --git a/src/V3EmitCFunc.h b/src/V3EmitCFunc.h index 144307b88..f4e72f380 100644 --- a/src/V3EmitCFunc.h +++ b/src/V3EmitCFunc.h @@ -595,11 +595,11 @@ public: } void checkMaxWords(AstNode* nodep) { - if (nodep->widthWords() > VL_TO_STRING_MAX_WORDS) { + if (nodep->widthWords() > VL_VALUE_STRING_MAX_WORDS) { nodep->v3error( "String of " << nodep->width() - << " bits exceeds hardcoded limit VL_TO_STRING_MAX_WORDS in verilatedos.h"); + << " bits exceeds hardcoded limit VL_VALUE_STRING_MAX_WORDS in verilatedos.h"); } } virtual void visit(AstFOpen* nodep) override { From 27d53691bdcb7321c72d4bd2a83f6645c9a88497 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Mon, 23 Aug 2021 21:43:54 -0400 Subject: [PATCH 78/84] Add header guards on Dpi.h generated files (#2979). --- Changes | 1 + src/V3EmitCSyms.cpp | 5 +++++ test_regress/t/t_dpi_arg_inout_type__Dpi.out | 5 +++++ test_regress/t/t_dpi_arg_inout_unpack__Dpi.out | 5 +++++ test_regress/t/t_dpi_arg_input_type__Dpi.out | 5 +++++ test_regress/t/t_dpi_arg_input_unpack__Dpi.out | 5 +++++ test_regress/t/t_dpi_arg_output_type__Dpi.out | 5 +++++ test_regress/t/t_dpi_arg_output_unpack__Dpi.out | 5 +++++ test_regress/t/t_dpi_result_type__Dpi.out | 5 +++++ 9 files changed, 41 insertions(+) diff --git a/Changes b/Changes index f3f250292..526a53539 100644 --- a/Changes +++ b/Changes @@ -24,6 +24,7 @@ Verilator 4.211 devel C++ direct references to parameters might require updating (#3077). [Geza Lore] * Refactored Verilated include files; include verilated.h not verilated_heavy.h. * Add error when constant function under a generate (#3103). [Don Owen] +* Add header guards on Dpi.h generated files (#2979). [Tood Strader] * Fix -G to treat simple integer literals as signed (#3060). [Anikin1610] * Fix emitted string array initializers (#2895). [Iztok Jeras] * Fix bitop tree optimization dropping necessary & operator (#3096). [Flavien Solt] diff --git a/src/V3EmitCSyms.cpp b/src/V3EmitCSyms.cpp index f86dd7487..fc430a369 100644 --- a/src/V3EmitCSyms.cpp +++ b/src/V3EmitCSyms.cpp @@ -940,6 +940,9 @@ void EmitCSyms::emitDpiHdr() { puts("// Verilator includes this file in all generated .cpp files that use DPI functions.\n"); puts("// Manually include this file where DPI .c import functions are declared to ensure\n"); puts("// the C functions match the expectations of the DPI imports.\n"); + + ofp()->putsGuard(); + puts("\n"); puts("#include \"svdpi.h\"\n"); puts("\n"); @@ -970,6 +973,8 @@ void EmitCSyms::emitDpiHdr() { puts("#ifdef __cplusplus\n"); puts("}\n"); puts("#endif\n"); + + ofp()->putsEndGuard(); } //###################################################################### diff --git a/test_regress/t/t_dpi_arg_inout_type__Dpi.out b/test_regress/t/t_dpi_arg_inout_type__Dpi.out index 01379ddcd..b800f7d8f 100644 --- a/test_regress/t/t_dpi_arg_inout_type__Dpi.out +++ b/test_regress/t/t_dpi_arg_inout_type__Dpi.out @@ -5,6 +5,9 @@ // Manually include this file where DPI .c import functions are declared to ensure // the C functions match the expectations of the DPI imports. +#ifndef VERILATED_VT_DPI_ARG_INOUT_TYPE__DPI_H_ +#define VERILATED_VT_DPI_ARG_INOUT_TYPE__DPI_H_ // guard + #include "svdpi.h" #ifdef __cplusplus @@ -152,3 +155,5 @@ extern "C" { #ifdef __cplusplus } #endif + +#endif // guard diff --git a/test_regress/t/t_dpi_arg_inout_unpack__Dpi.out b/test_regress/t/t_dpi_arg_inout_unpack__Dpi.out index e4f0d5949..8c6d9be71 100644 --- a/test_regress/t/t_dpi_arg_inout_unpack__Dpi.out +++ b/test_regress/t/t_dpi_arg_inout_unpack__Dpi.out @@ -5,6 +5,9 @@ // Manually include this file where DPI .c import functions are declared to ensure // the C functions match the expectations of the DPI imports. +#ifndef VERILATED_VT_DPI_ARG_INOUT_UNPACK__DPI_H_ +#define VERILATED_VT_DPI_ARG_INOUT_UNPACK__DPI_H_ // guard + #include "svdpi.h" #ifdef __cplusplus @@ -181,3 +184,5 @@ extern "C" { #ifdef __cplusplus } #endif + +#endif // guard diff --git a/test_regress/t/t_dpi_arg_input_type__Dpi.out b/test_regress/t/t_dpi_arg_input_type__Dpi.out index 632b44b9c..d0994c659 100644 --- a/test_regress/t/t_dpi_arg_input_type__Dpi.out +++ b/test_regress/t/t_dpi_arg_input_type__Dpi.out @@ -5,6 +5,9 @@ // Manually include this file where DPI .c import functions are declared to ensure // the C functions match the expectations of the DPI imports. +#ifndef VERILATED_VT_DPI_ARG_INPUT_TYPE__DPI_H_ +#define VERILATED_VT_DPI_ARG_INPUT_TYPE__DPI_H_ // guard + #include "svdpi.h" #ifdef __cplusplus @@ -152,3 +155,5 @@ extern "C" { #ifdef __cplusplus } #endif + +#endif // guard diff --git a/test_regress/t/t_dpi_arg_input_unpack__Dpi.out b/test_regress/t/t_dpi_arg_input_unpack__Dpi.out index 0867d13f0..4d1afa518 100644 --- a/test_regress/t/t_dpi_arg_input_unpack__Dpi.out +++ b/test_regress/t/t_dpi_arg_input_unpack__Dpi.out @@ -5,6 +5,9 @@ // Manually include this file where DPI .c import functions are declared to ensure // the C functions match the expectations of the DPI imports. +#ifndef VERILATED_VT_DPI_ARG_INPUT_UNPACK__DPI_H_ +#define VERILATED_VT_DPI_ARG_INPUT_UNPACK__DPI_H_ // guard + #include "svdpi.h" #ifdef __cplusplus @@ -181,3 +184,5 @@ extern "C" { #ifdef __cplusplus } #endif + +#endif // guard diff --git a/test_regress/t/t_dpi_arg_output_type__Dpi.out b/test_regress/t/t_dpi_arg_output_type__Dpi.out index 1a9db977f..e84de47c3 100644 --- a/test_regress/t/t_dpi_arg_output_type__Dpi.out +++ b/test_regress/t/t_dpi_arg_output_type__Dpi.out @@ -5,6 +5,9 @@ // Manually include this file where DPI .c import functions are declared to ensure // the C functions match the expectations of the DPI imports. +#ifndef VERILATED_VT_DPI_ARG_OUTPUT_TYPE__DPI_H_ +#define VERILATED_VT_DPI_ARG_OUTPUT_TYPE__DPI_H_ // guard + #include "svdpi.h" #ifdef __cplusplus @@ -152,3 +155,5 @@ extern "C" { #ifdef __cplusplus } #endif + +#endif // guard diff --git a/test_regress/t/t_dpi_arg_output_unpack__Dpi.out b/test_regress/t/t_dpi_arg_output_unpack__Dpi.out index 24675921e..ddcd704de 100644 --- a/test_regress/t/t_dpi_arg_output_unpack__Dpi.out +++ b/test_regress/t/t_dpi_arg_output_unpack__Dpi.out @@ -5,6 +5,9 @@ // Manually include this file where DPI .c import functions are declared to ensure // the C functions match the expectations of the DPI imports. +#ifndef VERILATED_VT_DPI_ARG_OUTPUT_UNPACK__DPI_H_ +#define VERILATED_VT_DPI_ARG_OUTPUT_UNPACK__DPI_H_ // guard + #include "svdpi.h" #ifdef __cplusplus @@ -181,3 +184,5 @@ extern "C" { #ifdef __cplusplus } #endif + +#endif // guard diff --git a/test_regress/t/t_dpi_result_type__Dpi.out b/test_regress/t/t_dpi_result_type__Dpi.out index cd2d0ecc8..344971158 100644 --- a/test_regress/t/t_dpi_result_type__Dpi.out +++ b/test_regress/t/t_dpi_result_type__Dpi.out @@ -5,6 +5,9 @@ // Manually include this file where DPI .c import functions are declared to ensure // the C functions match the expectations of the DPI imports. +#ifndef VERILATED_VT_DPI_RESULT_TYPE__DPI_H_ +#define VERILATED_VT_DPI_RESULT_TYPE__DPI_H_ // guard + #include "svdpi.h" #ifdef __cplusplus @@ -86,3 +89,5 @@ extern "C" { #ifdef __cplusplus } #endif + +#endif // guard From 43aa3229fb153403b76eb98c3ad1e517c5106ba3 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 28 Aug 2021 17:23:35 -0400 Subject: [PATCH 79/84] Support empty queue assignment to delete queue --- src/V3Ast.cpp | 7 +++-- src/V3Ast.h | 2 ++ src/V3AstNodes.cpp | 15 ++++++++- src/V3AstNodes.h | 46 ++++++++++++++++++++++++++-- src/V3Width.cpp | 25 +++++++++++++++ src/verilog.y | 3 +- test_regress/t/t_queue_empty_bad.out | 10 ++++++ test_regress/t/t_queue_empty_bad.pl | 19 ++++++++++++ test_regress/t/t_queue_empty_bad.v | 18 +++++++++++ test_regress/t/t_queue_slice.v | 3 ++ 10 files changed, 140 insertions(+), 8 deletions(-) create mode 100755 test_regress/t/t_queue_empty_bad.out create mode 100755 test_regress/t/t_queue_empty_bad.pl create mode 100644 test_regress/t/t_queue_empty_bad.v diff --git a/src/V3Ast.cpp b/src/V3Ast.cpp index ae9cb530c..356c8b201 100644 --- a/src/V3Ast.cpp +++ b/src/V3Ast.cpp @@ -1264,12 +1264,15 @@ AstNodeDType* AstNode::findBitRangeDType(const VNumRange& range, int widthMin, AstBasicDType* AstNode::findInsertSameDType(AstBasicDType* nodep) { return v3Global.rootp()->typeTablep()->findInsertSameDType(nodep); } -AstNodeDType* AstNode::findVoidDType() const { - return v3Global.rootp()->typeTablep()->findVoidDType(fileline()); +AstNodeDType* AstNode::findEmptyQueueDType() const { + return v3Global.rootp()->typeTablep()->findEmptyQueueDType(fileline()); } AstNodeDType* AstNode::findQueueIndexDType() const { return v3Global.rootp()->typeTablep()->findQueueIndexDType(fileline()); } +AstNodeDType* AstNode::findVoidDType() const { + return v3Global.rootp()->typeTablep()->findVoidDType(fileline()); +} //###################################################################### // AstNVisitor diff --git a/src/V3Ast.h b/src/V3Ast.h index 907f716f3..3351a1b54 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -1698,6 +1698,7 @@ public: void dtypeSetSigned32() { dtypep(findSigned32DType()); } void dtypeSetUInt32() { dtypep(findUInt32DType()); } // Twostate void dtypeSetUInt64() { dtypep(findUInt64DType()); } // Twostate + void dtypeSetEmptyQueue() { dtypep(findEmptyQueueDType()); } void dtypeSetVoid() { dtypep(findVoidDType()); } // Data type locators @@ -1708,6 +1709,7 @@ public: AstNodeDType* findUInt32DType() { return findBasicDType(AstBasicDTypeKwd::UINT32); } AstNodeDType* findUInt64DType() { return findBasicDType(AstBasicDTypeKwd::UINT64); } AstNodeDType* findCHandleDType() { return findBasicDType(AstBasicDTypeKwd::CHANDLE); } + AstNodeDType* findEmptyQueueDType() const; AstNodeDType* findVoidDType() const; AstNodeDType* findQueueIndexDType() const; AstNodeDType* findBitDType(int width, int widthMin, VSigning numeric) const; diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index 8ce2682d0..ea3ecdf8e 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -932,9 +932,18 @@ void AstTypeTable::repairCache() { } } +AstEmptyQueueDType* AstTypeTable::findEmptyQueueDType(FileLine* fl) { + if (VL_UNLIKELY(!m_emptyQueuep)) { + AstEmptyQueueDType* newp = new AstEmptyQueueDType{fl}; + addTypesp(newp); + m_emptyQueuep = newp; + } + return m_emptyQueuep; +} + AstVoidDType* AstTypeTable::findVoidDType(FileLine* fl) { if (VL_UNLIKELY(!m_voidp)) { - AstVoidDType* newp = new AstVoidDType(fl); + AstVoidDType* newp = new AstVoidDType{fl}; addTypesp(newp); m_voidp = newp; } @@ -1642,6 +1651,10 @@ void AstUnsizedArrayDType::dumpSmall(std::ostream& str) const { this->AstNodeDType::dumpSmall(str); str << "[]"; } +void AstEmptyQueueDType::dumpSmall(std::ostream& str) const { + this->AstNodeDType::dumpSmall(str); + str << "emptyq"; +} void AstVoidDType::dumpSmall(std::ostream& str) const { this->AstNodeDType::dumpSmall(str); str << "void"; diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index d1706bbdd..b5c324b03 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -183,6 +183,17 @@ public: static AstConst* parseParamLiteral(FileLine* fl, const string& literal); }; +class AstEmptyQueue final : public AstNodeMath { +public: + AstEmptyQueue(FileLine* fl) + : ASTGEN_SUPER_EmptyQueue(fl) {} + ASTNODE_NODE_FUNCS(EmptyQueue) + virtual string emitC() { V3ERROR_NA_RETURN(""); } + virtual string emitVerilog() { return "{}"; } + virtual bool same(const AstNode* samep) const override { return true; } + virtual bool cleanOut() const { return true; } +}; + class AstRange final : public AstNodeRange { // Range specification, for use under variables and cells public: @@ -1354,6 +1365,33 @@ public: } }; +class AstEmptyQueueDType final : public AstNodeDType { + // For EmptyQueue +public: + explicit AstEmptyQueueDType(FileLine* fl) + : ASTGEN_SUPER_EmptyQueueDType(fl) { + dtypep(this); + } + ASTNODE_NODE_FUNCS(EmptyQueueDType) + virtual void dumpSmall(std::ostream& str) const override; + virtual bool hasDType() const override { return true; } + virtual bool maybePointedTo() const override { return true; } + virtual AstNodeDType* subDTypep() const override { return nullptr; } + virtual AstNodeDType* virtRefDTypep() const override { return nullptr; } + virtual void virtRefDTypep(AstNodeDType* nodep) override {} + virtual bool similarDType(AstNodeDType* samep) const override { return this == samep; } + virtual AstBasicDType* basicp() const override { return nullptr; } + // cppcheck-suppress csyleCast + virtual AstNodeDType* skipRefp() const override { return (AstNodeDType*)this; } + // cppcheck-suppress csyleCast + virtual AstNodeDType* skipRefToConstp() const override { return (AstNodeDType*)this; } + // cppcheck-suppress csyleCast + virtual AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; } + virtual int widthAlignBytes() const override { return 1; } + virtual int widthTotalBytes() const override { return 1; } + virtual bool isCompound() const override { return false; } +}; + class AstVoidDType final : public AstNodeDType { // For e.g. a function returning void public: @@ -9088,8 +9126,9 @@ public: class AstTypeTable final : public AstNode { // Container for hash of standard data types // Children: NODEDTYPEs - AstVoidDType* m_voidp = nullptr; + AstEmptyQueueDType* m_emptyQueuep = nullptr; AstQueueDType* m_queueIndexp = nullptr; + AstVoidDType* m_voidp = nullptr; AstBasicDType* m_basicps[AstBasicDTypeKwd::_ENUM_MAX]; // using DetailedMap = std::map; @@ -9100,14 +9139,15 @@ public: ASTNODE_NODE_FUNCS(TypeTable) AstNodeDType* typesp() const { return VN_CAST(op1p(), NodeDType); } // op1 = List of dtypes void addTypesp(AstNodeDType* nodep) { addOp1p(nodep); } - AstVoidDType* findVoidDType(FileLine* fl); - AstQueueDType* findQueueIndexDType(FileLine* fl); AstBasicDType* findBasicDType(FileLine* fl, AstBasicDTypeKwd kwd); AstBasicDType* findLogicBitDType(FileLine* fl, AstBasicDTypeKwd kwd, int width, int widthMin, VSigning numeric); AstBasicDType* findLogicBitDType(FileLine* fl, AstBasicDTypeKwd kwd, const VNumRange& range, int widthMin, VSigning numeric); AstBasicDType* findInsertSameDType(AstBasicDType* nodep); + AstEmptyQueueDType* findEmptyQueueDType(FileLine* fl); + AstQueueDType* findQueueIndexDType(FileLine* fl); + AstVoidDType* findVoidDType(FileLine* fl); void clearCache(); void repairCache(); virtual void dump(std::ostream& str = std::cout) const override; diff --git a/src/V3Width.cpp b/src/V3Width.cpp index cf2c264ae..a26b7f47e 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -1078,6 +1078,12 @@ private: // We don't size the constant until we commit the widths, as need parameters // to remain unsized, and numbers to remain unsized to avoid backp() warnings } + virtual void visit(AstEmptyQueue* nodep) override { + nodep->dtypeSetEmptyQueue(); + if (!VN_IS(nodep->backp(), Assign)) + nodep->v3warn(E_UNSUPPORTED, + "Unsupported/Illegal: empty queue ('{}') in this context"); + } virtual void visit(AstFell* nodep) override { if (m_vup->prelim()) { iterateCheckSizedSelf(nodep, "LHS", nodep->exprp(), SELF, BOTH); @@ -1173,6 +1179,7 @@ private: return; } } + nodep->backp()->dumpTree(cout, "-FIXME-tr "); nodep->v3warn(E_UNSUPPORTED, "Unsupported/illegal unbounded ('$') in this context."); } virtual void visit(AstIsUnbounded* nodep) override { @@ -3761,6 +3768,23 @@ private: nodep->v3warn(E_UNSUPPORTED, "Unsupported: assignment of event data type"); } } + if (VN_IS(nodep->rhsp(), EmptyQueue)) { + UINFO(9, "= {} -> .delete(): " << nodep); + if (!VN_IS(nodep->lhsp()->dtypep()->skipRefp(), QueueDType)) { + nodep->v3warn(E_UNSUPPORTED, + "Unsupported/Illegal: empty queue ('{}') in this assign context"); + VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep); + return; + } + AstMethodCall* const newp = new AstMethodCall{ + nodep->fileline(), nodep->lhsp()->unlinkFrBack(), "delete", nullptr}; + newp->makeStatement(); + nodep->replaceWith(newp); + VL_DO_DANGLING(pushDeletep(nodep), nodep); + // Need to now convert it + visit(newp); + return; + } if (AstNewDynamic* dynp = VN_CAST(nodep->rhsp(), NewDynamic)) { UINFO(9, "= new[] -> .resize(): " << nodep); AstCMethodHard* newp; @@ -3777,6 +3801,7 @@ private: newp->makeStatement(); nodep->replaceWith(newp); VL_DO_DANGLING(pushDeletep(nodep), nodep); + // return; } } diff --git a/src/verilog.y b/src/verilog.y index a5ec944e3..2d56fa9a7 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -4268,8 +4268,7 @@ expr: // IEEE: part of expression/constant_expression/primary // // IEEE: "... hierarchical_identifier select" see below // // // IEEE: empty_queue (IEEE 1800-2017 empty_unpacked_array_concatenation) - | '{' '}' { $$ = new AstConst($1, AstConst::BitFalse()); - BBUNSUP($1, "Unsupported: empty queues (\"{ }\")"); } + | '{' '}' { $$ = new AstEmptyQueue($1); } // // // IEEE: concatenation/constant_concatenation // // Part of exprOkLvalue below diff --git a/test_regress/t/t_queue_empty_bad.out b/test_regress/t/t_queue_empty_bad.out new file mode 100755 index 000000000..a9762673b --- /dev/null +++ b/test_regress/t/t_queue_empty_bad.out @@ -0,0 +1,10 @@ +%Error-UNSUPPORTED: t/t_queue_empty_bad.v:11:11: Unsupported/Illegal: empty queue ('{}') in this context + : ... In instance t + 11 | i = {} + 1; + | ^ + ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest +%Error-UNSUPPORTED: t/t_queue_empty_bad.v:13:9: Unsupported/Illegal: empty queue ('{}') in this assign context + : ... In instance t + 13 | i = {}; + | ^ +%Error: Exiting due to diff --git a/test_regress/t/t_queue_empty_bad.pl b/test_regress/t/t_queue_empty_bad.pl new file mode 100755 index 000000000..a5846c699 --- /dev/null +++ b/test_regress/t/t_queue_empty_bad.pl @@ -0,0 +1,19 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 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 + +scenarios(vlt => 1); + +lint( + fails => 1, + expect_filename => $Self->{golden_filename}, + ); + +ok(1); +1; diff --git a/test_regress/t/t_queue_empty_bad.v b/test_regress/t/t_queue_empty_bad.v new file mode 100644 index 000000000..09695b14a --- /dev/null +++ b/test_regress/t/t_queue_empty_bad.v @@ -0,0 +1,18 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2019 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +module t (/*AUTOARG*/); + initial begin + int i; + + i = {} + 1; + + i = {}; + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_queue_slice.v b/test_regress/t/t_queue_slice.v index 3ee6e2844..fde0ebd62 100644 --- a/test_regress/t/t_queue_slice.v +++ b/test_regress/t/t_queue_slice.v @@ -26,6 +26,9 @@ module t (/*AUTOARG*/); q = '{"q"}; v = $sformatf("%p", q); `checks(v, "'{\"q\"} "); + q = {}; + i = q.size(); `checkh(i, 0); + q = '{"q", "b", "c", "d", "e", "f"}; if (q[0] !== "q") $stop; v = $sformatf("%p", q); `checks(v, "'{\"q\", \"b\", \"c\", \"d\", \"e\", \"f\"} "); From f64d9d87f60133ce4a8bc6a6abac4c008c759d33 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 28 Aug 2021 17:44:14 -0400 Subject: [PATCH 80/84] Clang compile fix --- src/V3AstNodes.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index b5c324b03..da6da2a01 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -188,10 +188,10 @@ public: AstEmptyQueue(FileLine* fl) : ASTGEN_SUPER_EmptyQueue(fl) {} ASTNODE_NODE_FUNCS(EmptyQueue) - virtual string emitC() { V3ERROR_NA_RETURN(""); } - virtual string emitVerilog() { return "{}"; } + virtual string emitC() override { V3ERROR_NA_RETURN(""); } + virtual string emitVerilog() override { return "{}"; } virtual bool same(const AstNode* samep) const override { return true; } - virtual bool cleanOut() const { return true; } + virtual bool cleanOut() const override { return true; } }; class AstRange final : public AstNodeRange { From cd9e9da4b5e537bd6e07acd0a63e7e88467b160d Mon Sep 17 00:00:00 2001 From: Krzysztof Bieganski Date: Mon, 30 Aug 2021 16:44:03 +0200 Subject: [PATCH 81/84] Support assignment patterns as children of pins (#3041) Signed-off-by: Krzysztof Bieganski --- src/V3Width.cpp | 3 +++ test_regress/t/t_param_pattern.pl | 22 +++++++++++++++ test_regress/t/t_param_pattern.v | 45 +++++++++++++++++++++++++++++++ 3 files changed, 70 insertions(+) create mode 100755 test_regress/t/t_param_pattern.pl create mode 100644 test_regress/t/t_param_pattern.v diff --git a/src/V3Width.cpp b/src/V3Width.cpp index a26b7f47e..24b8e9c12 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -4152,6 +4152,9 @@ private: // TOP LEVEL NODE if (nodep->modVarp() && nodep->modVarp()->isGParam()) { // Widthing handled as special init() case + if (auto* patternp = VN_CAST(nodep->exprp(), Pattern)) + if (auto* modVarp = nodep->modVarp()) + patternp->childDTypep(modVarp->childDTypep()->cloneTree(false)); userIterateChildren(nodep, WidthVP(SELF, BOTH).p()); } else if (!m_paramsOnly) { if (!nodep->modVarp()->didWidth()) { diff --git a/test_regress/t/t_param_pattern.pl b/test_regress/t/t_param_pattern.pl new file mode 100755 index 000000000..e0e57ed54 --- /dev/null +++ b/test_regress/t/t_param_pattern.pl @@ -0,0 +1,22 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 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 + +scenarios(simulator => 1); + +compile( + verilator_flags2 => ['--dump-tree'] + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_param_pattern.v b/test_regress/t/t_param_pattern.v new file mode 100644 index 000000000..5f9306d64 --- /dev/null +++ b/test_regress/t/t_param_pattern.v @@ -0,0 +1,45 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2021 by Krzysztof Bieganski. +// SPDX-License-Identifier: CC0-1.0 + +`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d\n", `__FILE__,`__LINE__, (gotv), (expv)); $stop; end while(0); + +package config_pkg; + typedef struct packed { + int UPPER0; + int UPPER2; + int USE_QUAD0; + int USE_QUAD1; + int USE_QUAD2; + } config_struct; + +endpackage : config_pkg + +module t; + import config_pkg::*; + + struct_submodule #(.MY_CONFIG('{ + UPPER0: 10, + UPPER2: 20, + USE_QUAD0: 4, + USE_QUAD1: 5, + USE_QUAD2: 6 + })) a_submodule_I (); +endmodule : t + +module struct_submodule + import config_pkg::*; + #(parameter config_struct MY_CONFIG = '0); + + initial begin + `checkd(MY_CONFIG.UPPER0, 10); + `checkd(MY_CONFIG.USE_QUAD0, 4); + `checkd(MY_CONFIG.USE_QUAD1, 5); + `checkd(MY_CONFIG.USE_QUAD2, 6); + `checkd(MY_CONFIG.UPPER2, 20); + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule : struct_submodule From 3e03cd5a4da61b6ad41e88d4f86a2e71ba5b5636 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Wed, 1 Sep 2021 20:39:59 -0400 Subject: [PATCH 82/84] Commentary --- Changes | 5 ++++- src/V3Const.cpp | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Changes b/Changes index 526a53539..d833c1d82 100644 --- a/Changes +++ b/Changes @@ -16,6 +16,7 @@ Verilator 4.211 devel * Fix re-evaluation of logic dependent on state set in DPI exports (#3091). [Geza Lore] * Support unpacked array localparams in tasks/functions (#3078). [Geza Lore] * Support timeunit/timeprecision in $unit. +* Support assignment patterns as children of pins (#3041). [Krzysztof Bieganski] * Add --instr-count-dpi to tune assumed DPI import cost for multithreaded model scheduling. Default value changed to 200 (#3068). [Yinan Xu] * Output files are split based on the set of headers required @@ -23,12 +24,14 @@ Verilator 4.211 devel * Parameter values are now emitted as 'static constexpr' instead of enum. C++ direct references to parameters might require updating (#3077). [Geza Lore] * Refactored Verilated include files; include verilated.h not verilated_heavy.h. -* Add error when constant function under a generate (#3103). [Don Owen] * Add header guards on Dpi.h generated files (#2979). [Tood Strader] +* Add XML ccall, constpool, initarray, and if/while begins (#3080). [Steven Hugg] +* Add error when constant function under a generate (#3103). [Don Owen] * Fix -G to treat simple integer literals as signed (#3060). [Anikin1610] * Fix emitted string array initializers (#2895). [Iztok Jeras] * Fix bitop tree optimization dropping necessary & operator (#3096). [Flavien Solt] * Fix internal error on wide -x-initial unique (#3106). [Alexandre Joannou] +* Fix traces to show array instances with brackets (#3092) (#3095). [Pieter Kapsenberg] Verilator 4.210 2021-07-07 diff --git a/src/V3Const.cpp b/src/V3Const.cpp index daec651bd..6c279f8ad 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -354,7 +354,7 @@ class ConstBitOpTreeVisitor final : public AstNVisitor { #define CONST_BITOP_SET_FAILED(reason, nodep) setFailed(true, reason, nodep, __LINE__) bool setFailed(bool fail, const char* reason, AstNode* nodep, int line) { - if (fail) { + if (fail && !m_failed) { UINFO(9, "cannot optimize " << m_rootp << " reason:" << reason << " called from line:" << line << " when checking:" << nodep << std::endl); // if (debug() >= 9) m_rootp->dumpTree(std::cout << "Root node:\n"); From cd56dfee1c7774b79818553f2f0e47591c530c79 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Wed, 1 Sep 2021 20:45:41 -0400 Subject: [PATCH 83/84] Update GTKwave from upstream. --- include/gtkwave/fstapi.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/include/gtkwave/fstapi.c b/include/gtkwave/fstapi.c index b4c9823d2..2e28e64ac 100644 --- a/include/gtkwave/fstapi.c +++ b/include/gtkwave/fstapi.c @@ -4989,9 +4989,7 @@ unsigned int secnum = 0; int blocks_skipped = 0; fst_off_t blkpos = 0; uint64_t seclen, beg_tim; -#ifdef FST_DEBUG uint64_t end_tim; -#endif uint64_t frame_uclen, frame_clen, frame_maxhandle, vc_maxhandle; fst_off_t vc_start; fst_off_t indx_pntr, indx_pos; @@ -5058,14 +5056,11 @@ for(;;) if(!seclen) break; beg_tim = fstReaderUint64(xc->f); -#ifdef FST_DEBUG - end_tim = -#endif - fstReaderUint64(xc->f); + end_tim = fstReaderUint64(xc->f); if(xc->limit_range_valid) { - if(beg_tim < xc->limit_range_start) + if(end_tim < xc->limit_range_start) { blocks_skipped++; blkpos += seclen; From 960813cb0f313d31a4ff3b4150dcdd3555ee3132 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Wed, 1 Sep 2021 20:58:03 -0400 Subject: [PATCH 84/84] Version bump --- Changes | 2 +- configure.ac | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Changes b/Changes index d833c1d82..82fe61b54 100644 --- a/Changes +++ b/Changes @@ -8,7 +8,7 @@ The changes in each Verilator version are described below. The contributors that suggested a given feature are shown in []. Thanks! -Verilator 4.211 devel +Verilator 4.212 2021-09-01 ========================== **Minor:** diff --git a/configure.ac b/configure.ac index 85faa4e90..720f03e4c 100644 --- a/configure.ac +++ b/configure.ac @@ -7,7 +7,7 @@ #AC_INIT([Verilator],[#.### YYYY-MM-DD]) #AC_INIT([Verilator],[#.### devel]) -AC_INIT([Verilator],[4.211 devel], +AC_INIT([Verilator],[4.212 2021-09-01], [https://verilator.org], [verilator],[https://verilator.org]) # When releasing, also update header of Changes file