feat(lint): add SYNTHUNPACKED warning for unpacked array ports
Add a new warning code SYNTHUNPACKED (disabled by default) that fires when a module IO port uses an unpacked array. Synthesis tools accept the construct but typically flatten the unpacked dimensions to packed bits in the netlist, so the post-synthesis port signature no longer matches the pre-synthesis source. A testbench that drove the RTL through such ports cannot connect to the netlist without modification, making gate-level simulation (GLS) harder. This commit: - Adds SYNTHUNPACKED to V3ErrorCode enum and names[] (alphabetical) - Adds SYNTHUNPACKED to defaultsOff() - Emits the warning in V3Width::visit(AstVar*) for non-interface IO ports whose dtype is an UnpackArrayDType - Documents the warning in docs/guide/warnings.rst - Adds a lint regression test (t_lint_synthunpacked_bad) Signed-off-by: Lucas Amaral <lucaaamaral@gmail.com>
This commit is contained in:
parent
c5798f902b
commit
821e86eb98
|
|
@ -2141,6 +2141,34 @@ List Of Warnings
|
||||||
correctly.
|
correctly.
|
||||||
|
|
||||||
|
|
||||||
|
.. option:: SYNTHUNPACKED
|
||||||
|
|
||||||
|
Warns that a module IO port uses an unpacked array. Synthesis tools
|
||||||
|
(e.g., Yosys, Cadence Genus, Synopsys Design Compiler) accept the
|
||||||
|
construct but typically flatten the unpacked dimensions to packed bits
|
||||||
|
in the netlist. The resulting post-synthesis port signature no longer
|
||||||
|
matches the pre-synthesis source, so a testbench that drove the RTL
|
||||||
|
through these ports cannot connect to the netlist without modification,
|
||||||
|
making gate-level simulation (GLS) harder.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
.. code-block:: sv
|
||||||
|
|
||||||
|
module dut(input wire [7:0] data [0:3]); // Will get SYNTHUNPACKED
|
||||||
|
...
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
To keep the pre- and post-synthesis port signatures aligned, flatten
|
||||||
|
the port to a packed vector at the module boundary, or wrap the array
|
||||||
|
in a packed struct.
|
||||||
|
|
||||||
|
Disabled by default since not all flows go through synthesis. Enable
|
||||||
|
with ``-Wwarn-SYNTHUNPACKED`` to surface it. Ignoring this warning does
|
||||||
|
not affect Verilator simulation; it only flags a downstream GLS
|
||||||
|
compatibility hazard.
|
||||||
|
|
||||||
|
|
||||||
.. option:: TASKNSVAR
|
.. option:: TASKNSVAR
|
||||||
|
|
||||||
Error when a call to a task or function has an inout from that task tied
|
Error when a call to a task or function has an inout from that task tied
|
||||||
|
|
|
||||||
|
|
@ -167,6 +167,7 @@ public:
|
||||||
SUPERNFIRST, // Super.new must be first statement
|
SUPERNFIRST, // Super.new must be first statement
|
||||||
SYMRSVDWORD, // Symbol is Reserved Word
|
SYMRSVDWORD, // Symbol is Reserved Word
|
||||||
SYNCASYNCNET, // Mixed sync + async reset
|
SYNCASYNCNET, // Mixed sync + async reset
|
||||||
|
SYNTHUNPACKED, // Unpacked array on IO port (synth packs these, breaking GLS)
|
||||||
TICKCOUNT, // Too large tick count
|
TICKCOUNT, // Too large tick count
|
||||||
TIMESCALEMOD, // Need timescale for module
|
TIMESCALEMOD, // Need timescale for module
|
||||||
UNDRIVEN, // No drivers
|
UNDRIVEN, // No drivers
|
||||||
|
|
@ -237,16 +238,17 @@ public:
|
||||||
"PROCASSWIRE", "PROFOUTOFDATE", "PROTECTED", "PROTOTYPEMIS", "RANDC", "REALCVT",
|
"PROCASSWIRE", "PROFOUTOFDATE", "PROTECTED", "PROTOTYPEMIS", "RANDC", "REALCVT",
|
||||||
"REDEFMACRO", "RISEFALLDLY", "SELRANGE", "SHORTREAL", "SIDEEFFECT", "SPECIFYIGN",
|
"REDEFMACRO", "RISEFALLDLY", "SELRANGE", "SHORTREAL", "SIDEEFFECT", "SPECIFYIGN",
|
||||||
"SPLITVAR", "STATICVAR", "STMTDLY", "SUPERNFIRST", "SYMRSVDWORD", "SYNCASYNCNET",
|
"SPLITVAR", "STATICVAR", "STMTDLY", "SUPERNFIRST", "SYMRSVDWORD", "SYNCASYNCNET",
|
||||||
"TICKCOUNT", "TIMESCALEMOD", "UNDRIVEN", "UNOPT", "UNOPTFLAT", "UNOPTTHREADS",
|
"SYNTHUNPACKED", "TICKCOUNT", "TIMESCALEMOD", "UNDRIVEN", "UNOPT", "UNOPTFLAT",
|
||||||
"UNPACKED", "UNSATCONSTR", "UNSIGNED", "UNUSED", "UNUSEDGENVAR", "UNUSEDLOOP",
|
"UNOPTTHREADS", "UNPACKED", "UNSATCONSTR", "UNSIGNED", "UNUSED", "UNUSEDGENVAR",
|
||||||
"UNUSEDPARAM", "UNUSEDSIGNAL", "USERERROR", "USERFATAL", "USERINFO", "USERWARN",
|
"UNUSEDLOOP", "UNUSEDPARAM", "UNUSEDSIGNAL", "USERERROR", "USERFATAL", "USERINFO",
|
||||||
"VARHIDDEN", "WAITCONST", "WIDTH", "WIDTHCONCAT", "WIDTHEXPAND", "WIDTHTRUNC",
|
"USERWARN", "VARHIDDEN", "WAITCONST", "WIDTH", "WIDTHCONCAT", "WIDTHEXPAND",
|
||||||
"WIDTHXZEXPAND", "ZERODLY", "ZEROREPL", " MAX"};
|
"WIDTHTRUNC", "WIDTHXZEXPAND", "ZERODLY", "ZEROREPL", " MAX"};
|
||||||
return names[m_e];
|
return names[m_e];
|
||||||
}
|
}
|
||||||
// Warnings that default to off
|
// Warnings that default to off
|
||||||
bool defaultsOff() const VL_MT_SAFE {
|
bool defaultsOff() const VL_MT_SAFE {
|
||||||
return (m_e == IMPERFECTSCH || m_e == I_CELLDEFINE || styleError());
|
return (m_e == IMPERFECTSCH || m_e == I_CELLDEFINE || m_e == SYNTHUNPACKED
|
||||||
|
|| styleError());
|
||||||
}
|
}
|
||||||
// Warnings that warn about nasty side effects
|
// Warnings that warn about nasty side effects
|
||||||
bool dangerous() const VL_MT_SAFE { return (m_e == COMBDLY); }
|
bool dangerous() const VL_MT_SAFE { return (m_e == COMBDLY); }
|
||||||
|
|
|
||||||
|
|
@ -2983,6 +2983,19 @@ class WidthVisitor final : public VNVisitor {
|
||||||
userIterateAndNext(nodep->delayp(), WidthVP{nodep->dtypep(), PRELIM}.p());
|
userIterateAndNext(nodep->delayp(), WidthVP{nodep->dtypep(), PRELIM}.p());
|
||||||
UINFO(4, "varWidthed " << nodep);
|
UINFO(4, "varWidthed " << nodep);
|
||||||
// UINFOTREE(1, nodep, "", "InitOut");
|
// UINFOTREE(1, nodep, "", "InitOut");
|
||||||
|
// Warn about unpacked arrays on module IO ports. Synthesis tools
|
||||||
|
// typically flatten these to packed bits in the netlist, which breaks
|
||||||
|
// pre-synthesis testbench connectivity for gate-level simulation.
|
||||||
|
if (nodep->isIO() && !nodep->isIfaceRef()) {
|
||||||
|
if (VN_IS(nodep->dtypep()->skipRefp(), UnpackArrayDType)) {
|
||||||
|
nodep->v3warn(SYNTHUNPACKED,
|
||||||
|
"Unpacked array on port "
|
||||||
|
<< nodep->prettyNameQ()
|
||||||
|
<< " is typically flattened to packed bits by synthesis,"
|
||||||
|
" breaking pre-synthesis testbench connectivity for"
|
||||||
|
" gate-level simulation");
|
||||||
|
}
|
||||||
|
}
|
||||||
nodep->didWidth(true);
|
nodep->didWidth(true);
|
||||||
nodep->doingWidth(false);
|
nodep->doingWidth(false);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
%Warning-SYNTHUNPACKED: t/t_lint_synthunpacked_bad.v:6:28: Unpacked array on port 'data' is typically flattened to packed bits by synthesis, breaking pre-synthesis testbench connectivity for gate-level simulation
|
||||||
|
: ... note: In instance 't'
|
||||||
|
6 | module t (input wire [7:0] data [0:3]);
|
||||||
|
| ^~~~
|
||||||
|
... For warning description see https://verilator.org/warn/SYNTHUNPACKED?v=latest
|
||||||
|
... Use "/* verilator lint_off SYNTHUNPACKED */" and lint_on around source to disable this message.
|
||||||
|
%Error: Exiting due to
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# DESCRIPTION: Verilator: Lint test for SYNTHUNPACKED warning
|
||||||
|
#
|
||||||
|
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||||
|
# SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
import vltest_bootstrap
|
||||||
|
|
||||||
|
test.scenarios('linter')
|
||||||
|
|
||||||
|
test.lint(fails=True, v_flags2=['-Wwarn-SYNTHUNPACKED'], expect_filename=test.golden_filename)
|
||||||
|
|
||||||
|
test.passes()
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
// DESCRIPTION: Verilator: Test of SYNTHUNPACKED warning on unpacked port
|
||||||
|
//
|
||||||
|
// SPDX-FileCopyrightText: 2026 Lucas Amaral
|
||||||
|
// SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
module t (input wire [7:0] data [0:3]);
|
||||||
|
initial $display("data[0]=%h", data[0]);
|
||||||
|
endmodule
|
||||||
Loading…
Reference in New Issue