Add error on force/release non-constant selects.

This commit is contained in:
Wilson Snyder 2025-08-29 21:19:37 -04:00
parent a3d7b7d779
commit 91ae4c35b7
5 changed files with 121 additions and 4 deletions

View File

@ -23,6 +23,7 @@ Verilator 5.039 devel
* Add ALWNEVER warning, for `always @*` that never execute (#6291).
* Add separate coverage counters for toggles 0->1 and 1->0 (#6086). [Ryszard Rozak, Antmicro Ltd.]
* Add error on class 'function static'.
* Add error on force/release non-constant selects.
* Add `-DVERILATOR=1` definition to compiler flags when using verilated.mk.
* Support member-level triggers for virtual interfaces (#5166) (#6148). [Yilou Wang]
* Support unassigned virtual interfaces (#5265) (#6245). [Szymon Gizler, Antmicro Ltd.]

View File

@ -5469,6 +5469,7 @@ class WidthVisitor final : public VNVisitor {
iterateCheckAssign(nodep, "Assign RHS", nodep->rhsp(), FINAL, lhsDTypep);
// UINFOTREE(1, nodep, "", "AssignOut");
}
if (VN_IS(nodep, AssignForce)) checkForceReleaseLhs(nodep, nodep->lhsp());
if (AstNode* const controlp = nodep->timingControlp()) {
if (VN_IS(m_ftaskp, Func)) {
controlp->v3error("Timing controls are not legal in functions. Suggest use a task "
@ -5537,6 +5538,7 @@ class WidthVisitor final : public VNVisitor {
userIterateAndNext(nodep->lhsp(), WidthVP{SELF, BOTH}.p());
UASSERT_OBJ(nodep->lhsp()->dtypep(), nodep, "How can LValue be untyped?");
UASSERT_OBJ(nodep->lhsp()->dtypep()->widthSized(), nodep, "How can LValue be unsized?");
checkForceReleaseLhs(nodep, nodep->lhsp());
}
void formatNoStringArg(AstNode* argp, char ch) {
@ -8544,7 +8546,7 @@ class WidthVisitor final : public VNVisitor {
const AstArg* const argp = tconnect.second;
const AstNode* const pinp = argp->exprp();
if (!pinp) continue; // Argument error we'll find later
if (hasOpenArrayIterateDType(portp->dtypep())) portp->dtypep(pinp->dtypep());
if (hasOpenArrayDTypeRecurse(portp->dtypep())) portp->dtypep(pinp->dtypep());
}
}
@ -8552,7 +8554,7 @@ class WidthVisitor final : public VNVisitor {
bool hasOpen = false;
for (AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
if (AstVar* const portp = VN_CAST(stmtp, Var)) {
if (portp->isDpiOpenArray() || hasOpenArrayIterateDType(portp->dtypep())) {
if (portp->isDpiOpenArray() || hasOpenArrayDTypeRecurse(portp->dtypep())) {
portp->isDpiOpenArray(true);
hasOpen = true;
}
@ -8560,10 +8562,10 @@ class WidthVisitor final : public VNVisitor {
}
return hasOpen;
}
bool hasOpenArrayIterateDType(AstNodeDType* nodep) {
bool hasOpenArrayDTypeRecurse(AstNodeDType* nodep) {
// Return true iff this datatype or child has an openarray
if (VN_IS(nodep, UnsizedArrayDType)) return true;
if (nodep->subDTypep()) return hasOpenArrayIterateDType(nodep->subDTypep()->skipRefp());
if (nodep->subDTypep()) return hasOpenArrayDTypeRecurse(nodep->subDTypep()->skipRefp());
return false;
}
AstNodeDType* dtypeNotIntAtomOrVecRecurse(AstNodeDType* nodep, bool ranged = false) {
@ -8599,6 +8601,44 @@ class WidthVisitor final : public VNVisitor {
}
return nodep;
}
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))
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"
<< nodep->warnContextPrimary() << '\n'
<< 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

View File

@ -0,0 +1,30 @@
%Error: t/t_force_select_bad.v:24:5: Force left-hand-side must not have variable bit/part select (IEEE 1800-2023 10.6.2)
: ... note: In instance 't'
24 | force array1[bad_index] = 1'b1;
| ^~~~~
t/t_force_select_bad.v:24:18: ... Location of non-constant index
24 | force array1[bad_index] = 1'b1;
| ^~~~~~~~~
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
%Error: t/t_force_select_bad.v:25:5: Release left-hand-side must not have variable bit/part select (IEEE 1800-2023 10.6.2)
: ... note: In instance 't'
25 | release array1[bad_index];
| ^~~~~~~
t/t_force_select_bad.v:25:20: ... Location of non-constant index
25 | release array1[bad_index];
| ^~~~~~~~~
%Error: t/t_force_select_bad.v:26:5: Force left-hand-side must not have variable bit/part select (IEEE 1800-2023 10.6.2)
: ... note: In instance 't'
26 | force vec[bad_index+:1] = 1'b1;
| ^~~~~
t/t_force_select_bad.v:26:15: ... Location of non-constant index
26 | force vec[bad_index+:1] = 1'b1;
| ^~~~~~~~~
%Error: t/t_force_select_bad.v:27:5: Release left-hand-side must not have variable bit/part select (IEEE 1800-2023 10.6.2)
: ... note: In instance 't'
27 | release vec[bad_index+:1];
| ^~~~~~~
t/t_force_select_bad.v:27:17: ... Location of non-constant index
27 | release vec[bad_index+:1];
| ^~~~~~~~~
%Error: Exiting due to

View File

@ -0,0 +1,16 @@
#!/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('linter')
test.lint(fails=True, expect_filename=test.golden_filename)
test.passes()

View File

@ -0,0 +1,30 @@
// 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
module t;
wire array1[2:1];
wire [2:-1] vec;
integer bad_index = 1;
parameter P_ONE = 1;
initial begin
force array1[P_ONE] = 1'b1; // ok
release array1[P_ONE]; // ok
force vec[P_ONE+:1] = 1'b1; // ok
release vec[P_ONE+:1]; // ok
// IEEE 1800-2023 10.6.2 [A force] shall not be a bit-select or a
// part-select of a [non-constant] variable or of a net with a user-defined
// nettype.
force array1[bad_index] = 1'b1; // <---- BAD not constant index
release array1[bad_index]; // <---- BAD not constant index
force vec[bad_index+:1] = 1'b1; // <---- BAD not constant index
release vec[bad_index+:1]; // <---- BAD not constant index
end
endmodule