diff --git a/src/V3AstNodeExpr.h b/src/V3AstNodeExpr.h index 739597a60..42050c20d 100644 --- a/src/V3AstNodeExpr.h +++ b/src/V3AstNodeExpr.h @@ -1142,6 +1142,39 @@ public: string emitC() override { V3ERROR_NA_RETURN(""); } bool cleanOut() const override { return true; } }; +class AstDist final : public AstNodeExpr { + // @astgen op1 := exprp : AstNodeExpr + // @astgen op2 := itemsp : List[AstDistItem] +public: + AstDist(FileLine* fl, AstNodeExpr* exprp, AstDistItem* itemsp) + : ASTGEN_SUPER_Inside(fl) { + this->exprp(exprp); + this->addItemsp(itemsp); + dtypeSetBit(); + } + ASTGEN_MEMBERS_AstDist; + string emitVerilog() override { return "%l dist { %r }"; } + string emitC() override { V3ERROR_NA_RETURN(""); } + bool cleanOut() const override { return false; } // NA +}; +class AstDistItem final : public AstNodeExpr { + // 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; + string emitVerilog() override { return "%l "s + (m_isWhole ? ":/" : ":=") + " %r"; } + string emitC() override { V3ERROR_NA_RETURN(""); } + bool cleanOut() const override { return false; } // NA + void isWhole(bool flag) { m_isWhole = flag; } + bool isWhole() const { return m_isWhole; } +}; class AstDot final : public AstNodeExpr { // A dot separating paths in an AstVarXRef, AstFuncRef or AstTaskRef // These are eliminated in the link stage diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index f1e09defc..6b7525810 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -1064,24 +1064,6 @@ 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. diff --git a/src/V3Randomize.cpp b/src/V3Randomize.cpp index d7c727868..c9992fa90 100644 --- a/src/V3Randomize.cpp +++ b/src/V3Randomize.cpp @@ -328,6 +328,11 @@ class ConstraintExprVisitor final : public VNVisitor { // Fall back to "(ite cond then else)" visit(static_cast(nodep)); } + void visit(AstDist* nodep) override { + nodep->v3warn(CONSTRAINTIGN, "Constraint expression ignored (unsupported)"); + nodep->replaceWith(new AstSFormatF{nodep->fileline(), "true", false, nullptr}); + VL_DO_DANGLING(nodep->deleteTree(), nodep); + } void visit(AstReplicate* nodep) override { // Biop, but RHS is harmful if (editFormat(nodep)) return; diff --git a/src/V3Width.cpp b/src/V3Width.cpp index ce4a8ae91..da0f99c14 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -2150,8 +2150,11 @@ class WidthVisitor final : public VNVisitor { 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()); + userIterate(nodep->rangep(), m_vup); + if (m_vup->prelim()) { // First stage evaluation + userIterate(nodep->weightp(), WidthVP{SELF, BOTH}.p()); + } + nodep->dtypep(nodep->rangep()->dtypep()); } void visit(AstVar* nodep) override { // if (debug()) nodep->dumpTree("- InitPre: "); @@ -2574,6 +2577,46 @@ class WidthVisitor final : public VNVisitor { } } } + void visit(AstDist* nodep) override { + userIterateAndNext(nodep->exprp(), WidthVP{CONTEXT_DET, PRELIM}.p()); + for (AstNode *nextip, *itemp = nodep->itemsp(); itemp; itemp = nextip) { + nextip = itemp->nextp(); // iterate may cause the node to get replaced + VL_DO_DANGLING(userIterate(itemp, WidthVP{CONTEXT_DET, PRELIM}.p()), itemp); + } + + AstBasicDType* dtype = VN_CAST(nodep->exprp()->dtypep(), BasicDType); + AstNodeDType* subDTypep = nullptr; + nodep->dtypeSetBit(); + + if (dtype && dtype->isString()) { + nodep->dtypeSetString(); + subDTypep = nodep->findStringDType(); + } else if (dtype && dtype->isDouble()) { + nodep->dtypeSetDouble(); + subDTypep = nodep->findDoubleDType(); + } else { + // Take width as maximum across all items + int width = nodep->exprp()->width(); + int mwidth = nodep->exprp()->widthMin(); + for (const AstNode* itemp = nodep->itemsp(); itemp; itemp = itemp->nextp()) { + width = std::max(width, itemp->width()); + mwidth = std::max(mwidth, itemp->widthMin()); + } + subDTypep = nodep->findLogicDType(width, mwidth, nodep->exprp()->dtypep()->numeric()); + } + + iterateCheck(nodep, "Dist expression", nodep->exprp(), CONTEXT_DET, FINAL, subDTypep, + EXTEND_EXP); + for (AstDistItem *nextip, *itemp = nodep->itemsp(); itemp; itemp = nextip) { + nextip + = VN_AS(itemp->nextp(), DistItem); // iterate may cause the node to get replaced + iterateCheck(nodep, "Dist Item", itemp, CONTEXT_DET, FINAL, subDTypep, EXTEND_EXP); + } + + if (debug() >= 9) nodep->dumpTree("- dist-out: "); + nodep->dtypep(subDTypep); + } + void visit(AstInside* nodep) override { userIterateAndNext(nodep->exprp(), WidthVP{CONTEXT_DET, PRELIM}.p()); for (AstNode *nextip, *itemp = nodep->itemsp(); itemp; itemp = nextip) { diff --git a/src/verilog.y b/src/verilog.y index 0e6c20b18..affa76167 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -5057,7 +5057,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; $4->deleteTree(); } + | ~l~expr yDIST '{' dist_list '}' { $$ = new AstDist{$2, $1, $4}; } ; fexpr: // For use as first part of statement (disambiguates <=) @@ -7383,7 +7383,7 @@ constraint_set: // ==IEEE: constraint_set | '{' constraint_expressionList '}' { $$ = $2; } ; -dist_list: // ==IEEE: dist_list +dist_list: // ==IEEE: dist_list dist_item { $$ = $1; } | dist_list ',' dist_item { $$ = addNextNull($1, $3); } ; diff --git a/test_regress/t/t_constraint_json_only.out b/test_regress/t/t_constraint_json_only.out index 48a4372f8..4a6ffec9b 100644 --- a/test_regress/t/t_constraint_json_only.out +++ b/test_regress/t/t_constraint_json_only.out @@ -216,14 +216,14 @@ {"type":"CONST","name":"\\\"(bvsge length header)\\\"","addr":"(RE)","loc":"d,24:14,24:16","dtypep":"(M)"} ]} ]}, - {"type":"STMTEXPR","name":"","addr":"(SE)","loc":"d,25:7,25:13", + {"type":"STMTEXPR","name":"","addr":"(SE)","loc":"d,25:14,25:18", "exprp": [ - {"type":"CMETHODHARD","name":"hard","addr":"(TE)","loc":"d,25:7,25:13","dtypep":"(LB)", + {"type":"CMETHODHARD","name":"hard","addr":"(TE)","loc":"d,25:14,25:18","dtypep":"(LB)", "fromp": [ - {"type":"VARREF","name":"constraint","addr":"(UE)","loc":"d,25:7,25:13","dtypep":"(PB)","access":"RW","varp":"(QB)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"constraint","addr":"(UE)","loc":"d,25:14,25:18","dtypep":"(PB)","access":"RW","varp":"(QB)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"} ], "pinsp": [ - {"type":"CONST","name":"\\\"length\\\"","addr":"(VE)","loc":"d,25:7,25:13","dtypep":"(M)"} + {"type":"CONST","name":"\\\"true\\\"","addr":"(VE)","loc":"d,25:14,25:18","dtypep":"(M)"} ]} ]} ],"scopeNamep": []}, diff --git a/test_regress/t/t_constraint_xml.out b/test_regress/t/t_constraint_xml.out index a7156fad1..8eb09eba6 100644 --- a/test_regress/t/t_constraint_xml.out +++ b/test_regress/t/t_constraint_xml.out @@ -159,10 +159,10 @@ - - - - + + + + diff --git a/test_regress/t/t_randomize.out b/test_regress/t/t_randomize.out index 5e8720300..454b0baab 100644 --- a/test_regress/t/t_randomize.out +++ b/test_regress/t/t_randomize.out @@ -1,9 +1,13 @@ +%Warning-CONSTRAINTIGN: t/t_randomize.v:22:14: Constraint expression ignored (unsupported) + : ... note: In instance 't' + 22 | length dist { [0:1], [2:5] :/ 2, 6 := 6, 7 := 10, 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_randomize.v:37:7: Constraint expression ignored (unsupported) : ... note: In instance 't' 37 | foreach (array[i]) { | ^~~~~~~ - ... 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:40:7: Constraint expression ignored (unsupported) : ... note: In instance 't' 40 | unique { array[0], array[1] };