diff --git a/src/grt/include/grt/GlobalRouter.h b/src/grt/include/grt/GlobalRouter.h index 8f71e1b7c6..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(); @@ -429,6 +431,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 cb7e854d3b..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; @@ -1523,30 +1540,35 @@ 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); } } @@ -4785,6 +4807,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) { @@ -6311,6 +6340,7 @@ std::vector IncrementalGRoute::updateRoutes(bool save_guides) IncrementalGRoute::~IncrementalGRoute() { + groute_->reportIncrementalCongestion(); db_cbk_.removeOwner(); } @@ -6344,12 +6374,29 @@ std::vector GlobalRouter::updateDirtyRoutes(bool save_guides) if (use_cugr_) { cugr_->setVerbose(false); - for (odb::dbNet* net : dirty_nets_) { - cugr_->updateNet(net); + const std::vector dirty_nets(dirty_nets_.begin(), + dirty_nets_.end()); + for (odb::dbNet* db_net : dirty_nets) { + // Rebuild the pin set from the netlist; positions are synced below. + Net* net = getNet(db_net); + updateNetPins(net); + net->setDirtyNet(false); + net->clearLastPinPositions(); + cugr_->updateNet(db_net); } dirty_nets_.clear(); cugr_->routeIncremental(); - routes_ = cugr_->getRoutes(); + // 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 6a28609f33..94dee6daed 100644 --- a/src/grt/src/cugr/include/CUGR.h +++ b/src/grt/src/cugr/include/CUGR.h @@ -90,9 +90,12 @@ 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(); + // 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, @@ -137,16 +140,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. @@ -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; /** @@ -263,6 +268,9 @@ class CUGR int congestion_iterations_ = 5; bool verbose_ = true; + // Suppresses the global parasitics re-estimate during incremental routing. + bool incremental_routing_ = false; + bool resistance_aware_ = false; // Per-run normalisers for getResAwareScore (default 1 => well-defined). float worst_slack_ = 1.0f; @@ -276,7 +284,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 0ce0ca803a..9f842e1b08 100644 --- a/src/grt/src/cugr/src/CUGR.cpp +++ b/src/grt/src/cugr/src/CUGR.cpp @@ -114,16 +114,29 @@ 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 uses a global slack percentile; skip it during incremental. + if (!incremental_routing_) { + demoteNonCriticalNets(criticalSlackThreshold()); + } } -void CUGR::updateNetSlacks() +void CUGR::updateNetSlacks(const std::vector& net_indices) { + if (incremental_routing_) { + // 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) { + net->setSlack(getNetSlack(net->getDbNet())); + } + } + return; + } if (auto* estimator = service_registry_->find()) { estimator->estimateAllGlobalRouteParasitics(); } @@ -173,10 +186,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; @@ -186,7 +209,7 @@ void CUGR::setInitialNetSlacks() } } -void CUGR::markResAwareNets() +void CUGR::markResAwareNets(const std::vector& net_indices) { if (!resistance_aware_) { return; @@ -206,10 +229,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())); @@ -227,7 +247,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()); @@ -243,6 +263,21 @@ void CUGR::markResAwareNets() // Skip already-marked nets so the res-aware set accumulates. candidates.push_back(net->getIndex()); } + }; + + // Incremental routing considers only the rerouted nets. + 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 @@ -255,8 +290,11 @@ 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)); + // Incremental marks all candidates (like FastRoute's percentage == 1). + 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); } @@ -399,7 +437,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. @@ -429,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) @@ -443,7 +484,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_) { @@ -506,7 +547,7 @@ void CUGR::patternRouteWithDetours(std::vector& net_indices) } if (critical_nets_percentage_ != 0) { - updateCriticalNets(); + updateCriticalNets(net_indices); } // (2d) direction -> x -> y -> has overflow? @@ -543,7 +584,7 @@ void CUGR::mazeRoute(std::vector& net_indices) } if (critical_nets_percentage_ != 0) { - updateCriticalNets(); + updateCriticalNets(net_indices); } for (const int net_index : net_indices) { @@ -596,10 +637,13 @@ 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() +void CUGR::route(bool incremental) { if (resistance_aware_ && critical_nets_percentage_ == 0) { logger_->warn(GRT, @@ -622,6 +666,18 @@ void CUGR::route() } } + if (incremental) { + // Reroute only the dirty nets: pattern + maze, skipping the global stages. + 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, @@ -873,6 +929,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; @@ -883,56 +985,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, @@ -1558,24 +1627,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 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()) {