V3Dfg: suppress MULTIDRIVEN for intentional interface tristate lowering while retaining coverage multidrive warnings
This commit is contained in:
parent
fb8452c563
commit
44ccdbf669
|
|
@ -1939,6 +1939,8 @@ class AstVar final : public AstNode {
|
|||
bool m_ignorePostWrite : 1; // Ignore writes in 'Post' blocks during ordering
|
||||
bool m_ignoreSchedWrite : 1; // Ignore writes in scheduling (for special optimizations)
|
||||
bool m_dfgMultidriven : 1; // Singal is multidriven, used by DFG to avoid repeat processing
|
||||
bool m_dfgTriLowered : 1; // Signal/temporary introduced by tristate lowering
|
||||
bool m_dfgAllowMultidriveTri : 1; // Allow DFG MULTIDRIVEN warning for intentional tri nets
|
||||
bool m_globalConstrained : 1; // Global constraint per IEEE 1800-2023 18.5.8
|
||||
bool m_isStdRandomizeArg : 1; // Argument variable created for std::randomize (__Varg*)
|
||||
void init() {
|
||||
|
|
@ -1993,6 +1995,8 @@ class AstVar final : public AstNode {
|
|||
m_ignorePostWrite = false;
|
||||
m_ignoreSchedWrite = false;
|
||||
m_dfgMultidriven = false;
|
||||
m_dfgTriLowered = false;
|
||||
m_dfgAllowMultidriveTri = false;
|
||||
m_globalConstrained = false;
|
||||
m_isStdRandomizeArg = false;
|
||||
}
|
||||
|
|
@ -2164,6 +2168,10 @@ public:
|
|||
void setIgnoreSchedWrite() { m_ignoreSchedWrite = true; }
|
||||
bool dfgMultidriven() const { return m_dfgMultidriven; }
|
||||
void setDfgMultidriven() { m_dfgMultidriven = true; }
|
||||
bool dfgTriLowered() const { return m_dfgTriLowered; }
|
||||
void setDfgTriLowered() { m_dfgTriLowered = true; }
|
||||
bool dfgAllowMultidriveTri() const { return m_dfgAllowMultidriveTri; }
|
||||
void setDfgAllowMultidriveTri() { m_dfgAllowMultidriveTri = true; }
|
||||
void globalConstrained(bool flag) { m_globalConstrained = flag; }
|
||||
bool globalConstrained() const { return m_globalConstrained; }
|
||||
bool isStdRandomizeArg() const { return m_isStdRandomizeArg; }
|
||||
|
|
|
|||
|
|
@ -662,6 +662,39 @@ class AstToDfgSynthesize final {
|
|||
return drivers;
|
||||
}
|
||||
|
||||
// Returns true if the driver cone contains any variable introduced by
|
||||
// tristate lowering. Used to distinguish intentional tristate contributor
|
||||
// overlap from accidental multidrive.
|
||||
static bool containsTriLoweredVar(DfgVertex* rootp) {
|
||||
std::vector<const DfgVertex*> stack;
|
||||
std::vector<const DfgVertex*> visited;
|
||||
stack.emplace_back(rootp);
|
||||
while (!stack.empty()) {
|
||||
const DfgVertex* const vtxp = stack.back();
|
||||
stack.pop_back();
|
||||
if (std::find(visited.begin(), visited.end(), vtxp) != visited.end()) continue;
|
||||
visited.emplace_back(vtxp);
|
||||
if (const DfgVertexVar* const varp = vtxp->cast<DfgVertexVar>()) {
|
||||
AstVar* const astVarp = [&]() -> AstVar* {
|
||||
if VL_CONSTEXPR_CXX17 (T_Scoped) {
|
||||
return reinterpret_cast<AstVarScope*>(varp->nodep())->varp();
|
||||
} else {
|
||||
return reinterpret_cast<AstVar*>(varp->nodep());
|
||||
}
|
||||
}();
|
||||
if (astVarp->dfgTriLowered()) return true;
|
||||
}
|
||||
vtxp->foreachSource([&](const DfgVertex& src) {
|
||||
stack.emplace_back(&src);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Returns true if the driver is a direct variable forward (no logic).
|
||||
static bool isDirectVarDriver(const DfgVertex* vtxp) { return vtxp->is<DfgVertexVar>(); }
|
||||
|
||||
// Gather all synthesized drivers of an unresolved variable
|
||||
static std::vector<Driver> gatherDriversUnresolved(DfgUnresolved* vtxp) {
|
||||
std::vector<Driver> drivers;
|
||||
|
|
@ -822,6 +855,18 @@ class AstToDfgSynthesize final {
|
|||
|
||||
// Loop index often abused, so suppress
|
||||
if (getAstVar(varp)->isUsedLoopIdx()) continue;
|
||||
// Tristate lowering can intentionally create overlapping contributors.
|
||||
// Keep the signal marked multidriven for DFG fallback, but suppress
|
||||
// warning only when both overlapping driver cones look tri-lowered.
|
||||
if (getAstVar(varp)->dfgAllowMultidriveTri()) {
|
||||
const bool iTri = containsTriLoweredVar(iD.m_vtxp);
|
||||
const bool jTri = containsTriLoweredVar(jD.m_vtxp);
|
||||
const bool triPair = iTri && jTri;
|
||||
const bool triAndBridge
|
||||
= (iTri && isDirectVarDriver(jD.m_vtxp))
|
||||
|| (jTri && isDirectVarDriver(iD.m_vtxp));
|
||||
if (triPair || triAndBridge) continue;
|
||||
}
|
||||
|
||||
// Warn the user now
|
||||
const std::string lo = std::to_string(jD.m_lo);
|
||||
|
|
|
|||
|
|
@ -776,6 +776,7 @@ class TristateVisitor final : public TristateBaseVisitor {
|
|||
AstVar* const newLhsp = new AstVar{varp->fileline(), VVarType::MODULETEMP,
|
||||
varp->name() + "__out" + cvtToStr(m_unique),
|
||||
varp}; // 2-state ok; sep enable
|
||||
newLhsp->setDfgTriLowered();
|
||||
UINFO(9, " newout " << newLhsp);
|
||||
nodep->addStmtsp(newLhsp);
|
||||
// When retargeting a VarXRef to a local __out var, the dotted path
|
||||
|
|
@ -797,6 +798,7 @@ class TristateVisitor final : public TristateBaseVisitor {
|
|||
AstVar* const newEnLhsp
|
||||
= new AstVar{varp->fileline(), VVarType::MODULETEMP,
|
||||
varp->name() + "__en" + cvtToStr(m_unique++), envarp}; // 2-state ok
|
||||
newEnLhsp->setDfgTriLowered();
|
||||
UINFO(9, " newenlhsp " << newEnLhsp);
|
||||
nodep->addStmtsp(newEnLhsp);
|
||||
|
||||
|
|
@ -903,12 +905,14 @@ class TristateVisitor final : public TristateBaseVisitor {
|
|||
// var__strength variable
|
||||
AstVar* const varStrengthp = new AstVar{fl, VVarType::MODULETEMP, strengthVarName,
|
||||
invarp}; // 2-state ok; sep enable;
|
||||
varStrengthp->setDfgTriLowered();
|
||||
UINFO(9, " newstrength " << varStrengthp);
|
||||
nodep->addStmtsp(varStrengthp);
|
||||
|
||||
// var__strength__en variable
|
||||
AstVar* const enVarStrengthp = new AstVar{
|
||||
fl, VVarType::MODULETEMP, strengthVarName + "__en", invarp}; // 2-state ok;
|
||||
enVarStrengthp->setDfgTriLowered();
|
||||
UINFO(9, " newenstrength " << enVarStrengthp);
|
||||
nodep->addStmtsp(enVarStrengthp);
|
||||
|
||||
|
|
@ -938,6 +942,9 @@ class TristateVisitor final : public TristateBaseVisitor {
|
|||
// The interface module owns the contribution vars; resolution happens in
|
||||
// combineIfaceContribs() after all modules are processed.
|
||||
if (isIfaceTri) {
|
||||
// This net intentionally has multiple contributors; DFG should not emit
|
||||
// MULTIDRIVEN warnings for this lowered tristate pattern.
|
||||
invarp->setDfgAllowMultidriveTri();
|
||||
AstNodeModule* const ifaceModp = findParentModule(invarp);
|
||||
UASSERT_OBJ(ifaceModp, invarp, "Interface tristate var has no parent module");
|
||||
const int contribIdx = static_cast<int>(m_ifaceContribs[invarp].size());
|
||||
|
|
@ -946,11 +953,13 @@ class TristateVisitor final : public TristateBaseVisitor {
|
|||
// Create contribution vars in the interface module
|
||||
AstVar* const contribOutp = new AstVar{
|
||||
fl, VVarType::MODULETEMP, invarp->name() + "__out" + cvtToStr(contribIdx), invarp};
|
||||
contribOutp->setDfgTriLowered();
|
||||
UINFO(9, " iface contribOut " << contribOutp);
|
||||
ifaceModp->addStmtsp(contribOutp);
|
||||
|
||||
AstVar* const contribEnp = new AstVar{
|
||||
fl, VVarType::MODULETEMP, invarp->name() + "__en" + cvtToStr(contribIdx), invarp};
|
||||
contribEnp->setDfgTriLowered();
|
||||
UINFO(9, " iface contribEn " << contribEnp);
|
||||
ifaceModp->addStmtsp(contribEnp);
|
||||
|
||||
|
|
@ -2137,6 +2146,7 @@ class TristateVisitor final : public TristateBaseVisitor {
|
|||
AstVar* envarp = VN_CAST(invarp->user1p(), Var);
|
||||
if (!envarp) {
|
||||
envarp = new AstVar{fl, VVarType::MODULETEMP, invarp->name() + "__en", invarp};
|
||||
envarp->setDfgTriLowered();
|
||||
ifaceModp->addStmtsp(envarp);
|
||||
invarp->user1p(envarp);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
%Warning-MULTIDRIVEN: t/t_lint_multidriven_coverage_bad.v:13:16: Bit [0] of signal 'w' have multiple combinational drivers. This can cause performance degradation.
|
||||
: ... note: In instance 't'
|
||||
t/t_lint_multidriven_coverage_bad.v:15:16: ... Location of offending driver
|
||||
15 | assign w[0] = a;
|
||||
| ^
|
||||
t/t_lint_multidriven_coverage_bad.v:16:16: ... Location of offending driver
|
||||
16 | assign w[0] = b;
|
||||
| ^
|
||||
... 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
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
#!/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('linter')
|
||||
|
||||
test.lint(verilator_flags2=["--coverage"], fails=True, expect_filename=test.golden_filename)
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
module t (
|
||||
input logic a,
|
||||
input logic b,
|
||||
output logic [1:0] y
|
||||
);
|
||||
|
||||
logic [1:0] w;
|
||||
|
||||
assign w[0] = a; // <--- Warning
|
||||
assign w[0] = b; // <--- Warning
|
||||
assign w[1] = 1'b0;
|
||||
|
||||
assign y = w;
|
||||
|
||||
endmodule
|
||||
Loading…
Reference in New Issue