diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 487980e40..6ef897b2f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -189,6 +189,7 @@ set(HEADERS V3Tristate.h V3Udp.h V3Undriven.h + V3UndrivenCapture.h V3UniqueNames.h V3Unknown.h V3Unroll.h @@ -358,6 +359,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 1bc6cc3af..77f951678 100644 --- a/src/Makefile_obj.in +++ b/src/Makefile_obj.in @@ -341,6 +341,7 @@ RAW_OBJS_PCH_ASTNOMT = \ V3Tristate.o \ V3Udp.o \ V3Undriven.o \ + V3UndrivenCapture.o \ V3Unknown.o \ V3Unroll.o \ V3UnrollGen.o \ diff --git a/src/V3Undriven.cpp b/src/V3Undriven.cpp index 75e646520..4a54cd54a 100644 --- a/src/V3Undriven.cpp +++ b/src/V3Undriven.cpp @@ -28,6 +28,7 @@ #include "V3Undriven.h" #include "V3Stats.h" +#include "V3UndrivenCapture.h" #include @@ -51,6 +52,9 @@ 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 + enum : uint8_t { FLAG_USED = 0, FLAG_DRIVEN = 1, FLAG_DRIVEN_ALWCOMB = 2, FLAGS_PER_BIT = 3 }; public: @@ -278,6 +282,12 @@ public: } } } + + void drivenViaCall(const AstNodeFTaskRef* nodep) { + drivenWhole(); + if (!m_callNodep) { m_callNodep = nodep; } + } + const AstNodeFTaskRef* callNodep() const { return m_callNodep; } }; //###################################################################### @@ -304,6 +314,9 @@ 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 + // METHODS UndrivenVarEntry* getEntryp(AstVar* nodep, int which_user) { @@ -420,6 +433,16 @@ class UndrivenVisitor final : public VNVisitorConst { << " (IEEE 1800-2023 13.5): " << nodep->prettyNameQ()); } } + + // 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 && !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) { UndrivenVarEntry* const entryp = getEntryp(nodep->varp(), usr); const bool fdrv = nodep->access().isWriteOrRW() @@ -432,7 +455,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()) { + && (entryp->getNodep() || (m_enableWriteSummary && entryp->callNodep()))) { + + const AstNode* const otherWritep + = entryp->getNodep() + ? static_cast(entryp->getNodep()) + : (m_enableWriteSummary ? entryp->callNodep() : nullptr); + if (m_alwaysCombp && (!entryp->isDrivenAlwaysCombWhole() || (m_alwaysCombp != entryp->getAlwCombp() @@ -443,20 +472,18 @@ class UndrivenVisitor final : public VNVisitorConst { << " (IEEE 1800-2023 9.2.2.2): " << nodep->prettyNameQ() << '\n' << nodep->warnOther() << '\n' << nodep->warnContextPrimary() << '\n' - << entryp->getNodep()->warnOther() - << "... Location of other write\n" - << entryp->getNodep()->warnContextSecondary()); + << 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' - << entryp->getNodep()->warnOther() - << "... Location of always_comb write\n" - << entryp->getNodep()->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()); @@ -523,10 +550,39 @@ class UndrivenVisitor final : public VNVisitorConst { iterateChildrenConst(nodep); if (nodep->keyword() == VAlwaysKwd::ALWAYS_COMB) UINFO(9, " Done " << nodep); } + void visit(AstNodeFTaskRef* nodep) override { VL_RESTORER(m_inFTaskRef); m_inFTaskRef = true; + iterateChildrenConst(nodep); + + 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. + + const auto inExecutedContext = [this]() { + return !(m_taskp && !m_alwaysp && !m_inContAssign && !m_inInitialStatic && !m_inBBox + && !m_taskp->dpiExport()); + }; + + if (!inExecutedContext()) 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); + if (m_alwaysCombp) + entryp->drivenAlwaysCombWhole(m_alwaysCombp, m_alwaysCombp->fileline()); + } + } } void visit(AstNodeFTask* nodep) override { @@ -556,7 +612,13 @@ class UndrivenVisitor final : public VNVisitorConst { public: // CONSTRUCTORS - 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 +632,12 @@ public: void V3Undriven::undrivenAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ":"); - { UndrivenVisitor{nodep}; } + const bool enable = V3UndrivenCapture::enableWriteSummary; + if (enable) { + V3UndrivenCapture capture{nodep}; + { UndrivenVisitor{nodep, &capture, enable}; } + } else { + { UndrivenVisitor{nodep, nullptr, enable}; } + } if (v3Global.opt.stats()) V3Stats::statsStage("undriven"); } diff --git a/src/V3UndrivenCapture.cpp b/src/V3UndrivenCapture.cpp new file mode 100644 index 000000000..ec6e23846 --- /dev/null +++ b/src/V3UndrivenCapture.cpp @@ -0,0 +1,211 @@ +// -*- 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 { + +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); + } + +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(9, "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(9, "undriven capture direct write in " << taskNameQ(m_curTaskp) + << " var=" << nodep->varp()->prettyNameQ() + << " at " << nodep->fileline()); + + m_cap.noteDirectWrite(m_curTaskp, 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(9, "undriven capture call edge " << taskNameQ(m_curTaskp) << " -> " + << taskNameQ(calleep)); + m_cap.noteCallEdge(m_curTaskp, calleep); + } else { + UINFO(9, "undriven capture unresolved call in " << taskNameQ(m_curTaskp) + << " name=" << nodep->name()); + } + } + iterateChildrenConst(nodep); // still scan pins/args + } + + void visit(AstNode* nodep) override { iterateChildrenConst(nodep); } +}; + +} // namespace + +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) { + 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 + 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()); +} + +void V3UndrivenCapture::gather(AstNetlist* netlistp) { + // Walk netlist and populate m_info with direct writes + call edges + CaptureVisitor{*this, netlistp}; +} + +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(const AstNodeFTask* taskp) { + // Ensure entry exists even if empty + (void)m_info[taskp]; + return computeWriteSummary(taskp); +} + +const std::vector& V3UndrivenCapture::computeWriteSummary(const AstNodeFTask* taskp) { + FTaskInfo& info = m_info[taskp]; + + if (info.state == State::DONE) { + UINFO(9, "undriven capture writeSummary cached size=" << info.writeSummary.size() + << " 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()); + // 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); + + 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; + return info.writeSummary; +} + +void V3UndrivenCapture::noteTask(const AstNodeFTask* taskp) { (void)m_info[taskp]; } + +void V3UndrivenCapture::noteDirectWrite(const AstNodeFTask* taskp, AstVar* 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(const AstNodeFTask* callerp, const AstNodeFTask* calleep) { + m_info[callerp].callees.push_back(calleep); + (void)m_info[calleep]; // ensure callee entry exists +} + +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); + return; + } + UINFO(level, "undriven capture dump task " << taskp << " " << taskp->prettyNameQ() + << " directWrites=" << infop->directWrites.size() + << " callees=" << infop->callees.size() + << " writeSummary=" << infop->writeSummary.size()); +} diff --git a/src/V3UndrivenCapture.h b/src/V3UndrivenCapture.h new file mode 100644 index 000000000..3e0646c6b --- /dev/null +++ b/src/V3UndrivenCapture.h @@ -0,0 +1,113 @@ +// -*- 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 +// +//************************************************************************* + +//************************************************************************* +// +// 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_ + +#include "config_build.h" + +#include "V3Ast.h" + +#include +#include + +class AstNetlist; + +class V3UndrivenCapture final { +public: + // 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 { + // Variables written directly in this task/function body. + 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. + 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 (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. + 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); + + // 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(const AstNodeFTask* 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). This is currently only used for the + // debug helper. + 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(const AstNodeFTask* 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(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(const AstNodeFTask* callerp, const AstNodeFTask* calleep); + + // Dump one task's summary for debugging. leaving this in, in case need to debug future + // functionality. + void debugDumpTask(const AstNodeFTask* taskp, int level = 9) const; +}; + +#endif // VERILATOR_V3UNDRIVENCAPTURE_H_ diff --git a/test_regress/t/t_lint_multidriven_taskcall_bad.out b/test_regress/t/t_lint_multidriven_taskcall_bad.out new file mode 100644 index 000000000..1d1278b14 --- /dev/null +++ b/test_regress/t/t_lint_multidriven_taskcall_bad.out @@ -0,0 +1,11 @@ +%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_multidriven_taskcall_bad.v:28:15: + 28 | if (sel2) out = 1'b1; + | ^~~ + 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 + ... 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_multidriven_taskcall_bad.py b/test_regress/t/t_lint_multidriven_taskcall_bad.py new file mode 100755 index 000000000..31228c9a7 --- /dev/null +++ b/test_regress/t/t_lint_multidriven_taskcall_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_multidriven_taskcall_bad.v b/test_regress/t/t_lint_multidriven_taskcall_bad.v new file mode 100644 index 000000000..b7943087a --- /dev/null +++ b/test_regress/t/t_lint_multidriven_taskcall_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_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..6df099a7e --- /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; + #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 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 diff --git a/test_regress/t/t_multidriven_funcret0.py b/test_regress/t/t_multidriven_funcret0.py new file mode 100755 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..0e82e300b --- /dev/null +++ b/test_regress/t/t_multidriven_funcret0.v @@ -0,0 +1,43 @@ +// 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. + +`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 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_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 diff --git a/test_regress/t/t_multidriven_simple.py b/test_regress/t/t_multidriven_simple.py new file mode 100755 index 000000000..c6e56559a --- /dev/null +++ b/test_regress/t/t_multidriven_simple.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_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