Skip to content
Merged
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
173 changes: 171 additions & 2 deletions src/gpl/src/nesterovBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include "backendContext.h"
#include "boost/polygon/polygon.hpp"
#include "boost/random/normal_distribution.hpp"
#include "boost/random/uniform_int_distribution.hpp"
#include "densityGradientBackend.h"
#include "fft.h"
#include "gpl/Replace.h"
Expand Down Expand Up @@ -3809,6 +3810,13 @@ void NesterovBase::commitCoordsToDeviceState(SlpSlot source)
#endif
}

void NesterovBase::updateDensityPenaltyFromRatio(float factor)
{
densityPenalty_ = (densityGradSum_ != 0)
? (wireLengthGradSum_ / densityGradSum_) * factor
: factor;
}

float NesterovBase::initDensity2(float wlCoeffX, float wlCoeffY)
{
if (wireLengthGradSum_ == 0) {
Expand All @@ -3817,8 +3825,7 @@ float NesterovBase::initDensity2(float wlCoeffX, float wlCoeffY)
}

if (wireLengthGradSum_ != 0) {
densityPenalty_
= (wireLengthGradSum_ / densityGradSum_) * npVars_->initDensityPenalty;
updateDensityPenaltyFromRatio(npVars_->initDensityPenalty);
}

sum_overflow_ = static_cast<float>(getOverflowArea())
Expand Down Expand Up @@ -5373,6 +5380,168 @@ void NesterovBase::restoreRemovedFillers()
rebuildNbDeviceCtx();
}

void NesterovBase::redistributeFillerCells()
{
// Reads and overwrites the per-filler host vectors below directly; they
// must be fresh first. Mirrors the very first line of cutFillerCells().
pullCoordsFromDevice();

if (fillerStor_.empty()) {
return;
}

dbBlock* block = pb_->db()->getChip()->getBlock();

// Free capacity per bin (filler area is excluded)
std::vector<Bin>& bins = getBins();
std::vector<double> free_capacity(bins.size(), 0.0);
double total_free_capacity = 0.0;
for (size_t b = 0; b < bins.size(); ++b) {
const Bin& bin = bins[b];
const double scaled_bin_area
= static_cast<double>(bin.getBinArea()) * bin.getTargetDensity();
const double free = scaled_bin_area
- static_cast<double>(bin.instPlacedArea())
- static_cast<double>(bin.getNonPlaceArea());
free_capacity[b] = std::max(0.0, free);
total_free_capacity += free_capacity[b];
}

const size_t num_fillers = fillerStor_.size();
const double free_capacity_um2
= block->dbuAreaToMicrons(static_cast<int64_t>(total_free_capacity));
const double filler_area_um2 = block->dbuAreaToMicrons(totalFillerArea_);
if (total_free_capacity <= 0.0
|| total_free_capacity < static_cast<double>(totalFillerArea_)) {
log_->warn(GPL,
332,
"Not enough free bin capacity ({:.3f} um^2) to redistribute "
"{} filler cells ({:.3f} um^2); leaving filler positions "
"unchanged.",
free_capacity_um2,
num_fillers,
filler_area_um2);
return;
}

// Give every bin an integer filler quota proportional to its free
// capacity (largest-remainder method, so the quotas sum to exactly
// num_fillers instead of drifting from independent rounding).
std::vector<size_t> quota(bins.size(), 0);
std::vector<double> remainder(bins.size(), 0.0);
size_t assigned = 0;
for (size_t b = 0; b < bins.size(); ++b) {
const double exact = free_capacity[b] / total_free_capacity
* static_cast<double>(num_fillers);
quota[b] = static_cast<size_t>(exact);
remainder[b] = exact - static_cast<double>(quota[b]);
assigned += quota[b];
}
assigned = std::min(assigned, num_fillers);
const size_t leftover = num_fillers - assigned;
if (leftover > 0) {
std::vector<size_t> order;
order.reserve(bins.size());
for (size_t b = 0; b < bins.size(); ++b) {
order.push_back(b);
}
std::partial_sort(
order.begin(),
order.begin() + leftover,
order.end(),
[&](size_t a, size_t b) { return remainder[a] > remainder[b]; });
for (size_t k = 0; k < leftover; ++k) {
quota[order[k]]++;
}
}

// Walk the fillers once, handing them out to bins in quota order.
size_t bin_idx = 0;
size_t remaining_in_bin = quota[0];
size_t repositioned = 0;
for (size_t i = 0; i < nb_gcells_.size(); ++i) {
if (!nb_gcells_[i]->isFiller()) {
continue;
}
while (remaining_in_bin == 0 && bin_idx + 1 < bins.size()) {
++bin_idx;
remaining_in_bin = quota[bin_idx];
}
if (remaining_in_bin == 0) {
// Quotas sum to num_fillers, so this should not happen; leave any
// stragglers where they are rather than crash.
break;
}

const Bin& bin = bins[bin_idx];
GCell* gcell = nb_gcells_[i];
const int half_dx = gcell->dx() / 2;
const int half_dy = gcell->dy() / 2;

// Jitter within the bin instead of always landing on its exact center
const int range_x = std::max(0, bin.dx() / 2 - half_dx);
const int range_y = std::max(0, bin.dy() / 2 - half_dy);
int jitter_x = 0;
int jitter_y = 0;
if (range_x > 0) {
jitter_x = boost::random::uniform_int_distribution<int>(
-range_x, range_x)(generator_);
}
if (range_y > 0) {
jitter_y = boost::random::uniform_int_distribution<int>(
-range_y, range_y)(generator_);
}
const int cx = bin.cx() + jitter_x;
const int cy = bin.cy() + jitter_y;
gcell->setCenterLocation(cx, cy);

// Update location in NesterovBase
const FloatPoint pos(static_cast<float>(cx), static_cast<float>(cy));
curCoordi_[i] = pos;
curSLPCoordi_[i] = pos;
prevSLPCoordi_[i] = pos;
nextCoordi_[i] = pos;
nextSLPCoordi_[i] = pos;
initCoordi_[i] = pos;
snapshotCoordi_[i] = pos;
snapshotSLPCoordi_[i] = pos;

// Reset the gradients
curSLPWireLengthGrads_[i] = FloatPoint();
curSLPDensityGrads_[i] = FloatPoint();
curSLPSumGrads_[i] = FloatPoint();

--remaining_in_bin;
++repositioned;
}
updateGCellDensityCenterLocation(curCoordi_);
updateDensityFieldBin();

#ifdef ENABLE_GPU
// The loop above wrote the new filler positions to the host vectors only;
// push them to the device context (built earlier, before this call, by
// Replace::initNesterovPlace) so the Nesterov loop does not start from the
// pre-redistribution filler positions. Mirrors revertToSnapshot().
if (nb_device_ctx_) {
nb_device_ctx_->syncCoordsToDevice(curSLPCoordi_,
prevSLPCoordi_,
curCoordi_,
curSLPSumGrads_,
prevSLPSumGrads_);
commitCoordsToDeviceState(SlpSlot::Cur);
host_coords_fresh_ = true;
}
#endif

log_->info(GPL,
333,
"Redistributed {} filler cells into free bin capacity ({:.3f} "
"um^2 available, {:.3f} um^2 needed).",
repositioned,
free_capacity_um2,
filler_area_um2);
}

void NesterovBaseCommon::destroyCbkGNet(odb::dbNet* db_net)
{
debugPrint(log_, GPL, "callbacks", 3, "NBC destroyGNet");
Expand Down
9 changes: 9 additions & 0 deletions src/gpl/src/nesterovBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -1096,6 +1096,9 @@ class NesterovBase
float getSumOverflowUnscaled() const { return sum_overflow_unscaled_; }
float getBaseWireLengthCoef() const { return baseWireLengthCoef_; }
float getDensityPenalty() const { return densityPenalty_; }
// Sets densityPenalty_ from the wirelength/density gradient ratio times
// factor
void updateDensityPenaltyFromRatio(float factor);

float getWireLengthGradSum() const { return wireLengthGradSum_; }
float getDensityGradSum() const { return densityGradSum_; }
Expand Down Expand Up @@ -1282,6 +1285,9 @@ class NesterovBase
void resetMinSumOverflow();

bool isDiverged() const { return isDiverged_; }
// Resets isDiverged_ when no snapshot exists to revert to instead (the only
// other place that clears it is revertToSnapshot()).
void clearDivergence() { isDiverged_ = false; }

void createCbkGCell(odb::dbInst* db_inst, size_t stor_index);
std::optional<std::pair<odb::dbInst*, size_t>> destroyCbkGCell(
Expand All @@ -1296,6 +1302,9 @@ class NesterovBase
void restoreRemovedFillers();
void clearRemovedFillers() { removed_fillers_.clear(); }

// Directly redistributes the existing filler into the free space.
void redistributeFillerCells();

void appendGCellCSVNote(const std::string& filename,
int iteration,
const std::string& message) const;
Expand Down
92 changes: 91 additions & 1 deletion src/gpl/src/nesterovPlace.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -583,6 +583,64 @@ void NesterovPlace::runTimingDriven(int iter,
}
}

void NesterovPlace::enableIncrementalDensityPenaltyGuard()
{
incremental_penalty_guard_requested_ = true;
}

void NesterovPlace::clearDivergence()
{
num_region_diverged_ = 0;
divergeMsg_ = "";
divergeCode_ = 0;
for (auto& nb : nbVec_) {
nb->clearDivergence();
}
}

void NesterovPlace::applyDensityPenaltyFactor(float factor)
{
for (auto& nb : nbVec_) {
nb->updateDensityPenaltyFromRatio(factor);
}
}

void NesterovPlace::guardIncrementalDensityPenalty(float& current_factor,
float& best_overflow,
int& retries)
{
// React on the very first regression (a guard-parameter sweep found no
// patience delay to be best) by escalating the density penalty in place.
constexpr float kOverflowTolerance = 0.005f;
constexpr float kGrowthRatio = 2.0f;
constexpr int kMaxRetries = 10;

if (average_overflow_unscaled_ < best_overflow - kOverflowTolerance) {
best_overflow = average_overflow_unscaled_;
return;
}

if (average_overflow_unscaled_ <= best_overflow + kOverflowTolerance) {
return;
}

if (retries >= kMaxRetries) {
return;
}

++retries;
current_factor *= kGrowthRatio;
applyDensityPenaltyFactor(current_factor);
Comment thread
LucasYuki marked this conversation as resolved.
log_->info(GPL,
193,
"Incremental density-penalty guard: overflow regressed past "
"{:.3f}; escalating the penalty factor to {:g} in place "
"(retry {}).",
best_overflow,
current_factor,
retries);
}

bool NesterovPlace::isDiverged(float& diverge_snapshot_WlCoefX,
float& diverge_snapshot_WlCoefY,
bool& is_diverge_snapshot_saved)
Expand Down Expand Up @@ -1079,6 +1137,10 @@ int NesterovPlace::doNesterovPlace(int start_iter)
{
// if replace diverged in init() function, Nesterov must be skipped.
if (num_region_diverged_ > 0) {
if (allow_divergence_recovery_) {
log_->warn(GPL, divergeCode_, divergeMsg_);
return start_iter;
}
log_->error(GPL, divergeCode_, divergeMsg_);
}

Expand All @@ -1096,6 +1158,24 @@ int NesterovPlace::doNesterovPlace(int start_iter)
// backTracking variable.
float curA = 1.0;

// density penalty guard info
bool penalty_guard_active = incremental_penalty_guard_requested_
&& !npVars_.routability_driven_mode;
incremental_penalty_guard_requested_ = false;
float penalty_guard_factor = 1;
float penalty_guard_best_overflow = average_overflow_unscaled_;
int penalty_guard_retries = 0;

if (penalty_guard_active) {
applyDensityPenaltyFactor(penalty_guard_factor);
log_->info(GPL,
191,
"Incremental density-penalty guard enabled: penalty factor "
"{:g}, starting overflow {:.3f}.",
penalty_guard_factor,
penalty_guard_best_overflow);
}

int routability_driven_revert_count = 0;
int routability_gpl_iter_count_ = 0;
int timing_driven_count = 0;
Expand Down Expand Up @@ -1166,6 +1246,12 @@ int NesterovPlace::doNesterovPlace(int start_iter)

updateNextIter(nesterov_iter);

if (penalty_guard_active) {
guardIncrementalDensityPenalty(penalty_guard_factor,
penalty_guard_best_overflow,
penalty_guard_retries);
}

updateIterGraphics(nesterov_iter,
reports_dir,
routability_driven_dir,
Expand Down Expand Up @@ -1236,7 +1322,11 @@ int NesterovPlace::doNesterovPlace(int start_iter)
updateDb();

if (num_region_diverged_ > 0) {
log_->error(GPL, divergeCode_, divergeMsg_);
if (allow_divergence_recovery_) {
log_->warn(GPL, divergeCode_, divergeMsg_);
} else {
log_->error(GPL, divergeCode_, divergeMsg_);
}
}

if (graphics_ && graphics_->enabled() && npVars_.debug) {
Expand Down
Loading
Loading