mirror of
https://github.com/verilator/verilator.git
synced 2025-01-01 04:07:34 +00:00
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;
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
// VlProcess stores metadata of running processes
|
||||
// Metadata of processes
|
||||
class VlProcess;
|
||||
|
||||
using VlProcessRef = std::shared_ptr<VlProcess>;
|
||||
|
||||
class VlProcess final {
|
||||
// MEMBERS
|
||||
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:
|
||||
// TYPES
|
||||
@ -98,16 +103,34 @@ public:
|
||||
};
|
||||
|
||||
// CONSTRUCTORS
|
||||
// Construct independent process
|
||||
VlProcess()
|
||||
: 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; }
|
||||
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"); }
|
||||
|
||||
//===================================================================
|
||||
|
@ -86,7 +86,7 @@ private:
|
||||
bool m_recursive : 1; // Recursive or part of recursion
|
||||
bool m_underGenerate : 1; // Under generate (for warning)
|
||||
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
|
||||
VIsCached m_purity; // Pure state
|
||||
|
||||
@ -300,7 +300,7 @@ class AstNodeProcedure VL_NOT_FINAL : public AstNode {
|
||||
// IEEE procedure: initial, final, always
|
||||
// @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_needProcess : 1; // Implements part of a process that allocates std::process
|
||||
bool m_needProcess : 1; // Uses VlProcess
|
||||
protected:
|
||||
AstNodeProcedure(VNType t, FileLine* fl, AstNode* stmtsp)
|
||||
: 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_dpiImportWrapper : 1; // Wrapper for invoking DPI import prototype from generated code
|
||||
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:
|
||||
AstCFunc(FileLine* fl, const string& name, AstScope* scopep, const string& rtnType = "")
|
||||
: ASTGEN_SUPER_CFunc(fl) {
|
||||
@ -2096,19 +2096,23 @@ class AstBegin final : public AstNodeBlock {
|
||||
// Parents: statement
|
||||
// @astgen op1 := genforp : Optional[AstNode]
|
||||
|
||||
bool m_generate; // Underneath a generate
|
||||
const bool m_implied; // Not inserted by user
|
||||
bool m_generate : 1; // Underneath a generate
|
||||
bool m_needProcess : 1; // Uses VlProcess
|
||||
const bool m_implied : 1; // Not inserted by user
|
||||
public:
|
||||
// Node that puts name into the output stream
|
||||
AstBegin(FileLine* fl, const string& name, AstNode* stmtsp, bool generate = false,
|
||||
bool implied = false)
|
||||
: ASTGEN_SUPER_Begin(fl, name, stmtsp)
|
||||
, m_generate{generate}
|
||||
, m_needProcess{false}
|
||||
, m_implied{implied} {}
|
||||
ASTGEN_MEMBERS_AstBegin;
|
||||
void dump(std::ostream& str) const override;
|
||||
void generate(bool flag) { m_generate = flag; }
|
||||
bool generate() const { return m_generate; }
|
||||
void setNeedProcess() { m_needProcess = true; }
|
||||
bool needProcess() const { return m_needProcess; }
|
||||
bool implied() const { return m_implied; }
|
||||
};
|
||||
class AstFork final : public AstNodeBlock {
|
||||
|
@ -2321,6 +2321,7 @@ void AstBegin::dump(std::ostream& str) const {
|
||||
if (generate()) str << " [GEN]";
|
||||
if (genforp()) str << " [GENFOR]";
|
||||
if (implied()) str << " [IMPLIED]";
|
||||
if (needProcess()) str << " [NPRC]";
|
||||
}
|
||||
void AstCoverDecl::dump(std::ostream& str) const {
|
||||
this->AstNodeStmt::dump(str);
|
||||
|
@ -410,7 +410,8 @@ void EmitCFunc::displayNode(AstNode* nodep, AstScopeName* scopenamep, const stri
|
||||
displayEmit(nodep, isScan);
|
||||
}
|
||||
|
||||
void EmitCFunc::emitCCallArgs(const AstNodeCCall* nodep, const string& selfPointer) {
|
||||
void EmitCFunc::emitCCallArgs(const AstNodeCCall* nodep, const string& selfPointer,
|
||||
bool inProcess) {
|
||||
puts("(");
|
||||
bool comma = false;
|
||||
if (nodep->funcp()->isLoose() && !nodep->funcp()->isStatic()) {
|
||||
@ -422,6 +423,8 @@ void EmitCFunc::emitCCallArgs(const AstNodeCCall* nodep, const string& selfPoint
|
||||
if (comma) puts(", ");
|
||||
if (VN_IS(nodep->backp(), CAwait) || !nodep->funcp()->isCoroutine()) {
|
||||
puts("vlProcess");
|
||||
} else if (inProcess) {
|
||||
puts("std::make_shared<VlProcess>(vlProcess)");
|
||||
} else {
|
||||
puts("std::make_shared<VlProcess>()");
|
||||
}
|
||||
|
@ -205,7 +205,7 @@ public:
|
||||
}
|
||||
void emitOpName(AstNode* nodep, const string& format, AstNode* lhsp, AstNode* rhsp,
|
||||
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 emitCvtPackStr(AstNode* nodep);
|
||||
void emitCvtWideArray(AstNode* nodep, AstNode* fromp);
|
||||
@ -500,7 +500,7 @@ public:
|
||||
}
|
||||
puts(funcp->nameProtect());
|
||||
}
|
||||
emitCCallArgs(nodep, nodep->selfPointerProtect(m_useSelfForThis));
|
||||
emitCCallArgs(nodep, nodep->selfPointerProtect(m_useSelfForThis), m_cfuncp->needProcess());
|
||||
}
|
||||
void visit(AstCMethodCall* nodep) override {
|
||||
const AstCFunc* const funcp = nodep->funcp();
|
||||
@ -508,7 +508,7 @@ public:
|
||||
iterateConst(nodep->fromp());
|
||||
putbs("->");
|
||||
puts(funcp->nameProtect());
|
||||
emitCCallArgs(nodep, "");
|
||||
emitCCallArgs(nodep, "", m_cfuncp->needProcess());
|
||||
}
|
||||
void visit(AstCAwait* nodep) override {
|
||||
puts("co_await ");
|
||||
@ -613,6 +613,7 @@ public:
|
||||
puts("]);\n");
|
||||
}
|
||||
}
|
||||
void visit(AstDisableFork* nodep) override { puts("vlProcess->disable_fork();\n"); }
|
||||
void visit(AstCReturn* nodep) override {
|
||||
puts("return (");
|
||||
iterateAndNextConstNull(nodep->lhsp());
|
||||
|
@ -330,7 +330,7 @@ private:
|
||||
AstJumpLabel* const labelp = findAddLabel(beginp, false);
|
||||
nodep->addNextHere(new AstJumpGo{nodep->fileline(), labelp});
|
||||
} else {
|
||||
nodep->v3warn(E_UNSUPPORTED, "Unsupported: disable fork");
|
||||
nodep->v3warn(E_UNSUPPORTED, "Unsupported: disabling fork by name");
|
||||
}
|
||||
nodep->unlinkFrBack();
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
|
@ -279,7 +279,6 @@ void transformForks(AstNetlist* const netlistp) {
|
||||
// STATE
|
||||
bool m_inClass = false; // Are we in a class?
|
||||
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
|
||||
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");
|
||||
// Start with children, so later we only find awaits that are actually in this begin
|
||||
m_beginHasAwaits = false;
|
||||
m_beginNeedProcess = false;
|
||||
iterateChildrenConst(nodep);
|
||||
if (m_beginHasAwaits || m_beginNeedProcess) {
|
||||
if (m_beginHasAwaits || nodep->needProcess()) {
|
||||
UASSERT_OBJ(!nodep->name().empty(), nodep, "Begin needs a name");
|
||||
// Create a function to put this begin's statements in
|
||||
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
|
||||
newfuncp->addStmtsp(nodep->stmtsp()->unlinkFrBackWithNext());
|
||||
if (m_beginNeedProcess) {
|
||||
if (nodep->needProcess()) {
|
||||
newfuncp->setNeedProcess();
|
||||
newfuncp->addStmtsp(new AstCStmt{nodep->fileline(),
|
||||
"vlProcess->state(VlProcess::FINISHED);\n"});
|
||||
@ -405,10 +403,6 @@ void transformForks(AstNetlist* const netlistp) {
|
||||
m_beginHasAwaits = true;
|
||||
iterateChildrenConst(nodep);
|
||||
}
|
||||
void visit(AstCCall* nodep) override {
|
||||
if (nodep->funcp()->needProcess()) m_beginNeedProcess = true;
|
||||
iterateChildrenConst(nodep);
|
||||
}
|
||||
void visit(AstExprStmt* nodep) override { iterateChildren(nodep); }
|
||||
|
||||
//--------------------
|
||||
|
145
src/V3Timing.cpp
145
src/V3Timing.cpp
@ -12,11 +12,27 @@
|
||||
// Version 2.0.
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
//
|
||||
// TimingSuspendableVisitor locates all C++ functions and processes that contain timing controls,
|
||||
// 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. It
|
||||
// does not perform any AST transformations.
|
||||
// TimingSuspendableVisitor does not perform any AST transformations.
|
||||
// Instead it propagates two types of flags:
|
||||
// - flag "suspendable": (for detecting what will need to become a coroutine)
|
||||
// The visitor locates all C++ functions and processes that contain timing controls,
|
||||
// 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:
|
||||
// - for each intra-assignment timing control:
|
||||
@ -70,8 +86,10 @@ VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
enum NodeFlag : uint8_t {
|
||||
T_SUSPENDEE = 1 << 0, // Suspendable (due to dependence on another suspendable)
|
||||
T_SUSPENDER = 1 << 1, // Suspendable (has timing control)
|
||||
T_HAS_PROC = 1 << 2, // Has an associated std::process
|
||||
T_CALLS_PROC_SELF = 1 << 3, // Calls std::process::self
|
||||
T_ALLOCS_PROC = 1 << 2, // Can allocate VlProcess
|
||||
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 {
|
||||
@ -86,6 +104,11 @@ enum PropagationType : uint8_t {
|
||||
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
|
||||
|
||||
@ -126,8 +149,8 @@ private:
|
||||
class SuspendDepVtx final : public DepVtx {
|
||||
VL_RTTI_IMPL(SuspendDepVtx, DepVtx)
|
||||
string dotColor() const override {
|
||||
if (nodep()->user2() & T_SUSPENDER) return "red";
|
||||
if (nodep()->user2() & T_SUSPENDEE) return "blue";
|
||||
if (hasFlags(nodep(), T_SUSPENDER)) return "red";
|
||||
if (hasFlags(nodep(), T_SUSPENDEE)) return "blue";
|
||||
return "black";
|
||||
}
|
||||
|
||||
@ -140,8 +163,9 @@ private:
|
||||
class NeedsProcDepVtx final : public DepVtx {
|
||||
VL_RTTI_IMPL(NeedsProcDepVtx, DepVtx)
|
||||
string dotColor() const override {
|
||||
if (nodep()->user2() & T_CALLS_PROC_SELF) return "red";
|
||||
if (nodep()->user2() & T_HAS_PROC) return "blue";
|
||||
if (hasFlags(nodep(), T_HAS_PROC)) return "blue";
|
||||
if (hasFlags(nodep(), T_NEEDS_PROC)) return "green";
|
||||
if (hasFlags(nodep(), T_FORCES_PROC)) return "red";
|
||||
return "black";
|
||||
}
|
||||
|
||||
@ -198,10 +222,10 @@ private:
|
||||
if (!nodep->user5p()) nodep->user5p(new NeedsProcDepVtx{&m_procGraph, nodep, classp});
|
||||
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) {
|
||||
if ((from->user2() & flag) && !(to->user2() & flag)) {
|
||||
to->user2(to->user2() | flag);
|
||||
addFlags(to, flag);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@ -216,6 +240,15 @@ private:
|
||||
}
|
||||
}
|
||||
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) {
|
||||
auto* const parentp = vxp->nodep();
|
||||
for (V3GraphEdge* edgep = vxp->inBeginp(); edgep; edgep = edgep->inNextp()) {
|
||||
@ -236,17 +269,22 @@ private:
|
||||
void visit(AstNodeProcedure* nodep) override {
|
||||
VL_RESTORER(m_procp);
|
||||
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)) {
|
||||
UINFO(1, "Always does " << (nodep->needProcess() ? "" : "NOT ") << "need process\n");
|
||||
}
|
||||
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 {
|
||||
VL_RESTORER(m_procp);
|
||||
m_procp = 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 pVxp = getNeedsProcDepVtx(nodep);
|
||||
if (!m_classp) return;
|
||||
@ -287,9 +325,12 @@ private:
|
||||
new V3GraphEdge{&m_suspGraph, getSuspendDepVtx(nodep->funcp()),
|
||||
getSuspendDepVtx(m_procp), m_underFork ? P_FORK : P_CALL};
|
||||
|
||||
if (!m_underFork)
|
||||
new V3GraphEdge{&m_procGraph, getNeedsProcDepVtx(nodep->funcp()),
|
||||
getNeedsProcDepVtx(m_procp), P_CALL};
|
||||
new V3GraphEdge{&m_procGraph, getNeedsProcDepVtx(nodep->funcp()),
|
||||
getNeedsProcDepVtx(m_procp), P_CALL};
|
||||
|
||||
if (m_underFork && !(m_underFork & F_MIGHT_SUSPEND)) {
|
||||
addFlags(nodep, T_NEEDS_PROC | T_ALLOCS_PROC);
|
||||
}
|
||||
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
@ -301,9 +342,12 @@ private:
|
||||
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};
|
||||
new V3GraphEdge{&m_procGraph, getNeedsProcDepVtx(nodep), getNeedsProcDepVtx(m_procp),
|
||||
P_CALL};
|
||||
|
||||
if (m_underFork && !(m_underFork & F_MIGHT_SUSPEND)) {
|
||||
addFlags(nodep, T_NEEDS_PROC | T_ALLOCS_PROC);
|
||||
}
|
||||
|
||||
m_procp = nodep;
|
||||
m_underFork = 0;
|
||||
@ -316,7 +360,7 @@ private:
|
||||
// so that transformForks() in V3SchedTiming gets called and
|
||||
// removes all forks and begins
|
||||
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_NEED_PROC;
|
||||
@ -325,7 +369,7 @@ private:
|
||||
void visit(AstNode* nodep) override {
|
||||
if (nodep->isTimingControl()) {
|
||||
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);
|
||||
}
|
||||
@ -342,22 +386,35 @@ public:
|
||||
// Propagate suspendability
|
||||
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 (hasFlags(depVxp->nodep(), T_SUSPENDEE)) propagateFlags(depVxp, T_SUSPENDEE);
|
||||
}
|
||||
if (dumpGraphLevel() >= 6) m_suspGraph.dumpDotFilePrefixed("timing_deps");
|
||||
// Propagate process
|
||||
|
||||
// Propagate T_HAS_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);
|
||||
// Find processes that'll allocate VlProcess
|
||||
if (hasFlags(depVxp->nodep(), T_FORCES_PROC)) {
|
||||
propagateFlagsIf(depVxp, T_FORCES_PROC, [&](const V3GraphEdge* e) -> bool {
|
||||
return !hasFlags(static_cast<DepVtx*>(e->fromp())->nodep(), T_ALLOCS_PROC);
|
||||
});
|
||||
}
|
||||
// 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");
|
||||
}
|
||||
@ -586,7 +643,7 @@ private:
|
||||
void addProcessInfo(AstCMethodHard* const methodp) const {
|
||||
FileLine* const flp = methodp->fileline();
|
||||
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();
|
||||
methodp->addPinsp(ap);
|
||||
}
|
||||
@ -697,8 +754,8 @@ private:
|
||||
VL_RESTORER(m_procp);
|
||||
m_procp = nodep;
|
||||
iterateChildren(nodep);
|
||||
if (nodep->user2() & T_SUSPENDEE) nodep->setSuspendable();
|
||||
if (nodep->user2() & T_HAS_PROC) nodep->setNeedProcess();
|
||||
if (hasFlags(nodep, T_SUSPENDEE)) nodep->setSuspendable();
|
||||
if (hasFlags(nodep, T_HAS_PROC)) nodep->setNeedProcess();
|
||||
}
|
||||
void visit(AstInitial* nodep) override {
|
||||
visit(static_cast<AstNodeProcedure*>(nodep));
|
||||
@ -719,11 +776,11 @@ private:
|
||||
|
||||
// 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);
|
||||
if (hasFlags(nodep, T_HAS_PROC)) addFlags(nodep, T_SUSPENDEE);
|
||||
|
||||
iterateChildren(nodep);
|
||||
if (nodep->user2() & T_HAS_PROC) nodep->setNeedProcess();
|
||||
if (!(nodep->user2() & T_SUSPENDEE)) return;
|
||||
if (hasFlags(nodep, T_HAS_PROC)) nodep->setNeedProcess();
|
||||
if (!hasFlags(nodep, T_SUSPENDEE)) return;
|
||||
nodep->setSuspendable();
|
||||
FileLine* const flp = nodep->fileline();
|
||||
AstSenTree* const sensesp = m_activep->sensesp();
|
||||
@ -744,8 +801,8 @@ private:
|
||||
VL_RESTORER(m_procp);
|
||||
m_procp = nodep;
|
||||
iterateChildren(nodep);
|
||||
if (nodep->user2() & T_HAS_PROC) nodep->setNeedProcess();
|
||||
if (!(nodep->user2() & T_SUSPENDEE)) return;
|
||||
if (hasFlags(nodep, T_HAS_PROC)) nodep->setNeedProcess();
|
||||
if (!(hasFlags(nodep, T_SUSPENDEE))) return;
|
||||
|
||||
nodep->rtnType("VlCoroutine");
|
||||
// If in a class, create a shared pointer to 'this'
|
||||
@ -768,7 +825,7 @@ private:
|
||||
}
|
||||
}
|
||||
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;
|
||||
nodep->unlinkFrBack(&relinker);
|
||||
AstCAwait* const awaitp = new AstCAwait{nodep->fileline(), nodep};
|
||||
@ -1048,6 +1105,7 @@ private:
|
||||
void visit(AstBegin* nodep) override {
|
||||
VL_RESTORER(m_procp);
|
||||
m_procp = nodep;
|
||||
if (hasFlags(nodep, T_HAS_PROC)) nodep->setNeedProcess();
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstFork* nodep) override {
|
||||
@ -1061,6 +1119,7 @@ private:
|
||||
while (stmtp) {
|
||||
if (!VN_IS(stmtp, Begin)) {
|
||||
auto* const beginp = new AstBegin{stmtp->fileline(), "", nullptr};
|
||||
if (hasFlags(stmtp, T_HAS_PROC)) addFlags(beginp, T_HAS_PROC);
|
||||
stmtp->replaceWith(beginp);
|
||||
beginp->addStmtsp(stmtp);
|
||||
stmtp = beginp;
|
||||
|
@ -696,8 +696,14 @@ private:
|
||||
}
|
||||
}
|
||||
void visit(AstDisableFork* nodep) override {
|
||||
nodep->v3warn(E_UNSUPPORTED, "Unsupported: disable fork statements");
|
||||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||
if (nodep->fileline()->timingOn()) {
|
||||
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 {
|
||||
nodep->v3warn(E_UNSUPPORTED, "Unsupported: wait fork statements");
|
||||
|
5
test_regress/t/t_disable.out
Normal file
5
test_regress/t/t_disable.out
Normal file
@ -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
|
20
test_regress/t/t_disable.pl
Executable file
20
test_regress/t/t_disable.pl
Executable file
@ -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;
|
19
test_regress/t/t_disable.v
Normal file
19
test_regress/t/t_disable.v
Normal file
@ -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
|
23
test_regress/t/t_disable_fork1.pl
Executable file
23
test_regress/t/t_disable_fork1.pl
Executable file
@ -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;
|
49
test_regress/t/t_disable_fork1.v
Normal file
49
test_regress/t/t_disable_fork1.v
Normal file
@ -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
|
23
test_regress/t/t_disable_fork2.pl
Executable file
23
test_regress/t/t_disable_fork2.pl
Executable file
@ -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;
|
58
test_regress/t/t_disable_fork2.v
Normal file
58
test_regress/t/t_disable_fork2.v
Normal file
@ -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
|
23
test_regress/t/t_disable_fork3.pl
Executable file
23
test_regress/t/t_disable_fork3.pl
Executable file
@ -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;
|
18
test_regress/t/t_disable_fork3.v
Normal file
18
test_regress/t/t_disable_fork3.v
Normal file
@ -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
|
6
test_regress/t/t_disable_fork_notiming.out
Normal file
6
test_regress/t/t_disable_fork_notiming.out
Normal file
@ -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
|
20
test_regress/t/t_disable_fork_notiming.pl
Executable file
20
test_regress/t/t_disable_fork_notiming.pl
Executable file
@ -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;
|
9
test_regress/t/t_disable_fork_notiming.v
Normal file
9
test_regress/t/t_disable_fork_notiming.v
Normal file
@ -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
|
||||
join_any
|
||||
//TODO issue #4125 - Support disable fork
|
||||
//TODO %Error-UNSUPPORTED: t/t_uvm_pkg_todo.vh:11187:12: Unsupported: disable fork statements
|
||||
//TODO disable fork;
|
||||
disable fork;
|
||||
end
|
||||
join
|
||||
end
|
||||
@ -15172,9 +15170,7 @@ class uvm_heartbeat extends uvm_object;
|
||||
end
|
||||
@(m_stop_event);
|
||||
join_any
|
||||
//TODO issue #4125 - Support disable fork
|
||||
//TODO %Error-UNSUPPORTED: t/t_uvm_pkg_todo.vh:15167:12: Unsupported: disable fork statements
|
||||
//TODO disable fork;
|
||||
disable fork;
|
||||
endtask
|
||||
endclass
|
||||
class uvm_heartbeat_callback extends uvm_objection_callback;
|
||||
@ -19218,9 +19214,7 @@ task uvm_sequencer_base::m_wait_for_available_sequence();
|
||||
join_any
|
||||
end
|
||||
join_any
|
||||
//TODO issue #4125 - Support disable fork
|
||||
//TODO %Error-UNSUPPORTED: t/t_uvm_pkg_todo.vh:19213:12: Unsupported: disable fork statements
|
||||
//TODO disable fork;
|
||||
disable fork;
|
||||
end
|
||||
join
|
||||
endtask
|
||||
|
6
test_regress/t/t_wait_fork.out
Normal file
6
test_regress/t/t_wait_fork.out
Normal file
@ -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
Block a user