diff --git a/src/V3Randomize.cpp b/src/V3Randomize.cpp index e10ec5830..735b171c2 100644 --- a/src/V3Randomize.cpp +++ b/src/V3Randomize.cpp @@ -640,11 +640,6 @@ 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; @@ -720,7 +715,7 @@ class ConstraintExprVisitor final : public VNVisitor { VL_DO_DANGLING(nodep->deleteTree(), nodep); } void visit(AstConstraintBefore* nodep) override { - nodep->v3warn(CONSTRAINTIGN, "Constraint expression ignored (unsupported)"); + nodep->v3warn(CONSTRAINTIGN, "Constraint expression ignored (imperfect distribution)"); VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep); } void visit(AstConstraintUnique* nodep) override { diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 82ddd46ac..e2e6f18a5 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -2614,6 +2614,8 @@ class WidthVisitor final : public VNVisitor { } } void visit(AstDist* nodep) override { + // x dist {a :/ p, b :/ q} --> (p > 0 && x == a) || (q > 0 && x == b) + nodep->v3warn(CONSTRAINTIGN, "Constraint expression ignored (imperfect distribution)"); 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 @@ -2643,14 +2645,31 @@ class WidthVisitor final : public VNVisitor { 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); + for (AstNode *nextip, *itemp = nodep->itemsp(); itemp; itemp = nextip) { + nextip = itemp->nextp(); + itemp = VN_AS(itemp, DistItem)->rangep(); + // InsideRange will get replaced with Lte&Gte and finalized later + if (!VN_IS(itemp, InsideRange)) + iterateCheck(nodep, "Dist Item", itemp, CONTEXT_DET, FINAL, subDTypep, EXTEND_EXP); } + AstNodeExpr* newp = nullptr; + for (AstDistItem* itemp = nodep->itemsp(); itemp; + itemp = VN_AS(itemp->nextp(), DistItem)) { + AstNodeExpr* inewp = insideItem(nodep, nodep->exprp(), itemp->rangep()); + if (!inewp) continue; + AstNodeExpr* const cmpp + = new AstGt{itemp->fileline(), itemp->weightp()->unlinkFrBack(), + new AstConst{itemp->fileline(), 0}}; + cmpp->fileline()->modifyWarnOff(V3ErrorCode::UNSIGNED, true); + cmpp->fileline()->modifyWarnOff(V3ErrorCode::CMPCONST, true); + inewp = new AstLogAnd{itemp->fileline(), cmpp, inewp}; + newp = newp ? new AstLogOr{nodep->fileline(), newp, inewp} : inewp; + } + if (!newp) newp = new AstConst{nodep->fileline(), AstConst::BitFalse{}}; if (debug() >= 9) nodep->dumpTree("- dist-out: "); - nodep->dtypep(subDTypep); + nodep->replaceWith(newp); + VL_DO_DANGLING(pushDeletep(nodep), nodep); } void visit(AstInside* nodep) override { @@ -2696,41 +2715,40 @@ class WidthVisitor final : public VNVisitor { AstNodeExpr* newp = nullptr; for (AstNodeExpr *nextip, *itemp = nodep->itemsp(); itemp; itemp = nextip) { nextip = VN_AS(itemp->nextp(), NodeExpr); // Will be unlinking - AstNodeExpr* inewp; - const AstNodeDType* const itemDtp = itemp->dtypep()->skipRefp(); - if (AstInsideRange* const irangep = VN_CAST(itemp, InsideRange)) { - // Similar logic in V3Case - inewp = irangep->newAndFromInside(nodep->exprp(), irangep->lhsp()->unlinkFrBack(), - irangep->rhsp()->unlinkFrBack()); - } else if (VN_IS(itemDtp, UnpackArrayDType) || VN_IS(itemDtp, DynArrayDType) - || VN_IS(itemDtp, QueueDType)) { - // Unsupported in parameters - AstNodeExpr* exprp = nodep->exprp()->cloneTreePure(true); - inewp = new AstCMethodHard{nodep->fileline(), itemp->unlinkFrBack(), "inside", - exprp}; - iterateCheckTyped(nodep, "inside value", exprp, itemDtp->subDTypep(), BOTH); - VL_DANGLING(exprp); // Might have been replaced - inewp->dtypeSetBit(); - inewp->didWidth(true); - } else if (VN_IS(itemDtp, AssocArrayDType)) { - nodep->v3error("Inside operator not specified on associative arrays " - "(IEEE 1800-2023 11.4.13)"); - continue; - } else { - inewp = AstEqWild::newTyped(itemp->fileline(), nodep->exprp()->cloneTreePure(true), - itemp->unlinkFrBack()); - } - if (newp) { - newp = new AstLogOr{nodep->fileline(), newp, inewp}; - } else { - newp = inewp; - } + AstNodeExpr* const inewp = insideItem(nodep, nodep->exprp(), itemp); + if (!inewp) continue; + newp = newp ? new AstLogOr{nodep->fileline(), newp, inewp} : inewp; } if (!newp) newp = new AstConst{nodep->fileline(), AstConst::BitFalse{}}; if (debug() >= 9) newp->dumpTree("- inside-out: "); nodep->replaceWith(newp); VL_DO_DANGLING(pushDeletep(nodep), nodep); } + AstNodeExpr* insideItem(AstNode* nodep, AstNodeExpr* exprp, AstNodeExpr* itemp) { + const AstNodeDType* const itemDtp = itemp->dtypep()->skipRefp(); + if (AstInsideRange* const irangep = VN_CAST(itemp, InsideRange)) { + // Similar logic in V3Case + return irangep->newAndFromInside(exprp, irangep->lhsp()->unlinkFrBack(), + irangep->rhsp()->unlinkFrBack()); + } else if (VN_IS(itemDtp, UnpackArrayDType) || VN_IS(itemDtp, DynArrayDType) + || VN_IS(itemDtp, QueueDType)) { + // Unsupported in parameters + AstNodeExpr* const cexprp = exprp->cloneTreePure(true); + AstNodeExpr* const inewp + = new AstCMethodHard{nodep->fileline(), itemp->unlinkFrBack(), "inside", cexprp}; + iterateCheckTyped(nodep, "inside value", cexprp, itemDtp->subDTypep(), BOTH); + VL_DANGLING(cexprp); // Might have been replaced + inewp->dtypeSetBit(); + inewp->didWidth(true); + return inewp; + } else if (VN_IS(itemDtp, AssocArrayDType)) { + nodep->v3error("Inside operator not specified on associative arrays " + "(IEEE 1800-2023 11.4.13)"); + return nullptr; + } + return AstEqWild::newTyped(itemp->fileline(), exprp->cloneTreePure(true), + itemp->unlinkFrBack()); + } void visit(AstInsideRange* nodep) override { // Just do each side; AstInside will rip these nodes out later userIterateAndNext(nodep->lhsp(), m_vup); diff --git a/test_regress/t/t_constraint_dist.py b/test_regress/t/t_constraint_dist.py new file mode 100755 index 000000000..dbae8a1dc --- /dev/null +++ b/test_regress/t/t_constraint_dist.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2024 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 + +import vltest_bootstrap + +test.scenarios('simulator') + +if not test.have_solver: + test.skip("No constraint solver installed") + +test.compile(verilator_flags2=['-Wno-CONSTRAINTIGN']) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_constraint_dist.v b/test_regress/t/t_constraint_dist.v new file mode 100644 index 000000000..2077ec821 --- /dev/null +++ b/test_regress/t/t_constraint_dist.v @@ -0,0 +1,39 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2024 by Antmicro Ltd. +// SPDX-License-Identifier: CC0-1.0 + +`define check_rand(cl, field, cond) \ +begin \ + longint prev_result; \ + int ok = 0; \ + for (int i = 0; i < 10; i++) begin \ + longint result; \ + if (!bit'(cl.randomize())) $stop; \ + result = longint'(field); \ + if (!(cond)) $stop; \ + if (i > 0 && result != prev_result) ok = 1; \ + prev_result = result; \ + end \ + if (ok != 1) $stop; \ +end + +class C; + rand int x, y; + constraint distrib { + x dist { [1:3] := 0, [5:6], [9:15] :/ 0 }; + y dist { [1:3] := 0, 5, 6 := 8, [9:15] :/ 0 }; + x < 20; + }; +endclass + +module t; + initial begin + C c = new; + `check_rand(c, c.x, 5 <= c.x && c.x <= 6); + `check_rand(c, c.y, 5 <= c.y && c.y <= 6); + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_constraint_dist_unsup.out b/test_regress/t/t_constraint_dist_unsup.out new file mode 100644 index 000000000..bfb970312 --- /dev/null +++ b/test_regress/t/t_constraint_dist_unsup.out @@ -0,0 +1,17 @@ +%Warning-CONSTRAINTIGN: t/t_constraint_dist_unsup.v:27:10: Constraint expression ignored (imperfect distribution) + : ... note: In instance 't' + 27 | x dist {que}; + | ^~~~ + ... 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_dist_unsup.v:28:10: Constraint expression ignored (imperfect distribution) + : ... note: In instance 't' + 28 | y dist {arr}; + | ^~~~ +%Warning-CONSTRAINTIGN: t/t_constraint_dist_unsup.v:27:10: Unsupported: randomizing this expression, treating as state + 27 | x dist {que}; + | ^~~~ +%Warning-CONSTRAINTIGN: t/t_constraint_dist_unsup.v:28:10: Unsupported: randomizing this expression, treating as state + 28 | y dist {arr}; + | ^~~~ +%Error: Exiting due to diff --git a/test_regress/t/t_constraint_dist_unsup.py b/test_regress/t/t_constraint_dist_unsup.py new file mode 100755 index 000000000..272fc1280 --- /dev/null +++ b/test_regress/t/t_constraint_dist_unsup.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2024 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 + +import vltest_bootstrap + +test.scenarios('linter') + +test.lint(fails=test.vlt_all, expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_constraint_dist_unsup.v b/test_regress/t/t_constraint_dist_unsup.v new file mode 100644 index 000000000..9f25aacda --- /dev/null +++ b/test_regress/t/t_constraint_dist_unsup.v @@ -0,0 +1,40 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2024 by Antmicro Ltd. +// SPDX-License-Identifier: CC0-1.0 + +`define check_rand(cl, field, cond) \ +begin \ + longint prev_result; \ + int ok = 0; \ + for (int i = 0; i < 10; i++) begin \ + longint result; \ + if (!bit'(cl.randomize())) $stop; \ + result = longint'(field); \ + if (!(cond)) $stop; \ + if (i > 0 && result != prev_result) ok = 1; \ + prev_result = result; \ + end \ + if (ok != 1) $stop; \ +end + +class C; + int que[$] = '{3, 4, 5}; + int arr[3] = '{5, 6, 7}; + rand int x, y; + constraint distrib { + x dist {que}; + y dist {arr}; + }; +endclass + +module t; + initial begin + C c = new; + `check_rand(c, c.x, 3 <= c.x && c.x <= 5); + `check_rand(c, c.y, 5 <= c.y && c.y <= 7); + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_randomize.out b/test_regress/t/t_randomize.out index 5c2adb883..136639289 100644 --- a/test_regress/t/t_randomize.out +++ b/test_regress/t/t_randomize.out @@ -1,4 +1,4 @@ -%Warning-CONSTRAINTIGN: t/t_randomize.v:22:14: Constraint expression ignored (unsupported) +%Warning-CONSTRAINTIGN: t/t_randomize.v:22:14: Constraint expression ignored (imperfect distribution) : ... note: In instance 't' 22 | length dist { [0:1], [2:5] :/ 2, 6 := 6, 7 := 10, 1}; | ^~~~ @@ -8,7 +8,7 @@ : ... note: In instance 't' 40 | unique { array[0], array[1] }; | ^~~~~~ -%Warning-CONSTRAINTIGN: t/t_randomize.v:43:23: Constraint expression ignored (unsupported) +%Warning-CONSTRAINTIGN: t/t_randomize.v:43:23: Constraint expression ignored (imperfect distribution) : ... note: In instance 't' 43 | constraint order { solve length before header; } | ^~~~~