Skip to content

dbSta: move levelizedDrvrVertices() out of OpenSTA fork#10352

Merged
maliberty merged 7 commits into
The-OpenROAD-Project:masterfrom
The-OpenROAD-Project-staging:or_move_drvr_level_to_dbsta
May 12, 2026
Merged

dbSta: move levelizedDrvrVertices() out of OpenSTA fork#10352
maliberty merged 7 commits into
The-OpenROAD-Project:masterfrom
The-OpenROAD-Project-staging:or_move_drvr_level_to_dbsta

Conversation

@openroad-ci
Copy link
Copy Markdown
Collaborator

Summary

Re-implements levelizedDrvrVertices() in dbSta using only public OpenSTA APIs, so the OpenSTA submodule can drop its fork-only PR #327 additions and stay compatible with upstream parallaxsw/OpenSTA.
All existing rsz/ call sites (Resizer.cc, RepairDesign.cc, Rebuffer.cc) are unchanged — they call sta_->levelizedDrvrVertices() where sta_ is sta::dbSta*, which inherits Sta, so the new
dbSta::levelizedDrvrVertices() resolves via overload.

Cache invalidation uses the existing LevelizeObserver hook. Because Levelize::setObserver() replaces the prior observer (deletes it, no chain), dbSta::makeObservers() installs a composite
(DbStaLevelizeObserver) that replicates Sta's StaLevelizeObserver work (forwarding to Search + GraphDelayCalc) and additionally invalidates the dbSta cache. Algorithm and sort key (level then
VertexNameLess) match the original Levelize implementation byte-for-byte.

Companion OpenSTA PR: <PR 58>

Changes

  • dbSta/include/db_sta/dbSta.hh — public levelizedDrvrVertices(); private makeObservers() override + invalidateLevelizedDrvrVertices() (friend of observer); cache members
  • dbSta/src/dbSta.ccDbStaLevelizeObserver (composite, file-local); makeObservers() override; cached levelizedDrvrVertices() rebuild
  • dbSta/src/dbSta.ista::report_levelized_drvr_vertices(int) Tcl helper (was in sta/graph/Graph.i)
  • dbSta/src/CMakeLists.txt — add ${OPENSTA_HOME} + ${OPENSTA_HOME}/include/sta PRIVATE on dbSta_lib (needed for search/Levelize.hh and the unprefixed transitive Graph.hh etc.)
  • dbSta/test/levelize_drvr_vertices1.{tcl,ok} + gcd_asap7.v — relocated regression test
  • dbSta/test/CMakeLists.txt + dbSta/test/BUILD — register test in CMake + Bazel with required asap7 LEFs/libs
  • Submodule bump: src/sta → companion fork PR head

Notes

  • Test .ok was regenerated against current OpenSTA semantics (full-liberty link in OpenROAD context). 375 driver-vertex count vs the old 106 because:
    1. Original test ran in standalone OpenSTA with INVBUF-only liberty → most cells were black-box (PortDirection::unknown ⇒ not driver per Vertex::isDriver).
    2. New test reads full RVT FF library set (AO + INVBUF + OA + SIMPLE + SEQ) — required by OpenROAD's link_design flow which needs LEF and resolves all cells.
    3. Driver levels start at 20 (one wire-edge + one cell-arc hop from root); level=10 contains only loads — empty in driver list, expected.

Type of Change

  • Refactoring

Impact

Hopefully no impact!

Verification

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

Related Issues

Reverts OSTA PR The-OpenROAD-Project/OpenSTA#327 (commit https://github.com/The-OpenROAD-Project-private/OpenSTA/commit/94bb37bb4a7b34e6839918fb5f7a37e9b801d85a)

Signed-off-by: dsengupta0628 <dsengupta@precisioninno.com>
Signed-off-by: dsengupta0628 <dsengupta@precisioninno.com>
Signed-off-by: dsengupta0628 <dsengupta@precisioninno.com>
Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

clang-tidy made some suggestions

using Sta::slack;

// Drivers sorted by (level, name) for determinism.
const VertexSeq& levelizedDrvrVertices();
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.

warning: no header providing "sta::VertexSeq" is directly included [misc-include-cleaner]

src/dbSta/include/db_sta/dbSta.hh:12:

- #include "db_sta/DelayFmt.hh"  // IWYU pragma: keep
+ #include "GraphClass.hh"
+ #include "db_sta/DelayFmt.hh"  // IWYU pragma: keep

Comment thread src/dbSta/src/dbSta.cc Outdated
}
VertexNameLess name_less(net);
std::sort(levelized_drvr_vertices_.begin(),
levelized_drvr_vertices_.end(),
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.

warning: use a ranges version of this algorithm [modernize-use-ranges]

Suggested change
levelized_drvr_vertices_.end(),
std::ranges::sort(levelized_drvr_vertices_,
,

Copy link
Copy Markdown
Contributor

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

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 a caching mechanism for levelized driver vertices within dbSta, including a new observer (DbStaLevelizeObserver) to handle cache invalidation when graph levels change. It also adds a Tcl command report_levelized_drvr_vertices and a test case to verify the functionality. Feedback was provided regarding the reliance on internal knowledge of the base class's observer setup and the necessity of ensuring deterministic sorting during vertex iteration.

Comment thread src/dbSta/src/dbSta.cc
Comment on lines +320 to +324
void dbSta::makeObservers()
{
Sta::makeObservers();
levelize_->setObserver(new DbStaLevelizeObserver(this));
}
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

The dbSta::makeObservers override replaces the StaLevelizeObserver created by the base class. While the DbStaLevelizeObserver correctly replicates the behavior of the base observer, this pattern relies on internal knowledge of Sta::makeObservers. If the base class is updated to install additional observers or change the behavior of StaLevelizeObserver, this override might become out of sync. However, given the current OpenSTA API where setObserver takes ownership and deletes the previous one, this is the only way to extend the behavior. It would be safer to verify if OpenSTA could be updated to support observer chaining or multiple observers.

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.

Hard to address without change in OpenSTA

  1. Behavior duplication: DbStaLevelizeObserver::levels{,Changed}Before literally repeats what file-local StaLevelizeObserver (sta/search/Sta.cc) does. If upstream changes that observer (adds work, adds another observer in Sta::makeObservers), our override silently drops the new behavior.
  2. API limitation: Levelize::setObserver (sta/search/Levelize.cc) deletes prior + replaces. Single-slot. No chaining. Forces composite-by-replacement pattern.

Proper fix would be to add chaining/multi-observer API to Levelize:
void addObserver(LevelizeObserver*); // append, takes ownership
void removeObserver(LevelizeObserver*);

Then dbSta installs only the cache-invalidation observer; upstream's StaLevelizeObserver stays untouched. No replication, no drift risk.

I don't know if I should add this in OpenSTA and push upstream @maliberty @precisionmoon

I am trying to see if I can create a regression to catch any such drift

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.

@povik FYI

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.

It would make more sense for this observer to inherit from the existing observer and then call the parent class function instead of duplicating the contents.

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.

Thanks, and agreed in principle but blocked with current implementation: StaLevelizeObserver is file-local in sta/search/Sta.cc, no header, so dbSta can't include or subclass it as-is.

Full fix needs OpenSTA changes (move StaLevelizeObserver + LevelizeObserver to public headers, add Sta::setLevelizeObserver). If you agree, I can file a PR in parallaxsw/OpenSTA, or if you add this, I can merge it too.

Comment thread src/dbSta/src/dbSta.cc
Comment on lines +334 to +361
const VertexSeq& dbSta::levelizedDrvrVertices()
{
ensureLevelized();
if (!drvr_vertices_level_valid_) {
Graph* g = graph();
// Approx half of vertices are drivers.
levelized_drvr_vertices_.reserve(g->vertexCount() / 2);
Network* net = network();
VertexIterator vertex_iter(g);
while (vertex_iter.hasNext()) {
Vertex* vertex = vertex_iter.next();
if (vertex->isDriver(net)) {
levelized_drvr_vertices_.push_back(vertex);
}
}
VertexNameLess name_less(net);
std::ranges::sort(levelized_drvr_vertices_,
[&name_less](const Vertex* a, const Vertex* b) {
if (a->level() != b->level()) {
return a->level() < b->level();
}
return name_less(a, b);
});
drvr_vertices_level_valid_ = true;
}
return levelized_drvr_vertices_;
}

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

The implementation of levelizedDrvrVertices iterates over all vertices in the graph (O(V)) and then sorts the drivers (O(D log D)). This matches the original implementation's complexity and maintains consistency. Ensure that the sorting of drivers is deterministic if they are represented by pointers, using a custom comparator if necessary. While a single-pass approach or iterating over nets might be more efficient for large designs, the current cached approach is acceptable and avoids replacing a specialized implementation without verification.

References
  1. Iteration or sorting over pointer keys is deterministic if a custom comparator is defined for the pointer type.
  2. In performance-critical code, do not replace a specialized implementation for small inputs with a more generic one without verifying that performance is not negatively impacted.
  3. A single-pass approach can be more efficient than a two-pass approach by allowing for early exit and avoiding redundant processing.

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.

There is no actionable item here

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 7, 2026

clang-tidy review says "All clean, LGTM! 👍"

Signed-off-by: dsengupta0628 <dsengupta@precisioninno.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 7, 2026

clang-tidy review says "All clean, LGTM! 👍"

@dsengupta0628
Copy link
Copy Markdown
Contributor

@codex review

@chatgpt-codex-connector
Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Swish!

ℹ️ 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".

@dsengupta0628
Copy link
Copy Markdown
Contributor

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 8, 2026

clang-tidy review says "All clean, LGTM! 👍"

Comment thread src/dbSta/src/dbSta.cc
{
if (drvr_vertices_level_valid_) {
drvr_vertices_level_valid_ = false;
levelized_drvr_vertices_.clear();
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.

the the clear is pointless.

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.

The clear is required for correctness- without it, the next rebuild's push_back appends to the stale contents, returning a corrupted concatenated list. After dbStaCbk::inDbInstDestroy it would also be dangling Vertex*. Did you mean to move the clear to the rebuild path? That would definitely be cheaper invalidate (flag flip only), but at the cost of holding stale pointers in the private member until the next query. Please clarify. Thanks.

Comment thread src/dbSta/src/dbSta.cc
{
Sta::makeObservers();
levelize_->setObserver(new DbStaLevelizeObserver(this));
}
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.

I think it makes more sense to add a set function for the levelize observer on the Sta class than to change the include search path to get access to Levelize.hh

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.

Absolutely agreed! The setter on Sta is much cleaner. Worth noting though LevelizeObserver itself lives in sta/search/Levelize.hh (private include path), and DbStaLevelizeObserver must subclass it. So even with sta::setLevelizeObserver, dbSta still needs OPENSTA_HOME on the include path unless we also promote LevelizeObserver to a public header (sta/include/sta/LevelizeObserver.hh). Would you be okay to add these two changes in upstream? Either I can create a PR or if you add it, I will merge it here. Thanks!

@dsengupta0628
Copy link
Copy Markdown
Contributor

dsengupta0628 commented May 11, 2026

parallaxsw/OpenSTA#433 to address @jjcherry56's comments. If upstream is accepted and merged, I will update the code in dbSta. For now we need this change to update the sta pointer in OpenROAD otherwise OpenROAD will not compile @maliberty

@dsengupta0628 dsengupta0628 requested a review from jhkim-pii May 11, 2026 19:05
@github-actions
Copy link
Copy Markdown
Contributor

clang-tidy review says "All clean, LGTM! 👍"

@maliberty maliberty merged commit 24dfa0b into The-OpenROAD-Project:master May 12, 2026
16 checks passed
@maliberty maliberty deleted the or_move_drvr_level_to_dbsta branch May 12, 2026 03:09
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.

4 participants