check_placement: padding check flags a legal cell above a fixed block whose bottom is in the upper half of a row

Open Beginner friendly
#12,038 1 comment 0 reactions 1 assignee View on GitHub

Maintainers usually reply within 1 day

@gudeh is already working on this.

Since Nov 2, 2026.

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
78/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
cpp
Domain
backend, devtools

Research direction

Read src/dpl/src/PlacementDRC.cpp around PlacementDRC::checkPadding(const Node* cell) and compare its row selection with Grid::gridSnapDownY in src/dpl/src/infrastructure/Grid.cpp. Reproduce with the supplied tech.lef, cells.lef, flagged.def and check.tcl, then run check_placement. Done when the legal cell above the fixed block passes while padding violations are still detected in covered rows.

Written by the indexing model from the issue text.

Description

bug dpl
Description

check_placement reports [WARNING DPL-0011] Padding check failed and then [ERROR DPL-0033] for a FIXED block
whose bottom edge lies in the upper half of a row and whose height is not a whole number of rows, when a standard
cell sits legally in the row just above the block's top edge. There is no overlap and no padding is set; in our flow
detailed_placement, run just before the check, reported Found 0 overlaps between adjacent cells and
0 padding violations.

The cause seems to be a mismatch in how rows are assigned to the block:

  • The grid marks a block's rows from the row its bottom snaps down to (Grid::gridSnapDownY, used when painting
    pixels, src/dpl/src/infrastructure/Grid.cpp), so the legalizer keeps cells out of exactly the rows the block
    covers and may put a cell in the row above its top.
  • PlacementDRC::checkPadding(const Node* cell) starts from the row nearest the bottom (grid_->gridRoundY(cell))
    and ends at gridEndY(gridYToDbu(y) + cell->getHeight()), measured from that rounded row
    (src/dpl/src/PlacementDRC.cpp). When the bottom is in the upper half of a row, the rounded row is one row higher,
    so the checked window extends one row past the block's real top, onto that legal cell, and the BLOCK-vs-CORE
    "no padded overlap" rule fails.

Flows that cut rows around macros (for example ORFS's tapcell step) never put a cell in that row, which may be why it
has not shown up. We met it in a flow that keeps uncut rows, with memory macros of fractional row height placed off
the row grid.

Suggested Solution

Start the padding check where the grid paints the cell, e.g. gridSnapDownY(cell) in the one-argument
checkPadding (or compute the window's end from the cell's real top rather than from the rounded row). For cells on
the row grid both give the same rows, so only off-row blocks would change.

Steps to reproduce

A self-contained case, no platform files: a 10 x 4.3 um block (3.07 rows of 1.4 um) FIXED at y = 3.6 um, 0.8 um
into row 2, so it covers rows 2-5 (top edge 7.9 um); an inverter PLACED in row 6 (y = 8.4 um), 0.5 um above the
block and not overlapping it.

tech.lef

VERSION 5.8 ;
BUSBITCHARS "[]" ;
DIVIDERCHAR "/" ;
UNITS
  DATABASE MICRONS 1000 ;
END UNITS
MANUFACTURINGGRID 0.005 ;
SITE core
  CLASS CORE ;
  SYMMETRY Y ;
  SIZE 0.2 BY 1.4 ;
END core
LAYER metal1
  TYPE ROUTING ;
  DIRECTION HORIZONTAL ;
  PITCH 0.2 ;
  WIDTH 0.07 ;
END metal1
END LIBRARY

cells.lef

VERSION 5.8 ;
BUSBITCHARS "[]" ;
DIVIDERCHAR "/" ;
MACRO INV
  CLASS CORE ;
  ORIGIN 0 0 ;
  SIZE 0.6 BY 1.4 ;
  SYMMETRY X Y ;
  SITE core ;
END INV
MACRO RAM
  CLASS BLOCK ;
  ORIGIN 0 0 ;
  SIZE 10 BY 4.3 ;
  SYMMETRY X Y ;
END RAM
END LIBRARY

flagged.def

VERSION 5.8 ;
DIVIDERCHAR "/" ;
BUSBITCHARS "[]" ;
DESIGN top ;
UNITS DISTANCE MICRONS 1000 ;
DIEAREA ( 0 0 ) ( 20000 14000 ) ;
ROW row_0 core 0 0 N DO 100 BY 1 STEP 200 0 ;
ROW row_1 core 0 1400 FS DO 100 BY 1 STEP 200 0 ;
ROW row_2 core 0 2800 N DO 100 BY 1 STEP 200 0 ;
ROW row_3 core 0 4200 FS DO 100 BY 1 STEP 200 0 ;
ROW row_4 core 0 5600 N DO 100 BY 1 STEP 200 0 ;
ROW row_5 core 0 7000 FS DO 100 BY 1 STEP 200 0 ;
ROW row_6 core 0 8400 N DO 100 BY 1 STEP 200 0 ;
ROW row_7 core 0 9800 FS DO 100 BY 1 STEP 200 0 ;
ROW row_8 core 0 11200 N DO 100 BY 1 STEP 200 0 ;
ROW row_9 core 0 12600 FS DO 100 BY 1 STEP 200 0 ;
COMPONENTS 2 ;
  - ram RAM + FIXED ( 2000 3600 ) N ;
  - inv INV + PLACED ( 4000 8400 ) N ;
END COMPONENTS
END DESIGN

check.tcl

read_lef tech.lef
read_lef cells.lef
read_def $::env(DEF)
if { [catch {check_placement -verbose} msg] } { puts "check_placement FAILED: $msg" } else { puts "check_placement passed" }
DEF=flagged.def openroad -no_init -exit check.tcl

Output:

[WARNING DPL-0011] Padding check failed (1).
 ram
[ERROR DPL-0033] detailed placement checks failed during check placement.
check_placement FAILED: DPL-0033

Moving the block down to y = 3.4 um (0.6 um into its row, the lower half; same rows covered, the inverter unchanged)
makes check_placement pass.

Version

Reproduced with OpenROAD 26Q3-2811-gc487fc7086. As of 2026-10-28, checkPadding(const Node*) on master still uses
gridRoundY as above.

Dominant language
Verilog
Stars
3.2k
Forks
1.1k
Avg merge
2d 9h
Merged PRs (30d)
270

Getting set up

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from The-OpenROAD-Project/OpenROAD

All issues in The-OpenROAD-Project/OpenROAD

Similar issues

More Backend & API Design issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.