Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
00374c1
grt/cugr: add resistance aware flag and wire it to GlobalRouter
eder-matheus Jun 3, 2026
5b9bf5e
grt/cugr: add resistance information to Layer class
eder-matheus Jun 3, 2026
b3d9c76
grt/cugr: add resistance aware fields to GRNet
eder-matheus Jun 3, 2026
3ee0883
grt/cugr: add res-aware constants and variables
eder-matheus Jun 3, 2026
9fc8ff5
grt/cugr: add functions to compute res-aware score and mark nets as r…
eder-matheus Jun 4, 2026
9dc4716
grt/cugr: mark nets as res-aware and sort the nets according to res-a…
eder-matheus Jun 4, 2026
4a877b3
grt/cugr: add functions to compute wire and via resistance costs
eder-matheus Jun 4, 2026
6ebaba0
grt/cugr: add functions to compute net resistance and net length
eder-matheus Jun 4, 2026
17f1289
grt/cugr: introduce resistance costs to PatternRoute routing costs
eder-matheus Jun 4, 2026
65cb30d
grt/cugr: add new unit tests for res-aware implementation
eder-matheus Jun 4, 2026
d74fbaa
Merge branch 'master' of https://github.com/The-OpenROAD-Project/Open…
eder-matheus Jun 4, 2026
798a90e
Merge branch 'master' of https://github.com/The-OpenROAD-Project/Open…
eder-matheus Jun 4, 2026
3ea68ca
grt/cugr: reduce res-aware cost from 500 to 50 for better tradeoff be…
eder-matheus Jun 4, 2026
ca56f50
grt/cugr: codex reviews
eder-matheus Jun 4, 2026
bed7a29
grt/cugr: set default critical_nets_percentage_ to 10
eder-matheus Jun 4, 2026
773ad05
grt/cugr: don't mark guides as congested when CUGR produces them
eder-matheus Jun 4, 2026
8e74248
grt/cugr: tclfmt
eder-matheus Jun 4, 2026
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/grt/src/GlobalRouter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,10 @@ void GlobalRouter::globalRoute(bool save_guides)
odb::PtrSet<odb::dbNet> clock_nets;
findClockNets(nets, clock_nets);
cugr_->setCongestionIterations(congestion_iterations_);
cugr_->setResistanceAware(resistance_aware_);
if (sta_->getDbNetwork()->defaultLibertyLibrary() == nullptr) {
cugr_->setCriticalNetsPercentage(0);
}
Comment thread
eder-matheus marked this conversation as resolved.
cugr_->init(min_layer, max_layer, clock_nets);
if (verbose_) {
reportResources();
Expand Down Expand Up @@ -2393,6 +2397,7 @@ void GlobalRouter::setResistanceAware(bool resistance_aware)
{
resistance_aware_ = resistance_aware;
fastroute_->setResistanceAware(resistance_aware);
cugr_->setResistanceAware(resistance_aware);
}

void GlobalRouter::setMacroExtension(int macro_extension)
Expand Down Expand Up @@ -2989,7 +2994,7 @@ void GlobalRouter::saveGuides(const std::vector<odb::dbNet*>& nets)
int offset_x = grid_origin_.x();
int offset_y = grid_origin_.y();

bool guide_is_congested = is_congested_ && !allow_congestion_;
bool guide_is_congested = is_congested_ && !allow_congestion_ && !use_cugr_;

int net_with_jumpers, total_jumpers;
net_with_jumpers = 0;
Expand Down
38 changes: 34 additions & 4 deletions src/grt/src/cugr/include/CUGR.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,13 @@ struct Constants

double maze_logistic_slope = 0.5;

// Min net length (bbox hp) for res-aware; short nets are skipped
// because the via cost of climbing exceeds their wire-R savings.
int resistance_min_net_length = 3;

// Scales the res-aware resistance cost to CUGR's wire-cost magnitude.
double resistance_weight = 50.0;

double pin_patch_threshold = 20.0;
int pin_patch_padding = 1;
double wire_patch_threshold = 2.0;
Expand Down Expand Up @@ -92,6 +99,10 @@ class CUGR
{
critical_nets_percentage_ = percentage;
}
void setResistanceAware(bool resistance_aware)
{
resistance_aware_ = resistance_aware;
}
void setCongestionIterations(int iterations)
{
congestion_iterations_ = iterations;
Expand Down Expand Up @@ -132,6 +143,8 @@ class CUGR
* at all).
*/
std::vector<double> computeNdrCosts(odb::dbNet* db_net) const;

std::vector<int> computeNdrWidths(odb::dbNet* db_net) const;
/**
* @brief Builds the rip-up set of nets touching a congested edge.
*
Expand Down Expand Up @@ -171,9 +184,6 @@ class CUGR
* cost. Emits `GRT-0117` per iteration and `GRT-0118` if overflow
* remains when the loop ends.
*
* See `src/grt/doc/01-iterative-rrr.md` for the cost-model audit
* and the rationale for the chosen defaults.
*
* @param net_indices Reused scratch buffer (cleared on entry by
* `updateCongestedNets`).
*/
Expand Down Expand Up @@ -222,9 +232,29 @@ class CUGR
int area_of_pin_patches_ = 0;
int area_of_wire_patches_ = 0;

float critical_nets_percentage_ = 0;
float critical_nets_percentage_ = 10;
int congestion_iterations_ = 5;

bool resistance_aware_ = false;
// Per-run normalisers for getResAwareScore (default 1 => well-defined).
float worst_slack_ = 1.0f;
float worst_resistance_ = 1.0f;
int worst_fanout_ = 1;
int worst_net_length_ = 1;

// The initial PatternRoute pass marks a wider critical set because its
// placement slack is noisier than the routing slack used later.
static constexpr float kPatternRouteWiden = 2.0f;

// Marks the top-`percentage` >= 2-pin nets res-aware and refreshes the
// per-net resistance/length + worst_* ordering normalisers. No-op
// unless resistance_aware_.
void markResAwareNets(float percentage);

// FR-style ordering score (lower routes first): slack/resistance/
// fanout/length blend, each normalised by the per-run worst.
float getResAwareScore(const GRNet* net) const;

std::vector<int> nets_to_route_;
};

Expand Down
126 changes: 126 additions & 0 deletions src/grt/src/cugr/src/CUGR.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ void CUGR::init(const int min_routing_layer,
for (const CUGRNet& base_net : base_nets) {
gr_nets_.push_back(std::make_unique<GRNet>(base_net, grid_graph_.get()));
gr_nets_.back()->setNdrCosts(computeNdrCosts(base_net.getDbNet()));
gr_nets_.back()->setNdrWidths(computeNdrWidths(base_net.getDbNet()));
net_indices_.push_back(index);
db_net_map_[base_net.getDbNet()] = gr_nets_.back().get();
index++;
Expand All @@ -119,6 +120,11 @@ float CUGR::calculatePartialSlack()
net->setSlack(slack);
}

// Re-mark res-aware nets using this (routing) slack. Must run here:
// the loop below overwrites non-critical nets' slack with a large
// value for sorting, so the real slack is only available now.
markResAwareNets(critical_nets_percentage_);

std::ranges::stable_sort(slacks);

// Find the slack threshold based on the percentage of critical nets
Expand Down Expand Up @@ -153,6 +159,81 @@ void CUGR::setInitialNetSlacks()
float slack = getNetSlack(net->getDbNet());
net->setSlack(slack);
}
// PatternRoute uses (noisier) placement slack, so widen the critical
// set; later passes re-mark and tighten with routing slack.
markResAwareNets(
std::min(100.0f, critical_nets_percentage_ * kPatternRouteWiden));
}

void CUGR::markResAwareNets(const float percentage)
{
if (!resistance_aware_) {
return;
}
Comment on lines +170 to +172
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

If percentage is 0.0f, the function still executes and will mark the worst net(s) as critical/res-aware because threshold_index becomes 0 and slack_th is set to slacks[0]. This contradicts the warning in CUGR::route() which states that -resistance_aware has no effect when -critical_nets_percentage is 0. We should early return if percentage <= 0.0f.

  if (!resistance_aware_ || percentage <= 0.0f) {
    return;
  }


// Per-net resistance/length + worst_* normalisers.
worst_slack_ = std::numeric_limits<float>::max();
worst_resistance_ = 1.0f;
worst_fanout_ = 1;
worst_net_length_ = 1;

for (const auto& net : gr_nets_) {
const auto& tree = net->getRoutingTree();
const float resistance = grid_graph_->getNetResistance(tree);
net->setResistance(resistance);
// Routed-tree planar length when available; bbox half-perimeter is
// only a fallback before the first route exists (PatternRoute).
net->setNetLength(tree ? grid_graph_->getTreeLength(tree)
: net->getBoundingBox().hp());
worst_resistance_ = std::max(worst_resistance_, resistance);
worst_fanout_ = std::max(worst_fanout_, net->getNumPins());
worst_net_length_ = std::max(worst_net_length_, net->getNetLength());
worst_slack_ = std::min(worst_slack_, net->getSlack());
}

// Mark the top-`percentage` nets by the same percentile slack threshold
// as calculatePartialSlack.
std::vector<float> slacks;
slacks.reserve(gr_nets_.size());
for (const auto& net : gr_nets_) {
slacks.push_back(net->getSlack());
}
std::ranges::stable_sort(slacks);
const int threshold_index = std::ceil(slacks.size() * percentage / 100);
const float slack_th
= slacks.empty() ? 0.0f
: slacks[std::min(static_cast<size_t>(threshold_index),
slacks.size() - 1)];
for (const auto& net : gr_nets_) {
const bool multi_pin = net->getNumPins() >= 2;
// Skip short nets (FR's kShortNetThreshold), using the same length
// metric as the ordering score (routed-tree length, bbox hp pre-route).
const bool long_enough
= net->getNetLength() > constants_.resistance_min_net_length;
const bool is_critical
= net->getSlack() <= slack_th && net->getSlack() <= 0.0f;
net->setResAware(multi_pin && long_enough && is_critical);
}
}

float CUGR::getResAwareScore(const GRNet* net) const
{
constexpr float kSlackWeight = 4.0f;
constexpr float kResistanceWeight = 1.0f;
constexpr float kFanoutWeight = 3.0f;
constexpr float kNetLengthWeight = 2.0f;

// Adapted from FastRoute's getResAwareScore; lower = more critical.
const float slack_norm = std::max(std::abs(worst_slack_), 1e-9f);
const float slack_term = net->getSlack() / slack_norm * kSlackWeight;
const float resistance_term
= net->getResistance() / worst_resistance_ * kResistanceWeight;
const float fanout_term
= static_cast<float>(net->getNumPins()) / worst_fanout_ * kFanoutWeight;
const float length_term = static_cast<float>(net->getNetLength())
/ worst_net_length_ * kNetLengthWeight;

return slack_term - resistance_term - fanout_term - length_term;
}

std::vector<double> CUGR::computeNdrCosts(odb::dbNet* db_net) const
Expand Down Expand Up @@ -189,6 +270,31 @@ std::vector<double> CUGR::computeNdrCosts(odb::dbNet* db_net) const
return factors;
}

std::vector<int> CUGR::computeNdrWidths(odb::dbNet* db_net) const
{
const int num_layers = grid_graph_->getNumLayers();
// 0 => "use the layer default width" in getWireResistanceCost.
std::vector<int> widths(std::max(num_layers, 0), 0);
odb::dbTechNonDefaultRule* ndr = db_net->getNonDefaultRule();
if (ndr == nullptr) {
return widths;
}
std::vector<odb::dbTechLayerRule*> layer_rules;
ndr->getLayerRules(layer_rules);
for (odb::dbTechLayerRule* lr : layer_rules) {
odb::dbTechLayer* tl = lr->getLayer();
if (tl == nullptr || tl->getType() != odb::dbTechLayerType::ROUTING) {
continue;
}
const int layer_idx = tl->getRoutingLevel() - 1; // 0-based
if (layer_idx < 0 || layer_idx >= num_layers) {
continue;
}
widths[layer_idx] = lr->getWidth();
}
return widths;
}

void CUGR::updateCongestedNets(std::vector<int>& net_indices,
const double threshold)
{
Expand Down Expand Up @@ -325,6 +431,13 @@ void CUGR::mazeRoute(std::vector<int>& net_indices)

void CUGR::route()
{
if (resistance_aware_ && critical_nets_percentage_ == 0) {
logger_->warn(GRT,
702,
"-resistance_aware has no effect without a non-zero "
"-critical_nets_percentage; no nets are marked critical.");
}

std::vector<int> net_indices;
if (!nets_to_route_.empty()) {
net_indices = nets_to_route_;
Expand Down Expand Up @@ -632,6 +745,19 @@ NetRouteMap CUGR::getRoutes()

void CUGR::sortNetIndices(std::vector<int>& net_indices) const
{
if (resistance_aware_ && critical_nets_percentage_ != 0) {
// Multi-factor res-aware ordering (slack + resistance + fanout +
// length): critical nets route first so they get the best topology
// and first pick of upper-metal capacity. (Mimicking FR's full
// netpinOrderInc tuple — clock/NDR prefix + length/minX tiebreakers
// — was measured to regress the maze-engaged asap7 case, so only the
// score is used.)
std::ranges::stable_sort(net_indices, [&](int lhs, int rhs) {
return getResAwareScore(gr_nets_[lhs].get())
< getResAwareScore(gr_nets_[rhs].get());
});
return;
}
std::ranges::stable_sort(net_indices, [&](int lhs, int rhs) {
return std::make_tuple(gr_nets_[lhs]->getSlack(),
gr_nets_[lhs]->getBoundingBox().hp())
Expand Down
29 changes: 29 additions & 0 deletions src/grt/src/cugr/src/GRNet.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,15 @@ class GRNet
float getSlack() const { return slack_; }
void setCritical(bool is_critical) { is_critical_ = is_critical; }
bool isCritical() const { return is_critical_; }

// Resistance-aware state: the flag gates the PatternRoute cost term;
// resistance_/net_length_ feed getResAwareScore.
void setResAware(bool res_aware) { is_res_aware_ = res_aware; }
bool isResAware() const { return is_res_aware_; }
void setResistance(float resistance) { resistance_ = resistance; }
float getResistance() const { return resistance_; }
void setNetLength(int net_length) { net_length_ = net_length; }
int getNetLength() const { return net_length_; }
void clearRoutingTree() { routing_tree_ = nullptr; }
bool isInsideLayerRange(int layer_index) const;

Expand Down Expand Up @@ -73,6 +82,22 @@ class GRNet

const std::vector<double>& getNdrCosts() const { return ndr_costs_; }

void setNdrWidths(std::vector<int> widths)
{
ndr_widths_ = std::move(widths);
}

// Effective wire width (DBU) on a layer: the net's NDR width where the
// rule sets one, else 0 to signal "use the layer default".
int getNdrWidth(int layer_index) const
{
if (layer_index < 0
|| std::cmp_greater_equal(layer_index, ndr_widths_.size())) {
return 0;
}
return ndr_widths_[layer_index];
}

/**
* @brief Checks whether the net has an active demand-scaling NDR.
*
Expand Down Expand Up @@ -128,7 +153,11 @@ class GRNet
LayerRange layer_range_;
float slack_;
bool is_critical_;
bool is_res_aware_ = false;
float resistance_ = 0.0f;
int net_length_ = 0;
std::vector<double> ndr_costs_;
std::vector<int> ndr_widths_;
bool soft_ndr_ = false;
};

Expand Down
Loading
Loading