Skip to content

[AP][GlobalPlacement] Improved Partial Legalizer Legality #2942

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
45 changes: 10 additions & 35 deletions vpr/src/analytical_place/analytical_solver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -236,41 +236,17 @@ void QPHybridSolver::init_linear_system() {
A_sparse.setFromTriplets(tripletList.begin(), tripletList.end());
}

/**
* @brief Helper method to update the linear system with anchors to the current
* partial placement.
*
* For each moveable block (with row = i) in the netlist:
* A[i][i] = A[i][i] + coeff_pseudo_anchor;
* b[i] = b[i] + pos[block(i)] * coeff_pseudo_anchor;
* Where coeff_pseudo_anchor grows with each iteration.
*
* This is basically a fast way of adding a connection between all moveable
* blocks in the netlist and their target fixed placement location.
*
* See add_connection_to_system.
*
* @param A_sparse_diff The ceofficient matrix to update.
* @param b_x_diff The x-dimension constant vector to update.
* @param b_y_diff The y-dimension constant vector to update.
* @param p_placement The location the moveable blocks should be anchored
* to.
* @param num_moveable_blocks The number of moveable blocks in the netlist.
* @param row_id_to_blk_id Lookup for the row id from the APBlock Id.
* @param iteration The current iteration of the Global Placer.
*/
static inline void update_linear_system_with_anchors(Eigen::SparseMatrix<double>& A_sparse_diff,
Eigen::VectorXd& b_x_diff,
Eigen::VectorXd& b_y_diff,
PartialPlacement& p_placement,
size_t num_moveable_blocks,
vtr::vector<APRowId, APBlockId> row_id_to_blk_id,
unsigned iteration) {
void QPHybridSolver::update_linear_system_with_anchors(
Eigen::SparseMatrix<double>& A_sparse_diff,
Eigen::VectorXd& b_x_diff,
Eigen::VectorXd& b_y_diff,
PartialPlacement& p_placement,
unsigned iteration) {
// Anchor weights grow exponentially with iteration.
double coeff_pseudo_anchor = 0.01 * std::exp((double)iteration / 5);
for (size_t row_id_idx = 0; row_id_idx < num_moveable_blocks; row_id_idx++) {
double coeff_pseudo_anchor = anchor_weight_mult_ * std::exp((double)iteration / anchor_weight_exp_fac_);
for (size_t row_id_idx = 0; row_id_idx < num_moveable_blocks_; row_id_idx++) {
APRowId row_id = APRowId(row_id_idx);
APBlockId blk_id = row_id_to_blk_id[row_id];
APBlockId blk_id = row_id_to_blk_id_[row_id];
double pseudo_w = coeff_pseudo_anchor;
A_sparse_diff.coeffRef(row_id_idx, row_id_idx) += pseudo_w;
b_x_diff(row_id_idx) += pseudo_w * p_placement.block_x_locs[blk_id];
Expand All @@ -289,8 +265,7 @@ void QPHybridSolver::solve(unsigned iteration, PartialPlacement& p_placement) {
// anchor-points (fixed block positions).
if (iteration != 0) {
update_linear_system_with_anchors(A_sparse_diff, b_x_diff, b_y_diff,
p_placement, num_moveable_blocks_,
row_id_to_blk_id_, iteration);
p_placement, iteration);
}
// Verify that the constant vectors are valid.
VTR_ASSERT_DEBUG(!b_x_diff.hasNaN() && "b_x has NaN!");
Expand Down
44 changes: 44 additions & 0 deletions vpr/src/analytical_place/analytical_solver.h
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,21 @@ class QPHybridSolver : public AnalyticalSolver {
/// sparse.
static constexpr size_t star_num_pins_threshold = 3;

// The following constants are used to configure the anchor weighting.
// The weights of anchors grow exponentially each iteration by the following
// function:
// anchor_w = anchor_weight_mult_ * e^(iter / anchor_weight_exp_fac_)
// The numbers below were empircally found to work well.

/// @brief Multiplier for the anchorweight. The smaller this number is, the
/// weaker the anchors will be at the start.
static constexpr double anchor_weight_mult_ = 0.001;

/// @brief Factor for controlling the growth of the exponential term in the
/// weight factor function. Larger numbers will cause the anchor
/// weights to grow slower.
static constexpr double anchor_weight_exp_fac_ = 5.0;

/**
* @brief Initializes the linear system of Ax = b_x and Ay = b_y based on
* the APNetlist and the fixed APBlock locations.
Expand All @@ -165,6 +180,35 @@ class QPHybridSolver : public AnalyticalSolver {
*/
void init_linear_system();

/**
* @brief Helper method to update the linear system with anchors to the
* current partial placement.
*
* For each moveable block (with row = i) in the netlist:
* A[i][i] = A[i][i] + coeff_pseudo_anchor;
* b[i] = b[i] + pos[block(i)] * coeff_pseudo_anchor;
* Where coeff_pseudo_anchor grows with each iteration.
*
* This is basically a fast way of adding a connection between all moveable
* blocks in the netlist and their target fixed placement location.
*
* See add_connection_to_system.
*
* @param A_sparse_diff The ceofficient matrix to update.
* @param b_x_diff The x-dimension constant vector to update.
* @param b_y_diff The y-dimension constant vector to update.
* @param p_placement The location the moveable blocks should be
* anchored to.
* @param num_moveable_blocks The number of moveable blocks in the netlist.
* @param row_id_to_blk_id Lookup for the row id from the APBlock Id.
* @param iteration The current iteration of the Global Placer.
*/
void update_linear_system_with_anchors(Eigen::SparseMatrix<double>& A_sparse_diff,
Eigen::VectorXd& b_x_diff,
Eigen::VectorXd& b_y_diff,
PartialPlacement& p_placement,
unsigned iteration);

// The following variables represent the linear system without any anchor
// points. These are filled in the constructor and never modified.
// When the anchor-points are taken into consideration, the diagonal of the
Expand Down
1 change: 0 additions & 1 deletion vpr/src/analytical_place/flat_placement_bins.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,6 @@ class FlatPlacementBins {
inline const vtr::Rect<double>& bin_region(FlatPlacementBinId bin_id) const {
VTR_ASSERT(bin_id.is_valid());
return bin_region_[bin_id];
;
}

/**
Expand Down
11 changes: 8 additions & 3 deletions vpr/src/analytical_place/flat_placement_density_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ FlatPlacementDensityManager::FlatPlacementDensityManager(const APNetlist& ap_net
auto tile_type = device_grid.get_physical_type(tile_loc);
int tw = tile_type->width;
int th = tile_type->height;
VTR_ASSERT_SAFE(tw != 0 && th != 0);
vtr::Rect<double> new_bin_region(vtr::Point<double>(x, y),
vtr::Point<double>(x + tw,
y + th));
Expand Down Expand Up @@ -162,6 +163,10 @@ void FlatPlacementDensityManager::remove_block_from_bin(APBlockId blk_id,
}

void FlatPlacementDensityManager::import_placement_into_bins(const PartialPlacement& p_placement) {
// Empty the bins such that all blocks are no longer within the bins.
empty_bins();

// Insert each block in the netlist into their bin based on their placement.
// TODO: Maybe import the fixed block locations in the constructor and then
// only import the moveable block locations.
for (APBlockId blk_id : ap_netlist_.blocks()) {
Expand Down Expand Up @@ -215,9 +220,9 @@ void FlatPlacementDensityManager::empty_bins() {
// Reset all of the bins and their utilizations.
for (FlatPlacementBinId bin_id : bins_.bins()) {
bins_.remove_all_blocks_from_bin(bin_id);
bin_utilization_[bin_id] = PrimitiveVector();
bin_overfill_[bin_id] = calc_bin_overfill(bin_utilization_[bin_id], bin_capacity_[bin_id]);
bin_underfill_[bin_id] = calc_bin_underfill(bin_utilization_[bin_id], bin_capacity_[bin_id]);
bin_utilization_[bin_id].clear();
bin_overfill_[bin_id].clear();
bin_underfill_[bin_id] = bin_capacity_[bin_id];
}
// Once all the bins are reset, all bins should be empty; therefore no bins
// are overfilled.
Expand Down
3 changes: 3 additions & 0 deletions vpr/src/analytical_place/flat_placement_density_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,9 @@ class FlatPlacementDensityManager {
* @brief Import the given flat placement into the bins.
*
* This will place AP blocks into the bins that they are placed over.
*
* This will reset the bins before importing the placement. Anything inside
* the bins will be removed.
*/
void import_placement_into_bins(const PartialPlacement& p_placement);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@ static void print_capacities(const std::vector<PrimitiveVector>& logical_block_t
VTR_LOG("\n");
}
VTR_LOG("\n");
// TODO: Print the masses of each model.
}

FlatPlacementMassCalculator::FlatPlacementMassCalculator(const APNetlist& ap_netlist,
Expand Down
76 changes: 76 additions & 0 deletions vpr/src/analytical_place/global_placer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,16 @@
#include "analytical_solver.h"
#include "ap_flow_enums.h"
#include "ap_netlist.h"
#include "ap_netlist_fwd.h"
#include "atom_netlist.h"
#include "device_grid.h"
#include "flat_placement_bins.h"
#include "flat_placement_density_manager.h"
#include "globals.h"
#include "partial_legalizer.h"
#include "partial_placement.h"
#include "physical_types.h"
#include "primitive_vector.h"
#include "vpr_error.h"
#include "vtr_log.h"
#include "vtr_time.h"
Expand Down Expand Up @@ -90,9 +94,74 @@ SimPLGlobalPlacer::SimPLGlobalPlacer(e_partial_legalizer partial_legalizer_type,
partial_legalizer_ = make_partial_legalizer(partial_legalizer_type,
ap_netlist_,
density_manager_,
prepacker,
log_verbosity_);
}

/**
* @brief Helper method to print the statistics on the given partial placement.
*/
static void print_placement_stats(const PartialPlacement& p_placement,
const APNetlist& ap_netlist,
FlatPlacementDensityManager& density_manager) {
// Print the placement HPWL
VTR_LOG("\tPlacement HPWL: %f\n", p_placement.get_hpwl(ap_netlist));

// Print density information. Need to reset the density manager to ensure
// the data is valid.
density_manager.import_placement_into_bins(p_placement);

// Print the number of overfilled bins.
size_t num_overfilled_bins = density_manager.get_overfilled_bins().size();
VTR_LOG("\tNumber of overfilled bins: %zu\n", num_overfilled_bins);

// Print the average overfill
float total_overfill = 0.0f;
for (FlatPlacementBinId bin_id : density_manager.get_overfilled_bins()) {
total_overfill += density_manager.get_bin_overfill(bin_id).manhattan_norm();
}
float avg_overfill = 0.0f;
if (num_overfilled_bins != 0)
avg_overfill = total_overfill / static_cast<float>(num_overfilled_bins);
VTR_LOG("\tAverage overfill magnitude: %f\n", avg_overfill);

// Print the number of overfilled tiles per type.
const auto& physical_tile_types = g_vpr_ctx.device().physical_tile_types;
const auto& device_grid = g_vpr_ctx.device().grid;
std::vector<unsigned> overfilled_tiles_by_type(physical_tile_types.size(), 0);
for (FlatPlacementBinId bin_id : density_manager.get_overfilled_bins()) {
const auto& bin_region = density_manager.flat_placement_bins().bin_region(bin_id);
auto tile_loc = t_physical_tile_loc((int)bin_region.xmin(),
(int)bin_region.ymin(),
0);
auto tile_type = device_grid.get_physical_type(tile_loc);
overfilled_tiles_by_type[tile_type->index]++;
}
VTR_LOG("\tOverfilled bins by tile type:\n");
for (size_t type_idx = 0; type_idx < physical_tile_types.size(); type_idx++) {
VTR_LOG("\t\t%10s: %zu\n",
physical_tile_types[type_idx].name.c_str(),
overfilled_tiles_by_type[type_idx]);
}

// Count the number of blocks that were placed in a bin which they cannot
// physically be placed into (according to their mass).
unsigned num_misplaced_blocks = 0;
for (FlatPlacementBinId bin_id : density_manager.get_overfilled_bins()) {
for (APBlockId ap_blk_id : density_manager.flat_placement_bins().bin_contained_blocks(bin_id)) {
// Get the blk mass and project it onto the capacity of its bin.
PrimitiveVector blk_mass = density_manager.mass_calculator().get_block_mass(ap_blk_id);
PrimitiveVector projected_mass = blk_mass;
projected_mass.project(density_manager.get_bin_capacity(bin_id));
// If the projected mass does not match its match, this implies that
// there this block does not belong in this bin.
if (projected_mass != blk_mass)
num_misplaced_blocks++;
}
}
VTR_LOG("\tNumber of blocks in an incompatible bin: %zu\n", num_misplaced_blocks);
}

/**
* @brief Helper method to print the header of the per-iteration status updates
* of the global placer.
Expand Down Expand Up @@ -177,6 +246,13 @@ PartialPlacement SimPLGlobalPlacer::place() {
if (hpwl_relative_gap < target_hpwl_relative_gap_)
break;
}

// Print some statistics on the final placement.
VTR_LOG("Placement after Global Placement:\n");
print_placement_stats(p_placement,
ap_netlist_,
*density_manager_);

// Return the placement from the final iteration.
// TODO: investigate saving the best solution found so far. It should be
// cheap to save a copy of the PartialPlacement object.
Expand Down
3 changes: 2 additions & 1 deletion vpr/src/analytical_place/global_placer.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,8 @@ class SimPLGlobalPlacer : public GlobalPlacer {
/// lower-bound placements. The placer will stop if the difference
/// between the two bounds, normalized to the upper-bound, is smaller
/// than this number.
static constexpr double target_hpwl_relative_gap_ = 0.10;
/// This number was empircally found to work well.
static constexpr double target_hpwl_relative_gap_ = 0.05;

/// @brief The solver which generates the lower-bound placement.
std::unique_ptr<AnalyticalSolver> solver_;
Expand Down
Loading