diff --git a/src/V3Undriven.cpp b/src/V3Undriven.cpp index 78daba34d..6f7244e63 100644 --- a/src/V3Undriven.cpp +++ b/src/V3Undriven.cpp @@ -442,6 +442,16 @@ class UndrivenVisitor final : public VNVisitorConst { << " (IEEE 1800-2023 13.5): " << nodep->prettyNameQ()); } } + + // EOM + // If writeSummary is enabled, task/function definitions are treated as non-executed. + // Their effects are applied at call sites via writeSummary(), so don't let definition + // traversal create phantom "other writes" for MULTIDRIVEN. + if (m_enableWriteSummary && m_taskp && !m_alwaysp && !m_inContAssign && !m_inInitialStatic + && !m_inBBox) { + return; + } + for (int usr = 1; usr < (m_alwaysCombp ? 3 : 2); ++usr) { UndrivenVarEntry* const entryp = getEntryp(nodep->varp(), usr); const bool fdrv = nodep->access().isWriteOrRW() @@ -573,6 +583,15 @@ class UndrivenVisitor final : public VNVisitorConst { iterateChildrenConst(nodep); if (!m_enableWriteSummary || !m_capturep) return; + + // EOM + // If writeSummary is enabled, task/function definitions are treated as non-executed. + // Do not apply writeSummary at calls inside a task definition, or they will look like + // independent drivers (phantom MULTIDRIVEN). + if (m_taskp && !m_alwaysp && !m_inContAssign && !m_inInitialStatic && !m_inBBox) { + return; + } + AstNodeFTask* const calleep = nodep->taskp(); if (!calleep) return; diff --git a/src/V3UndrivenCapture.cpp b/src/V3UndrivenCapture.cpp index b8fcbd70f..9721edf6d 100644 --- a/src/V3UndrivenCapture.cpp +++ b/src/V3UndrivenCapture.cpp @@ -26,11 +26,13 @@ VL_DEFINE_DEBUG_FUNCTIONS; namespace { constexpr int DBG = 9; + struct Stats final { uint64_t ftasks = 0; uint64_t varWrites = 0; uint64_t callEdges = 0; } g_stats; + static std::string taskNameQ(const AstNodeFTask* taskp) { if (!taskp) return ""; return taskp->prettyNameQ(); @@ -105,7 +107,7 @@ private: } // namespace -bool V3UndrivenCapture::enableWriteSummary = false; +bool V3UndrivenCapture::enableWriteSummary = true; // static void V3UndrivenCapture::sortUniqueVars(std::vector& vec) { diff --git a/test_regress/t/t_lint_taskcall_multidriven_bad.out b/test_regress/t/t_lint_taskcall_multidriven_bad.out new file mode 100644 index 000000000..3e19de72c --- /dev/null +++ b/test_regress/t/t_lint_taskcall_multidriven_bad.out @@ -0,0 +1,11 @@ +%Warning-MULTIDRIVEN: t/t_lint_taskcall_multidriven_bad.v:28:15: Variable written to in always_comb also written by other process (IEEE 1800-2023 9.2.2.2): 'out' + : ... note: In instance 't' + t/t_lint_taskcall_multidriven_bad.v:28:15: + 28 | if (sel2) out = 1'b1; + | ^~~ + t/t_lint_taskcall_multidriven_bad.v:20:5: ... Location of other write + 20 | out = 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. +%Error: Exiting due to diff --git a/test_regress/t/t_lint_taskcall_multidriven_bad.py b/test_regress/t/t_lint_taskcall_multidriven_bad.py new file mode 100644 index 000000000..31228c9a7 --- /dev/null +++ b/test_regress/t/t_lint_taskcall_multidriven_bad.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2024 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() diff --git a/test_regress/t/t_lint_taskcall_multidriven_bad.v b/test_regress/t/t_lint_taskcall_multidriven_bad.v new file mode 100644 index 000000000..1872e8bcb --- /dev/null +++ b/test_regress/t/t_lint_taskcall_multidriven_bad.v @@ -0,0 +1,32 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty. +// SPDX-License-Identifier: CC0-1.0 + +module t ( + input logic sel + , input logic sel2 + , input logic d + , output logic out +); + + task automatic do_stuff(input logic din); + out = din; + endtask + + // Driver #1 (via task call) + always_comb begin + out = 1'b0; + if (sel) do_stuff(d); + end + + // Driver #2 (separate process) + // I only want the MULTIDRIVEN. +/* verilator lint_off LATCH */ + always_comb begin + if (sel2) out = 1'b1; + end +/* verilator lint_on LATCH */ + +endmodule diff --git a/test_regress/t/t_multidriven_simple0.py b/test_regress/t/t_multidriven_simple0.py new file mode 100755 index 000000000..c6e56559a --- /dev/null +++ b/test_regress/t/t_multidriven_simple0.py @@ -0,0 +1,18 @@ +#!/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('simulator') + +test.compile(verilator_flags2=["--binary"]) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_multidriven_simple0.v b/test_regress/t/t_multidriven_simple0.v new file mode 100644 index 000000000..66a50beed --- /dev/null +++ b/test_regress/t/t_multidriven_simple0.v @@ -0,0 +1,63 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty. +// SPDX-License-Identifier: CC0-1.0 + +// verilog_format: off +`define stop $stop +`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0); +// verilog_format: on + +module mod #()( + input logic sel + ,output logic val +); + + logic l0; + + task do_stuff(); + l0 = 'b1; + endtask + + always_comb begin + l0 = 'b0; + + if(sel) begin + do_stuff(); + end + end + + assign val = l0; + +endmodule + +module m_tb#()(); + + logic sel, val; + + mod m( + .sel(sel) + ,.val(val) + ); + + initial begin + #1; + sel = 'b0; + `checkd(val, 1'b0); + #1; + sel = 'b1; + `checkd(val, 1'b1); + #1; + sel = 'b0; + `checkd(val, 1'b0); + #1; + end + + initial begin + #5; + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule diff --git a/test_regress/t/t_multidriven_simple1.py b/test_regress/t/t_multidriven_simple1.py new file mode 100755 index 000000000..c6e56559a --- /dev/null +++ b/test_regress/t/t_multidriven_simple1.py @@ -0,0 +1,18 @@ +#!/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('simulator') + +test.compile(verilator_flags2=["--binary"]) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_multidriven_simple1.v b/test_regress/t/t_multidriven_simple1.v new file mode 100644 index 000000000..d5ad1d890 --- /dev/null +++ b/test_regress/t/t_multidriven_simple1.v @@ -0,0 +1,64 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty. +// SPDX-License-Identifier: CC0-1.0 + +// verilog_format: off +`define stop $stop +`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0); +// verilog_format: on + +module mod #()( + input logic sel + ,output logic val +); + + logic l0; + + task do_inner(); + l0 = 'b1; + endtask + + task do_outer(); + do_inner(); + endtask + + always_comb begin + l0 = 'b0; + if (sel) do_outer(); + end + + assign val = l0; + +endmodule + +module m_tb#()(); + + logic sel, val; + + mod m( + .sel(sel) + ,.val(val) + ); + + initial begin + #1; + sel = 'b0; + `checkd(val, 1'b0); + #1; + sel = 'b1; + `checkd(val, 1'b1); + #1; + sel = 'b0; + `checkd(val, 1'b0); + #1; + end + + initial begin + #5; + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule