diff --git a/src/rsz/include/rsz/Resizer.hh b/src/rsz/include/rsz/Resizer.hh index 3af444d066..36634d0321 100644 --- a/src/rsz/include/rsz/Resizer.hh +++ b/src/rsz/include/rsz/Resizer.hh @@ -957,6 +957,9 @@ class Resizer : public sta::dbStaState, public sta::dbNetworkObserver bool replacementPreservesMaxCap(sta::Instance* inst, const sta::LibertyCell* replacement, const sta::Mode* mode = nullptr); + // An input pin whose fanin max-cap check a cell swap skips: a register + // clock pin of an ideal clock, or a scan-enable/scan-in pin. + bool skipsFaninMaxCap(const sta::Pin* pin, const sta::Mode* mode); // The load a driver can still take under its max-cap limit: none once the // limit is violated, and no bound without a limit. float maxCapIncrease(const sta::Pin* drvr_pin); @@ -974,7 +977,9 @@ class Resizer : public sta::dbStaState, public sta::dbNetworkObserver }; // Capture fanin max-cap state once before screening equivalent cells. - std::vector inputCapBudgets(sta::Instance* inst); + // The pins that skipsFaninMaxCap() exempts get no budget. + std::vector inputCapBudgets(sta::Instance* inst, + const sta::Mode* mode = nullptr); // Test a replacement using captured headroom without new STA queries. bool replacementFitsInputCapBudgets( const std::vector& budgets, diff --git a/src/rsz/src/OptimizationPolicyConfig.cc b/src/rsz/src/OptimizationPolicyConfig.cc index 176893cd5f..59645525e8 100644 --- a/src/rsz/src/OptimizationPolicyConfig.cc +++ b/src/rsz/src/OptimizationPolicyConfig.cc @@ -51,4 +51,30 @@ OptimizationPolicyConfig OptimizationPolicyConfig::forParallelEval( return config; } +float OptimizationPolicyConfig::scoreScale(const MoveType type) const +{ + // No default case: a new move type must pick its weight here. + switch (type) { + case MoveType::kVtSwap: + return vt_swap_mt_score_scale; + case MoveType::kSizeUp: + return size_up_mt_score_scale; + case MoveType::kBuffer: + return rebuffer_mt_score_scale; + case MoveType::kSplitLoad: + return split_load_mt_score_scale; + case MoveType::kSizeDownFanout: + return size_down_fanout_mt_score_scale; + case MoveType::kClone: + case MoveType::kSizeUpMatch: + case MoveType::kSwapPins: + case MoveType::kUnbuffer: + case MoveType::kReroute: + case MoveType::kBufferToInverters: + case MoveType::kCount: + return 1.0f; + } + return 1.0f; +} + } // namespace rsz diff --git a/src/rsz/src/OptimizationPolicyConfig.hh b/src/rsz/src/OptimizationPolicyConfig.hh index e0f28aff12..ed424d12f9 100644 --- a/src/rsz/src/OptimizationPolicyConfig.hh +++ b/src/rsz/src/OptimizationPolicyConfig.hh @@ -199,6 +199,10 @@ struct OptimizationPolicyConfig { static OptimizationPolicyConfig forParallelEval(RepairEffort effort); + // Weight of a move's score in the PARALLEL_EVAL shared pool; 1 for a move + // without one. + float scoreScale(MoveType type) const; + // --- Delay estimator ------------------------------------------------------ // Stages scored before and after the target stage; zero scores only the @@ -230,10 +234,14 @@ struct OptimizationPolicyConfig // PARALLEL_EVAL turns the guard off. bool dual_rf_drive_guard{true}; - // Weight of the SizeUpMt score in the shared pool; VtSwapMt keeps 1. - // Empirically tuned. A size-up costs more than a VT swap of similar gain - // (area, input pin cap, legalizer displacement, side paths), so it ranks - // lower. + // Weights of the VtSwapMt and SizeUpMt scores in the shared pool. + // Empirically tuned together with the relative cell cost of the two moves + // (relativeCellCosts()). A size-up costs more than a VT swap of similar + // gain (area, input pin cap, legalizer displacement, side paths), so it + // ranks lower. The relative cost compares a VT swap only with the other VT + // options, so the cheapest faster VT pays nothing although it adds far more + // leakage than a size-up; a VtSwapMt weight above 1 would favor it further. + float vt_swap_mt_score_scale{1.0f}; float size_up_mt_score_scale{0.75f}; // Largest drive-resistance ratio (current / replacement) of a size-up; diff --git a/src/rsz/src/OptimizerTypes.cc b/src/rsz/src/OptimizerTypes.cc index 4eacb495d4..82e9272b3a 100644 --- a/src/rsz/src/OptimizerTypes.cc +++ b/src/rsz/src/OptimizerTypes.cc @@ -3,9 +3,11 @@ #include "OptimizerTypes.hh" +#include #include #include #include +#include #include "rsz/Resizer.hh" #include "sta/Delay.hh" @@ -209,6 +211,28 @@ const sta::TimingArc* findMatchingTimingArc(const sta::TimingArc* reference, reference, candidate, ArcMatchMode::kExact, nullptr); } +std::vector relativeCellCosts(const std::vector& cost_ratios) +{ + // The cheapest option that costs more than the current cell is the + // reference; without one, no option pays. + float reference = 0.0f; + for (const float ratio : cost_ratios) { + if (ratio > 1.0f && (reference == 0.0f || ratio < reference)) { + reference = ratio; + } + } + + // Clamp at 1 after taking the relative cost, so a cheaper option never + // scores above its cost-free score. + std::vector divisors; + divisors.reserve(cost_ratios.size()); + for (const float ratio : cost_ratios) { + divisors.push_back(reference > 0.0f ? std::max(1.0f, ratio / reference) + : 1.0f); + } + return divisors; +} + bool driveResNoHigherOnBothRf(const sta::LibertyPort* current, const sta::LibertyPort* candidate) { diff --git a/src/rsz/src/OptimizerTypes.hh b/src/rsz/src/OptimizerTypes.hh index 3119a975b7..01cadbde72 100644 --- a/src/rsz/src/OptimizerTypes.hh +++ b/src/rsz/src/OptimizerTypes.hh @@ -133,6 +133,15 @@ const sta::TimingArc* findMatchingTimingArc(const sta::TimingArc* reference, bool driveResNoHigherOnBothRf(const sta::LibertyPort* current, const sta::LibertyPort* candidate); +// Score divisors of one move's options for a target, from their new/old cell +// cost ratios r: max(1, r / r_min), where r_min is the smallest ratio above 1. +// The cheapest costlier option keeps its full score and a costlier one pays +// only its extra cost; an option at most as costly as the current cell, and +// every option when none costs more, gets 1. A divisor is never below 1, so +// the cost never raises a score. The cost so orders the options of a move, +// and the move weights set the balance between moves. +std::vector relativeCellCosts(const std::vector& cost_ratios); + // === Target preparation flags ============================================== // The target-level data the generators read, prepared for every target of a diff --git a/src/rsz/src/Resizer.cc b/src/rsz/src/Resizer.cc index a8db6bedeb..ad0a4b6bac 100644 --- a/src/rsz/src/Resizer.cc +++ b/src/rsz/src/Resizer.cc @@ -8449,27 +8449,10 @@ bool Resizer::replacementPreservesMaxCap(sta::Instance* inst, continue; } - // Skip the max-cap check on a register clock pin only when its clock is - // ideal: the clock net's capacitance is irrelevant to timing then. - if (network_->isRegClkPin(pin) && mode != nullptr - && mode->clkNetwork()->isIdealClock(pin)) { + if (skipsFaninMaxCap(pin, mode)) { continue; } - // Skip the max-cap check on scan-enable/scan-in pins because they - // tend to be high fanout pins - if (db_network_ != nullptr) { - odb::dbITerm* iterm = nullptr; - odb::dbBTerm* bterm = nullptr; - odb::dbModITerm* moditerm = nullptr; - db_network_->staToDb(pin, iterm, bterm, moditerm); - if (iterm != nullptr - && (db_network_->isScanEnable(iterm) - || db_network_->isScanIn(iterm))) { - continue; - } - } - const float old_cap = inputPinCapacitance(network_, pin, current_cell, max_); const float new_cap = inputPinCapacitance(network_, pin, replacement, max_); @@ -8487,8 +8470,33 @@ bool Resizer::replacementPreservesMaxCap(sta::Instance* inst, return true; } +bool Resizer::skipsFaninMaxCap(const sta::Pin* pin, const sta::Mode* mode) +{ + // Skip the max-cap check on a register clock pin only when its clock is + // ideal: the clock net's capacitance is irrelevant to timing then. + if (network_->isRegClkPin(pin) && mode != nullptr + && mode->clkNetwork()->isIdealClock(pin)) { + return true; + } + + // Skip the max-cap check on scan-enable/scan-in pins because they + // tend to be high fanout pins + if (db_network_ != nullptr) { + odb::dbITerm* iterm = nullptr; + odb::dbBTerm* bterm = nullptr; + odb::dbModITerm* moditerm = nullptr; + db_network_->staToDb(pin, iterm, bterm, moditerm); + if (iterm != nullptr + && (db_network_->isScanEnable(iterm) || db_network_->isScanIn(iterm))) { + return true; + } + } + return false; +} + std::vector Resizer::inputCapBudgets( - sta::Instance* inst) + sta::Instance* inst, + const sta::Mode* mode) { std::vector budgets; sta::LibertyCell* current_cell = network_->libertyCell(inst); @@ -8510,6 +8518,11 @@ std::vector Resizer::inputCapBudgets( if (drivers == nullptr || port == nullptr) { continue; } + // Same exemptions as replacementPreservesMaxCap(), so generation screens + // like apply() checks. + if (skipsFaninMaxCap(pin, mode)) { + continue; + } InputCapBudget budget; budget.port = port; diff --git a/src/rsz/src/move/MoveCandidate.cc b/src/rsz/src/move/MoveCandidate.cc index f73e8eb993..103512b072 100644 --- a/src/rsz/src/move/MoveCandidate.cc +++ b/src/rsz/src/move/MoveCandidate.cc @@ -85,7 +85,6 @@ MoveImpact MoveCandidate::cellSwapImpact(sta::Instance* inst) const void MoveCandidate::chargeCellSwapSideCosts(const DelayEstimate& delay_est, const sta::LibertyCell* replacement, - const float score_scale, Estimate& estimate) const { float adjusted_gain = delay_est.arrival_impr; @@ -95,7 +94,7 @@ void MoveCandidate::chargeCellSwapSideCosts(const DelayEstimate& delay_est, estimate.sequential_penalty = side.weighted_side_slack_loss; estimate.sequential_side_loss = side.max_side_slack_loss; adjusted_gain -= *estimate.sequential_penalty; - estimate.score = adjusted_gain * score_scale; + estimate.score = adjusted_gain; if (!side.legal || adjusted_gain <= 0.0f) { estimate.legal = false; estimate.reason = side.legal ? "sequential_non_improving" : side.reason; @@ -108,7 +107,7 @@ void MoveCandidate::chargeCellSwapSideCosts(const DelayEstimate& delay_est, if (delay_est.side_paths_evaluated > 0) { estimate.side_loss = delay_est.side_loss; adjusted_gain -= delay_est.side_loss; - estimate.score = adjusted_gain * score_scale; + estimate.score = adjusted_gain; if (adjusted_gain <= 0.0f) { estimate.legal = false; estimate.reason = "side_loss_non_improving"; diff --git a/src/rsz/src/move/MoveCandidate.hh b/src/rsz/src/move/MoveCandidate.hh index 1311542722..85ac7237bc 100644 --- a/src/rsz/src/move/MoveCandidate.hh +++ b/src/rsz/src/move/MoveCandidate.hh @@ -66,10 +66,9 @@ class MoveCandidate // The side costs of swapping the target cell for `replacement`, charged // against the own-path gain of delay_est: the flip-flop input-side checks, // then the fanin side paths. Updates estimate's penalties and score - // (adjusted gain times score_scale) and rejects it once the gain is gone. + // (the adjusted gain) and rejects it once the gain is gone. void chargeCellSwapSideCosts(const DelayEstimate& delay_est, const sta::LibertyCell* replacement, - float score_scale, Estimate& estimate) const; // === Candidate identity =================================================== diff --git a/src/rsz/src/move/MoveGenerator.cc b/src/rsz/src/move/MoveGenerator.cc index 060db43396..c627d7f77d 100644 --- a/src/rsz/src/move/MoveGenerator.cc +++ b/src/rsz/src/move/MoveGenerator.cc @@ -3,9 +3,12 @@ #include "MoveGenerator.hh" +#include #include #include +#include "db_sta/dbNetwork.hh" +#include "odb/db.h" #include "rsz/Resizer.hh" #include "sta/Delay.hh" #include "sta/Liberty.hh" @@ -14,6 +17,35 @@ namespace rsz { +MoveGenerator::CellCosts MoveGenerator::cellCosts(sta::LibertyCell* cell) const +{ + CellCosts costs; + const std::optional leakage = resizer_.cellLeakage(cell); + if (leakage && *leakage > 0.0f) { + costs[0] = leakage; + } + const odb::dbMaster* master = resizer_.dbNetwork()->staToDb(cell); + if (master != nullptr && master->getArea() > 0) { + costs[1] = static_cast(master->getArea()); + } + if (cell->area() > 0.0f) { + costs[2] = cell->area(); + } + return costs; +} + +std::optional MoveGenerator::cellCostRatio(const CellCosts& from_costs, + sta::LibertyCell* to) const +{ + const CellCosts to_costs = cellCosts(to); + for (size_t metric = 0; metric < from_costs.size(); ++metric) { + if (from_costs[metric] && to_costs[metric]) { + return *to_costs[metric] / *from_costs[metric]; + } + } + return std::nullopt; +} + const sta::LibertyPort* MoveGenerator::findScenePort( const sta::LibertyCell* cell, const std::string& port_name, diff --git a/src/rsz/src/move/MoveGenerator.hh b/src/rsz/src/move/MoveGenerator.hh index a6e7fbe132..165694c58f 100644 --- a/src/rsz/src/move/MoveGenerator.hh +++ b/src/rsz/src/move/MoveGenerator.hh @@ -3,8 +3,10 @@ #pragma once +#include #include #include +#include #include #include #include @@ -148,6 +150,19 @@ class MoveGenerator const sta::RiseFall* drvr_rf, const sta::MinMax* min_max) const; + // === Shared cell-cost helpers ============================================ + // Cell costs in the order SizeUpGenerator::upsizeCell tries them: leakage, + // then LEF area, then Liberty area. An unavailable metric is nullopt. + // Main thread only: cellLeakage() fills a cache. + using CellCosts = std::array, 3>; + CellCosts cellCosts(sta::LibertyCell* cell) const; + // New/old cost of a swap from a cell with `from_costs` to `to`, on the + // first metric both cells have, so units never mix. nullopt when the two + // cells share no metric. A caller finds from_costs once for all the swaps + // of one cell. + std::optional cellCostRatio(const CellCosts& from_costs, + sta::LibertyCell* to) const; + // === Shared fanin max-cap helper ========================================= // The cell-swap generators' fanin max-cap headroom, captured by their // prepareInputs(): the STA capacitance check behind it is main-thread only. @@ -155,8 +170,8 @@ class MoveGenerator void prepareInputCapBudgets(Target& target) const { if (!target.input_cap_budgets.has_value()) { - target.input_cap_budgets - = resizer_.inputCapBudgets(target.inst(resizer_)); + target.input_cap_budgets = resizer_.inputCapBudgets( + target.inst(resizer_), target.mode(resizer_)); } } const std::vector& inputCapBudgets( diff --git a/src/rsz/src/move/RebufferMtCandidate.cc b/src/rsz/src/move/RebufferMtCandidate.cc index a3ed03aeb1..fabc4670a9 100644 --- a/src/rsz/src/move/RebufferMtCandidate.cc +++ b/src/rsz/src/move/RebufferMtCandidate.cc @@ -38,13 +38,11 @@ RebufferMtCandidate::RebufferMtCandidate( Resizer& resizer, const Target& target, const RebufferTreePlan& plan, - const float score_scale, const float min_score, std::vector side_contexts) : MoveCandidate(resizer, target), plan_(plan), side_contexts_(std::move(side_contexts)), - score_scale_(score_scale), min_score_(min_score) { } @@ -59,7 +57,7 @@ Estimate RebufferMtCandidate::estimate() = estimateTree(estimator_context, plan_, side_contexts_); raw_score_ = delay_est.arrival_impr - delay_est.side_loss; Estimate estimate{.reason = failReasonName(delay_est.reason)}; - estimate.score = scaledScore(); + estimate.score = raw_score_; if (delay_est.side_paths_evaluated > 0) { estimate.side_loss = delay_est.side_loss; } @@ -151,12 +149,11 @@ MoveResult RebufferMtCandidate::apply() "rebuffer_mt", 1, "ACCEPT rebuffer_mt {}: inserted {} buffers, tree loads {}, " - "raw_score {:.3e}, scaled_score {:.3e}", + "raw_score {:.3e}", logName(), inserted_count, plan_.load_count, - raw_score_, - scaledScore()); + raw_score_); return { .accepted = true, diff --git a/src/rsz/src/move/RebufferMtCandidate.hh b/src/rsz/src/move/RebufferMtCandidate.hh index 5d24766795..dc1ef4c90e 100644 --- a/src/rsz/src/move/RebufferMtCandidate.hh +++ b/src/rsz/src/move/RebufferMtCandidate.hh @@ -24,7 +24,6 @@ class RebufferMtCandidate : public MoveCandidate RebufferMtCandidate(Resizer& resizer, const Target& target, const RebufferTreePlan& plan, - float score_scale, float min_score, std::vector side_contexts = {}); @@ -35,7 +34,6 @@ class RebufferMtCandidate : public MoveCandidate MoveType type() const override { return MoveType::kBuffer; } MoveImpact impact() const override; std::vector guardedSidePins() const override; - float scaledScore() const { return raw_score_ * score_scale_; } // The tree plan and its side contexts, for the accuracy report. const RebufferTreePlan& plan() const { return plan_; } const std::vector& sideContexts() const @@ -58,7 +56,6 @@ class RebufferMtCandidate : public MoveCandidate // === Candidate state ====================================================== RebufferTreePlan plan_; std::vector side_contexts_; - const float score_scale_; const float min_score_; float raw_score_{0.0f}; }; diff --git a/src/rsz/src/move/RebufferMtGenerator.cc b/src/rsz/src/move/RebufferMtGenerator.cc index c1a6c00d6e..8afa045847 100644 --- a/src/rsz/src/move/RebufferMtGenerator.cc +++ b/src/rsz/src/move/RebufferMtGenerator.cc @@ -254,7 +254,6 @@ void RebufferMtGenerator::addTreePlanCandidates( resizer_, target, plan, - policy_config_.rebuffer_mt_score_scale, policy_config_.rebuffer_mt_min_score, makeSideContexts(plan, target.rebuffer_load_timing))); } diff --git a/src/rsz/src/move/SizeDownFanoutMtCandidate.cc b/src/rsz/src/move/SizeDownFanoutMtCandidate.cc index de016d8332..37c05a4548 100644 --- a/src/rsz/src/move/SizeDownFanoutMtCandidate.cc +++ b/src/rsz/src/move/SizeDownFanoutMtCandidate.cc @@ -24,11 +24,8 @@ using utl::RSZ; SizeDownFanoutMtCandidate::SizeDownFanoutMtCandidate( Resizer& resizer, const Target& target, - std::shared_ptr load, - const float score_scale) - : MoveCandidate(resizer, target), - load_(std::move(load)), - score_scale_(score_scale) + std::shared_ptr load) + : MoveCandidate(resizer, target), load_(std::move(load)) { } @@ -43,7 +40,7 @@ Estimate SizeDownFanoutMtCandidate::estimate() ? std::optional(delay_est.arrival_impr) : std::nullopt, .reason = failReasonName(delay_est.reason)}; - estimate.score = delay_est.arrival_impr * score_scale_; + estimate.score = delay_est.arrival_impr; return estimate; } diff --git a/src/rsz/src/move/SizeDownFanoutMtCandidate.hh b/src/rsz/src/move/SizeDownFanoutMtCandidate.hh index ca239d37cd..6a93fbcc2d 100644 --- a/src/rsz/src/move/SizeDownFanoutMtCandidate.hh +++ b/src/rsz/src/move/SizeDownFanoutMtCandidate.hh @@ -62,8 +62,7 @@ class SizeDownFanoutMtCandidate : public MoveCandidate // === Construction ========================================================= SizeDownFanoutMtCandidate(Resizer& resizer, const Target& target, - std::shared_ptr load, - float score_scale); + std::shared_ptr load); // === MoveCandidate API ==================================================== Estimate estimate() override; @@ -74,7 +73,6 @@ class SizeDownFanoutMtCandidate : public MoveCandidate private: // === Candidate state ====================================================== std::shared_ptr load_; - float score_scale_{1.0f}; }; } // namespace rsz diff --git a/src/rsz/src/move/SizeDownFanoutMtGenerator.cc b/src/rsz/src/move/SizeDownFanoutMtGenerator.cc index 9bc4294217..4df780814b 100644 --- a/src/rsz/src/move/SizeDownFanoutMtGenerator.cc +++ b/src/rsz/src/move/SizeDownFanoutMtGenerator.cc @@ -703,11 +703,8 @@ std::vector> SizeDownFanoutMtGenerator::generate( continue; } - candidates.push_back(std::make_unique( - resizer_, - target, - load, - policy_config_.size_down_fanout_mt_score_scale)); + candidates.push_back( + std::make_unique(resizer_, target, load)); } return candidates; } diff --git a/src/rsz/src/move/SizeUpMtCandidate.cc b/src/rsz/src/move/SizeUpMtCandidate.cc index 4e00afe56e..446d38ad0f 100644 --- a/src/rsz/src/move/SizeUpMtCandidate.cc +++ b/src/rsz/src/move/SizeUpMtCandidate.cc @@ -23,12 +23,12 @@ SizeUpMtCandidate::SizeUpMtCandidate(Resizer& resizer, const Target& target, sta::Instance* inst, sta::LibertyCell* replacement, - float score_scale) + float cost_divisor) : MoveCandidate(resizer, target), inst_(inst), current_cell_(resizer.network()->libertyCell(inst)), replacement_(replacement), - score_scale_(score_scale) + cost_divisor_(cost_divisor) { } @@ -41,12 +41,12 @@ Estimate SizeUpMtCandidate::estimate() Estimate estimate{.legal = delay_est.legal, .raw_gain = delay_est.arrival_impr, .reason = failReasonName(delay_est.reason)}; - estimate.score = delay_est.arrival_impr * score_scale_; + estimate.score = delay_est.arrival_impr; if (!delay_est.legal) { // The opposite-transition reject carries the target gain in its score. return { .legal = false, - .score = estimate.score, + .score = costScaled(estimate.score), .raw_gain = delay_est.reason == FailReason::kEstimateNonImproving ? std::optional(delay_est.arrival_impr) : std::nullopt, @@ -55,10 +55,21 @@ Estimate SizeUpMtCandidate::estimate() && estimate.score > 0.0f}; } - chargeCellSwapSideCosts(delay_est, replacement_, score_scale_, estimate); + chargeCellSwapSideCosts(delay_est, replacement_, estimate); + // Rank the gain per unit of cost, as SizeUpGenerator::upsizeCell does, so a + // larger cell must buy its extra leakage or area with extra slack. + estimate.score = costScaled(estimate.score); return estimate; } +float SizeUpMtCandidate::costScaled(const float score) const +{ + // Only a positive score pays the cost: dividing a negative score by a + // divisor of at least 1 would pull it toward zero and rank a costlier move + // higher. + return score > 0.0f ? score / cost_divisor_ : score; +} + MoveResult SizeUpMtCandidate::apply() { // Re-validate max-cap against state mutated earlier in this commit batch. diff --git a/src/rsz/src/move/SizeUpMtCandidate.hh b/src/rsz/src/move/SizeUpMtCandidate.hh index a4a355799f..76901c3f0e 100644 --- a/src/rsz/src/move/SizeUpMtCandidate.hh +++ b/src/rsz/src/move/SizeUpMtCandidate.hh @@ -36,7 +36,7 @@ class SizeUpMtCandidate : public MoveCandidate const Target& target, sta::Instance* inst, sta::LibertyCell* replacement, - float score_scale); + float cost_divisor); // === MoveCandidate API ==================================================== Estimate estimate() override; @@ -50,12 +50,16 @@ class SizeUpMtCandidate : public MoveCandidate private: std::string logName() const; + // A positive score per unit of the replacement's relative cost. + float costScaled(float score) const; // === Candidate state ====================================================== sta::Instance* inst_{nullptr}; sta::LibertyCell* current_cell_{nullptr}; sta::LibertyCell* replacement_{nullptr}; - const float score_scale_; + // Relative cell cost of the replacement among the target's sizes, at least + // 1 (relativeCellCosts()). + const float cost_divisor_; }; } // namespace rsz diff --git a/src/rsz/src/move/SizeUpMtGenerator.cc b/src/rsz/src/move/SizeUpMtGenerator.cc index 14b8775b83..511efdce46 100644 --- a/src/rsz/src/move/SizeUpMtGenerator.cc +++ b/src/rsz/src/move/SizeUpMtGenerator.cc @@ -4,6 +4,7 @@ #include "SizeUpMtGenerator.hh" #include +#include #include #include @@ -53,7 +54,6 @@ std::vector> SizeUpMtGenerator::generate( sta::Instance* target_inst = target.inst(resizer_); const EstimatorContext& estimator_context = target.estimator_context.value(); const SelectedArc& target_arc = estimator_context.target().arc; - const float score_scale = policy_config_.size_up_mt_score_scale; const SizeUpOptions& options = size_up_options_.at( {target_arc.outputPort(), target_arc.scene, target_arc.min_max}); if (options.dual_rf_rejects > 0) { @@ -66,16 +66,26 @@ std::vector> SizeUpMtGenerator::generate( // Fanin max-cap headroom is instance-level, captured once per target. const std::vector& budgets = inputCapBudgets(target); - candidates.reserve(replacements.size()); - for (sta::LibertyCell* replacement : replacements) { + std::vector fitting; + std::vector cost_ratios; + for (size_t i = 0; i < replacements.size(); ++i) { + sta::LibertyCell* replacement = replacements[i]; if (!resizer_.replacementFitsInputCapBudgets(budgets, replacement)) { RepairSetupProfiler::event( "max_cap_rejects", 1, moveName(MoveType::kSizeUp)); continue; } + fitting.push_back(replacement); + cost_ratios.push_back(options.cost_ratios[i]); + } + // The cheapest fitting size keeps its full score; a larger one pays only + // its extra cost. + const std::vector cost_divisors = relativeCellCosts(cost_ratios); + candidates.reserve(fitting.size()); + for (size_t i = 0; i < fitting.size(); ++i) { candidates.push_back(std::make_unique( - resizer_, target, target_inst, replacement, score_scale)); + resizer_, target, target_inst, fitting[i], cost_divisors[i])); } return candidates; } @@ -129,6 +139,8 @@ SizeUpMtGenerator::SizeUpOptions SizeUpMtGenerator::buildSizeUpOptions( const bool limit_ratio = max_ratio > 0.0f && drive_r > 0.0f; const float min_drive_r = limit_ratio ? drive_r / max_ratio : 0.0f; odb::dbMaster* current_master = resizer_.dbNetwork()->staToDb(cell); + // The current cell's costs, shared by every swap below. + const CellCosts cell_costs = cellCosts(cell); for (sta::LibertyCell* swappable : swappable_cells) { if (swappable == cell) { continue; @@ -163,7 +175,16 @@ SizeUpMtGenerator::SizeUpOptions SizeUpMtGenerator::buildSizeUpOptions( const float swappable_drive_r = swappable_drvr->driveResistance(); if (swappable_drive_r <= drive_r && (!limit_ratio || swappable_drive_r >= min_drive_r)) { + // As in SizeUpGenerator::upsizeCell, a cell without a cost on a metric + // both cells have is not a candidate. cellLeakage() fills a cache, so + // the costs are found here on the main thread. + const std::optional cost_ratio + = cellCostRatio(cell_costs, swappable); + if (!cost_ratio) { + continue; + } replacements.push_back(swappable); + options.cost_ratios.push_back(*cost_ratio); } } return options; diff --git a/src/rsz/src/move/SizeUpMtGenerator.hh b/src/rsz/src/move/SizeUpMtGenerator.hh index 95562191d0..1fb1738b2a 100644 --- a/src/rsz/src/move/SizeUpMtGenerator.hh +++ b/src/rsz/src/move/SizeUpMtGenerator.hh @@ -60,6 +60,9 @@ class SizeUpMtGenerator : public MoveGenerator struct SizeUpOptions { std::vector cells; + // New/old cell cost of each entry of cells; generate() turns the ratios + // of the fitting cells into score divisors with relativeCellCosts(). + std::vector cost_ratios; int dual_rf_rejects{0}; }; diff --git a/src/rsz/src/move/SplitLoadMtCandidate.cc b/src/rsz/src/move/SplitLoadMtCandidate.cc index 57e4595c35..874f7c18e7 100644 --- a/src/rsz/src/move/SplitLoadMtCandidate.cc +++ b/src/rsz/src/move/SplitLoadMtCandidate.cc @@ -26,11 +26,8 @@ using utl::RSZ; SplitLoadMtCandidate::SplitLoadMtCandidate( Resizer& resizer, const Target& target, - std::shared_ptr input, - const float score_scale) - : MoveCandidate(resizer, target), - input_(std::move(input)), - score_scale_(score_scale) + std::shared_ptr input) + : MoveCandidate(resizer, target), input_(std::move(input)) { } @@ -45,7 +42,7 @@ Estimate SplitLoadMtCandidate::estimate() = DelayEstimator::estimateLoadChange(context, input.target_load_delta); if (!delay_est.legal) { return {.legal = false, - .score = delay_est.arrival_impr * score_scale_, + .score = delay_est.arrival_impr, .raw_gain = delay_est.reason == FailReason::kEstimateNonImproving ? std::optional(delay_est.arrival_impr) : std::nullopt, @@ -81,7 +78,7 @@ Estimate SplitLoadMtCandidate::estimate() .raw_gain = delay_est.arrival_impr, .reason = failReasonName(delay_est.reason), .side_loss = delay_est.side_loss}; - estimate.score = gain * score_scale_; + estimate.score = gain; if (delay_est.legal && gain <= 0.0f) { estimate.reason = "side_loss_non_improving"; estimate.tail_admissible = delay_est.arrival_impr > 0.0f; diff --git a/src/rsz/src/move/SplitLoadMtCandidate.hh b/src/rsz/src/move/SplitLoadMtCandidate.hh index 914c077c3f..1be5d6f5ab 100644 --- a/src/rsz/src/move/SplitLoadMtCandidate.hh +++ b/src/rsz/src/move/SplitLoadMtCandidate.hh @@ -54,8 +54,7 @@ class SplitLoadMtCandidate : public MoveCandidate // === Construction ========================================================= SplitLoadMtCandidate(Resizer& resizer, const Target& target, - std::shared_ptr input, - float score_scale); + std::shared_ptr input); // === MoveCandidate API ==================================================== Estimate estimate() override; @@ -69,7 +68,6 @@ class SplitLoadMtCandidate : public MoveCandidate // === Candidate state ====================================================== std::shared_ptr input_; - float score_scale_{1.0f}; }; } // namespace rsz diff --git a/src/rsz/src/move/SplitLoadMtGenerator.cc b/src/rsz/src/move/SplitLoadMtGenerator.cc index f54a36883c..e81e88e090 100644 --- a/src/rsz/src/move/SplitLoadMtGenerator.cc +++ b/src/rsz/src/move/SplitLoadMtGenerator.cc @@ -275,10 +275,7 @@ std::vector> SplitLoadMtGenerator::generate( } candidates.push_back(std::make_unique( - resizer_, - target, - target.split_load_input, - policy_config_.split_load_mt_score_scale)); + resizer_, target, target.split_load_input)); return candidates; } diff --git a/src/rsz/src/move/VtSwapMtCandidate.cc b/src/rsz/src/move/VtSwapMtCandidate.cc index e208233ed6..d99b06405c 100644 --- a/src/rsz/src/move/VtSwapMtCandidate.cc +++ b/src/rsz/src/move/VtSwapMtCandidate.cc @@ -44,11 +44,13 @@ const char* applyLogFormat(const bool accepted) VtSwapMtCandidate::VtSwapMtCandidate(Resizer& resizer, const Target& target, sta::Instance* inst, - sta::LibertyCell* candidate_cell) + sta::LibertyCell* candidate_cell, + const float cost_divisor) : MoveCandidate(resizer, target), inst_(inst), current_cell_(resizer.network()->libertyCell(inst)), - candidate_cell_(candidate_cell) + candidate_cell_(candidate_cell), + cost_divisor_(cost_divisor) { } @@ -62,8 +64,10 @@ Estimate VtSwapMtCandidate::estimate() // Preserve the computed score for non-improving swaps so policy ranking can // still compare rejected candidates consistently. if (delay_est.reason == FailReason::kEstimateNonImproving) { - return makeRejectedEstimate(failReasonName(delay_est.reason), - delay_est.arrival_impr); + Estimate rejected = makeRejectedEstimate(failReasonName(delay_est.reason), + delay_est.arrival_impr); + rejected.score = costScaled(rejected.score); + return rejected; } return makeRejectedEstimate(failReasonName(delay_est.reason)); } @@ -73,11 +77,22 @@ Estimate VtSwapMtCandidate::estimate() .reason = failReasonName(FailReason::kEstimateLegal)}; estimate.score = delay_est.arrival_impr; - // VtSwapMt keeps score scale 1; the side costs come off the raw gain. - chargeCellSwapSideCosts(delay_est, candidate_cell_, 1.0f, estimate); + // The side costs come off the raw gain before the weight. + chargeCellSwapSideCosts(delay_est, candidate_cell_, estimate); + // Rank the gain per unit of cost, as SizeUpMt does, so a jump over more VTs + // must buy its extra leakage with extra slack. + estimate.score = costScaled(estimate.score); return estimate; } +float VtSwapMtCandidate::costScaled(const float score) const +{ + // Only a positive score pays the cost: dividing a negative score by a + // divisor of at least 1 would pull it toward zero and rank a costlier move + // higher. + return score > 0.0f ? score / cost_divisor_ : score; +} + MoveResult VtSwapMtCandidate::apply() { // Apply the chosen VT replacement after the policy has selected the best diff --git a/src/rsz/src/move/VtSwapMtCandidate.hh b/src/rsz/src/move/VtSwapMtCandidate.hh index 9ce5594666..062012e9a5 100644 --- a/src/rsz/src/move/VtSwapMtCandidate.hh +++ b/src/rsz/src/move/VtSwapMtCandidate.hh @@ -33,7 +33,8 @@ class VtSwapMtCandidate : public MoveCandidate VtSwapMtCandidate(Resizer& resizer, const Target& target, sta::Instance* inst, - sta::LibertyCell* candidate_cell); + sta::LibertyCell* candidate_cell, + float cost_divisor); // === MoveCandidate API ==================================================== Estimate estimate() override; @@ -46,10 +47,16 @@ class VtSwapMtCandidate : public MoveCandidate MoveType type() const override { return MoveType::kVtSwap; } private: + // A positive score per unit of the candidate's relative cost. + float costScaled(float score) const; + // === Candidate state ====================================================== sta::Instance* inst_{nullptr}; sta::LibertyCell* current_cell_{nullptr}; sta::LibertyCell* candidate_cell_{nullptr}; + // Relative cell cost of the candidate among the target's VTs, at least 1 + // (relativeCellCosts()). + const float cost_divisor_; }; } // namespace rsz diff --git a/src/rsz/src/move/VtSwapMtGenerator.cc b/src/rsz/src/move/VtSwapMtGenerator.cc index 601b7ad2bb..adbe723cbc 100644 --- a/src/rsz/src/move/VtSwapMtGenerator.cc +++ b/src/rsz/src/move/VtSwapMtGenerator.cc @@ -3,7 +3,9 @@ #include "VtSwapMtGenerator.hh" +#include #include +#include #include #include @@ -36,13 +38,14 @@ std::vector> VtSwapMtGenerator::generate( // captured before workers start. sta::LibertyCell* current_cell = resizer_.network()->libertyCell(target.inst(resizer_)); - const std::vector& candidate_cells - = candidate_cells_cache_.at(current_cell).cells; + const CandidateCells& options = candidate_cells_cache_.at(current_cell); // Fanin max-cap headroom is instance-level, captured once per target. const std::vector& budgets = inputCapBudgets(target); - candidates.reserve(candidate_cells.size()); - for (sta::LibertyCell* candidate_cell : candidate_cells) { + std::vector fitting; + std::vector cost_ratios; + for (size_t i = 0; i < options.cells.size(); ++i) { + sta::LibertyCell* candidate_cell = options.cells[i]; if (policy_config_.dual_rf_drive_guard) { sta::LibertyCell* candidate_scene = candidate_cell->sceneCell(target_arc.scene, target_arc.min_max); @@ -67,8 +70,17 @@ std::vector> VtSwapMtGenerator::generate( continue; } + fitting.push_back(candidate_cell); + cost_ratios.push_back(options.cost_ratios[i]); + } + + // The cheapest faster VT keeps its full score; a jump over more VTs pays + // only its extra cost. + const std::vector cost_divisors = relativeCellCosts(cost_ratios); + candidates.reserve(fitting.size()); + for (size_t i = 0; i < fitting.size(); ++i) { candidates.push_back(std::make_unique( - resizer_, target, target.inst(resizer_), candidate_cell)); + resizer_, target, target.inst(resizer_), fitting[i], cost_divisors[i])); } return candidates; } @@ -101,11 +113,24 @@ bool VtSwapMtGenerator::isApplicable(const Target& target) const = resizer_.getVTEquivCells(current_cell); CandidateCells& entry = cached.first->second; // Map iterator's value. entry.cells.reserve(equiv_cells.size()); + entry.cost_ratios.reserve(equiv_cells.size()); + // The current cell's costs, shared by every swap below. + const CellCosts current_costs = cellCosts(current_cell); // Enumerate all VT-equivalent cells except the current implementation. for (sta::LibertyCell* cell : equiv_cells) { - if (cell != nullptr && cell != current_cell) { - entry.cells.push_back(cell); + if (cell == nullptr || cell == current_cell) { + continue; + } + // As for SizeUpMt, a cell without a cost on a metric both cells have is + // not a candidate. cellLeakage() fills a cache, so the costs are found + // here on the main thread. + const std::optional cost_ratio + = cellCostRatio(current_costs, cell); + if (!cost_ratio) { + continue; } + entry.cells.push_back(cell); + entry.cost_ratios.push_back(*cost_ratio); } entry.applicable = !entry.cells.empty(); } diff --git a/src/rsz/src/move/VtSwapMtGenerator.hh b/src/rsz/src/move/VtSwapMtGenerator.hh index 0bd43cd8c9..c5ea9db7a6 100644 --- a/src/rsz/src/move/VtSwapMtGenerator.hh +++ b/src/rsz/src/move/VtSwapMtGenerator.hh @@ -50,10 +50,13 @@ class VtSwapMtGenerator : public MoveGenerator { bool applicable{false}; std::vector cells; + // New/old cell cost ratio of each cell in cells. + std::vector cost_ratios; }; // Reuse VT replacement cells across targets with the same current cell. - // Key: current Liberty cell. Value: applicability and alternative cells. + // Key: current Liberty cell. Value: applicability, alternative cells, and + // their cost ratios. // Filled by isApplicable() on the main thread before generate() workers. mutable std::map candidate_cells_cache_; }; diff --git a/src/rsz/src/policy/SetupParallelEvalPolicy.cc b/src/rsz/src/policy/SetupParallelEvalPolicy.cc index 8df31ce9fc..96f748dfdb 100644 --- a/src/rsz/src/policy/SetupParallelEvalPolicy.cc +++ b/src/rsz/src/policy/SetupParallelEvalPolicy.cc @@ -198,9 +198,9 @@ void SetupParallelEvalPolicy::selectMoveSequence() { // -sequence is the move set; without it the default runs. The candidates // of a target compete on score, and the sequence order only breaks exact - // ties; SizeUpMt scores carry the 0.75 size_up_mt_score_scale. The default - // goes through the same filter, so -skip_vt_swap and a single-VT library - // still remove VtSwapMt. + // ties; the move weights such as size_up_mt_score_scale scale the scores. + // The default goes through the same filter, so -skip_vt_swap and a + // single-VT library still remove VtSwapMt. std::vector requested_sequence = config_.sequence; if (requested_sequence.empty()) { requested_sequence = {MoveType::kUnbuffer, @@ -927,11 +927,14 @@ void SetupParallelEvalPolicy::estimateTargets( tasks.push_back(&evaluation); } + const OptimizationPolicyConfig& policy_config = policy_config_; thread_pool_->parallelFor( - tasks, [profiling, permissive](TargetEvaluation* const& evaluation) { + tasks, + [profiling, permissive, &policy_config]( + TargetEvaluation* const& evaluation) { const WorkerTimer timer(profiling ? &evaluation->worker_elapsed : nullptr); - estimateTarget(*evaluation, permissive); + estimateTarget(*evaluation, permissive, policy_config); }); // Fold the candidate counters into the profiler on the main thread, after @@ -956,12 +959,17 @@ void SetupParallelEvalPolicy::estimateTargets( } } -void SetupParallelEvalPolicy::estimateTarget(TargetEvaluation& evaluation, - const bool permissive) +void SetupParallelEvalPolicy::estimateTarget( + TargetEvaluation& evaluation, + const bool permissive, + const OptimizationPolicyConfig& policy_config) { evaluation.estimates.reserve(evaluation.candidates.size()); for (std::unique_ptr& candidate : evaluation.candidates) { Estimate estimate = candidate->estimate(); + // The candidate scores its own gain and cost; the move weight sets the + // balance between moves. + estimate.score *= policy_config.scoreScale(candidate->type()); if (permissive && !estimate.legal && estimate.tail_admissible) { // Trust the own-path gain and leave the side cost to the batch STA. The // adjusted score keeps clean estimates ahead: it is zero or less, or diff --git a/src/rsz/src/policy/SetupParallelEvalPolicy.hh b/src/rsz/src/policy/SetupParallelEvalPolicy.hh index 24a8d10376..143eb9673c 100644 --- a/src/rsz/src/policy/SetupParallelEvalPolicy.hh +++ b/src/rsz/src/policy/SetupParallelEvalPolicy.hh @@ -179,8 +179,11 @@ class SetupParallelEvalPolicy : public OptimizationPolicy // Score each target's generated candidates on the worker pool. void estimateTargets(std::vector& evaluations); // permissive (WNS tail) keeps estimator-rejected candidates with a positive - // own gain; the batch STA judges them. - static void estimateTarget(TargetEvaluation& evaluation, bool permissive); + // own gain; the batch STA judges them. policy_config holds the move + // weights. + static void estimateTarget(TargetEvaluation& evaluation, + bool permissive, + const OptimizationPolicyConfig& policy_config); // === Selection ============================================================ // Candidates know their Target, so the pool alone identifies each entry. diff --git a/src/rsz/test/BUILD b/src/rsz/test/BUILD index 5fcc8479b3..20e18d1027 100644 --- a/src/rsz/test/BUILD +++ b/src/rsz/test/BUILD @@ -299,6 +299,7 @@ TESTS = [ "repair_setup_parallel_eval_path_group", "repair_setup_parallel_eval_reference_period", "repair_setup_parallel_eval_reference_period_modes", + "repair_setup_parallel_eval_fanin_max_cap", "repair_setup_vt_swap2", "global_sizing", "global_sizing_threads", diff --git a/src/rsz/test/cpp/TestCellSwapMt.cc b/src/rsz/test/cpp/TestCellSwapMt.cc index e10b258bcb..3d594929ec 100644 --- a/src/rsz/test/cpp/TestCellSwapMt.cc +++ b/src/rsz/test/cpp/TestCellSwapMt.cc @@ -81,7 +81,7 @@ TEST_F(TestResizerMt, SwapPinsMtCandidateChargesDisplacedSignal) EXPECT_EQ(impact.timing_effect_nets.size(), 3u); } -TEST_F(TestResizerMt, SizeUpMtCandidateAppliesScoreScale) +TEST_F(TestResizerMt, SizeUpMtCandidateAppliesCellCost) { Resizer& resizer = resizer_; resizer.runRepairSetupPreamble(); @@ -107,17 +107,59 @@ TEST_F(TestResizerMt, SizeUpMtCandidateAppliesScoreScale) = DelayEstimator::estimate(estimator_context.value(), replacement_cell); ASSERT_TRUE(raw_estimate.legal); - SizeUpMtCandidate default_scale_candidate( - resizer, target, target_inst, replacement_cell, 1.0f); - const Estimate default_scale = default_scale_candidate.estimate(); - EXPECT_EQ(default_scale.legal, raw_estimate.legal); - EXPECT_FLOAT_EQ(default_scale.score, raw_estimate.arrival_impr); + // The policy applies the move weight; the candidate scores its own gain. + SizeUpMtCandidate cheapest_candidate(resizer, + target, + target_inst, + replacement_cell, + /*cost_divisor=*/1.0f); + const Estimate cheapest = cheapest_candidate.estimate(); + EXPECT_EQ(cheapest.legal, raw_estimate.legal); + EXPECT_FLOAT_EQ(cheapest.score, raw_estimate.arrival_impr); - SizeUpMtCandidate half_scale_candidate( - resizer, target, target_inst, replacement_cell, 0.5f); - const Estimate half_scale = half_scale_candidate.estimate(); - EXPECT_EQ(half_scale.legal, raw_estimate.legal); - EXPECT_FLOAT_EQ(half_scale.score, raw_estimate.arrival_impr * 0.5f); + // The score is the gain per unit of relative cell cost. raw_gain stays the + // estimated gain. + SizeUpMtCandidate costly_candidate(resizer, + target, + target_inst, + replacement_cell, + /*cost_divisor=*/2.0f); + const Estimate costly = costly_candidate.estimate(); + EXPECT_FLOAT_EQ(costly.score, raw_estimate.arrival_impr / 2.0f); + ASSERT_TRUE(costly.raw_gain.has_value()); + EXPECT_FLOAT_EQ(*costly.raw_gain, raw_estimate.arrival_impr); + + // VtSwapMt applies its relative cell cost the same way. + VtSwapMtCandidate vt_candidate(resizer, + target, + target_inst, + replacement_cell, + /*cost_divisor=*/2.0f); + const Estimate vt = vt_candidate.estimate(); + EXPECT_FLOAT_EQ(vt.score, raw_estimate.arrival_impr / 2.0f); + ASSERT_TRUE(vt.raw_gain.has_value()); + EXPECT_FLOAT_EQ(*vt.raw_gain, raw_estimate.arrival_impr); +} + +TEST_F(TestResizerMt, RelativeCellCostsKeepTheCheapestOptionWhole) +{ + // The cheapest option above the current cost is the reference; an option at + // most as costly as the current cell keeps its full score. + const std::vector divisors + = relativeCellCosts({1.25f, 2.5f, 0.8f, 1.0f, 10.0f}); + ASSERT_EQ(divisors.size(), 5); + EXPECT_FLOAT_EQ(divisors[0], 1.0f); + EXPECT_FLOAT_EQ(divisors[1], 2.0f); + EXPECT_FLOAT_EQ(divisors[2], 1.0f); + EXPECT_FLOAT_EQ(divisors[3], 1.0f); + EXPECT_FLOAT_EQ(divisors[4], 8.0f); + EXPECT_TRUE(relativeCellCosts({}).empty()); + + // Without a costlier option, as for a VT swap compared by LEF area, every + // option keeps its full score; no divisor ever drops below 1. + for (const float divisor : relativeCellCosts({0.5f, 1.0f, 0.9f})) { + EXPECT_FLOAT_EQ(divisor, 1.0f); + } } TEST_F(TestResizerMt, DualRfDriveGuardRequiresBothTransitions) diff --git a/src/rsz/test/cpp/TestParallelEvalPolicy.cc b/src/rsz/test/cpp/TestParallelEvalPolicy.cc index 0956b1959a..7d073e8cd9 100644 --- a/src/rsz/test/cpp/TestParallelEvalPolicy.cc +++ b/src/rsz/test/cpp/TestParallelEvalPolicy.cc @@ -60,6 +60,68 @@ TEST_F(TestResizerMt, } } +TEST_F(TestResizerMt, OptimizationPolicyConfigScoreScaleMapsMoveWeights) +{ + OptimizationPolicyConfig config; + config.vt_swap_mt_score_scale = 1.5f; + config.size_up_mt_score_scale = 0.25f; + config.rebuffer_mt_score_scale = 0.125f; + config.split_load_mt_score_scale = 0.5f; + config.size_down_fanout_mt_score_scale = 0.75f; + EXPECT_FLOAT_EQ(config.scoreScale(MoveType::kVtSwap), 1.5f); + EXPECT_FLOAT_EQ(config.scoreScale(MoveType::kSizeUp), 0.25f); + EXPECT_FLOAT_EQ(config.scoreScale(MoveType::kBuffer), 0.125f); + EXPECT_FLOAT_EQ(config.scoreScale(MoveType::kSplitLoad), 0.5f); + EXPECT_FLOAT_EQ(config.scoreScale(MoveType::kSizeDownFanout), 0.75f); + // SwapPinsMt and UnbufferMt keep their scores. + EXPECT_FLOAT_EQ(config.scoreScale(MoveType::kSwapPins), 1.0f); + EXPECT_FLOAT_EQ(config.scoreScale(MoveType::kUnbuffer), 1.0f); +} + +TEST_F(TestResizerMt, SetupParallelEvalPolicyWeightsCandidateScores) +{ + ep_.setHWireSignalRC(nullptr, sta_->cmdScene(), 1.0e3, 1.0e-10); + ep_.setVWireSignalRC(nullptr, sta_->cmdScene(), 1.0e3, 1.0e-10); + ep_.estimateWireParasitics(); + resizer_.runRepairSetupPreamble(); + MoveCommitter committer(resizer_); + RepairSetupContext setup_context(resizer_); + OptimizerRunConfig config; + config.setup_slack_margin = 1.0; + SetupParallelEvalPolicy policy(resizer_, committer, setup_context, config); + ASSERT_TRUE(policy.start()); + std::vector targets{makeTarget("out0", "target", "Z"), + makeTarget("path_out", "path_target", "ZN")}; + ResizerTestPeer::prewarmTargets(policy, targets); + ResizerTestPeer::prepareTargets(policy, targets); + std::vector scored; + for (Target& target : targets) { + scored.push_back(&target); + } + + // The candidates score their own gain and cost; the policy weights each + // score by its move. + std::vector evaluations + = ResizerTestPeer::generateCandidates(policy, scored); + ResizerTestPeer::estimateTargets(policy, evaluations); + const OptimizationPolicyConfig& policy_config + = ResizerTestPeer::policyConfig(policy); + int weighted = 0; + for (TargetEvaluation& evaluation : evaluations) { + ASSERT_EQ(evaluation.estimates.size(), evaluation.candidates.size()); + for (size_t index = 0; index < evaluation.candidates.size(); ++index) { + MoveCandidate& candidate = *evaluation.candidates[index]; + const float scale = policy_config.scoreScale(candidate.type()); + EXPECT_FLOAT_EQ(evaluation.estimates[index].score, + candidate.estimate().score * scale); + if (scale != 1.0f) { + ++weighted; + } + } + } + EXPECT_GT(weighted, 0); +} + TEST_F(TestResizerMt, SetupParallelEvalPolicyCountsWorkerPlannerWarnings) { // Placed loads give RebufferMt a Steiner tree to plan on. @@ -362,8 +424,11 @@ TEST_F(TestResizerMt, SetupParallelEvalPolicyAreaStopCountsARemoval) // the run goes on. ResizerTestPeer::openBatchJournal(policy); UnbufferMtCandidate unbuffer(resizer_, removal); - SizeUpMtCandidate size_up( - resizer_, upsize, upsize.inst(resizer_), larger, 1.0f); + SizeUpMtCandidate size_up(resizer_, + upsize, + upsize.inst(resizer_), + larger, + /*cost_divisor=*/1.0f); ASSERT_TRUE(committer.commit(unbuffer).accepted); ASSERT_TRUE(committer.commit(size_up).accepted); EXPECT_FALSE(resizer_.overMaxArea()); @@ -533,14 +598,19 @@ TEST_F(TestResizerMtSequential, for (const bool size_up : {true, false}) { SCOPED_TRACE(size_up ? "size_up" : "vt_swap"); sta::LibertyCell* replacement = size_up ? larger : low_vt; - const float scale = size_up ? 0.5f : 1.0f; const DelayEstimate raw = DelayEstimator::estimate(*target.estimator_context, replacement); ASSERT_TRUE(raw.legal) << failReasonName(raw.reason); - SizeUpMtCandidate size( - resizer_, target, target.inst(resizer_), replacement, scale); - VtSwapMtCandidate vt( - resizer_, target, target.inst(resizer_), replacement); + SizeUpMtCandidate size(resizer_, + target, + target.inst(resizer_), + replacement, + /*cost_divisor=*/1.0f); + VtSwapMtCandidate vt(resizer_, + target, + target.inst(resizer_), + replacement, + /*cost_divisor=*/1.0f); const Estimate estimate = size_up ? size.estimate() : vt.estimate(); ASSERT_TRUE(estimate.legal) << estimate.reason; ASSERT_TRUE(estimate.raw_gain.has_value()); @@ -548,7 +618,7 @@ TEST_F(TestResizerMtSequential, if (!enabled) { EXPECT_FALSE(estimate.sequential_penalty.has_value()); EXPECT_FALSE(estimate.sequential_side_loss.has_value()); - EXPECT_FLOAT_EQ(estimate.score, raw.arrival_impr * scale); + EXPECT_FLOAT_EQ(estimate.score, raw.arrival_impr); continue; } @@ -563,7 +633,7 @@ TEST_F(TestResizerMtSequential, EXPECT_FLOAT_EQ(*estimate.sequential_side_loss, side.max_side_slack_loss); const float adjusted_gain = raw.arrival_impr - side.weighted_side_slack_loss; - EXPECT_FLOAT_EQ(estimate.score, adjusted_gain * scale); + EXPECT_FLOAT_EQ(estimate.score, adjusted_gain); } } diff --git a/src/rsz/test/cpp/TestResizerMt.cc b/src/rsz/test/cpp/TestResizerMt.cc index bdc17c1546..204373edfd 100644 --- a/src/rsz/test/cpp/TestResizerMt.cc +++ b/src/rsz/test/cpp/TestResizerMt.cc @@ -356,7 +356,11 @@ TEST_F(TestResizerMt, MtCandidateEstimateUsesSelectedArcAndCachedState) = sta_->network()->findLibertyCell("BUF_X1_L"); ASSERT_NE(candidate_cell, nullptr); - VtSwapMtCandidate candidate(resizer, target, target_inst, candidate_cell); + VtSwapMtCandidate candidate(resizer, + target, + target_inst, + candidate_cell, + /*cost_divisor=*/1.0f); const Estimate estimate = candidate.estimate(); ASSERT_TRUE(estimate.legal); diff --git a/src/rsz/test/repair_setup_parallel_eval_all_moves.ok b/src/rsz/test/repair_setup_parallel_eval_all_moves.ok index 694b1ad664..c802e49b9c 100644 --- a/src/rsz/test/repair_setup_parallel_eval_all_moves.ok +++ b/src/rsz/test/repair_setup_parallel_eval_all_moves.ok @@ -13,12 +13,12 @@ | Buffers | Gates | Buffers | Gates | Swaps | | | | Endpts | Endpoint ------------------------------------------------------------------------------------------------------------------------------ 0* | 0 | 0 | 0 | 0 | 0 | +0.0% | -66.849 | -1781.2 | 46 | resp_msg[13] - 1* | 0 | 6 | 10 | 0 | 4 | +2.0% | -62.391 | -1504.6 | 45 | resp_msg[13] - 2* | 0 | 15 | 16 | 0 | 8 | +3.3% | -45.303 | -1111.2 | 45 | dpath.a_reg.out\[7\]$_DFFE_PP_/D - 3* | 0 | 23 | 20 | 0 | 14 | +4.3% | -40.178 | -938.9 | 44 | dpath.a_reg.out\[7\]$_DFFE_PP_/D - final | 0 | 31 | 24 | 0 | 20 | +5.3% | -31.242 | -749.7 | 44 | dpath.a_reg.out\[7\]$_DFFE_PP_/D + 1* | 0 | 6 | 10 | 0 | 4 | +1.9% | -62.391 | -1504.5 | 45 | resp_msg[13] + 2* | 0 | 15 | 16 | 0 | 8 | +3.2% | -45.377 | -1102.2 | 45 | resp_msg[15] + 3* | 0 | 23 | 21 | 0 | 14 | +4.3% | -40.242 | -933.3 | 44 | dpath.a_reg.out\[7\]$_DFFE_PP_/D + final | 0 | 29 | 25 | 0 | 22 | +5.2% | -31.328 | -744.0 | 44 | dpath.a_reg.out\[7\]$_DFFE_PP_/D ------------------------------------------------------------------------------------------------------------------------------ -[INFO RSZ-0045] Inserted 24 buffers, 1 to split loads. -[INFO RSZ-0051] Resized 31 instances: 25 up, 0 up match, 6 down, 0 VT -[INFO RSZ-0043] Swapped pins on 20 instances. +[INFO RSZ-0045] Inserted 25 buffers, 1 to split loads. +[INFO RSZ-0051] Resized 29 instances: 22 up, 0 up match, 7 down, 0 VT +[INFO RSZ-0043] Swapped pins on 22 instances. [WARNING RSZ-0062] Unable to repair all setup violations. diff --git a/src/rsz/test/repair_setup_parallel_eval_default_sequence.ok b/src/rsz/test/repair_setup_parallel_eval_default_sequence.ok index 5cf3a6217d..9742e1793c 100644 --- a/src/rsz/test/repair_setup_parallel_eval_default_sequence.ok +++ b/src/rsz/test/repair_setup_parallel_eval_default_sequence.ok @@ -13,38 +13,43 @@ | Buffers | Gates | Buffers | Gates | Swaps | | | | Endpts | Endpoint ------------------------------------------------------------------------------------------------------------------------------ 0* | 0 | 0 | 0 | 0 | 0 | +0.0% | -66.849 | -1781.2 | 46 | resp_msg[13] - 1* | 0 | 27 | 0 | 0 | 0 | +0.0% | -59.413 | -1430.1 | 43 | resp_msg[13] - 2* | 0 | 49 | 0 | 0 | 0 | +0.0% | -43.282 | -778.5 | 42 | resp_msg[15] - 3* | 0 | 65 | 0 | 0 | 0 | +0.0% | -28.082 | -349.5 | 35 | resp_msg[13] - 4* | 0 | 79 | 2 | 0 | 0 | +0.6% | -26.709 | -191.9 | 27 | resp_msg[13] - 5* | 0 | 88 | 8 | 0 | 0 | +2.2% | -24.757 | -120.8 | 21 | resp_msg[13] - 6* | 0 | 94 | 13 | 0 | 0 | +3.4% | -21.112 | -58.0 | 11 | resp_msg[13] - 7* | 0 | 102 | 13 | 0 | 0 | +3.4% | -19.006 | -36.9 | 5 | resp_msg[13] - 8* | 0 | 107 | 18 | 0 | 0 | +4.4% | -17.867 | -34.7 | 7 | resp_msg[13] - 9* | 0 | 114 | 19 | 0 | 0 | +4.8% | -15.861 | -83.0 | 17 | resp_msg[13] - 10* | 0 | 119 | 20 | 0 | 0 | +5.0% | -15.184 | -19.8 | 4 | resp_msg[13] - 11* | 0 | 121 | 21 | 0 | 0 | +5.2% | -14.311 | -16.9 | 3 | resp_msg[13] - 12* | 0 | 125 | 21 | 0 | 0 | +5.3% | -12.610 | -13.3 | 2 | resp_msg[13] - 13* | 0 | 126 | 22 | 0 | 0 | +5.5% | -11.746 | -11.7 | 1 | resp_msg[13] - 14* | 0 | 126 | 23 | 0 | 0 | +5.7% | -11.609 | -11.6 | 1 | resp_msg[13] - 15* | 0 | 127 | 23 | 0 | 0 | +5.7% | -11.263 | -11.3 | 1 | resp_msg[13] - 16* | 0 | 127 | 24 | 0 | 0 | +5.9% | -11.025 | -11.0 | 1 | resp_msg[13] - 17* | 0 | 127 | 24 | 0 | 0 | +5.9% | -11.025 | -11.0 | 1 | resp_msg[13] - 18* | 0 | 127 | 24 | 0 | 1 | +5.9% | -10.213 | -10.2 | 1 | resp_msg[13] - 19* | 0 | 127 | 25 | 0 | 2 | +6.1% | -7.768 | -12.8 | 2 | resp_msg[13] - 20* | 0 | 129 | 27 | 0 | 2 | +6.5% | -7.091 | -10.1 | 2 | resp_msg[13] - 21* | 0 | 132 | 27 | 0 | 3 | +6.5% | -5.881 | -7.6 | 2 | resp_msg[13] - 22* | 0 | 133 | 28 | 0 | 3 | +6.7% | -5.050 | -5.9 | 2 | resp_msg[13] - 23* | 0 | 136 | 28 | 0 | 3 | +6.6% | -4.269 | -4.6 | 2 | resp_msg[13] - 24* | 0 | 138 | 28 | 0 | 4 | +6.6% | -3.488 | -4.1 | 2 | resp_msg[13] - 25* | 0 | 138 | 28 | 0 | 4 | +6.6% | -3.488 | -4.1 | 2 | resp_msg[13] - 26* | 0 | 139 | 28 | 0 | 4 | +6.6% | -3.387 | -3.9 | 2 | resp_msg[13] - 27* | 0 | 139 | 28 | 0 | 5 | +6.6% | -3.167 | -3.4 | 2 | resp_msg[13] - 28* | 0 | 139 | 28 | 0 | 5 | +6.6% | -3.167 | -3.4 | 2 | resp_msg[13] - 29* | 0 | 139 | 28 | 0 | 5 | +6.6% | -3.167 | -3.4 | 2 | resp_msg[13] - final | 0 | 139 | 28 | 0 | 5 | +6.6% | -3.167 | -3.4 | 2 | resp_msg[13] + 1* | 0 | 29 | 0 | 0 | 0 | +0.0% | -57.301 | -1421.2 | 43 | resp_msg[13] + 2* | 0 | 49 | 0 | 0 | 0 | +0.0% | -44.737 | -872.5 | 43 | resp_msg[15] + 3* | 0 | 65 | 4 | 0 | 0 | +0.8% | -34.747 | -574.9 | 42 | resp_msg[13] + 4* | 0 | 79 | 6 | 0 | 0 | +1.3% | -34.225 | -449.0 | 36 | resp_msg[13] + 5* | 0 | 91 | 14 | 0 | 0 | +3.2% | -24.344 | -147.3 | 22 | resp_msg[13] + 6* | 0 | 103 | 20 | 0 | 0 | +4.7% | -22.242 | -89.2 | 20 | resp_msg[13] + 7* | 0 | 118 | 21 | 0 | 0 | +5.1% | -18.095 | -132.4 | 18 | resp_msg[13] + 8* | 0 | 127 | 21 | 0 | 0 | +5.2% | -16.897 | -59.3 | 13 | resp_msg[13] + 9* | 0 | 131 | 22 | 0 | 0 | +5.5% | -16.189 | -49.9 | 12 | resp_msg[13] + 10* | 0 | 139 | 22 | 0 | 0 | +5.5% | -15.277 | -39.6 | 11 | resp_msg[13] + 11* | 0 | 145 | 22 | 0 | 0 | +5.6% | -14.387 | -27.9 | 9 | resp_msg[13] + 12* | 0 | 149 | 23 | 0 | 0 | +5.8% | -14.234 | -20.4 | 7 | resp_msg[13] + 13* | 0 | 154 | 24 | 0 | 0 | +6.0% | -14.073 | -17.0 | 3 | resp_msg[13] + 14* | 0 | 155 | 25 | 0 | 0 | +6.2% | -13.074 | -15.7 | 3 | resp_msg[13] + 15* | 0 | 160 | 26 | 0 | 0 | +6.5% | -12.611 | -13.0 | 3 | resp_msg[13] + 16* | 0 | 161 | 27 | 0 | 0 | +6.7% | -11.118 | -11.1 | 1 | resp_msg[13] + 17* | 0 | 162 | 27 | 0 | 0 | +6.7% | -11.015 | -11.0 | 1 | resp_msg[13] + 18* | 0 | 163 | 27 | 0 | 0 | +6.7% | -10.701 | -10.7 | 1 | resp_msg[13] + 19* | 0 | 165 | 27 | 0 | 0 | +6.7% | -10.637 | -10.6 | 1 | resp_msg[13] + 20* | 0 | 165 | 28 | 0 | 0 | +6.9% | -10.113 | -10.1 | 1 | resp_msg[13] + 21* | 0 | 165 | 28 | 0 | 0 | +6.9% | -10.113 | -10.1 | 1 | resp_msg[13] + 22* | 0 | 165 | 28 | 0 | 1 | +6.9% | -9.301 | -9.3 | 1 | resp_msg[13] + 23* | 0 | 165 | 30 | 0 | 1 | +7.2% | -6.917 | -11.0 | 2 | resp_msg[13] + 24* | 0 | 168 | 30 | 0 | 1 | +7.2% | -6.780 | -9.4 | 2 | resp_msg[13] + 25* | 0 | 170 | 30 | 0 | 3 | +7.2% | -6.047 | -7.9 | 2 | resp_msg[13] + 26* | 0 | 170 | 32 | 0 | 3 | +7.6% | -5.376 | -6.5 | 2 | resp_msg[13] + 27* | 0 | 171 | 33 | 0 | 3 | +7.8% | -4.658 | -5.1 | 2 | resp_msg[13] + 28* | 0 | 173 | 33 | 0 | 3 | +7.8% | -4.056 | -4.1 | 2 | resp_msg[13] + 29* | 0 | 173 | 33 | 0 | 4 | +7.8% | -3.316 | -3.6 | 2 | resp_msg[13] + 30* | 0 | 173 | 33 | 0 | 4 | +7.8% | -3.316 | -3.6 | 2 | resp_msg[13] + 31* | 0 | 174 | 33 | 0 | 4 | +7.8% | -3.256 | -3.5 | 2 | resp_msg[13] + 32* | 0 | 174 | 33 | 0 | 5 | +7.8% | -2.847 | -2.8 | 1 | resp_msg[13] + 33* | 0 | 174 | 33 | 0 | 5 | +7.8% | -2.847 | -2.8 | 1 | resp_msg[13] + 34* | 0 | 174 | 33 | 0 | 5 | +7.8% | -2.847 | -2.8 | 1 | resp_msg[13] + final | 0 | 174 | 33 | 0 | 5 | +7.8% | -2.847 | -2.8 | 1 | resp_msg[13] ------------------------------------------------------------------------------------------------------------------------------ -[INFO RSZ-0045] Inserted 28 buffers, 1 to split loads. -[INFO RSZ-0051] Resized 139 instances: 23 up, 0 up match, 10 down, 106 VT +[INFO RSZ-0045] Inserted 33 buffers, 1 to split loads. +[INFO RSZ-0051] Resized 174 instances: 28 up, 0 up match, 8 down, 138 VT [INFO RSZ-0043] Swapped pins on 5 instances. [WARNING RSZ-0062] Unable to repair all setup violations. diff --git a/src/rsz/test/repair_setup_parallel_eval_effort.ok b/src/rsz/test/repair_setup_parallel_eval_effort.ok index d2e8c5564d..40ab4b7e47 100644 --- a/src/rsz/test/repair_setup_parallel_eval_effort.ok +++ b/src/rsz/test/repair_setup_parallel_eval_effort.ok @@ -13,10 +13,10 @@ | Buffers | Gates | Buffers | Gates | Swaps | | | | Endpts | Endpoint ------------------------------------------------------------------------------------------------------------------------------ 0* | 0 | 0 | 0 | 0 | 0 | +0.0% | -66.849 | -1781.2 | 46 | resp_msg[13] - final | 0 | 127 | 24 | 0 | 0 | +5.9% | -11.025 | -11.0 | 1 | resp_msg[13] + final | 0 | 165 | 28 | 0 | 0 | +6.9% | -10.113 | -10.1 | 1 | resp_msg[13] ------------------------------------------------------------------------------------------------------------------------------ -[INFO RSZ-0045] Inserted 24 buffers, 1 to split loads. -[INFO RSZ-0051] Resized 127 instances: 23 up, 0 up match, 0 down, 104 VT +[INFO RSZ-0045] Inserted 28 buffers, 1 to split loads. +[INFO RSZ-0051] Resized 165 instances: 28 up, 0 up match, 0 down, 137 VT [WARNING RSZ-0062] Unable to repair all setup violations. -worst slack max -11.025 -tns max -11.025 +worst slack max -10.113 +tns max -10.113 diff --git a/src/rsz/test/repair_setup_parallel_eval_fanin_max_cap.ok b/src/rsz/test/repair_setup_parallel_eval_fanin_max_cap.ok new file mode 100644 index 0000000000..787de13080 --- /dev/null +++ b/src/rsz/test/repair_setup_parallel_eval_fanin_max_cap.ok @@ -0,0 +1,24 @@ +[INFO ODB-0227] LEF file: sky130hd/sky130hd.tlef, created 13 layers, 25 vias +[INFO ODB-0227] LEF file: sky130hd/sky130hd_std_cell.lef, created 437 library cells +max capacitance + +Pin Limit Cap Slack +------------------------------------------------------------ +drv_se_full/X 0.13 0.14 -0.01 (VIOLATED) + +drv_si_full/X 0.13 0.14 -0.01 (VIOLATED) + +drv_clk_prop_full/X 0.13 0.14 -0.01 (VIOLATED) + +[WARNING RSZ-2024] Experimental repair setup policy 'SetupParallelEvalPolicy' selected. Do not use this for production. + Iter | Removed | Resized | Inserted | Cloned | Pin | Area | WNS | TNS | Viol | Worst + | Buffers | Gates | Buffers | Gates | Swaps | | | | Endpts | Endpoint +------------------------------------------------------------------------------------------------------------------------------ + 0* | 0 | 0 | 0 | 0 | 0 | +0.0% | -1.859 | -2.7 | 3 | q_prop + final | 0 | 2 | 0 | 0 | 0 | +4.3% | -1.859 | -2.2 | 3 | q_prop +------------------------------------------------------------------------------------------------------------------------------ +[INFO RSZ-0051] Resized 2 instances: 2 up, 0 up match, 0 down, 0 VT +[WARNING RSZ-0062] Unable to repair all setup violations. +ff_scan sky130_fd_sc_hd__sdfxbp_2 +ff_ideal sky130_fd_sc_hd__sdfxbp_2 +ff_prop sky130_fd_sc_hd__sdfxbp_1 diff --git a/src/rsz/test/repair_setup_parallel_eval_fanin_max_cap.tcl b/src/rsz/test/repair_setup_parallel_eval_fanin_max_cap.tcl new file mode 100644 index 0000000000..d19e8e9f16 --- /dev/null +++ b/src/rsz/test/repair_setup_parallel_eval_fanin_max_cap.tcl @@ -0,0 +1,39 @@ +# PARALLEL_EVAL size-ups of scan flops whose fanin drivers have no max-cap +# headroom. As in LEGACY, the scan-in and scan-enable pins skip the fanin +# max-cap check. STA does not check the max cap of an ideal clock net, and a +# propagated clock net keeps the check. +source "helpers.tcl" + +read_liberty sky130hd/sky130hd_tt.lib +read_lef sky130hd/sky130hd.tlef +read_lef sky130hd/sky130hd_std_cell.lef +read_verilog repair_setup_parallel_eval_fanin_max_cap.v +link_design top + +create_clock -name clk -period 2 [get_ports clk] +create_clock -name clk_prop -period 2 [get_ports clk_prop] +set_propagated_clock [get_clocks clk_prop] +# The late propagated clock would fail hold on the clk inputs of ff_prop and +# block its size-up for a reason other than max cap. +set_false_path -hold -from [get_clocks clk] -to [get_clocks clk_prop] +set_input_delay 0 -clock clk [get_ports {d se si}] +set_output_delay 1.5 -clock clk [get_ports {q_scan q_ideal}] +set_output_delay 1.5 -clock clk_prop [get_ports q_prop] +set_load 0.1 [get_ports {q_scan q_ideal q_prop}] +# Push the drivers of the tested pins past their max cap. +set_load 0.14 [get_ports {se_full si_full clk_full clk_prop_full}] + +source sky130hd/sky130hd.rc +set_wire_rc -layer met2 +estimate_parasitics -placement + +# STA leaves the ideal clock net of drv_clk_full unchecked. +report_check_types -max_capacitance -violators + +# sdfxbp_2 has larger CLK, SCE and SCD pins than sdfxbp_1. +repair_timing -setup -phases PARALLEL_EVAL -sequence sizeup + +# ff_scan and ff_ideal size up; ff_prop keeps sdfxbp_1. +foreach inst {ff_scan ff_ideal ff_prop} { + puts "$inst [get_property [get_cells $inst] ref_name]" +} diff --git a/src/rsz/test/repair_setup_parallel_eval_fanin_max_cap.v b/src/rsz/test/repair_setup_parallel_eval_fanin_max_cap.v new file mode 100644 index 0000000000..a6ad4df6b7 --- /dev/null +++ b/src/rsz/test/repair_setup_parallel_eval_fanin_max_cap.v @@ -0,0 +1,28 @@ +// Three scan flops on output paths with heavy loads. The *_full output ports +// load the drivers of the tested pins past their max cap: the scan pins of +// ff_scan, the ideal clock pin of ff_ideal and the propagated clock pin of +// ff_prop. Every other flop input has a driver with headroom. +module top (clk, clk_prop, d, se, si, + se_full, si_full, clk_full, clk_prop_full, + q_scan, q_ideal, q_prop); + input clk, clk_prop, d, se, si; + output se_full, si_full, clk_full, clk_prop_full; + output q_scan, q_ideal, q_prop; + wire d_buf, se_buf, si_buf; + + sky130_fd_sc_hd__buf_1 drv_d (.A(d), .X(d_buf)); + sky130_fd_sc_hd__buf_1 drv_se (.A(se), .X(se_buf)); + sky130_fd_sc_hd__buf_1 drv_si (.A(si), .X(si_buf)); + sky130_fd_sc_hd__buf_1 drv_se_full (.A(se), .X(se_full)); + sky130_fd_sc_hd__buf_1 drv_si_full (.A(si), .X(si_full)); + sky130_fd_sc_hd__clkbuf_1 drv_clk_full (.A(clk), .X(clk_full)); + sky130_fd_sc_hd__clkbuf_1 drv_clk_prop_full (.A(clk_prop), + .X(clk_prop_full)); + + sky130_fd_sc_hd__sdfxbp_1 ff_scan (.CLK(clk), .D(d_buf), .SCD(si_full), + .SCE(se_full), .Q(q_scan), .Q_N()); + sky130_fd_sc_hd__sdfxbp_1 ff_ideal (.CLK(clk_full), .D(d_buf), + .SCD(si_buf), .SCE(se_buf), .Q(q_ideal), .Q_N()); + sky130_fd_sc_hd__sdfxbp_1 ff_prop (.CLK(clk_prop_full), .D(d_buf), + .SCD(si_buf), .SCE(se_buf), .Q(q_prop), .Q_N()); +endmodule diff --git a/src/rsz/test/repair_setup_parallel_eval_path_group.ok b/src/rsz/test/repair_setup_parallel_eval_path_group.ok index 4c94b22aeb..baef0c902e 100644 --- a/src/rsz/test/repair_setup_parallel_eval_path_group.ok +++ b/src/rsz/test/repair_setup_parallel_eval_path_group.ok @@ -21,17 +21,17 @@ req_msg[28] (input) _877_/D (DFF_X1) -0.1 | Buffers | Gates | Buffers | Gates | Swaps | | | | Endpts | Endpoint ------------------------------------------------------------------------------------------------------------------------------ 0* | 0 | 0 | 0 | 0 | 0 | +0.0% | -0.112 | -2.5 | 32 | _869_/D - final | 52 | 76 | 12 | 0 | 2 | +2.0% | -0.005 | -0.0 | 13 | _875_/D + final | 54 | 94 | 18 | 0 | 1 | +3.8% | -0.007 | -0.0 | 11 | _880_/D ------------------------------------------------------------------------------------------------------------------------------ -[INFO RSZ-0059] Removed 52 buffers. -[INFO RSZ-0040] Inserted 12 buffers. -[INFO RSZ-0051] Resized 76 instances: 72 up, 0 up match, 4 down, 0 VT -[INFO RSZ-0043] Swapped pins on 2 instances. +[INFO RSZ-0059] Removed 54 buffers. +[INFO RSZ-0040] Inserted 18 buffers. +[INFO RSZ-0051] Resized 94 instances: 85 up, 0 up match, 9 down, 0 VT +[INFO RSZ-0043] Swapped pins on 1 instances. [WARNING RSZ-0062] Unable to repair all setup violations. -- reg2reg after Startpoint Endpoint Slack ---------------------------------------------------------------------------------- -_872_/Q (DFF_X1) _875_/D (DFF_X1) -0.0045 +_872_/Q (DFF_X1) _880_/D (DFF_X1) -0.0067 -- in2reg after Startpoint Endpoint Slack diff --git a/src/rsz/test/repair_setup_parallel_eval_reference_period.ok b/src/rsz/test/repair_setup_parallel_eval_reference_period.ok index ae395b2874..84c96a16f4 100755 --- a/src/rsz/test/repair_setup_parallel_eval_reference_period.ok +++ b/src/rsz/test/repair_setup_parallel_eval_reference_period.ok @@ -14,12 +14,12 @@ | Buffers | Gates | Buffers | Gates | Swaps | | | | Endpts | Endpoint ------------------------------------------------------------------------------------------------------------------------------ 0* | 0 | 0 | 0 | 0 | 0 | +0.0% | -34.849 | -241.8 | 46 | resp_msg[13] -[DEBUG RSZ-repair_setup] PARALLEL_EVAL WNS tail after no_committed_moves at batch 16 (WNS 2.098e-11) - final | 0 | 160 | 35 | 0 | 16 | +8.8% | 28.389 | 0.0 | 24 | resp_msg[12] +[DEBUG RSZ-repair_setup] PARALLEL_EVAL WNS tail after no_committed_moves at batch 21 (WNS 2.155e-11) + final | 0 | 212 | 34 | 0 | 5 | +9.0% | 28.694 | 0.0 | 20 | resp_msg[14] ------------------------------------------------------------------------------------------------------------------------------ -[INFO RSZ-0040] Inserted 35 buffers. -[INFO RSZ-0051] Resized 160 instances: 33 up, 0 up match, 9 down, 118 VT -[INFO RSZ-0043] Swapped pins on 16 instances. +[INFO RSZ-0045] Inserted 34 buffers, 1 to split loads. +[INFO RSZ-0051] Resized 212 instances: 43 up, 0 up match, 6 down, 163 VT +[INFO RSZ-0043] Swapped pins on 5 instances. [WARNING RSZ-0062] Unable to repair all setup violations. -worst slack max 28.389 +worst slack max 28.694 tns max 0.000 diff --git a/src/rsz/test/repair_setup_parallel_eval_reference_period_modes.ok b/src/rsz/test/repair_setup_parallel_eval_reference_period_modes.ok index 8c2b22c3a3..ff9b3ddfdd 100644 --- a/src/rsz/test/repair_setup_parallel_eval_reference_period_modes.ok +++ b/src/rsz/test/repair_setup_parallel_eval_reference_period_modes.ok @@ -10,11 +10,11 @@ | Buffers | Gates | Buffers | Gates | Swaps | | | | Endpts | Endpoint ------------------------------------------------------------------------------------------------------------------------------ 0* | 0 | 0 | 0 | 0 | 0 | +0.0% | -0.212 | -7.0 | 45 | _869_/D -[DEBUG RSZ-repair_setup] PARALLEL_EVAL WNS tail after no_committed_moves at batch 16 (WNS -1.222e-10) - final | 58 | 104 | 26 | 0 | 2 | +5.6% | -0.110 | -3.6 | 42 | resp_msg[15] +[DEBUG RSZ-repair_setup] PARALLEL_EVAL WNS tail after no_committed_moves at batch 20 (WNS -1.146e-10) + final | 60 | 112 | 34 | 0 | 2 | +8.6% | -0.111 | -3.5 | 44 | resp_msg[15] ------------------------------------------------------------------------------------------------------------------------------ -[INFO RSZ-0059] Removed 58 buffers. -[INFO RSZ-0040] Inserted 26 buffers. -[INFO RSZ-0051] Resized 104 instances: 101 up, 0 up match, 3 down, 0 VT +[INFO RSZ-0059] Removed 60 buffers. +[INFO RSZ-0040] Inserted 34 buffers. +[INFO RSZ-0051] Resized 112 instances: 110 up, 0 up match, 2 down, 0 VT [INFO RSZ-0043] Swapped pins on 2 instances. [WARNING RSZ-0062] Unable to repair all setup violations. diff --git a/src/rsz/test/repair_setup_parallel_eval_vt_swap.ok b/src/rsz/test/repair_setup_parallel_eval_vt_swap.ok index 9b40a45267..4e33edbac0 100644 --- a/src/rsz/test/repair_setup_parallel_eval_vt_swap.ok +++ b/src/rsz/test/repair_setup_parallel_eval_vt_swap.ok @@ -13,8 +13,8 @@ | Buffers | Gates | Buffers | Gates | Swaps | | | | Endpts | Endpoint ------------------------------------------------------------------------------------------------------------------------------ 0* | 0 | 0 | 0 | 0 | 0 | +0.0% | -66.849 | -1781.2 | 46 | resp_msg[13] - 1* | 0 | 27 | 0 | 0 | 0 | +0.0% | -59.413 | -1430.1 | 43 | resp_msg[13] - final | 0 | 49 | 0 | 0 | 0 | +0.0% | -43.282 | -778.5 | 42 | resp_msg[15] + 1* | 0 | 29 | 0 | 0 | 0 | +0.0% | -57.301 | -1421.2 | 43 | resp_msg[13] + final | 0 | 49 | 0 | 0 | 0 | +0.0% | -44.737 | -872.5 | 43 | resp_msg[15] ------------------------------------------------------------------------------------------------------------------------------ [INFO RSZ-0051] Resized 49 instances: 0 up, 0 up match, 0 down, 49 VT [WARNING RSZ-0062] Unable to repair all setup violations. diff --git a/src/rsz/test/repair_setup_parallel_eval_vt_swap_corners.ok b/src/rsz/test/repair_setup_parallel_eval_vt_swap_corners.ok index 05a2046d70..88d3700d52 100644 --- a/src/rsz/test/repair_setup_parallel_eval_vt_swap_corners.ok +++ b/src/rsz/test/repair_setup_parallel_eval_vt_swap_corners.ok @@ -13,8 +13,8 @@ | Buffers | Gates | Buffers | Gates | Swaps | | | | Endpts | Endpoint ------------------------------------------------------------------------------------------------------------------------------ 0* | 0 | 0 | 0 | 0 | 0 | +0.0% | -88.835 | -2700.2 | 47 | resp_msg[13] - 1* | 0 | 29 | 0 | 0 | 0 | +0.0% | -78.128 | -2296.8 | 45 | resp_msg[13] - final | 0 | 49 | 0 | 0 | 0 | +0.0% | -61.275 | -1995.2 | 43 | resp_msg[14] + 1* | 0 | 28 | 0 | 0 | 0 | +0.0% | -77.404 | -2303.4 | 46 | resp_msg[13] + final | 0 | 53 | 0 | 0 | 0 | +0.0% | -63.202 | -2005.6 | 44 | resp_msg[12] ------------------------------------------------------------------------------------------------------------------------------ -[INFO RSZ-0051] Resized 49 instances: 0 up, 0 up match, 0 down, 49 VT +[INFO RSZ-0051] Resized 53 instances: 0 up, 0 up match, 0 down, 53 VT [WARNING RSZ-0062] Unable to repair all setup violations.