verilator/src/V3GenClk.cpp

226 lines
8.7 KiB
C++
Raw Normal View History

// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Generated Clock repairs
//
2019-11-08 04:33:59 +01:00
// Code available from: https://verilator.org
//
//*************************************************************************
//
2022-01-01 14:26:40 +01:00
// Copyright 2003-2022 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
//
//*************************************************************************
// GENCLK TRANSFORMATIONS:
// Follow control-flow graph with assignments and var usages
// ASSIGNDLY to variable later used as clock requires change detect
//
//*************************************************************************
#include "config_build.h"
#include "verilatedos.h"
#include "V3GenClk.h"
#include "V3Ast.h"
#include "V3Global.h"
//######################################################################
// GenClk state, as a visitor of each AstNode
class GenClkBaseVisitor VL_NOT_FINAL : public VNVisitor {
protected:
VL_DEBUG_FUNC; // Declare debug()
};
//######################################################################
// GenClk Read
class GenClkRenameVisitor final : public GenClkBaseVisitor {
private:
// NODE STATE
// Cleared on top scope
// AstVarScope::user2() -> AstVarScope*. Signal replacing activation with
// AstVarRef::user3() -> bool. Signal is replaced activation (already done)
const VNUser2InUse m_inuser2;
const VNUser3InUse m_inuser3;
// STATE
const AstActive* m_activep = nullptr; // Inside activate statement
AstNodeModule* const m_topModp; // Top module
AstScope* const m_scopetopp = v3Global.rootp()->topScopep()->scopep(); // The top AstScope
// METHODS
AstVarScope* genInpClk(AstVarScope* vscp) {
2022-02-11 11:46:02 +01:00
if (!vscp->user2p()) {
// In order to create a __VinpClk* for a signal, it needs to be marked circular.
// The DPI export trigger is never marked circular by V3Order (see comments in
// OrderVisitor::nodeMarkCircular). The only other place where one might mark
// a node circular is in this pass (V3GenClk), if the signal is assigned but was
// previously used as a clock. The DPI export trigger is only ever assigned in
// a DPI export called from outside eval, or from a DPI import, which are not
// discovered by GenClkReadVisitor (note that impure tasks - i.e.: those setting
// non-local variables - cannot be no-inline, see V3Task), hence the DPI export
// trigger should never be marked circular. Note that ordering should still be
// correct as there will be a change detect on any signals set from a DPI export
// that might have dependents scheduled earlier.
UASSERT_OBJ(vscp != v3Global.rootp()->dpiExportTriggerp(), vscp,
"DPI export trigger should not need __VinpClk");
AstVar* const varp = vscp->varp();
const string newvarname
= "__VinpClk__" + vscp->scopep()->nameDotless() + "__" + varp->name();
// Create: VARREF(inpclk)
// ...
// ASSIGN(VARREF(inpclk), VARREF(var))
AstVar* const newvarp
= new AstVar(varp->fileline(), VVarType::MODULETEMP, newvarname, varp);
m_topModp->addStmtp(newvarp);
AstVarScope* const newvscp = new AstVarScope(vscp->fileline(), m_scopetopp, newvarp);
m_scopetopp->addVarp(newvscp);
AstAssign* const asninitp = new AstAssign(
vscp->fileline(), new AstVarRef(vscp->fileline(), newvscp, VAccess::WRITE),
new AstVarRef(vscp->fileline(), vscp, VAccess::READ));
m_scopetopp->addFinalClkp(asninitp);
//
vscp->user2p(newvscp);
}
2022-02-11 11:46:02 +01:00
return VN_AS(vscp->user2p(), VarScope);
}
// VISITORS
virtual void visit(AstVarRef* nodep) override {
// Consumption/generation of a variable,
2022-02-11 11:46:02 +01:00
if (m_activep && !nodep->user3SetOnce()) {
AstVarScope* const vscp = nodep->varScopep();
if (vscp->isCircular()) {
UINFO(8, " VarActReplace " << nodep << endl);
// Replace with the new variable
AstVarScope* const newvscp = genInpClk(vscp);
AstVarRef* const newrefp
= new AstVarRef(nodep->fileline(), newvscp, nodep->access());
nodep->replaceWith(newrefp);
VL_DO_DANGLING(pushDeletep(nodep), nodep);
}
}
}
virtual void visit(AstActive* nodep) override {
m_activep = nodep;
iterate(nodep->sensesp());
m_activep = nullptr;
}
//-----
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
public:
// CONSTRUCTORS
GenClkRenameVisitor(AstTopScope* nodep, AstNodeModule* topModp)
: m_topModp{topModp} {
iterate(nodep);
}
virtual ~GenClkRenameVisitor() override = default;
};
//######################################################################
// GenClk Read
class GenClkReadVisitor final : public GenClkBaseVisitor {
private:
// NODE STATE
// Cleared on top scope
// AstVarScope::user() -> bool. Set when the var has been used as clock
// STATE
bool m_tracingCall = false; // Iterating into a call to a cfunc
const AstActive* m_activep = nullptr; // Inside activate statement
const AstNodeAssign* m_assignp = nullptr; // Inside assigndly statement
AstNodeModule* m_topModp = nullptr; // Top module
// VISITORS
virtual void visit(AstTopScope* nodep) override {
{
2022-02-11 11:46:02 +01:00
const VNUser1InUse user1InUse;
iterateChildren(nodep);
}
2022-02-11 11:46:02 +01:00
// Make the new clock signals and replace any activate references
// See rename, it does some AstNode::userClearTree()'s
GenClkRenameVisitor{nodep, m_topModp};
}
virtual void visit(AstNodeModule* nodep) override {
// Only track the top scopes, not lower level functions
if (nodep->isTop()) {
m_topModp = nodep;
iterateChildren(nodep);
}
}
virtual void visit(AstNodeCCall* nodep) override {
iterateChildren(nodep);
if (!nodep->funcp()->entryPoint()) {
// Enter the function and trace it
m_tracingCall = true;
iterate(nodep->funcp());
}
}
virtual void visit(AstCFunc* nodep) override {
if (!m_tracingCall && !nodep->entryPoint()) {
// Only consider logic within a CFunc when looking
// at the call to it, and not when scanning whatever
// scope it happens to live beneath.
return;
}
m_tracingCall = false;
iterateChildren(nodep);
}
//----
virtual void visit(AstVarRef* nodep) override {
// Consumption/generation of a variable,
AstVarScope* const vscp = nodep->varScopep();
UASSERT_OBJ(vscp, nodep, "Scope not assigned");
if (m_activep) {
UINFO(8, " VarAct " << nodep << endl);
vscp->user1(true);
}
if (m_assignp && nodep->access().isWriteOrRW() && vscp->user1()) {
// Variable was previously used as a clock, and is now being set
// Thus a unordered generated clock...
UINFO(8, " VarSetAct " << nodep << endl);
vscp->circular(true);
}
}
virtual void visit(AstNodeAssign* nodep) override {
// UINFO(8, "ASS " << nodep << endl);
m_assignp = nodep;
iterateChildren(nodep);
m_assignp = nullptr;
}
virtual void visit(AstActive* nodep) override {
UINFO(8, "ACTIVE " << nodep << endl);
m_activep = nodep;
UASSERT_OBJ(nodep->sensesp(), nodep, "Unlinked");
iterate(nodep->sensesp());
m_activep = nullptr;
iterateChildren(nodep);
}
//-----
virtual void visit(AstVar*) override {} // Don't want varrefs under it
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
public:
// CONSTRUCTORS
explicit GenClkReadVisitor(AstNetlist* nodep) { iterate(nodep); }
virtual ~GenClkReadVisitor() override = default;
};
//######################################################################
// GenClk class functions
void V3GenClk::genClkAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ GenClkReadVisitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("genclk", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
}