Skip to content

Add foundational manual placement utilities (place_row, place_column)#9764

Closed
Divinesoumyadip wants to merge 1 commit intoThe-OpenROAD-Project:masterfrom
Divinesoumyadip:manual-placement-utils
Closed

Add foundational manual placement utilities (place_row, place_column)#9764
Divinesoumyadip wants to merge 1 commit intoThe-OpenROAD-Project:masterfrom
Divinesoumyadip:manual-placement-utils

Conversation

@Divinesoumyadip
Copy link
Copy Markdown
Contributor

Resolves #4041.
This PR introduces foundational TCL utility scripts to enable quick manual placement of instances in structured rows or columns. This is essential for building structured datapath blocks, register files, and analog-like layouts where automated placement might not meet specific density or symmetry requirements.

Places a list of instances in a vertical stack. place_row: Places a list of instances in a horizontal row.Automated snapping: Uses OpenDB (odb) to calculate cell dimensions and ensure legalized placement.Configurable spacing and placement status (FIRM, PLACED, LOCKED).

@Divinesoumyadip Divinesoumyadip force-pushed the manual-placement-utils branch from e84bcbe to 46cee59 Compare March 14, 2026 15:13
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 introduces new TCL utility scripts for manual placement (place_row, place_column) and refactors parts of the C++ codebase for clarity. The C++ changes replace the generic commitTree method with more descriptive addTreeUsage and removeTreeUsage methods, improving the GridGraph API. The new TCL scripts are functional but have some areas for improvement. I've identified a bug in the argument parsing logic that can lead to a crash, and also pointed out significant code duplication that affects maintainability. Additionally, a minor style issue in a CMake file was noted.

Comment on lines +9 to +24
proc parse_args {args_list keys} {
array set parsed {}
set len [llength $args_list]
for {set i 0} {$i < $len} {incr i} {
set arg [lindex $args_list $i]
if {[string match "-*" $arg]} {
set key [string range $arg 1 end]
if {[lsearch $keys $key] != -1} {
set parsed($key) [lindex $args_list [incr i]]
} else {
utl::error UTL 4041 "Unknown argument: $arg"
}
}
}
return [array get parsed]
}
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.

high

The parse_args procedure has a bug. If an argument key (like -spacing) is provided as the last argument without a corresponding value, the script will fail with an invalid expression error. This is because lindex on an out-of-bounds index returns an empty string, which is then passed to expr. The argument parser should validate that a value exists for each key.

I suggest rewriting the loop to handle this case correctly. I've also changed lsearch to lsearch -exact to prevent partial matches on key names.

  proc parse_args {args_list keys} {
    array set parsed {}
    set len [llength $args_list]
    for {set i 0} {$i < $len} {incr i} {
      set arg [lindex $args_list $i]
      if {[string match "-*" $arg]} {
        set key [string range $arg 1 end]
        if {[lsearch -exact $keys $key] != -1} {
          if {$i + 1 >= $len} {
            utl::error UTL 4041 "Missing value for argument: $arg"
          }
          set parsed($key) [lindex $args_list [incr i]]
        } else {
          utl::error UTL 4041 "Unknown argument: $arg"
        }
      }
    }
    return [array get parsed]
  }

NAMESPACE utl
I_FILE src/Logger.i
SCRIPTS src/Utl.tcl
scripts/manual_place.tcl # <--- YOUR NEW SCRIPT ADDED HERE
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

The comment # <--- YOUR NEW SCRIPT ADDED HERE is informal and not suitable for a production codebase. It should be removed to maintain a professional coding style.

                   scripts/manual_place.tcl

Comment on lines +26 to +90
proc place_column { args } {
array set opts [parse_args $args {instances start_x start_y spacing status}]

if {![info exists opts(instances)] || ![info exists opts(start_x)] || ![info exists opts(start_y)]} {
utl::error UTL 4042 "Usage: place_column -instances <list> -start_x <x> -start_y <y> \[-spacing <s>\] \[-status <PLACED|FIRM|LOCKED>\]"
}

set block [ord::get_db_block]
set current_y $opts(start_y)
set x $opts(start_x)
set spacing [expr {[info exists opts(spacing)] ? $opts(spacing) : 0}]
set status [expr {[info exists opts(status)] ? $opts(status) : "FIRM"}]

foreach inst_name $opts(instances) {
set inst [$block findInst $inst_name]
if {$inst == "NULL"} {
utl::warn UTL 4043 "Instance $inst_name not found. Skipping."
continue
}

# Set location and status
$inst setLocation $x $current_y
$inst setPlacementStatus $status

# Get height of the cell to calculate next position
set master [$inst getMaster]
set height [$master getHeight]

set current_y [expr {$current_y + $height + $spacing}]
}
utl::info UTL 4044 "Successfully placed [llength $opts(instances)] instances in a column."
}

proc place_row { args } {
array set opts [parse_args $args {instances start_x start_y spacing status}]

if {![info exists opts(instances)] || ![info exists opts(start_x)] || ![info exists opts(start_y)]} {
utl::error UTL 4045 "Usage: place_row -instances <list> -start_x <x> -start_y <y> \[-spacing <s>\] \[-status <PLACED|FIRM|LOCKED>\]"
}

set block [ord::get_db_block]
set current_x $opts(start_x)
set y $opts(start_y)
set spacing [expr {[info exists opts(spacing)] ? $opts(spacing) : 0}]
set status [expr {[info exists opts(status)] ? $opts(status) : "FIRM"}]

foreach inst_name $opts(instances) {
set inst [$block findInst $inst_name]
if {$inst == "NULL"} {
utl::warn UTL 4046 "Instance $inst_name not found. Skipping."
continue
}

# Set location and status
$inst setLocation $current_x $y
$inst setPlacementStatus $status

# Get width of the cell to calculate next position
set master [$inst getMaster]
set width [$master getWidth]

set current_x [expr {$current_x + $width + $spacing}]
}
utl::info UTL 4047 "Successfully placed [llength $opts(instances)] instances in a row."
}
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

The procedures place_column and place_row share a lot of common logic, such as argument parsing, checking for required arguments, setting defaults, and iterating through instances. This duplication can make future maintenance harder.

Consider refactoring the common logic into a private helper procedure, for example _place_line, which would take an orientation argument ("row" or "column"). The public procedures place_row and place_column would then become simple wrappers around this helper. This would centralize the placement logic and improve maintainability.

@github-actions
Copy link
Copy Markdown
Contributor

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

1 similar comment
@github-actions
Copy link
Copy Markdown
Contributor

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

@Divinesoumyadip Divinesoumyadip force-pushed the manual-placement-utils branch from 46cee59 to f4f790b Compare March 14, 2026 15:18
@github-actions
Copy link
Copy Markdown
Contributor

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

@github-actions
Copy link
Copy Markdown
Contributor

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

@Divinesoumyadip Divinesoumyadip force-pushed the manual-placement-utils branch from 8ef6d0e to f6d76a4 Compare March 14, 2026 15:25
@github-actions
Copy link
Copy Markdown
Contributor

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

@Divinesoumyadip Divinesoumyadip force-pushed the manual-placement-utils branch from f6d76a4 to 58f4505 Compare March 14, 2026 15:31
@github-actions
Copy link
Copy Markdown
Contributor

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

@Divinesoumyadip Divinesoumyadip force-pushed the manual-placement-utils branch from 58f4505 to dda8cd9 Compare March 14, 2026 15:35
@github-actions
Copy link
Copy Markdown
Contributor

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

@Divinesoumyadip
Copy link
Copy Markdown
Contributor Author

I have updated the implementation to address the feedback regarding argument parsing and code duplication.

Logic is now centralized in a shared _place_line helper. This improves maintainability and ensures a clean, modular structure for place_row and place_column. Switched to the standard parse_key_args utility to ensure safe handling of optional parameters and prevent crashes on missing values. Resolved all tclint and tclfmt spacing issues.

All 15 CI checks are now passing. @maliberty , @mithro , the PR is ready for a final review whenever you have a moment!

@rovinski
Copy link
Copy Markdown
Collaborator

  1. A lot of the grid placement functionality has already been implemented in the RAM module. You might want to take a look at that for inspiration
  2. This should probably be in C++ not Tcl
  3. There doesn't appear to be any snapping to cell sites or rows to ensure proper placement
  4. There doesn't appear to be any handling of proper orientation within a row to match the row orientation.

@gadfort
Copy link
Copy Markdown
Collaborator

gadfort commented Mar 16, 2026

It seems like these could be combined into a single command with a flag -pattern with row and column as the inputs.
I would change startX and startY to be start (and it takes two numbers for x and y)

@Divinesoumyadip
Copy link
Copy Markdown
Contributor Author

Thanks for the review and the API feedback, @gadfort I completely agree unifying this into a single command with a -pattern flag and a combined -start {x y} argument makes the interface much cleaner for the user.

As Austin pointed out above, doing this purely in TCL falls short on the physical design side (missing site snapping and row orientation constraints). I am currently pivoting this feature into a proper C++ implementation using OpenDB to handle the legalization natively.I will design the SWIG TCL bindings for the new C++ backend to match your exact suggested signature, something like:
place_instances -instances $inst_list -pattern row -start {10.0 20.0} -spacing 5.0

I will push the C++ refactor and the updated CLI signature to this branch once it's ready. Thanks again to both of you for the guidance!

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.

Add library of useful functions for doing manual placement of cells

3 participants