mirror of https://github.com/YosysHQ/yosys.git
cxxrtl: emit debug information for constant wires.
Constant wires can represent a significant chunk of the design in generic designs or after optimization. Emitting them in VCD files significantly improves usability because gtkwave removes all traces that are not present in the VCD file after reload, and iterative development suffers if switching a varying signal to a constant disrupts the workflow.
This commit is contained in:
parent
d5c07e5b6f
commit
f2d7a18756
|
@ -741,6 +741,17 @@ struct debug_item : ::cxxrtl_object {
|
||||||
next = item.data;
|
next = item.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<size_t Bits>
|
||||||
|
debug_item(const value<Bits> &item) {
|
||||||
|
static_assert(sizeof(item) == value<Bits>::chunks * sizeof(chunk_t),
|
||||||
|
"value<Bits> is not compatible with C layout");
|
||||||
|
type = VALUE;
|
||||||
|
width = Bits;
|
||||||
|
depth = 1;
|
||||||
|
curr = const_cast<uint32_t*>(item.data);
|
||||||
|
next = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
template<size_t Bits>
|
template<size_t Bits>
|
||||||
debug_item(wire<Bits> &item) {
|
debug_item(wire<Bits> &item) {
|
||||||
static_assert(sizeof(item.curr) == value<Bits>::chunks * sizeof(chunk_t) &&
|
static_assert(sizeof(item.curr) == value<Bits>::chunks * sizeof(chunk_t) &&
|
||||||
|
|
|
@ -540,6 +540,7 @@ struct CxxrtlWorker {
|
||||||
dict<const RTLIL::Module*, std::vector<FlowGraph::Node>> schedule;
|
dict<const RTLIL::Module*, std::vector<FlowGraph::Node>> schedule;
|
||||||
pool<const RTLIL::Wire*> localized_wires;
|
pool<const RTLIL::Wire*> localized_wires;
|
||||||
dict<const RTLIL::Wire*, const RTLIL::Wire*> debug_alias_wires;
|
dict<const RTLIL::Wire*, const RTLIL::Wire*> debug_alias_wires;
|
||||||
|
dict<const RTLIL::Wire*, RTLIL::Const> debug_const_wires;
|
||||||
dict<const RTLIL::Module*, pool<std::string>> blackbox_specializations;
|
dict<const RTLIL::Module*, pool<std::string>> blackbox_specializations;
|
||||||
dict<const RTLIL::Module*, bool> eval_converges;
|
dict<const RTLIL::Module*, bool> eval_converges;
|
||||||
|
|
||||||
|
@ -1607,30 +1608,37 @@ struct CxxrtlWorker {
|
||||||
|
|
||||||
void dump_debug_info_method(RTLIL::Module *module)
|
void dump_debug_info_method(RTLIL::Module *module)
|
||||||
{
|
{
|
||||||
size_t count_member_wires = 0;
|
size_t count_const_wires = 0;
|
||||||
size_t count_alias_wires = 0;
|
size_t count_alias_wires = 0;
|
||||||
|
size_t count_member_wires = 0;
|
||||||
size_t count_skipped_wires = 0;
|
size_t count_skipped_wires = 0;
|
||||||
inc_indent();
|
inc_indent();
|
||||||
f << indent << "assert(path.empty() || path[path.size() - 1] == ' ');\n";
|
f << indent << "assert(path.empty() || path[path.size() - 1] == ' ');\n";
|
||||||
for (auto wire : module->wires()) {
|
for (auto wire : module->wires()) {
|
||||||
if (wire->name[0] != '\\')
|
if (wire->name[0] != '\\')
|
||||||
continue;
|
continue;
|
||||||
if (debug_alias_wires.count(wire)) {
|
if (debug_const_wires.count(wire)) {
|
||||||
|
// Wire tied to a constant
|
||||||
|
f << indent << "static const value<" << wire->width << "> const_" << mangle(wire) << " = ";
|
||||||
|
dump_const(debug_const_wires[wire]);
|
||||||
|
f << ";\n";
|
||||||
|
f << indent << "items.emplace(path + " << escape_cxx_string(get_hdl_name(wire));
|
||||||
|
f << ", debug_item(const_" << mangle(wire) << "));\n";
|
||||||
|
count_const_wires++;
|
||||||
|
} else if (debug_alias_wires.count(wire)) {
|
||||||
// Alias of a member wire
|
// Alias of a member wire
|
||||||
f << indent << "items.emplace(path + " << escape_cxx_string(get_hdl_name(wire));
|
f << indent << "items.emplace(path + " << escape_cxx_string(get_hdl_name(wire));
|
||||||
f << ", debug_item(" << mangle(debug_alias_wires[wire]) << "));\n";
|
f << ", debug_item(" << mangle(debug_alias_wires[wire]) << "));\n";
|
||||||
count_alias_wires++;
|
count_alias_wires++;
|
||||||
continue;
|
} else if (!localized_wires.count(wire)) {
|
||||||
}
|
|
||||||
if (!localized_wires.count(wire)) {
|
|
||||||
// Member wire
|
// Member wire
|
||||||
f << indent << "items.emplace(path + " << escape_cxx_string(get_hdl_name(wire));
|
f << indent << "items.emplace(path + " << escape_cxx_string(get_hdl_name(wire));
|
||||||
f << ", debug_item(" << mangle(wire) << "));\n";
|
f << ", debug_item(" << mangle(wire) << "));\n";
|
||||||
count_member_wires++;
|
count_member_wires++;
|
||||||
continue;
|
} else {
|
||||||
}
|
|
||||||
count_skipped_wires++;
|
count_skipped_wires++;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
for (auto &memory_it : module->memories) {
|
for (auto &memory_it : module->memories) {
|
||||||
if (memory_it.first[0] != '\\')
|
if (memory_it.first[0] != '\\')
|
||||||
continue;
|
continue;
|
||||||
|
@ -1647,8 +1655,9 @@ struct CxxrtlWorker {
|
||||||
dec_indent();
|
dec_indent();
|
||||||
|
|
||||||
log_debug("Debug information statistics for module %s:\n", log_id(module));
|
log_debug("Debug information statistics for module %s:\n", log_id(module));
|
||||||
log_debug(" Member wires: %zu\n", count_member_wires);
|
log_debug(" Const wires: %zu\n", count_const_wires);
|
||||||
log_debug(" Alias wires: %zu\n", count_alias_wires);
|
log_debug(" Alias wires: %zu\n", count_alias_wires);
|
||||||
|
log_debug(" Member wires: %zu\n", count_member_wires);
|
||||||
log_debug(" Other wires: %zu (no debug information)\n", count_skipped_wires);
|
log_debug(" Other wires: %zu (no debug information)\n", count_skipped_wires);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2163,8 +2172,8 @@ struct CxxrtlWorker {
|
||||||
eval_converges[module] = feedback_wires.empty() && buffered_wires.empty();
|
eval_converges[module] = feedback_wires.empty() && buffered_wires.empty();
|
||||||
|
|
||||||
if (debug_info) {
|
if (debug_info) {
|
||||||
// Find wires that alias other wires; debug information can be enriched with these at essentially zero
|
// Find wires that alias other wires or are tied to a constant; debug information can be enriched with these
|
||||||
// additional cost.
|
// at essentially zero additional cost.
|
||||||
//
|
//
|
||||||
// Note that the information collected here can't be used for optimizing the netlist: debug information queries
|
// 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.
|
// are pure and run on a design in a stable state, which allows assumptions that do not otherwise hold.
|
||||||
|
@ -2179,14 +2188,20 @@ struct CxxrtlWorker {
|
||||||
break; // not an alias: complex def
|
break; // not an alias: complex def
|
||||||
log_assert(flow.wire_comb_defs[wire_it].size() == 1);
|
log_assert(flow.wire_comb_defs[wire_it].size() == 1);
|
||||||
FlowGraph::Node *node = *flow.wire_comb_defs[wire_it].begin();
|
FlowGraph::Node *node = *flow.wire_comb_defs[wire_it].begin();
|
||||||
if (node->connect.second.is_wire()) {
|
if (node->type != FlowGraph::Node::Type::CONNECT)
|
||||||
RTLIL::Wire *rhs_wire = node->connect.second.as_wire();
|
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 (localized_wires[rhs_wire]) {
|
if (localized_wires[rhs_wire]) {
|
||||||
wire_it = rhs_wire; // maybe an alias
|
wire_it = rhs_wire; // maybe an alias
|
||||||
} else {
|
} else {
|
||||||
debug_alias_wires[wire] = rhs_wire; // is an alias
|
debug_alias_wires[wire] = rhs_wire; // is an alias
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
} else if (rhs_sig.is_fully_const()) {
|
||||||
|
debug_const_wires[wire] = rhs_sig.as_const(); // is a const
|
||||||
|
break;
|
||||||
} else {
|
} else {
|
||||||
break; // not an alias: complex rhs
|
break; // not an alias: complex rhs
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,9 +64,10 @@ enum cxxrtl_type {
|
||||||
// Values correspond to singly buffered netlist nodes, i.e. nodes driven exclusively by
|
// Values correspond to singly buffered netlist nodes, i.e. nodes driven exclusively by
|
||||||
// combinatorial cells, or toplevel input nodes.
|
// combinatorial cells, or toplevel input nodes.
|
||||||
//
|
//
|
||||||
// Values can be inspected via the `curr` pointer and modified via the `next` pointer (which are
|
// Values can be inspected via the `curr` pointer. If the `next` pointer is NULL, the value is
|
||||||
// equal for values); however, note that changes to the bits driven by combinatorial cells will
|
// driven by a constant and can never be modified. Otherwise, the value can be modified through
|
||||||
// be ignored.
|
// the `next` pointer (which is equal to `curr` if not NULL). Note that changes to the bits
|
||||||
|
// driven by combinatorial cells will be ignored.
|
||||||
//
|
//
|
||||||
// Values always have depth 1.
|
// Values always have depth 1.
|
||||||
CXXRTL_VALUE = 0,
|
CXXRTL_VALUE = 0,
|
||||||
|
@ -75,8 +76,8 @@ enum cxxrtl_type {
|
||||||
// storage cells, or by combinatorial cells that are a part of a feedback path.
|
// storage cells, or by combinatorial cells that are a part of a feedback path.
|
||||||
//
|
//
|
||||||
// Wires can be inspected via the `curr` pointer and modified via the `next` pointer (which are
|
// Wires can be inspected via the `curr` pointer and modified via the `next` pointer (which are
|
||||||
// distinct for wires); however, note that changes to the bits driven by combinatorial cells will
|
// distinct for wires). Note that changes to the bits driven by combinatorial cells will be
|
||||||
// be ignored.
|
// ignored.
|
||||||
//
|
//
|
||||||
// Wires always have depth 1.
|
// Wires always have depth 1.
|
||||||
CXXRTL_WIRE = 1,
|
CXXRTL_WIRE = 1,
|
||||||
|
|
Loading…
Reference in New Issue