Fix parameterized class module parameters (#6754) (#6834)

This commit is contained in:
em2machine 2025-12-19 18:57:15 +01:00 committed by GitHub
parent 5244766b7b
commit 3ceac0b37e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 497 additions and 7 deletions

View File

@ -796,7 +796,7 @@ public:
void classOrPackagep(AstNodeModule* nodep) {
m_classOrPackageNodep = reinterpret_cast<AstNode*>(nodep);
}
bool hasDType() const override VL_MT_SAFE { return false; }
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
string emitC() override { V3ERROR_NA_RETURN(""); }
bool cleanOut() const override { V3ERROR_NA_RETURN(true); }

View File

@ -4138,7 +4138,8 @@ class ConstVisitor final : public VNVisitor {
void visit(AstNode* nodep) override {
// Default: Just iterate
if (m_required) {
if (VN_IS(nodep, NodeDType) || VN_IS(nodep, Range) || VN_IS(nodep, SliceSel)) {
if (VN_IS(nodep, NodeDType) || VN_IS(nodep, Range) || VN_IS(nodep, SliceSel)
|| VN_IS(nodep, Dot)) {
// Ignore dtypes for parameter type pins
} else {
nodep->v3error("Expecting expression to be constant, but can't convert a "

View File

@ -5345,6 +5345,20 @@ class LinkDotResolveVisitor final : public VNVisitor {
nodep->typedefp(defp);
nodep->classOrPackagep(foundp->classOrPackagep());
resolvedCapturedTypedef = true;
// class capture: capture typedef references inside parameterized classes
// Only capture if we're referencing from OUTSIDE the class (not
// self-references)
if (m_statep->forPrimary()) {
AstClass* const classp = VN_CAST(nodep->classOrPackagep(), Class);
if (classp && classp->hasGParam() && classp != m_modp) {
UINFO(9, indent()
<< "class capture add typedef name=" << nodep->name()
<< " class=" << classp->name() << " typedef=" << defp);
V3LinkDotIfaceCapture::addClass(nodep, classp, m_modp, defp);
}
}
} else if (AstParamTypeDType* const defp
= foundp ? VN_CAST(foundp->nodep(), ParamTypeDType) : nullptr) {
if (defp == nodep->backp()) { // Where backp is typically typedef

View File

@ -57,13 +57,22 @@ void V3LinkDotIfaceCapture::add(AstRefDType* refp, AstCell* cellp, AstNodeModule
AstTypedef* typedefp, AstNodeModule* typedefOwnerModp,
AstVar* ifacePortVarp) {
if (!refp) return;
if (!typedefp) typedefp = refp->typedefp();
if (!typedefOwnerModp && typedefp) typedefOwnerModp = findOwnerModule(typedefp);
s_map[refp] = CapturedIfaceTypedef{
refp, cellp, ownerModp, typedefp, typedefOwnerModp, nullptr, ifacePortVarp};
CaptureType::IFACE, refp, cellp, nullptr, ownerModp, typedefp,
typedefOwnerModp, nullptr, ifacePortVarp};
}
void V3LinkDotIfaceCapture::addClass(AstRefDType* refp, AstClass* origClassp,
AstNodeModule* ownerModp, AstTypedef* typedefp,
AstNodeModule* typedefOwnerModp) {
if (!refp) return;
if (!typedefp) typedefp = refp->typedefp();
if (!typedefOwnerModp && typedefp) typedefOwnerModp = findOwnerModule(typedefp);
s_map[refp] = CapturedIfaceTypedef{CaptureType::CLASS, refp, nullptr,
origClassp, ownerModp, typedefp,
typedefOwnerModp, nullptr, nullptr};
}
const V3LinkDotIfaceCapture::CapturedIfaceTypedef*
@ -99,6 +108,18 @@ bool V3LinkDotIfaceCapture::replaceTypedef(const AstRefDType* refp, AstTypedef*
if (it == s_map.end()) return false;
it->second.typedefp = newTypedefp;
it->second.typedefOwnerModp = findOwnerModule(newTypedefp);
// For CLASS captures, update the RefDType node directly
if (it->second.captureType == CaptureType::CLASS && it->second.refp) {
it->second.refp->typedefp(newTypedefp);
// Also update classOrPackagep to point to the specialized class
if (AstClass* const newClassp = VN_CAST(it->second.typedefOwnerModp, Class)) {
it->second.refp->classOrPackagep(newClassp);
}
UINFO(9, "class capture updated RefDType typedefp: " << it->second.refp << " -> "
<< newTypedefp);
}
finalizeCapturedEntry(it, "typedef clone");
return true;
}

View File

@ -31,9 +31,12 @@
class V3LinkDotIfaceCapture final {
public:
enum class CaptureType { IFACE, CLASS };
struct CapturedIfaceTypedef final {
CaptureType captureType = CaptureType::IFACE;
AstRefDType* refp = nullptr;
AstCell* cellp = nullptr;
AstCell* cellp = nullptr; // now for IFACE captures
AstClass* origClassp = nullptr; // new for CLASS captures
// Module where the RefDType lives
AstNodeModule* ownerModp = nullptr;
// Typedef definition being referenced
@ -69,6 +72,9 @@ public:
static void add(AstRefDType* refp, AstCell* cellp, AstNodeModule* ownerModp,
AstTypedef* typedefp = nullptr, AstNodeModule* typedefOwnerModp = nullptr,
AstVar* ifacePortVarp = nullptr);
static void addClass(AstRefDType* refp, AstClass* origClassp, AstNodeModule* ownerModp,
AstTypedef* typedefp = nullptr,
AstNodeModule* typedefOwnerModp = nullptr);
static const CapturedIfaceTypedef* find(const AstRefDType* refp);
static void forEach(const std::function<void(const CapturedIfaceTypedef&)>& fn);
static void forEachOwned(const AstNodeModule* ownerModp,

View File

@ -810,6 +810,27 @@ class ParamProcessor final {
}
}
// Helper to resolve DOT to RefDType for class type references
void resolveDotToTypedef(AstNode* exprp) {
AstDot* const dotp = VN_CAST(exprp, Dot);
if (!dotp) return;
const AstClassOrPackageRef* const classRefp = VN_CAST(dotp->lhsp(), ClassOrPackageRef);
if (!classRefp) return;
const AstClass* const lhsClassp = VN_CAST(classRefp->classOrPackageSkipp(), Class);
if (!lhsClassp) return;
AstParseRef* const parseRefp = VN_CAST(dotp->rhsp(), ParseRef);
if (!parseRefp) return;
AstTypedef* const tdefp
= VN_CAST(m_memberMap.findMember(lhsClassp, parseRefp->name()), Typedef);
if (tdefp) {
AstRefDType* const refp = new AstRefDType{dotp->fileline(), tdefp->name()};
refp->typedefp(tdefp);
dotp->replaceWith(refp);
VL_DO_DANGLING(dotp->deleteTree(), dotp);
}
}
void cellPinCleanup(AstNode* nodep, AstPin* pinp, AstNodeModule* srcModp, string& longnamer,
bool& any_overridesr) {
if (!pinp->exprp()) return; // No-connect
@ -869,6 +890,10 @@ class ParamProcessor final {
}
}
} else if (AstParamTypeDType* const modvarp = pinp->modPTypep()) {
// Handle DOT with ParseRef RHS (e.g., p_class#(8)::p_type)
// by this point ClassOrPackageRef should be updated to point to the specialized class.
resolveDotToTypedef(pinp->exprp());
AstNodeDType* rawTypep = VN_CAST(pinp->exprp(), NodeDType);
if (rawTypep) V3Width::widthParamsEdit(rawTypep);
AstNodeDType* exprp = rawTypep ? rawTypep->skipRefToNonRefp() : nullptr;

View File

@ -0,0 +1,18 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2025 by Wilson Snyder. 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-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()

View File

@ -0,0 +1,41 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed into the Public Domain, for any use,
// without warranty.
// 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 (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0);
// verilog_format: on
class p_class #(
parameter TLEN = 2,
localparam type T = logic [TLEN-1:0]
);
typedef struct packed {
T a;
} p_type;
endclass
module p_mod #(
parameter type T = logic
);
initial begin
#1;
`checkd($bits(T), 8);
end
endmodule
module the_top #()();
typedef p_class#(8)::p_type p_type_t;
p_mod #(p_type_t) p1();
p_mod #( p_class#(8)::p_type ) p2();
initial begin
#2;
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -0,0 +1,18 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2025 by Wilson Snyder. 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-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()

View File

@ -0,0 +1,44 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2025 by Wilson Snyder.
// 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 (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0);
// verilog_format: on
class p_class #(
parameter TLEN = 2,
localparam type T = logic [TLEN-1:0]
);
typedef struct packed {
T a, b;
} p_type;
endclass
module p_mod #(
parameter type T = logic
);
initial begin
#1;
`checkd($bits(T), 16);
end
endmodule
module the_top #() ();
p_mod #(.T(p_class#(8)::p_type)) p1();
typedef p_class#(8) p_class_8;
p_mod #(.T(p_class_8::p_type)) p2();
typedef p_class#(8)::p_type p_class_type_8;
p_mod #(.T(p_class_type_8)) p4();
initial begin
#2;
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -0,0 +1,18 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2025 by Wilson Snyder. 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-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()

View File

@ -0,0 +1,41 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2025 by Wilson Snyder.
// 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 (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0);
// verilog_format: on
class p_class #(
parameter TLEN = 2,
localparam type T = logic [TLEN-1:0]
);
typedef struct packed {
T a, b;
} p_type;
endclass
module p_mod #(
parameter type T = logic
);
initial begin
#1;
`checkd($bits(T), 16);
end
endmodule
module the_top #() ();
typedef p_class#(8) p_class_8;
typedef p_class_8::p_type p_type_8;
p_mod #(.T(p_type_8)) p3();
initial begin
#2;
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -0,0 +1,18 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2025 by Wilson Snyder. 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-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()

View File

@ -0,0 +1,47 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2025 by Wilson Snyder.
// 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 (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0);
// verilog_format: on
class p_class #(
parameter TLEN = 2,
localparam type T = logic [TLEN-1:0]
);
typedef struct packed {
T a, b;
} p_type;
endclass
module p_mod #(
parameter type T = logic
);
initial begin
#1;
`checkd($bits(T), 16);
end
endmodule
module the_top #() ();
p_mod #(.T(p_class#(8)::p_type)) p1();
typedef p_class#(8) p_class_8;
p_mod #(.T(p_class_8::p_type)) p2();
typedef p_class_8::p_type p_type_8;
p_mod #(.T(p_type_8)) p3();
typedef p_class#(8)::p_type p_class_type_8;
p_mod #(.T(p_class_type_8)) p4();
initial begin
#2;
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -0,0 +1,18 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2025 by Wilson Snyder. 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-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()

View File

@ -0,0 +1,42 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2025 by Wilson Snyder.
// 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 (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0);
// verilog_format: on
class xxx_class #(parameter int X = 1);
typedef logic [X-1:0] cmd_tag_t;
endclass
module mod_a #(parameter p_width=16) ();
endmodule
module mod_b #(parameter type io_type_t = logic) (
io_type_t io
);
initial begin
#1;
`checkd($bits(io), 16);
end
endmodule
module the_top();
xxx_class#(16)::cmd_tag_t tag;
mod_a #($bits(tag)) t0();
typedef xxx_class#(16)::cmd_tag_t tag_t;
tag_t tag_io;
mod_b #(tag_t) t1(tag_io);
initial begin
#2;
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -0,0 +1,18 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2025 by Wilson Snyder. 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-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()

View File

@ -0,0 +1,29 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2025 by Wilson Snyder.
// 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 (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0);
// verilog_format: on
class xxx_class #(parameter int X = 1);
typedef logic [X-1:0] cmd_tag_t;
endclass
module mod_a #(parameter p_width=16) ();
endmodule
module the_top();
xxx_class#(16)::cmd_tag_t tag;
mod_a #($bits(tag)) t0();
initial begin
#2;
`checkd($bits(tag), 16);
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -0,0 +1,18 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2025 by Wilson Snyder. 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-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()

View File

@ -0,0 +1,53 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2025 by Wilson Snyder.
// 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 (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0);
// verilog_format: on
package pf;
typedef struct packed {
int unsigned CcNumTl;
int unsigned PqSize;
} cfg_t;
endpackage
virtual class xxx_class #(parameter pf::cfg_t Cfg);
typedef struct packed {
logic [$clog2(Cfg.CcNumTl)-1:0] tl_index;
logic [$clog2(Cfg.PqSize)-1:0] pq_index;
} cmd_tag_t;
endclass
module mod2 #(parameter p_width=16) (
output logic [p_width-1:0] q,
input logic [p_width-1:0] d
);
assign q = d;
initial begin
#1;
`checkd(p_width, 7);
end
endmodule
module top();
localparam pf::cfg_t Cfg0 = '{
CcNumTl:8
,PqSize:12
};
xxx_class#(Cfg0)::cmd_tag_t tag, tag_q;
mod2 #($bits(tag)) t0(tag_q, tag);
initial begin
#1;
$write("*-* All Finished *-*\n");
$finish;
end
endmodule