Skip to content

dpl: PDN aware detailed placement legalization - #11120

Open
phsauter wants to merge 6 commits into
The-OpenROAD-Project:masterfrom
phsauter:phsauter/pdn-aware-detailed-placement
Open

dpl: PDN aware detailed placement legalization#11120
phsauter wants to merge 6 commits into
The-OpenROAD-Project:masterfrom
phsauter:phsauter/pdn-aware-detailed-placement

Conversation

@phsauter

Copy link
Copy Markdown

Summary

detailed_placement -pdn_aware adds PDN routing awareness to the placement legalizer and makes it so standard-cell to pdn-shape spacing rules are checked and satisfied.
The rough procedure is:

  1. Collect fixed PDN via stackes through OpenDB and check against which layers are used by cells, discard rectangles on unused layers and collect rectangles on used layers into an R-tree (from Boost).
  2. Use OpenDB to lazily collect and cache the geometry info for all cell masters and orientations encountered (pins, vias, metal).
  3. Divide placement rows into 64 site chunks, then per master, orientation and chunk, it queries the R-tree for close-by shapes based on size of cell and spacing rules. If there are, use cell geometry to calculate which cell origins would violate spacing and store the result in a 64-bit mask.
  4. Cache the result for later reuse.
  5. When the legalizer evaluates a location, check the corresponding mask and reject or accept based on the result. Same net overlaps remain legal.

Initial implementation is written by me, Codex Sol was used to improve performance from a 12x slowdown to a 1.5x overhead, to lint and cleanup code and to construct test cases.

Type of Change

  • New feature

Impact

Adds a new flag -pdn_aware since activating this features increases legalization runtime by ~1.5x (35s to 55s) on a 500k cell design in GF22FDX. Compared to the total runtime of the flow the time increase is small though so its debatable if it should be active by default or even always active.
Using this it is possible to avoid cell internals to PDN via spacing violations.
Without this change we observed spacing violation in GF22FDX both with minimal-size vias (mainly related to coloring and the more complex spacing rules there) and especially with recommended via arrays following design-for-manufacturability (DFM) guidelines. The new pdn-awareness completely prevents these violations at a a very minimal runtime and QoR cost (~0.2% HPWL increase)

Note: These DRC violations are currently not reported by TritonRoute because the geometry checker suppress fixed-to-fixed checks, which includes PDN geometry against placed cells internal geometry.

Verification

  • I have verified that the local build succeeds (./etc/Build.sh). Cannot on workstation, it does build without Bazel though
  • 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).

Index fixed supply via constituents on cell-used routing and cut layers, and materialize 64-site conflict recipes on demand. Use Euclidean spacing for routing and cut geometry while exempting supply pins connected to the same special net.

Reject conflicting negotiation candidates and mirrored orientations before they are committed.

Signed-off-by: Philippe Sauter <phsauter@iis.ee.ethz.ch>
Add an opt-in -pdn_aware flag for both legalization engines. Limit fixed supply via initialization to requested detailed placement and same-block check_placement or optimize_mirroring calls so unrelated DPL commands retain their existing path.

Report fixed supply via failures separately from blocked-layer failures.

Signed-off-by: Philippe Sauter <phsauter@iis.ee.ethz.ch>
Cover default-off and same-block mode reset behavior, both legalization engines, standalone mirroring, routing and cut constituents, same-net exemptions, finite via arrays, Euclidean spacing boundaries, and core-boundary handling.

Register all cases in CMake and Bazel.

Signed-off-by: Philippe Sauter <phsauter@iis.ee.ethz.ch>
Signed-off-by: Philippe Sauter <phsauter@iis.ee.ethz.ch>
@phsauter
phsauter requested a review from a team as a code owner August 10, 2026 20:52
@phsauter
phsauter requested a review from gudeh August 10, 2026 20:52

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

Welcome to OpenROAD! Thanks for opening your first PR.
Before we review:

Please ensure:

  • CI passes
  • Code is properly formatted
  • Tests are included where applicable
    A maintainer will review shortly!

@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 PDN-aware detailed placement mode (-pdn_aware) to prevent cell pin and obstruction spacing conflicts with fixed vias on special power and ground nets. The changes include adding fixed supply via checking to the DRC engine, updating grid initialization, and integrating these checks into detailed placement, check placement, and mirroring optimization. The review feedback highlights several safety and robustness improvements, including adding null checks to prevent potential crashes from null pointers in FixedSupplyVias and NegotiationLegalizer, and ensuring deleteGrid() is called in optimizeMirroring to avoid memory leaks.

Comment on lines +73 to +76
FixedSupplyVias(utl::Logger* logger, Grid* grid, odb::dbBlock* block)
: logger_(logger), grid_(grid)
{
for (odb::dbNet* net : block->getNets()) {

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.

high

If block is null, dereferencing it to call getNets() or getInsts() will cause a segmentation fault. Add a null check at the beginning of the constructor to prevent potential crashes when no design is loaded.

Suggested change
FixedSupplyVias(utl::Logger* logger, Grid* grid, odb::dbBlock* block)
: logger_(logger), grid_(grid)
{
for (odb::dbNet* net : block->getNets()) {
FixedSupplyVias(utl::Logger* logger, Grid* grid, odb::dbBlock* block)
: logger_(logger), grid_(grid)
{
if (block == nullptr) {
return;
}
for (odb::dbNet* net : block->getNets()) {

Comment on lines +184 to +189
for (const RecipeEntry& entry : recipe.entries) {
if ((entry.mask & query_mask) != 0
&& cell->getDbInst()->getITerm(entry.term)->getNet() != entry.net) {
return false;
}
}

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

In a corrupted or inconsistent database state, getITerm(entry.term) could potentially return nullptr. Dereferencing it directly to call getNet() would cause a crash. It is safer to use a defensive null check.

    for (const RecipeEntry& entry : recipe.entries) {
      if ((entry.mask & query_mask) != 0) {
        odb::dbITerm* iterm = cell->getDbInst()->getITerm(entry.term);
        if (iterm == nullptr || iterm->getNet() != entry.net) {
          return false;
        }
      }
    }

Comment thread src/dpl/src/Opendp.cpp
Comment on lines 374 to 389
void Opendp::optimizeMirroring()
{
OptimizeMirroring opt(logger_, db_);
if (isPdnAwareForCurrentBlock()) {
importDb();
adjustNodesOrient();
initGrid(true);
opt.setLegalPredicate(
[this](odb::dbInst* inst, const odb::dbOrientType& orient) {
Node* cell = network_->getNode(inst);
return drc_engine_->checkFixedSupplyVias(
cell, grid_->gridX(cell), grid_->gridRoundY(cell), orient);
});
}
opt.run();
}

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

When isPdnAwareForCurrentBlock() is true, initGrid(true) is called to initialize the grid and fixed supply vias. However, the grid is never deleted at the end of optimizeMirroring(), which can lead to memory leaks and inconsistent state. Call deleteGrid() at the end of the function if the grid was initialized.

void Opendp::optimizeMirroring()
{
  OptimizeMirroring opt(logger_, db_);
  if (isPdnAwareForCurrentBlock()) {
    importDb();
    adjustNodesOrient();
    initGrid(true);
    opt.setLegalPredicate(
        [this](odb::dbInst* inst, const odb::dbOrientType& orient) {
          Node* cell = network_->getNode(inst);
          return drc_engine_->checkFixedSupplyVias(
              cell, grid_->gridX(cell), grid_->gridRoundY(cell), orient);
        });
  }
  opt.run();
  if (isPdnAwareForCurrentBlock()) {
    deleteGrid();
  }
}

Comment on lines +656 to +660
if (node != nullptr
&& !opendp_->drc_engine_->checkFixedSupplyVias(
node, GridX{tx}, GridY{ty}, targetOrient)) {
return;
}

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

Ensure opendp_ and opendp_->drc_engine_ are non-null before calling checkFixedSupplyVias to prevent potential null pointer dereferences.

Suggested change
if (node != nullptr
&& !opendp_->drc_engine_->checkFixedSupplyVias(
node, GridX{tx}, GridY{ty}, targetOrient)) {
return;
}
if (node != nullptr && opendp_ && opendp_->drc_engine_
&& !opendp_->drc_engine_->checkFixedSupplyVias(
node, GridX{tx}, GridY{ty}, targetOrient)) {
return;
}

@gudeh

gudeh commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

@osamahammad21 FYI

@gudeh

gudeh commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Wonderful, this is a known limitation which we were planning to work with!

I should review your code on the upcoming days.

I would also like to investigate your test case. Have you dived deeply into what Codex implemented, had a look with the GUI? I also assume you can't share the design where you require this fix, right?

@phsauter

Copy link
Copy Markdown
Author

Wonderful, this is a known limitation which we were planning to work with!

I should review your code on the upcoming days.

I would also like to investigate your test case. Have you dived deeply into what Codex implemented, had a look with the GUI? I also assume you can't share the design where you require this fix, right?

I did have a look in the GUI and in Siemens Calibre and they seemed reasonable and correct to me, though I do not want to make any statement regarding coverage, might be bad.
I can share the design privately with PI but not publicly in GF22FDX. I could however probably rewrite ASAP7 or Nangate45s via definitions to get a similar behavior, shouldn't be too much effort.
I will also address any existing feedback later this week (Thursday or Friday) or then next week.

Another thing that could be considered: From my understanding sroute etc is already covered, hence the focus on vias. In principle this could be expanded to cover all PDN-related placement legalization cases.

@gadfort

gadfort commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

@phsauter what's the runtime penalty to leave this on by default? it seems like something we would always want to check (maybe there is a way to determine if its needed automatically?)

@maliberty

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: 8d623071fa

ℹ️ 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 on lines +549 to +553
// Fixed supply via constituent checks cover width/PRL routing spacing and
// default cut spacing. Detailed routing remains authoritative for other
// LEF58 rules.
if (layer->getType() == odb::dbTechLayerType::CUT) {
return layer->getSpacing();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Enforce LEF58 rules when screening supply vias

For PDKs where the applicable separation is defined by LEF58 EOL or cut-spacing-table rules rather than the legacy/default spacing, this function returns a smaller or even zero spacing and -pdn_aware can accept a cell that still violates DRC. The comment's reliance on detailed routing does not close the gap: the corresponding fixed-to-fixed checks are explicitly skipped in src/drt/src/gc/FlexGC_eol.cpp and src/drt/src/gc/FlexGC_cut.cpp, so these violations can remain entirely unreported. Include the applicable LEF58 routing and cut rules in the cached keepout calculation.

Useful? React with 👍 / 👎.

Merge standard-cell geometry into per-row vertical ranges before building the fixed supply via index. Skip candidate checks when every via constituent is separated by the maximum applicable spacing at all legal sites.

Retain the collected geometry for a lazy fallback when check_placement sees an invalid site or orientation.

Signed-off-by: Philippe Sauter <phsauter@iis.ee.ethz.ch>
Fall back to the exact fixed supply via checks for rows with right-angle rotations. Preserve cell geometry beyond the top and bottom core boundaries so the vertical envelope cannot miss a conflict.

Keep the maximum spacing local and describe skipped checks in terms of spacing.

Signed-off-by: Philippe Sauter <phsauter@iis.ee.ethz.ch>
@phsauter

Copy link
Copy Markdown
Author

@phsauter what's the runtime penalty to leave this on by default? it seems like something we would always want to check (maybe there is a way to determine if its needed automatically?)

Previously once you activate it, you will always have close to the full overhead no matter if you have PDN geometry and cells that can cause conflicts or not (since it still needs to check).
Just now I pushed a first version of an early exist scheme, it takes all cells and calculates the y-range in which there is geometry per-row. Then it does the same for PDN via shapes. If the ranges are disjoint and separated by the spacing rule applicable, then we know it cannot generate a violation and it should just exit early.
This pushes down the overhead in this case to about 0.2s, meaning it could be always-on as now the overhead when its not needed is extremely small and otherwise it should probably run anyway.

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