Merge pull request #2479 from zachjs/const-arg-hint
[yosys.git] / backends / cxxrtl / cxxrtl_backend.cc
index 5e2f4f31a5cc2c1e7a2b153b27b7d74090630cfb..916303bfe49d37f0bd491e490fb27ad70e460060 100644 (file)
@@ -539,6 +539,7 @@ struct CxxrtlWorker {
        bool inline_public = false;
 
        bool debug_info = false;
+       bool debug_alias = false;
        bool debug_eval = false;
 
        std::ostringstream f;
@@ -831,11 +832,26 @@ struct CxxrtlWorker {
                } 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;
                }
@@ -910,7 +926,16 @@ struct CxxrtlWorker {
                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 << " = ";
@@ -1692,19 +1717,19 @@ struct CxxrtlWorker {
                                        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";
@@ -2037,6 +2062,7 @@ struct CxxrtlWorker {
                                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";
@@ -2425,43 +2451,6 @@ struct CxxrtlWorker {
                        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
@@ -2470,7 +2459,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()) {
@@ -2486,6 +2475,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
@@ -2575,7 +2611,7 @@ struct CxxrtlWorker {
 
 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
@@ -2793,14 +2829,18 @@ struct CxxrtlBackend : public Backend {
                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");
        }
@@ -2832,13 +2872,13 @@ struct CxxrtlBackend : public Backend {
                                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;
                        }
@@ -2900,9 +2940,12 @@ struct CxxrtlBackend : public Backend {
                }
                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