forked from github/verilator
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:
parent
d1836b7b6f
commit
536bdf506e
1
Changes
1
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
|
||||
|
@ -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]";
|
||||
|
@ -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; }
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
{
|
||||
|
@ -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();
|
||||
|
@ -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); }
|
||||
|
@ -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: ");
|
||||
|
@ -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(
|
||||
|
37
test_regress/t/t_order_dpi_export_1.cpp
Normal file
37
test_regress/t/t_order_dpi_export_1.cpp
Normal 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;
|
||||
}
|
24
test_regress/t/t_order_dpi_export_1.pl
Executable file
24
test_regress/t/t_order_dpi_export_1.pl
Executable 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;
|
36
test_regress/t/t_order_dpi_export_1.v
Normal file
36
test_regress/t/t_order_dpi_export_1.v
Normal 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
|
||||
|
38
test_regress/t/t_order_dpi_export_2.cpp
Normal file
38
test_regress/t/t_order_dpi_export_2.cpp
Normal 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;
|
||||
}
|
24
test_regress/t/t_order_dpi_export_2.pl
Executable file
24
test_regress/t/t_order_dpi_export_2.pl
Executable 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;
|
43
test_regress/t/t_order_dpi_export_2.v
Normal file
43
test_regress/t/t_order_dpi_export_2.v
Normal 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
|
||||
|
40
test_regress/t/t_order_dpi_export_3.cpp
Normal file
40
test_regress/t/t_order_dpi_export_3.cpp
Normal 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;
|
||||
}
|
24
test_regress/t/t_order_dpi_export_3.pl
Executable file
24
test_regress/t/t_order_dpi_export_3.pl
Executable 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;
|
56
test_regress/t/t_order_dpi_export_3.v
Normal file
56
test_regress/t/t_order_dpi_export_3.v
Normal 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
|
||||
|
40
test_regress/t/t_order_dpi_export_4.cpp
Normal file
40
test_regress/t/t_order_dpi_export_4.cpp
Normal 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;
|
||||
}
|
24
test_regress/t/t_order_dpi_export_4.pl
Executable file
24
test_regress/t/t_order_dpi_export_4.pl
Executable 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;
|
58
test_regress/t/t_order_dpi_export_4.v
Normal file
58
test_regress/t/t_order_dpi_export_4.v
Normal 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
|
||||
|
39
test_regress/t/t_order_dpi_export_5.cpp
Normal file
39
test_regress/t/t_order_dpi_export_5.cpp
Normal 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;
|
||||
}
|
24
test_regress/t/t_order_dpi_export_5.pl
Executable file
24
test_regress/t/t_order_dpi_export_5.pl
Executable 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;
|
46
test_regress/t/t_order_dpi_export_5.v
Normal file
46
test_regress/t/t_order_dpi_export_5.v
Normal 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
|
||||
|
Loading…
Reference in New Issue
Block a user