diff --git a/Changes b/Changes index a9b0b3003..87ea80044 100644 --- a/Changes +++ b/Changes @@ -29,6 +29,8 @@ Verilator 4.225 devel * Fix table misoptimizing away display (#3488). [Stefan Post] * Fix wrong bit op tree optimization (#3509). [Nathan Graybeal] * Fix incorrect tristate logic (#3399) [shareefj, Vighnesh Iyer] +* Fix segfault exporting non-existant package (#3535). +* Fix case statement comparing string literal (#3544). [Gustav Svensk] Verilator 4.224 2022-06-19 diff --git a/docs/CONTRIBUTORS b/docs/CONTRIBUTORS index 0b003034b..228787d5e 100644 --- a/docs/CONTRIBUTORS +++ b/docs/CONTRIBUTORS @@ -99,6 +99,7 @@ Rafal Kapuscik Raynard Qiao Richard Myers Rupert Swarbrick +Ryszard Rozak Samuel Riedel Sean Cross Sebastien Van Cauwenberghe diff --git a/include/verilatedos.h b/include/verilatedos.h index d06d5fd99..c89b4c6dc 100644 --- a/include/verilatedos.h +++ b/include/verilatedos.h @@ -218,22 +218,23 @@ // C++-2011 #if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__) || defined(VL_CPPCHECK) -# ifndef VL_NO_LEGACY -// These are deprecated historical defines. We leave them in case users referenced them. -# define VL_EQ_DELETE = delete -# define vl_unique_ptr std::unique_ptr -# define vl_unordered_map std::unordered_map -# define vl_unordered_set std::unordered_set -# define VL_INCLUDE_UNORDERED_MAP -# define VL_INCLUDE_UNORDERED_SET -# define VL_FINAL final -# define VL_MUTABLE mutable -# define VL_OVERRIDE override -# endif #else # error "Verilator requires a C++11 or newer compiler" #endif +#ifndef VL_NO_LEGACY +// These are deprecated historical defines. We leave them in case users referenced them. +# define VL_EQ_DELETE = delete +# define vl_unique_ptr std::unique_ptr +# define vl_unordered_map std::unordered_map +# define vl_unordered_set std::unordered_set +# define VL_INCLUDE_UNORDERED_MAP +# define VL_INCLUDE_UNORDERED_SET +# define VL_FINAL final +# define VL_MUTABLE mutable +# define VL_OVERRIDE override +#endif + //========================================================================= // C++-2017 diff --git a/src/V3Ast.h b/src/V3Ast.h index 46f4b1dcc..ed8b8bc93 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -1520,6 +1520,16 @@ public: AstNode* firstAbovep() const { // Returns nullptr when second or later in list return ((backp() && backp()->nextp() != this) ? backp() : nullptr); } + // isFirstInMyListOfStatements(n) -- implemented by child classes: + // AstNodeBlock, AstCaseItem, AstNodeIf, AstNodeFTask, and possibly others. + virtual bool isFirstInMyListOfStatements(AstNode* n) const { return false; } + // isStandaloneBodyStmt == Do we need a ; on generated cpp for this node? + bool isStandaloneBodyStmt() { + return (!firstAbovep() // we're 2nd or later in the list, so yes need ; + + // If we're first in the list, check what backp() thinks of us: + || (backp() && backp()->isFirstInMyListOfStatements(this))); + } uint8_t brokenState() const { return m_brokenState; } void brokenState(uint8_t value) { m_brokenState = value; } @@ -2027,7 +2037,7 @@ public: // Same as above, but for 'const' nodes template - void exists(std::function p) const { + bool exists(std::function p) const { static_assert(checkTypeParameter(), "Invalid type parameter 'T_Node'"); return predicateImpl(this, p); } @@ -2044,7 +2054,7 @@ public: // Same as above, but for 'const' nodes template - void forall(std::function p) const { + bool forall(std::function p) const { static_assert(checkTypeParameter(), "Invalid type parameter 'T_Node'"); return predicateImpl(this, p); } @@ -2589,6 +2599,7 @@ public: AstNode* stmtsp() const { return op1p(); } // op1 = List of statements void addStmtsp(AstNode* nodep) { addNOp1p(nodep); } bool unnamed() const { return m_unnamed; } + bool isFirstInMyListOfStatements(AstNode* nodep) const override { return nodep == stmtsp(); } }; class AstNodePreSel VL_NOT_FINAL : public AstNode { @@ -2734,6 +2745,9 @@ public: VBranchPred branchPred() const { return m_branchPred; } void isBoundsCheck(bool flag) { m_isBoundsCheck = flag; } bool isBoundsCheck() const { return m_isBoundsCheck; } + bool isFirstInMyListOfStatements(AstNode* n) const override { + return n == ifsp() || n == elsesp(); + } }; class AstNodeCase VL_NOT_FINAL : public AstNodeStmt { @@ -3261,6 +3275,7 @@ public: bool isVirtual() const { return m_virtual; } void lifetime(const VLifetime& flag) { m_lifetime = flag; } VLifetime lifetime() const { return m_lifetime; } + bool isFirstInMyListOfStatements(AstNode* n) const override { return n == stmtsp(); } }; class AstNodeFTaskRef VL_NOT_FINAL : public AstNodeStmt { diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index af560f87d..0316ce921 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -77,10 +77,11 @@ public: , m_num(this, width, value) { initWithNumber(); } - class DtypedValue {}; // for creator type-overload selection - AstConst(FileLine* fl, DtypedValue, AstNodeDType* nodedtypep, uint32_t value) + class DTyped {}; // for creator type-overload selection + // Zero/empty constant with a type matching nodetypep + AstConst(FileLine* fl, DTyped, const AstNodeDType* nodedtypep) : ASTGEN_SUPER_Const(fl) - , m_num(this, nodedtypep->width(), value, nodedtypep->widthSized()) { + , m_num(this, nodedtypep) { initWithNumber(); } class StringToParse {}; // for creator type-overload selection @@ -3592,6 +3593,7 @@ public: void addStmtp(AstNode* nodep) { addOp2p(nodep); } // Special accessors bool isJustOneBodyStmt() const { return bodysp() && !bodysp()->nextp(); } + bool isFirstInMyListOfStatements(AstNode* n) const override { return n == bodysp(); } }; class AstAssign final : public AstNodeAssign { @@ -4018,6 +4020,7 @@ public: void condsp(AstNode* nodep) { setOp1p(nodep); } void addBodysp(AstNode* newp) { addOp2p(newp); } bool isDefault() const { return condsp() == nullptr; } + bool isFirstInMyListOfStatements(AstNode* n) const override { return n == bodysp(); } }; class AstSFormatF final : public AstNode { @@ -4703,6 +4706,7 @@ public: virtual bool isGateOptimizable() const override { return false; } virtual int instrCount() const override { return INSTR_COUNT_BRANCH; } virtual bool same(const AstNode* /*samep*/) const override { return true; } + bool isFirstInMyListOfStatements(AstNode* n) const override { return n == bodysp(); } }; class AstRepeat final : public AstNodeStmt { @@ -4720,6 +4724,7 @@ public: } // Not relevant - converted to FOR virtual int instrCount() const override { return INSTR_COUNT_BRANCH; } virtual bool same(const AstNode* /*samep*/) const override { return true; } + bool isFirstInMyListOfStatements(AstNode* n) const override { return n == bodysp(); } }; class AstWait final : public AstNodeStmt { @@ -4731,6 +4736,7 @@ public: } ASTNODE_NODE_FUNCS(Wait) AstNode* bodysp() const { return op3p(); } // op3 = body of loop + bool isFirstInMyListOfStatements(AstNode* n) const override { return n == bodysp(); } }; class AstWhile final : public AstNodeStmt { @@ -4757,6 +4763,7 @@ public: virtual void addBeforeStmt(AstNode* newp, AstNode* belowp) override; // Stop statement searchback here virtual void addNextStmt(AstNode* newp, AstNode* belowp) override; + bool isFirstInMyListOfStatements(AstNode* n) const override { return n == bodysp(); } }; class AstBreak final : public AstNodeStmt { diff --git a/src/V3Common.cpp b/src/V3Common.cpp index 0d5ca45f3..ad4ecaf6e 100644 --- a/src/V3Common.cpp +++ b/src/V3Common.cpp @@ -87,10 +87,10 @@ static void makeToStringMiddle(AstClass* nodep) { } } if (nodep->extendsp() && nodep->extendsp()->classp()->user1()) { - string stmt = "out += \""; + string stmt = "out += "; if (!comma.empty()) stmt += "\", \"+ "; // comma = ", "; // Nothing further so not needed - stmt += nodep->extendsp()->dtypep()->nameProtect(); + stmt += EmitCBaseVisitor::prefixNameProtect(nodep->extendsp()->dtypep()); stmt += "::to_string_middle();\n"; nodep->user1(true); // So what we extend dumps this funcp->addStmtsp(new AstCStmt{nodep->fileline(), stmt}); @@ -104,13 +104,13 @@ static void makeToStringMiddle(AstClass* nodep) { void V3Common::commonAll() { UINFO(2, __FUNCTION__ << ": " << endl); + // NODE STATE + // Entire netlist: + // AstClass::user1() -> bool. True if class needs to_string dumper + const VNUser1InUse m_inuser1; // 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 - const VNUser1InUse m_inuser1; // Create ToString methods makeVlToString(classp); makeToString(classp); diff --git a/src/V3Const.cpp b/src/V3Const.cpp index 4e9f60d5e..05f673851 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -1089,7 +1089,7 @@ private: if (orLIsRedundant && orRIsRedundant) { nodep->replaceWith( - new AstConst(nodep->fileline(), AstConst::DtypedValue(), nodep->dtypep(), 0)); + new AstConst(nodep->fileline(), AstConst::DTyped{}, nodep->dtypep())); VL_DO_DANGLING(nodep->deleteTree(), nodep); return true; } else if (orLIsRedundant) { diff --git a/src/V3EmitV.cpp b/src/V3EmitV.cpp index 9349e70dc..eec9034f8 100644 --- a/src/V3EmitV.cpp +++ b/src/V3EmitV.cpp @@ -504,6 +504,11 @@ class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor { emitVerilogFormat(nodep, nodep->emitVerilog(), nodep->lhsp(), nodep->rhsp(), nodep->thsp()); } + virtual void visit(AstMemberSel* nodep) override { + iterate(nodep->fromp()); + puts("."); + puts(nodep->prettyName()); + } virtual void visit(AstAttrOf* nodep) override { putfs(nodep, "$_ATTROF("); iterateAndNextConstNull(nodep->fromp()); diff --git a/src/V3Number.cpp b/src/V3Number.cpp index 9ff6bf5b1..214465f70 100644 --- a/src/V3Number.cpp +++ b/src/V3Number.cpp @@ -110,6 +110,18 @@ V3Number::V3Number(VerilogStringLiteral, AstNode* nodep, const string& str) { opCleanThis(true); } +V3Number::V3Number(AstNode* nodep, const AstNodeDType* nodedtypep) { + if (nodedtypep->isString()) { + init(nodep, 0); + setString(""); + } else if (nodedtypep->isDouble()) { + init(nodep, 64); + setDouble(0.0); + } else { + init(nodep, nodedtypep->width(), nodedtypep->widthSized()); + } +} + void V3Number::V3NumberCreate(AstNode* nodep, const char* sourcep, FileLine* fl) { init(nodep, 0); m_fileline = fl; diff --git a/src/V3Number.h b/src/V3Number.h index a574bb191..fe2f11c5d 100644 --- a/src/V3Number.h +++ b/src/V3Number.h @@ -39,6 +39,7 @@ inline bool v3EpsilonEqual(double a, double b) { //============================================================================ class AstNode; +class AstNodeDType; class FileLine; // Holds a few entries of ValueAndX to avoid dynamic allocation in std::vector for less width of @@ -252,6 +253,11 @@ public: opCleanThis(); m_fileline = nump->fileline(); } + V3Number(AstNode* nodep, double value) { + init(nodep, 64); + setDouble(value); + } + V3Number(AstNode* nodep, const AstNodeDType* nodedtypep); private: void V3NumberCreate(AstNode* nodep, const char* sourcep, FileLine* fl); @@ -311,9 +317,7 @@ public: // (use AstConst::isSigned()) bool isDouble() const { return m_double; } // Only if have 64 bit value loaded, and want to indicate it's real - void isDouble(bool flag) { m_double = flag; } bool isString() const { return m_isString; } - void isString(bool flag) { m_isString = flag; } bool isNegative() const { return bitIs1(width() - 1); } bool isNull() const { return m_isNull; } bool isFourState() const; @@ -337,7 +341,7 @@ public: bool isAnyX() const; bool isAnyXZ() const; bool isAnyZ() const; - bool isMsbXZ() const { return bitIsXZ(m_width); } + bool isMsbXZ() const { return bitIsXZ(m_width - 1); } uint32_t toUInt() const; int32_t toSInt() const; uint64_t toUQuad() const; diff --git a/src/V3Param.cpp b/src/V3Param.cpp index 8a2b5a415..62aa66541 100644 --- a/src/V3Param.cpp +++ b/src/V3Param.cpp @@ -186,19 +186,21 @@ public: return pinValuep->num().toString() == hierOptParamp->num().toString(); } - // Bitwidth of hierOptParamp is accurate because V3Width already caluclated in the previous - // run. Bitwidth of pinValuep is before width analysis, so pinValuep is casted to - // hierOptParamp width. - V3Number varNum(pinValuep, hierOptParamp->num().width()); if (hierOptParamp->isDouble()) { - varNum.isDouble(true); + double var; if (pinValuep->isDouble()) { - varNum.opAssign(pinValuep->num()); + var = pinValuep->num().toDouble(); } else { // Cast from integer to real + V3Number varNum{pinValuep, 0.0}; varNum.opIToRD(pinValuep->num()); + var = varNum.toDouble(); } - return v3EpsilonEqual(varNum.toDouble(), hierOptParamp->num().toDouble()); + return v3EpsilonEqual(var, hierOptParamp->num().toDouble()); } else { // Now integer type is assumed + // Bitwidth of hierOptParamp is accurate because V3Width already caluclated in the + // previous run. Bitwidth of pinValuep is before width analysis, so pinValuep is casted + // to hierOptParamp width. + V3Number varNum{pinValuep, hierOptParamp->num().width()}; if (pinValuep->isDouble()) { // Need to cast to int // Parameter is actually an integral type, but passed value is floating point. // Conversion from real to integer uses rounding in V3Width.cpp diff --git a/src/V3ParseSym.h b/src/V3ParseSym.h index 998dac016..3a60bfbea 100644 --- a/src/V3ParseSym.h +++ b/src/V3ParseSym.h @@ -168,7 +168,7 @@ public: "Export package not found"); symCurrentp()->exportFromPackage(&m_syms, symp, id_or_star); } - void exportStarStar(AstNode* packagep) { + void exportStarStar() { // Export *::* from remote packages symCurrentp()->exportStarStar(&m_syms); } diff --git a/src/V3Sched.cpp b/src/V3Sched.cpp index 24f496c99..f1085acc6 100644 --- a/src/V3Sched.cpp +++ b/src/V3Sched.cpp @@ -93,8 +93,9 @@ std::vector getSenTreesUsedBy(const std::vectorfileline(); AstVarRef* const refp = new AstVarRef{flp, vscp, VAccess::WRITE}; - AstConst* const zerop = new AstConst{flp, AstConst::DtypedValue{}, vscp->dtypep(), val}; - return new AstAssign{flp, refp, zerop}; + AstConst* const valp = new AstConst{flp, AstConst::DTyped{}, vscp->dtypep()}; + valp->num().setLong(val); + return new AstAssign{flp, refp, valp}; }; void remapSensitivities(LogicByScope& lbs, @@ -686,8 +687,8 @@ std::pair makeEvalLoop(AstNetlist* netlistp, const strin { const uint32_t limit = v3Global.opt.convergeLimit(); AstVarRef* const refp = new AstVarRef{flp, counterp, VAccess::READ}; - AstConst* const constp - = new AstConst{flp, AstConst::DtypedValue{}, counterp->dtypep(), limit}; + AstConst* const constp = new AstConst{flp, AstConst::DTyped{}, counterp->dtypep()}; + constp->num().setLong(limit); AstNodeMath* const condp = new AstGt{flp, refp, constp}; AstIf* const failp = new AstIf{flp, condp}; ifp->addIfsp(failp); @@ -708,8 +709,8 @@ std::pair makeEvalLoop(AstNetlist* netlistp, const strin { AstVarRef* const wrefp = new AstVarRef{flp, counterp, VAccess::WRITE}; AstVarRef* const rrefp = new AstVarRef{flp, counterp, VAccess::READ}; - AstConst* const onep - = new AstConst{flp, AstConst::DtypedValue{}, counterp->dtypep(), 1}; + AstConst* const onep = new AstConst{flp, AstConst::DTyped{}, counterp->dtypep()}; + onep->num().setLong(1); ifp->addIfsp(new AstAssign{flp, wrefp, new AstAdd{flp, rrefp, onep}}); } diff --git a/src/V3Simulate.h b/src/V3Simulate.h index e2418e60d..42fadf297 100644 --- a/src/V3Simulate.h +++ b/src/V3Simulate.h @@ -239,13 +239,11 @@ private: } if (allocNewConst) { // Need to allocate new constant - constp = new AstConst{nodep->fileline(), AstConst::DtypedValue{}, nodep->dtypep(), 0}; + constp = new AstConst{nodep->fileline(), AstConst::DTyped{}, nodep->dtypep()}; // Mark as in use, add to free list for later reuse constp->user2(1); freeList.push_back(constp); } - constp->num().isDouble(nodep->isDouble()); - constp->num().isString(nodep->isString()); return constp; } diff --git a/src/V3Tristate.cpp b/src/V3Tristate.cpp index 6f80c2f82..21abe682c 100644 --- a/src/V3Tristate.cpp +++ b/src/V3Tristate.cpp @@ -385,7 +385,15 @@ class TristateVisitor final : public TristateBaseVisitor { return newp; } AstNode* getEnp(AstNode* nodep) { - if (!nodep->user1p()) { + if (nodep->user1p()) { + if (AstVarRef* const refp = VN_CAST(nodep, VarRef)) { + if (refp->varp()->isIO()) { + // When reading a tri-state port, we can always use the value + // because such port will have resolution logic in upper module. + return newAllZerosOrOnes(nodep, true); + } + } + } else { // There's no select being built yet, so add what will become a // constant output enable driver of all 1's nodep->user1p(newAllZerosOrOnes(nodep, true)); @@ -929,7 +937,7 @@ class TristateVisitor final : public TristateBaseVisitor { iterateChildren(nodep); UINFO(9, dbgState() << nodep << endl); // Constification always moves const to LHS - const AstConst* const constp = VN_CAST(nodep->lhsp(), Const); + AstConst* const constp = VN_CAST(nodep->lhsp(), Const); AstVarRef* const varrefp = VN_CAST(nodep->rhsp(), VarRef); // Input variable if (constp && constp->user1p() && varrefp) { // 3'b1z0 -> ((3'b101 == in__en) && (3'b100 == in)) @@ -952,6 +960,21 @@ class TristateVisitor final : public TristateBaseVisitor { if (debug() >= 9) newp->dumpTree(cout, "-caseeq-new: "); nodep->replaceWith(newp); VL_DO_DANGLING(pushDeletep(nodep), nodep); + } else if (constp && nodep->rhsp()->user1p()) { + FileLine* const fl = nodep->fileline(); + constp->unlinkFrBack(); + AstNode* const rhsp = nodep->rhsp()->unlinkFrBack(); + AstNode* newp = new AstLogAnd{ + fl, new AstEq{fl, newAllZerosOrOnes(constp, false), rhsp->user1p()}, + // Keep the caseeq if there are X's present + new AstEqCase{fl, constp, rhsp}}; + if (neq) newp = new AstLogNot{fl, newp}; + rhsp->user1p(nullptr); + UINFO(9, " newceq " << newp << endl); + if (debug() >= 9) nodep->dumpTree(cout, "-caseeq-old: "); + if (debug() >= 9) newp->dumpTree(cout, "-caseeq-new: "); + nodep->replaceWith(newp); + VL_DO_DANGLING(pushDeletep(nodep), nodep); } else { checkUnhandled(nodep); } diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 9e7f4077d..ba22697b5 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -209,6 +209,7 @@ private: // TYPES using TableMap = std::map, AstVar*>; using PatVecMap = std::map; + using DTypeMap = std::map; // STATE WidthVP* m_vup = nullptr; // Current node state @@ -3090,7 +3091,9 @@ private: newp = new AstCMethodHard(nodep->fileline(), nodep->fromp()->unlinkFrBack(), nodep->name()); newp->dtypeFrom(adtypep->subDTypep()); - if (!nodep->firstAbovep()) newp->makeStatement(); + // Because queue methods pop_front() or pop_back() can be void cast, + // they use makeStatement to check if they need the c++ ";" added. + if (nodep->isStandaloneBodyStmt()) newp->makeStatement(); } else if (nodep->name() == "push_back" || nodep->name() == "push_front") { methodOkArguments(nodep, 1, 1); methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::WRITE); @@ -3564,11 +3567,9 @@ private: // which member each AstPatMember corresponds to before we can // determine the dtypep for that PatMember's value, and then // width the initial value appropriately. - using PatMap = std::map; // Store member: value - using DTypeMap - = std::map; // Store data_type: default_value - PatMap patmap; - DTypeMap dtypemap; + using PatMap = std::map; + PatMap patmap; // Store member: value + DTypeMap dtypemap; // Store data_type: default_value { const AstMemberDType* memp = vdtypep->membersp(); AstPatMember* patp = VN_CAST(nodep->itemsp(), PatMember); @@ -3631,44 +3632,23 @@ private: for (AstMemberDType* memp = vdtypep->membersp(); memp; memp = VN_AS(memp->nextp(), MemberDType)) { const auto it = patmap.find(memp); - AstPatMember* newpatp = nullptr; AstPatMember* patp = nullptr; if (it == patmap.end()) { - const string memp_DType = memp->virtRefDTypep()->prettyDTypeName(); - const auto it2 = dtypemap.find(memp_DType); - if (it2 != dtypemap.end()) { - // default_value for data_type - patp = it2->second; - newpatp = patp->cloneTree(false); - patp = newpatp; - } else if (defaultp) { - // default_value for any unassigned member yet - newpatp = defaultp->cloneTree(false); - patp = newpatp; + // default or deafult_type assignment + if (AstNodeUOrStructDType* const memp_nested_vdtypep + = VN_CAST(memp->virtRefDTypep(), NodeUOrStructDType)) { + newp = nestedvalueConcat_patternUOrStruct(memp_nested_vdtypep, defaultp, newp, + nodep, dtypemap); } else { - if (!VN_IS(vdtypep, UnionDType)) { - nodep->v3error("Assignment pattern missed initializing elements: " - << memp->virtRefDTypep()->prettyDTypeName() << " " - << memp->prettyName()); - } + patp = Defaultpatp_patternUOrStruct(nodep, memp, patp, vdtypep, defaultp, + dtypemap); + newp = valueConcat_patternUOrStruct(patp, newp, memp, nodep); } } else { + // member assignment patp = it->second; + newp = valueConcat_patternUOrStruct(patp, newp, memp, nodep); } - if (patp) { - // Determine initial values - patp->dtypep(memp); - AstNode* const valuep = patternMemberValueIterate(patp); - if (!newp) { - newp = valuep; - } else { - AstConcat* const concatp = new AstConcat(patp->fileline(), newp, valuep); - newp = concatp; - newp->dtypeSetLogicSized(concatp->lhsp()->width() + concatp->rhsp()->width(), - nodep->dtypep()->numeric()); - } - } - if (newpatp) VL_DO_DANGLING(pushDeletep(newpatp), newpatp); } if (newp) { nodep->replaceWith(newp); @@ -3677,6 +3657,67 @@ private: } VL_DO_DANGLING(pushDeletep(nodep), nodep); // Deletes defaultp also, if present } + + AstNode* nestedvalueConcat_patternUOrStruct(AstNodeUOrStructDType* memp_vdtypep, + AstPatMember* defaultp, AstNode* newp, + AstPattern* nodep, DTypeMap dtypemap) { + AstPatMember* patp = nullptr; + for (AstMemberDType* memp_nested = memp_vdtypep->membersp(); memp_nested; + memp_nested = VN_AS(memp_nested->nextp(), MemberDType)) { + if (AstNodeUOrStructDType* const memp_multinested_vdtypep + = VN_CAST(memp_nested->virtRefDTypep(), NodeUOrStructDType)) { + // When unpacked struct/union is supported this if will need some additional + // conditions + newp = nestedvalueConcat_patternUOrStruct(memp_multinested_vdtypep, defaultp, newp, + nodep, dtypemap); + } else { + patp = Defaultpatp_patternUOrStruct(nodep, memp_nested, patp, memp_vdtypep, + defaultp, dtypemap); + newp = valueConcat_patternUOrStruct(patp, newp, memp_nested, nodep); + } + } + return newp; + } + + AstPatMember* Defaultpatp_patternUOrStruct(AstPattern* nodep, AstMemberDType* memp, + AstPatMember* patp, + AstNodeUOrStructDType* memp_vdtypep, + AstPatMember* defaultp, DTypeMap dtypemap) { + const string memp_DType = memp->virtRefDTypep()->prettyDTypeName(); + const auto it = dtypemap.find(memp_DType); + if (it != dtypemap.end()) { + // default_value for data_type + patp = it->second->cloneTree(false); + } else if (defaultp) { + // default_value for any unmatched member yet + patp = defaultp->cloneTree(false); + } else { + if (!VN_IS(memp_vdtypep, UnionDType)) { + nodep->v3error("Assignment pattern missed initializing elements: " + << memp->virtRefDTypep()->prettyDTypeNameQ() << " " + << memp->prettyNameQ()); + } + } + return patp; + } + + AstNode* valueConcat_patternUOrStruct(AstPatMember* patp, AstNode* newp, AstMemberDType* memp, + AstPattern* nodep) { + if (patp) { + patp->dtypep(memp); + AstNode* const valuep = patternMemberValueIterate(patp); + if (!newp) { + newp = valuep; + } else { + AstConcat* const concatp = new AstConcat{patp->fileline(), newp, valuep}; + newp = concatp; + newp->dtypeSetLogicSized(concatp->lhsp()->width() + concatp->rhsp()->width(), + nodep->dtypep()->numeric()); + } + } + return newp; + } + void patternArray(AstPattern* nodep, AstNodeArrayDType* arrayDtp, AstPatMember* defaultp) { const VNumRange range = arrayDtp->declRange(); PatVecMap patmap = patVectorMap(nodep, range); @@ -3938,8 +3979,10 @@ private: itemp = VN_AS(itemp->nextp(), CaseItem)) { for (AstNode* condp = itemp->condsp(); condp; condp = condp->nextp()) { if (condp->dtypep() != subDTypep) { - if (condp->dtypep()->isDouble()) { + if (condp->dtypep()->isDouble() || subDTypep->isDouble()) { subDTypep = nodep->findDoubleDType(); + } else if (condp->dtypep()->isString() || subDTypep->isString()) { + subDTypep = nodep->findStringDType(); } else { const int width = std::max(subDTypep->width(), condp->width()); const int mwidth = std::max(subDTypep->widthMin(), condp->widthMin()); diff --git a/src/verilog.y b/src/verilog.y index afa64c124..b3baf5ac3 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -1171,7 +1171,7 @@ package_import_itemObj: // IEEE: part of package_import_item package_export_declaration: // IEEE: package_export_declaration yEXPORT '*' yP_COLONCOLON '*' ';' - { $$ = new AstPackageExportStarStar{$2}; SYMP->exportStarStar($1); } + { $$ = new AstPackageExportStarStar{$2}; SYMP->exportStarStar(); } | yEXPORT package_export_itemList ';' { $$ = $2; } ; @@ -1182,8 +1182,8 @@ package_export_itemList: package_export_item: // ==IEEE: package_export_item idCC yP_COLONCOLON package_import_itemObj - { $$ = new AstPackageExport($3, VN_CAST($1, Package), *$3); - SYMP->exportItem($1,*$3); } + { $$ = new AstPackageExport{$3, VN_CAST($1, Package), *$3}; + if ($1) SYMP->exportItem($1, *$3); } ; //********************************************************************** @@ -3533,8 +3533,6 @@ patternKey: // IEEE: merge structure_pattern_key, array_patt // // id/*member*/ is part of constExpr below //UNSUP constExpr { $$ = $1; } // // IEEE: assignment_pattern_key - //UNSUP simple_type { $1->v3error("Unsupported: '{} with data type as key"); $$ = $1; } - // // simple_type reference looks like constExpr // // Verilator: // // The above expressions cause problems because "foo" may be // // a constant identifier (if array) or a reference to the diff --git a/test_regress/t/t_array_list_bad.out b/test_regress/t/t_array_list_bad.out index 0eeb8a06f..3ce83ac48 100644 --- a/test_regress/t/t_array_list_bad.out +++ b/test_regress/t/t_array_list_bad.out @@ -1,4 +1,4 @@ -%Error: t/t_array_list_bad.v:38:25: Assignment pattern missed initializing elements: logic t3 +%Error: t/t_array_list_bad.v:38:25: Assignment pattern missed initializing elements: 'logic' 't3' : ... In instance t 38 | test_out <= '{'0, '0}; | ^~ diff --git a/test_regress/t/t_array_packed_write_read.pl b/test_regress/t/t_array_packed_write_read.pl index d0de24f1c..b46d46042 100755 --- a/test_regress/t/t_array_packed_write_read.pl +++ b/test_regress/t/t_array_packed_write_read.pl @@ -9,7 +9,6 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di # SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 scenarios(simulator => 1); -$Self->{vlt_all} and unsupported("Verilator unsupported, bug446"); compile( ); diff --git a/test_regress/t/t_case_string2.pl b/test_regress/t/t_case_string2.pl new file mode 100755 index 000000000..1aa73f80a --- /dev/null +++ b/test_regress/t/t_case_string2.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 2022 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_case_string2.v b/test_regress/t/t_case_string2.v new file mode 100644 index 000000000..f2722639b --- /dev/null +++ b/test_regress/t/t_case_string2.v @@ -0,0 +1,21 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2022 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +module t; + +function automatic string broken_case(input string some_string); + case(some_string) + "alpha": return "alpha"; + default: return "beta"; + endcase +endfunction + + initial begin + $display(broken_case("gamma")); + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_flag_help.pl b/test_regress/t/t_flag_help.pl index cb38bfb87..93524f9d4 100755 --- a/test_regress/t/t_flag_help.pl +++ b/test_regress/t/t_flag_help.pl @@ -8,6 +8,8 @@ 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 +use File::Basename; + scenarios(dist => 1); # See also t_flag_version.pl @@ -16,27 +18,25 @@ sub check { my $interpreter = shift; my $prog = shift; + my $logfile = "$Self->{obj_dir}/t_help__" . basename($prog) . ".log"; + run(fails => 0, cmd => [$interpreter, $prog, "--help"], - logfile => "$Self->{obj_dir}/t_help.log", + logfile => $logfile, tee => 0, verilator_run => 1, ); - file_grep("$Self->{obj_dir}/t_help.log", qr/DISTRIBUTION/i); + file_grep($logfile, qr/(DISTRIBUTION|usage:)/i); } -foreach my $prog ( - "../bin/verilator", - "../bin/verilator_coverage", - "../bin/verilator_difftree", - "../bin/verilator_gantt", - "../bin/verilator_profcfunc", - ) { - check("perl", $prog); -} +check("perl", "../bin/verilator"); +check("perl", "../bin/verilator_coverage"); check("python3", "../bin/verilator_ccache_report"); +check("python3", "../bin/verilator_difftree"); +check("python3", "../bin/verilator_gantt"); +check("python3", "../bin/verilator_profcfunc"); ok(1); 1; diff --git a/test_regress/t/t_lint_block_redecl_bad.out b/test_regress/t/t_lint_block_redecl_bad.out new file mode 100644 index 000000000..a3729f293 --- /dev/null +++ b/test_regress/t/t_lint_block_redecl_bad.out @@ -0,0 +1,7 @@ +%Error: t/t_lint_block_redecl_bad.v:21:34: Duplicate declaration of block: 'COMB' + 21 | for(i=0; i<9; i++ ) begin: COMB + | ^~~~ + t/t_lint_block_redecl_bad.v:18:35: ... Location of original declaration + 18 | for(i=0; i<10; i++ ) begin: COMB + | ^~~~ +%Error: Exiting due to diff --git a/test_regress/t/t_lint_block_redecl_bad.pl b/test_regress/t/t_lint_block_redecl_bad.pl index 30201602c..dbc9830f6 100755 --- a/test_regress/t/t_lint_block_redecl_bad.pl +++ b/test_regress/t/t_lint_block_redecl_bad.pl @@ -9,7 +9,6 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di # SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 scenarios(vlt_all => 1); -$Self->{vlt_all} and unsupported("Verilator unsupported, bug485, false begin due to WHILE conversion blocks duplicate name detection"); lint( fails => 1, diff --git a/test_regress/t/t_package_alone_bad.out b/test_regress/t/t_package_alone_bad.out new file mode 100644 index 000000000..6144b36ec --- /dev/null +++ b/test_regress/t/t_package_alone_bad.out @@ -0,0 +1,5 @@ +%Error-PKGNODECL: t/t_package_alone_bad.v:7:8: Package/class 'pkg' not found, and needs to be predeclared (IEEE 1800-2017 26.3) + 7 | export pkg::something; + | ^~~ + ... For error description see https://verilator.org/warn/PKGNODECL?v=latest +%Error: Exiting due to diff --git a/test_regress/t/t_package_alone_bad.pl b/test_regress/t/t_package_alone_bad.pl new file mode 100755 index 000000000..27159da5b --- /dev/null +++ b/test_regress/t/t_package_alone_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 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_package_alone_bad.v b/test_regress/t/t_package_alone_bad.v new file mode 100644 index 000000000..34adc1b82 --- /dev/null +++ b/test_regress/t/t_package_alone_bad.v @@ -0,0 +1,7 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2022 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +export pkg::something; diff --git a/test_regress/t/t_structu_dataType_assignment.v b/test_regress/t/t_structu_dataType_assignment.v index 2962a6e78..209dc34b6 100644 --- a/test_regress/t/t_structu_dataType_assignment.v +++ b/test_regress/t/t_structu_dataType_assignment.v @@ -34,6 +34,20 @@ module top(); } DEF_struct; + typedef struct { // IEEE 1800-2017 SV CH:10.9.2 + int A; + struct { + int B, C; + struct{ + int D, E; + struct{ + int F; + shortint G; + } FG1; + } DE1; + } BC1; + } HIJ_struct; + // struct ab ab_struct ab; ab_struct abkey[1:0]; @@ -48,6 +62,9 @@ module top(); // struct DEF DEF_struct DEF; + // struct HIJ + HIJ_struct HIJ; + initial begin; // struct ab ab = '{0, 0}; //constant member by position @@ -130,6 +147,39 @@ module top(); if (DEF.BC2.B != 5) $stop; if (DEF.BC2.C != 5) $stop; + DEF = '{default:10}; + if (DEF.A != 10) $stop; + if (DEF.BC1.B != 10) $stop; + if (DEF.BC1.C != 10) $stop; + if (DEF.BC2.B != 10) $stop; + if (DEF.BC2.C != 10) $stop; + + DEF = '{int:10}; + if (DEF.A != 10) $stop; + if (DEF.BC1.B != 10) $stop; + if (DEF.BC1.C != 10) $stop; + if (DEF.BC2.B != 10) $stop; + if (DEF.BC2.C != 10) $stop; + + // struct HIJ + HIJ = '{int:10, default: 5}; + if (HIJ.A != 10) $stop; + if (HIJ.BC1.B != 10) $stop; + if (HIJ.BC1.C != 10) $stop; + if (HIJ.BC1.DE1.D != 10) $stop; + if (HIJ.BC1.DE1.E != 10) $stop; + if (HIJ.BC1.DE1.FG1.F != 10) $stop; + if (HIJ.BC1.DE1.FG1.G != 5) $stop; + + HIJ = '{shortint:10, default: 5}; + if (HIJ.A != 5) $stop; + if (HIJ.BC1.B != 5) $stop; + if (HIJ.BC1.C != 5) $stop; + if (HIJ.BC1.DE1.D != 5) $stop; + if (HIJ.BC1.DE1.E != 5) $stop; + if (HIJ.BC1.DE1.FG1.F != 5) $stop; + if (HIJ.BC1.DE1.FG1.G != 10) $stop; + $write("*-* All Finished *-*\n"); $finish; end diff --git a/test_regress/t/t_sv_bus_mux_demux.pl b/test_regress/t/t_sv_bus_mux_demux.pl index c31dd4c0c..b46d46042 100755 --- a/test_regress/t/t_sv_bus_mux_demux.pl +++ b/test_regress/t/t_sv_bus_mux_demux.pl @@ -9,7 +9,6 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di # SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 scenarios(simulator => 1); -$Self->{vlt_all} and unsupported("Verilator unsupported, bug181"); compile( ); diff --git a/test_regress/t/t_tri_cond_eqcase_with_1.pl b/test_regress/t/t_tri_cond_eqcase_with_1.pl new file mode 100755 index 000000000..f5e338520 --- /dev/null +++ b/test_regress/t/t_tri_cond_eqcase_with_1.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 2022 by Antmicro Ltd. 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_tri_cond_eqcase_with_1.v b/test_regress/t/t_tri_cond_eqcase_with_1.v new file mode 100644 index 000000000..552280b98 --- /dev/null +++ b/test_regress/t/t_tri_cond_eqcase_with_1.v @@ -0,0 +1,21 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2022 by Antmicro Ltd. +// SPDX-License-Identifier: CC0-1.0 + +module t (/*AUTOARG*/ + // Inputs + clk + ); + input clk; + wire a; + assign a = 1 === (clk ? 1 : 1'bz); + + always begin + if (!a) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end +endmodule diff --git a/test_regress/t/t_tri_eqcase_input.pl b/test_regress/t/t_tri_eqcase_input.pl new file mode 100755 index 000000000..f5e338520 --- /dev/null +++ b/test_regress/t/t_tri_eqcase_input.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 2022 by Antmicro Ltd. 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_tri_eqcase_input.v b/test_regress/t/t_tri_eqcase_input.v new file mode 100644 index 000000000..518ae40e1 --- /dev/null +++ b/test_regress/t/t_tri_eqcase_input.v @@ -0,0 +1,20 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2022 by Antmicro Ltd. +// SPDX-License-Identifier: CC0-1.0 + +module t (/*AUTOARG*/ + // Inputs + clk + ); + input clk; + wire a = 1'bz === clk; + + always begin + if (a) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end +endmodule diff --git a/test_regress/t/t_tri_inout.cpp b/test_regress/t/t_tri_inout.cpp index b3bd8aa46..874c5f15b 100644 --- a/test_regress/t/t_tri_inout.cpp +++ b/test_regress/t/t_tri_inout.cpp @@ -47,6 +47,16 @@ int main() { } } } + tb->SEL = tb->A = tb->B = 0; + + for (int i = 0; i < 256; ++i) { + tb->clk = 0; + tb->eval(); + tb->clk = 1; + tb->eval(); + if (tb->done) break; + if (i + 1 == 256) pass = false; + } if (pass) { VL_PRINTF("*-* All Finished *-*\n"); diff --git a/test_regress/t/t_tri_inout.v b/test_regress/t/t_tri_inout.v index d6624b04d..d9673beb9 100644 --- a/test_regress/t/t_tri_inout.v +++ b/test_regress/t/t_tri_inout.v @@ -4,10 +4,12 @@ // without warranty, 2008 by Lane Brooks. // SPDX-License-Identifier: CC0-1.0 -module top (input A, input B, input SEL, output Y1, output Y2, output Z); +module top (input A, input B, input SEL, input clk, output Y1, output Y2, output Z, output done); io io1(.A(A), .OE( SEL), .Z(Z), .Y(Y1)); pass io2(.A(B), .OE(!SEL), .Z(Z), .Y(Y2)); assign Z = 1'bz; + + pad_checker u_pad_checker(.clk(clk), .done(done)); endmodule module pass (input A, input OE, inout Z, output Y); @@ -27,3 +29,84 @@ module io_noinline (input A, input OE, inout Z, output Y); assign Y = Z; assign Z = 1'bz; endmodule + + +module pad_checker(input wire clk, output wire done); + wire tri_pad; + reg [1:0] ie = '0; + reg [1:0] oe = '0; + reg [1:0] in = '0; + wire out_0, out_1; + + pad u_pad0(.pad(tri_pad), .ie(ie[0]), .oe(oe[0]), .to_pad(in[0]), .from_pad(out_0)); + pad u_pad1(.pad(tri_pad), .ie(ie[1]), .oe(oe[1]), .to_pad(in[1]), .from_pad(out_1)); + + wire bin_pad_in_0, bin_pad_in_1; + wire bin_pad_01, bin_pad_10; + wire bin_pad_en_01, bin_pad_en_10; + wire bin_from_pad_out_0, bin_from_pad_out_1; + wire bin_from_pad_en_0, bin_from_pad_en_1; + + // Expectation model that simulates how Verilator solves tri-state + pad_binary u_pad_bin_0(.pad_in(bin_pad_in_0), + .pad_out(bin_pad_01), + .pad_en(bin_pad_en_01), + .ie(ie[0]), .oe(oe[0]), + .to_pad(in[0]), + .from_pad_out(bin_from_pad_out_0), + .from_pad_en(bin_from_pad_en_0)); + + pad_binary u_pad_bin_1(.pad_in(bin_pad_in_1), + .pad_out(bin_pad_10), + .pad_en(bin_pad_en_10), + .ie(ie[1]), + .oe(oe[1]), + .to_pad(in[1]), + .from_pad_out(bin_from_pad_out_1), + .from_pad_en(bin_from_pad_en_1)); + + assign bin_pad_in_0 = (bin_pad_en_10 & bin_pad_10) | (bin_pad_en_01 & bin_pad_01); + assign bin_pad_in_1 = (bin_pad_en_01 & bin_pad_01) | (bin_pad_en_10 & bin_pad_10); + + + logic done_reg = 0; + assign done = done_reg; + always @(posedge clk) begin + if ({ie, oe, in} == 6'b111111) begin + done_reg <= 1'b1; + end else begin + if (out_0 != bin_from_pad_out_0) begin + $display("ie:%b oe:%b in:%b out0 act:%b exp:%b", ie[0], oe[0], in[0], out_0, bin_from_pad_out_0); + $stop; + end + if (out_1 != bin_from_pad_out_1) begin + $display("ie:%b oe:%b in:%b out1 act:%b exp:%b", ie[1], oe[1], in[1], out_1, bin_from_pad_out_1); + $stop; + end + // Let's try all combination + {ie, oe, in} <= {ie, oe, in} + 1; + end + end + +endmodule + +module pad(inout wire pad, input wire ie, input wire oe, input wire to_pad, output wire from_pad); + + assign pad = oe ? to_pad : 1'bz; + assign from_pad = ie ? pad : 1'bz; +endmodule + +module pad_binary(input wire pad_in, + output wire pad_out, + output wire pad_en, + input wire ie, + input wire oe, + input wire to_pad, + output from_pad_out, + output wire from_pad_en); + + assign pad_out = oe & to_pad; + assign pad_en = oe; + assign from_pad_out = ie & ((oe & to_pad) | pad_in); + assign from_pad_en = ie; +endmodule diff --git a/test_regress/t/t_void_queue_ops.pl b/test_regress/t/t_void_queue_ops.pl new file mode 100755 index 000000000..b46d46042 --- /dev/null +++ b/test_regress/t/t_void_queue_ops.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_void_queue_ops.v b/test_regress/t/t_void_queue_ops.v new file mode 100644 index 000000000..f618cb876 --- /dev/null +++ b/test_regress/t/t_void_queue_ops.v @@ -0,0 +1,190 @@ +module t + (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + + int cyc = 0; + + + // Test for https://github.com/verilator/verilator/issues/3364 + // Make sure all SV queue API is supported and verilator can generate + // compile-able C++ models for it. + + // simple queue + logic [31:0] my_int_queue [$]; + + // On the functions and tasks, the my_int_queue.pop_[front|back]() call will + // have nodep->firstAbovep() != nullptr. Because the pop_front or pop_back is + // the first node on the "list". + // To fix this, V3Width.cpp will not use firstAbovep(), and instead us + // isStandalongStmt() -- which checks if the pop_front or pop_back is + // 2nd or later, or if it's first in the list that it's in a "block" of code. + // For functions/tasks, that is checked with: + // VN_IS(backp(), NodeFTask)=True, so even though + function automatic void f_pop_back__my_int_queue(); + void'(my_int_queue.pop_back()); + endfunction : f_pop_back__my_int_queue + + function automatic void f_pop_front__my_int_queue(); + void'(my_int_queue.pop_front()); + endfunction : f_pop_front__my_int_queue + + task automatic t_pop_back__my_int_queue(); + void'(my_int_queue.pop_back()); + endtask : t_pop_back__my_int_queue + + task automatic t_pop_front__my_int_queue(); + void'(my_int_queue.pop_front()); + endtask : t_pop_front__my_int_queue + + + task automatic do_random_queue_operation(); + bit [7:0] rand_op; + int rand_index; + logic [31:0] item; + + + rand_op = 8'($urandom_range(32, 0)); + case(rand_op) + 8'd0: ; // nop + + // pushes (2x of these) + 8'd1, 8'd2: my_int_queue.push_back($urandom); + 8'd3, 8'd4: my_int_queue.push_front($urandom); + + // delete: + 8'd5: my_int_queue.delete(); + + // insert(index, item): + 8'd6: begin + rand_index = $urandom_range(my_int_queue.size()); + my_int_queue.insert(rand_index, item); + end + + // shuffle + 8'd7: my_int_queue.shuffle(); + + // Various pops for rand_op >= 8: + // pops to var + // V3Width debug -- firstAbovep()=ASSIGN (which I guess does the ; for us + // so we don't need the queue op to + // do it.) + // isStandalongStmt() will ignore ASSIGN, return false (NodeAssign is + // child of AstNodeStmt) + 8'd8: if (my_int_queue.size() > 0) item = my_int_queue.pop_front(); + 8'd9: if (my_int_queue.size() > 0) item = my_int_queue.pop_back(); + + // pops to the void + // V3Width debug -- firstAbovep()=IF + // This is fixed with isStandalongStmt() -- VN_IS(backp(), NodeIf)=True + 8'd10: if (my_int_queue.size() > 0) void'(my_int_queue.pop_front()); + 8'd11: if (my_int_queue.size() > 0) void'(my_int_queue.pop_back()); + + // pop result to the lhs of a condition, and do something with it. + 8'd12: + if (my_int_queue.size() > 0) + // V3Width debug -- firstAbovep()=LTE (good we don't want a ; here) + if (my_int_queue.pop_front() <= 2022) + my_int_queue.push_front(3022); // living in the year 3022. + + // pop result to the rhs of a condition, and do something with it. + 8'd13: + if (my_int_queue.size() > 0) + // V3Width debug -- firstAbovep()=GT (good we don't want a ; here) + if (4022 > my_int_queue.pop_front()) + my_int_queue.push_front(3023); // living in the year 3023. + + // pops to the void after yet another case: + // V3Width debug -- firstAbovep()=CASEITEM (not a nullptr) + // This is fixed with isStandalongStmt() -- VN_IS(backp(), CaseItem)=True + 8'd14: + case (my_int_queue.size() > 0) + 0: ; + 1: void'(my_int_queue.pop_front()); + default: ; + endcase // case (my_int_queue.size() > 0) + + // V3Width debug -- firstAbovep()=CASEITEM (not a nullptr) + // backp()->nextp()=CASEITEM (different one) + // This is fixed with isStandalongStmt() -- VN_IS(backp(), CaseItem)=True + 8'd15: + case (my_int_queue.size() > 0) + 0: ; + 1: void'(my_int_queue.pop_back()); + default; + endcase // case (my_int_queue.size() > 0) + + // pops in a function or task + 8'd16: if (my_int_queue.size() > 0) f_pop_back__my_int_queue(); + 8'd17: if (my_int_queue.size() > 0) f_pop_front__my_int_queue(); + 8'd18: if (my_int_queue.size() > 0) t_pop_back__my_int_queue(); + 8'd19: if (my_int_queue.size() > 0) t_pop_front__my_int_queue(); + + // But what if we put some dummy code before the pop_back() or pop_front(): + 8'd20: begin + if (my_int_queue.size() > 0) begin + ; // dummy line + // V3Width debug -- firstAbovep()=BEGIN (is not nullptr). + // This is fixed with isStandalongStmt() -- VN_IS(backp(), NodeIf)=True + void'(my_int_queue.pop_back()); + end + end + 8'd21: begin + automatic int temp_int = 0; + if (my_int_queue.size() > 0) begin + temp_int = 5; // dummy line + // V3Width debug -- firstAbovep()=nullptr (good) + void'(my_int_queue.pop_back()); + end + end + 8'd22: begin + if (my_int_queue.size() > 0) begin + automatic int some_temp_dummy_int; + some_temp_dummy_int = 42; + // V3Width debug -- firstAbovep()=nullptr (good) + void'(my_int_queue.pop_back()); + end + end + 8'd23: begin + if (my_int_queue.size() > 0) begin + // no dummy here, just a 'begin' helper before it. + // V3Width debug -- firstAbovep()=BEGIN (is not nullptr). + // This is fixed with isStandalongStmt() -- VN_IS(backp(), NodeIf)=True + void'(my_int_queue.pop_back()); + end + end + + // What about an if of something else, followed by a pop_front? + 8'd24: begin + automatic int temp_int = 0; + if (my_int_queue.size() == 0) begin // dummy + temp_int = 1000; + end + void'(my_int_queue.pop_front()); // firstAbovep() should be nullptr here. + end + + + default: ; // nop + endcase // case (rand_op) + + endtask : do_random_queue_operation + + + + always @ (posedge clk) begin : main + cyc <= cyc + 1; + + do_random_queue_operation(); + + if (cyc > 100) begin + $write("*-* All Finished *-*\n"); + $finish(); + end + end + + + +endmodule : t