dbSta: read cross-die parasitics on 3D-IC designs - #11189
Conversation
Signed-off-by: dsengupta0628 <dsengupta@precisioninno.com>
There was a problem hiding this comment.
Code Review
This pull request enhances 3DIC support in dbNetwork by correctly mapping and resolving chip-nets above chiplet boundaries, which prevents stack overflows and crashes during parasitic annotation and delay calculation. It also adds a regression test suite for cross-die parasitics. The review feedback highlights a potential safety issue in the three-argument overload of staToDb, where a dbChipNet could be unsafely cast to a dbNet* without validation, and suggests a robust fallback mechanism in highestNetAbove.
| staToDb(net, dbnet, modnet); | ||
|
|
||
| if (dbnet) { |
There was a problem hiding this comment.
The three-argument overload staToDb(const Net* net, dbNet*& dbnet, dbModNet*& modnet) does not currently check if net is a dbChipNet before casting it to dbNet*. If net is a dbChipNet, dbnet will be populated with a pointer to a dbChipNet object but typed as dbNet*. This is unsafe and can cause undefined behavior or crashes if callers of the three-argument overload attempt to use dbnet as a real dbNet.
To make this robust, please:
- Update
staToDb(const Net* net, dbNet*& dbnet, dbModNet*& modnet)to only setdbnetif the object type isodb::dbNetObj(similar to the fix in the single-argumentstaToDb). - Update
highestNetAboveto handle the case wherenetis adbChipNet(and thus bothdbnetandmodnetare null) by returningnetas a fallback.
staToDb(net, dbnet, modnet);
if (net && !dbnet && !modnet) {
return net;
}
if (dbnet) {There was a problem hiding this comment.
Both of Gemini's points are already handled. Assessment:
Point 1 — incorrect. The 3-arg staToDb never blind-casts. It reads getObjectType() and branches, with an explicit dbChipNetObj case that leaves both outputs null. Note the else clause errors with ORD-2034 on anything unexpected, so an unhandled type would be loud, not silent. Nothing to fix.
Point 2 — already the behavior, via a different mechanism. You propose an early if (net && !dbnet && !modnet) return net;. The function already ends in return net;, so a chip net falls through both blocks and comes back unchanged. The suggested code is functionally identical — it would be dead.
Problem
A cross-die net is one physical wire, but the 3D RCX flow writes it as three SPEF pieces — each die's wiring, plus the die-to-die bond resistor:
graph LR D["driver"] ---|"top.die1.spef"| PA["bump pad"] PA ---|"top.bonds.spef"| PB["bump pad"] PB ---|"top.die2.spef"| R["receiver"] style PA fill:#fef7e0,stroke:#f9ab00 style PB fill:#fef7e0,stroke:#f9ab00Reading all three left the pieces on three different nets. The delay calculator fetches parasitics by the driver pin's net, so it saw one third of the wire and cross-die paths came out optimistic.
Fix
The top chip already holds a
dbChipNetper bond. That is the net above both dies, and the net the bonds SPEF is written against. ThreedbNetworkoverrides now ascend to it, so all three pieces land on one net and every driver finds them:net(const Pin*)on a chiplet boundary portnullptrdbChipNetabovehighestConnectedNet()on a bonded die netdbChipNetabovehighestNetAbove()on a bonded die netdbChipNetabovenet()is whatSpefReader::dspfBeginclimbs to decide which net owns a parasitic network, so that one makes the merge happen.highestConnectedNet()is whatParasitics::findParasiticNetcanonicalizes through, so that one makes the driver find it.highestNetAbove()decides whether a parasitic node is "external" and has to give the same answer, or every node of a merged network is dropped.Usage
Order matters. A top-scope read replaces a net's parasitic network where a
-pathread merges into it, so reading the bonds SPEF last silently discards the die contributions. Documented indbNetwork.hh; making it order-independent is aSpefReaderchange and out of scope here.Test
src/dbSta/test/3dic_spef— reuses the existing3dic_crosstwo-chiplet fixture with three hand-written SPEFs: 2 fF near side, 2 kΩ bond, 100 fF far side. The far-side capacitance sits behind the bond, so the driver only sees it if the merge worked: arrival 0.15 ns → 0.41 ns, and its load reads 102.97 fF in the golden.Uses hand-written SPEFs rather than extraction, so it does not depend on the unmerged rcx half of #11124.
Verified: full
dbStaandestsuites pass (2560 tests);dmp_ceff_elmore,arnoldiandprimaall run on the merged network.Verification
./etc/Build.sh).Related Issues
#10664