diff --git a/common/route/router2.cc b/common/route/router2.cc index 53596c24af..5c00a6e772 100644 --- a/common/route/router2.cc +++ b/common/route/router2.cc @@ -1025,6 +1025,120 @@ struct Router2 } } + int trim_routing(NetInfo *net) + { + // Find places where we have a cheaper way of reaching a wire from earlier in the route tree + // and rewrite routing trees to use those cheaper routes. + int cleaned = 0; +#ifdef ARCH_ECP5 + if (net->is_global) + return 0; +#endif + // Build a reverse of the wires map going downhill + auto &nd = nets.at(net->udata); + dict> src2pips; + pool sources; + for (const auto &wire : nd.wires) { + PipId pip = wire.second.first; + if (pip == PipId()) { + sources.insert(wire.first); + } else { + src2pips[ctx->getPipSrcWire(pip)].insert(pip); + } + } + // Compute depths of wires in the route tree for a net + dict depth; + auto update_depth = [&](WireId src, int src_depth) { + std::queue> visit; + visit.emplace(src, src_depth); + depth[src] = src_depth; + while (!visit.empty()) { + auto cursor = visit.front(); + visit.pop(); + if (!src2pips.count(cursor.first)) + continue; + for (PipId dh : src2pips.at(cursor.first)) { + WireId dst = ctx->getPipDstWire(dh); + depth[dst] = cursor.second; + visit.emplace(dst, cursor.second + 1); + } + } + }; + for (WireId src : sources) { + update_depth(src, 0); + } + for (auto entry : depth) { + // Check it wasn't removed by a previous prune + if (!nd.wires.count(entry.first)) + continue; + // Find cheaper route + PipId best; + int score = entry.second; + for (PipId pip : ctx->getPipsUphill(entry.first)) { + WireId src = ctx->getPipSrcWire(pip); + auto fnd_depth = depth.find(src); + if (fnd_depth == depth.end()) + continue; + if ((fnd_depth->second + 1) < score) { + if (!nd.wires.count(src)) + continue; + if (ctx->debug) { + log_info(" found cheaper route to '%s' (%d) from '%s' (%d) on net '%s'\n", + ctx->nameOfWire(entry.first), entry.second, ctx->nameOfWire(src), fnd_depth->second, ctx->nameOf(net)); + } + score = fnd_depth->second + 1; + best = pip; + } + } + if (best != PipId()) { + cleaned++; + WireId new_src = ctx->getPipSrcWire(best); + // Transplant the route tree on top of the cheaper pip + auto &dst_entry = nd.wires.at(entry.first); + // Add before removing to avoid any unnecessary remove-and-re-add + WireId cursor = new_src; + while (true) { + auto &entry = nd.wires.at(cursor); + entry.second += dst_entry.second; + if (entry.first != PipId()) { + cursor = ctx->getPipSrcWire(entry.first); + } else { + break; + } + } + cursor = ctx->getPipSrcWire(dst_entry.first); + src2pips[cursor].erase(dst_entry.first); + + while (true) { + auto &entry = nd.wires.at(cursor); + entry.second -= dst_entry.second; + WireId next_cursor; + if (entry.first != PipId()) { + next_cursor = ctx->getPipSrcWire(entry.first); + } + + NPNR_ASSERT(entry.second >= 0); + + if (entry.second == 0) { + nd.wires.erase(cursor); + } + + if (next_cursor != WireId()) { + cursor = next_cursor; + } else { + break; + } + } + + dst_entry.first = best; + src2pips[new_src].insert(best); + + update_depth(entry.first, score); + } + } + return cleaned; + } + bool bind_and_check(NetInfo *net, store_index usr_idx, int phys_pin) { #ifdef ARCH_ECP5 @@ -1439,6 +1553,10 @@ struct Router2 } do_route(); + int cleaned = 0; + for (auto n : route_queue) { + cleaned += trim_routing(nets_by_udata.at(n)); + } update_route_delays(); route_queue.clear(); update_congestion(); @@ -1472,12 +1590,12 @@ struct Router2 for (auto cn : failed_nets) route_queue.push_back(cn); if (timing_driven_ripup) - log_info(" iter=%d wires=%d overused=%d overuse=%d tmgfail=%d archfail=%s\n", iter, total_wire_use, - overused_wires, total_overuse, tmgfail, + log_info(" iter=%d wires=%d overused=%d overuse=%d tmgfail=%d cleanedup=%d archfail=%s\n", iter, total_wire_use, + overused_wires, total_overuse, tmgfail, cleaned, (overused_wires > 0 || tmgfail > 0) ? "NA" : std::to_string(arch_fail).c_str()); else - log_info(" iter=%d wires=%d overused=%d overuse=%d archfail=%s\n", iter, total_wire_use, - overused_wires, total_overuse, + log_info(" iter=%d wires=%d overused=%d overuse=%d cleanedup=%d archfail=%s\n", iter, total_wire_use, + overused_wires, total_overuse, cleaned, (overused_wires > 0 || tmgfail > 0) ? "NA" : std::to_string(arch_fail).c_str()); ++iter; if (curr_cong_weight < 1e9)