diff --git a/docs/gen/ex_NOTREDOP_msg.rst b/docs/gen/ex_NOTREDOP_msg.rst new file mode 100644 index 000000000..1ab976a52 --- /dev/null +++ b/docs/gen/ex_NOTREDOP_msg.rst @@ -0,0 +1,7 @@ +.. comment: generated by t_lint_notredop_bad +.. code-block:: + + %Error-NOTREDOP: example.v:1:17 Logical not directly before reduction operator is illegal + : ... Suggest use parentheses, e.g. '!(|expr)' + 12 | assign y[0] = !|v; + | ^ diff --git a/docs/guide/warnings.rst b/docs/guide/warnings.rst index 923ed4d0e..4b0d1472b 100644 --- a/docs/guide/warnings.rst +++ b/docs/guide/warnings.rst @@ -1559,6 +1559,25 @@ List Of Warnings :vlopt:`--no-timing` option. +.. option:: NOTREDOP + + Error that a logical not operator is directly followed by an + unparenthesized reduction operator, such as ``!|a``. The IEEE 1800-2023 Annex A + grammar requires the operand of ``!`` to be a primary expression, not an + unparenthesized reduction expression. + + For example: + + .. include:: ../../docs/gen/ex_NOTREDOP_msg.rst + + Some simulators support this syntax as an extension, but it is recommended to fix + these to match IEEE. To do so, add parentheses around the reduction expression, + for example use ``!(|a)`` instead of ``!|a``. + + Suppressing this error will suppress the error message check; it will simulate + correctly. + + .. option:: NULLPORT Warns that a null port was detected in the module definition port diff --git a/src/V3Error.h b/src/V3Error.h index 29eb5fced..b38bf8203 100644 --- a/src/V3Error.h +++ b/src/V3Error.h @@ -140,6 +140,7 @@ public: NOLATCH, // No latch detected in always_latch block NONSTD, // Non-standard feature present in other sims NORETURN, // Function with no return + NOTREDOP, // Error: Logical not before reduction operator NULLPORT, // Null port detected in module definition PARAMNODEFAULT, // Parameter without default PINCONNECTEMPTY,// Cell pin connected by name with empty reference @@ -232,7 +233,7 @@ public: "IMPLICITSTATIC", "IMPORTSTAR", "IMPURE", "INCABSPATH", "INFINITELOOP", "INITIALDLY", "INSECURE", "INSIDETRUE", "LATCH", "LITENDIAN", "MINTYPMAXDLY", "MISINDENT", "MODDUP", "MODMISSING", "MULTIDRIVEN", "MULTITOP", "NEWERSTD", "NOEFFECT", "NOLATCH", "NONSTD", - "NORETURN", "NULLPORT", "PARAMNODEFAULT", "PINCONNECTEMPTY", "PINMISSING", + "NORETURN", "NOTREDOP", "NULLPORT", "PARAMNODEFAULT", "PINCONNECTEMPTY", "PINMISSING", "PINNOCONNECT", "PINNOTFOUND", "PKGNODECL", "PREPROCZERO", "PROCASSINIT", "PROCASSWIRE", "PROFOUTOFDATE", "PROTECTED", "PROTOTYPEMIS", "RANDC", "REALCVT", "REDEFMACRO", "RISEFALLDLY", "SELRANGE", "SHORTREAL", "SIDEEFFECT", "SPECIFYIGN", @@ -270,7 +271,7 @@ public: || m_e == BLKLOOPINIT || m_e == CONTASSREG || m_e == ENCAPSULATED || m_e == ENDLABEL || m_e == ENUMITEMWIDTH || m_e == ENUMVALUE || m_e == HIERPARAM || m_e == FUNCTIMECTL || m_e == IMPURE || m_e == MODMISSING - || m_e == PARAMNODEFAULT || m_e == PINNOTFOUND || m_e == PKGNODECL + || m_e == NOTREDOP || m_e == PARAMNODEFAULT || m_e == PINNOTFOUND || m_e == PKGNODECL || m_e == PROCASSWIRE || m_e == PROTOTYPEMIS || m_e == SUPERNFIRST || m_e == ZEROREPL); } diff --git a/src/V3ParseImp.cpp b/src/V3ParseImp.cpp index ee6459f5e..f7e7042d1 100644 --- a/src/V3ParseImp.cpp +++ b/src/V3ParseImp.cpp @@ -844,6 +844,15 @@ int V3ParseImp::tokenToBison() { // Called as global since bison doesn't have our pointer tokenPipelineSym(); // sets yylval m_bisonLastFileline = yylval.fl; + if (m_tokenLastBison.token == '!' + && (yylval.token == '&' || yylval.token == '|' || yylval.token == '^' + || yylval.token == yP_NAND || yylval.token == yP_NOR || yylval.token == yP_XNOR)) { + m_tokenLastBison.fl->v3warn( + NOTREDOP, + "Logical not directly before reduction operator is illegal\n" + << m_tokenLastBison.fl->warnMore() + << "... Suggest use parentheses, e.g. '!(|expr)'"); + } m_tokenLastBison = yylval; if (debug() >= 6 || debugFlex() >= 6 diff --git a/test_regress/t/t_lint_historical.v b/test_regress/t/t_lint_historical.v index ac72beaca..014c059a9 100644 --- a/test_regress/t/t_lint_historical.v +++ b/test_regress/t/t_lint_historical.v @@ -70,6 +70,7 @@ module t; // verilator lint_off NOLATCH // verilator lint_off NONSTD // verilator lint_off NORETURN + // verilator lint_off NOTREDOP // verilator lint_off NULLPORT // verilator lint_off PARAMNODEFAULT // verilator lint_off PINCONNECTEMPTY diff --git a/test_regress/t/t_lint_notredop.py b/test_regress/t/t_lint_notredop.py new file mode 100644 index 000000000..baf37ad0f --- /dev/null +++ b/test_regress/t/t_lint_notredop.py @@ -0,0 +1,17 @@ +#!/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('vlt') + +test.lint(top_filename="t/t_lint_notredop_bad.v", + verilator_flags2=["-Wno-NOTREDOP"]) + +test.passes() diff --git a/test_regress/t/t_lint_notredop_bad.out b/test_regress/t/t_lint_notredop_bad.out new file mode 100644 index 000000000..9e39c0ea9 --- /dev/null +++ b/test_regress/t/t_lint_notredop_bad.out @@ -0,0 +1,30 @@ +%Error-NOTREDOP: t/t_lint_notredop_bad.v:12:17: Logical not directly before reduction operator is illegal + : ... Suggest use parentheses, e.g. '!(|expr)' + 12 | assign y[0] = !|v; + | ^ + ... For error description see https://verilator.org/warn/NOTREDOP?v=latest +%Error-NOTREDOP: t/t_lint_notredop_bad.v:13:17: Logical not directly before reduction operator is illegal + : ... Suggest use parentheses, e.g. '!(|expr)' + 13 | assign y[1] = !&v; + | ^ +%Error-NOTREDOP: t/t_lint_notredop_bad.v:14:17: Logical not directly before reduction operator is illegal + : ... Suggest use parentheses, e.g. '!(|expr)' + 14 | assign y[2] = !^v; + | ^ +%Error-NOTREDOP: t/t_lint_notredop_bad.v:15:17: Logical not directly before reduction operator is illegal + : ... Suggest use parentheses, e.g. '!(|expr)' + 15 | assign y[3] = !~^v; + | ^ +%Error-NOTREDOP: t/t_lint_notredop_bad.v:16:17: Logical not directly before reduction operator is illegal + : ... Suggest use parentheses, e.g. '!(|expr)' + 16 | assign y[4] = !^~v; + | ^ +%Error-NOTREDOP: t/t_lint_notredop_bad.v:17:17: Logical not directly before reduction operator is illegal + : ... Suggest use parentheses, e.g. '!(|expr)' + 17 | assign y[5] = !~&v; + | ^ +%Error-NOTREDOP: t/t_lint_notredop_bad.v:18:17: Logical not directly before reduction operator is illegal + : ... Suggest use parentheses, e.g. '!(|expr)' + 18 | assign y[6] = !~|v; + | ^ +%Error: Exiting due to diff --git a/test_regress/t/t_lint_notredop_bad.py b/test_regress/t/t_lint_notredop_bad.py new file mode 100644 index 000000000..1c23f4712 --- /dev/null +++ b/test_regress/t/t_lint_notredop_bad.py @@ -0,0 +1,20 @@ +#!/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('vlt') + +test.lint(fails=True, expect_filename=test.golden_filename) + +test.extract(in_filename=test.golden_filename, + out_filename=test.root + "/docs/gen/ex_NOTREDOP_msg.rst", + lines="1-4") + +test.passes() diff --git a/test_regress/t/t_lint_notredop_bad.v b/test_regress/t/t_lint_notredop_bad.v new file mode 100644 index 000000000..07fd9946c --- /dev/null +++ b/test_regress/t/t_lint_notredop_bad.v @@ -0,0 +1,20 @@ +// 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 ( + input logic [3:0] v, + output logic [6:0] y +); + + assign y[0] = !|v; + assign y[1] = !&v; + assign y[2] = !^v; + assign y[3] = !~^v; + assign y[4] = !^~v; + assign y[5] = !~&v; + assign y[6] = !~|v; + +endmodule