Skip to content

[Do Not Merge Yet] Missing para dbmodnet#9745

Open
openroad-ci wants to merge 14 commits intoThe-OpenROAD-Project:masterfrom
The-OpenROAD-Project-staging:missing_para_dbmodnet
Open

[Do Not Merge Yet] Missing para dbmodnet#9745
openroad-ci wants to merge 14 commits intoThe-OpenROAD-Project:masterfrom
The-OpenROAD-Project-staging:missing_para_dbmodnet

Conversation

@openroad-ci
Copy link
Copy Markdown
Collaborator

OpenROAD's ODB maintains two net representations simultaneously for hierarchical designs:

  • dbNet (flat): the physical wire — one per connected signal on the chip
  • dbModNet (hierarchical): a logical net scoped to a module level — multiple can exist for the same physical signal at different hierarchy levels

A flat dbITerm (leaf cell pin) can be connected to both. dbNetwork::net(pin) returns the modnet wrapper first

The bug (before fix)

highestConnectedNet is called by findParasiticNet to get the canonical lookup key for the parasitic map:

findParasiticNet(driver_pin)
→ network_->net(driver_pin) # returns modnet STA wrapper (priority)
→ network_->highestConnectedNet() # OLD: trivially returns net unchanged
→ parasitic_network_map_.find(modnet_key) # MISS — SPEF stored under flat net key
→ nullptr

SPEF is a flat format — when read, parasitics are always stored keyed by the flat dbNet STA wrapper. But the lookup was being done with the modnet STA wrapper → cache miss → parasitic = nullptr → LumpedCap fallback → And this was hitting the bug in LumpedCapDelayCalc::makeResult (tracked separately with James Cherry)

With the fix, findRelatedNet() walks the modnet's connected flat dbITerm objects and returns the first iterm->getNet() it finds — which is the flat dbNet that the SPEF parasitic is keyed under.

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 addresses a bug in parasitic extraction for hierarchical designs by ensuring highestConnectedNet correctly resolves a dbModNet to its corresponding flat dbNet. The logic in dbNetwork.cc is sound and directly fixes the described issue. The addition of the parasitics_annotated helper function in dbSta.i is a useful addition for testing and scripting. The minor suggestion in dbNetwork.cc to improve code clarity and conciseness remains valid.

Comment on lines 2021 to 2030
dbNet* db_net = nullptr;
odb::dbModNet* db_modnet = nullptr;
staToDb(net, db_net, db_modnet);
if (db_modnet) {
dbNet* flat_net = db_modnet->findRelatedNet();
if (flat_net) {
return dbToSta(flat_net);
}
}
return net;
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

This implementation is correct. It can be slightly refactored for conciseness and to clarify intent regarding an unused variable. The db_net variable is required for the staToDb call but is not used thereafter. Renaming it to db_net_unused makes this explicit. Additionally, the nested if can be simplified using a C++17 if-statement with an initializer, which also scopes the flat_net variable more tightly.

  dbNet* db_net_unused = nullptr;
  odb::dbModNet* db_modnet = nullptr;
  staToDb(net, db_net_unused, db_modnet);

  if (db_modnet) {
    if (dbNet* flat_net = db_modnet->findRelatedNet()) {
      return dbToSta(flat_net);
    }
  }

  return net;

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.

Net* dbNetwork::findFlatNet(const Net* net) does the same thing.
You can simply call it.

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.

Thank you. I think I did this because of the difference in fallback logic. But there is a simple change that can fix that and use findFlatNet instead, just uploaded it now.

@github-actions
Copy link
Copy Markdown
Contributor

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

Copy link
Copy Markdown
Member

@maliberty maliberty left a comment

Choose a reason for hiding this comment

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

Looks fine but I think worth a secure-CI run as it may affect parasiti

sta->checkSanity();
}

bool parasitics_annotated(Pin *pin, Scene *scene) {
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.

unrelated

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 dropped this commit- it was from Martin

@dsengupta0628
Copy link
Copy Markdown
Contributor

Looks fine but I think worth a secure-CI run as it may affect parasiti

Secure-CI ongoing https://jenkins.openroad.tools/job/OpenROAD-flow-scripts-Private/job/secure-fix_missing_para_dbmodnet/

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 objective of Network::highestConnectedNet(Net* net) is to retrieve the hierarchical net in the highest module.
But the this dbNetwork::highestConnectedNet(Net* net) finds the corresponding flat net for the given hier net, which is a different behavior.

Fortunately, there is only one caller Parasitics::findParasiticNet(const Pin *pin). So the current fix works.
But if Network::highestConnectedNet(Net* net) is used by another caller for a different purpose later, this fix can make a trouble.

I don't have a better idea about how to solve this issue.
But let's leave a comment to describe this issue.

e.g.,

// Caution:
// - `Network::highestConnectedNet(Net *net)` retrieves the highest hierarchical net connected to the given net.
// - But `dbNetwork::highestConnectedNet(Net* net)` retrieves the corresponding flat net for the given net. 
//   It behaves differently to cope with the issue #9724.
// - This redefinition may cause another issue later when `Network::highestConnectedNet(Net *net)` is used elsewhere.

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 don't have a better idea about how to solve this issue.

To me the clean solution is to only ever expose hierarchical nets to STA since that seems to be what it's expecting and that's how ConcreteNetwork implements Network

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.

Unfortunately that isn't possible with the way hierarchy was implemented in odb. For a net that is local to a single module we only create the flat net and skip the hierarchical one (since it would be equivalent as the net doesn't span a boundary). That complicates the API but does save memory on a flat design. The tradeoff could be revisited.

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.

@maliberty in that case shouldn't we expose dbNet to STA only for those nets local to a module, and use dbModNet otherwise? That would match ConcreteNetwork as far as STA could tell

…tails

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

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

@openroad-ci openroad-ci force-pushed the missing_para_dbmodnet branch from fb1b762 to 09730f1 Compare March 13, 2026 16:27
@github-actions
Copy link
Copy Markdown
Contributor

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

@dsengupta0628
Copy link
Copy Markdown
Contributor

dsengupta0628 commented Mar 13, 2026

Looks fine but I think worth a secure-CI run as it may affect parasiti

Secure-CI ongoing https://jenkins.openroad.tools/job/OpenROAD-flow-scripts-Private/job/secure-fix_missing_para_dbmodnet/

I see some concerning things in this CI. There is a crash in a design (asap/swerv_wrapper), plus one design still triggered Martin's missing parasitics check (ihp-sg13g2/i2c-gpio-extender). So I will need to debug this a bit more before this PR can be merged

@dsengupta0628
Copy link
Copy Markdown
Contributor

There were secure-CI failures with this change- please do not merge this before I review those. Thanks

@maliberty
Copy link
Copy Markdown
Member

Does this need a metrics update?

@maliberty maliberty changed the title Missing para dbmodnet [Do Not Merge Yet] Missing para dbmodnet Mar 17, 2026
Signed-off-by: dsengupta0628 <dsengupta@precisioninno.com>
@dsengupta0628
Copy link
Copy Markdown
Contributor

There is a crash in asap7/werv_wrapper with current change. The crash is a threshold_times_ out-of-bounds access in PrimaDelayCalc::measureThresholds, triggered because node_count_ = 0 for certain nets in swerv_wrapper after this fix enables PRIMA to run on nets it never touched before.

This happens because the highestConnectedNet change while correct to solve the null return for findParasiticNetwork, exposes a secondary bug: the isExternal flag in parasitic pin nodes was set incorrectly during SPEF loading for hierarchical nets. Now every pin node in the parasitic network for a hierarchically-connected net is flagged external.

Before my fix

findParasiticNetwork(pin) → findParasiticNet(pin) → highestConnectedNet(net) → returned mod net → parasitic_network_map_.find(mod_net) → null → findParasitic() returns null → gateDelays() hits failed = true → falls back to tableDcalcResults(). PRIMA never ran. No crash.

I am exploring what best additional fix needed to take care of this.

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

@github-actions
Copy link
Copy Markdown
Contributor

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

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

Secure-ci run with new set of changes: http://secure-ci:8080/view/My/job/SB/job/secure-fix_missing_para_dbmodnet/

@github-actions
Copy link
Copy Markdown
Contributor

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

@dsengupta0628 dsengupta0628 requested a review from maliberty March 23, 2026 20:45
@maliberty
Copy link
Copy Markdown
Member

Did you intend to update sta here? If so you have some issues in sta to address.

@dsengupta0628
Copy link
Copy Markdown
Contributor

Did you intend to update sta here? If so you have some issues in sta to address.

I did not. The fix strictly requires changes in dbSta. Let me force the sta/ to match whatever in master.

@github-actions
Copy link
Copy Markdown
Contributor

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

= related_dbnet->findModNetInHighestHier()) {
return dbToSta(highest_modnet); // Found the highest modnet
}
return dbToSta(related_dbnet);
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.

ReportPath::descriptionNet(const Pin *pin) uses the API.
Is this ok?

Image

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.

Net* dbNetwork::highestNetAbove that I modified is not called by ReportPath::descriptionNet(const Pin *pin). Rather it calls Network::highestNetAbove(Net *net) function. This highestNetAbove is defined virtual in the Network class

Copy link
Copy Markdown
Contributor

@dsengupta0628 dsengupta0628 Mar 24, 2026

Choose a reason for hiding this comment

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

Okay I see what you mean. In OpenROAD binary, ReportPath::descriptionNet() dispatches to dbNetwork::highestNetAbove(). So the opposite is guaranteed: OpenROAD will always call dbNetwork::highestNetAbove(), not Network::highestNetAbove(). Hmm.. I don't know the answer yet- I don't see any regression failures, but I will get CC to check for 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.

Yes, dbNetwork::highestNetAbove() changes report_checks output (and any command using ReportPath::descriptionNet), but only for hierarchical designs with modNets — and it changes the net name displayed, not the timing values.

top --> u_core (instance of "core") --> u_alu (instance of "alu") --> adder gate drives wire "sum_out"

Image

If the same physical signal has a different modNet name at each hierarchy level as shown above, after flattening, one flat dbNet is created. Its name depends on where/how flattening named it — suppose it's u_core/alu_result (a common convention: the net retains the name from an intermediate hierarchy level).
Then the visible difference would be:

Image

The old behavior always showed the topmost hierarchical name — the name at the highest module boundary the signal reached. The new behavior shows the flat dbNet name, which is whatever name the net was assigned during flattening/synthesis.

What is NOT affected

  • Timing values (slack, delay, slew, capacitance) — completely unchanged
  • Path selection (which paths are reported) — unchanged
  • All non-net lines in the report — unchanged
  • The net line is only printed when report_net_ is enabled (the -nets option to report_checks), so default report_checks without -nets sees no difference at all

@dsengupta0628
Copy link
Copy Markdown
Contributor

QoR change in CI run. Will analyze the QoR changes in these 3 designs to ensure this is explainable change.

Number of designs: 63

------------------------------
       Failing designs
------------------------------
asap7 riscv32i (base)
  Flow reached last stage.
  Found 1 metrics failures.
      [ERROR] finish__timing__setup__tns fail test: -6385.82 >= -4450

asap7 swerv_wrapper (base)
  Flow reached last stage.
  Found 2 metrics failures.
      [ERROR] finish__timing__setup__ws fail test: -625.186 >= -276
      [ERROR] finish__timing__setup__tns fail test: -251445 >= -12900

sky130hd microwatt (base)
  Flow reached last stage.
  Found 3 metrics failures.
      [ERROR] finish__timing__setup__ws fail test: -1.50039 >= -1.38
      [ERROR] finish__timing__setup__tns fail test: -237.787 >= -61.5
      [ERROR] finish__timing__hold__tns fail test: -22.9742 >= -22.1

@github-actions
Copy link
Copy Markdown
Contributor

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

@dsengupta0628
Copy link
Copy Markdown
Contributor

QoR change in CI run. Will analyze the QoR changes in these 3 designs to ensure this is explainable change.

Number of designs: 63

------------------------------
       Failing designs
------------------------------
asap7 riscv32i (base)
  Flow reached last stage.
  Found 1 metrics failures.
      [ERROR] finish__timing__setup__tns fail test: -6385.82 >= -4450

asap7 swerv_wrapper (base)
  Flow reached last stage.
  Found 2 metrics failures.
      [ERROR] finish__timing__setup__ws fail test: -625.186 >= -276
      [ERROR] finish__timing__setup__tns fail test: -251445 >= -12900

sky130hd microwatt (base)
  Flow reached last stage.
  Found 3 metrics failures.
      [ERROR] finish__timing__setup__ws fail test: -1.50039 >= -1.38
      [ERROR] finish__timing__setup__tns fail test: -237.787 >= -61.5
      [ERROR] finish__timing__hold__tns fail test: -22.9742 >= -22.1

I checked one design asap/riscv32i with a binary without my changes but Martin's check_para_annotation utility and see that this indeed had many missing para annotations. So now some of these testcases will indeed change QoR but hopefully this is a correct change and we can absorb this.

@dsengupta0628
Copy link
Copy Markdown
Contributor

ORFS PR for metrics update: The-OpenROAD-Project/OpenROAD-flow-scripts#4050

@github-actions
Copy link
Copy Markdown
Contributor

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants