Fix MULTIDRIVEN in generates (#7709)

This commit is contained in:
Todd Strader 2026-06-03 19:50:56 -04:00 committed by GitHub
parent 72db0b575e
commit 7bd41bfbb9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 79 additions and 32 deletions

View File

@ -29,6 +29,7 @@
#include "V3Stats.h"
#include "V3UndrivenCapture.h"
#include "V3Width.h"
#include <vector>
@ -369,6 +370,7 @@ class UndrivenVisitor final : public VNVisitorConst {
bool m_inProcAssign = false; // In procedural assignment
bool m_inFTaskRef = false; // In function or task call
bool m_inInoutOrRefPin = false; // Connected to pin that is inout
bool m_inSelLhs = false; // Iterating the fromp of an AstSel (a partial-bit write target)
const AstNodeFTask* m_taskp = nullptr; // Current task
const AstAlways* m_alwaysp = nullptr; // Current always of either type
const AstAlways* m_alwaysCombp = nullptr; // Current always if combo, otherwise nullptr
@ -473,8 +475,11 @@ class UndrivenVisitor final : public VNVisitorConst {
entryp->usedBit(lsb, nodep->width(), varrefp);
}
} else {
// else other varrefs handled as unknown mess in AstVarRef
iterateChildrenConst(nodep);
// skip over static longest static prefix
iterateConst(nodep->lsbp());
VL_RESTORER(m_inSelLhs);
m_inSelLhs = !V3Width::selectNonConstantRecurse(nodep->lsbp(), /*inSel=*/true);
iterateConst(nodep->fromp());
}
}
void visit(AstNodeVarRef* nodep) override {
@ -530,7 +535,7 @@ class UndrivenVisitor final : public VNVisitorConst {
if (entryp->isDrivenWhole() && !m_inBBox && !VN_IS(nodep, VarXRef)
&& !VN_IS(nodep->dtypep()->skipRefp(), UnpackArrayDType) && !sameFileLine
&& !entryp->isUnderGen() && otherWritep && !entryp->isFtaskDriven()
&& !ftaskDef
&& !ftaskDef && !m_inSelLhs
&& !nodep->varp()->fileline()->warnIsOff(V3ErrorCode::MULTIDRIVEN)) {
const bool otherWriteIsStaticInit
= nodep->varp()->hasUserInit() && otherWritep == entryp->initStaticp();

View File

@ -9948,7 +9948,7 @@ class WidthVisitor final : public VNVisitor {
}
void checkForceReleaseLhs(AstNode* nodep, AstNode* lhsp) {
// V3Force can't check as vector may have expanded, or propagated constant into index
if (AstNode* const selNodep = selectNonConstantRecurse(lhsp))
if (AstNode* const selNodep = V3Width::selectNonConstantRecurse(lhsp))
nodep->v3error((VN_IS(nodep, Release) ? "Release"s : "Force"s)
+ " left-hand-side must not have variable bit/part select "
"(IEEE 1800-2023 10.6.2)\n"
@ -9956,34 +9956,6 @@ class WidthVisitor final : public VNVisitor {
<< selNodep->warnOther() << "... Location of non-constant index\n"
<< selNodep->warnContextSecondary());
}
AstNode* selectNonConstantRecurse(AstNode* nodep, bool inSel = false) {
// If node has a non-constant select, return that select
AstNode* resultp = nullptr;
if (AstNodeSel* const anodep = VN_CAST(nodep, NodeSel)) {
resultp = selectNonConstantRecurse(anodep->fromp(), inSel);
if (resultp) return resultp;
resultp = selectNonConstantRecurse(anodep->bitp(), true);
} else if (AstSel* const anodep = VN_CAST(nodep, Sel)) {
resultp = selectNonConstantRecurse(anodep->fromp(), inSel);
if (resultp) return resultp;
resultp = selectNonConstantRecurse(anodep->lsbp(), true);
} else if (AstNodeVarRef* const anodep = VN_CAST(nodep, NodeVarRef)) {
if (inSel && !anodep->varp()->isParam() && !anodep->varp()->isGenVar()) return anodep;
} else {
if (AstNode* const refp = nodep->op1p())
resultp = selectNonConstantRecurse(refp, inSel);
if (resultp) return resultp;
if (AstNode* const refp = nodep->op2p())
resultp = selectNonConstantRecurse(refp, inSel);
if (resultp) return resultp;
if (AstNode* const refp = nodep->op3p())
resultp = selectNonConstantRecurse(refp, inSel);
if (resultp) return resultp;
if (AstNode* const refp = nodep->op4p())
resultp = selectNonConstantRecurse(refp, inSel);
}
return resultp;
}
//----------------------------------------------------------------------
// METHODS - special type detection
@ -10139,3 +10111,28 @@ AstNode* V3Width::widthGenerateParamsEdit(
// No WidthRemoveVisitor, as don't want to drop $signed etc inside gen blocks
return nodep;
}
AstNode* V3Width::selectNonConstantRecurse(AstNode* nodep, bool inSel) {
// If node has a non-constant select, return that select
AstNode* resultp = nullptr;
if (AstNodeSel* const anodep = VN_CAST(nodep, NodeSel)) {
resultp = selectNonConstantRecurse(anodep->fromp(), inSel);
if (resultp) return resultp;
resultp = selectNonConstantRecurse(anodep->bitp(), true);
} else if (AstSel* const anodep = VN_CAST(nodep, Sel)) {
resultp = selectNonConstantRecurse(anodep->fromp(), inSel);
if (resultp) return resultp;
resultp = selectNonConstantRecurse(anodep->lsbp(), true);
} else if (AstNodeVarRef* const anodep = VN_CAST(nodep, NodeVarRef)) {
if (inSel && !anodep->varp()->isParam() && !anodep->varp()->isGenVar()) return anodep;
} else {
if (AstNode* const refp = nodep->op1p()) resultp = selectNonConstantRecurse(refp, inSel);
if (resultp) return resultp;
if (AstNode* const refp = nodep->op2p()) resultp = selectNonConstantRecurse(refp, inSel);
if (resultp) return resultp;
if (AstNode* const refp = nodep->op3p()) resultp = selectNonConstantRecurse(refp, inSel);
if (resultp) return resultp;
if (AstNode* const refp = nodep->op4p()) resultp = selectNonConstantRecurse(refp, inSel);
}
return resultp;
}

View File

@ -31,6 +31,7 @@ public:
static void width(AstNetlist* nodep) VL_MT_DISABLED;
static AstNode* widthParamsEdit(AstNode* nodep) VL_MT_DISABLED;
static AstNode* widthGenerateParamsEdit(AstNode* nodep) VL_MT_DISABLED;
static AstNode* selectNonConstantRecurse(AstNode* nodep, bool inSel = false) VL_MT_DISABLED;
// For use only in WidthVisitor
// Replace AstSelBit, etc with AstSel/AstArraySel

View File

@ -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()
test.passes()

View File

@ -0,0 +1,28 @@
// 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
module t(input wire clk);
parameter int max_ports = 4;
logic [max_ports-1:0][7:0] x;
localparam logic [max_ports-1:0] use_a = 4'b0101;
for (genvar port = 0; port < max_ports; port++) begin : g
if (use_a[port]) begin : g_a
always_ff @(posedge clk) x[max_ports - port - 1] <= "a";
end else begin : g_b
always_ff @(posedge clk) x[max_ports - port - 1] <= "b";
end
end
initial begin
$write("*-* All Finished *-*\n");
$finish;
end
endmodule