diff --git a/src/V3EmitV.cpp b/src/V3EmitV.cpp index b3d31bcdc..a4f34de38 100644 --- a/src/V3EmitV.cpp +++ b/src/V3EmitV.cpp @@ -614,6 +614,7 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst { putfs(nodep, "$_EXPRSTMT(\n"); iterateAndNextConstNull(nodep->stmtsp()); putbs(", "); + iterateAndNextConstNull(nodep->resultp()); puts(");\n"); } diff --git a/src/V3SchedAcyclic.cpp b/src/V3SchedAcyclic.cpp index a1ff79556..c91f97b83 100644 --- a/src/V3SchedAcyclic.cpp +++ b/src/V3SchedAcyclic.cpp @@ -35,14 +35,17 @@ #include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT +#include "V3EmitV.h" +#include "V3File.h" #include "V3Graph.h" #include "V3Sched.h" #include "V3SenTree.h" #include "V3SplitVar.h" #include "V3Stats.h" -#include +#include #include +#include #include #include @@ -222,13 +225,31 @@ bool isCut(const SchedAcyclicVarVertex* vtxp) { } std::vector findCutVertices(Graph* graphp) { + // List of cut vertices being computed here std::vector result; const VNUser1InUse user1InUse; // bool: already added to result + + // Statistics + size_t nCyclicVtxs = 0; // Number of vertices that are part of an SCC (cycle) + size_t nCyclicVars = 0; // Number of variables that are part of an SCC (cycle) + std::unordered_set sccs; // Unique SCC colors + for (V3GraphVertex& vtx : graphp->vertices()) { + if (!vtx.color()) continue; // Not part of an SCC (cycle), can ignore + ++nCyclicVtxs; if (SchedAcyclicVarVertex* const vvtxp = vtx.cast()) { + ++nCyclicVars; if (!vvtxp->vscp()->user1SetOnce() && isCut(vvtxp)) result.push_back(vvtxp); } + // Don't bother counting if not dumping statistics + if (v3Global.opt.stats()) sccs.insert(vtx.color()); } + + V3Stats::addStat("Scheduling, Cycles, cyclic variables", nCyclicVars); + V3Stats::addStat("Scheduling, Cycles, cyclic logic blocks", nCyclicVtxs - nCyclicVars); + V3Stats::addStat("Scheduling, Cycles, unique SCCs", sccs.size()); + V3Stats::addStat("Scheduling, Cycles, cut variables", result.size()); + return result; } @@ -367,6 +388,61 @@ void reportCycles(Graph* graphp, const std::vector& cutV } } +void dumpSccs(V3Graph* graphp) { + // Map from SCC color to vertices in that SCC + std::map> scc2Vtxps; + + // Gather all vertices in each SCC + for (V3GraphVertex& vtx : graphp->vertices()) { + if (!vtx.color()) continue; + scc2Vtxps[vtx.color()].push_back(&vtx); + } + + // Dump Verilog for each SCC into separate files + for (const auto& pair : scc2Vtxps) { + const uint32_t color = pair.first; + const std::vector& vtxps = pair.second; + + // Open dump file + const std::string fname + = v3Global.debugFilename("sched_scc_" + std::to_string(color) + ".v"); + const std::unique_ptr ofp{V3File::new_ofstream(fname)}; + if (ofp->fail()) v3fatal("Can't write file: " << fname); + + // Write header + *ofp << "// SCC " << color << ", size: " << vtxps.size() << "\n\n"; + + // Dump variables + *ofp << "//////////////////////////////////////////////////////////////////////\n"; + *ofp << "// Variables\n"; + *ofp << "//////////////////////////////////////////////////////////////////////\n"; + *ofp << "\n"; + for (V3GraphVertex* vtxp : vtxps) { + const SchedAcyclicVarVertex* const vvtxp = vtxp->cast(); + if (!vvtxp) continue; + AstVarScope* const vscp = vvtxp->vscp(); + *ofp << "// " << vscp->fileline()->ascii() << "\n"; + *ofp << "// " << vscp->prettyName() << "\n"; + V3EmitV::debugVerilogForTree(vscp->varp(), *ofp); + *ofp << "\n"; + } + + // Dump logic + *ofp << "\n"; + *ofp << "//////////////////////////////////////////////////////////////////////\n"; + *ofp << "// Logic\n"; + *ofp << "//////////////////////////////////////////////////////////////////////\n"; + *ofp << "\n"; + for (V3GraphVertex* vtxp : vtxps) { + const SchedAcyclicLogicVertex* const lvtxp = vtxp->cast(); + if (!lvtxp) continue; + *ofp << "// " << lvtxp->logicp()->fileline()->ascii() << "\n"; + V3EmitV::debugVerilogForTree(lvtxp->logicp(), *ofp); + *ofp << "\n"; + } + } +} + LogicByScope fixCuts(AstNetlist* netlistp, const std::vector& cutVertices) { // For all logic that reads a cut vertex, build a map from logic -> list of cut AstVarScope @@ -439,6 +515,9 @@ LogicByScope breakCycles(AstNetlist* netlistp, const LogicByScope& combinational // Report warnings/diagnostics reportCycles(graphp.get(), cutVertices); + // Debug dump + if (dumpLevel() >= 6) dumpSccs(graphp.get()); + // Fix cuts by converting dependent logic to use hybrid sensitivities return fixCuts(netlistp, cutVertices); } diff --git a/test_regress/t/t_debug_emitv.out b/test_regress/t/t_debug_emitv.out index e09dc1e14..8a5472854 100644 --- a/test_regress/t/t_debug_emitv.out +++ b/test_regress/t/t_debug_emitv.out @@ -653,7 +653,7 @@ module Vt_debug_emitv_t; begin : assert_intrinsic assert ((| $_EXPRSTMT( ao = (a); - , ); + , 32'h1); )) begin end else begin diff --git a/test_regress/t/t_timing_debug2.out b/test_regress/t/t_timing_debug2.out index c5b101bbd..8a4f379ab 100644 --- a/test_regress/t/t_timing_debug2.out +++ b/test_regress/t/t_timing_debug2.out @@ -1215,7 +1215,7 @@ -V{t#,#}+ Vt_timing_debug2_std__03a__03aprocess::_ctor_var_reset -V{t#,#}+ Vt_timing_debug2_t___eval_initial__TOP__t__Vtiming__6____Vfork_1__0 -V{t#,#}+ Vt_timing_debug2_std__03a__03aprocess::__VnoInFunc_status --V{t#,#} Suspending process waiting for @([true] (32'h1 != $_EXPRSTMT( // Function: status t.__Vtask___VforkTask_0__25____VforkParent.(t.__Vtask_status__26__Vfuncout); , ); )) at t/t_timing_class.v:224 +-V{t#,#} Suspending process waiting for @([true] (32'h1 != $_EXPRSTMT( // Function: status t.__Vtask___VforkTask_0__25____VforkParent.(t.__Vtask_status__26__Vfuncout); , t.__Vtask_status__26__Vfuncout); )) at t/t_timing_class.v:224 -V{t#,#}+ Vt_timing_debug2_t__03a__03aAssignDelayClass::__VnoInFunc_do_assign -V{t#,#} Resuming: Process waiting at t/t_timing_class.v:76 -V{t#,#} Process forked at t/t_timing_class.v:76 finished @@ -1231,7 +1231,7 @@ -V{t#,#} Process waiting for @([true] ((32'sh2a == t::LocalWaitClass.a) | (32'sh64 != t::LocalWaitClass.b))) at t/t_timing_class.v:75 awaiting resumption -V{t#,#} Resuming: Process waiting at t/t_timing_class.v:224 -V{t#,#}+ Vt_timing_debug2_std__03a__03aprocess::__VnoInFunc_status --V{t#,#} Process waiting for @([true] (32'h1 != $_EXPRSTMT( // Function: status t.__Vtask___VforkTask_0__25____VforkParent.(t.__Vtask_status__26__Vfuncout); , ); )) at t/t_timing_class.v:224 awaiting resumption +-V{t#,#} Process waiting for @([true] (32'h1 != $_EXPRSTMT( // Function: status t.__Vtask___VforkTask_0__25____VforkParent.(t.__Vtask_status__26__Vfuncout); , t.__Vtask_status__26__Vfuncout); )) at t/t_timing_class.v:224 awaiting resumption -V{t#,#}+ Vt_timing_debug2___024root___dump_triggers__act -V{t#,#}+ Vt_timing_debug2___024root___trigger_anySet__act -V{t#,#} 'act' region trigger index 2 is active: @([true] __VdynSched.evaluate()) @@ -1347,7 +1347,7 @@ -V{t#,#}+ Vt_timing_debug2_std__03a__03aprocess::_ctor_var_reset -V{t#,#}+ Vt_timing_debug2_t___eval_initial__TOP__t__Vtiming__6____Vfork_2__0 -V{t#,#}+ Vt_timing_debug2_std__03a__03aprocess::__VnoInFunc_status --V{t#,#} Suspending process waiting for @([true] (32'h1 != $_EXPRSTMT( // Function: status t.__Vtask___VforkTask_1__29____VforkParent.(t.__Vtask_status__30__Vfuncout); , ); )) at t/t_timing_class.v:229 +-V{t#,#} Suspending process waiting for @([true] (32'h1 != $_EXPRSTMT( // Function: status t.__Vtask___VforkTask_1__29____VforkParent.(t.__Vtask_status__30__Vfuncout); , t.__Vtask_status__30__Vfuncout); )) at t/t_timing_class.v:229 -V{t#,#} Resuming: Process waiting at t/t_timing_class.v:131 -V{t#,#}+ Vt_timing_debug2_t__03a__03aClkClass::__VnoInFunc_flip -V{t#,#}+ Vt_timing_debug2___024root___eval_phase__act @@ -1356,7 +1356,7 @@ -V{t#,#} - Process waiting at t/t_timing_class.v:229 -V{t#,#} Resuming: Process waiting at t/t_timing_class.v:229 -V{t#,#}+ Vt_timing_debug2_std__03a__03aprocess::__VnoInFunc_status --V{t#,#} Process waiting for @([true] (32'h1 != $_EXPRSTMT( // Function: status t.__Vtask___VforkTask_1__29____VforkParent.(t.__Vtask_status__30__Vfuncout); , ); )) at t/t_timing_class.v:229 awaiting resumption +-V{t#,#} Process waiting for @([true] (32'h1 != $_EXPRSTMT( // Function: status t.__Vtask___VforkTask_1__29____VforkParent.(t.__Vtask_status__30__Vfuncout); , t.__Vtask_status__30__Vfuncout); )) at t/t_timing_class.v:229 awaiting resumption -V{t#,#}+ Vt_timing_debug2___024root___dump_triggers__act -V{t#,#}+ Vt_timing_debug2___024root___trigger_anySet__act -V{t#,#} 'act' region trigger index 2 is active: @([true] __VdynSched.evaluate()) diff --git a/test_regress/t/t_unoptflat_simple_2_bad.py b/test_regress/t/t_unoptflat_simple_2_bad.py index 7a79c987a..43b3725ee 100755 --- a/test_regress/t/t_unoptflat_simple_2_bad.py +++ b/test_regress/t/t_unoptflat_simple_2_bad.py @@ -14,7 +14,7 @@ test.top_filename = "t/t_unoptflat_simple_2.v" # Compile only test.compile(verilator_flags3=[], - verilator_flags2=["--report-unoptflat", "-fno-dfg"], + verilator_flags2=["--report-unoptflat", "-fno-dfg", "--dumpi-V3SchedAcyclic", "6"], fails=True, expect_filename=test.golden_filename)