Skip to content

Commit b3d9694

Browse files
authored
Merge pull request #2944 from AlexandreSinger/feature-ap-solver
[AP][Solver] Supporting Unfixed Blocks
2 parents c9e6075 + b26c2d2 commit b3d9694

File tree

5 files changed

+194
-16
lines changed

5 files changed

+194
-16
lines changed

vpr/src/analytical_place/analytical_solver.cpp

+91-12
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
#include <memory>
1313
#include <utility>
1414
#include <vector>
15+
#include "device_grid.h"
16+
#include "flat_placement_types.h"
1517
#include "partial_placement.h"
1618
#include "ap_netlist.h"
1719
#include "vpr_error.h"
@@ -36,14 +38,16 @@
3638
#endif // EIGEN_INSTALLED
3739

3840
std::unique_ptr<AnalyticalSolver> make_analytical_solver(e_analytical_solver solver_type,
39-
const APNetlist& netlist) {
41+
const APNetlist& netlist,
42+
const DeviceGrid& device_grid) {
4043
// Based on the solver type passed in, build the solver.
4144
switch (solver_type) {
4245
case e_analytical_solver::QP_HYBRID:
4346
#ifdef EIGEN_INSTALLED
44-
return std::make_unique<QPHybridSolver>(netlist);
47+
return std::make_unique<QPHybridSolver>(netlist, device_grid);
4548
#else
4649
(void)netlist;
50+
(void)device_grid;
4751
VPR_FATAL_ERROR(VPR_ERROR_AP,
4852
"QP Hybrid Solver requires the Eigen library");
4953
break;
@@ -64,8 +68,11 @@ AnalyticalSolver::AnalyticalSolver(const APNetlist& netlist)
6468
// row ID from [0, num_moveable_blocks) for each moveable block in the
6569
// netlist.
6670
num_moveable_blocks_ = 0;
71+
num_fixed_blocks_ = 0;
6772
size_t current_row_id = 0;
6873
for (APBlockId blk_id : netlist.blocks()) {
74+
if (netlist.block_mobility(blk_id) == APBlockMobility::FIXED)
75+
num_fixed_blocks_++;
6976
if (netlist.block_mobility(blk_id) != APBlockMobility::MOVEABLE)
7077
continue;
7178
APRowId new_row_id = APRowId(current_row_id);
@@ -155,10 +162,10 @@ void QPHybridSolver::init_linear_system() {
155162
}
156163

157164
// Initialize the linear system with zeros.
158-
size_t num_variables = num_moveable_blocks_ + num_star_nodes;
159-
A_sparse = Eigen::SparseMatrix<double>(num_variables, num_variables);
160-
b_x = Eigen::VectorXd::Zero(num_variables);
161-
b_y = Eigen::VectorXd::Zero(num_variables);
165+
num_variables_ = num_moveable_blocks_ + num_star_nodes;
166+
A_sparse = Eigen::SparseMatrix<double>(num_variables_, num_variables_);
167+
b_x = Eigen::VectorXd::Zero(num_variables_);
168+
b_y = Eigen::VectorXd::Zero(num_variables_);
162169

163170
// Create a list of triplets that will be used to create the sparse
164171
// coefficient matrix. This is the method recommended by Eigen to initialize
@@ -254,7 +261,54 @@ void QPHybridSolver::update_linear_system_with_anchors(
254261
}
255262
}
256263

264+
void QPHybridSolver::init_guesses(const DeviceGrid& device_grid) {
265+
// If the number of fixed blocks is zero, initialized the guesses to the
266+
// center of the device.
267+
if (num_fixed_blocks_ == 0) {
268+
guess_x = Eigen::VectorXd::Constant(num_variables_, device_grid.width() / 2.0);
269+
guess_y = Eigen::VectorXd::Constant(num_variables_, device_grid.height() / 2.0);
270+
return;
271+
}
272+
273+
// Compute the centroid of all fixed blocks in the netlist.
274+
t_flat_pl_loc centroid({0.0f, 0.0f, 0.0f});
275+
unsigned num_blks_summed = 0;
276+
for (APBlockId blk_id : netlist_.blocks()) {
277+
// We only get the centroid of fixed blocks since these are the only
278+
// blocks with positions that we know.
279+
if (netlist_.block_mobility(blk_id) != APBlockMobility::FIXED)
280+
continue;
281+
// Get the flat location of the fixed block.
282+
APFixedBlockLoc fixed_blk_loc = netlist_.block_loc(blk_id);
283+
VTR_ASSERT_SAFE(fixed_blk_loc.x != APFixedBlockLoc::UNFIXED_DIM);
284+
VTR_ASSERT_SAFE(fixed_blk_loc.y != APFixedBlockLoc::UNFIXED_DIM);
285+
VTR_ASSERT_SAFE(fixed_blk_loc.layer_num != APFixedBlockLoc::UNFIXED_DIM);
286+
t_flat_pl_loc flat_blk_loc;
287+
flat_blk_loc.x = fixed_blk_loc.x;
288+
flat_blk_loc.y = fixed_blk_loc.y;
289+
flat_blk_loc.layer = fixed_blk_loc.layer_num;
290+
// Accumulate into the centroid.
291+
centroid += flat_blk_loc;
292+
num_blks_summed++;
293+
}
294+
// Divide the sum by the number of fixed blocks.
295+
VTR_ASSERT_SAFE(num_blks_summed == num_fixed_blocks_);
296+
centroid /= static_cast<float>(num_blks_summed);
297+
298+
// Set the guesses to the centroid location.
299+
guess_x = Eigen::VectorXd::Constant(num_variables_, centroid.x);
300+
guess_y = Eigen::VectorXd::Constant(num_variables_, centroid.y);
301+
}
302+
257303
void QPHybridSolver::solve(unsigned iteration, PartialPlacement& p_placement) {
304+
// In the first iteration, if the number of fixed blocks is 0, set the
305+
// placement to be equal to the guess. The solver below will just set the
306+
// solution to the zero vector if we do not set it to the guess directly.
307+
if (iteration == 0 && num_fixed_blocks_ == 0) {
308+
store_solution_into_placement(guess_x, guess_y, p_placement);
309+
return;
310+
}
311+
258312
// Create a temporary linear system which will contain the original linear
259313
// system which may be updated to include the anchor points.
260314
Eigen::SparseMatrix<double> A_sparse_diff = Eigen::SparseMatrix<double>(A_sparse);
@@ -280,14 +334,24 @@ void QPHybridSolver::solve(unsigned iteration, PartialPlacement& p_placement) {
280334
cg.compute(A_sparse_diff);
281335
VTR_ASSERT(cg.info() == Eigen::Success && "Conjugate Gradient failed at compute!");
282336
// Use the solver to solve for x and y using the constant vectors
283-
// TODO: Use solve with guess to make this faster. Use the previous placement
284-
// as a guess.
285-
Eigen::VectorXd x = cg.solve(b_x_diff);
337+
Eigen::VectorXd x = cg.solveWithGuess(b_x_diff, guess_x);
286338
VTR_ASSERT(cg.info() == Eigen::Success && "Conjugate Gradient failed at solving b_x!");
287-
Eigen::VectorXd y = cg.solve(b_y_diff);
339+
Eigen::VectorXd y = cg.solveWithGuess(b_y_diff, guess_y);
288340
VTR_ASSERT(cg.info() == Eigen::Success && "Conjugate Gradient failed at solving b_y!");
289341

290342
// Write the results back into the partial placement object.
343+
store_solution_into_placement(x, y, p_placement);
344+
345+
// Update the guess. The guess for the next iteration is the solution in
346+
// this iteration.
347+
guess_x = x;
348+
guess_y = y;
349+
}
350+
351+
void QPHybridSolver::store_solution_into_placement(const Eigen::VectorXd& x_soln,
352+
const Eigen::VectorXd& y_soln,
353+
PartialPlacement& p_placement) {
354+
291355
// NOTE: The first [0, num_moveable_blocks_) rows always represent the
292356
// moveable APBlocks. The star nodes always come after and are ignored
293357
// in the solution.
@@ -296,8 +360,23 @@ void QPHybridSolver::solve(unsigned iteration, PartialPlacement& p_placement) {
296360
APBlockId blk_id = row_id_to_blk_id_[row_id];
297361
VTR_ASSERT_DEBUG(blk_id.is_valid());
298362
VTR_ASSERT_DEBUG(netlist_.block_mobility(blk_id) == APBlockMobility::MOVEABLE);
299-
p_placement.block_x_locs[blk_id] = x[row_id_idx];
300-
p_placement.block_y_locs[blk_id] = y[row_id_idx];
363+
// Due to the iterative nature of CG, it is possible for the solver to
364+
// overstep 0 and return a negative number by an incredibly small margin.
365+
// Clamp the number to 0 in this case.
366+
// TODO: Should investigate good bounds on this, the bounds below were
367+
// chosen since any difference higher than 1e-9 would concern me.
368+
double x_pos = x_soln[row_id_idx];
369+
if (x_pos < 0.0) {
370+
VTR_ASSERT_SAFE(std::abs(x_pos) < negative_soln_tolerance_);
371+
x_pos = 0.0;
372+
}
373+
double y_pos = y_soln[row_id_idx];
374+
if (y_pos < 0.0) {
375+
VTR_ASSERT_SAFE(std::abs(y_pos) < negative_soln_tolerance_);
376+
y_pos = 0.0;
377+
}
378+
p_placement.block_x_locs[blk_id] = x_pos;
379+
p_placement.block_y_locs[blk_id] = y_pos;
301380
}
302381
}
303382

vpr/src/analytical_place/analytical_solver.h

+44-3
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
#pragma once
1010

1111
#include <memory>
12-
#include "ap_netlist_fwd.h"
12+
#include "ap_netlist.h"
13+
#include "device_grid.h"
1314
#include "vtr_strong_id.h"
1415
#include "vtr_vector.h"
1516

@@ -98,6 +99,9 @@ class AnalyticalSolver {
9899
/// when allocating matrices.
99100
size_t num_moveable_blocks_ = 0;
100101

102+
/// @brief The number of fixed blocks in the netlist.
103+
size_t num_fixed_blocks_ = 0;
104+
101105
/// @brief A lookup between a moveable APBlock and its linear ID from
102106
/// [0, num_moveable_blocks). Fixed blocks will return an invalid row
103107
/// ID. This is useful when knowing which row in the matrix
@@ -114,7 +118,8 @@ class AnalyticalSolver {
114118
* @brief A factory method which creates an Analytical Solver of the given type.
115119
*/
116120
std::unique_ptr<AnalyticalSolver> make_analytical_solver(e_analytical_solver solver_type,
117-
const APNetlist& netlist);
121+
const APNetlist& netlist,
122+
const DeviceGrid& device_grid);
118123

119124
// The Eigen library is used to solve matrix equations in the following solvers.
120125
// The solver cannot be built if Eigen is not installed.
@@ -170,6 +175,14 @@ class QPHybridSolver : public AnalyticalSolver {
170175
/// weights to grow slower.
171176
static constexpr double anchor_weight_exp_fac_ = 5.0;
172177

178+
/// @brief Due to the iterative nature of Conjugate Gradient method, the
179+
/// solver may overstep 0 to give a slightly negative solution. This
180+
/// is ok, and we can just clamp the position to 0. However, negative
181+
/// values that are too large may be indicative of an issue in the
182+
/// formulation. This value is how negative we tolerate the positions
183+
/// to be.
184+
static constexpr double negative_soln_tolerance_ = 1e-9;
185+
173186
/**
174187
* @brief Initializes the linear system of Ax = b_x and Ay = b_y based on
175188
* the APNetlist and the fixed APBlock locations.
@@ -180,6 +193,14 @@ class QPHybridSolver : public AnalyticalSolver {
180193
*/
181194
void init_linear_system();
182195

196+
/**
197+
* @brief Intializes the guesses which will be used in the solver.
198+
*
199+
* The guesses will be used as starting points for the CG solver. The better
200+
* these guesses are, the faster the solver will converge.
201+
*/
202+
void init_guesses(const DeviceGrid& device_grid);
203+
183204
/**
184205
* @brief Helper method to update the linear system with anchors to the
185206
* current partial placement.
@@ -209,6 +230,14 @@ class QPHybridSolver : public AnalyticalSolver {
209230
PartialPlacement& p_placement,
210231
unsigned iteration);
211232

233+
/**
234+
* @brief Store the x and y solutions in Eigen's vectors into the partial
235+
* placement object.
236+
*/
237+
void store_solution_into_placement(const Eigen::VectorXd& x_soln,
238+
const Eigen::VectorXd& y_soln,
239+
PartialPlacement& p_placement);
240+
212241
// The following variables represent the linear system without any anchor
213242
// points. These are filled in the constructor and never modified.
214243
// When the anchor-points are taken into consideration, the diagonal of the
@@ -224,19 +253,31 @@ class QPHybridSolver : public AnalyticalSolver {
224253
Eigen::VectorXd b_x;
225254
/// @brief The constant vector in the y dimension for the linear system.
226255
Eigen::VectorXd b_y;
256+
/// @brief The number of variables in the solver. This is the sum of the
257+
/// number of moveable blocks in the netlist and the number of star
258+
/// nodes that exist.
259+
size_t num_variables_ = 0;
260+
261+
/// @brief The current guess for the x positions of the blocks.
262+
Eigen::VectorXd guess_x;
263+
/// @brief The current guess for the y positions of the blocks.
264+
Eigen::VectorXd guess_y;
227265

228266
public:
229267
/**
230268
* @brief Constructor of the QPHybridSolver
231269
*
232270
* Initializes internal data and constructs the initial linear system.
233271
*/
234-
QPHybridSolver(const APNetlist& netlist)
272+
QPHybridSolver(const APNetlist& netlist, const DeviceGrid& device_grid)
235273
: AnalyticalSolver(netlist) {
236274
// Initializing the linear system only depends on the netlist and fixed
237275
// block locations. Both are provided by the netlist, allowing this to
238276
// be initialized in the constructor.
239277
init_linear_system();
278+
279+
// Initialize the guesses for the first iteration.
280+
init_guesses(device_grid);
240281
}
241282

242283
/**

vpr/src/analytical_place/global_placer.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,8 @@ SimPLGlobalPlacer::SimPLGlobalPlacer(e_partial_legalizer partial_legalizer_type,
7777
// Build the solver.
7878
VTR_LOGV(log_verbosity_ >= 10, "\tBuilding the solver...\n");
7979
solver_ = make_analytical_solver(e_analytical_solver::QP_HYBRID,
80-
ap_netlist_);
80+
ap_netlist_,
81+
device_grid);
8182

8283
// Build the density manager used by the partial legalizer.
8384
VTR_LOGV(log_verbosity_ >= 10, "\tBuilding the density manager...\n");
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
###############################################################################
2+
# Configuration file for running the MCNC benchmarks through the AP flow.
3+
#
4+
# The AP flow requires that each circuit contains fixed blocks and is fixed
5+
# to a specific device size. The device sizes here were chosen to match the
6+
# device sizes of the default VTR flow.
7+
###############################################################################
8+
9+
# Path to directory of circuits to use
10+
circuits_dir=benchmarks/verilog
11+
12+
# Path to directory of architectures to use
13+
archs_dir=arch/timing
14+
15+
# Add architectures to list to sweep
16+
arch_list_add=k6_frac_N10_frac_chain_mem32K_40nm.xml
17+
18+
# Add circuits to list to sweep
19+
circuit_list_add=boundtop.v
20+
circuit_list_add=ch_intrinsics.v
21+
circuit_list_add=or1200.v
22+
circuit_list_add=spree.v
23+
circuit_list_add=stereovision3.v
24+
25+
# Constrain the circuits to their devices
26+
circuit_constraint_list_add=(stereovision3.v, device=vtr_extra_small)
27+
circuit_constraint_list_add=(ch_intrinsics.v, device=vtr_extra_small)
28+
circuit_constraint_list_add=(spree.v, device=vtr_extra_small)
29+
circuit_constraint_list_add=(boundtop.v, device=vtr_extra_small)
30+
circuit_constraint_list_add=(or1200.v, device=vtr_small)
31+
32+
# Constrain the circuits to their channel widths
33+
# 1.3 * minW
34+
circuit_constraint_list_add=(stereovision3.v, route_chan_width=44)
35+
circuit_constraint_list_add=(ch_intrinsics.v, route_chan_width=52)
36+
circuit_constraint_list_add=(spree.v, route_chan_width=78)
37+
circuit_constraint_list_add=(boundtop.v, route_chan_width=50)
38+
circuit_constraint_list_add=(or1200.v, route_chan_width=118)
39+
40+
# Parse info and how to parse
41+
parse_file=vpr_fixed_chan_width.txt
42+
43+
# How to parse QoR info
44+
qor_parse_file=qor_ap_fixed_chan_width.txt
45+
46+
# Pass requirements
47+
pass_requirements_file=pass_requirements_ap_fixed_chan_width.txt
48+
49+
# Pass the script params while writing the vpr constraints.
50+
script_params=-track_memory_usage -crit_path_router_iterations 100 --analytical_place --route
51+

0 commit comments

Comments
 (0)