Skip to content
Open
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
37bf65b
grt/cugr: model via demand from real via enclosure geometry
eder-matheus Jul 2, 2026
836430a
grt/cugr: update CUGR test goldens for geometry-based via demand
eder-matheus Jul 2, 2026
512a2b6
grt/cugr: add via_geometry_cugr regression test
eder-matheus Jul 2, 2026
87eba10
grt/cugr: fall back to drt-style via selection when no default via
eder-matheus Jul 2, 2026
48cf181
grt/cugr: harden via-demand spacing fallback, warn on missing via, dr…
eder-matheus Jul 2, 2026
d5fd3d3
grt/cugr: rank candidate vias by priority instead of arbitrary db-ord…
eder-matheus Jul 3, 2026
ba01782
grt/cugr: add via_priority_cugr test for db-order-independent via ran…
eder-matheus Jul 3, 2026
1d75d37
grt/cugr: union all via boxes per layer for the demand footprint
eder-matheus Jul 3, 2026
c8ec166
grt/cugr: add via_multirect_cugr test for compound-via footprint union
eder-matheus Jul 3, 2026
6ac1f5e
grt/cugr: include Layers.h directly for MetalLayer (include-cleaner)
eder-matheus Jul 3, 2026
3209d3f
grt/cugr: ceil perpendicular track blockage in via demand
eder-matheus Jul 3, 2026
63bd0c5
grt/cugr: update CUGR goldens for integer via track blockage
eder-matheus Jul 3, 2026
19820be
grt/cugr: prefer single-cut vias before LEF default in via ranking
eder-matheus Jul 4, 2026
abdeb88
grt/cugr: extend via_priority test to cover single-cut over default m…
eder-matheus Jul 4, 2026
835ac63
grt/cugr: check written guides in via geometry tests
eder-matheus Jul 6, 2026
58b735b
grt/cugr: tidy via-demand comments and fix stale wording
eder-matheus Jul 6, 2026
352c666
Merge branch 'master' of https://github.com/The-OpenROAD-Project/Open…
eder-matheus Jul 6, 2026
b18ebbc
Merge branch 'master' of https://github.com/The-OpenROAD-Project/Open…
eder-matheus Jul 6, 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
143 changes: 143 additions & 0 deletions src/grt/src/cugr/src/Design.cpp
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
#include "Design.h"

#include <cmath>
#include <cstdint>
#include <iostream>
#include <set>
#include <tuple>
#include <utility>
#include <vector>

#include "CUGR.h"
#include "GeoTypes.h"
#include "Layers.h"
#include "Netlist.h"
#include "db_sta/dbSta.hh"
#include "odb/PtrSetMap.h"
Expand Down Expand Up @@ -57,6 +60,8 @@ void Design::read()

computeGrid();

computeViaDemandLengths();

if (verbose_) {
logger_->report("Design statistics");
logger_->report("Nets: {}", nets_.size());
Expand Down Expand Up @@ -347,6 +352,144 @@ void Design::setUnitCosts()
}
}

odb::dbTechVia* Design::chooseViaForPair(odb::dbTechLayer* lower_tl,
odb::dbTechLayer* upper_tl) const
{
// Select the via whose footprint best approximates what drt will place.
// odb::dbBlock::getDefaultVias fabricates a default from the first via in db
// order when no OR_DEFAULT is set, which is arbitrary; instead rank all vias
// connecting the pair: prefer an OR_DEFAULT via, then a LEF-default one, then
// fewest cuts, then the smallest metal enclosure (mimicking drt's priority in
// io_parser_helper.cpp).
odb::dbTechLayer* cut_tl = lower_tl->getUpperLayer();
odb::dbTechVia* best = nullptr;
std::tuple<bool, bool, int, int64_t> best_key;
for (odb::dbTechVia* via : tech_->getVias()) {
if (via->getBottomLayer() != lower_tl || via->getTopLayer() != upper_tl) {
continue;
}
int cuts = 0;
int64_t enc_area = 0;
for (odb::dbBox* box : via->getBoxes()) {
odb::dbTechLayer* bl = box->getTechLayer();
if (bl == cut_tl) {
cuts++;
} else if (bl == lower_tl || bl == upper_tl) {
enc_area += box->getBox().area();
}
}
const bool or_default
= odb::dbStringProperty::find(via, "OR_DEFAULT") != nullptr;
const std::tuple<bool, bool, int, int64_t> key{
!or_default, !via->isDefault(), cuts, enc_area};
Comment thread
eder-matheus marked this conversation as resolved.
Outdated
if (best == nullptr || key < best_key) {
best = via;
best_key = key;
}
}
return best;
}

void Design::computeViaDemandLengths()
{
const int num_layers = getNumLayers();
// Legacy proxy: min-area stub length inflated by the flat via_multiplier.
// Used as the fallback when the tech has no default via for a layer pair.
via_demand_length_lower_.assign(num_layers, 0.0);
via_demand_length_upper_.assign(num_layers, 0.0);

const bool debug = logger_->debugCheck(utl::GRT, "via_geom", 1);
int fallback_pairs = 0;
for (int i = 0; i + 1 < num_layers; i++) {
const MetalLayer& lower = layers_[i];
const MetalLayer& upper = layers_[i + 1];
odb::dbTechLayer* lower_tl = lower.getTechLayer();
odb::dbTechLayer* upper_tl = upper.getTechLayer();
odb::dbTechVia* via = chooseViaForPair(lower_tl, upper_tl);
if (via == nullptr) {
fallback_pairs++;
}

double num_lower = lower.getMinLength() * constants_.via_multiplier;
double num_upper = upper.getMinLength() * constants_.via_multiplier;
// Union all boxes on each routing layer: a via may have several rects on a
// layer, and the footprint is their combined extent, not the last one.
odb::Rect lo_box, up_box;
lo_box.mergeInit();
up_box.mergeInit();
if (via != nullptr) {
for (odb::dbBox* box : via->getBoxes()) {
if (box->getTechLayer() == lower_tl) {
lo_box.merge(box->getBox());
} else if (box->getTechLayer() == upper_tl) {
up_box.merge(box->getBox());
}
}
if (!lo_box.isInverted() && lo_box.dx() > 0 && lo_box.dy() > 0) {
num_lower = viaDemandLength(lower, lo_box.dx(), lo_box.dy());
}
if (!up_box.isInverted() && up_box.dx() > 0 && up_box.dy() > 0) {
num_upper = viaDemandLength(upper, up_box.dx(), up_box.dy());
}
}
via_demand_length_lower_[i] = num_lower;
via_demand_length_upper_[i] = num_upper;

if (debug) {
// Enclosure sizes are the via pad extents (x*y DBU); demand is the
// per-via fraction of one track for a uniform gcell.
const int gcell = default_gridline_spacing_;
const std::string via_src
= via != nullptr ? via->getName() : std::string("min_area-fallback");
debugPrint(
logger_,
utl::GRT,
"via_geom",
1,
"via {}->{} [{}]: encl lower={}x{} upper={}x{} -> len "
"lower={:.1f} upper={:.1f} -> demand lower={:.4f} upper={:.4f}",
lower.getName(),
upper.getName(),
via_src,
lo_box.isInverted() ? 0 : lo_box.dx(),
lo_box.isInverted() ? 0 : lo_box.dy(),
up_box.isInverted() ? 0 : up_box.dx(),
up_box.isInverted() ? 0 : up_box.dy(),
num_lower,
num_upper,
gcell > 0 ? num_lower / gcell : 0.0,
gcell > 0 ? num_upper / gcell : 0.0);
}
}

if (fallback_pairs > 0) {
logger_->warn(utl::GRT,
173,
"{} layer pair(s) have no via; using min-area via demand.",
fallback_pairs);
}
}

double Design::viaDemandLength(const MetalLayer& layer,
const int dx,
const int dy) const
{
const int pitch = layer.getPitch();
// getSpacing() is 0 on techs that give spacing only via a parallel table;
// fall back to the table-derived default so the spacing term is not lost.
const int spacing
= layer.getSpacing() > 0 ? layer.getSpacing() : layer.getDefaultSpacing();
// Split the pad into extent along the routing direction and across tracks.
const int along = (layer.getDirection() == MetalLayer::H) ? dx : dy;
const int perp = (layer.getDirection() == MetalLayer::H) ? dy : dx;
// A via blocks whole tracks perpendicular to the routing direction; round the
// keep-out (via metal + spacing on both sides) up to an integer track count.
const double tracks_blocked
= pitch > 0 ? std::ceil(static_cast<double>(perp + 2 * spacing) / pitch)
: 1.0;
return along * tracks_blocked;
}

void Design::getAllObstacles(std::vector<std::vector<BoxT>>& all_obstacles,
const bool skip_m1) const
{
Expand Down
27 changes: 27 additions & 0 deletions src/grt/src/cugr/src/Design.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ class dbDatabase;
class dbITerm;
class dbNet;
class dbTech;
class dbTechLayer;
class dbTechVia;
} // namespace odb

namespace sta {
Expand Down Expand Up @@ -56,6 +58,16 @@ class Design
{
return layers_[layer_index];
}
// Effective via demand length (enclosure_along * tracks_blocked) for the via
// between routing layers i and i+1, charged to the lower/upper layer.
double getViaDemandLengthLower(int i) const
{
return via_demand_length_lower_[i];
}
double getViaDemandLengthUpper(int i) const
{
return via_demand_length_upper_[i];
}

// For global routing
const std::vector<std::vector<int>>& getGridlines() const
Expand Down Expand Up @@ -85,6 +97,16 @@ class Design
void readDesignObstructions();
void computeGrid();
void setUnitCosts();
// Fill via_demand_length_{lower,upper}_ from the tech's default vias.
void computeViaDemandLengths();
// Pick the via connecting two routing layers, ranked to approximate drt:
// OR_DEFAULT, then LEF-default, then fewest cuts, then smallest enclosure.
odb::dbTechVia* chooseViaForPair(odb::dbTechLayer* lower_tl,
odb::dbTechLayer* upper_tl) const;
// Effective demand length of a via pad on one layer: its enclosure extent
// along the routing direction scaled by the number of tracks it blocks
// across.
double viaDemandLength(const MetalLayer& layer, int dx, int dy) const;

// debug functions
void printNets() const;
Expand All @@ -93,6 +115,11 @@ class Design
int lib_dbu_;
BoxT die_region_;
std::vector<MetalLayer> layers_;
// Indexed by the via's lower routing-layer; lower_ is charged to layer i,
// upper_ to layer i+1. Falls back to min_length * via_multiplier when the
// tech exposes no default via for the pair.
std::vector<double> via_demand_length_lower_;
std::vector<double> via_demand_length_upper_;
std::vector<CUGRNet> nets_;
std::unordered_map<odb::dbNet*, int> db_net_to_id_;
std::vector<BoxOnLayer> obstacles_;
Expand Down
25 changes: 17 additions & 8 deletions src/grt/src/cugr/src/GridGraph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,10 @@ GridGraph::GridGraph(const Design* design,

layer_names_.resize(num_layers_);
layer_directions_.resize(num_layers_);
layer_min_lengths_.resize(num_layers_);
for (int layer_index = 0; layer_index < num_layers_; layer_index++) {
const auto& layer = design->getLayer(layer_index);
layer_names_[layer_index] = layer.getName();
layer_directions_[layer_index] = layer.getDirection();
layer_min_lengths_[layer_index] = layer.getMinLength();
// First non-zero sheet/via resistance is the res-aware cost reference.
if (ref_resistance_ <= 0.0 && layer.getResistance() > 0.0) {
ref_resistance_ = layer.getResistance();
Expand Down Expand Up @@ -295,6 +293,19 @@ GridGraph::GridGraph(const Design* design,
}
}

CapacityT GridGraph::viaDemand(const int layer_index,
const int l,
const int edge_sum) const
{
// Design digested the via geometry into an effective length per layer role;
// spread it over the two flanking edges (lower role for the via's lower
// layer, upper role for the upper layer).
const double via_num = (l == layer_index)
? design_->getViaDemandLengthLower(layer_index)
: design_->getViaDemandLengthUpper(layer_index);
return edge_sum > 0 ? (CapacityT) via_num / edge_sum : (CapacityT) 0;
}

void GridGraph::computeCongestionInformation()
{
if (!congestion_info_dirty_) {
Expand Down Expand Up @@ -508,9 +519,8 @@ CostT GridGraph::getViaCost(const int layer_index,

// Prevent division by zero
if (lower_edge_length > 0 || higher_edge_length > 0) {
const CapacityT demand = (CapacityT) layer_min_lengths_[l]
/ (lower_edge_length + higher_edge_length)
* constants_.via_multiplier;
const CapacityT demand
= viaDemand(layer_index, l, lower_edge_length + higher_edge_length);
const double layer_factor
= std::cmp_less(l, net_costs.size()) ? net_costs[l] : 1.0;
if (lower_edge_length > 0) {
Expand Down Expand Up @@ -870,9 +880,8 @@ void GridGraph::commitVia(const int layer_index,

// Prevent division by zero
if (lower_edge_length > 0 || higher_edge_length > 0) {
const CapacityT demand = (CapacityT) layer_min_lengths_[l]
/ (lower_edge_length + higher_edge_length)
* constants_.via_multiplier;
const CapacityT demand
= viaDemand(layer_index, l, lower_edge_length + higher_edge_length);
// Use the per-layer NDR factor for `l`, not a net-wide value.
const double layer_factor
= std::cmp_less(l, net_costs.size()) ? net_costs[l] : 1.0;
Expand Down
4 changes: 3 additions & 1 deletion src/grt/src/cugr/src/GridGraph.h
Original file line number Diff line number Diff line change
Expand Up @@ -354,13 +354,15 @@ class GridGraph
void commitTree(const std::shared_ptr<GRTreeNode>& tree,
bool rip_up = false,
const std::vector<double>& net_costs = {});
// Per-via demand on layer `l` (lower or upper role) of the via at
// `layer_index`, spread over its two flanking edges (`edge_sum`).
CapacityT viaDemand(int layer_index, int l, int edge_sum) const;

utl::Logger* logger_;
const std::vector<std::vector<int>> gridlines_;
std::vector<std::vector<int>> grid_centers_;
std::vector<std::string> layer_names_;
std::vector<int> layer_directions_;
std::vector<int> layer_min_lengths_;

const int lib_dbu_;
const int m2_pitch_;
Expand Down
2 changes: 2 additions & 0 deletions src/grt/src/cugr/src/Layers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@ namespace grt {
MetalLayer::MetalLayer(odb::dbTechLayer* tech_layer,
odb::dbTrackGrid* track_grid)
{
tech_layer_ = tech_layer;
name_ = tech_layer->getName();
index_ = tech_layer->getRoutingLevel() - 1;
direction_
= tech_layer->getDirection() == odb::dbTechLayerDir::HORIZONTAL ? H : V;
width_ = tech_layer->getWidth();
min_width_ = tech_layer->getMinWidth();
spacing_ = tech_layer->getSpacing();

// Sheet R and the via-cut R above (for the res-aware cost); 0 when the
// tech leaves them undefined.
Expand Down
5 changes: 5 additions & 0 deletions src/grt/src/cugr/src/Layers.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ class MetalLayer
int getDirection() const { return direction_; }
int getWidth() const { return width_; }
int getPitch() const { return pitch_; }
// Min spacing to a neighbour on this layer; used for the via-demand model.
int getSpacing() const { return spacing_; }
odb::dbTechLayer* getTechLayer() const { return tech_layer_; }
// Sheet resistance (ohms/square) of this routing layer.
double getResistance() const { return resistance_; }
// Per-cut resistance (ohms) of the via layer just above this routing
Expand All @@ -39,11 +42,13 @@ class MetalLayer
void setAdjustment(float adjustment) { adjustment_ = adjustment; }

private:
odb::dbTechLayer* tech_layer_ = nullptr;
std::string name_;
int index_;
int direction_;
int width_;
int min_width_;
int spacing_ = 0;
double resistance_ = 0.0;
double via_resistance_ = 0.0;

Expand Down
3 changes: 3 additions & 0 deletions src/grt/test/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,9 @@ TESTS = [
"tracks3",
"unplaced_inst",
"upper_layer_net",
"via_geometry_cugr",
"via_multirect_cugr",
"via_priority_cugr",
"write_segments1",
"write_segments2",
]
Expand Down
3 changes: 3 additions & 0 deletions src/grt/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,9 @@ or_integration_tests(
tracks3
unplaced_inst
upper_layer_net
via_geometry_cugr
via_multirect_cugr
via_priority_cugr
write_segments1
write_segments2
PASSFAIL_TESTS
Expand Down
8 changes: 4 additions & 4 deletions src/grt/test/clock_route_cugr.ok
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,13 @@ Bottleneck: (5, 0, 0)
Layer Resource Demand Usage (%) Max H / Max V / Total Congestion
---------------------------------------------------------------------------------------
li1 0 0 0.00% 0 / 0 / 0
met1 27555 74 0.27% 0 / 0 / 0
met1 27555 68 0.25% 0 / 0 / 0
met2 21571 63 0.29% 0 / 0 / 0
met3 14023 25 0.18% 0 / 0 / 0
met4 10804 13 0.12% 0 / 0 / 0
met3 14023 24 0.17% 0 / 0 / 0
met4 10804 12 0.11% 0 / 0 / 0
met5 3108 0 0.00% 0 / 0 / 0
---------------------------------------------------------------------------------------
Total 77061 175 0.23% 0 / 0 / 0
Total 77061 167 0.22% 0 / 0 / 0

[INFO GRT-0018] Total wirelength: 1641 um
[INFO GRT-0014] Routed nets: 15
Expand Down
Loading
Loading