mirror of
https://github.com/verilator/verilator.git
synced 2026-09-03 08:24:31 +02:00
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:
+33
-19
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user