From c64afda7fc291fce4e0209e48d4852447396dcfe Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Fri, 28 Aug 2026 15:27:50 +0200 Subject: [PATCH] Fix MULTIDRIVENPROC on static loop induction variables (#8242) Disable MULTIDRIVENPROC for static variables used as loop induction variables. Same way as we disable similar warning in Dfg. While bad style and bad for performance, it's common legacy code style. --- docs/gen/ex_MULTIDRIVENPROC_loopidx.rst | 13 ++++ docs/gen/ex_MULTIDRIVENPROC_msg.rst | 4 +- docs/guide/warnings.rst | 4 ++ src/V3Undriven.cpp | 2 +- .../t/t_lint_multidriven_proc_bad.out | 22 +++---- test_regress/t/t_lint_multidriven_proc_bad.py | 14 ++-- test_regress/t/t_lint_multidriven_proc_bad.v | 65 ++++++++++++++++++- 7 files changed, 102 insertions(+), 22 deletions(-) create mode 100644 docs/gen/ex_MULTIDRIVENPROC_loopidx.rst diff --git a/docs/gen/ex_MULTIDRIVENPROC_loopidx.rst b/docs/gen/ex_MULTIDRIVENPROC_loopidx.rst new file mode 100644 index 000000000..3938d9d03 --- /dev/null +++ b/docs/gen/ex_MULTIDRIVENPROC_loopidx.rst @@ -0,0 +1,13 @@ +.. comment: generated by t_lint_multidriven_proc_bad +.. code-block:: sv + :linenos: + + integer i; + + always @(posedge clk) begin + for (i = 0; i < 4; i = i + 1) q1[i] <= d[i]; + end + + always @(posedge clk) begin + for (i = 0; i < 4; i = i + 1) q2[i] <= ~d[i]; + end diff --git a/docs/gen/ex_MULTIDRIVENPROC_msg.rst b/docs/gen/ex_MULTIDRIVENPROC_msg.rst index 38b7fffd8..8d42753fc 100644 --- a/docs/gen/ex_MULTIDRIVENPROC_msg.rst +++ b/docs/gen/ex_MULTIDRIVENPROC_msg.rst @@ -2,7 +2,7 @@ .. code-block:: %Warning-MULTIDRIVENPROC: example.v:1:25 Variable written to in always block also written by another always block: 'q' - : ... note: In instance 't' + : ... note: In instance 't.u1' example.v:1:25 - 16 | always @(posedge clk) q <= ~d; + 54 | always @(posedge clk) q <= ~d; | ^ diff --git a/docs/guide/warnings.rst b/docs/guide/warnings.rst index 6d784b0eb..546f142b8 100644 --- a/docs/guide/warnings.rst +++ b/docs/guide/warnings.rst @@ -1512,6 +1512,10 @@ List Of Warnings ``always_ff``/``always_comb`` if the intent is a single specialized process. + Does not warn for static variables used as loop induction variables: + + .. include:: ../../docs/gen/ex_MULTIDRIVENPROC_loopidx.rst + .. option:: MULTITOP diff --git a/src/V3Undriven.cpp b/src/V3Undriven.cpp index b4bfb98ba..76b5fbf8d 100644 --- a/src/V3Undriven.cpp +++ b/src/V3Undriven.cpp @@ -691,7 +691,7 @@ class UndrivenVisitor final : public VNVisitorConst { } warnClockingDriven(nodep, entryp, otherWritep, otherWriteIsStaticInit); } - if (multidrivenCommon + if (multidrivenCommon && !nodep->varp()->isUsedLoopIdx() && !nodep->varp()->fileline()->warnIsOff(V3ErrorCode::MULTIDRIVENPROC)) { // Two plain always blocks driving the whole signal: legal // SystemVerilog, but a driver conflict for synthesis. The diff --git a/test_regress/t/t_lint_multidriven_proc_bad.out b/test_regress/t/t_lint_multidriven_proc_bad.out index 014ca8a31..950613faf 100644 --- a/test_regress/t/t_lint_multidriven_proc_bad.out +++ b/test_regress/t/t_lint_multidriven_proc_bad.out @@ -1,19 +1,19 @@ -%Warning-MULTIDRIVENPROC: t/t_lint_multidriven_proc_bad.v:16:25: Variable written to in always block also written by another always block: 'q' - : ... note: In instance 't' - t/t_lint_multidriven_proc_bad.v:16:25: - 16 | always @(posedge clk) q <= ~d; +%Warning-MULTIDRIVENPROC: t/t_lint_multidriven_proc_bad.v:54:25: Variable written to in always block also written by another always block: 'q' + : ... note: In instance 't.u1' + t/t_lint_multidriven_proc_bad.v:54:25: + 54 | always @(posedge clk) q <= ~d; | ^ - t/t_lint_multidriven_proc_bad.v:15:25: ... Location of other write - 15 | always @(posedge clk) q <= d; + t/t_lint_multidriven_proc_bad.v:53:25: ... Location of other write + 53 | always @(posedge clk) q <= d; | ^ ... For warning description see https://verilator.org/warn/MULTIDRIVENPROC?v=latest ... Use "/* verilator lint_off MULTIDRIVENPROC */" and lint_on around source to disable this message. -%Warning-MULTIDRIVEN: t/t_lint_multidriven_proc_bad.v:26:18: Signal has multiple driving blocks with different clocking: 't2.q' - t/t_lint_multidriven_proc_bad.v:29:26: ... Location of first driving block - 29 | always @(posedge clka) q <= d; +%Warning-MULTIDRIVEN: t/t_lint_multidriven_proc_bad.v:15:18: Signal has multiple driving blocks with different clocking: 'q2' + t/t_lint_multidriven_proc_bad.v:67:26: ... Location of first driving block + 67 | always @(posedge clka) q <= d; | ^ - t/t_lint_multidriven_proc_bad.v:30:26: ... Location of other driving block - 30 | always @(posedge clkb) q <= ~d; + t/t_lint_multidriven_proc_bad.v:68:26: ... Location of other driving block + 68 | always @(posedge clkb) q <= ~d; | ^ ... 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. diff --git a/test_regress/t/t_lint_multidriven_proc_bad.py b/test_regress/t/t_lint_multidriven_proc_bad.py index 1f8c7cc3e..f0fd7cac3 100755 --- a/test_regress/t/t_lint_multidriven_proc_bad.py +++ b/test_regress/t/t_lint_multidriven_proc_bad.py @@ -11,18 +11,18 @@ import vltest_bootstrap test.scenarios('linter') -# MULTIDRIVENPROC is off by default; enable it explicitly. Module 't' (same -# clock) triggers it; module 't2' (different clocks) is reported by MULTIDRIVEN -# instead, confirming MULTIDRIVENPROC is suppressed there. -# -Wno-MULTITOP: both modules are top-level here on purpose (two independent -# demonstrations), so silence the unrelated multiple-top-module warning. +# MULTIDRIVENPROC is off by default; enable it explicitly. test.lint(fails=True, - verilator_flags2=['-Wwarn-MULTIDRIVENPROC', '-Wno-MULTITOP'], + verilator_flags2=['-Wwarn-MULTIDRIVENPROC'], expect_filename=test.golden_filename) test.extract(in_filename=test.top_filename, out_filename=test.root + "/docs/gen/ex_MULTIDRIVENPROC_faulty.rst", - lines="15-16") + lines="53-54") + +test.extract(in_filename=test.top_filename, + out_filename=test.root + "/docs/gen/ex_MULTIDRIVENPROC_loopidx.rst", + lines="81-89") test.extract(in_filename=test.golden_filename, out_filename=test.root + "/docs/gen/ex_MULTIDRIVENPROC_msg.rst", diff --git a/test_regress/t/t_lint_multidriven_proc_bad.v b/test_regress/t/t_lint_multidriven_proc_bad.v index 2bccbbf71..957e01143 100644 --- a/test_regress/t/t_lint_multidriven_proc_bad.v +++ b/test_regress/t/t_lint_multidriven_proc_bad.v @@ -4,9 +4,47 @@ // SPDX-FileCopyrightText: 2026 Aisha Salimgereyeva // SPDX-License-Identifier: CC0-1.0 + +module t ( + input wire clk, + input wire clka, + input wire clkb, + input wire d, + input wire [3:0] dv, + output logic q1, + output logic q2, + output logic [3:0] q3a, + output logic [3:0] q3b, + output logic [3:0] q3c +); + + t1 u1 ( + .clk(clk), + .d (d), + .q (q1) + ); + + t2 u2 ( + .clka(clka), + .clkb(clkb), + .d (d), + .q (q2) + ); + + t3 u3 ( + .clk(clk), + .d (dv), + .q1 (q3a), + .q2 (q3b), + .q3 (q3c) + ); + +endmodule + + // Same clock: two plain always blocks drive the whole of 'q', reported by // MULTIDRIVENPROC. -module t ( +module t1 ( input wire clk, input wire d, output logic q @@ -30,3 +68,28 @@ module t2 ( always @(posedge clkb) q <= ~d; endmodule + +// A static loop induction variable should not warn. +module t3 ( + input wire clk, + input wire [3:0] d, + output logic [3:0] q1, + output logic [3:0] q2, + output logic [3:0] q3 +); + + integer i; + + always @(posedge clk) begin + for (i = 0; i < 4; i = i + 1) q1[i] <= d[i]; + end + + always @(posedge clk) begin + for (i = 0; i < 4; i = i + 1) q2[i] <= ~d[i]; + end + + always @* begin + for (i = 0; i < 4; i = i + 1) q3[i] = d[i] ^ q1[i]; + end + +endmodule