Fix false MULTIDRIVEN warning on always_ff variables (#7351) (#7621)

This commit is contained in:
Cookie 2026-05-27 20:34:11 +08:00 committed by GitHub
parent c9e012ba9a
commit 8ae0e48103
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 236 additions and 203 deletions

View File

@ -0,0 +1,12 @@
.. code-block:: sv
:linenos:
:emphasize-lines: 3,6
module t(input logic clk, input logic d, output logic q);
initial q = 1'b0; // <--- Warning
always_ff @(posedge clk) begin
q <= d; // <--- Warning
end
endmodule

View File

@ -1414,9 +1414,11 @@ List Of Warnings
.. option:: MULTIDRIVEN
Warns that the specified signal comes from multiple ``always``
blocks, each with different clocking. This warning does not look at
individual bits (see the example below).
Warns that the specified signal has multiple procedural drivers.
One case is when the signal comes from multiple ``always`` blocks,
each with different clocking. This warning does not look at individual
bits (see the example below).
This is considered bad style, as the consumer of a given signal may be
unaware of the inconsistent clocking, causing clock domain crossing
@ -1430,8 +1432,28 @@ List Of Warnings
.. include:: ../../docs/gen/ex_MULTIDRIVEN_msg.rst
Ignoring this warning will only slow simulations; it will simulate
correctly. It may, however, cause longer simulation runtimes due to
Another case is when a variable written in an ``always_comb`` or
``always_ff`` block is also written by another process. IEEE 1800
requires variables written in these specialized always blocks to be
written only by that process.
Faulty ``always_ff`` example:
.. include:: ../../docs/gen/ex_MULTIDRIVEN_alwaysff_faulty.rst
To retain an initialization without adding another process, use a
declaration initializer (or, for synthesizable code, add a reset term):
.. code-block:: sv
logic q = 1'b0;
always_ff @(posedge clk) begin
q <= d;
end
Ignoring this warning may hide clock domain crossing, timing, or
portability bugs. It may also cause longer simulation runtimes due to
reduced optimizations.
@ -1798,12 +1820,18 @@ List Of Warnings
.. include:: ../../docs/gen/ex_PROCASSINIT_fixed.rst
Alternatively, use an initial block for the initialization:
For non-``always_ff`` logic, alternatively use an initial block for
the initialization:
.. code-block:: sv
initial flop_out = 1; // <--- Fixed
Do not use a separate initial block for a variable assigned by an
``always_ff`` process, as IEEE 1800 requires an ``always_ff`` variable
to be written only by that process. Use a declaration initializer, or
initialize the variable from reset logic inside the ``always_ff``.
Disabled by default as this is a code-style warning; it will simulate
correctly.

View File

@ -47,6 +47,7 @@ class UndrivenVarEntry final {
= nullptr; // always_comb of var if driven within always_comb, else nullptr
const FileLine* m_alwCombFileLinep = nullptr; // File line of always_comb of var if driven
// within always_comb, else nullptr
const AstAlways* m_alwFFp = nullptr; // always_ff of var if driven within always_ff
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
@ -58,7 +59,13 @@ class UndrivenVarEntry final {
const AstNodeFTaskRef* m_callNodep = nullptr; // Call node if driven via writeSummary
enum : uint8_t { FLAG_USED = 0, FLAG_DRIVEN = 1, FLAG_DRIVEN_ALWCOMB = 2, FLAGS_PER_BIT = 3 };
enum : uint8_t {
FLAG_USED = 0, // Signal or bit has been read/observed
FLAG_DRIVEN = 1, // Signal or bit has been written/driven
FLAG_DRIVEN_ALWCOMB = 2, // Whole signal has been driven from always_comb
FLAG_DRIVEN_ALWFF = 3, // Whole signal has been driven from always_ff
FLAGS_PER_BIT = 4 // Number of flags stored for each tracked bit
};
public:
// CONSTRUCTORS
@ -151,6 +158,10 @@ public:
m_alwCombp = alwCombp;
m_alwCombFileLinep = fileLinep;
}
void drivenAlwaysFFWhole(const AstAlways* alwFFp, const AstVar* varp) {
m_wholeFlags[FLAG_DRIVEN_ALWFF] = true;
m_alwFFp = alwFFp;
}
const AstNode* initStaticp() const { return m_initStaticp; }
void initStaticp(const AstNode* nodep) { m_initStaticp = nodep; }
@ -164,11 +175,13 @@ public:
bool isUnderGen() const { return m_underGen; }
bool isDrivenWhole() const { return m_wholeFlags[FLAG_DRIVEN]; }
bool isDrivenAlwaysCombWhole() const { return m_wholeFlags[FLAG_DRIVEN_ALWCOMB]; }
bool isDrivenAlwaysFFWhole() const { return m_wholeFlags[FLAG_DRIVEN_ALWFF]; }
bool isFtaskDriven() const { return m_ftaskDriven; }
const AstNodeVarRef* getNodep() const { return m_nodep; }
const FileLine* getNodeFileLinep() const { return m_nodeFileLinep; }
const AstAlways* getAlwCombp() const { return m_alwCombp; }
const FileLine* getAlwCombFileLinep() const { return m_alwCombFileLinep; }
const AstAlways* getAlwFFp() const { return m_alwFFp; }
void usedBit(int bit, int width, const AstNode* nodep) {
UINFO(9, "set u[" << (bit + width - 1) << ":" << bit << "] " << m_varp->name());
for (int i = 0; i < width; i++) {
@ -366,6 +379,7 @@ class UndrivenVisitor final : public VNVisitorConst {
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
const AstAlways* m_alwaysFFp = nullptr; // Current always if ff, otherwise nullptr
V3UndrivenCapture* const m_capturep = nullptr; // Capture object. 'nullptr' if disabled.
@ -523,6 +537,8 @@ class UndrivenVisitor final : public VNVisitorConst {
const AstNode* const otherWritep
= entryp->getNodep() ? static_cast<const AstNode*>(entryp->getNodep())
: entryp->callNodep();
const bool otherWriteIsStaticInit
= nodep->varp()->hasUserInit() && otherWritep == entryp->initStaticp();
if (m_alwaysCombp
&& (!entryp->isDrivenAlwaysCombWhole()
@ -545,6 +561,31 @@ class UndrivenVisitor final : public VNVisitorConst {
<< nodep->warnContextPrimary() << '\n'
<< otherWritep->warnOther()
<< "... Location of always_comb write\n"
<< otherWritep->warnContextSecondary());
}
if (m_alwaysFFp
&& !otherWriteIsStaticInit
&& (!entryp->isDrivenAlwaysFFWhole()
|| (m_alwaysFFp != entryp->getAlwFFp()
&& m_alwaysFFp->fileline()
!= entryp->getAlwFFp()->fileline()))) {
nodep->v3warn(
MULTIDRIVEN,
"Variable written to in always_ff also written by other process"
<< " (IEEE 1800-2023 9.2.2.4): " << nodep->prettyNameQ() << '\n'
<< nodep->warnOther() << '\n'
<< nodep->warnContextPrimary() << '\n'
<< otherWritep->warnOther() << "... Location of other write\n"
<< otherWritep->warnContextSecondary());
}
if (!m_alwaysFFp && !m_inInitialStatic && entryp->isDrivenAlwaysFFWhole()) {
nodep->v3warn(MULTIDRIVEN, "Variable also written to in always_ff"
<< " (IEEE 1800-2023 9.2.2.4): "
<< nodep->prettyNameQ() << '\n'
<< nodep->warnOther() << '\n'
<< nodep->warnContextPrimary() << '\n'
<< otherWritep->warnOther()
<< "... Location of always_ff write\n"
<< otherWritep->warnContextSecondary());
}
}
@ -558,6 +599,8 @@ class UndrivenVisitor final : public VNVisitorConst {
entryp->underGenerate();
if (m_alwaysCombp)
entryp->drivenAlwaysCombWhole(m_alwaysCombp, m_alwaysCombp->fileline());
if (m_alwaysFFp)
entryp->drivenAlwaysFFWhole(m_alwaysFFp, nodep->varp());
}
if (nodep->access().isWriteOrRW()) {
if (m_inInitialStatic && !entryp->initStaticp()) entryp->initStaticp(nodep);
@ -645,6 +688,7 @@ class UndrivenVisitor final : public VNVisitorConst {
void visit(AstAlways* nodep) override {
VL_RESTORER(m_alwaysp);
VL_RESTORER(m_alwaysCombp);
VL_RESTORER(m_alwaysFFp);
AstNode::user2ClearTree();
m_alwaysp = nodep;
if (nodep->keyword() == VAlwaysKwd::ALWAYS_COMB) {
@ -653,6 +697,7 @@ class UndrivenVisitor final : public VNVisitorConst {
} else {
m_alwaysCombp = nullptr;
}
m_alwaysFFp = nodep->keyword() == VAlwaysKwd::ALWAYS_FF ? nodep : nullptr;
iterateChildrenConst(nodep);
if (nodep->keyword() == VAlwaysKwd::ALWAYS_COMB) UINFO(9, " Done " << nodep);
}
@ -684,6 +729,7 @@ class UndrivenVisitor final : public VNVisitorConst {
entryp->drivenViaCall(nodep);
if (m_alwaysCombp)
entryp->drivenAlwaysCombWhole(m_alwaysCombp, m_alwaysCombp->fileline());
if (m_alwaysFFp) entryp->drivenAlwaysFFWhole(m_alwaysFFp, varp);
}
}
}

View File

@ -75,8 +75,7 @@ module t
// FSM with ++/--
// A more elaborate case statement, with if-else, for loops, etc
// to confirm that ++/-- is handled by V3LinkInc.cpp
logic [3:0] state_d, state_q;
initial state_q = '0;
logic [3:0] state_d, state_q = '0;
logic [3:0] state_counter_d, state_counter_q;
always_ff @(posedge clk) begin
state_q <= state_d;

View File

@ -368,10 +368,11 @@
S2 = 3'd2
} state_t;
int cyc;
logic start;
logic side;
logic [2:0] dyn_case;
int cyc = 0;
logic start = 1'b0;
logic side = 1'b0;
logic dyn_side = 1'b0;
logic [2:0] dyn_case = 3'd7;
state_t state /*verilator fsm_reset_arc*/;
fsm_if_mixed_vars_bad mixed_vars_u (.clk(clk));
@ -392,17 +393,10 @@
fsm_direct_active_low_dynamic_reset_bad active_low_dynamic_reset_u (
.clk(clk), .rst_n(cyc != 0), .dyn_reset(dyn_case[1:0]));
initial begin
cyc = 0;
start = 1'b0;
side = 1'b0;
dyn_case = 3'd7;
end
always @(posedge clk) begin
cyc <= cyc + 1;
if (cyc == 1) side <= 1'b1;
dyn_case <= {2'b11, side};
if (cyc == 1) dyn_side <= 1'b1;
dyn_case <= {2'b11, dyn_side};
if (cyc == 5) begin
$write("*-* All Finished *-*\n");
$finish;

View File

@ -362,10 +362,11 @@ module t (
S2 = 3'd2
} state_t;
int cyc;
logic start;
logic side;
logic [2:0] dyn_case;
int cyc = 0;
logic start = 1'b0;
logic side = 1'b0;
logic dyn_side = 1'b0;
logic [2:0] dyn_case = 3'd7;
state_t state /*verilator fsm_reset_arc*/;
fsm_if_mixed_vars_bad mixed_vars_u (.clk(clk));
@ -386,17 +387,10 @@ module t (
fsm_direct_active_low_dynamic_reset_bad active_low_dynamic_reset_u (
.clk(clk), .rst_n(cyc != 0), .dyn_reset(dyn_case[1:0]));
initial begin
cyc = 0;
start = 1'b0;
side = 1'b0;
dyn_case = 3'd7;
end
always @(posedge clk) begin
cyc <= cyc + 1;
if (cyc == 1) side <= 1'b1;
dyn_case <= {2'b11, side};
if (cyc == 1) dyn_side <= 1'b1;
dyn_case <= {2'b11, dyn_side};
if (cyc == 5) begin
$write("*-* All Finished *-*\n");
$finish;

View File

@ -17,11 +17,6 @@
int cyc;
state_t state;
initial begin
cyc = 0;
state = S0;
end
always @(posedge clk) begin
cyc <= cyc + 1;
if (cyc == 4) begin

View File

@ -16,11 +16,6 @@ module t (
int cyc;
state_t state;
initial begin
cyc = 0;
state = S0;
end
always @(posedge clk) begin
cyc <= cyc + 1;
if (cyc == 4) begin

View File

@ -65,13 +65,10 @@
state_t state_q;
state_t state_d;
initial begin
aux = 1'b1;
start = 1'b1;
end
always_ff @(posedge clk) begin
if (rst) begin
aux <= 1'b1;
start <= 1'b1;
state_q <= S0;
aux <= 1'b0;
end else begin
@ -81,14 +78,7 @@
always_comb begin
state_d = state_q;
%000002 case (state_q)
// [FSM coverage]
%000001 // [fsm_arc t.reset_other_assign_ok_u.state_q::ANY->S0[reset]] [reset arc, excluded from %]
%000002 // [fsm_arc t.reset_other_assign_ok_u.state_q::S0->S1]
%000000 // [fsm_arc t.reset_other_assign_ok_u.state_q::S0->S2]
%000002 // [fsm_state t.reset_other_assign_ok_u.state_q::S0]
%000002 // [fsm_state t.reset_other_assign_ok_u.state_q::S1]
%000000 // [fsm_state t.reset_other_assign_ok_u.state_q::S2] *** UNCOVERED ***
case (state_q)
S0: state_d = start ? S1 : S2;
default: state_d = S0;
endcase
@ -105,13 +95,8 @@
S2 = 2'd2
} state_t;
state_t state_q;
state_t other_q;
initial begin
state_q = S0;
other_q = S2;
end
state_t state_q = S0;
state_t other_q = S2;
always_ff @(posedge clk) begin
if (rst) begin
@ -140,13 +125,8 @@
S2 = 2'd2
} state_t;
state_t state_q;
state_t other_q;
initial begin
state_q = S0;
other_q = S2;
end
state_t state_q = S0;
state_t other_q = S2;
// Useful because the one-block reset fallback must ignore non-constant reset
// assignments and still retain the supported case(state_q) FSM below.

View File

@ -54,13 +54,10 @@ module fsm_reset_other_assign_ok (
state_t state_q;
state_t state_d;
initial begin
aux = 1'b1;
start = 1'b1;
end
always_ff @(posedge clk) begin
if (rst) begin
aux <= 1'b1;
start <= 1'b1;
state_q <= S0;
aux <= 1'b0;
end else begin
@ -87,13 +84,8 @@ module fsm_oneblock_reset_mismatch_ok (
S2 = 2'd2
} state_t;
state_t state_q;
state_t other_q;
initial begin
state_q = S0;
other_q = S2;
end
state_t state_q = S0;
state_t other_q = S2;
always_ff @(posedge clk) begin
if (rst) begin
@ -117,13 +109,8 @@ module fsm_oneblock_reset_nonconst_ok (
S2 = 2'd2
} state_t;
state_t state_q;
state_t other_q;
initial begin
state_q = S0;
other_q = S2;
end
state_t state_q = S0;
state_t other_q = S2;
// Useful because the one-block reset fallback must ignore non-constant reset
// assignments and still retain the supported case(state_q) FSM below.

View File

@ -65,13 +65,10 @@
state_t state_q;
state_t state_d;
initial begin
aux = 1'b1;
start = 1'b1;
end
always_ff @(posedge clk) begin
if (rst) begin
aux <= 1'b1;
start <= 1'b1;
state_q <= S0;
aux <= 1'b0;
end else begin
@ -81,14 +78,7 @@
always_comb begin
state_d = state_q;
%000002 case (state_q)
// [FSM coverage]
%000001 // [fsm_arc t.reset_other_assign_ok_u.state_q::ANY->S0[reset]]
%000002 // [fsm_arc t.reset_other_assign_ok_u.state_q::S0->S1]
%000000 // [fsm_arc t.reset_other_assign_ok_u.state_q::S0->S2]
%000002 // [fsm_state t.reset_other_assign_ok_u.state_q::S0]
%000002 // [fsm_state t.reset_other_assign_ok_u.state_q::S1]
%000000 // [fsm_state t.reset_other_assign_ok_u.state_q::S2] *** UNCOVERED ***
case (state_q)
S0: state_d = start ? S1 : S2;
default: state_d = S0;
endcase
@ -105,13 +95,8 @@
S2 = 2'd2
} state_t;
state_t state_q;
state_t other_q;
initial begin
state_q = S0;
other_q = S2;
end
state_t state_q = S0;
state_t other_q = S2;
always_ff @(posedge clk) begin
if (rst) begin
@ -140,13 +125,8 @@
S2 = 2'd2
} state_t;
state_t state_q;
state_t other_q;
initial begin
state_q = S0;
other_q = S2;
end
state_t state_q = S0;
state_t other_q = S2;
// Useful because the one-block reset fallback must ignore non-constant reset
// assignments and still retain the supported case(state_q) FSM below.

View File

@ -393,8 +393,6 @@
%000004 state_t state_q;
%000004 state_t state_d;
%000001 initial side = 1'b0;
000010 always_comb begin
000010 state_d = state_q;
000010 case (state_q)
@ -404,8 +402,10 @@
end
%000009 always_ff @(posedge clk) begin
%000007 if (rst) state_q <= S0;
%000007 else begin
%000007 if (rst) begin
%000002 side <= 1'b0;
%000002 state_q <= S0;
%000007 end else begin
%000007 side <= ~side;
%000007 state_q <= state_d;
end

View File

@ -336,8 +336,6 @@ module fsm_seqmix_off (
state_t state_q;
state_t state_d;
initial side = 1'b0;
always_comb begin
state_d = state_q;
case (state_q)
@ -347,8 +345,10 @@ module fsm_seqmix_off (
end
always_ff @(posedge clk) begin
if (rst) state_q <= S0;
else begin
if (rst) begin
side <= 1'b0;
state_q <= S0;
end else begin
side <= ~side;
state_q <= state_d;
end

View File

@ -760,6 +760,7 @@
input logic start
);
/* verilator lint_off BLKANDNBLK */
/* verilator lint_off MULTIDRIVEN */
typedef enum logic [1:0] {
S0 = 2'd0,
S1 = 2'd1,
@ -800,6 +801,7 @@
.state_i(state_d),
.state_o(state_q)
);
/* verilator lint_on MULTIDRIVEN */
/* verilator lint_on BLKANDNBLK */
endmodule

View File

@ -660,6 +660,7 @@ module fsm_competing_direct (
input logic start
);
/* verilator lint_off BLKANDNBLK */
/* verilator lint_off MULTIDRIVEN */
typedef enum logic [1:0] {
S0 = 2'd0,
S1 = 2'd1,
@ -692,6 +693,7 @@ module fsm_competing_direct (
.state_i(state_d),
.state_o(state_q)
);
/* verilator lint_on MULTIDRIVEN */
/* verilator lint_on BLKANDNBLK */
endmodule

View File

@ -86,22 +86,22 @@ module Test(/*AUTOARG*/
wdata_d1r <= wdata;
end
reg [31:0] csr0000;
reg [31:0] csr0001;
reg [31:0] csr0002;
reg [31:0] csr0003;
reg [31:0] csr0004;
reg [31:0] csr0005;
reg [31:0] csr0006;
reg [31:0] csr0007;
reg [31:0] csr0008;
reg [31:0] csr0009;
reg [31:0] csr000a;
reg [31:0] csr000b;
reg [31:0] csr000c;
reg [31:0] csr000d;
reg [31:0] csr000e;
reg [31:0] csr000f;
reg [31:0] csr0000 = 32'he172d365;
reg [31:0] csr0001 = 32'h35cc25e2;
reg [31:0] csr0002 = 32'haf48436e;
reg [31:0] csr0003 = 32'h135e55e4;
reg [31:0] csr0004 = 32'h5fd6e48a;
reg [31:0] csr0005 = 32'hb07d34ad;
reg [31:0] csr0006 = 32'h2aa05deb;
reg [31:0] csr0007 = 32'hfe97b680;
reg [31:0] csr0008 = 32'h960f20bb;
reg [31:0] csr0009 = 32'h251129f0;
reg [31:0] csr000a = 32'hef3d2f93;
reg [31:0] csr000b = 32'hef4bc127;
reg [31:0] csr000c = 32'h3dfecb10;
reg [31:0] csr000d = 32'h1b4690f5;
reg [31:0] csr000e = 32'ha07822ab;
reg [31:0] csr000f = 32'hf817cbf6;
wire [31:0] csr0010 = 32'h33675230;
wire [31:0] csr0011 = 32'h00fa2144;
wire [31:0] csr0012 = 32'h6a5e8e10;
@ -118,25 +118,6 @@ module Test(/*AUTOARG*/
wire [31:0] csr001d = 32'h02e7b33c;
wire [31:0] csr001e = 32'h12101533;
wire [31:0] csr001f = 32'h2cc1cce5;
initial begin
csr0000 = 32'he172d365;
csr0001 = 32'h35cc25e2;
csr0002 = 32'haf48436e;
csr0003 = 32'h135e55e4;
csr0004 = 32'h5fd6e48a;
csr0005 = 32'hb07d34ad;
csr0006 = 32'h2aa05deb;
csr0007 = 32'hfe97b680;
csr0008 = 32'h960f20bb;
csr0009 = 32'h251129f0;
csr000a = 32'hef3d2f93;
csr000b = 32'hef4bc127;
csr000c = 32'h3dfecb10;
csr000d = 32'h1b4690f5;
csr000e = 32'ha07822ab;
csr000f = 32'hf817cbf6;
end
always_ff @ (posedge clk) begin
if (we_d1r && sel_d1r == 16'h0000) csr0000 <= wdata_d1r;
if (we_d1r && sel_d1r == 16'h0001) csr0001 <= wdata_d1r;

View File

@ -130,22 +130,22 @@ module Test ( /*AUTOARG*/
wdata_d1r <= wdata;
end
reg [31:0] csr0000;
reg [31:0] csr0001;
reg [31:0] csr0002;
reg [31:0] csr0003;
reg [31:0] csr0004;
reg [31:0] csr0005;
reg [31:0] csr0006;
reg [31:0] csr0007;
reg [31:0] csr0008;
reg [31:0] csr0009;
reg [31:0] csr000a;
reg [31:0] csr000b;
reg [31:0] csr000c;
reg [31:0] csr000d;
reg [31:0] csr000e;
reg [31:0] csr000f;
reg [31:0] csr0000 = 32'he172d365;
reg [31:0] csr0001 = 32'h35cc25e2;
reg [31:0] csr0002 = 32'haf48436e;
reg [31:0] csr0003 = 32'h135e55e4;
reg [31:0] csr0004 = 32'h5fd6e48a;
reg [31:0] csr0005 = 32'hb07d34ad;
reg [31:0] csr0006 = 32'h2aa05deb;
reg [31:0] csr0007 = 32'hfe97b680;
reg [31:0] csr0008 = 32'h960f20bb;
reg [31:0] csr0009 = 32'h251129f0;
reg [31:0] csr000a = 32'hef3d2f93;
reg [31:0] csr000b = 32'hef4bc127;
reg [31:0] csr000c = 32'h3dfecb10;
reg [31:0] csr000d = 32'h1b4690f5;
reg [31:0] csr000e = 32'ha07822ab;
reg [31:0] csr000f = 32'hf817cbf6;
wire [31:0] csr0010 = 32'h33675230;
wire [31:0] csr0011 = 32'h00fa2144;
wire [31:0] csr0012 = 32'h6a5e8e10;
@ -162,25 +162,6 @@ module Test ( /*AUTOARG*/
wire [31:0] csr001d = 32'h02e7b33c;
wire [31:0] csr001e = 32'h12101533;
wire [31:0] csr001f = 32'h2cc1cce5;
initial begin
csr0000 = 32'he172d365;
csr0001 = 32'h35cc25e2;
csr0002 = 32'haf48436e;
csr0003 = 32'h135e55e4;
csr0004 = 32'h5fd6e48a;
csr0005 = 32'hb07d34ad;
csr0006 = 32'h2aa05deb;
csr0007 = 32'hfe97b680;
csr0008 = 32'h960f20bb;
csr0009 = 32'h251129f0;
csr000a = 32'hef3d2f93;
csr000b = 32'hef4bc127;
csr000c = 32'h3dfecb10;
csr000d = 32'h1b4690f5;
csr000e = 32'ha07822ab;
csr000f = 32'hf817cbf6;
end
always_ff @(posedge clk) begin
if (we_d1r && sel_d1r == 16'h0000) csr0000 <= wdata_d1r;
if (we_d1r && sel_d1r == 16'h0001) csr0001 <= wdata_d1r;

View File

@ -0,0 +1,19 @@
%Warning-MULTIDRIVEN: t/t_lint_always_ff_multidriven_bad.v:14:5: Variable written to in always_ff also written by other process (IEEE 1800-2023 9.2.2.4): 'a'
: ... note: In instance 't'
t/t_lint_always_ff_multidriven_bad.v:14:5:
14 | a <= 1'b1;
| ^
t/t_lint_always_ff_multidriven_bad.v:11:11: ... Location of other write
11 | initial a = 1'b0;
| ^
... For warning description see https://verilator.org/warn/MULTIDRIVEN?v=latest
... Use "/* verilator lint_off MULTIDRIVEN */" and lint_on around source to disable this message.
%Warning-MULTIDRIVEN: t/t_lint_always_ff_multidriven_bad.v:23:11: Variable also written to in always_ff (IEEE 1800-2023 9.2.2.4): 'b'
: ... note: In instance 't'
t/t_lint_always_ff_multidriven_bad.v:23:11:
23 | initial b = 1'b0;
| ^
t/t_lint_always_ff_multidriven_bad.v:20:5: ... Location of always_ff write
20 | b <= 1'b1;
| ^
%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,25 @@
// 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;
logic a;
initial a = 1'b0;
always_ff @(posedge a) begin
a <= 1'b1; // <--- Warning
end
logic b;
always_ff @(posedge b) begin
b <= 1'b1;
end
initial b = 1'b0; // <--- Warning
endmodule

View File

@ -254,15 +254,13 @@ endmodule
// -> ^d[31:0]
// Of course the correct result is ^d[38:0] = ^d
module bug3470(input wire clk, input wire [31:0] in, output wire out);
logic [38:0] d;
initial d = 0;
initial tmp = 0;
initial expected = 0;
logic [38:0] d = 0;
logic tmp = 0;
logic expected = 0;
always_ff @(posedge clk)
d <= {d[6:0], in};
logic tmp, expected;
always_ff @(posedge clk) begin
tmp <= ^(d >> 32) ^ (^d[31:0]);
expected <= ^d;

View File

@ -19,9 +19,8 @@ module t ( /*AUTOARG*/
);
input clk;
int cyc;
int cyc = 0;
logic [`NUM_SUBS - 1:0] x;
initial cyc = 0;
always_ff @(posedge clk) begin
cyc <= cyc + 1;