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
3 changes: 3 additions & 0 deletions src/gpl/src/AbstractGraphics.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,9 @@ class AbstractGraphics
// #2 and #3 of enabled() above.
virtual void setDebugOn(bool set_on) = 0;

// Called before db_inst is destroyed so a selection can be dropped.
virtual void instDestroyed(odb::dbInst* db_inst) {}

void addFrameLabel(const odb::Rect& bbox,
std::string_view label,
std::string_view label_name,
Expand Down
58 changes: 58 additions & 0 deletions src/gpl/src/graphicsImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ void GraphicsImpl::debugForNesterovPlace(
auto cell = nbc_->getGCellByIndex(idx);
if (cell->contains(debug_inst)) {
selected_ = idx;
selected_inst_ = debug_inst;
break;
}
}
Expand Down Expand Up @@ -569,6 +570,40 @@ void GraphicsImpl::drawObjects(web::Painter& painter)
}
}

void GraphicsImpl::instDestroyed(odb::dbInst* db_inst)
{
if (db_inst == selected_inst_) {
selected_ = kInvalidIndex;
nb_selected_index_ = kInvalidIndex;
selected_inst_ = nullptr;
}
}

// Timing-driven repairs swap-remove and append GCells, so selected_ can be
// stale or past the end of storage; find the selected instance's GCell again.
void GraphicsImpl::resyncSelection()
{
if (selected_ == kInvalidIndex || !nbc_) {
return;
}
const size_t size = nbc_->getGCells().size();
if (selected_ < size
&& (!selected_inst_
|| nbc_->getGCellByIndex(selected_)->contains(selected_inst_))) {
return;
}
selected_ = kInvalidIndex;
if (!selected_inst_) {
return;
}
for (size_t idx = 0; idx < size; ++idx) {
if (nbc_->getGCellByIndex(idx)->contains(selected_inst_)) {
selected_ = idx;
return;
}
}
}

void GraphicsImpl::reportSelected()
{
if (selected_ == kInvalidIndex) {
Expand Down Expand Up @@ -613,6 +648,27 @@ void GraphicsImpl::reportSelected()
logger_->report(" overall ({: .2e}, {: .2e})",
wlGrad.x + densityPenalty * densityGrad.x,
wlGrad.y + densityPenalty * densityGrad.y);

// Previous and current position with the stored gradient the Nesterov
// step uses (preconditioned, so on a different scale than "overall").
const auto state
= nbVec_[nb_index]->getSlpState(gcell->insts().front()->dbInst());
if (state) {
Comment on lines +654 to +656

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

Calling gcell->insts().front() without checking if gcell->insts() is empty can lead to undefined behavior or a crash if the selected GCell does not contain any instances (e.g., if it is a filler or empty GCell). Adding a check for !gcell->insts().empty() ensures safety.

Suggested change
const auto state
= nbVec_[nb_index]->getSlpState(gcell->insts().front()->dbInst());
if (state) {
const auto state = (!gcell->insts().empty())
? nbVec_[nb_index]->getSlpState(gcell->insts().front()->dbInst())
: std::nullopt;
if (state) {

odb::dbBlock* block = pbc_->db()->getChip()->getBlock();
auto um = [block](float dbu) {
return block->dbuToMicrons(static_cast<double>(dbu));
};
logger_->report(" prev pos ({:.4f}, {:.4f}) um grad ({:+.6e}, {:+.6e})",
um(state->prev_pos.x),
um(state->prev_pos.y),
state->prev_grad.x,
state->prev_grad.y);
logger_->report(" cur pos ({:.4f}, {:.4f}) um grad ({:+.6e}, {:+.6e})",
um(state->cur_pos.x),
um(state->cur_pos.y),
state->cur_grad.x,
state->cur_grad.y);
}
}
}

Expand Down Expand Up @@ -700,6 +756,7 @@ void GraphicsImpl::addRoutabilityIter(const int iter, const bool revert)

void GraphicsImpl::cellPlotImpl(bool pause)
{
resyncSelection();
web::Gui::get()->redraw();
if (pause) {
reportSelected();
Expand Down Expand Up @@ -750,6 +807,7 @@ web::SelectionSet GraphicsImpl::select(odb::dbTechLayer* layer,
selected_ = idx;
odb::dbInst* db_inst
= cell->isInstance() ? cell->insts().front()->dbInst() : nullptr;
selected_inst_ = db_inst;
if (db_inst != nullptr) {
for (size_t nb_idx = 0; nb_idx < nbVec_.size(); ++nb_idx) {
for (size_t gc_idx = 0; gc_idx < nbVec_[nb_idx]->getGCells().size();
Expand Down
5 changes: 5 additions & 0 deletions src/gpl/src/graphicsImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ class GraphicsImpl : public gpl::AbstractGraphics,
int gifStart(std::string_view path) override;
void deleteLabel(std::string_view label_name) override;
void gifEnd(int key) override;
void instDestroyed(odb::dbInst* db_inst) override;

protected:
void cellPlotImpl(bool pause) override;
Expand Down Expand Up @@ -171,6 +172,9 @@ class GraphicsImpl : public gpl::AbstractGraphics,
static constexpr const char* kDrawTimingNets = "Draw Timing Nets";
size_t selected_ = kInvalidIndex;
size_t nb_selected_index_ = kInvalidIndex;
// Selected instance by identity; selected_ indexes GCell storage, which
// timing-driven repairs reorder.
odb::dbInst* selected_inst_ = nullptr;
bool draw_bins_ = false;
utl::Logger* logger_ = nullptr;
HeatMapType heatmap_type_ = Density;
Expand All @@ -191,6 +195,7 @@ class GraphicsImpl : public gpl::AbstractGraphics,
void drawInitial(web::Painter& painter);
void drawMBFF(web::Painter& painter);
void drawBounds(web::Painter& painter);
void resyncSelection();
void reportSelected();
};

Expand Down
122 changes: 25 additions & 97 deletions src/gpl/src/nesterovBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4084,78 +4084,6 @@ void NesterovBase::nbUpdateNextGradient(float wlCoeffX, float wlCoeffY)
wlCoeffY);
}

void NesterovBase::updateSinglePrevGradient(size_t gCellIndex,
float wlCoeffX,
float wlCoeffY)
{
updateSingleGradient(gCellIndex,
prevSLPSumGrads_,
prevSLPWireLengthGrads_,
prevSLPDensityGrads_,
wlCoeffX,
wlCoeffY);
}

void NesterovBase::updateSingleCurGradient(size_t gCellIndex,
float wlCoeffX,
float wlCoeffY)
{
updateSingleGradient(gCellIndex,
curSLPSumGrads_,
curSLPWireLengthGrads_,
curSLPDensityGrads_,
wlCoeffX,
wlCoeffY);
}

void NesterovBase::updateSingleGradient(
size_t gCellIndex,
std::vector<FloatPoint>& sumGrads,
std::vector<FloatPoint>& wireLengthGrads,
std::vector<FloatPoint>& densityGrads,
float wlCoeffX,
float wlCoeffY)
{
if (gCellIndex >= nb_gcells_.size()) {
return;
}

GCell* gCell = nb_gcells_.at(gCellIndex);
if (gCell->isLocked()) {
wireLengthGrads[gCellIndex] = FloatPoint(0, 0);
densityGrads[gCellIndex] = FloatPoint(0, 0);
sumGrads[gCellIndex] = FloatPoint(0, 0);
return;
}

(void) wlCoeffX;
(void) wlCoeffY;
// Cold path (db callback when a gCell is added mid-iter). updateForce
// has been refreshed by the most recent NesterovPlace iter's
// updateWireLengthForceWA call; the backend (CPU or GPU) returns the
// per-cell grad consistent with that state.
wireLengthGrads[gCellIndex] = nbc_->getSingleWireLengthGradientWA(gCell);
densityGrads[gCellIndex] = density_grad_backend_->getCellGradient(gCell);

sumGrads[gCellIndex].x = wireLengthGrads[gCellIndex].x
+ densityPenalty_ * densityGrads[gCellIndex].x;
sumGrads[gCellIndex].y = wireLengthGrads[gCellIndex].y
+ densityPenalty_ * densityGrads[gCellIndex].y;

FloatPoint wireLengthPreCondi = nbc_->getWireLengthPreconditioner(gCell);
FloatPoint densityPrecondi = getDensityPreconditioner(gCell);

FloatPoint sumPrecondi(
wireLengthPreCondi.x + (densityPenalty_ * densityPrecondi.x),
wireLengthPreCondi.y + (densityPenalty_ * densityPrecondi.y));

sumPrecondi.x = std::max(sumPrecondi.x, NesterovPlaceVars::minPreconditioner);
sumPrecondi.y = std::max(sumPrecondi.y, NesterovPlaceVars::minPreconditioner);

sumGrads[gCellIndex].x /= sumPrecondi.x;
sumGrads[gCellIndex].y /= sumPrecondi.y;
}

void NesterovBase::updateInitialPrevSLPCoordi()
{
assert(omp_get_thread_num() == 0);
Expand Down Expand Up @@ -4821,7 +4749,21 @@ void NesterovBaseCommon::resizeGCell(odb::dbInst* db_inst)
}
}

void NesterovBase::updateGCellState(float wlCoeffX, float wlCoeffY)
std::optional<NesterovBase::SlpState> NesterovBase::getSlpState(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

getSlpState() reads the host copies of prevSLPCoordi_/curSLPCoordi_ and prevSLPSumGrads_/curSLPSumGrads_. GPU runs that use neither timing-driven nor routability mode keep these on the device, so the new prev/cur pos and grad lines will show zeros or stale values. Should this sync from the device first (pullCoordsFromDevice() plus the sum gradients), as saveSnapshot() does?

odb::dbInst* db_inst) const
{
const auto it = db_inst_to_nb_index_.find(db_inst);
if (it == db_inst_to_nb_index_.end()) {
return std::nullopt;
}
const size_t k = it->second;
return SlpState{prevSLPCoordi_[k],
prevSLPSumGrads_[k],
curSLPCoordi_[k],
curSLPSumGrads_[k]};
}

void NesterovBase::updateGCellState()
{
for (auto& db_inst : new_instances_) {
auto db_it = db_inst_to_nb_index_.find(db_inst);
Expand Down Expand Up @@ -4865,32 +4807,14 @@ void NesterovBase::updateGCellState(float wlCoeffX, float wlCoeffY)
= curCoordi_[gcells_index] = initCoordi_[gcells_index]
= FloatPoint(gcell->dCx(), gcell->dCy());
Comment thread
gudeh marked this conversation as resolved.

// analogous to updateCurGradient()
updateSingleCurGradient(gcells_index, wlCoeffX, wlCoeffY);

// analogous to NesterovBase::updateInitialPrevSLPCoordi()
GCell* curGCell = nb_gcells_[gcells_index];
float prevCoordiX = curSLPCoordi_[gcells_index].x
- npVars_->initialPrevCoordiUpdateCoef
* curSLPSumGrads_[gcells_index].x;
float prevCoordiY = curSLPCoordi_[gcells_index].y
- npVars_->initialPrevCoordiUpdateCoef
* curSLPSumGrads_[gcells_index].y;
FloatPoint newCoordi(
getDensityCoordiLayoutInsideX(curGCell, prevCoordiX),
getDensityCoordiLayoutInsideY(curGCell, prevCoordiY));
prevSLPCoordi_[gcells_index] = newCoordi;

// analogous to
// NesterovBase::updateGCellDensityCenterLocation(prevSLPCoordi_)
nb_gcells_[gcells_index]->setDensityCenterLocation(
prevSLPCoordi_[gcells_index].x, prevSLPCoordi_[gcells_index].y);

// analogous to updatePrevGradient()
updateSinglePrevGradient(gcells_index, wlCoeffX, wlCoeffY);
// Gradients are left to NesterovPlace::refreshCurGradients(), which
// evaluates every GCell once the whole repair has landed. The prev slot
// needs no seeding: only init() reads it, and updateNextIter() rotates
// it out before the next read.
} else {
// Not finding a db_inst in the map should not be a problem. Just ignore
// Occurs when instance created and destroyed in same iteration.
// destroyCbkGCell() now drops such entries, so this is not expected.
debugPrint(log_,
GPL,
"callbacks",
Expand All @@ -4913,7 +4837,7 @@ void NesterovBase::createCbkGCell(odb::dbInst* db_inst, size_t stor_index)
db_inst->getName());
auto gcell = nbc_->getGCellByIndex(stor_index);
if (gcell != nullptr) {
new_instances_.push_back(db_inst);
new_instances_.insert(db_inst);
nb_gcells_.emplace_back(nbc_.get(), stor_index);
size_t gcells_index = nb_gcells_.size() - 1;
debugPrint(log_,
Expand Down Expand Up @@ -4993,6 +4917,10 @@ std::optional<std::pair<odb::dbInst*, size_t>> NesterovBase::destroyCbkGCell(
"NesterovBase {}: destroyCbkGCell {}",
pb_->getGroup() ? pb_->getGroup()->getName() : "Top-level",
db_inst->getName());
// An instance created and destroyed before updateGCellState() runs must not
// be seeded later through a stale pointer.
new_instances_.erase(db_inst);

auto db_it = db_inst_to_nb_index_.find(db_inst);
if (db_it == db_inst_to_nb_index_.end()) {
// not found
Expand Down
41 changes: 18 additions & 23 deletions src/gpl/src/nesterovBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include <ostream>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <variant>
#include <vector>
Expand Down Expand Up @@ -932,11 +933,6 @@ class NesterovBaseCommon
void getAllWireLengthGradientsWA(const std::vector<GCellHandle>& gCells,
std::vector<FloatPoint>& out);

// Single-cell wirelength gradient (cold path — NesterovBase::
// updateSingleGradient via the db callback). Defined in
// wirelengthGradient.cpp.
FloatPoint getSingleWireLengthGradientWA(const GCell* gCell);

// GPU path: run the per-inst gradient gather on device only (no host
// copy) so the NB-level device scatter can consume it. No-op on the CPU
// backend. Defined in wirelengthGradient.cpp.
Expand Down Expand Up @@ -1213,21 +1209,6 @@ class NesterovBase
void nbUpdateCurGradient(float wlCoeffX, float wlCoeffY);
void nbUpdateNextGradient(float wlCoeffX, float wlCoeffY);

// Used for updates based on callbacks
void updateSingleGradient(size_t gCellIndex,
std::vector<FloatPoint>& sumGrads,
std::vector<FloatPoint>& wireLengthGrads,
std::vector<FloatPoint>& densityGrads,
float wlCoeffX,
float wlCoeffY);

void updateSinglePrevGradient(size_t gCellIndex,
float wlCoeffX,
float wlCoeffY);
void updateSingleCurGradient(size_t gCellIndex,
float wlCoeffX,
float wlCoeffY);

void updateInitialPrevSLPCoordi();

float getStepLength(const std::vector<FloatPoint>& prevSLPCoordi_,
Expand Down Expand Up @@ -1290,7 +1271,19 @@ class NesterovBase

// Must be called after fixPointers() to initialize internal values of gcells,
// including parallel vectors.
void updateGCellState(float wlCoeffX, float wlCoeffY);
void updateGCellState();

// Previous and current position and stored gradient of the instance's GCell
// (the current gradient is the one the next Nesterov step uses).
struct SlpState
{
FloatPoint prev_pos;
FloatPoint prev_grad;
FloatPoint cur_pos;
FloatPoint cur_grad;
};
// std::nullopt when the instance is not in this region.
std::optional<SlpState> getSlpState(odb::dbInst* db_inst) const;

void destroyFillerGCell(size_t index_remove);
void restoreRemovedFillers();
Expand Down Expand Up @@ -1385,8 +1378,10 @@ class NesterovBase
std::unordered_map<odb::dbInst*, size_t> db_inst_to_nb_index_;
std::unordered_map<size_t, size_t> filler_stor_index_to_nb_index_;

// used to update gcell states after fixPointers() is called
std::vector<odb::dbInst*> new_instances_;
// used to update gcell states after fixPointers() is called. Holds only
// instances that are still alive: destroyCbkGCell() drops its entry, so a
// deleted dbInst whose memory odb reuses cannot appear twice.
std::unordered_set<odb::dbInst*> new_instances_;

struct RemovedFillerState
{
Expand Down
Loading
Loading