Merge dd1eb2dfe7 into 5ead758f28
This commit is contained in:
commit
bf0320ca89
|
|
@ -67,6 +67,7 @@ Drew Ranck
|
||||||
Drew Taussig
|
Drew Taussig
|
||||||
Driss Hafdi
|
Driss Hafdi
|
||||||
Edgar E. Iglesias
|
Edgar E. Iglesias
|
||||||
|
Edmund Lam
|
||||||
Eric Mejdrich
|
Eric Mejdrich
|
||||||
Eric Müller
|
Eric Müller
|
||||||
Eric Rippey
|
Eric Rippey
|
||||||
|
|
|
||||||
152
src/V3Param.cpp
152
src/V3Param.cpp
|
|
@ -1288,10 +1288,84 @@ 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* const 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));
|
// Walk up any outer `.fieldN[.fieldM...]` chain (struct lparam
|
||||||
VL_DO_DANGLING(dotp->deleteTree(), dotp);
|
// member access like `CFG::cfg.jt.cam_type`) and accumulate the
|
||||||
|
// packed-struct bit offset. Replace the topmost Dot with a
|
||||||
|
// Sel(constp, lsb, width) matching V3Width::memberSelStruct.
|
||||||
|
int totalLsb = 0;
|
||||||
|
int sliceWidth = varp->width();
|
||||||
|
AstNodeDType* curDTypep = varp->dtypep();
|
||||||
|
AstNode* topp = dotp;
|
||||||
|
AstDot* outerDotp = VN_CAST(dotp->backp(), Dot);
|
||||||
|
const auto errorRecover = [&](AstDot* const badp, const string& msg) {
|
||||||
|
badp->v3error(msg);
|
||||||
|
badp->replaceWith(new AstConst{badp->fileline(), AstConst::Signed32{}, 0});
|
||||||
|
VL_DO_DANGLING(badp->deleteTree(), badp);
|
||||||
|
};
|
||||||
|
bool errored = false;
|
||||||
|
while (outerDotp && outerDotp->lhsp() == topp) { // LCOV_EXCL_BR_LINE
|
||||||
|
const AstParseRef* const fieldRefp = VN_CAST(outerDotp->rhsp(), ParseRef);
|
||||||
|
if (!fieldRefp) {
|
||||||
|
errorRecover(outerDotp, "Malformed dotted select in parameter value");
|
||||||
|
errored = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
UASSERT_OBJ(curDTypep, outerDotp,
|
||||||
|
"curDTypep null in struct field chain walk");
|
||||||
|
AstNodeDType* const skippedp = curDTypep->skipRefp();
|
||||||
|
AstNodeUOrStructDType* const structp
|
||||||
|
= VN_CAST(skippedp, NodeUOrStructDType);
|
||||||
|
if (!structp) {
|
||||||
|
errorRecover(outerDotp,
|
||||||
|
"Dotted member select on non-struct parameter value");
|
||||||
|
errored = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
AstMemberDType* const foundMemp = VN_CAST(
|
||||||
|
m_memberMap.findMember(structp, fieldRefp->name()), MemberDType);
|
||||||
|
if (!foundMemp) {
|
||||||
|
errorRecover(outerDotp, "Member " + fieldRefp->prettyNameQ()
|
||||||
|
+ " not found in structure");
|
||||||
|
errored = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
UASSERT_OBJ(foundMemp->subDTypep(), outerDotp,
|
||||||
|
"member has null subDTypep");
|
||||||
|
totalLsb += foundMemp->lsb();
|
||||||
|
sliceWidth = foundMemp->width();
|
||||||
|
curDTypep = foundMemp->subDTypep();
|
||||||
|
topp = outerDotp;
|
||||||
|
outerDotp = VN_CAST(outerDotp->backp(), Dot);
|
||||||
|
}
|
||||||
|
if (errored) return;
|
||||||
|
if (topp == dotp) {
|
||||||
|
dotp->replaceWith(constp->cloneTree(false));
|
||||||
|
VL_DO_DANGLING(dotp->deleteTree(), dotp);
|
||||||
|
} else {
|
||||||
|
AstConst* const clonep = static_cast<AstConst*>(constp->cloneTree(false));
|
||||||
|
AstSel* const selp
|
||||||
|
= new AstSel{topp->fileline(), clonep, totalLsb, sliceWidth};
|
||||||
|
// Match V3Width::memberSelStruct: skip RefDTypes to surface
|
||||||
|
// enum dtype, and mark didWidth so V3Width doesn't reflatten.
|
||||||
|
selp->dtypep(curDTypep->skipRefToEnump());
|
||||||
|
selp->didWidth(true);
|
||||||
|
topp->replaceWith(selp);
|
||||||
|
VL_DO_DANGLING(topp->deleteTree(), topp);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2192,6 +2266,58 @@ public:
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Walk every subtree reachable from `rootp` for deferred work V3LinkDot left behind
|
||||||
|
// when `class#(...)::member` couldn't yet be resolved:
|
||||||
|
// (1) `AstDot` with a ClassOrPackageRef lhs - a `class::lparam` value Dot to
|
||||||
|
// constify (e.g. `c8::b` in a pin expression).
|
||||||
|
// (2) `AstRefDType` with a typedefp - descend into the typedef's subDType
|
||||||
|
// to find buried Dots (typedef range like `logic [(CFG::w - 1):0]`) and
|
||||||
|
// unresolved class-scoped struct-member RefDTypes (`CFG::input_meta_t`
|
||||||
|
// as a struct member).
|
||||||
|
// (3) `AstVarRef` to a deferred lparam - descend into its valuep so the
|
||||||
|
// chain `pin -> lparam -> ... -> class::lparam Dot` reaches the Dot.
|
||||||
|
// Discoveries of (2) and (3) feed back into the walk worklist; (1) and (2)
|
||||||
|
// are also kept in lists for the resolution passes at the end.
|
||||||
|
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;
|
||||||
|
std::vector<AstNode*> worklist{rootp};
|
||||||
|
while (!worklist.empty()) {
|
||||||
|
AstNode* const curDotp = worklist.back();
|
||||||
|
worklist.pop_back();
|
||||||
|
curDotp->foreach([&](AstNode* const np) {
|
||||||
|
if (AstDot* const dotp = VN_CAST(np, Dot)) {
|
||||||
|
if (VN_IS(dotp->lhsp(), ClassOrPackageRef)) dotps.push_back(dotp);
|
||||||
|
} else if (const AstRefDType* const refp = VN_CAST(np, RefDType)) {
|
||||||
|
AstTypedef* const tdefp = refp->typedefp();
|
||||||
|
if (tdefp && reachedTypedefs.insert(tdefp).second) {
|
||||||
|
tdefps.push_back(tdefp);
|
||||||
|
worklist.push_back(tdefp->subDTypep());
|
||||||
|
}
|
||||||
|
} else if (const AstVarRef* const refp = VN_CAST(np, VarRef)) {
|
||||||
|
AstVar* const varp = refp->varp();
|
||||||
|
if (varp && varp->varType() == VVarType::LPARAM && deferredVarps.count(varp)
|
||||||
|
&& reachedDeferred.insert(varp).second) {
|
||||||
|
UASSERT_OBJ(varp->valuep(), varp, "VarRef should have non-null valuep");
|
||||||
|
worklist.push_back(varp->valuep());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (dotps.empty() && tdefps.empty()) return;
|
||||||
|
VL_RESTORER(m_modp);
|
||||||
|
m_modp = modp;
|
||||||
|
// Link 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);
|
||||||
|
}
|
||||||
|
|
||||||
AstNodeModule* nodeDeparam(AstNode* nodep, AstNodeModule* srcModp, AstNodeModule* modp,
|
AstNodeModule* nodeDeparam(AstNode* nodep, AstNodeModule* srcModp, AstNodeModule* modp,
|
||||||
const string& someInstanceName) {
|
const string& someInstanceName) {
|
||||||
// Return new or reused de-parameterized module
|
// Return new or reused de-parameterized module
|
||||||
|
|
@ -2232,14 +2358,9 @@ public:
|
||||||
}
|
}
|
||||||
// Create new module name with _'s between the constants
|
// Create new module name with _'s between the constants
|
||||||
UINFOTREE(10, nodep, "", "cell");
|
UINFOTREE(10, nodep, "", "cell");
|
||||||
// Resolve `class::member` Dots in pin values so constify sees Consts.
|
// Resolve `class::member` Dots in pin values, and in any deferred
|
||||||
// Single-pass: filter-collect class-scoped Dots, then reverse-iterate so inner
|
// lparam reachable from the pin tree, so constify sees Consts.
|
||||||
// Dots resolve before outer (vector stays empty for cells with no class Dots).
|
resolveDeferredDotsReachableFrom(nodep, modp);
|
||||||
std::vector<AstDot*> dotps;
|
|
||||||
nodep->foreach([&](AstDot* dotp) {
|
|
||||||
if (VN_IS(dotp->lhsp(), ClassOrPackageRef)) dotps.push_back(dotp);
|
|
||||||
});
|
|
||||||
for (auto it = dotps.rbegin(); it != dotps.rend(); ++it) resolveDotToTypedef(*it);
|
|
||||||
// Evaluate all module constants
|
// Evaluate all module constants
|
||||||
V3Const::constifyParamsEdit(nodep);
|
V3Const::constifyParamsEdit(nodep);
|
||||||
// Set name for warnings for when we param propagate the module
|
// Set name for warnings for when we param propagate the module
|
||||||
|
|
@ -3185,6 +3306,9 @@ class ParamVisitor final : public VNVisitor {
|
||||||
void visit(AstGenIf* nodep) override {
|
void visit(AstGenIf* nodep) override {
|
||||||
UINFO(9, " GENIF " << nodep);
|
UINFO(9, " GENIF " << nodep);
|
||||||
iterateAndNextNull(nodep->condp());
|
iterateAndNextNull(nodep->condp());
|
||||||
|
// condp may reference deferred lparams whose Dot is still pending;
|
||||||
|
// resolve them so widthing/constify can see Consts.
|
||||||
|
m_processor.resolveDeferredDotsReachableFrom(nodep->condp(), m_modp);
|
||||||
// We suppress errors when widthing params since short-circuiting in
|
// We suppress errors when widthing params since short-circuiting in
|
||||||
// the conditional evaluation may mean these error can never occur. We
|
// the conditional evaluation may mean these error can never occur. We
|
||||||
// then make sure that short-circuiting is used by constifyParamsEdit.
|
// then make sure that short-circuiting is used by constifyParamsEdit.
|
||||||
|
|
@ -3220,6 +3344,9 @@ class ParamVisitor final : public VNVisitor {
|
||||||
iterateAndNextNull(forp->initsp());
|
iterateAndNextNull(forp->initsp());
|
||||||
iterateAndNextNull(forp->condp());
|
iterateAndNextNull(forp->condp());
|
||||||
iterateAndNextNull(forp->incsp());
|
iterateAndNextNull(forp->incsp());
|
||||||
|
// Cond/init/inc may reference deferred lparams whose Dot is still
|
||||||
|
// pending; resolve them so widthing/constify can see Consts.
|
||||||
|
m_processor.resolveDeferredDotsReachableFrom(forp, m_modp);
|
||||||
V3Width::widthParamsEdit(forp); // Param typed widthing will NOT recurse the body
|
V3Width::widthParamsEdit(forp); // Param typed widthing will NOT recurse the body
|
||||||
// Outer wrapper around generate used to hold genvar, and to ensure genvar
|
// Outer wrapper around generate used to hold genvar, and to ensure genvar
|
||||||
// doesn't conflict in V3LinkDot resolution with other genvars
|
// doesn't conflict in V3LinkDot resolution with other genvars
|
||||||
|
|
@ -3253,6 +3380,9 @@ class ParamVisitor final : public VNVisitor {
|
||||||
AstNode* keepp = nullptr;
|
AstNode* keepp = nullptr;
|
||||||
iterateAndNextNull(nodep->exprp());
|
iterateAndNextNull(nodep->exprp());
|
||||||
V3Case::caseLint(nodep);
|
V3Case::caseLint(nodep);
|
||||||
|
// expr / case items may reference deferred lparams whose Dot is still
|
||||||
|
// pending; resolve them so widthing/constify can see Consts.
|
||||||
|
m_processor.resolveDeferredDotsReachableFrom(nodep, m_modp);
|
||||||
V3Width::widthParamsEdit(nodep); // Param typed widthing will NOT recurse the
|
V3Width::widthParamsEdit(nodep); // Param typed widthing will NOT recurse the
|
||||||
// body, don't trigger errors yet.
|
// body, don't trigger errors yet.
|
||||||
V3Const::constifyParamsEdit(nodep->exprp()); // exprp may change
|
V3Const::constifyParamsEdit(nodep->exprp()); // exprp may change
|
||||||
|
|
|
||||||
|
|
@ -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,65 @@
|
||||||
|
// DESCRIPTION: Verilator: Verilog Test module
|
||||||
|
//
|
||||||
|
// Generate-for / generate-if / generate-case conditions that reference
|
||||||
|
// a class-scope-resolved deferred localparam (e.g. inst::b). V3Param's
|
||||||
|
// visit(AstGenIf/Block/Case) calls V3Width::widthParamsEdit /
|
||||||
|
// constify on the cond/expr; the same transitive Dot-resolution used
|
||||||
|
// for cell pins must be applied here so the deferred lparam folds.
|
||||||
|
//
|
||||||
|
// 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
|
||||||
|
|
||||||
|
// Cell whose presence is observable, used inside generate blocks
|
||||||
|
// below to confirm each generate flavour elaborated the right way.
|
||||||
|
module Tag #(
|
||||||
|
parameter int ID
|
||||||
|
) ();
|
||||||
|
initial $write("Tag ID=%0d\n", ID);
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
module t;
|
||||||
|
virtual class C #(
|
||||||
|
parameter int a
|
||||||
|
);
|
||||||
|
localparam int b = a;
|
||||||
|
endclass
|
||||||
|
|
||||||
|
typedef C#(3) c3;
|
||||||
|
typedef C#(5) c5;
|
||||||
|
|
||||||
|
// Deferred lparams
|
||||||
|
localparam int b3 = c3::b;
|
||||||
|
localparam int b5 = c5::b;
|
||||||
|
|
||||||
|
// (1) Generate-for bound = deferred lparam
|
||||||
|
for (genvar i = 0; i < b3; i++) begin : gf
|
||||||
|
Tag #(100 + i) inst ();
|
||||||
|
end
|
||||||
|
|
||||||
|
// (2) Generate-if cond = deferred lparam
|
||||||
|
if (b5 > b3) begin : gi_t
|
||||||
|
Tag #(200) inst ();
|
||||||
|
end else begin : gi_f
|
||||||
|
Tag #(201) inst ();
|
||||||
|
end
|
||||||
|
|
||||||
|
// (3) Generate-case selector = deferred lparam
|
||||||
|
case (b5)
|
||||||
|
3: begin : gc Tag #(303) inst (); end
|
||||||
|
5: begin : gc Tag #(305) inst (); end
|
||||||
|
default: begin : gc Tag #(399) inst (); end
|
||||||
|
endcase
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
`checkh(b3, 32'd3);
|
||||||
|
`checkh(b5, 32'd5);
|
||||||
|
$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,66 @@
|
||||||
|
// DESCRIPTION: Verilator: Verilog Test module
|
||||||
|
//
|
||||||
|
// Interface-instance parameter pins whose value chains through a
|
||||||
|
// class-scope-resolved localparam (typedef alias of a parameterized
|
||||||
|
// class, e.g. inst::b). Same cell-deparam fix that handles module
|
||||||
|
// instances must also handle interface instances and downstream
|
||||||
|
// port-bound consumers.
|
||||||
|
//
|
||||||
|
// 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
|
||||||
|
|
||||||
|
interface SubIface #(
|
||||||
|
parameter int IW
|
||||||
|
) ();
|
||||||
|
logic [IW-1:0] data;
|
||||||
|
endinterface
|
||||||
|
|
||||||
|
// Module that takes an interface port - must elaborate after the iface
|
||||||
|
// cell pin is fully constified.
|
||||||
|
module Consumer (
|
||||||
|
SubIface si
|
||||||
|
);
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
module t;
|
||||||
|
virtual class C #(
|
||||||
|
parameter int a
|
||||||
|
);
|
||||||
|
localparam int b = a;
|
||||||
|
endclass
|
||||||
|
|
||||||
|
typedef C#(8) c8;
|
||||||
|
typedef C#(13) c13;
|
||||||
|
|
||||||
|
// Deferred lparams
|
||||||
|
localparam int b8 = c8::b;
|
||||||
|
localparam int b13 = c13::b;
|
||||||
|
// Chained
|
||||||
|
localparam int b8_alias = b8;
|
||||||
|
|
||||||
|
// Iface pin = bare VarRef to deferred lparam
|
||||||
|
SubIface #(b8) i_bare ();
|
||||||
|
// Iface pin = chained VarRef
|
||||||
|
SubIface #(b8_alias) i_chain ();
|
||||||
|
// Iface pin = expression mixing deferred lparams
|
||||||
|
SubIface #(b8 + b13) i_expr ();
|
||||||
|
// Iface used as a module port - exercises the iface-cell-first path
|
||||||
|
Consumer cons (.si(i_bare));
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
`checkh(b8, 32'd8);
|
||||||
|
`checkh(b13, 32'd13);
|
||||||
|
`checkh(b8_alias, 32'd8);
|
||||||
|
`checkh($bits(i_bare.data), 32'd8);
|
||||||
|
`checkh($bits(i_chain.data), 32'd8);
|
||||||
|
`checkh($bits(i_expr.data), 32'd21);
|
||||||
|
$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,71 @@
|
||||||
|
// DESCRIPTION: Verilator: Verilog Test module
|
||||||
|
//
|
||||||
|
// Module-instance parameter pins whose value chains through a
|
||||||
|
// class-scope-resolved localparam (typedef alias of a parameterized
|
||||||
|
// class, e.g. inst::b). V3LinkDot defers the inst::b Dot until
|
||||||
|
// post-V3Param; the cell deparam must follow VarRefs into any
|
||||||
|
// deferred lparam reachable from the pin tree and resolve its Dots
|
||||||
|
// inline so constify on the cell can fold.
|
||||||
|
//
|
||||||
|
// 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
|
||||||
|
|
||||||
|
// Two-level: outer module forwards its param to an inner cell, so the
|
||||||
|
// deferred lparam value must flow through the cell-deparam chain.
|
||||||
|
module SubL1 #(
|
||||||
|
parameter int W
|
||||||
|
) ();
|
||||||
|
Sub #(W) inner ();
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
module t;
|
||||||
|
virtual class C #(
|
||||||
|
parameter int a
|
||||||
|
);
|
||||||
|
localparam int b = a;
|
||||||
|
endclass
|
||||||
|
|
||||||
|
typedef C#(4) c4;
|
||||||
|
typedef C#(5) c5;
|
||||||
|
typedef C#(8) c8;
|
||||||
|
|
||||||
|
// Deferred lparams (value contains class::member Dot)
|
||||||
|
localparam int b4 = c4::b;
|
||||||
|
localparam int b5 = c5::b;
|
||||||
|
localparam int b8 = c8::b;
|
||||||
|
// Chained: value references another deferred lparam
|
||||||
|
localparam int c8_ref = b8;
|
||||||
|
localparam int d8_ref = c8_ref + 1;
|
||||||
|
|
||||||
|
// Pin is bare VarRef to a deferred lparam
|
||||||
|
Sub #(b8) m_bare ();
|
||||||
|
// Pin chains through multiple deferred lparams (b8 -> c8_ref -> d8_ref)
|
||||||
|
Sub #(d8_ref) m_chain ();
|
||||||
|
// Pin is an expression mixing two deferred lparams
|
||||||
|
Sub #(b4 + b5) m_expr ();
|
||||||
|
// Pin mixes a Dot and a deferred lparam in the same expression
|
||||||
|
Sub #(c4::b + b5) m_mix ();
|
||||||
|
// Two-level: outer module forwards param to inner cell
|
||||||
|
SubL1 #(b8) m_l1 ();
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
`checkh(b4, 32'd4);
|
||||||
|
`checkh(b5, 32'd5);
|
||||||
|
`checkh(b8, 32'd8);
|
||||||
|
`checkh(c8_ref, 32'd8);
|
||||||
|
`checkh(d8_ref, 32'd9);
|
||||||
|
$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,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,76 @@
|
||||||
|
// DESCRIPTION: Verilator: Verilog Test module
|
||||||
|
//
|
||||||
|
// Struct localparam field access via a class-scope Dot used in a cell
|
||||||
|
// parameter pin (e.g. `CFG::cfg.jt.cam_type`), where CFG is a typedef
|
||||||
|
// alias of a parameterized class. V3LinkDot defers the `CFG::cfg` Dot
|
||||||
|
// until post-V3Param; when the cell is deparameterized, resolveDotToTypedef
|
||||||
|
// folds the class localparam to a Const and must then walk up the outer
|
||||||
|
// `.field[.field...]` Dot chain, accumulating the packed-struct bit offset,
|
||||||
|
// and replace the topmost Dot with a Sel(const, lsb, width) matching
|
||||||
|
// V3Width::memberSelStruct. Without this, the struct-field select on the
|
||||||
|
// deferred class localparam cannot be constified for the cell pin.
|
||||||
|
//
|
||||||
|
// 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
|
||||||
|
|
||||||
|
typedef struct packed {
|
||||||
|
logic [7:0] cam_type;
|
||||||
|
logic [7:0] depth;
|
||||||
|
} inner_t;
|
||||||
|
|
||||||
|
typedef struct packed {
|
||||||
|
inner_t jt;
|
||||||
|
logic [7:0] tag;
|
||||||
|
} cfg_t;
|
||||||
|
|
||||||
|
class C #(parameter int W = 1);
|
||||||
|
// Struct localparam whose fields derive from the class parameter.
|
||||||
|
localparam cfg_t cfg = '{jt: '{cam_type: W[7:0], depth: W[7:0] + 8'd1},
|
||||||
|
tag: W[7:0] + 8'd2};
|
||||||
|
endclass
|
||||||
|
|
||||||
|
// Wrapper class containing the use of another class
|
||||||
|
class D #(parameter int W = 1);
|
||||||
|
typedef C#(W) CC;
|
||||||
|
localparam int q = int'(CC::cfg.tag) + 100;
|
||||||
|
endclass
|
||||||
|
|
||||||
|
module Sub #(parameter int WIDTH = 0) ();
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
// Parameterized wrapper so the class specialization is deferred until
|
||||||
|
// V3Param processes the cell instance.
|
||||||
|
module Mid #(parameter int W = 8) ();
|
||||||
|
typedef C#(W) CFG;
|
||||||
|
// (1) single-level struct field access -> one loop iteration
|
||||||
|
Sub #(int'(CFG::cfg.tag)) u_tag ();
|
||||||
|
// (2) nested struct field access -> two loop iterations, lsb accumulation
|
||||||
|
Sub #(int'(CFG::cfg.jt.cam_type)) u_cam ();
|
||||||
|
Sub #(int'(CFG::cfg.jt.depth)) u_depth ();
|
||||||
|
// (3) nested struct-field Dot buried in a wrapper class's lparam value
|
||||||
|
typedef D#(W) DD;
|
||||||
|
Sub #(int'(DD::q)) u_q ();
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
module t;
|
||||||
|
Mid #(.W(8)) u ();
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
// cfg.tag = W + 2 = 10 (offset above the 16-bit jt struct)
|
||||||
|
`checkh(u.u_tag.WIDTH, 32'd10);
|
||||||
|
// cfg.jt.cam_type = W = 8 (nested, lsb 0)
|
||||||
|
`checkh(u.u_cam.WIDTH, 32'd8);
|
||||||
|
// cfg.jt.depth = W + 1 = 9 (nested, lsb 8)
|
||||||
|
`checkh(u.u_depth.WIDTH, 32'd9);
|
||||||
|
// q = cfg.tag + 100 = 10 + 100 = 110
|
||||||
|
`checkh(u.u_q.WIDTH, 32'd110);
|
||||||
|
$write("*-* All Finished *-*\n");
|
||||||
|
$finish;
|
||||||
|
end
|
||||||
|
endmodule
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
%Error: t/t_class_lparam_struct_field_chain_bad.v:37:25: Malformed dotted select in parameter value
|
||||||
|
: ... note: In instance 't.u'
|
||||||
|
37 | Sub #(int'(CFG::cfg.jt.cam_type[0])) u_fieldref ();
|
||||||
|
| ^
|
||||||
|
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
|
||||||
|
%Error: t/t_class_lparam_struct_field_chain_bad.v:39:26: Dotted member select on non-struct parameter value
|
||||||
|
: ... note: In instance 't.u'
|
||||||
|
39 | Sub #(int'(CFG::cfg.tag.bogus)) u_nonstruct ();
|
||||||
|
| ^
|
||||||
|
%Error: t/t_class_lparam_struct_field_chain_bad.v:41:25: Member 'nosuchfield' not found in structure
|
||||||
|
: ... note: In instance 't.u'
|
||||||
|
41 | Sub #(int'(CFG::cfg.jt.nosuchfield)) u_nomember ();
|
||||||
|
| ^
|
||||||
|
%Error: Exiting due to
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
#!/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('linter')
|
||||||
|
|
||||||
|
test.lint(fails=True, expect_filename=test.golden_filename)
|
||||||
|
|
||||||
|
test.passes()
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
// DESCRIPTION: Verilator: Verilog Test module
|
||||||
|
//
|
||||||
|
// Negative test for the struct-localparam field-access folding in V3Param
|
||||||
|
// (resolveDotToTypedef). Malformed outer `.field` chains on a class
|
||||||
|
// localparam value must produce clean user-facing errors, NOT an internal
|
||||||
|
// error or a crash in the following width pass. Exercises the three
|
||||||
|
// mismatch guards in the field-chain walk:
|
||||||
|
// (1) outer Dot rhs is not a plain field identifier (bit-select)
|
||||||
|
// (2) `.field` applied to a non-struct value
|
||||||
|
// (3) field name not a member of the struct
|
||||||
|
//
|
||||||
|
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||||
|
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||||
|
// SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
typedef struct packed {
|
||||||
|
logic [7:0] cam_type;
|
||||||
|
logic [7:0] depth;
|
||||||
|
} inner_t;
|
||||||
|
|
||||||
|
typedef struct packed {
|
||||||
|
inner_t jt;
|
||||||
|
logic [7:0] tag;
|
||||||
|
} cfg_t;
|
||||||
|
|
||||||
|
class C #(parameter int W = 1);
|
||||||
|
localparam cfg_t cfg = '{jt: '{cam_type: W[7:0], depth: W[7:0] + 8'd1},
|
||||||
|
tag: W[7:0] + 8'd2};
|
||||||
|
endclass
|
||||||
|
|
||||||
|
module Sub #(parameter int WIDTH = 0) ();
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
module Mid #(parameter int W = 8) ();
|
||||||
|
typedef C#(W) CFG;
|
||||||
|
// (1) bit-select after the field chain: outer Dot rhs is not a ParseRef
|
||||||
|
Sub #(int'(CFG::cfg.jt.cam_type[0])) u_fieldref ();
|
||||||
|
// (2) `.bogus` on the scalar field `tag`: dotting into a non-struct
|
||||||
|
Sub #(int'(CFG::cfg.tag.bogus)) u_nonstruct ();
|
||||||
|
// (3) `.nosuchfield` not a member of struct `jt`
|
||||||
|
Sub #(int'(CFG::cfg.jt.nosuchfield)) u_nomember ();
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
module t;
|
||||||
|
Mid #(.W(8)) u ();
|
||||||
|
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
|
||||||
|
|
@ -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,94 @@
|
||||||
|
// DESCRIPTION: Verilator: Verilog Test module
|
||||||
|
//
|
||||||
|
// Typedefs and class type parameters that resolve through a
|
||||||
|
// class-scope-resolved localparam (typedef alias of a parameterized
|
||||||
|
// class, e.g. inst::b). Exercises five sub-cases:
|
||||||
|
// - Deferred lparam used as a packed range bound in a typedef
|
||||||
|
// - Deferred lparam used as a value argument to a parameterized class
|
||||||
|
// - Class-scope-resolved typedef that itself depends on the param
|
||||||
|
// - Class-scope Dot used DIRECTLY (no intermediate lparam) in a typedef
|
||||||
|
// range, consumed by `$bits` on a child cell pin
|
||||||
|
// - Struct lparam field access via class-scope Dot
|
||||||
|
// (`CFG::struct_lparam.field`) inside a parameterized module
|
||||||
|
//
|
||||||
|
// 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 W = 8) (); endmodule
|
||||||
|
|
||||||
|
// Sub-case (5): struct lparam field access via class-scope Dot. Needs a
|
||||||
|
// parameterized-module wrapper so the class specialization is deferred until
|
||||||
|
// V3Param processes the cell instance.
|
||||||
|
typedef struct packed { logic [7:0] hi; logic [7:0] lo; } pair_t;
|
||||||
|
|
||||||
|
class P #(parameter pair_t p);
|
||||||
|
localparam pair_t pp = p;
|
||||||
|
endclass
|
||||||
|
|
||||||
|
module PairHolder #(parameter pair_t cfg = '{hi:'d7, lo:'d11}) ();
|
||||||
|
typedef P#(cfg) PALIAS;
|
||||||
|
localparam logic [7:0] hi_val = PALIAS::pp.hi;
|
||||||
|
localparam logic [7:0] lo_val = PALIAS::pp.lo;
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
module t;
|
||||||
|
virtual class C #(
|
||||||
|
parameter int a
|
||||||
|
);
|
||||||
|
localparam int b = a;
|
||||||
|
typedef logic [a-1:0] inner_t;
|
||||||
|
// localparam derived from a typedef inside the same class -
|
||||||
|
// resolveDotToTypedef must constify this when reached via CFG::width.
|
||||||
|
localparam int width = $bits(inner_t);
|
||||||
|
endclass
|
||||||
|
|
||||||
|
typedef C#(8) c8;
|
||||||
|
|
||||||
|
// Deferred lparam (direct Dot)
|
||||||
|
localparam int b8 = c8::b;
|
||||||
|
|
||||||
|
// (1) typedef range driven by a deferred lparam
|
||||||
|
typedef logic [b8-1:0] data_t;
|
||||||
|
data_t data_value;
|
||||||
|
|
||||||
|
// (2) class type-arg uses a deferred lparam value
|
||||||
|
typedef C#(b8) c_from_def;
|
||||||
|
localparam int from_def_b = c_from_def::b;
|
||||||
|
|
||||||
|
// (3) class-scope-resolved typedef computed via $bits(inner_t)
|
||||||
|
localparam int via_bits = c8::width;
|
||||||
|
|
||||||
|
// Wire whose range comes from a deferred lparam
|
||||||
|
logic [b8-1:0] wide_bus;
|
||||||
|
|
||||||
|
// (4) Cell pin $bits consumes a typedef whose packed range is built from
|
||||||
|
// class-scope Dots directly (no intermediate deferred lparam).
|
||||||
|
typedef logic [(c8::b + c8::b - 1):0] direct_use_t;
|
||||||
|
Sub #(.W($bits(direct_use_t))) u_sub ();
|
||||||
|
|
||||||
|
// (5) struct lparam field access via class-scope Dot
|
||||||
|
PairHolder #(.cfg('{hi:'d7, lo:'d11})) u_ph ();
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
`checkh(b8, 32'd8);
|
||||||
|
`checkh(from_def_b, 32'd8);
|
||||||
|
`checkh(via_bits, 32'd8);
|
||||||
|
data_value = '1;
|
||||||
|
`checkh(data_value, 8'hff);
|
||||||
|
wide_bus = '1;
|
||||||
|
`checkh(wide_bus, 8'hff);
|
||||||
|
// sub-case (4): $bits(direct_use_t) = c8::b + c8::b = 8 + 8 = 16
|
||||||
|
`checkh($bits(direct_use_t), 32'd16);
|
||||||
|
// sub-case (5): struct lparam field access folds to the right field
|
||||||
|
`checkh(u_ph.hi_val, 8'd7);
|
||||||
|
`checkh(u_ph.lo_val, 8'd11);
|
||||||
|
$write("*-* All Finished *-*\n");
|
||||||
|
$finish;
|
||||||
|
end
|
||||||
|
endmodule
|
||||||
Loading…
Reference in New Issue