mirror of
https://github.com/verilator/verilator.git
synced 2025-02-01 19:24:05 +00:00
517 lines
24 KiB
C++
517 lines
24 KiB
C++
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
|
//*************************************************************************
|
|
// DESCRIPTION: Verilator: Add temporaries, such as for delayed nodes
|
|
//
|
|
// Code available from: https://verilator.org
|
|
//
|
|
//*************************************************************************
|
|
//
|
|
// Copyright 2003-2021 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
|
|
//
|
|
//*************************************************************************
|
|
// V3Delayed's Transformations:
|
|
//
|
|
// Each module:
|
|
// Replace ASSIGNDLY var, exp
|
|
// With ASSIGNDLY newvar, exp
|
|
// At top of block: VAR newvar
|
|
// At bottom of block: ASSIGNW var newvar
|
|
// Need _x_dly = x at top of active if "x" is not always set
|
|
// For now we'll say it's set if at top of block (not under IF, etc)
|
|
// Need x = _x_dly at bottom of active if "x" is never referenced on LHS
|
|
// in the active, and above rule applies too.
|
|
// (If so, use x on LHS, not _x_dly.)
|
|
//
|
|
// If a signal is set in multiple always blocks, we need a dly read & set with
|
|
// multiple clock sensitivities. We have 3 options:
|
|
// 1. When detected, make a new ACTIVE and move earlier created delayed assignment there
|
|
// 2. Form unique ACTIVE for every multiple clocked assignment
|
|
// 3. Predetect signals from multiple always blocks and do #2 on them
|
|
// Since all 3 require a top activation cleanup, we do #2 which is easiest.
|
|
//
|
|
// ASSIGNDLY (BITSEL(ARRAYSEL (VARREF(v), bits), selbits), rhs)
|
|
// -> VAR __Vdlyvset_x
|
|
// VAR __Vdlyvval_x
|
|
// VAR __Vdlyvdim_x
|
|
// VAR __Vdlyvlsb_x
|
|
// ASSIGNW (__Vdlyvset_x,0)
|
|
// ...
|
|
// ASSIGNW (VARREF(__Vdlyvval_x), rhs)
|
|
// ASSIGNW (__Vdlyvdim_x, dimension_number)
|
|
// ASSIGNW (__Vdlyvset_x, 1)
|
|
// ...
|
|
// ASSIGNW (BITSEL(ARRAYSEL(VARREF(x), __Vdlyvdim_x), __Vdlyvlsb_x), __Vdlyvval_x)
|
|
//
|
|
//*************************************************************************
|
|
|
|
#include "config_build.h"
|
|
#include "verilatedos.h"
|
|
|
|
#include "V3Global.h"
|
|
#include "V3Delayed.h"
|
|
#include "V3Ast.h"
|
|
#include "V3Stats.h"
|
|
|
|
#include <algorithm>
|
|
#include <deque>
|
|
#include <map>
|
|
|
|
//######################################################################
|
|
// Delayed state, as a visitor of each AstNode
|
|
|
|
class DelayedVisitor final : public AstNVisitor {
|
|
private:
|
|
// NODE STATE
|
|
// Cleared each module:
|
|
// AstVarScope::user1p() -> AstVarScope*. Points to temp var created.
|
|
// AstVarScope::user2p() -> AstActive*. Points to activity block of signal
|
|
// (valid when AstVarScope::user1p is valid)
|
|
// AstVarScope::user4p() -> AstAlwaysPost*. Post block for this variable
|
|
// AstVarScope::user5p() -> AstVarRef*. Last blocking or non-blocking reference
|
|
// AstVar::user2() -> bool. Set true if already made warning
|
|
// AstVarRef::user2() -> bool. Set true if already processed
|
|
// AstVarRef::user5() -> bool. Set true if was blocking reference
|
|
// AstAlwaysPost::user2() -> ActActive*. Points to activity block of signal
|
|
// (valid when AstAlwaysPost::user4p is valid)
|
|
// AstAlwaysPost::user4() -> AstIf*. Last IF (__Vdlyvset__) created under this AlwaysPost
|
|
// Cleared each scope/active:
|
|
// AstAssignDly::user3() -> AstVarScope*. __Vdlyvset__ created for this assign
|
|
// AstAlwaysPost::user3() -> AstVarScope*. __Vdlyvset__ last referenced in IF
|
|
AstUser1InUse m_inuser1;
|
|
AstUser2InUse m_inuser2;
|
|
AstUser3InUse m_inuser3;
|
|
AstUser4InUse m_inuser4;
|
|
AstUser5InUse m_inuser5;
|
|
|
|
// STATE
|
|
AstActive* m_activep = nullptr; // Current activate
|
|
AstCFunc* m_cfuncp = nullptr; // Current public C Function
|
|
AstAssignDly* m_nextDlyp = nullptr; // Next delayed assignment in a list of assignments
|
|
bool m_inDly = false; // True in delayed assignments
|
|
bool m_inLoop = false; // True in for loops
|
|
bool m_inInitial = false; // True in initial blocks
|
|
typedef std::map<const std::pair<AstNodeModule*, string>, AstVar*> VarMap;
|
|
VarMap m_modVarMap; // Table of new var names created under module
|
|
VDouble0 m_statSharedSet; // Statistic tracking
|
|
typedef std::unordered_map<const AstVarScope*, int> ScopeVecMap;
|
|
ScopeVecMap m_scopeVecMap; // Next var number for each scope
|
|
|
|
// METHODS
|
|
VL_DEBUG_FUNC; // Declare debug()
|
|
|
|
void markVarUsage(AstNodeVarRef* nodep, bool blocking) {
|
|
if (blocking) nodep->user5(true);
|
|
AstVarScope* vscp = nodep->varScopep();
|
|
// UINFO(4, " MVU " << blocking << " " << nodep << endl);
|
|
AstNode* lastrefp = vscp->user5p();
|
|
if (!lastrefp) {
|
|
vscp->user5p(nodep);
|
|
} else {
|
|
bool last_was_blocking = lastrefp->user5();
|
|
if (last_was_blocking != blocking) {
|
|
AstNode* nonblockingp = blocking ? nodep : lastrefp;
|
|
AstNode* blockingp = blocking ? lastrefp : nodep;
|
|
vscp->v3warn(
|
|
BLKANDNBLK,
|
|
"Unsupported: Blocked and non-blocking assignments to same variable: "
|
|
<< vscp->varp()->prettyNameQ() << '\n'
|
|
<< vscp->warnContextPrimary() << '\n'
|
|
<< blockingp->warnOther() << "... Location of blocking assignment\n"
|
|
<< blockingp->warnContextSecondary() << '\n'
|
|
<< nonblockingp->warnOther() << "... Location of nonblocking assignment\n"
|
|
<< nonblockingp->warnContextSecondary());
|
|
}
|
|
}
|
|
}
|
|
AstVarScope* createVarSc(AstVarScope* oldvarscp, const string& name,
|
|
int width /*0==fromoldvar*/, AstNodeDType* newdtypep) {
|
|
// Because we've already scoped it, we may need to add both the AstVar and the AstVarScope
|
|
UASSERT_OBJ(oldvarscp->scopep(), oldvarscp, "Var unscoped");
|
|
AstVar* varp;
|
|
AstNodeModule* addmodp = oldvarscp->scopep()->modp();
|
|
// We need a new AstVar, but only one for all scopes, to match the new AstVarScope
|
|
const auto it = m_modVarMap.find(make_pair(addmodp, name));
|
|
if (it != m_modVarMap.end()) {
|
|
// Created module's AstVar earlier under some other scope
|
|
varp = it->second;
|
|
} else {
|
|
if (newdtypep) {
|
|
varp = new AstVar(oldvarscp->fileline(), AstVarType::BLOCKTEMP, name, newdtypep);
|
|
} else if (width == 0) {
|
|
varp = new AstVar(oldvarscp->fileline(), AstVarType::BLOCKTEMP, name,
|
|
oldvarscp->varp());
|
|
varp->dtypeFrom(oldvarscp);
|
|
} else { // Used for vset and dimensions, so can zero init
|
|
varp = new AstVar(oldvarscp->fileline(), AstVarType::BLOCKTEMP, name,
|
|
VFlagBitPacked(), width);
|
|
}
|
|
addmodp->addStmtp(varp);
|
|
m_modVarMap.emplace(make_pair(addmodp, name), varp);
|
|
}
|
|
|
|
AstVarScope* varscp = new AstVarScope(oldvarscp->fileline(), oldvarscp->scopep(), varp);
|
|
oldvarscp->scopep()->addVarp(varscp);
|
|
return varscp;
|
|
}
|
|
|
|
AstActive* createActivePost(AstVarRef* varrefp) {
|
|
AstActive* newactp
|
|
= new AstActive(varrefp->fileline(), "sequentdly", m_activep->sensesp());
|
|
// Was addNext(), but addNextHere() avoids a linear search.
|
|
m_activep->addNextHere(newactp);
|
|
return newactp;
|
|
}
|
|
void checkActivePost(AstVarRef* varrefp, AstActive* oldactivep) {
|
|
// Check for MULTIDRIVEN, and if so make new sentree that joins old & new sentree
|
|
UASSERT_OBJ(oldactivep, varrefp, "<= old dly assignment not put under sensitivity block");
|
|
if (oldactivep->sensesp() != m_activep->sensesp()) {
|
|
if (!varrefp->varp()->fileline()->warnIsOff(V3ErrorCode::MULTIDRIVEN)
|
|
&& !varrefp->varp()->user2()) {
|
|
varrefp->varp()->v3warn(
|
|
MULTIDRIVEN,
|
|
"Signal has multiple driving blocks with different clocking: "
|
|
<< varrefp->varp()->prettyNameQ() << '\n'
|
|
<< varrefp->warnOther() << "... Location of first driving block\n"
|
|
<< varrefp->warnContextPrimary() << '\n'
|
|
<< oldactivep->warnOther() << "... Location of other driving block\n"
|
|
<< oldactivep->warnContextSecondary());
|
|
varrefp->varp()->user2(true);
|
|
}
|
|
UINFO(4, "AssignDupDlyVar: " << varrefp << endl);
|
|
UINFO(4, " Act: " << m_activep << endl);
|
|
UINFO(4, " Act: " << oldactivep << endl);
|
|
// Make a new sensitivity list, which is the combination of both blocks
|
|
AstSenItem* sena = m_activep->sensesp()->sensesp()->cloneTree(true);
|
|
AstSenItem* senb = oldactivep->sensesp()->sensesp()->cloneTree(true);
|
|
AstSenTree* treep = new AstSenTree(m_activep->fileline(), sena);
|
|
if (senb) treep->addSensesp(senb);
|
|
if (AstSenTree* storep = oldactivep->sensesStorep()) {
|
|
storep->unlinkFrBack();
|
|
pushDeletep(storep);
|
|
}
|
|
oldactivep->sensesStorep(treep);
|
|
oldactivep->sensesp(treep);
|
|
}
|
|
}
|
|
|
|
AstNode* createDlyArray(AstAssignDly* nodep, AstNode* lhsp) {
|
|
// Create delayed assignment
|
|
// See top of this file for transformation
|
|
// Return the new LHS for the assignment, Null = unlink
|
|
// Find selects
|
|
AstNode* newlhsp = nullptr; // nullptr = unlink old assign
|
|
AstSel* bitselp = nullptr;
|
|
AstArraySel* arrayselp = nullptr;
|
|
if (VN_IS(lhsp, Sel)) {
|
|
bitselp = VN_CAST(lhsp, Sel);
|
|
arrayselp = VN_CAST(bitselp->fromp(), ArraySel);
|
|
} else {
|
|
arrayselp = VN_CAST(lhsp, ArraySel);
|
|
}
|
|
UASSERT_OBJ(arrayselp, nodep, "No arraysel under bitsel?");
|
|
UASSERT_OBJ(!VN_IS(arrayselp->dtypep()->skipRefp(), UnpackArrayDType), nodep,
|
|
"ArraySel with unpacked arrays should have been removed in V3Slice");
|
|
UINFO(4, "AssignDlyArray: " << nodep << endl);
|
|
//
|
|
//=== Dimensions: __Vdlyvdim__
|
|
std::deque<AstNode*> dimvalp; // Assignment value for each dimension of assignment
|
|
AstNode* dimselp = arrayselp;
|
|
for (; VN_IS(dimselp, ArraySel); dimselp = VN_CAST(dimselp, ArraySel)->fromp()) {
|
|
AstNode* valp = VN_CAST(dimselp, ArraySel)->bitp()->unlinkFrBack();
|
|
dimvalp.push_front(valp);
|
|
}
|
|
AstVarRef* varrefp = VN_CAST(dimselp, VarRef);
|
|
UASSERT_OBJ(varrefp, nodep, "No var underneath arraysels");
|
|
UASSERT_OBJ(varrefp->varScopep(), varrefp, "Var didn't get varscoped in V3Scope.cpp");
|
|
varrefp->unlinkFrBack();
|
|
AstVar* oldvarp = varrefp->varp();
|
|
int modVecNum = m_scopeVecMap[varrefp->varScopep()]++;
|
|
//
|
|
std::deque<AstNode*> dimreadps; // Read value for each dimension of assignment
|
|
for (unsigned dimension = 0; dimension < dimvalp.size(); dimension++) {
|
|
AstNode* dimp = dimvalp[dimension];
|
|
if (VN_IS(dimp, Const)) { // bit = const, can just use it
|
|
dimreadps.push_front(dimp);
|
|
} else {
|
|
string bitvarname = (string("__Vdlyvdim") + cvtToStr(dimension) + "__"
|
|
+ oldvarp->shortName() + "__v" + cvtToStr(modVecNum));
|
|
AstVarScope* bitvscp
|
|
= createVarSc(varrefp->varScopep(), bitvarname, dimp->width(), nullptr);
|
|
AstAssign* bitassignp = new AstAssign(
|
|
nodep->fileline(), new AstVarRef(nodep->fileline(), bitvscp, VAccess::WRITE),
|
|
dimp);
|
|
nodep->addNextHere(bitassignp);
|
|
dimreadps.push_front(new AstVarRef(nodep->fileline(), bitvscp, VAccess::READ));
|
|
}
|
|
}
|
|
//
|
|
//=== Bitselect: __Vdlyvlsb__
|
|
AstNode* bitreadp = nullptr; // Code to read Vdlyvlsb
|
|
if (bitselp) {
|
|
AstNode* lsbvaluep = bitselp->lsbp()->unlinkFrBack();
|
|
if (VN_IS(bitselp->fromp(), Const)) {
|
|
// vlsb = constant, can just push constant into where we use it
|
|
bitreadp = lsbvaluep;
|
|
} else {
|
|
string bitvarname = (string("__Vdlyvlsb__") + oldvarp->shortName() + "__v"
|
|
+ cvtToStr(modVecNum));
|
|
AstVarScope* bitvscp
|
|
= createVarSc(varrefp->varScopep(), bitvarname, lsbvaluep->width(), nullptr);
|
|
AstAssign* bitassignp = new AstAssign(
|
|
nodep->fileline(), new AstVarRef(nodep->fileline(), bitvscp, VAccess::WRITE),
|
|
lsbvaluep);
|
|
nodep->addNextHere(bitassignp);
|
|
bitreadp = new AstVarRef(nodep->fileline(), bitvscp, VAccess::READ);
|
|
}
|
|
}
|
|
//
|
|
//=== Value: __Vdlyvval__
|
|
AstNode* valreadp; // Code to read Vdlyvval
|
|
if (VN_IS(nodep->rhsp(), Const)) {
|
|
// vval = constant, can just push constant into where we use it
|
|
valreadp = nodep->rhsp()->unlinkFrBack();
|
|
} else {
|
|
string valvarname
|
|
= (string("__Vdlyvval__") + oldvarp->shortName() + "__v" + cvtToStr(modVecNum));
|
|
AstVarScope* valvscp
|
|
= createVarSc(varrefp->varScopep(), valvarname, 0, nodep->rhsp()->dtypep());
|
|
newlhsp = new AstVarRef(nodep->fileline(), valvscp, VAccess::WRITE);
|
|
valreadp = new AstVarRef(nodep->fileline(), valvscp, VAccess::READ);
|
|
}
|
|
//
|
|
//=== Setting/not setting boolean: __Vdlyvset__
|
|
AstVarScope* setvscp;
|
|
AstAssignPre* setinitp = nullptr;
|
|
|
|
if (nodep->user3p()) {
|
|
// Simplistic optimization. If the previous statement in same scope was also a =>,
|
|
// then we told this nodep->user3 we can use its Vdlyvset rather than making a new one.
|
|
// This is good for code like:
|
|
// for (i=0; i<5; i++) vector[i] <= something;
|
|
setvscp = VN_CAST(nodep->user3p(), VarScope);
|
|
++m_statSharedSet;
|
|
} else { // Create new one
|
|
string setvarname
|
|
= (string("__Vdlyvset__") + oldvarp->shortName() + "__v" + cvtToStr(modVecNum));
|
|
setvscp = createVarSc(varrefp->varScopep(), setvarname, 1, nullptr);
|
|
setinitp = new AstAssignPre(nodep->fileline(),
|
|
new AstVarRef(nodep->fileline(), setvscp, VAccess::WRITE),
|
|
new AstConst(nodep->fileline(), 0));
|
|
AstAssign* setassignp = new AstAssign(
|
|
nodep->fileline(), new AstVarRef(nodep->fileline(), setvscp, VAccess::WRITE),
|
|
new AstConst(nodep->fileline(), AstConst::BitTrue()));
|
|
nodep->addNextHere(setassignp);
|
|
}
|
|
if (m_nextDlyp) { // Tell next assigndly it can share the variable
|
|
m_nextDlyp->user3p(setvscp);
|
|
}
|
|
//
|
|
// Create ALWAYSPOST for delayed variable
|
|
// We add all logic to the same block if it's for the same memory
|
|
// This ensures that multiple assignments to the same memory will result
|
|
// in correctly ordered code - the last assignment must be last.
|
|
// It also has the nice side effect of assisting cache locality.
|
|
AstNode* selectsp = varrefp;
|
|
for (int dimension = int(dimreadps.size()) - 1; dimension >= 0; --dimension) {
|
|
selectsp = new AstArraySel(nodep->fileline(), selectsp, dimreadps[dimension]);
|
|
}
|
|
if (bitselp) {
|
|
selectsp = new AstSel(nodep->fileline(), selectsp, bitreadp,
|
|
bitselp->widthp()->cloneTree(false));
|
|
}
|
|
// Build "IF (changeit) ...
|
|
UINFO(9, " For " << setvscp << endl);
|
|
UINFO(9, " & " << varrefp << endl);
|
|
AstAlwaysPost* finalp = VN_CAST(varrefp->varScopep()->user4p(), AlwaysPost);
|
|
if (finalp) {
|
|
AstActive* oldactivep = VN_CAST(finalp->user2p(), Active);
|
|
checkActivePost(varrefp, oldactivep);
|
|
if (setinitp) oldactivep->addStmtsp(setinitp);
|
|
} else { // first time we've dealt with this memory
|
|
finalp = new AstAlwaysPost(nodep->fileline(), nullptr /*sens*/, nullptr /*body*/);
|
|
UINFO(9, " Created " << finalp << endl);
|
|
AstActive* newactp = createActivePost(varrefp);
|
|
newactp->addStmtsp(finalp);
|
|
varrefp->varScopep()->user4p(finalp);
|
|
finalp->user2p(newactp);
|
|
if (setinitp) newactp->addStmtsp(setinitp);
|
|
}
|
|
AstIf* postLogicp;
|
|
if (finalp->user3p() == setvscp) {
|
|
// Optimize as above; if sharing Vdlyvset *ON SAME VARIABLE*,
|
|
// we can share the IF statement too
|
|
postLogicp = VN_CAST(finalp->user4p(), If);
|
|
UASSERT_OBJ(postLogicp, nodep,
|
|
"Delayed assignment misoptimized; prev var found w/o associated IF");
|
|
} else {
|
|
postLogicp = new AstIf(nodep->fileline(),
|
|
new AstVarRef(nodep->fileline(), setvscp, VAccess::READ),
|
|
nullptr, nullptr);
|
|
UINFO(9, " Created " << postLogicp << endl);
|
|
finalp->addBodysp(postLogicp);
|
|
finalp->user3p(setvscp); // Remember IF's vset variable
|
|
finalp->user4p(postLogicp); // and the associated IF, as we may be able to reuse it
|
|
}
|
|
postLogicp->addIfsp(new AstAssign(nodep->fileline(), selectsp, valreadp));
|
|
return newlhsp;
|
|
}
|
|
|
|
// VISITORS
|
|
virtual void visit(AstNetlist* nodep) override {
|
|
// VV***** We reset all userp() on the netlist
|
|
m_modVarMap.clear();
|
|
iterateChildren(nodep);
|
|
}
|
|
virtual void visit(AstScope* nodep) override {
|
|
UINFO(4, " MOD " << nodep << endl);
|
|
AstNode::user3ClearTree();
|
|
iterateChildren(nodep);
|
|
}
|
|
virtual void visit(AstCFunc* nodep) override {
|
|
VL_RESTORER(m_cfuncp);
|
|
{
|
|
m_cfuncp = nodep;
|
|
iterateChildren(nodep);
|
|
}
|
|
}
|
|
virtual void visit(AstActive* nodep) override {
|
|
m_activep = nodep;
|
|
VL_RESTORER(m_inInitial);
|
|
{
|
|
m_inInitial = nodep->hasInitial();
|
|
// Two sets to same variable in different actives must use different vars.
|
|
AstNode::user3ClearTree();
|
|
iterateChildren(nodep);
|
|
}
|
|
}
|
|
virtual void visit(AstAssignDly* nodep) override {
|
|
m_inDly = true;
|
|
m_nextDlyp
|
|
= VN_CAST(nodep->nextp(), AssignDly); // Next assignment in same block, maybe nullptr.
|
|
if (m_cfuncp) {
|
|
nodep->v3warn(E_UNSUPPORTED,
|
|
"Unsupported: Delayed assignment inside public function/task");
|
|
}
|
|
if (VN_IS(nodep->lhsp(), ArraySel)
|
|
|| (VN_IS(nodep->lhsp(), Sel)
|
|
&& VN_IS(VN_CAST(nodep->lhsp(), Sel)->fromp(), ArraySel))) {
|
|
AstNode* lhsp = nodep->lhsp()->unlinkFrBack();
|
|
AstNode* newlhsp = createDlyArray(nodep, lhsp);
|
|
if (m_inLoop) {
|
|
nodep->v3warn(BLKLOOPINIT, "Unsupported: Delayed assignment to array inside for "
|
|
"loops (non-delayed is ok - see docs)");
|
|
}
|
|
AstBasicDType* basicp = lhsp->dtypep()->basicp();
|
|
if (basicp && basicp->isEventValue()) {
|
|
nodep->v3warn(E_UNSUPPORTED, "Unsupported: event arrays");
|
|
}
|
|
if (newlhsp) {
|
|
nodep->lhsp(newlhsp);
|
|
} else {
|
|
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
|
}
|
|
VL_DO_DANGLING(pushDeletep(lhsp), lhsp);
|
|
} else {
|
|
iterateChildren(nodep);
|
|
}
|
|
m_inDly = false;
|
|
m_nextDlyp = nullptr;
|
|
}
|
|
|
|
virtual void visit(AstVarRef* nodep) override {
|
|
if (!nodep->user2Inc()) { // Not done yet
|
|
if (m_inDly && nodep->access().isWriteOrRW()) {
|
|
UINFO(4, "AssignDlyVar: " << nodep << endl);
|
|
markVarUsage(nodep, true);
|
|
UASSERT_OBJ(m_activep, nodep, "<= not under sensitivity block");
|
|
UASSERT_OBJ(!nodep->access().isRW(), nodep, "<= on read+write method");
|
|
if (!m_activep->hasClocked()) {
|
|
nodep->v3error("Internal: Blocking <= assignment in non-clocked block, should "
|
|
"have converted in V3Active");
|
|
}
|
|
AstVarScope* oldvscp = nodep->varScopep();
|
|
UASSERT_OBJ(oldvscp, nodep, "Var didn't get varscoped in V3Scope.cpp");
|
|
AstVarScope* dlyvscp = VN_CAST(oldvscp->user1p(), VarScope);
|
|
if (dlyvscp) { // Multiple use of delayed variable
|
|
AstActive* oldactivep = VN_CAST(dlyvscp->user2p(), Active);
|
|
checkActivePost(nodep, oldactivep);
|
|
}
|
|
if (!dlyvscp) { // First use of this delayed variable
|
|
string newvarname = (string("__Vdly__") + nodep->varp()->shortName());
|
|
dlyvscp = createVarSc(oldvscp, newvarname, 0, nullptr);
|
|
AstNodeAssign* prep;
|
|
AstBasicDType* basicp = oldvscp->dtypep()->basicp();
|
|
if (basicp && basicp->isEventValue()) {
|
|
// Events go to zero on next timestep unless reactivated
|
|
prep = new AstAssignPre(
|
|
nodep->fileline(),
|
|
new AstVarRef(nodep->fileline(), dlyvscp, VAccess::WRITE),
|
|
new AstConst(nodep->fileline(), AstConst::BitFalse()));
|
|
} else {
|
|
prep = new AstAssignPre(
|
|
nodep->fileline(),
|
|
new AstVarRef(nodep->fileline(), dlyvscp, VAccess::WRITE),
|
|
new AstVarRef(nodep->fileline(), oldvscp, VAccess::READ));
|
|
}
|
|
AstNodeAssign* postp = new AstAssignPost(
|
|
nodep->fileline(),
|
|
new AstVarRef(nodep->fileline(), oldvscp, VAccess::WRITE),
|
|
new AstVarRef(nodep->fileline(), dlyvscp, VAccess::READ));
|
|
postp->lhsp()->user2(true); // Don't detect this assignment
|
|
oldvscp->user1p(dlyvscp); // So we can find it later
|
|
// Make new ACTIVE with identical sensitivity tree
|
|
AstActive* newactp = createActivePost(nodep);
|
|
dlyvscp->user2p(newactp);
|
|
newactp->addStmtsp(prep); // Add to FRONT of statements
|
|
newactp->addStmtsp(postp);
|
|
}
|
|
AstVarRef* newrefp = new AstVarRef(nodep->fileline(), dlyvscp, VAccess::WRITE);
|
|
newrefp->user2(true); // No reason to do it again
|
|
nodep->replaceWith(newrefp);
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
|
} else if (!m_inDly && nodep->access().isWriteOrRW()) {
|
|
// UINFO(9, "NBA " << nodep << endl);
|
|
if (!m_inInitial) {
|
|
UINFO(4, "AssignNDlyVar: " << nodep << endl);
|
|
markVarUsage(nodep, false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
virtual void visit(AstNodeFor* nodep) override { // LCOV_EXCL_LINE
|
|
nodep->v3fatalSrc(
|
|
"For statements should have been converted to while statements in V3Begin");
|
|
}
|
|
virtual void visit(AstWhile* nodep) override {
|
|
VL_RESTORER(m_inLoop);
|
|
{
|
|
m_inLoop = true;
|
|
iterateChildren(nodep);
|
|
}
|
|
}
|
|
|
|
//--------------------
|
|
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
|
|
|
public:
|
|
// CONSTRUCTORS
|
|
explicit DelayedVisitor(AstNetlist* nodep) { iterate(nodep); }
|
|
virtual ~DelayedVisitor() override {
|
|
V3Stats::addStat("Optimizations, Delayed shared-sets", m_statSharedSet);
|
|
}
|
|
};
|
|
|
|
//######################################################################
|
|
// Delayed class functions
|
|
|
|
void V3Delayed::delayedAll(AstNetlist* nodep) {
|
|
UINFO(2, __FUNCTION__ << ": " << endl);
|
|
{ DelayedVisitor visitor(nodep); } // Destruct before checking
|
|
V3Global::dumpCheckGlobalTree("delayed", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
|
}
|