From 4d40595d644cfe58425a3de023c712641c429010 Mon Sep 17 00:00:00 2001 From: whitequark Date: Sun, 13 Dec 2020 15:33:47 +0000 Subject: [PATCH] cxxrtl: make alias analysis outlining-aware. Before this commit, if a sequence of wires assigned in a chain would terminate on a cell, none of the wires would get marked as aliases, and typically all of the public wires would get outlined. The reason for this behavior is that alias analysis predates outlining and in fact runs before it. After this commit, alias analysis runs after outlining and considers outlined wires valid aliasees. More importantly, if the chained wires contain any valid aliasees, then all of the wires are aliased to the one that is topologically deepest. Aliased wires incur virtually no overhead for the VCD writer, unlike outlined wires that would otherwise take their place. On Minerva SoC SRAM, size of the full VCD dump is reduced by ~65%, and throughput is increased by ~55%. --- backends/cxxrtl/cxxrtl_backend.cc | 86 +++++++++++++++++-------------- 1 file changed, 48 insertions(+), 38 deletions(-) diff --git a/backends/cxxrtl/cxxrtl_backend.cc b/backends/cxxrtl/cxxrtl_backend.cc index 588cca12e..fa19a8dd6 100644 --- a/backends/cxxrtl/cxxrtl_backend.cc +++ b/backends/cxxrtl/cxxrtl_backend.cc @@ -2426,43 +2426,6 @@ struct CxxrtlWorker { for (auto item : flow.bit_has_state) bit_has_state.insert(item); - if (debug_info && debug_alias) { - // Find wires that alias other wires or are tied to a constant; debug information can be enriched with these - // at essentially zero additional cost. - // - // Note that the information collected here can't be used for optimizing the netlist: debug information queries - // are pure and run on a design in a stable state, which allows assumptions that do not otherwise hold. - for (auto wire : module->wires()) { - if (!wire->name.isPublic()) - continue; - if (!unbuffered_wires[wire]) - continue; - const RTLIL::Wire *wire_it = wire; - while (1) { - if (!(flow.wire_def_inlinable.count(wire_it) && flow.wire_def_inlinable[wire_it])) - break; // not an alias: complex def - log_assert(flow.wire_comb_defs[wire_it].size() == 1); - FlowGraph::Node *node = *flow.wire_comb_defs[wire_it].begin(); - if (node->type != FlowGraph::Node::Type::CONNECT) - break; // not an alias: def by cell - RTLIL::SigSpec rhs_sig = node->connect.second; - if (rhs_sig.is_wire()) { - RTLIL::Wire *rhs_wire = rhs_sig.as_wire(); - if (unbuffered_wires[rhs_wire]) { - wire_it = rhs_wire; // maybe an alias - } else { - debug_alias_wires[wire] = rhs_wire; // is an alias - break; - } - } else if (rhs_sig.is_fully_const()) { - debug_const_wires[wire] = rhs_sig.as_const(); // is a const - break; - } else { - break; // not an alias: complex rhs - } - } - } - } if (debug_info && debug_eval) { // Find wires that can be be outlined, i.e. whose values can be always recovered from // the values of other wires. (This is the inverse of inlining--any wire that can be @@ -2471,7 +2434,7 @@ struct CxxrtlWorker { pool worklist, visited; for (auto wire : module->wires()) { if (!wire->name.isPublic()) - continue; // only outline public wires + continue; worklist.insert(wire); } while (!worklist.empty()) { @@ -2487,6 +2450,53 @@ struct CxxrtlWorker { worklist.insert(node_use); } } + if (debug_info && debug_alias) { + // Find wires that alias other wires or are tied to a constant. Both of these cases are + // directly expressible in the debug information, improving coverage at zero cost. + for (auto wire : module->wires()) { + if (!wire->name.isPublic()) + continue; + const RTLIL::Wire *cursor = wire; + RTLIL::SigSpec alias_of; + while (1) { + if (!(flow.wire_def_inlinable.count(cursor) && flow.wire_def_inlinable[cursor])) + break; // not an alias: complex def + log_assert(flow.wire_comb_defs[cursor].size() == 1); + FlowGraph::Node *node = *flow.wire_comb_defs[cursor].begin(); + if (node->type != FlowGraph::Node::Type::CONNECT) + break; // not an alias: def by cell + RTLIL::SigSpec rhs_sig = node->connect.second; + if (rhs_sig.is_fully_const()) { + alias_of = rhs_sig; // alias of const + break; + } else if (rhs_sig.is_wire()) { + RTLIL::Wire *rhs_wire = rhs_sig.as_wire(); // possible alias of wire + if (rhs_wire->port_input && !rhs_wire->port_output) { + alias_of = rhs_wire; // alias of input + break; + } else if (!localized_wires.count(rhs_wire) && !inlined_wires.count(rhs_wire)) { + alias_of = rhs_wire; // alias of member + break; + } else { + if (rhs_wire->name.isPublic() && debug_outlined_wires.count(rhs_wire)) + alias_of = rhs_wire; // alias of either outline or another alias + cursor = rhs_wire; // keep looking + } + } else { + break; // not an alias: complex rhs + } + } + if (alias_of.empty()) { + continue; + } else if (alias_of.is_fully_const()) { + debug_const_wires[wire] = alias_of.as_const(); + } else if (alias_of.is_wire()) { + debug_alias_wires[wire] = alias_of.as_wire(); + } else log_abort(); + if (inlined_wires.count(wire)) + debug_outlined_wires.erase(wire); + } + } } if (has_feedback_arcs || has_buffered_comb_wires) { // Although both non-feedback buffered combinatorial wires and apparent feedback wires may be eliminated -- 2.30.2