Internals: Split some code from V3Sched.cpp

Add V3SchedUtil.cpp that contains common small utility functions.

Add V3SchedTrigger.cpp that contains functionality building the trigger
mechanism code.

No functional change, just code movement. Prep for some further work.
This commit is contained in:
Geza Lore
2025-10-27 21:18:47 +00:00
parent 151b17ac82
commit cddbb5e095
8 changed files with 624 additions and 520 deletions
+197
View File
@@ -0,0 +1,197 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Utility functions used by code scheduling
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// Copyright 2003-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
//
//*************************************************************************
//
//
//
//*************************************************************************
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
#include "V3Const.h"
#include "V3EmitCBase.h"
#include "V3EmitV.h"
#include "V3Order.h"
#include "V3Sched.h"
#include "V3SenExprBuilder.h"
#include "V3Stats.h"
VL_DEFINE_DEBUG_FUNCTIONS;
namespace V3Sched {
namespace util {
AstCFunc* makeSubFunction(AstNetlist* netlistp, const string& name, bool slow) {
AstScope* const scopeTopp = netlistp->topScopep()->scopep();
AstCFunc* const funcp = new AstCFunc{netlistp->fileline(), name, scopeTopp, ""};
funcp->dontCombine(true);
funcp->isStatic(false);
funcp->isLoose(true);
funcp->slow(slow);
funcp->isConst(false);
funcp->declPrivate(true);
scopeTopp->addBlocksp(funcp);
return funcp;
}
AstCFunc* makeTopFunction(AstNetlist* netlistp, const string& name, bool slow) {
AstCFunc* const funcp = makeSubFunction(netlistp, name, slow);
funcp->entryPoint(true);
funcp->keepIfEmpty(true);
return funcp;
}
AstNodeStmt* setVar(AstVarScope* vscp, uint32_t val) {
FileLine* const flp = vscp->fileline();
AstVarRef* const refp = new AstVarRef{flp, vscp, VAccess::WRITE};
AstConst* const valp = new AstConst{flp, AstConst::DTyped{}, vscp->dtypep()};
valp->num().setLong(val);
return new AstAssign{flp, refp, valp};
}
AstNodeStmt* incrementVar(AstVarScope* vscp) {
FileLine* const flp = vscp->fileline();
AstVarRef* const wrefp = new AstVarRef{flp, vscp, VAccess::WRITE};
AstVarRef* const rrefp = new AstVarRef{flp, vscp, VAccess::READ};
AstConst* const onep = new AstConst{flp, AstConst::DTyped{}, vscp->dtypep()};
onep->num().setLong(1);
return new AstAssign{flp, wrefp, new AstAdd{flp, rrefp, onep}};
}
AstNodeStmt* callVoidFunc(AstCFunc* funcp) {
AstCCall* const callp = new AstCCall{funcp->fileline(), funcp};
callp->dtypeSetVoid();
return callp->makeStmt();
}
AstNodeStmt* checkIterationLimit(AstNetlist* netlistp, const string& name, AstVarScope* counterp,
AstCFunc* trigDumpp) {
FileLine* const flp = netlistp->fileline();
// If we exceeded the iteration limit, die
const uint32_t limit = v3Global.opt.convergeLimit();
AstVarRef* const counterRefp = new AstVarRef{flp, counterp, VAccess::READ};
AstConst* const constp = new AstConst{flp, AstConst::DTyped{}, counterp->dtypep()};
constp->num().setLong(limit);
AstNodeExpr* const condp = new AstGt{flp, counterRefp, constp};
AstIf* const ifp = new AstIf{flp, condp};
ifp->branchPred(VBranchPred::BP_UNLIKELY);
AstCStmt* const stmtp = new AstCStmt{flp};
ifp->addThensp(stmtp);
FileLine* const locp = netlistp->topModulep()->fileline();
const std::string& file = VIdProtect::protect(locp->filename());
const std::string& line = std::to_string(locp->lineno());
stmtp->add("#ifdef VL_DEBUG\n");
stmtp->add(callVoidFunc(trigDumpp));
stmtp->add("#endif\n");
stmtp->add("VL_FATAL_MT(\"" + V3OutFormatter::quoteNameControls(file) + "\", " + line
+ ", \"\", \"" + name + " region did not converge after " + std::to_string(limit)
+ " tries\");");
return ifp;
}
AstNodeStmt* profExecSectionPush(FileLine* flp, const string& section) {
const string name
= (v3Global.opt.hierChild() ? (v3Global.opt.topModule() + " ") : "") + section;
return new AstCStmt{flp, "VL_EXEC_TRACE_ADD_RECORD(vlSymsp).sectionPush(\"" + name + "\");"};
}
AstNodeStmt* profExecSectionPop(FileLine* flp) {
return new AstCStmt{flp, "VL_EXEC_TRACE_ADD_RECORD(vlSymsp).sectionPop();"};
}
static AstCFunc* splitCheckCreateNewSubFunc(AstCFunc* ofuncp) {
static std::map<AstCFunc*, uint32_t> funcNums; // What split number to attach to a function
const uint32_t funcNum = funcNums[ofuncp]++;
const std::string name = ofuncp->name() + "__" + cvtToStr(funcNum);
AstCFunc* const subFuncp = new AstCFunc{ofuncp->fileline(), name, ofuncp->scopep()};
subFuncp->dontCombine(true);
subFuncp->isStatic(false);
subFuncp->isLoose(true);
subFuncp->slow(ofuncp->slow());
subFuncp->declPrivate(ofuncp->declPrivate());
if (ofuncp->needProcess()) subFuncp->setNeedProcess();
return subFuncp;
};
// Split large function according to --output-split-cfuncs
void splitCheck(AstCFunc* ofuncp) {
if (!v3Global.opt.outputSplitCFuncs() || !ofuncp->stmtsp()) return;
if (ofuncp->nodeCount() < v3Global.opt.outputSplitCFuncs()) return;
int func_stmts = 0;
const bool is_ofuncp_coroutine = ofuncp->isCoroutine();
AstCFunc* funcp = nullptr;
const auto finishSubFuncp = [&](AstCFunc* subFuncp) {
ofuncp->scopep()->addBlocksp(subFuncp);
AstCCall* const callp = new AstCCall{subFuncp->fileline(), subFuncp};
callp->dtypeSetVoid();
if (is_ofuncp_coroutine && subFuncp->exists([](const AstCAwait*) {
return true;
})) { // Wrap call with co_await
subFuncp->rtnType("VlCoroutine");
AstCAwait* const awaitp = new AstCAwait{subFuncp->fileline(), callp};
awaitp->dtypeSetVoid();
ofuncp->addStmtsp(awaitp->makeStmt());
} else {
ofuncp->addStmtsp(callp->makeStmt());
}
};
funcp = splitCheckCreateNewSubFunc(ofuncp);
func_stmts = 0;
// Unlink all statements, then add item by item to new sub-functions
AstBegin* const tempp = new AstBegin{ofuncp->fileline(), "[EditWrapper]",
ofuncp->stmtsp()->unlinkFrBackWithNext(), false};
while (tempp->stmtsp()) {
AstNode* const itemp = tempp->stmtsp()->unlinkFrBack();
const int stmts = itemp->nodeCount();
if ((func_stmts + stmts) > v3Global.opt.outputSplitCFuncs()) {
finishSubFuncp(funcp);
funcp = splitCheckCreateNewSubFunc(ofuncp);
func_stmts = 0;
}
funcp->addStmtsp(itemp);
func_stmts += stmts;
}
finishSubFuncp(funcp);
VL_DO_DANGLING(tempp->deleteTree(), tempp);
}
// Build an AstIf conditional on the given SenTree being triggered
AstIf* createIfFromSenTree(AstSenTree* senTreep) {
senTreep = VN_AS(V3Const::constifyExpensiveEdit(senTreep), SenTree);
UASSERT_OBJ(senTreep->sensesp(), senTreep, "No sensitivity list during scheduling");
// Convert the SenTree to a boolean expression that is true when triggered
AstNodeExpr* senEqnp = nullptr;
for (AstSenItem *senp = senTreep->sensesp(), *nextp; senp; senp = nextp) {
nextp = VN_AS(senp->nextp(), SenItem);
// They should all be ET_TRUE, as set up by V3Sched
UASSERT_OBJ(senp->edgeType() == VEdgeType::ET_TRUE, senp, "Bad scheduling trigger type");
AstNodeExpr* const senOnep = senp->sensp()->cloneTree(false);
senEqnp = senEqnp ? new AstOr{senp->fileline(), senEqnp, senOnep} : senOnep;
}
// Create the if statement conditional on the triggers
return new AstIf{senTreep->fileline(), senEqnp};
}
} // namespace util
} // namespace V3Sched