From 57979136006b084411fd53bcd20e45128a154fce Mon Sep 17 00:00:00 2001 From: Yilou Wang Date: Mon, 6 Jul 2026 20:14:24 +0200 Subject: [PATCH] Fix dist range bounds over rand variables failing randomization since #7802 --- src/V3Randomize.cpp | 61 ++++++++++++++++++++++++++++++--------------- 1 file changed, 41 insertions(+), 20 deletions(-) diff --git a/src/V3Randomize.cpp b/src/V3Randomize.cpp index 2786ea305..da5d32b90 100644 --- a/src/V3Randomize.cpp +++ b/src/V3Randomize.cpp @@ -4571,31 +4571,47 @@ class RandomizeVisitor final : public VNVisitor { continue; } + const auto boundRefsRandVarp = [](const AstNode* boundp) -> bool { + bool found = false; + boundp->foreach([&](const AstVarRef* vrefp) { + if (vrefp->varp()->rand().isRandomizable()) found = true; + }); + return found; + }; + + // (distExpr >= lo) && (distExpr <= hi); signed comparisons for signed vars + const auto rangeMembershipp = [&](const AstInsideRange* irp) -> AstNodeExpr* { + const bool isSigned = distp->exprp()->isSigned(); + AstNodeExpr* const distExprGtep = distp->exprp()->cloneTreePure(false); + AstNodeExpr* const distExprLtep = distp->exprp()->cloneTreePure(false); + distExprGtep->user1(true); + distExprLtep->user1(true); + AstNodeExpr* const gep + = isSigned + ? static_cast( + new AstGteS{fl, distExprGtep, irp->lhsp()->cloneTreePure(false)}) + : static_cast( + new AstGte{fl, distExprGtep, irp->lhsp()->cloneTreePure(false)}); + AstNodeExpr* const lep + = isSigned + ? static_cast( + new AstLteS{fl, distExprLtep, irp->rhsp()->cloneTreePure(false)}) + : static_cast( + new AstLte{fl, distExprLtep, irp->rhsp()->cloneTreePure(false)}); + gep->user1(true); + lep->user1(true); + AstNodeExpr* const andp = new AstLogAnd{fl, gep, lep}; + andp->user1(true); + return andp; + }; + // IEEE 1800-2023 18.5.3: values not in the distribution must never appear. // Build the union of all non-zero-weight ranges as a single hard ConstraintExpr AstNodeExpr* unionExprp = nullptr; for (const auto& bucket : buckets) { AstNodeExpr* memberp; if (const AstInsideRange* const irp = VN_CAST(bucket.rangep, InsideRange)) { - // (distExpr >= lo) && (distExpr <= hi); signed comparisons for signed vars - const bool isSigned = distp->exprp()->isSigned(); - AstNodeExpr* const distExprGtep = distp->exprp()->cloneTreePure(false); - AstNodeExpr* const distExprLtep = distp->exprp()->cloneTreePure(false); - distExprGtep->user1(true); - distExprLtep->user1(true); - AstNodeExpr* const gep - = isSigned ? static_cast(new AstGteS{ - fl, distExprGtep, irp->lhsp()->cloneTreePure(false)}) - : static_cast(new AstGte{ - fl, distExprGtep, irp->lhsp()->cloneTreePure(false)}); - AstNodeExpr* const lep - = isSigned ? static_cast(new AstLteS{ - fl, distExprLtep, irp->rhsp()->cloneTreePure(false)}) - : static_cast(new AstLte{ - fl, distExprLtep, irp->rhsp()->cloneTreePure(false)}); - gep->user1(true); - lep->user1(true); - memberp = new AstLogAnd{fl, gep, lep}; + memberp = rangeMembershipp(irp); } else { // distExpr == val AstNodeExpr* const distExprCopyp = distp->exprp()->cloneTreePure(false); @@ -4673,7 +4689,12 @@ class RandomizeVisitor final : public VNVisitor { AstNode* chainp = nullptr; for (int i = static_cast(buckets.size()) - 1; i >= 0; --i) { AstNodeExpr* constraintExprp; - if (const AstInsideRange* const irp = VN_CAST(buckets[i].rangep, InsideRange)) { + const AstInsideRange* const irp = VN_CAST(buckets[i].rangep, InsideRange); + if (irp && (boundRefsRandVarp(irp->lhsp()) || boundRefsRandVarp(irp->rhsp()))) { + // Bounds solved concurrently cannot pin a pre-solve value; softly + // prefer the symbolic range so the hard membership stays satisfiable + constraintExprp = rangeMembershipp(irp); + } else if (irp) { // Pick distExpr = lo + rand64() % (hi - lo + 1) for a uniform value in range AstNodeExpr* const distExprCopyp = distp->exprp()->cloneTreePure(false); distExprCopyp->user1(true);