diff --git a/src/gpl/README.md b/src/gpl/README.md index ae76d590bdd..977522dcdb3 100644 --- a/src/gpl/README.md +++ b/src/gpl/README.md @@ -79,9 +79,9 @@ global_placement [-reference_hpwl reference_hpwl] [-overflow overflow] [-initial_place_max_iter initial_place_max_iter] - [-initial_place_max_fanout initial_place_max_fanout] [-pad_left pad_left] [-pad_right pad_right] + [-fanout_limit fanout_limit] [-skip_io] [-skip_nesterov_place] [-routability_use_grt] @@ -118,9 +118,9 @@ global_placement | `-max_phi_coef` | Set `pcof_max` ($\mu_k$ Upper Bound). Default value is `1.05`. Allowed values are `[1.00-1.20, float]`. | | `-overflow` | Set target overflow for termination condition. The default value is `0.1`. Allowed values are floats `[0, 1]`. | | `-initial_place_max_iter` | Set maximum iterations in the initial place. The default value is `20`. Allowed values are integers `[0, MAX_INT]`. | -| `-initial_place_max_fanout` | Set net escape condition in initial place when $fanout \geq initial\_place\_max\_fanout$. The default value is 200. Allowed values are integers `[1, MAX_INT]`. | | `-pad_left` | Set left padding in terms of number of sites. The default value is `0`, and the allowed values are integers `[1, MAX_INT]` | | `-pad_right` | Set right padding in terms of number of sites. The default value is `0`, and the allowed values are integers `[1, MAX_INT]` | +| `-fanout_limit` | Set the fanout limit. Nets with higher fanout than this will be ignored during placement. The default value is `200`, and the allowed values are integers `[1, MAX_INT]` | | `-skip_io` | Flag to ignore the IO ports when computing wirelength during placement. The default value is False, allowed values are boolean. | | `-disable_revert_if_diverge` | Flag to make gpl store the placement state along iterations, if a divergence is detected, gpl reverts to the snapshot state. The default value is disabled. | | `-disable_pin_density_adjust` | Flag to disable instance pin density area adjustment. The pin density area adjustment is enabled by default. | diff --git a/src/gpl/include/gpl/Replace.h b/src/gpl/include/gpl/Replace.h index aac1a05b4be..e0b23cbf518 100644 --- a/src/gpl/include/gpl/Replace.h +++ b/src/gpl/include/gpl/Replace.h @@ -49,7 +49,6 @@ struct PlaceOptions int initialPlaceMaxIter = 20; int initialPlaceMinDiffLength = 1500; int initialPlaceMaxSolverIter = 100; - int initialPlaceMaxFanout = 200; float initialPlaceNetWeightScale = 800; bool skipIoMode = false; @@ -76,6 +75,8 @@ struct PlaceOptions int binGridCntY = 0; float density = 0.7; + int fanoutLimit = 200; + float routabilityCheckOverflow = 0.3; float routabilitySnapshotOverflow = 0.6; float routabilityMaxDensity = 0.99; diff --git a/src/gpl/src/initialPlace.cpp b/src/gpl/src/initialPlace.cpp index 3f91df5c033..6ee4013c014 100644 --- a/src/gpl/src/initialPlace.cpp +++ b/src/gpl/src/initialPlace.cpp @@ -27,7 +27,6 @@ InitialPlaceVars::InitialPlaceVars(const PlaceOptions& options, : maxIter(options.initialPlaceMaxIter), minDiffLength(options.initialPlaceMinDiffLength), maxSolverIter(options.initialPlaceMaxSolverIter), - maxFanout(options.initialPlaceMaxFanout), netWeightScale(options.initialPlaceNetWeightScale), debug(debug), forceCenter(options.forceCenterInitialPlace) @@ -294,14 +293,13 @@ void InitialPlace::createSparseMatrix() // for each net for (auto& net : pbc_->getNets()) { - // skip for small nets. - if (net->getPins().size() <= 1) { + // skip ignored nets + if (net->shouldIgnore()) { continue; } - // escape long time cals on huge fanout. - // - if (net->getPins().size() >= ipVars_.maxFanout) { + // skip for small nets. + if (net->getPins().size() <= 1) { continue; } diff --git a/src/gpl/src/initialPlace.h b/src/gpl/src/initialPlace.h index 90b05752fdc..122b29ad271 100644 --- a/src/gpl/src/initialPlace.h +++ b/src/gpl/src/initialPlace.h @@ -28,7 +28,6 @@ struct InitialPlaceVars const int maxIter; const int minDiffLength; const int maxSolverIter; - const int maxFanout; const float netWeightScale; const bool debug; const bool forceCenter; diff --git a/src/gpl/src/nesterovBase.cpp b/src/gpl/src/nesterovBase.cpp index d5542ca2deb..fa4e76d0272 100644 --- a/src/gpl/src/nesterovBase.cpp +++ b/src/gpl/src/nesterovBase.cpp @@ -302,14 +302,22 @@ void GCell::writeAttributesToCSV(std::ostream& out) const //////////////////////////////////////////////// // GNet -GNet::GNet(Net* net) +GNet::GNet(Net* net) : GNet(std::vector{net}) { - nets_.push_back(net); } GNet::GNet(const std::vector& nets) { nets_ = nets; + + // Check if all nets are set to ignore, if so, mark this GNet as don't care. + isDontCare_ = true; + for (const auto& net : nets_) { + if (!net->shouldIgnore()) { + isDontCare_ = false; + break; + } + } } Net* GNet::getPbNet() const @@ -1307,6 +1315,10 @@ void NesterovBaseCommon::updateWireLengthForceWA(float wlCoeffX, float wlCoeffY) gNet->clearWaVars(); gNet->updateBox(); + if (gNet->isDontCare()) { + continue; + } + for (auto& gPin : gNet->getGPins()) { // The WA terms are shift invariant: // @@ -1416,6 +1428,11 @@ FloatPoint NesterovBaseCommon::getWireLengthGradientWA(const GCell* gCell, FloatPoint gradientPair; for (auto& gPin : gCell->gPins()) { + if (gPin->getGNet() == nullptr) { + // Can happen for ignored nets due to fanout violation. + continue; + } + auto tmpPair = getWireLengthGradientPinWA(gPin, wlCoeffX, wlCoeffY); debugPrint(log_, @@ -3575,7 +3592,7 @@ size_t NesterovBaseCommon::createCbkGCell(odb::dbInst* db_inst) void NesterovBaseCommon::createCbkGNet(odb::dbNet* db_net, bool skip_io_mode) { debugPrint(log_, GPL, "callbacks", 3, "NBC createGNet"); - Net gpl_net(db_net, skip_io_mode); + Net gpl_net(db_net, skip_io_mode, false); pb_nets_stor_.push_back(gpl_net); GNet gnet(&pb_nets_stor_.back()); gNetStor_.push_back(gnet); @@ -3794,16 +3811,16 @@ void NesterovBase::cutFillerCells(int64_t inflation_area) } log_->info(GPL, - 76, - "Removing fillers, count: Before: {}, After: {} ({:+.2f}%)", - num_filler_before_removal, - fillerStor_.size(), + 76, + "Removing fillers, count: Before: {}, After: {} ({:+.2f}%)", + num_filler_before_removal, + fillerStor_.size(), (num_filler_before_removal != 0) ? (static_cast( static_cast(fillerStor_.size()) - - static_cast(num_filler_before_removal)) - / num_filler_before_removal * 100.0) - : 0.0); + - static_cast(num_filler_before_removal)) + / num_filler_before_removal * 100.0) + : 0.0); log_->info( GPL, diff --git a/src/gpl/src/nesterovBase.h b/src/gpl/src/nesterovBase.h index 4905df2c87f..ab0f52c83ec 100644 --- a/src/gpl/src/nesterovBase.h +++ b/src/gpl/src/nesterovBase.h @@ -1360,8 +1360,8 @@ class GCellHandle inline bool isValidSigType(const odb::dbSigType& db_type) { - return (db_type == odb::dbSigType::SIGNAL - || db_type == odb::dbSigType::CLOCK); + return (db_type == odb::dbSigType::SIGNAL || db_type == odb::dbSigType::CLOCK + || db_type == odb::dbSigType::RESET); } inline constexpr const char* format_label_int = "{:27} {:10}"; diff --git a/src/gpl/src/placerBase.cpp b/src/gpl/src/placerBase.cpp index 5032ada7d76..c0caa56c415 100644 --- a/src/gpl/src/placerBase.cpp +++ b/src/gpl/src/placerBase.cpp @@ -7,16 +7,21 @@ #include #include #include +#include #include #include #include #include +#include "db_sta/dbNetwork.hh" +#include "db_sta/dbSta.hh" #include "nesterovBase.h" #include "odb/db.h" #include "odb/dbSet.h" #include "odb/dbTransform.h" #include "odb/dbTypes.h" +#include "sta/MinMax.hh" +#include "sta/Scene.hh" #include "utl/Logger.h" namespace gpl { @@ -553,9 +558,10 @@ Pin::~Pin() Net::Net() = default; -Net::Net(odb::dbNet* net, bool skipIoMode) : Net() +Net::Net(odb::dbNet* net, bool skipIoMode, bool ignore_net) : Net() { net_ = net; + ignore_net_ = ignore_net; updateBox(skipIoMode); } @@ -724,7 +730,8 @@ int64_t Die::coreArea() const } PlacerBaseVars::PlacerBaseVars(const PlaceOptions& options) - : padLeft(options.padLeft), + : fanoutLimit(options.fanoutLimit), + padLeft(options.padLeft), padRight(options.padRight), skipIoMode(options.skipIoMode), disablePinDensityAdjust(options.disablePinDensityAdjust) @@ -735,11 +742,13 @@ PlacerBaseVars::PlacerBaseVars(const PlaceOptions& options) // PlacerBaseCommon PlacerBaseCommon::PlacerBaseCommon(odb::dbDatabase* db, + sta::dbSta* sta, PlacerBaseVars pbVars, utl::Logger* log) : pbVars_(pbVars) { db_ = db; + sta_ = sta; log_ = log; init(); } @@ -924,12 +933,74 @@ void PlacerBaseCommon::init() // nets fill dbSet db_nets = block->getNets(); netStor_.reserve(db_nets.size()); + + // Only check fanout if network is linked and liberty is available + const bool enable_sta_fanout_check + = (sta_->network() != nullptr && sta_->network()->isLinked() + && sta_->network()->defaultLibertyLibrary() != nullptr); + + if (enable_sta_fanout_check) { + sta_->checkFanoutPreamble(); + } + for (dbNet* db_net : db_nets) { dbSigType net_type = db_net->getSigType(); - // escape nets with VDD/VSS/reset nets - if (net_type == dbSigType::SIGNAL || net_type == dbSigType::CLOCK) { - Net temp_net(db_net, pbVars_.skipIoMode); + // escape nets with VDD/VSS nets + if (net_type == dbSigType::SIGNAL || net_type == dbSigType::CLOCK + || net_type == dbSigType::RESET) { + // Check fanout of the net. + const float simple_fanout + = (db_net->getITerms().size() + db_net->getBTerms().size()) - 1.0f; + // A net with no fanout is ignored + bool ignore_net = simple_fanout <= 0; + if (!ignore_net) { + // Do a simple fanout check first + // This also catches block pin fanout + float fanout_limit = pbVars_.fanoutLimit; + float fanout_slack = fanout_limit - simple_fanout; + if (enable_sta_fanout_check && fanout_slack > 0.0) { + for (dbITerm* iTerm : db_net->getITerms()) { + if (iTerm->isOutputSignal(true)) { + sta::Pin* sta_pin = sta_->getDbNetwork()->dbToSta(iTerm); + if (sta_pin == nullptr) { + log_->warn(GPL, + 88, + "Unable to find STA pin for {}.", + iTerm->getName()); + } + + for (sta::Mode* mode : sta_->modes()) { + float pin_fanout = 0.0; + float pin_fanout_slack = 0.0; + float pin_fanout_limit = 0.0; + sta_->checkFanout(sta_pin, + mode, + sta::MinMax::max(), + pin_fanout, + pin_fanout_limit, + pin_fanout_slack); + + fanout_slack = std::min(fanout_slack, pin_fanout_slack); + fanout_limit = std::min(fanout_limit, pin_fanout_limit); + } + } + } + } + if (fanout_slack < 0.0) { + log_->warn(GPL, + 93, + "Net {} will be ignored during placement because it has " + "high fanout {} (fanout slack: {}, fanout limit: {}).", + db_net->getName(), + simple_fanout, + fanout_slack, + fanout_limit); + ignore_net = true; + } + } + + Net temp_net(db_net, pbVars_.skipIoMode, ignore_net); netStor_.push_back(temp_net); // this is safe because of "reserve" diff --git a/src/gpl/src/placerBase.h b/src/gpl/src/placerBase.h index e177ae3ef55..152c22691cb 100644 --- a/src/gpl/src/placerBase.h +++ b/src/gpl/src/placerBase.h @@ -32,6 +32,9 @@ class Rect; class Point; } // namespace odb +namespace sta { +class dbSta; +} namespace utl { class Logger; @@ -195,7 +198,7 @@ class Net { public: Net(); - Net(odb::dbNet* net, bool skipIoMode); + Net(odb::dbNet* net, bool skipIoMode, bool ignore_net); ~Net(); int lx() const; @@ -205,6 +208,8 @@ class Net int cx() const; int cy() const; + bool shouldIgnore() const { return ignore_net_; } + // HPWL: half-parameter-wire-length int64_t getHpwl() const; @@ -224,6 +229,7 @@ class Net int ly_ = 0; int ux_ = 0; int uy_ = 0; + bool ignore_net_ = false; }; class Die @@ -273,6 +279,7 @@ struct PlacerBaseVars { PlacerBaseVars(const PlaceOptions& options); + const int fanoutLimit; const int padLeft; const int padRight; const bool skipIoMode; @@ -285,6 +292,7 @@ class PlacerBaseCommon public: // temp padLeft/Right before OpenDB supporting... PlacerBaseCommon(odb::dbDatabase* db, + sta::dbSta* sta, PlacerBaseVars pbVars, utl::Logger* log); ~PlacerBaseCommon(); @@ -320,6 +328,7 @@ class PlacerBaseCommon private: odb::dbDatabase* db_ = nullptr; + sta::dbSta* sta_ = nullptr; utl::Logger* log_ = nullptr; PlacerBaseVars pbVars_; diff --git a/src/gpl/src/replace.cpp b/src/gpl/src/replace.cpp index 597d9b3df36..c46dd0780ca 100644 --- a/src/gpl/src/replace.cpp +++ b/src/gpl/src/replace.cpp @@ -88,7 +88,7 @@ void Replace::doIncrementalPlace(const int threads, const PlaceOptions& options) bool is_pbc_new = (pbc_ == nullptr); if (is_pbc_new) { - pbc_ = std::make_shared(db_, options, log_); + pbc_ = std::make_shared(db_, sta_, options, log_); } // Count placed vs unplaced, and conditionally lock them down @@ -173,7 +173,7 @@ void Replace::doInitialPlace(const int threads, const PlaceOptions& options) { checkHasCoreRows(); if (pbc_ == nullptr) { - pbc_ = std::make_shared(db_, options, log_); + pbc_ = std::make_shared(db_, sta_, options, log_); pbVec_.push_back(std::make_shared(db_, pbc_, log_, true)); @@ -229,7 +229,7 @@ bool Replace::initNesterovPlace(const PlaceOptions& options, bool check_density) { if (!pbc_) { - pbc_ = std::make_shared(db_, options, log_); + pbc_ = std::make_shared(db_, sta_, options, log_); pbVec_.push_back( std::make_shared(db_, pbc_, log_, check_density)); @@ -390,9 +390,8 @@ void PlaceOptions::validate(utl::Logger* logger) { utl::Validator val(logger, GPL); val.check_non_negative("initialPlaceMaxIter", initialPlaceMaxIter, 326); - val.check_positive("initialPlaceMaxFanout", initialPlaceMaxFanout, 327); - val.check_positive("initialPlaceMaxFanout", initialPlaceMaxFanout, 327); val.check_range("Target density", density, 0.0f, 1.0f, 328); + val.check_positive("fanoutLimit", fanoutLimit, 329); } void PlaceOptions::skipIo() diff --git a/src/gpl/src/replace.i b/src/gpl/src/replace.i index 6ec876a454b..7995d5abffb 100644 --- a/src/gpl/src/replace.i +++ b/src/gpl/src/replace.i @@ -37,7 +37,6 @@ static gpl::PlaceOptions getOptions( options.initialPlaceMaxIter = 0; }); checkKey(keys, "-initial_place_max_iter", options.initialPlaceMaxIter); - checkKey(keys, "-initial_place_max_fanout", options.initialPlaceMaxFanout); checkKey( keys, "-routability_check_overflow", options.routabilityCheckOverflow); checkKey( @@ -73,6 +72,7 @@ static gpl::PlaceOptions getOptions( checkKey(keys, "-init_density_penalty", options.initDensityPenaltyFactor); checkKey(keys, "-init_wirelength_coef", options.initWireLengthCoef); checkKey(keys, "-reference_hpwl", options.referenceHpwl); + checkKey(keys, "-fanout_limit", options.fanoutLimit); if (auto it = keys.find("-density"); it != keys.end()) { if (it->second == "uniform") { diff --git a/src/gpl/src/replace.tcl b/src/gpl/src/replace.tcl index cb4f0be5375..5bf7424ef7e 100644 --- a/src/gpl/src/replace.tcl +++ b/src/gpl/src/replace.tcl @@ -20,7 +20,6 @@ sta::define_cmd_args "global_placement" {\ [-reference_hpwl reference_hpwl]\ [-overflow overflow]\ [-initial_place_max_iter initial_place_max_iter]\ - [-initial_place_max_fanout initial_place_max_fanout]\ [-routability_use_grt]\ [-routability_target_rc_metric routability_target_rc_metric]\ [-routability_check_overflow routability_check_overflow]\ @@ -35,6 +34,7 @@ sta::define_cmd_args "global_placement" {\ [-timing_driven_nets_percentage timing_driven_nets_percentage]\ [-pad_left pad_left]\ [-pad_right pad_right]\ + [-fanout_limit fanout_limit]\ [-disable_revert_if_diverge]\ [-disable_pin_density_adjust]\ [-enable_routing_congestion] @@ -46,7 +46,7 @@ proc global_placement { args } { -init_density_penalty -init_wirelength_coef \ -min_phi_coef -max_phi_coef -overflow \ -reference_hpwl \ - -initial_place_max_iter -initial_place_max_fanout \ + -initial_place_max_iter \ -routability_check_overflow -routability_snapshot_overflow \ -routability_max_density \ -routability_target_rc_metric \ @@ -57,7 +57,8 @@ proc global_placement { args } { -timing_driven_net_weight_max \ -timing_driven_nets_percentage \ -keep_resize_below_overflow \ - -pad_left -pad_right} \ + -pad_left -pad_right \ + -fanout_limit} \ flags {-skip_initial_place \ -force_center_initial_place \ -skip_nesterov_place \