Fix constraint_mode()/rand_mode() in constructor being overwritten by init code (#7054)

This commit is contained in:
Yilou Wang 2026-02-11 18:32:08 +01:00 committed by GitHub
parent 84350859e0
commit 996a4b6e1a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 90 additions and 6 deletions

View File

@ -2366,12 +2366,28 @@ class RandomizeVisitor final : public VNVisitor {
= new AstCMethodHard{fl, new AstVarRef{fl, modeVarModp, modeVarp, VAccess::WRITE},
VCMethod::DYN_RESIZE, new AstConst{fl, modeCount}};
dynarrayNewp->dtypeSetVoid();
AstNodeFTask* const newp = VN_AS(m_memberMap.findMember(classp, "new"), NodeFTask);
UASSERT_OBJ(newp, classp, "No new() in class");
newp->addStmtsp(dynarrayNewp->makeStmt());
newp->addStmtsp(makeModeSetLoop(fl,
new AstVarRef{fl, modeVarModp, modeVarp, VAccess::WRITE},
new AstConst{fl, 1}, true));
AstNodeFTask* const ctorNewp = VN_AS(m_memberMap.findMember(classp, "new"), NodeFTask);
UASSERT_OBJ(ctorNewp, classp, "No new() in class");
// Build init chain: resize -> set-all-to-1 loop
AstNode* const initFirstp = dynarrayNewp->makeStmt();
initFirstp->addNext(
makeModeSetLoop(fl, new AstVarRef{fl, modeVarModp, modeVarp, VAccess::WRITE},
new AstConst{fl, 1}, true));
// Prepend init code before user statements in constructor body, but after
// var declarations and super.new(). This ensures that user's constraint_mode()
// or rand_mode() calls in the constructor execute after mode arrays are initialized.
// Pattern from V3LinkDot::addImplicitSuperNewCall.
for (AstNode* stmtp = ctorNewp->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
if (!VN_IS(stmtp, NodeStmt)) continue; // Skip var declarations
if (const AstStmtExpr* const sep = VN_CAST(stmtp, StmtExpr)) {
if (VN_IS(sep->exprp(), New)) continue; // Skip super.new()
}
// Found first user statement - insert init code before it
stmtp->addHereThisAsNext(initFirstp);
return;
}
// No user statements (empty constructor or only var decls/super.new)
ctorNewp->addStmtsp(initFirstp);
}
void makeStaticModeInit(AstVar* modeVarp, AstClass* classp, uint32_t modeCount) {
// For static constraint mode, we need lazy initialization since it's shared across

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,47 @@
// 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
// Test constraint_mode() called inside class constructor (function new)
class ConstraintModeInCtor;
rand bit [7:0] value;
constraint low_range_c { value < 50; };
constraint high_range_c { value >= 50; value < 200; };
function new;
// Disable high_range_c in constructor - only low_range_c should be active
high_range_c.constraint_mode(0);
endfunction
endclass
module t;
initial begin
automatic ConstraintModeInCtor obj = new;
automatic int i;
// Test 1: constraint_mode(0) in constructor should disable constraint
for (i = 0; i < 20; i++) begin
void'(obj.randomize());
if (obj.value >= 50) $stop;
end
// Test 2: Query constraint_mode state set in constructor
if (obj.low_range_c.constraint_mode != 1) $stop;
if (obj.high_range_c.constraint_mode != 0) $stop;
// Test 3: Switch constraints at runtime
obj.low_range_c.constraint_mode(0);
obj.high_range_c.constraint_mode(1);
for (i = 0; i < 20; i++) begin
void'(obj.randomize());
if (obj.value < 50 || obj.value >= 200) $stop;
end
$write("*-* All Finished *-*\n");
$finish;
end
endmodule