diff --git a/passes/hierarchy/hierarchy.cc b/passes/hierarchy/hierarchy.cc index 4077edae8..ad348a028 100644 --- a/passes/hierarchy/hierarchy.cc +++ b/passes/hierarchy/hierarchy.cc @@ -1524,7 +1524,7 @@ struct HierarchyPass : public Pass { } if (!conn.second.is_fully_const() || !w->port_input || w->port_output) - log_warning("Resizing cell port %s.%s.%s from %d bits to %d bits.\n", module, cell, + log("Resizing cell port %s.%s.%s from %d bits to %d bits.\n", module, cell, conn.first.unescape(), GetSize(conn.second), GetSize(sig)); cell->setPort(conn.first, sig); } diff --git a/passes/silimate/opt_compact_prefix.cc b/passes/silimate/opt_compact_prefix.cc index 1432790fe..fdc902a64 100644 --- a/passes/silimate/opt_compact_prefix.cc +++ b/passes/silimate/opt_compact_prefix.cc @@ -961,6 +961,22 @@ struct OptCompactPrefixWorker : CutRegionWorker true, std::max(8192, max_width * max_width * 4), max_width * (max_width + 8)); } + // True if any bit of `sig` is driven by a cell this pass emitted on an + // earlier fixpoint sweep (tagged in execute()). The emitted compaction + // logic still fingerprints as the same function, so this guard stops the + // outer fixpoint loop from re-rewriting its own output indefinitely. + bool root_driver_emitted(const SigSpec &sig) + { + for (auto bit : sigmap(sig)) { + if (!bit.wire) + continue; + Cell *drv = bit_to_driver.at(bit, nullptr); + if (drv && drv->get_bool_attribute(ID(opt_compact_prefix_emitted))) + return true; + } + return false; + } + void run() { if (module->has_processes_warn()) @@ -990,6 +1006,8 @@ struct OptCompactPrefixWorker : CutRegionWorker } if (root_claimed(root.sig)) continue; + if (root_driver_emitted(root.sig)) + continue; int width = GetSize(root.sig); // Wide roots (>62) are valid forward-pack candidates; the @@ -1505,28 +1523,63 @@ struct OptCompactPrefixPass : public Pass int total_removed = 0; int total_emitted = 0; + // Overlapping compaction loops can share post-synthesis logic (e.g. one + // loop's start mask feeding the other loop's cone), so a single sweep + // claims the first region and masks the second. Iterate each module to + // a fixpoint: rewrite, tag the freshly-emitted cells, clean, and re-scan + // until a sweep finds nothing new. The tag (consumed by + // root_driver_emitted) stops the next sweep from re-matching this pass's + // own output, which still fingerprints as the same compaction function. + const int max_sweeps = 16; for (auto module : design->selected_modules()) { - OptCompactPrefixWorker worker(module, max_width); - if (walk_budget > 0) - worker.walk_budget = walk_budget; - if (eval_budget > 0) - worker.eval_budget = eval_budget; - if (attempt_budget > 0) - worker.attempt_budget = attempt_budget; - worker.run(); - total_forward += worker.forward_rewrites; - total_reverse += worker.reverse_rewrites; - total_modulo += worker.modulo_rewrites; - total_removed += worker.old_cells_removed; - total_emitted += worker.new_cells_emitted; + for (int sweep = 0; sweep < max_sweeps; sweep++) { + pool before; + for (auto c : module->cells()) + before.insert(c); + + OptCompactPrefixWorker worker(module, max_width); + if (walk_budget > 0) + worker.walk_budget = walk_budget; + if (eval_budget > 0) + worker.eval_budget = eval_budget; + if (attempt_budget > 0) + worker.attempt_budget = attempt_budget; + worker.run(); + + total_forward += worker.forward_rewrites; + total_reverse += worker.reverse_rewrites; + total_modulo += worker.modulo_rewrites; + total_removed += worker.old_cells_removed; + total_emitted += worker.new_cells_emitted; + + if (worker.forward_rewrites + worker.reverse_rewrites + + worker.modulo_rewrites == 0) + break; + if (sweep == max_sweeps - 1) + log_warning("opt_compact_prefix: fixpoint not reached for module %s " + "after %d sweeps; some compaction opportunities may remain.\n", + log_id(module), max_sweeps); + + // Tag the cells emitted by this sweep so the next sweep skips + // them, then drop the now-dangling old cone so the masked + // sibling region becomes visible. Scope the clean to the module + // under rewrite so untouched modules keep their dangling cells + // until their own sweep, matching the original single-call run. + for (auto c : module->cells()) + if (!before.count(c)) + c->set_bool_attribute(ID(opt_compact_prefix_emitted)); + Pass::call_on_module(design, module, "clean -purge"); + } } + // Drop the internal bookkeeping tag so it never leaks into the netlist. + for (auto module : design->modules()) + for (auto c : module->cells()) + c->attributes.erase(ID(opt_compact_prefix_emitted)); + log("Rewrote %d forward pack(s), %d reverse suffix read(s), %d modulo decimation(s); " "removed %d old cell(s), emitted %d new cell(s).\n", total_forward, total_reverse, total_modulo, total_removed, total_emitted); - - if (total_forward || total_reverse || total_modulo) - Yosys::run_pass("clean -purge"); } } OptCompactPrefixPass; diff --git a/tests/various/sv_implicit_ports.sh b/tests/various/sv_implicit_ports.sh index ace95b3ba..1be68917d 100755 --- a/tests/various/sv_implicit_ports.sh +++ b/tests/various/sv_implicit_ports.sh @@ -112,7 +112,9 @@ endmodule EOT # Mixed implicit and explicit 2 -(${YOSYS} -f "verilog -sv" -qp "prep -flatten -top top; select -assert-count 1 t:\$add" - <&1 | grep -F "Warning: Resizing cell port top.add_i.b from 10 bits to 8 bits." > /dev/null +) 2>&1 | grep -F "Resizing cell port top.add_i.b from 10 bits to 8 bits." > /dev/null