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