parent
25dde58297
commit
ad3bcbb1bb
|
|
@ -81,11 +81,16 @@ constexpr IData VL_CLOG2_CE_Q(QData lhs) VL_PURE {
|
||||||
return lhs <= 1 ? 0 : VL_CLOG2_CE_Q((lhs + 1) >> 1ULL) + 1;
|
return lhs <= 1 ? 0 : VL_CLOG2_CE_Q((lhs + 1) >> 1ULL) + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
//===================================================================
|
// Metadata of processes
|
||||||
// VlProcess stores metadata of running processes
|
class VlProcess;
|
||||||
|
|
||||||
|
using VlProcessRef = std::shared_ptr<VlProcess>;
|
||||||
|
|
||||||
class VlProcess final {
|
class VlProcess final {
|
||||||
// MEMBERS
|
// MEMBERS
|
||||||
int m_state; // Current state of the process
|
int m_state; // Current state of the process
|
||||||
|
VlProcessRef m_parentp = nullptr; // Parent process, if exists
|
||||||
|
std::set<VlProcess*> m_children; // Active child processes
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// TYPES
|
// TYPES
|
||||||
|
|
@ -98,16 +103,34 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
// CONSTRUCTORS
|
// CONSTRUCTORS
|
||||||
|
// Construct independent process
|
||||||
VlProcess()
|
VlProcess()
|
||||||
: m_state{RUNNING} {}
|
: m_state{RUNNING} {}
|
||||||
|
// Construct child process of parent
|
||||||
|
VlProcess(VlProcessRef parentp)
|
||||||
|
: m_state{RUNNING}
|
||||||
|
, m_parentp{parentp} {
|
||||||
|
m_parentp->attach(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
~VlProcess() {
|
||||||
|
if (m_parentp) m_parentp->detach(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void attach(VlProcess* childp) { m_children.insert(childp); }
|
||||||
|
void detach(VlProcess* childp) { m_children.erase(childp); }
|
||||||
|
|
||||||
// METHODS
|
|
||||||
int state() { return m_state; }
|
int state() { return m_state; }
|
||||||
void state(int s) { m_state = s; }
|
void state(int s) { m_state = s; }
|
||||||
|
void disable() {
|
||||||
|
state(KILLED);
|
||||||
|
disable_fork();
|
||||||
|
}
|
||||||
|
void disable_fork() {
|
||||||
|
for (VlProcess* childp : m_children) childp->disable();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
using VlProcessRef = std::shared_ptr<VlProcess>;
|
|
||||||
|
|
||||||
inline std::string VL_TO_STRING(const VlProcessRef& p) { return std::string("process"); }
|
inline std::string VL_TO_STRING(const VlProcessRef& p) { return std::string("process"); }
|
||||||
|
|
||||||
//===================================================================
|
//===================================================================
|
||||||
|
|
|
||||||
|
|
@ -86,7 +86,7 @@ private:
|
||||||
bool m_recursive : 1; // Recursive or part of recursion
|
bool m_recursive : 1; // Recursive or part of recursion
|
||||||
bool m_underGenerate : 1; // Under generate (for warning)
|
bool m_underGenerate : 1; // Under generate (for warning)
|
||||||
bool m_virtual : 1; // Virtual method in class
|
bool m_virtual : 1; // Virtual method in class
|
||||||
bool m_needProcess : 1; // Implements part of a process that allocates std::process
|
bool m_needProcess : 1; // Needs access to VlProcess of the caller
|
||||||
VLifetime m_lifetime; // Lifetime
|
VLifetime m_lifetime; // Lifetime
|
||||||
VIsCached m_purity; // Pure state
|
VIsCached m_purity; // Pure state
|
||||||
|
|
||||||
|
|
@ -300,7 +300,7 @@ class AstNodeProcedure VL_NOT_FINAL : public AstNode {
|
||||||
// IEEE procedure: initial, final, always
|
// IEEE procedure: initial, final, always
|
||||||
// @astgen op2 := stmtsp : List[AstNode] // Note: op1 is used in some sub-types only
|
// @astgen op2 := stmtsp : List[AstNode] // Note: op1 is used in some sub-types only
|
||||||
bool m_suspendable : 1; // Is suspendable by a Delay, EventControl, etc.
|
bool m_suspendable : 1; // Is suspendable by a Delay, EventControl, etc.
|
||||||
bool m_needProcess : 1; // Implements part of a process that allocates std::process
|
bool m_needProcess : 1; // Uses VlProcess
|
||||||
protected:
|
protected:
|
||||||
AstNodeProcedure(VNType t, FileLine* fl, AstNode* stmtsp)
|
AstNodeProcedure(VNType t, FileLine* fl, AstNode* stmtsp)
|
||||||
: AstNode{t, fl} {
|
: AstNode{t, fl} {
|
||||||
|
|
@ -608,7 +608,7 @@ private:
|
||||||
bool m_dpiImportPrototype : 1; // This is the DPI import prototype (i.e.: provided by user)
|
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_dpiImportWrapper : 1; // Wrapper for invoking DPI import prototype from generated code
|
||||||
bool m_dpiTraceInit : 1; // DPI trace_init
|
bool m_dpiTraceInit : 1; // DPI trace_init
|
||||||
bool m_needProcess : 1; // Implements part of a process that allocates std::process
|
bool m_needProcess : 1; // Needs access to VlProcess of the caller
|
||||||
public:
|
public:
|
||||||
AstCFunc(FileLine* fl, const string& name, AstScope* scopep, const string& rtnType = "")
|
AstCFunc(FileLine* fl, const string& name, AstScope* scopep, const string& rtnType = "")
|
||||||
: ASTGEN_SUPER_CFunc(fl) {
|
: ASTGEN_SUPER_CFunc(fl) {
|
||||||
|
|
@ -2096,19 +2096,23 @@ class AstBegin final : public AstNodeBlock {
|
||||||
// Parents: statement
|
// Parents: statement
|
||||||
// @astgen op1 := genforp : Optional[AstNode]
|
// @astgen op1 := genforp : Optional[AstNode]
|
||||||
|
|
||||||
bool m_generate; // Underneath a generate
|
bool m_generate : 1; // Underneath a generate
|
||||||
const bool m_implied; // Not inserted by user
|
bool m_needProcess : 1; // Uses VlProcess
|
||||||
|
const bool m_implied : 1; // Not inserted by user
|
||||||
public:
|
public:
|
||||||
// Node that puts name into the output stream
|
// Node that puts name into the output stream
|
||||||
AstBegin(FileLine* fl, const string& name, AstNode* stmtsp, bool generate = false,
|
AstBegin(FileLine* fl, const string& name, AstNode* stmtsp, bool generate = false,
|
||||||
bool implied = false)
|
bool implied = false)
|
||||||
: ASTGEN_SUPER_Begin(fl, name, stmtsp)
|
: ASTGEN_SUPER_Begin(fl, name, stmtsp)
|
||||||
, m_generate{generate}
|
, m_generate{generate}
|
||||||
|
, m_needProcess{false}
|
||||||
, m_implied{implied} {}
|
, m_implied{implied} {}
|
||||||
ASTGEN_MEMBERS_AstBegin;
|
ASTGEN_MEMBERS_AstBegin;
|
||||||
void dump(std::ostream& str) const override;
|
void dump(std::ostream& str) const override;
|
||||||
void generate(bool flag) { m_generate = flag; }
|
void generate(bool flag) { m_generate = flag; }
|
||||||
bool generate() const { return m_generate; }
|
bool generate() const { return m_generate; }
|
||||||
|
void setNeedProcess() { m_needProcess = true; }
|
||||||
|
bool needProcess() const { return m_needProcess; }
|
||||||
bool implied() const { return m_implied; }
|
bool implied() const { return m_implied; }
|
||||||
};
|
};
|
||||||
class AstFork final : public AstNodeBlock {
|
class AstFork final : public AstNodeBlock {
|
||||||
|
|
|
||||||
|
|
@ -2321,6 +2321,7 @@ void AstBegin::dump(std::ostream& str) const {
|
||||||
if (generate()) str << " [GEN]";
|
if (generate()) str << " [GEN]";
|
||||||
if (genforp()) str << " [GENFOR]";
|
if (genforp()) str << " [GENFOR]";
|
||||||
if (implied()) str << " [IMPLIED]";
|
if (implied()) str << " [IMPLIED]";
|
||||||
|
if (needProcess()) str << " [NPRC]";
|
||||||
}
|
}
|
||||||
void AstCoverDecl::dump(std::ostream& str) const {
|
void AstCoverDecl::dump(std::ostream& str) const {
|
||||||
this->AstNodeStmt::dump(str);
|
this->AstNodeStmt::dump(str);
|
||||||
|
|
|
||||||
|
|
@ -410,7 +410,8 @@ void EmitCFunc::displayNode(AstNode* nodep, AstScopeName* scopenamep, const stri
|
||||||
displayEmit(nodep, isScan);
|
displayEmit(nodep, isScan);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmitCFunc::emitCCallArgs(const AstNodeCCall* nodep, const string& selfPointer) {
|
void EmitCFunc::emitCCallArgs(const AstNodeCCall* nodep, const string& selfPointer,
|
||||||
|
bool inProcess) {
|
||||||
puts("(");
|
puts("(");
|
||||||
bool comma = false;
|
bool comma = false;
|
||||||
if (nodep->funcp()->isLoose() && !nodep->funcp()->isStatic()) {
|
if (nodep->funcp()->isLoose() && !nodep->funcp()->isStatic()) {
|
||||||
|
|
@ -422,6 +423,8 @@ void EmitCFunc::emitCCallArgs(const AstNodeCCall* nodep, const string& selfPoint
|
||||||
if (comma) puts(", ");
|
if (comma) puts(", ");
|
||||||
if (VN_IS(nodep->backp(), CAwait) || !nodep->funcp()->isCoroutine()) {
|
if (VN_IS(nodep->backp(), CAwait) || !nodep->funcp()->isCoroutine()) {
|
||||||
puts("vlProcess");
|
puts("vlProcess");
|
||||||
|
} else if (inProcess) {
|
||||||
|
puts("std::make_shared<VlProcess>(vlProcess)");
|
||||||
} else {
|
} else {
|
||||||
puts("std::make_shared<VlProcess>()");
|
puts("std::make_shared<VlProcess>()");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -205,7 +205,7 @@ public:
|
||||||
}
|
}
|
||||||
void emitOpName(AstNode* nodep, const string& format, AstNode* lhsp, AstNode* rhsp,
|
void emitOpName(AstNode* nodep, const string& format, AstNode* lhsp, AstNode* rhsp,
|
||||||
AstNode* thsp);
|
AstNode* thsp);
|
||||||
void emitCCallArgs(const AstNodeCCall* nodep, const string& selfPointer);
|
void emitCCallArgs(const AstNodeCCall* nodep, const string& selfPointer, bool inProcess);
|
||||||
void emitDereference(const string& pointer);
|
void emitDereference(const string& pointer);
|
||||||
void emitCvtPackStr(AstNode* nodep);
|
void emitCvtPackStr(AstNode* nodep);
|
||||||
void emitCvtWideArray(AstNode* nodep, AstNode* fromp);
|
void emitCvtWideArray(AstNode* nodep, AstNode* fromp);
|
||||||
|
|
@ -500,7 +500,7 @@ public:
|
||||||
}
|
}
|
||||||
puts(funcp->nameProtect());
|
puts(funcp->nameProtect());
|
||||||
}
|
}
|
||||||
emitCCallArgs(nodep, nodep->selfPointerProtect(m_useSelfForThis));
|
emitCCallArgs(nodep, nodep->selfPointerProtect(m_useSelfForThis), m_cfuncp->needProcess());
|
||||||
}
|
}
|
||||||
void visit(AstCMethodCall* nodep) override {
|
void visit(AstCMethodCall* nodep) override {
|
||||||
const AstCFunc* const funcp = nodep->funcp();
|
const AstCFunc* const funcp = nodep->funcp();
|
||||||
|
|
@ -508,7 +508,7 @@ public:
|
||||||
iterateConst(nodep->fromp());
|
iterateConst(nodep->fromp());
|
||||||
putbs("->");
|
putbs("->");
|
||||||
puts(funcp->nameProtect());
|
puts(funcp->nameProtect());
|
||||||
emitCCallArgs(nodep, "");
|
emitCCallArgs(nodep, "", m_cfuncp->needProcess());
|
||||||
}
|
}
|
||||||
void visit(AstCAwait* nodep) override {
|
void visit(AstCAwait* nodep) override {
|
||||||
puts("co_await ");
|
puts("co_await ");
|
||||||
|
|
@ -613,6 +613,7 @@ public:
|
||||||
puts("]);\n");
|
puts("]);\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
void visit(AstDisableFork* nodep) override { puts("vlProcess->disable_fork();\n"); }
|
||||||
void visit(AstCReturn* nodep) override {
|
void visit(AstCReturn* nodep) override {
|
||||||
puts("return (");
|
puts("return (");
|
||||||
iterateAndNextConstNull(nodep->lhsp());
|
iterateAndNextConstNull(nodep->lhsp());
|
||||||
|
|
|
||||||
|
|
@ -330,7 +330,7 @@ private:
|
||||||
AstJumpLabel* const labelp = findAddLabel(beginp, false);
|
AstJumpLabel* const labelp = findAddLabel(beginp, false);
|
||||||
nodep->addNextHere(new AstJumpGo{nodep->fileline(), labelp});
|
nodep->addNextHere(new AstJumpGo{nodep->fileline(), labelp});
|
||||||
} else {
|
} else {
|
||||||
nodep->v3warn(E_UNSUPPORTED, "Unsupported: disable fork");
|
nodep->v3warn(E_UNSUPPORTED, "Unsupported: disabling fork by name");
|
||||||
}
|
}
|
||||||
nodep->unlinkFrBack();
|
nodep->unlinkFrBack();
|
||||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||||
|
|
|
||||||
|
|
@ -279,7 +279,6 @@ void transformForks(AstNetlist* const netlistp) {
|
||||||
// STATE
|
// STATE
|
||||||
bool m_inClass = false; // Are we in a class?
|
bool m_inClass = false; // Are we in a class?
|
||||||
bool m_beginHasAwaits = false; // Does the current begin have awaits?
|
bool m_beginHasAwaits = false; // Does the current begin have awaits?
|
||||||
bool m_beginNeedProcess = false; // Does the current begin have process::self dependency?
|
|
||||||
AstFork* m_forkp = nullptr; // Current fork
|
AstFork* m_forkp = nullptr; // Current fork
|
||||||
AstCFunc* m_funcp = nullptr; // Current function
|
AstCFunc* m_funcp = nullptr; // Current function
|
||||||
|
|
||||||
|
|
@ -358,9 +357,8 @@ void transformForks(AstNetlist* const netlistp) {
|
||||||
UASSERT_OBJ(m_forkp, nodep, "Begin outside of a fork");
|
UASSERT_OBJ(m_forkp, nodep, "Begin outside of a fork");
|
||||||
// Start with children, so later we only find awaits that are actually in this begin
|
// Start with children, so later we only find awaits that are actually in this begin
|
||||||
m_beginHasAwaits = false;
|
m_beginHasAwaits = false;
|
||||||
m_beginNeedProcess = false;
|
|
||||||
iterateChildrenConst(nodep);
|
iterateChildrenConst(nodep);
|
||||||
if (m_beginHasAwaits || m_beginNeedProcess) {
|
if (m_beginHasAwaits || nodep->needProcess()) {
|
||||||
UASSERT_OBJ(!nodep->name().empty(), nodep, "Begin needs a name");
|
UASSERT_OBJ(!nodep->name().empty(), nodep, "Begin needs a name");
|
||||||
// Create a function to put this begin's statements in
|
// Create a function to put this begin's statements in
|
||||||
FileLine* const flp = nodep->fileline();
|
FileLine* const flp = nodep->fileline();
|
||||||
|
|
@ -384,7 +382,7 @@ void transformForks(AstNetlist* const netlistp) {
|
||||||
}
|
}
|
||||||
// Put the begin's statements in the function, delete the begin
|
// Put the begin's statements in the function, delete the begin
|
||||||
newfuncp->addStmtsp(nodep->stmtsp()->unlinkFrBackWithNext());
|
newfuncp->addStmtsp(nodep->stmtsp()->unlinkFrBackWithNext());
|
||||||
if (m_beginNeedProcess) {
|
if (nodep->needProcess()) {
|
||||||
newfuncp->setNeedProcess();
|
newfuncp->setNeedProcess();
|
||||||
newfuncp->addStmtsp(new AstCStmt{nodep->fileline(),
|
newfuncp->addStmtsp(new AstCStmt{nodep->fileline(),
|
||||||
"vlProcess->state(VlProcess::FINISHED);\n"});
|
"vlProcess->state(VlProcess::FINISHED);\n"});
|
||||||
|
|
@ -405,10 +403,6 @@ void transformForks(AstNetlist* const netlistp) {
|
||||||
m_beginHasAwaits = true;
|
m_beginHasAwaits = true;
|
||||||
iterateChildrenConst(nodep);
|
iterateChildrenConst(nodep);
|
||||||
}
|
}
|
||||||
void visit(AstCCall* nodep) override {
|
|
||||||
if (nodep->funcp()->needProcess()) m_beginNeedProcess = true;
|
|
||||||
iterateChildrenConst(nodep);
|
|
||||||
}
|
|
||||||
void visit(AstExprStmt* nodep) override { iterateChildren(nodep); }
|
void visit(AstExprStmt* nodep) override { iterateChildren(nodep); }
|
||||||
|
|
||||||
//--------------------
|
//--------------------
|
||||||
|
|
|
||||||
145
src/V3Timing.cpp
145
src/V3Timing.cpp
|
|
@ -12,11 +12,27 @@
|
||||||
// Version 2.0.
|
// Version 2.0.
|
||||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
//
|
//
|
||||||
// TimingSuspendableVisitor locates all C++ functions and processes that contain timing controls,
|
// TimingSuspendableVisitor does not perform any AST transformations.
|
||||||
// and marks them as suspendable. If a process calls a suspendable function, then it is also marked
|
// Instead it propagates two types of flags:
|
||||||
// as suspendable. If a function calls or overrides a suspendable function, it is also marked as
|
// - flag "suspendable": (for detecting what will need to become a coroutine)
|
||||||
// suspendable. TimingSuspendableVisitor creates a dependency graph to propagate this property. It
|
// The visitor locates all C++ functions and processes that contain timing controls,
|
||||||
// does not perform any AST transformations.
|
// and marks them as suspendable. If a process calls a suspendable function,
|
||||||
|
// then it is also marked as suspendable. If a function calls or overrides
|
||||||
|
// a suspendable function, it is also marked as suspendable.
|
||||||
|
// TimingSuspendableVisitor creates a dependency graph to propagate this property.
|
||||||
|
// - flag "needs process": (for detecting what needs a VlProcess argument in signature)
|
||||||
|
// The visitor distinguishes 4 types of nodes:
|
||||||
|
// - T_ALLOCS_PROC: nodes that can allocate VlProcess (forks, always, initial etc.),
|
||||||
|
// - T_FORCES_PROC: nodes that make it necessary for the previous type to allocate VlProcess
|
||||||
|
// (like process::self which then wraps it, allowing use inside Verilog).
|
||||||
|
// - T_NEEDS_PROC: nodes that should obtain VlProcess if it will be allocated
|
||||||
|
// (all of the previous type + timing controls, so they could update process state),
|
||||||
|
// - T_HAS_PROC: nodes that are going to be emitted with a VlProcess argument.
|
||||||
|
// T_FORCES_PROC and T_NEEDS_PROC are propagated upwards up to the nodes of type T_ALLOCS_PROC,
|
||||||
|
// this is to detect which processes have to allocate VlProcess. Then nodes of these processes
|
||||||
|
// get marked as T_HAS_PROC and the flag is propagated downwards through nodes
|
||||||
|
// type T_NEEDS_PROC. Using only nodes type T_NEEDS_PROC assures the flags are only propagated
|
||||||
|
// through paths leading to nodes that actually use VlProcess.
|
||||||
//
|
//
|
||||||
// TimingControlVisitor is the one that actually performs transformations:
|
// TimingControlVisitor is the one that actually performs transformations:
|
||||||
// - for each intra-assignment timing control:
|
// - for each intra-assignment timing control:
|
||||||
|
|
@ -70,8 +86,10 @@ VL_DEFINE_DEBUG_FUNCTIONS;
|
||||||
enum NodeFlag : uint8_t {
|
enum NodeFlag : uint8_t {
|
||||||
T_SUSPENDEE = 1 << 0, // Suspendable (due to dependence on another suspendable)
|
T_SUSPENDEE = 1 << 0, // Suspendable (due to dependence on another suspendable)
|
||||||
T_SUSPENDER = 1 << 1, // Suspendable (has timing control)
|
T_SUSPENDER = 1 << 1, // Suspendable (has timing control)
|
||||||
T_HAS_PROC = 1 << 2, // Has an associated std::process
|
T_ALLOCS_PROC = 1 << 2, // Can allocate VlProcess
|
||||||
T_CALLS_PROC_SELF = 1 << 3, // Calls std::process::self
|
T_FORCES_PROC = 1 << 3, // Forces VlProcess allocation
|
||||||
|
T_NEEDS_PROC = 1 << 4, // Needs access to VlProcess if it's allocated
|
||||||
|
T_HAS_PROC = 1 << 5, // Has VlProcess argument in the signature
|
||||||
};
|
};
|
||||||
|
|
||||||
enum ForkType : uint8_t {
|
enum ForkType : uint8_t {
|
||||||
|
|
@ -86,6 +104,11 @@ enum PropagationType : uint8_t {
|
||||||
P_SIGNATURE = 3, // Propagation required to maintain C++ function's signature requirements
|
P_SIGNATURE = 3, // Propagation required to maintain C++ function's signature requirements
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Add timing flag to a node
|
||||||
|
static void addFlags(AstNode* const nodep, uint8_t flags) { nodep->user2(nodep->user2() | flags); }
|
||||||
|
// Check if a node has ALL of the expected flags set
|
||||||
|
static bool hasFlags(AstNode* const nodep, uint8_t flags) { return !(~nodep->user2() & flags); }
|
||||||
|
|
||||||
// ######################################################################
|
// ######################################################################
|
||||||
// Detect nodes affected by timing and/or requiring a process
|
// Detect nodes affected by timing and/or requiring a process
|
||||||
|
|
||||||
|
|
@ -126,8 +149,8 @@ private:
|
||||||
class SuspendDepVtx final : public DepVtx {
|
class SuspendDepVtx final : public DepVtx {
|
||||||
VL_RTTI_IMPL(SuspendDepVtx, DepVtx)
|
VL_RTTI_IMPL(SuspendDepVtx, DepVtx)
|
||||||
string dotColor() const override {
|
string dotColor() const override {
|
||||||
if (nodep()->user2() & T_SUSPENDER) return "red";
|
if (hasFlags(nodep(), T_SUSPENDER)) return "red";
|
||||||
if (nodep()->user2() & T_SUSPENDEE) return "blue";
|
if (hasFlags(nodep(), T_SUSPENDEE)) return "blue";
|
||||||
return "black";
|
return "black";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -140,8 +163,9 @@ private:
|
||||||
class NeedsProcDepVtx final : public DepVtx {
|
class NeedsProcDepVtx final : public DepVtx {
|
||||||
VL_RTTI_IMPL(NeedsProcDepVtx, DepVtx)
|
VL_RTTI_IMPL(NeedsProcDepVtx, DepVtx)
|
||||||
string dotColor() const override {
|
string dotColor() const override {
|
||||||
if (nodep()->user2() & T_CALLS_PROC_SELF) return "red";
|
if (hasFlags(nodep(), T_HAS_PROC)) return "blue";
|
||||||
if (nodep()->user2() & T_HAS_PROC) return "blue";
|
if (hasFlags(nodep(), T_NEEDS_PROC)) return "green";
|
||||||
|
if (hasFlags(nodep(), T_FORCES_PROC)) return "red";
|
||||||
return "black";
|
return "black";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -198,10 +222,10 @@ private:
|
||||||
if (!nodep->user5p()) nodep->user5p(new NeedsProcDepVtx{&m_procGraph, nodep, classp});
|
if (!nodep->user5p()) nodep->user5p(new NeedsProcDepVtx{&m_procGraph, nodep, classp});
|
||||||
return nodep->user5u().to<NeedsProcDepVtx*>();
|
return nodep->user5u().to<NeedsProcDepVtx*>();
|
||||||
}
|
}
|
||||||
// Set timing flag of a node
|
// Pass timing flag between nodes
|
||||||
bool passFlag(const AstNode* from, AstNode* to, NodeFlag flag) {
|
bool passFlag(const AstNode* from, AstNode* to, NodeFlag flag) {
|
||||||
if ((from->user2() & flag) && !(to->user2() & flag)) {
|
if ((from->user2() & flag) && !(to->user2() & flag)) {
|
||||||
to->user2(to->user2() | flag);
|
addFlags(to, flag);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -216,6 +240,15 @@ private:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
template <typename Predicate>
|
template <typename Predicate>
|
||||||
|
void propagateFlagsIf(DepVtx* const vxp, NodeFlag flag, Predicate p) {
|
||||||
|
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 (p(edgep) && passFlag(parentp, depp, flag)) propagateFlagsIf(depVxp, flag, p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
template <typename Predicate>
|
||||||
void propagateFlagsReversedIf(DepVtx* const vxp, NodeFlag flag, Predicate p) {
|
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()) {
|
||||||
|
|
@ -236,17 +269,22 @@ 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);
|
getNeedsProcDepVtx(nodep);
|
||||||
|
addFlags(nodep, T_ALLOCS_PROC);
|
||||||
if (VN_IS(nodep, Always)) {
|
if (VN_IS(nodep, Always)) {
|
||||||
UINFO(1, "Always does " << (nodep->needProcess() ? "" : "NOT ") << "need process\n");
|
UINFO(1, "Always does " << (nodep->needProcess() ? "" : "NOT ") << "need process\n");
|
||||||
}
|
}
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
}
|
}
|
||||||
|
void visit(AstDisableFork* nodep) override {
|
||||||
|
visit(static_cast<AstNode*>(nodep));
|
||||||
|
addFlags(m_procp, T_FORCES_PROC | T_NEEDS_PROC);
|
||||||
|
}
|
||||||
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);
|
||||||
if (nodep->needProcess()) nodep->user2(T_HAS_PROC | T_CALLS_PROC_SELF);
|
if (nodep->needProcess()) addFlags(nodep, T_FORCES_PROC | T_NEEDS_PROC);
|
||||||
DepVtx* const sVxp = getSuspendDepVtx(nodep);
|
DepVtx* const sVxp = getSuspendDepVtx(nodep);
|
||||||
DepVtx* const pVxp = getNeedsProcDepVtx(nodep);
|
DepVtx* const pVxp = getNeedsProcDepVtx(nodep);
|
||||||
if (!m_classp) return;
|
if (!m_classp) return;
|
||||||
|
|
@ -287,9 +325,12 @@ private:
|
||||||
new V3GraphEdge{&m_suspGraph, getSuspendDepVtx(nodep->funcp()),
|
new V3GraphEdge{&m_suspGraph, getSuspendDepVtx(nodep->funcp()),
|
||||||
getSuspendDepVtx(m_procp), m_underFork ? P_FORK : P_CALL};
|
getSuspendDepVtx(m_procp), m_underFork ? P_FORK : P_CALL};
|
||||||
|
|
||||||
if (!m_underFork)
|
new V3GraphEdge{&m_procGraph, getNeedsProcDepVtx(nodep->funcp()),
|
||||||
new V3GraphEdge{&m_procGraph, getNeedsProcDepVtx(nodep->funcp()),
|
getNeedsProcDepVtx(m_procp), P_CALL};
|
||||||
getNeedsProcDepVtx(m_procp), P_CALL};
|
|
||||||
|
if (m_underFork && !(m_underFork & F_MIGHT_SUSPEND)) {
|
||||||
|
addFlags(nodep, T_NEEDS_PROC | T_ALLOCS_PROC);
|
||||||
|
}
|
||||||
|
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
}
|
}
|
||||||
|
|
@ -301,9 +342,12 @@ private:
|
||||||
new V3GraphEdge{&m_suspGraph, getSuspendDepVtx(nodep), getSuspendDepVtx(m_procp),
|
new V3GraphEdge{&m_suspGraph, getSuspendDepVtx(nodep), getSuspendDepVtx(m_procp),
|
||||||
m_underFork ? P_FORK : P_CALL};
|
m_underFork ? P_FORK : P_CALL};
|
||||||
|
|
||||||
if (!m_underFork)
|
new V3GraphEdge{&m_procGraph, getNeedsProcDepVtx(nodep), getNeedsProcDepVtx(m_procp),
|
||||||
new V3GraphEdge{&m_procGraph, getNeedsProcDepVtx(nodep), getNeedsProcDepVtx(m_procp),
|
P_CALL};
|
||||||
P_CALL};
|
|
||||||
|
if (m_underFork && !(m_underFork & F_MIGHT_SUSPEND)) {
|
||||||
|
addFlags(nodep, T_NEEDS_PROC | T_ALLOCS_PROC);
|
||||||
|
}
|
||||||
|
|
||||||
m_procp = nodep;
|
m_procp = nodep;
|
||||||
m_underFork = 0;
|
m_underFork = 0;
|
||||||
|
|
@ -316,7 +360,7 @@ private:
|
||||||
// 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) {
|
if (nodep->isTimingControl() && m_procp) {
|
||||||
m_procp->user2(T_SUSPENDEE | T_SUSPENDER);
|
addFlags(m_procp, T_SUSPENDEE | T_SUSPENDER);
|
||||||
m_underFork |= F_MIGHT_SUSPEND;
|
m_underFork |= F_MIGHT_SUSPEND;
|
||||||
}
|
}
|
||||||
m_underFork |= F_MIGHT_NEED_PROC;
|
m_underFork |= F_MIGHT_NEED_PROC;
|
||||||
|
|
@ -325,7 +369,7 @@ private:
|
||||||
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_SUSPENDEE | T_SUSPENDER);
|
if (m_procp) addFlags(m_procp, T_SUSPENDEE | T_SUSPENDER | T_NEEDS_PROC);
|
||||||
}
|
}
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
}
|
}
|
||||||
|
|
@ -342,22 +386,35 @@ public:
|
||||||
// Propagate suspendability
|
// Propagate suspendability
|
||||||
for (V3GraphVertex* vxp = m_suspGraph.verticesBeginp(); vxp; vxp = vxp->verticesNextp()) {
|
for (V3GraphVertex* vxp = m_suspGraph.verticesBeginp(); vxp; vxp = vxp->verticesNextp()) {
|
||||||
DepVtx* const depVxp = static_cast<DepVtx*>(vxp);
|
DepVtx* const depVxp = static_cast<DepVtx*>(vxp);
|
||||||
if (depVxp->nodep()->user2() & T_SUSPENDEE) propagateFlags(depVxp, T_SUSPENDEE);
|
if (hasFlags(depVxp->nodep(), T_SUSPENDEE)) propagateFlags(depVxp, T_SUSPENDEE);
|
||||||
}
|
}
|
||||||
if (dumpGraphLevel() >= 6) m_suspGraph.dumpDotFilePrefixed("timing_deps");
|
if (dumpGraphLevel() >= 6) m_suspGraph.dumpDotFilePrefixed("timing_deps");
|
||||||
// Propagate process
|
|
||||||
|
// Propagate T_HAS_PROCESS
|
||||||
for (V3GraphVertex* vxp = m_procGraph.verticesBeginp(); vxp; vxp = vxp->verticesNextp()) {
|
for (V3GraphVertex* vxp = m_procGraph.verticesBeginp(); vxp; vxp = vxp->verticesNextp()) {
|
||||||
DepVtx* const depVxp = static_cast<DepVtx*>(vxp);
|
DepVtx* const depVxp = static_cast<DepVtx*>(vxp);
|
||||||
if (depVxp->nodep()->user2() & T_HAS_PROC) propagateFlags(depVxp, T_HAS_PROC);
|
// Find processes that'll allocate VlProcess
|
||||||
}
|
if (hasFlags(depVxp->nodep(), T_FORCES_PROC)) {
|
||||||
// Propagate process downwards (from caller to callee) for suspendable calls
|
propagateFlagsIf(depVxp, T_FORCES_PROC, [&](const V3GraphEdge* e) -> bool {
|
||||||
for (V3GraphVertex* vxp = m_suspGraph.verticesBeginp(); vxp; vxp = vxp->verticesNextp()) {
|
return !hasFlags(static_cast<DepVtx*>(e->fromp())->nodep(), T_ALLOCS_PROC);
|
||||||
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);
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
// Mark nodes on paths between processes and statements that use VlProcess
|
||||||
|
if (hasFlags(depVxp->nodep(), T_NEEDS_PROC)) {
|
||||||
|
propagateFlagsIf(depVxp, T_NEEDS_PROC, [&](const V3GraphEdge* e) -> bool {
|
||||||
|
return !hasFlags(static_cast<DepVtx*>(e->top())->nodep(), T_ALLOCS_PROC);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (V3GraphVertex* vxp = m_procGraph.verticesBeginp(); vxp; vxp = vxp->verticesNextp()) {
|
||||||
|
DepVtx* const depVxp = static_cast<DepVtx*>(vxp);
|
||||||
|
// Mark nodes that will be emitted with a VlProcess argument
|
||||||
|
if (hasFlags(depVxp->nodep(), T_ALLOCS_PROC | T_FORCES_PROC)) {
|
||||||
|
addFlags(depVxp->nodep(), T_HAS_PROC);
|
||||||
|
propagateFlagsReversedIf(depVxp, T_HAS_PROC, [&](const V3GraphEdge* e) -> bool {
|
||||||
|
return hasFlags(static_cast<DepVtx*>(e->fromp())->nodep(), T_NEEDS_PROC);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (dumpGraphLevel() >= 6) m_procGraph.dumpDotFilePrefixed("proc_deps");
|
if (dumpGraphLevel() >= 6) m_procGraph.dumpDotFilePrefixed("proc_deps");
|
||||||
}
|
}
|
||||||
|
|
@ -586,7 +643,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_HAS_PROC) ? "vlProcess" : "nullptr", 0};
|
flp, m_procp && (hasFlags(m_procp, T_HAS_PROC)) ? "vlProcess" : "nullptr", 0};
|
||||||
ap->dtypeSetVoid();
|
ap->dtypeSetVoid();
|
||||||
methodp->addPinsp(ap);
|
methodp->addPinsp(ap);
|
||||||
}
|
}
|
||||||
|
|
@ -697,8 +754,8 @@ private:
|
||||||
VL_RESTORER(m_procp);
|
VL_RESTORER(m_procp);
|
||||||
m_procp = nodep;
|
m_procp = nodep;
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
if (nodep->user2() & T_SUSPENDEE) nodep->setSuspendable();
|
if (hasFlags(nodep, T_SUSPENDEE)) nodep->setSuspendable();
|
||||||
if (nodep->user2() & T_HAS_PROC) nodep->setNeedProcess();
|
if (hasFlags(nodep, 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));
|
||||||
|
|
@ -719,11 +776,11 @@ private:
|
||||||
|
|
||||||
// Workaround for killing `always` processes (doing that is pretty much UB)
|
// Workaround for killing `always` processes (doing that is pretty much UB)
|
||||||
// TODO: Disallow killing `always` at runtime (throw an error)
|
// TODO: Disallow killing `always` at runtime (throw an error)
|
||||||
if (nodep->user2() & T_HAS_PROC) nodep->user2(nodep->user2() | T_SUSPENDEE);
|
if (hasFlags(nodep, T_HAS_PROC)) addFlags(nodep, T_SUSPENDEE);
|
||||||
|
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
if (nodep->user2() & T_HAS_PROC) nodep->setNeedProcess();
|
if (hasFlags(nodep, T_HAS_PROC)) nodep->setNeedProcess();
|
||||||
if (!(nodep->user2() & T_SUSPENDEE)) return;
|
if (!hasFlags(nodep, 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();
|
||||||
|
|
@ -744,8 +801,8 @@ private:
|
||||||
VL_RESTORER(m_procp);
|
VL_RESTORER(m_procp);
|
||||||
m_procp = nodep;
|
m_procp = nodep;
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
if (nodep->user2() & T_HAS_PROC) nodep->setNeedProcess();
|
if (hasFlags(nodep, T_HAS_PROC)) nodep->setNeedProcess();
|
||||||
if (!(nodep->user2() & T_SUSPENDEE)) return;
|
if (!(hasFlags(nodep, 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'
|
||||||
|
|
@ -768,7 +825,7 @@ private:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void visit(AstNodeCCall* nodep) override {
|
void visit(AstNodeCCall* nodep) override {
|
||||||
if ((nodep->funcp()->user2() & T_SUSPENDEE) && !nodep->user1SetOnce()) { // If suspendable
|
if (hasFlags(nodep->funcp(), 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};
|
||||||
|
|
@ -1048,6 +1105,7 @@ private:
|
||||||
void visit(AstBegin* nodep) override {
|
void visit(AstBegin* nodep) override {
|
||||||
VL_RESTORER(m_procp);
|
VL_RESTORER(m_procp);
|
||||||
m_procp = nodep;
|
m_procp = nodep;
|
||||||
|
if (hasFlags(nodep, T_HAS_PROC)) nodep->setNeedProcess();
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
}
|
}
|
||||||
void visit(AstFork* nodep) override {
|
void visit(AstFork* nodep) override {
|
||||||
|
|
@ -1061,6 +1119,7 @@ private:
|
||||||
while (stmtp) {
|
while (stmtp) {
|
||||||
if (!VN_IS(stmtp, Begin)) {
|
if (!VN_IS(stmtp, Begin)) {
|
||||||
auto* const beginp = new AstBegin{stmtp->fileline(), "", nullptr};
|
auto* const beginp = new AstBegin{stmtp->fileline(), "", nullptr};
|
||||||
|
if (hasFlags(stmtp, T_HAS_PROC)) addFlags(beginp, T_HAS_PROC);
|
||||||
stmtp->replaceWith(beginp);
|
stmtp->replaceWith(beginp);
|
||||||
beginp->addStmtsp(stmtp);
|
beginp->addStmtsp(stmtp);
|
||||||
stmtp = beginp;
|
stmtp = beginp;
|
||||||
|
|
|
||||||
|
|
@ -696,8 +696,14 @@ private:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void visit(AstDisableFork* nodep) override {
|
void visit(AstDisableFork* nodep) override {
|
||||||
nodep->v3warn(E_UNSUPPORTED, "Unsupported: disable fork statements");
|
if (nodep->fileline()->timingOn()) {
|
||||||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
if (v3Global.opt.timing().isSetFalse()) {
|
||||||
|
nodep->v3warn(E_NOTIMING, "Support for disable fork statement requires --timing");
|
||||||
|
} else if (!v3Global.opt.timing().isSetTrue()) {
|
||||||
|
nodep->v3warn(E_NEEDTIMINGOPT, "Use --timing or --no-timing to specify how "
|
||||||
|
<< "disable fork should be handled");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
void visit(AstWaitFork* nodep) override {
|
void visit(AstWaitFork* nodep) override {
|
||||||
nodep->v3warn(E_UNSUPPORTED, "Unsupported: wait fork statements");
|
nodep->v3warn(E_UNSUPPORTED, "Unsupported: wait fork statements");
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
%Error-UNSUPPORTED: t/t_disable.v:11:10: Unsupported: disabling fork by name
|
||||||
|
11 | disable foo;
|
||||||
|
| ^~~~~~~
|
||||||
|
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
|
||||||
|
%Error: Exiting due to
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
#!/usr/bin/env perl
|
||||||
|
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||||
|
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||||
|
#
|
||||||
|
# Copyright 2023 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(linter => 1);
|
||||||
|
|
||||||
|
lint(
|
||||||
|
verilator_flags2 => ['--lint-only --timing'],
|
||||||
|
fails => 1,
|
||||||
|
expect_filename => $Self->{golden_filename},
|
||||||
|
);
|
||||||
|
|
||||||
|
ok(1);
|
||||||
|
1;
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
// DESCRIPTION: Verilator: Verilog Test module
|
||||||
|
//
|
||||||
|
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||||
|
// any use, without warranty, 2023 by Wilson Snyder.
|
||||||
|
// SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
module t(/*AUTOARG*/);
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
fork : foo
|
||||||
|
disable foo;
|
||||||
|
#1 $stop;
|
||||||
|
join_none
|
||||||
|
#2;
|
||||||
|
$write("*-* All Finished *-*\n");
|
||||||
|
$finish;
|
||||||
|
end
|
||||||
|
|
||||||
|
endmodule
|
||||||
|
|
@ -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 2023 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,49 @@
|
||||||
|
// 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
|
||||||
|
|
||||||
|
`define N 3
|
||||||
|
|
||||||
|
class Cls;
|
||||||
|
task runforks(integer n);
|
||||||
|
for (integer i = 0; i < n; i++) fork
|
||||||
|
#1 $stop;
|
||||||
|
join_none
|
||||||
|
endtask
|
||||||
|
endclass
|
||||||
|
|
||||||
|
module t;
|
||||||
|
Cls cls = new;
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
// run forks
|
||||||
|
for (integer i = 0; i < `N; i++) fork
|
||||||
|
#1 $stop;
|
||||||
|
join_none
|
||||||
|
|
||||||
|
// run forks inside a method
|
||||||
|
cls.runforks(`N);
|
||||||
|
|
||||||
|
// run forks in forks
|
||||||
|
for (integer i = 0; i < `N; i++) fork
|
||||||
|
for (integer j = 0; j < `N; j++) fork
|
||||||
|
#1 $stop;
|
||||||
|
join_none
|
||||||
|
join_none
|
||||||
|
|
||||||
|
for (integer i = 0; i < `N; i++) fork
|
||||||
|
cls.runforks(`N);
|
||||||
|
join_none
|
||||||
|
|
||||||
|
// kill them all
|
||||||
|
disable fork;
|
||||||
|
|
||||||
|
// check if we can still fork
|
||||||
|
fork
|
||||||
|
#2 $write("*-* All Finished *-*\n");
|
||||||
|
#3 $finish;
|
||||||
|
join_none
|
||||||
|
end
|
||||||
|
endmodule
|
||||||
|
|
@ -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 2023 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,58 @@
|
||||||
|
// 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
|
||||||
|
|
||||||
|
// USING THIS FOR DEBUGGING PROCESS PROPAGATION:
|
||||||
|
//
|
||||||
|
// The example contains most cases that were problematic during the
|
||||||
|
// works on support of 'disable fork' statement, including:
|
||||||
|
//
|
||||||
|
// - indirect use of disable fork (through a task),
|
||||||
|
// - indirect use of forks that are to be disabled,
|
||||||
|
// - forks in forks,
|
||||||
|
// - a function taking VlProcess argument shared between a process that
|
||||||
|
// allocates VlProcess, and one that doesnt,
|
||||||
|
// - a function that has a delay and obtains VlProcess argument,
|
||||||
|
// - a function that has a delay and doesn't obtain it.
|
||||||
|
//
|
||||||
|
// Blocks below contain info on whether they should (YES) or shouldn't (NO)
|
||||||
|
// be emitted as functions with a VlProcess argument.
|
||||||
|
//
|
||||||
|
// To check if that corresponds to reality, see blue nodes in proc_deps.dot
|
||||||
|
|
||||||
|
class Cls;
|
||||||
|
task print; /*NO*/
|
||||||
|
$write("*-* All ");
|
||||||
|
endtask
|
||||||
|
task disable_fork_func; /*YES*/
|
||||||
|
disable fork;
|
||||||
|
endtask
|
||||||
|
task common_func; /*YES*/
|
||||||
|
fork /*YES*/ #1; join_none
|
||||||
|
endtask
|
||||||
|
task fork_func; /*YES*/
|
||||||
|
fork /*YES*/ #1 $stop; join_none
|
||||||
|
endtask
|
||||||
|
task delay_func; /*NO*/
|
||||||
|
fork /*NO*/ #1 $write("Finished *-*\n"); join_none
|
||||||
|
endtask
|
||||||
|
endclass
|
||||||
|
|
||||||
|
module t;
|
||||||
|
Cls cls = new;
|
||||||
|
|
||||||
|
initial begin /*YES*/
|
||||||
|
fork /*YES*/ cls.common_func(); join_none
|
||||||
|
cls.fork_func();
|
||||||
|
cls.disable_fork_func();
|
||||||
|
cls.print();
|
||||||
|
end
|
||||||
|
|
||||||
|
initial begin /*NO*/
|
||||||
|
cls.delay_func();
|
||||||
|
cls.common_func();
|
||||||
|
fork /*YES*/ disable fork; join_none
|
||||||
|
end
|
||||||
|
endmodule
|
||||||
|
|
@ -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 2023 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,18 @@
|
||||||
|
// 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
|
||||||
|
|
||||||
|
module t;
|
||||||
|
initial begin
|
||||||
|
fork begin
|
||||||
|
fork begin
|
||||||
|
#3 $stop;
|
||||||
|
end join_none
|
||||||
|
#1;
|
||||||
|
end join_none
|
||||||
|
#2 disable fork;
|
||||||
|
end
|
||||||
|
initial #4 $write("*-* All Finished *-*\n");
|
||||||
|
endmodule
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
%Error-NOTIMING: t/t_disable_fork_notiming.v:8:12: Support for disable fork statement requires --timing
|
||||||
|
: ... note: In instance 't'
|
||||||
|
8 | initial disable fork;
|
||||||
|
| ^~~~~~~
|
||||||
|
... For error description see https://verilator.org/warn/NOTIMING?v=latest
|
||||||
|
%Error: Exiting due to
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
#!/usr/bin/env perl
|
||||||
|
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||||
|
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||||
|
#
|
||||||
|
# Copyright 2023 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(
|
||||||
|
expect_filename => $Self->{golden_filename},
|
||||||
|
v_flags2 => ["--no-timing"],
|
||||||
|
fails => 1,
|
||||||
|
);
|
||||||
|
|
||||||
|
ok(1);
|
||||||
|
1;
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
// 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
|
||||||
|
|
||||||
|
module t;
|
||||||
|
initial disable fork;
|
||||||
|
endmodule
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
%Error-UNSUPPORTED: t/t_fork_disable.v:16:7: Unsupported: disable fork statements
|
|
||||||
: ... note: In instance 't'
|
|
||||||
16 | disable fork;
|
|
||||||
| ^~~~~~~
|
|
||||||
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
|
|
||||||
%Error-UNSUPPORTED: t/t_fork_disable.v:17:7: Unsupported: wait fork statements
|
|
||||||
: ... note: In instance 't'
|
|
||||||
17 | wait fork;
|
|
||||||
| ^~~~
|
|
||||||
%Error: Exiting due to
|
|
||||||
|
|
@ -11190,9 +11190,7 @@ task uvm_phase::execute_phase();
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
join_any
|
join_any
|
||||||
//TODO issue #4125 - Support disable fork
|
disable fork;
|
||||||
//TODO %Error-UNSUPPORTED: t/t_uvm_pkg_todo.vh:11187:12: Unsupported: disable fork statements
|
|
||||||
//TODO disable fork;
|
|
||||||
end
|
end
|
||||||
join
|
join
|
||||||
end
|
end
|
||||||
|
|
@ -15172,9 +15170,7 @@ class uvm_heartbeat extends uvm_object;
|
||||||
end
|
end
|
||||||
@(m_stop_event);
|
@(m_stop_event);
|
||||||
join_any
|
join_any
|
||||||
//TODO issue #4125 - Support disable fork
|
disable fork;
|
||||||
//TODO %Error-UNSUPPORTED: t/t_uvm_pkg_todo.vh:15167:12: Unsupported: disable fork statements
|
|
||||||
//TODO disable fork;
|
|
||||||
endtask
|
endtask
|
||||||
endclass
|
endclass
|
||||||
class uvm_heartbeat_callback extends uvm_objection_callback;
|
class uvm_heartbeat_callback extends uvm_objection_callback;
|
||||||
|
|
@ -19218,9 +19214,7 @@ task uvm_sequencer_base::m_wait_for_available_sequence();
|
||||||
join_any
|
join_any
|
||||||
end
|
end
|
||||||
join_any
|
join_any
|
||||||
//TODO issue #4125 - Support disable fork
|
disable fork;
|
||||||
//TODO %Error-UNSUPPORTED: t/t_uvm_pkg_todo.vh:19213:12: Unsupported: disable fork statements
|
|
||||||
//TODO disable fork;
|
|
||||||
end
|
end
|
||||||
join
|
join
|
||||||
endtask
|
endtask
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
%Error-UNSUPPORTED: t/t_wait_fork.v:17:7: Unsupported: wait fork statements
|
||||||
|
: ... note: In instance 't'
|
||||||
|
17 | wait fork;
|
||||||
|
| ^~~~
|
||||||
|
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
|
||||||
|
%Error: Exiting due to
|
||||||
Loading…
Reference in New Issue