3
0
Fork 0
mirror of https://github.com/YosysHQ/yosys synced 2025-04-11 03:33:36 +00:00

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%.
This commit is contained in:
whitequark 2020-12-13 15:33:47 +00:00
parent dd6a761db0
commit 4d40595d64

View file

@ -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<const RTLIL::Wire*> 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