Fix for HIERPARAM - relax checking (#7570) (#7690)

This commit is contained in:
em2machine 2026-06-06 17:55:47 +02:00 committed by GitHub
parent 85d9c38ebf
commit 680ef8dda9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 115 additions and 11 deletions

View File

@ -370,6 +370,24 @@ public:
// clang-format on
return names[m_e];
}
// True for attributes that read an operand's type rather than its value, such as $bits.
bool isTypeQuery() const {
switch (m_e) {
case DIM_BITS:
case DIM_BITS_OR_NUMBER:
case DIM_DIMENSIONS:
case DIM_HIGH:
case DIM_INCREMENT:
case DIM_LEFT:
case DIM_LOW:
case DIM_RIGHT:
case DIM_SIZE:
case DIM_UNPK_DIMENSIONS:
case TYPEID:
case TYPENAME: return true;
default: return false;
}
}
VAttrType()
: m_e{ILLEGAL} {}
// cppcheck-suppress noExplicitConstructor

View File

@ -2722,10 +2722,14 @@ class ParamVisitor final : public VNVisitor {
// LCOV_EXCL_STOP
}
void checkParamNotHier(AstNode* valuep) {
if (!valuep) return;
valuep->foreachAndNext([&](const AstNodeExpr* exprp) {
if (const AstVarXRef* const refp = VN_CAST(exprp, VarXRef)) {
// Flag hierarchical refs in a parameter value. Single top-down pass.
void checkParamNotHierRecurse(AstNode* nodep, bool underTypeQuery = false) {
for (; nodep; nodep = nodep->nextp()) {
// Refs read only for their type ($bits etc.) are allowed
const AstAttrOf* const attrp = VN_CAST(nodep, AttrOf);
const bool childUnderQuery
= underTypeQuery || (attrp && attrp->attrType().isTypeQuery());
if (const AstVarXRef* const refp = VN_CAST(nodep, VarXRef)) {
// Allow hierarchical ref to interface params through interface/modport ports
// or local interface instances
bool isIfaceRef = false;
@ -2735,18 +2739,21 @@ class ParamVisitor final : public VNVisitor {
= !refname.empty()
&& (m_ifacePortNames.count(refname) || m_ifaceInstCells.count(refname));
}
if (!isIfaceRef) {
if (!isIfaceRef && !underTypeQuery) {
refp->v3warn(HIERPARAM, "Parameter values cannot use hierarchical values"
" (IEEE 1800-2023 6.20.2)");
}
} else if (const AstNodeFTaskRef* refp = VN_CAST(exprp, NodeFTaskRef)) {
} else if (const AstNodeFTaskRef* const refp = VN_CAST(nodep, NodeFTaskRef)) {
if (refp->dotted() != "") {
refp->v3error("Parameter values cannot call hierarchical functions"
" (IEEE 1800-2023 6.20.2)");
}
}
});
checkParamNotHierRecurse(nodep->op1p(), childUnderQuery);
checkParamNotHierRecurse(nodep->op2p(), childUnderQuery);
checkParamNotHierRecurse(nodep->op3p(), childUnderQuery);
checkParamNotHierRecurse(nodep->op4p(), childUnderQuery);
}
}
// Deparameterize and constify nested interface cells within ifaceModp.
@ -2888,7 +2895,7 @@ class ParamVisitor final : public VNVisitor {
iterateChildren(nodep);
}
void visit(AstCell* nodep) override {
checkParamNotHier(nodep->paramsp());
checkParamNotHierRecurse(nodep->paramsp());
if (VN_IS(nodep->modp(), Iface)) m_ifaceInstCells.emplace(nodep->name(), nodep);
visitCellOrClassRef(nodep, VN_IS(nodep->modp(), Iface));
}
@ -2896,7 +2903,7 @@ class ParamVisitor final : public VNVisitor {
if (nodep->ifacep()) visitCellOrClassRef(nodep, true);
}
void visit(AstClassRefDType* nodep) override {
checkParamNotHier(nodep->paramsp());
checkParamNotHierRecurse(nodep->paramsp());
visitCellOrClassRef(nodep, false);
}
void visit(AstClassOrPackageRef* nodep) override {
@ -2913,7 +2920,7 @@ class ParamVisitor final : public VNVisitor {
if (nodep->isIfaceRef()) { m_ifacePortNames.insert(nodep->name()); }
iterateChildren(nodep);
if (nodep->isParam()) {
checkParamNotHier(nodep->valuep());
checkParamNotHierRecurse(nodep->valuep());
if (!nodep->valuep() && !VN_IS(m_modp, Class)) {
nodep->v3error("Parameter without default value is never given value"
<< " (IEEE 1800-2023 6.20.1): " << nodep->prettyNameQ());

View File

@ -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()

View File

@ -0,0 +1,61 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// $bits() of interface member signals used as a child module parameter value.
// $bits reads the type, not the hierarchical value, so it is legal in a
// parameter (IEEE 1800-2023 6.20.2 forbids hierarchical values only).
//
// 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 axi_if #(
parameter int ID_W = 8,
parameter int ADDR_W = 32
);
logic [ID_W-1:0] AWID;
logic [ADDR_W-1:0] AWADDR;
logic [7:0] AWLEN;
endinterface
module chkmod #(
parameter int PAYLOAD_WIDTH = 1,
parameter int EXPECT = 1
);
initial `checkh(PAYLOAD_WIDTH, EXPECT);
endmodule
module bridge #(
parameter int EXPECT = 1
) (
axi_if axi
);
// $bits of a concat of interface members
chkmod #(
.PAYLOAD_WIDTH($bits({axi.AWID, axi.AWADDR, axi.AWLEN})),
.EXPECT(EXPECT)
) u_concat ();
// $bits of a single interface member
chkmod #(
.PAYLOAD_WIDTH($bits(axi.AWADDR)),
.EXPECT(EXPECT - 12 - 8)
) u_single ();
endmodule
module t;
axi_if #(.ID_W(12), .ADDR_W(64)) if0 (); // 12 + 64 + 8 = 84
axi_if #(.ID_W(12), .ADDR_W(16)) if1 (); // 12 + 16 + 8 = 36
bridge #(.EXPECT(84)) dut0 (.axi(if0));
bridge #(.EXPECT(36)) dut1 (.axi(if1));
initial begin
#1;
$write("*-* All Finished *-*\n");
$finish;
end
endmodule