From 471171701f37c22e15d89084d859daf37b0cbe98 Mon Sep 17 00:00:00 2001 From: em2machine Date: Sun, 21 Dec 2025 17:09:27 +0100 Subject: [PATCH 01/37] initial UndrivenCapture, just initial hacking --- src/CMakeLists.txt | 2 + src/Makefile_obj.in | 1 + src/V3UndrivenCapture.cpp | 201 ++++++++++++++++++++++++++++++++++++++ src/V3UndrivenCapture.h | 78 +++++++++++++++ 4 files changed, 282 insertions(+) create mode 100644 src/V3UndrivenCapture.cpp create mode 100644 src/V3UndrivenCapture.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a08cb45c2..705b2d0cf 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -188,6 +188,7 @@ set(HEADERS V3Tristate.h V3Udp.h V3Undriven.h + V3UndrivenCapture.h V3UniqueNames.h V3Unknown.h V3Unroll.h @@ -356,6 +357,7 @@ set(COMMON_SOURCES V3Tristate.cpp V3Udp.cpp V3Undriven.cpp + V3UndrivenCapture.cpp V3Unknown.cpp V3Unroll.cpp V3UnrollGen.cpp diff --git a/src/Makefile_obj.in b/src/Makefile_obj.in index 74696c1b1..adeaa97be 100644 --- a/src/Makefile_obj.in +++ b/src/Makefile_obj.in @@ -340,6 +340,7 @@ RAW_OBJS_PCH_ASTNOMT = \ V3Tristate.o \ V3Udp.o \ V3Undriven.o \ + V3UndrivenCapture.o \ V3Unknown.o \ V3Unroll.o \ V3UnrollGen.o \ diff --git a/src/V3UndrivenCapture.cpp b/src/V3UndrivenCapture.cpp new file mode 100644 index 000000000..cf06278cb --- /dev/null +++ b/src/V3UndrivenCapture.cpp @@ -0,0 +1,201 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// DESCRIPTION: Verilator: Capture task/function write summaries for undriven checks +// +// Code available from: https://verilator.org +// +//************************************************************************* +// +// Copyright 2003-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 +// +//************************************************************************* + +#include "V3UndrivenCapture.h" + +#include "V3Error.h" +#include "V3Global.h" + +#include + +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(); +} + +class CaptureVisitor final : public VNVisitorConst { + V3UndrivenCapture& m_cap; + const AstNodeFTask* m_curTaskp = nullptr; + + static void iterateListConst(VNVisitorConst& v, AstNode* nodep) { + for (AstNode* np = nodep; np; np = np->nextp()) np->accept(v); + } + + /* + V3UndrivenCapture::FTaskInfo& infoFor(const AstNodeFTask* taskp) { + // Ensure entry exists + return m_cap.writeSummary(taskp), const_cast( + *m_cap.find(taskp)); // not used; see below + } + */ + +public: + explicit CaptureVisitor(V3UndrivenCapture& cap, AstNetlist* netlistp) + : m_cap{cap} { + iterateConst(netlistp); + } + +private: + // Visit a task/function definition and collect direct writes and direct callees. + void visit(AstNodeFTask* nodep) override { + VL_RESTORER(m_curTaskp); + m_curTaskp = nodep; + ++g_stats.ftasks; + UINFO(DBG, "UndrivenCapture: enter ftask " << nodep << " " << nodep->prettyNameQ()); + (void)m_cap.ensureEntry(nodep); // ensure map entry exists + iterateListConst(*this, nodep->stmtsp()); + } + + void visit(AstNodeVarRef* nodep) override { + if (m_curTaskp && nodep->access().isWriteOrRW()) { + ++g_stats.varWrites; + UINFO(DBG, "UndrivenCapture: direct write in " << taskNameQ(m_curTaskp) << " var=" << nodep->varp()->prettyNameQ() << " at " << nodep->fileline()); + // Ensure entry exists + (void)m_cap.ensureEntry(m_curTaskp); + // Safe: find() must succeed after writeSummary() creates entry + auto* const infop = const_cast(m_cap.find(m_curTaskp)); + if (infop) infop->directWrites.push_back(nodep->varp()); + } + iterateChildrenConst(nodep); + } + + void visit(AstNodeFTaskRef* nodep) override { + // Record the call edge if resolved + if (m_curTaskp) { + if (AstNodeFTask* const calleep = nodep->taskp()) { + ++g_stats.callEdges; + UINFO(DBG, "UndrivenCapture: call edge " << taskNameQ(m_curTaskp) << " -> " << taskNameQ(calleep)); + (void)m_cap.ensureEntry(m_curTaskp); + (void)m_cap.ensureEntry(calleep); + auto* const infop + = const_cast(m_cap.find(m_curTaskp)); + if (infop) infop->callees.push_back(calleep); + } else { + UINFO(DBG, "UndrivenCapture: unresolved call in " << taskNameQ(m_curTaskp) << " name=" << nodep->name()); + } + } + iterateChildrenConst(nodep); // still scan pins/args + } + + void visit(AstNode* nodep) override { iterateChildrenConst(nodep); } +}; + +} // namespace + +// static +void V3UndrivenCapture::sortUniqueVars(std::vector& vec) { + std::sort(vec.begin(), vec.end()); + vec.erase(std::unique(vec.begin(), vec.end()), vec.end()); +} + +// static +void V3UndrivenCapture::sortUniqueFTasks(std::vector& vec) { + std::sort(vec.begin(), vec.end()); + vec.erase(std::unique(vec.begin(), vec.end()), vec.end()); +} + +V3UndrivenCapture::V3UndrivenCapture(AstNetlist* netlistp) { + gather(netlistp); + + // Normalize direct lists + for (auto& kv : m_info) { + sortUniqueVars(kv.second.directWrites); + sortUniqueFTasks(kv.second.callees); + } + + // Compute summaries for all tasks (MVP: DFS memoization, no SCC yet) + for (const auto& kv : m_info) (void)computeWriteSummary(kv.first); + + UINFO(DBG, "UndrivenCapture: stats ftasks=" << g_stats.ftasks << " varWrites=" << g_stats.varWrites << " callEdges=" << g_stats.callEdges << " uniqueTasks=" << m_info.size()); +} + +void V3UndrivenCapture::gather(AstNetlist* netlistp) { + // Walk netlist and populate m_info with direct writes + call edges + CaptureVisitor{*this, netlistp}; +} + +const V3UndrivenCapture::FTaskInfo* V3UndrivenCapture::find(FTask taskp) const { + const auto it = m_info.find(taskp); + if (it == m_info.end()) return nullptr; + return &it->second; +} + +const std::vector& V3UndrivenCapture::writeSummary(FTask taskp) { + // Ensure entry exists even if empty + (void)m_info[taskp]; + return computeWriteSummary(taskp); +} + +const std::vector& V3UndrivenCapture::computeWriteSummary(FTask taskp) { + FTaskInfo& info = m_info[taskp]; + + if (info.state == State::DONE) { + UINFO(DBG, "UndrivenCapture: writeSummary cached size=" << info.writeSummary.size() << " for " << taskNameQ(taskp)); + return info.writeSummary; + } + if (info.state == State::VISITING) { + UINFO(DBG, "UndrivenCapture: recursion detected at " << taskNameQ(taskp) << " returning directWrites size=" << info.directWrites.size()); + // Cycle detected (recursion/mutual recursion). MVP behavior: + // return directWrites only to guarantee termination. + if (info.writeSummary.empty()) info.writeSummary = info.directWrites; + sortUniqueVars(info.writeSummary); + return info.writeSummary; + } + + info.state = State::VISITING; + + // Start with direct writes + info.writeSummary = info.directWrites; + + // Union in callees + for (FTask calleep : info.callees) { + if (m_info.find(calleep) == m_info.end()) continue; + const std::vector& sub = computeWriteSummary(calleep); + info.writeSummary.insert(info.writeSummary.end(), sub.begin(), sub.end()); + } + + sortUniqueVars(info.writeSummary); + + UINFO(DBG, "UndrivenCapture: writeSummary computed size=" << info.writeSummary.size() << " for " << taskNameQ(taskp)); + + info.state = State::DONE; + return info.writeSummary; +} + +void V3UndrivenCapture::debugDumpTask(FTask taskp, int level) const { + const auto* const infop = find(taskp); + if (!infop) { + UINFO(level, "UndrivenCapture: no entry for task " << taskp); + return; + } + UINFO(level, "UndrivenCapture: dump task " + << taskp << " " << taskp->prettyNameQ() + << " directWrites=" << infop->directWrites.size() + << " callees=" << infop->callees.size() + << " writeSummary=" << infop->writeSummary.size()); +} + +void V3UndrivenCapture::ensureEntry(FTask taskp) { (void)m_info[taskp]; } diff --git a/src/V3UndrivenCapture.h b/src/V3UndrivenCapture.h new file mode 100644 index 000000000..aaf5b253d --- /dev/null +++ b/src/V3UndrivenCapture.h @@ -0,0 +1,78 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// DESCRIPTION: Verilator: Capture task/function write summaries for undriven checks +// +// Code available from: https://verilator.org +// +//************************************************************************* +// +// Copyright 2003-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 +// +//************************************************************************* + +#ifndef VERILATOR_V3UNDRIVENCAPTURE_H_ +#define VERILATOR_V3UNDRIVENCAPTURE_H_ + +#include "config_build.h" + +#include "V3Ast.h" + +#include +#include + +class AstNetlist; + +class V3UndrivenCapture final { +public: + using FTask = const AstNodeFTask*; + using Var = AstVar*; + + // DFS computation state for writeSummary propagation. + enum class State : uint8_t { UNVISITED, VISITING, DONE }; + + struct FTaskInfo final { + // Variables written directly in this task/function body. + std::vector directWrites; + // Direct resolved callees from this task/function body. + std::vector callees; + // Transitive "may write" summary: directWrites plus all callees' summaries. + std::vector writeSummary; + // Memoization state for writeSummary computation. + State state = State::UNVISITED; + }; + +private: + // Per-task/function capture info keyed by resolved AstNodeFTask* identity. + std::unordered_map m_info; + + // Sort and remove duplicates from a vector of variables. + static void sortUniqueVars(std::vector& vec); + // Sort and remove duplicates from a vector of callees. + static void sortUniqueFTasks(std::vector& vec); + + // Collect direct writes and call edges for all tasks/functions. + void gather(AstNetlist* netlistp); + // Compute (and memoize) transitive writeSummary for the given task/function. + const std::vector& computeWriteSummary(FTask taskp); + +public: + // Build capture database and precompute writeSummary for all discovered tasks/functions. + explicit V3UndrivenCapture(AstNetlist* netlistp); + + // Lookup task/function capture info (nullptr if unknown). + const FTaskInfo* find(FTask taskp) const; + // Get transitive writeSummary for a task/function (creates empty entry if needed). + const std::vector& writeSummary(FTask taskp); + + // Ensure an entry exists for a task/function without computing its writeSummary. + void ensureEntry(FTask taskp); + + // Optional: dump one task's summary (for debug bring-up). You can omit if you prefer. + void debugDumpTask(FTask taskp, int level = 9) const; +}; + +#endif // VERILATOR_V3UNDRIVENCAPTURE_H_ \ No newline at end of file From c801718ba1fcde2275eb48435f459565164c0eeb Mon Sep 17 00:00:00 2001 From: em2machine Date: Sun, 21 Dec 2025 18:22:42 +0100 Subject: [PATCH 02/37] it compiles ^^ --- src/V3Undriven.cpp | 85 +++++++++++++++++++++++++++++++++++---- src/V3UndrivenCapture.cpp | 2 + src/V3UndrivenCapture.h | 2 + 3 files changed, 82 insertions(+), 7 deletions(-) diff --git a/src/V3Undriven.cpp b/src/V3Undriven.cpp index 75e646520..78daba34d 100644 --- a/src/V3Undriven.cpp +++ b/src/V3Undriven.cpp @@ -29,6 +29,9 @@ #include "V3Stats.h" +// EOM +#include "V3UndrivenCapture.h" + #include VL_DEFINE_DEBUG_FUNCTIONS; @@ -51,6 +54,10 @@ class UndrivenVarEntry final { const FileLine* m_nodeFileLinep = nullptr; // File line of varref if driven, else nullptr bool m_underGen = false; // Under a generate + // EOM + const AstNode* m_callNodep = nullptr; // call node if driven via writeSummary, else nullptr + const FileLine* m_callFileLinep = nullptr; // file line of call node if driven via summary + enum : uint8_t { FLAG_USED = 0, FLAG_DRIVEN = 1, FLAG_DRIVEN_ALWCOMB = 2, FLAGS_PER_BIT = 3 }; public: @@ -278,6 +285,17 @@ public: } } } + + // EOM + void drivenViaCall(const AstNode* nodep, const FileLine* fileLinep) { + drivenWhole(); + if (!m_callNodep) { + m_callNodep = nodep; + m_callFileLinep = fileLinep; + } + } + const AstNode* getCallNodep() const { return m_callNodep; } + const FileLine* getCallFileLinep() const { return m_callFileLinep; } }; //###################################################################### @@ -304,6 +322,10 @@ class UndrivenVisitor final : public VNVisitorConst { const AstAlways* m_alwaysp = nullptr; // Current always of either type const AstAlways* m_alwaysCombp = nullptr; // Current always if combo, otherwise nullptr + // EOM + V3UndrivenCapture* const m_capturep = nullptr; + const bool m_enableWriteSummary = false; + // METHODS UndrivenVarEntry* getEntryp(AstVar* nodep, int which_user) { @@ -432,7 +454,14 @@ class UndrivenVisitor final : public VNVisitorConst { if (entryp->isDrivenWhole() && !m_inBBox && !VN_IS(nodep, VarXRef) && !VN_IS(nodep->dtypep()->skipRefp(), UnpackArrayDType) && nodep->fileline() != entryp->getNodeFileLinep() && !entryp->isUnderGen() - && entryp->getNodep()) { + // EOM + //&& entryp->getNodep()) { + && (entryp->getNodep() || (V3UndrivenCapture::enableWriteSummary && entryp->getCallNodep()))) { + + // EOM + const AstNode* const otherWritep = entryp->getNodep() ? static_cast(entryp->getNodep()) + : (V3UndrivenCapture::enableWriteSummary ? entryp->getCallNodep() : nullptr); + if (m_alwaysCombp && (!entryp->isDrivenAlwaysCombWhole() || (m_alwaysCombp != entryp->getAlwCombp() @@ -443,9 +472,12 @@ class UndrivenVisitor final : public VNVisitorConst { << " (IEEE 1800-2023 9.2.2.2): " << nodep->prettyNameQ() << '\n' << nodep->warnOther() << '\n' << nodep->warnContextPrimary() << '\n' - << entryp->getNodep()->warnOther() + // EOM + //<< entryp->getNodep()->warnOther() + << otherWritep->warnOther() << "... Location of other write\n" - << entryp->getNodep()->warnContextSecondary()); + //<< entryp->getNodep()->warnContextSecondary()); + << otherWritep->warnContextSecondary()); } if (!m_alwaysCombp && entryp->isDrivenAlwaysCombWhole()) { nodep->v3warn(MULTIDRIVEN, @@ -454,9 +486,12 @@ class UndrivenVisitor final : public VNVisitorConst { << '\n' << nodep->warnOther() << '\n' << nodep->warnContextPrimary() << '\n' - << entryp->getNodep()->warnOther() + //EOM + //<< entryp->getNodep()->warnOther() + << otherWritep->warnOther() << "... Location of always_comb write\n" - << entryp->getNodep()->warnContextSecondary()); + //<< entryp->getNodep()->warnContextSecondary()); + << otherWritep->warnContextSecondary()); } } entryp->drivenWhole(nodep, nodep->fileline()); @@ -523,11 +558,34 @@ class UndrivenVisitor final : public VNVisitorConst { iterateChildrenConst(nodep); if (nodep->keyword() == VAlwaysKwd::ALWAYS_COMB) UINFO(9, " Done " << nodep); } + // EOM + /* void visit(AstNodeFTaskRef* nodep) override { VL_RESTORER(m_inFTaskRef); m_inFTaskRef = true; iterateChildrenConst(nodep); } + */ + void visit(AstNodeFTaskRef* nodep) override { + VL_RESTORER(m_inFTaskRef); + m_inFTaskRef = true; + + iterateChildrenConst(nodep); + + if (!m_enableWriteSummary || !m_capturep) return; + AstNodeFTask* const calleep = nodep->taskp(); + if (!calleep) return; + + const auto& vars = m_capturep->writeSummary(calleep); + for (AstVar* const varp : vars) { + for (int usr = 1; usr < (m_alwaysCombp ? 3 : 2); ++usr) { + UndrivenVarEntry* const entryp = getEntryp(varp, usr); + entryp->drivenViaCall(nodep, nodep->fileline()); + if (m_alwaysCombp) + entryp->drivenAlwaysCombWhole(m_alwaysCombp, m_alwaysCombp->fileline()); + } + } + } void visit(AstNodeFTask* nodep) override { VL_RESTORER(m_taskp); @@ -556,7 +614,13 @@ class UndrivenVisitor final : public VNVisitorConst { public: // CONSTRUCTORS - explicit UndrivenVisitor(AstNetlist* nodep) { iterateConst(nodep); } + // EOM + // explicit UndrivenVisitor(AstNetlist* nodep) { iterateConst(nodep); } + explicit UndrivenVisitor(AstNetlist* nodep, V3UndrivenCapture* capturep, bool enableWriteSummary) : + m_capturep{capturep}, + m_enableWriteSummary{enableWriteSummary} + { iterateConst(nodep); } + ~UndrivenVisitor() override { for (UndrivenVarEntry* ip : m_entryps[1]) ip->reportViolations(); for (int usr = 1; usr < 3; ++usr) { @@ -570,6 +634,13 @@ public: void V3Undriven::undrivenAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ":"); - { UndrivenVisitor{nodep}; } + // EOM + //{ UndrivenVisitor{nodep}; } + if (V3UndrivenCapture::enableWriteSummary) { + V3UndrivenCapture capture{nodep}; + { UndrivenVisitor{nodep, &capture, V3UndrivenCapture::enableWriteSummary}; } + } else { + { UndrivenVisitor{nodep, nullptr, V3UndrivenCapture::enableWriteSummary}; } + } if (v3Global.opt.stats()) V3Stats::statsStage("undriven"); } diff --git a/src/V3UndrivenCapture.cpp b/src/V3UndrivenCapture.cpp index cf06278cb..b8fcbd70f 100644 --- a/src/V3UndrivenCapture.cpp +++ b/src/V3UndrivenCapture.cpp @@ -105,6 +105,8 @@ private: } // namespace +bool V3UndrivenCapture::enableWriteSummary = false; + // static void V3UndrivenCapture::sortUniqueVars(std::vector& vec) { std::sort(vec.begin(), vec.end()); diff --git a/src/V3UndrivenCapture.h b/src/V3UndrivenCapture.h index aaf5b253d..9b2881610 100644 --- a/src/V3UndrivenCapture.h +++ b/src/V3UndrivenCapture.h @@ -45,6 +45,8 @@ public: State state = State::UNVISITED; }; + static bool enableWriteSummary; + private: // Per-task/function capture info keyed by resolved AstNodeFTask* identity. std::unordered_map m_info; From 76886e14e6ca2a20b273952cebbdead18d762d9b Mon Sep 17 00:00:00 2001 From: em2machine Date: Sun, 21 Dec 2025 19:11:39 +0100 Subject: [PATCH 03/37] my OG test, a test that should fail, and a variant of the OG with nested tasks --- src/V3Undriven.cpp | 19 ++++++ src/V3UndrivenCapture.cpp | 4 +- .../t/t_lint_taskcall_multidriven_bad.out | 11 ++++ .../t/t_lint_taskcall_multidriven_bad.py | 16 +++++ .../t/t_lint_taskcall_multidriven_bad.v | 32 ++++++++++ test_regress/t/t_multidriven_simple0.py | 18 ++++++ test_regress/t/t_multidriven_simple0.v | 63 ++++++++++++++++++ test_regress/t/t_multidriven_simple1.py | 18 ++++++ test_regress/t/t_multidriven_simple1.v | 64 +++++++++++++++++++ 9 files changed, 244 insertions(+), 1 deletion(-) create mode 100644 test_regress/t/t_lint_taskcall_multidriven_bad.out create mode 100644 test_regress/t/t_lint_taskcall_multidriven_bad.py create mode 100644 test_regress/t/t_lint_taskcall_multidriven_bad.v create mode 100755 test_regress/t/t_multidriven_simple0.py create mode 100644 test_regress/t/t_multidriven_simple0.v create mode 100755 test_regress/t/t_multidriven_simple1.py create mode 100644 test_regress/t/t_multidriven_simple1.v 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 From 58bc98c82bdba6f568648294bde1d1ebb06cbc4a Mon Sep 17 00:00:00 2001 From: em2machine Date: Sun, 21 Dec 2025 19:19:04 +0100 Subject: [PATCH 04/37] rest of common variants --- test_regress/t/t_multidriven_simple2.py | 18 +++++++ test_regress/t/t_multidriven_simple2.v | 62 +++++++++++++++++++++++++ test_regress/t/t_multidriven_simple3.py | 18 +++++++ test_regress/t/t_multidriven_simple3.v | 62 +++++++++++++++++++++++++ test_regress/t/t_multidriven_simple4.py | 18 +++++++ test_regress/t/t_multidriven_simple4.v | 61 ++++++++++++++++++++++++ 6 files changed, 239 insertions(+) create mode 100755 test_regress/t/t_multidriven_simple2.py create mode 100644 test_regress/t/t_multidriven_simple2.v create mode 100755 test_regress/t/t_multidriven_simple3.py create mode 100644 test_regress/t/t_multidriven_simple3.v create mode 100755 test_regress/t/t_multidriven_simple4.py create mode 100644 test_regress/t/t_multidriven_simple4.v diff --git a/test_regress/t/t_multidriven_simple2.py b/test_regress/t/t_multidriven_simple2.py new file mode 100755 index 000000000..c6e56559a --- /dev/null +++ b/test_regress/t/t_multidriven_simple2.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_simple2.v b/test_regress/t/t_multidriven_simple2.v new file mode 100644 index 000000000..15f80e1e5 --- /dev/null +++ b/test_regress/t/t_multidriven_simple2.v @@ -0,0 +1,62 @@ +// 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 + +// task writes through an argument (output argument of a task) + +// 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 automatic do_stuff(output logic q); + q = 1'b1; + endtask + + always_comb begin + l0 = 1'b0; + if (sel) do_stuff(l0); + 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_simple3.py b/test_regress/t/t_multidriven_simple3.py new file mode 100755 index 000000000..c6e56559a --- /dev/null +++ b/test_regress/t/t_multidriven_simple3.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_simple3.v b/test_regress/t/t_multidriven_simple3.v new file mode 100644 index 000000000..7ea624d45 --- /dev/null +++ b/test_regress/t/t_multidriven_simple3.v @@ -0,0 +1,62 @@ +// 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 + +// function call that writes + +// 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; + +function automatic void do_func(); + l0 = 1'b1; +endfunction + +always_comb begin + l0 = 1'b0; + if (sel) do_func(); +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_simple4.py b/test_regress/t/t_multidriven_simple4.py new file mode 100755 index 000000000..c6e56559a --- /dev/null +++ b/test_regress/t/t_multidriven_simple4.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_simple4.v b/test_regress/t/t_multidriven_simple4.v new file mode 100644 index 000000000..475c8589b --- /dev/null +++ b/test_regress/t/t_multidriven_simple4.v @@ -0,0 +1,61 @@ +// 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 automatic set1(); l0 = 1'b1; endtask + task automatic set0(); l0 = 1'b0; endtask + + always_comb begin + set0(); + if (sel) begin + set1(); + 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 From 19880520ea838c79f60b06d51ec0045f075eb2e0 Mon Sep 17 00:00:00 2001 From: em2machine Date: Sun, 21 Dec 2025 19:32:01 +0100 Subject: [PATCH 05/37] multidriven iface variant tests --- test_regress/t/t_multidriven_iface0.py | 18 +++++++ test_regress/t/t_multidriven_iface0.v | 68 ++++++++++++++++++++++++ test_regress/t/t_multidriven_iface1.py | 18 +++++++ test_regress/t/t_multidriven_iface1.v | 68 ++++++++++++++++++++++++ test_regress/t/t_multidriven_iface2.py | 18 +++++++ test_regress/t/t_multidriven_iface2.v | 69 ++++++++++++++++++++++++ test_regress/t/t_multidriven_iface3.py | 18 +++++++ test_regress/t/t_multidriven_iface3.v | 73 ++++++++++++++++++++++++++ test_regress/t/t_multidriven_iface4.py | 18 +++++++ test_regress/t/t_multidriven_iface4.v | 69 ++++++++++++++++++++++++ test_regress/t/t_multidriven_simple1.v | 2 + 11 files changed, 439 insertions(+) create mode 100755 test_regress/t/t_multidriven_iface0.py create mode 100644 test_regress/t/t_multidriven_iface0.v create mode 100755 test_regress/t/t_multidriven_iface1.py create mode 100644 test_regress/t/t_multidriven_iface1.v create mode 100755 test_regress/t/t_multidriven_iface2.py create mode 100644 test_regress/t/t_multidriven_iface2.v create mode 100755 test_regress/t/t_multidriven_iface3.py create mode 100644 test_regress/t/t_multidriven_iface3.v create mode 100755 test_regress/t/t_multidriven_iface4.py create mode 100644 test_regress/t/t_multidriven_iface4.v diff --git a/test_regress/t/t_multidriven_iface0.py b/test_regress/t/t_multidriven_iface0.py new file mode 100755 index 000000000..c6e56559a --- /dev/null +++ b/test_regress/t/t_multidriven_iface0.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_iface0.v b/test_regress/t/t_multidriven_iface0.v new file mode 100644 index 000000000..e54160162 --- /dev/null +++ b/test_regress/t/t_multidriven_iface0.v @@ -0,0 +1,68 @@ +// 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 + +// multidriven interface test - direct assignment to interface signal and task assign in same process + +// 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 + +interface my_if; + logic l0; + + task set_l0_1(); l0 = 1'b1; endtask + task set_l0_0(); l0 = 1'b0; endtask +endinterface + +module mod #()( + input logic sel + ,output logic val +); + + my_if if0(); + + always_comb begin + if0.l0 = 1'b0; + + if(sel) begin + if0.set_l0_1(); + end + end + + assign val = if0.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_iface1.py b/test_regress/t/t_multidriven_iface1.py new file mode 100755 index 000000000..c6e56559a --- /dev/null +++ b/test_regress/t/t_multidriven_iface1.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_iface1.v b/test_regress/t/t_multidriven_iface1.v new file mode 100644 index 000000000..7ff3bd263 --- /dev/null +++ b/test_regress/t/t_multidriven_iface1.v @@ -0,0 +1,68 @@ +// 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 + +// interface task chain - nested task calls write interface signal in same always_comb + +// 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 + +interface my_if; + logic l0; + + task set_l0_1_inner(); l0 = 1'b1; endtask + task set_l0_1_outer(); set_l0_1_inner(); endtask +endinterface + +module mod #()( + input logic sel + ,output logic val +); + + my_if if0(); + + always_comb begin + if0.l0 = 1'b0; + + if (sel) begin + if0.set_l0_1_outer(); + end + end + + assign val = if0.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_iface2.py b/test_regress/t/t_multidriven_iface2.py new file mode 100755 index 000000000..c6e56559a --- /dev/null +++ b/test_regress/t/t_multidriven_iface2.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_iface2.v b/test_regress/t/t_multidriven_iface2.v new file mode 100644 index 000000000..c0dd55c33 --- /dev/null +++ b/test_regress/t/t_multidriven_iface2.v @@ -0,0 +1,69 @@ +// 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 + +// interface passed through module port - direct assign + task call in same always_comb + +// 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 + +interface my_if; + logic l0; + + task set_l0_1(); l0 = 1'b1; endtask + task set_l0_0(); l0 = 1'b0; endtask +endinterface + +module mod #()( + input logic sel + ,output logic val + ,my_if ifp +); + + always_comb begin + ifp.l0 = 1'b0; + + if (sel) begin + ifp.set_l0_1(); + end + end + + assign val = ifp.l0; + +endmodule + +module m_tb#()(); + + logic sel, val; + my_if if0(); + + mod m( + .sel(sel) + ,.val(val) + ,.ifp(if0) + ); + + 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_iface3.py b/test_regress/t/t_multidriven_iface3.py new file mode 100755 index 000000000..c6e56559a --- /dev/null +++ b/test_regress/t/t_multidriven_iface3.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_iface3.v b/test_regress/t/t_multidriven_iface3.v new file mode 100644 index 000000000..597ab6ed9 --- /dev/null +++ b/test_regress/t/t_multidriven_iface3.v @@ -0,0 +1,73 @@ +// 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 + +// interface modport + task import - write interface signal in same always_comb + +// 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 + +interface my_if; + logic l0; + + task set_l0_1(); l0 = 1'b1; endtask + + modport mp ( + output l0, + import set_l0_1 + ); +endinterface + +module mod #()( + input logic sel + ,output logic val + ,my_if.mp ifp +); + + always_comb begin + ifp.l0 = 1'b0; + + if (sel) begin + ifp.set_l0_1(); + end + end + + assign val = ifp.l0; + +endmodule + +module m_tb#()(); + + logic sel, val; + my_if if0(); + + mod m( + .sel(sel) + ,.val(val) + ,.ifp(if0) + ); + + 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_iface4.py b/test_regress/t/t_multidriven_iface4.py new file mode 100755 index 000000000..c6e56559a --- /dev/null +++ b/test_regress/t/t_multidriven_iface4.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_iface4.v b/test_regress/t/t_multidriven_iface4.v new file mode 100644 index 000000000..af9d93589 --- /dev/null +++ b/test_regress/t/t_multidriven_iface4.v @@ -0,0 +1,69 @@ +// 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 + +// interface task writes through output formal - actual is interface member + +// 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 + +interface my_if; + logic l0; + + task automatic set_any(output logic q); + q = 1'b1; + endtask +endinterface + +module mod #()( + input logic sel + ,output logic val +); + + my_if if0(); + + always_comb begin + if0.l0 = 1'b0; + + if (sel) begin + if0.set_any(if0.l0); + end + end + + assign val = if0.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.v b/test_regress/t/t_multidriven_simple1.v index d5ad1d890..ef5e9d164 100644 --- a/test_regress/t/t_multidriven_simple1.v +++ b/test_regress/t/t_multidriven_simple1.v @@ -4,6 +4,8 @@ // without warranty. // SPDX-License-Identifier: CC0-1.0 +// task chain - testing nested task calls + // 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); From 667f99bc4149c5a05462d8eb03b57533f4540eec Mon Sep 17 00:00:00 2001 From: em2machine <92717390+em2machine@users.noreply.github.com> Date: Sun, 21 Dec 2025 19:34:55 +0100 Subject: [PATCH 06/37] a class test --- test_regress/t/t_multidriven_class0.py | 18 +++++++ test_regress/t/t_multidriven_class0.v | 72 ++++++++++++++++++++++++++ 2 files changed, 90 insertions(+) create mode 100755 test_regress/t/t_multidriven_class0.py create mode 100644 test_regress/t/t_multidriven_class0.v diff --git a/test_regress/t/t_multidriven_class0.py b/test_regress/t/t_multidriven_class0.py new file mode 100755 index 000000000..c6e56559a --- /dev/null +++ b/test_regress/t/t_multidriven_class0.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_class0.v b/test_regress/t/t_multidriven_class0.v new file mode 100644 index 000000000..37bdb3240 --- /dev/null +++ b/test_regress/t/t_multidriven_class0.v @@ -0,0 +1,72 @@ +// 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 + +// class task writes through ref argument (direct assignment + class task in same always_comb) + +// 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 + +class C; + task automatic set1(ref logic q); + q = 1'b1; + endtask + task automatic set0(ref logic q); + q = 1'b0; + endtask +endclass + +module mod #()( + input logic sel + ,output logic val +); + + logic l0; + C c; + + initial c = new; + + always_comb begin + l0 = 1'b0; + if (sel) begin + c.set1(l0); + 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 From 96d2b15debb5b01778221ae77cc95da7bcc2fc9e Mon Sep 17 00:00:00 2001 From: em2machine <92717390+em2machine@users.noreply.github.com> Date: Sun, 21 Dec 2025 20:03:09 +0100 Subject: [PATCH 07/37] multidriven class task and class function tests --- test_regress/t/t_multidriven_class0.v | 6 +- test_regress/t/t_multidriven_class1.py | 18 ++++++ test_regress/t/t_multidriven_class1.v | 72 ++++++++++++++++++++++ test_regress/t/t_multidriven_class2.py | 18 ++++++ test_regress/t/t_multidriven_class2.v | 71 ++++++++++++++++++++++ test_regress/t/t_multidriven_class3.py | 18 ++++++ test_regress/t/t_multidriven_class3.v | 66 +++++++++++++++++++++ test_regress/t/t_multidriven_class4.py | 18 ++++++ test_regress/t/t_multidriven_class4.v | 79 +++++++++++++++++++++++++ test_regress/t/t_multidriven_classf0.py | 18 ++++++ test_regress/t/t_multidriven_classf0.v | 69 +++++++++++++++++++++ test_regress/t/t_multidriven_classf1.py | 18 ++++++ test_regress/t/t_multidriven_classf1.v | 66 +++++++++++++++++++++ 13 files changed, 534 insertions(+), 3 deletions(-) create mode 100755 test_regress/t/t_multidriven_class1.py create mode 100644 test_regress/t/t_multidriven_class1.v create mode 100755 test_regress/t/t_multidriven_class2.py create mode 100644 test_regress/t/t_multidriven_class2.v create mode 100755 test_regress/t/t_multidriven_class3.py create mode 100644 test_regress/t/t_multidriven_class3.v create mode 100755 test_regress/t/t_multidriven_class4.py create mode 100644 test_regress/t/t_multidriven_class4.v create mode 100755 test_regress/t/t_multidriven_classf0.py create mode 100644 test_regress/t/t_multidriven_classf0.v create mode 100755 test_regress/t/t_multidriven_classf1.py create mode 100644 test_regress/t/t_multidriven_classf1.v diff --git a/test_regress/t/t_multidriven_class0.v b/test_regress/t/t_multidriven_class0.v index 37bdb3240..6df099a7e 100644 --- a/test_regress/t/t_multidriven_class0.v +++ b/test_regress/t/t_multidriven_class0.v @@ -53,14 +53,14 @@ module m_tb#()(); initial begin #1; sel = 'b0; - `checkd(val, 1'b0); #1; + `checkd(val, 1'b0); sel = 'b1; + #1; `checkd(val, 1'b1); - #1; sel = 'b0; - `checkd(val, 1'b0); #1; + `checkd(val, 1'b0); end initial begin diff --git a/test_regress/t/t_multidriven_class1.py b/test_regress/t/t_multidriven_class1.py new file mode 100755 index 000000000..c6e56559a --- /dev/null +++ b/test_regress/t/t_multidriven_class1.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_class1.v b/test_regress/t/t_multidriven_class1.v new file mode 100644 index 000000000..773f051e9 --- /dev/null +++ b/test_regress/t/t_multidriven_class1.v @@ -0,0 +1,72 @@ +// 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 + +// class task chain - nested method calls write through ref in same always_comb + +// 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 + +class C; + task automatic inner(inout logic q); + q = 1'b1; + endtask + task automatic outer(inout logic q); + inner(q); + endtask +endclass + +module mod #()( + input logic sel + ,output logic val +); + + logic l0; + C c; + + initial c = new; + + always_comb begin + l0 = 1'b0; + if (sel) begin + c.outer(l0); + end + end + + assign val = l0; + +endmodule + +module m_tb#()(); + + logic sel, val; + + mod m( + .sel(sel) + ,.val(val) + ); + + initial begin + #1; + sel = 'b0; + #1; + `checkd(val, 1'b0); + sel = 'b1; + #1; + `checkd(val, 1'b1); + sel = 'b0; + #1; + `checkd(val, 1'b0); + end + + initial begin + #5; + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule diff --git a/test_regress/t/t_multidriven_class2.py b/test_regress/t/t_multidriven_class2.py new file mode 100755 index 000000000..c6e56559a --- /dev/null +++ b/test_regress/t/t_multidriven_class2.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_class2.v b/test_regress/t/t_multidriven_class2.v new file mode 100644 index 000000000..dff4ec2cc --- /dev/null +++ b/test_regress/t/t_multidriven_class2.v @@ -0,0 +1,71 @@ +// 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 + +// class handle passed through module port - class method writes through ref + +// 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 + +class C; + task automatic set1(ref logic q); + q = 1'b1; + endtask +endclass + +module mod #()( + input logic sel + ,output logic val + ,C c +); + + logic l0; + + always_comb begin + l0 = 1'b0; + if (sel) begin + c.set1(l0); + end + end + + assign val = l0; + +endmodule + +module m_tb#()(); + + logic sel, val; + C c; + + initial c = new; + + mod m( + .sel(sel) + ,.val(val) + ,.c(c) + ); + + initial begin + #1; + sel = 'b0; + #1; + `checkd(val, 1'b0); + sel = 'b1; + #1; + `checkd(val, 1'b1); + sel = 'b0; + #1; + `checkd(val, 1'b0); + end + + initial begin + #5; + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule diff --git a/test_regress/t/t_multidriven_class3.py b/test_regress/t/t_multidriven_class3.py new file mode 100755 index 000000000..c6e56559a --- /dev/null +++ b/test_regress/t/t_multidriven_class3.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_class3.v b/test_regress/t/t_multidriven_class3.v new file mode 100644 index 000000000..1c0c4eedd --- /dev/null +++ b/test_regress/t/t_multidriven_class3.v @@ -0,0 +1,66 @@ +// 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 + +// static class task - call via class scope, writes through ref in same always_comb + +// 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 + +class C; + static task automatic set1(ref logic q); + q = 1'b1; + endtask +endclass + +module mod #()( + input logic sel + ,output logic val +); + + logic l0; + + always_comb begin + l0 = 1'b0; + if (sel) begin + C::set1(l0); + end + end + + assign val = l0; + +endmodule + +module m_tb#()(); + + logic sel, val; + + mod m( + .sel(sel) + ,.val(val) + ); + + initial begin + #1; + sel = 'b0; + #1; + `checkd(val, 1'b0); + sel = 'b1; + #1; + `checkd(val, 1'b1); + sel = 'b0; + #1; + `checkd(val, 1'b0); + end + + initial begin + #5; + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule diff --git a/test_regress/t/t_multidriven_class4.py b/test_regress/t/t_multidriven_class4.py new file mode 100755 index 000000000..c6e56559a --- /dev/null +++ b/test_regress/t/t_multidriven_class4.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_class4.v b/test_regress/t/t_multidriven_class4.v new file mode 100644 index 000000000..41c91c415 --- /dev/null +++ b/test_regress/t/t_multidriven_class4.v @@ -0,0 +1,79 @@ +// 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 + +// class composition - one class calls another task, ultimately writes through ref + +// 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 + +class CInner; + task automatic set1(ref logic q); + q = 1'b1; + endtask +endclass + +class COuter; + CInner inner; + function new(); + inner = new; + endfunction + task automatic set1(ref logic q); + inner.set1(q); + endtask +endclass + +module mod #()( + input logic sel + ,output logic val +); + + logic l0; + COuter c; + + initial c = new; + + always_comb begin + l0 = 1'b0; + if (sel) begin + c.set1(l0); + end + end + + assign val = l0; + +endmodule + +module m_tb#()(); + + logic sel, val; + + mod m( + .sel(sel) + ,.val(val) + ); + + initial begin + #1; + sel = 'b0; + #1; + `checkd(val, 1'b0); + sel = 'b1; + #1; + `checkd(val, 1'b1); + sel = 'b0; + #1; + `checkd(val, 1'b0); + end + + initial begin + #5; + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule diff --git a/test_regress/t/t_multidriven_classf0.py b/test_regress/t/t_multidriven_classf0.py new file mode 100755 index 000000000..c6e56559a --- /dev/null +++ b/test_regress/t/t_multidriven_classf0.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_classf0.v b/test_regress/t/t_multidriven_classf0.v new file mode 100644 index 000000000..c5c996887 --- /dev/null +++ b/test_regress/t/t_multidriven_classf0.v @@ -0,0 +1,69 @@ +// 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 + +// class function returns value - always_comb writes var directly + via class function call + +// 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 + +class C; + function automatic logic ret1(); + return 1'b1; + endfunction +endclass + +module mod #()( + input logic sel + ,output logic val +); + + logic l0; + C c; + + initial c = new; + + always_comb begin + l0 = 1'b0; + if (sel) begin + l0 = c.ret1(); + end + end + + assign val = l0; + +endmodule + +module m_tb#()(); + + logic sel, val; + + mod m( + .sel(sel) + ,.val(val) + ); + + initial begin + #1; + sel = 'b0; + #1; + `checkd(val, 1'b0); + sel = 'b1; + #1; + `checkd(val, 1'b1); + sel = 'b0; + #1; + `checkd(val, 1'b0); + end + + initial begin + #5; + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule diff --git a/test_regress/t/t_multidriven_classf1.py b/test_regress/t/t_multidriven_classf1.py new file mode 100755 index 000000000..c6e56559a --- /dev/null +++ b/test_regress/t/t_multidriven_classf1.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_classf1.v b/test_regress/t/t_multidriven_classf1.v new file mode 100644 index 000000000..9b8efd46f --- /dev/null +++ b/test_regress/t/t_multidriven_classf1.v @@ -0,0 +1,66 @@ +// 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 + +// static class function returns value - always_comb uses class scope call + +// 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 + +class C; + static function automatic logic ret1(); + return 1'b1; + endfunction +endclass + +module mod #()( + input logic sel + ,output logic val +); + + logic l0; + + always_comb begin + l0 = 1'b0; + if (sel) begin + l0 = C::ret1(); + end + end + + assign val = l0; + +endmodule + +module m_tb#()(); + + logic sel, val; + + mod m( + .sel(sel) + ,.val(val) + ); + + initial begin + #1; + sel = 'b0; + #1; + `checkd(val, 1'b0); + sel = 'b1; + #1; + `checkd(val, 1'b1); + sel = 'b0; + #1; + `checkd(val, 1'b0); + end + + initial begin + #5; + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule From fd60e349eedb54a1af507b1ed0e8fe69221d970b Mon Sep 17 00:00:00 2001 From: em2machine <92717390+em2machine@users.noreply.github.com> Date: Sun, 21 Dec 2025 21:08:41 +0100 Subject: [PATCH 08/37] nested interface tests --- test_regress/t/t_multidriven_iface5.py | 18 +++++++ test_regress/t/t_multidriven_iface5.v | 69 ++++++++++++++++++++++++++ test_regress/t/t_multidriven_iface6.py | 18 +++++++ test_regress/t/t_multidriven_iface6.v | 68 +++++++++++++++++++++++++ 4 files changed, 173 insertions(+) create mode 100755 test_regress/t/t_multidriven_iface5.py create mode 100644 test_regress/t/t_multidriven_iface5.v create mode 100755 test_regress/t/t_multidriven_iface6.py create mode 100644 test_regress/t/t_multidriven_iface6.v diff --git a/test_regress/t/t_multidriven_iface5.py b/test_regress/t/t_multidriven_iface5.py new file mode 100755 index 000000000..c6e56559a --- /dev/null +++ b/test_regress/t/t_multidriven_iface5.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_iface5.v b/test_regress/t/t_multidriven_iface5.v new file mode 100644 index 000000000..0c64246f9 --- /dev/null +++ b/test_regress/t/t_multidriven_iface5.v @@ -0,0 +1,69 @@ +// 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 + +// nested interface test - direct assignment + nested interface task call in same always_comb + +// 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 + +interface leaf_if; + logic l0; + task set1(); l0 = 1'b1; endtask +endinterface + +interface top_if; + leaf_if sub(); +endinterface + +module mod #()( + input logic sel + ,output logic val +); + + top_if if0(); + + always_comb begin + if0.sub.l0 = 1'b0; + if (sel) begin + if0.sub.set1(); + end + end + + assign val = if0.sub.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_iface6.py b/test_regress/t/t_multidriven_iface6.py new file mode 100755 index 000000000..c6e56559a --- /dev/null +++ b/test_regress/t/t_multidriven_iface6.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_iface6.v b/test_regress/t/t_multidriven_iface6.v new file mode 100644 index 000000000..83024eb74 --- /dev/null +++ b/test_regress/t/t_multidriven_iface6.v @@ -0,0 +1,68 @@ +// 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 + +// nested interface aggregator - two nested interfaces, only one driven + +// 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 + +interface chan_if; + logic b0; + task set1(); b0 = 1'b1; endtask +endinterface + +interface agg_if; + chan_if tlb(); + chan_if ic(); +endinterface + +module mod #()( + input logic sel + ,output logic val +); + + agg_if a(); + + always_comb begin + a.tlb.b0 = 1'b0; + if (sel) a.tlb.set1(); + end + + assign val = a.tlb.b0; + +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 From 7a49fdf9f3330b5ee1af5763c9e90511847af9ea Mon Sep 17 00:00:00 2001 From: em2machine <92717390+em2machine@users.noreply.github.com> Date: Sun, 21 Dec 2025 21:37:51 +0100 Subject: [PATCH 09/37] removed ensureEntry --- src/V3Undriven.cpp | 32 ++++++++++++++++---------------- src/V3UndrivenCapture.cpp | 35 +++++++++++++++++------------------ src/V3UndrivenCapture.h | 5 ++++- 3 files changed, 37 insertions(+), 35 deletions(-) diff --git a/src/V3Undriven.cpp b/src/V3Undriven.cpp index 6f7244e63..3452c9b2f 100644 --- a/src/V3Undriven.cpp +++ b/src/V3Undriven.cpp @@ -568,14 +568,7 @@ class UndrivenVisitor final : public VNVisitorConst { iterateChildrenConst(nodep); if (nodep->keyword() == VAlwaysKwd::ALWAYS_COMB) UINFO(9, " Done " << nodep); } - // EOM - /* - void visit(AstNodeFTaskRef* nodep) override { - VL_RESTORER(m_inFTaskRef); - m_inFTaskRef = true; - iterateChildrenConst(nodep); - } - */ + void visit(AstNodeFTaskRef* nodep) override { VL_RESTORER(m_inFTaskRef); m_inFTaskRef = true; @@ -584,11 +577,18 @@ class UndrivenVisitor final : public VNVisitorConst { 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) { + // Do not apply writeSummary at calls inside a task definition, or they will look like independent drivers (phantom MULTIDRIVEN). + // did the lambda on purpose - lessen chance of screwup in future edits. + //const auto inExecutedContext = [this]() const { + // return !(m_taskp && !m_alwaysp && !m_inContAssign && !m_inInitialStatic && !m_inBBox); + //}; + + const auto inExecutedContext = [this]() { + return !(m_taskp && !m_alwaysp && !m_inContAssign && !m_inInitialStatic && !m_inBBox); + }; + + if (!inExecutedContext()) { return; } @@ -654,12 +654,12 @@ public: void V3Undriven::undrivenAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ":"); // EOM - //{ UndrivenVisitor{nodep}; } - if (V3UndrivenCapture::enableWriteSummary) { + const bool enable = V3UndrivenCapture::enableWriteSummary; + if (enable) { V3UndrivenCapture capture{nodep}; - { UndrivenVisitor{nodep, &capture, V3UndrivenCapture::enableWriteSummary}; } + { UndrivenVisitor{nodep, &capture, enable}; } } else { - { UndrivenVisitor{nodep, nullptr, V3UndrivenCapture::enableWriteSummary}; } + { UndrivenVisitor{nodep, nullptr, enable}; } } if (v3Global.opt.stats()) V3Stats::statsStage("undriven"); } diff --git a/src/V3UndrivenCapture.cpp b/src/V3UndrivenCapture.cpp index 9721edf6d..e6a63f3df 100644 --- a/src/V3UndrivenCapture.cpp +++ b/src/V3UndrivenCapture.cpp @@ -46,14 +46,6 @@ class CaptureVisitor final : public VNVisitorConst { for (AstNode* np = nodep; np; np = np->nextp()) np->accept(v); } - /* - V3UndrivenCapture::FTaskInfo& infoFor(const AstNodeFTask* taskp) { - // Ensure entry exists - return m_cap.writeSummary(taskp), const_cast( - *m_cap.find(taskp)); // not used; see below - } - */ - public: explicit CaptureVisitor(V3UndrivenCapture& cap, AstNetlist* netlistp) : m_cap{cap} { @@ -67,7 +59,8 @@ private: m_curTaskp = nodep; ++g_stats.ftasks; UINFO(DBG, "UndrivenCapture: enter ftask " << nodep << " " << nodep->prettyNameQ()); - (void)m_cap.ensureEntry(nodep); // ensure map entry exists + //(void)m_cap.ensureEntry(nodep); // ensure map entry exists + m_cap.info(nodep); iterateListConst(*this, nodep->stmtsp()); } @@ -76,10 +69,11 @@ private: ++g_stats.varWrites; UINFO(DBG, "UndrivenCapture: direct write in " << taskNameQ(m_curTaskp) << " var=" << nodep->varp()->prettyNameQ() << " at " << nodep->fileline()); // Ensure entry exists - (void)m_cap.ensureEntry(m_curTaskp); + //(void)m_cap.ensureEntry(m_curTaskp); // Safe: find() must succeed after writeSummary() creates entry - auto* const infop = const_cast(m_cap.find(m_curTaskp)); - if (infop) infop->directWrites.push_back(nodep->varp()); + //auto* const infop = const_cast(m_cap.find(m_curTaskp)); + //if (infop) infop->directWrites.push_back(nodep->varp()); + m_cap.info(m_curTaskp).directWrites.push_back(nodep->varp()); } iterateChildrenConst(nodep); } @@ -90,11 +84,14 @@ private: if (AstNodeFTask* const calleep = nodep->taskp()) { ++g_stats.callEdges; UINFO(DBG, "UndrivenCapture: call edge " << taskNameQ(m_curTaskp) << " -> " << taskNameQ(calleep)); - (void)m_cap.ensureEntry(m_curTaskp); - (void)m_cap.ensureEntry(calleep); - auto* const infop - = const_cast(m_cap.find(m_curTaskp)); - if (infop) infop->callees.push_back(calleep); + //(void)m_cap.ensureEntry(m_curTaskp); + //(void)m_cap.ensureEntry(calleep); + //auto* const infop + // = const_cast(m_cap.find(m_curTaskp)); + //if (infop) infop->callees.push_back(calleep); + m_cap.info(m_curTaskp).callees.push_back(calleep); + m_cap.info(calleep); // ensure entry exists for callee too (optional but keeps map complete) + } else { UINFO(DBG, "UndrivenCapture: unresolved call in " << taskNameQ(m_curTaskp) << " name=" << nodep->name()); } @@ -202,4 +199,6 @@ void V3UndrivenCapture::debugDumpTask(FTask taskp, int level) const { << " writeSummary=" << infop->writeSummary.size()); } -void V3UndrivenCapture::ensureEntry(FTask taskp) { (void)m_info[taskp]; } +//void V3UndrivenCapture::ensureEntry(FTask taskp) { (void)m_info[taskp]; } + +V3UndrivenCapture::FTaskInfo& V3UndrivenCapture::info(FTask taskp) { return m_info[taskp]; } diff --git a/src/V3UndrivenCapture.h b/src/V3UndrivenCapture.h index 9b2881610..6ba33b247 100644 --- a/src/V3UndrivenCapture.h +++ b/src/V3UndrivenCapture.h @@ -47,6 +47,9 @@ public: static bool enableWriteSummary; + // Get (and create if needed) mutable info entry for a task/function. + FTaskInfo& info(FTask taskp); + private: // Per-task/function capture info keyed by resolved AstNodeFTask* identity. std::unordered_map m_info; @@ -71,7 +74,7 @@ public: const std::vector& writeSummary(FTask taskp); // Ensure an entry exists for a task/function without computing its writeSummary. - void ensureEntry(FTask taskp); + //void ensureEntry(FTask taskp); // Optional: dump one task's summary (for debug bring-up). You can omit if you prefer. void debugDumpTask(FTask taskp, int level = 9) const; From 6850b3653e14c2a69dd8ebb939ad9b499f86ea21 Mon Sep 17 00:00:00 2001 From: em2machine <92717390+em2machine@users.noreply.github.com> Date: Sun, 21 Dec 2025 21:43:45 +0100 Subject: [PATCH 10/37] cleanup of some comments --- src/V3UndrivenCapture.cpp | 20 +++----------------- src/V3UndrivenCapture.h | 13 +++++-------- 2 files changed, 8 insertions(+), 25 deletions(-) diff --git a/src/V3UndrivenCapture.cpp b/src/V3UndrivenCapture.cpp index e6a63f3df..b7b2b439b 100644 --- a/src/V3UndrivenCapture.cpp +++ b/src/V3UndrivenCapture.cpp @@ -59,7 +59,6 @@ private: m_curTaskp = nodep; ++g_stats.ftasks; UINFO(DBG, "UndrivenCapture: enter ftask " << nodep << " " << nodep->prettyNameQ()); - //(void)m_cap.ensureEntry(nodep); // ensure map entry exists m_cap.info(nodep); iterateListConst(*this, nodep->stmtsp()); } @@ -68,11 +67,6 @@ private: if (m_curTaskp && nodep->access().isWriteOrRW()) { ++g_stats.varWrites; UINFO(DBG, "UndrivenCapture: direct write in " << taskNameQ(m_curTaskp) << " var=" << nodep->varp()->prettyNameQ() << " at " << nodep->fileline()); - // Ensure entry exists - //(void)m_cap.ensureEntry(m_curTaskp); - // Safe: find() must succeed after writeSummary() creates entry - //auto* const infop = const_cast(m_cap.find(m_curTaskp)); - //if (infop) infop->directWrites.push_back(nodep->varp()); m_cap.info(m_curTaskp).directWrites.push_back(nodep->varp()); } iterateChildrenConst(nodep); @@ -84,14 +78,8 @@ private: if (AstNodeFTask* const calleep = nodep->taskp()) { ++g_stats.callEdges; UINFO(DBG, "UndrivenCapture: call edge " << taskNameQ(m_curTaskp) << " -> " << taskNameQ(calleep)); - //(void)m_cap.ensureEntry(m_curTaskp); - //(void)m_cap.ensureEntry(calleep); - //auto* const infop - // = const_cast(m_cap.find(m_curTaskp)); - //if (infop) infop->callees.push_back(calleep); m_cap.info(m_curTaskp).callees.push_back(calleep); - m_cap.info(calleep); // ensure entry exists for callee too (optional but keeps map complete) - + m_cap.info(calleep); } else { UINFO(DBG, "UndrivenCapture: unresolved call in " << taskNameQ(m_curTaskp) << " name=" << nodep->name()); } @@ -127,7 +115,7 @@ V3UndrivenCapture::V3UndrivenCapture(AstNetlist* netlistp) { sortUniqueFTasks(kv.second.callees); } - // Compute summaries for all tasks (MVP: DFS memoization, no SCC yet) + // Compute summaries for all tasks for (const auto& kv : m_info) (void)computeWriteSummary(kv.first); UINFO(DBG, "UndrivenCapture: stats ftasks=" << g_stats.ftasks << " varWrites=" << g_stats.varWrites << " callEdges=" << g_stats.callEdges << " uniqueTasks=" << m_info.size()); @@ -159,7 +147,7 @@ const std::vector& V3UndrivenCapture::computeWriteSummar } if (info.state == State::VISITING) { UINFO(DBG, "UndrivenCapture: recursion detected at " << taskNameQ(taskp) << " returning directWrites size=" << info.directWrites.size()); - // Cycle detected (recursion/mutual recursion). MVP behavior: + // Cycle detected. Simple behaviour: // return directWrites only to guarantee termination. if (info.writeSummary.empty()) info.writeSummary = info.directWrites; sortUniqueVars(info.writeSummary); @@ -199,6 +187,4 @@ void V3UndrivenCapture::debugDumpTask(FTask taskp, int level) const { << " writeSummary=" << infop->writeSummary.size()); } -//void V3UndrivenCapture::ensureEntry(FTask taskp) { (void)m_info[taskp]; } - V3UndrivenCapture::FTaskInfo& V3UndrivenCapture::info(FTask taskp) { return m_info[taskp]; } diff --git a/src/V3UndrivenCapture.h b/src/V3UndrivenCapture.h index 6ba33b247..6ac005b94 100644 --- a/src/V3UndrivenCapture.h +++ b/src/V3UndrivenCapture.h @@ -39,9 +39,9 @@ public: std::vector directWrites; // Direct resolved callees from this task/function body. std::vector callees; - // Transitive "may write" summary: directWrites plus all callees' summaries. + // 'write through write' writeSummary for the given task/function. std::vector writeSummary; - // Memoization state for writeSummary computation. + // state for writeSummary computation. State state = State::UNVISITED; }; @@ -61,7 +61,7 @@ private: // Collect direct writes and call edges for all tasks/functions. void gather(AstNetlist* netlistp); - // Compute (and memoize) transitive writeSummary for the given task/function. + // Compute (and cache) 'write through write' writeSummary for the given task/function. const std::vector& computeWriteSummary(FTask taskp); public: @@ -70,13 +70,10 @@ public: // Lookup task/function capture info (nullptr if unknown). const FTaskInfo* find(FTask taskp) const; - // Get transitive writeSummary for a task/function (creates empty entry if needed). + // Get write through write through write, etc (call chain) writeSummary for a task/function (creates empty entry if needed). const std::vector& writeSummary(FTask taskp); - // Ensure an entry exists for a task/function without computing its writeSummary. - //void ensureEntry(FTask taskp); - - // Optional: dump one task's summary (for debug bring-up). You can omit if you prefer. + // Optional: dump one task's summary (for debug bring-up). void debugDumpTask(FTask taskp, int level = 9) const; }; From 42e49fa0eeb1e003b467605e7d160d4b407f4e3b Mon Sep 17 00:00:00 2001 From: em2machine <92717390+em2machine@users.noreply.github.com> Date: Sun, 21 Dec 2025 21:47:06 +0100 Subject: [PATCH 11/37] carriage return --- src/V3UndrivenCapture.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/V3UndrivenCapture.h b/src/V3UndrivenCapture.h index 6ac005b94..f5c127513 100644 --- a/src/V3UndrivenCapture.h +++ b/src/V3UndrivenCapture.h @@ -77,4 +77,4 @@ public: void debugDumpTask(FTask taskp, int level = 9) const; }; -#endif // VERILATOR_V3UNDRIVENCAPTURE_H_ \ No newline at end of file +#endif // VERILATOR_V3UNDRIVENCAPTURE_H_ From 2646a39be641bc12e6ef5c98c0c810abd8f09034 Mon Sep 17 00:00:00 2001 From: github action Date: Sun, 21 Dec 2025 20:48:16 +0000 Subject: [PATCH 12/37] Apply 'make format' --- src/V3Undriven.cpp | 41 +++++++++++-------- src/V3UndrivenCapture.cpp | 33 +++++++++------ src/V3UndrivenCapture.h | 3 +- .../t/t_lint_taskcall_multidriven_bad.py | 0 4 files changed, 48 insertions(+), 29 deletions(-) mode change 100644 => 100755 test_regress/t/t_lint_taskcall_multidriven_bad.py diff --git a/src/V3Undriven.cpp b/src/V3Undriven.cpp index 3452c9b2f..d95001a2b 100644 --- a/src/V3Undriven.cpp +++ b/src/V3Undriven.cpp @@ -463,14 +463,19 @@ class UndrivenVisitor final : public VNVisitorConst { } if (entryp->isDrivenWhole() && !m_inBBox && !VN_IS(nodep, VarXRef) && !VN_IS(nodep->dtypep()->skipRefp(), UnpackArrayDType) - && nodep->fileline() != entryp->getNodeFileLinep() && !entryp->isUnderGen() + && nodep->fileline() != entryp->getNodeFileLinep() + && !entryp->isUnderGen() // EOM //&& entryp->getNodep()) { - && (entryp->getNodep() || (V3UndrivenCapture::enableWriteSummary && entryp->getCallNodep()))) { + && (entryp->getNodep() + || (V3UndrivenCapture::enableWriteSummary && entryp->getCallNodep()))) { // EOM - const AstNode* const otherWritep = entryp->getNodep() ? static_cast(entryp->getNodep()) - : (V3UndrivenCapture::enableWriteSummary ? entryp->getCallNodep() : nullptr); + const AstNode* const otherWritep + = entryp->getNodep() + ? static_cast(entryp->getNodep()) + : (V3UndrivenCapture::enableWriteSummary ? entryp->getCallNodep() + : nullptr); if (m_alwaysCombp && (!entryp->isDrivenAlwaysCombWhole() @@ -481,7 +486,8 @@ class UndrivenVisitor final : public VNVisitorConst { "Variable written to in always_comb also written by other process" << " (IEEE 1800-2023 9.2.2.2): " << nodep->prettyNameQ() << '\n' << nodep->warnOther() << '\n' - << nodep->warnContextPrimary() << '\n' + << nodep->warnContextPrimary() + << '\n' // EOM //<< entryp->getNodep()->warnOther() << otherWritep->warnOther() @@ -495,7 +501,8 @@ class UndrivenVisitor final : public VNVisitorConst { << " (IEEE 1800-2023 9.2.2.2): " << nodep->prettyNameQ() << '\n' << nodep->warnOther() << '\n' - << nodep->warnContextPrimary() << '\n' + << nodep->warnContextPrimary() + << '\n' //EOM //<< entryp->getNodep()->warnOther() << otherWritep->warnOther() @@ -578,19 +585,19 @@ class UndrivenVisitor final : public VNVisitorConst { if (!m_enableWriteSummary || !m_capturep) return; // 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). - // did the lambda on purpose - lessen chance of screwup in future edits. + // Do not apply writeSummary at calls inside a task definition, or they will look like + // independent drivers (phantom MULTIDRIVEN). did the lambda on purpose - lessen chance of + // screwup in future edits. //const auto inExecutedContext = [this]() const { - // return !(m_taskp && !m_alwaysp && !m_inContAssign && !m_inInitialStatic && !m_inBBox); + // return !(m_taskp && !m_alwaysp && !m_inContAssign && !m_inInitialStatic && + // !m_inBBox); //}; const auto inExecutedContext = [this]() { return !(m_taskp && !m_alwaysp && !m_inContAssign && !m_inInitialStatic && !m_inBBox); }; - if (!inExecutedContext()) { - return; - } + if (!inExecutedContext()) { return; } AstNodeFTask* const calleep = nodep->taskp(); if (!calleep) return; @@ -635,10 +642,12 @@ public: // CONSTRUCTORS // EOM // explicit UndrivenVisitor(AstNetlist* nodep) { iterateConst(nodep); } - explicit UndrivenVisitor(AstNetlist* nodep, V3UndrivenCapture* capturep, bool enableWriteSummary) : - m_capturep{capturep}, - m_enableWriteSummary{enableWriteSummary} - { iterateConst(nodep); } + explicit UndrivenVisitor(AstNetlist* nodep, V3UndrivenCapture* capturep, + bool enableWriteSummary) + : m_capturep{capturep} + , m_enableWriteSummary{enableWriteSummary} { + iterateConst(nodep); + } ~UndrivenVisitor() override { for (UndrivenVarEntry* ip : m_entryps[1]) ip->reportViolations(); diff --git a/src/V3UndrivenCapture.cpp b/src/V3UndrivenCapture.cpp index b7b2b439b..795fd69de 100644 --- a/src/V3UndrivenCapture.cpp +++ b/src/V3UndrivenCapture.cpp @@ -66,7 +66,9 @@ private: void visit(AstNodeVarRef* nodep) override { if (m_curTaskp && nodep->access().isWriteOrRW()) { ++g_stats.varWrites; - UINFO(DBG, "UndrivenCapture: direct write in " << taskNameQ(m_curTaskp) << " var=" << nodep->varp()->prettyNameQ() << " at " << nodep->fileline()); + UINFO(DBG, "UndrivenCapture: direct write in " + << taskNameQ(m_curTaskp) << " var=" << nodep->varp()->prettyNameQ() + << " at " << nodep->fileline()); m_cap.info(m_curTaskp).directWrites.push_back(nodep->varp()); } iterateChildrenConst(nodep); @@ -77,11 +79,13 @@ private: if (m_curTaskp) { if (AstNodeFTask* const calleep = nodep->taskp()) { ++g_stats.callEdges; - UINFO(DBG, "UndrivenCapture: call edge " << taskNameQ(m_curTaskp) << " -> " << taskNameQ(calleep)); + UINFO(DBG, "UndrivenCapture: call edge " << taskNameQ(m_curTaskp) << " -> " + << taskNameQ(calleep)); m_cap.info(m_curTaskp).callees.push_back(calleep); m_cap.info(calleep); } else { - UINFO(DBG, "UndrivenCapture: unresolved call in " << taskNameQ(m_curTaskp) << " name=" << nodep->name()); + UINFO(DBG, "UndrivenCapture: unresolved call in " << taskNameQ(m_curTaskp) + << " name=" << nodep->name()); } } iterateChildrenConst(nodep); // still scan pins/args @@ -118,7 +122,9 @@ V3UndrivenCapture::V3UndrivenCapture(AstNetlist* netlistp) { // Compute summaries for all tasks for (const auto& kv : m_info) (void)computeWriteSummary(kv.first); - UINFO(DBG, "UndrivenCapture: stats ftasks=" << g_stats.ftasks << " varWrites=" << g_stats.varWrites << " callEdges=" << g_stats.callEdges << " uniqueTasks=" << m_info.size()); + UINFO(DBG, "UndrivenCapture: stats ftasks=" + << g_stats.ftasks << " varWrites=" << g_stats.varWrites + << " callEdges=" << g_stats.callEdges << " uniqueTasks=" << m_info.size()); } void V3UndrivenCapture::gather(AstNetlist* netlistp) { @@ -142,11 +148,14 @@ const std::vector& V3UndrivenCapture::computeWriteSummar FTaskInfo& info = m_info[taskp]; if (info.state == State::DONE) { - UINFO(DBG, "UndrivenCapture: writeSummary cached size=" << info.writeSummary.size() << " for " << taskNameQ(taskp)); + UINFO(DBG, "UndrivenCapture: writeSummary cached size=" << info.writeSummary.size() + << " for " << taskNameQ(taskp)); return info.writeSummary; } if (info.state == State::VISITING) { - UINFO(DBG, "UndrivenCapture: recursion detected at " << taskNameQ(taskp) << " returning directWrites size=" << info.directWrites.size()); + UINFO(DBG, "UndrivenCapture: recursion detected at " + << taskNameQ(taskp) + << " returning directWrites size=" << info.directWrites.size()); // Cycle detected. Simple behaviour: // return directWrites only to guarantee termination. if (info.writeSummary.empty()) info.writeSummary = info.directWrites; @@ -168,7 +177,8 @@ const std::vector& V3UndrivenCapture::computeWriteSummar sortUniqueVars(info.writeSummary); - UINFO(DBG, "UndrivenCapture: writeSummary computed size=" << info.writeSummary.size() << " for " << taskNameQ(taskp)); + UINFO(DBG, "UndrivenCapture: writeSummary computed size=" << info.writeSummary.size() + << " for " << taskNameQ(taskp)); info.state = State::DONE; return info.writeSummary; @@ -180,11 +190,10 @@ void V3UndrivenCapture::debugDumpTask(FTask taskp, int level) const { UINFO(level, "UndrivenCapture: no entry for task " << taskp); return; } - UINFO(level, "UndrivenCapture: dump task " - << taskp << " " << taskp->prettyNameQ() - << " directWrites=" << infop->directWrites.size() - << " callees=" << infop->callees.size() - << " writeSummary=" << infop->writeSummary.size()); + UINFO(level, "UndrivenCapture: dump task " << taskp << " " << taskp->prettyNameQ() + << " directWrites=" << infop->directWrites.size() + << " callees=" << infop->callees.size() + << " writeSummary=" << infop->writeSummary.size()); } V3UndrivenCapture::FTaskInfo& V3UndrivenCapture::info(FTask taskp) { return m_info[taskp]; } diff --git a/src/V3UndrivenCapture.h b/src/V3UndrivenCapture.h index f5c127513..c940c53bf 100644 --- a/src/V3UndrivenCapture.h +++ b/src/V3UndrivenCapture.h @@ -70,7 +70,8 @@ public: // Lookup task/function capture info (nullptr if unknown). const FTaskInfo* find(FTask taskp) const; - // Get write through write through write, etc (call chain) writeSummary for a task/function (creates empty entry if needed). + // Get write through write through write, etc (call chain) writeSummary for a task/function + // (creates empty entry if needed). const std::vector& writeSummary(FTask taskp); // Optional: dump one task's summary (for debug bring-up). diff --git a/test_regress/t/t_lint_taskcall_multidriven_bad.py b/test_regress/t/t_lint_taskcall_multidriven_bad.py old mode 100644 new mode 100755 From 10f51dc1bf3d7dd229ac4aee69e3e9aa5ea0e86d Mon Sep 17 00:00:00 2001 From: em2machine <92717390+em2machine@users.noreply.github.com> Date: Mon, 22 Dec 2025 08:15:09 +0100 Subject: [PATCH 13/37] added fix for further tightening ingress point --- src/V3Undriven.cpp | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/V3Undriven.cpp b/src/V3Undriven.cpp index d95001a2b..7d85614de 100644 --- a/src/V3Undriven.cpp +++ b/src/V3Undriven.cpp @@ -447,9 +447,20 @@ class UndrivenVisitor final : public VNVisitorConst { // 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. + + //const auto inExecutedContext = [this, nodep]() { + // return !(m_enableWriteSummary && m_taskp && !m_alwaysp && !m_inContAssign && !m_inInitialStatic + // && !m_inBBox && !m_taskp->dpiExport() && !nodep->varp()->isFuncLocal()); + //}; + + //if (!inExecutedContext()) { + // return; + //} + if (m_enableWriteSummary && m_taskp && !m_alwaysp && !m_inContAssign && !m_inInitialStatic - && !m_inBBox) { - return; + && !m_inBBox && !m_taskp->dpiExport()) { + AstVar* const retVarp = VN_CAST(m_taskp->fvarp(), Var); + if (!retVarp || nodep->varp() != retVarp) return; } for (int usr = 1; usr < (m_alwaysCombp ? 3 : 2); ++usr) { @@ -594,7 +605,7 @@ class UndrivenVisitor final : public VNVisitorConst { //}; const auto inExecutedContext = [this]() { - return !(m_taskp && !m_alwaysp && !m_inContAssign && !m_inInitialStatic && !m_inBBox); + return !(m_taskp && !m_alwaysp && !m_inContAssign && !m_inInitialStatic && !m_inBBox && !m_taskp->dpiExport()); }; if (!inExecutedContext()) { return; } From 939dbb1fecd62e12f2a96b3208f7324cb8ab78cc Mon Sep 17 00:00:00 2001 From: github action Date: Mon, 22 Dec 2025 07:16:07 +0000 Subject: [PATCH 14/37] Apply 'make format' --- src/V3Undriven.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/V3Undriven.cpp b/src/V3Undriven.cpp index 7d85614de..51cf61090 100644 --- a/src/V3Undriven.cpp +++ b/src/V3Undriven.cpp @@ -449,7 +449,8 @@ class UndrivenVisitor final : public VNVisitorConst { // traversal create phantom "other writes" for MULTIDRIVEN. //const auto inExecutedContext = [this, nodep]() { - // return !(m_enableWriteSummary && m_taskp && !m_alwaysp && !m_inContAssign && !m_inInitialStatic + // return !(m_enableWriteSummary && m_taskp && !m_alwaysp && !m_inContAssign && + // !m_inInitialStatic // && !m_inBBox && !m_taskp->dpiExport() && !nodep->varp()->isFuncLocal()); //}; @@ -605,7 +606,8 @@ class UndrivenVisitor final : public VNVisitorConst { //}; const auto inExecutedContext = [this]() { - return !(m_taskp && !m_alwaysp && !m_inContAssign && !m_inInitialStatic && !m_inBBox && !m_taskp->dpiExport()); + return !(m_taskp && !m_alwaysp && !m_inContAssign && !m_inInitialStatic && !m_inBBox + && !m_taskp->dpiExport()); }; if (!inExecutedContext()) { return; } From bde2a829eac9fdf0ccff5db767f44c79b7f5373f Mon Sep 17 00:00:00 2001 From: em2machine <92717390+em2machine@users.noreply.github.com> Date: Mon, 22 Dec 2025 08:59:38 +0100 Subject: [PATCH 15/37] regression in my large design --- src/V3Undriven.cpp | 4 +-- src/V3UndrivenCapture.cpp | 8 ++++- test_regress/t/t_multidriven_funcret0.py | 18 ++++++++++++ test_regress/t/t_multidriven_funcret0.v | 37 ++++++++++++++++++++++++ 4 files changed, 64 insertions(+), 3 deletions(-) create mode 100644 test_regress/t/t_multidriven_funcret0.py create mode 100644 test_regress/t/t_multidriven_funcret0.v diff --git a/src/V3Undriven.cpp b/src/V3Undriven.cpp index 51cf61090..2a9488edf 100644 --- a/src/V3Undriven.cpp +++ b/src/V3Undriven.cpp @@ -480,13 +480,13 @@ class UndrivenVisitor final : public VNVisitorConst { // EOM //&& entryp->getNodep()) { && (entryp->getNodep() - || (V3UndrivenCapture::enableWriteSummary && entryp->getCallNodep()))) { + || (m_enableWriteSummary && entryp->getCallNodep()))) { // EOM const AstNode* const otherWritep = entryp->getNodep() ? static_cast(entryp->getNodep()) - : (V3UndrivenCapture::enableWriteSummary ? entryp->getCallNodep() + : (m_enableWriteSummary ? entryp->getCallNodep() : nullptr); if (m_alwaysCombp diff --git a/src/V3UndrivenCapture.cpp b/src/V3UndrivenCapture.cpp index 795fd69de..935e03eb0 100644 --- a/src/V3UndrivenCapture.cpp +++ b/src/V3UndrivenCapture.cpp @@ -69,7 +69,13 @@ private: UINFO(DBG, "UndrivenCapture: direct write in " << taskNameQ(m_curTaskp) << " var=" << nodep->varp()->prettyNameQ() << " at " << nodep->fileline()); - m_cap.info(m_curTaskp).directWrites.push_back(nodep->varp()); + //m_cap.info(m_curTaskp).directWrites.push_back(nodep->varp()); + AstVar* const retVarp = VN_CAST(m_curTaskp->fvarp(), Var); + if (retVarp && nodep->varp() == retVarp) { + // Skip: function return variable is local, not a side-effect + } else { + m_cap.info(m_curTaskp).directWrites.push_back(nodep->varp()); + } } iterateChildrenConst(nodep); } diff --git a/test_regress/t/t_multidriven_funcret0.py b/test_regress/t/t_multidriven_funcret0.py new file mode 100644 index 000000000..c6e56559a --- /dev/null +++ b/test_regress/t/t_multidriven_funcret0.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_funcret0.v b/test_regress/t/t_multidriven_funcret0.v new file mode 100644 index 000000000..792164033 --- /dev/null +++ b/test_regress/t/t_multidriven_funcret0.v @@ -0,0 +1,37 @@ +// DESCRIPTION: Verilator: MULTIDRIVEN false positive - package function return var +// +// Minimal reproducer for: package function with "return expr" used in always_comb expression. +// The function return variable must not be treated as a side-effect "writeSummary" target. + +`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); + +package p; + function automatic int num_bytes(input int size); + return 1 << size; + endfunction +endpackage + +module t; + typedef struct packed { + logic [31:0] addr; + logic [2:0] size; + } meta_t; + + meta_t rd_meta_q; + meta_t rd_meta; + + always_comb begin + rd_meta = rd_meta_q; + rd_meta.addr = rd_meta_q.addr + p::num_bytes(int'(rd_meta_q.size)); + end + + initial begin + rd_meta_q.addr = 32'h100; + rd_meta_q.size = 3'd2; // num_bytes = 4 + #1; + `checkd(rd_meta.addr, 32'h104); + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule From 0ba36488e95722f70cc9067009b3e0bdbdb6507a Mon Sep 17 00:00:00 2001 From: github action Date: Mon, 22 Dec 2025 08:00:37 +0000 Subject: [PATCH 16/37] Apply 'make format' --- src/V3Undriven.cpp | 6 ++---- test_regress/t/t_multidriven_funcret0.py | 0 2 files changed, 2 insertions(+), 4 deletions(-) mode change 100644 => 100755 test_regress/t/t_multidriven_funcret0.py diff --git a/src/V3Undriven.cpp b/src/V3Undriven.cpp index 2a9488edf..07746302b 100644 --- a/src/V3Undriven.cpp +++ b/src/V3Undriven.cpp @@ -479,15 +479,13 @@ class UndrivenVisitor final : public VNVisitorConst { && !entryp->isUnderGen() // EOM //&& entryp->getNodep()) { - && (entryp->getNodep() - || (m_enableWriteSummary && entryp->getCallNodep()))) { + && (entryp->getNodep() || (m_enableWriteSummary && entryp->getCallNodep()))) { // EOM const AstNode* const otherWritep = entryp->getNodep() ? static_cast(entryp->getNodep()) - : (m_enableWriteSummary ? entryp->getCallNodep() - : nullptr); + : (m_enableWriteSummary ? entryp->getCallNodep() : nullptr); if (m_alwaysCombp && (!entryp->isDrivenAlwaysCombWhole() diff --git a/test_regress/t/t_multidriven_funcret0.py b/test_regress/t/t_multidriven_funcret0.py old mode 100644 new mode 100755 From 0a517f260df727730351d14cc753d28fce0de790 Mon Sep 17 00:00:00 2001 From: em2machine <92717390+em2machine@users.noreply.github.com> Date: Mon, 22 Dec 2025 09:10:40 +0100 Subject: [PATCH 17/37] added header --- test_regress/t/t_multidriven_funcret0.v | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/test_regress/t/t_multidriven_funcret0.v b/test_regress/t/t_multidriven_funcret0.v index 792164033..0e82e300b 100644 --- a/test_regress/t/t_multidriven_funcret0.v +++ b/test_regress/t/t_multidriven_funcret0.v @@ -1,4 +1,10 @@ -// DESCRIPTION: Verilator: MULTIDRIVEN false positive - package function return var +// 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 + +// MULTIDRIVEN false positive - package function return var // // Minimal reproducer for: package function with "return expr" used in always_comb expression. // The function return variable must not be treated as a side-effect "writeSummary" target. From b174ee4a31817d33cc9995d2aa23940e2c27aca8 Mon Sep 17 00:00:00 2001 From: em2machine <92717390+em2machine@users.noreply.github.com> Date: Mon, 22 Dec 2025 12:25:27 +0100 Subject: [PATCH 18/37] additional cleanup --- src/V3Undriven.cpp | 28 -------------------- src/V3UndrivenCapture.cpp | 54 +++++++++++++++++++++++---------------- src/V3UndrivenCapture.h | 8 +++--- 3 files changed, 37 insertions(+), 53 deletions(-) diff --git a/src/V3Undriven.cpp b/src/V3Undriven.cpp index 07746302b..23f2756ed 100644 --- a/src/V3Undriven.cpp +++ b/src/V3Undriven.cpp @@ -29,7 +29,6 @@ #include "V3Stats.h" -// EOM #include "V3UndrivenCapture.h" #include @@ -54,7 +53,6 @@ class UndrivenVarEntry final { const FileLine* m_nodeFileLinep = nullptr; // File line of varref if driven, else nullptr bool m_underGen = false; // Under a generate - // EOM const AstNode* m_callNodep = nullptr; // call node if driven via writeSummary, else nullptr const FileLine* m_callFileLinep = nullptr; // file line of call node if driven via summary @@ -286,7 +284,6 @@ public: } } - // EOM void drivenViaCall(const AstNode* nodep, const FileLine* fileLinep) { drivenWhole(); if (!m_callNodep) { @@ -322,7 +319,6 @@ class UndrivenVisitor final : public VNVisitorConst { const AstAlways* m_alwaysp = nullptr; // Current always of either type const AstAlways* m_alwaysCombp = nullptr; // Current always if combo, otherwise nullptr - // EOM V3UndrivenCapture* const m_capturep = nullptr; const bool m_enableWriteSummary = false; @@ -443,21 +439,9 @@ class UndrivenVisitor final : public VNVisitorConst { } } - // 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. - - //const auto inExecutedContext = [this, nodep]() { - // return !(m_enableWriteSummary && m_taskp && !m_alwaysp && !m_inContAssign && - // !m_inInitialStatic - // && !m_inBBox && !m_taskp->dpiExport() && !nodep->varp()->isFuncLocal()); - //}; - - //if (!inExecutedContext()) { - // return; - //} - if (m_enableWriteSummary && m_taskp && !m_alwaysp && !m_inContAssign && !m_inInitialStatic && !m_inBBox && !m_taskp->dpiExport()) { AstVar* const retVarp = VN_CAST(m_taskp->fvarp(), Var); @@ -477,11 +461,8 @@ class UndrivenVisitor final : public VNVisitorConst { && !VN_IS(nodep->dtypep()->skipRefp(), UnpackArrayDType) && nodep->fileline() != entryp->getNodeFileLinep() && !entryp->isUnderGen() - // EOM - //&& entryp->getNodep()) { && (entryp->getNodep() || (m_enableWriteSummary && entryp->getCallNodep()))) { - // EOM const AstNode* const otherWritep = entryp->getNodep() ? static_cast(entryp->getNodep()) @@ -498,11 +479,8 @@ class UndrivenVisitor final : public VNVisitorConst { << nodep->warnOther() << '\n' << nodep->warnContextPrimary() << '\n' - // EOM - //<< entryp->getNodep()->warnOther() << otherWritep->warnOther() << "... Location of other write\n" - //<< entryp->getNodep()->warnContextSecondary()); << otherWritep->warnContextSecondary()); } if (!m_alwaysCombp && entryp->isDrivenAlwaysCombWhole()) { @@ -513,11 +491,8 @@ class UndrivenVisitor final : public VNVisitorConst { << nodep->warnOther() << '\n' << nodep->warnContextPrimary() << '\n' - //EOM - //<< entryp->getNodep()->warnOther() << otherWritep->warnOther() << "... Location of always_comb write\n" - //<< entryp->getNodep()->warnContextSecondary()); << otherWritep->warnContextSecondary()); } } @@ -651,8 +626,6 @@ class UndrivenVisitor final : public VNVisitorConst { public: // CONSTRUCTORS - // EOM - // explicit UndrivenVisitor(AstNetlist* nodep) { iterateConst(nodep); } explicit UndrivenVisitor(AstNetlist* nodep, V3UndrivenCapture* capturep, bool enableWriteSummary) : m_capturep{capturep} @@ -673,7 +646,6 @@ public: void V3Undriven::undrivenAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ":"); - // EOM const bool enable = V3UndrivenCapture::enableWriteSummary; if (enable) { V3UndrivenCapture capture{nodep}; diff --git a/src/V3UndrivenCapture.cpp b/src/V3UndrivenCapture.cpp index 935e03eb0..7e62b8040 100644 --- a/src/V3UndrivenCapture.cpp +++ b/src/V3UndrivenCapture.cpp @@ -58,24 +58,20 @@ private: VL_RESTORER(m_curTaskp); m_curTaskp = nodep; ++g_stats.ftasks; - UINFO(DBG, "UndrivenCapture: enter ftask " << nodep << " " << nodep->prettyNameQ()); - m_cap.info(nodep); + UINFO(DBG, "undriven capture enter ftask " << nodep << " " << nodep->prettyNameQ()); + m_cap.noteTask(nodep); iterateListConst(*this, nodep->stmtsp()); } void visit(AstNodeVarRef* nodep) override { if (m_curTaskp && nodep->access().isWriteOrRW()) { ++g_stats.varWrites; - UINFO(DBG, "UndrivenCapture: direct write in " + UINFO(DBG, "undriven capture direct write in " << taskNameQ(m_curTaskp) << " var=" << nodep->varp()->prettyNameQ() << " at " << nodep->fileline()); - //m_cap.info(m_curTaskp).directWrites.push_back(nodep->varp()); - AstVar* const retVarp = VN_CAST(m_curTaskp->fvarp(), Var); - if (retVarp && nodep->varp() == retVarp) { - // Skip: function return variable is local, not a side-effect - } else { - m_cap.info(m_curTaskp).directWrites.push_back(nodep->varp()); - } + + m_cap.noteDirectWrite(m_curTaskp, nodep->varp()); + } iterateChildrenConst(nodep); } @@ -85,12 +81,11 @@ private: if (m_curTaskp) { if (AstNodeFTask* const calleep = nodep->taskp()) { ++g_stats.callEdges; - UINFO(DBG, "UndrivenCapture: call edge " << taskNameQ(m_curTaskp) << " -> " + UINFO(DBG, "undriven capture call edge " << taskNameQ(m_curTaskp) << " -> " << taskNameQ(calleep)); - m_cap.info(m_curTaskp).callees.push_back(calleep); - m_cap.info(calleep); + m_cap.noteCallEdge(m_curTaskp, calleep); } else { - UINFO(DBG, "UndrivenCapture: unresolved call in " << taskNameQ(m_curTaskp) + UINFO(DBG, "undriven capture unresolved call in " << taskNameQ(m_curTaskp) << " name=" << nodep->name()); } } @@ -128,7 +123,7 @@ V3UndrivenCapture::V3UndrivenCapture(AstNetlist* netlistp) { // Compute summaries for all tasks for (const auto& kv : m_info) (void)computeWriteSummary(kv.first); - UINFO(DBG, "UndrivenCapture: stats ftasks=" + UINFO(DBG, "undriven capture stats ftasks=" << g_stats.ftasks << " varWrites=" << g_stats.varWrites << " callEdges=" << g_stats.callEdges << " uniqueTasks=" << m_info.size()); } @@ -154,12 +149,12 @@ const std::vector& V3UndrivenCapture::computeWriteSummar FTaskInfo& info = m_info[taskp]; if (info.state == State::DONE) { - UINFO(DBG, "UndrivenCapture: writeSummary cached size=" << info.writeSummary.size() + UINFO(DBG, "undriven capture writeSummary cached size=" << info.writeSummary.size() << " for " << taskNameQ(taskp)); return info.writeSummary; } if (info.state == State::VISITING) { - UINFO(DBG, "UndrivenCapture: recursion detected at " + UINFO(DBG, "undriven capture recursion detected at " << taskNameQ(taskp) << " returning directWrites size=" << info.directWrites.size()); // Cycle detected. Simple behaviour: @@ -183,23 +178,38 @@ const std::vector& V3UndrivenCapture::computeWriteSummar sortUniqueVars(info.writeSummary); - UINFO(DBG, "UndrivenCapture: writeSummary computed size=" << info.writeSummary.size() + UINFO(DBG, "undriven capture writeSummary computed size=" << info.writeSummary.size() << " for " << taskNameQ(taskp)); info.state = State::DONE; return info.writeSummary; } +void V3UndrivenCapture::noteTask(FTask taskp) { (void)m_info[taskp]; } + +void V3UndrivenCapture::noteDirectWrite(FTask taskp, Var varp) { + FTaskInfo& info = m_info[taskp]; + + // Exclude function return variable (not an externally visible side-effect) + AstVar* const retVarp = VN_CAST(taskp->fvarp(), Var); + if (retVarp && varp == retVarp) return; + + info.directWrites.push_back(varp); +} + +void V3UndrivenCapture::noteCallEdge(FTask callerp, FTask calleep) { + m_info[callerp].callees.push_back(calleep); + (void)m_info[calleep]; // ensure callee entry exists +} + void V3UndrivenCapture::debugDumpTask(FTask taskp, int level) const { const auto* const infop = find(taskp); if (!infop) { - UINFO(level, "UndrivenCapture: no entry for task " << taskp); + UINFO(level, "undriven capture no entry for task " << taskp); return; } - UINFO(level, "UndrivenCapture: dump task " << taskp << " " << taskp->prettyNameQ() + UINFO(level, "undriven capture dump task " << taskp << " " << taskp->prettyNameQ() << " directWrites=" << infop->directWrites.size() << " callees=" << infop->callees.size() << " writeSummary=" << infop->writeSummary.size()); } - -V3UndrivenCapture::FTaskInfo& V3UndrivenCapture::info(FTask taskp) { return m_info[taskp]; } diff --git a/src/V3UndrivenCapture.h b/src/V3UndrivenCapture.h index c940c53bf..e3803af6b 100644 --- a/src/V3UndrivenCapture.h +++ b/src/V3UndrivenCapture.h @@ -47,9 +47,6 @@ public: static bool enableWriteSummary; - // Get (and create if needed) mutable info entry for a task/function. - FTaskInfo& info(FTask taskp); - private: // Per-task/function capture info keyed by resolved AstNodeFTask* identity. std::unordered_map m_info; @@ -74,6 +71,11 @@ public: // (creates empty entry if needed). const std::vector& writeSummary(FTask taskp); + // used by the capture visitor + void noteTask(FTask taskp); + void noteDirectWrite(FTask taskp, Var varp); + void noteCallEdge(FTask callerp, FTask calleep); + // Optional: dump one task's summary (for debug bring-up). void debugDumpTask(FTask taskp, int level = 9) const; }; From c39c3f09a6434b5f6f1aad333ae7f209f6313df5 Mon Sep 17 00:00:00 2001 From: github action Date: Mon, 22 Dec 2025 11:26:22 +0000 Subject: [PATCH 19/37] Apply 'make format' --- src/V3Undriven.cpp | 28 +++++++++++----------------- src/V3UndrivenCapture.cpp | 1 - 2 files changed, 11 insertions(+), 18 deletions(-) diff --git a/src/V3Undriven.cpp b/src/V3Undriven.cpp index 23f2756ed..15b0a7b39 100644 --- a/src/V3Undriven.cpp +++ b/src/V3Undriven.cpp @@ -28,7 +28,6 @@ #include "V3Undriven.h" #include "V3Stats.h" - #include "V3UndrivenCapture.h" #include @@ -459,8 +458,7 @@ class UndrivenVisitor final : public VNVisitorConst { } if (entryp->isDrivenWhole() && !m_inBBox && !VN_IS(nodep, VarXRef) && !VN_IS(nodep->dtypep()->skipRefp(), UnpackArrayDType) - && nodep->fileline() != entryp->getNodeFileLinep() - && !entryp->isUnderGen() + && nodep->fileline() != entryp->getNodeFileLinep() && !entryp->isUnderGen() && (entryp->getNodep() || (m_enableWriteSummary && entryp->getCallNodep()))) { const AstNode* const otherWritep @@ -477,23 +475,19 @@ class UndrivenVisitor final : public VNVisitorConst { "Variable written to in always_comb also written by other process" << " (IEEE 1800-2023 9.2.2.2): " << nodep->prettyNameQ() << '\n' << nodep->warnOther() << '\n' - << nodep->warnContextPrimary() - << '\n' - << otherWritep->warnOther() - << "... Location of other write\n" + << nodep->warnContextPrimary() << '\n' + << otherWritep->warnOther() << "... Location of other write\n" << otherWritep->warnContextSecondary()); } if (!m_alwaysCombp && entryp->isDrivenAlwaysCombWhole()) { - nodep->v3warn(MULTIDRIVEN, - "Variable also written to in always_comb" - << " (IEEE 1800-2023 9.2.2.2): " << nodep->prettyNameQ() - << '\n' - << nodep->warnOther() << '\n' - << nodep->warnContextPrimary() - << '\n' - << otherWritep->warnOther() - << "... Location of always_comb write\n" - << otherWritep->warnContextSecondary()); + nodep->v3warn(MULTIDRIVEN, "Variable also written to in always_comb" + << " (IEEE 1800-2023 9.2.2.2): " + << nodep->prettyNameQ() << '\n' + << nodep->warnOther() << '\n' + << nodep->warnContextPrimary() << '\n' + << otherWritep->warnOther() + << "... Location of always_comb write\n" + << otherWritep->warnContextSecondary()); } } entryp->drivenWhole(nodep, nodep->fileline()); diff --git a/src/V3UndrivenCapture.cpp b/src/V3UndrivenCapture.cpp index 7e62b8040..958810c10 100644 --- a/src/V3UndrivenCapture.cpp +++ b/src/V3UndrivenCapture.cpp @@ -71,7 +71,6 @@ private: << " at " << nodep->fileline()); m_cap.noteDirectWrite(m_curTaskp, nodep->varp()); - } iterateChildrenConst(nodep); } From 89fbb7c5b4ec33f7174bd29f214dc358cee6dffd Mon Sep 17 00:00:00 2001 From: em2machine <92717390+em2machine@users.noreply.github.com> Date: Mon, 22 Dec 2025 13:10:42 +0100 Subject: [PATCH 20/37] cleaned up comments --- src/V3UndrivenCapture.cpp | 7 ++++--- src/V3UndrivenCapture.h | 26 ++++++++++++++++---------- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/src/V3UndrivenCapture.cpp b/src/V3UndrivenCapture.cpp index 958810c10..0b9e8af20 100644 --- a/src/V3UndrivenCapture.cpp +++ b/src/V3UndrivenCapture.cpp @@ -156,8 +156,7 @@ const std::vector& V3UndrivenCapture::computeWriteSummar UINFO(DBG, "undriven capture recursion detected at " << taskNameQ(taskp) << " returning directWrites size=" << info.directWrites.size()); - // Cycle detected. Simple behaviour: - // return directWrites only to guarantee termination. + // Cycle detected. return directWrites only to guarantee termination. if (info.writeSummary.empty()) info.writeSummary = info.directWrites; sortUniqueVars(info.writeSummary); return info.writeSummary; @@ -168,18 +167,20 @@ const std::vector& V3UndrivenCapture::computeWriteSummar // Start with direct writes info.writeSummary = info.directWrites; - // Union in callees + // Need callees for (FTask calleep : info.callees) { if (m_info.find(calleep) == m_info.end()) continue; const std::vector& sub = computeWriteSummary(calleep); info.writeSummary.insert(info.writeSummary.end(), sub.begin(), sub.end()); } + // Remove duplicates and sort because grabbing all of the callees can result in duplicates sortUniqueVars(info.writeSummary); UINFO(DBG, "undriven capture writeSummary computed size=" << info.writeSummary.size() << " for " << taskNameQ(taskp)); + // We are done, so set the m_info state correctly and return the vector of variables info.state = State::DONE; return info.writeSummary; } diff --git a/src/V3UndrivenCapture.h b/src/V3UndrivenCapture.h index e3803af6b..0c33bd199 100644 --- a/src/V3UndrivenCapture.h +++ b/src/V3UndrivenCapture.h @@ -32,6 +32,9 @@ public: using Var = AstVar*; // DFS computation state for writeSummary propagation. + // UNVISITED: write summary not computed yet + // VISITING: currently computing on the call stack - used to detect cycles + // DONE: write summary computed enum class State : uint8_t { UNVISITED, VISITING, DONE }; struct FTaskInfo final { @@ -39,24 +42,25 @@ public: std::vector directWrites; // Direct resolved callees from this task/function body. std::vector callees; - // 'write through write' writeSummary for the given task/function. + // 'write through write' writeSummary for the given task/function. Meaning ultimately everything that this function/task writes to. std::vector writeSummary; // state for writeSummary computation. State state = State::UNVISITED; }; + // Enable writeSummary computation. If disabled, then the existing V3Undriven behaviour is used. static bool enableWriteSummary; private: - // Per-task/function capture info keyed by resolved AstNodeFTask* identity. + // Per-task/function capture info keyed by resolved AstNodeFTask* identity (FTask = function or task). This is our 'graph' of tasks/functions. Each node has a list of direct callees and a list of variables written in the function body. There are methods to remove duplicates otherwise this could explode. std::unordered_map m_info; - // Sort and remove duplicates from a vector of variables. + // Sort and remove duplicates from a vector of variables. This is called after a task/function write summary is computed. writeSummary can accumulate duplicates if a variable is written in multiple tasks/functions. static void sortUniqueVars(std::vector& vec); - // Sort and remove duplicates from a vector of callees. + // Sort and remove duplicates from a vector of callees. The visitor can record the same callee multiple times (multiple call sites, branches, etc). static void sortUniqueFTasks(std::vector& vec); - // Collect direct writes and call edges for all tasks/functions. + // Collect direct writes and call edges for all tasks/functions. Run one time when UndrivenCapture is created. This runs the visitor over the tree. void gather(AstNetlist* netlistp); // Compute (and cache) 'write through write' writeSummary for the given task/function. const std::vector& computeWriteSummary(FTask taskp); @@ -65,18 +69,20 @@ public: // Build capture database and precompute writeSummary for all discovered tasks/functions. explicit V3UndrivenCapture(AstNetlist* netlistp); - // Lookup task/function capture info (nullptr if unknown). + // Lookup task/function capture info (nullptr if unknown). This is currently only used for the debug helper. const FTaskInfo* find(FTask taskp) const; - // Get write through write through write, etc (call chain) writeSummary for a task/function - // (creates empty entry if needed). + // Get write through write through write, etc (call chain) writeSummary for a task/function (creates empty entry if needed). This returns a vector of variables that a particular task/function writes to, including all variables written by functions called by this task/function, and so on. const std::vector& writeSummary(FTask taskp); - // used by the capture visitor + // used by the capture visitor to record information about tasks/functions and their statements and callees. + // noteTask() makes sure there is an entry for the given taskp. void noteTask(FTask taskp); + // inside the body of taskp there is a write to variable varp void noteDirectWrite(FTask taskp, Var varp); + // inside the body of callerp there is a call to calleep, this is needed so we can create a summary that includes all variables written by functions called by this task/function, and so on. void noteCallEdge(FTask callerp, FTask calleep); - // Optional: dump one task's summary (for debug bring-up). + // dump one task's summary for debugging. leaving this in, in case need to debug future functionality. void debugDumpTask(FTask taskp, int level = 9) const; }; From 26bc0c001275209281e014c718e58fa14cb44903 Mon Sep 17 00:00:00 2001 From: github action Date: Mon, 22 Dec 2025 12:11:47 +0000 Subject: [PATCH 21/37] Apply 'make format' --- src/V3UndrivenCapture.h | 40 ++++++++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/src/V3UndrivenCapture.h b/src/V3UndrivenCapture.h index 0c33bd199..462590b2d 100644 --- a/src/V3UndrivenCapture.h +++ b/src/V3UndrivenCapture.h @@ -42,25 +42,34 @@ public: std::vector directWrites; // Direct resolved callees from this task/function body. std::vector callees; - // 'write through write' writeSummary for the given task/function. Meaning ultimately everything that this function/task writes to. + // 'write through write' writeSummary for the given task/function. Meaning ultimately + // everything that this function/task writes to. std::vector writeSummary; // state for writeSummary computation. State state = State::UNVISITED; }; - // Enable writeSummary computation. If disabled, then the existing V3Undriven behaviour is used. + // Enable writeSummary computation. If disabled, then the existing V3Undriven behaviour is + // used. static bool enableWriteSummary; private: - // Per-task/function capture info keyed by resolved AstNodeFTask* identity (FTask = function or task). This is our 'graph' of tasks/functions. Each node has a list of direct callees and a list of variables written in the function body. There are methods to remove duplicates otherwise this could explode. + // Per-task/function capture info keyed by resolved AstNodeFTask* identity (FTask = function or + // task). This is our 'graph' of tasks/functions. Each node has a list of direct callees and + // a list of variables written in the function body. There are methods to remove duplicates + // otherwise this could explode. std::unordered_map m_info; - // Sort and remove duplicates from a vector of variables. This is called after a task/function write summary is computed. writeSummary can accumulate duplicates if a variable is written in multiple tasks/functions. + // Sort and remove duplicates from a vector of variables. This is called after a task/function + // write summary is computed. writeSummary can accumulate duplicates if a variable is written + // in multiple tasks/functions. static void sortUniqueVars(std::vector& vec); - // Sort and remove duplicates from a vector of callees. The visitor can record the same callee multiple times (multiple call sites, branches, etc). + // Sort and remove duplicates from a vector of callees. The visitor can record the same callee + // multiple times (multiple call sites, branches, etc). static void sortUniqueFTasks(std::vector& vec); - // Collect direct writes and call edges for all tasks/functions. Run one time when UndrivenCapture is created. This runs the visitor over the tree. + // Collect direct writes and call edges for all tasks/functions. Run one time when + // UndrivenCapture is created. This runs the visitor over the tree. void gather(AstNetlist* netlistp); // Compute (and cache) 'write through write' writeSummary for the given task/function. const std::vector& computeWriteSummary(FTask taskp); @@ -69,20 +78,27 @@ public: // Build capture database and precompute writeSummary for all discovered tasks/functions. explicit V3UndrivenCapture(AstNetlist* netlistp); - // Lookup task/function capture info (nullptr if unknown). This is currently only used for the debug helper. + // Lookup task/function capture info (nullptr if unknown). This is currently only used for the + // debug helper. const FTaskInfo* find(FTask taskp) const; - // Get write through write through write, etc (call chain) writeSummary for a task/function (creates empty entry if needed). This returns a vector of variables that a particular task/function writes to, including all variables written by functions called by this task/function, and so on. + // Get write through write through write, etc (call chain) writeSummary for a task/function + // (creates empty entry if needed). This returns a vector of variables that a particular + // task/function writes to, including all variables written by functions called by this + // task/function, and so on. const std::vector& writeSummary(FTask taskp); - // used by the capture visitor to record information about tasks/functions and their statements and callees. - // noteTask() makes sure there is an entry for the given taskp. + // used by the capture visitor to record information about tasks/functions and their statements + // and callees. noteTask() makes sure there is an entry for the given taskp. void noteTask(FTask taskp); // inside the body of taskp there is a write to variable varp void noteDirectWrite(FTask taskp, Var varp); - // inside the body of callerp there is a call to calleep, this is needed so we can create a summary that includes all variables written by functions called by this task/function, and so on. + // inside the body of callerp there is a call to calleep, this is needed so we can create a + // summary that includes all variables written by functions called by this task/function, and + // so on. void noteCallEdge(FTask callerp, FTask calleep); - // dump one task's summary for debugging. leaving this in, in case need to debug future functionality. + // dump one task's summary for debugging. leaving this in, in case need to debug future + // functionality. void debugDumpTask(FTask taskp, int level = 9) const; }; From f7ebfd34bf1087c262139e8373dfd2c05ee6b297 Mon Sep 17 00:00:00 2001 From: em2machine <92717390+em2machine@users.noreply.github.com> Date: Mon, 22 Dec 2025 16:01:08 +0100 Subject: [PATCH 22/37] hardcode debug in UndrivenCapture --- src/V3UndrivenCapture.cpp | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/V3UndrivenCapture.cpp b/src/V3UndrivenCapture.cpp index 0b9e8af20..e3adfc83f 100644 --- a/src/V3UndrivenCapture.cpp +++ b/src/V3UndrivenCapture.cpp @@ -25,8 +25,6 @@ VL_DEFINE_DEBUG_FUNCTIONS; namespace { -constexpr int DBG = 9; - struct Stats final { uint64_t ftasks = 0; uint64_t varWrites = 0; @@ -58,7 +56,7 @@ private: VL_RESTORER(m_curTaskp); m_curTaskp = nodep; ++g_stats.ftasks; - UINFO(DBG, "undriven capture enter ftask " << nodep << " " << nodep->prettyNameQ()); + UINFO(9, "undriven capture enter ftask " << nodep << " " << nodep->prettyNameQ()); m_cap.noteTask(nodep); iterateListConst(*this, nodep->stmtsp()); } @@ -66,7 +64,7 @@ private: void visit(AstNodeVarRef* nodep) override { if (m_curTaskp && nodep->access().isWriteOrRW()) { ++g_stats.varWrites; - UINFO(DBG, "undriven capture direct write in " + UINFO(9, "undriven capture direct write in " << taskNameQ(m_curTaskp) << " var=" << nodep->varp()->prettyNameQ() << " at " << nodep->fileline()); @@ -80,11 +78,11 @@ private: if (m_curTaskp) { if (AstNodeFTask* const calleep = nodep->taskp()) { ++g_stats.callEdges; - UINFO(DBG, "undriven capture call edge " << taskNameQ(m_curTaskp) << " -> " + UINFO(9, "undriven capture call edge " << taskNameQ(m_curTaskp) << " -> " << taskNameQ(calleep)); m_cap.noteCallEdge(m_curTaskp, calleep); } else { - UINFO(DBG, "undriven capture unresolved call in " << taskNameQ(m_curTaskp) + UINFO(9, "undriven capture unresolved call in " << taskNameQ(m_curTaskp) << " name=" << nodep->name()); } } @@ -122,7 +120,7 @@ V3UndrivenCapture::V3UndrivenCapture(AstNetlist* netlistp) { // Compute summaries for all tasks for (const auto& kv : m_info) (void)computeWriteSummary(kv.first); - UINFO(DBG, "undriven capture stats ftasks=" + UINFO(9, "undriven capture stats ftasks=" << g_stats.ftasks << " varWrites=" << g_stats.varWrites << " callEdges=" << g_stats.callEdges << " uniqueTasks=" << m_info.size()); } @@ -148,12 +146,12 @@ const std::vector& V3UndrivenCapture::computeWriteSummar FTaskInfo& info = m_info[taskp]; if (info.state == State::DONE) { - UINFO(DBG, "undriven capture writeSummary cached size=" << info.writeSummary.size() + UINFO(9, "undriven capture writeSummary cached size=" << info.writeSummary.size() << " for " << taskNameQ(taskp)); return info.writeSummary; } if (info.state == State::VISITING) { - UINFO(DBG, "undriven capture recursion detected at " + UINFO(9, "undriven capture recursion detected at " << taskNameQ(taskp) << " returning directWrites size=" << info.directWrites.size()); // Cycle detected. return directWrites only to guarantee termination. @@ -177,7 +175,7 @@ const std::vector& V3UndrivenCapture::computeWriteSummar // Remove duplicates and sort because grabbing all of the callees can result in duplicates sortUniqueVars(info.writeSummary); - UINFO(DBG, "undriven capture writeSummary computed size=" << info.writeSummary.size() + UINFO(9, "undriven capture writeSummary computed size=" << info.writeSummary.size() << " for " << taskNameQ(taskp)); // We are done, so set the m_info state correctly and return the vector of variables From 4a6582cc9b40e8d31cb7118cccbeb174719b41bf Mon Sep 17 00:00:00 2001 From: em2machine <92717390+em2machine@users.noreply.github.com> Date: Mon, 22 Dec 2025 16:14:57 +0100 Subject: [PATCH 23/37] change test to be near other tests --- ...tidriven_bad.out => t_lint_multidriven_taskcall_bad.out} | 6 +++--- ...ultidriven_bad.py => t_lint_multidriven_taskcall_bad.py} | 0 ..._multidriven_bad.v => t_lint_multidriven_taskcall_bad.v} | 0 3 files changed, 3 insertions(+), 3 deletions(-) rename test_regress/t/{t_lint_taskcall_multidriven_bad.out => t_lint_multidriven_taskcall_bad.out} (74%) rename test_regress/t/{t_lint_taskcall_multidriven_bad.py => t_lint_multidriven_taskcall_bad.py} (100%) rename test_regress/t/{t_lint_taskcall_multidriven_bad.v => t_lint_multidriven_taskcall_bad.v} (100%) diff --git a/test_regress/t/t_lint_taskcall_multidriven_bad.out b/test_regress/t/t_lint_multidriven_taskcall_bad.out similarity index 74% rename from test_regress/t/t_lint_taskcall_multidriven_bad.out rename to test_regress/t/t_lint_multidriven_taskcall_bad.out index 3e19de72c..1d1278b14 100644 --- a/test_regress/t/t_lint_taskcall_multidriven_bad.out +++ b/test_regress/t/t_lint_multidriven_taskcall_bad.out @@ -1,9 +1,9 @@ -%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' +%Warning-MULTIDRIVEN: t/t_lint_multidriven_taskcall_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: + t/t_lint_multidriven_taskcall_bad.v:28:15: 28 | if (sel2) out = 1'b1; | ^~~ - t/t_lint_taskcall_multidriven_bad.v:20:5: ... Location of other write + t/t_lint_multidriven_taskcall_bad.v:20:5: ... Location of other write 20 | out = 1'b0; | ^~~ ... For warning description see https://verilator.org/warn/MULTIDRIVEN?v=latest diff --git a/test_regress/t/t_lint_taskcall_multidriven_bad.py b/test_regress/t/t_lint_multidriven_taskcall_bad.py similarity index 100% rename from test_regress/t/t_lint_taskcall_multidriven_bad.py rename to test_regress/t/t_lint_multidriven_taskcall_bad.py diff --git a/test_regress/t/t_lint_taskcall_multidriven_bad.v b/test_regress/t/t_lint_multidriven_taskcall_bad.v similarity index 100% rename from test_regress/t/t_lint_taskcall_multidriven_bad.v rename to test_regress/t/t_lint_multidriven_taskcall_bad.v From 87cfd53949e9495eddfb57945f1b3469bd335f74 Mon Sep 17 00:00:00 2001 From: em2machine <92717390+em2machine@users.noreply.github.com> Date: Mon, 22 Dec 2025 17:08:21 +0100 Subject: [PATCH 24/37] move commas >< --- test_regress/t/t_lint_multidriven_taskcall_bad.v | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test_regress/t/t_lint_multidriven_taskcall_bad.v b/test_regress/t/t_lint_multidriven_taskcall_bad.v index 1872e8bcb..b7943087a 100644 --- a/test_regress/t/t_lint_multidriven_taskcall_bad.v +++ b/test_regress/t/t_lint_multidriven_taskcall_bad.v @@ -5,10 +5,10 @@ // SPDX-License-Identifier: CC0-1.0 module t ( - input logic sel - , input logic sel2 - , input logic d - , output logic out + input logic sel, + input logic sel2, + input logic d, + output logic out ); task automatic do_stuff(input logic din); From 74c7d598cb9f27228592d5f38fd61461433982ec Mon Sep 17 00:00:00 2001 From: em2machine <92717390+em2machine@users.noreply.github.com> Date: Mon, 22 Dec 2025 17:28:55 +0100 Subject: [PATCH 25/37] consolidate t_multidriven_simple --- ...ven_simple0.py => t_multidriven_simple.py} | 0 test_regress/t/t_multidriven_simple.v | 120 ++++++++++++++++++ test_regress/t/t_multidriven_simple0.v | 63 --------- test_regress/t/t_multidriven_simple1.py | 18 --- test_regress/t/t_multidriven_simple1.v | 66 ---------- test_regress/t/t_multidriven_simple2.py | 18 --- test_regress/t/t_multidriven_simple2.v | 62 --------- test_regress/t/t_multidriven_simple3.py | 18 --- test_regress/t/t_multidriven_simple3.v | 62 --------- test_regress/t/t_multidriven_simple4.py | 18 --- test_regress/t/t_multidriven_simple4.v | 61 --------- 11 files changed, 120 insertions(+), 386 deletions(-) rename test_regress/t/{t_multidriven_simple0.py => t_multidriven_simple.py} (100%) create mode 100644 test_regress/t/t_multidriven_simple.v delete mode 100644 test_regress/t/t_multidriven_simple0.v delete mode 100755 test_regress/t/t_multidriven_simple1.py delete mode 100644 test_regress/t/t_multidriven_simple1.v delete mode 100755 test_regress/t/t_multidriven_simple2.py delete mode 100644 test_regress/t/t_multidriven_simple2.v delete mode 100755 test_regress/t/t_multidriven_simple3.py delete mode 100644 test_regress/t/t_multidriven_simple3.v delete mode 100755 test_regress/t/t_multidriven_simple4.py delete mode 100644 test_regress/t/t_multidriven_simple4.v diff --git a/test_regress/t/t_multidriven_simple0.py b/test_regress/t/t_multidriven_simple.py similarity index 100% rename from test_regress/t/t_multidriven_simple0.py rename to test_regress/t/t_multidriven_simple.py diff --git a/test_regress/t/t_multidriven_simple.v b/test_regress/t/t_multidriven_simple.v new file mode 100644 index 000000000..e1a240578 --- /dev/null +++ b/test_regress/t/t_multidriven_simple.v @@ -0,0 +1,120 @@ + +// 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 + +// direct task call +module mod0 #()( + 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 + +// nested task call chain +module mod1 #()( + 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 + +// task writes through an output arguement +module mod2 #()( + input logic sel, + output logic val +); + logic l0; + task automatic do_stuff(output logic q); + q = 1'b1; + endtask + always_comb begin + l0 = 1'b0; + if (sel) do_stuff(l0); + end + assign val = l0; +endmodule + +// function call that writes +module mod3 #()( + input logic sel, + output logic val +); + logic l0; + function automatic void do_func(); + l0 = 1'b1; + endfunction + always_comb begin + l0 = 1'b0; + if (sel) do_func(); + end + assign val = l0; +endmodule + +// two tasks set0/set1 +module mod4 #()( + input logic sel, + output logic val +); + logic l0; + task automatic set1(); l0 = 1'b1; endtask + task automatic set0(); l0 = 1'b0; endtask + always_comb begin + set0(); + if (sel) begin + set1(); + end + end + assign val = l0; +endmodule + +module m_tb; + logic sel; + logic v0, v1, v2, v3, v4; + + mod0 u0(.sel(sel), .val(v0)); + mod1 u1(.sel(sel), .val(v1)); + mod2 u2(.sel(sel), .val(v2)); + mod3 u3(.sel(sel), .val(v3)); + mod4 u4(.sel(sel), .val(v4)); + + initial begin + #1; sel = 0; + `checkd(v0, 0); `checkd(v1, 0); `checkd(v2, 0); `checkd(v3, 0); `checkd(v4, 0); + #1; sel = 1; + `checkd(v0, 1); `checkd(v1, 1); `checkd(v2, 1); `checkd(v3, 1); `checkd(v4, 1); + #1; sel = 0; + `checkd(v0, 0); `checkd(v1, 0); `checkd(v2, 0); `checkd(v3, 0); `checkd(v4, 0); + #1; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_multidriven_simple0.v b/test_regress/t/t_multidriven_simple0.v deleted file mode 100644 index 66a50beed..000000000 --- a/test_regress/t/t_multidriven_simple0.v +++ /dev/null @@ -1,63 +0,0 @@ -// 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 deleted file mode 100755 index c6e56559a..000000000 --- a/test_regress/t/t_multidriven_simple1.py +++ /dev/null @@ -1,18 +0,0 @@ -#!/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 deleted file mode 100644 index ef5e9d164..000000000 --- a/test_regress/t/t_multidriven_simple1.v +++ /dev/null @@ -1,66 +0,0 @@ -// 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 - -// task chain - testing nested task calls - -// 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 diff --git a/test_regress/t/t_multidriven_simple2.py b/test_regress/t/t_multidriven_simple2.py deleted file mode 100755 index c6e56559a..000000000 --- a/test_regress/t/t_multidriven_simple2.py +++ /dev/null @@ -1,18 +0,0 @@ -#!/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_simple2.v b/test_regress/t/t_multidriven_simple2.v deleted file mode 100644 index 15f80e1e5..000000000 --- a/test_regress/t/t_multidriven_simple2.v +++ /dev/null @@ -1,62 +0,0 @@ -// 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 - -// task writes through an argument (output argument of a task) - -// 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 automatic do_stuff(output logic q); - q = 1'b1; - endtask - - always_comb begin - l0 = 1'b0; - if (sel) do_stuff(l0); - 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_simple3.py b/test_regress/t/t_multidriven_simple3.py deleted file mode 100755 index c6e56559a..000000000 --- a/test_regress/t/t_multidriven_simple3.py +++ /dev/null @@ -1,18 +0,0 @@ -#!/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_simple3.v b/test_regress/t/t_multidriven_simple3.v deleted file mode 100644 index 7ea624d45..000000000 --- a/test_regress/t/t_multidriven_simple3.v +++ /dev/null @@ -1,62 +0,0 @@ -// 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 - -// function call that writes - -// 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; - -function automatic void do_func(); - l0 = 1'b1; -endfunction - -always_comb begin - l0 = 1'b0; - if (sel) do_func(); -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_simple4.py b/test_regress/t/t_multidriven_simple4.py deleted file mode 100755 index c6e56559a..000000000 --- a/test_regress/t/t_multidriven_simple4.py +++ /dev/null @@ -1,18 +0,0 @@ -#!/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_simple4.v b/test_regress/t/t_multidriven_simple4.v deleted file mode 100644 index 475c8589b..000000000 --- a/test_regress/t/t_multidriven_simple4.v +++ /dev/null @@ -1,61 +0,0 @@ -// 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 automatic set1(); l0 = 1'b1; endtask - task automatic set0(); l0 = 1'b0; endtask - - always_comb begin - set0(); - if (sel) begin - set1(); - 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 From d8d46b8fce82075d48e05e4ce8bb8577c95b75a3 Mon Sep 17 00:00:00 2001 From: em2machine <92717390+em2machine@users.noreply.github.com> Date: Mon, 22 Dec 2025 17:58:11 +0100 Subject: [PATCH 26/37] removed aliases in V3UndrivenCapture, updated comments per feedback --- src/V3Undriven.cpp | 16 ++++++--------- src/V3UndrivenCapture.cpp | 24 +++++++++++------------ src/V3UndrivenCapture.h | 41 ++++++++++++++++++--------------------- 3 files changed, 36 insertions(+), 45 deletions(-) diff --git a/src/V3Undriven.cpp b/src/V3Undriven.cpp index 15b0a7b39..401bd17ca 100644 --- a/src/V3Undriven.cpp +++ b/src/V3Undriven.cpp @@ -52,8 +52,8 @@ class UndrivenVarEntry final { const FileLine* m_nodeFileLinep = nullptr; // File line of varref if driven, else nullptr bool m_underGen = false; // Under a generate - const AstNode* m_callNodep = nullptr; // call node if driven via writeSummary, else nullptr - const FileLine* m_callFileLinep = nullptr; // file line of call node if driven via summary + const AstNode* m_callNodep = nullptr; // Call node if driven via writeSummary, else nullptr + const FileLine* m_callFileLinep = nullptr; // File line of call node if driven via summary enum : uint8_t { FLAG_USED = 0, FLAG_DRIVEN = 1, FLAG_DRIVEN_ALWCOMB = 2, FLAGS_PER_BIT = 3 }; @@ -318,8 +318,8 @@ class UndrivenVisitor final : public VNVisitorConst { const AstAlways* m_alwaysp = nullptr; // Current always of either type const AstAlways* m_alwaysCombp = nullptr; // Current always if combo, otherwise nullptr - V3UndrivenCapture* const m_capturep = nullptr; - const bool m_enableWriteSummary = false; + V3UndrivenCapture* const m_capturep = nullptr; // Capture object. 'nullptr' if disabled. + const bool m_enableWriteSummary = false; // Enable writeSummary computation plumbing // METHODS @@ -565,19 +565,15 @@ class UndrivenVisitor final : public VNVisitorConst { // 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). did the lambda on purpose - lessen chance of + // independent drivers (phantom MULTIDRIVEN). Did the lambda on purpose - lessen chance of // screwup in future edits. - //const auto inExecutedContext = [this]() const { - // return !(m_taskp && !m_alwaysp && !m_inContAssign && !m_inInitialStatic && - // !m_inBBox); - //}; const auto inExecutedContext = [this]() { return !(m_taskp && !m_alwaysp && !m_inContAssign && !m_inInitialStatic && !m_inBBox && !m_taskp->dpiExport()); }; - if (!inExecutedContext()) { return; } + if (!inExecutedContext()) return; AstNodeFTask* const calleep = nodep->taskp(); if (!calleep) return; diff --git a/src/V3UndrivenCapture.cpp b/src/V3UndrivenCapture.cpp index e3adfc83f..6c23c20a7 100644 --- a/src/V3UndrivenCapture.cpp +++ b/src/V3UndrivenCapture.cpp @@ -96,14 +96,12 @@ private: bool V3UndrivenCapture::enableWriteSummary = true; -// static -void V3UndrivenCapture::sortUniqueVars(std::vector& vec) { +void V3UndrivenCapture::sortUniqueVars(std::vector& vec) { std::sort(vec.begin(), vec.end()); vec.erase(std::unique(vec.begin(), vec.end()), vec.end()); } -// static -void V3UndrivenCapture::sortUniqueFTasks(std::vector& vec) { +void V3UndrivenCapture::sortUniqueFTasks(std::vector& vec) { std::sort(vec.begin(), vec.end()); vec.erase(std::unique(vec.begin(), vec.end()), vec.end()); } @@ -130,19 +128,19 @@ void V3UndrivenCapture::gather(AstNetlist* netlistp) { CaptureVisitor{*this, netlistp}; } -const V3UndrivenCapture::FTaskInfo* V3UndrivenCapture::find(FTask taskp) const { +const V3UndrivenCapture::FTaskInfo* V3UndrivenCapture::find(const AstNodeFTask* taskp) const { const auto it = m_info.find(taskp); if (it == m_info.end()) return nullptr; return &it->second; } -const std::vector& V3UndrivenCapture::writeSummary(FTask taskp) { +const std::vector& V3UndrivenCapture::writeSummary(const AstNodeFTask* taskp) { // Ensure entry exists even if empty (void)m_info[taskp]; return computeWriteSummary(taskp); } -const std::vector& V3UndrivenCapture::computeWriteSummary(FTask taskp) { +const std::vector& V3UndrivenCapture::computeWriteSummary(const AstNodeFTask* taskp) { FTaskInfo& info = m_info[taskp]; if (info.state == State::DONE) { @@ -166,9 +164,9 @@ const std::vector& V3UndrivenCapture::computeWriteSummar info.writeSummary = info.directWrites; // Need callees - for (FTask calleep : info.callees) { + for (const AstNodeFTask* calleep : info.callees) { if (m_info.find(calleep) == m_info.end()) continue; - const std::vector& sub = computeWriteSummary(calleep); + const std::vector& sub = computeWriteSummary(calleep); info.writeSummary.insert(info.writeSummary.end(), sub.begin(), sub.end()); } @@ -183,9 +181,9 @@ const std::vector& V3UndrivenCapture::computeWriteSummar return info.writeSummary; } -void V3UndrivenCapture::noteTask(FTask taskp) { (void)m_info[taskp]; } +void V3UndrivenCapture::noteTask(const AstNodeFTask* taskp) { (void)m_info[taskp]; } -void V3UndrivenCapture::noteDirectWrite(FTask taskp, Var varp) { +void V3UndrivenCapture::noteDirectWrite(const AstNodeFTask* taskp, AstVar* varp) { FTaskInfo& info = m_info[taskp]; // Exclude function return variable (not an externally visible side-effect) @@ -195,12 +193,12 @@ void V3UndrivenCapture::noteDirectWrite(FTask taskp, Var varp) { info.directWrites.push_back(varp); } -void V3UndrivenCapture::noteCallEdge(FTask callerp, FTask calleep) { +void V3UndrivenCapture::noteCallEdge(const AstNodeFTask* callerp, const AstNodeFTask* calleep) { m_info[callerp].callees.push_back(calleep); (void)m_info[calleep]; // ensure callee entry exists } -void V3UndrivenCapture::debugDumpTask(FTask taskp, int level) const { +void V3UndrivenCapture::debugDumpTask(const AstNodeFTask* taskp, int level) const { const auto* const infop = find(taskp); if (!infop) { UINFO(level, "undriven capture no entry for task " << taskp); diff --git a/src/V3UndrivenCapture.h b/src/V3UndrivenCapture.h index 462590b2d..27e6ffeb7 100644 --- a/src/V3UndrivenCapture.h +++ b/src/V3UndrivenCapture.h @@ -28,9 +28,6 @@ class AstNetlist; class V3UndrivenCapture final { public: - using FTask = const AstNodeFTask*; - using Var = AstVar*; - // DFS computation state for writeSummary propagation. // UNVISITED: write summary not computed yet // VISITING: currently computing on the call stack - used to detect cycles @@ -39,13 +36,13 @@ public: struct FTaskInfo final { // Variables written directly in this task/function body. - std::vector directWrites; + std::vector directWrites; // Direct resolved callees from this task/function body. - std::vector callees; - // 'write through write' writeSummary for the given task/function. Meaning ultimately + std::vector callees; + // 'Write through write' writeSummary for the given task/function. Meaning ultimately // everything that this function/task writes to. - std::vector writeSummary; - // state for writeSummary computation. + std::vector writeSummary; + // State for writeSummary computation. State state = State::UNVISITED; }; @@ -58,21 +55,21 @@ private: // task). This is our 'graph' of tasks/functions. Each node has a list of direct callees and // a list of variables written in the function body. There are methods to remove duplicates // otherwise this could explode. - std::unordered_map m_info; + std::unordered_map m_info; // Sort and remove duplicates from a vector of variables. This is called after a task/function // write summary is computed. writeSummary can accumulate duplicates if a variable is written // in multiple tasks/functions. - static void sortUniqueVars(std::vector& vec); + static void sortUniqueVars(std::vector& vec); // Sort and remove duplicates from a vector of callees. The visitor can record the same callee // multiple times (multiple call sites, branches, etc). - static void sortUniqueFTasks(std::vector& vec); + static void sortUniqueFTasks(std::vector& vec); // Collect direct writes and call edges for all tasks/functions. Run one time when // UndrivenCapture is created. This runs the visitor over the tree. void gather(AstNetlist* netlistp); // Compute (and cache) 'write through write' writeSummary for the given task/function. - const std::vector& computeWriteSummary(FTask taskp); + const std::vector& computeWriteSummary(const AstNodeFTask* taskp); public: // Build capture database and precompute writeSummary for all discovered tasks/functions. @@ -80,26 +77,26 @@ public: // Lookup task/function capture info (nullptr if unknown). This is currently only used for the // debug helper. - const FTaskInfo* find(FTask taskp) const; + const FTaskInfo* find(const AstNodeFTask* taskp) const; // Get write through write through write, etc (call chain) writeSummary for a task/function // (creates empty entry if needed). This returns a vector of variables that a particular // task/function writes to, including all variables written by functions called by this // task/function, and so on. - const std::vector& writeSummary(FTask taskp); + const std::vector& writeSummary(const AstNodeFTask* taskp); - // used by the capture visitor to record information about tasks/functions and their statements + // Used by the capture visitor to record information about tasks/functions and their statements // and callees. noteTask() makes sure there is an entry for the given taskp. - void noteTask(FTask taskp); - // inside the body of taskp there is a write to variable varp - void noteDirectWrite(FTask taskp, Var varp); - // inside the body of callerp there is a call to calleep, this is needed so we can create a + void noteTask(const AstNodeFTask* taskp); + // Inside the body of taskp there is a write to variable varp + void noteDirectWrite(const AstNodeFTask* taskp, AstVar* varp); + // Inside the body of callerp there is a call to calleep, this is needed so we can create a // summary that includes all variables written by functions called by this task/function, and // so on. - void noteCallEdge(FTask callerp, FTask calleep); + void noteCallEdge(const AstNodeFTask* callerp, const AstNodeFTask* calleep); - // dump one task's summary for debugging. leaving this in, in case need to debug future + // Dump one task's summary for debugging. leaving this in, in case need to debug future // functionality. - void debugDumpTask(FTask taskp, int level = 9) const; + void debugDumpTask(const AstNodeFTask* taskp, int level = 9) const; }; #endif // VERILATOR_V3UNDRIVENCAPTURE_H_ From 2db7e9e60c009f13876360c51cbaa390d2ad436a Mon Sep 17 00:00:00 2001 From: em2machine <92717390+em2machine@users.noreply.github.com> Date: Mon, 22 Dec 2025 18:04:51 +0100 Subject: [PATCH 27/37] added commentary --- src/V3UndrivenCapture.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/V3UndrivenCapture.h b/src/V3UndrivenCapture.h index 27e6ffeb7..12bc4968c 100644 --- a/src/V3UndrivenCapture.h +++ b/src/V3UndrivenCapture.h @@ -14,6 +14,17 @@ // //************************************************************************* +//************************************************************************* +// +// Capture task/function write summaries for multidriven checks. +// Per-task/function capture info keyed by resolved AstNodeFTask* identity (FTask = function or +// task). This is our 'graph' of tasks/functions. Each node has a list of direct callees and +// a list of variables written in the function body. There are methods to dedup after walking the tree. +// V3Undriven then uses the writeSummary for multidriven checks - i.e. it treats writes (side effects) +// inside subroutines as part of the caller's process. +// +//************************************************************************* + #ifndef VERILATOR_V3UNDRIVENCAPTURE_H_ #define VERILATOR_V3UNDRIVENCAPTURE_H_ From a573a5373dee9ee118544577c75d1da444450da4 Mon Sep 17 00:00:00 2001 From: em2machine <92717390+em2machine@users.noreply.github.com> Date: Mon, 22 Dec 2025 18:36:35 +0100 Subject: [PATCH 28/37] getCallFileLinep removed and changed getCallNodep to callNodep --- src/V3Undriven.cpp | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/V3Undriven.cpp b/src/V3Undriven.cpp index 401bd17ca..6bbf68308 100644 --- a/src/V3Undriven.cpp +++ b/src/V3Undriven.cpp @@ -53,7 +53,6 @@ class UndrivenVarEntry final { bool m_underGen = false; // Under a generate const AstNode* m_callNodep = nullptr; // Call node if driven via writeSummary, else nullptr - const FileLine* m_callFileLinep = nullptr; // File line of call node if driven via summary enum : uint8_t { FLAG_USED = 0, FLAG_DRIVEN = 1, FLAG_DRIVEN_ALWCOMB = 2, FLAGS_PER_BIT = 3 }; @@ -283,15 +282,13 @@ public: } } - void drivenViaCall(const AstNode* nodep, const FileLine* fileLinep) { + void drivenViaCall(const AstNode* nodep) { drivenWhole(); if (!m_callNodep) { m_callNodep = nodep; - m_callFileLinep = fileLinep; } } - const AstNode* getCallNodep() const { return m_callNodep; } - const FileLine* getCallFileLinep() const { return m_callFileLinep; } + const AstNode* callNodep() const { return m_callNodep; } }; //###################################################################### @@ -459,12 +456,12 @@ class UndrivenVisitor final : public VNVisitorConst { if (entryp->isDrivenWhole() && !m_inBBox && !VN_IS(nodep, VarXRef) && !VN_IS(nodep->dtypep()->skipRefp(), UnpackArrayDType) && nodep->fileline() != entryp->getNodeFileLinep() && !entryp->isUnderGen() - && (entryp->getNodep() || (m_enableWriteSummary && entryp->getCallNodep()))) { + && (entryp->getNodep() || (m_enableWriteSummary && entryp->callNodep()))) { const AstNode* const otherWritep = entryp->getNodep() ? static_cast(entryp->getNodep()) - : (m_enableWriteSummary ? entryp->getCallNodep() : nullptr); + : (m_enableWriteSummary ? entryp->callNodep() : nullptr); if (m_alwaysCombp && (!entryp->isDrivenAlwaysCombWhole() @@ -582,7 +579,7 @@ class UndrivenVisitor final : public VNVisitorConst { for (AstVar* const varp : vars) { for (int usr = 1; usr < (m_alwaysCombp ? 3 : 2); ++usr) { UndrivenVarEntry* const entryp = getEntryp(varp, usr); - entryp->drivenViaCall(nodep, nodep->fileline()); + entryp->drivenViaCall(nodep); if (m_alwaysCombp) entryp->drivenAlwaysCombWhole(m_alwaysCombp, m_alwaysCombp->fileline()); } From 3585d3e1c5029096a9935247792dd46334581bc3 Mon Sep 17 00:00:00 2001 From: em2machine <92717390+em2machine@users.noreply.github.com> Date: Mon, 22 Dec 2025 18:40:01 +0100 Subject: [PATCH 29/37] changed AstNode to AstNodeFTaskRef --- src/V3Undriven.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/V3Undriven.cpp b/src/V3Undriven.cpp index 6bbf68308..fa11e5911 100644 --- a/src/V3Undriven.cpp +++ b/src/V3Undriven.cpp @@ -52,7 +52,7 @@ class UndrivenVarEntry final { const FileLine* m_nodeFileLinep = nullptr; // File line of varref if driven, else nullptr bool m_underGen = false; // Under a generate - const AstNode* m_callNodep = nullptr; // Call node if driven via writeSummary, else nullptr + const AstNodeFTaskRef* m_callNodep = nullptr; // Call node if driven via writeSummary, else nullptr enum : uint8_t { FLAG_USED = 0, FLAG_DRIVEN = 1, FLAG_DRIVEN_ALWCOMB = 2, FLAGS_PER_BIT = 3 }; @@ -282,13 +282,13 @@ public: } } - void drivenViaCall(const AstNode* nodep) { + void drivenViaCall(const AstNodeFTaskRef* nodep) { drivenWhole(); if (!m_callNodep) { m_callNodep = nodep; } } - const AstNode* callNodep() const { return m_callNodep; } + const AstNodeFTaskRef* callNodep() const { return m_callNodep; } }; //###################################################################### From 184502471df3c9b422296f926847077a4ff2a062 Mon Sep 17 00:00:00 2001 From: github action Date: Mon, 22 Dec 2025 17:42:33 +0000 Subject: [PATCH 30/37] Apply 'make format' --- src/V3Undriven.cpp | 11 +++++------ src/V3UndrivenCapture.cpp | 24 ++++++++++++------------ src/V3UndrivenCapture.h | 6 +++--- 3 files changed, 20 insertions(+), 21 deletions(-) diff --git a/src/V3Undriven.cpp b/src/V3Undriven.cpp index fa11e5911..4a54cd54a 100644 --- a/src/V3Undriven.cpp +++ b/src/V3Undriven.cpp @@ -52,7 +52,8 @@ class UndrivenVarEntry final { const FileLine* m_nodeFileLinep = nullptr; // File line of varref if driven, else nullptr bool m_underGen = false; // Under a generate - const AstNodeFTaskRef* m_callNodep = nullptr; // Call node if driven via writeSummary, else nullptr + const AstNodeFTaskRef* m_callNodep + = nullptr; // Call node if driven via writeSummary, else nullptr enum : uint8_t { FLAG_USED = 0, FLAG_DRIVEN = 1, FLAG_DRIVEN_ALWCOMB = 2, FLAGS_PER_BIT = 3 }; @@ -284,9 +285,7 @@ public: void drivenViaCall(const AstNodeFTaskRef* nodep) { drivenWhole(); - if (!m_callNodep) { - m_callNodep = nodep; - } + if (!m_callNodep) { m_callNodep = nodep; } } const AstNodeFTaskRef* callNodep() const { return m_callNodep; } }; @@ -315,8 +314,8 @@ class UndrivenVisitor final : public VNVisitorConst { const AstAlways* m_alwaysp = nullptr; // Current always of either type const AstAlways* m_alwaysCombp = nullptr; // Current always if combo, otherwise nullptr - V3UndrivenCapture* const m_capturep = nullptr; // Capture object. 'nullptr' if disabled. - const bool m_enableWriteSummary = false; // Enable writeSummary computation plumbing + V3UndrivenCapture* const m_capturep = nullptr; // Capture object. 'nullptr' if disabled. + const bool m_enableWriteSummary = false; // Enable writeSummary computation plumbing // METHODS diff --git a/src/V3UndrivenCapture.cpp b/src/V3UndrivenCapture.cpp index 6c23c20a7..ec6e23846 100644 --- a/src/V3UndrivenCapture.cpp +++ b/src/V3UndrivenCapture.cpp @@ -64,9 +64,9 @@ private: void visit(AstNodeVarRef* nodep) override { if (m_curTaskp && nodep->access().isWriteOrRW()) { ++g_stats.varWrites; - UINFO(9, "undriven capture direct write in " - << taskNameQ(m_curTaskp) << " var=" << nodep->varp()->prettyNameQ() - << " at " << nodep->fileline()); + UINFO(9, "undriven capture direct write in " << taskNameQ(m_curTaskp) + << " var=" << nodep->varp()->prettyNameQ() + << " at " << nodep->fileline()); m_cap.noteDirectWrite(m_curTaskp, nodep->varp()); } @@ -79,11 +79,11 @@ private: if (AstNodeFTask* const calleep = nodep->taskp()) { ++g_stats.callEdges; UINFO(9, "undriven capture call edge " << taskNameQ(m_curTaskp) << " -> " - << taskNameQ(calleep)); + << taskNameQ(calleep)); m_cap.noteCallEdge(m_curTaskp, calleep); } else { UINFO(9, "undriven capture unresolved call in " << taskNameQ(m_curTaskp) - << " name=" << nodep->name()); + << " name=" << nodep->name()); } } iterateChildrenConst(nodep); // still scan pins/args @@ -119,8 +119,8 @@ V3UndrivenCapture::V3UndrivenCapture(AstNetlist* netlistp) { for (const auto& kv : m_info) (void)computeWriteSummary(kv.first); UINFO(9, "undriven capture stats ftasks=" - << g_stats.ftasks << " varWrites=" << g_stats.varWrites - << " callEdges=" << g_stats.callEdges << " uniqueTasks=" << m_info.size()); + << g_stats.ftasks << " varWrites=" << g_stats.varWrites + << " callEdges=" << g_stats.callEdges << " uniqueTasks=" << m_info.size()); } void V3UndrivenCapture::gather(AstNetlist* netlistp) { @@ -145,13 +145,13 @@ const std::vector& V3UndrivenCapture::computeWriteSummary(const AstNode if (info.state == State::DONE) { UINFO(9, "undriven capture writeSummary cached size=" << info.writeSummary.size() - << " for " << taskNameQ(taskp)); + << " for " << taskNameQ(taskp)); return info.writeSummary; } if (info.state == State::VISITING) { UINFO(9, "undriven capture recursion detected at " - << taskNameQ(taskp) - << " returning directWrites size=" << info.directWrites.size()); + << taskNameQ(taskp) + << " returning directWrites size=" << info.directWrites.size()); // Cycle detected. return directWrites only to guarantee termination. if (info.writeSummary.empty()) info.writeSummary = info.directWrites; sortUniqueVars(info.writeSummary); @@ -173,8 +173,8 @@ const std::vector& V3UndrivenCapture::computeWriteSummary(const AstNode // Remove duplicates and sort because grabbing all of the callees can result in duplicates sortUniqueVars(info.writeSummary); - UINFO(9, "undriven capture writeSummary computed size=" << info.writeSummary.size() - << " for " << taskNameQ(taskp)); + UINFO(9, "undriven capture writeSummary computed size=" << info.writeSummary.size() << " for " + << taskNameQ(taskp)); // We are done, so set the m_info state correctly and return the vector of variables info.state = State::DONE; diff --git a/src/V3UndrivenCapture.h b/src/V3UndrivenCapture.h index 12bc4968c..3e0646c6b 100644 --- a/src/V3UndrivenCapture.h +++ b/src/V3UndrivenCapture.h @@ -19,9 +19,9 @@ // Capture task/function write summaries for multidriven checks. // Per-task/function capture info keyed by resolved AstNodeFTask* identity (FTask = function or // task). This is our 'graph' of tasks/functions. Each node has a list of direct callees and -// a list of variables written in the function body. There are methods to dedup after walking the tree. -// V3Undriven then uses the writeSummary for multidriven checks - i.e. it treats writes (side effects) -// inside subroutines as part of the caller's process. +// a list of variables written in the function body. There are methods to dedup after walking the +// tree. V3Undriven then uses the writeSummary for multidriven checks - i.e. it treats writes (side +// effects) inside subroutines as part of the caller's process. // //************************************************************************* From 3497e0f8ad48ac544e4ce5f4085e0a5fa62595f7 Mon Sep 17 00:00:00 2001 From: em2machine <92717390+em2machine@users.noreply.github.com> Date: Wed, 24 Dec 2025 08:49:02 +0100 Subject: [PATCH 31/37] sort on insert --- src/V3UndrivenCapture.cpp | 19 ++++++++++++------- src/V3UndrivenCapture.h | 5 ++++- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/V3UndrivenCapture.cpp b/src/V3UndrivenCapture.cpp index ec6e23846..cd6d84c76 100644 --- a/src/V3UndrivenCapture.cpp +++ b/src/V3UndrivenCapture.cpp @@ -101,10 +101,10 @@ void V3UndrivenCapture::sortUniqueVars(std::vector& vec) { vec.erase(std::unique(vec.begin(), vec.end()), vec.end()); } -void V3UndrivenCapture::sortUniqueFTasks(std::vector& vec) { - std::sort(vec.begin(), vec.end()); - vec.erase(std::unique(vec.begin(), vec.end()), vec.end()); -} +//void V3UndrivenCapture::sortUniqueFTasks(std::vector& vec) { +// std::sort(vec.begin(), vec.end()); +// vec.erase(std::unique(vec.begin(), vec.end()), vec.end()); +//} V3UndrivenCapture::V3UndrivenCapture(AstNetlist* netlistp) { gather(netlistp); @@ -112,7 +112,7 @@ V3UndrivenCapture::V3UndrivenCapture(AstNetlist* netlistp) { // Normalize direct lists for (auto& kv : m_info) { sortUniqueVars(kv.second.directWrites); - sortUniqueFTasks(kv.second.callees); + //sortUniqueFTasks(kv.second.callees); } // Compute summaries for all tasks @@ -194,8 +194,13 @@ void V3UndrivenCapture::noteDirectWrite(const AstNodeFTask* taskp, AstVar* varp) } void V3UndrivenCapture::noteCallEdge(const AstNodeFTask* callerp, const AstNodeFTask* calleep) { - m_info[callerp].callees.push_back(calleep); - (void)m_info[calleep]; // ensure callee entry exists + //m_info[callerp].callees.push_back(calleep); + //(void)m_info[calleep]; // ensure callee entry exists + FTaskInfo& callerInfo = m_info[callerp]; + if (callerInfo.calleesSet.insert(calleep).second) { + callerInfo.callees.push_back(calleep); + } + (void)m_info[calleep]; } void V3UndrivenCapture::debugDumpTask(const AstNodeFTask* taskp, int level) const { diff --git a/src/V3UndrivenCapture.h b/src/V3UndrivenCapture.h index 3e0646c6b..d16e9e072 100644 --- a/src/V3UndrivenCapture.h +++ b/src/V3UndrivenCapture.h @@ -55,6 +55,9 @@ public: std::vector writeSummary; // State for writeSummary computation. State state = State::UNVISITED; + // This is used to test whether weve already recorded a callee. Used to 'filter' on insert + // versus sorting at the end. + std::unordered_set calleesSet; }; // Enable writeSummary computation. If disabled, then the existing V3Undriven behaviour is @@ -74,7 +77,7 @@ private: static void sortUniqueVars(std::vector& vec); // Sort and remove duplicates from a vector of callees. The visitor can record the same callee // multiple times (multiple call sites, branches, etc). - static void sortUniqueFTasks(std::vector& vec); + //static void sortUniqueFTasks(std::vector& vec); // Collect direct writes and call edges for all tasks/functions. Run one time when // UndrivenCapture is created. This runs the visitor over the tree. From 0aa46150c06032d7846c6e9271f9e0d2a0f254b4 Mon Sep 17 00:00:00 2001 From: em2machine <92717390+em2machine@users.noreply.github.com> Date: Wed, 24 Dec 2025 11:47:15 +0100 Subject: [PATCH 32/37] removed the last sort --- src/V3UndrivenCapture.cpp | 63 ++++++++++++++++++++++++++------------- src/V3UndrivenCapture.h | 13 ++++---- 2 files changed, 50 insertions(+), 26 deletions(-) diff --git a/src/V3UndrivenCapture.cpp b/src/V3UndrivenCapture.cpp index cd6d84c76..382bfe9ea 100644 --- a/src/V3UndrivenCapture.cpp +++ b/src/V3UndrivenCapture.cpp @@ -19,7 +19,7 @@ #include "V3Error.h" #include "V3Global.h" -#include +//#include VL_DEFINE_DEBUG_FUNCTIONS; @@ -96,12 +96,7 @@ private: bool V3UndrivenCapture::enableWriteSummary = true; -void V3UndrivenCapture::sortUniqueVars(std::vector& vec) { - std::sort(vec.begin(), vec.end()); - vec.erase(std::unique(vec.begin(), vec.end()), vec.end()); -} - -//void V3UndrivenCapture::sortUniqueFTasks(std::vector& vec) { +//void V3UndrivenCapture::sortUniqueVars(std::vector& vec) { // std::sort(vec.begin(), vec.end()); // vec.erase(std::unique(vec.begin(), vec.end()), vec.end()); //} @@ -110,14 +105,21 @@ V3UndrivenCapture::V3UndrivenCapture(AstNetlist* netlistp) { gather(netlistp); // Normalize direct lists - for (auto& kv : m_info) { - sortUniqueVars(kv.second.directWrites); - //sortUniqueFTasks(kv.second.callees); - } + //for (auto& kv : m_info) { + // sortUniqueVars(kv.second.directWrites); + //} // Compute summaries for all tasks for (const auto& kv : m_info) (void)computeWriteSummary(kv.first); + // Release the filter memory + for (auto& kv : m_info) { + kv.second.calleesSet.clear(); + kv.second.calleesSet.rehash(0); + kv.second.directWritesSet.clear(); + kv.second.directWritesSet.rehash(0); + } + UINFO(9, "undriven capture stats ftasks=" << g_stats.ftasks << " varWrites=" << g_stats.varWrites << " callEdges=" << g_stats.callEdges << " uniqueTasks=" << m_info.size()); @@ -154,25 +156,42 @@ const std::vector& V3UndrivenCapture::computeWriteSummary(const AstNode << " returning directWrites size=" << info.directWrites.size()); // Cycle detected. return directWrites only to guarantee termination. if (info.writeSummary.empty()) info.writeSummary = info.directWrites; - sortUniqueVars(info.writeSummary); + //sortUniqueVars(info.writeSummary); return info.writeSummary; } info.state = State::VISITING; // Start with direct writes - info.writeSummary = info.directWrites; + //info.writeSummary = info.directWrites; // Need callees + //for (const AstNodeFTask* calleep : info.callees) { + // if (m_info.find(calleep) == m_info.end()) continue; + // const std::vector& sub = computeWriteSummary(calleep); + // info.writeSummary.insert(info.writeSummary.end(), sub.begin(), sub.end()); + //} + + // Remove duplicates and sort because grabbing all of the callees can result in duplicates + //sortUniqueVars(info.writeSummary); + + info.writeSummary.clear(); + std::unordered_set seen; + + auto addVar = [&](AstVar* v) { + if (seen.insert(v).second) info.writeSummary.push_back(v); + }; + + // Start with direct writes + for (AstVar* v : info.directWrites) addVar(v); + + // Add callee summaries for (const AstNodeFTask* calleep : info.callees) { if (m_info.find(calleep) == m_info.end()) continue; const std::vector& sub = computeWriteSummary(calleep); - info.writeSummary.insert(info.writeSummary.end(), sub.begin(), sub.end()); + for (AstVar* v : sub) addVar(v); } - // Remove duplicates and sort because grabbing all of the callees can result in duplicates - sortUniqueVars(info.writeSummary); - UINFO(9, "undriven capture writeSummary computed size=" << info.writeSummary.size() << " for " << taskNameQ(taskp)); @@ -190,16 +209,20 @@ void V3UndrivenCapture::noteDirectWrite(const AstNodeFTask* taskp, AstVar* varp) AstVar* const retVarp = VN_CAST(taskp->fvarp(), Var); if (retVarp && varp == retVarp) return; - info.directWrites.push_back(varp); + //info.directWrites.push_back(varp); + // filter out duplicates. + if (info.directWritesSet.insert(varp).second) { + info.directWrites.push_back(varp); + } } void V3UndrivenCapture::noteCallEdge(const AstNodeFTask* callerp, const AstNodeFTask* calleep) { - //m_info[callerp].callees.push_back(calleep); - //(void)m_info[calleep]; // ensure callee entry exists FTaskInfo& callerInfo = m_info[callerp]; + // prevents duplicate entries from being appended, if calleep already exists then insert will return false, and then is not inserted into the callees vector. if (callerInfo.calleesSet.insert(calleep).second) { callerInfo.callees.push_back(calleep); } + // ensure callee entry exists, if already exists then this is a no-op. unordered_map<> so cheap. (void)m_info[calleep]; } diff --git a/src/V3UndrivenCapture.h b/src/V3UndrivenCapture.h index d16e9e072..477ce0b47 100644 --- a/src/V3UndrivenCapture.h +++ b/src/V3UndrivenCapture.h @@ -33,6 +33,7 @@ #include "V3Ast.h" #include +#include #include class AstNetlist; @@ -46,9 +47,9 @@ public: enum class State : uint8_t { UNVISITED, VISITING, DONE }; struct FTaskInfo final { - // Variables written directly in this task/function body. + // Variables written directly in this task/function body (iteration order) std::vector directWrites; - // Direct resolved callees from this task/function body. + // Direct resolved callees from this task/function body (iteration order) std::vector callees; // 'Write through write' writeSummary for the given task/function. Meaning ultimately // everything that this function/task writes to. @@ -58,6 +59,9 @@ public: // This is used to test whether weve already recorded a callee. Used to 'filter' on insert // versus sorting at the end. std::unordered_set calleesSet; + // This is used to test whether weve already recorded a direct write. Used to 'filter' on insert + // versus sorting at the end. + std::unordered_set directWritesSet; }; // Enable writeSummary computation. If disabled, then the existing V3Undriven behaviour is @@ -74,10 +78,7 @@ private: // Sort and remove duplicates from a vector of variables. This is called after a task/function // write summary is computed. writeSummary can accumulate duplicates if a variable is written // in multiple tasks/functions. - static void sortUniqueVars(std::vector& vec); - // Sort and remove duplicates from a vector of callees. The visitor can record the same callee - // multiple times (multiple call sites, branches, etc). - //static void sortUniqueFTasks(std::vector& vec); + //static void sortUniqueVars(std::vector& vec); // Collect direct writes and call edges for all tasks/functions. Run one time when // UndrivenCapture is created. This runs the visitor over the tree. From b4e73cd6d8815ffb81f23a4ff1a4923391fd9aaa Mon Sep 17 00:00:00 2001 From: em2machine <92717390+em2machine@users.noreply.github.com> Date: Wed, 24 Dec 2025 11:57:20 +0100 Subject: [PATCH 33/37] cleanup comments --- src/V3UndrivenCapture.cpp | 27 +++------------------------ src/V3UndrivenCapture.h | 5 ----- 2 files changed, 3 insertions(+), 29 deletions(-) diff --git a/src/V3UndrivenCapture.cpp b/src/V3UndrivenCapture.cpp index 382bfe9ea..cf4b279a0 100644 --- a/src/V3UndrivenCapture.cpp +++ b/src/V3UndrivenCapture.cpp @@ -96,19 +96,9 @@ private: bool V3UndrivenCapture::enableWriteSummary = true; -//void V3UndrivenCapture::sortUniqueVars(std::vector& vec) { -// std::sort(vec.begin(), vec.end()); -// vec.erase(std::unique(vec.begin(), vec.end()), vec.end()); -//} - V3UndrivenCapture::V3UndrivenCapture(AstNetlist* netlistp) { gather(netlistp); - // Normalize direct lists - //for (auto& kv : m_info) { - // sortUniqueVars(kv.second.directWrites); - //} - // Compute summaries for all tasks for (const auto& kv : m_info) (void)computeWriteSummary(kv.first); @@ -156,28 +146,17 @@ const std::vector& V3UndrivenCapture::computeWriteSummary(const AstNode << " returning directWrites size=" << info.directWrites.size()); // Cycle detected. return directWrites only to guarantee termination. if (info.writeSummary.empty()) info.writeSummary = info.directWrites; - //sortUniqueVars(info.writeSummary); return info.writeSummary; } info.state = State::VISITING; - // Start with direct writes - //info.writeSummary = info.directWrites; - - // Need callees - //for (const AstNodeFTask* calleep : info.callees) { - // if (m_info.find(calleep) == m_info.end()) continue; - // const std::vector& sub = computeWriteSummary(calleep); - // info.writeSummary.insert(info.writeSummary.end(), sub.begin(), sub.end()); - //} - - // Remove duplicates and sort because grabbing all of the callees can result in duplicates - //sortUniqueVars(info.writeSummary); - info.writeSummary.clear(); + + // Prevent duplicates across all sources that can contribute to a write summary (direct writes and call chains) std::unordered_set seen; + // Simple lambda for filtering duplicates auto addVar = [&](AstVar* v) { if (seen.insert(v).second) info.writeSummary.push_back(v); }; diff --git a/src/V3UndrivenCapture.h b/src/V3UndrivenCapture.h index 477ce0b47..7815eca61 100644 --- a/src/V3UndrivenCapture.h +++ b/src/V3UndrivenCapture.h @@ -75,11 +75,6 @@ private: // otherwise this could explode. std::unordered_map m_info; - // Sort and remove duplicates from a vector of variables. This is called after a task/function - // write summary is computed. writeSummary can accumulate duplicates if a variable is written - // in multiple tasks/functions. - //static void sortUniqueVars(std::vector& vec); - // Collect direct writes and call edges for all tasks/functions. Run one time when // UndrivenCapture is created. This runs the visitor over the tree. void gather(AstNetlist* netlistp); From d2146f0e5604ea9bbfb71142f2799adb5faeaf15 Mon Sep 17 00:00:00 2001 From: em2machine <92717390+em2machine@users.noreply.github.com> Date: Wed, 24 Dec 2025 13:48:15 +0100 Subject: [PATCH 34/37] removed enable --- src/V3Undriven.cpp | 30 +++++++++++++----------------- src/V3UndrivenCapture.cpp | 11 +++-------- src/V3UndrivenCapture.h | 4 ---- 3 files changed, 16 insertions(+), 29 deletions(-) diff --git a/src/V3Undriven.cpp b/src/V3Undriven.cpp index 4a54cd54a..0dfd6c353 100644 --- a/src/V3Undriven.cpp +++ b/src/V3Undriven.cpp @@ -315,7 +315,6 @@ class UndrivenVisitor final : public VNVisitorConst { const AstAlways* m_alwaysCombp = nullptr; // Current always if combo, otherwise nullptr V3UndrivenCapture* const m_capturep = nullptr; // Capture object. 'nullptr' if disabled. - const bool m_enableWriteSummary = false; // Enable writeSummary computation plumbing // METHODS @@ -437,7 +436,7 @@ class UndrivenVisitor final : public VNVisitorConst { // 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 + if (m_taskp && !m_alwaysp && !m_inContAssign && !m_inInitialStatic && !m_inBBox && !m_taskp->dpiExport()) { AstVar* const retVarp = VN_CAST(m_taskp->fvarp(), Var); if (!retVarp || nodep->varp() != retVarp) return; @@ -455,12 +454,13 @@ class UndrivenVisitor final : public VNVisitorConst { if (entryp->isDrivenWhole() && !m_inBBox && !VN_IS(nodep, VarXRef) && !VN_IS(nodep->dtypep()->skipRefp(), UnpackArrayDType) && nodep->fileline() != entryp->getNodeFileLinep() && !entryp->isUnderGen() - && (entryp->getNodep() || (m_enableWriteSummary && entryp->callNodep()))) { + && (entryp->getNodep() || + entryp->callNodep())) { const AstNode* const otherWritep - = entryp->getNodep() - ? static_cast(entryp->getNodep()) - : (m_enableWriteSummary ? entryp->callNodep() : nullptr); + = entryp->getNodep() ? + static_cast(entryp->getNodep()) : + entryp->callNodep(); if (m_alwaysCombp && (!entryp->isDrivenAlwaysCombWhole() @@ -557,7 +557,7 @@ class UndrivenVisitor final : public VNVisitorConst { iterateChildrenConst(nodep); - if (!m_enableWriteSummary || !m_capturep) return; + if (!m_capturep) return; // 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 @@ -612,10 +612,9 @@ class UndrivenVisitor final : public VNVisitorConst { public: // CONSTRUCTORS - explicit UndrivenVisitor(AstNetlist* nodep, V3UndrivenCapture* capturep, - bool enableWriteSummary) + explicit UndrivenVisitor(AstNetlist* nodep, V3UndrivenCapture* capturep) : m_capturep{capturep} - , m_enableWriteSummary{enableWriteSummary} { + { iterateConst(nodep); } @@ -632,12 +631,9 @@ public: void V3Undriven::undrivenAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ":"); - const bool enable = V3UndrivenCapture::enableWriteSummary; - if (enable) { - V3UndrivenCapture capture{nodep}; - { UndrivenVisitor{nodep, &capture, enable}; } - } else { - { UndrivenVisitor{nodep, nullptr, enable}; } - } + + V3UndrivenCapture capture{nodep}; + UndrivenVisitor{nodep, &capture}; + if (v3Global.opt.stats()) V3Stats::statsStage("undriven"); } diff --git a/src/V3UndrivenCapture.cpp b/src/V3UndrivenCapture.cpp index cf4b279a0..991175121 100644 --- a/src/V3UndrivenCapture.cpp +++ b/src/V3UndrivenCapture.cpp @@ -19,8 +19,6 @@ #include "V3Error.h" #include "V3Global.h" -//#include - VL_DEFINE_DEBUG_FUNCTIONS; namespace { @@ -94,8 +92,6 @@ private: } // namespace -bool V3UndrivenCapture::enableWriteSummary = true; - V3UndrivenCapture::V3UndrivenCapture(AstNetlist* netlistp) { gather(netlistp); @@ -188,8 +184,7 @@ void V3UndrivenCapture::noteDirectWrite(const AstNodeFTask* taskp, AstVar* varp) AstVar* const retVarp = VN_CAST(taskp->fvarp(), Var); if (retVarp && varp == retVarp) return; - //info.directWrites.push_back(varp); - // filter out duplicates. + // Filter out duplicates. if (info.directWritesSet.insert(varp).second) { info.directWrites.push_back(varp); } @@ -197,11 +192,11 @@ void V3UndrivenCapture::noteDirectWrite(const AstNodeFTask* taskp, AstVar* varp) void V3UndrivenCapture::noteCallEdge(const AstNodeFTask* callerp, const AstNodeFTask* calleep) { FTaskInfo& callerInfo = m_info[callerp]; - // prevents duplicate entries from being appended, if calleep already exists then insert will return false, and then is not inserted into the callees vector. + // Prevents duplicate entries from being appended, if calleep already exists then insert will return false, and then is not inserted into the callees vector. if (callerInfo.calleesSet.insert(calleep).second) { callerInfo.callees.push_back(calleep); } - // ensure callee entry exists, if already exists then this is a no-op. unordered_map<> so cheap. + // Ensure callee entry exists, if already exists then this is a no-op. unordered_map<> so cheap. (void)m_info[calleep]; } diff --git a/src/V3UndrivenCapture.h b/src/V3UndrivenCapture.h index 7815eca61..573ab4f70 100644 --- a/src/V3UndrivenCapture.h +++ b/src/V3UndrivenCapture.h @@ -64,10 +64,6 @@ public: std::unordered_set directWritesSet; }; - // Enable writeSummary computation. If disabled, then the existing V3Undriven behaviour is - // used. - static bool enableWriteSummary; - private: // Per-task/function capture info keyed by resolved AstNodeFTask* identity (FTask = function or // task). This is our 'graph' of tasks/functions. Each node has a list of direct callees and From 8e55fa1f05d39a54fd95c034c3474c1fbc1cec35 Mon Sep 17 00:00:00 2001 From: em2machine <92717390+em2machine@users.noreply.github.com> Date: Wed, 24 Dec 2025 13:57:35 +0100 Subject: [PATCH 35/37] coalesced class tests into a single test --- ...riven_class0.py => t_multidriven_class.py} | 0 test_regress/t/t_multidriven_class.v | 284 ++++++++++++++++++ test_regress/t/t_multidriven_class0.v | 72 ----- test_regress/t/t_multidriven_class1.py | 18 -- test_regress/t/t_multidriven_class1.v | 72 ----- test_regress/t/t_multidriven_class2.py | 18 -- test_regress/t/t_multidriven_class2.v | 71 ----- test_regress/t/t_multidriven_class3.py | 18 -- test_regress/t/t_multidriven_class3.v | 66 ---- test_regress/t/t_multidriven_class4.py | 18 -- test_regress/t/t_multidriven_class4.v | 79 ----- test_regress/t/t_multidriven_classf0.py | 18 -- test_regress/t/t_multidriven_classf0.v | 69 ----- test_regress/t/t_multidriven_classf1.py | 18 -- test_regress/t/t_multidriven_classf1.v | 66 ---- 15 files changed, 284 insertions(+), 603 deletions(-) rename test_regress/t/{t_multidriven_class0.py => t_multidriven_class.py} (100%) mode change 100755 => 100644 create mode 100644 test_regress/t/t_multidriven_class.v delete mode 100644 test_regress/t/t_multidriven_class0.v delete mode 100755 test_regress/t/t_multidriven_class1.py delete mode 100644 test_regress/t/t_multidriven_class1.v delete mode 100755 test_regress/t/t_multidriven_class2.py delete mode 100644 test_regress/t/t_multidriven_class2.v delete mode 100755 test_regress/t/t_multidriven_class3.py delete mode 100644 test_regress/t/t_multidriven_class3.v delete mode 100755 test_regress/t/t_multidriven_class4.py delete mode 100644 test_regress/t/t_multidriven_class4.v delete mode 100755 test_regress/t/t_multidriven_classf0.py delete mode 100644 test_regress/t/t_multidriven_classf0.v delete mode 100755 test_regress/t/t_multidriven_classf1.py delete mode 100644 test_regress/t/t_multidriven_classf1.v diff --git a/test_regress/t/t_multidriven_class0.py b/test_regress/t/t_multidriven_class.py old mode 100755 new mode 100644 similarity index 100% rename from test_regress/t/t_multidriven_class0.py rename to test_regress/t/t_multidriven_class.py diff --git a/test_regress/t/t_multidriven_class.v b/test_regress/t/t_multidriven_class.v new file mode 100644 index 000000000..9e93c55b6 --- /dev/null +++ b/test_regress/t/t_multidriven_class.v @@ -0,0 +1,284 @@ +// 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 + +// Consolidated class-based task/function multidriven tests +// (formerly t_multidriven_class{0,1,2,3,4,f0,f1}.v) + +// 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 + +//---------------------------------------------------------------------- +// class0: class task writes through ref argument (direct assignment + class task in same always_comb) + +class C0; + task automatic set1(ref logic q); + q = 1'b1; + endtask + task automatic set0(ref logic q); + q = 1'b0; + endtask +endclass + +module class0 #()( + input logic sel + ,output logic val +); + + logic l0; + C0 c; + + initial c = new; + + always_comb begin + l0 = 1'b0; + if (sel) begin + c.set1(l0); + end + end + + assign val = l0; + +endmodule + +//---------------------------------------------------------------------- +// class1: class task chain - nested method calls write through ref in same always_comb + +class C1; + task automatic inner(inout logic q); + q = 1'b1; + endtask + task automatic outer(inout logic q); + inner(q); + endtask +endclass + +module class1 #()( + input logic sel + ,output logic val +); + + logic l0; + C1 c; + + initial c = new; + + always_comb begin + l0 = 1'b0; + if (sel) begin + c.outer(l0); + end + end + + assign val = l0; + +endmodule + +//---------------------------------------------------------------------- +// class2: class handle passed through module port - class method writes through ref + +class C2; + task automatic set1(ref logic q); + q = 1'b1; + endtask +endclass + +module class2 #()( + input logic sel + ,output logic val + ,C2 c +); + + logic l0; + + always_comb begin + l0 = 1'b0; + if (sel) begin + c.set1(l0); + end + end + + assign val = l0; + +endmodule + +//---------------------------------------------------------------------- +// class3: static class task - call via class scope, writes through ref in same always_comb + +class C3; + static task automatic set1(ref logic q); + q = 1'b1; + endtask +endclass + +module class3 #()( + input logic sel + ,output logic val +); + + logic l0; + + always_comb begin + l0 = 1'b0; + if (sel) begin + C3::set1(l0); + end + end + + assign val = l0; + +endmodule + +//---------------------------------------------------------------------- +// class4: class composition - one class calls another task, ultimately writes through ref + +class C4Inner; + task automatic set1(ref logic q); + q = 1'b1; + endtask +endclass + +class C4Outer; + C4Inner inner; + function new(); + inner = new; + endfunction + task automatic set1(ref logic q); + inner.set1(q); + endtask +endclass + +module class4 #()( + input logic sel + ,output logic val +); + + logic l0; + C4Outer c; + + initial c = new; + + always_comb begin + l0 = 1'b0; + if (sel) begin + c.set1(l0); + end + end + + assign val = l0; + +endmodule + +//---------------------------------------------------------------------- +// classf0: class function returns value - always_comb writes var directly + via class function call + +class Cf0; + function automatic logic ret1(); + return 1'b1; + endfunction +endclass + +module classf0 #()( + input logic sel + ,output logic val +); + + logic l0; + Cf0 c; + + initial c = new; + + always_comb begin + l0 = 1'b0; + if (sel) begin + l0 = c.ret1(); + end + end + + assign val = l0; + +endmodule + +//---------------------------------------------------------------------- +// classf1: static class function returns value - always_comb uses class scope call + +class Cf1; + static function automatic logic ret1(); + return 1'b1; + endfunction +endclass + +module classf1 #()( + input logic sel + ,output logic val +); + + logic l0; + + always_comb begin + l0 = 1'b0; + if (sel) begin + l0 = Cf1::ret1(); + end + end + + assign val = l0; + +endmodule + +//---------------------------------------------------------------------- +// Shared TB + +module m_tb#()(); + + logic sel; + + logic val0, val1, val2, val3, val4, valf0, valf1; + + C2 c2; + initial c2 = new; + + class0 u0(.sel(sel), .val(val0)); + class1 u1(.sel(sel), .val(val1)); + class2 u2(.sel(sel), .val(val2), .c(c2)); + class3 u3(.sel(sel), .val(val3)); + class4 u4(.sel(sel), .val(val4)); + classf0 uf0(.sel(sel), .val(valf0)); + classf1 uf1(.sel(sel), .val(valf1)); + + task automatic check_all(input logic exp); + `checkd(val0, exp); + `checkd(val1, exp); + `checkd(val2, exp); + `checkd(val3, exp); + `checkd(val4, exp); + `checkd(valf0, exp); + `checkd(valf1, exp); + endtask + + initial begin + #1; + sel = 'b0; + #1; + check_all(1'b0); + + sel = 'b1; + #1; + check_all(1'b1); + + sel = 'b0; + #1; + check_all(1'b0); + end + + initial begin + #5; + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule diff --git a/test_regress/t/t_multidriven_class0.v b/test_regress/t/t_multidriven_class0.v deleted file mode 100644 index 6df099a7e..000000000 --- a/test_regress/t/t_multidriven_class0.v +++ /dev/null @@ -1,72 +0,0 @@ -// 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 - -// class task writes through ref argument (direct assignment + class task in same always_comb) - -// 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 - -class C; - task automatic set1(ref logic q); - q = 1'b1; - endtask - task automatic set0(ref logic q); - q = 1'b0; - endtask -endclass - -module mod #()( - input logic sel - ,output logic val -); - - logic l0; - C c; - - initial c = new; - - always_comb begin - l0 = 1'b0; - if (sel) begin - c.set1(l0); - end - end - - assign val = l0; - -endmodule - -module m_tb#()(); - - logic sel, val; - - mod m( - .sel(sel) - ,.val(val) - ); - - initial begin - #1; - sel = 'b0; - #1; - `checkd(val, 1'b0); - sel = 'b1; - #1; - `checkd(val, 1'b1); - sel = 'b0; - #1; - `checkd(val, 1'b0); - end - - initial begin - #5; - $write("*-* All Finished *-*\n"); - $finish; - end - -endmodule diff --git a/test_regress/t/t_multidriven_class1.py b/test_regress/t/t_multidriven_class1.py deleted file mode 100755 index c6e56559a..000000000 --- a/test_regress/t/t_multidriven_class1.py +++ /dev/null @@ -1,18 +0,0 @@ -#!/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_class1.v b/test_regress/t/t_multidriven_class1.v deleted file mode 100644 index 773f051e9..000000000 --- a/test_regress/t/t_multidriven_class1.v +++ /dev/null @@ -1,72 +0,0 @@ -// 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 - -// class task chain - nested method calls write through ref in same always_comb - -// 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 - -class C; - task automatic inner(inout logic q); - q = 1'b1; - endtask - task automatic outer(inout logic q); - inner(q); - endtask -endclass - -module mod #()( - input logic sel - ,output logic val -); - - logic l0; - C c; - - initial c = new; - - always_comb begin - l0 = 1'b0; - if (sel) begin - c.outer(l0); - end - end - - assign val = l0; - -endmodule - -module m_tb#()(); - - logic sel, val; - - mod m( - .sel(sel) - ,.val(val) - ); - - initial begin - #1; - sel = 'b0; - #1; - `checkd(val, 1'b0); - sel = 'b1; - #1; - `checkd(val, 1'b1); - sel = 'b0; - #1; - `checkd(val, 1'b0); - end - - initial begin - #5; - $write("*-* All Finished *-*\n"); - $finish; - end - -endmodule diff --git a/test_regress/t/t_multidriven_class2.py b/test_regress/t/t_multidriven_class2.py deleted file mode 100755 index c6e56559a..000000000 --- a/test_regress/t/t_multidriven_class2.py +++ /dev/null @@ -1,18 +0,0 @@ -#!/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_class2.v b/test_regress/t/t_multidriven_class2.v deleted file mode 100644 index dff4ec2cc..000000000 --- a/test_regress/t/t_multidriven_class2.v +++ /dev/null @@ -1,71 +0,0 @@ -// 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 - -// class handle passed through module port - class method writes through ref - -// 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 - -class C; - task automatic set1(ref logic q); - q = 1'b1; - endtask -endclass - -module mod #()( - input logic sel - ,output logic val - ,C c -); - - logic l0; - - always_comb begin - l0 = 1'b0; - if (sel) begin - c.set1(l0); - end - end - - assign val = l0; - -endmodule - -module m_tb#()(); - - logic sel, val; - C c; - - initial c = new; - - mod m( - .sel(sel) - ,.val(val) - ,.c(c) - ); - - initial begin - #1; - sel = 'b0; - #1; - `checkd(val, 1'b0); - sel = 'b1; - #1; - `checkd(val, 1'b1); - sel = 'b0; - #1; - `checkd(val, 1'b0); - end - - initial begin - #5; - $write("*-* All Finished *-*\n"); - $finish; - end - -endmodule diff --git a/test_regress/t/t_multidriven_class3.py b/test_regress/t/t_multidriven_class3.py deleted file mode 100755 index c6e56559a..000000000 --- a/test_regress/t/t_multidriven_class3.py +++ /dev/null @@ -1,18 +0,0 @@ -#!/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_class3.v b/test_regress/t/t_multidriven_class3.v deleted file mode 100644 index 1c0c4eedd..000000000 --- a/test_regress/t/t_multidriven_class3.v +++ /dev/null @@ -1,66 +0,0 @@ -// 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 - -// static class task - call via class scope, writes through ref in same always_comb - -// 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 - -class C; - static task automatic set1(ref logic q); - q = 1'b1; - endtask -endclass - -module mod #()( - input logic sel - ,output logic val -); - - logic l0; - - always_comb begin - l0 = 1'b0; - if (sel) begin - C::set1(l0); - end - end - - assign val = l0; - -endmodule - -module m_tb#()(); - - logic sel, val; - - mod m( - .sel(sel) - ,.val(val) - ); - - initial begin - #1; - sel = 'b0; - #1; - `checkd(val, 1'b0); - sel = 'b1; - #1; - `checkd(val, 1'b1); - sel = 'b0; - #1; - `checkd(val, 1'b0); - end - - initial begin - #5; - $write("*-* All Finished *-*\n"); - $finish; - end - -endmodule diff --git a/test_regress/t/t_multidriven_class4.py b/test_regress/t/t_multidriven_class4.py deleted file mode 100755 index c6e56559a..000000000 --- a/test_regress/t/t_multidriven_class4.py +++ /dev/null @@ -1,18 +0,0 @@ -#!/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_class4.v b/test_regress/t/t_multidriven_class4.v deleted file mode 100644 index 41c91c415..000000000 --- a/test_regress/t/t_multidriven_class4.v +++ /dev/null @@ -1,79 +0,0 @@ -// 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 - -// class composition - one class calls another task, ultimately writes through ref - -// 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 - -class CInner; - task automatic set1(ref logic q); - q = 1'b1; - endtask -endclass - -class COuter; - CInner inner; - function new(); - inner = new; - endfunction - task automatic set1(ref logic q); - inner.set1(q); - endtask -endclass - -module mod #()( - input logic sel - ,output logic val -); - - logic l0; - COuter c; - - initial c = new; - - always_comb begin - l0 = 1'b0; - if (sel) begin - c.set1(l0); - end - end - - assign val = l0; - -endmodule - -module m_tb#()(); - - logic sel, val; - - mod m( - .sel(sel) - ,.val(val) - ); - - initial begin - #1; - sel = 'b0; - #1; - `checkd(val, 1'b0); - sel = 'b1; - #1; - `checkd(val, 1'b1); - sel = 'b0; - #1; - `checkd(val, 1'b0); - end - - initial begin - #5; - $write("*-* All Finished *-*\n"); - $finish; - end - -endmodule diff --git a/test_regress/t/t_multidriven_classf0.py b/test_regress/t/t_multidriven_classf0.py deleted file mode 100755 index c6e56559a..000000000 --- a/test_regress/t/t_multidriven_classf0.py +++ /dev/null @@ -1,18 +0,0 @@ -#!/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_classf0.v b/test_regress/t/t_multidriven_classf0.v deleted file mode 100644 index c5c996887..000000000 --- a/test_regress/t/t_multidriven_classf0.v +++ /dev/null @@ -1,69 +0,0 @@ -// 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 - -// class function returns value - always_comb writes var directly + via class function call - -// 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 - -class C; - function automatic logic ret1(); - return 1'b1; - endfunction -endclass - -module mod #()( - input logic sel - ,output logic val -); - - logic l0; - C c; - - initial c = new; - - always_comb begin - l0 = 1'b0; - if (sel) begin - l0 = c.ret1(); - end - end - - assign val = l0; - -endmodule - -module m_tb#()(); - - logic sel, val; - - mod m( - .sel(sel) - ,.val(val) - ); - - initial begin - #1; - sel = 'b0; - #1; - `checkd(val, 1'b0); - sel = 'b1; - #1; - `checkd(val, 1'b1); - sel = 'b0; - #1; - `checkd(val, 1'b0); - end - - initial begin - #5; - $write("*-* All Finished *-*\n"); - $finish; - end - -endmodule diff --git a/test_regress/t/t_multidriven_classf1.py b/test_regress/t/t_multidriven_classf1.py deleted file mode 100755 index c6e56559a..000000000 --- a/test_regress/t/t_multidriven_classf1.py +++ /dev/null @@ -1,18 +0,0 @@ -#!/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_classf1.v b/test_regress/t/t_multidriven_classf1.v deleted file mode 100644 index 9b8efd46f..000000000 --- a/test_regress/t/t_multidriven_classf1.v +++ /dev/null @@ -1,66 +0,0 @@ -// 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 - -// static class function returns value - always_comb uses class scope call - -// 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 - -class C; - static function automatic logic ret1(); - return 1'b1; - endfunction -endclass - -module mod #()( - input logic sel - ,output logic val -); - - logic l0; - - always_comb begin - l0 = 1'b0; - if (sel) begin - l0 = C::ret1(); - end - end - - assign val = l0; - -endmodule - -module m_tb#()(); - - logic sel, val; - - mod m( - .sel(sel) - ,.val(val) - ); - - initial begin - #1; - sel = 'b0; - #1; - `checkd(val, 1'b0); - sel = 'b1; - #1; - `checkd(val, 1'b1); - sel = 'b0; - #1; - `checkd(val, 1'b0); - end - - initial begin - #5; - $write("*-* All Finished *-*\n"); - $finish; - end - -endmodule From e3f7d0fb1aac24886c7230018037b7ad96793f03 Mon Sep 17 00:00:00 2001 From: em2machine <92717390+em2machine@users.noreply.github.com> Date: Wed, 24 Dec 2025 14:01:44 +0100 Subject: [PATCH 36/37] aggregated iface tests into single test --- ...riven_iface0.py => t_multidriven_iface.py} | 0 test_regress/t/t_multidriven_iface.v | 235 ++++++++++++++++++ test_regress/t/t_multidriven_iface0.v | 68 ----- test_regress/t/t_multidriven_iface1.py | 18 -- test_regress/t/t_multidriven_iface1.v | 68 ----- test_regress/t/t_multidriven_iface2.py | 18 -- test_regress/t/t_multidriven_iface2.v | 69 ----- test_regress/t/t_multidriven_iface3.py | 18 -- test_regress/t/t_multidriven_iface3.v | 73 ------ test_regress/t/t_multidriven_iface4.py | 18 -- test_regress/t/t_multidriven_iface4.v | 69 ----- test_regress/t/t_multidriven_iface5.py | 18 -- test_regress/t/t_multidriven_iface5.v | 69 ----- test_regress/t/t_multidriven_iface6.py | 18 -- test_regress/t/t_multidriven_iface6.v | 68 ----- 15 files changed, 235 insertions(+), 592 deletions(-) rename test_regress/t/{t_multidriven_iface0.py => t_multidriven_iface.py} (100%) mode change 100755 => 100644 create mode 100644 test_regress/t/t_multidriven_iface.v delete mode 100644 test_regress/t/t_multidriven_iface0.v delete mode 100755 test_regress/t/t_multidriven_iface1.py delete mode 100644 test_regress/t/t_multidriven_iface1.v delete mode 100755 test_regress/t/t_multidriven_iface2.py delete mode 100644 test_regress/t/t_multidriven_iface2.v delete mode 100755 test_regress/t/t_multidriven_iface3.py delete mode 100644 test_regress/t/t_multidriven_iface3.v delete mode 100755 test_regress/t/t_multidriven_iface4.py delete mode 100644 test_regress/t/t_multidriven_iface4.v delete mode 100755 test_regress/t/t_multidriven_iface5.py delete mode 100644 test_regress/t/t_multidriven_iface5.v delete mode 100755 test_regress/t/t_multidriven_iface6.py delete mode 100644 test_regress/t/t_multidriven_iface6.v diff --git a/test_regress/t/t_multidriven_iface0.py b/test_regress/t/t_multidriven_iface.py old mode 100755 new mode 100644 similarity index 100% rename from test_regress/t/t_multidriven_iface0.py rename to test_regress/t/t_multidriven_iface.py diff --git a/test_regress/t/t_multidriven_iface.v b/test_regress/t/t_multidriven_iface.v new file mode 100644 index 000000000..4a028ac9f --- /dev/null +++ b/test_regress/t/t_multidriven_iface.v @@ -0,0 +1,235 @@ +// 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 + +// Consolidated interface-based multidriven tests +// (formerly t_multidriven_iface{0,1,2,3,4,5,6}.v) + +// 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 + +//---------------------------------------------------------------------- +// iface0: direct assignment to interface signal + interface task assign in same process + +interface my_if0; + logic l0; + task set_l0_1(); l0 = 1'b1; endtask + task set_l0_0(); l0 = 1'b0; endtask +endinterface + +module iface0 #()( + input logic sel, + output logic val +); + my_if0 if0(); + always_comb begin + if0.l0 = 1'b0; + if (sel) begin + if0.set_l0_1(); + end + end + assign val = if0.l0; +endmodule + +//---------------------------------------------------------------------- +// iface1: interface task chain - nested calls write interface signal in same always_comb + +interface my_if1; + logic l0; + task set_l0_1_inner(); l0 = 1'b1; endtask + task set_l0_1_outer(); set_l0_1_inner(); endtask +endinterface + +module iface1 #()( + input logic sel, + output logic val +); + my_if1 if0(); + always_comb begin + if0.l0 = 1'b0; + if (sel) begin + if0.set_l0_1_outer(); + end + end + assign val = if0.l0; +endmodule + +//---------------------------------------------------------------------- +// iface2: interface passed through module port - direct assign + task call in same always_comb + +interface my_if2; + logic l0; + task set_l0_1(); l0 = 1'b1; endtask + task set_l0_0(); l0 = 1'b0; endtask +endinterface + +module iface2 #()( + input logic sel, + output logic val, + my_if2 ifp +); + always_comb begin + ifp.l0 = 1'b0; + if (sel) begin + ifp.set_l0_1(); + end + end + assign val = ifp.l0; +endmodule + +//---------------------------------------------------------------------- +// iface3: interface modport + task import - write interface signal in same always_comb + +interface my_if3; + logic l0; + task set_l0_1(); l0 = 1'b1; endtask + modport mp ( + output l0, + import set_l0_1 + ); +endinterface + +module iface3 #()( + input logic sel, + output logic val, + my_if3.mp ifp +); + always_comb begin + ifp.l0 = 1'b0; + if (sel) begin + ifp.set_l0_1(); + end + end + assign val = ifp.l0; +endmodule + +//---------------------------------------------------------------------- +// iface4: interface task writes through output formal - actual is interface member + +interface my_if4; + logic l0; + task automatic set_any(output logic q); + q = 1'b1; + endtask +endinterface + +module iface4 #()( + input logic sel, + output logic val +); + my_if4 if0(); + always_comb begin + if0.l0 = 1'b0; + if (sel) begin + if0.set_any(if0.l0); + end + end + assign val = if0.l0; +endmodule + +//---------------------------------------------------------------------- +// iface5: nested interface test - direct assignment + nested interface task call in same always_comb + +interface leaf_if5; + logic l0; + task set1(); l0 = 1'b1; endtask +endinterface + +interface top_if5; + leaf_if5 sub(); +endinterface + +module iface5 #()( + input logic sel, + output logic val +); + top_if5 if0(); + always_comb begin + if0.sub.l0 = 1'b0; + if (sel) begin + if0.sub.set1(); + end + end + assign val = if0.sub.l0; +endmodule + +//---------------------------------------------------------------------- +// iface6: nested interface aggregator - two nested interfaces, only one driven + +interface chan_if6; + logic b0; + task set1(); b0 = 1'b1; endtask +endinterface + +interface agg_if6; + chan_if6 tlb(); + chan_if6 ic(); +endinterface + +module iface6 #()( + input logic sel, + output logic val +); + agg_if6 a(); + always_comb begin + a.tlb.b0 = 1'b0; + if (sel) a.tlb.set1(); + end + assign val = a.tlb.b0; +endmodule + +//---------------------------------------------------------------------- +// Shared TB + +module m_tb#()(); + + logic sel; + logic val0, val1, val2, val3, val4, val5, val6; + + my_if2 if2(); + my_if3 if3(); + + iface0 u0(.sel(sel), .val(val0)); + iface1 u1(.sel(sel), .val(val1)); + iface2 u2(.sel(sel), .val(val2), .ifp(if2)); + iface3 u3(.sel(sel), .val(val3), .ifp(if3)); + iface4 u4(.sel(sel), .val(val4)); + iface5 u5(.sel(sel), .val(val5)); + iface6 u6(.sel(sel), .val(val6)); + + task automatic check_all(input logic exp); + `checkd(val0, exp); + `checkd(val1, exp); + `checkd(val2, exp); + `checkd(val3, exp); + `checkd(val4, exp); + `checkd(val5, exp); + `checkd(val6, exp); + endtask + + initial begin + #1; + sel = 'b0; + #1; + check_all(1'b0); + + sel = 'b1; + #1; + check_all(1'b1); + + sel = 'b0; + #1; + check_all(1'b0); + end + + initial begin + #5; + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule diff --git a/test_regress/t/t_multidriven_iface0.v b/test_regress/t/t_multidriven_iface0.v deleted file mode 100644 index e54160162..000000000 --- a/test_regress/t/t_multidriven_iface0.v +++ /dev/null @@ -1,68 +0,0 @@ -// 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 - -// multidriven interface test - direct assignment to interface signal and task assign in same process - -// 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 - -interface my_if; - logic l0; - - task set_l0_1(); l0 = 1'b1; endtask - task set_l0_0(); l0 = 1'b0; endtask -endinterface - -module mod #()( - input logic sel - ,output logic val -); - - my_if if0(); - - always_comb begin - if0.l0 = 1'b0; - - if(sel) begin - if0.set_l0_1(); - end - end - - assign val = if0.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_iface1.py b/test_regress/t/t_multidriven_iface1.py deleted file mode 100755 index c6e56559a..000000000 --- a/test_regress/t/t_multidriven_iface1.py +++ /dev/null @@ -1,18 +0,0 @@ -#!/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_iface1.v b/test_regress/t/t_multidriven_iface1.v deleted file mode 100644 index 7ff3bd263..000000000 --- a/test_regress/t/t_multidriven_iface1.v +++ /dev/null @@ -1,68 +0,0 @@ -// 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 - -// interface task chain - nested task calls write interface signal in same always_comb - -// 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 - -interface my_if; - logic l0; - - task set_l0_1_inner(); l0 = 1'b1; endtask - task set_l0_1_outer(); set_l0_1_inner(); endtask -endinterface - -module mod #()( - input logic sel - ,output logic val -); - - my_if if0(); - - always_comb begin - if0.l0 = 1'b0; - - if (sel) begin - if0.set_l0_1_outer(); - end - end - - assign val = if0.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_iface2.py b/test_regress/t/t_multidriven_iface2.py deleted file mode 100755 index c6e56559a..000000000 --- a/test_regress/t/t_multidriven_iface2.py +++ /dev/null @@ -1,18 +0,0 @@ -#!/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_iface2.v b/test_regress/t/t_multidriven_iface2.v deleted file mode 100644 index c0dd55c33..000000000 --- a/test_regress/t/t_multidriven_iface2.v +++ /dev/null @@ -1,69 +0,0 @@ -// 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 - -// interface passed through module port - direct assign + task call in same always_comb - -// 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 - -interface my_if; - logic l0; - - task set_l0_1(); l0 = 1'b1; endtask - task set_l0_0(); l0 = 1'b0; endtask -endinterface - -module mod #()( - input logic sel - ,output logic val - ,my_if ifp -); - - always_comb begin - ifp.l0 = 1'b0; - - if (sel) begin - ifp.set_l0_1(); - end - end - - assign val = ifp.l0; - -endmodule - -module m_tb#()(); - - logic sel, val; - my_if if0(); - - mod m( - .sel(sel) - ,.val(val) - ,.ifp(if0) - ); - - 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_iface3.py b/test_regress/t/t_multidriven_iface3.py deleted file mode 100755 index c6e56559a..000000000 --- a/test_regress/t/t_multidriven_iface3.py +++ /dev/null @@ -1,18 +0,0 @@ -#!/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_iface3.v b/test_regress/t/t_multidriven_iface3.v deleted file mode 100644 index 597ab6ed9..000000000 --- a/test_regress/t/t_multidriven_iface3.v +++ /dev/null @@ -1,73 +0,0 @@ -// 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 - -// interface modport + task import - write interface signal in same always_comb - -// 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 - -interface my_if; - logic l0; - - task set_l0_1(); l0 = 1'b1; endtask - - modport mp ( - output l0, - import set_l0_1 - ); -endinterface - -module mod #()( - input logic sel - ,output logic val - ,my_if.mp ifp -); - - always_comb begin - ifp.l0 = 1'b0; - - if (sel) begin - ifp.set_l0_1(); - end - end - - assign val = ifp.l0; - -endmodule - -module m_tb#()(); - - logic sel, val; - my_if if0(); - - mod m( - .sel(sel) - ,.val(val) - ,.ifp(if0) - ); - - 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_iface4.py b/test_regress/t/t_multidriven_iface4.py deleted file mode 100755 index c6e56559a..000000000 --- a/test_regress/t/t_multidriven_iface4.py +++ /dev/null @@ -1,18 +0,0 @@ -#!/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_iface4.v b/test_regress/t/t_multidriven_iface4.v deleted file mode 100644 index af9d93589..000000000 --- a/test_regress/t/t_multidriven_iface4.v +++ /dev/null @@ -1,69 +0,0 @@ -// 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 - -// interface task writes through output formal - actual is interface member - -// 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 - -interface my_if; - logic l0; - - task automatic set_any(output logic q); - q = 1'b1; - endtask -endinterface - -module mod #()( - input logic sel - ,output logic val -); - - my_if if0(); - - always_comb begin - if0.l0 = 1'b0; - - if (sel) begin - if0.set_any(if0.l0); - end - end - - assign val = if0.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_iface5.py b/test_regress/t/t_multidriven_iface5.py deleted file mode 100755 index c6e56559a..000000000 --- a/test_regress/t/t_multidriven_iface5.py +++ /dev/null @@ -1,18 +0,0 @@ -#!/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_iface5.v b/test_regress/t/t_multidriven_iface5.v deleted file mode 100644 index 0c64246f9..000000000 --- a/test_regress/t/t_multidriven_iface5.v +++ /dev/null @@ -1,69 +0,0 @@ -// 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 - -// nested interface test - direct assignment + nested interface task call in same always_comb - -// 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 - -interface leaf_if; - logic l0; - task set1(); l0 = 1'b1; endtask -endinterface - -interface top_if; - leaf_if sub(); -endinterface - -module mod #()( - input logic sel - ,output logic val -); - - top_if if0(); - - always_comb begin - if0.sub.l0 = 1'b0; - if (sel) begin - if0.sub.set1(); - end - end - - assign val = if0.sub.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_iface6.py b/test_regress/t/t_multidriven_iface6.py deleted file mode 100755 index c6e56559a..000000000 --- a/test_regress/t/t_multidriven_iface6.py +++ /dev/null @@ -1,18 +0,0 @@ -#!/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_iface6.v b/test_regress/t/t_multidriven_iface6.v deleted file mode 100644 index 83024eb74..000000000 --- a/test_regress/t/t_multidriven_iface6.v +++ /dev/null @@ -1,68 +0,0 @@ -// 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 - -// nested interface aggregator - two nested interfaces, only one driven - -// 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 - -interface chan_if; - logic b0; - task set1(); b0 = 1'b1; endtask -endinterface - -interface agg_if; - chan_if tlb(); - chan_if ic(); -endinterface - -module mod #()( - input logic sel - ,output logic val -); - - agg_if a(); - - always_comb begin - a.tlb.b0 = 1'b0; - if (sel) a.tlb.set1(); - end - - assign val = a.tlb.b0; - -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 From fdeccdc45e43513caecd9c983dd5dfc68dd1fc00 Mon Sep 17 00:00:00 2001 From: github action Date: Wed, 24 Dec 2025 13:08:12 +0000 Subject: [PATCH 37/37] Apply 'make format' --- src/V3Undriven.cpp | 15 ++++++--------- src/V3UndrivenCapture.cpp | 17 ++++++++--------- src/V3UndrivenCapture.h | 4 ++-- test_regress/t/t_multidriven_class.py | 0 test_regress/t/t_multidriven_iface.py | 0 5 files changed, 16 insertions(+), 20 deletions(-) mode change 100644 => 100755 test_regress/t/t_multidriven_class.py mode change 100644 => 100755 test_regress/t/t_multidriven_iface.py diff --git a/src/V3Undriven.cpp b/src/V3Undriven.cpp index 0dfd6c353..e3e986e8b 100644 --- a/src/V3Undriven.cpp +++ b/src/V3Undriven.cpp @@ -436,8 +436,8 @@ class UndrivenVisitor final : public VNVisitorConst { // 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_taskp && !m_alwaysp && !m_inContAssign && !m_inInitialStatic - && !m_inBBox && !m_taskp->dpiExport()) { + if (m_taskp && !m_alwaysp && !m_inContAssign && !m_inInitialStatic && !m_inBBox + && !m_taskp->dpiExport()) { AstVar* const retVarp = VN_CAST(m_taskp->fvarp(), Var); if (!retVarp || nodep->varp() != retVarp) return; } @@ -454,13 +454,11 @@ class UndrivenVisitor final : public VNVisitorConst { if (entryp->isDrivenWhole() && !m_inBBox && !VN_IS(nodep, VarXRef) && !VN_IS(nodep->dtypep()->skipRefp(), UnpackArrayDType) && nodep->fileline() != entryp->getNodeFileLinep() && !entryp->isUnderGen() - && (entryp->getNodep() || - entryp->callNodep())) { + && (entryp->getNodep() || entryp->callNodep())) { const AstNode* const otherWritep - = entryp->getNodep() ? - static_cast(entryp->getNodep()) : - entryp->callNodep(); + = entryp->getNodep() ? static_cast(entryp->getNodep()) + : entryp->callNodep(); if (m_alwaysCombp && (!entryp->isDrivenAlwaysCombWhole() @@ -613,8 +611,7 @@ class UndrivenVisitor final : public VNVisitorConst { public: // CONSTRUCTORS explicit UndrivenVisitor(AstNetlist* nodep, V3UndrivenCapture* capturep) - : m_capturep{capturep} - { + : m_capturep{capturep} { iterateConst(nodep); } diff --git a/src/V3UndrivenCapture.cpp b/src/V3UndrivenCapture.cpp index 991175121..4aa1ff06a 100644 --- a/src/V3UndrivenCapture.cpp +++ b/src/V3UndrivenCapture.cpp @@ -149,7 +149,8 @@ const std::vector& V3UndrivenCapture::computeWriteSummary(const AstNode info.writeSummary.clear(); - // Prevent duplicates across all sources that can contribute to a write summary (direct writes and call chains) + // Prevent duplicates across all sources that can contribute to a write summary (direct writes + // and call chains) std::unordered_set seen; // Simple lambda for filtering duplicates @@ -185,18 +186,16 @@ void V3UndrivenCapture::noteDirectWrite(const AstNodeFTask* taskp, AstVar* varp) if (retVarp && varp == retVarp) return; // Filter out duplicates. - if (info.directWritesSet.insert(varp).second) { - info.directWrites.push_back(varp); - } + if (info.directWritesSet.insert(varp).second) { info.directWrites.push_back(varp); } } void V3UndrivenCapture::noteCallEdge(const AstNodeFTask* callerp, const AstNodeFTask* calleep) { FTaskInfo& callerInfo = m_info[callerp]; - // Prevents duplicate entries from being appended, if calleep already exists then insert will return false, and then is not inserted into the callees vector. - if (callerInfo.calleesSet.insert(calleep).second) { - callerInfo.callees.push_back(calleep); - } - // Ensure callee entry exists, if already exists then this is a no-op. unordered_map<> so cheap. + // Prevents duplicate entries from being appended, if calleep already exists then insert will + // return false, and then is not inserted into the callees vector. + if (callerInfo.calleesSet.insert(calleep).second) { callerInfo.callees.push_back(calleep); } + // Ensure callee entry exists, if already exists then this is a no-op. unordered_map<> so + // cheap. (void)m_info[calleep]; } diff --git a/src/V3UndrivenCapture.h b/src/V3UndrivenCapture.h index 573ab4f70..526e6843c 100644 --- a/src/V3UndrivenCapture.h +++ b/src/V3UndrivenCapture.h @@ -59,8 +59,8 @@ public: // This is used to test whether weve already recorded a callee. Used to 'filter' on insert // versus sorting at the end. std::unordered_set calleesSet; - // This is used to test whether weve already recorded a direct write. Used to 'filter' on insert - // versus sorting at the end. + // This is used to test whether weve already recorded a direct write. Used to 'filter' on + // insert versus sorting at the end. std::unordered_set directWritesSet; }; diff --git a/test_regress/t/t_multidriven_class.py b/test_regress/t/t_multidriven_class.py old mode 100644 new mode 100755 diff --git a/test_regress/t/t_multidriven_iface.py b/test_regress/t/t_multidriven_iface.py old mode 100644 new mode 100755