diff --git a/src/V3Sched.cpp b/src/V3Sched.cpp index 7795c51b5..6f713cce0 100644 --- a/src/V3Sched.cpp +++ b/src/V3Sched.cpp @@ -369,7 +369,35 @@ void orderSequentially(AstCFunc* funcp, const LogicByScope& lbs) { AstCFunc* createStatic(AstNetlist* netlistp, const LogicClasses& logicClasses) { AstCFunc* const funcp = util::makeTopFunction(netlistp, "_eval_static", /* slow: */ true); - orderSequentially(funcp, logicClasses.m_static); + + const LogicByScope& orig = logicClasses.m_static; + if (orig.size() <= 1) { + orderSequentially(funcp, orig); + return funcp; + } + + // Level-based module sorting can reorder packages so that an importing + // package runs before the imported one. Re-sort package entries by source + // file position to restore compilation order (IEEE 1800-2023 26.3). + std::vector indices(orig.size()); + for (size_t i = 0; i < orig.size(); ++i) indices[i] = i; + std::stable_sort(indices.begin(), indices.end(), [&](size_t a, size_t b) { + const AstNodeModule* const modA = orig[a].first->modp(); + const AstNodeModule* const modB = orig[b].first->modp(); + const bool isPkgA = VN_IS(modA, Package); + const bool isPkgB = VN_IS(modB, Package); + if (isPkgA != isPkgB) return isPkgA; // Packages before non-packages + if (isPkgA && isPkgB) { + // Sort packages by source file position (compilation order) + return modA->fileline()->operatorCompare(*modB->fileline()) < 0; + } + return false; // Both non-package: preserve original order + }); + LogicByScope sorted; + sorted.reserve(orig.size()); + for (const size_t i : indices) sorted.emplace_back(orig[i].first, orig[i].second); + + orderSequentially(funcp, sorted); return funcp; // Not splitting yet as it is not final } diff --git a/test_regress/t/t_static_init_pkg_order.py b/test_regress/t/t_static_init_pkg_order.py new file mode 100755 index 000000000..8a938befd --- /dev/null +++ b/test_regress/t/t_static_init_pkg_order.py @@ -0,0 +1,18 @@ +#!/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') + +test.compile() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_static_init_pkg_order.v b/test_regress/t/t_static_init_pkg_order.v new file mode 100644 index 000000000..a45bf0c41 --- /dev/null +++ b/test_regress/t/t_static_init_pkg_order.v @@ -0,0 +1,99 @@ +// 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); +// verilog_format: on + +// Test package static init ordering with cross-package side effects. +// Mirrors the UVM factory pattern (uvm_coreservice_t / uvm_init). + +package pkg_a; + typedef enum int { + STATE_UNINIT = 0, + STATE_INIT = 1, + STATE_DONE = 2 + } state_e; + + state_e g_state = STATE_UNINIT; + string deferred_q[$]; + + class service_t; + local static service_t inst; + + static function service_t get(); + if (inst == null) do_init(); + return inst; + endfunction + + static function void set(service_t s); + inst = s; + endfunction + endclass + + function automatic void do_init(); + service_t s; + if (g_state != STATE_UNINIT) return; + g_state = STATE_INIT; + foreach (deferred_q[i]) begin + // Process deferred registrations + end + deferred_q.delete(); + s = new(); + service_t::set(s); + g_state = STATE_DONE; + endfunction + + function void register_type(string name); + if (g_state == STATE_UNINIT) begin + deferred_q.push_back(name); + end + endfunction +endpackage + +package pkg_b; + import pkg_a::*; + + class component_c; + function new(string name); + service_t s; + s = pkg_a::service_t::get(); + endfunction + endclass + + component_c default_monitor = new("default_monitor"); +endpackage + +package pkg_c; + import pkg_a::*; + + class registry_common; + static bit m_initialized = deferred_init(); + + static function bit deferred_init(); + if (pkg_a::g_state == STATE_UNINIT) begin + pkg_a::register_type("test_class"); + end + return 1; + endfunction + endclass +endpackage + +module t; + import pkg_a::*; + import pkg_b::*; + import pkg_c::*; + + initial begin + service_t svc; + svc = pkg_a::service_t::get(); + `checkd(pkg_a::deferred_q.size(), 0); + `checkd(pkg_a::g_state, 32'd2); + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule