Improve propagation of process requirement and decouple it from suspendability (#4321)
This commit is contained in:
parent
ced1dfeffd
commit
4bdda3f240
|
|
@ -419,7 +419,7 @@ void EmitCFunc::emitCCallArgs(const AstNodeCCall* nodep, const string& selfPoint
|
||||||
}
|
}
|
||||||
if (nodep->funcp()->needProcess()) {
|
if (nodep->funcp()->needProcess()) {
|
||||||
if (comma) puts(", ");
|
if (comma) puts(", ");
|
||||||
if (VN_IS(nodep->backp(), CAwait)) {
|
if (VN_IS(nodep->backp(), CAwait) || !nodep->funcp()->isCoroutine()) {
|
||||||
puts("vlProcess");
|
puts("vlProcess");
|
||||||
} else {
|
} else {
|
||||||
puts("std::make_shared<VlProcess>()");
|
puts("std::make_shared<VlProcess>()");
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@
|
||||||
|
|
||||||
#include "V3EmitCConstInit.h"
|
#include "V3EmitCConstInit.h"
|
||||||
#include "V3Global.h"
|
#include "V3Global.h"
|
||||||
|
#include "V3MemberMap.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
@ -115,6 +116,7 @@ public:
|
||||||
|
|
||||||
class EmitCFunc VL_NOT_FINAL : public EmitCConstInit {
|
class EmitCFunc VL_NOT_FINAL : public EmitCConstInit {
|
||||||
private:
|
private:
|
||||||
|
VMemberMap m_memberMap;
|
||||||
AstVarRef* m_wideTempRefp = nullptr; // Variable that _WW macros should be setting
|
AstVarRef* m_wideTempRefp = nullptr; // Variable that _WW macros should be setting
|
||||||
int m_labelNum = 0; // Next label number
|
int m_labelNum = 0; // Next label number
|
||||||
int m_splitSize = 0; // # of cfunc nodes placed into output file
|
int m_splitSize = 0; // # of cfunc nodes placed into output file
|
||||||
|
|
@ -148,6 +150,22 @@ protected:
|
||||||
bool m_useSelfForThis = false; // Replace "this" with "vlSelf"
|
bool m_useSelfForThis = false; // Replace "this" with "vlSelf"
|
||||||
const AstNodeModule* m_modp = nullptr; // Current module being emitted
|
const AstNodeModule* m_modp = nullptr; // Current module being emitted
|
||||||
const AstCFunc* m_cfuncp = nullptr; // Current function being emitted
|
const AstCFunc* m_cfuncp = nullptr; // Current function being emitted
|
||||||
|
bool m_instantiatesOwnProcess = false;
|
||||||
|
|
||||||
|
bool constructorNeedsProcess(const AstClass* const classp) {
|
||||||
|
const AstNode* const newp = m_memberMap.findMember(classp, "new");
|
||||||
|
if (!newp) return false;
|
||||||
|
const AstCFunc* const ctorp = VN_CAST(newp, CFunc);
|
||||||
|
if (!ctorp) return false;
|
||||||
|
UASSERT_OBJ(ctorp->isConstructor(), ctorp, "`new` is not a constructor!");
|
||||||
|
return ctorp->needProcess();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool constructorNeedsProcess(const AstNodeDType* const dtypep) {
|
||||||
|
if (const AstClassRefDType* const crefdtypep = VN_CAST(dtypep, ClassRefDType))
|
||||||
|
return constructorNeedsProcess(crefdtypep->classp());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// METHODS
|
// METHODS
|
||||||
|
|
@ -212,13 +230,19 @@ public:
|
||||||
comma = true;
|
comma = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
template <typename T>
|
||||||
|
string optionalProcArg(const T* const nodep) {
|
||||||
|
return (nodep && constructorNeedsProcess(nodep)) ? "vlProcess, " : "";
|
||||||
|
}
|
||||||
|
|
||||||
// VISITORS
|
// VISITORS
|
||||||
using EmitCConstInit::visit;
|
using EmitCConstInit::visit;
|
||||||
void visit(AstCFunc* nodep) override {
|
void visit(AstCFunc* nodep) override {
|
||||||
VL_RESTORER(m_useSelfForThis);
|
VL_RESTORER(m_useSelfForThis);
|
||||||
VL_RESTORER(m_cfuncp);
|
VL_RESTORER(m_cfuncp);
|
||||||
|
VL_RESTORER(m_instantiatesOwnProcess)
|
||||||
m_cfuncp = nodep;
|
m_cfuncp = nodep;
|
||||||
|
m_instantiatesOwnProcess = false;
|
||||||
|
|
||||||
splitSizeInc(nodep);
|
splitSizeInc(nodep);
|
||||||
|
|
||||||
|
|
@ -231,7 +255,8 @@ public:
|
||||||
if (!nodep->baseCtors().empty()) {
|
if (!nodep->baseCtors().empty()) {
|
||||||
puts(": ");
|
puts(": ");
|
||||||
puts(nodep->baseCtors());
|
puts(nodep->baseCtors());
|
||||||
puts("(vlSymsp");
|
const AstClass* const classp = VN_CAST(nodep->scopep()->modp(), Class);
|
||||||
|
bool baseCtorCall = false;
|
||||||
// Find call to super.new to get the arguments
|
// Find call to super.new to get the arguments
|
||||||
for (AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
|
for (AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
|
||||||
AstNode* exprp;
|
AstNode* exprp;
|
||||||
|
|
@ -241,11 +266,14 @@ public:
|
||||||
exprp = stmtp;
|
exprp = stmtp;
|
||||||
}
|
}
|
||||||
if (AstCNew* const newRefp = VN_CAST(exprp, CNew)) {
|
if (AstCNew* const newRefp = VN_CAST(exprp, CNew)) {
|
||||||
|
puts("(" + optionalProcArg(classp) + "vlSymsp");
|
||||||
|
baseCtorCall = true;
|
||||||
putCommaIterateNext(newRefp->argsp(), true);
|
putCommaIterateNext(newRefp->argsp(), true);
|
||||||
|
puts(")");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
puts(")");
|
if (!baseCtorCall) { puts("(" + optionalProcArg(classp) + "vlSymsp)"); }
|
||||||
}
|
}
|
||||||
puts(" {\n");
|
puts(" {\n");
|
||||||
|
|
||||||
|
|
@ -265,6 +293,23 @@ public:
|
||||||
puts(nodep->isLoose() ? "__" : "::");
|
puts(nodep->isLoose() ? "__" : "::");
|
||||||
puts(nodep->nameProtect() + "\\n\"); );\n");
|
puts(nodep->nameProtect() + "\\n\"); );\n");
|
||||||
|
|
||||||
|
// Instantiate a process class if it's going to be needed somewhere later
|
||||||
|
nodep->forall([&](const AstNodeCCall* ccallp) -> bool {
|
||||||
|
if (ccallp->funcp()->needProcess()
|
||||||
|
&& (ccallp->funcp()->isCoroutine() == VN_IS(ccallp->backp(), CAwait))) {
|
||||||
|
if (!nodep->needProcess() && !m_instantiatesOwnProcess) {
|
||||||
|
m_instantiatesOwnProcess = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
if (m_instantiatesOwnProcess) {
|
||||||
|
AstNode* const vlprocp = new AstCStmt{
|
||||||
|
nodep->fileline(), "VlProcessRef vlProcess = std::make_shared<VlProcess>();\n"};
|
||||||
|
nodep->stmtsp()->addHereThisAsNext(vlprocp);
|
||||||
|
}
|
||||||
|
|
||||||
for (AstNode* subnodep = nodep->argsp(); subnodep; subnodep = subnodep->nextp()) {
|
for (AstNode* subnodep = nodep->argsp(); subnodep; subnodep = subnodep->nextp()) {
|
||||||
if (AstVar* const varp = VN_CAST(subnodep, Var)) {
|
if (AstVar* const varp = VN_CAST(subnodep, Var)) {
|
||||||
if (varp->isFuncReturn()) emitVarDecl(varp);
|
if (varp->isFuncReturn()) emitVarDecl(varp);
|
||||||
|
|
@ -441,8 +486,9 @@ public:
|
||||||
// super.new case
|
// super.new case
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// assignment case
|
// assignment case;
|
||||||
puts("VL_NEW(" + prefixNameProtect(nodep->dtypep()) + ", vlSymsp");
|
puts("VL_NEW(" + prefixNameProtect(nodep->dtypep()) + ", "
|
||||||
|
+ optionalProcArg(nodep->dtypep()) + "vlSymsp");
|
||||||
putCommaIterateNext(nodep->argsp(), true);
|
putCommaIterateNext(nodep->argsp(), true);
|
||||||
puts(")");
|
puts(")");
|
||||||
}
|
}
|
||||||
|
|
@ -1087,7 +1133,8 @@ public:
|
||||||
puts(")");
|
puts(")");
|
||||||
}
|
}
|
||||||
void visit(AstNewCopy* nodep) override {
|
void visit(AstNewCopy* nodep) override {
|
||||||
puts("VL_NEW(" + prefixNameProtect(nodep->dtypep()) + ", ");
|
puts("VL_NEW(" + prefixNameProtect(nodep->dtypep()) + ", "
|
||||||
|
+ optionalProcArg(nodep->dtypep()));
|
||||||
puts("*"); // i.e. make into a reference
|
puts("*"); // i.e. make into a reference
|
||||||
iterateAndNextConstNull(nodep->rhsp());
|
iterateAndNextConstNull(nodep->rhsp());
|
||||||
puts(")");
|
puts(")");
|
||||||
|
|
|
||||||
|
|
@ -274,7 +274,6 @@ void orderSequentially(AstCFunc* funcp, const LogicByScope& lbs) {
|
||||||
subFuncp = createNewSubFuncp(scopep);
|
subFuncp = createNewSubFuncp(scopep);
|
||||||
subFuncp->name(subFuncp->name() + "__" + cvtToStr(scopep->user2Inc()));
|
subFuncp->name(subFuncp->name() + "__" + cvtToStr(scopep->user2Inc()));
|
||||||
subFuncp->rtnType("VlCoroutine");
|
subFuncp->rtnType("VlCoroutine");
|
||||||
if (procp->needProcess()) subFuncp->setNeedProcess();
|
|
||||||
if (VN_IS(procp, Always)) {
|
if (VN_IS(procp, Always)) {
|
||||||
subFuncp->slow(false);
|
subFuncp->slow(false);
|
||||||
FileLine* const flp = procp->fileline();
|
FileLine* const flp = procp->fileline();
|
||||||
|
|
@ -283,6 +282,7 @@ void orderSequentially(AstCFunc* funcp, const LogicByScope& lbs) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
subFuncp->addStmtsp(bodyp);
|
subFuncp->addStmtsp(bodyp);
|
||||||
|
if (procp->needProcess()) subFuncp->setNeedProcess();
|
||||||
splitCheck(subFuncp);
|
splitCheck(subFuncp);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
237
src/V3Timing.cpp
237
src/V3Timing.cpp
|
|
@ -64,41 +64,85 @@ VL_DEFINE_DEBUG_FUNCTIONS;
|
||||||
|
|
||||||
// ######################################################################
|
// ######################################################################
|
||||||
|
|
||||||
enum TimingFlag : uint8_t {
|
enum NodeFlag : uint8_t {
|
||||||
// Properties of flags with higher numbers include properties of flags with
|
T_SUSPENDEE = 1 << 0, // Suspendable (due to dependence on another suspendable)
|
||||||
// lower numbers
|
T_SUSPENDER = 1 << 1, // Suspendable (has timing control)
|
||||||
T_NORM = 0, // Normal non-suspendable process
|
T_HAS_PROC = 1 << 2, // Has an associated std::process
|
||||||
T_SUSP = 1, // Suspendable
|
T_CALLS_PROC_SELF = 1 << 3, // Calls std::process::self
|
||||||
T_PROC = 2 // Suspendable with process metadata
|
};
|
||||||
|
|
||||||
|
enum ForkType : uint8_t {
|
||||||
|
F_NONE = 0, // Not under a fork
|
||||||
|
F_MIGHT_SUSPEND = 1 << 0, // Fork might suspend the execution of current process
|
||||||
|
F_MIGHT_NEED_PROC = 1 << 1, // Fork might need a process (any fork really)
|
||||||
|
};
|
||||||
|
|
||||||
|
enum PropagationType : uint8_t {
|
||||||
|
P_CALL = 1, // Propagation through call to a function/task/method
|
||||||
|
P_FORK = 2, // Propagation due to fork's behaviour
|
||||||
|
P_SIGNATURE = 3, // Propagation required to maintain C++ function's signature requirements
|
||||||
};
|
};
|
||||||
|
|
||||||
// ######################################################################
|
// ######################################################################
|
||||||
// Detect nodes affected by timing
|
// Detect nodes affected by timing and/or requiring a process
|
||||||
|
|
||||||
class TimingSuspendableVisitor final : public VNVisitor {
|
class TimingSuspendableVisitor final : public VNVisitor {
|
||||||
private:
|
private:
|
||||||
// TYPES
|
// TYPES
|
||||||
// Vertex of a dependency graph of suspendable nodes, e.g. if a node (process or task) is
|
// Vertex of a dependency graph of suspendable nodes, e.g. if a node (process or task) is
|
||||||
// suspendable, all its dependents should also be suspendable
|
// suspendable, all its dependents should also be suspendable
|
||||||
class TimingDependencyVertex final : public V3GraphVertex {
|
class DepVtx VL_NOT_FINAL : public V3GraphVertex {
|
||||||
|
AstClass* const m_classp; // Class associated with a method
|
||||||
AstNode* const m_nodep; // AST node represented by this graph vertex
|
AstNode* const m_nodep; // AST node represented by this graph vertex
|
||||||
|
|
||||||
// ACCESSORS
|
// ACCESSORS
|
||||||
string name() const override VL_MT_STABLE {
|
string name() const override VL_MT_STABLE {
|
||||||
|
if (m_classp) {
|
||||||
|
if (VN_IS(nodep(), CFunc)) {
|
||||||
|
return cvtToHex(nodep()) + ' ' + classp()->name() + "::" + nodep()->name();
|
||||||
|
}
|
||||||
|
}
|
||||||
return cvtToHex(nodep()) + ' ' + nodep()->prettyTypeName();
|
return cvtToHex(nodep()) + ' ' + nodep()->prettyTypeName();
|
||||||
}
|
}
|
||||||
FileLine* fileline() const override { return nodep()->fileline(); }
|
FileLine* fileline() const override { return nodep()->fileline(); }
|
||||||
string dotColor() const override { return nodep()->user2() ? "red" : "black"; }
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// CONSTRUCTORS
|
// CONSTRUCTORS
|
||||||
TimingDependencyVertex(V3Graph* graphp, AstNode* nodep)
|
DepVtx(V3Graph* graphp, AstNode* nodep, AstClass* classp)
|
||||||
: V3GraphVertex{graphp}
|
: V3GraphVertex{graphp}
|
||||||
|
, m_classp{classp}
|
||||||
, m_nodep{nodep} {}
|
, m_nodep{nodep} {}
|
||||||
~TimingDependencyVertex() override = default;
|
~DepVtx() override = default;
|
||||||
|
|
||||||
// ACCESSORS
|
// ACCESSORS
|
||||||
virtual AstNode* nodep() const VL_MT_STABLE { return m_nodep; }
|
virtual AstNode* nodep() const VL_MT_STABLE { return m_nodep; }
|
||||||
|
virtual AstNode* classp() const VL_MT_STABLE { return m_classp; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class SuspendDepVtx final : public DepVtx {
|
||||||
|
string dotColor() const override {
|
||||||
|
if (nodep()->user2() & T_SUSPENDER) return "red";
|
||||||
|
if (nodep()->user2() & T_SUSPENDEE) return "blue";
|
||||||
|
return "black";
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
SuspendDepVtx(V3Graph* graphp, AstNode* nodep, AstClass* classp)
|
||||||
|
: DepVtx{graphp, nodep, classp} {}
|
||||||
|
~SuspendDepVtx() override = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
class NeedsProcDepVtx final : public DepVtx {
|
||||||
|
string dotColor() const override {
|
||||||
|
if (nodep()->user2() & T_CALLS_PROC_SELF) return "red";
|
||||||
|
if (nodep()->user2() & T_HAS_PROC) return "blue";
|
||||||
|
return "black";
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
NeedsProcDepVtx(V3Graph* graphp, AstNode* nodep, AstClass* classp)
|
||||||
|
: DepVtx{graphp, nodep, classp} {}
|
||||||
|
~NeedsProcDepVtx() override = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
// NODE STATE
|
// NODE STATE
|
||||||
|
|
@ -109,41 +153,70 @@ private:
|
||||||
// process/task suspendable
|
// process/task suspendable
|
||||||
// and to T_PROC if it
|
// and to T_PROC if it
|
||||||
// needs process metadata.
|
// needs process metadata.
|
||||||
// Ast{NodeProcedure,CFunc,Begin}::user3() -> DependencyVertex*. Vertex in m_depGraph
|
// Ast{NodeProcedure,CFunc,Begin}::user3() -> DependencyVertex*. Vertex in m_suspGraph
|
||||||
|
// Ast{NodeProcedure,CFunc,Begin}::user3() -> DependencyVertex*. Vertex in m_procGraph
|
||||||
const VNUser1InUse m_user1InUse;
|
const VNUser1InUse m_user1InUse;
|
||||||
const VNUser2InUse m_user2InUse;
|
const VNUser2InUse m_user2InUse;
|
||||||
const VNUser3InUse m_user3InUse;
|
const VNUser3InUse m_user3InUse;
|
||||||
|
const VNUser5InUse m_user5InUse;
|
||||||
|
|
||||||
// STATE
|
// STATE
|
||||||
VMemberMap memberMap; // Member names cached for fast lookup
|
VMemberMap memberMap; // Member names cached for fast lookup
|
||||||
AstClass* m_classp = nullptr; // Current class
|
AstClass* m_classp = nullptr; // Current class
|
||||||
AstNode* m_procp = nullptr; // NodeProcedure/CFunc/Begin we're under
|
AstNode* m_procp = nullptr; // NodeProcedure/CFunc/Begin we're under
|
||||||
V3Graph m_depGraph; // Dependency graph where a node is a dependency of another if it being
|
uint8_t m_underFork = F_NONE; // F_NONE or flags of a fork we are under
|
||||||
// suspendable makes the other node suspendable
|
V3Graph m_suspGraph; // Dependency graph where a node is a dependency of another if it being
|
||||||
|
// suspendable makes the other node suspendable
|
||||||
|
V3Graph m_procGraph; // Dependency graph where a node is a dependency of another if it being
|
||||||
|
// suspendable makes the other node suspendable
|
||||||
|
|
||||||
// METHODS
|
// METHODS
|
||||||
// Get or create the dependency vertex for the given node
|
// Get or create the dependency vertex for the given node
|
||||||
TimingDependencyVertex* getDependencyVertex(AstNode* const nodep) {
|
DepVtx* getSuspendDepVtx(AstNode* const nodep) {
|
||||||
if (!nodep->user3p()) nodep->user3p(new TimingDependencyVertex{&m_depGraph, nodep});
|
AstClass* classp = nullptr;
|
||||||
return nodep->user3u().to<TimingDependencyVertex*>();
|
if (AstCFunc* funcp = VN_CAST(nodep, CFunc)) {
|
||||||
|
if (funcp->scopep() && funcp->scopep()->modp()) {
|
||||||
|
classp = VN_CAST(funcp->scopep()->modp(), Class);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!nodep->user3p()) nodep->user3p(new SuspendDepVtx{&m_suspGraph, nodep, classp});
|
||||||
|
return nodep->user3u().to<SuspendDepVtx*>();
|
||||||
|
}
|
||||||
|
DepVtx* getNeedsProcDepVtx(AstNode* const nodep) {
|
||||||
|
AstClass* classp = nullptr;
|
||||||
|
if (AstCFunc* funcp = VN_CAST(nodep, CFunc)) {
|
||||||
|
if (funcp->scopep() && funcp->scopep()->modp()) {
|
||||||
|
classp = VN_CAST(funcp->scopep()->modp(), Class);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!nodep->user5p()) nodep->user5p(new NeedsProcDepVtx{&m_procGraph, nodep, classp});
|
||||||
|
return nodep->user5u().to<NeedsProcDepVtx*>();
|
||||||
}
|
}
|
||||||
// Set timing flag of a node
|
// Set timing flag of a node
|
||||||
bool setTimingFlag(AstNode* nodep, int flag) {
|
bool passFlag(const AstNode* from, AstNode* to, NodeFlag flag) {
|
||||||
// Properties of flags with higher numbers include properties of flags with lower
|
if ((from->user2() & flag) && !(to->user2() & flag)) {
|
||||||
// numbers, so modify nodep->user2() only if it will increase.
|
to->user2(to->user2() | flag);
|
||||||
if (nodep->user2() < flag) {
|
|
||||||
nodep->user2(flag);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Propagate suspendable/needProcess flag to all nodes that depend on the given one
|
// Propagate flag to all nodes that depend on the given one
|
||||||
void propagateTimingFlags(TimingDependencyVertex* const vxp) {
|
void propagateFlags(DepVtx* const vxp, NodeFlag flag) {
|
||||||
|
auto* const parentp = vxp->nodep();
|
||||||
|
for (V3GraphEdge* edgep = vxp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
||||||
|
auto* const depVxp = static_cast<DepVtx*>(edgep->top());
|
||||||
|
AstNode* const depp = depVxp->nodep();
|
||||||
|
if (passFlag(parentp, depp, flag)) propagateFlags(depVxp, flag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
template <typename Predicate>
|
||||||
|
void propagateFlagsReversedIf(DepVtx* const vxp, NodeFlag flag, Predicate p) {
|
||||||
auto* const parentp = vxp->nodep();
|
auto* const parentp = vxp->nodep();
|
||||||
for (V3GraphEdge* edgep = vxp->inBeginp(); edgep; edgep = edgep->inNextp()) {
|
for (V3GraphEdge* edgep = vxp->inBeginp(); edgep; edgep = edgep->inNextp()) {
|
||||||
auto* const depVxp = static_cast<TimingDependencyVertex*>(edgep->fromp());
|
auto* const depVxp = static_cast<DepVtx*>(edgep->fromp());
|
||||||
AstNode* const depp = depVxp->nodep();
|
AstNode* const depp = depVxp->nodep();
|
||||||
if (setTimingFlag(depp, parentp->user2())) propagateTimingFlags(depVxp);
|
if (p(edgep) && passFlag(parentp, depp, flag))
|
||||||
|
propagateFlagsReversedIf(depVxp, flag, p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -157,14 +230,19 @@ private:
|
||||||
void visit(AstNodeProcedure* nodep) override {
|
void visit(AstNodeProcedure* nodep) override {
|
||||||
VL_RESTORER(m_procp);
|
VL_RESTORER(m_procp);
|
||||||
m_procp = nodep;
|
m_procp = nodep;
|
||||||
|
if (nodep->needProcess()) nodep->user2(T_HAS_PROC | T_CALLS_PROC_SELF);
|
||||||
|
if (VN_IS(nodep, Always)) {
|
||||||
|
UINFO(1, "Always does " << (nodep->needProcess() ? "" : "NOT ") << "need process\n");
|
||||||
|
}
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
}
|
}
|
||||||
void visit(AstCFunc* nodep) override {
|
void visit(AstCFunc* nodep) override {
|
||||||
VL_RESTORER(m_procp);
|
VL_RESTORER(m_procp);
|
||||||
m_procp = nodep;
|
m_procp = nodep;
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
TimingDependencyVertex* const vxp = getDependencyVertex(nodep);
|
if (nodep->needProcess()) nodep->user2(T_HAS_PROC | T_CALLS_PROC_SELF);
|
||||||
if (nodep->needProcess()) nodep->user2(T_PROC);
|
DepVtx* const sVxp = getSuspendDepVtx(nodep);
|
||||||
|
DepVtx* const pVxp = getNeedsProcDepVtx(nodep);
|
||||||
if (!m_classp) return;
|
if (!m_classp) return;
|
||||||
|
|
||||||
// Go over overridden functions
|
// Go over overridden functions
|
||||||
|
|
@ -184,16 +262,13 @@ private:
|
||||||
// virtual or not.
|
// virtual or not.
|
||||||
if (auto* const overriddenp
|
if (auto* const overriddenp
|
||||||
= VN_CAST(memberMap.findMember(cextp->classp(), nodep->name()), CFunc)) {
|
= VN_CAST(memberMap.findMember(cextp->classp(), nodep->name()), CFunc)) {
|
||||||
setTimingFlag(nodep, overriddenp->user2());
|
// Suspendability and process affects typing, so they propagate both ways
|
||||||
if (nodep->user2()
|
DepVtx* const overriddenSVxp = getSuspendDepVtx(overriddenp);
|
||||||
< T_PROC) { // Add a vertex only if the flag can still change
|
DepVtx* const overriddenPVxp = getNeedsProcDepVtx(overriddenp);
|
||||||
// Make a dependency cycle, as being suspendable should propagate both up
|
new V3GraphEdge{&m_suspGraph, sVxp, overriddenSVxp, P_SIGNATURE};
|
||||||
// and down the inheritance tree
|
new V3GraphEdge{&m_suspGraph, overriddenSVxp, sVxp, P_SIGNATURE};
|
||||||
TimingDependencyVertex* const overriddenVxp
|
new V3GraphEdge{&m_procGraph, pVxp, overriddenPVxp, P_SIGNATURE};
|
||||||
= getDependencyVertex(overriddenp);
|
new V3GraphEdge{&m_procGraph, overriddenPVxp, pVxp, P_SIGNATURE};
|
||||||
new V3GraphEdge{&m_depGraph, vxp, overriddenVxp, 1};
|
|
||||||
new V3GraphEdge{&m_depGraph, overriddenVxp, vxp, 1};
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
AstClassExtends* more_extends = cextp->classp()->extendsp();
|
AstClassExtends* more_extends = cextp->classp()->extendsp();
|
||||||
if (more_extends) extends.push(more_extends);
|
if (more_extends) extends.push(more_extends);
|
||||||
|
|
@ -202,30 +277,49 @@ private:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void visit(AstNodeCCall* nodep) override {
|
void visit(AstNodeCCall* nodep) override {
|
||||||
setTimingFlag(m_procp, nodep->funcp()->user2());
|
if (!m_underFork || (m_underFork & F_MIGHT_SUSPEND))
|
||||||
if (m_procp->user2() < T_PROC) { // Add a vertex only if the flag can still change
|
new V3GraphEdge{&m_suspGraph, getSuspendDepVtx(nodep->funcp()),
|
||||||
TimingDependencyVertex* const procVxp = getDependencyVertex(m_procp);
|
getSuspendDepVtx(m_procp), m_underFork ? P_FORK : P_CALL};
|
||||||
TimingDependencyVertex* const funcVxp = getDependencyVertex(nodep->funcp());
|
|
||||||
new V3GraphEdge{&m_depGraph, procVxp, funcVxp, 1};
|
if (!m_underFork)
|
||||||
iterateChildren(nodep);
|
new V3GraphEdge{&m_procGraph, getNeedsProcDepVtx(nodep->funcp()),
|
||||||
}
|
getNeedsProcDepVtx(m_procp), P_CALL};
|
||||||
|
|
||||||
|
iterateChildren(nodep);
|
||||||
}
|
}
|
||||||
void visit(AstBegin* nodep) override {
|
void visit(AstBegin* nodep) override {
|
||||||
VL_RESTORER(m_procp);
|
VL_RESTORER(m_procp);
|
||||||
|
VL_RESTORER(m_underFork);
|
||||||
|
|
||||||
|
if (!m_underFork || (m_underFork & F_MIGHT_SUSPEND))
|
||||||
|
new V3GraphEdge{&m_suspGraph, getSuspendDepVtx(nodep), getSuspendDepVtx(m_procp),
|
||||||
|
m_underFork ? P_FORK : P_CALL};
|
||||||
|
|
||||||
|
if (!m_underFork)
|
||||||
|
new V3GraphEdge{&m_procGraph, getNeedsProcDepVtx(nodep), getNeedsProcDepVtx(m_procp),
|
||||||
|
P_CALL};
|
||||||
|
|
||||||
m_procp = nodep;
|
m_procp = nodep;
|
||||||
|
m_underFork = 0;
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
}
|
}
|
||||||
void visit(AstFork* nodep) override {
|
void visit(AstFork* nodep) override {
|
||||||
|
VL_RESTORER(m_underFork);
|
||||||
|
|
||||||
v3Global.setUsesTiming(); // Even if there are no event controls, we have to set this flag
|
v3Global.setUsesTiming(); // Even if there are no event controls, we have to set this flag
|
||||||
// so that transformForks() in V3SchedTiming gets called and
|
// so that transformForks() in V3SchedTiming gets called and
|
||||||
// removes all forks and begins
|
// removes all forks and begins
|
||||||
if (nodep->isTimingControl() && m_procp) m_procp->user2(T_SUSP);
|
if (nodep->isTimingControl() && m_procp) {
|
||||||
|
m_procp->user2(T_SUSPENDEE | T_SUSPENDER);
|
||||||
|
m_underFork |= F_MIGHT_SUSPEND;
|
||||||
|
}
|
||||||
|
m_underFork |= F_MIGHT_NEED_PROC;
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
}
|
}
|
||||||
void visit(AstNode* nodep) override {
|
void visit(AstNode* nodep) override {
|
||||||
if (nodep->isTimingControl()) {
|
if (nodep->isTimingControl()) {
|
||||||
v3Global.setUsesTiming();
|
v3Global.setUsesTiming();
|
||||||
if (m_procp) m_procp->user2(T_SUSP);
|
if (m_procp) m_procp->user2(T_SUSPENDEE | T_SUSPENDER);
|
||||||
}
|
}
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
}
|
}
|
||||||
|
|
@ -237,12 +331,29 @@ public:
|
||||||
// CONSTRUCTORS
|
// CONSTRUCTORS
|
||||||
explicit TimingSuspendableVisitor(AstNetlist* nodep) {
|
explicit TimingSuspendableVisitor(AstNetlist* nodep) {
|
||||||
iterate(nodep);
|
iterate(nodep);
|
||||||
m_depGraph.removeTransitiveEdges();
|
m_suspGraph.removeTransitiveEdges();
|
||||||
for (V3GraphVertex* vxp = m_depGraph.verticesBeginp(); vxp; vxp = vxp->verticesNextp()) {
|
m_procGraph.removeTransitiveEdges();
|
||||||
TimingDependencyVertex* const depVxp = static_cast<TimingDependencyVertex*>(vxp);
|
// Propagate suspendability
|
||||||
if (depVxp->nodep()->user2()) propagateTimingFlags(depVxp);
|
for (V3GraphVertex* vxp = m_suspGraph.verticesBeginp(); vxp; vxp = vxp->verticesNextp()) {
|
||||||
|
DepVtx* const depVxp = static_cast<DepVtx*>(vxp);
|
||||||
|
if (depVxp->nodep()->user2() & T_SUSPENDEE) propagateFlags(depVxp, T_SUSPENDEE);
|
||||||
}
|
}
|
||||||
if (dumpGraphLevel() >= 6) m_depGraph.dumpDotFilePrefixed("timing_deps");
|
if (dumpGraphLevel() >= 6) m_suspGraph.dumpDotFilePrefixed("timing_deps");
|
||||||
|
// Propagate process
|
||||||
|
for (V3GraphVertex* vxp = m_procGraph.verticesBeginp(); vxp; vxp = vxp->verticesNextp()) {
|
||||||
|
DepVtx* const depVxp = static_cast<DepVtx*>(vxp);
|
||||||
|
if (depVxp->nodep()->user2() & T_HAS_PROC) propagateFlags(depVxp, T_HAS_PROC);
|
||||||
|
}
|
||||||
|
// Propagate process downwards (from caller to callee) for suspendable calls
|
||||||
|
for (V3GraphVertex* vxp = m_suspGraph.verticesBeginp(); vxp; vxp = vxp->verticesNextp()) {
|
||||||
|
DepVtx* const depVxp = static_cast<DepVtx*>(vxp);
|
||||||
|
if (depVxp->nodep()->user2() & T_HAS_PROC)
|
||||||
|
propagateFlagsReversedIf(depVxp, T_HAS_PROC, [&](const V3GraphEdge* e) -> bool {
|
||||||
|
return (e->weight() != P_FORK)
|
||||||
|
&& (static_cast<DepVtx*>(e->top())->nodep()->user2() & T_SUSPENDEE);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (dumpGraphLevel() >= 6) m_procGraph.dumpDotFilePrefixed("proc_deps");
|
||||||
}
|
}
|
||||||
~TimingSuspendableVisitor() override = default;
|
~TimingSuspendableVisitor() override = default;
|
||||||
};
|
};
|
||||||
|
|
@ -488,7 +599,7 @@ private:
|
||||||
void addProcessInfo(AstCMethodHard* const methodp) const {
|
void addProcessInfo(AstCMethodHard* const methodp) const {
|
||||||
FileLine* const flp = methodp->fileline();
|
FileLine* const flp = methodp->fileline();
|
||||||
AstCExpr* const ap = new AstCExpr{
|
AstCExpr* const ap = new AstCExpr{
|
||||||
flp, m_procp && m_procp->user2() == T_PROC ? "vlProcess" : "nullptr", 0};
|
flp, m_procp && (m_procp->user2() & T_HAS_PROC) ? "vlProcess" : "nullptr", 0};
|
||||||
ap->dtypeSetVoid();
|
ap->dtypeSetVoid();
|
||||||
methodp->addPinsp(ap);
|
methodp->addPinsp(ap);
|
||||||
}
|
}
|
||||||
|
|
@ -587,8 +698,8 @@ private:
|
||||||
VL_RESTORER(m_procp);
|
VL_RESTORER(m_procp);
|
||||||
m_procp = nodep;
|
m_procp = nodep;
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
if (nodep->user2() >= T_SUSP) nodep->setSuspendable();
|
if (nodep->user2() & T_SUSPENDEE) nodep->setSuspendable();
|
||||||
if (nodep->user2() >= T_PROC) nodep->setNeedProcess();
|
if (nodep->user2() & T_HAS_PROC) nodep->setNeedProcess();
|
||||||
}
|
}
|
||||||
void visit(AstInitial* nodep) override {
|
void visit(AstInitial* nodep) override {
|
||||||
visit(static_cast<AstNodeProcedure*>(nodep));
|
visit(static_cast<AstNodeProcedure*>(nodep));
|
||||||
|
|
@ -601,9 +712,14 @@ private:
|
||||||
if (nodep->user1SetOnce()) return;
|
if (nodep->user1SetOnce()) return;
|
||||||
VL_RESTORER(m_procp);
|
VL_RESTORER(m_procp);
|
||||||
m_procp = nodep;
|
m_procp = nodep;
|
||||||
|
|
||||||
|
// Workaround for killing `always` processes (doing that is pretty much UB)
|
||||||
|
// TODO: Disallow killing `always` at runtime (throw an error)
|
||||||
|
if (nodep->user2() & T_HAS_PROC) nodep->user2(nodep->user2() | T_SUSPENDEE);
|
||||||
|
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
if (!nodep->user2()) return;
|
if (nodep->user2() & T_HAS_PROC) nodep->setNeedProcess();
|
||||||
if (nodep->user2() == T_PROC) nodep->setNeedProcess();
|
if (!(nodep->user2() & T_SUSPENDEE)) return;
|
||||||
nodep->setSuspendable();
|
nodep->setSuspendable();
|
||||||
FileLine* const flp = nodep->fileline();
|
FileLine* const flp = nodep->fileline();
|
||||||
AstSenTree* const sensesp = m_activep->sensesp();
|
AstSenTree* const sensesp = m_activep->sensesp();
|
||||||
|
|
@ -624,7 +740,9 @@ private:
|
||||||
VL_RESTORER(m_procp);
|
VL_RESTORER(m_procp);
|
||||||
m_procp = nodep;
|
m_procp = nodep;
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
if (!nodep->user2()) return;
|
if (nodep->user2() & T_HAS_PROC) nodep->setNeedProcess();
|
||||||
|
if (!(nodep->user2() & T_SUSPENDEE)) return;
|
||||||
|
|
||||||
nodep->rtnType("VlCoroutine");
|
nodep->rtnType("VlCoroutine");
|
||||||
// If in a class, create a shared pointer to 'this'
|
// If in a class, create a shared pointer to 'this'
|
||||||
if (m_classp) nodep->addInitsp(new AstCStmt{nodep->fileline(), "VL_KEEP_THIS;\n"});
|
if (m_classp) nodep->addInitsp(new AstCStmt{nodep->fileline(), "VL_KEEP_THIS;\n"});
|
||||||
|
|
@ -644,10 +762,9 @@ private:
|
||||||
firstCoStmtp->v3warn(E_UNSUPPORTED,
|
firstCoStmtp->v3warn(E_UNSUPPORTED,
|
||||||
"Unsupported: Timing controls inside DPI-exported tasks");
|
"Unsupported: Timing controls inside DPI-exported tasks");
|
||||||
}
|
}
|
||||||
if (nodep->user2() == T_PROC) nodep->setNeedProcess();
|
|
||||||
}
|
}
|
||||||
void visit(AstNodeCCall* nodep) override {
|
void visit(AstNodeCCall* nodep) override {
|
||||||
if (nodep->funcp()->user2() && !nodep->user1SetOnce()) { // If suspendable
|
if ((nodep->funcp()->user2() & T_SUSPENDEE) && !nodep->user1SetOnce()) { // If suspendable
|
||||||
VNRelinker relinker;
|
VNRelinker relinker;
|
||||||
nodep->unlinkFrBack(&relinker);
|
nodep->unlinkFrBack(&relinker);
|
||||||
AstCAwait* const awaitp = new AstCAwait{nodep->fileline(), nodep};
|
AstCAwait* const awaitp = new AstCAwait{nodep->fileline(), nodep};
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
#!/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(simulator => 1);
|
||||||
|
|
||||||
|
compile(
|
||||||
|
verilator_flags2 => ["--exe --main --timing"],
|
||||||
|
make_main => 0,
|
||||||
|
);
|
||||||
|
|
||||||
|
execute(
|
||||||
|
check_finished => 1,
|
||||||
|
);
|
||||||
|
|
||||||
|
ok(1);
|
||||||
|
1;
|
||||||
|
|
@ -0,0 +1,68 @@
|
||||||
|
// DESCRIPTION: Verilator: Verilog Test module
|
||||||
|
//
|
||||||
|
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||||
|
// any use, without warranty, 2023 by Antmicro Ltd.
|
||||||
|
// SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
event evt1, evt2;
|
||||||
|
|
||||||
|
class Foo;
|
||||||
|
process p;
|
||||||
|
bit event_received;
|
||||||
|
|
||||||
|
function new();
|
||||||
|
p = process::self();
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
virtual task ewait();
|
||||||
|
@evt1 $display("Foo received event `evt1`");
|
||||||
|
event_received = 1;
|
||||||
|
->evt2;
|
||||||
|
endtask
|
||||||
|
endclass
|
||||||
|
|
||||||
|
class Bar extends Foo;
|
||||||
|
function new();
|
||||||
|
super.new();
|
||||||
|
$display("Constructing Bar");
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
virtual task ewait();
|
||||||
|
@evt1 $display("Bar received event `evt1`");
|
||||||
|
event_received = 1;
|
||||||
|
endtask
|
||||||
|
endclass
|
||||||
|
|
||||||
|
module t();
|
||||||
|
initial begin
|
||||||
|
process p;
|
||||||
|
Foo foo;
|
||||||
|
Bar bar;
|
||||||
|
|
||||||
|
fork
|
||||||
|
begin
|
||||||
|
foo = new;
|
||||||
|
foo.ewait();
|
||||||
|
end
|
||||||
|
begin
|
||||||
|
bar = new;
|
||||||
|
p = process::self();
|
||||||
|
bar.ewait();
|
||||||
|
end
|
||||||
|
join_none
|
||||||
|
|
||||||
|
p.kill();
|
||||||
|
|
||||||
|
->evt1;
|
||||||
|
|
||||||
|
@evt2 begin
|
||||||
|
if (!foo.event_received)
|
||||||
|
$stop;
|
||||||
|
if (bar.event_received)
|
||||||
|
$stop;
|
||||||
|
|
||||||
|
$write("*-* All Finished *-*\n");
|
||||||
|
$finish;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
endmodule
|
||||||
Loading…
Reference in New Issue