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
|
// Param/lparam member: substitute its constant value so the caller's constify can
|
||||||
// succeed.
|
// succeed.
|
||||||
if (varp->isParam() && varp->valuep()) {
|
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)) {
|
if (AstConst* const constp = VN_CAST(varp->valuep(), Const)) {
|
||||||
dotp->replaceWith(constp->cloneTree(false));
|
dotp->replaceWith(constp->cloneTree(false));
|
||||||
VL_DO_DANGLING(dotp->deleteTree(), dotp);
|
VL_DO_DANGLING(dotp->deleteTree(), dotp);
|
||||||
|
|
@ -2182,13 +2194,21 @@ public:
|
||||||
// VarRefs to all leaf dependencies. Then resolve these to typedefs.
|
// VarRefs to all leaf dependencies. Then resolve these to typedefs.
|
||||||
void resolveDeferredDotsReachableFrom(AstNode* rootp, AstNodeModule* modp) {
|
void resolveDeferredDotsReachableFrom(AstNode* rootp, AstNodeModule* modp) {
|
||||||
std::vector<AstDot*> dotps;
|
std::vector<AstDot*> dotps;
|
||||||
|
std::vector<AstTypedef*> tdefps;
|
||||||
const auto& deferredVarps = v3Global.rootp()->deferredParamVarps();
|
const auto& deferredVarps = v3Global.rootp()->deferredParamVarps();
|
||||||
std::set<AstVar*> reachedDeferred;
|
std::set<AstVar*> reachedDeferred;
|
||||||
|
std::set<const AstTypedef*> reachedTypedefs;
|
||||||
auto collectDots = [&](AstNode* p) {
|
auto collectDots = [&](AstNode* p) {
|
||||||
p->foreach([&](AstDot* dotp) {
|
p->foreach([&](AstDot* dotp) {
|
||||||
if (VN_IS(dotp->lhsp(), ClassOrPackageRef)) dotps.push_back(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;
|
std::vector<AstVar*> workVarps;
|
||||||
auto enqueueRefs = [&](AstNode* p) {
|
auto enqueueRefs = [&](AstNode* p) {
|
||||||
p->foreach([&](const AstVarRef* refp) {
|
p->foreach([&](const AstVarRef* refp) {
|
||||||
|
|
@ -2200,18 +2220,25 @@ public:
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
collectDots(rootp);
|
collectDots(rootp);
|
||||||
|
collectTypedefs(rootp);
|
||||||
enqueueRefs(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()) {
|
while (!workVarps.empty()) {
|
||||||
AstVar* const varp = workVarps.back();
|
AstVar* const varp = workVarps.back();
|
||||||
workVarps.pop_back();
|
workVarps.pop_back();
|
||||||
if (!varp->valuep()) continue;
|
if (!varp->valuep()) continue;
|
||||||
collectDots(varp->valuep());
|
collectDots(varp->valuep());
|
||||||
|
collectTypedefs(varp->valuep());
|
||||||
enqueueRefs(varp->valuep());
|
enqueueRefs(varp->valuep());
|
||||||
}
|
}
|
||||||
if (dotps.empty()) return;
|
if (dotps.empty() && tdefps.empty()) return;
|
||||||
VL_RESTORER(m_modp);
|
VL_RESTORER(m_modp);
|
||||||
m_modp = 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.
|
// Reverse-iterate so inner Dots resolve before outer.
|
||||||
for (auto it = dotps.rbegin(); it != dotps.rend(); ++it) resolveDotToTypedef(*it);
|
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