Fix fork scheduling semantics (#6730)
Signed-off-by: Artur Bieniek <abieniek@internships.antmicro.com>
This commit is contained in:
parent
f4654a451b
commit
2c5ff3f63f
|
|
@ -1418,9 +1418,10 @@ class AstFork final : public AstNodeBlock {
|
|||
// spawned. This is necessary to implement things like local variable
|
||||
// initializers properly. The parallel statements inside the fork must all
|
||||
// be AstBegin, as lowering stages will introduce additional statements to
|
||||
// be executed sequentially within eaach fork branch.
|
||||
// be executed sequentially within each fork branch.
|
||||
//
|
||||
// @astgen op3 := forksp : List[AstBegin]
|
||||
// @astgen op4 := parentProcessp : Optional[AstVarRef]
|
||||
const VJoinType m_joinType; // Join keyword type
|
||||
public:
|
||||
AstFork(FileLine* fl, VJoinType joinType, const string& name = "")
|
||||
|
|
|
|||
|
|
@ -1251,6 +1251,8 @@ public:
|
|||
puts(");\n");
|
||||
}
|
||||
void visit(AstFinish* nodep) override {
|
||||
// Disable all the forks so they don't operate after simulation is finished.
|
||||
if (m_cfuncp && m_cfuncp->needProcess()) putns(nodep, "vlProcess->disableFork();\n");
|
||||
putns(nodep, "VL_FINISH_MT(");
|
||||
putsQuoted(protect(nodep->fileline()->filename()));
|
||||
puts(", ");
|
||||
|
|
@ -1258,6 +1260,8 @@ public:
|
|||
puts(", \"\");\n");
|
||||
}
|
||||
void visit(AstFinishFork* nodep) override {
|
||||
// Disable all the forks so they don't operate after simulation is finished.
|
||||
if (m_cfuncp && m_cfuncp->needProcess()) putns(nodep, "vlProcess->disableFork();\n");
|
||||
putns(nodep, "VL_FINISH_MT(");
|
||||
putsQuoted(protect(nodep->fileline()->filename()));
|
||||
puts(", ");
|
||||
|
|
|
|||
|
|
@ -524,6 +524,11 @@ class ForkVisitor final : public VNVisitor {
|
|||
AstVar* m_capturedVarsp = nullptr; // Local copies of captured variables
|
||||
AstArg* m_capturedArgsp = nullptr; // References to captured variables (as args)
|
||||
|
||||
// STATE - across all visitors
|
||||
AstClass* m_processClassp = nullptr;
|
||||
AstFunc* m_statusMethodp = nullptr;
|
||||
VMemberMap m_memberMap; // for lookup of process class methods
|
||||
|
||||
// METHODS
|
||||
AstVar* capture(AstVarRef* refp) {
|
||||
AstVar* varp = nullptr;
|
||||
|
|
@ -577,6 +582,19 @@ class ForkVisitor final : public VNVisitor {
|
|||
return true;
|
||||
}
|
||||
|
||||
AstClass* getProcessClassp() {
|
||||
if (!m_processClassp)
|
||||
m_processClassp
|
||||
= VN_AS(m_memberMap.findMember(v3Global.rootp()->stdPackagep(), "process"), Class);
|
||||
return m_processClassp;
|
||||
}
|
||||
|
||||
AstFunc* getStatusmethodp() {
|
||||
if (m_statusMethodp == nullptr)
|
||||
m_statusMethodp = VN_AS(m_memberMap.findMember(getProcessClassp(), "status"), Func);
|
||||
return m_statusMethodp;
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
void visit(AstNodeModule* nodep) override {
|
||||
VL_RESTORER(m_modp);
|
||||
|
|
@ -596,6 +614,34 @@ class ForkVisitor final : public VNVisitor {
|
|||
return;
|
||||
}
|
||||
|
||||
// IEEE 1800-2023 9.3.2: In all cases, processes spawned by a fork-join block shall not
|
||||
// start executing until the parent process is blocked or terminates.
|
||||
// Because join and join_any block the parent process, it is only needed when join_none
|
||||
// is used.
|
||||
if (nodep->joinType().joinNone()) {
|
||||
UINFO(9, "Visiting fork..join_none " << nodep);
|
||||
FileLine* fl = nodep->fileline();
|
||||
AstVarRef* forkParentrefp = nodep->parentProcessp();
|
||||
|
||||
if (forkParentrefp) { // Forks created by V3Fork will not have this
|
||||
for (AstBegin *itemp = nodep->forksp(), *nextp; itemp; itemp = nextp) {
|
||||
nextp = VN_AS(itemp->nextp(), Begin);
|
||||
if (!itemp->stmtsp()) continue;
|
||||
AstMethodCall* const statusCallp = new AstMethodCall{
|
||||
fl, forkParentrefp->cloneTree(false), "status", nullptr};
|
||||
statusCallp->taskp(getStatusmethodp());
|
||||
statusCallp->classOrPackagep(getProcessClassp());
|
||||
statusCallp->dtypep(getStatusmethodp()->dtypep());
|
||||
AstNeq* const condp
|
||||
= new AstNeq{fl, statusCallp,
|
||||
new AstConst{fl, AstConst::WidthedValue{},
|
||||
getStatusmethodp()->dtypep()->width(), 1}};
|
||||
AstWait* const waitStmt = new AstWait{fl, condp, nullptr};
|
||||
itemp->stmtsp()->addHereThisAsNext(waitStmt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
iterateAndNextNull(nodep->declsp());
|
||||
iterateAndNextNull(nodep->stmtsp());
|
||||
std::vector<AstBegin*> wrappedp;
|
||||
|
|
|
|||
|
|
@ -622,6 +622,29 @@ private:
|
|||
return findp;
|
||||
}
|
||||
|
||||
VSymEnt* findForkParentAlias(VSymEnt* symp, const string& ident) {
|
||||
static const string suffix = "__VgetForkParent";
|
||||
VSymEnt* const wrapperp = symp->findIdFlat(ident + suffix);
|
||||
if (!wrapperp) return nullptr;
|
||||
if (!VN_IS(wrapperp->nodep(), Begin)) return nullptr;
|
||||
if (VSymEnt* const forkSymp = wrapperp->findIdFlat(ident)) {
|
||||
if (VN_IS(forkSymp->nodep(), Fork)) return forkSymp;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
VSymEnt* unwrapForkParent(VSymEnt* symp, const string& ident) {
|
||||
static const string suffix = "__VgetForkParent";
|
||||
if (AstBegin* const beginp = VN_CAST(symp->nodep(), Begin)) {
|
||||
if (VString::endsWith(beginp->name(), suffix)) {
|
||||
if (VSymEnt* const forkSymp = symp->findIdFlat(ident)) {
|
||||
if (VN_IS(forkSymp->nodep(), Fork)) return forkSymp;
|
||||
}
|
||||
}
|
||||
}
|
||||
return symp;
|
||||
}
|
||||
|
||||
VSymEnt* findWithAltFlat(VSymEnt* symp, const string& name, const string& altname) {
|
||||
VSymEnt* findp = symp->findIdFlat(name);
|
||||
if (findp) return findp;
|
||||
|
|
@ -672,9 +695,10 @@ public:
|
|||
const AstCellInline* inlinep = lookupSymp
|
||||
? VN_CAST(lookupSymp->nodep(), CellInline)
|
||||
: nullptr; // Replicated below
|
||||
if (VSymEnt* const findSymp = findWithAltFallback(lookupSymp, ident, altIdent)) {
|
||||
lookupSymp = findSymp;
|
||||
}
|
||||
VSymEnt* findSymp = findWithAltFallback(lookupSymp, ident, altIdent);
|
||||
if (!findSymp) findSymp = findForkParentAlias(lookupSymp, ident);
|
||||
if (findSymp) lookupSymp = unwrapForkParent(findSymp, ident);
|
||||
|
||||
// Check this module - cur modname
|
||||
else if ((cellp && cellp->modp()->origName() == ident)
|
||||
|| (inlinep && inlinep->origModName() == ident)) {
|
||||
|
|
@ -720,9 +744,11 @@ public:
|
|||
if (!lookupSymp) return nullptr; // Not found
|
||||
}
|
||||
} else { // Searching for middle submodule, must be a cell name
|
||||
if (VSymEnt* const findSymp = findWithAltFlat(lookupSymp, ident, altIdent)) {
|
||||
lookupSymp = findSymp;
|
||||
} else {
|
||||
VSymEnt* findSymp = findWithAltFlat(lookupSymp, ident, altIdent);
|
||||
if (!findSymp) findSymp = findForkParentAlias(lookupSymp, ident);
|
||||
if (findSymp)
|
||||
lookupSymp = unwrapForkParent(findSymp, ident);
|
||||
else {
|
||||
return nullptr; // Not found
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
#include "V3LinkParse.h"
|
||||
|
||||
#include "V3Control.h"
|
||||
#include "V3MemberMap.h"
|
||||
#include "V3Stats.h"
|
||||
|
||||
#include <set>
|
||||
|
|
@ -46,6 +47,7 @@ class LinkParseVisitor final : public VNVisitor {
|
|||
|
||||
// STATE - across all visitors
|
||||
std::unordered_set<FileLine*> m_filelines; // Filelines that have been seen
|
||||
VMemberMap m_memberMap; // for lookup of process class methods
|
||||
|
||||
// STATE - for current visit position (use VL_RESTORER)
|
||||
// If set, move AstVar->valuep() initial values to this module
|
||||
|
|
@ -72,6 +74,9 @@ class LinkParseVisitor final : public VNVisitor {
|
|||
// STATE - Statistic tracking
|
||||
VDouble0 m_statModules; // Number of modules seen
|
||||
|
||||
bool m_unprotectedStdProcess
|
||||
= false; // Set when std::process internals were unprotected, we only need to do this once
|
||||
|
||||
// METHODS
|
||||
void cleanFileline(AstNode* nodep) {
|
||||
if (nodep->user2SetOnce()) return; // Process once
|
||||
|
|
@ -105,6 +110,21 @@ class LinkParseVisitor final : public VNVisitor {
|
|||
return "";
|
||||
}
|
||||
|
||||
void unprotectStdProcessHandle() {
|
||||
if (m_unprotectedStdProcess) return;
|
||||
m_unprotectedStdProcess = true;
|
||||
if (!v3Global.opt.protectIds()) return;
|
||||
if (AstPackage* const stdp = v3Global.rootp()->stdPackagep()) {
|
||||
if (AstClass* const processp
|
||||
= VN_CAST(m_memberMap.findMember(stdp, "process"), Class)) {
|
||||
if (AstVar* const handlep
|
||||
= VN_CAST(m_memberMap.findMember(processp, "m_process"), Var)) {
|
||||
handlep->protect(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void visitIterateNodeDType(AstNodeDType* nodep) {
|
||||
if (nodep->user1SetOnce()) return; // Process only once.
|
||||
cleanFileline(nodep);
|
||||
|
|
@ -180,6 +200,38 @@ class LinkParseVisitor final : public VNVisitor {
|
|||
<< nodep->warnContextSecondary());
|
||||
}
|
||||
|
||||
void addForkParentProcess(AstFork* forkp) {
|
||||
FileLine* const fl = forkp->fileline();
|
||||
|
||||
const std::string parentName = "__VforkParent";
|
||||
AstRefDType* const dtypep = new AstRefDType{fl, "process"};
|
||||
AstVar* const parentVar
|
||||
= new AstVar{fl, VVarType::BLOCKTEMP, parentName, VFlagChildDType{}, dtypep};
|
||||
parentVar->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
|
||||
|
||||
AstParseRef* const lhsp = new AstParseRef{fl, parentName, nullptr, nullptr};
|
||||
AstClassOrPackageRef* const processRefp
|
||||
= new AstClassOrPackageRef{fl, "process", nullptr, nullptr};
|
||||
AstParseRef* const selfRefp = new AstParseRef{fl, "self", nullptr, nullptr};
|
||||
AstDot* const processSelfp = new AstDot{fl, true, processRefp, selfRefp};
|
||||
AstMethodCall* const callp = new AstMethodCall{fl, processSelfp, "self", nullptr};
|
||||
AstAssign* const initp = new AstAssign{fl, lhsp, callp};
|
||||
|
||||
AstVarRef* const parentRefp = new AstVarRef{fl, parentVar, VAccess::READ};
|
||||
forkp->parentProcessp(parentRefp);
|
||||
|
||||
VNRelinker relinker;
|
||||
forkp->unlinkFrBack(&relinker);
|
||||
|
||||
parentVar->addNextHere(initp);
|
||||
initp->addNextHere(forkp);
|
||||
|
||||
AstBegin* const beginp = new AstBegin{
|
||||
fl, forkp->name() == "" ? "" : forkp->name() + "__VgetForkParent", parentVar, true};
|
||||
|
||||
relinker.relink(beginp);
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
void visit(AstNodeFTask* nodep) override {
|
||||
if (nodep->user1SetOnce()) return; // Process only once.
|
||||
|
|
@ -791,7 +843,11 @@ class LinkParseVisitor final : public VNVisitor {
|
|||
}
|
||||
cleanFileline(nodep);
|
||||
iterateAndNextNull(nodep->stmtsp());
|
||||
if (AstFork* const forkp = VN_CAST(nodep, Fork)) iterateAndNextNull(forkp->forksp());
|
||||
if (AstFork* const forkp = VN_CAST(nodep, Fork)) {
|
||||
iterateAndNextNull(forkp->forksp());
|
||||
if (!forkp->parentProcessp() && forkp->joinType().joinNone() && forkp->forksp())
|
||||
addForkParentProcess(forkp);
|
||||
}
|
||||
}
|
||||
void visit(AstCase* nodep) override {
|
||||
V3Control::applyCase(nodep);
|
||||
|
|
@ -954,7 +1010,10 @@ class LinkParseVisitor final : public VNVisitor {
|
|||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit LinkParseVisitor(AstNetlist* rootp) { iterate(rootp); }
|
||||
explicit LinkParseVisitor(AstNetlist* rootp) {
|
||||
unprotectStdProcessHandle();
|
||||
iterate(rootp);
|
||||
}
|
||||
~LinkParseVisitor() override {
|
||||
V3Stats::addStatSum(V3Stats::STAT_SOURCE_MODULES, m_statModules);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -89,6 +89,10 @@ private:
|
|||
for (AstNode* itemp = anodep->membersp(); itemp; itemp = itemp->nextp()) {
|
||||
memberInsert(mmapr, itemp);
|
||||
}
|
||||
} else if (const AstPackage* const anodep = VN_CAST(nodep, Package)) {
|
||||
for (AstNode* itemp = anodep->stmtsp(); itemp; itemp = itemp->nextp()) {
|
||||
memberInsert(mmapr, itemp);
|
||||
}
|
||||
} else {
|
||||
nodep->v3fatalSrc("Unsupported node type");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -340,4 +340,12 @@ public:
|
|||
}
|
||||
return resp;
|
||||
}
|
||||
|
||||
// Wrap fork statements in AstBegin, ensure fork...join_none have process
|
||||
static AstNodeStmt* wrapFork(V3ParseImp* parsep, AstFork* forkp, AstNodeStmt* stmtsp) {
|
||||
if (forkp->joinType() == VJoinType::JOIN_NONE && stmtsp)
|
||||
parsep->importIfInStd(forkp->fileline(), "process", true);
|
||||
forkp->addForksp(wrapInBegin(stmtsp));
|
||||
return forkp;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -469,6 +469,7 @@ class TimingControlVisitor final : public VNVisitor {
|
|||
AstScope* m_scopep = nullptr; // Current scope
|
||||
AstActive* m_activep = nullptr; // Current active
|
||||
AstNode* m_procp = nullptr; // NodeProcedure/CFunc/Begin we're under
|
||||
bool m_hasProcess = false; // True if current scope has a VlProcess handle available
|
||||
int m_forkCnt = 0; // Number of forks inside a module
|
||||
bool m_underJumpBlock = false; // True if we are inside of a jump-block
|
||||
bool m_underProcedure = false; // True if we are under an always or initial
|
||||
|
|
@ -654,26 +655,34 @@ class TimingControlVisitor final : public VNVisitor {
|
|||
}
|
||||
// Adds debug info to a hardcoded method call
|
||||
void addDebugInfo(AstCMethodHard* const methodp) const {
|
||||
if (v3Global.opt.protectIds()) return;
|
||||
FileLine* const flp = methodp->fileline();
|
||||
AstCExpr* const ap = new AstCExpr{flp, '"' + flp->filenameEsc() + '"'};
|
||||
const bool protectIds = v3Global.opt.protectIds();
|
||||
AstCExpr* const ap
|
||||
= new AstCExpr{flp, protectIds ? "VL_UNKNOWN" : '"' + flp->filenameEsc() + '"'};
|
||||
ap->dtypeSetString();
|
||||
methodp->addPinsp(ap);
|
||||
AstCExpr* const bp = new AstCExpr{flp, cvtToStr(flp->lineno())};
|
||||
AstCExpr* const bp = new AstCExpr{flp, protectIds ? "0" : cvtToStr(flp->lineno())};
|
||||
bp->dtypeSetString();
|
||||
methodp->addPinsp(bp);
|
||||
}
|
||||
// Adds debug info to a trigSched.trigger() call
|
||||
void addEventDebugInfo(AstCMethodHard* const methodp, AstSenTree* const sentreep) const {
|
||||
if (v3Global.opt.protectIds()) return;
|
||||
if (v3Global.opt.protectIds()) {
|
||||
FileLine* const flp = sentreep->fileline();
|
||||
AstCExpr* const descp = new AstCExpr{flp, "VL_UNKNOWN"};
|
||||
descp->dtypeSetString();
|
||||
methodp->addPinsp(descp);
|
||||
addDebugInfo(methodp);
|
||||
return;
|
||||
}
|
||||
methodp->addPinsp(createEventDescription(sentreep));
|
||||
addDebugInfo(methodp);
|
||||
}
|
||||
// Adds process pointer to a hardcoded method call
|
||||
void addProcessInfo(AstCMethodHard* const methodp) const {
|
||||
FileLine* const flp = methodp->fileline();
|
||||
AstCExpr* const ap = new AstCExpr{
|
||||
flp, m_procp && (hasFlags(m_procp, T_HAS_PROC)) ? "vlProcess" : "nullptr"};
|
||||
AstCExpr* const ap
|
||||
= new AstCExpr{flp, (m_procp && m_hasProcess) ? "vlProcess" : "nullptr"};
|
||||
methodp->addPinsp(ap);
|
||||
}
|
||||
// Creates the fork handle type and returns it
|
||||
|
|
@ -779,7 +788,9 @@ class TimingControlVisitor final : public VNVisitor {
|
|||
}
|
||||
void visit(AstNodeProcedure* nodep) override {
|
||||
VL_RESTORER(m_procp);
|
||||
VL_RESTORER(m_hasProcess);
|
||||
m_procp = nodep;
|
||||
m_hasProcess = hasFlags(nodep, T_HAS_PROC);
|
||||
VL_RESTORER(m_underProcedure);
|
||||
m_underProcedure = true;
|
||||
iterateChildren(nodep);
|
||||
|
|
@ -801,7 +812,9 @@ class TimingControlVisitor final : public VNVisitor {
|
|||
void visit(AstAlways* nodep) override {
|
||||
if (nodep->user1SetOnce()) return;
|
||||
VL_RESTORER(m_procp);
|
||||
VL_RESTORER(m_hasProcess);
|
||||
m_procp = nodep;
|
||||
m_hasProcess = hasFlags(nodep, T_HAS_PROC);
|
||||
VL_RESTORER(m_underProcedure);
|
||||
m_underProcedure = true;
|
||||
// Workaround for killing `always` processes (doing that is pretty much UB)
|
||||
|
|
@ -829,7 +842,9 @@ class TimingControlVisitor final : public VNVisitor {
|
|||
}
|
||||
void visit(AstCFunc* nodep) override {
|
||||
VL_RESTORER(m_procp);
|
||||
VL_RESTORER(m_hasProcess);
|
||||
m_procp = nodep;
|
||||
m_hasProcess = hasFlags(nodep, T_HAS_PROC);
|
||||
iterateChildren(nodep);
|
||||
if (hasFlags(nodep, T_HAS_PROC)) nodep->setNeedProcess();
|
||||
if (!(hasFlags(nodep, T_SUSPENDEE))) return;
|
||||
|
|
@ -862,6 +877,7 @@ class TimingControlVisitor final : public VNVisitor {
|
|||
}
|
||||
}
|
||||
void visit(AstNodeCCall* nodep) override {
|
||||
if (nodep->funcp()->needProcess()) m_hasProcess = true;
|
||||
if (hasFlags(nodep->funcp(), T_SUSPENDEE) && !nodep->user1SetOnce()) { // If suspendable
|
||||
VNRelinker relinker;
|
||||
nodep->unlinkFrBack(&relinker);
|
||||
|
|
@ -1165,12 +1181,12 @@ class TimingControlVisitor final : public VNVisitor {
|
|||
forkp->unlinkFrBack()});
|
||||
}
|
||||
void visit(AstDisableFork* nodep) override {
|
||||
if (hasFlags(m_procp, T_HAS_PROC)) return;
|
||||
if (m_hasProcess) return;
|
||||
// never reached by any process; remove to avoid compilation error
|
||||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||
}
|
||||
void visit(AstWaitFork* nodep) override {
|
||||
if (hasFlags(m_procp, T_HAS_PROC)) {
|
||||
if (m_hasProcess) {
|
||||
FileLine* const flp = nodep->fileline();
|
||||
AstCExpr* const exprp = new AstCExpr{flp, "vlProcess->completedFork()", 1};
|
||||
AstWait* const waitp = new AstWait{flp, exprp, nullptr};
|
||||
|
|
@ -1233,8 +1249,10 @@ class TimingControlVisitor final : public VNVisitor {
|
|||
}
|
||||
void visit(AstBegin* nodep) override {
|
||||
VL_RESTORER(m_procp);
|
||||
VL_RESTORER(m_hasProcess);
|
||||
m_hasProcess |= hasFlags(nodep, T_HAS_PROC);
|
||||
m_procp = nodep;
|
||||
if (hasFlags(nodep, T_HAS_PROC)) nodep->setNeedProcess();
|
||||
if (m_hasProcess) nodep->setNeedProcess();
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstFork* nodep) override {
|
||||
|
|
|
|||
|
|
@ -3400,23 +3400,23 @@ par_blockJoin<joinType>:
|
|||
| yJOIN_NONE { $$ = VJoinType::JOIN_NONE; }
|
||||
;
|
||||
|
||||
par_block<forkp>: // ==IEEE: par_block
|
||||
par_block<nodeStmtp>: // ==IEEE: par_block
|
||||
yFORK startLabelE blockDeclListE stmtListE par_blockJoin endLabelE
|
||||
{
|
||||
$$ = new AstFork{$1, $5, $2 ? *$2 : ""};
|
||||
GRAMMARP->endLabel($<fl>6, $$, $6);
|
||||
$$->addDeclsp($3);
|
||||
$$->addForksp(V3ParseGrammar::wrapInBegin($4));
|
||||
AstFork* const forkp = new AstFork{$1, $5, $2 ? *$2 : ""};
|
||||
GRAMMARP->endLabel($<fl>6, forkp, $6);
|
||||
forkp->addDeclsp($3);
|
||||
$$ = V3ParseGrammar::wrapFork(PARSEP, forkp, $4);
|
||||
}
|
||||
;
|
||||
|
||||
par_blockPreId<forkp>: // ==IEEE: par_block but called with leading ID
|
||||
par_blockPreId<nodeStmtp>: // ==IEEE: par_block but called with leading ID
|
||||
id yP_COLON__FORK yFORK blockDeclListE stmtListE par_blockJoin endLabelE
|
||||
{
|
||||
$$ = new AstFork{$3, $6, *$1};
|
||||
GRAMMARP->endLabel($<fl>7, $$, $7);
|
||||
$$->addDeclsp($4);
|
||||
$$->addForksp(V3ParseGrammar::wrapInBegin($5));
|
||||
AstFork* const forkp = new AstFork{$3, $6, *$1};
|
||||
GRAMMARP->endLabel($<fl>7, forkp, $7);
|
||||
forkp->addDeclsp($4);
|
||||
$$ = V3ParseGrammar::wrapFork(PARSEP, forkp, $5);
|
||||
}
|
||||
;
|
||||
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ class Cls;
|
|||
y = 2;
|
||||
end
|
||||
join_none
|
||||
#1;
|
||||
endtask
|
||||
endclass
|
||||
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ class Foo;
|
|||
$stop;
|
||||
end
|
||||
join_none : frk
|
||||
#1;
|
||||
endtask
|
||||
endclass
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2025 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
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.compile(verilator_flags2=["--timing"])
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
|
@ -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, 2025 by Antmicro.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
module t;
|
||||
export "DPI-C" task cfunc_finish; // this is just so the task becomes AstCFunc, we don't really use the export
|
||||
task cfunc_finish;
|
||||
$finish;
|
||||
endtask
|
||||
|
||||
initial begin
|
||||
fork
|
||||
cfunc_finish();
|
||||
join_none
|
||||
#1 $stop;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2025 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
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.compile(verilator_flags2=["--timing"])
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2025 by Antmicro.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
module t;
|
||||
integer i=0;
|
||||
initial begin
|
||||
fork
|
||||
i=1;
|
||||
join_none
|
||||
if(i==1) $stop;
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2025 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
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.compile(verilator_flags2=["--timing"])
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2025 by Antmicro.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
module t;
|
||||
bit flag;
|
||||
initial begin
|
||||
fork begin
|
||||
$stop;
|
||||
end join_none
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -20,9 +20,10 @@ module t;
|
|||
fork
|
||||
p = 1;
|
||||
join_none
|
||||
#0;
|
||||
endtask
|
||||
|
||||
task t2(output q);
|
||||
q <= 1;
|
||||
q = 1;
|
||||
endtask
|
||||
endmodule
|
||||
|
|
|
|||
|
|
@ -8,12 +8,15 @@ module t;
|
|||
process job[] = new [8];
|
||||
|
||||
initial begin
|
||||
foreach (job[j]) fork
|
||||
begin
|
||||
$write("job started\n");
|
||||
job[j] = process::self();
|
||||
end
|
||||
join_none
|
||||
foreach (job[j]) begin
|
||||
fork
|
||||
begin
|
||||
$write("job started\n");
|
||||
job[j] = process::self();
|
||||
end
|
||||
join_none
|
||||
#0;
|
||||
end
|
||||
foreach (job[j]) begin
|
||||
wait (job[j]);
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,4 @@
|
|||
job started
|
||||
all jobs started
|
||||
all jobs finished
|
||||
*-* All Finished *-*
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2025 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
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.compile(verilator_flags2=["--binary"])
|
||||
|
||||
test.execute(expect_filename=test.golden_filename)
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2025 by Antmicro Ltd.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
module t;
|
||||
process job;
|
||||
|
||||
initial begin
|
||||
process p1 = process::self();
|
||||
fork
|
||||
begin
|
||||
wait(p1.status() != process::RUNNING);
|
||||
$write("job started\n");
|
||||
job = process::self();
|
||||
end
|
||||
join_none
|
||||
wait (job);
|
||||
$write("all jobs started\n");
|
||||
job.await();
|
||||
$write("all jobs finished\n");
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -50,7 +50,7 @@ module t();
|
|||
bar.ewait();
|
||||
end
|
||||
join_none
|
||||
|
||||
#1;
|
||||
p.kill();
|
||||
|
||||
->evt1;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,10 @@
|
|||
-V{t#,#}+ Vt_timing_debug2___024root___ctor_var_reset
|
||||
-V{t#,#}+ Vt_timing_debug2___024unit__03a__03aBaseClass__Vclpkg___ctor_var_reset
|
||||
-V{t#,#}+ Vt_timing_debug2___024unit___ctor_var_reset
|
||||
-V{t#,#}+ Vt_timing_debug2_std___ctor_var_reset
|
||||
-V{t#,#}+ Vt_timing_debug2_t___ctor_var_reset
|
||||
-V{t#,#}+ Vt_timing_debug2_std__03a__03aprocess__Vclpkg___ctor_var_reset
|
||||
-V{t#,#}+ Vt_timing_debug2_std__03a__03asemaphore__Vclpkg___ctor_var_reset
|
||||
-V{t#,#}+ Vt_timing_debug2_t__03a__03aAssignDelayClass__Vclpkg___ctor_var_reset
|
||||
-V{t#,#}+ Vt_timing_debug2_t__03a__03aClkClass__Vclpkg___ctor_var_reset
|
||||
-V{t#,#}+ Vt_timing_debug2_t__03a__03aDelay10__Vclpkg___ctor_var_reset
|
||||
|
|
@ -1207,7 +1210,12 @@
|
|||
-V{t#,#}+ Vt_timing_debug2_t__03a__03aDelay40::__VnoInFunc_do_sth_else
|
||||
-V{t#,#}+ Vt_timing_debug2_t__03a__03aNoDelay::__VnoInFunc_do_delay
|
||||
-V{t#,#}+ Vt_timing_debug2_t__03a__03aNoDelay::__VnoInFunc_do_sth_else
|
||||
-V{t#,#}+ Vt_timing_debug2_std__03a__03aprocess__Vclpkg::__VnoInFunc_self
|
||||
-V{t#,#}+ Vt_timing_debug2_std__03a__03aprocess::new
|
||||
-V{t#,#}+ Vt_timing_debug2_std__03a__03aprocess::_ctor_var_reset
|
||||
-V{t#,#}+ Vt_timing_debug2_t___eval_initial__TOP__t__Vtiming__6____Vfork_1__0
|
||||
-V{t#,#}+ Vt_timing_debug2_std__03a__03aprocess::__VnoInFunc_status
|
||||
-V{t#,#} Suspending process waiting for @([true] (32'h1 != $_EXPRSTMT( // Function: status t.__Vtask___VforkTask_0__25____VforkParent.(t.__Vtask_status__26__Vfuncout); , ); )) at t/t_timing_class.v:224
|
||||
-V{t#,#}+ Vt_timing_debug2_t__03a__03aAssignDelayClass::__VnoInFunc_do_assign
|
||||
-V{t#,#} Resuming: Process waiting at t/t_timing_class.v:76
|
||||
-V{t#,#} Process forked at t/t_timing_class.v:76 finished
|
||||
|
|
@ -1217,8 +1225,13 @@
|
|||
-V{t#,#}+ Vt_timing_debug2___024root___eval_triggers__act
|
||||
-V{t#,#} Suspended processes waiting for dynamic trigger evaluation:
|
||||
-V{t#,#} - Process waiting at t/t_timing_class.v:75
|
||||
-V{t#,#} Suspended processes waiting for dynamic trigger evaluation:
|
||||
-V{t#,#} - Process waiting at t/t_timing_class.v:224
|
||||
-V{t#,#} Resuming: Process waiting at t/t_timing_class.v:75
|
||||
-V{t#,#} Process waiting for @([true] ((32'sh2a == t::LocalWaitClass.a) | (32'sh64 != t::LocalWaitClass.b))) at t/t_timing_class.v:75 awaiting resumption
|
||||
-V{t#,#} Resuming: Process waiting at t/t_timing_class.v:224
|
||||
-V{t#,#}+ Vt_timing_debug2_std__03a__03aprocess::__VnoInFunc_status
|
||||
-V{t#,#} Process waiting for @([true] (32'h1 != $_EXPRSTMT( // Function: status t.__Vtask___VforkTask_0__25____VforkParent.(t.__Vtask_status__26__Vfuncout); , ); )) at t/t_timing_class.v:224 awaiting resumption
|
||||
-V{t#,#}+ Vt_timing_debug2___024root___dump_triggers__act
|
||||
-V{t#,#}+ Vt_timing_debug2___024root___trigger_anySet__act
|
||||
-V{t#,#} 'act' region trigger index 2 is active: @([true] __VdynSched.evaluate())
|
||||
|
|
@ -1228,9 +1241,11 @@
|
|||
-V{t#,#}+ Vt_timing_debug2___024root___timing_resume
|
||||
-V{t#,#} Resuming processes:
|
||||
-V{t#,#} - Process waiting at t/t_timing_class.v:75
|
||||
-V{t#,#} - Process waiting at t/t_timing_class.v:224
|
||||
-V{t#,#} Resuming: Process waiting at t/t_timing_class.v:75
|
||||
-V{t#,#} Process forked at t/t_timing_class.v:75 finished
|
||||
-V{t#,#} Resuming: Process waiting at t/t_timing_class.v:74
|
||||
-V{t#,#} Resuming: Process waiting at t/t_timing_class.v:224
|
||||
-V{t#,#}+ Vt_timing_debug2___024root___eval_phase__act
|
||||
-V{t#,#}+ Vt_timing_debug2___024root___eval_triggers__act
|
||||
-V{t#,#} No suspended processes waiting for dynamic trigger evaluation
|
||||
|
|
@ -1271,15 +1286,15 @@
|
|||
-V{t#,#}+ Vt_timing_debug2___024root___trigger_anySet__act
|
||||
-V{t#,#}+ Vt_timing_debug2___024root___timing_resume
|
||||
-V{t#,#} Delayed processes:
|
||||
-V{t#,#} Awaiting time 75: Process waiting at t/t_timing_class.v:224
|
||||
-V{t#,#} Awaiting time 75: Process waiting at t/t_timing_class.v:131
|
||||
-V{t#,#} Awaiting time 75: Process waiting at t/t_timing_class.v:224
|
||||
-V{t#,#} Awaiting time 80: Process waiting at t/t_timing_class.v:136
|
||||
-V{t#,#} Awaiting time 80: Process waiting at t/t_timing_class.v:190
|
||||
-V{t#,#} Awaiting time 101: Process waiting at t/t_timing_class.v:274
|
||||
-V{t#,#} Resuming delayed processes
|
||||
-V{t#,#} Resuming: Process waiting at t/t_timing_class.v:224
|
||||
-V{t#,#} Resuming: Process waiting at t/t_timing_class.v:131
|
||||
-V{t#,#}+ Vt_timing_debug2_t__03a__03aClkClass::__VnoInFunc_flip
|
||||
-V{t#,#} Resuming: Process waiting at t/t_timing_class.v:224
|
||||
-V{t#,#}+ Vt_timing_debug2___024root___eval_phase__act
|
||||
-V{t#,#}+ Vt_timing_debug2___024root___eval_triggers__act
|
||||
-V{t#,#} No suspended processes waiting for dynamic trigger evaluation
|
||||
|
|
@ -1327,12 +1342,34 @@
|
|||
-V{t#,#} Resuming delayed processes
|
||||
-V{t#,#} Resuming: Process waiting at t/t_timing_class.v:136
|
||||
-V{t#,#} Resuming: Process waiting at t/t_timing_class.v:190
|
||||
-V{t#,#}+ Vt_timing_debug2_std__03a__03aprocess__Vclpkg::__VnoInFunc_self
|
||||
-V{t#,#}+ Vt_timing_debug2_std__03a__03aprocess::new
|
||||
-V{t#,#}+ Vt_timing_debug2_std__03a__03aprocess::_ctor_var_reset
|
||||
-V{t#,#}+ Vt_timing_debug2_t___eval_initial__TOP__t__Vtiming__6____Vfork_2__0
|
||||
-V{t#,#}+ Vt_timing_debug2_t__03a__03aAssignDelayClass::__VnoInFunc_do_assign
|
||||
-V{t#,#}+ Vt_timing_debug2_std__03a__03aprocess::__VnoInFunc_status
|
||||
-V{t#,#} Suspending process waiting for @([true] (32'h1 != $_EXPRSTMT( // Function: status t.__Vtask___VforkTask_1__29____VforkParent.(t.__Vtask_status__30__Vfuncout); , ); )) at t/t_timing_class.v:229
|
||||
-V{t#,#} Resuming: Process waiting at t/t_timing_class.v:131
|
||||
-V{t#,#}+ Vt_timing_debug2_t__03a__03aClkClass::__VnoInFunc_flip
|
||||
-V{t#,#}+ Vt_timing_debug2___024root___eval_phase__act
|
||||
-V{t#,#}+ Vt_timing_debug2___024root___eval_triggers__act
|
||||
-V{t#,#} Suspended processes waiting for dynamic trigger evaluation:
|
||||
-V{t#,#} - Process waiting at t/t_timing_class.v:229
|
||||
-V{t#,#} Resuming: Process waiting at t/t_timing_class.v:229
|
||||
-V{t#,#}+ Vt_timing_debug2_std__03a__03aprocess::__VnoInFunc_status
|
||||
-V{t#,#} Process waiting for @([true] (32'h1 != $_EXPRSTMT( // Function: status t.__Vtask___VforkTask_1__29____VforkParent.(t.__Vtask_status__30__Vfuncout); , ); )) at t/t_timing_class.v:229 awaiting resumption
|
||||
-V{t#,#}+ Vt_timing_debug2___024root___dump_triggers__act
|
||||
-V{t#,#}+ Vt_timing_debug2___024root___trigger_anySet__act
|
||||
-V{t#,#} 'act' region trigger index 2 is active: @([true] __VdynSched.evaluate())
|
||||
-V{t#,#}+ Vt_timing_debug2___024root___timing_commit
|
||||
-V{t#,#}+ Vt_timing_debug2___024root___trigger_orInto__act
|
||||
-V{t#,#}+ Vt_timing_debug2___024root___trigger_anySet__act
|
||||
-V{t#,#}+ Vt_timing_debug2___024root___timing_resume
|
||||
-V{t#,#} Resuming processes:
|
||||
-V{t#,#} - Process waiting at t/t_timing_class.v:229
|
||||
-V{t#,#} Resuming: Process waiting at t/t_timing_class.v:229
|
||||
-V{t#,#}+ Vt_timing_debug2_t__03a__03aAssignDelayClass::__VnoInFunc_do_assign
|
||||
-V{t#,#}+ Vt_timing_debug2___024root___eval_phase__act
|
||||
-V{t#,#}+ Vt_timing_debug2___024root___eval_triggers__act
|
||||
-V{t#,#} No suspended processes waiting for dynamic trigger evaluation
|
||||
-V{t#,#}+ Vt_timing_debug2___024root___dump_triggers__act
|
||||
-V{t#,#}+ Vt_timing_debug2___024root___trigger_anySet__act
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import vltest_bootstrap
|
|||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.compile(verilator_flags2=["--binary -Wno-UNOPTFLAT"])
|
||||
test.compile(verilator_flags2=["--binary"])
|
||||
|
||||
test.execute()
|
||||
|
||||
|
|
|
|||
|
|
@ -1,23 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2024 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
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('vlt')
|
||||
test.top_filename = "t/t_timing_fork_comb.v"
|
||||
|
||||
# Should convert the first always into combo and detect cycle
|
||||
test.lint(fails=True, verilator_flags2=["--timing"])
|
||||
|
||||
test.file_grep(
|
||||
test.compile_log_filename,
|
||||
r'%Warning-UNOPTFLAT: t/t_timing_fork_comb.v:\d+:\d+: Signal unoptimizable: Circular combinational logic:'
|
||||
)
|
||||
|
||||
test.passes()
|
||||
Loading…
Reference in New Issue