Internals: Some V3Tristate cleanups. No functional change.

This commit is contained in:
Wilson Snyder 2021-12-19 16:06:45 -05:00
parent ff425369ac
commit 4135867e53
1 changed files with 147 additions and 155 deletions

View File

@ -51,7 +51,10 @@
// //
// Note 1800-2012 adds user defined resolution functions. This suggests // Note 1800-2012 adds user defined resolution functions. This suggests
// long term this code should be scoped-based and resolve all nodes at once // long term this code should be scoped-based and resolve all nodes at once
// rather than hierarchically. // rather than hierarchically. If/when that is done, make sure to avoid
// duplicating vars and logic that is common between each instance of a
// module.
//
//************************************************************************* //*************************************************************************
#include "config_build.h" #include "config_build.h"
@ -235,10 +238,10 @@ public:
} }
if (debug() >= 9) m_graph.dumpDotFilePrefixed("tri_pos__" + nodep->name()); if (debug() >= 9) m_graph.dumpDotFilePrefixed("tri_pos__" + nodep->name());
} }
void setTristate(AstNode* nodep) { makeVertex(nodep)->isTristate(true); }
void associate(AstNode* fromp, AstNode* top) { void associate(AstNode* fromp, AstNode* top) {
new V3GraphEdge(&m_graph, makeVertex(fromp), makeVertex(top), 1); new V3GraphEdge(&m_graph, makeVertex(fromp), makeVertex(top), 1);
} }
void setTristate(AstNode* nodep) { makeVertex(nodep)->isTristate(true); }
bool isTristate(AstNode* nodep) { bool isTristate(AstNode* nodep) {
const TristateVertex* const vertexp = reinterpret_cast<TristateVertex*>(nodep->user5p()); const TristateVertex* const vertexp = reinterpret_cast<TristateVertex*>(nodep->user5p());
return vertexp && vertexp->isTristate(); return vertexp && vertexp->isTristate();
@ -347,7 +350,7 @@ class TristateVisitor final : public TristateBaseVisitor {
// //
AstNodeModule* m_modp = nullptr; // Current module AstNodeModule* m_modp = nullptr; // Current module
AstCell* m_cellp = nullptr; // current cell AstCell* m_cellp = nullptr; // current cell
VarMap m_lhsmap; // LHS driver map VarMap m_lhsmap; // Tristate left-hand-side driver map
int m_unique = 0; int m_unique = 0;
bool m_alhs = false; // On LHS of assignment bool m_alhs = false; // On LHS of assignment
const AstNode* m_logicp = nullptr; // Current logic being built const AstNode* m_logicp = nullptr; // Current logic being built
@ -362,67 +365,57 @@ class TristateVisitor final : public TristateBaseVisitor {
if (m_alhs) o += "alhs "; if (m_alhs) o += "alhs ";
return o; return o;
} }
void modAddStmtp(AstNode* nodep, AstNode* newp) {
if (!m_modp) {
nodep->v3warn(E_UNSUPPORTED,
"Unsupported: Creating tristate signal not underneath a module: "
<< nodep->prettyNameQ());
} else {
m_modp->addStmtp(newp);
}
}
void associateLogic(AstNode* fromp, AstNode* top) { void associateLogic(AstNode* fromp, AstNode* top) {
if (m_logicp) m_tgraph.associate(fromp, top); if (m_logicp) m_tgraph.associate(fromp, top);
} }
AstNode* getEnp(AstNode* nodep) { AstNode* getEnp(AstNode* nodep) {
// checks if user1p() is null, and if so, adds a constant output
// enable driver of all 1's. Otherwise returns the user1p() data.
if (!nodep->user1p()) { if (!nodep->user1p()) {
// There's no select being built yet, so add what will become a
// constant output enable driver of all 1's
V3Number num(nodep, nodep->width()); V3Number num(nodep, nodep->width());
num.setAllBits1(); num.setAllBits1();
AstNode* const enp = new AstConst(nodep->fileline(), num); AstNode* const enp = new AstConst(nodep->fileline(), num);
nodep->user1p(enp); nodep->user1p(enp);
} }
// Otherwise return the previous output enable
return nodep->user1p(); return nodep->user1p();
} }
AstVar* getCreateEnVarp(AstVar* invarp) { AstVar* getCreateEnVarp(AstVar* invarp) {
// Return the master __en for the specified input variable // Return the master __en for the specified input variable
if (!invarp->user1p()) { if (!invarp->user1p()) {
AstVar* const newp = new AstVar(invarp->fileline(), AstVarType::MODULETEMP, AstVar* const newp = new AstVar(invarp->fileline(), AstVarType::MODULETEMP,
invarp->name() + "__en", invarp); invarp->name() + "__en", invarp);
UINFO(9, " newenv " << newp << endl); UINFO(9, " newenv " << newp << endl);
if (!m_modp) { modAddStmtp(invarp, newp);
invarp->v3warn(E_UNSUPPORTED,
"Unsupported: Creating tristate signal not underneath a module: "
<< invarp->prettyNameQ());
} else {
m_modp->addStmtp(newp);
}
invarp->user1p(newp); // find envar given invarp invarp->user1p(newp); // find envar given invarp
} }
return VN_AS(invarp->user1p(), Var); return VN_AS(invarp->user1p(), Var);
} }
AstVar* getCreateOutVarp(AstVar* invarp) { AstVar* getCreateOutVarp(AstVar* invarp) {
// Return the master __out for the specified input variable // Return the master __out for the specified input variable
if (!invarp->user4p()) { if (!invarp->user4p()) {
AstVar* const newp = new AstVar(invarp->fileline(), AstVarType::MODULETEMP, AstVar* const newp = new AstVar(invarp->fileline(), AstVarType::MODULETEMP,
invarp->name() + "__out", invarp); invarp->name() + "__out", invarp);
UINFO(9, " newout " << newp << endl); UINFO(9, " newout " << newp << endl);
if (!m_modp) { modAddStmtp(invarp, newp);
invarp->v3warn(E_UNSUPPORTED,
"Unsupported: Creating tristate signal not underneath a module: "
<< invarp->prettyNameQ());
} else {
m_modp->addStmtp(newp);
}
invarp->user4p(newp); // find outvar given invarp invarp->user4p(newp); // find outvar given invarp
} }
return VN_AS(invarp->user4p(), Var); return VN_AS(invarp->user4p(), Var);
} }
AstVar* getCreateUnconnVarp(AstNode* fromp, AstNodeDType* dtypep) { AstVar* getCreateUnconnVarp(AstNode* fromp, AstNodeDType* dtypep) {
AstVar* const newp = new AstVar(fromp->fileline(), AstVarType::MODULETEMP, AstVar* const newp = new AstVar(fromp->fileline(), AstVarType::MODULETEMP,
"__Vtriunconn" + cvtToStr(m_unique++), dtypep); "__Vtriunconn" + cvtToStr(m_unique++), dtypep);
UINFO(9, " newunc " << newp << endl); UINFO(9, " newunc " << newp << endl);
if (!m_modp) { modAddStmtp(newp, newp);
newp->v3warn(E_UNSUPPORTED,
"Unsupported: Creating tristate signal not underneath a module");
} else {
m_modp->addStmtp(newp);
}
return newp; return newp;
} }
@ -490,8 +483,8 @@ class TristateVisitor final : public TristateBaseVisitor {
if (m_tgraph.isTristate(varp)) { if (m_tgraph.isTristate(varp)) {
const auto it = m_lhsmap.find(varp); const auto it = m_lhsmap.find(varp);
if (it == m_lhsmap.end()) { if (it == m_lhsmap.end()) {
// set output enable to always be off on this assign // This variable is floating, set output enable to
// statement so that this var is floating // always be off on this assign
UINFO(8, " Adding driver to var " << varp << endl); UINFO(8, " Adding driver to var " << varp << endl);
AstConst* const constp = new AstConst( AstConst* const constp = new AstConst(
varp->fileline(), AstConst::WidthedValue(), varp->width(), 0); varp->fileline(), AstConst::WidthedValue(), varp->width(), 0);
@ -503,7 +496,7 @@ class TristateVisitor final : public TristateBaseVisitor {
varp->width(), 0)); varp->width(), 0));
nodep->addStmtp(newp); nodep->addStmtp(newp);
mapInsertLhsVarRef(varrefp); // insertTristates will convert mapInsertLhsVarRef(varrefp); // insertTristates will convert
// // to a varref to the __out# variable // // to a varref to the __out# variable
} }
} }
} }
@ -516,133 +509,132 @@ class TristateVisitor final : public TristateBaseVisitor {
++nextit; ++nextit;
AstVar* const invarp = it->first; AstVar* const invarp = it->first;
const RefVec* const refsp = it->second; const RefVec* const refsp = it->second;
// Figure out if this var needs tristate expanded. // Figure out if this var needs tristate expanded.
if (!m_tgraph.isTristate(invarp)) { if (m_tgraph.isTristate(invarp)) {
// This var has no tristate logic, so we leave it alone. insertTristatesSignal(nodep, invarp, refsp);
UINFO(8, " NO TRISTATE ON:" << invarp << endl);
m_lhsmap.erase(invarp);
VL_DO_DANGLING(delete refsp, refsp);
continue;
}
++m_statTriSigs;
m_tgraph.didProcess(invarp);
UINFO(8, " TRISTATE EXPANDING:" << invarp << endl);
// If the lhs var is a port, then we need to create ports for
// the output (__out) and output enable (__en) signals. The
// original port gets converted to an input. Don't tristate expand
// if this is the top level so that we can force the final
// tristate resolution at the top.
AstVar* envarp = nullptr;
AstVar* outvarp = nullptr; // __out
AstVar* lhsp = invarp; // Variable to assign drive-value to (<in> or __out)
if (!nodep->isTop() && invarp->isIO()) {
// This var becomes an input
invarp->varType2In(); // convert existing port to type input
// Create an output port (__out)
outvarp = getCreateOutVarp(invarp);
outvarp->varType2Out();
lhsp = outvarp; // Must assign to __out, not to normal input signal
UINFO(9, " TRISTATE propagates up with " << lhsp << endl);
// Create an output enable port (__en)
// May already be created if have foo === 1'bz somewhere
envarp = getCreateEnVarp(invarp);
envarp->varType2Out();
//
outvarp->user1p(envarp);
outvarp->user3p(invarp->user3p()); // AstPull* propagation
if (invarp->user3p()) UINFO(9, "propagate pull to " << outvarp << endl);
} else if (invarp->user1p()) {
envarp = VN_AS(invarp->user1p(), Var); // From CASEEQ, foo === 1'bz
}
AstNode* orp = nullptr;
AstNode* enp = nullptr;
AstNode* undrivenp = nullptr;
// loop through the lhs drivers to build the driver resolution logic
for (auto refp : *refsp) {
const int w = lhsp->width();
// create the new lhs driver for this var
AstVar* const newlhsp = new AstVar(lhsp->fileline(), AstVarType::MODULETEMP,
lhsp->name() + "__out" + cvtToStr(m_unique),
VFlagBitPacked(), w); // 2-state ok; sep enable
UINFO(9, " newout " << newlhsp << endl);
nodep->addStmtp(newlhsp);
refp->varp(newlhsp); // assign the new var to the varref
refp->name(newlhsp->name());
// create a new var for this drivers enable signal
AstVar* const newenp = new AstVar(lhsp->fileline(), AstVarType::MODULETEMP,
lhsp->name() + "__en" + cvtToStr(m_unique++),
VFlagBitPacked(), w); // 2-state ok
UINFO(9, " newenp " << newenp << endl);
nodep->addStmtp(newenp);
AstNode* const enassp = new AstAssignW(
refp->fileline(), new AstVarRef(refp->fileline(), newenp, VAccess::WRITE),
getEnp(refp));
UINFO(9, " newass " << enassp << endl);
nodep->addStmtp(enassp);
// now append this driver to the driver logic.
AstNode* const ref1p = new AstVarRef(refp->fileline(), newlhsp, VAccess::READ);
AstNode* const ref2p = new AstVarRef(refp->fileline(), newenp, VAccess::READ);
AstNode* const andp = new AstAnd(refp->fileline(), ref1p, ref2p);
// or this to the others
orp = (!orp) ? andp : new AstOr(refp->fileline(), orp, andp);
if (envarp) {
AstNode* const ref3p = new AstVarRef(refp->fileline(), newenp, VAccess::READ);
enp = (!enp) ? ref3p : new AstOr(ref3p->fileline(), enp, ref3p);
}
AstNode* const tmp = new AstNot(
newenp->fileline(), new AstVarRef(newenp->fileline(), newenp, VAccess::READ));
undrivenp = ((!undrivenp) ? tmp : new AstAnd(refp->fileline(), tmp, undrivenp));
}
if (!undrivenp) { // No drivers on the bus
V3Number ones(invarp, lhsp->width());
ones.setAllBits1();
undrivenp = new AstConst(invarp->fileline(), ones);
}
if (!outvarp) {
// This is the final resolution of the tristate, so we apply
// the pull direction to any undriven pins.
V3Number pull(invarp, lhsp->width());
const AstPull* const pullp = static_cast<AstPull*>(lhsp->user3p());
if (pullp && pullp->direction() == 1) {
pull.setAllBits1();
UINFO(9, "Has pullup " << pullp << endl);
} else {
pull.setAllBits0(); // Default pull direction is down.
}
undrivenp = new AstAnd(invarp->fileline(), undrivenp,
new AstConst(invarp->fileline(), pull));
orp = new AstOr(invarp->fileline(), orp, undrivenp);
} else { } else {
VL_DO_DANGLING(undrivenp->deleteTree(), undrivenp); UINFO(8, " NO TRISTATE ON:" << invarp << endl);
} }
if (envarp) {
nodep->addStmtp(new AstAssignW(
enp->fileline(), new AstVarRef(envarp->fileline(), envarp, VAccess::WRITE),
enp));
}
// __out (child) or <in> (parent) = drive-value expression
AstNode* const assp = new AstAssignW(
lhsp->fileline(), new AstVarRef(lhsp->fileline(), lhsp, VAccess::WRITE), orp);
assp->user2(U2_BOTH); // Don't process further; already resolved
if (debug() >= 9) assp->dumpTree(cout, "-lhsp-eqn: ");
nodep->addStmtp(assp);
// Delete the map and vector list now that we have expanded it. // Delete the map and vector list now that we have expanded it.
m_lhsmap.erase(invarp); m_lhsmap.erase(invarp);
VL_DO_DANGLING(delete refsp, refsp); VL_DO_DANGLING(delete refsp, refsp);
} }
} }
void insertTristatesSignal(AstNodeModule* nodep, AstVar* const invarp,
const RefVec* const refsp) {
UINFO(8, " TRISTATE EXPANDING:" << invarp << endl);
++m_statTriSigs;
m_tgraph.didProcess(invarp);
// If the lhs var is a port, then we need to create ports for
// the output (__out) and output enable (__en) signals. The
// original port gets converted to an input. Don't tristate expand
// if this is the top level so that we can force the final
// tristate resolution at the top.
AstVar* envarp = nullptr;
AstVar* outvarp = nullptr; // __out
AstVar* lhsp = invarp; // Variable to assign drive-value to (<in> or __out)
if (!nodep->isTop() && invarp->isIO()) {
// This var becomes an input
invarp->varType2In(); // convert existing port to type input
// Create an output port (__out)
outvarp = getCreateOutVarp(invarp);
outvarp->varType2Out();
lhsp = outvarp; // Must assign to __out, not to normal input signal
UINFO(9, " TRISTATE propagates up with " << lhsp << endl);
// Create an output enable port (__en)
// May already be created if have foo === 1'bz somewhere
envarp = getCreateEnVarp(invarp);
envarp->varType2Out();
//
outvarp->user1p(envarp);
outvarp->user3p(invarp->user3p()); // AstPull* propagation
if (invarp->user3p()) UINFO(9, "propagate pull to " << outvarp << endl);
} else if (invarp->user1p()) {
envarp = VN_AS(invarp->user1p(), Var); // From CASEEQ, foo === 1'bz
}
AstNode* orp = nullptr;
AstNode* enp = nullptr;
AstNode* undrivenp = nullptr;
// loop through the lhs drivers to build the driver resolution logic
for (auto refp : *refsp) {
const int w = lhsp->width();
// create the new lhs driver for this var
AstVar* const newlhsp = new AstVar(lhsp->fileline(), AstVarType::MODULETEMP,
lhsp->name() + "__out" + cvtToStr(m_unique),
VFlagBitPacked(), w); // 2-state ok; sep enable
UINFO(9, " newout " << newlhsp << endl);
nodep->addStmtp(newlhsp);
refp->varp(newlhsp); // assign the new var to the varref
refp->name(newlhsp->name());
// create a new var for this drivers enable signal
AstVar* const newenp = new AstVar(lhsp->fileline(), AstVarType::MODULETEMP,
lhsp->name() + "__en" + cvtToStr(m_unique++),
VFlagBitPacked(), w); // 2-state ok
UINFO(9, " newenp " << newenp << endl);
nodep->addStmtp(newenp);
AstNode* const enassp = new AstAssignW(
refp->fileline(), new AstVarRef(refp->fileline(), newenp, VAccess::WRITE),
getEnp(refp));
UINFO(9, " newass " << enassp << endl);
nodep->addStmtp(enassp);
// now append this driver to the driver logic.
AstNode* const ref1p = new AstVarRef(refp->fileline(), newlhsp, VAccess::READ);
AstNode* const ref2p = new AstVarRef(refp->fileline(), newenp, VAccess::READ);
AstNode* const andp = new AstAnd(refp->fileline(), ref1p, ref2p);
// or this to the others
orp = (!orp) ? andp : new AstOr(refp->fileline(), orp, andp);
if (envarp) {
AstNode* const ref3p = new AstVarRef(refp->fileline(), newenp, VAccess::READ);
enp = (!enp) ? ref3p : new AstOr(ref3p->fileline(), enp, ref3p);
}
AstNode* const tmp = new AstNot(
newenp->fileline(), new AstVarRef(newenp->fileline(), newenp, VAccess::READ));
undrivenp = ((!undrivenp) ? tmp : new AstAnd(refp->fileline(), tmp, undrivenp));
}
if (!undrivenp) { // No drivers on the bus
V3Number ones(invarp, lhsp->width());
ones.setAllBits1();
undrivenp = new AstConst(invarp->fileline(), ones);
}
if (!outvarp) {
// This is the final pre-forced resolution of the tristate, so we apply
// the pull direction to any undriven pins.
V3Number pull(invarp, lhsp->width());
const AstPull* const pullp = static_cast<AstPull*>(lhsp->user3p());
if (pullp && pullp->direction() == 1) {
pull.setAllBits1();
UINFO(9, "Has pullup " << pullp << endl);
} else {
pull.setAllBits0(); // Default pull direction is down.
}
undrivenp = new AstAnd(invarp->fileline(), undrivenp,
new AstConst(invarp->fileline(), pull));
orp = new AstOr(invarp->fileline(), orp, undrivenp);
} else {
VL_DO_DANGLING(undrivenp->deleteTree(), undrivenp);
}
if (envarp) {
nodep->addStmtp(new AstAssignW(
enp->fileline(), new AstVarRef(envarp->fileline(), envarp, VAccess::WRITE), enp));
}
// __out (child) or <in> (parent) = drive-value expression
AstNode* const assp = new AstAssignW(
lhsp->fileline(), new AstVarRef(lhsp->fileline(), lhsp, VAccess::WRITE), orp);
assp->user2(U2_BOTH); // Don't process further; already resolved
if (debug() >= 9) assp->dumpTree(cout, "-lhsp-eqn: ");
nodep->addStmtp(assp);
}
// VISITORS // VISITORS
virtual void visit(AstConst* nodep) override { virtual void visit(AstConst* nodep) override {
UINFO(9, dbgState() << nodep << endl); UINFO(9, dbgState() << nodep << endl);
@ -897,15 +889,15 @@ class TristateVisitor final : public TristateBaseVisitor {
void visitAssign(AstNodeAssign* nodep) { void visitAssign(AstNodeAssign* nodep) {
if (m_graphing) { if (m_graphing) {
if (nodep->user2() & U2_GRAPHING) return; if (nodep->user2() & U2_GRAPHING) return;
nodep->user2(U2_GRAPHING); VL_RESTORER(m_logicp);
m_logicp = nodep; m_logicp = nodep;
nodep->user2(U2_GRAPHING);
iterateAndNextNull(nodep->rhsp()); iterateAndNextNull(nodep->rhsp());
m_alhs = true; m_alhs = true;
iterateAndNextNull(nodep->lhsp()); iterateAndNextNull(nodep->lhsp());
m_alhs = false; m_alhs = false;
associateLogic(nodep->rhsp(), nodep); associateLogic(nodep->rhsp(), nodep);
associateLogic(nodep, nodep->lhsp()); associateLogic(nodep, nodep->lhsp());
m_logicp = nullptr;
} else { } else {
if (nodep->user2() & U2_NONGRAPH) { if (nodep->user2() & U2_NONGRAPH) {
return; // Iterated here, or created assignment to ignore return; // Iterated here, or created assignment to ignore
@ -1063,11 +1055,11 @@ class TristateVisitor final : public TristateBaseVisitor {
nodep->v3warn(E_UNSUPPORTED, "Unsupported pullup/down (weak driver) construct."); nodep->v3warn(E_UNSUPPORTED, "Unsupported pullup/down (weak driver) construct.");
} else { } else {
if (m_graphing) { if (m_graphing) {
varrefp->access(VAccess::WRITE); VL_RESTORER(m_logicp);
m_logicp = nodep; m_logicp = nodep;
varrefp->access(VAccess::WRITE);
m_tgraph.setTristate(nodep); m_tgraph.setTristate(nodep);
associateLogic(nodep, varrefp->varp()); associateLogic(nodep, varrefp->varp());
m_logicp = nullptr;
} else { } else {
// Replace any pullup/pulldowns with assignw logic and set the // Replace any pullup/pulldowns with assignw logic and set the
// direction of the pull in the user3() data on the var. Given // direction of the pull in the user3() data on the var. Given
@ -1089,13 +1081,13 @@ class TristateVisitor final : public TristateBaseVisitor {
void iteratePinGuts(AstPin* nodep) { void iteratePinGuts(AstPin* nodep) {
if (m_graphing) { if (m_graphing) {
VL_RESTORER(m_logicp);
m_logicp = nodep; m_logicp = nodep;
if (nodep->exprp()) { if (nodep->exprp()) {
associateLogic(nodep->exprp(), nodep); associateLogic(nodep->exprp(), nodep);
associateLogic(nodep, nodep->exprp()); associateLogic(nodep, nodep->exprp());
} }
iterateChildren(nodep); iterateChildren(nodep);
m_logicp = nullptr;
} else { } else {
// All heavy lifting completed in graph visitor. // All heavy lifting completed in graph visitor.
if (nodep->exprp()) m_tgraph.didProcess(nodep); if (nodep->exprp()) m_tgraph.didProcess(nodep);
@ -1399,10 +1391,10 @@ class TristateVisitor final : public TristateBaseVisitor {
} }
virtual void visit(AstCell* nodep) override { virtual void visit(AstCell* nodep) override {
VL_RESTORER(m_cellp);
m_cellp = nodep; m_cellp = nodep;
m_alhs = false; m_alhs = false;
iterateChildren(nodep); iterateChildren(nodep);
m_cellp = nullptr;
} }
virtual void visit(AstNetlist* nodep) override { iterateChildrenBackwards(nodep); } virtual void visit(AstNetlist* nodep) override { iterateChildrenBackwards(nodep); }