fix ci failing tests

This commit is contained in:
Yilou Wang 2026-03-01 17:53:04 +01:00
parent cf556aa4ab
commit 2acc674a7a
5 changed files with 15 additions and 48 deletions

View File

@ -3479,10 +3479,7 @@ class RandomizeVisitor final : public VNVisitor {
}
}
// Lower dist constraints into weighted bucket selection + AstConstraintIf chains.
// Called for each constraint before ConstraintExprVisitor.
// taskp: constraint setup task (bucket var declarations + assignments go here)
// constrItemsp: first item in the constraint item list
// Replace AstDist with weighted bucket selection via AstConstraintIf chain
void lowerDistConstraints(AstTask* taskp, AstNode* constrItemsp) {
for (AstNode *nextip, *itemp = constrItemsp; itemp; itemp = nextip) {
nextip = itemp->nextp();
@ -3493,9 +3490,8 @@ class RandomizeVisitor final : public VNVisitor {
FileLine* const fl = distp->fileline();
// Collect buckets and compute effective weights
struct BucketInfo final {
AstNodeExpr* rangep; // Points into distItem (will be cloned, not unlinked)
AstNodeExpr* rangep;
uint64_t effectiveWeight;
};
std::vector<BucketInfo> buckets;
@ -3504,21 +3500,20 @@ class RandomizeVisitor final : public VNVisitor {
for (AstDistItem* ditemp = distp->itemsp(); ditemp;
ditemp = VN_AS(ditemp->nextp(), DistItem)) {
const AstConst* const weightp = VN_CAST(ditemp->weightp(), Const);
if (!weightp) continue; // Non-const weight: skip
if (!weightp) continue;
const uint64_t w = weightp->toUQuad();
if (w == 0) continue; // weight=0 means never
if (w == 0) continue;
uint64_t effectiveW = w;
if (!ditemp->isWhole()) { // := weight is per-value; multiply by range size
// := is per-value weight, so multiply by range size
if (!ditemp->isWhole()) {
if (const AstInsideRange* const irp = VN_CAST(ditemp->rangep(), InsideRange)) {
const AstConst* const lop = VN_CAST(irp->lhsp(), Const);
const AstConst* const hip = VN_CAST(irp->rhsp(), Const);
if (lop && hip && hip->toUQuad() >= lop->toUQuad())
effectiveW = w * (hip->toUQuad() - lop->toUQuad() + 1);
}
// scalar: effectiveW stays w
}
// :/ weight: effectiveW stays w (weight for whole range, not per-value)
buckets.push_back({ditemp->rangep(), effectiveW});
totalWeight += effectiveW;
@ -3526,7 +3521,6 @@ class RandomizeVisitor final : public VNVisitor {
if (buckets.empty() || totalWeight == 0) continue;
// Create bucket selection variable: uint64_t __Vdist_bucketN
const std::string bucketName = "__Vdist_bucket" + cvtToStr(m_distNum++);
AstVar* const bucketVarp
= new AstVar{fl, VVarType::BLOCKTEMP, bucketName, taskp->findUInt64DType()};
@ -3535,8 +3529,7 @@ class RandomizeVisitor final : public VNVisitor {
bucketVarp->funcLocal(true);
taskp->addStmtsp(bucketVarp);
// Assign: bucketVar = (rand64() % totalWeight) + 1
// +1 ensures that weight=0 items are never selected
// bucketVar = (rand64() % totalWeight) + 1
AstNodeExpr* randp = new AstRand{fl, nullptr, false};
randp->dtypeSetUInt64();
taskp->addStmtsp(new AstAssign{
@ -3545,10 +3538,7 @@ class RandomizeVisitor final : public VNVisitor {
new AstModDiv{fl, randp,
new AstConst{fl, AstConst::Unsized64{}, totalWeight}}}});
// Build AstConstraintIf chain (last-to-first so nesting is natural):
// if (bucket <= w0) { exprp == range0 }
// else if (bucket <= w0+w1) { exprp in range1 }
// ...
// Build if/else chain keyed on cumulative weights
AstNode* chainp = nullptr;
uint64_t cumWeight = totalWeight;
@ -3556,10 +3546,8 @@ class RandomizeVisitor final : public VNVisitor {
cumWeight -= buckets[i].effectiveWeight;
const uint64_t thisCumWeight = cumWeight + buckets[i].effectiveWeight;
// Build range constraint expression (user1=true for rand-dependent nodes)
AstNodeExpr* constraintExprp;
if (const AstInsideRange* const irp = VN_CAST(buckets[i].rangep, InsideRange)) {
// Range bucket: exprp >= lo && exprp <= hi
AstNodeExpr* const exprCopy1p = distp->exprp()->cloneTreePure(false);
exprCopy1p->user1(true);
AstNodeExpr* const exprCopy2p = distp->exprp()->cloneTreePure(false);
@ -3573,7 +3561,6 @@ class RandomizeVisitor final : public VNVisitor {
constraintExprp = new AstLogAnd{fl, gtep, ltep};
constraintExprp->user1(true);
} else {
// Scalar bucket: exprp == value
AstNodeExpr* const exprCopyp = distp->exprp()->cloneTreePure(false);
exprCopyp->user1(true);
constraintExprp
@ -3584,10 +3571,8 @@ class RandomizeVisitor final : public VNVisitor {
AstConstraintExpr* const thenp = new AstConstraintExpr{fl, constraintExprp};
if (!chainp) {
// Last bucket: no condition needed (always selected when reached)
chainp = thenp;
} else {
// Bucket condition: bucketVar <= thisCumWeight (NOT rand-dependent)
AstNodeExpr* const condp
= new AstLte{fl, new AstVarRef{fl, bucketVarp, VAccess::READ},
new AstConst{fl, AstConst::Unsized64{}, thisCumWeight}};
@ -3596,7 +3581,6 @@ class RandomizeVisitor final : public VNVisitor {
}
if (chainp) {
// Replace AstConstraintExpr(AstDist) with the weighted-bucket chain
constrExprp->replaceWith(chainp);
VL_DO_DANGLING(constrExprp->deleteTree(), constrExprp);
}
@ -3669,7 +3653,6 @@ class RandomizeVisitor final : public VNVisitor {
}
if (constrp->itemsp()) expandUniqueElementList(constrp->itemsp());
// Lower dist constraints into weighted bucket selection before SMT encoding
if (constrp->itemsp()) lowerDistConstraints(taskp, constrp->itemsp());
ConstraintExprVisitor{classp, m_memberMap, constrp->itemsp(), nullptr,
genp, randModeVarp, m_writtenVars};

View File

@ -3088,9 +3088,7 @@ class WidthVisitor final : public VNVisitor {
iterateCheck(nodep, "Dist Item", itemp, CONTEXT_DET, FINAL, subDTypep, EXTEND_EXP);
}
// Inside a constraint: keep AstDist alive for V3Randomize to do weighted bucket
// selection, but only if all items have const weights and simple ranges (scalar or
// InsideRange). Array/queue-based dist items are not supported by lowerDistConstraints.
// Keep AstDist for V3Randomize if all items have const weights and simple ranges
if (m_constraintp) {
bool canLower = true;
for (const AstDistItem* itemp = nodep->itemsp(); itemp;
@ -3104,7 +3102,7 @@ class WidthVisitor final : public VNVisitor {
if (canLower) return;
}
// Outside constraint: lower to inside expressions (ignores weights - imperfect)
// Outside constraint or complex items: lower to inside (ignores weights)
nodep->v3warn(CONSTRAINTIGN, "Constraint expression ignored (imperfect distribution)");
AstNodeExpr* newp = nullptr;
for (AstDistItem* itemp = nodep->itemsp(); itemp;

View File

@ -10,24 +10,18 @@
`define check_range(gotv,minv,maxv) do if ((gotv) < (minv) || (gotv) > (maxv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d-%0d\n", `__FILE__,`__LINE__, (gotv), (minv), (maxv)); `stop; end while(0);
// verilog_format: on
// Test that dist constraint weights (:= and :/) produce correct distributions.
// IEEE 1800-2017 18.5.4
class DistScalar;
rand bit [7:0] x;
// := weight is per-value: 0 has weight 1, 255 has weight 3 => ~75% should be 255
constraint c { x dist { 8'd0 := 1, 8'd255 := 3 }; }
endclass
class DistRange;
rand bit [7:0] x;
// :/ weight is per-range: [0:9] has total weight 1, [10:19] has total weight 3
constraint c { x dist { [8'd0:8'd9] :/ 1, [8'd10:8'd19] :/ 3 }; }
endclass
class DistZeroWeight;
rand bit [7:0] x;
// Weight 0 means never selected
constraint c { x dist { 8'd0 := 0, 8'd1 := 1, 8'd2 := 1 }; }
endclass
@ -42,18 +36,17 @@ module t;
total = 2000;
// Test 1: := scalar weights (expect ~75% for value 255)
// := scalar weights: expect ~75% for value 255
sc = new;
count_high = 0;
repeat (total) begin
`checkd(sc.randomize(), 1);
if (sc.x == 8'd255) count_high++;
else `checkd(sc.x, 0); // Only 0 or 255 should appear
else `checkd(sc.x, 0);
end
// 75% of 2000 = 1500, allow wide range for statistical test
`check_range(count_high, 1200, 1800);
// Test 2: :/ range weights (expect ~75% in [10:19])
// :/ range weights: expect ~75% in [10:19]
rg = new;
count_range_high = 0;
repeat (total) begin
@ -66,7 +59,7 @@ module t;
end
`check_range(count_range_high, 1200, 1800);
// Test 3: Zero weight exclusion (value 0 should never appear)
// Zero weight: value 0 must never appear
zw = new;
repeat (total) begin
`checkd(zw.randomize(), 1);

View File

@ -1,7 +0,0 @@
%Warning-CONSTRAINTIGN: t/t_randomize.v:43:23: Constraint expression ignored (imperfect distribution)
: ... note: In instance 't'
43 | constraint order { solve length before header; }
| ^~~~~
... 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.
%Error: Exiting due to

View File

@ -11,6 +11,6 @@ import vltest_bootstrap
test.scenarios('vlt')
test.lint(fails=True, expect_filename=test.golden_filename)
test.lint()
test.passes()