Skip to content

grt: dedup duplicate pin-via guides when reloading routes_ from ODB (#10474)#10714

Open
saurav-fermions wants to merge 1 commit into
The-OpenROAD-Project:masterfrom
Fermions-ASI:fix/10474
Open

grt: dedup duplicate pin-via guides when reloading routes_ from ODB (#10474)#10714
saurav-fermions wants to merge 1 commit into
The-OpenROAD-Project:masterfrom
Fermions-ASI:fix/10474

Conversation

@saurav-fermions

Copy link
Copy Markdown
Contributor

Summary

Global routing produced different final results when repair_antennas started from ODB-reconstructed guides versus a warm in-memory state, because the globalRoutingToBox -> ODB guide -> boxToGlobalRouting round-trip was not an identity. This de-duplicates the double-written pin-via guides so reloading routes_ from ODB is deterministic.

Type of Change

  • Bug fix

Impact

Antenna-repair global routing is reproducible regardless of warm/cold start.

Verification

  • Local build succeeds.
  • Relevant tests pass (rebuilt from source): ctest -R '^grt\.' 131/131.
  • Code follows the repository's formatting guidelines.
  • I have signed my commits (DCO).

Related Issues

Fixes #10474


Developed with SAIGE, Fermions' autonomous RTL/EDA debugging agent; root-caused, tested, and signed off by the submitter (@saurav-fermions).

…he-OpenROAD-Project#10474)

Global routing produced different final results when repair_antennas
started from an uninitialized state (routes_ reconstructed from ODB
guides) versus a warm in-memory state.

Root cause: saveGuides() writes two dbGuide objects per pin-covering
via (one per direction, with swapped layer/via-layer). loadGuidesFromDB()
read both back and created two via GSegments in routes_, whereas the
in-memory FastRouteCore::getRoutes() represents each physical via with a
single GSegment (it explicitly skips the layer-swapped inverse). The
round-trip globalRoutingToBox -> ODB guide -> boxToGlobalRouting was
therefore not an identity, leaving extra via segments and diverging the
subsequent antenna-repair result.

Fix: add GlobalRouter::dedupViaSegments() and call it in
loadGuidesFromDB() before addImplicitVias()/mergeSegments(). A via and
its layer-swapped inverse (same coords, same unordered layer pair) are
treated as the same physical via; the first is kept and later duplicates
dropped. Wire segments are untouched.

Verification (gcd, sky130hs): reloaded routes_ had 51 extra via segments
vs the in-memory routes_ before the fix; after the fix the two match
exactly as a multiset (2016 vias, 1098 wires, 3114 records each). The
repair_antennas_from_odb output DEF is byte-identical to the reference;
only two duplicate via lines are removed from the guide golden, which is
updated accordingly. All 131 grt regression tests pass.

Signed-off-by: Saurav Singh <saurav.singh@fermions.co>
@saurav-fermions saurav-fermions requested a review from a team as a code owner June 21, 2026 05:16

@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 introduces a dedupViaSegments method to remove duplicate via segments created when reconstructing routes from ODB guides, preventing redundant via GSegments and their layer-swapped inverses. The review feedback suggests optimizing this method by replacing std::set with std::vector and std::find to avoid heap allocation overhead and improve cache locality, as the number of vias per net is typically small.

Comment on lines +2769 to +2787
std::set<std::tuple<int, int, int, int>> seen_vias; // x, y, lo_layer, hi
size_t write = 0;
for (size_t read = 0; read < route.size(); read++) {
const GSegment& seg = route[read];
if (seg.isVia() && seg.init_layer != seg.final_layer) {
const int lo = std::min(seg.init_layer, seg.final_layer);
const int hi = std::max(seg.init_layer, seg.final_layer);
const std::tuple<int, int, int, int> key{seg.init_x, seg.init_y, lo, hi};
if (!seen_vias.insert(key).second) {
// Duplicate via (or its inverse) already kept; skip it.
continue;
}
}
if (write != read) {
route[write] = route[read];
}
write++;
}
route.resize(write);

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.

medium

Using std::set inside a function called for every net in the design can introduce significant performance overhead due to heap allocations for each unique via. Since the number of vias per net is typically very small, using a std::vector with std::find is much more efficient because it avoids node allocation overhead and benefits from cache locality.

  std::vector<std::tuple<int, int, int, int>> seen_vias;  // x, y, lo_layer, hi
  size_t write = 0;
  for (size_t read = 0; read < route.size(); read++) {
    const GSegment& seg = route[read];
    if (seg.isVia() && seg.init_layer != seg.final_layer) {
      const int lo = std::min(seg.init_layer, seg.final_layer);
      const int hi = std::max(seg.init_layer, seg.final_layer);
      const std::tuple<int, int, int, int> key{seg.init_x, seg.init_y, lo, hi};
      if (std::find(seen_vias.begin(), seen_vias.end(), key) != seen_vias.end()) {
        // Duplicate via (or its inverse) already kept; skip it.
        continue;
      }
      seen_vias.push_back(key);
    }
    if (write != read) {
      route[write] = route[read];
    }
    write++;
  }
  route.resize(write);

@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.

It would be nice to have a specific unit test that exercises the specific problem. This test could compare the guides generated post a fresh GRT run with the guides generated after loading them from ODB.

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.

GRT: Difference on final results starting repair_antennas from uninitialized state

2 participants