fix typedefs
This commit is contained in:
parent
e0c55cc296
commit
f18aaeea83
|
|
@ -1288,7 +1288,19 @@ class ParamProcessor final {
|
|||
// Param/lparam member: substitute its constant value so the caller's constify can
|
||||
// succeed.
|
||||
if (varp->isParam() && varp->valuep()) {
|
||||
if (!VN_IS(varp->valuep(), Const)) V3Const::constifyParamsEdit(varp);
|
||||
if (!VN_IS(varp->valuep(), Const)) {
|
||||
// If the value contains nested class::member Dots (e.g. this
|
||||
// member's expression references another paramed class), resolve
|
||||
// those first so constify can fold the whole chain.
|
||||
std::vector<AstDot*> nestedDots;
|
||||
varp->valuep()->foreach([&](AstDot* dp) {
|
||||
if (VN_IS(dp->lhsp(), ClassOrPackageRef)) nestedDots.push_back(dp);
|
||||
});
|
||||
for (auto it = nestedDots.rbegin(); it != nestedDots.rend(); ++it) {
|
||||
resolveDotToTypedef(*it);
|
||||
}
|
||||
V3Const::constifyParamsEdit(varp);
|
||||
}
|
||||
if (AstConst* const constp = VN_CAST(varp->valuep(), Const)) {
|
||||
dotp->replaceWith(constp->cloneTree(false));
|
||||
VL_DO_DANGLING(dotp->deleteTree(), dotp);
|
||||
|
|
@ -2182,13 +2194,21 @@ public:
|
|||
// VarRefs to all leaf dependencies. Then resolve these to typedefs.
|
||||
void resolveDeferredDotsReachableFrom(AstNode* rootp, AstNodeModule* modp) {
|
||||
std::vector<AstDot*> dotps;
|
||||
std::vector<AstTypedef*> tdefps;
|
||||
const auto& deferredVarps = v3Global.rootp()->deferredParamVarps();
|
||||
std::set<AstVar*> reachedDeferred;
|
||||
std::set<const AstTypedef*> reachedTypedefs;
|
||||
auto collectDots = [&](AstNode* p) {
|
||||
p->foreach([&](AstDot* dotp) {
|
||||
if (VN_IS(dotp->lhsp(), ClassOrPackageRef)) dotps.push_back(dotp);
|
||||
});
|
||||
};
|
||||
auto collectTypedefs = [&](AstNode* p) {
|
||||
p->foreach([&](const AstRefDType* refp) {
|
||||
AstTypedef* const tdefp = refp->typedefp();
|
||||
if (tdefp && reachedTypedefs.insert(tdefp).second) tdefps.push_back(tdefp);
|
||||
});
|
||||
};
|
||||
std::vector<AstVar*> workVarps;
|
||||
auto enqueueRefs = [&](AstNode* p) {
|
||||
p->foreach([&](const AstVarRef* refp) {
|
||||
|
|
@ -2200,18 +2220,25 @@ public:
|
|||
});
|
||||
};
|
||||
collectDots(rootp);
|
||||
collectTypedefs(rootp);
|
||||
enqueueRefs(rootp);
|
||||
// Handle deferred VarRefs recursively until no more, accumulating Dots along the way.
|
||||
// Handle deferred VarRefs recursively until no more, accumulating Dots and
|
||||
// typedef references along the way.
|
||||
while (!workVarps.empty()) {
|
||||
AstVar* const varp = workVarps.back();
|
||||
workVarps.pop_back();
|
||||
if (!varp->valuep()) continue;
|
||||
collectDots(varp->valuep());
|
||||
collectTypedefs(varp->valuep());
|
||||
enqueueRefs(varp->valuep());
|
||||
}
|
||||
if (dotps.empty()) return;
|
||||
if (dotps.empty() && tdefps.empty()) return;
|
||||
VL_RESTORER(m_modp);
|
||||
m_modp = modp;
|
||||
// Resolve unresolved class-scoped RefDTypes buried in any typedef reachable from
|
||||
// the pin/expr tree (e.g. `$bits(wrap_t)` where `wrap_t`'s struct members
|
||||
// reference `CFG::data_t` from a parameterized-class typedef alias).
|
||||
for (AstTypedef* const tdefp : tdefps) resolveParamClassRefDType(tdefp->subDTypep());
|
||||
// Reverse-iterate so inner Dots resolve before outer.
|
||||
for (auto it = dotps.rbegin(); it != dotps.rend(); ++it) resolveDotToTypedef(*it);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(verilator_flags2=['--binary'])
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// Multi-level class hierarchies where one parameterized class's
|
||||
// localparam value references another parameterized class via its
|
||||
// own class-scope Dot (e.g. B::width = inner_a::v where inner_a is
|
||||
// a typedef of A#(...)). resolveDotToTypedef must recursively resolve
|
||||
// nested class::member Dots in a member's value before constifying,
|
||||
// otherwise cell-pin deparam fails for the outer Dot.
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
// verilog_format: off
|
||||
`define stop $stop
|
||||
`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0)
|
||||
// verilog_format: on
|
||||
|
||||
module Sub #(
|
||||
parameter int WIDTH
|
||||
) ();
|
||||
endmodule
|
||||
|
||||
module t;
|
||||
// Two-level: B's lparam value contains a class::member Dot of its own
|
||||
virtual class A #(
|
||||
parameter int x
|
||||
);
|
||||
localparam int v = x * 2;
|
||||
endclass
|
||||
|
||||
virtual class B #(
|
||||
parameter int y
|
||||
);
|
||||
typedef A#(y + 1) inner_a;
|
||||
localparam int width = inner_a::v; // nested Dot inside B's lparam
|
||||
endclass
|
||||
|
||||
// Three-level: C wraps B wraps A
|
||||
virtual class C #(
|
||||
parameter int z
|
||||
);
|
||||
typedef B#(z * 3) inner_b;
|
||||
localparam int total = inner_b::width; // double-nested Dot
|
||||
endclass
|
||||
|
||||
typedef B#(5) BInst;
|
||||
typedef C#(2) CInst;
|
||||
|
||||
// (1) Standalone localparam — resolves via deferred-lparam path
|
||||
localparam int two_level = BInst::width; // = A#(6)::v = 12
|
||||
localparam int three_level = CInst::total; // = B#(6)::width = A#(7)::v = 14
|
||||
|
||||
// (2) Cell pin uses the nested-class value directly — exercises the
|
||||
// recursive Dot resolution inside resolveDotToTypedef
|
||||
Sub #(BInst::width) m_pin_direct ();
|
||||
Sub #(CInst::total) m_pin_deep ();
|
||||
Sub #(BInst::width + 1) m_pin_expr ();
|
||||
|
||||
// (3) Cell pin chains through a deferred lparam whose value is
|
||||
// itself a nested-class Dot
|
||||
localparam int chain = BInst::width;
|
||||
Sub #(chain) m_pin_chain ();
|
||||
|
||||
initial begin
|
||||
`checkh(two_level, 32'd12);
|
||||
`checkh(three_level, 32'd14);
|
||||
`checkh(chain, 32'd12);
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -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(verilator_flags2=['--binary'])
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// A struct typedef whose member references a class-scope-resolved typedef
|
||||
// from a parameterized-class typedef alias (e.g. CFG::data_t). The struct
|
||||
// is consumed by `$bits(...)` on a cell parameter pin, which forces V3Param
|
||||
// to constify the pin during cell deparameterization. V3Param must follow
|
||||
// typedef references in the pin tree and resolve the unresolved struct-member
|
||||
// RefDTypes that target the parameterized class, otherwise V3Width hits
|
||||
// "REFDTYPE 'data_t' not linked to type" (V3AstNodes.cpp).
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
// verilog_format: off
|
||||
`define stop $stop
|
||||
`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0)
|
||||
// verilog_format: on
|
||||
|
||||
package pkg;
|
||||
virtual class C #(parameter int W = 1);
|
||||
typedef logic [W-1:0] data_t;
|
||||
endclass
|
||||
endpackage
|
||||
|
||||
module Sink #(parameter int BITS = 1) ();
|
||||
logic [BITS-1:0] data;
|
||||
endmodule
|
||||
|
||||
module Sub #(parameter int W = 8) ();
|
||||
typedef pkg::C#(W) CFG;
|
||||
|
||||
// Struct typedef whose member is a class-scope-resolved typedef from
|
||||
// a parameterized-class typedef alias. Without the V3Param fix, the
|
||||
// member's RefDType stays UNLINKED and the cell pin's $bits trips an
|
||||
// internal error.
|
||||
typedef struct packed {
|
||||
CFG::data_t payload;
|
||||
logic v;
|
||||
} wrap_t;
|
||||
|
||||
Sink #(.BITS($bits(wrap_t))) u_sink ();
|
||||
endmodule
|
||||
|
||||
module t;
|
||||
Sub #(.W(8)) u ();
|
||||
|
||||
initial begin
|
||||
// $bits(wrap_t) = 8 (payload) + 1 (v) = 9
|
||||
`checkh($bits(u.u_sink.data), 32'd9);
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
Loading…
Reference in New Issue