diff --git a/src/V3Scope.cpp b/src/V3Scope.cpp index a18a2f9a2..37fddcda7 100644 --- a/src/V3Scope.cpp +++ b/src/V3Scope.cpp @@ -53,7 +53,8 @@ class ScopeVisitor final : public VNVisitor { AstCell* m_aboveCellp = nullptr; // Cell that instantiates this module AstScope* m_aboveScopep = nullptr; // Scope that instantiates this scope - std::unordered_map m_packageScopes; // Scopes for each package + std::unordered_map + m_classOrPackageScopes; // Scopes for each class or package VarScopeMap m_varScopes; // Varscopes created for each scope and var std::set> m_varRefScopes; // Varrefs-in-scopes needing fixup when done @@ -64,9 +65,12 @@ class ScopeVisitor final : public VNVisitor { for (const auto& itr : m_varRefScopes) { AstVarRef* const nodep = itr.first; AstScope* scopep = itr.second; - if (nodep->classOrPackagep()) { - const auto it2 = m_packageScopes.find(nodep->classOrPackagep()); - UASSERT_OBJ(it2 != m_packageScopes.end(), nodep, "Can't locate package scope"); + if (nodep->classOrPackagep() + && !VN_IS(nodep->classOrPackagep(), + Module)) { // Module scopes are not in m_classOrPackageScopes + const auto it2 = m_classOrPackageScopes.find(nodep->classOrPackagep()); + UASSERT_OBJ(it2 != m_classOrPackageScopes.end(), nodep, + "Can't locate class or package scope"); scopep = it2->second; } // Search up the scope hierarchy for the variable @@ -114,7 +118,7 @@ class ScopeVisitor final : public VNVisitor { (m_aboveCellp ? static_cast(m_aboveCellp) : static_cast(nodep)) ->fileline(), nodep, scopename, m_aboveScopep, m_aboveCellp}; - if (VN_IS(nodep, Package)) m_packageScopes.emplace(nodep, m_scopep); + if (VN_IS(nodep, Package)) m_classOrPackageScopes.emplace(nodep, m_scopep); // Get list of cells before we edit, to avoid excess visits (issue #6059) std::deque cells; @@ -180,7 +184,7 @@ class ScopeVisitor final : public VNVisitor { = (m_aboveCellp ? static_cast(m_aboveCellp) : static_cast(nodep)); m_scopep = new AstScope{abovep->fileline(), m_modp, scopename, m_aboveScopep, m_aboveCellp}; - m_packageScopes.emplace(nodep, m_scopep); + m_classOrPackageScopes.emplace(nodep, m_scopep); // Create scope for the current usage of this cell AstNode::user1ClearTree(); diff --git a/test_regress/t/t_randomize_module_var.py b/test_regress/t/t_randomize_module_var.py new file mode 100755 index 000000000..9aaadb3d0 --- /dev/null +++ b/test_regress/t/t_randomize_module_var.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2026 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_module_var.v b/test_regress/t/t_randomize_module_var.v new file mode 100755 index 000000000..e9c7e6858 --- /dev/null +++ b/test_regress/t/t_randomize_module_var.v @@ -0,0 +1,43 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2026 by PlanV GmbH. +// SPDX-License-Identifier: CC0-1.0 + + +module t_randomize_module_var; + int golden_queue[$]; + + class Cls; + rand bit deq; + constraint valid_enq { + if (golden_queue.size() == 0) {deq == 0;} + } + endclass + + Cls tr; + + initial begin + tr = new; + + // Test 1: Empty queue - deq must be 0 + if (tr.randomize() == 0) begin + $stop; + end + if (tr.deq != 0) begin + $display("Error: Expected deq=0 when queue is empty, got %0d", tr.deq); + $stop; + end + + // Test 2: Non-empty queue - deq can be 0 or 1 + golden_queue.push_back(42); + if (tr.randomize() == 0) begin + $stop; + end + // deq can be 0 or 1, both are valid + + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule