From 71334a92342f19a5e172d0b95d7406feea1331a7 Mon Sep 17 00:00:00 2001 From: Yilou Wang Date: Tue, 14 Jul 2026 15:02:47 +0200 Subject: [PATCH] Gate dist by whether the expression value can still be drawn, fixing frozen-index weights --- src/V3Randomize.cpp | 25 +++++++++++++----- .../t/t_constraint_dist_randmode_frozen.v | 26 +++++++++++++++++++ 2 files changed, 45 insertions(+), 6 deletions(-) diff --git a/src/V3Randomize.cpp b/src/V3Randomize.cpp index c7deebfa5..41d963b60 100644 --- a/src/V3Randomize.cpp +++ b/src/V3Randomize.cpp @@ -4561,19 +4561,25 @@ class RandomizeVisitor final : public VNVisitor { return new AstConstraintExpr{fl, unionExprp}; } - // Gate the weighted chain on the rand_mode bits of the dist's variables, so a - // frozen variable requires only membership, not a freshly drawn bucket. + // Weighted chain while any value-source variable is active, membership once + // all are frozen. AstNode* gateFrozenDist(AstNode* chainp, AstDist* distp, const std::vector& buckets, AstVar* randModeVarp) { FileLine* const fl = distp->fileline(); AstNodeExpr* gatep = nullptr; std::unordered_set gatedVars; + // Handles are never re-pointed by randomize(), only read through. + std::unordered_set handleVars; + distp->exprp()->foreach([&](const AstMemberSel* mselp) { + mselp->fromp()->foreach( + [&](const AstNodeVarRef* hrefp) { handleVars.insert(hrefp->varp()); }); + }); const auto addModeGate = [&](AstNodeExpr* modeArrayp, uint32_t index) { AstCMethodHard* const atp = new AstCMethodHard{fl, modeArrayp, VCMethod::ARRAY_AT, new AstConst{fl, index}}; atp->dtypeSetUInt32(); if (gatep) { - gatep = new AstLogAnd{fl, gatep, atp}; + gatep = new AstLogOr{fl, gatep, atp}; gatep->dtypeSetBit(); } else { gatep = atp; @@ -4586,13 +4592,21 @@ class RandomizeVisitor final : public VNVisitor { return new AstVarRef{fl, VN_AS(smodep->user2p(), NodeModule), smodep, VAccess::READ}; }; // Each direct, static, or sub-object member reference carries a mode bit. + // A rand variable without a mode bit can never freeze, so no gate is needed. + bool alwaysActive = false; distp->exprp()->foreach([&](const AstNode* nodep) { const AstNodeVarRef* const refp = VN_CAST(nodep, NodeVarRef); const AstMemberSel* const mselp = VN_CAST(nodep, MemberSel); if (!refp && !mselp) return; AstVar* const varp = refp ? refp->varp() : mselp->varp(); + if (refp && handleVars.count(varp)) return; + if (!varp->rand().isRandomizable()) return; const RandomizeMode rmode = {.asInt = varp->user1()}; - if (!rmode.usesMode || !gatedVars.insert(varp).second) return; + if (!rmode.usesMode) { + alwaysActive = true; + return; + } + if (!gatedVars.insert(varp).second) return; if (varp->lifetime().isStatic()) { if (AstNodeExpr* const readp = staticModeRead(varp)) { addModeGate(readp, rmode.index); @@ -4603,7 +4617,6 @@ class RandomizeVisitor final : public VNVisitor { if (!memberModep) return; AstMemberSel* const modeSelp = new AstMemberSel{fl, mselp->fromp()->cloneTreePure(false), memberModep}; - modeSelp->dtypep(memberModep->dtypep()); addModeGate(modeSelp, rmode.index); } else if (randModeVarp) { addModeGate(new AstVarRef{fl, VN_AS(randModeVarp->user2p(), NodeModule), @@ -4611,7 +4624,7 @@ class RandomizeVisitor final : public VNVisitor { rmode.index); } }); - if (!gatep) return chainp; + if (alwaysActive || !gatep) return chainp; AstNodeExpr* unionExprp = nullptr; for (auto& bucket : buckets) { diff --git a/test_regress/t/t_constraint_dist_randmode_frozen.v b/test_regress/t/t_constraint_dist_randmode_frozen.v index 396327658..7971a2672 100644 --- a/test_regress/t/t_constraint_dist_randmode_frozen.v +++ b/test_regress/t/t_constraint_dist_randmode_frozen.v @@ -158,6 +158,18 @@ class RtWeight; } endclass +class IdxSel; + rand uint a[4]; + rand uint idx; + constraint ci {idx inside {[0 : 3]};} + constraint c { + a[idx] dist { + 10 := 9, + 20 := 1 + }; + } +endclass + module t; int ok; initial begin @@ -170,6 +182,7 @@ module t; RtWeight r; DistInIf di; DistInForeach df; + IdxSel ix; // Frozen in the low-weight bucket -> succeeds, value untouched. sv = new; @@ -441,6 +454,19 @@ module t; foreach (df.arr[j]) if (df.arr[j] != 10 && df.arr[j] != 20) `checkd(0, 1); end + // Frozen index into an active array: a[idx] is still freshly drawn. + ix = new; + ok = ix.randomize(); + `checkd(ok, 1); + ix.idx.rand_mode(0); + ix.idx = 2; + for (int i = 0; i < 16; ++i) begin + ok = ix.randomize(); + `checkd(ok, 1); + `checkd(ix.idx, 2); + if (ix.a[2] != 10 && ix.a[2] != 20) `checkd(ix.a[2], 10); + end + $write("*-* All Finished *-*\n"); $finish; end