Fix error on mixed-initialization (#7352) (#7357)

This commit is contained in:
Cookie 2026-05-12 06:32:55 +08:00 committed by GitHub
parent 18e06b1e7d
commit cf9334f2c1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 139 additions and 27 deletions

View File

@ -217,7 +217,7 @@ Verilator 5.048 2026-04-26
* Fix std::randomize inside {typedef array} internal error (#7481). [Yilou Wang]
* Fix module parameters not re-evaluated upon instantiation (#7463) (#7477). [em2machine]
* Fix infinite recursion with VERILATOR_BIN (#7496) (#7497).
* Add error on mixed declaration initialization with continuous assignment (#7352). [Zhi QU]
Verilator 5.046 2026-02-28
==========================

View File

@ -45,6 +45,7 @@ Chuxuan Wang
Chykon
Congcong Cai
Conor McCullough
Cookie
Dan Ruelas-Petrisko
Daniel Bates
Danny Oler

View File

@ -521,6 +521,48 @@ List Of Warnings
Suppressing this error will suppress the error message check; it will
simulate as if the ``const`` as not present.
.. option:: CONTASSINIT
Error that a continuous assignment is setting a variable with an initial
value. Use either the initial value or the continuous assignment, but not
both.
For example:
.. code-block:: sv
:emphasize-lines: 2,4
module t;
logic b = 1'b0;
logic a;
assign b = a; //<--- Error
endmodule
Results in:
.. code-block::
%Error-CONTASSINIT: example.sv:4:10: Continuous assignment to variable with initial value: 'b'
: ... note: In instance 't'
: ... Location of variable initialization
2 | logic b = 1'b0;
| ^
example.sv:4:10: ... Location of continuous assignment
4 | assign b = a;
| ^
%Error: Exiting due to 1 error(s)
The following is legal because the driven variable does not have a declaration
initializer:
.. code-block:: sv
module t;
logic b;
logic a = 1'b0;
assign b = a;
endmodule
.. option:: CONTASSREG

View File

@ -61,6 +61,7 @@ public:
I_TIMING, // Enable timing from /*verilator timing_on/off*/
I_TRACING, // Tracing is on/off from /*verilator tracing_on/off*/
// Error codes:
E_CONTASSINIT, // Error: Continuous assignment versus initialization
E_CONSTWRITTEN, // Error: Const variable being written.
E_LIFETIME, // Error: Reference to a variable might outlive the variable.
E_NEEDTIMINGOPT, // Error: --timing/--no-timing option not specified
@ -217,8 +218,8 @@ public:
" I_CELLDEFINE", " I_COVERAGE", " I_DEF_NETTYPE_WIRE", " I_LINT", " I_STYLE",
" I_TIMING", " I_TRACING",
// Errors
"CONSTWRITTEN", "LIFETIME", "NEEDTIMINGOPT", "NOTIMING", "PORTSHORT", "TASKNSVAR",
"UNSUPPORTED",
"CONTASSINIT", "CONSTWRITTEN", "LIFETIME", "NEEDTIMINGOPT", "NOTIMING", "PORTSHORT",
"TASKNSVAR", "UNSUPPORTED",
// Warnings
" EC_FIRST_WARN", "ALWCOMBORDER", "ALWNEVER", "ASCRANGE", "ASSIGNDLY", "ASSIGNEQEXPR",
"ASSIGNIN", "BADSTDPRAGMA", "BADVLTPRAGMA", "BLKANDNBLK", "BLKLOOPINIT", "BLKSEQ",

View File

@ -48,6 +48,8 @@ class UndrivenVarEntry final {
// within always_comb, else nullptr
const AstNodeVarRef* m_nodep = nullptr; // varref if driven, else nullptr
const AstNode* m_initStaticp = nullptr; // varref if in InitialStatic driven
const AstNode* m_initialp = nullptr; // varref if driven in an explicit initial block
const AstNode* m_contAssignp = nullptr; // varref if in continuous assignment driven
const AstNode* m_procWritep = nullptr; // varref if written in process
const FileLine* m_nodeFileLinep = nullptr; // File line of varref if driven, else nullptr
bool m_underGen = false; // Under a generate
@ -139,6 +141,10 @@ public:
const AstNode* initStaticp() const { return m_initStaticp; }
void initStaticp(const AstNode* nodep) { m_initStaticp = nodep; }
const AstNode* initialp() const { return m_initialp; }
void initialp(const AstNode* nodep) { m_initialp = nodep; }
const AstNode* contAssignp() const { return m_contAssignp; }
void contAssignp(const AstNode* nodep) { m_contAssignp = nodep; }
const AstNode* procWritep() const { return m_procWritep; }
void procWritep(const AstNode* nodep) { m_procWritep = nodep; }
void underGenerate() { m_underGen = true; }
@ -198,6 +204,17 @@ public:
<< "... Perhaps should initialize instead using a reset in this process\n"
<< procWritep()->warnContextSecondary());
}
const AstNode* const initp = nodep->hasUserInit() ? initStaticp() : initialp();
if (initp && contAssignp() && !nodep->isClassMember() && !nodep->isFuncLocal()) {
initp->v3warn(
E_CONTASSINIT,
"Continuous assignment to variable with initial value: " << nodep->prettyNameQ()
<< '\n'
<< initp->warnMore() << "... Location of variable initialization\n"
<< initp->warnContextPrimary() << '\n'
<< contAssignp()->warnOther() << "... Location of continuous assignment\n"
<< contAssignp()->warnContextSecondary());
}
if (nodep->isGenVar()) { // Genvar
if (!nodep->isIfaceRef() && !nodep->isUsedParam() && !unusedMatch(nodep)) {
nodep->v3warn(UNUSEDGENVAR, "Genvar is not used: " << nodep->prettyNameQ());
@ -323,6 +340,7 @@ class UndrivenVisitor final : public VNVisitorConst {
std::array<std::vector<UndrivenVarEntry*>, 3> m_entryps = {}; // Nodes to delete when finished
bool m_inBBox = false; // In black box; mark as driven+used
bool m_inContAssign = false; // In continuous assignment
bool m_inInitial = false; // In explicit initial block
bool m_inInitialSetup = false; // In InitialAutomatic*/InitialStatic* assignment LHS
bool m_inInitialStatic = false; // In InitialStatic
bool m_inProcAssign = false; // In procedural assignment
@ -518,6 +536,8 @@ class UndrivenVisitor final : public VNVisitorConst {
}
if (nodep->access().isWriteOrRW()) {
if (m_inInitialStatic && !entryp->initStaticp()) entryp->initStaticp(nodep);
if (m_inInitial && !entryp->initialp()) entryp->initialp(nodep);
if (m_inContAssign && !entryp->contAssignp()) entryp->contAssignp(nodep);
if (m_alwaysp && m_inProcAssign && !entryp->procWritep())
entryp->procWritep(nodep);
}
@ -554,11 +574,27 @@ class UndrivenVisitor final : public VNVisitorConst {
m_inProcAssign = true;
iterateChildrenConst(nodep);
}
void visit(AstAssignForce* nodep) override {
iterateConst(nodep->rhsp());
VL_RESTORER(m_inInitial);
m_inInitial = false;
iterateConst(nodep->lhsp());
}
void visit(AstAssignW* nodep) override {
VL_RESTORER(m_inContAssign);
m_inContAssign = true;
iterateChildrenConst(nodep);
}
void visit(AstRelease* nodep) override {
VL_RESTORER(m_inInitial);
m_inInitial = false;
iterateConst(nodep->lhsp());
}
void visit(AstInitial* nodep) override {
VL_RESTORER(m_inInitial);
m_inInitial = true;
iterateChildrenConst(nodep);
}
void visit(AstInitialAutomatic* nodep) override {
VL_RESTORER(m_inInitialSetup);
m_inInitialSetup = true;

View File

@ -5,7 +5,7 @@
// SPDX-License-Identifier: CC0-1.0
interface Iface;
logic clk = 1'b0, inp = 1'b0, io = 1'b0, out = 1'b0, out2 = 1'b0;
logic clk = 1'b0, inp = 1'b0, io = 1'b0, out = 1'b0, out2;
clocking cb @(posedge clk);
input #7 inp;
output out;

View File

@ -12,10 +12,6 @@ module t (
);
logic a;
logic b;
initial begin
a = 1'd0;
b = 1'd0;
end
assign a = ~i;
assign b = a;
assign o = b;

View File

@ -0,0 +1,10 @@
%Error-CONTASSINIT: t/t_fuzz_mixed_initialization.v:11:15: Continuous assignment to variable with initial value: 'a'
: ... note: In instance 't'
: ... Location of variable initialization
11 | logic a = 1'b0;
| ^~~~
t/t_fuzz_mixed_initialization.v:12:12: ... Location of continuous assignment
12 | assign a = 1'b1;
| ^
... For error description see https://verilator.org/warn/CONTASSINIT?v=latest
%Error: Exiting due to

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(fails=True, expect_filename=test.golden_filename)
test.passes()

View File

@ -0,0 +1,16 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 Zhi QU
// SPDX-License-Identifier: CC0-1.0
module t (
output wire out
);
logic a = 1'b0; // declaration initialization
assign a = 1'b1; // continuous assignment
assign out = a;
endmodule

View File

@ -33,10 +33,6 @@ module t;
wire bar2;
`DELAY_INIT_CHECK(foo2, bar2)
reg foo3 = '0;
reg bar3 = '1;
`DELAY_INIT_CHECK(foo3, bar3)
initial begin
#30;
$write("*-* All Finished *-*\n");

View File

@ -17,7 +17,6 @@ module t (
logic [7:0][3:0] arr;
logic [31:0] arr2_c;
initial arr2_c = 0;
logic [7:0][3:0] arr2;
assign arr2_c = arr2;

View File

@ -22,7 +22,7 @@ module t (
// update in **combinational** logic
reg [1:0] x = 2'b00;
// '{y1, y0}' should have exactly the same value as 'x', at all times
reg y0 = 1'b0;
reg y0;
reg y1 = 1'b0;
// 'z[0]' should equal '{8{x[0]}', 'z[1]' should equal '{8{x[1]}}'
// verilator lint_off BLKANDNBLK
@ -33,7 +33,7 @@ module t (
struct {
logic a;
logic b;
} pair = '{a: 1'b0, b: 1'b0};
} pair;
// verilator lint_on BLKANDNBLK
assign x[0] = cyc[0];

View File

@ -23,7 +23,7 @@ module top(
reg clk_half = 0;
reg [31:0] cyc = 0;
reg [31:0] a = 1, b = 2, c = 2;
reg [31:0] a = 1, b = 2, c;
always @(posedge clk) begin
$display("tick %d: a: %d, b: %d, c: %d", cyc, a, b, c);

View File

@ -25,11 +25,11 @@ module t (/*AUTOARG*/
wire fake_D;
logic[3:0] sh1 = 1;
logic[3:0] sh2 = 2;
logic[3:0] sh2;
logic[3:0] sh3 = 3;
logic[3:0] sh4 = 4;
logic[3:0] sh4;
logic[3:0] sh5 = 5;
logic[3:0] sh6 = 6;
logic[3:0] sh6;
int cyc = 0;

View File

@ -5,7 +5,7 @@
// SPDX-License-Identifier: CC0-1.0
module t;
logic clk1 = 0, clk2 = 0, clk3 = 0, clk4 = 0;
logic clk1 = 0, clk2, clk3, clk4;
always #2 clk1 = ~clk1;
assign #1 clk2 = clk1;
assign #1 clk3 = clk2;

View File

@ -5,11 +5,11 @@
// SPDX-License-Identifier: CC0-1.0
module t;
logic clk1 = 0;
bit clk1;
assign #3 clk1 = ~clk1;
logic clk2 = 0;
bit clk2;
assign #11 clk2 = ~clk2;
int a1 = 0;

View File

@ -5,11 +5,11 @@
// SPDX-License-Identifier: CC0-1.0
module t;
bit clk1 = 0;
bit clk1;
assign #3 clk1 = ~clk1;
bit clk2 = 0;
bit clk2;
assign #11 clk2 = ~clk2;
bit flag = 0;

View File

@ -5,11 +5,11 @@
// SPDX-License-Identifier: CC0-1.0
module t;
bit clk1 = 0;
bit clk1;
assign #3 clk1 = ~clk1;
bit clk2 = 0;
bit clk2;
assign #11 clk2 = ~clk2;
int a1 = 0;

View File

@ -33,7 +33,6 @@ module t;
assign i.clk = c.clk;
Clocker clocker;
initial begin
i.clk = 0;
i.v = 0;
clocker = new;
clocker.clk = c;