Internals: Add stats/dump of circular logic in scheduling (#6953)

This commit is contained in:
Geza Lore 2026-01-28 10:45:23 +00:00 committed by GitHub
parent 5b84635bde
commit 7875552354
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 87 additions and 7 deletions

View File

@ -614,6 +614,7 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst {
putfs(nodep, "$_EXPRSTMT(\n");
iterateAndNextConstNull(nodep->stmtsp());
putbs(", ");
iterateAndNextConstNull(nodep->resultp());
puts(");\n");
}

View File

@ -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 <tuple>
#include <map>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>
@ -222,13 +225,31 @@ bool isCut(const SchedAcyclicVarVertex* vtxp) {
}
std::vector<SchedAcyclicVarVertex*> findCutVertices(Graph* graphp) {
// List of cut vertices being computed here
std::vector<SchedAcyclicVarVertex*> 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<uint32_t> 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<SchedAcyclicVarVertex>()) {
++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<SchedAcyclicVarVertex*>& cutV
}
}
void dumpSccs(V3Graph* graphp) {
// Map from SCC color to vertices in that SCC
std::map<uint32_t, std::vector<V3GraphVertex*>> 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<V3GraphVertex*>& vtxps = pair.second;
// Open dump file
const std::string fname
= v3Global.debugFilename("sched_scc_" + std::to_string(color) + ".v");
const std::unique_ptr<std::ofstream> 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<SchedAcyclicVarVertex>();
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<SchedAcyclicLogicVertex>();
if (!lvtxp) continue;
*ofp << "// " << lvtxp->logicp()->fileline()->ascii() << "\n";
V3EmitV::debugVerilogForTree(lvtxp->logicp(), *ofp);
*ofp << "\n";
}
}
}
LogicByScope fixCuts(AstNetlist* netlistp,
const std::vector<SchedAcyclicVarVertex*>& 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);
}

View File

@ -653,7 +653,7 @@ module Vt_debug_emitv_t;
begin : assert_intrinsic
assert ((| $_EXPRSTMT(
ao = (a);
, );
, 32'h1);
)) begin
end
else begin

View File

@ -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())

View File

@ -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)