Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
e57891b
grt/cugr: add resistance aware flag and wire it to GlobalRouter
eder-matheus Jun 3, 2026
ff36a11
grt/cugr: add resistance information to Layer class
eder-matheus Jun 3, 2026
e3c6689
grt/cugr: add resistance aware fields to GRNet
eder-matheus Jun 3, 2026
ad2f0f6
grt/cugr: add res-aware constants and variables
eder-matheus Jun 3, 2026
d0d0e3f
grt/cugr: add functions to compute res-aware score and mark nets as r…
eder-matheus Jun 4, 2026
f842dcd
grt/cugr: mark nets as res-aware and sort the nets according to res-a…
eder-matheus Jun 4, 2026
7dd958d
grt/cugr: add functions to compute wire and via resistance costs
eder-matheus Jun 4, 2026
c42f17e
grt/cugr: add functions to compute net resistance and net length
eder-matheus Jun 4, 2026
73c779c
grt/cugr: introduce resistance costs to PatternRoute routing costs
eder-matheus Jun 4, 2026
52b90c3
grt/cugr: add new unit tests for res-aware implementation
eder-matheus Jun 4, 2026
466a155
grt/cugr: reduce res-aware cost from 500 to 50 for better tradeoff be…
eder-matheus Jun 4, 2026
54375d8
grt/cugr: codex reviews
eder-matheus Jun 4, 2026
89801f1
grt/cugr: set default critical_nets_percentage_ to 10
eder-matheus Jun 4, 2026
3b8536b
grt/cugr: don't mark guides as congested when CUGR produces them
eder-matheus Jun 4, 2026
7045c4c
grt/cugr: tclfmt
eder-matheus Jun 4, 2026
0af4cdb
grt/cugr: small memory saving
eder-matheus Jun 5, 2026
24896b6
grt/cugr: small fixes
eder-matheus Jun 5, 2026
b3e2db3
grt/cugr: gemini reviews
eder-matheus Jun 5, 2026
9d0e94a
grt/cugr: use correct width when computing resistance aware score
eder-matheus Jun 5, 2026
0a6794d
grt: clang-tidy
eder-matheus Jun 8, 2026
40b7629
Merge branch 'master' of https://github.com/The-OpenROAD-Project/Open…
eder-matheus Jun 11, 2026
a5eb9da
grt/cugr: add nullptr guards
eder-matheus Jun 11, 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
8 changes: 7 additions & 1 deletion src/grt/src/GlobalRouter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,9 @@ void GlobalRouter::globalRoute(bool save_guides)
odb::PtrSet<odb::dbNet> clock_nets;
findClockNets(nets, clock_nets);
cugr_->setCongestionIterations(congestion_iterations_);
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 @@ -2392,6 +2395,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 @@ -2988,7 +2992,9 @@ 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_;
// CUGR can produce congested guides that DRT can handle, resulting
// on DRC free final routing.
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 @@ -59,6 +59,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 @@ -93,6 +100,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 @@ -134,6 +145,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 @@ -173,9 +186,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 @@ -224,9 +234,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
136 changes: 136 additions & 0 deletions src/grt/src/cugr/src/CUGR.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ void CUGR::init(const int min_routing_layer,
}
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 @@ -128,6 +129,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 @@ -168,6 +174,91 @@ 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_ || 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_) {
if (net == nullptr) {
continue;
}
const auto& tree = net->getRoutingTree();
const float resistance
= grid_graph_->getNetResistance(tree, net->getNdrWidths());
Comment thread
eder-matheus marked this conversation as resolved.
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_) {
if (net == nullptr) {
continue;
}
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_) {
if (net == nullptr) {
continue;
}
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 @@ -204,6 +295,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 {};
}
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 @@ -361,6 +477,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 @@ -680,6 +803,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) {
if (gr_nets_[lhs] == nullptr || gr_nets_[rhs] == nullptr) {
return false;
Expand Down
34 changes: 34 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,24 @@ 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];
}

const std::vector<int>& getNdrWidths() const { return ndr_widths_; }

/**
* @brief Checks whether the net has an active demand-scaling NDR.
*
Expand All @@ -98,6 +125,9 @@ class GRNet
{
soft_ndr_ = true;
std::ranges::fill(ndr_costs_, 1.0);
// Drop the NDR width too, so the res-aware cost reverts to the default
// wire width after demotion (keeps getNdrWidth in sync with hasNdr).
std::ranges::fill(ndr_widths_, 0);
}

bool isSoftNdr() const { return soft_ndr_; }
Expand Down Expand Up @@ -128,7 +158,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