mirror of
https://github.com/verilator/verilator.git
synced 2026-10-06 01:54:01 +02:00
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.
138 lines
5.2 KiB
C++
138 lines
5.2 KiB
C++
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
|
//*************************************************************************
|
|
// DESCRIPTION: Verilator: Post scheduling transformations
|
|
//
|
|
// Code available from: https://verilator.org
|
|
//
|
|
//*************************************************************************
|
|
//
|
|
// 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-FileCopyrightText: 2003-2026 Wilson Snyder
|
|
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
|
//
|
|
//*************************************************************************
|
|
// V3Clock's Transformations:
|
|
//
|
|
// This pass is historic and does some arbitray post scheduling rewrites
|
|
//
|
|
//*************************************************************************
|
|
|
|
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
|
|
|
|
#include "V3Clock.h"
|
|
|
|
#include "V3Const.h"
|
|
#include "V3Sched.h"
|
|
|
|
VL_DEFINE_DEBUG_FUNCTIONS;
|
|
|
|
//######################################################################
|
|
// Convert every WRITE AstVarRef to a READ ref
|
|
|
|
class ConvertWriteRefsToRead final : public VNVisitor {
|
|
// MEMBERS
|
|
AstNodeExpr* m_result = nullptr;
|
|
|
|
// CONSTRUCTORS
|
|
explicit ConvertWriteRefsToRead(AstNodeExpr* nodep) {
|
|
m_result = VN_AS(iterateSubtreeReturnEdits(nodep), NodeExpr);
|
|
}
|
|
|
|
// VISITORS
|
|
void visit(AstVarRef* nodep) override {
|
|
UASSERT_OBJ(!nodep->access().isRW(), nodep, "Cannot handle a READWRITE reference");
|
|
if (nodep->access().isWriteOnly()) {
|
|
nodep->replaceWith(
|
|
new AstVarRef{nodep->fileline(), nodep->varScopep(), VAccess::READ});
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
|
}
|
|
}
|
|
|
|
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
|
|
|
public:
|
|
static AstNodeExpr* main(AstNodeExpr* nodep) { return ConvertWriteRefsToRead{nodep}.m_result; }
|
|
};
|
|
|
|
//######################################################################
|
|
// Clock state, as a visitor of each AstNode
|
|
|
|
class ClockVisitor final : public VNVisitor {
|
|
// NODE STATE
|
|
|
|
// STATE
|
|
AstCFunc* const m_sampleCFuncp; // The CFunc to populate with sampled value assignments
|
|
|
|
// VISITORS
|
|
void visit(AstCoverToggle* nodep) override {
|
|
// UINFOTREE(1, nodep, "", "ct");
|
|
// COVERTOGGLE(INC, ORIG, CHANGE) ->
|
|
// IF(ORIG ^ CHANGE) { INC; CHANGE = ORIG; }
|
|
AstCoverInc* const incp = nodep->incp()->unlinkFrBack();
|
|
AstNodeExpr* const origp = nodep->origp()->unlinkFrBack();
|
|
AstNodeExpr* const changeWrp = nodep->changep()->unlinkFrBack();
|
|
AstNodeExpr* const changeRdp = ConvertWriteRefsToRead::main(changeWrp->cloneTree(false));
|
|
AstNodeExpr* comparedp = nullptr;
|
|
incp->toggleExprp(origp->cloneTree(false));
|
|
incp->toggleCovExprp(changeRdp->cloneTree(false));
|
|
FileLine* const flp = nodep->fileline();
|
|
// Xor will optimize better than Eq, when CoverToggle has bit selects,
|
|
// but can only use Xor with non-opaque types
|
|
if (const AstBasicDType* const bdtypep
|
|
= VN_CAST(origp->dtypep()->skipRefp(), BasicDType)) {
|
|
if (!bdtypep->isOpaque()) {
|
|
AstNodeExpr* const zp = new AstConst{flp, AstConst::DTyped{}, origp->dtypep()};
|
|
comparedp = new AstNeq{flp, zp, new AstXor{flp, origp, changeRdp}};
|
|
}
|
|
}
|
|
if (!comparedp) comparedp = AstNeq::newTyped(flp, origp, changeRdp);
|
|
AstIf* const newp = new AstIf{flp, comparedp, incp};
|
|
// We could add another IF to detect posedges, and only increment if so.
|
|
// It's another whole branch though versus a potential memory miss.
|
|
// We'll go with the miss.
|
|
newp->addThensp(new AstAssign{flp, changeWrp, origp->cloneTree(false)});
|
|
nodep->replaceWith(newp);
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
|
}
|
|
void visit(AstSenTree* nodep) override {
|
|
pushDeletep(nodep->unlinkFrBack()); // No longer needed
|
|
}
|
|
|
|
//========== Move sampled assignments
|
|
void visit(AstVarScope* nodep) override {
|
|
AstVar* const varp = nodep->varp();
|
|
if (!varp->valuep()) return;
|
|
if (!varp->sampled()) return;
|
|
|
|
FileLine* const flp = nodep->fileline();
|
|
AstNodeExpr* const rhsp = VN_AS(varp->valuep()->unlinkFrBack(), NodeExpr);
|
|
AstVarRef* const lhsp = new AstVarRef{flp, nodep, VAccess::WRITE};
|
|
m_sampleCFuncp->addStmtsp(new AstAssign{flp, lhsp, rhsp});
|
|
varp->direction(VDirection::NONE); // Restore defaults
|
|
varp->primaryIO(false);
|
|
}
|
|
|
|
//--------------------
|
|
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
|
|
|
public:
|
|
// CONSTRUCTORS
|
|
explicit ClockVisitor(AstNetlist* netlistp)
|
|
: m_sampleCFuncp{netlistp->evalFuncp(VEval::SAMPLE)} {
|
|
iterate(netlistp);
|
|
V3Sched::util::splitCheck(m_sampleCFuncp);
|
|
}
|
|
~ClockVisitor() override = default;
|
|
};
|
|
|
|
//######################################################################
|
|
// Clock class functions
|
|
|
|
void V3Clock::clockAll(AstNetlist* nodep) {
|
|
UINFO(2, __FUNCTION__ << ":");
|
|
{ ClockVisitor{nodep}; } // Destruct before checking
|
|
V3Global::dumpCheckGlobalTree("clock", 0, dumpTreeEitherLevel() >= 3);
|
|
}
|