Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
4e06f0e
add basic nesterov placer
WilliamZhang20 Jun 17, 2026
782df47
Merge branch 'verilog-to-routing:master' into nesterov-placer
WilliamZhang20 Jun 17, 2026
3ddac80
improve nesterov a bit more
WilliamZhang20 Jun 18, 2026
8c5d66f
fix CI
WilliamZhang20 Jun 19, 2026
2ab3fa1
Merge branch 'verilog-to-routing:master' into nesterov-placer
WilliamZhang20 Jun 20, 2026
15da14d
separate global and analytical placers
WilliamZhang20 Jun 20, 2026
c789578
Merge branch 'nesterov-placer' of https://github.com/WilliamZhang20/v…
WilliamZhang20 Jun 20, 2026
b236069
Merge branch 'master' into nesterov-placer
WilliamZhang20 Jun 20, 2026
9c082b7
rename nesterov placer files
WilliamZhang20 Jun 20, 2026
d00ce21
document electrostatic density formulation
WilliamZhang20 Jun 21, 2026
f50fe6a
attempt to fix CI
WilliamZhang20 Jun 21, 2026
24a2db1
make code cleaner
WilliamZhang20 Jun 21, 2026
c7214f0
adjust algorithm to use full electrostatic, lagrangian, and precondit…
WilliamZhang20 Jun 25, 2026
8709492
fix MSVC build
WilliamZhang20 Jun 25, 2026
552f5e9
reach competitive QoR, very good runtime
WilliamZhang20 Jul 1, 2026
b586099
reach parity with b2b
WilliamZhang20 Jul 3, 2026
a018668
improve code, format
WilliamZhang20 Jul 6, 2026
1ee4530
bring overall geomeans down after clean slate
WilliamZhang20 Jul 8, 2026
a4cadbf
Merge branch 'master' into nesterov-placer
WilliamZhang20 Jul 8, 2026
4fade90
address commenting/doc reviews
WilliamZhang20 Jul 8, 2026
b40145d
fix pointers and comments
WilliamZhang20 Jul 8, 2026
50e000e
resolve timing and naming comments
WilliamZhang20 Jul 8, 2026
018fe03
improve runtime via caching
WilliamZhang20 Jul 9, 2026
3880e72
improve cpd on lu_network and runtime avg
WilliamZhang20 Jul 20, 2026
d9e0df3
fix gradient evaluations, improve density multipliers
WilliamZhang20 Jul 30, 2026
b0204df
fix gradient, extract density math to separate file so better maintai…
WilliamZhang20 Aug 2, 2026
72d4716
fix formatting
WilliamZhang20 Aug 3, 2026
f43e1f9
add ADMM and bring WL to around 0.98
WilliamZhang20 Aug 6, 2026
7814654
fix addm formulation and CI
WilliamZhang20 Aug 7, 2026
fb0c81d
expanding epochs and better descent can unlock breakthrough
WilliamZhang20 Aug 9, 2026
e11689d
update files for strong QoR improvement
WilliamZhang20 Aug 13, 2026
03ca7b2
clean up code more, include thread pool fix
WilliamZhang20 Aug 13, 2026
b1ab7ac
deliver 4% better WL, parity CPD, 6% better runtime
WilliamZhang20 Aug 15, 2026
5783544
improve comments, format
WilliamZhang20 Aug 18, 2026
205983f
Merge remote-tracking branch 'origin/master' into nesterov-placer
WilliamZhang20 Aug 18, 2026
6b9d5fa
big refactor to compress diff
WilliamZhang20 Aug 18, 2026
17c6aaa
fix CI
WilliamZhang20 Aug 18, 2026
474e707
remove hardcoding to fpga arch
WilliamZhang20 Aug 23, 2026
c0faf4f
fix CI again
WilliamZhang20 Aug 23, 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
17 changes: 16 additions & 1 deletion doc/src/vpr/command_line_usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1387,9 +1387,24 @@ When using a pre-computed flat placement file with the ``flat-recon`` full legal
``--analysis`` must be specified explicitly to run post-route timing analysis.
It is not implied by ``--route``.

.. option:: --ap_global_placer {simpl | nonlinear-nesterov}

Controls which Global Placer the AP Flow will use.

* ``simpl`` Alternates the selected analytical solver with partial
legalization. This is the default global placer.

* ``nonlinear-nesterov`` Runs an experimental nonlinear global placer that
applies accelerated first-order updates to a smooth wirelength and density
objective before invoking the selected partial legalizer.

**Default:** ``simpl``

.. option:: --ap_analytical_solver {identity | qp-hybrid | lp-b2b}

Controls which Analytical Solver the Global Placer will use in the AP Flow.
Controls which Analytical Solver the SimPL Global Placer will use in the AP
Flow. This option has no effect when the nonlinear Nesterov global placer is
selected.
The Analytical Solver solves for a placement which optimizes some objective
function, ignorant of the FPGA legality constraints. This provides a "lower-
bound" solution. The Global Placer will legalize this solution and feed it
Expand Down
13 changes: 10 additions & 3 deletions libs/libvtrutil/src/vtr_thread_pool.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,12 @@ class thread_pool {
for (size_t i = 0; i < thread_count; i++) {
auto thread_data = std::make_unique<ThreadData>();

thread_data->thread = std::thread([&]() {
ThreadData* td = thread_data.get();

// Capture the ThreadData pointer by value. Capturing the local
// unique_ptr by reference races with the std::move below: the
// thread may dereference the moved-from (or already destroyed)
// local, which crashes at pool construction.
ThreadData* td = thread_data.get();
thread_data->thread = std::thread([td]() {
while (true) {
std::function<void()> task;

Expand Down Expand Up @@ -125,6 +128,10 @@ class thread_pool {

size_t remaining = --active_tasks;
if (remaining == 0) {
// Take the completion mutex before notifying, otherwise the
// notification can fire between a waiter's predicate check and
// its sleep and be lost, deadlocking wait_for_all().
std::lock_guard<std::mutex> lock(completion_mutex);
completion_cv.notify_all();
}
};
Expand Down
3 changes: 2 additions & 1 deletion vpr/src/analytical_place/analytical_placement_flow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,8 @@ static PartialPlacement run_global_placer(const t_ap_opts& ap_opts,
return p_placement;
} else {
// Run the Global Placer
std::unique_ptr<GlobalPlacer> global_placer = make_global_placer(ap_opts.analytical_solver_type,
std::unique_ptr<GlobalPlacer> global_placer = make_global_placer(ap_opts.global_placer_type,
ap_opts.analytical_solver_type,
ap_opts.partial_legalizer_type,
ap_netlist,
prepacker,
Expand Down
18 changes: 14 additions & 4 deletions vpr/src/analytical_place/ap_flow_enums.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,27 @@
* @brief Enumerations used by the Analytical Placement Flow.
*/

/**
* @brief The type of a Global Placer.
*
* The Analytical Placement flow may implement different Global Placers. This
* enum can select between these different Global Placers.
*/
enum class e_ap_global_placer {
SimPL, ///< Global placer which alternates an analytical solver with partial legalization.
NonlinearNesterov, ///< Global placer which uses nonlinear Nesterov optimization of smooth wirelength and density objectives.
};

/**
* @brief The type of an Analytical Solver.
*
* The Analytical Placement flow may implement different Analytical Solvers as
* part of the Global Placer. This enum can select between these different
* Analytical Solvers.
* The SimPL Global Placer may implement different Analytical Solvers. This
* enum can select between these different Analytical Solvers.
*/
enum class e_ap_analytical_solver {
Identity, ///< The identity analytical solver, which does not perform any optimizations on the placement. Used as a placeholder when solving should not be used.
QP_Hybrid, ///< Analytical Solver which uses the hybrid net model to optimize the quadratic HPWL objective.
LP_B2B ///< Analytical Solver which uses the B2B net model to optimize the linear HPWL objective.
LP_B2B, ///< Analytical Solver which uses the B2B net model to optimize the linear HPWL objective.
};

/**
Expand Down
85 changes: 85 additions & 0 deletions vpr/src/analytical_place/global_placement/affinity_spring_term.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/**
* @file
* @brief Quadratic centroid affinity-spring objective term (see affinity_spring_term.h).
*/

#include "affinity_spring_term.h"

#include "partial_placement.h"
#include "preconditioner_math.h"
#include "vtr_assert.h"

using vtr::ap::affinity_spring_curvature;

AffinitySpringTerm::AffinitySpringTerm(const APNetlist& ap_netlist,
double io_pair_attraction_weight,
double pack_pattern_weight)
: io_pair_attraction_weight_(io_pair_attraction_weight)
, pack_pattern_weight_(pack_pattern_weight)
, moveable_(ap_netlist.blocks().size(), false) {
for (APBlockId blk_id : ap_netlist.blocks())
moveable_[blk_id] = ap_netlist.block_mobility(blk_id) == APBlockMobility::MOVEABLE;
}

double AffinitySpringTerm::kernel_weight_(e_affinity_kind kind) const {
switch (kind) {
case e_affinity_kind::IO_PAIR:
// Legacy I/O pair spring used grad += W * dx (no 1/n). Pack-math kernel
// uses W_kernel / n; for n=2 set W_kernel = 2W to preserve strength.
return 2. * io_pair_attraction_weight_;
case e_affinity_kind::PACK_PATTERN:
return pack_pattern_weight_;
default:
VTR_ASSERT_MSG(false, "Unhandled affinity kind");
return 0.;
}
}

double AffinitySpringTerm::evaluate(const PartialPlacement& p_placement,
std::optional<std::reference_wrapper<PlacementGradient>> grad) const {
if (groups_.empty())
return 0.;

double weighted_penalty = 0.;
for (const AffinityGroup& group : groups_) {
VTR_ASSERT_SAFE(group.blocks.size() >= 2);
double weight = kernel_weight_(group.kind);
if (weight == 0.)
continue;

double centroid_x = 0.;
double centroid_y = 0.;
for (APBlockId blk_id : group.blocks) {
centroid_x += p_placement.block_x_locs[blk_id];
centroid_y += p_placement.block_y_locs[blk_id];
}

double inv_group_size = 1. / static_cast<double>(group.blocks.size());
centroid_x *= inv_group_size;
centroid_y *= inv_group_size;

double unweighted = 0.;
for (APBlockId blk_id : group.blocks) {
double dx = p_placement.block_x_locs[blk_id] - centroid_x;
double dy = p_placement.block_y_locs[blk_id] - centroid_y;
unweighted += 0.5 * inv_group_size * (dx * dx + dy * dy);
if (grad && moveable_[blk_id]) {
grad->get().dx[blk_id] += weight * inv_group_size * dx;
grad->get().dy[blk_id] += weight * inv_group_size * dy;
}
}
weighted_penalty += weight * unweighted;
}
return weighted_penalty;
}

void AffinitySpringTerm::add_curvature(vtr::vector<APBlockId, double>& diagonal) const {
for (const AffinityGroup& group : groups_) {
double curvature = affinity_spring_curvature(kernel_weight_(group.kind),
group.blocks.size());
if (curvature == 0.)
continue;
for (APBlockId blk_id : group.blocks)
diagonal[blk_id] += curvature;
}
}
80 changes: 80 additions & 0 deletions vpr/src/analytical_place/global_placement/affinity_spring_term.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
#pragma once
/**
* @file
* @author William Zhang
* @date August 2026
* @brief Quadratic centroid affinity-spring objective term.
*
* Groups of AP blocks (direct output-driver↔outpad pairs, long prepacker
* chains) are pulled toward their group centroid by a quadratic spring. Group
* *detection* stays with its producers (the placer's io-pair/pack-pattern
* initialization); this term owns the groups' energy, gradient, and
* preconditioner contribution.
*/

#include <vector>
#include "objective_term.h"

/**
* @brief Affinity-spring detector kind (logging / per-kind weights).
*/
enum class e_affinity_kind {
IO_PAIR, ///< Direct 2-pin output-driver↔outpad pairs.
PACK_PATTERN ///< Long prepacker chain groups spanning multiple AP blocks.
};

/**
* @brief A set of AP blocks pulled together by a quadratic centroid spring.
*/
struct AffinityGroup {
e_affinity_kind kind = e_affinity_kind::IO_PAIR; ///< Detector that created this group.
std::vector<APBlockId> blocks; ///< AP blocks in the group (size >= 2).
};

class AffinitySpringTerm final : public ObjectiveTerm {
public:
/**
* @brief Construct with per-kind kernel inputs; precomputes block mobility.
*
* @param io_pair_attraction_weight Legacy per-block I/O pair spring strength
* (the kernel applies 2x for n=2 pack math).
* @param pack_pattern_weight Pack-pattern kernel weight; may later be
* zeroed via @ref set_pack_pattern_weight.
*/
AffinitySpringTerm(const APNetlist& ap_netlist,
double io_pair_attraction_weight,
double pack_pattern_weight);

void clear_groups() { groups_.clear(); }
void add_group(AffinityGroup group) { groups_.push_back(std::move(group)); }
const std::vector<AffinityGroup>& groups() const { return groups_; }

/// @brief Runtime gate: pack-pattern springs are disabled on designs
/// without long direct I/O-chain nets.
void set_pack_pattern_weight(double weight) { pack_pattern_weight_ = weight; }

const char* name() const final { return "affinity-springs"; }

/**
* @brief Weighted spring penalty; gradient only on moveable blocks.
*
* Penalty (unweighted): sum_b 0.5 / n * ||x_b - c||^2 per group.
*/
double evaluate(const PartialPlacement& p_placement,
std::optional<std::reference_wrapper<PlacementGradient>> grad) const final;

/**
* @brief Frozen-centroid curvature per group member (see
* affinity_spring_curvature() for why the exact (1 - 1/n) factor is
* not used).
*/
void add_curvature(vtr::vector<APBlockId, double>& diagonal) const final;

private:
double kernel_weight_(e_affinity_kind kind) const;

std::vector<AffinityGroup> groups_;
double io_pair_attraction_weight_ = 0.;
double pack_pattern_weight_ = 0.;
vtr::vector<APBlockId, bool> moveable_; ///< Block mobility, precomputed (static per netlist).
};
Loading
Loading