Support more complex combinational assignments in DFG.
Previously DFG was limited to having a Sel, or an ArraySel potentially under a Concat on the LHS of combinational assignments. Other forms or combinations were not representable in the graph. This patch add supports to arbitrary combinations of the above by combining DfgSplicePacked and DfgSpliceArray vertices introduced in #6176. In particular, Sel(ArraySel(VarRef,_),_) enables a lot more code to be represented in DFG.
This commit is contained in:
parent
a50ea2a1a6
commit
2eae6d0c62
|
@ -239,6 +239,20 @@ DfgVertexVar* DfgGraph::makeNewVar(FileLine* flp, const std::string& name, AstNo
|
||||||
|
|
||||||
static const string toDotId(const DfgVertex& vtx) { return '"' + cvtToHex(&vtx) + '"'; }
|
static const string toDotId(const DfgVertex& vtx) { return '"' + cvtToHex(&vtx) + '"'; }
|
||||||
|
|
||||||
|
static void dumpType(std::ostream& os, const AstNodeDType* dtypep) {
|
||||||
|
if (const AstUnpackArrayDType* const typep = VN_CAST(dtypep, UnpackArrayDType)) {
|
||||||
|
os << '(';
|
||||||
|
dumpType(os, typep->subDTypep());
|
||||||
|
os << ")[" << typep->elementsConst() << ']';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (const AstBasicDType* const typep = VN_CAST(dtypep, BasicDType)) {
|
||||||
|
os << "W" << typep->width();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dtypep->v3fatalSrc("Unahndled AstNodeDType sub-type");
|
||||||
|
}
|
||||||
|
|
||||||
// Dump one DfgVertex in Graphviz format
|
// Dump one DfgVertex in Graphviz format
|
||||||
static void dumpDotVertex(std::ostream& os, const DfgVertex& vtx) {
|
static void dumpDotVertex(std::ostream& os, const DfgVertex& vtx) {
|
||||||
|
|
||||||
|
@ -246,8 +260,9 @@ static void dumpDotVertex(std::ostream& os, const DfgVertex& vtx) {
|
||||||
AstNode* const nodep = varVtxp->nodep();
|
AstNode* const nodep = varVtxp->nodep();
|
||||||
AstVar* const varp = varVtxp->varp();
|
AstVar* const varp = varVtxp->varp();
|
||||||
os << toDotId(vtx);
|
os << toDotId(vtx);
|
||||||
os << " [label=\"" << nodep->name() << "\nW" << varVtxp->width() << " / F"
|
os << " [label=\"" << nodep->name() << "\n";
|
||||||
<< varVtxp->fanout() << '"';
|
dumpType(os, varVtxp->dtypep());
|
||||||
|
os << " / F" << varVtxp->fanout() << '"';
|
||||||
|
|
||||||
if (varp->direction() == VDirection::INPUT) {
|
if (varp->direction() == VDirection::INPUT) {
|
||||||
os << ", shape=box, style=filled, fillcolor=chartreuse2"; // Green
|
os << ", shape=box, style=filled, fillcolor=chartreuse2"; // Green
|
||||||
|
@ -273,9 +288,10 @@ static void dumpDotVertex(std::ostream& os, const DfgVertex& vtx) {
|
||||||
if (const DfgVarArray* const arrVtxp = vtx.cast<DfgVarArray>()) {
|
if (const DfgVarArray* const arrVtxp = vtx.cast<DfgVarArray>()) {
|
||||||
AstNode* const nodep = arrVtxp->nodep();
|
AstNode* const nodep = arrVtxp->nodep();
|
||||||
AstVar* const varp = arrVtxp->varp();
|
AstVar* const varp = arrVtxp->varp();
|
||||||
const int elements = VN_AS(arrVtxp->dtypep(), UnpackArrayDType)->elementsConst();
|
|
||||||
os << toDotId(vtx);
|
os << toDotId(vtx);
|
||||||
os << " [label=\"" << nodep->name() << "[" << elements << "]\"";
|
os << " [label=\"" << nodep->name() << "\n";
|
||||||
|
dumpType(os, arrVtxp->dtypep());
|
||||||
|
os << " / F" << arrVtxp->fanout() << '"';
|
||||||
if (varp->direction() == VDirection::INPUT) {
|
if (varp->direction() == VDirection::INPUT) {
|
||||||
os << ", shape=box3d, style=filled, fillcolor=chartreuse2"; // Green
|
os << ", shape=box3d, style=filled, fillcolor=chartreuse2"; // Green
|
||||||
} else if (varp->direction() == VDirection::OUTPUT) {
|
} else if (varp->direction() == VDirection::OUTPUT) {
|
||||||
|
@ -318,8 +334,9 @@ static void dumpDotVertex(std::ostream& os, const DfgVertex& vtx) {
|
||||||
const uint32_t lsb = selVtxp->lsb();
|
const uint32_t lsb = selVtxp->lsb();
|
||||||
const uint32_t msb = lsb + selVtxp->width() - 1;
|
const uint32_t msb = lsb + selVtxp->width() - 1;
|
||||||
os << toDotId(vtx);
|
os << toDotId(vtx);
|
||||||
os << " [label=\"SEL\n_[" << msb << ":" << lsb << "]\nW" << vtx.width() << " / F"
|
os << " [label=\"SEL\n_[" << msb << ":" << lsb << "]\n";
|
||||||
<< vtx.fanout() << '"';
|
dumpType(os, vtx.dtypep());
|
||||||
|
os << " / F" << vtx.fanout() << '"';
|
||||||
if (vtx.hasMultipleSinks()) {
|
if (vtx.hasMultipleSinks()) {
|
||||||
os << ", shape=doublecircle";
|
os << ", shape=doublecircle";
|
||||||
} else {
|
} else {
|
||||||
|
@ -332,12 +349,7 @@ static void dumpDotVertex(std::ostream& os, const DfgVertex& vtx) {
|
||||||
if (vtx.is<DfgVertexSplice>()) {
|
if (vtx.is<DfgVertexSplice>()) {
|
||||||
os << toDotId(vtx);
|
os << toDotId(vtx);
|
||||||
os << " [label=\"" << vtx.typeName() << "\n";
|
os << " [label=\"" << vtx.typeName() << "\n";
|
||||||
if (const DfgSpliceArray* const sp = vtx.cast<DfgSpliceArray>()) {
|
dumpType(os, vtx.dtypep());
|
||||||
const int elements = VN_AS(sp->dtypep(), UnpackArrayDType)->elementsConst();
|
|
||||||
os << "_[" << elements << "]";
|
|
||||||
} else {
|
|
||||||
os << "W" << vtx.width();
|
|
||||||
}
|
|
||||||
os << " / F" << vtx.fanout() << '"';
|
os << " / F" << vtx.fanout() << '"';
|
||||||
if (vtx.hasMultipleSinks()) {
|
if (vtx.hasMultipleSinks()) {
|
||||||
os << ", shape=doubleoctagon";
|
os << ", shape=doubleoctagon";
|
||||||
|
@ -349,7 +361,9 @@ static void dumpDotVertex(std::ostream& os, const DfgVertex& vtx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
os << toDotId(vtx);
|
os << toDotId(vtx);
|
||||||
os << " [label=\"" << vtx.typeName() << "\nW" << vtx.width() << " / F" << vtx.fanout() << '"';
|
os << " [label=\"" << vtx.typeName() << "\n";
|
||||||
|
dumpType(os, vtx.dtypep());
|
||||||
|
os << " / F" << vtx.fanout() << '"';
|
||||||
if (vtx.hasMultipleSinks()) {
|
if (vtx.hasMultipleSinks()) {
|
||||||
os << ", shape=doublecircle";
|
os << ", shape=doublecircle";
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -28,9 +28,12 @@
|
||||||
|
|
||||||
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
|
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
|
||||||
|
|
||||||
|
#include "V3Const.h"
|
||||||
#include "V3Dfg.h"
|
#include "V3Dfg.h"
|
||||||
#include "V3DfgPasses.h"
|
#include "V3DfgPasses.h"
|
||||||
|
|
||||||
|
#include <iterator>
|
||||||
|
|
||||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -42,6 +45,17 @@ T_Vertex* makeVertex(const T_Node* nodep, DfgGraph& dfg) {
|
||||||
return new T_Vertex{dfg, nodep->fileline(), DfgVertex::dtypeFor(nodep)};
|
return new T_Vertex{dfg, nodep->fileline(), DfgVertex::dtypeFor(nodep)};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
DfgArraySel* makeVertex<DfgArraySel, AstArraySel>(const AstArraySel* nodep, DfgGraph& dfg) {
|
||||||
|
// Some earlier passes create malformed ArraySels, just bail on those...
|
||||||
|
// See t_bitsel_wire_array_bad
|
||||||
|
if (VN_IS(nodep->fromp(), Const)) return nullptr;
|
||||||
|
AstUnpackArrayDType* const fromDtypep
|
||||||
|
= VN_CAST(nodep->fromp()->dtypep()->skipRefp(), UnpackArrayDType);
|
||||||
|
if (!fromDtypep) return nullptr;
|
||||||
|
return new DfgArraySel{dfg, nodep->fileline(), DfgVertex::dtypeFor(nodep)};
|
||||||
|
}
|
||||||
|
|
||||||
//======================================================================
|
//======================================================================
|
||||||
// Currently unhandled nodes
|
// Currently unhandled nodes
|
||||||
// LCOV_EXCL_START
|
// LCOV_EXCL_START
|
||||||
|
@ -77,17 +91,6 @@ class AstToDfgVisitor final : public VNVisitor {
|
||||||
const VNUser1InUse m_user1InUse;
|
const VNUser1InUse m_user1InUse;
|
||||||
|
|
||||||
// TYPES
|
// TYPES
|
||||||
// Represents a driver during canonicalization
|
|
||||||
struct Driver final {
|
|
||||||
FileLine* m_fileline;
|
|
||||||
DfgVertex* m_vtxp;
|
|
||||||
uint32_t m_lsb;
|
|
||||||
Driver(FileLine* flp, uint32_t lsb, DfgVertex* vtxp)
|
|
||||||
: m_fileline{flp}
|
|
||||||
, m_vtxp{vtxp}
|
|
||||||
, m_lsb{lsb} {}
|
|
||||||
};
|
|
||||||
|
|
||||||
using RootType = std::conditional_t<T_Scoped, AstNetlist, AstModule>;
|
using RootType = std::conditional_t<T_Scoped, AstNetlist, AstModule>;
|
||||||
using VariableType = std::conditional_t<T_Scoped, AstVarScope, AstVar>;
|
using VariableType = std::conditional_t<T_Scoped, AstVarScope, AstVar>;
|
||||||
|
|
||||||
|
@ -183,93 +186,144 @@ class AstToDfgVisitor final : public VNVisitor {
|
||||||
return m_foundUnhandled;
|
return m_foundUnhandled;
|
||||||
}
|
}
|
||||||
|
|
||||||
DfgVertexSplice* convertLValue(AstNode* nodep) {
|
std::pair<DfgVertexSplice*, uint32_t> convertLValue(AstNode* nodep) {
|
||||||
FileLine* const flp = nodep->fileline();
|
|
||||||
|
|
||||||
if (AstVarRef* const vrefp = VN_CAST(nodep, VarRef)) {
|
if (AstVarRef* const vrefp = VN_CAST(nodep, VarRef)) {
|
||||||
m_foundUnhandled = false;
|
m_foundUnhandled = false;
|
||||||
visit(vrefp);
|
visit(vrefp);
|
||||||
if (m_foundUnhandled) return nullptr;
|
if (m_foundUnhandled) return {nullptr, 0};
|
||||||
|
|
||||||
|
// Get the variable vertex
|
||||||
DfgVertexVar* const vtxp = getVertex(vrefp)->template as<DfgVertexVar>();
|
DfgVertexVar* const vtxp = getVertex(vrefp)->template as<DfgVertexVar>();
|
||||||
// Ensure driving splice vertex exists
|
// Ensure the Splice driver exists for this variable
|
||||||
if (!vtxp->srcp()) {
|
if (!vtxp->srcp()) {
|
||||||
if (VN_IS(vtxp->dtypep(), UnpackArrayDType)) {
|
FileLine* const flp = vtxp->fileline();
|
||||||
vtxp->srcp(new DfgSpliceArray{*m_dfgp, flp, vtxp->dtypep()});
|
AstNodeDType* const dtypep = vtxp->dtypep();
|
||||||
|
if (vtxp->is<DfgVarPacked>()) {
|
||||||
|
vtxp->srcp(new DfgSplicePacked{*m_dfgp, flp, dtypep});
|
||||||
|
} else if (vtxp->is<DfgVarArray>()) {
|
||||||
|
vtxp->srcp(new DfgSpliceArray{*m_dfgp, flp, dtypep});
|
||||||
} else {
|
} else {
|
||||||
vtxp->srcp(new DfgSplicePacked{*m_dfgp, flp, vtxp->dtypep()});
|
nodep->v3fatalSrc("Unhandled DfgVertexVar sub-type"); // LCOV_EXCL_LINE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return vtxp->srcp()->as<DfgVertexSplice>();
|
// Return the Splice driver
|
||||||
|
return {vtxp->srcp()->as<DfgVertexSplice>(), 0};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (AstSel* selp = VN_CAST(nodep, Sel)) {
|
||||||
|
// Only handle constant selects
|
||||||
|
const AstConst* const lsbp = VN_CAST(selp->lsbp(), Const);
|
||||||
|
if (!lsbp) {
|
||||||
|
++m_ctx.m_nonRepLhs;
|
||||||
|
return {nullptr, 0};
|
||||||
|
}
|
||||||
|
uint32_t lsb = lsbp->toUInt();
|
||||||
|
|
||||||
|
// Convert the 'fromp' sub-expression
|
||||||
|
const auto pair = convertLValue(selp->fromp());
|
||||||
|
if (!pair.first) return {nullptr, 0};
|
||||||
|
DfgSplicePacked* const splicep = pair.first->template as<DfgSplicePacked>();
|
||||||
|
// Adjust index.
|
||||||
|
lsb += pair.second;
|
||||||
|
|
||||||
|
// AstSel doesn't change type kind (array vs packed), so we can use
|
||||||
|
// the existing splice driver with adjusted lsb
|
||||||
|
return {splicep, lsb};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (AstArraySel* const aselp = VN_CAST(nodep, ArraySel)) {
|
||||||
|
// Only handle constant selects
|
||||||
|
const AstConst* const indexp = VN_CAST(aselp->bitp(), Const);
|
||||||
|
if (!indexp) {
|
||||||
|
++m_ctx.m_nonRepLhs;
|
||||||
|
return {nullptr, 0};
|
||||||
|
}
|
||||||
|
uint32_t index = indexp->toUInt();
|
||||||
|
|
||||||
|
// Convert the 'fromp' sub-expression
|
||||||
|
const auto pair = convertLValue(aselp->fromp());
|
||||||
|
if (!pair.first) return {nullptr, 0};
|
||||||
|
DfgSpliceArray* const splicep = pair.first->template as<DfgSpliceArray>();
|
||||||
|
// Adjust index. Note pair.second is always 0, but we might handle array slices later..
|
||||||
|
index += pair.second;
|
||||||
|
|
||||||
|
// Ensure the Splice driver exists for this element
|
||||||
|
if (!splicep->driverAt(index)) {
|
||||||
|
FileLine* const flp = nodep->fileline();
|
||||||
|
AstNodeDType* const dtypep = DfgVertex::dtypeFor(nodep);
|
||||||
|
if (VN_IS(dtypep, BasicDType)) {
|
||||||
|
splicep->addDriver(flp, index, new DfgSplicePacked{*m_dfgp, flp, dtypep});
|
||||||
|
} else if (VN_IS(dtypep, UnpackArrayDType)) {
|
||||||
|
splicep->addDriver(flp, index, new DfgSpliceArray{*m_dfgp, flp, dtypep});
|
||||||
|
} else {
|
||||||
|
nodep->v3fatalSrc("Unhandled AstNodeDType sub-type"); // LCOV_EXCL_LINE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the splice driver
|
||||||
|
return {splicep->driverAt(index)->as<DfgVertexSplice>(), 0};
|
||||||
}
|
}
|
||||||
|
|
||||||
++m_ctx.m_nonRepLhs;
|
++m_ctx.m_nonRepLhs;
|
||||||
return nullptr;
|
return {nullptr, 0};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build DfgEdge representing the LValue assignment. Returns false if unsuccessful.
|
// Build DfgEdge representing the LValue assignment. Returns false if unsuccessful.
|
||||||
bool convertAssignment(FileLine* flp, AstNode* nodep, DfgVertex* vtxp) {
|
bool convertAssignment(FileLine* flp, AstNode* lhsp, DfgVertex* vtxp) {
|
||||||
|
// Simplify the LHS, to get rid of things like SEL(CONCAT(_, _), _)
|
||||||
|
lhsp = V3Const::constifyExpensiveEdit(lhsp);
|
||||||
|
|
||||||
// Concatenation on the LHS. Select parts of the driving 'vtxp' then convert each part
|
// Concatenation on the LHS. Select parts of the driving 'vtxp' then convert each part
|
||||||
if (AstConcat* const concatp = VN_CAST(nodep, Concat)) {
|
if (AstConcat* const concatp = VN_CAST(lhsp, Concat)) {
|
||||||
AstNode* const lhsp = concatp->lhsp();
|
AstNode* const cLhsp = concatp->lhsp();
|
||||||
AstNode* const rhsp = concatp->rhsp();
|
AstNode* const cRhsp = concatp->rhsp();
|
||||||
|
|
||||||
{ // Convet LHS of concat
|
{ // Convet LHS of concat
|
||||||
FileLine* const lFlp = lhsp->fileline();
|
FileLine* const lFlp = cLhsp->fileline();
|
||||||
DfgSel* const lVtxp = new DfgSel{*m_dfgp, lFlp, DfgVertex::dtypeFor(lhsp)};
|
DfgSel* const lVtxp = new DfgSel{*m_dfgp, lFlp, DfgVertex::dtypeFor(cLhsp)};
|
||||||
lVtxp->fromp(vtxp);
|
lVtxp->fromp(vtxp);
|
||||||
lVtxp->lsb(rhsp->width());
|
lVtxp->lsb(cRhsp->width());
|
||||||
if (!convertAssignment(flp, lhsp, lVtxp)) return false;
|
if (!convertAssignment(flp, cLhsp, lVtxp)) return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
{ // Convert RHS of concat
|
{ // Convert RHS of concat
|
||||||
FileLine* const rFlp = rhsp->fileline();
|
FileLine* const rFlp = cRhsp->fileline();
|
||||||
DfgSel* const rVtxp = new DfgSel{*m_dfgp, rFlp, DfgVertex::dtypeFor(rhsp)};
|
DfgSel* const rVtxp = new DfgSel{*m_dfgp, rFlp, DfgVertex::dtypeFor(cRhsp)};
|
||||||
rVtxp->fromp(vtxp);
|
rVtxp->fromp(vtxp);
|
||||||
rVtxp->lsb(0);
|
rVtxp->lsb(0);
|
||||||
return convertAssignment(flp, rhsp, rVtxp);
|
return convertAssignment(flp, cRhsp, rVtxp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (AstSel* const selp = VN_CAST(nodep, Sel)) {
|
// Construct LHS
|
||||||
AstVarRef* const vrefp = VN_CAST(selp->fromp(), VarRef);
|
const auto pair = convertLValue(lhsp);
|
||||||
const AstConst* const lsbp = VN_CAST(selp->lsbp(), Const);
|
if (!pair.first) return false;
|
||||||
if (!vrefp || !lsbp) {
|
|
||||||
++m_ctx.m_nonRepLhs;
|
// If successful connect the driver
|
||||||
return false;
|
if (DfgSplicePacked* const sPackedp = pair.first->template cast<DfgSplicePacked>()) {
|
||||||
}
|
sPackedp->addDriver(flp, pair.second, vtxp);
|
||||||
if (DfgVertexSplice* const splicep = convertLValue(vrefp)) {
|
} else if (DfgSpliceArray* const sArrayp = pair.first->template cast<DfgSpliceArray>()) {
|
||||||
splicep->template as<DfgSplicePacked>()->addDriver(flp, lsbp->toUInt(), vtxp);
|
sArrayp->addDriver(flp, pair.second, vtxp);
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} else if (AstArraySel* const selp = VN_CAST(nodep, ArraySel)) {
|
|
||||||
AstVarRef* const vrefp = VN_CAST(selp->fromp(), VarRef);
|
|
||||||
const AstConst* const idxp = VN_CAST(selp->bitp(), Const);
|
|
||||||
if (!vrefp || !idxp) {
|
|
||||||
++m_ctx.m_nonRepLhs;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (DfgVertexSplice* const splicep = convertLValue(vrefp)) {
|
|
||||||
splicep->template as<DfgSpliceArray>()->addDriver(flp, idxp->toUInt(), vtxp);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} else if (VN_IS(nodep, VarRef)) {
|
|
||||||
if (DfgVertexSplice* const splicep = convertLValue(nodep)) {
|
|
||||||
splicep->template as<DfgSplicePacked>()->addDriver(flp, 0, vtxp);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
++m_ctx.m_nonRepLhs;
|
lhsp->v3fatalSrc("Unhandled DfgVertexSplice sub-type"); // LCOV_EXCL_LINE
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool convertEquation(AstNode* nodep, FileLine* flp, AstNode* lhsp, AstNode* rhsp) {
|
bool convertEquation(AstNode* nodep, FileLine* flp, AstNode* lhsp, AstNode* rhsp) {
|
||||||
UASSERT_OBJ(m_uncommittedVertices.empty(), nodep, "Should not nest");
|
UASSERT_OBJ(m_uncommittedVertices.empty(), nodep, "Should not nest");
|
||||||
|
|
||||||
// Currently cannot handle direct assignments between unpacked types. These arise e.g.
|
// Check data types are compatible.
|
||||||
// when passing an unpacked array through a module port.
|
if (!DfgVertex::isSupportedDType(lhsp->dtypep())
|
||||||
if (!DfgVertex::isSupportedPackedDType(lhsp->dtypep())
|
|| !DfgVertex::isSupportedDType(rhsp->dtypep())) {
|
||||||
|| !DfgVertex::isSupportedPackedDType(rhsp->dtypep())) {
|
markReferenced(nodep);
|
||||||
|
++m_ctx.m_nonRepDType;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For now, only direct array assignment is supported (e.g. a = b, but not a = _ ? b : c)
|
||||||
|
if (VN_IS(rhsp->dtypep()->skipRefp(), UnpackArrayDType) && !VN_IS(rhsp, VarRef)) {
|
||||||
markReferenced(nodep);
|
markReferenced(nodep);
|
||||||
++m_ctx.m_nonRepDType;
|
++m_ctx.m_nonRepDType;
|
||||||
return false;
|
return false;
|
||||||
|
@ -312,180 +366,310 @@ class AstToDfgVisitor final : public VNVisitor {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sometime assignment ranges are coalesced by V3Const,
|
// Prune vertices potentially unused due to resolving multiple drivers.
|
||||||
// so we unpack concatenations for better error reporting.
|
// Having multiple drivers is an error and is hence assumed to be rare,
|
||||||
void addDriver(FileLine* flp, uint32_t lsb, DfgVertex* vtxp,
|
// so performance is not very important, set will suffice.
|
||||||
std::vector<Driver>& drivers) const {
|
void removeUnused(std::set<DfgVertex*>& prune) {
|
||||||
if (DfgConcat* const concatp = vtxp->cast<DfgConcat>()) {
|
while (!prune.empty()) {
|
||||||
DfgVertex* const rhsp = concatp->rhsp();
|
// Pop last vertex
|
||||||
auto const rhs_width = rhsp->width();
|
const auto it = prune.begin();
|
||||||
addDriver(rhsp->fileline(), lsb, rhsp, drivers);
|
DfgVertex* const vtxp = *it;
|
||||||
DfgVertex* const lhsp = concatp->lhsp();
|
prune.erase(it);
|
||||||
addDriver(lhsp->fileline(), lsb + rhs_width, lhsp, drivers);
|
// If used (or a variable), then done
|
||||||
concatp->unlinkDelete(*m_dfgp);
|
if (vtxp->hasSinks() || vtxp->is<DfgVertexVar>()) continue;
|
||||||
} else {
|
// If unused, then add sources to work list and delete
|
||||||
drivers.emplace_back(flp, lsb, vtxp);
|
vtxp->forEachSource([&](DfgVertex& src) { prune.emplace(&src); });
|
||||||
|
vtxp->unlinkDelete(*m_dfgp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Canonicalize packed variables
|
// Normalize packed driver - return the normalized vertex and location for 'splicep'
|
||||||
void canonicalizePacked() {
|
std::pair<DfgVertex*, FileLine*> //
|
||||||
for (DfgVarPacked* const varp : m_varPackedps) {
|
normalizePacked(DfgVertexVar* varp, const std::string& sub, DfgSplicePacked* const splicep) {
|
||||||
// Delete variables with no sinks nor sources (this can happen due to reverting
|
// Represents a driver of 'splicep'
|
||||||
// uncommitted vertices, which does not remove variables)
|
struct Driver final {
|
||||||
if (!varp->hasSinks() && !varp->srcp()) {
|
FileLine* m_fileline;
|
||||||
VL_DO_DANGLING(varp->unlinkDelete(*m_dfgp), varp);
|
DfgVertex* m_vtxp;
|
||||||
|
uint32_t m_lsb;
|
||||||
|
Driver() = delete;
|
||||||
|
Driver(FileLine* flp, uint32_t lsb, DfgVertex* vtxp)
|
||||||
|
: m_fileline{flp}
|
||||||
|
, m_vtxp{vtxp}
|
||||||
|
, m_lsb{lsb} {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// The drivers of 'splicep'
|
||||||
|
std::vector<Driver> drivers;
|
||||||
|
drivers.reserve(splicep->arity());
|
||||||
|
|
||||||
|
// Sometime assignment ranges are coalesced by V3Const,
|
||||||
|
// so we unpack concatenations for better error reporting.
|
||||||
|
const std::function<void(FileLine*, uint32_t, DfgVertex*)> gather
|
||||||
|
= [&](FileLine* flp, uint32_t lsb, DfgVertex* vtxp) -> void {
|
||||||
|
if (DfgConcat* const concatp = vtxp->cast<DfgConcat>()) {
|
||||||
|
DfgVertex* const rhsp = concatp->rhsp();
|
||||||
|
auto const rhs_width = rhsp->width();
|
||||||
|
gather(rhsp->fileline(), lsb, rhsp);
|
||||||
|
DfgVertex* const lhsp = concatp->lhsp();
|
||||||
|
gather(lhsp->fileline(), lsb + rhs_width, lhsp);
|
||||||
|
concatp->unlinkDelete(*m_dfgp);
|
||||||
|
} else {
|
||||||
|
drivers.emplace_back(flp, lsb, vtxp);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Gather and unlink all drivers
|
||||||
|
splicep->forEachSourceEdge([&](DfgEdge& edge, size_t idx) {
|
||||||
|
DfgVertex* const driverp = edge.sourcep();
|
||||||
|
UASSERT(driverp, "Should not have created undriven sources");
|
||||||
|
UASSERT_OBJ(!driverp->is<DfgVertexSplice>(), splicep, "Should not be DfgVertexSplice");
|
||||||
|
gather(splicep->driverFileLine(idx), splicep->driverLsb(idx), driverp);
|
||||||
|
edge.unlinkSource();
|
||||||
|
});
|
||||||
|
|
||||||
|
const auto cmp = [](const Driver& a, const Driver& b) {
|
||||||
|
if (a.m_lsb != b.m_lsb) return a.m_lsb < b.m_lsb;
|
||||||
|
return a.m_fileline->operatorCompare(*b.m_fileline) < 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Sort drivers by LSB
|
||||||
|
std::stable_sort(drivers.begin(), drivers.end(), cmp);
|
||||||
|
|
||||||
|
// Vertices that might have become unused due to multiple driver resolution. Having
|
||||||
|
// multiple drivers is an error and is hence assumed to be rare, so performance is
|
||||||
|
// not very important, set will suffice.
|
||||||
|
std::set<DfgVertex*> prune;
|
||||||
|
|
||||||
|
// Fix multiply driven ranges
|
||||||
|
for (auto it = drivers.begin(); it != drivers.end();) {
|
||||||
|
Driver& a = *it++;
|
||||||
|
const uint32_t aWidth = a.m_vtxp->width();
|
||||||
|
const uint32_t aEnd = a.m_lsb + aWidth;
|
||||||
|
while (it != drivers.end()) {
|
||||||
|
Driver& b = *it;
|
||||||
|
// If no overlap, then nothing to do
|
||||||
|
if (b.m_lsb >= aEnd) break;
|
||||||
|
|
||||||
|
const uint32_t bWidth = b.m_vtxp->width();
|
||||||
|
const uint32_t bEnd = b.m_lsb + bWidth;
|
||||||
|
const uint32_t overlapEnd = std::min(aEnd, bEnd) - 1;
|
||||||
|
|
||||||
|
if (a.m_fileline->operatorCompare(*b.m_fileline) != 0
|
||||||
|
&& !varp->varp()->isUsedLoopIdx() // Loop index often abused, so suppress
|
||||||
|
) {
|
||||||
|
AstNode* const vp = varp->varScopep()
|
||||||
|
? static_cast<AstNode*>(varp->varScopep())
|
||||||
|
: static_cast<AstNode*>(varp->varp());
|
||||||
|
|
||||||
|
vp->v3warn( //
|
||||||
|
MULTIDRIVEN,
|
||||||
|
"Bits [" //
|
||||||
|
<< overlapEnd << ":" << b.m_lsb << "] of signal '" << vp->prettyName()
|
||||||
|
<< sub << "' have multiple combinational drivers\n"
|
||||||
|
<< a.m_fileline->warnOther() << "... Location of first driver\n"
|
||||||
|
<< a.m_fileline->warnContextPrimary() << '\n'
|
||||||
|
<< b.m_fileline->warnOther() << "... Location of other driver\n"
|
||||||
|
<< b.m_fileline->warnContextSecondary() << vp->warnOther()
|
||||||
|
<< "... Only the first driver will be respected");
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the first driver completely covers the range of the second driver,
|
||||||
|
// we can just delete the second driver completely, otherwise adjust the
|
||||||
|
// second driver to apply from the end of the range of the first driver.
|
||||||
|
if (aEnd >= bEnd) {
|
||||||
|
prune.emplace(b.m_vtxp);
|
||||||
|
it = drivers.erase(it);
|
||||||
|
} else {
|
||||||
|
const auto dtypep = DfgVertex::dtypeForWidth(bEnd - aEnd);
|
||||||
|
DfgSel* const selp = new DfgSel{*m_dfgp, b.m_vtxp->fileline(), dtypep};
|
||||||
|
selp->fromp(b.m_vtxp);
|
||||||
|
selp->lsb(aEnd - b.m_lsb);
|
||||||
|
b.m_lsb = aEnd;
|
||||||
|
b.m_vtxp = selp;
|
||||||
|
std::stable_sort(it, drivers.end(), cmp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Coalesce adjacent ranges
|
||||||
|
for (size_t i = 0, j = 1; j < drivers.size(); ++j) {
|
||||||
|
Driver& a = drivers[i];
|
||||||
|
Driver& b = drivers[j];
|
||||||
|
|
||||||
|
// Coalesce adjacent range
|
||||||
|
const uint32_t aWidth = a.m_vtxp->width();
|
||||||
|
const uint32_t bWidth = b.m_vtxp->width();
|
||||||
|
if (a.m_lsb + aWidth == b.m_lsb) {
|
||||||
|
const auto dtypep = DfgVertex::dtypeForWidth(aWidth + bWidth);
|
||||||
|
DfgConcat* const concatp = new DfgConcat{*m_dfgp, a.m_fileline, dtypep};
|
||||||
|
concatp->rhsp(a.m_vtxp);
|
||||||
|
concatp->lhsp(b.m_vtxp);
|
||||||
|
a.m_vtxp = concatp;
|
||||||
|
b.m_vtxp = nullptr; // Mark as moved
|
||||||
|
++m_ctx.m_coalescedAssignments;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Nothing to do for un-driven (input) variables
|
++i;
|
||||||
if (!varp->srcp()) continue;
|
|
||||||
|
|
||||||
DfgSplicePacked* const splicep = varp->srcp()->as<DfgSplicePacked>();
|
// Compact non-adjacent ranges within the vector
|
||||||
|
if (j != i) {
|
||||||
// Gather (and unlink) all drivers
|
Driver& c = drivers[i];
|
||||||
std::vector<Driver> drivers;
|
UASSERT_OBJ(!c.m_vtxp, c.m_fileline, "Should have been marked moved");
|
||||||
drivers.reserve(splicep->arity());
|
c = b;
|
||||||
splicep->forEachSourceEdge([this, splicep, &drivers](DfgEdge& edge, size_t idx) {
|
b.m_vtxp = nullptr; // Mark as moved
|
||||||
DfgVertex* const driverp = edge.sourcep();
|
|
||||||
UASSERT(driverp, "Should not have created undriven sources");
|
|
||||||
addDriver(splicep->driverFileLine(idx), splicep->driverLsb(idx), driverp, drivers);
|
|
||||||
edge.unlinkSource();
|
|
||||||
});
|
|
||||||
|
|
||||||
const auto cmp = [](const Driver& a, const Driver& b) {
|
|
||||||
if (a.m_lsb != b.m_lsb) return a.m_lsb < b.m_lsb;
|
|
||||||
return a.m_fileline->operatorCompare(*b.m_fileline) < 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Sort drivers by LSB
|
|
||||||
std::stable_sort(drivers.begin(), drivers.end(), cmp);
|
|
||||||
|
|
||||||
// Vertices that might have become unused due to multiple driver resolution. Having
|
|
||||||
// multiple drivers is an error and is hence assumed to be rare, so performance is
|
|
||||||
// not very important, set will suffice.
|
|
||||||
std::set<DfgVertex*> prune;
|
|
||||||
|
|
||||||
// Fix multiply driven ranges
|
|
||||||
for (auto it = drivers.begin(); it != drivers.end();) {
|
|
||||||
Driver& a = *it++;
|
|
||||||
const uint32_t aWidth = a.m_vtxp->width();
|
|
||||||
const uint32_t aEnd = a.m_lsb + aWidth;
|
|
||||||
while (it != drivers.end()) {
|
|
||||||
Driver& b = *it;
|
|
||||||
// If no overlap, then nothing to do
|
|
||||||
if (b.m_lsb >= aEnd) break;
|
|
||||||
|
|
||||||
const uint32_t bWidth = b.m_vtxp->width();
|
|
||||||
const uint32_t bEnd = b.m_lsb + bWidth;
|
|
||||||
const uint32_t overlapEnd = std::min(aEnd, bEnd) - 1;
|
|
||||||
|
|
||||||
if (a.m_fileline->operatorCompare(*b.m_fileline) != 0
|
|
||||||
&& !varp->varp()->isUsedLoopIdx() // Loop index often abused, so suppress
|
|
||||||
) {
|
|
||||||
AstNode* const vp = varp->varScopep()
|
|
||||||
? static_cast<AstNode*>(varp->varScopep())
|
|
||||||
: static_cast<AstNode*>(varp->varp());
|
|
||||||
vp->v3warn( //
|
|
||||||
MULTIDRIVEN,
|
|
||||||
"Bits [" //
|
|
||||||
<< overlapEnd << ":" << b.m_lsb << "] of signal "
|
|
||||||
<< vp->prettyNameQ() << " have multiple combinational drivers\n"
|
|
||||||
<< a.m_fileline->warnOther() << "... Location of first driver\n"
|
|
||||||
<< a.m_fileline->warnContextPrimary() << '\n'
|
|
||||||
<< b.m_fileline->warnOther() << "... Location of other driver\n"
|
|
||||||
<< b.m_fileline->warnContextSecondary() << vp->warnOther()
|
|
||||||
<< "... Only the first driver will be respected");
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the first driver completely covers the range of the second driver,
|
|
||||||
// we can just delete the second driver completely, otherwise adjust the
|
|
||||||
// second driver to apply from the end of the range of the first driver.
|
|
||||||
if (aEnd >= bEnd) {
|
|
||||||
prune.emplace(b.m_vtxp);
|
|
||||||
it = drivers.erase(it);
|
|
||||||
} else {
|
|
||||||
const auto dtypep = DfgVertex::dtypeForWidth(bEnd - aEnd);
|
|
||||||
DfgSel* const selp = new DfgSel{*m_dfgp, b.m_vtxp->fileline(), dtypep};
|
|
||||||
selp->fromp(b.m_vtxp);
|
|
||||||
selp->lsb(aEnd - b.m_lsb);
|
|
||||||
b.m_lsb = aEnd;
|
|
||||||
b.m_vtxp = selp;
|
|
||||||
std::stable_sort(it, drivers.end(), cmp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Coalesce adjacent ranges
|
|
||||||
for (size_t i = 0, j = 1; j < drivers.size(); ++j) {
|
|
||||||
Driver& a = drivers[i];
|
|
||||||
Driver& b = drivers[j];
|
|
||||||
|
|
||||||
// Coalesce adjacent range
|
|
||||||
const uint32_t aWidth = a.m_vtxp->width();
|
|
||||||
const uint32_t bWidth = b.m_vtxp->width();
|
|
||||||
if (a.m_lsb + aWidth == b.m_lsb) {
|
|
||||||
const auto dtypep = DfgVertex::dtypeForWidth(aWidth + bWidth);
|
|
||||||
DfgConcat* const concatp = new DfgConcat{*m_dfgp, a.m_fileline, dtypep};
|
|
||||||
concatp->rhsp(a.m_vtxp);
|
|
||||||
concatp->lhsp(b.m_vtxp);
|
|
||||||
a.m_vtxp = concatp;
|
|
||||||
b.m_vtxp = nullptr; // Mark as moved
|
|
||||||
++m_ctx.m_coalescedAssignments;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
++i;
|
|
||||||
|
|
||||||
// Compact non-adjacent ranges within the vector
|
|
||||||
if (j != i) {
|
|
||||||
Driver& c = drivers[i];
|
|
||||||
UASSERT_OBJ(!c.m_vtxp, c.m_fileline, "Should have been marked moved");
|
|
||||||
c = b;
|
|
||||||
b.m_vtxp = nullptr; // Mark as moved
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reinsert drivers in order
|
|
||||||
splicep->resetSources();
|
|
||||||
for (const Driver& driver : drivers) {
|
|
||||||
if (!driver.m_vtxp) break; // Stop at end of compacted list
|
|
||||||
splicep->addDriver(driver.m_fileline, driver.m_lsb, driver.m_vtxp);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prune vertices potentially unused due to resolving multiple drivers.
|
|
||||||
while (!prune.empty()) {
|
|
||||||
// Pop last vertex
|
|
||||||
const auto it = prune.begin();
|
|
||||||
DfgVertex* const vtxp = *it;
|
|
||||||
prune.erase(it);
|
|
||||||
// If used (or a variable), then done
|
|
||||||
if (vtxp->hasSinks() || vtxp->is<DfgVertexVar>()) continue;
|
|
||||||
// If unused, then add sources to work list and delete
|
|
||||||
vtxp->forEachSource([&](DfgVertex& src) { prune.emplace(&src); });
|
|
||||||
vtxp->unlinkDelete(*m_dfgp);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the whole variable is driven, remove the splice node
|
|
||||||
if (splicep->arity() == 1 //
|
|
||||||
&& splicep->driverLsb(0) == 0 //
|
|
||||||
&& splicep->source(0)->width() == varp->width()) {
|
|
||||||
varp->srcp(splicep->source(0));
|
|
||||||
varp->driverFileLine(splicep->driverFileLine(0));
|
|
||||||
splicep->unlinkDelete(*m_dfgp);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reinsert drivers in order
|
||||||
|
splicep->resetSources();
|
||||||
|
for (const Driver& driver : drivers) {
|
||||||
|
if (!driver.m_vtxp) break; // Stop at end of compacted list
|
||||||
|
splicep->addDriver(driver.m_fileline, driver.m_lsb, driver.m_vtxp);
|
||||||
|
}
|
||||||
|
|
||||||
|
removeUnused(prune);
|
||||||
|
|
||||||
|
// If the whole variable is driven whole, we can just use that driver
|
||||||
|
if (splicep->arity() == 1 //
|
||||||
|
&& splicep->driverLsb(0) == 0 //
|
||||||
|
&& splicep->source(0)->width() == splicep->width()) {
|
||||||
|
const auto result = std::make_pair(splicep->source(0), splicep->driverFileLine(0));
|
||||||
|
VL_DO_DANGLING(splicep->unlinkDelete(*m_dfgp), splicep);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return {splicep, splicep->fileline()};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Canonicalize array variables
|
// Normalize array driver - return the normalized vertex and location for 'splicep'
|
||||||
void canonicalizeArray() {
|
std::pair<DfgVertex*, FileLine*> //
|
||||||
for (DfgVarArray* const varp : m_varArrayps) {
|
normalizeArray(DfgVertexVar* varp, const std::string& sub, DfgSpliceArray* const splicep) {
|
||||||
// Delete variables with no sinks nor sources (this can happen due to reverting
|
// Represents a driver of 'splicep'
|
||||||
// uncommitted vertices, which does not remove variables)
|
struct Driver final {
|
||||||
if (!varp->hasSinks() && !varp->srcp()) {
|
FileLine* m_fileline;
|
||||||
VL_DO_DANGLING(varp->unlinkDelete(*m_dfgp), varp);
|
DfgVertex* m_vtxp;
|
||||||
|
uint32_t m_idx;
|
||||||
|
Driver() = delete;
|
||||||
|
Driver(FileLine* flp, uint32_t idx, DfgVertex* vtxp)
|
||||||
|
: m_fileline{flp}
|
||||||
|
, m_vtxp{vtxp}
|
||||||
|
, m_idx{idx} {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// The drivers of 'splicep'
|
||||||
|
std::vector<Driver> drivers;
|
||||||
|
drivers.reserve(splicep->arity());
|
||||||
|
|
||||||
|
// Normalize, gather, and unlink all drivers
|
||||||
|
splicep->forEachSourceEdge([&](DfgEdge& edge, size_t i) {
|
||||||
|
DfgVertex* const driverp = edge.sourcep();
|
||||||
|
UASSERT(driverp, "Should not have created undriven sources");
|
||||||
|
const uint32_t idx = splicep->driverIndex(i);
|
||||||
|
if (DfgSplicePacked* const spp = driverp->cast<DfgSplicePacked>()) {
|
||||||
|
const auto pair
|
||||||
|
= normalizePacked(varp, sub + "[" + std::to_string(idx) + "]", spp);
|
||||||
|
drivers.emplace_back(pair.second, idx, pair.first);
|
||||||
|
} else if (DfgSpliceArray* const sap = driverp->cast<DfgSpliceArray>()) {
|
||||||
|
const auto pair = normalizeArray(varp, sub + "[" + std::to_string(idx) + "]", sap);
|
||||||
|
drivers.emplace_back(pair.second, idx, pair.first);
|
||||||
|
} else if (driverp->is<DfgVertexSplice>()) {
|
||||||
|
driverp->v3fatalSrc("Unhandled DfgVertexSplice sub-type");
|
||||||
|
} else {
|
||||||
|
drivers.emplace_back(splicep->driverFileLine(i), idx, driverp);
|
||||||
|
}
|
||||||
|
edge.unlinkSource();
|
||||||
|
});
|
||||||
|
|
||||||
|
const auto cmp = [](const Driver& a, const Driver& b) {
|
||||||
|
if (a.m_idx != b.m_idx) return a.m_idx < b.m_idx;
|
||||||
|
return a.m_fileline->operatorCompare(*b.m_fileline) < 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Sort drivers by index
|
||||||
|
std::stable_sort(drivers.begin(), drivers.end(), cmp);
|
||||||
|
|
||||||
|
// Vertices that become unused due to multiple driver resolution
|
||||||
|
std::set<DfgVertex*> prune;
|
||||||
|
|
||||||
|
// Fix multiply driven ranges
|
||||||
|
for (auto it = drivers.begin(); it != drivers.end();) {
|
||||||
|
Driver& a = *it++;
|
||||||
|
AstUnpackArrayDType* aArrayDTypep = VN_CAST(a.m_vtxp->dtypep(), UnpackArrayDType);
|
||||||
|
const uint32_t aElements = aArrayDTypep ? aArrayDTypep->elementsConst() : 1;
|
||||||
|
const uint32_t aEnd = a.m_idx + aElements;
|
||||||
|
while (it != drivers.end()) {
|
||||||
|
Driver& b = *it;
|
||||||
|
// If no overlap, then nothing to do
|
||||||
|
if (b.m_idx >= aEnd) break;
|
||||||
|
|
||||||
|
AstUnpackArrayDType* bArrayDTypep = VN_CAST(b.m_vtxp->dtypep(), UnpackArrayDType);
|
||||||
|
const uint32_t bElements = bArrayDTypep ? bArrayDTypep->elementsConst() : 1;
|
||||||
|
const uint32_t bEnd = b.m_idx + bElements;
|
||||||
|
const uint32_t overlapEnd = std::min(aEnd, bEnd) - 1;
|
||||||
|
|
||||||
|
if (a.m_fileline->operatorCompare(*b.m_fileline) != 0) {
|
||||||
|
AstNode* const vp = varp->varScopep()
|
||||||
|
? static_cast<AstNode*>(varp->varScopep())
|
||||||
|
: static_cast<AstNode*>(varp->varp());
|
||||||
|
|
||||||
|
vp->v3warn( //
|
||||||
|
MULTIDRIVEN,
|
||||||
|
"Elements [" //
|
||||||
|
<< overlapEnd << ":" << b.m_idx << "] of signal '" << vp->prettyName()
|
||||||
|
<< sub << "' have multiple combinational drivers\n"
|
||||||
|
<< a.m_fileline->warnOther() << "... Location of first driver\n"
|
||||||
|
<< a.m_fileline->warnContextPrimary() << '\n'
|
||||||
|
<< b.m_fileline->warnOther() << "... Location of other driver\n"
|
||||||
|
<< b.m_fileline->warnContextSecondary() << vp->warnOther()
|
||||||
|
<< "... Only the first driver will be respected");
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the first driver completely covers the range of the second driver,
|
||||||
|
// we can just delete the second driver completely, otherwise adjust the
|
||||||
|
// second driver to apply from the end of the range of the first driver.
|
||||||
|
if (aEnd >= bEnd) {
|
||||||
|
prune.emplace(b.m_vtxp);
|
||||||
|
it = drivers.erase(it);
|
||||||
|
} else {
|
||||||
|
const auto distance = std::distance(drivers.begin(), it);
|
||||||
|
DfgVertex* const bVtxp = b.m_vtxp;
|
||||||
|
FileLine* const flp = b.m_vtxp->fileline();
|
||||||
|
AstNodeDType* const elemDtypep = DfgVertex::dtypeFor(
|
||||||
|
VN_AS(splicep->dtypep(), UnpackArrayDType)->subDTypep());
|
||||||
|
// Remove this driver
|
||||||
|
it = drivers.erase(it);
|
||||||
|
// Add missing items element-wise
|
||||||
|
for (uint32_t i = aEnd; i < bEnd; ++i) {
|
||||||
|
DfgArraySel* const aselp = new DfgArraySel{*m_dfgp, flp, elemDtypep};
|
||||||
|
aselp->fromp(bVtxp);
|
||||||
|
aselp->bitp(new DfgConst{*m_dfgp, flp, 32, i});
|
||||||
|
drivers.emplace_back(flp, i, aselp);
|
||||||
|
}
|
||||||
|
it = drivers.begin();
|
||||||
|
std::advance(it, distance);
|
||||||
|
std::stable_sort(it, drivers.end(), cmp);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reinsert drivers in order
|
||||||
|
splicep->resetSources();
|
||||||
|
for (const Driver& driver : drivers) {
|
||||||
|
if (!driver.m_vtxp) break; // Stop at end of compacted list
|
||||||
|
splicep->addDriver(driver.m_fileline, driver.m_idx, driver.m_vtxp);
|
||||||
|
}
|
||||||
|
|
||||||
|
removeUnused(prune);
|
||||||
|
|
||||||
|
// If the whole variable is driven whole, we can just use that driver
|
||||||
|
if (splicep->arity() == 1 //
|
||||||
|
&& splicep->driverIndex(0) == 0 //
|
||||||
|
&& splicep->source(0)->dtypep()->isSame(splicep->dtypep())) {
|
||||||
|
const auto result = std::make_pair(splicep->source(0), splicep->driverFileLine(0));
|
||||||
|
VL_DO_DANGLING(splicep->unlinkDelete(*m_dfgp), splicep);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return {splicep, splicep->fileline()};
|
||||||
}
|
}
|
||||||
|
|
||||||
// VISITORS
|
// VISITORS
|
||||||
|
@ -707,9 +891,32 @@ class AstToDfgVisitor final : public VNVisitor {
|
||||||
iterate(&root);
|
iterate(&root);
|
||||||
UASSERT_OBJ(m_uncommittedVertices.empty(), &root, "Uncommitted vertices remain");
|
UASSERT_OBJ(m_uncommittedVertices.empty(), &root, "Uncommitted vertices remain");
|
||||||
|
|
||||||
// Canonicalize variables
|
if (dumpDfgLevel() >= 8) m_dfgp->dumpDotFilePrefixed(ctx.prefix() + "ast2dfg");
|
||||||
canonicalizePacked();
|
|
||||||
canonicalizeArray();
|
// Normalize variable drivers (remove multiple drivers, remove unnecessary splice vertices)
|
||||||
|
for (DfgVertexVar* const varp : m_dfgp->varVertices().unlinkable()) {
|
||||||
|
// Delete variables with no sinks nor sources (this can happen due to reverting
|
||||||
|
// uncommitted vertices, which does not remove variables)
|
||||||
|
if (!varp->hasSinks() && !varp->srcp()) {
|
||||||
|
VL_DO_DANGLING(varp->unlinkDelete(*m_dfgp), varp);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nothing to do for un-driven (input) variables
|
||||||
|
if (!varp->srcp()) continue;
|
||||||
|
|
||||||
|
// The driver of a variable must always be a splice vertex, normalize it
|
||||||
|
std::pair<DfgVertex*, FileLine*> normalizedDriver;
|
||||||
|
if (DfgSpliceArray* const sArrayp = varp->srcp()->cast<DfgSpliceArray>()) {
|
||||||
|
normalizedDriver = normalizeArray(varp, "", sArrayp);
|
||||||
|
} else if (DfgSplicePacked* const sPackedp = varp->srcp()->cast<DfgSplicePacked>()) {
|
||||||
|
normalizedDriver = normalizePacked(varp, "", sPackedp);
|
||||||
|
} else {
|
||||||
|
varp->v3fatalSrc("Unhandled DfgSplicePacked sub-type"); // LCOV_EXCL_LINE
|
||||||
|
}
|
||||||
|
varp->srcp(normalizedDriver.first);
|
||||||
|
varp->driverFileLine(normalizedDriver.second);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -188,65 +188,53 @@ class DfgToAstVisitor final : DfgVisitor {
|
||||||
return resultp;
|
return resultp;
|
||||||
}
|
}
|
||||||
|
|
||||||
void addResultEquation(const DfgVertexVar* vtxp, FileLine* flp, AstNodeExpr* lhsp,
|
void convertDriver(AstScope* scopep, FileLine* flp, AstNodeExpr* lhsp, DfgVertex* driverp) {
|
||||||
AstNodeExpr* rhsp) {
|
if (!driverp->is<DfgVertexSplice>()) {
|
||||||
AstAssignW* const assignp = new AstAssignW{flp, lhsp, rhsp};
|
// Base case: assign vertex to current lhs
|
||||||
if VL_CONSTEXPR_CXX17 (T_Scoped) {
|
AstNodeExpr* const rhsp = convertDfgVertexToAstNodeExpr(driverp);
|
||||||
// Add it to the scope holding the target variable
|
AstAssignW* const assignp = new AstAssignW{flp, lhsp, rhsp};
|
||||||
getCombActive(vtxp->varScopep()->scopep())->addStmtsp(assignp);
|
lhsp->foreach([flp](AstNode* nodep) { nodep->fileline(flp); });
|
||||||
} else {
|
if VL_CONSTEXPR_CXX17 (T_Scoped) {
|
||||||
// Add it to the parend module of the DfgGraph
|
// Add it to the scope holding the target variable
|
||||||
m_modp->addStmtsp(assignp);
|
getCombActive(scopep)->addStmtsp(assignp);
|
||||||
|
} else {
|
||||||
|
// Add it to the parend module of the DfgGraph
|
||||||
|
m_modp->addStmtsp(assignp);
|
||||||
|
}
|
||||||
|
++m_ctx.m_resultEquations;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
++m_ctx.m_resultEquations;
|
|
||||||
}
|
|
||||||
|
|
||||||
void convertPackedDriver(const DfgVarPacked* dfgVarp) {
|
if (DfgSplicePacked* const sPackedp = driverp->cast<DfgSplicePacked>()) {
|
||||||
if (DfgSplicePacked* const splicep = dfgVarp->srcp()->cast<DfgSplicePacked>()) {
|
// Partial assignment of packed value
|
||||||
// Variable is driven partially. Render each driver as a separate assignment.
|
sPackedp->forEachSourceEdge([&](const DfgEdge& edge, size_t i) {
|
||||||
splicep->forEachSourceEdge([&](const DfgEdge& edge, size_t idx) {
|
UASSERT_OBJ(edge.sourcep(), sPackedp, "Should have removed undriven sources");
|
||||||
UASSERT_OBJ(edge.sourcep(), dfgVarp, "Should have removed undriven sources");
|
// Create Sel
|
||||||
// Render the rhs expression
|
FileLine* const dflp = sPackedp->driverFileLine(i);
|
||||||
AstNodeExpr* const rhsp = convertDfgVertexToAstNodeExpr(edge.sourcep());
|
AstConst* const lsbp = new AstConst{dflp, sPackedp->driverLsb(i)};
|
||||||
// Create select LValue
|
|
||||||
FileLine* const flp = splicep->driverFileLine(idx);
|
|
||||||
AstVarRef* const refp = new AstVarRef{flp, getNode(dfgVarp), VAccess::WRITE};
|
|
||||||
AstConst* const lsbp = new AstConst{flp, splicep->driverLsb(idx)};
|
|
||||||
const int width = static_cast<int>(edge.sourcep()->width());
|
const int width = static_cast<int>(edge.sourcep()->width());
|
||||||
AstSel* const lhsp = new AstSel{flp, refp, lsbp, width};
|
AstSel* const nLhsp = new AstSel{dflp, lhsp->cloneTreePure(false), lsbp, width};
|
||||||
// Add assignment of the value to the selected bits
|
// Convert source
|
||||||
addResultEquation(dfgVarp, flp, lhsp, rhsp);
|
convertDriver(scopep, dflp, nLhsp, edge.sourcep());
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Whole variable is driven. Render driver and assign directly to whole variable.
|
if (DfgSpliceArray* const sArrayp = driverp->cast<DfgSpliceArray>()) {
|
||||||
FileLine* const flp
|
// Partial assignment of array variable
|
||||||
= dfgVarp->driverFileLine() ? dfgVarp->driverFileLine() : dfgVarp->fileline();
|
sArrayp->forEachSourceEdge([&](const DfgEdge& edge, size_t i) {
|
||||||
AstVarRef* const lhsp = new AstVarRef{flp, getNode(dfgVarp), VAccess::WRITE};
|
UASSERT_OBJ(edge.sourcep(), sArrayp, "Should have removed undriven sources");
|
||||||
AstNodeExpr* const rhsp = convertDfgVertexToAstNodeExpr(dfgVarp->srcp());
|
// Create ArraySel
|
||||||
addResultEquation(dfgVarp, flp, lhsp, rhsp);
|
FileLine* const dflp = sArrayp->driverFileLine(i);
|
||||||
}
|
AstConst* const idxp = new AstConst{dflp, sArrayp->driverIndex(i)};
|
||||||
|
AstArraySel* const nLhsp = new AstArraySel{dflp, lhsp->cloneTreePure(false), idxp};
|
||||||
void convertArrayDiver(const DfgVarArray* dfgVarp) {
|
// Convert source
|
||||||
if (DfgSpliceArray* const splicep = dfgVarp->srcp()->cast<DfgSpliceArray>()) {
|
convertDriver(scopep, dflp, nLhsp, edge.sourcep());
|
||||||
// Variable is driven partially. Assign from parts of the canonical var.
|
|
||||||
splicep->forEachSourceEdge([&](const DfgEdge& edge, size_t idx) {
|
|
||||||
UASSERT_OBJ(edge.sourcep(), dfgVarp, "Should have removed undriven sources");
|
|
||||||
// Render the rhs expression
|
|
||||||
AstNodeExpr* const rhsp = convertDfgVertexToAstNodeExpr(edge.sourcep());
|
|
||||||
// Create select LValue
|
|
||||||
FileLine* const flp = splicep->driverFileLine(idx);
|
|
||||||
AstVarRef* const refp = new AstVarRef{flp, getNode(dfgVarp), VAccess::WRITE};
|
|
||||||
AstConst* const idxp = new AstConst{flp, splicep->driverIndex(idx)};
|
|
||||||
AstArraySel* const lhsp = new AstArraySel{flp, refp, idxp};
|
|
||||||
// Add assignment of the value to the selected bits
|
|
||||||
addResultEquation(dfgVarp, flp, lhsp, rhsp);
|
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
UASSERT_OBJ(false, dfgVarp, "Should not have wholly driven arrays in Dfg");
|
driverp->v3fatalSrc("Unhandled DfgVertexSplice sub-type"); // LCOV_EXCL_LINE
|
||||||
}
|
}
|
||||||
|
|
||||||
// VISITORS
|
// VISITORS
|
||||||
|
@ -294,14 +282,13 @@ class DfgToAstVisitor final : DfgVisitor {
|
||||||
// If there is no driver (this vertex is an input to the graph), then nothing to do.
|
// If there is no driver (this vertex is an input to the graph), then nothing to do.
|
||||||
if (!vtx.srcp()) continue;
|
if (!vtx.srcp()) continue;
|
||||||
|
|
||||||
// Render packed variable assignments
|
// Render variable assignments
|
||||||
if (const DfgVarPacked* const dfgVarp = vtx.cast<DfgVarPacked>()) {
|
FileLine* const flp = vtx.driverFileLine() ? vtx.driverFileLine() : vtx.fileline();
|
||||||
convertPackedDriver(dfgVarp);
|
AstScope* const scopep = T_Scoped ? vtx.varScopep()->scopep() : nullptr;
|
||||||
continue;
|
AstVarRef* const lhsp = new AstVarRef{flp, getNode(&vtx), VAccess::WRITE};
|
||||||
}
|
convertDriver(scopep, flp, lhsp, vtx.srcp());
|
||||||
|
// convetDriver clones and might not use up the original lhsp
|
||||||
// Render array variable assignments
|
if (!lhsp->backp()) VL_DO_DANGLING(lhsp->deleteTree(), lhsp);
|
||||||
convertArrayDiver(vtx.as<DfgVarArray>());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1202,13 +1202,14 @@ class V3DfgPeephole final : public DfgVisitor {
|
||||||
void visit(DfgArraySel* vtxp) override {
|
void visit(DfgArraySel* vtxp) override {
|
||||||
if (DfgConst* const idxp = vtxp->bitp()->cast<DfgConst>()) {
|
if (DfgConst* const idxp = vtxp->bitp()->cast<DfgConst>()) {
|
||||||
if (DfgVarArray* const varp = vtxp->fromp()->cast<DfgVarArray>()) {
|
if (DfgVarArray* const varp = vtxp->fromp()->cast<DfgVarArray>()) {
|
||||||
if (varp->srcp()) {
|
if (varp->srcp() && !varp->varp()->isForced() && !varp->varp()->isSc()) {
|
||||||
if (DfgSpliceArray* const splicep = varp->srcp()->cast<DfgSpliceArray>()) {
|
if (DfgSpliceArray* const splicep = varp->srcp()->cast<DfgSpliceArray>()) {
|
||||||
const size_t idx = idxp->toSizeT();
|
if (DfgVertex* const driverp = splicep->driverAt(idxp->toSizeT())) {
|
||||||
if (DfgVertex* const driverp = splicep->driverAt(idx)) {
|
if (!driverp->is<DfgVertexSplice>()) {
|
||||||
APPLYING(INLINE_ARRAYSEL) {
|
APPLYING(INLINE_ARRAYSEL) {
|
||||||
replace(vtxp, driverp);
|
replace(vtxp, driverp);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,7 +50,7 @@ class DfgRegularize final {
|
||||||
if (vtx.is<DfgVertexSplice>()) {
|
if (vtx.is<DfgVertexSplice>()) {
|
||||||
const bool hasNonVarSink
|
const bool hasNonVarSink
|
||||||
= vtx.findSink<DfgVertex>([](const DfgVertex& snk) { //
|
= vtx.findSink<DfgVertex>([](const DfgVertex& snk) { //
|
||||||
return !snk.is<DfgVertexVar>();
|
return !snk.is<DfgVertexVar>() && !snk.is<DfgVertexSplice>();
|
||||||
});
|
});
|
||||||
return hasNonVarSink;
|
return hasNonVarSink;
|
||||||
}
|
}
|
||||||
|
|
|
@ -229,7 +229,14 @@ class DfgSpliceArray final : public DfgVertexSplice {
|
||||||
friend class DfgVertex;
|
friend class DfgVertex;
|
||||||
friend class DfgVisitor;
|
friend class DfgVisitor;
|
||||||
|
|
||||||
using DriverData = std::pair<FileLine*, uint32_t>;
|
struct DriverData final {
|
||||||
|
FileLine* m_flp;
|
||||||
|
uint32_t m_index;
|
||||||
|
DriverData() = delete;
|
||||||
|
DriverData(FileLine* flp, uint32_t index)
|
||||||
|
: m_flp{flp}
|
||||||
|
, m_index{index} {}
|
||||||
|
};
|
||||||
|
|
||||||
std::vector<DriverData> m_driverData; // Additional data associated with each driver
|
std::vector<DriverData> m_driverData; // Additional data associated with each driver
|
||||||
|
|
||||||
|
@ -253,8 +260,8 @@ public:
|
||||||
DfgVertexVariadic::resetSources();
|
DfgVertexVariadic::resetSources();
|
||||||
}
|
}
|
||||||
|
|
||||||
FileLine* driverFileLine(size_t idx) const { return m_driverData[idx].first; }
|
FileLine* driverFileLine(size_t i) const { return m_driverData.at(i).m_flp; }
|
||||||
uint32_t driverIndex(size_t idx) const { return m_driverData[idx].second; }
|
uint32_t driverIndex(size_t i) const { return m_driverData.at(i).m_index; }
|
||||||
|
|
||||||
DfgVertex* driverAt(size_t idx) const {
|
DfgVertex* driverAt(size_t idx) const {
|
||||||
const DfgEdge* const edgep = findSourceEdge([this, idx](const DfgEdge&, size_t i) { //
|
const DfgEdge* const edgep = findSourceEdge([this, idx](const DfgEdge&, size_t i) { //
|
||||||
|
@ -271,8 +278,14 @@ class DfgSplicePacked final : public DfgVertexSplice {
|
||||||
friend class DfgVertex;
|
friend class DfgVertex;
|
||||||
friend class DfgVisitor;
|
friend class DfgVisitor;
|
||||||
|
|
||||||
using DriverData = std::pair<FileLine*, uint32_t>;
|
struct DriverData final {
|
||||||
|
FileLine* m_flp;
|
||||||
|
uint32_t m_lsb;
|
||||||
|
DriverData() = delete;
|
||||||
|
DriverData(FileLine* flp, uint32_t lsb)
|
||||||
|
: m_flp{flp}
|
||||||
|
, m_lsb{lsb} {}
|
||||||
|
};
|
||||||
std::vector<DriverData> m_driverData; // Additional data associated with each driver
|
std::vector<DriverData> m_driverData; // Additional data associated with each driver
|
||||||
|
|
||||||
bool selfEquals(const DfgVertex& that) const override VL_MT_DISABLED;
|
bool selfEquals(const DfgVertex& that) const override VL_MT_DISABLED;
|
||||||
|
@ -295,8 +308,8 @@ public:
|
||||||
DfgVertexVariadic::resetSources();
|
DfgVertexVariadic::resetSources();
|
||||||
}
|
}
|
||||||
|
|
||||||
FileLine* driverFileLine(size_t idx) const { return m_driverData[idx].first; }
|
FileLine* driverFileLine(size_t i) const { return m_driverData.at(i).m_flp; }
|
||||||
uint32_t driverLsb(size_t idx) const { return m_driverData[idx].second; }
|
uint32_t driverLsb(size_t i) const { return m_driverData.at(i).m_lsb; }
|
||||||
|
|
||||||
const std::string srcName(size_t idx) const override { return std::to_string(driverLsb(idx)); }
|
const std::string srcName(size_t idx) const override { return std::to_string(driverLsb(idx)); }
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,39 +1,75 @@
|
||||||
%Warning-MULTIDRIVEN: t/t_dfg_multidriver_dfg_bad.v:13:18: Bits [3:1] of signal 'a' have multiple combinational drivers
|
%Warning-MULTIDRIVEN: t/t_dfg_multidriver_dfg_bad.v:16:18: Bits [3:1] of signal 'a' have multiple combinational drivers
|
||||||
: ... note: In instance 't'
|
|
||||||
t/t_dfg_multidriver_dfg_bad.v:14:19: ... Location of first driver
|
|
||||||
14 | assign a[3:0] = i[3:0];
|
|
||||||
| ^
|
|
||||||
t/t_dfg_multidriver_dfg_bad.v:15:19: ... Location of other driver
|
|
||||||
15 | assign a[4:1] = ~i[4:1];
|
|
||||||
| ^
|
|
||||||
t/t_dfg_multidriver_dfg_bad.v:13:18: ... Only the first driver will be respected
|
|
||||||
... For warning description see https://verilator.org/warn/MULTIDRIVEN?v=latest
|
|
||||||
... Use "/* verilator lint_off MULTIDRIVEN */" and lint_on around source to disable this message.
|
|
||||||
%Warning-MULTIDRIVEN: t/t_dfg_multidriver_dfg_bad.v:13:18: Bits [3:3] of signal 'a' have multiple combinational drivers
|
|
||||||
: ... note: In instance 't'
|
|
||||||
t/t_dfg_multidriver_dfg_bad.v:14:19: ... Location of first driver
|
|
||||||
14 | assign a[3:0] = i[3:0];
|
|
||||||
| ^
|
|
||||||
t/t_dfg_multidriver_dfg_bad.v:16:17: ... Location of other driver
|
|
||||||
16 | assign a[3] = ~i[3];
|
|
||||||
| ^
|
|
||||||
t/t_dfg_multidriver_dfg_bad.v:13:18: ... Only the first driver will be respected
|
|
||||||
%Warning-MULTIDRIVEN: t/t_dfg_multidriver_dfg_bad.v:13:18: Bits [7:6] of signal 'a' have multiple combinational drivers
|
|
||||||
: ... note: In instance 't'
|
: ... note: In instance 't'
|
||||||
t/t_dfg_multidriver_dfg_bad.v:17:19: ... Location of first driver
|
t/t_dfg_multidriver_dfg_bad.v:17:19: ... Location of first driver
|
||||||
17 | assign a[8:5] = i[8:5];
|
17 | assign a[3:0] = i[3:0];
|
||||||
| ^
|
| ^
|
||||||
t/t_dfg_multidriver_dfg_bad.v:18:19: ... Location of other driver
|
t/t_dfg_multidriver_dfg_bad.v:18:19: ... Location of other driver
|
||||||
18 | assign a[7:6] = ~i[7:6];
|
18 | assign a[4:1] = ~i[4:1];
|
||||||
| ^
|
| ^
|
||||||
t/t_dfg_multidriver_dfg_bad.v:13:18: ... Only the first driver will be respected
|
t/t_dfg_multidriver_dfg_bad.v:16:18: ... Only the first driver will be respected
|
||||||
%Warning-MULTIDRIVEN: t/t_dfg_multidriver_dfg_bad.v:13:18: Bits [9:9] of signal 'a' have multiple combinational drivers
|
... For warning description see https://verilator.org/warn/MULTIDRIVEN?v=latest
|
||||||
|
... Use "/* verilator lint_off MULTIDRIVEN */" and lint_on around source to disable this message.
|
||||||
|
%Warning-MULTIDRIVEN: t/t_dfg_multidriver_dfg_bad.v:16:18: Bits [3:3] of signal 'a' have multiple combinational drivers
|
||||||
: ... note: In instance 't'
|
: ... note: In instance 't'
|
||||||
t/t_dfg_multidriver_dfg_bad.v:19:17: ... Location of first driver
|
t/t_dfg_multidriver_dfg_bad.v:17:19: ... Location of first driver
|
||||||
19 | assign a[9] = i[9];
|
17 | assign a[3:0] = i[3:0];
|
||||||
| ^
|
|
||||||
t/t_dfg_multidriver_dfg_bad.v:20:19: ... Location of other driver
|
|
||||||
20 | assign a[9] = ~i[9];
|
|
||||||
| ^
|
| ^
|
||||||
t/t_dfg_multidriver_dfg_bad.v:13:18: ... Only the first driver will be respected
|
t/t_dfg_multidriver_dfg_bad.v:19:17: ... Location of other driver
|
||||||
|
19 | assign a[3] = ~i[3];
|
||||||
|
| ^
|
||||||
|
t/t_dfg_multidriver_dfg_bad.v:16:18: ... Only the first driver will be respected
|
||||||
|
%Warning-MULTIDRIVEN: t/t_dfg_multidriver_dfg_bad.v:16:18: Bits [7:6] of signal 'a' have multiple combinational drivers
|
||||||
|
: ... note: In instance 't'
|
||||||
|
t/t_dfg_multidriver_dfg_bad.v:20:19: ... Location of first driver
|
||||||
|
20 | assign a[8:5] = i[8:5];
|
||||||
|
| ^
|
||||||
|
t/t_dfg_multidriver_dfg_bad.v:21:19: ... Location of other driver
|
||||||
|
21 | assign a[7:6] = ~i[7:6];
|
||||||
|
| ^
|
||||||
|
t/t_dfg_multidriver_dfg_bad.v:16:18: ... Only the first driver will be respected
|
||||||
|
%Warning-MULTIDRIVEN: t/t_dfg_multidriver_dfg_bad.v:16:18: Bits [9:9] of signal 'a' have multiple combinational drivers
|
||||||
|
: ... note: In instance 't'
|
||||||
|
t/t_dfg_multidriver_dfg_bad.v:22:17: ... Location of first driver
|
||||||
|
22 | assign a[9] = i[9];
|
||||||
|
| ^
|
||||||
|
t/t_dfg_multidriver_dfg_bad.v:23:19: ... Location of other driver
|
||||||
|
23 | assign a[9] = ~i[9];
|
||||||
|
| ^
|
||||||
|
t/t_dfg_multidriver_dfg_bad.v:16:18: ... Only the first driver will be respected
|
||||||
|
%Warning-MULTIDRIVEN: t/t_dfg_multidriver_dfg_bad.v:26:18: Elements [3:0] of signal 'u' have multiple combinational drivers
|
||||||
|
: ... note: In instance 't'
|
||||||
|
t/t_dfg_multidriver_dfg_bad.v:27:14: ... Location of first driver
|
||||||
|
27 | assign u = j;
|
||||||
|
| ^
|
||||||
|
t/t_dfg_multidriver_dfg_bad.v:28:14: ... Location of other driver
|
||||||
|
28 | assign u = k;
|
||||||
|
| ^
|
||||||
|
t/t_dfg_multidriver_dfg_bad.v:26:18: ... Only the first driver will be respected
|
||||||
|
%Warning-MULTIDRIVEN: t/t_dfg_multidriver_dfg_bad.v:30:18: Elements [1:1] of signal 'v' have multiple combinational drivers
|
||||||
|
: ... note: In instance 't'
|
||||||
|
t/t_dfg_multidriver_dfg_bad.v:31:14: ... Location of first driver
|
||||||
|
31 | assign v = j;
|
||||||
|
| ^
|
||||||
|
t/t_dfg_multidriver_dfg_bad.v:32:17: ... Location of other driver
|
||||||
|
32 | assign v[1] = i;
|
||||||
|
| ^
|
||||||
|
t/t_dfg_multidriver_dfg_bad.v:30:18: ... Only the first driver will be respected
|
||||||
|
%Warning-MULTIDRIVEN: t/t_dfg_multidriver_dfg_bad.v:34:18: Elements [0:0] of signal 'w' have multiple combinational drivers
|
||||||
|
: ... note: In instance 't'
|
||||||
|
t/t_dfg_multidriver_dfg_bad.v:35:17: ... Location of first driver
|
||||||
|
35 | assign w[0] = i;
|
||||||
|
| ^
|
||||||
|
t/t_dfg_multidriver_dfg_bad.v:36:14: ... Location of other driver
|
||||||
|
36 | assign w = j;
|
||||||
|
| ^
|
||||||
|
t/t_dfg_multidriver_dfg_bad.v:34:18: ... Only the first driver will be respected
|
||||||
|
%Warning-MULTIDRIVEN: t/t_dfg_multidriver_dfg_bad.v:38:18: Bits [3:2] of signal 'x[3]' have multiple combinational drivers
|
||||||
|
: ... note: In instance 't'
|
||||||
|
t/t_dfg_multidriver_dfg_bad.v:39:17: ... Location of first driver
|
||||||
|
39 | assign x[3] = i;
|
||||||
|
| ^
|
||||||
|
t/t_dfg_multidriver_dfg_bad.v:40:22: ... Location of other driver
|
||||||
|
40 | assign x[3][3:2] = ~i[1:0];
|
||||||
|
| ^
|
||||||
|
t/t_dfg_multidriver_dfg_bad.v:38:18: ... Only the first driver will be respected
|
||||||
%Error: Exiting due to
|
%Error: Exiting due to
|
||||||
|
|
|
@ -7,7 +7,10 @@
|
||||||
`default_nettype none
|
`default_nettype none
|
||||||
|
|
||||||
module t(
|
module t(
|
||||||
input wire [10:0] i,
|
input wire [10:0] i,
|
||||||
|
input wire [10:0] j [4],
|
||||||
|
input wire [10:0] k [4],
|
||||||
|
|
||||||
output wire [10:0] o
|
output wire [10:0] o
|
||||||
);
|
);
|
||||||
logic [10:0] a;
|
logic [10:0] a;
|
||||||
|
@ -19,5 +22,26 @@ module t(
|
||||||
assign a[9] = i[9];
|
assign a[9] = i[9];
|
||||||
assign a[9] = ~i[9];
|
assign a[9] = ~i[9];
|
||||||
assign a[10] = i[10];
|
assign a[10] = i[10];
|
||||||
assign o = a;
|
|
||||||
|
logic [10:0] u [4];
|
||||||
|
assign u = j;
|
||||||
|
assign u = k;
|
||||||
|
|
||||||
|
logic [10:0] v [4];
|
||||||
|
assign v = j;
|
||||||
|
assign v[1] = i;
|
||||||
|
|
||||||
|
logic [10:0] w [4];
|
||||||
|
assign w[0] = i;
|
||||||
|
assign w = j;
|
||||||
|
|
||||||
|
logic [10:0] x [4];
|
||||||
|
assign x[3] = i;
|
||||||
|
assign x[3][3:2] = ~i[1:0];
|
||||||
|
// No warning for w[2]!
|
||||||
|
assign x[2][3:2] = ~i[1:0];
|
||||||
|
assign x[2][1:0] = ~i[1:0];
|
||||||
|
|
||||||
|
assign o = a ^ u[3] ^ v[3] ^ w[3] ^ x[3];
|
||||||
|
|
||||||
endmodule
|
endmodule
|
||||||
|
|
|
@ -29,9 +29,13 @@ module t (
|
||||||
wire logic [63:0] const_b;
|
wire logic [63:0] const_b;
|
||||||
wire logic signed [63:0] sconst_a;
|
wire logic signed [63:0] sconst_a;
|
||||||
wire logic signed [63:0] sconst_b;
|
wire logic signed [63:0] sconst_b;
|
||||||
wire logic [63:0] array [3:0];
|
logic [63:0] array [3:0];
|
||||||
assign array[0] = (rand_a << 32) | (rand_a >> 32);
|
assign array[0] = (rand_a << 32) | (rand_a >> 32);
|
||||||
assign array[1] = (rand_a << 16) | (rand_a >> 48);
|
assign array[1] = (rand_a << 16) | (rand_a >> 48);
|
||||||
|
assign array[2][3:0] = rand_a[3:0];
|
||||||
|
always @(rand_b) begin // Intentional non-combinational partial driver
|
||||||
|
array[2][7:4] = rand_a[7:4];
|
||||||
|
end
|
||||||
|
|
||||||
`signal(FOLD_UNARY_CLog2, $clog2(const_a));
|
`signal(FOLD_UNARY_CLog2, $clog2(const_a));
|
||||||
`signal(FOLD_UNARY_CountOnes, $countones(const_a));
|
`signal(FOLD_UNARY_CountOnes, $countones(const_a));
|
||||||
|
@ -184,6 +188,7 @@ module t (
|
||||||
`signal(REPLACE_COND_WITH_ELSE_BRANCH_ZERO, rand_a[0] ? rand_a[1] : 1'd0);
|
`signal(REPLACE_COND_WITH_ELSE_BRANCH_ZERO, rand_a[0] ? rand_a[1] : 1'd0);
|
||||||
`signal(REPLACE_COND_WITH_ELSE_BRANCH_ONES, rand_a[0] ? rand_a[1] : 1'd1);
|
`signal(REPLACE_COND_WITH_ELSE_BRANCH_ONES, rand_a[0] ? rand_a[1] : 1'd1);
|
||||||
`signal(INLINE_ARRAYSEL, array[0]);
|
`signal(INLINE_ARRAYSEL, array[0]);
|
||||||
|
`signal(NO_INLINE_ARRAYSEL_PARTIAL, array[2]);
|
||||||
`signal(PUSH_BITWISE_THROUGH_REDUCTION_AND, (&(rand_a + 64'd105)) & (&(rand_b + 64'd108)));
|
`signal(PUSH_BITWISE_THROUGH_REDUCTION_AND, (&(rand_a + 64'd105)) & (&(rand_b + 64'd108)));
|
||||||
`signal(PUSH_BITWISE_THROUGH_REDUCTION_OR, (|(rand_a + 64'd106)) | (|(rand_b + 64'd109)));
|
`signal(PUSH_BITWISE_THROUGH_REDUCTION_OR, (|(rand_a + 64'd106)) | (|(rand_b + 64'd109)));
|
||||||
`signal(PUSH_BITWISE_THROUGH_REDUCTION_XOR, (^(rand_a + 64'd107)) ^ (^(rand_b + 64'd110)));
|
`signal(PUSH_BITWISE_THROUGH_REDUCTION_XOR, (^(rand_a + 64'd107)) ^ (^(rand_b + 64'd110)));
|
||||||
|
|
|
@ -10,7 +10,9 @@ module t (/*AUTOARG*/
|
||||||
);
|
);
|
||||||
input clk;
|
input clk;
|
||||||
|
|
||||||
|
// verilator lint_off MULTIDRIVEN
|
||||||
wire [19:10] bitout;
|
wire [19:10] bitout;
|
||||||
|
// verilator lint_on MULTIDRIVEN
|
||||||
wire [29:24] short_bitout;
|
wire [29:24] short_bitout;
|
||||||
wire [7:0] allbits;
|
wire [7:0] allbits;
|
||||||
wire [15:0] twobits;
|
wire [15:0] twobits;
|
||||||
|
|
Loading…
Reference in New Issue