From 5761de9378e806380c0a7df957816461727c7575 Mon Sep 17 00:00:00 2001 From: Jaehyun Kim Date: Thu, 5 Nov 2026 10:58:55 -0800 Subject: [PATCH 1/5] rsz: Add a relative leakage cost to PARALLEL_EVAL cell swaps PARALLEL_EVAL ranked SizeUpMt and VtSwapMt candidates by timing gain alone, while SizeUpGenerator::upsizeCell (LEGACY, #11916) already picks the size with the best gain per unit of cost. Without a cost, a target could take the largest size or jump over several VTs when a cheaper option gave a similar gain. A raw leakage ratio cannot be shared between the two moves: in ASAP7 one VT step multiplies a cell's leakage by about 10x and one size step by about 1.3x, so dividing the scores by the raw ratio drops VT swaps from the shared pool. What and how: - MoveGenerator::cellCostRatio() returns the new/old cell cost: leakage, else LEF area, else Liberty area, as upsizeCell compares them. It runs on the main thread because cellLeakage() fills a cache. - relativeCellCosts() turns the ratios of one move's options for a target into score divisors max(1, r / r_min), where r_min is the smallest ratio above 1. The cheapest costlier option keeps its full score, a costlier one pays only its extra cost, and no divisor drops below 1, so the cost never raises a score. - SizeUpMtGenerator and VtSwapMtGenerator find the ratios in prepareInputs(), cache them per library cell, and pass the divisors of the options that fit the max-cap budgets to the candidates, which divide positive scores by them after the side costs. - As the cost only orders the options of a move, the move weights set the balance between moves: VtSwapMt gets its own weight (vt_swap_mt_score_scale = 1.25) and SizeUpMt goes from 0.75 to 0.60, tuned on post-placement setup repair benchmarks. - Resizer::inputCapBudgets() now skips ideal register clock pins and scan pins the same way as replacementPreservesMaxCap(), through the new skipsFaninMaxCap(), so the generation-time max-cap screen matches the apply-time check. - Add unit tests for the move weights, the divisors and relativeCellCosts(), and update the PARALLEL_EVAL goldens. Signed-off-by: Jaehyun Kim --- src/rsz/include/rsz/Resizer.hh | 7 +- src/rsz/src/OptimizationPolicyConfig.hh | 12 +-- src/rsz/src/OptimizerTypes.cc | 24 ++++++ src/rsz/src/OptimizerTypes.hh | 9 +++ src/rsz/src/Resizer.cc | 51 ++++++++----- src/rsz/src/move/MoveGenerator.cc | 42 +++++++++++ src/rsz/src/move/MoveGenerator.hh | 13 +++- src/rsz/src/move/SizeUpMtCandidate.cc | 19 ++++- src/rsz/src/move/SizeUpMtCandidate.hh | 8 +- src/rsz/src/move/SizeUpMtGenerator.cc | 31 +++++++- src/rsz/src/move/SizeUpMtGenerator.hh | 3 + src/rsz/src/move/VtSwapMtCandidate.cc | 31 ++++++-- src/rsz/src/move/VtSwapMtCandidate.hh | 12 ++- src/rsz/src/move/VtSwapMtGenerator.cc | 57 ++++++++++++-- src/rsz/src/move/VtSwapMtGenerator.hh | 23 ++++-- src/rsz/src/policy/SetupParallelEvalPolicy.cc | 6 +- src/rsz/test/cpp/TestCellSwapMt.cc | 62 +++++++++++++++- src/rsz/test/cpp/TestParallelEvalPolicy.cc | 24 ++++-- src/rsz/test/cpp/TestResizerMt.cc | 7 +- .../repair_setup_parallel_eval_all_moves.ok | 14 ++-- ...ir_setup_parallel_eval_default_sequence.ok | 74 +++++++++++-------- .../test/repair_setup_parallel_eval_effort.ok | 10 +-- .../repair_setup_parallel_eval_max_util.ok | 8 +- .../repair_setup_parallel_eval_path_group.ok | 10 +-- ...ir_setup_parallel_eval_reference_period.ok | 12 +-- ...up_parallel_eval_reference_period_modes.ok | 10 +-- .../repair_setup_parallel_eval_vt_swap.ok | 4 +- ...air_setup_parallel_eval_vt_swap_corners.ok | 6 +- 28 files changed, 452 insertions(+), 137 deletions(-) diff --git a/src/rsz/include/rsz/Resizer.hh b/src/rsz/include/rsz/Resizer.hh index 3fbf30638d7..caa0029bd60 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.hh b/src/rsz/src/OptimizationPolicyConfig.hh index 8c581d1f4d9..8b1753b215b 100644 --- a/src/rsz/src/OptimizationPolicyConfig.hh +++ b/src/rsz/src/OptimizationPolicyConfig.hh @@ -228,11 +228,13 @@ 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. - float size_up_mt_score_scale{0.75f}; + // 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. + float vt_swap_mt_score_scale{1.25f}; + float size_up_mt_score_scale{0.60f}; // Largest drive-resistance ratio (current / replacement) of a size-up; // zero means no bound. PARALLEL_EVAL sets one. diff --git a/src/rsz/src/OptimizerTypes.cc b/src/rsz/src/OptimizerTypes.cc index 4eacb495d4f..82e9272b3ad 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 1d1aac0991e..017d51af5dd 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 668f8ce5543..99f3fc7f1e2 100644 --- a/src/rsz/src/Resizer.cc +++ b/src/rsz/src/Resizer.cc @@ -8619,27 +8619,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_); @@ -8657,8 +8640,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); @@ -8680,6 +8688,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/MoveGenerator.cc b/src/rsz/src/move/MoveGenerator.cc index 060db43396b..9691516eed6 100644 --- a/src/rsz/src/move/MoveGenerator.cc +++ b/src/rsz/src/move/MoveGenerator.cc @@ -3,9 +3,13 @@ #include "MoveGenerator.hh" +#include +#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 +18,44 @@ namespace rsz { +namespace { + +// Cell costs in the order SizeUpGenerator::upsizeCell tries them: leakage, +// then LEF area, then Liberty area. An unavailable metric is nullopt. +using CellCosts = std::array, 3>; + +CellCosts cellCosts(Resizer& resizer, sta::LibertyCell* cell) +{ + 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; +} + +} // namespace + +std::optional MoveGenerator::cellCostRatio(sta::LibertyCell* from, + sta::LibertyCell* to) const +{ + const CellCosts from_costs = cellCosts(resizer_, from); + const CellCosts to_costs = cellCosts(resizer_, 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 a6e7fbe1324..c2d01de57b8 100644 --- a/src/rsz/src/move/MoveGenerator.hh +++ b/src/rsz/src/move/MoveGenerator.hh @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -148,6 +149,14 @@ class MoveGenerator const sta::RiseFall* drvr_rf, const sta::MinMax* min_max) const; + // === Shared cell-cost helper ============================================= + // New/old cost of a swap from `from` to `to`: leakage, else LEF area, else + // Liberty area, the order SizeUpGenerator::upsizeCell uses, so units never + // mix. nullopt when the two cells share no metric. Main thread only: + // cellLeakage() fills a cache. + std::optional cellCostRatio(sta::LibertyCell* from, + 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 +164,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/SizeUpMtCandidate.cc b/src/rsz/src/move/SizeUpMtCandidate.cc index 4e00afe56e2..3a535a63399 100644 --- a/src/rsz/src/move/SizeUpMtCandidate.cc +++ b/src/rsz/src/move/SizeUpMtCandidate.cc @@ -23,12 +23,14 @@ SizeUpMtCandidate::SizeUpMtCandidate(Resizer& resizer, const Target& target, sta::Instance* inst, sta::LibertyCell* replacement, - float score_scale) + float score_scale, + float cost_divisor) : MoveCandidate(resizer, target), inst_(inst), current_cell_(resizer.network()->libertyCell(inst)), replacement_(replacement), - score_scale_(score_scale) + score_scale_(score_scale), + cost_divisor_(cost_divisor) { } @@ -46,7 +48,7 @@ Estimate SizeUpMtCandidate::estimate() // 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, @@ -56,9 +58,20 @@ Estimate SizeUpMtCandidate::estimate() } chargeCellSwapSideCosts(delay_est, replacement_, score_scale_, 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 a4a355799f3..3e55a79e99e 100644 --- a/src/rsz/src/move/SizeUpMtCandidate.hh +++ b/src/rsz/src/move/SizeUpMtCandidate.hh @@ -36,7 +36,8 @@ class SizeUpMtCandidate : public MoveCandidate const Target& target, sta::Instance* inst, sta::LibertyCell* replacement, - float score_scale); + float score_scale, + float cost_divisor); // === MoveCandidate API ==================================================== Estimate estimate() override; @@ -50,12 +51,17 @@ 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 14b8775b831..59e4f9446ca 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 @@ -66,16 +67,30 @@ 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]); + } - candidates.push_back(std::make_unique( - resizer_, target, target_inst, replacement, score_scale)); + // 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, + fitting[i], + score_scale, + cost_divisors[i])); } return candidates; } @@ -163,7 +178,15 @@ 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, 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 95562191d0c..1fb1738b2a7 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/VtSwapMtCandidate.cc b/src/rsz/src/move/VtSwapMtCandidate.cc index e208233ed62..e0d03760486 100644 --- a/src/rsz/src/move/VtSwapMtCandidate.cc +++ b/src/rsz/src/move/VtSwapMtCandidate.cc @@ -44,11 +44,15 @@ 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 score_scale, + const float cost_divisor) : MoveCandidate(resizer, target), inst_(inst), current_cell_(resizer.network()->libertyCell(inst)), - candidate_cell_(candidate_cell) + candidate_cell_(candidate_cell), + score_scale_(score_scale), + cost_divisor_(cost_divisor) { } @@ -62,8 +66,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 * score_scale_); + return rejected; } return makeRejectedEstimate(failReasonName(delay_est.reason)); } @@ -71,13 +77,24 @@ Estimate VtSwapMtCandidate::estimate() Estimate estimate{.legal = true, .raw_gain = delay_est.arrival_impr, .reason = failReasonName(FailReason::kEstimateLegal)}; - estimate.score = delay_est.arrival_impr; + estimate.score = delay_est.arrival_impr * score_scale_; - // 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_, score_scale_, 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 9ce55946663..42745ce5393 100644 --- a/src/rsz/src/move/VtSwapMtCandidate.hh +++ b/src/rsz/src/move/VtSwapMtCandidate.hh @@ -33,7 +33,9 @@ class VtSwapMtCandidate : public MoveCandidate VtSwapMtCandidate(Resizer& resizer, const Target& target, sta::Instance* inst, - sta::LibertyCell* candidate_cell); + sta::LibertyCell* candidate_cell, + float score_scale, + float cost_divisor); // === MoveCandidate API ==================================================== Estimate estimate() override; @@ -46,10 +48,18 @@ 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}; + // Weight of the score in the shared pool. + const float score_scale_; + // 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 6262e33dfcb..a3539fc8bd9 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 "MoveCandidate.hh" @@ -24,6 +26,36 @@ VtSwapMtGenerator::VtSwapMtGenerator(const GeneratorContext& context) { } +void VtSwapMtGenerator::prepareInputs(Target& target) const +{ + prepareInputCapBudgets(target); + + // Find the cell costs here, so generate() on a worker only reads the cache. + sta::LibertyCell* current_cell + = resizer_.network()->libertyCell(target.inst(resizer_)); + if (!vt_swap_options_.contains(current_cell)) { + vt_swap_options_.emplace(current_cell, buildVtSwapOptions(current_cell)); + } +} + +VtSwapMtGenerator::VtSwapOptions VtSwapMtGenerator::buildVtSwapOptions( + sta::LibertyCell* current_cell) const +{ + VtSwapOptions options; + for (sta::LibertyCell* candidate_cell : selectCandidateCells(current_cell)) { + // As for SizeUpMt, a cell without a cost on a metric both cells have is + // not a candidate. + const std::optional cost_ratio + = cellCostRatio(current_cell, candidate_cell); + if (!cost_ratio) { + continue; + } + options.cells.push_back(candidate_cell); + options.cost_ratios.push_back(*cost_ratio); + } + return options; +} + std::vector> VtSwapMtGenerator::generate( const Target& target) { @@ -34,13 +66,14 @@ std::vector> VtSwapMtGenerator::generate( // the workers start, key on the link cell. sta::LibertyCell* current_cell = resizer_.network()->libertyCell(target.inst(resizer_)); - const std::vector candidate_cells - = selectCandidateCells(current_cell); + const VtSwapOptions& options = vt_swap_options_.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); @@ -65,8 +98,22 @@ 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], + policy_config_.vt_swap_mt_score_scale, + cost_divisors[i])); } return candidates; } diff --git a/src/rsz/src/move/VtSwapMtGenerator.hh b/src/rsz/src/move/VtSwapMtGenerator.hh index c28c6fb8230..bf52a79b318 100644 --- a/src/rsz/src/move/VtSwapMtGenerator.hh +++ b/src/rsz/src/move/VtSwapMtGenerator.hh @@ -3,6 +3,7 @@ #pragma once +#include #include #include @@ -21,8 +22,8 @@ namespace rsz { // multiple VtSwapMtCandidate instances for parallel evaluation. // // prepareRequirements() asks for the estimator context and the flip-flop -// input-side checks, and prepareInputs() captures the input-cap budgets, so -// generate() runs on worker threads without live STA. +// input-side checks, and prepareInputs() captures the input-cap budgets and +// the cell costs, so generate() runs on worker threads without live STA. class VtSwapMtGenerator : public MoveGenerator { public: @@ -36,17 +37,27 @@ class VtSwapMtGenerator : public MoveGenerator return kEstimatorContextCache | kSequentialEstimatorCache; } bool isApplicable(const Target& target) const override; - void prepareInputs(Target& target) const override - { - prepareInputCapBudgets(target); - } + void prepareInputs(Target& target) const override; std::vector> generate( const Target& target) override; private: + // The VT-equivalent cells of one current cell and their new/old cell cost + // ratios. The library does not change in a run, so the options are cached + // for the run: prepareInputs() fills the cache on the main thread and + // generate() only reads it. + struct VtSwapOptions + { + std::vector cells; + std::vector cost_ratios; + }; + // === Candidate-cell selection ============================================ std::vector selectCandidateCells( sta::LibertyCell* current_cell) const; + VtSwapOptions buildVtSwapOptions(sta::LibertyCell* current_cell) const; + + mutable std::map vt_swap_options_; }; } // namespace rsz diff --git a/src/rsz/src/policy/SetupParallelEvalPolicy.cc b/src/rsz/src/policy/SetupParallelEvalPolicy.cc index aff0e728531..ea05f360a90 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, diff --git a/src/rsz/test/cpp/TestCellSwapMt.cc b/src/rsz/test/cpp/TestCellSwapMt.cc index e10b258bcbb..20962264666 100644 --- a/src/rsz/test/cpp/TestCellSwapMt.cc +++ b/src/rsz/test/cpp/TestCellSwapMt.cc @@ -107,17 +107,71 @@ 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); + SizeUpMtCandidate default_scale_candidate(resizer, + target, + target_inst, + replacement_cell, + 1.0f, + /*cost_divisor=*/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); - SizeUpMtCandidate half_scale_candidate( - resizer, target, target_inst, replacement_cell, 0.5f); + SizeUpMtCandidate half_scale_candidate(resizer, + target, + target_inst, + replacement_cell, + 0.5f, + /*cost_divisor=*/1.0f); 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, + 1.0f, + /*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 weight and its relative cell cost the same way. + VtSwapMtCandidate vt_candidate(resizer, + target, + target_inst, + replacement_cell, + 1.25f, + /*cost_divisor=*/2.0f); + const Estimate vt = vt_candidate.estimate(); + EXPECT_FLOAT_EQ(vt.score, raw_estimate.arrival_impr * 1.25f / 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 0956b1959a4..c37c17a7c99 100644 --- a/src/rsz/test/cpp/TestParallelEvalPolicy.cc +++ b/src/rsz/test/cpp/TestParallelEvalPolicy.cc @@ -362,8 +362,12 @@ 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, + 1.0f, + /*cost_divisor=*/1.0f); ASSERT_TRUE(committer.commit(unbuffer).accepted); ASSERT_TRUE(committer.commit(size_up).accepted); EXPECT_FALSE(resizer_.overMaxArea()); @@ -537,10 +541,18 @@ TEST_F(TestResizerMtSequential, 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, + scale, + /*cost_divisor=*/1.0f); + VtSwapMtCandidate vt(resizer_, + target, + target.inst(resizer_), + replacement, + /*score_scale=*/1.0f, + /*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()); diff --git a/src/rsz/test/cpp/TestResizerMt.cc b/src/rsz/test/cpp/TestResizerMt.cc index bdc17c15463..393e815c4d2 100644 --- a/src/rsz/test/cpp/TestResizerMt.cc +++ b/src/rsz/test/cpp/TestResizerMt.cc @@ -356,7 +356,12 @@ 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, + /*score_scale=*/1.0f, + /*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 694b1ad6649..0101ba809ed 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 | -1507.4 | 45 | resp_msg[13] + 2* | 0 | 15 | 16 | 0 | 8 | +3.2% | -45.376 | -1080.7 | 45 | resp_msg[15] + 3* | 0 | 23 | 21 | 0 | 14 | +4.3% | -38.866 | -911.6 | 44 | dpath.a_reg.out\[7\]$_DFFE_PP_/D + final | 0 | 29 | 25 | 0 | 22 | +5.2% | -31.243 | -729.7 | 44 | resp_msg[13] ------------------------------------------------------------------------------------------------------------------------------ -[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: 21 up, 0 up match, 8 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 5cf3a6217d3..bb2c8dd8e90 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,48 @@ | 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 | 68 | 2 | 0 | 0 | +0.4% | -34.690 | -537.8 | 41 | resp_msg[13] + 4* | 0 | 82 | 4 | 0 | 0 | +0.8% | -34.259 | -455.2 | 37 | resp_msg[13] + 5* | 0 | 94 | 12 | 0 | 0 | +2.6% | -24.436 | -156.6 | 27 | resp_msg[13] + 6* | 0 | 107 | 13 | 0 | 0 | +3.2% | -23.017 | -110.5 | 18 | resp_msg[13] + 7* | 0 | 120 | 14 | 0 | 0 | +3.7% | -19.789 | -151.9 | 21 | resp_msg[13] + 8* | 0 | 128 | 20 | 0 | 0 | +4.9% | -19.188 | -72.2 | 14 | resp_msg[13] + 9* | 0 | 134 | 20 | 0 | 0 | +4.9% | -17.401 | -60.5 | 13 | resp_msg[13] + 10* | 0 | 140 | 20 | 0 | 0 | +4.9% | -15.381 | -41.9 | 11 | resp_msg[13] + 11* | 0 | 147 | 21 | 0 | 0 | +5.1% | -15.180 | -36.3 | 10 | resp_msg[13] + 12* | 0 | 153 | 21 | 0 | 0 | +5.2% | -14.481 | -27.7 | 8 | resp_msg[13] + 13* | 0 | 156 | 22 | 0 | 0 | +5.3% | -14.419 | -18.7 | 3 | resp_msg[13] + 14* | 0 | 159 | 22 | 0 | 0 | +5.3% | -14.297 | -17.9 | 3 | resp_msg[13] + 15* | 0 | 163 | 22 | 0 | 0 | +5.3% | -13.012 | -16.3 | 3 | resp_msg[13] + 16* | 0 | 167 | 22 | 0 | 0 | +5.4% | -12.782 | -14.4 | 3 | resp_msg[13] + 17* | 0 | 168 | 23 | 0 | 0 | +5.6% | -12.657 | -13.6 | 3 | resp_msg[13] + 18* | 0 | 169 | 23 | 0 | 0 | +5.6% | -12.438 | -13.2 | 3 | resp_msg[13] + 19* | 0 | 171 | 25 | 0 | 0 | +6.0% | -12.134 | -12.4 | 2 | resp_msg[13] + 20* | 0 | 171 | 26 | 0 | 0 | +6.2% | -12.086 | -12.3 | 2 | resp_msg[13] + 21* | 0 | 172 | 26 | 0 | 0 | +6.2% | -11.563 | -11.7 | 2 | resp_msg[13] + 22* | 0 | 174 | 26 | 0 | 0 | +6.3% | -10.706 | -10.7 | 1 | resp_msg[13] + 23* | 0 | 174 | 27 | 0 | 0 | +6.5% | -10.182 | -10.2 | 1 | resp_msg[13] + 24* | 0 | 174 | 27 | 0 | 0 | +6.5% | -10.182 | -10.2 | 1 | resp_msg[13] + 25* | 0 | 174 | 27 | 0 | 1 | +6.5% | -9.374 | -9.4 | 1 | resp_msg[13] + 26* | 0 | 174 | 29 | 0 | 1 | +6.9% | -7.054 | -11.2 | 2 | resp_msg[13] + 27* | 0 | 177 | 29 | 0 | 2 | +6.9% | -6.540 | -8.9 | 2 | resp_msg[13] + 28* | 0 | 180 | 30 | 0 | 3 | +7.1% | -5.601 | -7.0 | 2 | resp_msg[13] + 29* | 0 | 181 | 30 | 0 | 3 | +7.1% | -5.231 | -6.2 | 2 | resp_msg[13] + 30* | 0 | 183 | 31 | 0 | 3 | +7.2% | -4.551 | -4.9 | 2 | resp_msg[13] + 31* | 0 | 185 | 31 | 0 | 3 | +7.2% | -3.914 | -3.9 | 1 | resp_msg[13] + 32* | 0 | 185 | 33 | 0 | 4 | +7.6% | -3.112 | -3.2 | 2 | resp_msg[13] + 33* | 0 | 186 | 33 | 0 | 4 | +7.6% | -2.977 | -3.0 | 1 | resp_msg[13] + 34* | 0 | 187 | 33 | 0 | 4 | +7.6% | -2.919 | -2.9 | 1 | resp_msg[13] + 35* | 0 | 187 | 33 | 0 | 4 | +7.6% | -2.919 | -2.9 | 1 | resp_msg[13] + 36* | 0 | 188 | 33 | 0 | 4 | +7.6% | -2.859 | -2.9 | 1 | resp_msg[13] + 37* | 0 | 188 | 33 | 0 | 5 | +7.6% | -2.450 | -2.5 | 1 | resp_msg[13] + 38* | 0 | 188 | 33 | 0 | 5 | +7.6% | -2.450 | -2.5 | 1 | resp_msg[13] + 39* | 0 | 188 | 33 | 0 | 5 | +7.6% | -2.450 | -2.5 | 1 | resp_msg[13] + final | 0 | 188 | 33 | 0 | 5 | +7.6% | -2.450 | -2.5 | 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 188 instances: 22 up, 0 up match, 8 down, 158 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 d2e8c5564db..dd9356ede8b 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 | 174 | 27 | 0 | 0 | +6.5% | -10.182 | -10.2 | 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 27 buffers, 1 to split loads. +[INFO RSZ-0051] Resized 174 instances: 22 up, 0 up match, 0 down, 152 VT [WARNING RSZ-0062] Unable to repair all setup violations. -worst slack max -11.025 -tns max -11.025 +worst slack max -10.182 +tns max -10.182 diff --git a/src/rsz/test/repair_setup_parallel_eval_max_util.ok b/src/rsz/test/repair_setup_parallel_eval_max_util.ok index 2fbc060b570..78bd9f3ef8b 100644 --- a/src/rsz/test/repair_setup_parallel_eval_max_util.ok +++ b/src/rsz/test/repair_setup_parallel_eval_max_util.ok @@ -12,11 +12,11 @@ Design area 894 um^2 1% utilization. 0* | 0 | 0 | 0 | 0 | 0 | +0.0% | -0.253 | -2.5 | 28 | inst13/D 1* | 26 | 2 | 3 | 0 | 0 | -2.7% | -0.195 | -1.2 | 17 | inst13/D 2* | 27 | 7 | 7 | 0 | 0 | -1.8% | -0.180 | -0.7 | 12 | inst13/D - final | 27 | 11 | 8 | 0 | 0 | -1.2% | -0.173 | -0.6 | 10 | inst13/D + final | 27 | 10 | 9 | 0 | 0 | -1.3% | -0.170 | -0.6 | 10 | inst13/D ------------------------------------------------------------------------------------------------------------------------------ [INFO RSZ-0059] Removed 27 buffers. -[INFO RSZ-0040] Inserted 8 buffers. -[INFO RSZ-0051] Resized 11 instances: 11 up, 0 up match, 0 down, 0 VT +[INFO RSZ-0040] Inserted 9 buffers. +[INFO RSZ-0051] Resized 10 instances: 10 up, 0 up match, 0 down, 0 VT [WARNING RSZ-0062] Unable to repair all setup violations. Design area 883 um^2 1% utilization. -utilization 1.4109% +utilization 1.4096% 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 4c94b22aeb2..85c343eb369 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 | 55 | 86 | 17 | 0 | 2 | +3.4% | -0.004 | -0.0 | 14 | _866_/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-0059] Removed 55 buffers. +[INFO RSZ-0040] Inserted 17 buffers. +[INFO RSZ-0051] Resized 86 instances: 80 up, 0 up match, 6 down, 0 VT [INFO RSZ-0043] Swapped pins on 2 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) _866_/D (DFF_X1) -0.0036 -- 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 ae395b2874e..67acd3d6a32 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 22 (WNS 1.908e-11) + final | 0 | 200 | 34 | 0 | 9 | +8.3% | 26.874 | 0.0 | 12 | resp_msg[13] ------------------------------------------------------------------------------------------------------------------------------ -[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 200 instances: 30 up, 0 up match, 4 down, 166 VT +[INFO RSZ-0043] Swapped pins on 9 instances. [WARNING RSZ-0062] Unable to repair all setup violations. -worst slack max 28.389 +worst slack max 26.874 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 8c2b22c3a38..20eed98c759 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 22 (WNS -1.270e-10) + final | 58 | 121 | 25 | 0 | 3 | +5.7% | -0.118 | -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-0043] Swapped pins on 2 instances. +[INFO RSZ-0040] Inserted 25 buffers. +[INFO RSZ-0051] Resized 121 instances: 109 up, 0 up match, 12 down, 0 VT +[INFO RSZ-0043] Swapped pins on 3 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 9b40a45267e..4e33edbac0e 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 05a2046d708..88d3700d520 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. From d0bc11064cb9e7d7e4efba24c2d80fcc671116b1 Mon Sep 17 00:00:00 2001 From: Jaehyun Kim Date: Thu, 5 Nov 2026 15:03:01 -0800 Subject: [PATCH 2/5] rsz: Add a PARALLEL_EVAL fanin max-cap test for scan pins The SizeUpMt and VtSwapMt generators screen each replacement with Resizer::inputCapBudgets(). Before skipsFaninMaxCap() was shared, that screen did not skip the scan-enable and scan-in pins as replacementPreservesMaxCap() does, so PARALLEL_EVAL kept a scan flop at its size when its scan pin drivers had no max-cap headroom, while LEGACY sized it up. No test covered the PARALLEL_EVAL side. repair_setup_parallel_eval_fanin_max_cap sizes up three sky130hd sdfxbp_1 flops with -sequence sizeup. sdfxbp_2 has larger CLK, SCE and SCD pins, and output port loads push the driver of each tested pin past its max cap: - ff_scan, on its SCE and SCD drivers, sizes up as in LEGACY. Without the scan pin skip it keeps sdfxbp_1. - ff_ideal, on its ideal clock driver, sizes up: STA does not check the max cap of an ideal clock net. - ff_prop, on its propagated clock driver, keeps sdfxbp_1. With headroom on that driver it sizes up. Every flop input has a gate driver because the flop estimator replays a pin cap change only through a gate model, and a hold false path keeps the late propagated clock from blocking ff_prop for another reason. Signed-off-by: Jaehyun Kim --- src/rsz/test/BUILD | 1 + ...epair_setup_parallel_eval_fanin_max_cap.ok | 24 ++++++++++++ ...pair_setup_parallel_eval_fanin_max_cap.tcl | 39 +++++++++++++++++++ ...repair_setup_parallel_eval_fanin_max_cap.v | 28 +++++++++++++ 4 files changed, 92 insertions(+) create mode 100644 src/rsz/test/repair_setup_parallel_eval_fanin_max_cap.ok create mode 100644 src/rsz/test/repair_setup_parallel_eval_fanin_max_cap.tcl create mode 100644 src/rsz/test/repair_setup_parallel_eval_fanin_max_cap.v diff --git a/src/rsz/test/BUILD b/src/rsz/test/BUILD index d7a2f7f1f56..3d51b6e146b 100644 --- a/src/rsz/test/BUILD +++ b/src/rsz/test/BUILD @@ -298,6 +298,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/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 00000000000..787de13080c --- /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 00000000000..d19e8e9f16a --- /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 00000000000..a6ad4df6b75 --- /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 From 6307600589de5b3b0698292eda5c2d1e94b02ea3 Mon Sep 17 00:00:00 2001 From: Jaehyun Kim Date: Thu, 5 Nov 2026 16:34:35 -0800 Subject: [PATCH 3/5] rsz: Apply the PARALLEL_EVAL move weights in the policy Five Mt candidates took their move weight (*_mt_score_scale) through the constructor and multiplied their own score by it, so the candidates, their generators and chargeCellSwapSideCosts() all carried the same plumbing. The weights only balance the moves in the policy's shared score pool, so the policy now owns them. - OptimizationPolicyConfig::scoreScale() maps a MoveType to its weight, 1 for SwapPinsMt, UnbufferMt and the moves without one. The switch has no default, so a new move type must pick a weight. - SetupParallelEvalPolicy::estimateTarget() multiplies each estimate by the weight of its move right after estimate(). It is static and runs on the workers, so it takes the policy config as an argument. - The candidates score their own gain and cost. SizeUpMt and VtSwapMt still divide a positive score by their relative cell cost inside the move; SplitLoadMt, SizeDownFanoutMt and RebufferMt drop the weight member and constructor parameter, and the generators stop passing it. The RebufferMt debug message prints the unweighted score. - The candidate unit test checks only the cell cost. New tests check the weight of each move type and that estimateTargets() applies it. The weight now follows the cost divisor, (gain / divisor) x weight instead of (gain x weight) / divisor. The rsz goldens and the QoR and move counts of 34 post-placement benchmark designs are unchanged. Signed-off-by: Jaehyun Kim --- src/rsz/src/OptimizationPolicyConfig.cc | 26 +++++++ src/rsz/src/OptimizationPolicyConfig.hh | 4 ++ src/rsz/src/move/MoveCandidate.cc | 5 +- src/rsz/src/move/MoveCandidate.hh | 3 +- src/rsz/src/move/RebufferMtCandidate.cc | 9 +-- src/rsz/src/move/RebufferMtCandidate.hh | 3 - src/rsz/src/move/RebufferMtGenerator.cc | 1 - src/rsz/src/move/SizeDownFanoutMtCandidate.cc | 9 +-- src/rsz/src/move/SizeDownFanoutMtCandidate.hh | 4 +- src/rsz/src/move/SizeDownFanoutMtGenerator.cc | 7 +- src/rsz/src/move/SizeUpMtCandidate.cc | 6 +- src/rsz/src/move/SizeUpMtCandidate.hh | 2 - src/rsz/src/move/SizeUpMtGenerator.cc | 9 +-- src/rsz/src/move/SplitLoadMtCandidate.cc | 11 ++- src/rsz/src/move/SplitLoadMtCandidate.hh | 4 +- src/rsz/src/move/SplitLoadMtGenerator.cc | 5 +- src/rsz/src/move/VtSwapMtCandidate.cc | 8 +-- src/rsz/src/move/VtSwapMtCandidate.hh | 3 - src/rsz/src/move/VtSwapMtGenerator.cc | 7 +- src/rsz/src/policy/SetupParallelEvalPolicy.cc | 16 +++-- src/rsz/src/policy/SetupParallelEvalPolicy.hh | 7 +- src/rsz/test/cpp/TestCellSwapMt.cc | 36 ++++------ src/rsz/test/cpp/TestParallelEvalPolicy.cc | 70 +++++++++++++++++-- src/rsz/test/cpp/TestResizerMt.cc | 1 - 24 files changed, 149 insertions(+), 107 deletions(-) diff --git a/src/rsz/src/OptimizationPolicyConfig.cc b/src/rsz/src/OptimizationPolicyConfig.cc index 176893cd5fb..59645525e87 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 8b1753b215b..cf7984033b5 100644 --- a/src/rsz/src/OptimizationPolicyConfig.hh +++ b/src/rsz/src/OptimizationPolicyConfig.hh @@ -197,6 +197,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 diff --git a/src/rsz/src/move/MoveCandidate.cc b/src/rsz/src/move/MoveCandidate.cc index f73e8eb9939..103512b0724 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 1311542722f..85ac7237bcd 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/RebufferMtCandidate.cc b/src/rsz/src/move/RebufferMtCandidate.cc index a3ed03aeb18..fabc4670a95 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 5d247667957..dc1ef4c90e0 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 c1a6c00d6ea..8afa0458478 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 de016d8332e..37c05a45482 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 ca239d37cd8..6a93fbcc2dd 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 5e1b39cf460..71c9be70701 100644 --- a/src/rsz/src/move/SizeDownFanoutMtGenerator.cc +++ b/src/rsz/src/move/SizeDownFanoutMtGenerator.cc @@ -685,11 +685,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 3a535a63399..446d38ad0f9 100644 --- a/src/rsz/src/move/SizeUpMtCandidate.cc +++ b/src/rsz/src/move/SizeUpMtCandidate.cc @@ -23,13 +23,11 @@ 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) { } @@ -43,7 +41,7 @@ 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 { @@ -57,7 +55,7 @@ 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); diff --git a/src/rsz/src/move/SizeUpMtCandidate.hh b/src/rsz/src/move/SizeUpMtCandidate.hh index 3e55a79e99e..76901c3f0ec 100644 --- a/src/rsz/src/move/SizeUpMtCandidate.hh +++ b/src/rsz/src/move/SizeUpMtCandidate.hh @@ -36,7 +36,6 @@ class SizeUpMtCandidate : public MoveCandidate const Target& target, sta::Instance* inst, sta::LibertyCell* replacement, - float score_scale, float cost_divisor); // === MoveCandidate API ==================================================== @@ -58,7 +57,6 @@ class SizeUpMtCandidate : public MoveCandidate 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_; diff --git a/src/rsz/src/move/SizeUpMtGenerator.cc b/src/rsz/src/move/SizeUpMtGenerator.cc index 59e4f9446ca..f39b876ba5e 100644 --- a/src/rsz/src/move/SizeUpMtGenerator.cc +++ b/src/rsz/src/move/SizeUpMtGenerator.cc @@ -54,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) { @@ -85,12 +84,8 @@ std::vector> SizeUpMtGenerator::generate( 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, - fitting[i], - score_scale, - cost_divisors[i])); + candidates.push_back(std::make_unique( + resizer_, target, target_inst, fitting[i], cost_divisors[i])); } return candidates; } diff --git a/src/rsz/src/move/SplitLoadMtCandidate.cc b/src/rsz/src/move/SplitLoadMtCandidate.cc index 57e4595c356..874f7c18e75 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 914c077c3f5..1be5d6f5ab5 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 f54a36883c4..e81e88e0902 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 e0d03760486..d99b06405ce 100644 --- a/src/rsz/src/move/VtSwapMtCandidate.cc +++ b/src/rsz/src/move/VtSwapMtCandidate.cc @@ -45,13 +45,11 @@ VtSwapMtCandidate::VtSwapMtCandidate(Resizer& resizer, const Target& target, sta::Instance* inst, sta::LibertyCell* candidate_cell, - const float score_scale, const float cost_divisor) : MoveCandidate(resizer, target), inst_(inst), current_cell_(resizer.network()->libertyCell(inst)), candidate_cell_(candidate_cell), - score_scale_(score_scale), cost_divisor_(cost_divisor) { } @@ -68,7 +66,7 @@ Estimate VtSwapMtCandidate::estimate() if (delay_est.reason == FailReason::kEstimateNonImproving) { Estimate rejected = makeRejectedEstimate(failReasonName(delay_est.reason), delay_est.arrival_impr); - rejected.score = costScaled(rejected.score * score_scale_); + rejected.score = costScaled(rejected.score); return rejected; } return makeRejectedEstimate(failReasonName(delay_est.reason)); @@ -77,10 +75,10 @@ Estimate VtSwapMtCandidate::estimate() Estimate estimate{.legal = true, .raw_gain = delay_est.arrival_impr, .reason = failReasonName(FailReason::kEstimateLegal)}; - estimate.score = delay_est.arrival_impr * score_scale_; + estimate.score = delay_est.arrival_impr; // The side costs come off the raw gain before the weight. - chargeCellSwapSideCosts(delay_est, candidate_cell_, score_scale_, estimate); + 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); diff --git a/src/rsz/src/move/VtSwapMtCandidate.hh b/src/rsz/src/move/VtSwapMtCandidate.hh index 42745ce5393..062012e9a55 100644 --- a/src/rsz/src/move/VtSwapMtCandidate.hh +++ b/src/rsz/src/move/VtSwapMtCandidate.hh @@ -34,7 +34,6 @@ class VtSwapMtCandidate : public MoveCandidate const Target& target, sta::Instance* inst, sta::LibertyCell* candidate_cell, - float score_scale, float cost_divisor); // === MoveCandidate API ==================================================== @@ -55,8 +54,6 @@ class VtSwapMtCandidate : public MoveCandidate sta::Instance* inst_{nullptr}; sta::LibertyCell* current_cell_{nullptr}; sta::LibertyCell* candidate_cell_{nullptr}; - // Weight of the score in the shared pool. - const float score_scale_; // Relative cell cost of the candidate among the target's VTs, at least 1 // (relativeCellCosts()). const float cost_divisor_; diff --git a/src/rsz/src/move/VtSwapMtGenerator.cc b/src/rsz/src/move/VtSwapMtGenerator.cc index a3539fc8bd9..758832a5335 100644 --- a/src/rsz/src/move/VtSwapMtGenerator.cc +++ b/src/rsz/src/move/VtSwapMtGenerator.cc @@ -108,12 +108,7 @@ std::vector> VtSwapMtGenerator::generate( candidates.reserve(fitting.size()); for (size_t i = 0; i < fitting.size(); ++i) { candidates.push_back(std::make_unique( - resizer_, - target, - target.inst(resizer_), - fitting[i], - policy_config_.vt_swap_mt_score_scale, - cost_divisors[i])); + resizer_, target, target.inst(resizer_), fitting[i], cost_divisors[i])); } return candidates; } diff --git a/src/rsz/src/policy/SetupParallelEvalPolicy.cc b/src/rsz/src/policy/SetupParallelEvalPolicy.cc index ea05f360a90..ea0376afa8c 100644 --- a/src/rsz/src/policy/SetupParallelEvalPolicy.cc +++ b/src/rsz/src/policy/SetupParallelEvalPolicy.cc @@ -925,11 +925,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 @@ -954,12 +957,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 24a8d103765..143eb9673c6 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/cpp/TestCellSwapMt.cc b/src/rsz/test/cpp/TestCellSwapMt.cc index 20962264666..3d594929ec2 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,25 +107,15 @@ 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, - /*cost_divisor=*/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); - - SizeUpMtCandidate half_scale_candidate(resizer, - target, - target_inst, - replacement_cell, - 0.5f, - /*cost_divisor=*/1.0f); - 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 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); // The score is the gain per unit of relative cell cost. raw_gain stays the // estimated gain. @@ -133,22 +123,20 @@ TEST_F(TestResizerMt, SizeUpMtCandidateAppliesScoreScale) target, target_inst, replacement_cell, - 1.0f, /*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 weight and its relative cell cost the same way. + // VtSwapMt applies its relative cell cost the same way. VtSwapMtCandidate vt_candidate(resizer, target, target_inst, replacement_cell, - 1.25f, /*cost_divisor=*/2.0f); const Estimate vt = vt_candidate.estimate(); - EXPECT_FLOAT_EQ(vt.score, raw_estimate.arrival_impr * 1.25f / 2.0f); + 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); } diff --git a/src/rsz/test/cpp/TestParallelEvalPolicy.cc b/src/rsz/test/cpp/TestParallelEvalPolicy.cc index c37c17a7c99..7d073e8cd99 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. @@ -366,7 +428,6 @@ TEST_F(TestResizerMt, SetupParallelEvalPolicyAreaStopCountsARemoval) upsize, upsize.inst(resizer_), larger, - 1.0f, /*cost_divisor=*/1.0f); ASSERT_TRUE(committer.commit(unbuffer).accepted); ASSERT_TRUE(committer.commit(size_up).accepted); @@ -537,7 +598,6 @@ 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); @@ -545,13 +605,11 @@ TEST_F(TestResizerMtSequential, target, target.inst(resizer_), replacement, - scale, /*cost_divisor=*/1.0f); VtSwapMtCandidate vt(resizer_, target, target.inst(resizer_), replacement, - /*score_scale=*/1.0f, /*cost_divisor=*/1.0f); const Estimate estimate = size_up ? size.estimate() : vt.estimate(); ASSERT_TRUE(estimate.legal) << estimate.reason; @@ -560,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; } @@ -575,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 393e815c4d2..204373edfdf 100644 --- a/src/rsz/test/cpp/TestResizerMt.cc +++ b/src/rsz/test/cpp/TestResizerMt.cc @@ -360,7 +360,6 @@ TEST_F(TestResizerMt, MtCandidateEstimateUsesSelectedArcAndCachedState) target, target_inst, candidate_cell, - /*score_scale=*/1.0f, /*cost_divisor=*/1.0f); const Estimate estimate = candidate.estimate(); From d3ca85c07fc239900c4a267ebec078869bdbc0c7 Mon Sep 17 00:00:00 2001 From: Jaehyun Kim Date: Fri, 6 Nov 2026 13:56:25 -0800 Subject: [PATCH 4/5] rsz: Find a cell's costs once per PARALLEL_EVAL option build buildSizeUpOptions() and buildVtSwapOptions() called cellCostRatio() for every candidate cell, and each call looked up the costs of the current cell again, although that cell is the same for the whole loop. MoveGenerator::cellCosts() now returns a cell's costs (leakage, LEF area, Liberty area), and cellCostRatio() takes the current cell's costs with the candidate cell. Each builder finds the current cell's costs once before its loop, as SizeUpGenerator::upsizeCell already does. The ratios do not change. Signed-off-by: Jaehyun Kim --- src/rsz/src/move/MoveGenerator.cc | 20 +++++--------------- src/rsz/src/move/MoveGenerator.hh | 18 ++++++++++++------ src/rsz/src/move/SizeUpMtGenerator.cc | 5 ++++- src/rsz/src/move/VtSwapMtGenerator.cc | 4 +++- 4 files changed, 24 insertions(+), 23 deletions(-) diff --git a/src/rsz/src/move/MoveGenerator.cc b/src/rsz/src/move/MoveGenerator.cc index 9691516eed6..c627d7f77d1 100644 --- a/src/rsz/src/move/MoveGenerator.cc +++ b/src/rsz/src/move/MoveGenerator.cc @@ -3,7 +3,6 @@ #include "MoveGenerator.hh" -#include #include #include #include @@ -18,20 +17,14 @@ namespace rsz { -namespace { - -// Cell costs in the order SizeUpGenerator::upsizeCell tries them: leakage, -// then LEF area, then Liberty area. An unavailable metric is nullopt. -using CellCosts = std::array, 3>; - -CellCosts cellCosts(Resizer& resizer, sta::LibertyCell* cell) +MoveGenerator::CellCosts MoveGenerator::cellCosts(sta::LibertyCell* cell) const { CellCosts costs; - const std::optional leakage = resizer.cellLeakage(cell); + const std::optional leakage = resizer_.cellLeakage(cell); if (leakage && *leakage > 0.0f) { costs[0] = leakage; } - const odb::dbMaster* master = resizer.dbNetwork()->staToDb(cell); + const odb::dbMaster* master = resizer_.dbNetwork()->staToDb(cell); if (master != nullptr && master->getArea() > 0) { costs[1] = static_cast(master->getArea()); } @@ -41,13 +34,10 @@ CellCosts cellCosts(Resizer& resizer, sta::LibertyCell* cell) return costs; } -} // namespace - -std::optional MoveGenerator::cellCostRatio(sta::LibertyCell* from, +std::optional MoveGenerator::cellCostRatio(const CellCosts& from_costs, sta::LibertyCell* to) const { - const CellCosts from_costs = cellCosts(resizer_, from); - const CellCosts to_costs = cellCosts(resizer_, to); + 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]; diff --git a/src/rsz/src/move/MoveGenerator.hh b/src/rsz/src/move/MoveGenerator.hh index c2d01de57b8..165694c58fc 100644 --- a/src/rsz/src/move/MoveGenerator.hh +++ b/src/rsz/src/move/MoveGenerator.hh @@ -3,6 +3,7 @@ #pragma once +#include #include #include #include @@ -149,12 +150,17 @@ class MoveGenerator const sta::RiseFall* drvr_rf, const sta::MinMax* min_max) const; - // === Shared cell-cost helper ============================================= - // New/old cost of a swap from `from` to `to`: leakage, else LEF area, else - // Liberty area, the order SizeUpGenerator::upsizeCell uses, so units never - // mix. nullopt when the two cells share no metric. Main thread only: - // cellLeakage() fills a cache. - std::optional cellCostRatio(sta::LibertyCell* from, + // === 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 ========================================= diff --git a/src/rsz/src/move/SizeUpMtGenerator.cc b/src/rsz/src/move/SizeUpMtGenerator.cc index f39b876ba5e..511efdce46f 100644 --- a/src/rsz/src/move/SizeUpMtGenerator.cc +++ b/src/rsz/src/move/SizeUpMtGenerator.cc @@ -139,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; @@ -176,7 +178,8 @@ SizeUpMtGenerator::SizeUpOptions SizeUpMtGenerator::buildSizeUpOptions( // 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, swappable); + const std::optional cost_ratio + = cellCostRatio(cell_costs, swappable); if (!cost_ratio) { continue; } diff --git a/src/rsz/src/move/VtSwapMtGenerator.cc b/src/rsz/src/move/VtSwapMtGenerator.cc index 758832a5335..1494e9c20af 100644 --- a/src/rsz/src/move/VtSwapMtGenerator.cc +++ b/src/rsz/src/move/VtSwapMtGenerator.cc @@ -42,11 +42,13 @@ VtSwapMtGenerator::VtSwapOptions VtSwapMtGenerator::buildVtSwapOptions( sta::LibertyCell* current_cell) const { VtSwapOptions options; + // The current cell's costs, shared by every swap below. + const CellCosts current_costs = cellCosts(current_cell); for (sta::LibertyCell* candidate_cell : selectCandidateCells(current_cell)) { // As for SizeUpMt, a cell without a cost on a metric both cells have is // not a candidate. const std::optional cost_ratio - = cellCostRatio(current_cell, candidate_cell); + = cellCostRatio(current_costs, candidate_cell); if (!cost_ratio) { continue; } From 94de9d98f73f6495319d4682dfab6080d50d5072 Mon Sep 17 00:00:00 2001 From: Jaehyun Kim Date: Sun, 8 Nov 2026 10:34:30 -0800 Subject: [PATCH 5/5] rsz: Restore the existing PARALLEL_EVAL move weights The relative leakage cost came with retuned move weights, VtSwapMt 1.25 and SizeUpMt 0.60. On 29 public designs at the nominal clock period and at 98% and 95% of it, the existing weights (VtSwapMt 1.0, SizeUpMt 0.75) with the relative cost give lower leakage at every period (-2.34%, -1.33% and -1.45% against master, versus -1.33%, -1.23% and -1.17%) and about the same Fmax. They also keep ihp-sg13g2 ibex closing as master does: with SizeUpMt at 0.60, other moves win the targets that size-ups fix on master, and setup repair stalls at -165 ps WNS. Restore the existing weights in OptimizationPolicyConfig, note why a VtSwapMt weight above 1 is not wanted, and update the PARALLEL_EVAL goldens. The max_util golden matches master again. Signed-off-by: Jaehyun Kim --- src/rsz/src/OptimizationPolicyConfig.hh | 8 +- .../repair_setup_parallel_eval_all_moves.ok | 10 +-- ...ir_setup_parallel_eval_default_sequence.ok | 73 +++++++++---------- .../test/repair_setup_parallel_eval_effort.ok | 10 +-- .../repair_setup_parallel_eval_max_util.ok | 8 +- .../repair_setup_parallel_eval_path_group.ok | 12 +-- ...ir_setup_parallel_eval_reference_period.ok | 10 +-- ...up_parallel_eval_reference_period_modes.ok | 12 +-- 8 files changed, 70 insertions(+), 73 deletions(-) diff --git a/src/rsz/src/OptimizationPolicyConfig.hh b/src/rsz/src/OptimizationPolicyConfig.hh index cf7984033b5..3b5cb9675c9 100644 --- a/src/rsz/src/OptimizationPolicyConfig.hh +++ b/src/rsz/src/OptimizationPolicyConfig.hh @@ -236,9 +236,11 @@ struct OptimizationPolicyConfig // 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. - float vt_swap_mt_score_scale{1.25f}; - float size_up_mt_score_scale{0.60f}; + // 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; // zero means no bound. PARALLEL_EVAL sets one. 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 0101ba809ed..c802e49b9cd 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 | +1.9% | -62.391 | -1507.4 | 45 | resp_msg[13] - 2* | 0 | 15 | 16 | 0 | 8 | +3.2% | -45.376 | -1080.7 | 45 | resp_msg[15] - 3* | 0 | 23 | 21 | 0 | 14 | +4.3% | -38.866 | -911.6 | 44 | dpath.a_reg.out\[7\]$_DFFE_PP_/D - final | 0 | 29 | 25 | 0 | 22 | +5.2% | -31.243 | -729.7 | 44 | resp_msg[13] + 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 25 buffers, 1 to split loads. -[INFO RSZ-0051] Resized 29 instances: 21 up, 0 up match, 8 down, 0 VT +[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 bb2c8dd8e90..9742e1793c0 100644 --- a/src/rsz/test/repair_setup_parallel_eval_default_sequence.ok +++ b/src/rsz/test/repair_setup_parallel_eval_default_sequence.ok @@ -15,46 +15,41 @@ 0* | 0 | 0 | 0 | 0 | 0 | +0.0% | -66.849 | -1781.2 | 46 | 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 | 68 | 2 | 0 | 0 | +0.4% | -34.690 | -537.8 | 41 | resp_msg[13] - 4* | 0 | 82 | 4 | 0 | 0 | +0.8% | -34.259 | -455.2 | 37 | resp_msg[13] - 5* | 0 | 94 | 12 | 0 | 0 | +2.6% | -24.436 | -156.6 | 27 | resp_msg[13] - 6* | 0 | 107 | 13 | 0 | 0 | +3.2% | -23.017 | -110.5 | 18 | resp_msg[13] - 7* | 0 | 120 | 14 | 0 | 0 | +3.7% | -19.789 | -151.9 | 21 | resp_msg[13] - 8* | 0 | 128 | 20 | 0 | 0 | +4.9% | -19.188 | -72.2 | 14 | resp_msg[13] - 9* | 0 | 134 | 20 | 0 | 0 | +4.9% | -17.401 | -60.5 | 13 | resp_msg[13] - 10* | 0 | 140 | 20 | 0 | 0 | +4.9% | -15.381 | -41.9 | 11 | resp_msg[13] - 11* | 0 | 147 | 21 | 0 | 0 | +5.1% | -15.180 | -36.3 | 10 | resp_msg[13] - 12* | 0 | 153 | 21 | 0 | 0 | +5.2% | -14.481 | -27.7 | 8 | resp_msg[13] - 13* | 0 | 156 | 22 | 0 | 0 | +5.3% | -14.419 | -18.7 | 3 | resp_msg[13] - 14* | 0 | 159 | 22 | 0 | 0 | +5.3% | -14.297 | -17.9 | 3 | resp_msg[13] - 15* | 0 | 163 | 22 | 0 | 0 | +5.3% | -13.012 | -16.3 | 3 | resp_msg[13] - 16* | 0 | 167 | 22 | 0 | 0 | +5.4% | -12.782 | -14.4 | 3 | resp_msg[13] - 17* | 0 | 168 | 23 | 0 | 0 | +5.6% | -12.657 | -13.6 | 3 | resp_msg[13] - 18* | 0 | 169 | 23 | 0 | 0 | +5.6% | -12.438 | -13.2 | 3 | resp_msg[13] - 19* | 0 | 171 | 25 | 0 | 0 | +6.0% | -12.134 | -12.4 | 2 | resp_msg[13] - 20* | 0 | 171 | 26 | 0 | 0 | +6.2% | -12.086 | -12.3 | 2 | resp_msg[13] - 21* | 0 | 172 | 26 | 0 | 0 | +6.2% | -11.563 | -11.7 | 2 | resp_msg[13] - 22* | 0 | 174 | 26 | 0 | 0 | +6.3% | -10.706 | -10.7 | 1 | resp_msg[13] - 23* | 0 | 174 | 27 | 0 | 0 | +6.5% | -10.182 | -10.2 | 1 | resp_msg[13] - 24* | 0 | 174 | 27 | 0 | 0 | +6.5% | -10.182 | -10.2 | 1 | resp_msg[13] - 25* | 0 | 174 | 27 | 0 | 1 | +6.5% | -9.374 | -9.4 | 1 | resp_msg[13] - 26* | 0 | 174 | 29 | 0 | 1 | +6.9% | -7.054 | -11.2 | 2 | resp_msg[13] - 27* | 0 | 177 | 29 | 0 | 2 | +6.9% | -6.540 | -8.9 | 2 | resp_msg[13] - 28* | 0 | 180 | 30 | 0 | 3 | +7.1% | -5.601 | -7.0 | 2 | resp_msg[13] - 29* | 0 | 181 | 30 | 0 | 3 | +7.1% | -5.231 | -6.2 | 2 | resp_msg[13] - 30* | 0 | 183 | 31 | 0 | 3 | +7.2% | -4.551 | -4.9 | 2 | resp_msg[13] - 31* | 0 | 185 | 31 | 0 | 3 | +7.2% | -3.914 | -3.9 | 1 | resp_msg[13] - 32* | 0 | 185 | 33 | 0 | 4 | +7.6% | -3.112 | -3.2 | 2 | resp_msg[13] - 33* | 0 | 186 | 33 | 0 | 4 | +7.6% | -2.977 | -3.0 | 1 | resp_msg[13] - 34* | 0 | 187 | 33 | 0 | 4 | +7.6% | -2.919 | -2.9 | 1 | resp_msg[13] - 35* | 0 | 187 | 33 | 0 | 4 | +7.6% | -2.919 | -2.9 | 1 | resp_msg[13] - 36* | 0 | 188 | 33 | 0 | 4 | +7.6% | -2.859 | -2.9 | 1 | resp_msg[13] - 37* | 0 | 188 | 33 | 0 | 5 | +7.6% | -2.450 | -2.5 | 1 | resp_msg[13] - 38* | 0 | 188 | 33 | 0 | 5 | +7.6% | -2.450 | -2.5 | 1 | resp_msg[13] - 39* | 0 | 188 | 33 | 0 | 5 | +7.6% | -2.450 | -2.5 | 1 | resp_msg[13] - final | 0 | 188 | 33 | 0 | 5 | +7.6% | -2.450 | -2.5 | 1 | resp_msg[13] + 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 33 buffers, 1 to split loads. -[INFO RSZ-0051] Resized 188 instances: 22 up, 0 up match, 8 down, 158 VT +[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 dd9356ede8b..40ab4b7e471 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 | 174 | 27 | 0 | 0 | +6.5% | -10.182 | -10.2 | 1 | resp_msg[13] + final | 0 | 165 | 28 | 0 | 0 | +6.9% | -10.113 | -10.1 | 1 | resp_msg[13] ------------------------------------------------------------------------------------------------------------------------------ -[INFO RSZ-0045] Inserted 27 buffers, 1 to split loads. -[INFO RSZ-0051] Resized 174 instances: 22 up, 0 up match, 0 down, 152 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 -10.182 -tns max -10.182 +worst slack max -10.113 +tns max -10.113 diff --git a/src/rsz/test/repair_setup_parallel_eval_max_util.ok b/src/rsz/test/repair_setup_parallel_eval_max_util.ok index 78bd9f3ef8b..2fbc060b570 100644 --- a/src/rsz/test/repair_setup_parallel_eval_max_util.ok +++ b/src/rsz/test/repair_setup_parallel_eval_max_util.ok @@ -12,11 +12,11 @@ Design area 894 um^2 1% utilization. 0* | 0 | 0 | 0 | 0 | 0 | +0.0% | -0.253 | -2.5 | 28 | inst13/D 1* | 26 | 2 | 3 | 0 | 0 | -2.7% | -0.195 | -1.2 | 17 | inst13/D 2* | 27 | 7 | 7 | 0 | 0 | -1.8% | -0.180 | -0.7 | 12 | inst13/D - final | 27 | 10 | 9 | 0 | 0 | -1.3% | -0.170 | -0.6 | 10 | inst13/D + final | 27 | 11 | 8 | 0 | 0 | -1.2% | -0.173 | -0.6 | 10 | inst13/D ------------------------------------------------------------------------------------------------------------------------------ [INFO RSZ-0059] Removed 27 buffers. -[INFO RSZ-0040] Inserted 9 buffers. -[INFO RSZ-0051] Resized 10 instances: 10 up, 0 up match, 0 down, 0 VT +[INFO RSZ-0040] Inserted 8 buffers. +[INFO RSZ-0051] Resized 11 instances: 11 up, 0 up match, 0 down, 0 VT [WARNING RSZ-0062] Unable to repair all setup violations. Design area 883 um^2 1% utilization. -utilization 1.4096% +utilization 1.4109% 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 85c343eb369..baef0c902ef 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 | 55 | 86 | 17 | 0 | 2 | +3.4% | -0.004 | -0.0 | 14 | _866_/D + final | 54 | 94 | 18 | 0 | 1 | +3.8% | -0.007 | -0.0 | 11 | _880_/D ------------------------------------------------------------------------------------------------------------------------------ -[INFO RSZ-0059] Removed 55 buffers. -[INFO RSZ-0040] Inserted 17 buffers. -[INFO RSZ-0051] Resized 86 instances: 80 up, 0 up match, 6 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) _866_/D (DFF_X1) -0.0036 +_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 67acd3d6a32..84c96a16f4f 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 22 (WNS 1.908e-11) - final | 0 | 200 | 34 | 0 | 9 | +8.3% | 26.874 | 0.0 | 12 | resp_msg[13] +[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-0045] Inserted 34 buffers, 1 to split loads. -[INFO RSZ-0051] Resized 200 instances: 30 up, 0 up match, 4 down, 166 VT -[INFO RSZ-0043] Swapped pins on 9 instances. +[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 26.874 +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 20eed98c759..ff9b3ddfdd1 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 22 (WNS -1.270e-10) - final | 58 | 121 | 25 | 0 | 3 | +5.7% | -0.118 | -3.5 | 44 | 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 25 buffers. -[INFO RSZ-0051] Resized 121 instances: 109 up, 0 up match, 12 down, 0 VT -[INFO RSZ-0043] Swapped pins on 3 instances. +[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.