Skip to content

Commit d46c844

Browse files
authored
Merge pull request #10642 from calewis/gpl-placement-perturbation
[GPL] Add randomized initial placement perturbation in GPL
2 parents 16b2727 + 3280e93 commit d46c844

85 files changed

Lines changed: 84011 additions & 83745 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/gpl/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ cc_library(
8181
"//src/utl",
8282
"@abseil-cpp//absl/container:inlined_vector",
8383
"@boost.polygon",
84+
"@boost.random",
8485
"@coin-or-lemon//:lemon",
8586
"@eigen",
8687
"@openmp",

src/gpl/README.md

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,9 +104,11 @@ global_placement
104104
[-pad_right pad_right]\
105105
[-disable_revert_if_diverge]\
106106
[-disable_pin_density_adjust]\
107-
[-enable_routing_congestion]
108-
[-virtual_cts]
109-
[-virtual_cts_max_skew_fraction virtual_cts_max_skew_fraction]
107+
[-enable_routing_congestion]\
108+
[-virtual_cts]\
109+
[-virtual_cts_max_skew_fraction virtual_cts_max_skew_fraction]\
110+
[-random_seed random_seed]\
111+
[-perturb_dist perturb_dist]
110112
```
111113

112114
#### Options
@@ -135,6 +137,8 @@ global_placement
135137
| `-enable_routing_congestion` | Flag to run global routing after global placement, enabling the Routing Congestion Heatmap.|
136138
| `-virtual_cts` | Flag to build a lightweight virtual clock tree during global placement. Clock tree is used to compute clock network latency per clock sink to model clock skew during timing-driven placement. Virtual CTS runs before each timing-driven iteration. |
137139
| `-virtual_cts_max_skew_fraction` | Set max insertion delay as fraction of clock period. Valid range is [0, 1]; out-of-range values are clamped. Default is 0.10 (10%). |
140+
| `-random_seed` | Set random seed for placement perturbation. Perturbation shifts standard cells by random offsets drawn from a 2D circular Gaussian distribution at the start of global placement to explore layout variations. Default is `1`. |
141+
| `-perturb_dist` | Set the perturbation distance (radius) in nanometers. Under a 2D circular Gaussian distribution, 99.5% of the cell perturbations will fall within this radius. If not provided (or <= 0), it defaults to 0.5 microns or one standard cell height (row height), whichever is smaller. |
138142

139143
#### Initial-Placement Arguments
140144

src/gpl/doc/TclCommands.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ global_placement
1414
[-initial_place_max_iter max_iter]
1515
[-initial_place_max_fanout max_fanout]
1616
[-verbose_level verbose_level]
17-
17+
[-random_seed random_seed]
18+
[-perturb_dist perturb_dist]
1819
```
1920

2021
## Flow Control
@@ -31,6 +32,8 @@ global_placement
3132

3233
## Other Options
3334
* __verbose_level__ [0-10, int] : Set verbose level for RePlAce. Default: 1
35+
* __random_seed__ [int] : Set random seed for placement perturbation. Perturbation shifts standard cells by random offsets drawn from a 2D circular Gaussian distribution at the start of global placement. Default is 1.
36+
* __perturb_dist__ [float] : Set the perturbation distance (radius) in nanometers. Under a 2D circular Gaussian distribution, 99.5% of the cell perturbations will fall within this radius. If not provided (or <= 0), it defaults to 0.5 microns or one standard cell height (row height), whichever is smaller.
3437

3538
Note that all of the TCL commands are defined in the
3639
[../src/replace.tcl](https://github.com/The-OpenROAD-Project/OpenROAD/blob/master/src/gpl/src/replace.tcl)

src/gpl/include/gpl/Replace.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ struct PlaceOptions
8484
int binGridCntX = 0;
8585
int binGridCntY = 0;
8686
float density = 0.7;
87+
int initialPlacePerturbationSeed = 1;
88+
float initialPlacePerturbationDist = -1.0f;
8789

8890
float routabilityCheckOverflow = 0.3;
8991
float routabilitySnapshotOverflow = 0.6;

src/gpl/src/nesterovBase.cpp

Lines changed: 60 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,14 @@
1515
#include <iterator>
1616
#include <memory>
1717
#include <optional>
18-
#include <random>
1918
#include <string>
2019
#include <unordered_map>
2120
#include <unordered_set>
2221
#include <utility>
2322
#include <vector>
2423

2524
#include "boost/polygon/polygon.hpp"
25+
#include "boost/random/normal_distribution.hpp"
2626
#include "fft.h"
2727
#include "gpl/Replace.h"
2828
#include "nesterovPlace.h"
@@ -1080,7 +1080,9 @@ NesterovBaseVars::NesterovBaseVars(const PlaceOptions& options)
10801080
binCntX(isSetBinCnt ? options.binGridCntX : 0),
10811081
binCntY(isSetBinCnt ? options.binGridCntY : 0),
10821082
minPhiCoef(options.minPhiCoef),
1083-
maxPhiCoef(options.maxPhiCoef)
1083+
maxPhiCoef(options.maxPhiCoef),
1084+
initialPlacePerturbationSeed(options.initialPlacePerturbationSeed),
1085+
initialPlacePerturbationDist(options.initialPlacePerturbationDist)
10841086
{
10851087
}
10861088

@@ -1941,6 +1943,58 @@ void NesterovBaseCommon::reportInstanceExtensionByPinDensity() const
19411943
}
19421944
}
19431945

1946+
// Calculate a random initial placement perturbation offset.
1947+
// Standard cells are shifted by random offsets drawn from a 2D circular
1948+
// Gaussian distribution.
1949+
//
1950+
// The displacement is generated by sampling independent normal distributions
1951+
// in X and Y with standard deviation sigma:
1952+
// sigma = r_dbu * sqrt(-1 / (2 * ln(1 - P)))
1953+
//
1954+
// where r_dbu is the target perturbation radius in DBUs, and P is the target
1955+
// probability (99.5%) of a cell falling within the circle of radius r_dbu.
1956+
// This Rayleigh-distributed radius has:
1957+
// sigma \approx 0.3071963263271184 * r_dbu.
1958+
//
1959+
// The target perturbation radius defaults to min(site_y, 0.5 micron) if not
1960+
// explicitly set by -perturb_dist.
1961+
std::pair<int, int> NesterovBase::calculatePlacementPerturbationOffset(
1962+
int dbu_per_micron) const
1963+
{
1964+
int site_y = pb_->getSiteSizeY();
1965+
if (site_y <= 0) {
1966+
return {0, 0};
1967+
}
1968+
1969+
// If perturbation distance is explicitly set to 0, skip perturbation.
1970+
if (nbVars_.initialPlacePerturbationDist == 0.0f) {
1971+
return {0, 0};
1972+
}
1973+
1974+
double r_dbu = 0.0;
1975+
if (nbVars_.initialPlacePerturbationDist > 0.0f) {
1976+
r_dbu = nbVars_.initialPlacePerturbationDist * (dbu_per_micron / 1000.0);
1977+
} else {
1978+
r_dbu = std::min(0.5 * dbu_per_micron, static_cast<double>(site_y));
1979+
}
1980+
1981+
// To ensure that 99.5% of the perturbations fall within a circle of radius
1982+
// r_dbu, we use a Rayleigh distribution for the radial distance R = sqrt(X^2
1983+
// + Y^2). CDF(r) = 1 - exp(-r^2 / (2 * sigma^2)) = 0.995 => sigma = r_dbu *
1984+
// sqrt(-1 / (2 * ln(0.005))) sigma \approx 0.3071963263271184 * r_dbu.
1985+
const double sigma = r_dbu * 0.3071963263271184;
1986+
1987+
boost::random::normal_distribution<double> dist(0.0, sigma);
1988+
1989+
double dx = dist(generator_);
1990+
double dy = dist(generator_);
1991+
1992+
int x_offset = std::round(dx);
1993+
int y_offset = std::round(dy);
1994+
1995+
return {x_offset, y_offset};
1996+
}
1997+
19441998
////////////////////////////////////////////////
19451999
// NesterovBase
19462000

@@ -1951,7 +2005,7 @@ NesterovBase::NesterovBase(
19512005
// NOLINTNEXTLINE(performance-unnecessary-value-param)
19522006
std::shared_ptr<NesterovBaseCommon> nbc,
19532007
utl::Logger* log)
1954-
: nbVars_(nbVars)
2008+
: nbVars_(nbVars), generator_(nbVars.initialPlacePerturbationSeed)
19552009
{
19562010
pb_ = std::move(pb);
19572011
nbc_ = std::move(nbc);
@@ -1961,8 +2015,6 @@ NesterovBase::NesterovBase(
19612015
"---- Initialize Nesterov Region: {}",
19622016
pb_->getGroup() ? pb_->getGroup()->getName() : "Top-level");
19632017

1964-
// Set a fixed seed
1965-
srand(42);
19662018
// area update from pb
19672019
stdInstsArea_ = pb_->stdInstsArea();
19682020
macroInstsArea_ = pb_->macroInstsArea();
@@ -1976,8 +2028,8 @@ NesterovBase::NesterovBase(
19762028

19772029
// add place instances
19782030
for (auto& pb_inst : pb_->placeInsts()) {
1979-
int x_offset = rand() % (2 * dbu_per_micron) - dbu_per_micron;
1980-
int y_offset = rand() % (2 * dbu_per_micron) - dbu_per_micron;
2031+
auto [x_offset, y_offset]
2032+
= calculatePlacementPerturbationOffset(dbu_per_micron);
19812033

19822034
GCell* gCell = nbc_->pbToNb(pb_inst);
19832035
if (pb_inst != gCell->insts()[0]) {
@@ -2244,7 +2296,7 @@ void NesterovBase::initFillerGCells()
22442296
// mt19937 supports huge range of random values.
22452297
// rand()'s RAND_MAX is only 32767.
22462298
//
2247-
std::mt19937 randVal(0);
2299+
boost::random::mt19937 randVal(0);
22482300
for (int i = 0; i < fillerCnt; i++) {
22492301
// instability problem between g++ and clang++!
22502302
auto randX = randVal();

src/gpl/src/nesterovBase.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include <variant>
2020
#include <vector>
2121

22+
#include "boost/random/mersenne_twister.hpp"
2223
#include "boost/unordered/unordered_flat_map.hpp"
2324
#include "gpl/Replace.h"
2425
#include "odb/db.h"
@@ -755,6 +756,8 @@ struct NesterovBaseVars
755756

756757
const float minPhiCoef;
757758
float maxPhiCoef; // may be updated after initialization
759+
const int initialPlacePerturbationSeed;
760+
const float initialPlacePerturbationDist;
758761

759762
static constexpr float minWireLengthForceBar = -300;
760763
};
@@ -1149,11 +1152,15 @@ class NesterovBase
11491152

11501153
odb::dbGroup* getGroup() const { return pb_->getGroup(); }
11511154

1155+
std::pair<int, int> calculatePlacementPerturbationOffset(
1156+
int dbu_per_micron) const;
1157+
11521158
private:
11531159
NesterovBaseVars nbVars_;
11541160
std::shared_ptr<PlacerBase> pb_;
11551161
std::shared_ptr<NesterovBaseCommon> nbc_;
11561162
utl::Logger* log_ = nullptr;
1163+
mutable boost::random::mt19937 generator_;
11571164

11581165
BinGrid bg_;
11591166
std::unique_ptr<FFT> fft_;

src/gpl/src/replace.i

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@ static gpl::PlaceOptions getOptions(
8282
checkKey(keys,
8383
"-timing_driven_repair_tns_end_percent",
8484
options.timingDrivenRepairTnsEndPercent);
85+
checkKey(keys, "-random_seed", options.initialPlacePerturbationSeed);
86+
checkKey(keys, "-perturb_dist", options.initialPlacePerturbationDist);
8587

8688
if (auto it = keys.find("-density"); it != keys.end()) {
8789
if (it->second == "uniform") {

src/gpl/src/replace.tcl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ sta::define_cmd_args "global_placement" {\
3939
[-pad_right pad_right]\
4040
[-disable_revert_if_diverge]\
4141
[-disable_pin_density_adjust]\
42+
[-random_seed random_seed]\
43+
[-perturb_dist perturb_dist]\
4244
[-enable_routing_congestion]
4345
}
4446

@@ -61,6 +63,8 @@ proc global_placement { args } {
6163
-timing_driven_repair_tns_end_percent \
6264
-keep_resize_below_overflow \
6365
-virtual_cts_max_skew_fraction \
66+
-random_seed \
67+
-perturb_dist \
6468
-pad_left -pad_right} \
6569
flags {-skip_initial_place \
6670
-force_center_initial_place \

0 commit comments

Comments
 (0)