diff --git a/src/V3Randomize.cpp b/src/V3Randomize.cpp index 3c80fb278..d96a7054c 100644 --- a/src/V3Randomize.cpp +++ b/src/V3Randomize.cpp @@ -1672,10 +1672,31 @@ class ConstraintExprVisitor final : public VNVisitor { if (editFormat(nodep)) return; FileLine* const fl = nodep->fileline(); VNRelinker handle; - AstNodeExpr* const indexp - = new AstSFormatF{fl, "#x%8x", false, nodep->bitp()->unlinkFrBack(&handle)}; - handle.relink(indexp); - editSMT(nodep, nodep->fromp(), indexp); + // Check if index actually references a rand variable (not just user1, + // which can be over-marked in sum/with expansion contexts) + bool indexIsRand = false; + nodep->bitp()->foreach([&](const AstNodeVarRef* vrefp) { + if (vrefp->varp()->rand().isRandomizable()) indexIsRand = true; + }); + if (indexIsRand) { + // Index depends on rand variable -- keep as SMT symbol. + // Array index sort is 32-bit, so zero-extend narrower indices. + AstNodeExpr* indexp = nodep->bitp()->unlinkFrBack(&handle); + if (indexp->width() < 32) { + AstExtend* const extendp = new AstExtend{fl, indexp, 32}; + extendp->dtypeSetLogicSized(32, VSigning::UNSIGNED); + extendp->user1(true); + indexp = extendp; + } + handle.relink(indexp); + editSMT(nodep, nodep->fromp(), indexp); + } else { + // Index is constant or non-rand -- format as hex literal + AstNodeExpr* const indexp + = new AstSFormatF{fl, "#x%8x", false, nodep->bitp()->unlinkFrBack(&handle)}; + handle.relink(indexp); + editSMT(nodep, nodep->fromp(), indexp); + } } void visit(AstMemberSel* nodep) override { // Check if rootVar is globalConstrained diff --git a/test_regress/t/t_constraint_rand_array_index.py b/test_regress/t/t_constraint_rand_array_index.py new file mode 100755 index 000000000..db1adb3f9 --- /dev/null +++ b/test_regress/t/t_constraint_rand_array_index.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_rand_array_index.v b/test_regress/t/t_constraint_rand_array_index.v new file mode 100644 index 000000000..4bd8c2a07 --- /dev/null +++ b/test_regress/t/t_constraint_rand_array_index.v @@ -0,0 +1,41 @@ +// 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 rand variables used as array indices in constraints are treated +// as symbolic in the solver, not evaluated at C++ constraint setup time. + +class RandArrayIndexTest; + rand bit [1:0] idx; + rand bit [7:0] data [4]; + rand bit [7:0] selected_value; + + constraint solve_order { solve idx before selected_value; } + constraint value_match { selected_value == data[idx]; } + constraint data_values { + foreach (data[i]) { + data[i] inside {[8'd10:8'd50]}; + } + } +endclass + +module t; + initial begin + static RandArrayIndexTest obj = new(); + repeat (20) begin + `checkd(obj.randomize(), 1) + `checkd(obj.selected_value, obj.data[obj.idx]) + `check_range(obj.selected_value, 8'd10, 8'd50) + end + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule