dpl: negotiation legalizer blocks the unrotated master footprint for R90/R270/MXR90/MYR90 fixed macros · Issue #11735 · The-OpenROAD-Project/OpenROAD · GitHub
Skip to content

dpl: negotiation legalizer blocks the unrotated master footprint for R90/R270/MXR90/MYR90 fixed macros #11735

Open

Description

@expsg-jiacheng

Describe the bug

With the default negotiation legalizer, detailed_placement blocks a fixed macro placed R90, R270, MXR90 or MYR90 using the master's unrotated width and height, anchored at the instance's bbox lower-left. The blocked area on the legalizer grid is the wrong shape:

  1. The part of the unrotated footprint that lies outside the real bbox is treated as blocked. Standard cells there are evicted even when they are already legal, and the legalizer moves them into the neighbouring rows.
  2. The part of the real bbox outside the unrotated footprint is treated as free. When rows run under the macro, cells are left overlapping it and check_placement fails with DPL-0033.

DPL-0393 shows the cause: the macro's height in rows is the master height in every orientation (81 rows for a 152.57 x 113.4 um macro, where R90 should give 109).

detailed_placement -use_diamond_legalizer handles the same placements correctly.

In NegotiationLegalizer::initFromDb() (src/dpl/src/NegotiationLegalizer.cpp, lines 421-425 at c4d317e; the file is identical on master eb14e40), every cell is sized from the master:

cell.width = std::max(1, static_cast<int>(std::round(static_cast<double>(master->getWidth()) / site_width_)));
cell.height = dpl_grid->gridHeight(master).v;

buildGrid() then sets capacity 0 over x .. x + width and y .. y + height for each fixed cell. Opendp::getBbox() in dbToOpendp.cpp swaps width and height for these orientations (swapWidthHeight()); the negotiation legalizer does not.

Expected Behavior

The negotiation legalizer blocks each fixed instance's oriented bbox, as the diamond legalizer does. In the reproducer below that means:

  • no outside cell moves;
  • no cell overlaps the macro;
  • check_placement passes in every orientation.

Environment

OpenROAD 26Q4 (c4d317e4f), CMake Release build, gcc 12.3.0
Rocky Linux 8.10, kernel 4.18.0-553
NegotiationLegalizer.cpp is unchanged on master (eb14e40b7b)

To Reproduce

Save the script below as src/dpl/test/rotated_macro.tcl, then:

cd src/dpl/test
openroad -no_init -exit rotated_macro.tcl | grep RESULT

The script puts one fakeram45_512x64 at (100, 100) um, FIRM, in each orientation. It pre-places INV_X1 cells, every fourth site of each row, in one of two regions:

  • outside: legal sites in a strip to the right of the macro's real bbox;
  • inside: sites on the part of the real bbox that the unrotated footprint misses. This region is empty for R0 and MX.
# Run from src/dpl/test. One fixed fakeram45_512x64 (152.57 x 113.4 um) in
# each orientation, with INV_X1 cells pre-placed in one of two regions:
#   outside: legal sites in a strip right of the macro's real bbox
#   inside:  sites on the part of the real bbox that the unrotated
#            master footprint (anchored at the bbox lower-left) misses
# A correct legalizer moves no "outside" cell and leaves no "inside" cell
# overlapping the macro.
read_lef Nangate45/Nangate45.lef
read_lef Nangate45/fakeram45_512x64.lef
set f [open rotated_macro.v w]
puts $f "module top (); fakeram45_512x64 ram (); endmodule"
close $f
read_verilog rotated_macro.v
link_design top
initialize_floorplan -die_area {0 0 400 400} -core_area {10 10 390 390} \
  -site FreePDK45_38x28_10R_NP_162NW_34O

set block [ord::get_db_block]
set dbu [$block getDbUnitsPerMicron]
set ram [$block findInst ram]
set mw [[$ram getMaster] getWidth]
set mh [[$ram getMaster] getHeight]
set inv [[ord::get_db] findMaster INV_X1]
set site_w [[lindex [$block getRows] 0] getSpacing]

proc fill {x0 y0 x1 y1} {
  set cells {}
  foreach row [$::block getRows] {
    lassign [$row getOrigin] rx ry
    if {$ry < $y0 || $ry + [[$row getSite] getHeight] > $y1} { continue }
    set k0 [expr {int(ceil(double($x0 - $rx) / $::site_w)) + 1}]
    for {set k $k0} {$rx + ($k + 2) * $::site_w <= $x1} {incr k 4} {
      set c [odb::dbInst_create $::block $::inv "c[llength $cells]"]
      $c setOrient [$row getOrient]
      $c setLocation [expr {$rx + $k * $::site_w}] $ry
      $c setPlacementStatus PLACED
      lappend cells [list $c [$c getLocation]]
    }
  }
  return $cells
}

# -use_diamond_legalizer persists for the rest of the session, so it runs last.
foreach legalizer {negotiation diamond} {
  foreach orient {R0 MX R90 MXR90 R270 MYR90} {
    foreach region {outside inside} {
      foreach i [$block getInsts] {
        if {[$i getMaster] eq $inv} { odb::dbInst_destroy $i }
      }
      $ram setPlacementStatus PLACED
      $ram setOrient $orient
      $ram setLocation [expr {100 * $dbu}] [expr {100 * $dbu}]
      $ram setPlacementStatus FIRM
      set bb [$ram getBBox]
      if {$region eq "outside"} {
        set cells [fill [$bb xMax] [$bb yMin] \
          [expr {[$bb xMax] + abs($mw - $mh)}] [expr {[$bb yMin] + min($mw, $mh)}]]
      } else {
        set cells [fill [$bb xMin] [expr {[$bb yMin] + min($mw, $mh)}] \
          [$bb xMax] [$bb yMax]]
      }
      if {$legalizer eq "diamond"} {
        detailed_placement -use_diamond_legalizer
      } else {
        detailed_placement
      }
      set moved 0
      set over 0
      foreach entry $cells {
        lassign $entry c loc
        if {[$c getLocation] ne $loc} { incr moved }
        set cb [$c getBBox]
        if {[$cb xMax] > [$bb xMin] && [$cb xMin] < [$bb xMax]
            && [$cb yMax] > [$bb yMin] && [$cb yMin] < [$bb yMax]} {
          incr over
        }
      }
      set check [expr {[catch {check_placement}] ? "FAIL" : "pass"}]
      puts [format "RESULT %-11s %-5s %-7s cells %4d  moved %4d  overlapping macro %4d  check_placement %s" \
        $legalizer $orient $region [llength $cells] $moved $over $check]
    }
  }
}

Relevant log output

RESULT negotiation R0    outside cells 4080  moved    0  overlapping macro    0  check_placement pass
RESULT negotiation R0    inside  cells    0  moved    0  overlapping macro    0  check_placement pass
RESULT negotiation MX    outside cells 4080  moved    0  overlapping macro    0  check_placement pass
RESULT negotiation MX    inside  cells    0  moved    0  overlapping macro    0  check_placement pass
RESULT negotiation R90   outside cells 4080  moved 4080  overlapping macro   13  check_placement FAIL
RESULT negotiation R90   inside  cells 4023  moved 4023  overlapping macro  330  check_placement FAIL
RESULT negotiation MXR90 outside cells 4080  moved 4080  overlapping macro   13  check_placement FAIL
RESULT negotiation MXR90 inside  cells 4023  moved 4023  overlapping macro  330  check_placement FAIL
RESULT negotiation R270  outside cells 4080  moved 4080  overlapping macro   13  check_placement FAIL
RESULT negotiation R270  inside  cells 4023  moved 4023  overlapping macro  330  check_placement FAIL
RESULT negotiation MYR90 outside cells 4080  moved 4080  overlapping macro   13  check_placement FAIL
RESULT negotiation MYR90 inside  cells 4023  moved 4023  overlapping macro  330  check_placement FAIL
RESULT diamond     R0    outside cells 4080  moved    0  overlapping macro    0  check_placement pass
RESULT diamond     R0    inside  cells    0  moved    0  overlapping macro    0  check_placement pass
RESULT diamond     MX    outside cells 4080  moved    0  overlapping macro    0  check_placement pass
RESULT diamond     MX    inside  cells    0  moved    0  overlapping macro    0  check_placement pass
RESULT diamond     R90   outside cells 4080  moved    0  overlapping macro    0  check_placement pass
RESULT diamond     R90   inside  cells 4023  moved 4023  overlapping macro    0  check_placement pass
RESULT diamond     MXR90 outside cells 4080  moved    0  overlapping macro    0  check_placement pass
RESULT diamond     MXR90 inside  cells 4023  moved 4023  overlapping macro    0  check_placement pass
RESULT diamond     R270  outside cells 4080  moved    0  overlapping macro    0  check_placement pass
RESULT diamond     R270  inside  cells 4023  moved 4023  overlapping macro    0  check_placement pass
RESULT diamond     MYR90 outside cells 4080  moved    0  overlapping macro    0  check_placement pass
RESULT diamond     MYR90 inside  cells 4023  moved 4023  overlapping macro    0  check_placement pass

# every negotiation run, all orientations:
[INFO DPL-0393]   height 1 row(s): 4080 cells
[INFO DPL-0393]   height 81 row(s): 1 cells

# negotiation, R90, outside (4080 cells that were already legal):
        0 |      39141 |      3617 |      7230
       74 |         25 |        13 |        12
[WARNING DPL-0700] Negotiation phase 1: violations stuck at 25 for 3 consecutive iterations.
[WARNING DPL-0701] NegotiationLegalizer did not fully converge. Violations remain: 5
max displacement           53.1 u
[WARNING DPL-0005] Overlap check failed (1).
[ERROR DPL-0033] detailed placement checks failed during check placement.

Additional Context

Found on a sky130hd block with OpenRAM SRAM macros placed MXR90 in a column. The legalizer emptied a strip beside each macro, as wide as the macro's width minus its height. It pushed those cells into the next strip at about 80% local utilization, and detailed routing then stalled on that strip. A rotated column at the die edge hides the problem, because the phantom area falls outside the core.

Workarounds: -use_diamond_legalizer, or placement blockages over the phantom area during global placement only, removed before detailed_placement.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions