Fix re-evaluation of logic dependent on state set in DPI exports (#3091).

Verilator should now correctly re-evaluate any logic that depends on
state set in a DPI exported function, including if the DPI export is
called outside eval, or if the DPI export is called from a DPI import.

Whenever the design contains a DPI exported function that sets a
non-local variable, we create a global __Vdpi_export_trigger flag, that
is set in the body of the DPI export, and make all variables set in any
DPI exported functions dependent on this flag (this ensures correct
ordering and change detection on state set in DPI exports when needed).
The DPI export trigger flag is cleared at the end of eval, which ensured
calls to DPI exports outside of eval are detected. Additionally the
ordering is modifies to assume that any call to a 'context' DPI import
might call DPI exports by adding an edge to the ordering graph from the
logic vertex containing the call to the DPI import to the DPI export
trigger variable vertex (note the standard does not allow calls to DPI
exports from DPI imports that were not imported with 'context', so we
do not enforce ordering on those).
This commit is contained in:
Geza Lore 2021-08-12 21:43:32 +01:00 committed by GitHub
parent d1836b7b6f
commit 536bdf506e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 722 additions and 5 deletions

View File

@ -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

View File

@ -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]";

View File

@ -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; }

View File

@ -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;

View File

@ -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);

View File

@ -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);
{

View File

@ -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();

View File

@ -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); }

View File

@ -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<AstVarScope*> 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<AstVarScope*> 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<AstVarScope*> 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: ");

View File

@ -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(

View File

@ -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 <svdpi.h>
#include <Vt_order_dpi_export_1.h>
#include <Vt_order_dpi_export_1__Dpi.h>
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;
}

View File

@ -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;

View File

@ -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

View File

@ -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 <svdpi.h>
#include <Vt_order_dpi_export_2.h>
#include <Vt_order_dpi_export_2__Dpi.h>
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;
}

View File

@ -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;

View File

@ -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

View File

@ -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 <svdpi.h>
#include <Vt_order_dpi_export_3.h>
#include <Vt_order_dpi_export_3__Dpi.h>
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;
}

View File

@ -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;

View File

@ -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

View File

@ -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 <svdpi.h>
#include <Vt_order_dpi_export_4.h>
#include <Vt_order_dpi_export_4__Dpi.h>
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;
}

View File

@ -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;

View File

@ -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

View File

@ -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 <svdpi.h>
#include <Vt_order_dpi_export_5.h>
#include <Vt_order_dpi_export_5__Dpi.h>
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;
}

View File

@ -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;

View File

@ -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