bool inline_public = false;
bool debug_info = false;
+ bool debug_alias = false;
bool debug_eval = false;
std::ostringstream f;
} else if (sig.is_chunk()) {
return dump_sigchunk(sig.as_chunk(), is_lhs, for_debug);
} else {
- dump_sigchunk(*sig.chunks().rbegin(), is_lhs, for_debug);
- for (auto it = sig.chunks().rbegin() + 1; it != sig.chunks().rend(); ++it) {
- f << ".concat(";
- dump_sigchunk(*it, is_lhs, for_debug);
- f << ")";
+ bool first = true;
+ auto chunks = sig.chunks();
+ for (auto it = chunks.rbegin(); it != chunks.rend(); it++) {
+ if (!first)
+ f << ".concat(";
+ bool is_complex = dump_sigchunk(*it, is_lhs, for_debug);
+ if (!is_lhs && it->width == 1) {
+ size_t repeat = 1;
+ while ((it + repeat) != chunks.rend() && *(it + repeat) == *it)
+ repeat++;
+ if (repeat > 1) {
+ if (is_complex)
+ f << ".val()";
+ f << ".repeat<" << repeat << ">()";
+ }
+ it += repeat - 1;
+ }
+ if (!first)
+ f << ")";
+ first = false;
}
return true;
}
if (for_debug && !is_connect_outlined(conn))
return;
- f << indent << "// connection\n";
+ std::vector<RTLIL::IdString> inlined_cells;
+ collect_sigspec_rhs(conn.second, inlined_cells);
+ if (for_debug || inlined_cells.empty()) {
+ f << indent << "// connection\n";
+ } else {
+ f << indent << "// cells";
+ for (auto inlined_cell : inlined_cells)
+ f << " " << inlined_cell.str();
+ f << "\n";
+ }
f << indent;
dump_sigspec_lhs(conn.first, for_debug);
f << " = ";
continue;
}
if (!module->get_bool_attribute(ID(cxxrtl_blackbox)) || wire->port_id != 0)
- f << indent << "changed |= " << mangle(wire) << ".commit();\n";
+ f << indent << "if (" << mangle(wire) << ".commit()) changed = true;\n";
}
if (!module->get_bool_attribute(ID(cxxrtl_blackbox))) {
for (auto memory : module->memories) {
if (!writable_memories[memory.second])
continue;
- f << indent << "changed |= " << mangle(memory.second) << ".commit();\n";
+ f << indent << "if (" << mangle(memory.second) << ".commit()) changed = true;\n";
}
for (auto cell : module->cells()) {
if (is_internal_cell(cell->type))
continue;
const char *access = is_cxxrtl_blackbox_cell(cell) ? "->" : ".";
- f << indent << "changed |= " << mangle(cell) << access << "commit();\n";
+ f << indent << "if (" << mangle(cell) << access << "commit()) changed = true;\n";
}
}
f << indent << "return changed;\n";
f << indent << "}\n";
f << "\n";
}
+ f << indent << "CXXRTL_EXTREMELY_COLD\n";
f << indent << "void " << mangle(module) << "::debug_info(debug_items &items, std::string path) {\n";
dump_debug_info_method(module);
f << indent << "}\n";
for (auto item : flow.bit_has_state)
bit_has_state.insert(item);
- if (debug_info) {
- // 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
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()) {
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
struct CxxrtlBackend : public Backend {
static const int DEFAULT_OPT_LEVEL = 6;
- static const int DEFAULT_DEBUG_LEVEL = 2;
+ static const int DEFAULT_DEBUG_LEVEL = 3;
CxxrtlBackend() : Backend("cxxrtl", "convert design to C++ RTL simulation") { }
void help() override
log(" more visibility and generate more code, but do not pessimize evaluation.\n");
log("\n");
log(" -g0\n");
- log(" no debug information.\n");
+ log(" no debug information. the C API is unavailable.\n");
log("\n");
log(" -g1\n");
- log(" debug information for non-optimized public wires. this also makes it\n");
- log(" possible to use the C API.\n");
+ log(" debug information for member public wires only. this is the bare minimum\n");
+ log(" necessary to access all design state. enables the C API.\n");
log("\n");
log(" -g2\n");
- log(" like -g1, and compute debug information on demand for all public wires\n");
+ log(" like -g1, and include debug information for public wires that are tied\n");
+ log(" to a constant or another public wire.\n");
+ log("\n");
+ log(" -g3\n");
+ log(" like -g2, and compute debug information on demand for all public wires\n");
log(" that were optimized out.\n");
log("\n");
}
continue;
}
if (args[argidx] == "-Og") {
- log_warning("The `-Og` option has been removed. Use `-g2` instead for complete "
+ log_warning("The `-Og` option has been removed. Use `-g3` instead for complete "
"design coverage regardless of optimization level.\n");
continue;
}
if (args[argidx] == "-O" && argidx+1 < args.size() && args[argidx+1] == "g") {
argidx++;
- log_warning("The `-Og` option has been removed. Use `-g2` instead for complete "
+ log_warning("The `-Og` option has been removed. Use `-g3` instead for complete "
"design coverage regardless of optimization level.\n");
continue;
}
}
switch (debug_level) {
// the highest level here must match DEFAULT_DEBUG_LEVEL
- case 2:
+ case 3:
worker.debug_eval = true;
YS_FALLTHROUGH
+ case 2:
+ worker.debug_alias = true;
+ YS_FALLTHROUGH
case 1:
worker.debug_info = true;
YS_FALLTHROUGH