verilator/src/V3Force.cpp
Wilson Snyder e6857df5c6 Internals: Rename Ast on non-node classes (#3262). No functional change.
This commit has the following replacements applied:

	s/\bAstUserInUseBase\b/VNUserInUseBase/g;
        s/\bAstAttrType\b/VAttrType/g;
        s/\bAstBasicDTypeKwd\b/VBasicDTypeKwd/g;
        s/\bAstDisplayType\b/VDisplayType/g;
        s/\bAstNDeleter\b/VNDeleter/g;
        s/\bAstNRelinker\b/VNRelinker/g;
        s/\bAstNVisitor\b/VNVisitor/g;
        s/\bAstPragmaType\b/VPragmaType/g;
        s/\bAstType\b/VNType/g;
        s/\bAstUser1InUse\b/VNUser1InUse/g;
        s/\bAstUser2InUse\b/VNUser2InUse/g;
        s/\bAstUser3InUse\b/VNUser3InUse/g;
        s/\bAstUser4InUse\b/VNUser4InUse/g;
        s/\bAstUser5InUse\b/VNUser5InUse/g;
        s/\bAstVarType\b/VVarType/g;
2022-01-02 14:03:20 -05:00

363 lines
15 KiB
C++

// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Make lookup forces
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// 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
//
//*************************************************************************
// FORCE TRANSFORMATIONS:
// Step 1 (ForceVisitor):
// For forced nets, make:
// __forceon - enable bitmask of what bits are forced
// __forcein - value being forced
// __preforce - value before force is applied
//
// Force sets the appropriate __forceon bits indicating a force is in
// effect using the value in __forcein. Release clears the
// appropriate __forceon bits.
//
// IEEE says that procedural assignments "hold" the forced value even
// after a release, so add an assignment to the original __preforce too.
//
// Tristates can't be forced, that would need a __forceen and makes a
// large mess, so just error out.
//
// Step 2 (ForceReplaceVisitor): (If any forces made)
//
// Replace any VarRef's to a forced signal to instead go to the
// reconsiled signal.
//
//*************************************************************************
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3Force.h"
#include "V3Simulate.h"
#include "V3Stats.h"
#include "V3Ast.h"
#include <cmath>
#include <vector>
//######################################################################
// Force shared state
class ForceBaseVisitor VL_NOT_FINAL : public VNVisitor {
// TYPES
public:
// Enum value must correspond to which user#p is used
enum class FVar : uint8_t { FORCEON = 2, FORCEIN = 3, PREFORCE = 4 };
private:
// NODE STATE
// Ast*::user1 -> bool - processed
// AstVar::user1 -> bool - created here
// AstVarScope::user1 -> bool - created here
// AstVar::user2p -> AstVar* pointer to __forceon
// AstVarScope::user2p -> AstVarScope* pointer to __forceon
// AstVar::user3p -> AstVar* pointer to __forcein
// AstVarScope::user3p -> AstVarScope* pointer to __forcein
// AstVar::user4p -> AstVar* pointer to __preforce
// AstVarScope::user4p -> AstVarScope* pointer to __preforce
// Uses are in ForceVisitor
public:
VL_DEBUG_FUNC; // Declare debug()
static string fvarName(FVar fvar) {
switch (fvar) {
case FVar::FORCEON: return "__forceon";
case FVar::FORCEIN: return "__forcein";
case FVar::PREFORCE: return "__preforce";
}
v3fatalSrc("bad case");
return "";
}
static AstVar* getForceVarNull(const AstVar* const nodep, FVar fvar) {
// E.g. trying to make a __perforce__FOO would be bad
UASSERT_OBJ(!nodep->user1(), nodep, "lookup on var that Force made itself");
return VN_AS(nodep->usernp(static_cast<int>(fvar)), Var);
}
static AstVar* getForceVar(AstVar* const nodep, FVar fvar) {
AstVar* const foundp = getForceVarNull(nodep, fvar);
if (foundp) return foundp;
if (nodep->isPrimaryIO()) {
nodep->v3warn(
E_UNSUPPORTED,
"Unsupported: Force/Release on primary input/output net "
<< nodep->prettyNameQ() << "\n"
<< nodep->warnMore()
<< "... Suggest assign it to/from a temporary net and force/release that");
}
auto* const newp = new AstVar{nodep->fileline(), VVarType::MODULETEMP,
nodep->name() + fvarName(fvar), nodep};
newp->user1(true);
UINFO(9, "getForceVar for " << nodep << endl);
UINFO(9, "getForceVar new " << newp << endl);
nodep->addNextHere(newp);
nodep->usernp(static_cast<int>(fvar), newp);
return newp;
}
static AstVarScope* getForceVscNull(const AstVarScope* const nodep, FVar fvar) {
// E.g. trying to make a __perforce__FOO would be bad
UASSERT_OBJ(!nodep->user1(), nodep, "lookup on varscope that Force made itself");
return VN_AS(nodep->usernp(static_cast<int>(fvar)), VarScope);
}
static AstVarScope* getForceVsc(AstVarScope* const nodep, FVar fvar) {
AstVarScope* const foundp = getForceVscNull(nodep, fvar);
if (foundp) return foundp;
FileLine* const fl_nowarn = new FileLine{nodep->fileline()};
fl_nowarn->warnOff(V3ErrorCode::BLKANDNBLK, true);
auto* const newp
= new AstVarScope{fl_nowarn, nodep->scopep(), getForceVar(nodep->varp(), fvar)};
newp->user1(true);
UINFO(9, "getForceVsc for " << nodep << endl);
UINFO(9, "getForceVsc new " << newp << endl);
nodep->addNextHere(newp);
nodep->usernp(static_cast<int>(fvar), newp);
return newp;
}
static AstVarRef* makeVarRef(AstNodeVarRef* nodep, FVar fvar, VAccess access) {
return new AstVarRef{nodep->fileline(), getForceVsc(nodep->varScopep(), fvar), access};
}
static AstNode* makeForcingEquation(AstNodeVarRef* nodep) {
// Forcing: out = ((__forceon & __forcein) | (~__forceon & __preforce))
UINFO(9, "makeForcingEquation for " << nodep << endl);
FileLine* const fl = nodep->fileline();
AstNode* const orp = new AstOr{
fl,
new AstAnd{fl, makeVarRef(nodep, FVar::FORCEON, VAccess::READ),
makeVarRef(nodep, FVar::FORCEIN, VAccess::READ)},
new AstAnd{fl, new AstNot{fl, makeVarRef(nodep, FVar::FORCEON, VAccess::READ)},
makeVarRef(nodep, FVar::PREFORCE, VAccess::READ)}};
return orp;
}
};
//######################################################################
// Recurse left-hand-side variables to do replaces underneath a force or release
class ForceLhsVisitor final : public ForceBaseVisitor {
private:
// STATE
FVar const m_fvar; // Which variable to replace with
AstNodeVarRef* m_releaseVarRefp = nullptr; // Left hand side variable under release
virtual void visit(AstNodeVarRef* nodep) override {
if (nodep->user1()) return;
if (nodep->access().isWriteOrRW()) {
if (m_releaseVarRefp) {
nodep->v3error("Multiple variables forced in single statement: "
<< m_releaseVarRefp->prettyNameQ() << ", "
<< nodep->varScopep()->prettyNameQ());
return;
}
m_releaseVarRefp = nodep;
AstNode* const newp = makeVarRef(nodep, m_fvar, VAccess::WRITE);
newp->user1(true);
nodep->replaceWith(newp);
pushDeletep(nodep);
}
}
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
public:
// CONSTRUCTORS
explicit ForceLhsVisitor(AstNode* nodep, FVar fvar)
: m_fvar(fvar) {
iterate(nodep);
}
virtual ~ForceLhsVisitor() override = default;
// METHODS
AstNodeVarRef* releaseVarRefp() const { return m_releaseVarRefp; }
};
//######################################################################
// Force class functions
class ForceVisitor final : public ForceBaseVisitor {
private:
// NODE STATE
// See ForceBaseVisitor
const VNUser1InUse m_inuser1;
const VNUser2InUse m_inuser2;
const VNUser3InUse m_inuser3;
const VNUser4InUse m_inuser4;
// STATE
bool m_anyForce = false; // Any force, need reconciliation step
VDouble0 m_statForces; // stat tracking
std::deque<AstAssignForce*> m_forces; // Pointer to found forces
std::deque<AstAssignRelease*> m_releases; // Pointer to found releases
virtual void visit(AstAssignForce* nodep) override { m_forces.push_back(nodep); }
void visitEarlierForce(AstAssignForce* nodep) {
if (nodep->user1SetOnce()) return;
if (debug() >= 9) nodep->dumpTree(cout, "-force-i- ");
++m_statForces;
m_anyForce = true;
// For force/release, duplicate assignment to make
// AssignForce __forceon = '1
// AssignForce __forcein = {value}
// and clone LHS's node tree to handle appropriate extractions
{ // __forceon = '1
AstNodeAssign* const newp = nodep->cloneTree(false);
pushDeletep(newp->rhsp()->unlinkFrBack());
V3Number num{nodep, nodep->width()};
num.setAllBits1();
newp->rhsp(new AstConst{nodep->fileline(), num});
{ ForceLhsVisitor{newp->lhsp(), FVar::FORCEON}; }
newp->user1(true); // Don't process it again
nodep->addNextHere(newp);
if (debug() >= 9) newp->dumpTree(cout, "-force-fo- ");
}
{ // Edit to create assignment to have VarRef that refers to __forceon
{ ForceLhsVisitor{nodep->lhsp(), FVar::FORCEIN}; }
nodep->user1(true); // Don't process it again
if (debug() >= 9) nodep->dumpTree(cout, "-force-fi- ");
}
}
virtual void visit(AstAssignRelease* nodep) override { m_releases.push_back(nodep); }
void visitEarlierRelease(AstAssignRelease* nodep) {
if (nodep->user1SetOnce()) return;
if (debug() >= 9) nodep->dumpTree(cout, "-release-i- ");
// RHS is not relevant, so no iterate
// For release, edit assignment to make
// AssignRelease __forceon = `0
// we already have 0's on RHS, were made when AstNode created
// Create assignment to have VarRef that refers to __forceon
ForceLhsVisitor fvisitor{nodep->lhsp(), FVar::FORCEON};
// releaseVarRefp might be deleted when ForceLhsVisitor destructs, so
// keep ForceLhsVisitor in scope for now
AstNodeVarRef* const releaseVarRefp = fvisitor.releaseVarRefp();
UASSERT_OBJ(releaseVarRefp, nodep, "No LHS variable found under release");
if (!getForceVscNull(releaseVarRefp->varScopep(), FVar::FORCEIN)) {
UINFO(9, "Deleting release of variable that's never forced: " << nodep << endl);
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
return;
}
if (!releaseVarRefp->varp()->isContinuously()) {
// Create assignment __out == _forceen so when release happens value sticks
// See IEEE - a strange historical language artifact
UINFO(9, "force var is procedural " << releaseVarRefp->varScopep() << endl);
FileLine* const fl_nowarn = new FileLine{nodep->fileline()};
fl_nowarn->warnOff(V3ErrorCode::BLKANDNBLK, true);
AstNodeAssign* const newp = new AstAssignRelease{
fl_nowarn, makeVarRef(releaseVarRefp, FVar::PREFORCE, VAccess::WRITE),
makeForcingEquation(releaseVarRefp)};
newp->user1(true); // Don't process it again
nodep->addHereThisAsNext(newp); // Must go before change forceon
if (debug() >= 9) newp->dumpTree(cout, "-release-rp- ");
}
if (debug() >= 9) nodep->dumpTree(cout, "-release-ro- ");
}
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
public:
// CONSTRUCTORS
explicit ForceVisitor(AstNetlist* nodep) {
iterate(nodep);
// Now that we know procedural markers in user5, do forces
for (auto* const nodep : m_forces) visitEarlierForce(nodep);
m_forces.clear(); // As dangling pointers now
// Do releases after all forces are processed, so we can just
// ignore any release with no corresponding force
for (auto* const nodep : m_releases) visitEarlierRelease(nodep);
m_releases.clear(); // As dangling pointers now
}
virtual ~ForceVisitor() override { //
V3Stats::addStat("Tristate, Forces", m_statForces);
}
// METHODS
bool anyForce() const { return m_anyForce; }
};
//######################################################################
// Force class functions
class ForceReplace final : public ForceBaseVisitor {
// This extra complete-netlist visit could be avoided by recording all
// AstVarRefs to every AstVar, but that's a lot of data structure
// building, faster to read-only iterate.
// As we only care about VarRef's we use direct recusion rather than a visitor
private:
void visitVarRef(AstNodeVarRef* nodep) {
if (nodep->varScopep()->user2p()) {
if (nodep->access().isRW()) {
nodep->v3warn(E_UNSUPPORTED,
"Unsupported: forced variable used in read-modify-write context");
} else if (nodep->access().isWriteOrRW()) {
UINFO(9, " changeRecurse-WR-replace " << nodep << endl);
AstNode* const newp = makeVarRef(nodep, FVar::PREFORCE, VAccess::WRITE);
newp->user1(true);
nodep->replaceWith(newp);
pushDeletep(nodep);
return;
} else if (nodep->access().isReadOrRW()) {
// We build forcing equation on each usage rather than making
// a variable otherwise we wouldn't know where between a statement
// that sets a preforce and uses a forced to insert the proposed
// assignment
UINFO(9, " changeRecurse-RD-replace " << nodep << endl);
AstNode* const newp = makeForcingEquation(nodep);
newp->user1(true);
nodep->replaceWith(newp);
pushDeletep(nodep);
return;
}
}
}
void changeRecurse(AstNode* nodep) {
// Recurse and replace any VarRef WRITEs to refer to the force equation
if (VL_LIKELY(!nodep->user1())) { // Else processed already
if (auto* const varrefp = VN_CAST(nodep, NodeVarRef)) {
visitVarRef(varrefp);
return; // Might have been edited -- and has no children so ok to exit
}
}
nodep->prefetch();
if (nodep->op1p()) changeRecurse(nodep->op1p());
if (nodep->op2p()) changeRecurse(nodep->op2p());
if (nodep->op3p()) changeRecurse(nodep->op3p());
if (nodep->op4p()) changeRecurse(nodep->op4p());
if (nodep->nextp()) changeRecurse(nodep->nextp());
}
virtual void visit(AstNode* nodep) override { v3error("Unused"); } // LCOV_EXCL_LINE
public:
// CONSTRUCTORS
explicit ForceReplace(AstNetlist* nodep) { changeRecurse(nodep); }
virtual ~ForceReplace() override = default;
};
//######################################################################
// Force class functions
void V3Force::forceAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ // Destruct before final check
ForceVisitor visitor{nodep};
if (visitor.anyForce()) {
V3Global::dumpCheckGlobalTree("force-mid", 0,
v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
ForceReplace{nodep};
}
}
V3Global::dumpCheckGlobalTree("force", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
}