Internals: Move the eval loop into the runtime library (#8225)

The loops modelling the SystemVerilog scheduling regions are no longer
generated. They now live in 'VerilatedEvalLoop' in the runtime library.
The generated model holds one as a member, passing itself to it, and
exposes each evaluation entry point to it as a pure virtual method on
VerilatedModel. The model's 'eval' and 'eval_step' remain the top level
entry points, and are backward compatible.

V3Sched no longer emits '_eval' or '_eval_settle', etc.. Instead every
evaluation entry point called from the runtime is enumerated by 'VEval',

Scheduling creates all entry points, for all scheduling regions, even if
they are empty, and the runtime eval loop calls everything
unconditionally. If regions are empty, this is simply a call to an empty
function. This will hurt performance on very small models, but should
not be noticeable on anything meaningful, so it is likely best to keep
to reduce complexity.

A scheduling entry points evaluate a single iteration and returns
whether it did any work, they are effectively the previous
`_eval_phase_*` functions.
This commit is contained in:
Geza Lore
2026-08-27 07:53:02 -04:00
committed by GitHub
parent 59f221986c
commit 8546d5db06
44 changed files with 2204 additions and 1418 deletions
+11 -28
View File
@@ -34,18 +34,7 @@ namespace V3Sched {
namespace {
AstVarScope* newArgument(AstCFunc* funcp, AstNodeDType* dtypep, const std::string& name,
VDirection direction) {
FileLine* const flp = funcp->fileline();
AstScope* const scopep = funcp->scopep();
AstVar* const varp = new AstVar{flp, VVarType::BLOCKTEMP, name, dtypep};
varp->funcLocal(true);
varp->direction(direction);
funcp->addArgsp(varp);
AstVarScope* const vscp = new AstVarScope{flp, scopep, varp};
scopep->addVarsp(vscp);
return vscp;
}
using util::newArgument;
AstVarScope* newLocal(AstCFunc* funcp, AstNodeDType* dtypep, const std::string& name) {
FileLine* const flp = funcp->fileline();
@@ -404,22 +393,24 @@ AstSenTree* TriggerKit::newExtraTriggerSenTree(AstVarScope* vscp, uint32_t index
return newTriggerSenTree(vscp, {index + m_nSenseWords * WORD_SIZE});
}
void TriggerKit::addExtraTriggerAssignment(AstVarScope* vscp, uint32_t index, bool clear) const {
AstNodeStmt* TriggerKit::newExtraTriggerAssignment(AstVarScope* vscp, uint32_t index) const {
index += m_nSenseWords * WORD_SIZE;
const uint32_t wordIndex = index / WORD_SIZE;
const uint32_t bitIndex = index % WORD_SIZE;
FileLine* const flp = vscp->fileline();
// Set the trigger bit
AstVarRef* const refp = new AstVarRef{flp, m_vscp, VAccess::WRITE};
AstNodeExpr* const wordp = new AstArraySel{flp, refp, static_cast<int>(wordIndex)};
AstNodeExpr* const trigLhsp = new AstSel{flp, wordp, static_cast<int>(bitIndex), 1};
AstNodeExpr* const trigRhsp = new AstVarRef{flp, vscp, VAccess::READ};
AstNode* const setp = new AstAssign{flp, trigLhsp, trigRhsp};
if (clear) {
// Clear the input variable
setp->addNext(new AstAssign{flp, new AstVarRef{flp, vscp, VAccess::WRITE},
new AstConst{flp, AstConst::BitFalse{}}});
}
return new AstAssign{flp, trigLhsp, trigRhsp};
}
void TriggerKit::addExtraTriggerAssignment(AstVarScope* vscp, uint32_t index) const {
FileLine* const flp = vscp->fileline();
// Set the trigger bit, then clear the input variable
AstNode* const setp = newExtraTriggerAssignment(vscp, index);
setp->addNext(new AstAssign{flp, new AstVarRef{flp, vscp, VAccess::WRITE},
new AstConst{flp, AstConst::BitFalse{}}});
if (AstNode* const nodep = m_compVecp->stmtsp()) {
setp->addNext(setp, nodep->unlinkFrBackWithNext());
}
@@ -779,10 +770,6 @@ TriggerKit TriggerKit::create(AstNetlist* netlistp, //
AstScope* const scopep = netlistp->topScopep()->scopep();
{
AstCFunc* const fp = kit.m_compVecp;
// Profiling push
if (v3Global.opt.profExec()) {
fp->addStmtsp(AstCStmt::profExecSectionPush(flp, "trigBase " + name));
}
// Trigger computation
for (AstNodeStmt* const nodep : senResults.m_preUpdates) fp->addStmtsp(nodep);
fp->addStmtsp(trigStmtsp);
@@ -796,10 +783,6 @@ TriggerKit TriggerKit::create(AstNetlist* netlistp, //
ifp->addThensp(util::setVar(initVscp, 1));
ifp->addThensp(initialTrigsp);
}
// Profiling pop
if (v3Global.opt.profExec()) {
fp->addStmtsp(AstCStmt::profExecSectionPop(flp, "trigBase " + name));
}
util::splitCheck(fp);
};
// If there are 'pre' triggers, compute them