From 76358753b11eab7831ea0adeb02b032db0b2c707 Mon Sep 17 00:00:00 2001 From: Eder Monteiro Date: Tue, 30 Jun 2026 16:50:38 -0300 Subject: [PATCH 1/9] grt/cugr: erase net from GlobalRouter when CUGR net is deleted Signed-off-by: Eder Monteiro --- src/grt/src/GlobalRouter.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/grt/src/GlobalRouter.cpp b/src/grt/src/GlobalRouter.cpp index cb7e854d3b..c757ae2770 100644 --- a/src/grt/src/GlobalRouter.cpp +++ b/src/grt/src/GlobalRouter.cpp @@ -4785,6 +4785,13 @@ void GlobalRouter::removeNet(odb::dbNet* db_net) if (use_cugr_) { cugr_->removeNet(db_net); + auto it = db_net_map_.find(db_net); + if (it != db_net_map_.end()) { + delete it->second; + db_net_map_.erase(it); + } + dirty_nets_.erase(db_net); + routes_.erase(db_net); } else { auto it = db_net_map_.find(db_net); if (it == db_net_map_.end() || it->second == nullptr) { From 108ad2cf0edd9206f8267af76bc3549cfe63f4aa Mon Sep 17 00:00:00 2001 From: Eder Monteiro Date: Tue, 30 Jun 2026 19:41:04 -0300 Subject: [PATCH 2/9] grt/cugr: update GlobalRouter's netlist with CUGR net updates Signed-off-by: Eder Monteiro --- src/grt/src/GlobalRouter.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/grt/src/GlobalRouter.cpp b/src/grt/src/GlobalRouter.cpp index c757ae2770..3733d0d67f 100644 --- a/src/grt/src/GlobalRouter.cpp +++ b/src/grt/src/GlobalRouter.cpp @@ -6351,12 +6351,20 @@ std::vector GlobalRouter::updateDirtyRoutes(bool save_guides) if (use_cugr_) { cugr_->setVerbose(false); - for (odb::dbNet* net : dirty_nets_) { - cugr_->updateNet(net); + for (odb::dbNet* db_net : dirty_nets_) { + // Rebuild the GlobalRouter pin set from the netlist (as updateDirtyNets + // does for FastRoute); updatePinAccessPoints below fixes the positions. + Net* net = getNet(db_net); + updateNetPins(net); + net->setDirtyNet(false); + net->clearLastPinPositions(); + cugr_->updateNet(db_net); } dirty_nets_.clear(); cugr_->routeIncremental(); routes_ = cugr_->getRoutes(); + // Sync pin access points with CUGR's routing, as the full route does. + updatePinAccessPoints(); return {}; } From 473e7aa67ea6ca8cadf160298297e87b41c03c33 Mon Sep 17 00:00:00 2001 From: Eder Monteiro Date: Tue, 30 Jun 2026 20:09:06 -0300 Subject: [PATCH 3/9] grt/cugr: limit incremental routing to dirty nets and skip global parasitics rebuild Signed-off-by: Eder Monteiro --- src/grt/src/cugr/include/CUGR.h | 7 ++++- src/grt/src/cugr/src/CUGR.cpp | 46 ++++++++++++++++++--------------- 2 files changed, 31 insertions(+), 22 deletions(-) diff --git a/src/grt/src/cugr/include/CUGR.h b/src/grt/src/cugr/include/CUGR.h index 6a28609f33..e4e7c7b6b5 100644 --- a/src/grt/src/cugr/include/CUGR.h +++ b/src/grt/src/cugr/include/CUGR.h @@ -90,7 +90,7 @@ class CUGR void init(int min_routing_layer, int max_routing_layer, const odb::PtrSet& clock_nets); - void route(); + void route(bool incremental = false); void write(const std::string& guide_file); NetRouteMap getRoutes(); void updateDbCongestion(); @@ -263,6 +263,11 @@ class CUGR int congestion_iterations_ = 5; bool verbose_ = true; + // True while routeIncremental() is running. Like FastRoute's is_incremental_, + // it suppresses the global estimateAllGlobalRouteParasitics() in + // updateNetSlacks(), which would otherwise wipe the resizer's parasitics. + bool incremental_routing_ = false; + bool resistance_aware_ = false; // Per-run normalisers for getResAwareScore (default 1 => well-defined). float worst_slack_ = 1.0f; diff --git a/src/grt/src/cugr/src/CUGR.cpp b/src/grt/src/cugr/src/CUGR.cpp index 0ce0ca803a..8108ae4055 100644 --- a/src/grt/src/cugr/src/CUGR.cpp +++ b/src/grt/src/cugr/src/CUGR.cpp @@ -124,8 +124,13 @@ void CUGR::updateCriticalNets() void CUGR::updateNetSlacks() { - if (auto* estimator = service_registry_->find()) { - estimator->estimateAllGlobalRouteParasitics(); + // During incremental routing the resizer maintains the parasitics; a global + // re-estimate here would clear and rebuild all of them, corrupting timing. + // Mirror FastRoute's updateSlacks(), which skips this when incremental. + if (!incremental_routing_) { + if (auto* estimator = service_registry_->find()) { + estimator->estimateAllGlobalRouteParasitics(); + } } for (const auto& net : gr_nets_) { if (net == nullptr) { @@ -599,7 +604,7 @@ void CUGR::mazeRoute(std::vector& net_indices) updateCongestedNets(net_indices); } -void CUGR::route() +void CUGR::route(bool incremental) { if (resistance_aware_ && critical_nets_percentage_ == 0) { logger_->warn(GRT, @@ -622,6 +627,22 @@ void CUGR::route() } } + if (incremental) { + // Reroute only the requested nets. The global stages (res-aware re-route, + // iterative RRR) and the per-stage updateCongestedNets expansion would rip + // up nets the caller never marked dirty, desyncing the resizer's + // parasitics. Pin the set to the dirty nets across the pattern + maze + // stages instead. + incremental_routing_ = true; + const std::vector dirty_nets = net_indices; + patternRoute(net_indices); + net_indices = dirty_nets; + mazeRoute(net_indices); + incremental_routing_ = false; + printStatistics(); + return; + } + patternRoute(net_indices); // Stage 2: resistance-aware re-route of the critical nets (always runs, @@ -1558,24 +1579,7 @@ void CUGR::routeIncremental() return; } - std::vector initial_nets = nets_to_route_; - std::ranges::sort(initial_nets); - auto [first, last] = std::ranges::unique(initial_nets); - initial_nets.erase(first, last); - - route(); - - std::vector overflow_nets; - updateCongestedNets(overflow_nets); - std::vector secondary_nets; - std::ranges::set_difference( - overflow_nets, initial_nets, std::back_inserter(secondary_nets)); - if (!secondary_nets.empty()) { - for (int idx : secondary_nets) { - addDirtyNet(gr_nets_[idx]->getDbNet()); - } - route(); - } + route(/*incremental=*/true); } } // namespace grt \ No newline at end of file From 2e794f0eb879ba20ea1112e1724b4b0de090a98a Mon Sep 17 00:00:00 2001 From: Eder Monteiro Date: Tue, 30 Jun 2026 20:09:06 -0300 Subject: [PATCH 4/9] grt/cugr: sync only dirty-net pin access points during incremental routing Signed-off-by: Eder Monteiro --- src/grt/include/grt/GlobalRouter.h | 1 + src/grt/src/GlobalRouter.cpp | 59 +++++++++++++++++------------- 2 files changed, 35 insertions(+), 25 deletions(-) diff --git a/src/grt/include/grt/GlobalRouter.h b/src/grt/include/grt/GlobalRouter.h index 8f71e1b7c6..bbac7b5549 100644 --- a/src/grt/include/grt/GlobalRouter.h +++ b/src/grt/include/grt/GlobalRouter.h @@ -429,6 +429,7 @@ class GlobalRouter odb::Point& pos_on_grid, bool has_access_points); void updatePinAccessPoints(); + void updatePinAccessPoints(Net* net, odb::dbNet* db_net); void suggestAdjustment(); void findFastRoutePins(Net* net, std::vector& pins_on_grid, diff --git a/src/grt/src/GlobalRouter.cpp b/src/grt/src/GlobalRouter.cpp index 3733d0d67f..803a28103a 100644 --- a/src/grt/src/GlobalRouter.cpp +++ b/src/grt/src/GlobalRouter.cpp @@ -1523,33 +1523,38 @@ void GlobalRouter::computePinPositionOnGrid( pin.setConnectionLayer(pin_position.layer()); } -void GlobalRouter::updatePinAccessPoints() +void GlobalRouter::updatePinAccessPoints(Net* net, odb::dbNet* db_net) { - for (const auto& [db_net, net] : db_net_map_) { - odb::PtrMap iterm_to_aps; - odb::PtrMap bterm_to_aps; - cugr_->getITermsAccessPoints(db_net, iterm_to_aps); - cugr_->getBTermsAccessPoints(db_net, bterm_to_aps); - - auto updatePinPos = [&](Pin& pin, auto* term, const auto& ap_map) { - if (auto it = ap_map.find(term); it != ap_map.end()) { - const auto& ap = it->second; - pin.setConnectionLayer(ap.z()); - pin.setOnGridPosition( - grid_->getPositionOnGrid(odb::Point(ap.x(), ap.y()))); - } - }; + odb::PtrMap iterm_to_aps; + odb::PtrMap bterm_to_aps; + cugr_->getITermsAccessPoints(db_net, iterm_to_aps); + cugr_->getBTermsAccessPoints(db_net, bterm_to_aps); - for (Pin& pin : net->getPins()) { - if (pin.isPort()) { - updatePinPos(pin, pin.getBTerm(), bterm_to_aps); - } else { - updatePinPos(pin, pin.getITerm(), iterm_to_aps); - } + auto updatePinPos = [&](Pin& pin, auto* term, const auto& ap_map) { + if (auto it = ap_map.find(term); it != ap_map.end()) { + const auto& ap = it->second; + pin.setConnectionLayer(ap.z()); + pin.setOnGridPosition( + grid_->getPositionOnGrid(odb::Point(ap.x(), ap.y()))); + } + }; + + for (Pin& pin : net->getPins()) { + if (pin.isPort()) { + updatePinPos(pin, pin.getBTerm(), bterm_to_aps); + } else { + updatePinPos(pin, pin.getITerm(), iterm_to_aps); } } } +void GlobalRouter::updatePinAccessPoints() +{ + for (const auto& [db_net, net] : db_net_map_) { + updatePinAccessPoints(net, db_net); + } +} + int GlobalRouter::getNetMaxRoutingLayer(const Net* net) { return net->getSignalType() == odb::dbSigType::CLOCK @@ -6351,9 +6356,11 @@ std::vector GlobalRouter::updateDirtyRoutes(bool save_guides) if (use_cugr_) { cugr_->setVerbose(false); - for (odb::dbNet* db_net : dirty_nets_) { + const std::vector dirty_nets(dirty_nets_.begin(), + dirty_nets_.end()); + for (odb::dbNet* db_net : dirty_nets) { // Rebuild the GlobalRouter pin set from the netlist (as updateDirtyNets - // does for FastRoute); updatePinAccessPoints below fixes the positions. + // does for FastRoute); the pin access point sync below fixes positions. Net* net = getNet(db_net); updateNetPins(net); net->setDirtyNet(false); @@ -6363,8 +6370,10 @@ std::vector GlobalRouter::updateDirtyRoutes(bool save_guides) dirty_nets_.clear(); cugr_->routeIncremental(); routes_ = cugr_->getRoutes(); - // Sync pin access points with CUGR's routing, as the full route does. - updatePinAccessPoints(); + // Sync pin access points only for the rerouted nets (full route syncs all). + for (odb::dbNet* db_net : dirty_nets) { + updatePinAccessPoints(getNet(db_net), db_net); + } return {}; } From eef5b966c53288c06413a541ed3aa42dbe09d8b7 Mon Sep 17 00:00:00 2001 From: Eder Monteiro Date: Tue, 30 Jun 2026 22:19:57 -0300 Subject: [PATCH 5/9] grt/cugr: scope incremental slack and res-aware updates to dirty nets Signed-off-by: Eder Monteiro --- src/grt/src/cugr/include/CUGR.h | 8 +-- src/grt/src/cugr/src/CUGR.cpp | 90 ++++++++++++++++++++++++--------- 2 files changed, 69 insertions(+), 29 deletions(-) diff --git a/src/grt/src/cugr/include/CUGR.h b/src/grt/src/cugr/include/CUGR.h index e4e7c7b6b5..acd40586ba 100644 --- a/src/grt/src/cugr/include/CUGR.h +++ b/src/grt/src/cugr/include/CUGR.h @@ -137,16 +137,16 @@ class CUGR private: // Refresh net slacks, re-mark the res-aware/critical set, and demote // non-critical nets so the next stage routes critical nets first. - void updateCriticalNets(); + void updateCriticalNets(const std::vector& net_indices); // Re-extract parasitics and refresh every net's slack from the routing. - void updateNetSlacks(); + void updateNetSlacks(const std::vector& net_indices); // Slack value at the critical_nets_percentage_ percentile of the nets. float criticalSlackThreshold() const; // Push nets with slack above the threshold to the back of the default // ordering by maxing their slack; res-aware nets are exempt. void demoteNonCriticalNets(float slack_th); float getNetSlack(odb::dbNet* net); - void setInitialNetSlacks(); + void setInitialNetSlacks(const std::vector& net_indices); /** * @brief Computes per-layer NDR demand / cost multipliers for a net. @@ -281,7 +281,7 @@ class CUGR // Select the res-aware net set (like FastRoute updateSlacks) and refresh the // worst_* normalisers; no-op unless resistance_aware_. - void markResAwareNets(); + void markResAwareNets(const std::vector& net_indices); // FR-style ordering score (lower routes first): slack/resistance/fanout/ // length blend, each normalised by the per-run worst. diff --git a/src/grt/src/cugr/src/CUGR.cpp b/src/grt/src/cugr/src/CUGR.cpp index 8108ae4055..8188c9e454 100644 --- a/src/grt/src/cugr/src/CUGR.cpp +++ b/src/grt/src/cugr/src/CUGR.cpp @@ -114,23 +114,37 @@ void CUGR::init(const int min_routing_layer, } } -void CUGR::updateCriticalNets() +void CUGR::updateCriticalNets(const std::vector& net_indices) { - updateNetSlacks(); + updateNetSlacks(net_indices); // Mark res-aware nets on the real slack, before demotion clobbers it. - markResAwareNets(); - demoteNonCriticalNets(criticalSlackThreshold()); + markResAwareNets(net_indices); + // Demotion re-ranks the whole design against a global slack percentile. + // During incremental routing we only touch the dirty nets, so skip it and + // mark all incremental candidates res-aware instead (like FastRoute). + if (!incremental_routing_) { + demoteNonCriticalNets(criticalSlackThreshold()); + } } -void CUGR::updateNetSlacks() +void CUGR::updateNetSlacks(const std::vector& net_indices) { - // During incremental routing the resizer maintains the parasitics; a global - // re-estimate here would clear and rebuild all of them, corrupting timing. - // Mirror FastRoute's updateSlacks(), which skips this when incremental. - if (!incremental_routing_) { - if (auto* estimator = service_registry_->find()) { - estimator->estimateAllGlobalRouteParasitics(); + if (incremental_routing_) { + // The resizer maintains the parasitics/slacks for the rest of the design; + // only refresh the rerouted nets. Skipping the global + // estimateAllGlobalRouteParasitics() (which clears and rebuilds all + // parasitics) also avoids corrupting the resizer's timing. Mirrors + // FastRoute's updateSlacks(), which iterates only the nets being routed. + for (const int net_index : net_indices) { + GRNet* net = gr_nets_[net_index].get(); + if (net != nullptr) { + net->setSlack(getNetSlack(net->getDbNet())); + } } + return; + } + if (auto* estimator = service_registry_->find()) { + estimator->estimateAllGlobalRouteParasitics(); } for (const auto& net : gr_nets_) { if (net == nullptr) { @@ -178,10 +192,20 @@ float CUGR::getNetSlack(odb::dbNet* net) return sta_->slack(net, sta::MinMax::max()); } -void CUGR::setInitialNetSlacks() +void CUGR::setInitialNetSlacks(const std::vector& net_indices) { // Stage 1 routes neutrally; this only computes placement slacks. Res-aware // marking happens in patternRouteResAware() once real 3D trees exist. + // During incremental routing only the rerouted nets need refreshing. + if (incremental_routing_) { + for (const int net_index : net_indices) { + GRNet* net = gr_nets_[net_index].get(); + if (net != nullptr) { + net->setSlack(getNetSlack(net->getDbNet())); + } + } + return; + } for (const auto& net : gr_nets_) { if (net == nullptr) { continue; @@ -191,7 +215,7 @@ void CUGR::setInitialNetSlacks() } } -void CUGR::markResAwareNets() +void CUGR::markResAwareNets(const std::vector& net_indices) { if (!resistance_aware_) { return; @@ -211,10 +235,7 @@ void CUGR::markResAwareNets() // eligible candidates (skip short/single-pin/positive-slack, like FastRoute). std::vector candidates; candidates.reserve(gr_nets_.size()); - for (const auto& net : gr_nets_) { - if (net == nullptr) { - continue; - } + auto collect = [&](GRNet* net) { const auto& tree = net->getRoutingTree(); net->setResistance( grid_graph_->getNetResistance(tree, net->getNdrWidths())); @@ -232,7 +253,7 @@ void CUGR::markResAwareNets() const bool is_short = net->getNetLength() <= constants_.resistance_min_net_length; if (net->getNumPins() < 2 || is_short || is_positive_slack) { - continue; + return; } worst_resistance_ = std::max(worst_resistance_, net->getResistance()); @@ -248,10 +269,27 @@ void CUGR::markResAwareNets() // Skip already-marked nets so the res-aware set accumulates. candidates.push_back(net->getIndex()); } + }; + + // During incremental routing consider only the rerouted nets; the full route + // considers the whole design. + if (incremental_routing_) { + for (const int net_index : net_indices) { + if (gr_nets_[net_index] != nullptr) { + collect(gr_nets_[net_index].get()); + } + } + } else { + for (const auto& net : gr_nets_) { + if (net != nullptr) { + collect(net.get()); + } + } } // Pass 2: rank eligible candidates by the multi-factor res-aware score - // (lower = more critical) and mark the most critical `percentage`. + // (lower = more critical) and mark the most critical `percentage`. During + // incremental all candidates are marked (like FastRoute's percentage == 1). std::vector> scored; scored.reserve(candidates.size()); for (const int index : candidates) { @@ -260,8 +298,10 @@ void CUGR::markResAwareNets() std::ranges::stable_sort(scored, [](const auto& lhs, const auto& rhs) { return std::tie(lhs.second, lhs.first) < std::tie(rhs.second, rhs.first); }); - const int count = static_cast( - std::ceil(scored.size() * res_aware_percentage_ / 100)); + const int count = incremental_routing_ + ? static_cast(scored.size()) + : static_cast(std::ceil( + scored.size() * res_aware_percentage_ / 100)); for (int i = 0; i < count && std::cmp_less(i, scored.size()); i++) { gr_nets_[scored[i].first]->setResAware(true); } @@ -404,7 +444,7 @@ void CUGR::patternRoute(std::vector& net_indices) } if (critical_nets_percentage_ != 0) { - setInitialNetSlacks(); + setInitialNetSlacks(net_indices); } // Stage 1 is neutral: order by the default slack/bbox key, no res-aware. @@ -448,7 +488,7 @@ void CUGR::patternRouteResAware(std::vector& net_indices) // Stage 1 routed neutrally, so real 3D trees now exist; mark the res-aware // set from their actual per-net resistance. - updateCriticalNets(); + updateCriticalNets(net_indices); std::vector res_aware_nets; for (const auto& net : gr_nets_) { @@ -511,7 +551,7 @@ void CUGR::patternRouteWithDetours(std::vector& net_indices) } if (critical_nets_percentage_ != 0) { - updateCriticalNets(); + updateCriticalNets(net_indices); } // (2d) direction -> x -> y -> has overflow? @@ -548,7 +588,7 @@ void CUGR::mazeRoute(std::vector& net_indices) } if (critical_nets_percentage_ != 0) { - updateCriticalNets(); + updateCriticalNets(net_indices); } for (const int net_index : net_indices) { From 4b8030b3a8c999ecbe978600a1689caa4e4a51ee Mon Sep 17 00:00:00 2001 From: Eder Monteiro Date: Tue, 30 Jun 2026 22:25:44 -0300 Subject: [PATCH 6/9] grt/cugr: shorten incremental routing comments to single lines Signed-off-by: Eder Monteiro --- src/grt/src/GlobalRouter.cpp | 3 +-- src/grt/src/cugr/include/CUGR.h | 4 +--- src/grt/src/cugr/src/CUGR.cpp | 23 ++++++----------------- 3 files changed, 8 insertions(+), 22 deletions(-) diff --git a/src/grt/src/GlobalRouter.cpp b/src/grt/src/GlobalRouter.cpp index 803a28103a..5f5a69bb2b 100644 --- a/src/grt/src/GlobalRouter.cpp +++ b/src/grt/src/GlobalRouter.cpp @@ -6359,8 +6359,7 @@ std::vector GlobalRouter::updateDirtyRoutes(bool save_guides) const std::vector dirty_nets(dirty_nets_.begin(), dirty_nets_.end()); for (odb::dbNet* db_net : dirty_nets) { - // Rebuild the GlobalRouter pin set from the netlist (as updateDirtyNets - // does for FastRoute); the pin access point sync below fixes positions. + // Rebuild the pin set from the netlist; positions are synced below. Net* net = getNet(db_net); updateNetPins(net); net->setDirtyNet(false); diff --git a/src/grt/src/cugr/include/CUGR.h b/src/grt/src/cugr/include/CUGR.h index acd40586ba..133c713f84 100644 --- a/src/grt/src/cugr/include/CUGR.h +++ b/src/grt/src/cugr/include/CUGR.h @@ -263,9 +263,7 @@ class CUGR int congestion_iterations_ = 5; bool verbose_ = true; - // True while routeIncremental() is running. Like FastRoute's is_incremental_, - // it suppresses the global estimateAllGlobalRouteParasitics() in - // updateNetSlacks(), which would otherwise wipe the resizer's parasitics. + // Suppresses the global parasitics re-estimate during incremental routing. bool incremental_routing_ = false; bool resistance_aware_ = false; diff --git a/src/grt/src/cugr/src/CUGR.cpp b/src/grt/src/cugr/src/CUGR.cpp index 8188c9e454..fdb69939f4 100644 --- a/src/grt/src/cugr/src/CUGR.cpp +++ b/src/grt/src/cugr/src/CUGR.cpp @@ -119,9 +119,7 @@ void CUGR::updateCriticalNets(const std::vector& net_indices) updateNetSlacks(net_indices); // Mark res-aware nets on the real slack, before demotion clobbers it. markResAwareNets(net_indices); - // Demotion re-ranks the whole design against a global slack percentile. - // During incremental routing we only touch the dirty nets, so skip it and - // mark all incremental candidates res-aware instead (like FastRoute). + // Demotion uses a global slack percentile; skip it during incremental. if (!incremental_routing_) { demoteNonCriticalNets(criticalSlackThreshold()); } @@ -130,11 +128,7 @@ void CUGR::updateCriticalNets(const std::vector& net_indices) void CUGR::updateNetSlacks(const std::vector& net_indices) { if (incremental_routing_) { - // The resizer maintains the parasitics/slacks for the rest of the design; - // only refresh the rerouted nets. Skipping the global - // estimateAllGlobalRouteParasitics() (which clears and rebuilds all - // parasitics) also avoids corrupting the resizer's timing. Mirrors - // FastRoute's updateSlacks(), which iterates only the nets being routed. + // Only refresh the rerouted nets; skip the global parasitics re-estimate. for (const int net_index : net_indices) { GRNet* net = gr_nets_[net_index].get(); if (net != nullptr) { @@ -271,8 +265,7 @@ void CUGR::markResAwareNets(const std::vector& net_indices) } }; - // During incremental routing consider only the rerouted nets; the full route - // considers the whole design. + // Incremental routing considers only the rerouted nets. if (incremental_routing_) { for (const int net_index : net_indices) { if (gr_nets_[net_index] != nullptr) { @@ -288,8 +281,7 @@ void CUGR::markResAwareNets(const std::vector& net_indices) } // Pass 2: rank eligible candidates by the multi-factor res-aware score - // (lower = more critical) and mark the most critical `percentage`. During - // incremental all candidates are marked (like FastRoute's percentage == 1). + // (lower = more critical) and mark the most critical `percentage`. std::vector> scored; scored.reserve(candidates.size()); for (const int index : candidates) { @@ -298,6 +290,7 @@ void CUGR::markResAwareNets(const std::vector& net_indices) std::ranges::stable_sort(scored, [](const auto& lhs, const auto& rhs) { return std::tie(lhs.second, lhs.first) < std::tie(rhs.second, rhs.first); }); + // Incremental marks all candidates (like FastRoute's percentage == 1). const int count = incremental_routing_ ? static_cast(scored.size()) : static_cast(std::ceil( @@ -668,11 +661,7 @@ void CUGR::route(bool incremental) } if (incremental) { - // Reroute only the requested nets. The global stages (res-aware re-route, - // iterative RRR) and the per-stage updateCongestedNets expansion would rip - // up nets the caller never marked dirty, desyncing the resizer's - // parasitics. Pin the set to the dirty nets across the pattern + maze - // stages instead. + // Reroute only the dirty nets: pattern + maze, skipping the global stages. incremental_routing_ = true; const std::vector dirty_nets = net_indices; patternRoute(net_indices); From 3e23ab802584fd535ec25f61f7c9f6dc21812017 Mon Sep 17 00:00:00 2001 From: Eder Monteiro Date: Tue, 30 Jun 2026 22:31:48 -0300 Subject: [PATCH 7/9] grt/cugr: patch only rerouted nets into routes_ during incremental routing Signed-off-by: Eder Monteiro --- src/grt/src/GlobalRouter.cpp | 10 ++- src/grt/src/cugr/include/CUGR.h | 5 ++ src/grt/src/cugr/src/CUGR.cpp | 105 ++++++++++++++++++-------------- 3 files changed, 72 insertions(+), 48 deletions(-) diff --git a/src/grt/src/GlobalRouter.cpp b/src/grt/src/GlobalRouter.cpp index 5f5a69bb2b..3892f84fb6 100644 --- a/src/grt/src/GlobalRouter.cpp +++ b/src/grt/src/GlobalRouter.cpp @@ -6368,9 +6368,15 @@ std::vector GlobalRouter::updateDirtyRoutes(bool save_guides) } dirty_nets_.clear(); cugr_->routeIncremental(); - routes_ = cugr_->getRoutes(); - // Sync pin access points only for the rerouted nets (full route syncs all). + // Patch only the rerouted nets into routes_ instead of rebuilding the whole + // map, and sync pin access points for the same nets (full route does all). for (odb::dbNet* db_net : dirty_nets) { + GRoute route = cugr_->getNetRoute(db_net); + if (route.empty()) { + routes_.erase(db_net); + } else { + routes_[db_net] = std::move(route); + } updatePinAccessPoints(getNet(db_net), db_net); } return {}; diff --git a/src/grt/src/cugr/include/CUGR.h b/src/grt/src/cugr/include/CUGR.h index 133c713f84..94dee6daed 100644 --- a/src/grt/src/cugr/include/CUGR.h +++ b/src/grt/src/cugr/include/CUGR.h @@ -93,6 +93,9 @@ class CUGR void route(bool incremental = false); void write(const std::string& guide_file); NetRouteMap getRoutes(); + // Route for a single net, so incremental updates patch only the dirty nets + // instead of rebuilding the whole map. + GRoute getNetRoute(odb::dbNet* db_net); void updateDbCongestion(); void getITermsAccessPoints( odb::dbNet* net, @@ -218,6 +221,8 @@ class CUGR bool res_aware_order) const; void getGuides(const GRNet* net, std::vector>& guides); + // Append net's routing tree to route as GRoute segments. + void buildNetRoute(const GRNet* net, GRoute& route) const; void printStatistics() const; /** diff --git a/src/grt/src/cugr/src/CUGR.cpp b/src/grt/src/cugr/src/CUGR.cpp index fdb69939f4..f786f79489 100644 --- a/src/grt/src/cugr/src/CUGR.cpp +++ b/src/grt/src/cugr/src/CUGR.cpp @@ -923,6 +923,52 @@ void CUGR::write(const std::string& guide_file) fout.close(); } +void CUGR::buildNetRoute(const GRNet* net, GRoute& route) const +{ + const auto& routing_tree = net->getRoutingTree(); + if (!routing_tree) { + return; + } + + const int half_gcell = design_->getGridlineSize() / 2; + GRTreeNode::preorder( + routing_tree, [&](const std::shared_ptr& node) { + for (const auto& child : node->getChildren()) { + if (node->getLayerIdx() == child->getLayerIdx()) { + auto [min_x, max_x] = std::minmax({node->x(), child->x()}); + auto [min_y, max_y] = std::minmax({node->y(), child->y()}); + + // convert to dbu + min_x = grid_graph_->getGridline(0, min_x) + half_gcell; + min_y = grid_graph_->getGridline(1, min_y) + half_gcell; + max_x = grid_graph_->getGridline(0, max_x) + half_gcell; + max_y = grid_graph_->getGridline(1, max_y) + half_gcell; + + route.emplace_back(min_x, + min_y, + node->getLayerIdx() + 1, + max_x, + max_y, + child->getLayerIdx() + 1, + false); + route.back().setIs3DRoute(true); + } else { + const auto [bottom_layer, top_layer] + = std::minmax({node->getLayerIdx(), child->getLayerIdx()}); + for (int layer_idx = bottom_layer; layer_idx < top_layer; + layer_idx++) { + const int x = grid_graph_->getGridline(0, node->x()) + half_gcell; + const int y = grid_graph_->getGridline(1, node->y()) + half_gcell; + + route.emplace_back( + x, y, layer_idx + 1, x, y, layer_idx + 2, true); + route.back().setIs3DRoute(true); + } + } + } + }); +} + NetRouteMap CUGR::getRoutes() { NetRouteMap routes; @@ -933,56 +979,23 @@ NetRouteMap CUGR::getRoutes() if (net->getNumPins() < 2 || net->isLocal()) { continue; } - odb::dbNet* db_net = net->getDbNet(); - GRoute& route = routes[db_net]; + buildNetRoute(net.get(), routes[net->getDbNet()]); + } - const int half_gcell = design_->getGridlineSize() / 2; + return routes; +} - auto& routing_tree = net->getRoutingTree(); - if (!routing_tree) { - continue; +GRoute CUGR::getNetRoute(odb::dbNet* db_net) +{ + GRoute route; + auto it = db_net_map_.find(db_net); + if (it != db_net_map_.end()) { + const GRNet* net = it->second; + if (net != nullptr && net->getNumPins() >= 2 && !net->isLocal()) { + buildNetRoute(net, route); } - GRTreeNode::preorder( - routing_tree, [&](const std::shared_ptr& node) { - for (const auto& child : node->getChildren()) { - if (node->getLayerIdx() == child->getLayerIdx()) { - auto [min_x, max_x] = std::minmax({node->x(), child->x()}); - auto [min_y, max_y] = std::minmax({node->y(), child->y()}); - - // convert to dbu - min_x = grid_graph_->getGridline(0, min_x) + half_gcell; - min_y = grid_graph_->getGridline(1, min_y) + half_gcell; - max_x = grid_graph_->getGridline(0, max_x) + half_gcell; - max_y = grid_graph_->getGridline(1, max_y) + half_gcell; - - route.emplace_back(min_x, - min_y, - node->getLayerIdx() + 1, - max_x, - max_y, - child->getLayerIdx() + 1, - false); - route.back().setIs3DRoute(true); - } else { - const auto [bottom_layer, top_layer] - = std::minmax({node->getLayerIdx(), child->getLayerIdx()}); - for (int layer_idx = bottom_layer; layer_idx < top_layer; - layer_idx++) { - const int x - = grid_graph_->getGridline(0, node->x()) + half_gcell; - const int y - = grid_graph_->getGridline(1, node->y()) + half_gcell; - - route.emplace_back( - x, y, layer_idx + 1, x, y, layer_idx + 2, true); - route.back().setIs3DRoute(true); - } - } - } - }); } - - return routes; + return route; } void CUGR::sortNetIndices(std::vector& net_indices, From 10ee2a698698b0cd341500e3bea04672f47fcc7b Mon Sep 17 00:00:00 2001 From: Eder Monteiro Date: Tue, 30 Jun 2026 22:48:47 -0300 Subject: [PATCH 8/9] grt/cugr: skip global congestion scan in pattern/maze during incremental Signed-off-by: Eder Monteiro --- src/grt/src/cugr/src/CUGR.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/grt/src/cugr/src/CUGR.cpp b/src/grt/src/cugr/src/CUGR.cpp index f786f79489..9f842e1b08 100644 --- a/src/grt/src/cugr/src/CUGR.cpp +++ b/src/grt/src/cugr/src/CUGR.cpp @@ -467,7 +467,10 @@ void CUGR::patternRoute(std::vector& net_indices) gr_nets_[net_index]->getNdrCosts()); } - updateCongestedNets(net_indices); + // The congested set feeds the next global stage; incremental discards it. + if (!incremental_routing_) { + updateCongestedNets(net_indices); + } } void CUGR::patternRouteResAware(std::vector& net_indices) @@ -634,7 +637,10 @@ void CUGR::mazeRoute(std::vector& net_indices) grid.step(); } - updateCongestedNets(net_indices); + // The congested set feeds the next global stage; incremental discards it. + if (!incremental_routing_) { + updateCongestedNets(net_indices); + } } void CUGR::route(bool incremental) From abfdd12584abde5a1a20b07ba8c95815b5fcaf10 Mon Sep 17 00:00:00 2001 From: Eder Monteiro Date: Thu, 2 Jul 2026 11:40:13 -0300 Subject: [PATCH 9/9] grt/cugr: warn once when incremental routing leaves congestion Signed-off-by: Eder Monteiro --- src/grt/include/grt/GlobalRouter.h | 2 ++ src/grt/src/GlobalRouter.cpp | 18 ++++++++++++++++++ src/grt/src/cugr/src/Design.cpp | 1 + 3 files changed, 21 insertions(+) diff --git a/src/grt/include/grt/GlobalRouter.h b/src/grt/include/grt/GlobalRouter.h index bbac7b5549..173679b2db 100644 --- a/src/grt/include/grt/GlobalRouter.h +++ b/src/grt/include/grt/GlobalRouter.h @@ -201,6 +201,8 @@ class GlobalRouter std::vector routeLayerLengths(odb::dbNet* db_net); void startIncremental(); void endIncremental(bool save_guides = false); + // Warn once, at the end of an incremental session, if CUGR left congestion. + void reportIncrementalCongestion(); void globalRoute(bool save_guides = false); void saveCongestion(); NetRouteMap& getRoutes(); diff --git a/src/grt/src/GlobalRouter.cpp b/src/grt/src/GlobalRouter.cpp index 3892f84fb6..6d0675f39d 100644 --- a/src/grt/src/GlobalRouter.cpp +++ b/src/grt/src/GlobalRouter.cpp @@ -441,12 +441,29 @@ void GlobalRouter::endIncremental(bool save_guides) { is_incremental_ = true; updateDirtyRoutes(save_guides); + reportIncrementalCongestion(); grouter_cbk_->removeOwner(); delete grouter_cbk_; grouter_cbk_ = nullptr; finishGlobalRouting(save_guides); } +void GlobalRouter::reportIncrementalCongestion() +{ + // CUGR incremental reroutes only dirty nets and does not recover congestion + // it induces on neighboring nets, so surface any residual overflow once. + if (!use_cugr_ || cugr_ == nullptr) { + return; + } + if (cugr_->totalOverflow() > 0) { + is_congested_ = true; + logger_->warn(GRT, + 128, + "Incremental global routing finished with congestion. Check " + "the congestion regions in the DRC Viewer."); + } +} + void GlobalRouter::globalRoute(bool save_guides) { utl::Timer timer; @@ -6323,6 +6340,7 @@ std::vector IncrementalGRoute::updateRoutes(bool save_guides) IncrementalGRoute::~IncrementalGRoute() { + groute_->reportIncrementalCongestion(); db_cbk_.removeOwner(); } diff --git a/src/grt/src/cugr/src/Design.cpp b/src/grt/src/cugr/src/Design.cpp index 1670c45735..28e0de36cb 100644 --- a/src/grt/src/cugr/src/Design.cpp +++ b/src/grt/src/cugr/src/Design.cpp @@ -193,6 +193,7 @@ void Design::removeNet(odb::dbNet* db_net) db_net_to_id_.erase(it); } } + void Design::readInstanceObstructions() { for (odb::dbInst* db_inst : block_->getInsts()) {