Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion src/rsz/include/rsz/Resizer.hh
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -974,7 +977,9 @@ class Resizer : public sta::dbStaState, public sta::dbNetworkObserver
};

// Capture fanin max-cap state once before screening equivalent cells.
std::vector<InputCapBudget> inputCapBudgets(sta::Instance* inst);
// The pins that skipsFaninMaxCap() exempts get no budget.
std::vector<InputCapBudget> inputCapBudgets(sta::Instance* inst,
const sta::Mode* mode = nullptr);
// Test a replacement using captured headroom without new STA queries.
bool replacementFitsInputCapBudgets(
const std::vector<InputCapBudget>& budgets,
Expand Down
26 changes: 26 additions & 0 deletions src/rsz/src/OptimizationPolicyConfig.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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
16 changes: 12 additions & 4 deletions src/rsz/src/OptimizationPolicyConfig.hh
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,10 @@ struct OptimizationPolicyConfig
{
static OptimizationPolicyConfig forParallelEval(RepairEffort effort);

// Weight of a move's score in the PARALLEL_EVAL shared pool; 1 for a move
// without one.
float scoreScale(MoveType type) const;

// --- Delay estimator ------------------------------------------------------

// Stages scored before and after the target stage; zero scores only the
Expand Down Expand Up @@ -230,10 +234,14 @@ struct OptimizationPolicyConfig
// PARALLEL_EVAL turns the guard off.
bool dual_rf_drive_guard{true};

// Weight of the SizeUpMt score in the shared pool; VtSwapMt keeps 1.
// Empirically tuned. A size-up costs more than a VT swap of similar gain
// (area, input pin cap, legalizer displacement, side paths), so it ranks
// lower.
// Weights of the VtSwapMt and SizeUpMt scores in the shared pool.
// Empirically tuned together with the relative cell cost of the two moves
// (relativeCellCosts()). A size-up costs more than a VT swap of similar
// gain (area, input pin cap, legalizer displacement, side paths), so it
// ranks lower. The relative cost compares a VT swap only with the other VT
// options, so the cheapest faster VT pays nothing although it adds far more
// leakage than a size-up; a VtSwapMt weight above 1 would favor it further.
float vt_swap_mt_score_scale{1.0f};
float size_up_mt_score_scale{0.75f};

// Largest drive-resistance ratio (current / replacement) of a size-up;
Expand Down
24 changes: 24 additions & 0 deletions src/rsz/src/OptimizerTypes.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@

#include "OptimizerTypes.hh"

#include <algorithm>
#include <functional>
#include <set>
#include <string>
#include <vector>

#include "rsz/Resizer.hh"
#include "sta/Delay.hh"
Expand Down Expand Up @@ -209,6 +211,28 @@ const sta::TimingArc* findMatchingTimingArc(const sta::TimingArc* reference,
reference, candidate, ArcMatchMode::kExact, nullptr);
}

std::vector<float> relativeCellCosts(const std::vector<float>& 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<float> 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)
{
Expand Down
9 changes: 9 additions & 0 deletions src/rsz/src/OptimizerTypes.hh
Original file line number Diff line number Diff line change
Expand Up @@ -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<float> relativeCellCosts(const std::vector<float>& cost_ratios);

// === Target preparation flags ==============================================

// The target-level data the generators read, prepared for every target of a
Expand Down
51 changes: 32 additions & 19 deletions src/rsz/src/Resizer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8449,27 +8449,10 @@ bool Resizer::replacementPreservesMaxCap(sta::Instance* inst,
continue;
}

// Skip the max-cap check on a register clock pin only when its clock is
// ideal: the clock net's capacitance is irrelevant to timing then.
if (network_->isRegClkPin(pin) && mode != nullptr
&& mode->clkNetwork()->isIdealClock(pin)) {
if (skipsFaninMaxCap(pin, mode)) {
continue;
}

// Skip the max-cap check on scan-enable/scan-in pins because they
// tend to be high fanout pins
if (db_network_ != nullptr) {
odb::dbITerm* iterm = nullptr;
odb::dbBTerm* bterm = nullptr;
odb::dbModITerm* moditerm = nullptr;
db_network_->staToDb(pin, iterm, bterm, moditerm);
if (iterm != nullptr
&& (db_network_->isScanEnable(iterm)
|| db_network_->isScanIn(iterm))) {
continue;
}
}

const float old_cap
= inputPinCapacitance(network_, pin, current_cell, max_);
const float new_cap = inputPinCapacitance(network_, pin, replacement, max_);
Expand All @@ -8487,8 +8470,33 @@ bool Resizer::replacementPreservesMaxCap(sta::Instance* inst,
return true;
}

bool Resizer::skipsFaninMaxCap(const sta::Pin* pin, const sta::Mode* mode)
{
// Skip the max-cap check on a register clock pin only when its clock is
// ideal: the clock net's capacitance is irrelevant to timing then.
if (network_->isRegClkPin(pin) && mode != nullptr
&& mode->clkNetwork()->isIdealClock(pin)) {
return true;
}

// Skip the max-cap check on scan-enable/scan-in pins because they
// tend to be high fanout pins
if (db_network_ != nullptr) {
odb::dbITerm* iterm = nullptr;
odb::dbBTerm* bterm = nullptr;
odb::dbModITerm* moditerm = nullptr;
db_network_->staToDb(pin, iterm, bterm, moditerm);
if (iterm != nullptr
&& (db_network_->isScanEnable(iterm) || db_network_->isScanIn(iterm))) {
return true;
}
}
return false;
}

std::vector<Resizer::InputCapBudget> Resizer::inputCapBudgets(
sta::Instance* inst)
sta::Instance* inst,
const sta::Mode* mode)
{
std::vector<InputCapBudget> budgets;
sta::LibertyCell* current_cell = network_->libertyCell(inst);
Expand All @@ -8510,6 +8518,11 @@ std::vector<Resizer::InputCapBudget> 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;
Expand Down
5 changes: 2 additions & 3 deletions src/rsz/src/move/MoveCandidate.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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";
Expand Down
3 changes: 1 addition & 2 deletions src/rsz/src/move/MoveCandidate.hh
Original file line number Diff line number Diff line change
Expand Up @@ -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 ===================================================
Expand Down
32 changes: 32 additions & 0 deletions src/rsz/src/move/MoveGenerator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@

#include "MoveGenerator.hh"

#include <optional>
#include <string>
#include <tuple>

#include "db_sta/dbNetwork.hh"
#include "odb/db.h"
#include "rsz/Resizer.hh"
#include "sta/Delay.hh"
#include "sta/Liberty.hh"
Expand All @@ -14,6 +17,35 @@

namespace rsz {

MoveGenerator::CellCosts MoveGenerator::cellCosts(sta::LibertyCell* cell) const
{
CellCosts costs;
const std::optional<float> 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<float>(master->getArea());
}
if (cell->area() > 0.0f) {
costs[2] = cell->area();
}
return costs;
}

std::optional<float> MoveGenerator::cellCostRatio(const CellCosts& from_costs,
sta::LibertyCell* to) const
{
const CellCosts to_costs = cellCosts(to);
for (size_t metric = 0; metric < from_costs.size(); ++metric) {
if (from_costs[metric] && to_costs[metric]) {
return *to_costs[metric] / *from_costs[metric];
}
}
return std::nullopt;
}

const sta::LibertyPort* MoveGenerator::findScenePort(
const sta::LibertyCell* cell,
const std::string& port_name,
Expand Down
19 changes: 17 additions & 2 deletions src/rsz/src/move/MoveGenerator.hh
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@

#pragma once

#include <array>
#include <cstddef>
#include <memory>
#include <optional>
#include <string>
#include <unordered_set>
#include <vector>
Expand Down Expand Up @@ -148,15 +150,28 @@ class MoveGenerator
const sta::RiseFall* drvr_rf,
const sta::MinMax* min_max) const;

// === Shared cell-cost helpers ============================================
// Cell costs in the order SizeUpGenerator::upsizeCell tries them: leakage,
// then LEF area, then Liberty area. An unavailable metric is nullopt.
// Main thread only: cellLeakage() fills a cache.
using CellCosts = std::array<std::optional<float>, 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<float> cellCostRatio(const CellCosts& from_costs,
sta::LibertyCell* to) const;

// === Shared fanin max-cap helper =========================================
// The cell-swap generators' fanin max-cap headroom, captured by their
// prepareInputs(): the STA capacitance check behind it is main-thread only.
// The second cell-swap generator of a target reuses it.
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<Resizer::InputCapBudget>& inputCapBudgets(
Expand Down
9 changes: 3 additions & 6 deletions src/rsz/src/move/RebufferMtCandidate.cc
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,11 @@ RebufferMtCandidate::RebufferMtCandidate(
Resizer& resizer,
const Target& target,
const RebufferTreePlan& plan,
const float score_scale,
const float min_score,
std::vector<BufferTreeSidePathContext> side_contexts)
: MoveCandidate(resizer, target),
plan_(plan),
side_contexts_(std::move(side_contexts)),
score_scale_(score_scale),
min_score_(min_score)
{
}
Expand All @@ -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;
}
Expand Down Expand Up @@ -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,
Expand Down
Loading
Loading