From ec027b5fa341f4fa7f6fba07aab65e6583a42478 Mon Sep 17 00:00:00 2001 From: Yilou Wang Date: Mon, 2 Mar 2026 23:10:36 +0100 Subject: [PATCH] follow comments and refine --- src/V3Randomize.cpp | 20 +++++++++++++++----- test_regress/t/t_constraint_dist_weight.v | 20 ++++++++++++++++---- test_regress/t/t_randomize.py | 9 +++++++-- test_regress/t/t_randomize.v | 4 ++-- 4 files changed, 40 insertions(+), 13 deletions(-) diff --git a/src/V3Randomize.cpp b/src/V3Randomize.cpp index d7333a1f1..b13184d31 100644 --- a/src/V3Randomize.cpp +++ b/src/V3Randomize.cpp @@ -3534,7 +3534,14 @@ class RandomizeVisitor final : public VNVisitor { buckets.push_back({ditemp->rangep(), weightExprp}); } - if (buckets.empty()) continue; + if (buckets.empty()) { + // All weights are zero: dist is vacuously true (unconstrained) + AstConstraintExpr* const truep + = new AstConstraintExpr{fl, new AstConst{fl, AstConst::BitTrue{}}}; + constrExprp->replaceWith(truep); + VL_DO_DANGLING(pushDeletep(constrExprp), constrExprp); + continue; + } // Build totalWeight expression: w[0] + w[1] + ... + w[N-1] AstNodeExpr* totalWeightExprp = nullptr; @@ -3556,6 +3563,7 @@ class RandomizeVisitor final : public VNVisitor { totalVarp->noSubst(true); totalVarp->lifetime(VLifetime::AUTOMATIC_EXPLICIT); totalVarp->funcLocal(true); + totalVarp->isInternal(true); taskp->addStmtsp(totalVarp); taskp->addStmtsp( new AstAssign{fl, new AstVarRef{fl, totalVarp, VAccess::WRITE}, totalWeightExprp}); @@ -3567,6 +3575,7 @@ class RandomizeVisitor final : public VNVisitor { bucketVarp->noSubst(true); bucketVarp->lifetime(VLifetime::AUTOMATIC_EXPLICIT); bucketVarp->funcLocal(true); + bucketVarp->isInternal(true); taskp->addStmtsp(bucketVarp); AstNodeExpr* randp = new AstRand{fl, nullptr, false}; @@ -3629,16 +3638,16 @@ class RandomizeVisitor final : public VNVisitor { if (chainp) { constrExprp->replaceWith(chainp); - VL_DO_DANGLING(constrExprp->deleteTree(), constrExprp); + VL_DO_DANGLING(pushDeletep(constrExprp), constrExprp); } // Clean up nodes used only as clone templates (never inserted into tree) for (auto& bucket : buckets) { - VL_DO_DANGLING(bucket.weightExprp->deleteTree(), bucket.weightExprp); + VL_DO_DANGLING(pushDeletep(bucket.weightExprp), bucket.weightExprp); } - VL_DO_DANGLING(runningSump->deleteTree(), runningSump); + VL_DO_DANGLING(pushDeletep(runningSump), runningSump); // Last cumSum is unused (last bucket is unconditional default) - VL_DO_DANGLING(cumSums.back()->deleteTree(), cumSums.back()); + pushDeletep(cumSums.back()); } } @@ -3658,6 +3667,7 @@ class RandomizeVisitor final : public VNVisitor { void visit(AstClass* nodep) override { VL_RESTORER(m_modp); VL_RESTORER(m_randCaseNum); + VL_RESTORER(m_distNum); m_modp = nodep; m_randCaseNum = 0; m_distNum = 0; diff --git a/test_regress/t/t_constraint_dist_weight.v b/test_regress/t/t_constraint_dist_weight.v index 93d0b8214..8312d6650 100644 --- a/test_regress/t/t_constraint_dist_weight.v +++ b/test_regress/t/t_constraint_dist_weight.v @@ -25,6 +25,11 @@ class DistZeroWeight; constraint c { x dist { 8'd0 := 0, 8'd1 := 1, 8'd2 := 1 }; } endclass +class DistAllZeroWeight; + rand bit [7:0] x; + constraint c { x dist { 8'd0 := 0, 8'd1 := 0, 8'd2 := 0 }; } +endclass + class DistVarWeight; rand bit [7:0] x; int w1, w2; @@ -42,6 +47,7 @@ module t; DistScalar sc; DistRange rg; DistZeroWeight zw; + DistAllZeroWeight azw; DistVarWeight vw; DistVarWeightRange vwr; int count_high; @@ -58,7 +64,7 @@ module t; if (sc.x == 8'd255) count_high++; else `checkd(sc.x, 0); end - `check_range(count_high, 1200, 1800); + `check_range(count_high, total * 60 / 100, total * 90 / 100); // :/ range weights: expect ~75% in [10:19] rg = new; @@ -71,7 +77,7 @@ module t; `stop; end end - `check_range(count_range_high, 1200, 1800); + `check_range(count_range_high, total * 60 / 100, total * 90 / 100); // Zero weight: value 0 must never appear zw = new; @@ -84,6 +90,12 @@ module t; `check_range(zw.x, 1, 2); end + // All-zero weights: dist constraint is effectively unconstrained, randomize succeeds + azw = new; + repeat (20) begin + `checkd(azw.randomize(), 1); + end + // Variable := scalar weights: w1=1, w2=3 => expect ~75% for value 255 vw = new; vw.w1 = 1; @@ -94,7 +106,7 @@ module t; if (vw.x == 8'd255) count_high++; else `checkd(vw.x, 0); end - `check_range(count_high, 1200, 1800); + `check_range(count_high, total * 60 / 100, total * 90 / 100); // Variable :/ range weights: w1=1, w2=3 => expect ~75% in [10:19] vwr = new; @@ -109,7 +121,7 @@ module t; `stop; end end - `check_range(count_range_high, 1200, 1800); + `check_range(count_range_high, total * 60 / 100, total * 90 / 100); $write("*-* All Finished *-*\n"); $finish; diff --git a/test_regress/t/t_randomize.py b/test_regress/t/t_randomize.py index 88752b416..ab048b5e8 100755 --- a/test_regress/t/t_randomize.py +++ b/test_regress/t/t_randomize.py @@ -9,8 +9,13 @@ import vltest_bootstrap -test.scenarios('vlt') +test.scenarios('simulator') -test.lint() +if not test.have_solver: + test.skip("No constraint solver installed") + +test.compile() + +test.execute() test.passes() diff --git a/test_regress/t/t_randomize.v b/test_regress/t/t_randomize.v index 2c613ac94..0a0231de6 100644 --- a/test_regress/t/t_randomize.v +++ b/test_regress/t/t_randomize.v @@ -11,7 +11,7 @@ class Packet; rand bit if_4; rand bit iff_5_6; - /*rand*/ int array[2]; // 2,4,6 // TODO: add rand when supported + rand int array[2]; // 2,4,6 constraint empty {} @@ -58,7 +58,7 @@ module t; automatic int v; automatic bit if_4 = '0; - // TODO not testing constrained values + p = new; v = p.randomize(); if (v != 1) $stop; v = p.randomize() with {};