diff --git a/src/V3Dfg.cpp b/src/V3Dfg.cpp index d51250bc3..273d1dcff 100644 --- a/src/V3Dfg.cpp +++ b/src/V3Dfg.cpp @@ -91,6 +91,11 @@ std::unique_ptr DfgGraph::clone() const { for (const DfgVertex& vtx : m_opVertices) { switch (vtx.type()) { #include "V3Dfg__gen_clone_cases.h" // From ./astgen + case VDfgType::CReset: { // LCOV_EXCL_START - No algorithm actually hits this today + DfgCReset* const cp = new DfgCReset{*clonep, vtx.fileline(), vtx.dtype()}; + vtxp2clonep.emplace(&vtx, cp); + break; + } // LCOV_EXCL_STOP case VDfgType::Sel: { DfgSel* const cp = new DfgSel{*clonep, vtx.fileline(), vtx.dtype()}; cp->lsb(vtx.as()->lsb()); @@ -132,11 +137,14 @@ std::unique_ptr DfgGraph::clone() const { VL_UNREACHABLE; break; } - default: { - vtx.v3fatalSrc("Unhandled operation vertex type: " + vtx.typeName()); + case VDfgType::AstRd: // LCOV_EXCL_START + case VDfgType::Const: + case VDfgType::VarArray: + case VDfgType::VarPacked: { + vtx.v3fatalSrc("Vertex should have been handled above: " + vtx.typeName()); VL_UNREACHABLE; break; - } + } // LCOV_EXCL_STOP } } UASSERT(size() == clonep->size(), "Size of clone should be the same"); @@ -618,6 +626,10 @@ void DfgVertex::typeCheck(const DfgGraph& dfg) const { CHECK(isPacked(), "Should be Packed type"); return; } + case VDfgType::CReset: { + CHECK(isPacked() || isArray(), "Should be Packed or Array type"); + return; + } case VDfgType::AstRd: { const DfgAstRd& v = *as(); CHECK(v.isPacked() || v.isArray(), "Should be Packed or Array type"); diff --git a/src/V3DfgColorSCCs.cpp b/src/V3DfgColorSCCs.cpp index 6bdce8ffb..1986a8775 100644 --- a/src/V3DfgColorSCCs.cpp +++ b/src/V3DfgColorSCCs.cpp @@ -125,6 +125,12 @@ class ColorStronglyConnectedComponents final { // Initialize state of operation vertices for (const DfgVertex& vtx : m_dfg.opVertices()) { + // If it has no inputs or no outputs, it cannot be part of a non-trivial SCC. + if (!vtx.nInputs() || !vtx.hasSinks()) { + index(vtx) = 0; + component(vtx) = 0; + continue; + } index(vtx) = UNASSIGNED; component(vtx) = UNASSIGNED; } diff --git a/src/V3DfgCse.cpp b/src/V3DfgCse.cpp index 40aa85190..7a60b4be7 100644 --- a/src/V3DfgCse.cpp +++ b/src/V3DfgCse.cpp @@ -52,6 +52,7 @@ class V3DfgCse final { // Special vertices case VDfgType::Const: // LCOV_EXCL_START + case VDfgType::CReset: case VDfgType::VarArray: case VDfgType::VarPacked: case VDfgType::AstRd: // LCOV_EXCL_STOP @@ -170,6 +171,7 @@ class V3DfgCse final { // Special vertices case VDfgType::Const: return a.as()->num().isCaseEq(b.as()->num()); + case VDfgType::CReset: return false; case VDfgType::VarArray: case VDfgType::VarPacked: // CSE does not combine variables @@ -303,6 +305,10 @@ class V3DfgCse final { for (const DfgVertexVar& vtx : dfg.varVertices()) m_hashCache[vtx] = V3Hash{++varHash}; // Pre-hash Ast references, these are all unique like variables for (const DfgVertexAst& vtx : dfg.astVertices()) m_hashCache[vtx] = V3Hash{++varHash}; + // Pre-hash CReset vertices, these are all unique + for (const DfgVertex& vtx : dfg.opVertices()) { + if (vtx.is()) m_hashCache[vtx] = V3Hash{++varHash}; + } // Similarly pre-hash constants for speed. While we don't combine constants, we do want // expressions using the same constants to be combined, so we do need to hash equal diff --git a/src/V3DfgDfgToAst.cpp b/src/V3DfgDfgToAst.cpp index 6da7bfc8d..ca349ccb3 100644 --- a/src/V3DfgDfgToAst.cpp +++ b/src/V3DfgDfgToAst.cpp @@ -227,6 +227,13 @@ class DfgToAstVisitor final : DfgVisitor { void visit(DfgConst* vtxp) override { // m_resultp = new AstConst{vtxp->fileline(), vtxp->num()}; } + void visit(DfgCReset* vtxp) override { + DfgVertex* const sinkp = vtxp->singleSink(); + UASSERT_OBJ(sinkp, vtxp, "CReset should only have one sink"); + UASSERT_OBJ(sinkp->is(), sinkp, "CReset should drive a variable"); + AstVar* const varp = sinkp->as()->vscp()->varp(); + m_resultp = new AstCReset{vtxp->fileline(), varp, false}; + } void visit(DfgRep* vtxp) override { FileLine* const flp = vtxp->fileline(); diff --git a/src/V3DfgPasses.cpp b/src/V3DfgPasses.cpp index eaad8872e..3e14397c6 100644 --- a/src/V3DfgPasses.cpp +++ b/src/V3DfgPasses.cpp @@ -120,6 +120,8 @@ void V3DfgPasses::inlineVars(DfgGraph& dfg) { // Partial driver cannot be inlined if (srcp->is()) continue; if (srcp->is()) continue; + // Don't inline CReset + if (srcp->is()) continue; // Okie dokie, here we go ... vtx.replaceWith(srcp); } diff --git a/src/V3DfgPeephole.cpp b/src/V3DfgPeephole.cpp index 8181dcfab..f89285eb7 100644 --- a/src/V3DfgPeephole.cpp +++ b/src/V3DfgPeephole.cpp @@ -1158,7 +1158,7 @@ class V3DfgPeephole final : public DfgVisitor { // Sel from a partial variable (including narrowed vertex) if (DfgVarPacked* const varp = fromp->cast()) { - if (varp->srcp() && !varp->isVolatile()) { + if (varp->srcp() && !varp->isVolatile() && !varp->srcp()->is()) { // Must be a splice, otherwise it would have been inlined DfgSplicePacked* splicep = varp->srcp()->as(); DfgVertex* driverp = nullptr; diff --git a/src/V3DfgPushDownSels.cpp b/src/V3DfgPushDownSels.cpp index 658be1b34..71b2bfaca 100644 --- a/src/V3DfgPushDownSels.cpp +++ b/src/V3DfgPushDownSels.cpp @@ -89,6 +89,9 @@ class V3DfgPushDownSels final { m_stack.push_back(&vtx); } for (DfgConst& vtx : m_dfg.constVertices()) m_stack.push_back(&vtx); + for (DfgVertex& vtx : m_dfg.opVertices()) { + if (!vtx.nInputs()) m_stack.push_back(&vtx); + } // Reverse post order number to assign to next vertex uint32_t rpoNext = m_dfg.size(); diff --git a/src/V3DfgRegularize.cpp b/src/V3DfgRegularize.cpp index 588dbd52c..f8e7b7935 100644 --- a/src/V3DfgRegularize.cpp +++ b/src/V3DfgRegularize.cpp @@ -100,6 +100,9 @@ class DfgRegularize final { // No need to add a temporary if the single sink is a variable already if (sink.is()) return false; + // CReset always needs to be driving a variable + if (aVtx.is()) return true; + // Do not inline expressions into a loop body if (const DfgAstRd* const astRdp = sink.cast()) { return astRdp->inLoop(); } diff --git a/src/V3DfgSynthesize.cpp b/src/V3DfgSynthesize.cpp index 827bede0e..706c9c0c9 100644 --- a/src/V3DfgSynthesize.cpp +++ b/src/V3DfgSynthesize.cpp @@ -384,6 +384,24 @@ class AstToDfgConverter final : public VNVisitor { nodep->user2p(vtxp); } } + void visit(AstCReset* nodep) override { + UASSERT_OBJ(m_converting, nodep, "AstToDfg visit called without m_converting"); + UASSERT_OBJ(!nodep->user2p(), nodep, "Already has Dfg vertex"); + if (unhandled(nodep)) return; + + const DfgDataType* const dtypep = DfgDataType::fromAst(nodep->dtypep()); + if (!dtypep) { + m_foundUnhandled = true; + ++m_ctx.m_conv.nonRepDType; + return; + } + + UASSERT_OBJ(!nodep->constructing(), nodep, + "CReset should be non-constructing at this stage"); + + DfgVertex* const vtxp = make(nodep->fileline(), *dtypep); + nodep->user2p(vtxp); + } void visit(AstReplicate* nodep) override { UASSERT_OBJ(m_converting, nodep, "AstToDfg visit called without m_converting"); UASSERT_OBJ(!nodep->user2p(), nodep, "Already has Dfg vertex"); diff --git a/src/V3DfgVertices.h b/src/V3DfgVertices.h index 8ab12c214..a3d3adf8e 100644 --- a/src/V3DfgVertices.h +++ b/src/V3DfgVertices.h @@ -248,6 +248,14 @@ public: } }; +class DfgCReset final : public DfgVertexNullary { +public: + DfgCReset(DfgGraph& dfg, FileLine* flp, const DfgDataType& dtype) + : DfgVertexNullary{dfg, dfgType(), flp, dtype} {} + + ASTGEN_MEMBERS_DfgCReset; +}; + //------------------------------------------------------------------------------ // Unary vertices - 1 inputs diff --git a/test_regress/t/t_dfg_synthesis.v b/test_regress/t/t_dfg_synthesis.v index 8bcb19f55..9076b4101 100644 --- a/test_regress/t/t_dfg_synthesis.v +++ b/test_regress/t/t_dfg_synthesis.v @@ -570,4 +570,11 @@ module t ( wire logic [7:0] func_3 = pkg::branchy(rand_a[7:0], rand_b[7:0]); `signal(FUNC_3, func_3); + logic [1:0] via_creset; + always_comb begin + automatic logic [1:0] a /* = AstCReset */; + via_creset = a + 2'd1; + end + `signal(VIA_CRESET, via_creset); + endmodule