Skip to content

grt: implement CUGR net merge mirroring FastRoute path#10899

Open
sparsh-karna wants to merge 18 commits into
The-OpenROAD-Project:masterfrom
sparsh-karna:cugr-merge-net
Open

grt: implement CUGR net merge mirroring FastRoute path#10899
sparsh-karna wants to merge 18 commits into
The-OpenROAD-Project:masterfrom
sparsh-karna:cugr-merge-net

Conversation

@sparsh-karna

Copy link
Copy Markdown
Contributor

Summary

When GlobalRouter::mergeNetsRouting() fires for the CUGR path, this PR enables attempting to stitch the two existing route segments at the former buffer pin position (connectCUGRRouting) rather than ripping up and rerouting the survivor from scratch.

Key changes:

  • GlobalRouter::connectCUGRRouting(preserved, removed): Mirrors connectRouting() but uses cugr_->hasAvailableResources() instead of fastroute_->hasAvailableResources() for the capacity check. Reuses the existing geometry helpers findBufferPinPostions, findTopLayerOverPosition, and createConnectionForPositions, which are FastRoute-agnostic.

  • GlobalRouter::mergeNetsRouting() (CUGR branch): If stitching succeeds, calls cugr_->mergeNet() to transfer tree ownership and save guides. On failure, falls back to addDirtyNet() so the survivor is rerouted incrementally (matching the previous safe behavior).

  • CUGR::mergeNet(preserved, removed): Attaches the removed net's GRTreeNode subtree as a child of the preserved net's routing tree so getRoutes() emits all wire segments. Inserts the removed net into merged_nets_ so removeNet() knows not to decrement GridGraph demand for it (the wires are still physically there, just owned by the preserved net).

  • CUGR::removeNet() (merged-net guard): Skips removeTreeUsage() for nets that were transferred via mergeNet, preventing double-decrement of GridGraph congestion numbers.

  • CUGR::hasAvailableResources(layer, tile_x, tile_y): A thin wrapper over GridGraph::getEdge().getResource() >= 1.0, equivalent to FastRouteCore::hasAvailableResources().

Type of Change

  • New feature

Impact

Improves QoR (Quality of Results) and runtime during incremental routing by preserving existing routing topology during buffer removal ECOs when using CUGR, matching FastRoute's behavior.

Verification

  • I have verified that the local build succeeds (./etc/Build.sh).
  • I have run the relevant tests and they pass. (All 143 GRT regression tests pass.)
  • My code follows the repository's formatting guidelines.
  • I have included tests to prevent regressions.
  • I have signed my commits (DCO).

Related Issues

None.

@sparsh-karna
sparsh-karna requested a review from a team as a code owner July 13, 2026 17:17
@sparsh-karna
sparsh-karna requested a review from jfgava July 13, 2026 17:17

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request implements support for merging nets in CUGR rather than always falling back to a full dirty-net reroute. It introduces connectCUGRRouting to stitch two CUGR routes together at the former buffer pin position and perform capacity checks against the CUGR GridGraph, and updates the CUGR class to handle routing tree ownership transfer during net merges. Feedback was provided to add an early return check if either route is empty to prevent a potential crash when finding the top layer over a position.

Comment thread src/grt/src/GlobalRouter.cpp Outdated
@eder-matheus

Copy link
Copy Markdown
Member

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 98e1a41ad8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/grt/src/cugr/src/CUGR.cpp Outdated
@eder-matheus
eder-matheus self-requested a review July 13, 2026 17:29

@eder-matheus eder-matheus left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please include unit tests in this PR. You can use the existing remove_buffer tests to create the new tests for the CUGR path.

Comment thread src/grt/src/GlobalRouter.cpp Outdated
Comment thread src/grt/src/GlobalRouter.cpp Outdated
Comment thread src/grt/src/cugr/src/CUGR.cpp
Comment thread src/grt/src/cugr/src/CUGR.cpp Outdated
Comment thread src/grt/src/GlobalRouter.cpp Outdated
Comment thread src/grt/src/GlobalRouter.cpp Outdated
Comment thread src/grt/src/GlobalRouter.cpp Outdated
@sparsh-karna

Copy link
Copy Markdown
Contributor Author

Please include unit tests in this PR. You can use the existing remove_buffer tests to create the new tests for the CUGR path.

I used the already available remove_buffer1 and remove_buffer2 tests, and added the -use_cugr flag to check the new merging functionality

@eder-matheus

Copy link
Copy Markdown
Member

@sparsh-karna It seems you have conflicts on the CUGR.cpp file. Please resolve

@sparsh-karna

Copy link
Copy Markdown
Contributor Author

@sparsh-karna It seems you have conflicts on the CUGR.cpp file. Please resolve

I have rebased the branch on top of upstream/master and resolved the conflict in CUGR.cpp.

When GlobalRouter::mergeNetsRouting() fires for the CUGR path, attempt
to stitch the two existing route segments at the former buffer pin
position (connectCUGRRouting) rather than ripping up and rerouting the
survivor from scratch.

Key changes:

  GlobalRouter::connectCUGRRouting(preserved, removed)
    Mirrors connectRouting() but uses cugr_->hasAvailableResources()
    instead of fastroute_->hasAvailableResources() for the capacity
    check.  Reuses the existing geometry helpers findBufferPinPostions,
    findTopLayerOverPosition, and createConnectionForPositions which are
    FastRoute-agnostic.

  GlobalRouter::mergeNetsRouting() -- CUGR branch
    If stitching succeeds, call cugr_->mergeNet() to transfer tree
    ownership and save guides.  On failure, fall back to addDirtyNet so
    the survivor is rerouted incrementally (matching the previous safe
    behaviour).

  CUGR::mergeNet(preserved, removed)
    Attaches the removed net's GRTreeNode subtree as a child of the
    preserved net's routing tree so getRoutes() emits all wire segments.
    Inserts the removed net into merged_nets_ so removeNet() knows not
    to decrement GridGraph demand for it (the wires are still there,
    now owned by the preserved net).

  CUGR::removeNet() -- merged-net guard
    Skips removeTreeUsage() for nets that were transferred via mergeNet,
    preventing double-decrement of GridGraph congestion numbers.

  CUGR::hasAvailableResources(layer, tile_x, tile_y)
    Thin wrapper over GridGraph::getEdge().getResource() >= 1.0,
    equivalent to FastRouteCore::hasAvailableResources.

All 143 GRT regression tests pass.

Signed-off-by: Sparsh Karna <sparsh2005karna@gmail.com>
Signed-off-by: Sparsh Karna <sparsh2005karna@gmail.com>
Signed-off-by: Sparsh Karna <sparsh2005karna@gmail.com>
Signed-off-by: Sparsh Karna <sparsh2005karna@gmail.com>
Remove the standalone connectCUGRRouting() function and fold the
CUGR-specific capacity check (against GridGraph) into the existing
connectRouting() function via a use_cugr_ branch.  A new optional
connection_out parameter receives the stitching segments when the
caller needs to update the CUGR routing tree.

The mergeNetsRouting() function now uses a single connectRouting() call.

Signed-off-by: Sparsh Karna <sparsh2005karna@gmail.com>
Consolidate mergeNetsRouting() to use a single connectRouting() logic
block for both FastRoute and CUGR paths, applying CUGR-specific capacity
checks inside connectRouting() and performing the final CUGR tree merge
conditionally after routing is connected.

Signed-off-by: Sparsh Karna <sparsh2005karna@gmail.com>
Add error logging with ID 705 to prevent negative layer indices from
failing silently in CUGR::hasAvailableResources().

Signed-off-by: Sparsh Karna <sparsh2005karna@gmail.com>
Rename nodeA/B, target_layerA/B, target_xA/B, target_yA/B, and
findPath to snake_case equivalent to comply with OpenROAD coding
guidelines.

Signed-off-by: Sparsh Karna <sparsh2005karna@gmail.com>
Signed-off-by: Sparsh Karna <sparsh2005karna@gmail.com>
Signed-off-by: Sparsh Karna <sparsh2005karna@gmail.com>
Signed-off-by: Sparsh Karna <sparsh2005karna@gmail.com>
Signed-off-by: Sparsh Karna <sparsh2005karna@gmail.com>
Signed-off-by: Sparsh Karna <sparsh2005karna@gmail.com>
Mirrors the remove_buffers3 test (disjoint layer range merge) using
-use_cugr flag to exercise the CUGR incremental net merge path.

Signed-off-by: Sparsh Karna <sparsh2005karna@gmail.com>
@eder-matheus

Copy link
Copy Markdown
Member

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

if (max1 != -1 && max2 != -1) {
if (max1 < min2) {
insertViasForConnection(net1_route, pin_pos1, max1, min2);
} else if (max2 < min1) {
insertViasForConnection(net1_route, pin_pos1, max2, min1);
}
}
net1_route.insert(net1_route.end(), net2_route.begin(), net2_route.end());

P1 Badge Merge same-gcell CUGR trees before deleting the removed net

For CUGR merges where the buffer pins are in the same gcell, this branch updates only routes_; it never calls cugr_->mergeNet() or marks the removed net in merged_nets_. In that scenario, the following net-destroy callback removes the deleted net's existing tree usage from GridGraph even though those wires were just folded into the survivor's guides, so congestion accounting and later CUGR incremental routes are based on missing demand. Please transfer the CUGR tree/ownership in this branch as well, including any via bridge inserted above.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/grt/src/cugr/src/CUGR.cpp Outdated
Comment thread src/grt/src/cugr/src/CUGR.cpp Outdated
…ting

In connectRouting(), when both buffer pins land in the same gcell the
else-branch folded net2 route segments into net1_route but never called
cugr_->mergeNet(). This left the removed net absent from merged_nets_,
so the subsequent inDbNetDestroy callback subtracted its tree usage from
GridGraph even though those wires had already been transferred to the
survivor net, corrupting congestion demand for later incremental routes.

Pass an empty connection vector to cugr_->mergeNet() so that:
  - merged_nets_ is updated, suppressing the erroneous removeTreeUsage
  - The removed tree is attached as a direct child of the preserved tree

Signed-off-by: Sparsh Karna <sparsh2005karna@gmail.com>
In CUGR::mergeNet() the connection chain nodes (node_a -> ... -> node_b)
were appended to the preserved tree without recording their demand in
GridGraph. Subsequent calls to totalOverflow() / saveCongestion() and
incremental routing therefore treated the stitched edges as free, and
a later updateNet() or removeNet() of the survivor would subtract demand
that was never added, resulting in negative edge usage.

Fix: build a temporary mini-tree for the connection segments and call
grid_graph_->addTreeUsage() on it (with the survivor net NDR costs)
before attaching the real chain into the preserved tree.

Signed-off-by: Sparsh Karna <sparsh2005karna@gmail.com>
hasAvailableResources() always compared available resource against 1.0,
but CUGR routes NDR nets with per-layer demand factors > 1 via
GRNet::getNdrCosts(). A connection stitch could therefore be accepted
on an edge with only 1 free track even though the NDR factor required
more, causing congestion undercount and incorrect incremental routes.

Changes:
- Add demand parameter (default 1.0) to CUGR::hasAvailableResources().
- Add CUGR::getNdrCosts(odb::dbNet*) public getter so GlobalRouter can
  retrieve the survivor nets per-layer demand vector.
- In connectRouting() CUGR capacity loop, look up the survivor nets
  NDR costs and pass the per-layer demand into hasAvailableResources()
  so NDR nets are checked against their true edge demand.

Signed-off-by: Sparsh Karna <sparsh2005karna@gmail.com>
Signed-off-by: Sparsh Karna <sparsh2005karna@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants