Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
206 changes: 206 additions & 0 deletions src/dpl/src/infrastructure/Grid.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "Padding.h"
#include "boost/polygon/polygon.hpp"
#include "dpl/Opendp.h"
#include "network.h"
#include "odb/db.h"
#include "odb/dbShape.h"
#include "odb/dbTransform.h"
Expand Down Expand Up @@ -781,4 +782,209 @@ DbuY Grid::rowHeight(GridY index)
return row_index_to_pixel_height_.at(index.v);
}

void Grid::computeUtilizationMap(Network* network,
float area_weight,
float pin_weight)
{
// Get grid dimensions
const int grid_size = row_count_.v * row_site_count_.v;

if (grid_size == 0) {
return; // No grid to work with
}

// Store weights for incremental updates
area_weight_ = area_weight;
pin_weight_ = pin_weight;

// Initialize member vectors for area and pin density accumulation
total_area_.assign(grid_size, 0.0f);
total_pins_.assign(grid_size, 0.0f);
utilization_density_.assign(grid_size, 0.0f);

// Iterate through all movable nodes in the network
for (const auto& node_ptr : network->getNodes()) {
Node* node = node_ptr.get();
if (!node || node->isFixed() || node->getType() != Node::Type::CELL) {
continue; // Skip fixed cells and non-standard cells
}

const float cell_area
= static_cast<float>(node->getWidth().v * node->getHeight().v);
const int num_pins = node->getNumPins();

// Count pixels covered by this cell first
int cell_pixel_count = 0;
GridRect cell_grid = gridCovering(node);

for (GridY y = cell_grid.ylo; y < cell_grid.yhi; y++) {
for (GridX x = cell_grid.xlo; x < cell_grid.xhi; x++) {
Pixel* pixel = gridPixel(x, y);
if (pixel && pixel->is_valid) {
cell_pixel_count++;
}
}
}

if (cell_pixel_count == 0) {
continue; // Cell doesn't cover any valid pixels
}

// Distribute cell's contribution across its pixels
const float area_per_pixel
= cell_area / static_cast<float>(cell_pixel_count);
const float pins_per_pixel = static_cast<float>(num_pins)
/ static_cast<float>(cell_pixel_count);
Comment on lines +833 to +837
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Somewhat inaccurate as the cell may fully cover some pixels and partially cover others. Probably not a huge effect.

Comment on lines +836 to +837
Copy link
Member

@maliberty maliberty Nov 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it suffices to cast just one of these.


for (GridY y = cell_grid.ylo; y < cell_grid.yhi; y++) {
for (GridX x = cell_grid.xlo; x < cell_grid.xhi; x++) {
Pixel* pixel = gridPixel(x, y);
if (pixel && pixel->is_valid) {
const int pixel_idx = y.v * row_site_count_.v + x.v;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: '*' has higher precedence than '+'; add parentheses to explicitly specify the order of operations [readability-math-missing-parentheses]

Suggested change
const int pixel_idx = y.v * row_site_count_.v + x.v;
const int pixel_idx = (y.v * row_site_count_.v) + x.v;

if (pixel_idx >= 0 && pixel_idx < grid_size) {
total_area_[pixel_idx] += area_per_pixel;
total_pins_[pixel_idx] += pins_per_pixel;
}
}
}
}
}

normalizeUtilization();
utilization_dirty_ = false;
}

void Grid::normalizeUtilization()
{
const int grid_size = total_area_.size();
if (grid_size == 0) {
return;
}

// Find maximum values for normalization
float max_area = 0.0f;
float max_pins = 0.0f;

// We iterate manually to find max to avoid multiple passes or copies
for(float v : total_area_) {
if(v > max_area) max_area = v;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: use std::max instead of > [readability-use-std-min-max]

Suggested change
if(v > max_area) max_area = v;
max_area = std::max(v, max_area);

}
for(float v : total_pins_) {
if(v > max_pins) max_pins = v;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: use std::max instead of > [readability-use-std-min-max]

Suggested change
if(v > max_pins) max_pins = v;
max_pins = std::max(v, max_pins);

}

// Avoid division by zero
if (max_area == 0.0f)
max_area = 1.0f;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: statement should be inside braces [google-readability-braces-around-statements]

Suggested change
max_area = 1.0f;
if (max_area == 0.0f) {
max_area = 1.0f;
}

Comment on lines +876 to +878
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok for safety but it is an impossible condition and should be an error instead. Likewise max_pins == 0.

if (max_pins == 0.0f)
max_pins = 1.0f;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: statement should be inside braces [google-readability-braces-around-statements]

Suggested change
max_pins = 1.0f;
if (max_pins == 0.0f) {
max_pins = 1.0f;
}


// Calculate weighted power density for each pixel
float max_util_density = 0.0f;
for (int i = 0; i < grid_size; i++) {
const float normalized_area = total_area_[i] / max_area;
const float normalized_pins = total_pins_[i] / max_pins;
float val = area_weight_ * normalized_area
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: '*' has higher precedence than '+'; add parentheses to explicitly specify the order of operations [readability-math-missing-parentheses]

Suggested change
float val = area_weight_ * normalized_area
float val = (area_weight_ * normalized_area)

+ pin_weight_ * normalized_pins;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: '*' has higher precedence than '+'; add parentheses to explicitly specify the order of operations [readability-math-missing-parentheses]

Suggested change
+ pin_weight_ * normalized_pins;
+ (pin_weight_ * normalized_pins);

utilization_density_[i] = val;
if(val > max_util_density) max_util_density = val;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: use std::max instead of > [readability-use-std-min-max]

Suggested change
if(val > max_util_density) max_util_density = val;
max_util_density = std::max(val, max_util_density);

}

// Final normalization of power density to [0, 1] range
if (max_util_density > 0.0f) {
for (float& density : utilization_density_) {
density /= max_util_density;
}
}
utilization_dirty_ = false;
}

void Grid::updateUtilizationMap(Node* node, DbuX x, DbuY y, bool add)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This largely duplicates logic from computeUtilizationMap. Common code should be refactored out.

{
if (!node || node->isFixed() || node->getType() != Node::Type::CELL) {
return; // Skip invalid, fixed, or non-standard cells
}

const float cell_area
= static_cast<float>(node->getWidth().v * node->getHeight().v);
const int num_pins = node->getNumPins();

// Calculate grid rectangle for the cell at the given position
const GridX grid_x = gridX(x);
const GridY grid_y = gridSnapDownY(y);
const GridX grid_x_end = grid_x + gridWidth(node);
const GridY grid_y_end = gridEndY(y + node->getHeight());

// Count valid pixels for this cell
int cell_pixel_count = 0;
for (GridY gy = grid_y; gy < grid_y_end; gy++) {
for (GridX gx = grid_x; gx < grid_x_end; gx++) {
Pixel* pixel = gridPixel(gx, gy);
if (pixel && pixel->is_valid) {
cell_pixel_count++;
}
}
}

if (cell_pixel_count == 0) {
return; // Cell doesn't cover any valid pixels
}

// Calculate per-pixel contributions
const float area_per_pixel
= cell_area / static_cast<float>(cell_pixel_count);
const float pins_per_pixel = static_cast<float>(num_pins)
/ static_cast<float>(cell_pixel_count);
const float sign = add ? 1.0f : -1.0f;

// Update raw totals for affected pixels
for (GridY gy = grid_y; gy < grid_y_end; gy++) {
for (GridX gx = grid_x; gx < grid_x_end; gx++) {
Pixel* pixel = gridPixel(gx, gy);
if (pixel && pixel->is_valid) {
const int pixel_idx = gy.v * row_site_count_.v + gx.v;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: '*' has higher precedence than '+'; add parentheses to explicitly specify the order of operations [readability-math-missing-parentheses]

Suggested change
const int pixel_idx = gy.v * row_site_count_.v + gx.v;
const int pixel_idx = (gy.v * row_site_count_.v) + gx.v;

if (pixel_idx >= 0
&& pixel_idx < static_cast<int>(total_area_.size())) {
total_area_[pixel_idx] += sign * area_per_pixel;
total_pins_[pixel_idx] += sign * pins_per_pixel;

// Ensure non-negative values (floating point precision safety)
if (total_area_[pixel_idx] < 0.0f)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: statement should be inside braces [google-readability-braces-around-statements]

          if (total_area_[pixel_idx] < 0.0f)
                                            ^

this fix will not be applied because it overlaps with another fix

total_area_[pixel_idx] = 0.0f;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: use std::max instead of < [readability-use-std-min-max]

Suggested change
total_area_[pixel_idx] = 0.0f;
total_area_[pixel_idx] = std::max(total_area_[pixel_idx], 0.0f);

if (total_pins_[pixel_idx] < 0.0f)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: statement should be inside braces [google-readability-braces-around-statements]

          if (total_pins_[pixel_idx] < 0.0f)
                                            ^

this fix will not be applied because it overlaps with another fix

total_pins_[pixel_idx] = 0.0f;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: use std::max instead of < [readability-use-std-min-max]

Suggested change
total_pins_[pixel_idx] = 0.0f;
total_pins_[pixel_idx] = std::max(total_pins_[pixel_idx], 0.0f);

}
}
}
}

utilization_dirty_ = true;
}

float Grid::getUtilizationDensity(int pixel_idx) const
{
// Ideally we would normalize here if dirty, but this is a const method
// and we want to avoid mutexes/mutable.
// The optimization loop should call normalizeUtilization() explicitly
// periodically if it needs fresh normalized values, OR we accept
// that we are using slightly stale normalized values based on fresh raw data
// (which is what we are doing here - we don't re-normalize on every read).
//
// However, to respect the expert advice: "Only call normalize... once per
// outer pass".
// So we return the value from the `utilization_density_` vector which might
// be stale relative to `total_area_`.
// To make this work better without re-normalizing, we could return
// the raw value scaled by the *old* max, but that's complex.
Comment on lines +977 to +978
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it that complex?

//
// For now, return the stored (potentially stale) normalized value.
// The user of this class is responsible for calling normalizeUtilization()
// when they want to "commit" the raw updates to the normalized view.

if (pixel_idx >= 0 && pixel_idx < utilization_density_.size()) {
return utilization_density_[pixel_idx];
}
return 0.0f;
}

} // namespace dpl
16 changes: 16 additions & 0 deletions src/dpl/src/infrastructure/Grid.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@

namespace dpl {

class Network;

struct GridIntervalX
{
GridX lo;
Expand Down Expand Up @@ -155,6 +157,12 @@ class Grid

bool isMultiHeight(odb::dbMaster* master) const;

// Utilization-aware placement support
void computeUtilizationMap(Network* network, float area_weight, float pin_weight);
void updateUtilizationMap(Node* node, DbuX x, DbuY y, bool add);
float getUtilizationDensity(int pixel_idx) const;
void normalizeUtilization();

private:
// Maps a site to the right orientation to use in a given row
using SiteToOrientation = std::map<odb::dbSite*, odb::dbOrientType>;
Expand Down Expand Up @@ -211,6 +219,14 @@ class Grid

GridY row_count_{0};
GridX row_site_count_{0};

// Utilization density map
std::vector<float> utilization_density_;
std::vector<float> total_area_;
std::vector<float> total_pins_;
float area_weight_ = 0.0f;
float pin_weight_ = 0.0f;
bool utilization_dirty_ = false;
};

} // namespace dpl
Loading
Loading