Add LEF58_BACKSIDE support for backside-power-delivery PDKs#10547
Conversation
LEF 5.8 lets a tech LEF mark routing and cut layers as backside via "PROPERTY LEF58_BACKSIDE BACKSIDE ;". Backside layers are used by buried-power-rail (BPR) and backside power-delivery network (BSPDN) flows on 2nm-class technologies. Previously, the property was logged as unsupported (ODB-0388) and discarded. This commit: * Adds a parser for LEF58_BACKSIDE (modelled on RECTONLY). * Stores the flag on dbTechLayer via a new flags_.is_backside bit (taken from spare_bits). * Exposes dbTechLayer::setBackside() / isBackside() via the public API. * Wires the parser into the ROUTING and CUT branches of lefin::createLayer so the property is consumed rather than warned about. The flag has no in-tree consumers yet; downstream modules (DRT, GRT, PDN, DPL, RCX) will be updated in follow-up commits to skip backside layers from front-side routing while still letting the geometry round- trip through DEF/GDS for downstream LVS/PEX tools. Signed-off-by: Matthew Guthaus <mrg@ucsc.edu>
OpenROAD's detailed router (TritonRoute) has no notion of front- versus
back-side metal stacks. On a backside-power PDK (GT2N 2nm with BPR /
BM* / BRDL), the router tries to build default vias for the backside
cut layers (BV0..BV4) and crashes when those vias have no front-side
geometry. Even after a defensive null-guard on the via probes, every
downstream consumer (track grids, BTerms, special-net wires, access
points, NDRs, via generate rules) still tries to look up backside
layers in frTech and crashes.
This commit filters backside layers out of DRT at every ingest point:
* setLayers - skip ROUTING / CUT / MASTERSLICE layers tagged
isBackside(); they never appear in frTech.
* setTracks - skip track grids on backside layers.
* setBTerms - skip BPin shapes on backside layers (e.g. block-
level VDD/VSS pins routed on BPR).
* setTechViaRules - skip generate rules that reference any backside
layer.
* setNDRs - skip per-layer / via / via-rule NDR entries that
reference backside layers.
* updateNetRouting (signal path) - skip path segments and vias whose
layer doesn't exist in frTech; works because the
router's name2layer_ table already excludes backside
layers.
* updateNetRouting (special path) - skip SWire boxes and SBox vias
on backside layers (BPR followpins, backside
stripes generated by PDN).
* updatefrAccessPoint - return false (and skip the access point in
callers) when the AP is on a backside layer.
The frLayer wrapper gets a passthrough isBackside() that consults the
underlying dbTechLayer::isBackside() (added by the previous commit).
With this, gt2n/gcd routes end-to-end without the previously needed
null-guard in FlexTAWorker::initFixedObjs; the null-guard is reverted
in the same commit because the filter supersedes it. asap7/gcd is
unaffected (no layers there report isBackside).
Signed-off-by: Matthew Guthaus <mrg@ucsc.edu>
A standard cut-layer via cannot bridge a front-side metal to a backside
metal (BPR, BM*, BRDL) in a backside-power-delivery PDK. The bridge
has to come from a TSV-like tap cell whose layout already stitches the
two sides together.
Today the user can write
add_pdn_connect -grid {grid} -layers {M1 BPR}
without any feedback, even though PDN cannot generate a via to satisfy
it. The connection just silently fails to materialize.
This commit adds a warning (PDN-1200) when the two layers of an
add_pdn_connect rule disagree on isBackside(). The warning is
informational only - we don't error, because the user may know that a
tap cell is providing the bridge and is intentionally documenting
intent in the .tcl. The warning at least makes the unusual topology
explicit.
Signed-off-by: Matthew Guthaus <mrg@ucsc.edu>
PSM's ITerm shape-generation puts a 'virtual base node' on the layer returned by getTech()->findRoutingLayer(1). On a backside-power PDK (LEF58_BACKSIDE present), ODB orders backside metals at the low end of the routing-layer index space - on GT2N findRoutingLayer(1) is BRDL, with BPR at level 6 and the lowest front-side metal M0 starting at level 7. The design has no BRDL geometry, so every base node lands on an orphan layer and the IRSolver flags every cell as 'Unconnected instance' (PSM-0039), making analyze_power_grid unusable. Scan upward through the routing-layer index space and use the first front-side layer instead. On front-side-only PDKs this still resolves to the original layer (findRoutingLayer(1)), so existing flows are unchanged. This is one piece of full BSPDN IR-drop support; the remaining piece is that PSM does not currently model the M1<->BPR bridge inside a tap cell, so PG-net IR drop on a backside-only PDN still flags 'Unconnected shape' warnings on the tap-cell M1 pins. That requires a first-class bridge-cell concept (LEF58 extension) and is tracked separately. Signed-off-by: Matthew Guthaus <mrg@ucsc.edu>
IRSolver::checkOpen seeds its BFS with a single node from the top routing layer and reports any unvisited node as 'unconnected'. When the PG network legitimately has more than one component touching the top layer - notably on a backside-power PDK where the front-side and backside stacks are stitched only through bridge cells, and each bridge cell ends up in its own subgraph as seen from the top - the BFS misses entire subgraphs and reports thousands of spurious PSM-0038/PSM-0039 warnings. Seed the BFS from every node on the top layer instead. The cost is linear in the number of seeds, which is bounded by the number of top-layer shape pieces, so the overhead is negligible. The visited flag guarantees we still touch each reachable node exactly once. This is the third piece of LEF58_BACKSIDE support in PSM (after the front-side base layer fix in 0e7eabb). On gt2n/gcd with the multi- port tap LEF, analyze_power_grid now passes connectivity check on both vdd and vss without any PSM-003x warnings; the IR-drop report runs cleanly (numbers are zero pending source / power assignment but the solver no longer aborts). Asap7 / nangate45 are unchanged: their PG network has only one component touching the top layer so the multi-seed BFS behaves identically to the single-seed version. Signed-off-by: Matthew Guthaus <mrg@ucsc.edu>
…isolated nodes
Adds src/psm/test/check_power_grid_backside.tcl plus a tiny synthetic
backside-power LEF/DEF under src/psm/test/backside_data/. The test
locks in both BSPDN-related PSM fixes:
* isBackside()-aware ITerm base-layer pick in ir_network.cpp
(commit 0e7eabb). Without it every ITerm reports PSM-0039
'Unconnected instance' on this design.
* Multi-seed connectivity BFS in ir_solver.cpp (commit 6d4c658).
Without it the BFS misses the BPR-only subgraph and flags
PSM-0038 'Unconnected shape' on every followpin.
The synthetic LEF declares a routing layer B1 with
LEF58_BACKSIDE BACKSIDE and adds two macros: a bridge_tap with
vdd / vss PINs on both M1 and B1, and a backside_filler with
vdd / vss only on B1. The DEF places two taps and three fillers
on one row with a B1 followpin per net.
Also tightens ir_solver.cpp: when multi-seeding the BFS, a top-layer
seed may be isolated (no LayerConnection built for it) and
connections_map.at(node) used to throw. Switch to find() and skip
isolated seeds; the !isVisited() sweep below already covers
reporting them. Without this guard the new test fires
std::out_of_range on the vss net.
Registered in both CMake (or_integration_tests TESTS) and Bazel
(BUILD COMPULSORY_TESTS + data files for the backside_data dir).
Signed-off-by: Matthew Guthaus <mrg@ucsc.edu>
Adds src/pdn/test/add_pdn_connect_backside_warn.tcl which exercises the warning added on the bspdn branch (commit b55936a): when add_pdn_connect is given two layers that disagree on isBackside(), PDN cannot synthesize a via to bridge them and the connection has to come from a tap cell. The test calls add_pdn_connect with layers M1 (front) and B1 (LEF58_BACKSIDE backside) and expects PDN-1200 to appear in the log. Reuses the synthetic LEF+DEF test fixture under src/psm/test/backside_data/ so we don't ship a second copy. Registered in both CMake or_integration_tests TESTS and Bazel COMPULSORY_TESTS. Signed-off-by: Matthew Guthaus <mrg@ucsc.edu>
There was a problem hiding this comment.
Code Review
This pull request introduces support for backside layers (such as buried power rails and backside power delivery networks) across ODB, the detailed router (DRT), PDN, and PSM. It ensures that DRT treats backside layers as invisible, adds a boundary-crossing sanity check in PDN, and updates PSM's connectivity analysis to correctly handle backside-power-delivery designs. The review feedback highlights two key improvements: resolving a template iterator type mismatch in the new LEF backside parser, and optimizing a redundant map lookup in the DRT parser's net routing update logic.
A PDK cell that physically stitches a front-side power layer to a
LEF58_BACKSIDE-tagged layer (a buried-power-rail tap, basically) can
already be expressed today by giving its VDD / VSS PINs multiple
PORT blocks on the two sides; LEF 5.x semantics make those electric-
ally one node. That convention works for connectivity but does not
let a tool reason about *which* cells are intended as bridges, which
matters for PDN sanity checks, IR-drop source/sink classification,
and tap-cell selection on a BSPDN PDK.
This commit lets the PDK author mark such cells explicitly:
MACRO bridge_tap
...
PROPERTY LEF58_BACKSIDE_BRIDGE "BACKSIDEBRIDGE ;" ;
...
END bridge_tap
Implementation mirrors the LAYER LEF58_BACKSIDE work earlier in the
series:
* Adds a 1-bit is_backside_bridge flag to dbMasterFlags (taken from
spare_bits_19, shrunk to 18).
* Exposes dbMaster::setBacksideBridge() / isBacksideBridge() via the
public ODB API.
* Adds lefMacroBacksideBridgeParser modelled on the existing
lefMacroClassTypeParser.
* Wires it into lefin.cpp's MACRO property dispatcher.
* operator== updated so the flag is included in dbMaster equality.
Registered in CMake (or_integration_tests TESTS) and Bazel
(COMPULSORY_TESTS) with a small standalone Tcl test that reads a
two-cell LEF and asserts isBacksideBridge() = 1 on the tagged cell
and 0 on the untagged one.
No in-tree consumers yet; PDN/PSM tooling that wants to walk PG-net
connectivity across the front/back boundary can now consult this
flag instead of inferring intent from pin geometry.
Signed-off-by: Matthew Guthaus <mrg@ucsc.edu>
* odb LEF58_BACKSIDE parser: use the template Iterator parameter instead of hardcoding std::string::const_iterator in the qi::rule. * drt updateNetRouting: reuse the iterator returned by find() rather than performing a second map lookup. Signed-off-by: Matthew Guthaus <mrg@ucsc.edu>
A standard cut-layer via cannot bridge a front-side metal to a backside metal; PDN has no way to create the TSV or backside cut via that would be needed. Refuse the add_pdn_connect outright instead of warning and continuing. Renames the regression test from add_pdn_connect_backside_warn to add_pdn_connect_backside_err and wraps the offending call in a [catch] so the harness can still log-compare. Signed-off-by: Matthew Guthaus <mrg@ucsc.edu>
dbTech now provides first-class accessors for "the lowest front-side routing layer" and "the lowest backside routing layer", instead of making every caller walk getRoutingLayerCount() and filter by isBackside(). Switch PSM's IRNetwork ITerm base-layer pick to use the new helper. Signed-off-by: Matthew Guthaus <mrg@ucsc.edu>
Replace the multi-seed-from-every-top-layer-node workaround with a proper graph-level fix: * Pick the iterm base node's layer from the cell's primary side. Backside-only cells land on firstBacksideLayer() so their virtual base sits on the backside grid instead of dangling on an empty front-side rail; front-side and mixed cells keep firstRoutingLayer(). * Introduce BridgeConnection: an explicit zero-impedance virtual edge emitted only for cross-side iterm terminals on instances whose master is tagged LEF58_BACKSIDE_BRIDGE. Non-bridge cross-side pins are intentionally left unlinked, so a malformed PG that relies on a non-bridge cell to cross the front/back boundary surfaces as an open net instead of being silently stitched together. * Revert checkOpen() to single-seed BFS. With the explicit bridge edges, one seed reaches every legitimately-connected component, and the prior multi-seed approach (which could declare disjoint grids "connected") goes away. The isolated-seed find()-guard is no longer needed. Updates the backside regression fixture: bridge_tap now carries LEF58_BACKSIDE_BRIDGE, and the toy M1 stripes (which were not electrically connected to anything else in the design) are removed - the test is now a clean BSPDN topology where front/back can only be crossed through the bridge cell. Signed-off-by: Matthew Guthaus <mrg@ucsc.edu>
The test was reading the backside fixture via a relative path that crosses module boundaries (../../psm/test/backside_data/...). That works under CMake/ctest, but Bazel sandboxes each test and only exposes files declared as data deps, so the path fails to resolve. Copy the .lef/.def into pdn/test/backside_data/ and read from there so the test is self-contained. Signed-off-by: Matthew Guthaus <mrg@ucsc.edu>
|
Looks like some of the code modifications you made were not accurately reflected in the python/json generators |
|
@QuantamHD Ah, I didn't even realize that was generated code. Fixing the json so it is generated now. |
* odb: declare is_backside in the dbTechLayer code-generator schema so future regenerations preserve the field, the operator== check, and the bit-field packing. The hand-written setBackside / isBackside methods continue to live in the existing "User Code dbTechLayerPublicMethods" block. * drt: restore a null-guard around cutLayer->getDefaultViaDef() in FlexTAWorker::initFixedObjs. The setLayers filter only removes LEF58_BACKSIDE layers; cut layers below MIN_ROUTING_LAYER (e.g. contact-layer cuts on PDKs whose MIN_ROUTING_LAYER is well above M0) can legitimately have a null defaultViaDef left by initDefaultVias and would crash here. Earlier commit message claimed the filter superseded the guard; that was wrong - the guard is correct in its own right and the filter does not cover this case. Signed-off-by: Matthew Guthaus <mrg@ucsc.edu>
|
Just some food for thought here. Perhaps we should treat back side layers as a 3d chiplet instead of part of the routing layer stack proper. That way we wouldn't have the weirdness of a layer sandwich. |
When the per-worker grid graph is large (many routing layers × fine pitch × full-die clip box, as on backside-power PDKs), the product `xDim * yDim * zDim * 3` used to size `prevDirs_` and index it from `setPrevAstarNodeDir`/`getPrevAstarNodeDir` exceeds INT32_MAX. With plain `int` math this wraps negative, causing `prevDirs_.resize()` to throw `std::length_error` from `vector<bool>::_M_fill_insert` and detailed routing to crash before the first iteration. Promote the multiply to `std::size_t` so the capacity and per-node base index stay correct for grids up to size_t. Signed-off-by: Matthew Guthaus <mrg@ucsc.edu>
dbBlock::getGCellTileSize() reads track spacings of the 2nd/3rd/4th routing layer via dbTech::findRoutingLayer(), assuming those indices hit the lowest signal metals (M2/M3/M4). On PDKs with backside power (BSPDN) the LEF orders BRDL/BM*/BPR routing layers first, so those indices instead pick coarse backside metals and the resulting tile size is roughly 30x too large. That collapses the GCell grid to a handful of cells, gives detailed routing one die-sized worker, and indirectly trips the FlexGridGraph capacity overflow. Walk getLayers() and count only non-backside routing layers so the heuristic targets frontside signal metals regardless of LEF ordering. Signed-off-by: Matthew Guthaus <mrg@ucsc.edu>
setLayers() already drops backside routing/cut layers from frTech, so
any block via whose cut/top/bottom layer is on the backside has no
matching frLayer and triggers DRT-0097/0098/0099 ("Cannot find ...
layer") during global routing. Detect such vias up front (via params
and via boxes both) and skip them, mirroring the existing backside
guards elsewhere in this file.
Signed-off-by: Matthew Guthaus <mrg@ucsc.edu>
Rename `firstRoutingLayer()` / `firstBacksideLayer()` to `firstFrontsideRoutingLayer()` / `firstBacksideRoutingLayer()` so the side filter is explicit in both names and "routing" is consistent. By PDK convention, backside metals are numbered going away from the substrate (e.g. BM1 nearest, BM4 farther out, BRDL outermost), mirror- ing the front-side numbering. In the LEF, however, the backside stack is listed in physical stack order from the outermost backside layer down (BRDL ... BPR) before the frontside (M0 ... RDL). Iterating `findRoutingLayer(level)` from level 1 upward therefore landed on the outermost backside layer, which is the opposite of what callers (PSM) need: they want the backside layer closest to the substrate. `firstBacksideRoutingLayer()` now iterates top-down and returns the substrate-closest backside layer (e.g. BPR on the bspdn gt2n PDK). Header doc updated to make that explicit. Signed-off-by: Matthew Guthaus <mrg@ucsc.edu>
Use the new `firstFrontsideRoutingLayer()` / `firstBacksideRoutingLayer()` names. Remove the defensive fallback that reassigned `front_base` to `findRoutingLayer(1)` when no front-side layer was found. That branch only triggered while the LEF backside flag wasn't being parsed (every layer reporting `isBackside()=0`); with the parser correctly hooked up in odb, a tech that legitimately has no front-side routing layers is not a real configuration. Letting `front_base` stay null surfaces a clear caller-side error instead of silently substituting a backside layer as the front-side base node. Signed-off-by: Matthew Guthaus <mrg@ucsc.edu>
Built-in LEF58 properties are dispatched by a hardcoded name table in the LEF reader (lefin.cpp) and do not require an explicit PROPERTYDEFINITIONS statement; matching the style of existing tests for LEF58_BACKSIDE, LEF58_RECTONLY, etc. Verified that the parser still picks up LEF58_BACKSIDE_BRIDGE on `bridge_tap` and the .ok golden still matches. Signed-off-by: Matthew Guthaus <mrg@ucsc.edu>
mguthaus
left a comment
There was a problem hiding this comment.
I've confirmed that this works on gcd and aes (which I have running on a soon-to-submit PR to ORFS). There are some integrated changes to fix the DRT routing grid and worker sizing which became a problem because of the DBU sizes in some of the back-side layers. However, those designs will work correctly now when I submit this PR and the later fixes.
Use the dbBox's tech layer directly instead of running every pin geometry through generatePolygonsFromBox(); we only need to know which side(s) of the stack the pin sits on, so the per-shape polygon math and the instance transform are unnecessary. Also reach the master via inst->getMaster() instead of iterm->getMTerm()->getMaster() since inst is already in scope. Signed-off-by: Matthew Guthaus <mrg@ucsc.edu>
The check that refused connect rules spanning the front-side/backside boundary lived only in the add_pdn_connect Tcl command. Moving it into Connect::Connect() makes it catch every PDN connect rule regardless of how it was constructed (Tcl entry point, programmatic API, future callers), which matches the underlying invariant: PDN can never bridge the two sides via a cut-layer via, so any such rule is wrong wherever it originates. Tcl-side duplicate guard removed. Signed-off-by: Matthew Guthaus <mrg@ucsc.edu>
Auto-applied by buildifier (format + lint=fix) to satisfy the Buildifier format / Buildifier lint GitHub-Action checks on PR The-OpenROAD-Project#10547. - src/psm/test/BUILD: move backside_data/backside.{def,lef} into alphabetical position within the data filegroup. - src/pdn/test/BUILD: move the add_pdn_connect_backside_err key into alphabetical position within the per-test data dict to clear the unsorted-dict-items warning at line 295. Signed-off-by: Matthew Guthaus <mrg@ucsc.edu>
Clears the remaining Clang-Tidy-Bazel warnings introduced by the LEF58_BACKSIDE PR (The-OpenROAD-Project#10547): - src/drt/src/io/io.cpp:480: change the loop index in the backside-via-skip path in setVias() from `int i = 0; i < (int) via->getViaLayerRuleCount()` to `uint32_t i = 0; i < via->getViaLayerRuleCount()` to match the API return type and silence modernize-use-integer-sign-comparison. - src/drt/src/io/io.cpp:1068: mark updatefrAccessPoint static. It is only invoked from within this TU (line 1209 and 1299); the return-type change from void to bool on the bspdn branch made clang-tidy re-flag misc-use-internal-linkage. - src/odb/src/lefin/lefTechLayerBacksideParser.cpp: wrap the template parse() helper in an unnamed namespace inside odb::lefTechLayerBackside so the helper has internal linkage. The public entry odb::lefTechLayerBacksideParser::parse() still resolves through lefTechLayerBackside::parse() because the unnamed namespace is implicitly part of its parent for qualified lookup. No functional change. Local build + odb.lef58_backside_bridge.tcl golden test both clean. Signed-off-by: Matthew Guthaus <mrg@ucsc.edu>
|
@gadfort are your concerns resolved? |
|
@mguthaus do you have plans for how bridge cells will get inserted? |
|
@maliberty In the GT2N PDK, the "TSV" is a nano-TSV built inside each standard cell during virtual fabrication: BPR -> 2 nm via -> S/D Wrap-Around Contact, so the cell's vdd/vss pin already sits on BPR and the chip-level PG never leaves the backside stack. ORFS mirrors this (I will submit this to ORFS today or tomorrow) which builds only a backside grid (BPR followpins -> BM1/BM2 mesh) and lets the followpin shapes overlap each cell's BPR pin directly, so PDN places no chip-level TSV by design. The LEF58_BACKSIDE_BRIDGE / BridgeConnection plumbing in this PR is there to support hybrid front+back PG flows in the future; on stock GT2N it stays dormant since there's no frontside PG to bridge to. The tap cells right now have both back-side and front-side connections, so these could be used to make a hybrid front+back now to mitigate any issues before we get a PDK that requires it though. Then we will need to think about tap placement for PDN IR drop. |
|
How does the power from the package reach the backside in GT2N? |
|
C4 bonds on the backside through the backside RDL layer. |
|
Is C4 on both side or should I reverse the question and ask how signals reach the front side? |
|
Presumably the signals on the front side would reach through a pad frame. The PDK doesn't solve this issue and doesn't provide area IO or perimeter IO libraries. |
|
@mguthaus I'm getting this: |
|
@gadfort I didn't see this in any of my runs. How are you getting this? My tests are in this other branch of ORFS |
Summary
Adds first-class support for LEF58_BACKSIDE-tagged layers and the LEF58_BACKSIDE_BRIDGE macro property across ODB, DRT, PDN, and PSM so OpenROAD can ingest tech LEFs from backside-power-delivery (BSPDN) PDKs without crashing the front-side router and without producing spurious power-grid connectivity errors.
Eight commits, each scoped to one module:
dbTechLayer::isBackside()and a parser for theLEF58_BACKSIDEproperty (previously logged as unsupported and discarded).setLayers,setTracks,setBTerms,setTechViaRules,setNDRs,updateNetRouting,updatefrAccessPoint). Without this DRT crashes inFlexTAWorker::initFixedObjswhen it tries to build a default via on a backside cut layer that has no geometry.add_pdn_connectis given two layers that disagree onisBackside(). A standard cut-layer via cannot bridge front-side to backside; the connection has to come from a tap cell.check_power_grid_backside.tclexercising both PSM fixes on a tiny synthetic backside design (LEF + DEF undersrc/psm/test/backside_data/). Also tightens the BFS to skip isolated seed nodes viafind()instead of throwing onat().add_pdn_connect_backside_warn.tclreusing the same synthetic LEF/DEF to verify the cross-boundary warning fires.dbMaster::isBacksideBridge()and a parser for theLEF58_BACKSIDE_BRIDGEmacro property. Lets a PDK author flag a tap cell as physically bridging the front-side and backside power stacks, instead of leaving downstream tools to infer the bridge from multi-port-PIN geometry. No in-tree consumers yet; this is a foundational API for follow-on PDN/PSM work.Test coverage
Unit tests in
src/odb/test/,src/psm/test/, andsrc/pdn/test/cover the new functionality directly. No existing tests should be affected -- the new code paths are gated onLEF58_BACKSIDE/LEF58_BACKSIDE_BRIDGE, which no shipped OpenROAD test PDK uses today, so on front-side-only PDKs the new code paths reduce to no-ops (the multi-seed BFS collapses to the single-seed behavior when the network has only one connected component touching the top layer).The full end-to-end PnR flow on a real BSPDN PDK is not tested here, because no LEF58_BACKSIDE-tagged tech ships in OpenROAD's test tree. The full flow is exercised separately via an ORFS PDK update that adds the GT2N 2nm PDK; that ORFS work depends on the OpenROAD changes in this PR landing first.
Out of scope
isBacksideBridge(). The parser and accessor land here; consumers can opt in incrementally.Test plan
ctest -R odb.lef58_backside_bridgepassesctest -R psm.check_power_grid_backsidepassesctest -R pdn.add_pdn_connect_backside_warnpassesctest -L odb,-L psm, or-L pdnon front-side-only PDKs (multi-seed BFS reduces to single-seed)🤖 Generated with Claude Code