diff --git a/src/V3Randomize.cpp b/src/V3Randomize.cpp index 24dd90822..c74f326f2 100644 --- a/src/V3Randomize.cpp +++ b/src/V3Randomize.cpp @@ -170,13 +170,23 @@ class RandomizeMarkVisitor final : public VNVisitor { if (VN_IS(pinp, With)) continue; const AstArg* const argp = VN_CAST(pinp, Arg); if (!argp) continue; - const AstNodeExpr* const exprp = argp->exprp(); - if (const AstNodeVarRef* const varrefp = VN_CAST(exprp, NodeVarRef)) { - if (varrefp->varp() == varp) return true; - } else if (const AstMemberSel* const memberselp = VN_CAST(exprp, MemberSel)) { - if (memberselp->varp() == varp) return true; - } else if (const AstArraySel* const arrselp = VN_CAST(exprp, ArraySel)) { - if (VN_AS(arrselp->fromp(), VarRef)->varp() == varp) return true; + const AstNodeExpr* exprp = argp->exprp(); + // Traverse through expression to find the base variable + while (exprp) { + if (const AstNodeVarRef* const varrefp = VN_CAST(exprp, NodeVarRef)) { + if (varrefp->varp() == varp) return true; + break; + } + if (const AstMemberSel* const memberselp = VN_CAST(exprp, MemberSel)) { + if (memberselp->varp() == varp) return true; + exprp = memberselp->fromp(); + } else if (const AstArraySel* const arrselp = VN_CAST(exprp, ArraySel)) { + exprp = arrselp->fromp(); + } else if (const AstStructSel* const strselp = VN_CAST(exprp, StructSel)) { + exprp = strselp->fromp(); + } else { + break; + } } } return false; @@ -331,6 +341,52 @@ class RandomizeMarkVisitor final : public VNVisitor { } } + // Get randomized variables from (std::)randomize() arguments + // and mark AstNodeModule nodes in which they are defined + void handleRandomizeArgument(AstNodeExpr* exprp, AstVar* const varp, const bool stdRandomize) { + // IEEE 1800-2023 18.11: "Arguments are limited to the names of properties + // of the calling object; expressions are not allowed." + // However, for compatibility with other simulators, we support complex + // expressions like obj.member[idx].field in inline randomize(). + while (exprp) { + AstVar* randVarp = nullptr; + if (AstMemberSel* const memberSelp = VN_CAST(exprp, MemberSel)) { + randVarp = memberSelp->varp(); + exprp = memberSelp->fromp(); + } else if (AstArraySel* const arraySelp = VN_CAST(exprp, ArraySel)) { + exprp = arraySelp->fromp(); + continue; // Skip ArraySel, continue traversing + } else if (AstStructSel* const structSelp = VN_CAST(exprp, StructSel)) { + exprp = structSelp->fromp(); + continue; // Skip StructSel, continue traversing + } else if (AstVarRef* const varrefp = VN_CAST(exprp, VarRef)) { + randVarp = varrefp->varp(); + varrefp->user1(true); + varrefp->access(VAccess::READWRITE); + exprp = nullptr; + } else { + // All invalid and unsupported expressions should be caught in V3Width + exprp->v3fatalSrc("Unexpected expression type in randomize() argument"); + } + UASSERT_OBJ(randVarp, exprp, "No rand variable found"); + if (randVarp == varp) return; + AstNode* backp = randVarp; + while (backp && !VN_IS(backp, NodeModule)) backp = backp->backp(); + if (stdRandomize) { + UASSERT_OBJ(backp, randVarp, "No class or module found for rand variable"); + backp->user1(IS_STD_RANDOMIZED); + } else { + // Inline randomized then + UASSERT_OBJ(VN_IS(backp, Class), randVarp, + "No class found for inline randomized variable"); + RandomizeMode randMode = {}; + randMode.usesMode = true; + randVarp->user1(randMode.asInt); + backp->user1(IS_RANDOMIZED_INLINE); + } + } + } + // VISITORS void visit(AstClass* nodep) override { VL_RESTORER(m_classp); @@ -534,33 +590,7 @@ class RandomizeMarkVisitor final : public VNVisitor { for (AstNode* pinp = nodep->pinsp(); pinp; pinp = pinp->nextp()) { AstArg* const argp = VN_CAST(pinp, Arg); if (!argp) continue; - AstNodeExpr* exprp = argp->exprp(); - while (exprp) { - AstVar* randVarp = nullptr; - AstVarRef* varrefp = nullptr; - if (AstMemberSel* const memberSelp = VN_CAST(exprp, MemberSel)) { - randVarp = memberSelp->varp(); - exprp = memberSelp->fromp(); - } else if ((varrefp = VN_CAST(exprp, VarRef))) { - randVarp = varrefp->varp(); - varrefp->user1(true); - exprp = nullptr; - } else { - varrefp = VN_AS(VN_CAST(exprp, ArraySel)->fromp(), VarRef); - randVarp = varrefp->varp(); - varrefp->user1(true); - varrefp->access(VAccess::READWRITE); - exprp = nullptr; - } - UASSERT_OBJ(randVarp, nodep, "No rand variable found"); - AstNode* backp = randVarp; - while (backp && (!VN_IS(backp, Class) && !VN_IS(backp, NodeModule))) { - backp = backp->backp(); - } - UASSERT_OBJ(VN_IS(backp, NodeModule), randVarp, - "No class or module found for rand variable"); - backp->user1(IS_STD_RANDOMIZED); - } + handleRandomizeArgument(argp->exprp(), nullptr, true); } return; } @@ -568,7 +598,6 @@ class RandomizeMarkVisitor final : public VNVisitor { AstArg* const argp = VN_CAST(pinp, Arg); if (!argp) continue; classp->user1(IS_RANDOMIZED_INLINE); - AstNodeExpr* exprp = argp->exprp(); AstVar* fromVarp = nullptr; // If nodep is a method call, this is its receiver if (AstMethodCall* methodCallp = VN_CAST(nodep, MethodCall)) { if (AstMemberSel* const memberSelp = VN_CAST(methodCallp->fromp(), MemberSel)) { @@ -578,25 +607,7 @@ class RandomizeMarkVisitor final : public VNVisitor { fromVarp = varrefp->varp(); } } - while (exprp) { - AstVar* randVarp = nullptr; - if (AstMemberSel* const memberSelp = VN_CAST(exprp, MemberSel)) { - randVarp = memberSelp->varp(); - exprp = memberSelp->fromp(); - } else { - AstVarRef* const varrefp = VN_AS(exprp, VarRef); - randVarp = varrefp->varp(); - exprp = nullptr; - } - if (randVarp == fromVarp) break; - UASSERT_OBJ(randVarp, nodep, "No rand variable found"); - AstNode* backp = randVarp; - while (backp && !VN_IS(backp, Class)) backp = backp->backp(); - RandomizeMode randMode = {}; - randMode.usesMode = true; - randVarp->user1(randMode.asInt); - VN_AS(backp, Class)->user1(IS_RANDOMIZED_INLINE); - } + handleRandomizeArgument(argp->exprp(), fromVarp, false); } } void visit(AstConstraintExpr* nodep) override { diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 3343b5c70..888c4bea7 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -6781,7 +6781,7 @@ class WidthVisitor final : public VNVisitor { // IEEE 1800-2023 (18.12) limits args to current scope variables. // Verilator accepts this for compatibility with other simulators. continue; - } else if (VN_IS(exprp, VarRef) || VN_IS(exprp, ArraySel)) { + } else if (VN_IS(exprp, VarRef) || VN_IS(exprp, ArraySel) || VN_IS(exprp, StructSel)) { // Valid usage continue; } else { diff --git a/test_regress/t/t_randomize_arraysel_membersel.py b/test_regress/t/t_randomize_arraysel_membersel.py new file mode 100755 index 000000000..466368b3d --- /dev/null +++ b/test_regress/t/t_randomize_arraysel_membersel.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_randomize_arraysel_membersel.v b/test_regress/t/t_randomize_arraysel_membersel.v new file mode 100644 index 000000000..02d93dbbe --- /dev/null +++ b/test_regress/t/t_randomize_arraysel_membersel.v @@ -0,0 +1,101 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +typedef struct {int x;} struct_t; + +class inner_class; + logic [3:0] c; + logic [3:0] d[2]; + struct_t s; +endclass + +class test_class; + logic [3:0] member[5]; + logic [3:0] member_2d[3][4]; + inner_class b; +endclass + +module t; + initial begin + test_class example; + example = new; + example.b = new; + + // Simple array element access + repeat (5) begin + if (std::randomize(example.member[1]) with {example.member[1] inside {[0 : 3]};} == 0) $stop; + if (example.member[1] > 3) $stop; + end + + // Different array indices + repeat (5) begin + if (std::randomize(example.member[0]) with {example.member[0] inside {[5 : 7]};} == 0) $stop; + if (example.member[0] < 5 || example.member[0] > 7) $stop; + end + + // Last element + repeat (5) begin + if (std::randomize(example.member[4]) with {example.member[4] inside {[10 : 15]};} == 0) + $stop; + if (example.member[4] < 10 || example.member[4] > 15) $stop; + end + + // 2D array access + repeat (5) begin + if (std::randomize( + example.member_2d[1][2] + ) with { + example.member_2d[1][2] inside {[8 : 12]}; + } == 0) + $stop; + if (example.member_2d[1][2] < 8 || example.member_2d[1][2] > 12) $stop; + end + + // 2D array different indices + repeat (5) begin + if (std::randomize( + example.member_2d[0][0] + ) with { + example.member_2d[0][0] inside {[1 : 4]}; + } == 0) + $stop; + if (example.member_2d[0][0] < 1 || example.member_2d[0][0] > 4) $stop; + end + + // Nested object: obj.b.c + repeat (5) begin + if (std::randomize(example.b.c) with {example.b.c inside {[5 : 9]};} == 0) $stop; + if (example.b.c < 5 || example.b.c > 9) $stop; + end + + // Nested object with array: obj.b.d[0] + repeat (5) begin + if (std::randomize(example.b.d[0]) with {example.b.d[0] inside {[11 : 14]};} == 0) $stop; + if (example.b.d[0] < 11 || example.b.d[0] > 14) $stop; + end + + // Nested object with array: obj.b.d[1] (different index) + repeat (5) begin + if (std::randomize(example.b.d[1]) with {example.b.d[1] inside {[2 : 6]};} == 0) $stop; + if (example.b.d[1] < 2 || example.b.d[1] > 6) $stop; + end + + // Nested object with struct: obj.b.s.x + repeat (5) begin + if (std::randomize(example.b.s.x) with {example.b.s.x inside {[7 : 9]};} == 0) $stop; + if (example.b.s.x < 7 || example.b.s.x > 9) $stop; + end + + // Inline randomization of object with array + repeat (5) begin + if (example.randomize(b) with {b.d[1] inside {[2 : 6]};} == 0) $stop; + if (example.b.d[1] < 2 || example.b.d[1] > 6) $stop; + end + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule