mirror of
https://github.com/verilator/verilator.git
synced 2026-08-29 01:13:53 +02:00
Fix crash on forward-referencing parameters (#8175)
This commit is contained in:
+36
-27
@@ -1893,36 +1893,45 @@ class ParamProcessor final {
|
||||
}
|
||||
}
|
||||
|
||||
// For each missing parameter, get its pin from dependency or direct default
|
||||
for (size_t paramIdx = 0; paramIdx < m_classTypeParams.size(); paramIdx++) {
|
||||
if (pinsByIndex[paramIdx]) continue;
|
||||
const int sourceParamIdx = m_classTypeParams[paramIdx].second;
|
||||
// For each missing parameter, get its pin from dependency or direct default.
|
||||
// A dependent default can reference a parameter declared later (not IEEE, but
|
||||
// we support it here), so repeat until no further parameter can be resolved.
|
||||
for (bool resolvedSome = true; resolvedSome;) {
|
||||
resolvedSome = false;
|
||||
for (size_t paramIdx = 0; paramIdx < m_classTypeParams.size(); paramIdx++) {
|
||||
if (pinsByIndex[paramIdx]) continue;
|
||||
const int sourceParamIdx = m_classTypeParams[paramIdx].second;
|
||||
|
||||
AstPin* newPinp = nullptr;
|
||||
AstPin* newPinp = nullptr;
|
||||
|
||||
// Case 1: Dependent default -> clone the source pin's type
|
||||
if (sourceParamIdx >= 0) newPinp = pinsByIndex[sourceParamIdx]->cloneTree(false);
|
||||
|
||||
// Case 2: Direct default type (e.g., int), create a new pin with that dtype
|
||||
if (!newPinp && defaultTypeNodes[paramIdx]) {
|
||||
AstNodeDType* const dtypep = defaultTypeNodes[paramIdx];
|
||||
newPinp = new AstPin{dtypep->fileline(), static_cast<int>(paramIdx) + 1,
|
||||
"__paramNumber" + cvtToStr(paramIdx + 1),
|
||||
dtypep->cloneTree(false)};
|
||||
}
|
||||
|
||||
if (newPinp) {
|
||||
newPinp->name("__paramNumber" + cvtToStr(paramIdx + 1));
|
||||
newPinp->param(true);
|
||||
newPinp->modPTypep(m_classTypeParams[paramIdx].first);
|
||||
if (classOrPackageRef) {
|
||||
classOrPackageRef->addParamsp(newPinp);
|
||||
} else if (classRefDType) {
|
||||
classRefDType->addParamsp(newPinp);
|
||||
// Case 1: Dependent default -> clone the source pin's type, but only once
|
||||
// the source parameter itself has been resolved (it might be listed later)
|
||||
if (sourceParamIdx >= 0 && pinsByIndex[sourceParamIdx]) {
|
||||
newPinp = pinsByIndex[sourceParamIdx]->cloneTree(false);
|
||||
}
|
||||
|
||||
// Case 2: Direct default type (e.g., int), create a new pin with that dtype
|
||||
if (!newPinp && defaultTypeNodes[paramIdx]) {
|
||||
AstNodeDType* const dtypep = defaultTypeNodes[paramIdx];
|
||||
newPinp = new AstPin{dtypep->fileline(), static_cast<int>(paramIdx) + 1,
|
||||
"__paramNumber" + cvtToStr(paramIdx + 1),
|
||||
dtypep->cloneTree(false)};
|
||||
}
|
||||
|
||||
if (newPinp) {
|
||||
newPinp->name("__paramNumber" + cvtToStr(paramIdx + 1));
|
||||
newPinp->param(true);
|
||||
newPinp->modPTypep(m_classTypeParams[paramIdx].first);
|
||||
if (classOrPackageRef) {
|
||||
classOrPackageRef->addParamsp(newPinp);
|
||||
} else if (classRefDType) {
|
||||
classRefDType->addParamsp(newPinp);
|
||||
}
|
||||
// Update local tracking so future dependent defaults can find it
|
||||
pinsByIndex[paramIdx] = newPinp;
|
||||
if (!paramsp) paramsp = newPinp;
|
||||
resolvedSome = true;
|
||||
}
|
||||
// Update local tracking so future dependent defaults can find it
|
||||
pinsByIndex[paramIdx] = newPinp;
|
||||
if (!paramsp) paramsp = newPinp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Executable
+18
@@ -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,470 @@
|
||||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
//
|
||||
// A parameter default can reference a parameter declared later, so they are
|
||||
// resolved in dependency order, not declaration order. Each case is present
|
||||
// as both a class and a module, and each is specialized three ways: with all
|
||||
// defaults, with the dependent parameter overridden, and with the parameter
|
||||
// it depends on overridden. All results are checked in the top level.
|
||||
//
|
||||
// WARNING: This is not strictly IEEE compliant, which says that parameter
|
||||
// definitions can refer to earlier parameters (IEEE 1800-2023 6.20.1), but
|
||||
// supported here as some tools might accept them.
|
||||
|
||||
// verilog_format: off
|
||||
`define stop $stop
|
||||
`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0x exp=%0x (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0);
|
||||
// verilog_format: on
|
||||
|
||||
package Pkg;
|
||||
localparam int LP = 3;
|
||||
localparam type LT = int;
|
||||
endpackage
|
||||
|
||||
typedef struct packed {
|
||||
logic [7:0] MEMBER;
|
||||
} s_t;
|
||||
|
||||
//======================================================================
|
||||
// Classes
|
||||
|
||||
// A type parameter default referencing a later declared type parameter
|
||||
class ClsType #(
|
||||
type A = B,
|
||||
type B = int
|
||||
);
|
||||
function int a_bits();
|
||||
return $bits(A);
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
// A value parameter default referencing a later declared value parameter
|
||||
class ClsValue #(
|
||||
int A = B,
|
||||
int B = 1
|
||||
);
|
||||
function int a_val();
|
||||
return int'(A);
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
// A range referencing a later declared value parameter
|
||||
class ClsRange #(
|
||||
logic [B:0] A = 0,
|
||||
int B = 1
|
||||
);
|
||||
function int a_bits();
|
||||
return $bits(A);
|
||||
endfunction
|
||||
function int a_val();
|
||||
return int'(A);
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
// A data type referencing a later declared type parameter
|
||||
class ClsDType #(
|
||||
B A = 0,
|
||||
type B = int
|
||||
);
|
||||
function int a_bits();
|
||||
return $bits(A);
|
||||
endfunction
|
||||
function int a_val();
|
||||
return int'(A);
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
// All of the above in a single parameter list
|
||||
class ClsAll #(
|
||||
type TA = TB,
|
||||
int VA = VB,
|
||||
logic [VB:0] RA = 0,
|
||||
TB DA = 0,
|
||||
int VB = 1,
|
||||
type TB = int
|
||||
);
|
||||
function int a_bits();
|
||||
return $bits(TA) + VA + $bits(RA) + $bits(DA);
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
// A chain, resolved in reverse declaration order
|
||||
class ClsChain #(
|
||||
type A = B,
|
||||
type B = C,
|
||||
type C = shortint
|
||||
);
|
||||
function int a_bits();
|
||||
return $bits(A);
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
// A nested class referencing parameters of the enclosing class. The dependent
|
||||
// parameters are the inner class's, which cannot be overridden from outside
|
||||
class ClsNest #(
|
||||
int N = 4,
|
||||
type T = int
|
||||
);
|
||||
class Inner #(
|
||||
int M = N,
|
||||
type U = T
|
||||
);
|
||||
function int m_bits();
|
||||
return M + $bits(U);
|
||||
endfunction
|
||||
endclass
|
||||
Inner inner = new;
|
||||
function int n_bits();
|
||||
return inner.m_bits();
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
// '::' references to package localparams. Both parameters are dependent, as a
|
||||
// package localparam cannot be overridden
|
||||
class ClsPkg #(
|
||||
int N = Pkg::LP,
|
||||
type T = Pkg::LT
|
||||
);
|
||||
function int n_bits();
|
||||
return N + $bits(T);
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
// References to earlier parameters in the same list, of both kinds
|
||||
class ClsBack #(
|
||||
int A = 1,
|
||||
int B = A,
|
||||
type T = int,
|
||||
type U = T,
|
||||
logic [A:0] R = 0,
|
||||
U V = 0
|
||||
);
|
||||
function int b_bits();
|
||||
return B + $bits(U) + $bits(R) + $bits(V);
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
// An assignment pattern key is a member name of the pattern's type, not a
|
||||
// parameter reference, even when a parameter of the same name is declared later
|
||||
class ClsPatKey #(
|
||||
s_t P = '{MEMBER: 8'h1},
|
||||
logic [7:0] MEMBER = 3
|
||||
);
|
||||
function int m_val();
|
||||
return int'(P.MEMBER) + int'(MEMBER);
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
// The value of an assignment pattern member is a reference, unlike the key
|
||||
class ClsPatVal #(
|
||||
s_t P = '{MEMBER: VAL},
|
||||
logic [7:0] VAL = 3
|
||||
);
|
||||
function int m_val();
|
||||
return int'(P.MEMBER) + int'(VAL);
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
//======================================================================
|
||||
// Modules
|
||||
|
||||
module ModType #(
|
||||
type A = B,
|
||||
type B = int
|
||||
);
|
||||
A a;
|
||||
endmodule
|
||||
|
||||
module ModValue #(
|
||||
int A = B,
|
||||
int B = 1
|
||||
);
|
||||
int a = A;
|
||||
endmodule
|
||||
|
||||
module ModRange #(
|
||||
logic [B:0] A = 0,
|
||||
int B = 1
|
||||
);
|
||||
logic [B:0] a = A;
|
||||
endmodule
|
||||
|
||||
module ModDType #(
|
||||
B A = 0,
|
||||
type B = int
|
||||
);
|
||||
B a = A;
|
||||
endmodule
|
||||
|
||||
module ModAll #(
|
||||
type TA = TB,
|
||||
int VA = VB,
|
||||
logic [VB:0] RA = 0,
|
||||
TB DA = 0,
|
||||
int VB = 1,
|
||||
type TB = int
|
||||
);
|
||||
TA ta;
|
||||
int va = VA;
|
||||
logic [VB:0] ra = RA;
|
||||
TB da = DA;
|
||||
endmodule
|
||||
|
||||
module ModChain #(
|
||||
type A = B,
|
||||
type B = C,
|
||||
type C = shortint
|
||||
);
|
||||
A a;
|
||||
endmodule
|
||||
|
||||
module ModNest #(
|
||||
int N = 4,
|
||||
type T = int
|
||||
);
|
||||
class Inner #(
|
||||
int M = N,
|
||||
type U = T
|
||||
);
|
||||
function int m_bits();
|
||||
return M + $bits(U);
|
||||
endfunction
|
||||
endclass
|
||||
Inner inner = new;
|
||||
endmodule
|
||||
|
||||
module ModPkg #(
|
||||
int N = Pkg::LP,
|
||||
type T = Pkg::LT
|
||||
);
|
||||
T t;
|
||||
int n = N;
|
||||
endmodule
|
||||
|
||||
module ModBack #(
|
||||
int A = 1,
|
||||
int B = A,
|
||||
type T = int,
|
||||
type U = T,
|
||||
logic [A:0] R = 0,
|
||||
U V = 0
|
||||
);
|
||||
int b = B;
|
||||
U v = V;
|
||||
logic [A:0] r = R;
|
||||
endmodule
|
||||
|
||||
module ModPatKey #(
|
||||
s_t P = '{MEMBER: 8'h1},
|
||||
logic [7:0] MEMBER = 3
|
||||
);
|
||||
logic [7:0] m = P.MEMBER + MEMBER;
|
||||
endmodule
|
||||
|
||||
module ModPatVal #(
|
||||
s_t P = '{MEMBER: VAL},
|
||||
logic [7:0] VAL = 3
|
||||
);
|
||||
logic [7:0] m = P.MEMBER + VAL;
|
||||
endmodule
|
||||
|
||||
//======================================================================
|
||||
|
||||
module t;
|
||||
// Defaults, dependent parameter overridden, depended-on parameter overridden
|
||||
ClsType ct_d = new;
|
||||
ClsType #(.A(byte)) ct_dep = new;
|
||||
ClsType #(.B(shortint)) ct_nod = new;
|
||||
|
||||
ClsValue cv_d = new;
|
||||
ClsValue #(.A(7)) cv_dep = new;
|
||||
ClsValue #(.B(5)) cv_nod = new;
|
||||
|
||||
ClsRange cr_d = new;
|
||||
ClsRange #(.A(3)) cr_dep = new;
|
||||
ClsRange #(.B(3)) cr_nod = new;
|
||||
|
||||
ClsDType cd_d = new;
|
||||
ClsDType #(.A(5)) cd_dep = new;
|
||||
ClsDType #(.B(byte)) cd_nod = new;
|
||||
|
||||
ClsAll ca_d = new;
|
||||
ClsAll #(.TA(byte), .VA(4)) ca_dep = new;
|
||||
ClsAll #(.VB(3), .TB(byte)) ca_nod = new;
|
||||
|
||||
ClsChain cc_d = new;
|
||||
ClsChain #(.A(int)) cc_dep = new;
|
||||
ClsChain #(.C(byte)) cc_nod = new;
|
||||
|
||||
ClsNest cn_d = new;
|
||||
ClsNest #(.N(7)) cn_dep = new;
|
||||
ClsNest #(.T(byte)) cn_nod = new;
|
||||
|
||||
ClsPkg cp_d = new;
|
||||
ClsPkg #(.N(10)) cp_dep = new;
|
||||
ClsPkg #(.T(byte)) cp_nod = new;
|
||||
|
||||
ClsBack cb_d = new;
|
||||
ClsBack #(.B(5), .U(byte)) cb_dep = new;
|
||||
ClsBack #(.A(2), .T(shortint)) cb_nod = new;
|
||||
|
||||
ClsPatKey cpk_d = new;
|
||||
ClsPatKey #(.P(8'h5)) cpk_dep = new;
|
||||
ClsPatKey #(.MEMBER(8'd10)) cpk_nod = new;
|
||||
|
||||
ClsPatVal cpv_d = new;
|
||||
ClsPatVal #(.P(8'h2)) cpv_dep = new;
|
||||
ClsPatVal #(.VAL(8'd7)) cpv_nod = new;
|
||||
|
||||
ModType mt_d ();
|
||||
ModType #(.A(byte)) mt_dep ();
|
||||
ModType #(.B(shortint)) mt_nod ();
|
||||
|
||||
ModValue mv_d ();
|
||||
ModValue #(.A(7)) mv_dep ();
|
||||
ModValue #(.B(5)) mv_nod ();
|
||||
|
||||
ModRange mr_d ();
|
||||
ModRange #(.A(3)) mr_dep ();
|
||||
ModRange #(.B(3)) mr_nod ();
|
||||
|
||||
ModDType md_d ();
|
||||
ModDType #(.A(5)) md_dep ();
|
||||
ModDType #(.B(byte)) md_nod ();
|
||||
|
||||
ModAll ma_d ();
|
||||
ModAll #(.TA(byte), .VA(4)) ma_dep ();
|
||||
ModAll #(.VB(3), .TB(byte)) ma_nod ();
|
||||
|
||||
ModChain mc_d ();
|
||||
ModChain #(.A(int)) mc_dep ();
|
||||
ModChain #(.C(byte)) mc_nod ();
|
||||
|
||||
ModNest mn_d ();
|
||||
ModNest #(.N(7)) mn_dep ();
|
||||
ModNest #(.T(byte)) mn_nod ();
|
||||
|
||||
ModPkg mp_d ();
|
||||
ModPkg #(.N(10)) mp_dep ();
|
||||
ModPkg #(.T(byte)) mp_nod ();
|
||||
|
||||
ModBack mb_d ();
|
||||
ModBack #(.B(5), .U(byte)) mb_dep ();
|
||||
ModBack #(.A(2), .T(shortint)) mb_nod ();
|
||||
|
||||
ModPatKey mpk_d ();
|
||||
ModPatKey #(.P(8'h5)) mpk_dep ();
|
||||
ModPatKey #(.MEMBER(8'd10)) mpk_nod ();
|
||||
|
||||
ModPatVal mpv_d ();
|
||||
ModPatVal #(.P(8'h2)) mpv_dep ();
|
||||
ModPatVal #(.VAL(8'd7)) mpv_nod ();
|
||||
|
||||
initial begin
|
||||
`checkh(ct_d.a_bits(), 32);
|
||||
`checkh(ct_dep.a_bits(), 8);
|
||||
`checkh(ct_nod.a_bits(), 16);
|
||||
|
||||
`checkh(cv_d.a_val(), 1);
|
||||
`checkh(cv_dep.a_val(), 7);
|
||||
`checkh(cv_nod.a_val(), 5);
|
||||
|
||||
`checkh(cr_d.a_bits(), 2);
|
||||
`checkh(cr_d.a_val(), 0);
|
||||
`checkh(cr_dep.a_bits(), 2);
|
||||
`checkh(cr_dep.a_val(), 3);
|
||||
`checkh(cr_nod.a_bits(), 4);
|
||||
`checkh(cr_nod.a_val(), 0);
|
||||
|
||||
`checkh(cd_d.a_bits(), 32);
|
||||
`checkh(cd_d.a_val(), 0);
|
||||
`checkh(cd_dep.a_bits(), 32);
|
||||
`checkh(cd_dep.a_val(), 5);
|
||||
`checkh(cd_nod.a_bits(), 8);
|
||||
`checkh(cd_nod.a_val(), 0);
|
||||
|
||||
`checkh(ca_d.a_bits(), 67);
|
||||
`checkh(ca_dep.a_bits(), 46);
|
||||
`checkh(ca_nod.a_bits(), 23);
|
||||
|
||||
`checkh(cc_d.a_bits(), 16);
|
||||
`checkh(cc_dep.a_bits(), 32);
|
||||
`checkh(cc_nod.a_bits(), 8);
|
||||
|
||||
`checkh(cn_d.n_bits(), 36);
|
||||
`checkh(cn_dep.n_bits(), 39);
|
||||
`checkh(cn_nod.n_bits(), 12);
|
||||
|
||||
`checkh(cp_d.n_bits(), 35);
|
||||
`checkh(cp_dep.n_bits(), 42);
|
||||
`checkh(cp_nod.n_bits(), 11);
|
||||
|
||||
`checkh(cb_d.b_bits(), 67);
|
||||
`checkh(cb_dep.b_bits(), 23);
|
||||
`checkh(cb_nod.b_bits(), 37);
|
||||
|
||||
`checkh(cpk_d.m_val(), 4);
|
||||
`checkh(cpk_dep.m_val(), 8);
|
||||
`checkh(cpk_nod.m_val(), 11);
|
||||
|
||||
`checkh(cpv_d.m_val(), 6);
|
||||
`checkh(cpv_dep.m_val(), 5);
|
||||
`checkh(cpv_nod.m_val(), 14);
|
||||
|
||||
`checkh($bits(mt_d.a), 32);
|
||||
`checkh($bits(mt_dep.a), 8);
|
||||
`checkh($bits(mt_nod.a), 16);
|
||||
|
||||
`checkh(mv_d.a, 1);
|
||||
`checkh(mv_dep.a, 7);
|
||||
`checkh(mv_nod.a, 5);
|
||||
|
||||
`checkh($bits(mr_d.a), 2);
|
||||
`checkh(mr_d.a, 0);
|
||||
`checkh($bits(mr_dep.a), 2);
|
||||
`checkh(mr_dep.a, 3);
|
||||
`checkh($bits(mr_nod.a), 4);
|
||||
`checkh(mr_nod.a, 0);
|
||||
|
||||
`checkh($bits(md_d.a), 32);
|
||||
`checkh(md_d.a, 0);
|
||||
`checkh($bits(md_dep.a), 32);
|
||||
`checkh(md_dep.a, 5);
|
||||
`checkh($bits(md_nod.a), 8);
|
||||
`checkh(md_nod.a, 0);
|
||||
|
||||
`checkh($bits(ma_d.ta) + ma_d.va + $bits(ma_d.ra) + $bits(ma_d.da), 67);
|
||||
`checkh($bits(ma_dep.ta) + ma_dep.va + $bits(ma_dep.ra) + $bits(ma_dep.da), 46);
|
||||
`checkh($bits(ma_nod.ta) + ma_nod.va + $bits(ma_nod.ra) + $bits(ma_nod.da), 23);
|
||||
|
||||
`checkh($bits(mc_d.a), 16);
|
||||
`checkh($bits(mc_dep.a), 32);
|
||||
`checkh($bits(mc_nod.a), 8);
|
||||
|
||||
`checkh(mn_d.inner.m_bits(), 36);
|
||||
`checkh(mn_dep.inner.m_bits(), 39);
|
||||
`checkh(mn_nod.inner.m_bits(), 12);
|
||||
|
||||
`checkh(mp_d.n + $bits(mp_d.t), 35);
|
||||
`checkh(mp_dep.n + $bits(mp_dep.t), 42);
|
||||
`checkh(mp_nod.n + $bits(mp_nod.t), 11);
|
||||
|
||||
`checkh(mb_d.b + $bits(mb_d.v) + $bits(mb_d.r) + $bits(mb_d.v), 67);
|
||||
`checkh(mb_dep.b + $bits(mb_dep.v) + $bits(mb_dep.r) + $bits(mb_dep.v), 23);
|
||||
`checkh(mb_nod.b + $bits(mb_nod.v) + $bits(mb_nod.r) + $bits(mb_nod.v), 37);
|
||||
|
||||
`checkh(mpk_d.m, 4);
|
||||
`checkh(mpk_dep.m, 8);
|
||||
`checkh(mpk_nod.m, 11);
|
||||
|
||||
`checkh(mpv_d.m, 6);
|
||||
`checkh(mpv_dep.m, 5);
|
||||
`checkh(mpv_nod.m, 14);
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
Reference in New Issue
Block a user