Fix static initialization order for packages with class hierarchies (#7324)
This commit is contained in:
parent
55e5f01758
commit
ba9a7aaa66
|
|
@ -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<size_t> 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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
@ -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
|
||||
Loading…
Reference in New Issue