forked from github/verilator
304 lines
12 KiB
C++
304 lines
12 KiB
C++
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
|
//*************************************************************************
|
|
// DESCRIPTION: Verilator: Add temporaries, such as for changed nodes
|
|
//
|
|
// Code available from: https://verilator.org
|
|
//
|
|
//*************************************************************************
|
|
//
|
|
// Copyright 2003-2020 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
|
|
//
|
|
//*************************************************************************
|
|
// V3Changed's Transformations:
|
|
//
|
|
// Each module:
|
|
// Each combo block
|
|
// For each variable that comes from combo block and is generated AFTER a usage
|
|
// Add __Vlast_{var} to local section, init to current value (just use array?)
|
|
// Change = if any var != last.
|
|
// If a signal is used as a clock in this module or any
|
|
// module *below*, and it isn't a input to this module,
|
|
// we need to indicate a new clock has been created.
|
|
//
|
|
//*************************************************************************
|
|
|
|
#include "config_build.h"
|
|
#include "verilatedos.h"
|
|
|
|
#include "V3Global.h"
|
|
#include "V3Ast.h"
|
|
#include "V3Changed.h"
|
|
#include "V3EmitCBase.h"
|
|
|
|
#include <algorithm>
|
|
|
|
//######################################################################
|
|
|
|
class ChangedState {
|
|
public:
|
|
// STATE
|
|
AstNodeModule* m_topModp; // Top module
|
|
AstScope* m_scopetopp; // Scope under TOPSCOPE
|
|
AstCFunc* m_chgFuncp; // Change function we're building
|
|
AstCFunc* m_tlChgFuncp; // Top level change function we're building
|
|
int m_numStmts; // Number of statements added to m_chgFuncp
|
|
int m_funcNum; // Number of change functions emitted
|
|
|
|
ChangedState() {
|
|
m_topModp = NULL;
|
|
m_chgFuncp = NULL;
|
|
m_scopetopp = NULL;
|
|
m_tlChgFuncp = NULL;
|
|
m_numStmts = 0;
|
|
m_funcNum = 0;
|
|
}
|
|
~ChangedState() {}
|
|
|
|
void maybeCreateChgFuncp() {
|
|
// Don't create an extra function call if splitting is disabled
|
|
if (!v3Global.opt.outputSplitCFuncs()) {
|
|
m_chgFuncp = m_tlChgFuncp;
|
|
return;
|
|
}
|
|
if (!m_chgFuncp || v3Global.opt.outputSplitCFuncs() < m_numStmts) {
|
|
m_chgFuncp
|
|
= new AstCFunc(m_scopetopp->fileline(), "_change_request_" + cvtToStr(++m_funcNum),
|
|
m_scopetopp, "QData");
|
|
m_chgFuncp->argTypes(EmitCBaseVisitor::symClassVar());
|
|
m_chgFuncp->symProlog(true);
|
|
m_chgFuncp->declPrivate(true);
|
|
m_scopetopp->addActivep(m_chgFuncp);
|
|
|
|
// Add a top call to it
|
|
AstCCall* callp = new AstCCall(m_scopetopp->fileline(), m_chgFuncp);
|
|
callp->argTypes("vlSymsp");
|
|
|
|
if (!m_tlChgFuncp->stmtsp()) {
|
|
m_tlChgFuncp->addStmtsp(new AstCReturn(m_scopetopp->fileline(), callp));
|
|
} else {
|
|
AstCReturn* returnp = VN_CAST(m_tlChgFuncp->stmtsp(), CReturn);
|
|
UASSERT_OBJ(returnp, m_scopetopp, "Lost CReturn in top change function");
|
|
// This is currently using AstLogOr which will shortcut the
|
|
// evaluation if any function returns true. This is likely what
|
|
// we want and is similar to the logic already in use inside
|
|
// V3EmitC, however, it also means that verbose logging may
|
|
// miss to print change detect variables.
|
|
AstNode* newp = new AstCReturn(
|
|
m_scopetopp->fileline(),
|
|
new AstLogOr(m_scopetopp->fileline(), callp, returnp->lhsp()->unlinkFrBack()));
|
|
returnp->replaceWith(newp);
|
|
VL_DO_DANGLING(returnp->deleteTree(), returnp);
|
|
}
|
|
m_numStmts = 0;
|
|
}
|
|
}
|
|
};
|
|
|
|
//######################################################################
|
|
// Utility visitor to find elements to be compared
|
|
|
|
class ChangedInsertVisitor : public AstNVisitor {
|
|
private:
|
|
// STATE
|
|
ChangedState* m_statep; // Shared state across visitors
|
|
AstVarScope* m_vscp; // Original (non-change) variable we're change-detecting
|
|
AstVarScope* m_newvscp; // New (change detect) variable we're change-detecting
|
|
AstNode* m_varEqnp; // Original var's equation to get var value
|
|
AstNode* m_newLvEqnp; // New var's equation to read value
|
|
AstNode* m_newRvEqnp; // New var's equation to set value
|
|
uint32_t m_detects; // # detects created
|
|
|
|
// CONSTANTS
|
|
enum MiscConsts {
|
|
DETECTARRAY_MAX_INDEXES = 256 // How many indexes before error
|
|
// Ok to increase this, but may result in much slower model
|
|
};
|
|
|
|
void newChangeDet() {
|
|
if (++m_detects > DETECTARRAY_MAX_INDEXES) {
|
|
m_vscp->v3warn(E_DETECTARRAY,
|
|
"Unsupported: Can't detect more than "
|
|
<< cvtToStr(DETECTARRAY_MAX_INDEXES)
|
|
<< " array indexes (probably with UNOPTFLAT warning suppressed): "
|
|
<< m_vscp->prettyName() << endl
|
|
<< m_vscp->warnMore()
|
|
<< "... Could recompile with DETECTARRAY_MAX_INDEXES increased"
|
|
<< endl);
|
|
return;
|
|
}
|
|
m_statep->maybeCreateChgFuncp();
|
|
|
|
AstChangeDet* changep = new AstChangeDet(m_vscp->fileline(), m_varEqnp->cloneTree(true),
|
|
m_newRvEqnp->cloneTree(true), false);
|
|
m_statep->m_chgFuncp->addStmtsp(changep);
|
|
AstAssign* initp = new AstAssign(m_vscp->fileline(), m_newLvEqnp->cloneTree(true),
|
|
m_varEqnp->cloneTree(true));
|
|
m_statep->m_chgFuncp->addFinalsp(initp);
|
|
EmitCBaseCounterVisitor visitor(initp);
|
|
m_statep->m_numStmts += visitor.count();
|
|
}
|
|
|
|
virtual void visit(AstBasicDType* nodep) VL_OVERRIDE { //
|
|
newChangeDet();
|
|
}
|
|
virtual void visit(AstPackArrayDType* nodep) VL_OVERRIDE { //
|
|
newChangeDet();
|
|
}
|
|
virtual void visit(AstUnpackArrayDType* nodep) VL_OVERRIDE {
|
|
for (int index = 0; index < nodep->elementsConst(); ++index) {
|
|
AstNode* origVEp = m_varEqnp;
|
|
AstNode* origNLEp = m_newLvEqnp;
|
|
AstNode* origNREp = m_newRvEqnp;
|
|
|
|
m_varEqnp = new AstArraySel(nodep->fileline(), m_varEqnp->cloneTree(true), index);
|
|
m_newLvEqnp = new AstArraySel(nodep->fileline(), m_newLvEqnp->cloneTree(true), index);
|
|
m_newRvEqnp = new AstArraySel(nodep->fileline(), m_newRvEqnp->cloneTree(true), index);
|
|
|
|
iterate(nodep->subDTypep()->skipRefp());
|
|
|
|
m_varEqnp->deleteTree();
|
|
m_newLvEqnp->deleteTree();
|
|
m_newRvEqnp->deleteTree();
|
|
|
|
m_varEqnp = origVEp;
|
|
m_newLvEqnp = origNLEp;
|
|
m_newRvEqnp = origNREp;
|
|
}
|
|
}
|
|
virtual void visit(AstNodeUOrStructDType* nodep) VL_OVERRIDE {
|
|
if (nodep->packedUnsup()) {
|
|
newChangeDet();
|
|
} else {
|
|
if (debug()) nodep->dumpTree(cout, "-DETECTARRAY-class-");
|
|
m_vscp->v3warn(E_DETECTARRAY, "Unsupported: Can't detect changes on complex variable"
|
|
" (probably with UNOPTFLAT warning suppressed): "
|
|
<< m_vscp->varp()->prettyNameQ());
|
|
}
|
|
}
|
|
virtual void visit(AstNode* nodep) VL_OVERRIDE {
|
|
iterateChildren(nodep);
|
|
if (debug()) nodep->dumpTree(cout, "-DETECTARRAY-general-");
|
|
m_vscp->v3warn(E_DETECTARRAY, "Unsupported: Can't detect changes on complex variable"
|
|
" (probably with UNOPTFLAT warning suppressed): "
|
|
<< m_vscp->varp()->prettyNameQ());
|
|
}
|
|
|
|
public:
|
|
// CONSTRUCTORS
|
|
ChangedInsertVisitor(AstVarScope* vscp, ChangedState* statep) {
|
|
m_statep = statep;
|
|
m_vscp = vscp;
|
|
m_detects = 0;
|
|
{
|
|
AstVar* varp = m_vscp->varp();
|
|
string newvarname
|
|
= ("__Vchglast__" + m_vscp->scopep()->nameDotless() + "__" + varp->shortName());
|
|
// Create: VARREF(_last)
|
|
// ASSIGN(VARREF(_last), VARREF(var))
|
|
// ...
|
|
// CHANGEDET(VARREF(_last), VARREF(var))
|
|
AstVar* newvarp
|
|
= new AstVar(varp->fileline(), AstVarType::MODULETEMP, newvarname, varp);
|
|
m_statep->m_topModp->addStmtp(newvarp);
|
|
m_newvscp = new AstVarScope(m_vscp->fileline(), m_statep->m_scopetopp, newvarp);
|
|
m_statep->m_scopetopp->addVarp(m_newvscp);
|
|
|
|
m_varEqnp = new AstVarRef(m_vscp->fileline(), m_vscp, false);
|
|
m_newLvEqnp = new AstVarRef(m_vscp->fileline(), m_newvscp, true);
|
|
m_newRvEqnp = new AstVarRef(m_vscp->fileline(), m_newvscp, false);
|
|
}
|
|
iterate(vscp->dtypep()->skipRefp());
|
|
m_varEqnp->deleteTree();
|
|
m_newLvEqnp->deleteTree();
|
|
m_newRvEqnp->deleteTree();
|
|
}
|
|
virtual ~ChangedInsertVisitor() {}
|
|
VL_UNCOPYABLE(ChangedInsertVisitor);
|
|
};
|
|
|
|
//######################################################################
|
|
// Changed state, as a visitor of each AstNode
|
|
|
|
class ChangedVisitor : public AstNVisitor {
|
|
private:
|
|
// NODE STATE
|
|
// Entire netlist:
|
|
// AstVarScope::user1() -> bool. True indicates processed
|
|
AstUser1InUse m_inuser1;
|
|
|
|
// STATE
|
|
ChangedState* m_statep; // Shared state across visitors
|
|
|
|
// METHODS
|
|
VL_DEBUG_FUNC; // Declare debug()
|
|
|
|
void genChangeDet(AstVarScope* vscp) {
|
|
vscp->v3warn(IMPERFECTSCH, "Imperfect scheduling of variable: " << vscp->prettyNameQ());
|
|
ChangedInsertVisitor visitor(vscp, m_statep);
|
|
}
|
|
|
|
// VISITORS
|
|
virtual void visit(AstNodeModule* nodep) VL_OVERRIDE {
|
|
UINFO(4, " MOD " << nodep << endl);
|
|
if (nodep->isTop()) m_statep->m_topModp = nodep;
|
|
iterateChildren(nodep);
|
|
}
|
|
|
|
virtual void visit(AstTopScope* nodep) VL_OVERRIDE {
|
|
UINFO(4, " TS " << nodep << endl);
|
|
// Clearing
|
|
AstNode::user1ClearTree();
|
|
// Create the change detection function
|
|
AstScope* scopep = nodep->scopep();
|
|
UASSERT_OBJ(scopep, nodep, "No scope found on top level, perhaps you have no statements?");
|
|
m_statep->m_scopetopp = scopep;
|
|
|
|
// Create a wrapper change detection function that calls each change detection function
|
|
m_statep->m_tlChgFuncp
|
|
= new AstCFunc(nodep->fileline(), "_change_request", scopep, "QData");
|
|
m_statep->m_tlChgFuncp->argTypes(EmitCBaseVisitor::symClassVar());
|
|
m_statep->m_tlChgFuncp->symProlog(true);
|
|
m_statep->m_tlChgFuncp->declPrivate(true);
|
|
m_statep->m_scopetopp->addActivep(m_statep->m_tlChgFuncp);
|
|
// Each change detection function needs at least one AstChangeDet
|
|
// to ensure that V3EmitC outputs the necessary code.
|
|
m_statep->maybeCreateChgFuncp();
|
|
m_statep->m_chgFuncp->addStmtsp(new AstChangeDet(nodep->fileline(), NULL, NULL, false));
|
|
|
|
iterateChildren(nodep);
|
|
}
|
|
virtual void visit(AstVarScope* nodep) VL_OVERRIDE {
|
|
if (nodep->isCircular()) {
|
|
UINFO(8, " CIRC " << nodep << endl);
|
|
if (!nodep->user1SetOnce()) genChangeDet(nodep);
|
|
}
|
|
}
|
|
//--------------------
|
|
virtual void visit(AstNodeMath*) VL_OVERRIDE {} // Accelerate
|
|
virtual void visit(AstNode* nodep) VL_OVERRIDE { iterateChildren(nodep); }
|
|
|
|
public:
|
|
// CONSTRUCTORS
|
|
ChangedVisitor(AstNetlist* nodep, ChangedState* statep) {
|
|
m_statep = statep;
|
|
iterate(nodep);
|
|
}
|
|
virtual ~ChangedVisitor() {}
|
|
};
|
|
|
|
//######################################################################
|
|
// Changed class functions
|
|
|
|
void V3Changed::changedAll(AstNetlist* nodep) {
|
|
UINFO(2, __FUNCTION__ << ": " << endl);
|
|
{
|
|
ChangedState state;
|
|
ChangedVisitor visitor(nodep, &state);
|
|
} // Destruct before checking
|
|
V3Global::dumpCheckGlobalTree("changed", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
|
}
|