mirror of
https://github.com/verilator/verilator.git
synced 2026-10-06 01:54:01 +02:00
Optimize complex combinational logic in DFG (#6298)
This patch adds DfgLogic, which is a vertex that represents a whole, arbitrarily complex combinational AstAlways or AstAssignW in the DfgGraph. Implementing this requires computing the variables live at entry to the AstAlways (variables read by the block), so there is a new ControlFlowGraph data structure and a classical data-flow analysis based live variable analysis to do that at the variable level (as opposed to bit/element level). The actual CFG construction and live variable analysis is best effort, and might fail for currently unhandled constructs or data types. This can be extended later. V3DfgAstToDfg is changed to convert the Ast into an initial DfgGraph containing only DfgLogic, DfgVertexSplice and DfgVertexVar vertices. The DfgLogic are then subsequently synthesized into primitive operations by the new V3DfgSynthesize pass, which is a combination of the old V3DfgAstToDfg conversion and new code to handle AstAlways blocks with complex flow control. V3DfgSynthesize by default will synthesize roughly the same constructs as V3DfgAstToDfg used to handle before, plus any logic that is part of a combinational cycle within the DfgGraph. This enables breaking up these cycles, for which there are extensions to V3DfgBreakCycles in this patch as well. V3DfgSynthesize will then delete all non synthesized or non synthesizable DfgLogic vertices and the rest of the Dfg pipeline is identical, with minor changes to adjust for the changed representation. Because with this change we can now eliminate many more UNOPTFLAT, DFG has been disabled in all the tests that specifically target testing the scheduling and reporting of circular combinational logic.
This commit is contained in:
+35
-6
@@ -103,7 +103,7 @@ void V3DfgPasses::removeUnused(DfgGraph& dfg) {
|
||||
DfgVertex* const sentinelp = reinterpret_cast<DfgVertex*>(&dfg);
|
||||
DfgVertex* workListp = sentinelp;
|
||||
|
||||
// Add all unused vertices to the work list. This also allocates all DfgVertex::user.
|
||||
// Add all unused operation vertices to the work list. This also allocates all DfgVertex::user.
|
||||
for (DfgVertex& vtx : dfg.opVertices()) {
|
||||
if (vtx.hasSinks()) {
|
||||
// This vertex is used. Allocate user, but don't add to work list.
|
||||
@@ -115,6 +115,19 @@ void V3DfgPasses::removeUnused(DfgGraph& dfg) {
|
||||
}
|
||||
}
|
||||
|
||||
// Also add all unused temporaries created during synthesis
|
||||
for (DfgVertexVar& vtx : dfg.varVertices()) {
|
||||
if (!vtx.tmpForp()) continue;
|
||||
if (vtx.hasSinks() || vtx.hasDfgRefs()) {
|
||||
// This vertex is used. Allocate user, but don't add to work list.
|
||||
vtx.setUser<DfgVertex*>(nullptr);
|
||||
} else {
|
||||
// This vertex is unused. Add to work list.
|
||||
vtx.setUser<DfgVertex*>(workListp);
|
||||
workListp = &vtx;
|
||||
}
|
||||
}
|
||||
|
||||
// Process the work list
|
||||
while (workListp != sentinelp) {
|
||||
// Pick up the head
|
||||
@@ -123,12 +136,23 @@ void V3DfgPasses::removeUnused(DfgGraph& dfg) {
|
||||
workListp = vtxp->getUser<DfgVertex*>();
|
||||
// Prefetch next item
|
||||
VL_PREFETCH_RW(workListp);
|
||||
// This item is now off the work list
|
||||
vtxp->setUser<DfgVertex*>(nullptr);
|
||||
// DfgLogic should have been synthesized or removed
|
||||
UASSERT_OBJ(!vtxp->is<DfgLogic>(), vtxp, "Should not be DfgLogic");
|
||||
// If used, then nothing to do, so move on
|
||||
if (vtxp->hasSinks()) continue;
|
||||
// If temporary used in another graph, we need to keep it
|
||||
if (const DfgVertexVar* const varp = vtxp->cast<DfgVertexVar>()) {
|
||||
UASSERT_OBJ(varp->tmpForp(), varp, "Non-temporary variable should not be visited");
|
||||
if (varp->hasDfgRefs()) continue;
|
||||
}
|
||||
// Add sources of unused vertex to work list
|
||||
vtxp->forEachSource([&](DfgVertex& src) {
|
||||
// We only remove actual operation vertices in this loop
|
||||
if (src.is<DfgConst>() || src.is<DfgVertexVar>()) return;
|
||||
// We only remove actual operation vertices and synthesis temporaries in this loop
|
||||
if (src.is<DfgConst>()) return;
|
||||
const DfgVertexVar* const varp = src.cast<DfgVertexVar>();
|
||||
if (varp && !varp->tmpForp()) return;
|
||||
// If already in work list then nothing to do
|
||||
if (src.getUser<DfgVertex*>()) return;
|
||||
// Actually add to work list.
|
||||
@@ -282,7 +306,7 @@ void V3DfgPasses::binToOneHot(DfgGraph& dfg, V3DfgBinToOneHotContext& ctx) {
|
||||
|
||||
// Required data types
|
||||
AstNodeDType* const idxDTypep = srcp->dtypep();
|
||||
AstNodeDType* const bitDTypep = DfgVertex::dtypeForWidth(1);
|
||||
AstNodeDType* const bitDTypep = DfgGraph::dtypePacked(1);
|
||||
AstUnpackArrayDType* const tabDTypep = new AstUnpackArrayDType{
|
||||
flp, bitDTypep, new AstRange{flp, static_cast<int>(nBits - 1), 0}};
|
||||
v3Global.rootp()->typeTablep()->addTypesp(tabDTypep);
|
||||
@@ -453,8 +477,13 @@ void V3DfgPasses::eliminateVars(DfgGraph& dfg, V3DfgEliminateVarsContext& ctx) {
|
||||
DfgVarPacked* const varp = vtxp->cast<DfgVarPacked>();
|
||||
if (!varp) continue;
|
||||
|
||||
// Can't remove if it has external drivers
|
||||
if (!varp->isDrivenFullyByDfg()) continue;
|
||||
if (!varp->tmpForp()) {
|
||||
// Can't remove regular variable if it has external drivers
|
||||
if (!varp->isDrivenFullyByDfg()) continue;
|
||||
} else {
|
||||
// Can't remove partially driven used temporaries
|
||||
if (!varp->isDrivenFullyByDfg() && varp->hasSinks()) continue;
|
||||
}
|
||||
|
||||
// Can't remove if referenced external to the module/netlist
|
||||
if (varp->hasExtRefs()) continue;
|
||||
|
||||
Reference in New Issue
Block a user