IEEE compliant scheduler (#3384)

This is a major re-design of the way code is scheduled in Verilator,
with the goal of properly supporting the Active and NBA regions of the
SystemVerilog scheduling model, as defined in IEEE 1800-2017 chapter 4.

With this change, all internally generated clocks should simulate
correctly, and there should be no more need for the `clock_enable` and
`clocker` attributes for correctness in the absence of Verilator
generated library models (`--lib-create`).

Details of the new scheduling model and algorithm are provided in
docs/internals.rst.

Implements #3278
This commit is contained in:
Geza Lore
2022-05-15 16:03:32 +01:00
committed by GitHub
parent 39b7c47e7b
commit 599d23697d
157 changed files with 6386 additions and 4395 deletions
+33 -19
View File
@@ -36,20 +36,28 @@
// Active class functions
class ActiveTopVisitor final : public VNVisitor {
private:
// NODE STATE
// Entire netlist
// AstNode::user() bool. True if processed
// Each call to V3Const::constify
// AstNode::user4() Used by V3Const::constify, called below
const VNUser1InUse m_inuser1;
// STATE
SenTreeFinder m_finder; // Find global sentree's / add them under the AstTopScope
// METHODS
VL_DEBUG_FUNC; // Declare debug()
static bool isInitial(AstNode* nodep) {
const VNUser1InUse user1InUse;
// Return true if no variables that read.
return nodep->forall<AstVarRef>([&](const AstVarRef* refp) -> bool {
AstVarScope* const vscp = refp->varScopep();
// Note: Use same heuristic as ordering does to ignore written variables
// TODO: Use live variable analysis.
if (refp->access().isWriteOnly()) {
vscp->user1(true);
return true;
}
// Read or ReadWrite: OK if written before
return vscp->user1();
});
}
// VISITORS
virtual void visit(AstNodeModule* nodep) override {
// Create required actives and add to module
@@ -70,15 +78,6 @@ private:
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
return;
}
// Copy combo tree to settlement tree with duplicated statements
if (sensesp->hasCombo()) {
AstSenTree* const newsentreep = new AstSenTree(
nodep->fileline(), new AstSenItem(nodep->fileline(), AstSenItem::Settle()));
AstActive* const newp = new AstActive(nodep->fileline(), "settle", newsentreep);
newp->sensesStorep(newsentreep);
if (nodep->stmtsp()) newp->addStmtsp(nodep->stmtsp()->cloneTree(true));
nodep->addNextHere(newp);
}
// Move the SENTREE for each active up to the global level.
// This way we'll easily see what clock domains are identical
AstSenTree* const wantp = m_finder.getSenTree(sensesp);
@@ -97,8 +96,23 @@ private:
}
nodep->sensesp(wantp);
}
// No need to do statements under it, they're already moved.
// iterateChildren(nodep);
// If this is combinational logic that does not read any variables, then it really is an
// initial block in disguise, so move such logic under an Initial AstActive, V3Order would
// prune these otherwise.
// TODO: we should warn for these if they were 'always @*' as some (including strictly
// compliant) simulators will never execute these.
if (nodep->sensesp()->hasCombo()) {
FileLine* const flp = nodep->fileline();
AstActive* initialp = nullptr;
for (AstNode *logicp = nodep->stmtsp(), *nextp; logicp; logicp = nextp) {
nextp = logicp->nextp();
if (!isInitial(logicp)) continue;
if (!initialp) initialp = new AstActive{flp, "", m_finder.getInitial()};
initialp->addStmtsp(logicp->unlinkFrBack());
}
if (initialp) nodep->addHereThisAsNext(initialp);
}
}
virtual void visit(AstNodeProcedure* nodep) override { // LCOV_EXCL_LINE
nodep->v3fatalSrc("Node should have been under ACTIVE");