diff --git a/Changes b/Changes index 8ab0c2ac7..083a2c4bc 100644 --- a/Changes +++ b/Changes @@ -20,6 +20,7 @@ Verilator 5.019 devel **Minor:** * Support ccache when compiling Verilator with CMake (#4678). [Anthony Donlon] +* Support passing constraints to --xml-only output (still otherwise unsupported) (#4683). [Shahid Ikram] * Remove deprecated options (#4663). [Geza Lore] * Optimize timing-delayed queue (#4584). [qrqiuren] * Fix VPI TOP level variable iteration (#3919) (#4618). [Marlon James] diff --git a/src/V3Ast.cpp b/src/V3Ast.cpp index 995fd1b5c..139aeb0de 100644 --- a/src/V3Ast.cpp +++ b/src/V3Ast.cpp @@ -1447,18 +1447,21 @@ AstNodeDType* AstNode::findBitRangeDType(const VNumRange& range, int widthMin, AstBasicDType* AstNode::findInsertSameDType(AstBasicDType* nodep) { return v3Global.rootp()->typeTablep()->findInsertSameDType(nodep); } +AstNodeDType* AstNode::findConstraintRefDType() const { + return v3Global.rootp()->typeTablep()->findConstraintRefDType(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()); -} AstNodeDType* AstNode::findStreamDType() const { return v3Global.rootp()->typeTablep()->findStreamDType(fileline()); } +AstNodeDType* AstNode::findVoidDType() const { + return v3Global.rootp()->typeTablep()->findVoidDType(fileline()); +} static const AstNodeDType* computeCastableBase(const AstNodeDType* nodep) { while (true) { diff --git a/src/V3Ast.h b/src/V3Ast.h index 937f98cd7..5e6c8b483 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -2121,8 +2121,8 @@ public: void dtypeSetUInt32() { dtypep(findUInt32DType()); } // Twostate void dtypeSetUInt64() { dtypep(findUInt64DType()); } // Twostate void dtypeSetEmptyQueue() { dtypep(findEmptyQueueDType()); } - void dtypeSetVoid() { dtypep(findVoidDType()); } void dtypeSetStream() { dtypep(findStreamDType()); } + void dtypeSetVoid() { dtypep(findVoidDType()); } // Data type locators AstNodeDType* findBitDType() const { return findBasicDType(VBasicDTypeKwd::LOGIC); } @@ -2132,10 +2132,11 @@ public: AstNodeDType* findUInt32DType() const { return findBasicDType(VBasicDTypeKwd::UINT32); } AstNodeDType* findUInt64DType() const { return findBasicDType(VBasicDTypeKwd::UINT64); } AstNodeDType* findCHandleDType() const { return findBasicDType(VBasicDTypeKwd::CHANDLE); } + AstNodeDType* findConstraintRefDType() const; AstNodeDType* findEmptyQueueDType() const; - AstNodeDType* findVoidDType() const; - AstNodeDType* findStreamDType() const; AstNodeDType* findQueueIndexDType() const; + AstNodeDType* findStreamDType() const; + AstNodeDType* findVoidDType() const; AstNodeDType* findBitDType(int width, int widthMin, VSigning numeric) const; AstNodeDType* findLogicDType(int width, int widthMin, VSigning numeric) const; AstNodeDType* findLogicRangeDType(const VNumRange& range, int widthMin, diff --git a/src/V3AstNodeDType.h b/src/V3AstNodeDType.h index c4da4ad26..ce0b1a5c1 100644 --- a/src/V3AstNodeDType.h +++ b/src/V3AstNodeDType.h @@ -622,6 +622,32 @@ public: return false; } }; +class AstConstraintRefDType final : public AstNodeDType { + // For e.g. a reference to constraint for constraint_mode +public: + explicit AstConstraintRefDType(FileLine* fl) + : ASTGEN_SUPER_ConstraintRefDType(fl) { + dtypep(this); + } + ASTGEN_MEMBERS_AstConstraintRefDType; + bool hasDType() const override { return true; } + bool maybePointedTo() const override { return true; } + bool undead() const override { return true; } + AstNodeDType* subDTypep() const override VL_MT_SAFE { return nullptr; } + AstNodeDType* virtRefDTypep() const override { return nullptr; } + void virtRefDTypep(AstNodeDType* nodep) override {} + bool similarDType(const AstNodeDType* samep) const override { return this == samep; } + AstBasicDType* basicp() const override VL_MT_STABLE { return nullptr; } + // cppcheck-suppress csyleCast + AstNodeDType* skipRefp() const override VL_MT_STABLE { return (AstNodeDType*)this; } + // cppcheck-suppress csyleCast + AstNodeDType* skipRefToConstp() const override { return (AstNodeDType*)this; } + // cppcheck-suppress csyleCast + AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; } + int widthAlignBytes() const override { return 1; } + int widthTotalBytes() const override { return 1; } + bool isCompound() const override { return false; } +}; class AstDefImplicitDType final : public AstNodeDType { // For parsing enum/struct/unions that are declared with a variable rather than typedef // This allows "var enum {...} a,b" to share the enum definition for both variables diff --git a/src/V3AstNodeExpr.h b/src/V3AstNodeExpr.h index d026e5fcc..3e42f73e1 100644 --- a/src/V3AstNodeExpr.h +++ b/src/V3AstNodeExpr.h @@ -1062,6 +1062,29 @@ public: // May return nullptr on parse failure. static AstConst* parseParamLiteral(FileLine* fl, const string& literal); }; +class AstConstraintRef final : public AstNodeExpr { + // A reference to a constraint identifier + // Not saving pointer to constraint yet, as constraint_mode is an unsupported construct + // @astgen op4 := scopeNamep : Optional[AstScopeName] + AstNodeModule* m_classOrPackagep = nullptr; // Class/package of the constraint + string m_name; // Name of constraint + +public: + AstConstraintRef(FileLine* fl, const string& name) + : ASTGEN_SUPER_ConstraintRef(fl) + , m_name{name} { + dtypep(findConstraintRefDType()); + } + ASTGEN_MEMBERS_AstConstraintRef; + string name() const override VL_MT_STABLE { return m_name; } // * = Var name + void name(const string& name) override { m_name = name; } + AstNodeModule* classOrPackagep() const { return m_classOrPackagep; } + void classOrPackagep(AstNodeModule* nodep) { m_classOrPackagep = nodep; } + + string emitVerilog() final override { V3ERROR_NA_RETURN(""); } + string emitC() final override { V3ERROR_NA_RETURN(""); } + bool cleanOut() const final override { V3ERROR_NA_RETURN(true); } +}; class AstCvtDynArrayToPacked final : public AstNodeExpr { // Cast from dynamic queue data type to packed array // @astgen op1 := fromp : AstNodeExpr @@ -1905,6 +1928,7 @@ public: string emitVerilog() override { V3ERROR_NA_RETURN(""); } string emitC() override { V3ERROR_NA_RETURN(""); } bool cleanOut() const override { V3ERROR_NA_RETURN(true); } + bool hasDType() const override { return false; } }; class AstSetAssoc final : public AstNodeExpr { // Set an assoc array element and return object, '{} diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index 23aaadb2a..02a471af4 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -434,6 +434,21 @@ public: int instrCount() const override { return INSTR_COUNT_BRANCH; } bool same(const AstNode* /*samep*/) const override { return true; } }; +class AstNodeForeach VL_NOT_FINAL : public AstNodeStmt { + // @astgen op1 := arrayp : AstNode + // @astgen op2 := stmtsp : List[AstNode] +public: + AstNodeForeach(VNType t, FileLine* fl, AstNode* arrayp, AstNode* stmtsp) + : AstNodeStmt(t, fl) { + this->arrayp(arrayp); + this->addStmtsp(stmtsp); + } + ASTGEN_MEMBERS_AstNodeForeach; + bool isGateOptimizable() const override { return false; } + int instrCount() const override { return INSTR_COUNT_BRANCH; } + bool same(const AstNode* /*samep*/) const override { return true; } + bool isFirstInMyListOfStatements(AstNode* n) const override { return n == stmtsp(); } +}; class AstNodeIf VL_NOT_FINAL : public AstNodeStmt { // @astgen op1 := condp : AstNodeExpr // @astgen op2 := thensp : List[AstNode] @@ -965,6 +980,40 @@ public: // false, the returned VarScope will have _->dtypep()->sameTree(initp->dtypep()) return true. AstVarScope* findConst(AstConst* initp, bool mergeDType); }; +class AstConstraint final : public AstNode { + // Constraint + // @astgen op1 := itemsp : List[AstNode] + string m_name; // Name of constraint + bool m_isStatic = false; // static constraint +public: + AstConstraint(FileLine* fl, const string& name, AstNode* itemsp) + : ASTGEN_SUPER_Constraint(fl) + , m_name(name) { + this->addItemsp(itemsp); + } + ASTGEN_MEMBERS_AstConstraint; + string name() const override VL_MT_STABLE { return m_name; } // * = Scope name + bool isGateOptimizable() const override { return false; } + bool isPredictOptimizable() const override { return false; } + bool same(const AstNode* /*samep*/) const override { return true; } + void isStatic(bool flag) { m_isStatic = flag; } + bool isStatic() const { return m_isStatic; } +}; +class AstConstraintBefore final : public AstNode { + // Constraint solve before item + // @astgen op1 := lhssp : List[AstNodeExpr] + // @astgen op2 := rhssp : List[AstNodeExpr] +public: + AstConstraintBefore(FileLine* fl, AstNodeExpr* lhssp, AstNodeExpr* rhssp) + : ASTGEN_SUPER_ConstraintBefore(fl) { + this->addLhssp(lhssp); + this->addRhssp(rhssp); + } + ASTGEN_MEMBERS_AstConstraintBefore; + bool isGateOptimizable() const override { return false; } + bool isPredictOptimizable() const override { return false; } + bool same(const AstNode* /*samep*/) const override { return true; } +}; class AstDefParam final : public AstNode { // A defparam assignment // Parents: MODULE @@ -983,6 +1032,24 @@ public: bool same(const AstNode*) const override { return true; } string path() const { return m_path; } }; +class AstDistItem final : public AstNode { + // Constraint distribution item + // @astgen op1 := rangep : AstNodeExpr + // @astgen op2 := weightp : AstNodeExpr + bool m_isWhole = false; // True for weight ':/', false for ':=' +public: + AstDistItem(FileLine* fl, AstNodeExpr* rangep, AstNodeExpr* weightp) + : ASTGEN_SUPER_DistItem(fl) { + this->rangep(rangep); + this->weightp(weightp); + } + ASTGEN_MEMBERS_AstDistItem; + bool isGateOptimizable() const override { return false; } + bool isPredictOptimizable() const override { return false; } + bool same(const AstNode* /*samep*/) const override { return true; } + void isWhole(bool flag) { m_isWhole = flag; } + bool isWhole() const { return m_isWhole; } +}; class AstDpiExport final : public AstNode { // We could put an AstNodeFTaskRef instead of the verilog function name, // however we're not *calling* it, so that seems somehow wrong. @@ -1560,10 +1627,11 @@ public: class AstTypeTable final : public AstNode { // Container for hash of standard data types // @astgen op1 := typesp : List[AstNodeDType] + AstConstraintRefDType* m_constraintRefp = nullptr; AstEmptyQueueDType* m_emptyQueuep = nullptr; AstQueueDType* m_queueIndexp = nullptr; - AstVoidDType* m_voidp = nullptr; AstStreamDType* m_streamp = nullptr; + AstVoidDType* m_voidp = nullptr; AstBasicDType* m_basicps[VBasicDTypeKwd::_ENUM_MAX]{}; // using DetailedMap = std::map; @@ -1586,10 +1654,11 @@ public: AstBasicDType* findLogicBitDType(FileLine* fl, VBasicDTypeKwd kwd, const VNumRange& range, int widthMin, VSigning numeric); AstBasicDType* findInsertSameDType(AstBasicDType* nodep); + AstConstraintRefDType* findConstraintRefDType(FileLine* fl); AstEmptyQueueDType* findEmptyQueueDType(FileLine* fl); AstQueueDType* findQueueIndexDType(FileLine* fl); - AstVoidDType* findVoidDType(FileLine* fl); AstStreamDType* findStreamDType(FileLine* fl); + AstVoidDType* findVoidDType(FileLine* fl); void clearCache(); void repairCache(); void dump(std::ostream& str = std::cout) const override; @@ -2562,6 +2631,38 @@ public: bool same(const AstNode* samep) const override { return true; } // Ignore name in comments virtual bool showAt() const { return m_showAt; } }; +class AstConstraintExpr final : public AstNodeStmt { + // Constraint expression + // @astgen op1 := exprp : AstNodeExpr + bool m_isSoft = false; // Soft constraint expression + bool m_isDisableSoft = false; // Disable soft constraint expression +public: + AstConstraintExpr(FileLine* fl, AstNodeExpr* exprp) + : ASTGEN_SUPER_ConstraintExpr(fl) { + this->exprp(exprp); + } + ASTGEN_MEMBERS_AstConstraintExpr; + bool isGateOptimizable() const override { return false; } + bool isPredictOptimizable() const override { return false; } + bool same(const AstNode* /*samep*/) const override { return true; } + void isDisableSoft(bool flag) { m_isDisableSoft = flag; } + bool isDisableSoft() const { return m_isDisableSoft; } + void isSoft(bool flag) { m_isSoft = flag; } + bool isSoft() const { return m_isSoft; } +}; +class AstConstraintUnique final : public AstNodeStmt { + // Constraint unique statement + // @astgen op1 := rangesp : List[AstNode] +public: + AstConstraintUnique(FileLine* fl, AstNode* rangesp) + : ASTGEN_SUPER_ConstraintUnique(fl) { + this->addRangesp(rangesp); + } + ASTGEN_MEMBERS_AstConstraintUnique; + bool isGateOptimizable() const override { return false; } + bool isPredictOptimizable() const override { return false; } + bool same(const AstNode* /*samep*/) const override { return true; } +}; class AstContinue final : public AstNodeStmt { public: explicit AstContinue(FileLine* fl) @@ -2869,21 +2970,6 @@ public: ASTGEN_MEMBERS_AstFireEvent; bool isDelayed() const { return m_delayed; } }; -class AstForeach final : public AstNodeStmt { - // @astgen op1 := arrayp : AstNode - // @astgen op2 := stmtsp : List[AstNode] -public: - AstForeach(FileLine* fl, AstNode* arrayp, AstNode* stmtsp) - : ASTGEN_SUPER_Foreach(fl) { - this->arrayp(arrayp); - this->addStmtsp(stmtsp); - } - ASTGEN_MEMBERS_AstForeach; - bool isGateOptimizable() const override { return false; } - int instrCount() const override { return INSTR_COUNT_BRANCH; } - bool same(const AstNode* /*samep*/) const override { return true; } - bool isFirstInMyListOfStatements(AstNode* n) const override { return n == stmtsp(); } -}; class AstJumpBlock final : public AstNodeStmt { // Block of code including a single JumpLabel, and 0+ JumpGo's to that label // Parents: {statement list} @@ -3539,7 +3625,28 @@ public: ASTGEN_MEMBERS_AstGenFor; }; +// === AstNodeForeach === +class AstConstraintForeach final : public AstNodeForeach { + // Constraint foreach statement +public: + AstConstraintForeach(FileLine* fl, AstNodeExpr* exprp, AstNode* bodysp) + : ASTGEN_SUPER_ConstraintForeach(fl, exprp, bodysp) {} + ASTGEN_MEMBERS_AstConstraintForeach; +}; +class AstForeach final : public AstNodeForeach { +public: + AstForeach(FileLine* fl, AstNode* arrayp, AstNode* stmtsp) + : ASTGEN_SUPER_Foreach(fl, arrayp, stmtsp) {} + ASTGEN_MEMBERS_AstForeach; +}; + // === AstNodeIf === +class AstConstraintIf final : public AstNodeIf { +public: + AstConstraintIf(FileLine* fl, AstNodeExpr* condp, AstNode* thensp, AstNode* elsesp) + : ASTGEN_SUPER_ConstraintIf(fl, condp, thensp, elsesp) {} + ASTGEN_MEMBERS_AstConstraintIf; +}; class AstGenIf final : public AstNodeIf { public: AstGenIf(FileLine* fl, AstNodeExpr* condp, AstNode* thensp, AstNode* elsesp) diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index 76ea7d7f4..e4de5f908 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -1096,6 +1096,14 @@ void AstTypeTable::repairCache() { } } +AstConstraintRefDType* AstTypeTable::findConstraintRefDType(FileLine* fl) { + if (VL_UNLIKELY(!m_constraintRefp)) { + AstConstraintRefDType* const newp = new AstConstraintRefDType{fl}; + addTypesp(newp); + m_constraintRefp = newp; + } + return m_constraintRefp; +} AstEmptyQueueDType* AstTypeTable::findEmptyQueueDType(FileLine* fl) { if (VL_UNLIKELY(!m_emptyQueuep)) { AstEmptyQueueDType* const newp = new AstEmptyQueueDType{fl}; @@ -1104,16 +1112,6 @@ AstEmptyQueueDType* AstTypeTable::findEmptyQueueDType(FileLine* fl) { } return m_emptyQueuep; } - -AstVoidDType* AstTypeTable::findVoidDType(FileLine* fl) { - if (VL_UNLIKELY(!m_voidp)) { - AstVoidDType* const newp = new AstVoidDType{fl}; - addTypesp(newp); - m_voidp = newp; - } - return m_voidp; -} - AstStreamDType* AstTypeTable::findStreamDType(FileLine* fl) { if (VL_UNLIKELY(!m_streamp)) { AstStreamDType* const newp = new AstStreamDType{fl}; @@ -1122,7 +1120,6 @@ AstStreamDType* AstTypeTable::findStreamDType(FileLine* fl) { } return m_streamp; } - AstQueueDType* AstTypeTable::findQueueIndexDType(FileLine* fl) { if (VL_UNLIKELY(!m_queueIndexp)) { AstQueueDType* const newp = new AstQueueDType{fl, AstNode::findUInt32DType(), nullptr}; @@ -1131,6 +1128,14 @@ AstQueueDType* AstTypeTable::findQueueIndexDType(FileLine* fl) { } return m_queueIndexp; } +AstVoidDType* AstTypeTable::findVoidDType(FileLine* fl) { + if (VL_UNLIKELY(!m_voidp)) { + AstVoidDType* const newp = new AstVoidDType{fl}; + addTypesp(newp); + m_voidp = newp; + } + return m_voidp; +} AstBasicDType* AstTypeTable::findBasicDType(FileLine* fl, VBasicDTypeKwd kwd) { if (m_basicps[kwd]) return m_basicps[kwd]; @@ -1467,7 +1472,7 @@ void AstClassPackage::cloneRelink() { if (m_classp && m_classp->clonep()) m_classp = m_classp->clonep(); } bool AstClass::isCacheableChild(const AstNode* nodep) { - return (VN_IS(nodep, Var) || VN_IS(nodep, EnumItemRef) + return (VN_IS(nodep, Var) || VN_IS(nodep, Constraint) || VN_IS(nodep, EnumItemRef) || (VN_IS(nodep, NodeFTask) && !VN_AS(nodep, NodeFTask)->isExternProto()) || VN_IS(nodep, CFunc)); } diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index 8ffb6a018..446209314 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -235,6 +235,8 @@ public: } } else if (VN_IS(nodep, Cell)) { return "instance"; + } else if (VN_IS(nodep, Constraint)) { + return "constraint"; } else if (VN_IS(nodep, Task)) { return "task"; } else if (VN_IS(nodep, Func)) { @@ -1188,6 +1190,11 @@ class LinkDotFindVisitor final : public VNVisitor { nodep->varp(newvarp); iterate(nodep->exprp()); } + void visit(AstConstraint* nodep) override { + VL_RESTORER(m_curSymp); + m_curSymp = m_statep->insertBlock(m_curSymp, nodep->name(), nodep, m_classOrPackagep); + iterateChildren(nodep); + } void visit(AstVar* nodep) override { // Var: Remember its name for later resolution UASSERT_OBJ(m_curSymp && m_modSymp, nodep, "Var not under module?"); @@ -1429,7 +1436,7 @@ class LinkDotFindVisitor final : public VNVisitor { // No longer needed, but can't delete until any multi-instantiated modules are expanded } - void visit(AstForeach* nodep) override { + void visit(AstNodeForeach* nodep) override { // Symbol table needs nodep->name() as the index variable's name VL_RESTORER(m_curSymp); { @@ -1810,7 +1817,7 @@ class LinkDotScopeVisitor final : public VNVisitor { symp->fallbackp(m_modSymp); iterateChildren(nodep); } - void visit(AstForeach* nodep) override { + void visit(AstNodeForeach* nodep) override { VSymEnt* const symp = m_statep->insertBlock(m_modSymp, nodep->name(), nodep, nullptr); symp->fallbackp(m_modSymp); // No recursion, we don't want to pick up variables @@ -2803,6 +2810,13 @@ private: nodep->replaceWith(newp); VL_DO_DANGLING(pushDeletep(nodep), nodep); } + } else if (AstConstraint* const consp = VN_CAST(foundp->nodep(), Constraint)) { + AstNode* const newp = new AstConstraintRef{nodep->fileline(), consp->name()}; + nodep->replaceWith(newp); + VL_DO_DANGLING(pushDeletep(nodep), nodep); + ok = true; + m_ds.m_dotPos = DP_MEMBER; + m_ds.m_dotText = ""; } else if (AstEnumItem* const valuep = VN_CAST(foundp->nodep(), EnumItem)) { if (allowVar) { AstNode* const newp @@ -2936,6 +2950,12 @@ private: << nodep->classOrPackageNodep()->prettyName() << "#()'"); } } + void visit(AstConstraintRef* nodep) override { + if (nodep->user3SetOnce()) return; + UINFO(8, " " << nodep << endl); + UINFO(8, " " << m_ds.ascii() << endl); + // No children defined + } void visit(AstVarRef* nodep) override { // VarRef: Resolve its reference // ParseRefs are used the first pass (forPrimary) so we shouldn't get can't find @@ -3388,7 +3408,7 @@ private: m_ds.m_dotSymp = VL_RESTORER_PREV(m_curSymp); m_ftaskp = nullptr; } - void visit(AstForeach* nodep) override { + void visit(AstNodeForeach* nodep) override { UINFO(5, " " << nodep << endl); checkNoDot(nodep); VL_RESTORER(m_curSymp); diff --git a/src/V3LinkInc.cpp b/src/V3LinkInc.cpp index d4a00861c..dec35bdfb 100644 --- a/src/V3LinkInc.cpp +++ b/src/V3LinkInc.cpp @@ -129,7 +129,7 @@ private: // Done the loop m_insStmtp = nullptr; // Next thing should be new statement } - void visit(AstForeach* nodep) override { + void visit(AstNodeForeach* nodep) override { // Special, as statements need to be put in different places // Body insert just before themselves m_insStmtp = nullptr; // First thing should be new statement diff --git a/src/V3LinkJump.cpp b/src/V3LinkJump.cpp index e777ac164..3fb29d306 100644 --- a/src/V3LinkJump.cpp +++ b/src/V3LinkJump.cpp @@ -245,7 +245,7 @@ private: whilep->addHereThisAsNext(copiedBodyp); } } - void visit(AstForeach* nodep) override { + void visit(AstNodeForeach* nodep) override { VL_RESTORER(m_loopp); { m_loopp = nodep; diff --git a/src/V3LinkParse.cpp b/src/V3LinkParse.cpp index baf66d3f7..5e25a0dcc 100644 --- a/src/V3LinkParse.cpp +++ b/src/V3LinkParse.cpp @@ -242,6 +242,10 @@ private: } } void visit(AstNodeDType* nodep) override { visitIterateNodeDType(nodep); } + void visit(AstConstraint* nodep) override { + v3Global.useRandomizeMethods(true); + iterateChildren(nodep); + } void visit(AstEnumDType* nodep) override { if (nodep->name() == "") { nodep->name(nameFromTypedef(nodep)); // Might still remain "" @@ -533,7 +537,7 @@ private: VL_DO_DANGLING(nodep->deleteTree(), nodep); } - void visit(AstForeach* nodep) override { + void visit(AstNodeForeach* nodep) override { // FOREACH(array, loopvars, body) UINFO(9, "FOREACH " << nodep << endl); cleanFileline(nodep); diff --git a/src/V3Randomize.cpp b/src/V3Randomize.cpp index 5e8f20d10..aa202fd48 100644 --- a/src/V3Randomize.cpp +++ b/src/V3Randomize.cpp @@ -358,6 +358,10 @@ private: addPrePostCall(nodep, funcp, "post_randomize"); nodep->user1(false); } + void visit(AstConstraint* nodep) override { + nodep->v3warn(CONSTRAINTIGN, "Constraint ignored (unsupported)"); + if (!v3Global.opt.xmlOnly()) VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep); + } void visit(AstRandCase* nodep) override { // RANDCASE // CASEITEM expr1 : stmt1 diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 4c5d0c0ee..6c41170d9 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -1693,6 +1693,10 @@ private: // visit VL_DO_DANGLING(userIterate(newp, nullptr), newp); } + void visit(AstConstraintRefDType* nodep) override { + if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed + nodep->dtypep(nodep); + } void visit(AstDynArrayDType* nodep) override { if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed // Iterate into subDTypep() to resolve that type and update pointer. @@ -2090,6 +2094,20 @@ private: VL_DANGLING(underp); } } + void visit(AstConstraintBefore* nodep) override { + userIterateAndNext(nodep->lhssp(), WidthVP{SELF, BOTH}.p()); + userIterateAndNext(nodep->rhssp(), WidthVP{SELF, BOTH}.p()); + } + void visit(AstConstraintExpr* nodep) override { + userIterateAndNext(nodep->exprp(), WidthVP{SELF, BOTH}.p()); + } + void visit(AstConstraintUnique* nodep) override { + userIterateAndNext(nodep->rangesp(), WidthVP{SELF, BOTH}.p()); + } + void visit(AstDistItem* nodep) override { + userIterate(nodep->rangep(), WidthVP{SELF, BOTH}.p()); + userIterate(nodep->weightp(), WidthVP{SELF, BOTH}.p()); + } void visit(AstVar* nodep) override { // if (debug()) nodep->dumpTree("- InitPre: "); // Must have deterministic constant width @@ -2791,6 +2809,13 @@ private: VL_DO_DANGLING(pushDeletep(nodep), nodep); return true; } + if (VN_IS(foundp, Constraint)) { + // We don't support constraints yet, so just keep as unlinked for now + // Presumably we'll next see a constraint_mode AstMethodCall + nodep->dtypep(nodep->findConstraintRefDType()); + UINFO(9, "Unsupported constraint select " << nodep << endl); + return true; + } UINFO(1, "found object " << foundp << endl); nodep->v3fatalSrc("MemberSel of non-variable\n" << nodep->warnContextPrimary() << '\n' @@ -2803,7 +2828,7 @@ private: VSpellCheck speller; for (AstClass* classp = first_classp; classp;) { for (AstNode* itemp = classp->membersp(); itemp; itemp = itemp->nextp()) { - if (VN_IS(itemp, Var) || VN_IS(itemp, EnumItemRef)) { + if (VN_IS(itemp, Constraint) || VN_IS(itemp, EnumItemRef) || VN_IS(itemp, Var)) { speller.pushCandidate(itemp->prettyName()); } } @@ -2915,8 +2940,8 @@ private: methodCallClass(nodep, adtypep); } else if (AstUnpackArrayDType* const adtypep = VN_CAST(fromDtp, UnpackArrayDType)) { methodCallUnpack(nodep, adtypep); - } else if (basicp && nodep->name() == "constraint_mode") { - methodCallConstraint(nodep, basicp); + } else if (AstConstraintRefDType* const adtypep = VN_CAST(fromDtp, ConstraintRefDType)) { + methodCallConstraint(nodep, adtypep); } else if (basicp && basicp->isEvent()) { methodCallEvent(nodep, basicp); } else if (basicp && basicp->isString()) { @@ -3557,6 +3582,11 @@ private: nodep->replaceWith(new AstConst{nodep->fileline(), AstConst::BitTrue{}}); VL_DO_DANGLING(pushDeletep(nodep), nodep); return; + } else if (nodep->name() == "constraint_mode") { + nodep->v3warn(CONSTRAINTIGN, "Unsupported: 'constraint_mode' called on object"); + nodep->replaceWith(new AstConst{nodep->fileline(), AstConst::BitTrue{}}); + VL_DO_DANGLING(pushDeletep(nodep), nodep); + return; } classp = classp->extendsp() ? classp->extendsp()->classp() : nullptr; } @@ -3576,8 +3606,8 @@ private: } nodep->dtypeSetSigned32(); // Guess on error } - void methodCallConstraint(AstMethodCall* nodep, AstBasicDType*) { - // Method call on constraint (currently hacked as just a var) + void methodCallConstraint(AstMethodCall* nodep, AstConstraintRefDType*) { + // Method call on constraint if (nodep->name() == "constraint_mode") { methodOkArguments(nodep, 0, 1); nodep->v3warn(CONSTRAINTIGN, "constraint_mode ignored (unsupported)"); @@ -3585,7 +3615,9 @@ private: nodep->replaceWith(new AstConst{nodep->fileline(), AstConst::BitFalse{}}); VL_DO_DANGLING(pushDeletep(nodep), nodep); } else { - nodep->v3fatalSrc("Unknown built-in constraint method " << nodep->prettyNameQ()); + nodep->v3error("No such constraint method " << nodep->prettyNameQ()); + nodep->replaceWith(new AstConst{nodep->fileline(), AstConst::BitFalse{}}); + VL_DO_DANGLING(pushDeletep(nodep), nodep); } } void methodCallRandMode(AstMethodCall* nodep) { @@ -4549,7 +4581,9 @@ private: userIterateAndNext(nodep->resultp(), m_vup); nodep->dtypeFrom(nodep->resultp()); } - void visit(AstForeach* nodep) override { + void visit(AstNodeForeach* nodep) override { + if (nodep->didWidth()) return; + nodep->didWidth(true); const AstSelLoopVars* const loopsp = VN_CAST(nodep->arrayp(), SelLoopVars); UASSERT_OBJ(loopsp, nodep, "No loop variables under foreach"); // if (debug()) nodep->dumpTree("- foreach-old: "); @@ -4558,20 +4592,16 @@ private: UASSERT_OBJ(fromp->dtypep(), fromp, "Missing data type"); AstNodeDType* fromDtp = fromp->dtypep()->skipRefp(); // Split into for loop - AstNode* bodyp = nodep->stmtsp(); // Might be null - if (bodyp) bodyp->unlinkFrBackWithNext(); // We record where the body needs to eventually go with bodyPointp - // (Can't use bodyp as might be null) AstNode* lastBodyPointp = nullptr; AstNode* newp = nullptr; // Major dimension first - while (AstNode* argsp - = loopsp->elementsp()) { // Loop advances due to below varp->unlinkFrBack() + for (AstNode *argsp = loopsp->elementsp(), *next_argsp; argsp; argsp = next_argsp) { + next_argsp = argsp->nextp(); const bool empty = VN_IS(argsp, Empty); AstVar* const varp = VN_CAST(argsp, Var); UASSERT_OBJ(varp || empty, argsp, "Missing foreach loop variable"); if (varp) varp->usedLoopIdx(true); - argsp->unlinkFrBack(); if (!fromDtp) { argsp->v3error("foreach loop variables exceed number of indices of array"); VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep); @@ -4582,121 +4612,138 @@ private: UINFO(9, "- from on " << fromp << endl); UINFO(9, "- from dtp " << fromDtp << endl); - FileLine* const fl = argsp->fileline(); - AstNode* bodyPointp = new AstBegin{fl, "[EditWrapper]", nullptr}; - AstNode* loopp = nullptr; - if (const AstNodeArrayDType* const adtypep = VN_CAST(fromDtp, NodeArrayDType)) { - if (varp) { - loopp = createForeachLoopRanged(nodep, bodyPointp, varp, adtypep->declRange()); - } + if (VN_IS(nodep, ConstraintForeach)) { + // For now unsupported + userIterate(varp, nullptr); // Prep for next - fromDtp = fromDtp->subDTypep(); - } else if (AstBasicDType* const adtypep = VN_CAST(fromDtp, BasicDType)) { - if (adtypep->isString()) { - if (varp) { - AstConst* const leftp = new AstConst{fl, AstConst::Signed32{}, 0}; - AstLt* const condp - = new AstLt{fl, new AstVarRef{fl, varp, VAccess::READ}, - new AstLenN{fl, fromp->cloneTreePure(false)}}; - AstAdd* const incp - = new AstAdd{fl, new AstConst{fl, AstConst::Signed32{}, 1}, - new AstVarRef{fl, varp, VAccess::READ}}; - loopp = createForeachLoop(nodep, bodyPointp, varp, leftp, condp, incp); - } - } else if (!adtypep->isRanged()) { - argsp->v3error("Illegal to foreach loop on basic '" + fromDtp->prettyTypeName() - + "'"); - VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep); - VL_DO_DANGLING(bodyPointp->deleteTree(), bodyPointp); - return; - } else { + fromDtp = nullptr; + } else { + argsp->unlinkFrBack(); + + FileLine* const fl = argsp->fileline(); + AstNode* bodyPointp = new AstBegin{fl, "[EditWrapper]", nullptr}; + AstNode* loopp = nullptr; + if (const AstNodeArrayDType* const adtypep = VN_CAST(fromDtp, NodeArrayDType)) { if (varp) { loopp = createForeachLoopRanged(nodep, bodyPointp, varp, adtypep->declRange()); } - } - // Prep for next - fromDtp = nullptr; - } else if (VN_IS(fromDtp, DynArrayDType) || VN_IS(fromDtp, QueueDType)) { - if (varp) { - auto* const leftp = new AstConst{fl, AstConst::Signed32{}, 0}; - auto* const sizep - = new AstCMethodHard{fl, fromp->cloneTreePure(false), "size"}; - sizep->dtypeSetSigned32(); - sizep->didWidth(true); - sizep->protect(false); - AstNodeExpr* const condp - = new AstLt{fl, new AstVarRef{fl, varp, VAccess::READ}, sizep}; - AstNodeExpr* const incp - = new AstAdd{fl, new AstConst{fl, AstConst::Signed32{}, 1}, - new AstVarRef{fl, varp, VAccess::READ}}; - loopp = createForeachLoop(nodep, bodyPointp, varp, leftp, condp, incp); - } - // Prep for next - fromDtp = fromDtp->subDTypep(); - } else if (const AstAssocArrayDType* const adtypep - = VN_CAST(fromDtp, AssocArrayDType)) { - // Make this: var KEY_TYPE index; - // bit index__Vfirst; - // index__Vfirst = 0; - // if (0 != array.first(index)) - // do body while (index__Vfirst || 0 != array.next(index)) - varp->dtypeFrom(adtypep->keyDTypep()); - AstVar* const first_varp = new AstVar{ - fl, VVarType::BLOCKTEMP, varp->name() + "__Vfirst", VFlagBitPacked{}, 1}; - first_varp->usedLoopIdx(true); - AstNodeExpr* const firstp = new AstMethodCall{ - fl, fromp->cloneTreePure(false), "first", - new AstArg{fl, "", new AstVarRef{fl, varp, VAccess::READWRITE}}}; - AstNodeExpr* const nextp = new AstMethodCall{ - fl, fromp->cloneTreePure(false), "next", - new AstArg{fl, "", new AstVarRef{fl, varp, VAccess::READWRITE}}}; - AstNode* const first_clearp - = new AstAssign{fl, new AstVarRef{fl, first_varp, VAccess::WRITE}, - new AstConst{fl, AstConst::BitFalse{}}}; - auto* const orp = new AstLogOr{fl, new AstVarRef{fl, first_varp, VAccess::READ}, - new AstNeq{fl, new AstConst{fl, 0}, nextp}}; - AstNode* const whilep = new AstWhile{fl, orp, first_clearp}; - first_clearp->addNext(bodyPointp); - AstNode* const ifbodyp - = new AstAssign{fl, new AstVarRef{fl, first_varp, VAccess::WRITE}, - new AstConst{fl, AstConst::BitTrue{}}}; - ifbodyp->addNext(whilep); - AstNode* const stmtsp = varp; // New statements for under new Begin - stmtsp->addNext(first_varp); - stmtsp->addNext( - new AstIf{fl, new AstNeq{fl, new AstConst{fl, 0}, firstp}, ifbodyp}); - loopp = new AstBegin{nodep->fileline(), "", stmtsp, false, true}; - // Prep for next - fromDtp = fromDtp->subDTypep(); - } else { - argsp->v3error("Illegal to foreach loop on '" + fromDtp->prettyTypeName() + "'"); - VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep); - return; - } - // New loop goes UNDER previous loop - if (varp) { - if (!newp) { - newp = loopp; + // Prep for next + fromDtp = fromDtp->subDTypep(); + } else if (AstBasicDType* const adtypep = VN_CAST(fromDtp, BasicDType)) { + if (adtypep->isString()) { + if (varp) { + AstConst* const leftp = new AstConst{fl, AstConst::Signed32{}, 0}; + AstLt* const condp + = new AstLt{fl, new AstVarRef{fl, varp, VAccess::READ}, + new AstLenN{fl, fromp->cloneTreePure(false)}}; + AstAdd* const incp + = new AstAdd{fl, new AstConst{fl, AstConst::Signed32{}, 1}, + new AstVarRef{fl, varp, VAccess::READ}}; + loopp = createForeachLoop(nodep, bodyPointp, varp, leftp, condp, incp); + } + } else if (!adtypep->isRanged()) { + argsp->v3error("Illegal to foreach loop on basic '" + + fromDtp->prettyTypeName() + "'"); + VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep); + VL_DO_DANGLING(bodyPointp->deleteTree(), bodyPointp); + return; + } else { + if (varp) { + loopp = createForeachLoopRanged(nodep, bodyPointp, varp, + adtypep->declRange()); + } + } + // Prep for next + fromDtp = nullptr; + } else if (VN_IS(fromDtp, DynArrayDType) || VN_IS(fromDtp, QueueDType)) { + if (varp) { + auto* const leftp = new AstConst{fl, AstConst::Signed32{}, 0}; + auto* const sizep + = new AstCMethodHard{fl, fromp->cloneTreePure(false), "size"}; + sizep->dtypeSetSigned32(); + sizep->didWidth(true); + sizep->protect(false); + AstNodeExpr* const condp + = new AstLt{fl, new AstVarRef{fl, varp, VAccess::READ}, sizep}; + AstNodeExpr* const incp + = new AstAdd{fl, new AstConst{fl, AstConst::Signed32{}, 1}, + new AstVarRef{fl, varp, VAccess::READ}}; + loopp = createForeachLoop(nodep, bodyPointp, varp, leftp, condp, incp); + } + // Prep for next + fromDtp = fromDtp->subDTypep(); + } else if (const AstAssocArrayDType* const adtypep + = VN_CAST(fromDtp, AssocArrayDType)) { + // Make this: var KEY_TYPE index; + // bit index__Vfirst; + // index__Vfirst = 0; + // if (0 != array.first(index)) + // do body while (index__Vfirst || 0 != array.next(index)) + varp->dtypeFrom(adtypep->keyDTypep()); + AstVar* const first_varp = new AstVar{ + fl, VVarType::BLOCKTEMP, varp->name() + "__Vfirst", VFlagBitPacked{}, 1}; + first_varp->usedLoopIdx(true); + AstNodeExpr* const firstp = new AstMethodCall{ + fl, fromp->cloneTreePure(false), "first", + new AstArg{fl, "", new AstVarRef{fl, varp, VAccess::READWRITE}}}; + AstNodeExpr* const nextp = new AstMethodCall{ + fl, fromp->cloneTreePure(false), "next", + new AstArg{fl, "", new AstVarRef{fl, varp, VAccess::READWRITE}}}; + AstNode* const first_clearp + = new AstAssign{fl, new AstVarRef{fl, first_varp, VAccess::WRITE}, + new AstConst{fl, AstConst::BitFalse{}}}; + auto* const orp + = new AstLogOr{fl, new AstVarRef{fl, first_varp, VAccess::READ}, + new AstNeq{fl, new AstConst{fl, 0}, nextp}}; + AstNode* const whilep = new AstWhile{fl, orp, first_clearp}; + first_clearp->addNext(bodyPointp); + AstNode* const ifbodyp + = new AstAssign{fl, new AstVarRef{fl, first_varp, VAccess::WRITE}, + new AstConst{fl, AstConst::BitTrue{}}}; + ifbodyp->addNext(whilep); + AstNode* const stmtsp = varp; // New statements for under new Begin + stmtsp->addNext(first_varp); + stmtsp->addNext( + new AstIf{fl, new AstNeq{fl, new AstConst{fl, 0}, firstp}, ifbodyp}); + loopp = new AstBegin{nodep->fileline(), "", stmtsp, false, true}; + // Prep for next + fromDtp = fromDtp->subDTypep(); } else { - lastBodyPointp->replaceWith(loopp); + argsp->v3error("Illegal to foreach loop on '" + fromDtp->prettyTypeName() + + "'"); + VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep); + return; + } + // New loop goes UNDER previous loop + if (varp) { + if (!newp) { + newp = loopp; + } else { + lastBodyPointp->replaceWith(loopp); + } + lastBodyPointp = bodyPointp; } - lastBodyPointp = bodyPointp; } } // The parser validates we don't have "foreach (array[,,,])" - UASSERT_OBJ(newp, nodep, "foreach has no non-empty loop variable"); - if (bodyp) { - lastBodyPointp->replaceWith(bodyp); + AstNode* const bodyp = nodep->stmtsp(); + if (VN_IS(nodep, ConstraintForeach)) { + userIterateAndNext(bodyp, nullptr); } else { - lastBodyPointp->unlinkFrBack(); + UASSERT_OBJ(newp, nodep, "foreach has no non-empty loop variable"); + if (bodyp) { + lastBodyPointp->replaceWith(bodyp->unlinkFrBackWithNext()); + } else { + lastBodyPointp->unlinkFrBack(); + } + // if (debug()) newp->dumpTreeAndNext(cout, "- foreach-new: "); + nodep->replaceWith(newp); + if (lastBodyPointp) VL_DO_DANGLING(lastBodyPointp->deleteTree(), lastBodyPointp); + VL_DO_DANGLING(nodep->deleteTree(), nodep); } - // if (debug()) newp->dumpTreeAndNext(cout, "- foreach-new: "); - nodep->replaceWith(newp); - VL_DO_DANGLING(lastBodyPointp->deleteTree(), lastBodyPointp); - VL_DO_DANGLING(nodep->deleteTree(), nodep); } - AstNode* createForeachLoopRanged(AstForeach* nodep, AstNode* bodysp, AstVar* varp, + AstNode* createForeachLoopRanged(AstNodeForeach* nodep, AstNode* bodysp, AstVar* varp, const VNumRange& declRange) { FileLine* const fl = varp->fileline(); AstNodeExpr* const leftp = new AstConst{fl, AstConst::Signed32{}, declRange.left()}; @@ -4714,7 +4761,7 @@ private: } return createForeachLoop(nodep, bodysp, varp, leftp, condp, incp); } - AstNode* createForeachLoop(AstForeach* nodep, AstNode* bodysp, AstVar* varp, + AstNode* createForeachLoop(AstNodeForeach* nodep, AstNode* bodysp, AstVar* varp, AstNodeExpr* leftp, AstNodeExpr* condp, AstNodeExpr* incp) { FileLine* const fl = varp->fileline(); auto* const whilep = new AstWhile{ @@ -4973,6 +5020,7 @@ private: nodep->text(newFormat); UINFO(9, " Display out " << nodep->text() << endl); } + void visit(AstConstraintRef* nodep) override { userIterateChildren(nodep, nullptr); } void visit(AstDisplay* nodep) override { assertAtStatement(nodep); if (nodep->filep()) iterateCheckFileDesc(nodep, nodep->filep(), BOTH); diff --git a/src/verilog.y b/src/verilog.y index 2f5d70a07..44fe50039 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -4907,7 +4907,7 @@ expr: // IEEE: part of expression/constant_expression/ // // // IEEE: expression_or_dist - here to avoid reduce problems // // "expr yDIST '{' dist_list '}'" - | ~l~expr yDIST '{' dist_list '}' { $$ = $1; BBUNSUP($2, "Unsupported: dist"); } + | ~l~expr yDIST '{' dist_list '}' { $$ = $1; } ; fexpr: // For use as first part of statement (disambiguates <=) @@ -7107,21 +7107,29 @@ memberQualOne: // IEEE: property_qualifier + me //********************************************************************** // Constraints -class_constraint: // ==IEEE: class_constraint +class_constraint: // ==IEEE: class_constraint // // IEEE: constraint_declaration // // UNSUP: We have the unsupported warning on the randomize() call, so don't bother on // // constraint blocks. When we support randomize we need to make AST nodes for below rules - constraintStaticE yCONSTRAINT idAny constraint_block - { // Variable so we can link and later ignore constraint_mode() methods - $$ = new AstVar{$3, VVarType::MEMBER, *$3, VFlagBitPacked{}, 1}; - $2->v3warn(CONSTRAINTIGN, "Constraint ignored (unsupported)"); } + constraintStaticE yCONSTRAINT constraintIdNew constraint_block + { $$ = $3; $$->isStatic($1); $$->addItemsp($4); SYMP->popScope($$); } + | constraintStaticE yCONSTRAINT constraintIdNew '{' '}' + { $$ = $3; $$->isStatic($1); SYMP->popScope($$); } // // 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"); } + | constraintStaticE yCONSTRAINT constraintIdNew ';' + { $$ = $3; $$->isStatic($1); SYMP->popScope($$); } + | yEXTERN constraintStaticE yCONSTRAINT constraintIdNew ';' + { $$ = $4; $$->isStatic($1); SYMP->popScope($4); + BBUNSUP($1, "Unsupported: extern constraint"); } + | yPURE constraintStaticE yCONSTRAINT constraintIdNew ';' + { $$ = $4; $$->isStatic($1); SYMP->popScope($4); + BBUNSUP($1, "Unsupported: pure constraint"); } + ; + +constraintIdNew: // IEEE: id part of class_constraint + idAny/*constraint_identifier*/ + { $$ = new AstConstraint{$1, *$1, nullptr}; + SYMP->pushNewUnderNodeOrCurrent($$, nullptr); } ; constraint_block: // ==IEEE: constraint_block @@ -7136,15 +7144,15 @@ 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"); } + { $$ = new AstConstraintBefore{$1, $2, $4}; } ; -solve_before_list: // ==IEEE: solve_before_list +solve_before_list: // ==IEEE: solve_before_list constraint_primary { $$ = $1; } | solve_before_list ',' constraint_primary { $$ = addNextNull($1, $3); } ; -constraint_primary: // ==IEEE: constraint_primary +constraint_primary: // ==IEEE: constraint_primary // // exprScope more general than: // // [ implicit_class_handle '.' | class_scope ] hierarchical_identifier select exprScope { $$ = $1; } @@ -7156,21 +7164,32 @@ constraint_expressionList: // ==IEEE: { constraint_expression } ; constraint_expression: // ==IEEE: constraint_expression - expr/*expression_or_dist*/ ';' { $$ = $1; } + expr/*expression_or_dist*/ ';' + { $$ = new AstConstraintExpr{$1->fileline(), $1}; } // // 1800-2012: - | ySOFT expr/*expression_or_dist*/ ';' { $$ = nullptr; /*UNSUP-no-UVM*/ } + | ySOFT expr/*expression_or_dist*/ ';' + { AstConstraintExpr* const newp = new AstConstraintExpr{$1, $2}; + newp->isSoft(true); + $$ = newp; } // // 1800-2012: // // IEEE: uniqueness_constraint ';' - | yUNIQUE '{' range_list '}' { $$ = nullptr; /*UNSUP-no-UVM*/ } + | yUNIQUE '{' range_list '}' + { $$ = new AstConstraintUnique{$1, $3}; } // // IEEE: expr yP_MINUSGT constraint_set // // Conflicts with expr:"expr yP_MINUSGT expr"; rule moved there // - | yIF '(' expr ')' constraint_set %prec prLOWER_THAN_ELSE { $$ = nullptr; /*UNSUP-UVM*/ } - | yIF '(' expr ')' constraint_set yELSE constraint_set { $$ = nullptr; /*UNSUP-UVM*/ } + | yIF '(' expr ')' constraint_set %prec prLOWER_THAN_ELSE + { $$ = new AstConstraintIf{$1, $3, $5, nullptr}; } + | yIF '(' expr ')' constraint_set yELSE constraint_set + { $$ = new AstConstraintIf{$1, $3, $5, $7}; } // // IEEE says array_identifier here, but dotted accepted in VMM + 1800-2009 - | yFOREACH '(' idClassSelForeach ')' constraint_set { $$ = nullptr; /*UNSUP-UVM*/ } + | yFOREACH '(' idClassSelForeach ')' constraint_set + { $$ = new AstConstraintForeach{$1, $3, $5}; } // // soft is 1800-2012 - | yDISABLE ySOFT expr/*constraint_primary*/ ';' { $$ = nullptr; /*UNSUP-no-UVM*/ } + | yDISABLE ySOFT expr/*constraint_primary*/ ';' + { AstConstraintExpr* const newp = new AstConstraintExpr{$1, $3}; + newp->isDisableSoft(true); + $$ = newp; } ; constraint_set: // ==IEEE: constraint_set @@ -7183,14 +7202,17 @@ dist_list: // ==IEEE: dist_list | dist_list ',' dist_item { $$ = addNextNull($1, $3); } ; -dist_item: // ==IEEE: dist_item + dist_weight - value_range { $$ = $1; /* Same as := 1 */ } - | value_range yP_COLONEQ expr { $$ = $1; /*UNSUP-no-UVM*/ } - | value_range yP_COLONDIV expr { $$ = $1; /*UNSUP-no-UVM*/ } +dist_item: // ==IEEE: dist_item + dist_weight + value_range + { $$ = new AstDistItem{$1->fileline(), $1, new AstConst{$1->fileline(), 1}}; } + | value_range yP_COLONEQ expr + { $$ = new AstDistItem{$2, $1, $3}; } + | value_range yP_COLONDIV expr + { $$ = new AstDistItem{$2, $1, $3}; $$->isWhole(true); } ; extern_constraint_declaration: // ==IEEE: extern_constraint_declaration - constraintStaticE yCONSTRAINT packageClassScopeE idAny + constraintStaticE yCONSTRAINT packageClassScopeE idAny constraint_block { $$ = nullptr; BBUNSUP($2, "Unsupported: extern constraint"); } ; diff --git a/test_regress/t/t_constraint_method_bad.out b/test_regress/t/t_constraint_method_bad.out new file mode 100644 index 000000000..9c27c3b0a --- /dev/null +++ b/test_regress/t/t_constraint_method_bad.out @@ -0,0 +1,5 @@ +%Error: t/t_constraint_method_bad.v:13:12: No such constraint method 'bad_method' + : ... note: In instance 't' + 13 | cons.bad_method(1); + | ^~~~~~~~~~ +%Error: Exiting due to diff --git a/test_regress/t/t_constraint_method_bad.pl b/test_regress/t/t_constraint_method_bad.pl new file mode 100755 index 000000000..66fa61649 --- /dev/null +++ b/test_regress/t/t_constraint_method_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 2020 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 => $Self->{vlt_all}, + expect_filename => $Self->{golden_filename}, + ); + +ok(1); +1; diff --git a/test_regress/t/t_constraint_method_bad.v b/test_regress/t/t_constraint_method_bad.v new file mode 100644 index 000000000..05f833a5e --- /dev/null +++ b/test_regress/t/t_constraint_method_bad.v @@ -0,0 +1,19 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2023 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +class Packet; + rand int m_one; + + constraint cons { m_one > 0 && m_one < 2; } + + task test1; + cons.bad_method(1); // BAD + endtask + +endclass + +module t (/*AUTOARG*/); +endmodule diff --git a/test_regress/t/t_constraint_mode.v b/test_regress/t/t_constraint_mode.v index 6d18156bc..1aa50c637 100644 --- a/test_regress/t/t_constraint_mode.v +++ b/test_regress/t/t_constraint_mode.v @@ -5,20 +5,17 @@ // SPDX-License-Identifier: CC0-1.0 class Packet; - rand int one; + rand int m_one; - constraint a { one > 0 && one < 2; } + constraint cons { m_one > 0 && m_one < 2; } + + Packet other; task test1; - // TODO Verilator ignores this setting currently, always returning 1 (rand on) - one.rand_mode(0); - one.rand_mode(1); - if (one.rand_mode() != 1) $stop; - // TODO Verilator ignores this setting currently, always returning 0 (constraint off) - a.constraint_mode(1); - a.constraint_mode(0); - if (a.constraint_mode() != 0) $stop; + cons.constraint_mode(1); + cons.constraint_mode(0); + if (cons.constraint_mode() != 0) $stop; endtask endclass @@ -34,23 +31,34 @@ module t (/*AUTOARG*/); v = p.randomize(); if (v != 1) $stop; `ifndef VERILATOR - if (p.one != 1) $stop; + if (p.m_one != 1) $stop; `endif + // IEEE: function void object[.constraint_identifier].constraint_mode(bit on_off); + // IEEE: function int object.constraint_identifier.constraint_mode(); + // TODO Verilator ignores this setting currently, always returning 1 (rand on) - p.one.rand_mode(0); - p.one.rand_mode(1); - if (p.one.rand_mode() != 1) $stop; + // TODO test that these control constraints as specified + p.constraint_mode(0); + p.constraint_mode(1); + // Not legal to get current constraint_mode() value on a class-only call // TODO Verilator ignores this setting currently, always returning 0 (constraint off) - p.a.constraint_mode(1); - p.a.constraint_mode(0); - if (p.a.constraint_mode() != 0) $stop; + // TODO test that these control constraints as specified + p.cons.constraint_mode(1); + p.cons.constraint_mode(0); + if (p.cons.constraint_mode() != 0) $stop; + + // TODO Verilator ignores this setting currently, always returning 0 (constraint off) + // TODO test that these control constraints as specified + p.other = new; + p.other.cons.constraint_mode(1); + p.other.cons.constraint_mode(0); + if (p.other.cons.constraint_mode() != 0) $stop; p.test1(); // TODO test can't redefine constraint_mode - // TODO test can't redefine rand_mode // TODO test can't call constraint_mode on non-constraint $write("*-* All Finished *-*\n"); diff --git a/test_regress/t/t_constraint_mode_warn_bad.out b/test_regress/t/t_constraint_mode_warn_bad.out index 7bcae1c14..261aaab42 100644 --- a/test_regress/t/t_constraint_mode_warn_bad.out +++ b/test_regress/t/t_constraint_mode_warn_bad.out @@ -1,54 +1,51 @@ -%Warning-CONSTRAINTIGN: t/t_constraint_mode.v:10:4: Constraint ignored (unsupported) - 10 | constraint a { one > 0 && one < 2; } - | ^~~~~~~~~~ +%Warning-CONSTRAINTIGN: t/t_constraint_mode.v:16:12: constraint_mode ignored (unsupported) + : ... note: In instance 't' + 16 | cons.constraint_mode(1); + | ^~~~~~~~~~~~~~~ ... For warning description see https://verilator.org/warn/CONSTRAINTIGN?v=latest ... Use "/* verilator lint_off CONSTRAINTIGN */" and lint_on around source to disable this message. -%Warning-CONSTRAINTIGN: t/t_constraint_mode.v:14:11: rand_mode ignored (unsupported) +%Warning-CONSTRAINTIGN: t/t_constraint_mode.v:17:12: constraint_mode ignored (unsupported) : ... note: In instance 't' - 14 | one.rand_mode(0); - | ^~~~~~~~~ -%Warning-CONSTRAINTIGN: t/t_constraint_mode.v:15:11: rand_mode ignored (unsupported) + 17 | cons.constraint_mode(0); + | ^~~~~~~~~~~~~~~ +%Warning-CONSTRAINTIGN: t/t_constraint_mode.v:18:16: constraint_mode ignored (unsupported) : ... note: In instance 't' - 15 | one.rand_mode(1); - | ^~~~~~~~~ -%Warning-CONSTRAINTIGN: t/t_constraint_mode.v:16:15: rand_mode ignored (unsupported) - : ... note: In instance 't' - 16 | if (one.rand_mode() != 1) $stop; - | ^~~~~~~~~ -%Warning-CONSTRAINTIGN: t/t_constraint_mode.v:19:9: constraint_mode ignored (unsupported) + 18 | if (cons.constraint_mode() != 0) $stop; + | ^~~~~~~~~~~~~~~ +%Warning-CONSTRAINTIGN: t/t_constraint_mode.v:42:9: Unsupported: 'constraint_mode' called on object : ... note: In instance 't' - 19 | a.constraint_mode(1); + 42 | p.constraint_mode(0); | ^~~~~~~~~~~~~~~ -%Warning-CONSTRAINTIGN: t/t_constraint_mode.v:20:9: constraint_mode ignored (unsupported) +%Warning-CONSTRAINTIGN: t/t_constraint_mode.v:43:9: Unsupported: 'constraint_mode' called on object : ... note: In instance 't' - 20 | a.constraint_mode(0); + 43 | p.constraint_mode(1); | ^~~~~~~~~~~~~~~ -%Warning-CONSTRAINTIGN: t/t_constraint_mode.v:21:13: constraint_mode ignored (unsupported) +%Warning-CONSTRAINTIGN: t/t_constraint_mode.v:48:14: constraint_mode ignored (unsupported) : ... note: In instance 't' - 21 | if (a.constraint_mode() != 0) $stop; - | ^~~~~~~~~~~~~~~ -%Warning-CONSTRAINTIGN: t/t_constraint_mode.v:41:13: rand_mode ignored (unsupported) + 48 | p.cons.constraint_mode(1); + | ^~~~~~~~~~~~~~~ +%Warning-CONSTRAINTIGN: t/t_constraint_mode.v:49:14: constraint_mode ignored (unsupported) : ... note: In instance 't' - 41 | p.one.rand_mode(0); - | ^~~~~~~~~ -%Warning-CONSTRAINTIGN: t/t_constraint_mode.v:42:13: rand_mode ignored (unsupported) + 49 | p.cons.constraint_mode(0); + | ^~~~~~~~~~~~~~~ +%Warning-CONSTRAINTIGN: t/t_constraint_mode.v:50:18: constraint_mode ignored (unsupported) : ... note: In instance 't' - 42 | p.one.rand_mode(1); - | ^~~~~~~~~ -%Warning-CONSTRAINTIGN: t/t_constraint_mode.v:43:17: rand_mode ignored (unsupported) + 50 | if (p.cons.constraint_mode() != 0) $stop; + | ^~~~~~~~~~~~~~~ +%Warning-CONSTRAINTIGN: t/t_constraint_mode.v:55:20: constraint_mode ignored (unsupported) : ... note: In instance 't' - 43 | if (p.one.rand_mode() != 1) $stop; - | ^~~~~~~~~ -%Warning-CONSTRAINTIGN: t/t_constraint_mode.v:46:11: constraint_mode ignored (unsupported) + 55 | p.other.cons.constraint_mode(1); + | ^~~~~~~~~~~~~~~ +%Warning-CONSTRAINTIGN: t/t_constraint_mode.v:56:20: constraint_mode ignored (unsupported) : ... note: In instance 't' - 46 | p.a.constraint_mode(1); - | ^~~~~~~~~~~~~~~ -%Warning-CONSTRAINTIGN: t/t_constraint_mode.v:47:11: constraint_mode ignored (unsupported) + 56 | p.other.cons.constraint_mode(0); + | ^~~~~~~~~~~~~~~ +%Warning-CONSTRAINTIGN: t/t_constraint_mode.v:57:24: constraint_mode ignored (unsupported) : ... note: In instance 't' - 47 | p.a.constraint_mode(0); - | ^~~~~~~~~~~~~~~ -%Warning-CONSTRAINTIGN: t/t_constraint_mode.v:48:15: constraint_mode ignored (unsupported) + 57 | if (p.other.cons.constraint_mode() != 0) $stop; + | ^~~~~~~~~~~~~~~ +%Warning-CONSTRAINTIGN: t/t_constraint_mode.v:10:15: Constraint ignored (unsupported) : ... note: In instance 't' - 48 | if (p.a.constraint_mode() != 0) $stop; - | ^~~~~~~~~~~~~~~ + 10 | constraint cons { m_one > 0 && m_one < 2; } + | ^~~~ %Error: Exiting due to diff --git a/test_regress/t/t_constraint_xml.out b/test_regress/t/t_constraint_xml.out new file mode 100644 index 000000000..fd9e5c556 --- /dev/null +++ b/test_regress/t/t_constraint_xml.out @@ -0,0 +1,202 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test_regress/t/t_constraint_xml.pl b/test_regress/t/t_constraint_xml.pl new file mode 100755 index 000000000..4cb7c4c79 --- /dev/null +++ b/test_regress/t/t_constraint_xml.pl @@ -0,0 +1,25 @@ +#!/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(vlt => 1); + +my $out_filename = "$Self->{obj_dir}/V$Self->{name}.xml"; + +compile( + verilator_flags2 => ['--no-std', '--xml-only', '-Wno-CONSTRAINTIGN'], + verilator_make_gmake => 0, + make_top_shell => 0, + make_main => 0, + ); + +files_identical($out_filename, $Self->{golden_filename}); + +ok(1); +1; diff --git a/test_regress/t/t_constraint_xml.v b/test_regress/t/t_constraint_xml.v new file mode 100644 index 000000000..6386361d2 --- /dev/null +++ b/test_regress/t/t_constraint_xml.v @@ -0,0 +1,62 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2020 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +class Packet; + rand int header; // 0..7 + rand int length; // 0..15 + rand int sublength; // 0..15 + rand bit if_4; + rand bit iff_5_6; + + rand int array[2]; // 2,4,6 + + constraint empty {} + + constraint size { + header > 0 && header <= 7; + length <= 15; + length >= header; + length dist { [0:1], [2:5] :/ 2, 6 := 6, 7 := 10, 1}; + } + + constraint ifs { + if (header > 4) { + if_4 == '1; + } + if (header == 5 || header == 6) { + iff_5_6 == '1; + } else { + iff_5_6 == '0; + } + } + + constraint arr_uniq { + foreach (array[i]) { + array[i] inside {2, 4, 6}; + } + unique { array[0], array[1] } + } + + constraint order { solve length before header; } + + constraint dis { + soft sublength; + disable soft sublength; + sublength <= length; + } + +endclass + +module t (/*AUTOARG*/); + + Packet p; + + initial begin + // Not testing use of constraints + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_dist_warn_coverage.pl b/test_regress/t/t_dist_warn_coverage.pl index 558a35b3d..f21e1e798 100755 --- a/test_regress/t/t_dist_warn_coverage.pl +++ b/test_regress/t/t_dist_warn_coverage.pl @@ -31,7 +31,6 @@ foreach my $s ( 'Unsupported: Ranges ignored in port-lists', # Hard to hit 'dynamic new() not expected in this context (expected under an assign)', # Instead get syntax error # Not yet analyzed - ' is not an in/out/inout/param/interface: ', ' loading non-variable', '--pipe-filter protocol error, unexpected: ', '/*verilator sformat*/ can only be applied to last argument of ', diff --git a/test_regress/t/t_foreach_nindex_bad.out b/test_regress/t/t_foreach_nindex_bad.out index 1433bce46..4e28c541f 100644 --- a/test_regress/t/t_foreach_nindex_bad.out +++ b/test_regress/t/t_foreach_nindex_bad.out @@ -1,4 +1,5 @@ %Error: t/t_foreach_nindex_bad.v:12:34: foreach loop variables exceed number of indices of array + : ... note: In instance 't' 12 | foreach (array[i, j, badk, badl]); | ^~~~ %Error: Exiting due to diff --git a/test_regress/t/t_randomize.out b/test_regress/t/t_randomize.out index 82e50c3f1..03a8a5ecf 100644 --- a/test_regress/t/t_randomize.out +++ b/test_regress/t/t_randomize.out @@ -1,36 +1,31 @@ -%Error-UNSUPPORTED: t/t_randomize.v:11:4: Unsupported: extern constraint - 11 | extern constraint ex; - | ^~~~~~ - ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest -%Warning-CONSTRAINTIGN: t/t_randomize.v:13:4: Constraint ignored (unsupported) - 13 | constraint a { header > 0 && header < 1000; } - | ^~~~~~~~~~ +%Warning-CONSTRAINTIGN: t/t_randomize.v:16:15: Constraint ignored (unsupported) + : ... note: In instance 't' + 16 | constraint empty {} + | ^~~~~ + ... For warning description see https://verilator.org/warn/CONSTRAINTIGN?v=latest ... Use "/* verilator lint_off CONSTRAINTIGN */" and lint_on around source to disable this message. -%Warning-CONSTRAINTIGN: t/t_randomize.v:14:4: Constraint ignored (unsupported) - 14 | constraint b { - | ^~~~~~~~~~ -%Warning-CONSTRAINTIGN: t/t_randomize.v:19:4: Constraint ignored (unsupported) - 19 | constraint c { - | ^~~~~~~~~~ -%Warning-CONSTRAINTIGN: t/t_randomize.v:23:4: Constraint ignored (unsupported) - 23 | constraint d { - | ^~~~~~~~~~ -%Error-UNSUPPORTED: t/t_randomize.v:29:29: Unsupported: solve before - 29 | constraint order { solve length before header; } - | ^~~~~~ -%Warning-CONSTRAINTIGN: t/t_randomize.v:29:4: Constraint ignored (unsupported) - 29 | constraint order { solve length before header; } - | ^~~~~~~~~~ -%Error-UNSUPPORTED: t/t_randomize.v:32:9: Unsupported: dist - 32 | x dist { [100:102] :/ 1, 200 := 2, 300 := 5, 400}; - | ^~~~ -%Warning-CONSTRAINTIGN: t/t_randomize.v:30:4: Constraint ignored (unsupported) - 30 | constraint dis { - | ^~~~~~~~~~ -%Error-UNSUPPORTED: t/t_randomize.v:37:1: Unsupported: extern constraint - 37 | constraint Packet::ex { header > 0 }; - | ^~~~~~~~~~ -%Error: t/t_randomize.v:37:23: syntax error, unexpected '{' - 37 | constraint Packet::ex { header > 0 }; - | ^ +%Warning-CONSTRAINTIGN: t/t_randomize.v:18:15: Constraint ignored (unsupported) + : ... note: In instance 't' + 18 | constraint size { + | ^~~~ +%Warning-CONSTRAINTIGN: t/t_randomize.v:25:15: Constraint ignored (unsupported) + : ... note: In instance 't' + 25 | constraint ifs { + | ^~~ +%Warning-CONSTRAINTIGN: t/t_randomize.v:36:15: Constraint ignored (unsupported) + : ... note: In instance 't' + 36 | constraint arr_uniq { + | ^~~~~~~~ +%Warning-CONSTRAINTIGN: t/t_randomize.v:43:15: Constraint ignored (unsupported) + : ... note: In instance 't' + 43 | constraint order { solve length before header; } + | ^~~~~ +%Warning-CONSTRAINTIGN: t/t_randomize.v:45:15: Constraint ignored (unsupported) + : ... note: In instance 't' + 45 | constraint dis { + | ^~~ +%Error-UNSUPPORTED: t/t_randomize.v:14:13: Unsupported: random member variable with type 'int$[0:1]' + : ... note: In instance 't' + 14 | rand int array[2]; + | ^~~~~ %Error: Exiting due to diff --git a/test_regress/t/t_randomize.v b/test_regress/t/t_randomize.v index 30634b113..f854daab2 100644 --- a/test_regress/t/t_randomize.v +++ b/test_regress/t/t_randomize.v @@ -5,37 +5,51 @@ // SPDX-License-Identifier: CC0-1.0 class Packet; - rand int header; - rand int length; + rand int header; // 0..7 + rand int length; // 0..15 + rand int sublength; // 0..15 + rand bit if_4; + rand bit iff_5_6; - extern constraint ex; + rand int array[2]; // 2,4,6 - constraint a { header > 0 && header < 1000; } - constraint b { - if (64 > header) { - header < (64'h1 << length); + constraint empty {} + + constraint size { + header > 0 && header <= 7; + length <= 15; + length >= header; + length dist { [0:1], [2:5] :/ 2, 6 := 6, 7 := 10, 1}; + } + + constraint ifs { + if (header > 4) { + if_4 == '1; + } + if (header == 5 || header == 6) { + iff_5_6 == '1; + } else { + iff_5_6 == '0; } } - constraint c { - header >= length - 10; - header <= length; - } - constraint d { - foreach (in_use[i]) { - !(start_offset <= in_use[i].Xend_offsetX && - start_offset + length - 1 >= in_use[i].Xstart_offsetX); + + constraint arr_uniq { + foreach (array[i]) { + array[i] inside {2, 4, 6}; } + unique { array[0], array[1] } } + constraint order { solve length before header; } + constraint dis { - disable soft x; - x dist { [100:102] :/ 1, 200 := 2, 300 := 5, 400}; + soft sublength; + disable soft sublength; + sublength <= length; } endclass -constraint Packet::ex { header > 0 }; - module t (/*AUTOARG*/); Packet p; @@ -43,15 +57,12 @@ module t (/*AUTOARG*/); initial begin int v; + // TODO not testing constrained values v = p.randomize(); if (v != 1) $stop; - v = p.randomize(1); - if (v != 1) $stop; - v = p.randomize(1, 2); - if (v != 1) $stop; v = p.randomize() with {}; if (v != 1) $stop; - // Not testing other randomize forms as unused in UVM + // TODO not testing other randomize forms as unused in UVM $write("*-* All Finished *-*\n"); $finish; diff --git a/test_regress/t/t_randomize_extern.out b/test_regress/t/t_randomize_extern.out new file mode 100644 index 000000000..a56aaa409 --- /dev/null +++ b/test_regress/t/t_randomize_extern.out @@ -0,0 +1,11 @@ +%Error-UNSUPPORTED: t/t_randomize_extern.v:8:4: Unsupported: pure constraint + 8 | pure constraint pur; + | ^~~~ + ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest +%Error-UNSUPPORTED: t/t_randomize_extern.v:17:4: Unsupported: extern constraint + 17 | extern constraint ex; + | ^~~~~~ +%Error-UNSUPPORTED: t/t_randomize_extern.v:21:1: Unsupported: extern constraint + 21 | constraint Packet::ex { header == 2; } + | ^~~~~~~~~~ +%Error: Exiting due to diff --git a/test_regress/t/t_randomize_extern.pl b/test_regress/t/t_randomize_extern.pl new file mode 100755 index 000000000..63947fd00 --- /dev/null +++ b/test_regress/t/t_randomize_extern.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(vlt => 1); + +lint( + fails => 1, + expect_filename => $Self->{golden_filename}, + ); + +ok(1); +1; diff --git a/test_regress/t/t_randomize_extern.v b/test_regress/t/t_randomize_extern.v new file mode 100644 index 000000000..cc74f0323 --- /dev/null +++ b/test_regress/t/t_randomize_extern.v @@ -0,0 +1,42 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2020 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +class Base; + pure constraint pur; +endclass + +class Packet extends Base; + rand int header; + rand int length; + + constraint pur { length == 3; } + + extern constraint ex; + +endclass + +constraint Packet::ex { header == 2; } + +module t (/*AUTOARG*/); + + Packet p; + + initial begin + + int v; + v = p.randomize(); + if (v != 1) $stop; + if (p.header != 2) $stop; + if (p.length != 3) $stop; + v = p.randomize(1); + if (v != 1) $stop; + if (p.header != 2) $stop; + if (p.length != 3) $stop; + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_randomize_rand_mode.pl b/test_regress/t/t_randomize_rand_mode.pl new file mode 100755 index 000000000..11d21fc86 --- /dev/null +++ b/test_regress/t/t_randomize_rand_mode.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 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 => ['-Wno-CONSTRAINTIGN'], + ); + +execute( + check_finished => 1, + ); + + +ok(1); +1; diff --git a/test_regress/t/t_randomize_rand_mode.v b/test_regress/t/t_randomize_rand_mode.v new file mode 100644 index 000000000..e6f7f3241 --- /dev/null +++ b/test_regress/t/t_randomize_rand_mode.v @@ -0,0 +1,56 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2023 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +class Packet; + rand int m_one; + + Packet other; + + task test1; + // TODO Verilator ignores this setting currently, always returning 1 (rand on) + // TODO test that these control randomization as specified + m_one.rand_mode(0); + m_one.rand_mode(1); + if (m_one.rand_mode() != 1) $stop; + endtask + +endclass + +module t (/*AUTOARG*/); + + Packet p; + + int v; + + initial begin + p = new; + v = p.randomize(); + if (v != 1) $stop; +`ifndef VERILATOR + if (p.m_one != 1) $stop; +`endif + + // IEEE: function void object[.random_variable].rand_mode(bit on_off); + // IEEE: function int object.random_variable.rand_mode(); + + // TODO Verilator ignores this setting currently, always returning 1 (rand on) + // TODO test that these control randomization as specified + p.rand_mode(0); + p.rand_mode(1); + // Not legal to get current rand() value on a class-only call + + // TODO Verilator ignores this setting currently, always returning 1 (rand on) + // TODO test that these control randomization as specified + p.m_one.rand_mode(0); + p.m_one.rand_mode(1); + if (p.m_one.rand_mode() != 1) $stop; + + // TODO test can't redefine rand_mode + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_randomize_rand_mode_warn_bad.out b/test_regress/t/t_randomize_rand_mode_warn_bad.out new file mode 100644 index 000000000..d8f42a152 --- /dev/null +++ b/test_regress/t/t_randomize_rand_mode_warn_bad.out @@ -0,0 +1,35 @@ +%Warning-CONSTRAINTIGN: t/t_randomize_rand_mode.v:15:13: rand_mode ignored (unsupported) + : ... note: In instance 't' + 15 | m_one.rand_mode(0); + | ^~~~~~~~~ + ... For warning description see https://verilator.org/warn/CONSTRAINTIGN?v=latest + ... Use "/* verilator lint_off CONSTRAINTIGN */" and lint_on around source to disable this message. +%Warning-CONSTRAINTIGN: t/t_randomize_rand_mode.v:16:13: rand_mode ignored (unsupported) + : ... note: In instance 't' + 16 | m_one.rand_mode(1); + | ^~~~~~~~~ +%Warning-CONSTRAINTIGN: t/t_randomize_rand_mode.v:17:17: rand_mode ignored (unsupported) + : ... note: In instance 't' + 17 | if (m_one.rand_mode() != 1) $stop; + | ^~~~~~~~~ +%Warning-CONSTRAINTIGN: t/t_randomize_rand_mode.v:41:9: rand_mode ignored (unsupported) + : ... note: In instance 't' + 41 | p.rand_mode(0); + | ^~~~~~~~~ +%Warning-CONSTRAINTIGN: t/t_randomize_rand_mode.v:42:9: rand_mode ignored (unsupported) + : ... note: In instance 't' + 42 | p.rand_mode(1); + | ^~~~~~~~~ +%Warning-CONSTRAINTIGN: t/t_randomize_rand_mode.v:47:15: rand_mode ignored (unsupported) + : ... note: In instance 't' + 47 | p.m_one.rand_mode(0); + | ^~~~~~~~~ +%Warning-CONSTRAINTIGN: t/t_randomize_rand_mode.v:48:15: rand_mode ignored (unsupported) + : ... note: In instance 't' + 48 | p.m_one.rand_mode(1); + | ^~~~~~~~~ +%Warning-CONSTRAINTIGN: t/t_randomize_rand_mode.v:49:19: rand_mode ignored (unsupported) + : ... note: In instance 't' + 49 | if (p.m_one.rand_mode() != 1) $stop; + | ^~~~~~~~~ +%Error: Exiting due to diff --git a/test_regress/t/t_randomize_rand_mode_warn_bad.pl b/test_regress/t/t_randomize_rand_mode_warn_bad.pl new file mode 100755 index 000000000..9c0016ff5 --- /dev/null +++ b/test_regress/t/t_randomize_rand_mode_warn_bad.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 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(vlt => 1); + +top_filename("t/t_randomize_rand_mode.v"); + +lint( + fails => 1, + expect_filename => $Self->{golden_filename}, + ); + +ok(1); +1;