From 996a4b6e1a5afcd89419810b5c2c8d6904bad750 Mon Sep 17 00:00:00 2001 From: Yilou Wang Date: Wed, 11 Feb 2026 18:32:08 +0100 Subject: [PATCH] Fix constraint_mode()/rand_mode() in constructor being overwritten by init code (#7054) --- src/V3Randomize.cpp | 28 +++++++++++--- test_regress/t/t_constraint_mode_ctor.py | 21 +++++++++++ test_regress/t/t_constraint_mode_ctor.v | 47 ++++++++++++++++++++++++ 3 files changed, 90 insertions(+), 6 deletions(-) create mode 100755 test_regress/t/t_constraint_mode_ctor.py create mode 100644 test_regress/t/t_constraint_mode_ctor.v diff --git a/src/V3Randomize.cpp b/src/V3Randomize.cpp index b72b7c3ed..22730bdb8 100644 --- a/src/V3Randomize.cpp +++ b/src/V3Randomize.cpp @@ -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 diff --git a/test_regress/t/t_constraint_mode_ctor.py b/test_regress/t/t_constraint_mode_ctor.py new file mode 100755 index 000000000..db1adb3f9 --- /dev/null +++ b/test_regress/t/t_constraint_mode_ctor.py @@ -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() diff --git a/test_regress/t/t_constraint_mode_ctor.v b/test_regress/t/t_constraint_mode_ctor.v new file mode 100644 index 000000000..67d9cf863 --- /dev/null +++ b/test_regress/t/t_constraint_mode_ctor.v @@ -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