diff --git a/Changes b/Changes index 158ecf997..aa60688ff 100644 --- a/Changes +++ b/Changes @@ -13,6 +13,7 @@ Verilator 4.211 devel **Minor:** +* Fix re-evaluation of logic dependent on state set in DPI exports (#3091). [Geza Lore] * Support unpacked array localparams in tasks/functions (#3078). [Geza Lore] * Support timeunit/timeprecision in $unit. * Add --instr-count-dpi to tune assumed DPI import cost for multithreaded diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index 661c40fca..8ce2682d0 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -1842,6 +1842,7 @@ void AstCFunc::dump(std::ostream& str) const { if (dpiExportImpl()) str << " [DPIEI]"; if (dpiImportPrototype()) str << " [DPIIP]"; if (dpiImportWrapper()) str << " [DPIIW]"; + if (dpiContext()) str << " [DPICTX]"; if (isConstructor()) str << " [CTOR]"; if (isDestructor()) str << " [DTOR]"; if (isVirtual()) str << " [VIRT]"; diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index cdf54bd7a..d1706bbdd 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -3479,6 +3479,17 @@ public: virtual bool brokeLhsMustBeLvalue() const override { return true; } }; +class AstDpiExportUpdated final : public AstNodeStmt { + // Denotes that the referenced variable may have been updated via a DPI Export +public: + AstDpiExportUpdated(FileLine* fl, AstVarScope* varScopep) + : ASTGEN_SUPER_DpiExportUpdated(fl) { + addOp1p(new AstVarRef{fl, varScopep, VAccess::WRITE}); + } + ASTNODE_NODE_FUNCS(DpiExportUpdated) + AstVarScope* varScopep() const { return VN_CAST(op1p(), VarRef)->varScopep(); } +}; + class AstExprStmt final : public AstNodeMath { // Perform a statement, often assignment inside an expression/math node, // the parent gets passed the 'resultp()'. @@ -8744,6 +8755,7 @@ private: bool m_dpiExportImpl : 1; // DPI export implementation (called from DPI dispatcher via lookup) bool m_dpiImportPrototype : 1; // This is the DPI import prototype (i.e.: provided by user) bool m_dpiImportWrapper : 1; // Wrapper for invoking DPI import prototype from generated code + bool m_dpiContext : 1; // Declared as 'context' DPI import/export function public: AstCFunc(FileLine* fl, const string& name, AstScope* scopep, const string& rtnType = "") : ASTGEN_SUPER_CFunc(fl) { @@ -8770,6 +8782,7 @@ public: m_dpiExportImpl = false; m_dpiImportPrototype = false; m_dpiImportWrapper = false; + m_dpiContext = false; } ASTNODE_NODE_FUNCS(CFunc) virtual string name() const override { return m_name; } @@ -8845,6 +8858,8 @@ public: void dpiImportPrototype(bool flag) { m_dpiImportPrototype = flag; } bool dpiImportWrapper() const { return m_dpiImportWrapper; } void dpiImportWrapper(bool flag) { m_dpiImportWrapper = flag; } + bool dpiContext() const { return m_dpiContext; } + void dpiContext(bool flag) { m_dpiContext = flag; } // // If adding node accessors, see below emptyBody AstNode* argsp() const { return op1p(); } @@ -9139,6 +9154,7 @@ private: AstPackage* m_dollarUnitPkgp = nullptr; // $unit AstCFunc* m_evalp = nullptr; // The '_eval' function AstExecGraph* m_execGraphp = nullptr; // Execution MTask graph for threads>1 mode + AstVarScope* m_dpiExportTriggerp = nullptr; // The DPI export trigger variable VTimescale m_timeunit; // Global time unit VTimescale m_timeprecision; // Global time precision bool m_changeRequest = false; // Have _change_request method @@ -9149,6 +9165,7 @@ public: virtual const char* broken() const override { BROKEN_RTN(m_dollarUnitPkgp && !m_dollarUnitPkgp->brokeExists()); BROKEN_RTN(m_evalp && !m_evalp->brokeExists()); + BROKEN_RTN(m_dpiExportTriggerp && !m_dpiExportTriggerp->brokeExists()); return nullptr; } virtual string name() const override { return "$root"; } @@ -9184,6 +9201,8 @@ public: void evalp(AstCFunc* evalp) { m_evalp = evalp; } AstExecGraph* execGraphp() const { return m_execGraphp; } void execGraphp(AstExecGraph* graphp) { m_execGraphp = graphp; } + AstVarScope* dpiExportTriggerp() const { return m_dpiExportTriggerp; } + void dpiExportTriggerp(AstVarScope* varScopep) { m_dpiExportTriggerp = varScopep; } VTimescale timeunit() const { return m_timeunit; } void timeunit(const VTimescale& value) { m_timeunit = value; } VTimescale timeprecision() const { return m_timeprecision; } diff --git a/src/V3Changed.cpp b/src/V3Changed.cpp index 589a681ac..aac14f59a 100644 --- a/src/V3Changed.cpp +++ b/src/V3Changed.cpp @@ -205,6 +205,10 @@ private: public: // CONSTRUCTORS ChangedInsertVisitor(AstVarScope* vscp, ChangedState* statep) { + // DPI export trigger should never need change detect. See similar assertions in V3Order + // (OrderVisitor::nodeMarkCircular), and V3GenClk (GenClkRenameVisitor::genInpClk). + UASSERT_OBJ(vscp != v3Global.rootp()->dpiExportTriggerp(), vscp, + "DPI export trigger should not need change detect"); m_statep = statep; m_vscp = vscp; m_detects = 0; diff --git a/src/V3Clock.cpp b/src/V3Clock.cpp index 6bb9a461c..7f941851d 100644 --- a/src/V3Clock.cpp +++ b/src/V3Clock.cpp @@ -257,6 +257,14 @@ private: // Process the activates iterateChildren(nodep); UINFO(4, " TOPSCOPE iter done " << nodep << endl); + // Clear the DPI export trigger flag at the end of eval + if (AstVarScope* const dpiExportTriggerp = v3Global.rootp()->dpiExportTriggerp()) { + FileLine* const fl = dpiExportTriggerp->fileline(); + AstAssign* const assignp + = new AstAssign{fl, new AstVarRef{fl, dpiExportTriggerp, VAccess::WRITE}, + new AstConst{fl, AstConst::BitFalse{}}}; + m_evalFuncp->addFinalsp(assignp); + } // Split large functions splitCheck(m_evalFuncp); splitCheck(m_initFuncp); diff --git a/src/V3Descope.cpp b/src/V3Descope.cpp index 0621075d7..8ab4dccf5 100644 --- a/src/V3Descope.cpp +++ b/src/V3Descope.cpp @@ -182,6 +182,10 @@ private: } // VISITORS + virtual void visit(AstNetlist* nodep) override { + nodep->dpiExportTriggerp(nullptr); + iterateChildren(nodep); + } virtual void visit(AstNodeModule* nodep) override { VL_RESTORER(m_modp); { diff --git a/src/V3GenClk.cpp b/src/V3GenClk.cpp index 48c39b63d..1845a95b3 100644 --- a/src/V3GenClk.cpp +++ b/src/V3GenClk.cpp @@ -56,6 +56,19 @@ private: if (vscp->user2p()) { return VN_CAST(vscp->user2p(), VarScope); } else { + // In order to create a __VinpClk* for a signal, it needs to be marked circular. + // The DPI export trigger is never marked circular by V3Order (see comments in + // OrderVisitor::nodeMarkCircular). The only other place where one might mark + // a node circular is in this pass (V3GenClk), if the signal is assigned but was + // previously used as a clock. The DPI export trigger is only ever assigned in + // a DPI export called from outside eval, or from a DPI import, which are not + // discovered by GenClkReadVisitor (note that impure tasks - i.e.: those setting + // non-local variables - cannot be no-inline, see V3Task), hence the DPI export + // trigger should never be marked circular. Note that ordering should still be + // correct as there will be a change detect on any signals set from a DPI export + // that might have dependents scheduled earlier. + UASSERT_OBJ(vscp != v3Global.rootp()->dpiExportTriggerp(), vscp, + "DPI export trigger should not need __VinpClk"); AstVar* varp = vscp->varp(); string newvarname = "__VinpClk__" + vscp->scopep()->nameDotless() + "__" + varp->name(); diff --git a/src/V3Order.cpp b/src/V3Order.cpp index 93ad38651..0cb9cf4d8 100644 --- a/src/V3Order.cpp +++ b/src/V3Order.cpp @@ -645,6 +645,7 @@ private: AstSenTree* m_comboDomainp = nullptr; // Combo activation tree AstSenTree* m_deleteDomainp = nullptr; // Delete this from tree OrderInputsVertex* m_inputsVxp = nullptr; // Top level vertex all inputs point from + OrderVarVertex* m_dpiExportTriggerVxp = nullptr; // DPI Export trigger condition vertex OrderLogicVertex* m_logicVxp = nullptr; // Current statement being tracked, nullptr=ignored AstTopScope* m_topScopep = nullptr; // Current top scope being processed AstScope* m_scopetopp = nullptr; // Scope under TOPSCOPE @@ -756,6 +757,10 @@ private: } void nodeMarkCircular(OrderVarVertex* vertexp, OrderEdge* edgep) { + // To be marked circular requires being a clock assigned in a delayed assignment, or + // having a cutable in or out edge, none of which is true for the DPI export trigger. + UASSERT(vertexp != m_dpiExportTriggerVxp, + "DPI expor trigger should not be marked circular"); AstVarScope* nodep = vertexp->varScp(); OrderLogicVertex* fromLVtxp = nullptr; OrderLogicVertex* toLVtxp = nullptr; @@ -953,6 +958,9 @@ private: // Base vertices m_activeSenVxp = nullptr; m_inputsVxp = new OrderInputsVertex(&m_graph, nullptr); + if (AstVarScope* const dpiExportTrigger = v3Global.rootp()->dpiExportTriggerp()) { + m_dpiExportTriggerVxp = newVarUserVertex(dpiExportTrigger, WV_STD); + } // iterateChildren(nodep); // Done topscope, erase extra user information @@ -1130,6 +1138,24 @@ private: } } } + virtual void visit(AstDpiExportUpdated* nodep) override { + // This is under an AstAlways, sensitive to a change in the DPI export trigger. We just + // need to add an edge to the enclosing logic vertex (the vertex for the AstAlways). + OrderVarVertex* const varVxp = newVarUserVertex(nodep->varScopep(), WV_STD); + new OrderComboCutEdge(&m_graph, m_logicVxp, varVxp); + // Only used for ordering, so we can get rid of it here + nodep->unlinkFrBack(); + VL_DO_DANGLING(pushDeletep(nodep), nodep); + } + virtual void visit(AstCCall* nodep) override { + // Calls to 'context' imported DPI function may call DPI exported functions + if (m_dpiExportTriggerVxp && nodep->funcp()->dpiImportWrapper() + && nodep->funcp()->dpiContext()) { + UASSERT_OBJ(m_logicVxp, nodep, "Call not under logic"); + new OrderEdge(&m_graph, m_logicVxp, m_dpiExportTriggerVxp, WEIGHT_NORMAL); + } + iterateChildren(nodep); + } virtual void visit(AstSenTree* nodep) override { // Having a node derived from the sentree isn't required for // correctness, it merely makes the graph better connected @@ -1187,10 +1213,10 @@ private: iterateNewStmt(nodep); } virtual void visit(AstCFunc*) override { - // Ignore for now - // We should detect what variables are set in the function, and make - // settlement code for them, then set a global flag, so we call "settle" - // on the next evaluation loop. + // Calls to DPI exports handled with AstCCall. /* verlator public */ functions are + // ignored for now (and hence potentially mis-ordered), but could use the same or + // similar mechanism as DPI exports. Every other impure function (including those + // that may set a non-local variable) must have been inlined in V3Task. } //-------------------- virtual void visit(AstNode* nodep) override { iterateChildren(nodep); } diff --git a/src/V3Task.cpp b/src/V3Task.cpp index 7dce81e29..a4bcff21d 100644 --- a/src/V3Task.cpp +++ b/src/V3Task.cpp @@ -364,6 +364,39 @@ struct TaskDpiUtils { } }; +//###################################################################### +// Gather non-local variables written by an AstCFunc + +class TaskGatherWrittenVisitor final : public AstNVisitor { + // NODE STATE + // AstVarScope::user5 -> Already considered variable + AstUser5InUse m_user5InUse; + + std::vector m_writtenVariables; // Variables written + + virtual void visit(AstVarRef* nodep) override { + if (nodep->access().isReadOnly()) return; // Ignore read reference + AstVarScope* const varScopep = nodep->varScopep(); + if (varScopep->user5()) return; // Ignore already processed variable + varScopep->user5(true); // Mark as already processed + // Note: We are ignoring function locals as they should not be referenced anywhere outside + // of the enclosing AstCFunc, and therefore they are irrelevant for code ordering. This is + // simply an optimization to avoid adding useless nodes to the ordering graph in V3Order. + if (varScopep->varp()->isFuncLocal()) return; + m_writtenVariables.push_back(varScopep); + } + virtual void visit(AstNode* nodep) override { iterateChildrenConst(nodep); } + + explicit TaskGatherWrittenVisitor(AstNode* nodep) { iterate(nodep); } + +public: + // Gather all written non-local variables + static const std::vector gather(AstCFunc* funcp) { + TaskGatherWrittenVisitor visitor{funcp}; + return std::move(visitor.m_writtenVariables); + } +}; + //###################################################################### // Task state, as a visitor of each AstNode @@ -768,6 +801,7 @@ private: AstCFunc* const funcp = new AstCFunc(nodep->fileline(), nodep->cname(), m_scopep, (rtnvarp ? rtnvarp->dpiArgType(true, true) : "")); funcp->dpiExportDispatcher(true); + funcp->dpiContext(nodep->dpiContext()); funcp->dontCombine(true); funcp->entryPoint(true); funcp->isStatic(true); @@ -899,6 +933,7 @@ private: : ""; AstCFunc* const funcp = new AstCFunc(nodep->fileline(), nodep->cname(), m_scopep, rtnType); funcp->dpiImportPrototype(true); + funcp->dpiContext(nodep->dpiContext()); funcp->dontCombine(true); funcp->entryPoint(false); funcp->isMethod(false); @@ -1062,6 +1097,22 @@ private: } } + AstVarScope* makeDpiExporTrigger() { + AstVarScope* dpiExportTriggerp = v3Global.rootp()->dpiExportTriggerp(); + if (!dpiExportTriggerp) { + // Create the global DPI export trigger flag the first time we encounter a DPI export. + // This flag is set any time a DPI export is invoked, and cleared at the end of eval. + FileLine* const fl = m_topScopep->fileline(); + AstVar* const varp + = new AstVar{fl, AstVarType::VAR, "__Vdpi_export_trigger", VFlagBitPacked{}, 1}; + m_topScopep->scopep()->modp()->addStmtp(varp); + dpiExportTriggerp = new AstVarScope{fl, m_topScopep->scopep(), varp}; + m_topScopep->scopep()->addVarp(dpiExportTriggerp); + v3Global.rootp()->dpiExportTriggerp(dpiExportTriggerp); + } + return dpiExportTriggerp; + } + AstCFunc* makeUserFunc(AstNodeFTask* nodep, bool ftaskNoInline) { // Given a already cloned node, make a public C function, or a non-inline C function // Probably some of this work should be done later, but... @@ -1151,6 +1202,7 @@ private: cfuncp->funcPublic(nodep->taskPublic()); cfuncp->dpiExportImpl(nodep->dpiExport()); cfuncp->dpiImportWrapper(nodep->dpiImport()); + cfuncp->dpiContext(nodep->dpiContext()); if (nodep->dpiImport() || nodep->dpiExport()) { cfuncp->isStatic(true); cfuncp->isLoose(true); @@ -1248,6 +1300,42 @@ private: tempp->stmtsp()->unlinkFrBackWithNext(); VL_DO_DANGLING(tempp->deleteTree(), tempp); } + + if (cfuncp->dpiExportImpl()) { + // Mark all non-local variables written by the DPI exported function as being updated + // by DPI exports. This ensures correct ordering and change detection later. + const std::vector writtenps = TaskGatherWrittenVisitor::gather(cfuncp); + if (!writtenps.empty()) { + AstVarScope* const dpiExportTriggerp = makeDpiExporTrigger(); + FileLine* const fl = cfuncp->fileline(); + + // Set DPI export trigger flag every time the DPI export is called. + AstAssign* const assignp + = new AstAssign{fl, new AstVarRef{fl, dpiExportTriggerp, VAccess::WRITE}, + new AstConst{fl, AstConst::BitTrue{}}}; + // Add as first statement (to avoid issues with early returns) to exported function + if (cfuncp->stmtsp()) { + cfuncp->stmtsp()->addHereThisAsNext(assignp); + } else { + cfuncp->addStmtsp(assignp); + } + + // Add an always block sensitive to the DPI export trigger flag, and add an + // AstDpiExportUpdated node under it for each variable that are writen by the + // exported function. + AstAlways* const alwaysp = new AstAlways{ + fl, VAlwaysKwd::ALWAYS, + new AstSenTree{ + fl, new AstSenItem{fl, VEdgeType::ET_HIGHEDGE, + new AstVarRef{fl, dpiExportTriggerp, VAccess::READ}}}, + nullptr}; + for (AstVarScope* const varScopep : writtenps) { + alwaysp->addStmtp(new AstDpiExportUpdated{fl, varScopep}); + } + m_scopep->addActivep(alwaysp); + } + } + // Delete rest of cloned task and return new func VL_DO_DANGLING(pushDeletep(nodep), nodep); if (debug() >= 9) cfuncp->dumpTree(cout, "-userFunc: "); diff --git a/test_regress/t/t_dpi_qw.pl b/test_regress/t/t_dpi_qw.pl index 74e3bae58..f5c98cf48 100755 --- a/test_regress/t/t_dpi_qw.pl +++ b/test_regress/t/t_dpi_qw.pl @@ -12,7 +12,7 @@ scenarios(simulator => 1); compile( v_flags2 => ["t/t_dpi_qw_c.cpp"], - verilator_flags2 => ["-Wall -Wno-DECLFILENAME -no-l2name"], + verilator_flags2 => ["-Wall -Wno-DECLFILENAME -Wno-UNOPTFLAT -no-l2name"], ); execute( diff --git a/test_regress/t/t_order_dpi_export_1.cpp b/test_regress/t/t_order_dpi_export_1.cpp new file mode 100644 index 000000000..1dcec5c1e --- /dev/null +++ b/test_regress/t/t_order_dpi_export_1.cpp @@ -0,0 +1,37 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// +// Copyright 2021 by Geza Lore. 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 + +#include +#include + +int main(int argc, char* argv[]) { + Vt_order_dpi_export_1* const tb = new Vt_order_dpi_export_1; + tb->contextp()->commandArgs(argc, argv); + bool clk = true; + + while (!tb->contextp()->gotFinish()) { + // Timeout + if (tb->contextp()->time() > 100000) break; + // Toggle and set clock + svSetScope(svGetScopeFromName("TOP.testbench")); + clk = !clk; + set_clk(clk); + // Eval + tb->eval(); + // Advance time + tb->contextp()->timeInc(500); + } + + delete tb; + return 0; +} diff --git a/test_regress/t/t_order_dpi_export_1.pl b/test_regress/t/t_order_dpi_export_1.pl new file mode 100755 index 000000000..21b3c76b6 --- /dev/null +++ b/test_regress/t/t_order_dpi_export_1.pl @@ -0,0 +1,24 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 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 + +scenarios(vlt_all => 1); + +compile( + make_top_shell => 0, + make_main => 0, + verilator_flags2 => ["--exe","$Self->{t_dir}/$Self->{name}.cpp"], + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_order_dpi_export_1.v b/test_regress/t/t_order_dpi_export_1.v new file mode 100644 index 000000000..e71069e06 --- /dev/null +++ b/test_regress/t/t_order_dpi_export_1.v @@ -0,0 +1,36 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// Copyright 2021 by Geza Lore. 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 + +module testbench; + + logic clk; + + export "DPI-C" function set_clk; + function void set_clk(bit val); + clk = val; + endfunction; + + // Downstream signal dependent on clk demonstrates scheduling issue. + // The '$c("1") &' simply ensures that dependent_clk does not get + // replaced with clk early and hence hiding the issue + wire dependent_clk = $c1("1") & clk; + + int n = 0; + + always @(posedge dependent_clk) begin + $display("t=%t n=%d", $time, n); + if ($time != (2*n+1) * 500) $stop; + if (n == 20) begin + $write("*-* All Finished *-*\n"); + $finish; + end + n += 1; + end + +endmodule + diff --git a/test_regress/t/t_order_dpi_export_2.cpp b/test_regress/t/t_order_dpi_export_2.cpp new file mode 100644 index 000000000..3ba47b0d3 --- /dev/null +++ b/test_regress/t/t_order_dpi_export_2.cpp @@ -0,0 +1,38 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// +// Copyright 2021 by Geza Lore. 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 + +#include +#include + +void toggle_other_clk(svBit val) { set_other_clk(val); } + +int main(int argc, char* argv[]) { + Vt_order_dpi_export_2* const tb = new Vt_order_dpi_export_2; + tb->contextp()->commandArgs(argc, argv); + bool clk = true; + + while (!tb->contextp()->gotFinish()) { + // Timeout + if (tb->contextp()->time() > 100000) break; + // Toggle and set main clock + clk = !clk; + tb->clk = clk; + // Eval + tb->eval(); + // Advance time + tb->contextp()->timeInc(500); + } + + delete tb; + return 0; +} diff --git a/test_regress/t/t_order_dpi_export_2.pl b/test_regress/t/t_order_dpi_export_2.pl new file mode 100755 index 000000000..21b3c76b6 --- /dev/null +++ b/test_regress/t/t_order_dpi_export_2.pl @@ -0,0 +1,24 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 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 + +scenarios(vlt_all => 1); + +compile( + make_top_shell => 0, + make_main => 0, + verilator_flags2 => ["--exe","$Self->{t_dir}/$Self->{name}.cpp"], + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_order_dpi_export_2.v b/test_regress/t/t_order_dpi_export_2.v new file mode 100644 index 000000000..a5c1f40a1 --- /dev/null +++ b/test_regress/t/t_order_dpi_export_2.v @@ -0,0 +1,43 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// Copyright 2021 by Geza Lore. 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 + +module testbench( + /*AUTOARG*/ + // Inputs + clk + ); + + input clk; // Top level input clock + logic other_clk; // Dependent clock set via DPI + + export "DPI-C" function set_other_clk; + function void set_other_clk(bit val); + other_clk = val; + endfunction; + + bit even_other = 1; + import "DPI-C" context function void toggle_other_clk(bit val); + always @(posedge clk) begin + even_other <= ~even_other; + toggle_other_clk(even_other); + end + + int n = 0; + + always @(posedge other_clk) begin + $display("t=%t n=%d", $time, n); + if ($time != (4*n+1) * 500) $stop; + if (n == 20) begin + $write("*-* All Finished *-*\n"); + $finish; + end + n += 1; + end + +endmodule + diff --git a/test_regress/t/t_order_dpi_export_3.cpp b/test_regress/t/t_order_dpi_export_3.cpp new file mode 100644 index 000000000..2855845c1 --- /dev/null +++ b/test_regress/t/t_order_dpi_export_3.cpp @@ -0,0 +1,40 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// +// Copyright 2021 by Geza Lore. 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 + +#include +#include + +void toggle_other_clk(svBit val) { set_other_clk(val); } + +void toggle_third_clk(svBit val) { set_third_clk(val); } + +int main(int argc, char* argv[]) { + Vt_order_dpi_export_3* const tb = new Vt_order_dpi_export_3; + tb->contextp()->commandArgs(argc, argv); + bool clk = true; + + while (!tb->contextp()->gotFinish()) { + // Timeout + if (tb->contextp()->time() > 100000) break; + // Toggle and set main clock + clk = !clk; + tb->clk = clk; + // Eval + tb->eval(); + // Advance time + tb->contextp()->timeInc(500); + } + + delete tb; + return 0; +} diff --git a/test_regress/t/t_order_dpi_export_3.pl b/test_regress/t/t_order_dpi_export_3.pl new file mode 100755 index 000000000..21b3c76b6 --- /dev/null +++ b/test_regress/t/t_order_dpi_export_3.pl @@ -0,0 +1,24 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 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 + +scenarios(vlt_all => 1); + +compile( + make_top_shell => 0, + make_main => 0, + verilator_flags2 => ["--exe","$Self->{t_dir}/$Self->{name}.cpp"], + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_order_dpi_export_3.v b/test_regress/t/t_order_dpi_export_3.v new file mode 100644 index 000000000..8083bae97 --- /dev/null +++ b/test_regress/t/t_order_dpi_export_3.v @@ -0,0 +1,56 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// Copyright 2021 by Geza Lore. 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 + +module testbench( + /*AUTOARG*/ + // Inputs + clk + ); + + input clk; // Top level input clock + logic other_clk; // Dependent clock set via DPI + logic third_clk; // Additional dependent clock set via DPI + + export "DPI-C" function set_other_clk; + function void set_other_clk(bit val); + other_clk = val; + endfunction; + + export "DPI-C" function set_third_clk; + function void set_third_clk(bit val); + third_clk = val; + endfunction; + + bit even_other = 1; + import "DPI-C" context function void toggle_other_clk(bit val); + always @(posedge clk) begin + even_other <= ~even_other; + toggle_other_clk(even_other); + end + + bit even_third = 1; + import "DPI-C" context function void toggle_third_clk(bit val); + always @(posedge other_clk) begin + even_third <= ~even_third; + toggle_third_clk(even_third); + end + + int n = 0; + + always @(posedge third_clk) begin + $display("t=%d n=%d", $time, n); + if ($time != (8*n+1) * 500) $stop; + if (n == 20) begin + $write("*-* All Finished *-*\n"); + $finish; + end + n += 1; + end + +endmodule + diff --git a/test_regress/t/t_order_dpi_export_4.cpp b/test_regress/t/t_order_dpi_export_4.cpp new file mode 100644 index 000000000..e4ecd5c89 --- /dev/null +++ b/test_regress/t/t_order_dpi_export_4.cpp @@ -0,0 +1,40 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// +// Copyright 2021 by Geza Lore. 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 + +#include +#include + +void toggle_other_clk(svBit val) { set_other_clk(val); } + +void toggle_third_clk(svBit val) { set_third_clk(val); } + +int main(int argc, char* argv[]) { + Vt_order_dpi_export_4* const tb = new Vt_order_dpi_export_4; + tb->contextp()->commandArgs(argc, argv); + bool clk = true; + + while (!tb->contextp()->gotFinish()) { + // Timeout + if (tb->contextp()->time() > 100000) break; + // Toggle and set main clock + clk = !clk; + tb->clk = clk; + // Eval + tb->eval(); + // Advance time + tb->contextp()->timeInc(500); + } + + delete tb; + return 0; +} diff --git a/test_regress/t/t_order_dpi_export_4.pl b/test_regress/t/t_order_dpi_export_4.pl new file mode 100755 index 000000000..21b3c76b6 --- /dev/null +++ b/test_regress/t/t_order_dpi_export_4.pl @@ -0,0 +1,24 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 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 + +scenarios(vlt_all => 1); + +compile( + make_top_shell => 0, + make_main => 0, + verilator_flags2 => ["--exe","$Self->{t_dir}/$Self->{name}.cpp"], + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_order_dpi_export_4.v b/test_regress/t/t_order_dpi_export_4.v new file mode 100644 index 000000000..73600c1c1 --- /dev/null +++ b/test_regress/t/t_order_dpi_export_4.v @@ -0,0 +1,58 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// Copyright 2021 by Geza Lore. 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 + +module testbench( + /*AUTOARG*/ + // Inputs + clk + ); + + input clk; // Top level input clock + logic other_clk; // Dependent clock set via DPI + logic third_clk; // Additional dependent clock set via DPI + + export "DPI-C" function set_other_clk; + function void set_other_clk(bit val); + other_clk = val; + endfunction; + + export "DPI-C" function set_third_clk; + function void set_third_clk(bit val); + third_clk = val; + endfunction; + + bit even_other = 1; + import "DPI-C" context function void toggle_other_clk(bit val); + always @(posedge clk) begin + even_other <= ~even_other; + toggle_other_clk(even_other); + end + + bit even_third = 1; + import "DPI-C" context function void toggle_third_clk(bit val); + always @(posedge other_clk) begin + even_third <= ~even_third; + toggle_third_clk(even_third); + end + + int n = 0; + + wire final_clk = $c1("1") & third_clk; + + always @(posedge final_clk) begin + $display("t=%d n=%d", $time, n); + if ($time != (8*n+1) * 500) $stop; + if (n == 20) begin + $write("*-* All Finished *-*\n"); + $finish; + end + n += 1; + end + +endmodule + diff --git a/test_regress/t/t_order_dpi_export_5.cpp b/test_regress/t/t_order_dpi_export_5.cpp new file mode 100644 index 000000000..c14bf9ef3 --- /dev/null +++ b/test_regress/t/t_order_dpi_export_5.cpp @@ -0,0 +1,39 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// +// Copyright 2021 by Geza Lore. 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 + +#include +#include + +int main(int argc, char* argv[]) { + Vt_order_dpi_export_5* const tb = new Vt_order_dpi_export_5; + tb->contextp()->commandArgs(argc, argv); + bool clk = true; + + while (!tb->contextp()->gotFinish()) { + // Timeout + if (tb->contextp()->time() > 100000) break; + // Toggle and set main clock + clk = !clk; + tb->clk = clk; + // Reset counter at falling clock edge, once it reached value 4 + svSetScope(svGetScopeFromName("TOP.testbench")); + if (get_cnt() == 4 && !clk) set_cnt(0); + // Eval + tb->eval(); + // Advance time + tb->contextp()->timeInc(500); + } + + delete tb; + return 0; +} diff --git a/test_regress/t/t_order_dpi_export_5.pl b/test_regress/t/t_order_dpi_export_5.pl new file mode 100755 index 000000000..21b3c76b6 --- /dev/null +++ b/test_regress/t/t_order_dpi_export_5.pl @@ -0,0 +1,24 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 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 + +scenarios(vlt_all => 1); + +compile( + make_top_shell => 0, + make_main => 0, + verilator_flags2 => ["--exe","$Self->{t_dir}/$Self->{name}.cpp"], + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_order_dpi_export_5.v b/test_regress/t/t_order_dpi_export_5.v new file mode 100644 index 000000000..744c64ca8 --- /dev/null +++ b/test_regress/t/t_order_dpi_export_5.v @@ -0,0 +1,46 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// Copyright 2021 by Geza Lore. 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 + +module testbench( + /*AUTOARG*/ + // Inputs + clk + ); + + input clk; + + int cnt = 0; + export "DPI-C" function set_cnt; + function void set_cnt(int val); + cnt = val; + endfunction; + export "DPI-C" function get_cnt; + function int get_cnt(); + return cnt; + endfunction; + + always @(posedge clk) cnt += 1; + + // Downstream combinational signal dependent on both input clock and + // DPI export. + wire dependent_clk = cnt == 2; + + int n = 0; + + always @(posedge dependent_clk) begin + $display("t=%t n=%d", $time, n); + if ($time != (8*n+3) * 500) $stop; + if (n == 20) begin + $write("*-* All Finished *-*\n"); + $finish; + end + n += 1; + end + +endmodule +