dict<const RTLIL::Module*, std::vector<FlowGraph::Node>> schedule;
        pool<const RTLIL::Wire*> localized_wires;
        dict<const RTLIL::Module*, pool<std::string>> blackbox_specializations;
+       dict<const RTLIL::Module*, bool> eval_converges;
 
        void inc_indent() {
                indent += "\t";
                                        dump_sigspec_rhs(conn.second);
                                        f << ";\n";
                                }
-                       f << indent << mangle(cell) << access << "eval();\n";
+                       f << indent << "converged &= " << mangle(cell) << access << "eval();\n";
                        for (auto conn : cell->connections()) {
                                if (conn.second.is_wire()) {
                                        RTLIL::Wire *wire = conn.second.as_wire();
        void dump_eval_method(RTLIL::Module *module)
        {
                inc_indent();
+                       f << indent << "bool converged = " << (eval_converges.at(module) ? "true" : "false") << ";\n";
                        if (!module->get_bool_attribute(ID(cxxrtl.blackbox))) {
                                for (auto wire : module->wires())
                                        dump_wire(wire, /*is_local_context=*/true);
                                        }
                                }
                        }
+                       f << indent << "return converged;\n";
                dec_indent();
        }
 
                                                dump_wire(wire, /*is_local_context=*/false);
                                }
                                f << "\n";
-                               f << indent << "void eval() override {\n";
+                               f << indent << "bool eval() override {\n";
                                dump_eval_method(module);
                                f << indent << "}\n";
                                f << "\n";
                                }
                                if (has_cells)
                                        f << "\n";
-                               f << indent << "void eval() override;\n";
+                               f << indent << "bool eval() override;\n";
                                f << indent << "bool commit() override;\n";
                        dec_indent();
                        f << indent << "}; // struct " << mangle(module) << "\n";
        {
                if (module->get_bool_attribute(ID(cxxrtl.blackbox)))
                        return;
-               f << indent << "void " << mangle(module) << "::eval() {\n";
+               f << indent << "bool " << mangle(module) << "::eval() {\n";
                dump_eval_method(module);
                f << indent << "}\n";
                f << "\n";
                                                }
                                        }
                                }
+
+                               // Black boxes converge by default, since their implementations are quite unlikely to require
+                               // internal propagation of comb signals.
+                               eval_converges[module] = true;
                                continue;
                        }
 
                        // it is possible that a design with no feedback arcs would end up with doubly buffered wires in such cases
                        // as a wire with multiple drivers where one of them is combinatorial and the other is synchronous. Such designs
                        // also require more than one delta cycle to converge.
-                       pool<RTLIL::Wire*> buffered_wires;
+                       pool<const RTLIL::Wire*> buffered_wires;
                        for (auto wire : module->wires()) {
                                if (flow.wire_comb_defs[wire].size() > 0 && !elided_wires.count(wire) && !localized_wires[wire]) {
                                        if (!feedback_wires[wire])
                                for (auto wire : buffered_wires)
                                        log("  %s\n", wire->name.c_str());
                        }
+
+                       eval_converges[module] = feedback_wires.empty() && buffered_wires.empty();
                }
                if (has_feedback_arcs || has_buffered_wires) {
                        // Although both non-feedback buffered combinatorial wires and apparent feedback wires may be eliminated
                log("      value<8> p_i_data;\n");
                log("      wire<8> p_o_data;\n");
                log("\n");
-               log("      void eval() override;\n");
+               log("      bool eval() override;\n");
                log("      bool commit() override;\n");
                log("\n");
                log("      static std::unique_ptr<bb_p_debug>\n");
                log("    namespace cxxrtl_design {\n");
                log("\n");
                log("    struct stderr_debug : public bb_p_debug {\n");
-               log("      void eval() override {\n");
+               log("      bool eval() override {\n");
                log("        if (posedge_p_clk() && p_en)\n");
                log("          fprintf(stderr, \"debug: %%02x\\n\", p_i_data.data[0]);\n");
                log("        p_o_data.next = p_i_data;\n");
-               log("        bb_p_debug::eval();\n");
+               log("        return bb_p_debug::eval();\n");
                log("      }\n");
                log("    };\n");
                log("\n");