diff --git a/src/V3Randomize.cpp b/src/V3Randomize.cpp index 95ae238d7..eeb270378 100644 --- a/src/V3Randomize.cpp +++ b/src/V3Randomize.cpp @@ -882,20 +882,49 @@ class ConstraintExprVisitor final : public VNVisitor { VNRelinker relinker; nodep->unlinkFrBack(&relinker); - AstNodeExpr* exprp = new AstSFormatF{nodep->fileline(), smtName, false, nullptr}; + AstNodeExpr* exprp; if (randMode.usesMode) { - AstNodeExpr* constFormatp = getConstFormat(nodep); - UASSERT_OBJ(m_randModeVarp, nodep, "No m_randModeVarp"); - AstCMethodHard* const atp = new AstCMethodHard{ - nodep->fileline(), - new AstVarRef{varp->fileline(), VN_AS(m_randModeVarp->user2p(), NodeModule), - m_randModeVarp, VAccess::READ}, - VCMethod::ARRAY_AT, new AstConst{nodep->fileline(), randMode.index}}; + // Use string literal to avoid double formatting + exprp = new AstCExpr{nodep->fileline(), "std::string(\"" + smtName + "\")", 1}; + exprp->dtypeSetString(); + + // Get const format, using membersel if available for correct width/value + AstNodeExpr* constFormatp + = membersel ? getConstFormat(membersel->cloneTree(false)) : getConstFormat(nodep); + + // Build randmode access: for membersel, access parent object's __Vrandmode + AstNodeExpr* randModeAccess; + if (membersel) { + AstNodeExpr* parentAccess = membersel->fromp()->cloneTree(false); + AstNodeModule* const varClassp = VN_AS(varp->user2p(), NodeModule); + AstVar* const effectiveRandModeVarp = VN_AS(varClassp->user2p(), Var); + if (effectiveRandModeVarp) { + AstMemberSel* randModeSel + = new AstMemberSel{varp->fileline(), parentAccess, effectiveRandModeVarp}; + randModeSel->dtypep(effectiveRandModeVarp->dtypep()); + randModeAccess = randModeSel; + } else { + UASSERT_OBJ(m_randModeVarp, nodep, "No m_randModeVarp"); + randModeAccess = new AstVarRef{varp->fileline(), + VN_AS(m_randModeVarp->user2p(), NodeModule), + m_randModeVarp, VAccess::READ}; + } + } else { + UASSERT_OBJ(m_randModeVarp, nodep, "No m_randModeVarp"); + randModeAccess + = new AstVarRef{varp->fileline(), VN_AS(m_randModeVarp->user2p(), NodeModule), + m_randModeVarp, VAccess::READ}; + } + + AstCMethodHard* const atp + = new AstCMethodHard{nodep->fileline(), randModeAccess, VCMethod::ARRAY_AT, + new AstConst{nodep->fileline(), randMode.index}}; atp->dtypeSetUInt32(); exprp = new AstCond{varp->fileline(), atp, exprp, constFormatp}; - } else if (!isGlobalConstrained) { - // Non-global constraints: delete nodep immediately - VL_DO_DANGLING(pushDeletep(nodep), nodep); + exprp->user1(true); // Mark as formatted + } else { + exprp = new AstSFormatF{nodep->fileline(), smtName, false, nullptr}; + if (!isGlobalConstrained) { VL_DO_DANGLING(pushDeletep(nodep), nodep); } } // else: Global constraints keep nodep alive for write_var processing relinker.relink(exprp); @@ -952,7 +981,9 @@ class ConstraintExprVisitor final : public VNVisitor { methodp->addPinsp(varnamep); methodp->addPinsp( new AstConst{varp->dtypep()->fileline(), AstConst::Unsized64{}, dimension}); - if (randMode.usesMode) { + // Don't pass randMode.index for global constraints with membersel + // because constraint object can't access nested object's randmode array + if (randMode.usesMode && !(isGlobalConstrained && membersel)) { methodp->addPinsp( new AstConst{varp->fileline(), AstConst::Unsized64{}, randMode.index}); } diff --git a/test_regress/t/t_constraint_global_randMode.py b/test_regress/t/t_constraint_global_randMode.py new file mode 100755 index 000000000..466368b3d --- /dev/null +++ b/test_regress/t/t_constraint_global_randMode.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 by Wilson Snyder. 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-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_global_randMode.v b/test_regress/t/t_constraint_global_randMode.v new file mode 100755 index 000000000..b6bde927b --- /dev/null +++ b/test_regress/t/t_constraint_global_randMode.v @@ -0,0 +1,35 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by PlanV GmbH. +// SPDX-License-Identifier: CC0-1.0 + +class RandomValue; + rand int value; + constraint small_int_c { + value < 10; + } + task disable_val(); + value.rand_mode(0); + endtask +endclass + +class Base; + rand RandomValue v = new; +endclass + +class Foo extends Base; +endclass + +module t; + initial begin + Foo d = new; + Base b = d; + b.v.disable_val(); + b.v.value = 11; + if (bit'(b.randomize())) $stop; + if (b.v.value != 11) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule