test pass, and reformat

This commit is contained in:
Yilou Wang 2026-03-01 15:26:15 +01:00
parent da3d1d9584
commit dce9d2056c
4 changed files with 128 additions and 13 deletions

View File

@ -3360,8 +3360,7 @@ class RandomizeVisitor final : public VNVisitor {
uint64_t effectiveW = w;
if (!ditemp->isWhole()) { // := weight is per-value; multiply by range size
if (const AstInsideRange* const irp
= VN_CAST(ditemp->rangep(), InsideRange)) {
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())
@ -3392,10 +3391,9 @@ class RandomizeVisitor final : public VNVisitor {
randp->dtypeSetUInt64();
taskp->addStmtsp(new AstAssign{
fl, new AstVarRef{fl, bucketVarp, VAccess::WRITE},
new AstAdd{
fl, new AstConst{fl, AstConst::Unsized64{}, 1},
new AstModDiv{fl, randp,
new AstConst{fl, AstConst::Unsized64{}, totalWeight}}}});
new AstAdd{fl, new AstConst{fl, AstConst::Unsized64{}, 1},
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 }
@ -3428,8 +3426,8 @@ class RandomizeVisitor final : public VNVisitor {
// Scalar bucket: exprp == value
AstNodeExpr* const exprCopyp = distp->exprp()->cloneTreePure(false);
exprCopyp->user1(true);
constraintExprp = new AstEq{fl, exprCopyp,
buckets[i].rangep->cloneTreePure(false)};
constraintExprp
= new AstEq{fl, exprCopyp, buckets[i].rangep->cloneTreePure(false)};
constraintExprp->user1(true);
}
@ -3440,9 +3438,9 @@ class RandomizeVisitor final : public VNVisitor {
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}};
AstNodeExpr* const condp
= new AstLte{fl, new AstVarRef{fl, bucketVarp, VAccess::READ},
new AstConst{fl, AstConst::Unsized64{}, thisCumWeight}};
chainp = new AstConstraintIf{fl, condp, thenp, chainp};
}
}

View File

@ -3088,8 +3088,21 @@ 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
if (m_constraintp) return;
// 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.
if (m_constraintp) {
bool canLower = true;
for (const AstDistItem* itemp = nodep->itemsp(); itemp;
itemp = VN_AS(itemp->nextp(), DistItem)) {
if (!VN_IS(itemp->weightp(), Const)
|| (!VN_IS(itemp->rangep(), Const) && !VN_IS(itemp->rangep(), InsideRange))) {
canLower = false;
break;
}
}
if (canLower) return;
}
// Outside constraint: lower to inside expressions (ignores weights - imperfect)
nodep->v3warn(CONSTRAINTIGN, "Constraint expression ignored (imperfect distribution)");

View File

@ -0,0 +1,21 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# 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-FileCopyrightText: 2026 Wilson Snyder
# 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()
test.execute()
test.passes()

View File

@ -0,0 +1,83 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 PlanV GmbH
// SPDX-License-Identifier: CC0-1.0
// verilog_format: off
`define stop $stop
`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
`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
module t;
initial begin
DistScalar sc;
DistRange rg;
DistZeroWeight zw;
int count_high;
int count_range_high;
int total;
total = 2000;
// Test 1: := 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
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])
rg = new;
count_range_high = 0;
repeat (total) begin
`checkd(rg.randomize(), 1);
if (rg.x >= 8'd10 && rg.x <= 8'd19) count_range_high++;
else if (rg.x > 8'd9) begin
$write("%%Error: x=%0d outside valid range [0:19]\n", rg.x);
`stop;
end
end
`check_range(count_range_high, 1200, 1800);
// Test 3: Zero weight exclusion (value 0 should never appear)
zw = new;
repeat (total) begin
`checkd(zw.randomize(), 1);
if (zw.x == 8'd0) begin
$write("%%Error: zero-weight value 0 was selected\n");
`stop;
end
`check_range(zw.x, 1, 2);
end
$write("*-* All Finished *-*\n");
$finish;
end
endmodule