Skip to content
Open
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
6 changes: 4 additions & 2 deletions vpr/src/analytical_place/analytical_placement_flow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,8 @@ void run_analytical_placement_flow(t_vpr_setup& vpr_setup) {
// that downstream stages (e.g. RAM mapper, global placement) can query realistic
// device dimensions before packing. The packer may later grow or shrink the device
// size to match the actual resource requirements after packing completes.
DeviceSizeEstimator device_size_estimator(vpr_setup, *device_ctx.arch, prepacker);
DeviceSizeEstimator device_size_estimator(vpr_setup, *device_ctx.arch, prepacker,
/*always_estimate_resource_requirement=*/ap_opts.full_legalizer_type == e_ap_full_legalizer::APPack);

// Set up the dedicated clock networks (if used) now that the device grid
// exists. This must happen before any RR graph is built in this flow
Expand Down Expand Up @@ -338,7 +339,8 @@ void run_analytical_placement_flow(t_vpr_setup& vpr_setup) {
ram_mapper,
vpr_setup,
*device_ctx.arch,
device_ctx.grid);
device_ctx.grid,
device_size_estimator.estimated_type_instance_counts());
full_legalizer->legalize(p_placement);

// Print the number of resources in netlist and number of resources available in architecture
Expand Down
15 changes: 10 additions & 5 deletions vpr/src/analytical_place/full_legalization/full_legalizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ std::unique_ptr<FullLegalizer> make_full_legalizer(e_ap_full_legalizer full_lega
const RamMapper& ram_mapper,
const t_vpr_setup& vpr_setup,
const t_arch& arch,
const DeviceGrid& device_grid) {
const DeviceGrid& device_grid,
const std::map<t_logical_block_type_ptr, size_t>& estimated_type_instance_counts) {
switch (full_legalizer_type) {
case e_ap_full_legalizer::Naive:
return std::make_unique<NaiveFullLegalizer>(ap_netlist,
Expand All @@ -82,7 +83,8 @@ std::unique_ptr<FullLegalizer> make_full_legalizer(e_ap_full_legalizer full_lega
ram_mapper,
vpr_setup,
arch,
device_grid);
device_grid,
estimated_type_instance_counts);
case e_ap_full_legalizer::APPack:
return std::make_unique<APPack>(ap_netlist,
atom_netlist,
Expand All @@ -91,7 +93,8 @@ std::unique_ptr<FullLegalizer> make_full_legalizer(e_ap_full_legalizer full_lega
ram_mapper,
vpr_setup,
arch,
device_grid);
device_grid,
estimated_type_instance_counts);
case e_ap_full_legalizer::FlatRecon:
return std::make_unique<FlatRecon>(ap_netlist,
atom_netlist,
Expand All @@ -100,7 +103,8 @@ std::unique_ptr<FullLegalizer> make_full_legalizer(e_ap_full_legalizer full_lega
ram_mapper,
vpr_setup,
arch,
device_grid);
device_grid,
estimated_type_instance_counts);
default:
VPR_FATAL_ERROR(VPR_ERROR_AP,
"Unrecognized full legalizer type");
Expand Down Expand Up @@ -1290,7 +1294,8 @@ void APPack::legalize(const PartialPlacement& p_placement) {
pre_cluster_timing_manager_,
flat_placement_info,
vpr_setup_,
ram_mapper_);
ram_mapper_,
estimated_type_instance_counts_);
}

// The Packer stores the clusters into a .net file. Load the packing file.
Expand Down
18 changes: 15 additions & 3 deletions vpr/src/analytical_place/full_legalization/full_legalizer.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
* routed by VTR.
*/

#include <map>
#include <memory>
#include <unordered_set>
#include "ap_flow_enums.h"
Expand All @@ -24,7 +25,9 @@ class PreClusterTimingManager;
class Prepacker;
class RamMapper;
struct t_arch;
struct t_logical_block_type;
struct t_vpr_setup;
using t_logical_block_type_ptr = const t_logical_block_type*;

/**
* @brief The full legalizer in an AP flow
Expand All @@ -44,15 +47,17 @@ class FullLegalizer {
const RamMapper& ram_mapper,
const t_vpr_setup& vpr_setup,
const t_arch& arch,
const DeviceGrid& device_grid)
const DeviceGrid& device_grid,
const std::map<t_logical_block_type_ptr, size_t>& estimated_type_instance_counts)
: ap_netlist_(ap_netlist)
, atom_netlist_(atom_netlist)
, prepacker_(prepacker)
, pre_cluster_timing_manager_(pre_cluster_timing_manager)
, ram_mapper_(ram_mapper)
, vpr_setup_(vpr_setup)
, arch_(arch)
, device_grid_(device_grid) {}
, device_grid_(device_grid)
, estimated_type_instance_counts_(estimated_type_instance_counts) {}

/**
* @brief Perform legalization on the given partial placement solution
Expand Down Expand Up @@ -101,6 +106,12 @@ class FullLegalizer {

/// @brief The device grid which records where clusters can be placed.
const DeviceGrid& device_grid_;

/// @brief Estimated number of instances required for each logical block
/// type, computed before Global Placement (see DeviceSizeEstimator).
/// Forwarded into the Packer during legalization so it can react
/// to block types that look like they will not fit densely.
const std::map<t_logical_block_type_ptr, size_t>& estimated_type_instance_counts_;
};

/**
Expand All @@ -114,7 +125,8 @@ std::unique_ptr<FullLegalizer> make_full_legalizer(e_ap_full_legalizer full_lega
const RamMapper& ram_mapper,
const t_vpr_setup& vpr_setup,
const t_arch& arch,
const DeviceGrid& device_grid);
const DeviceGrid& device_grid,
const std::map<t_logical_block_type_ptr, size_t>& estimated_type_instance_counts);

/**
* @brief FlatRecon: The Flat Placement Reconstruction Full Legalizer.
Expand Down
6 changes: 4 additions & 2 deletions vpr/src/base/vpr_api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -757,7 +757,8 @@ bool vpr_pack(t_vpr_setup& vpr_setup, const t_arch& arch) {
// that downstream stages (e.g. RAM mapper) can query realistic device
// dimensions before packing. The packer may later grow or shrink the device
// size to match the actual resource requirements after packing completes.
DeviceSizeEstimator device_size_estimator(vpr_setup, arch, prepacker);
DeviceSizeEstimator device_size_estimator(vpr_setup, arch, prepacker,
/*always_estimate_resource_requirement=*/g_vpr_ctx.atom().flat_placement_info().valid);

// Infer logical RAMs and assign to physical types to prioritize during packing.
// For the auto-device flow, reuse the groups already computed by the estimator.
Expand All @@ -778,7 +779,8 @@ bool vpr_pack(t_vpr_setup& vpr_setup, const t_arch& arch) {
pre_cluster_timing_manager,
g_vpr_ctx.atom().flat_placement_info(),
vpr_setup,
ram_mapper);
ram_mapper,
device_size_estimator.estimated_type_instance_counts());
}

void vpr_load_packing(const t_vpr_setup& vpr_setup, const t_arch& arch) {
Expand Down
115 changes: 115 additions & 0 deletions vpr/src/pack/appack_context.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/**
* @file
* @author Alex Singer
* @date August 2026
* @brief Implementation of the APPack Context object.
*/

#include "appack_context.h"

#include <algorithm>
#include <cstddef>
#include <map>
#include <vector>
#include "device_grid.h"
#include "physical_types.h"
#include "vpr_utils.h"
#include "vtr_log.h"

/**
* @brief Counts how many instances of the given logical block type the device
* can hold.
*/
static size_t count_available_instances(const t_logical_block_type& type,
const DeviceGrid& device_grid) {
size_t num_instances = 0;
for (const t_physical_tile_type_ptr equivalent_tile : type.equivalent_tiles)
num_instances += device_grid.num_instances(equivalent_tile, -1);
return num_instances;
}

/**
* @brief Logs a table showing, for each logical block type the netlist uses, how
* many instances the device has, how many the netlist is estimated to
* need, and the multiplier applied to that type's max candidate distance
* threshold (1.0 = unchanged).
*/
static void log_device_size_adjustments(
const std::map<t_logical_block_type_ptr, size_t>& estimated_type_instance_counts,
const std::map<t_logical_block_type_ptr, float>& type_th_multiplier,
const std::vector<t_logical_block_type>& logical_block_types,
const DeviceGrid& device_grid) {

VTR_LOG("\nAPPack device size estimate reaction (per used block type):\n");
VTR_LOG("%-20s %12s %12s %10s %10s\n",
"Block Type", "Available", "Estimated", "Util(%)", "DistThMul");
for (const t_logical_block_type& type : logical_block_types) {
auto itr = estimated_type_instance_counts.find(&type);
size_t estimated = (itr != estimated_type_instance_counts.end()) ? itr->second : 0;
if (estimated == 0)
continue; // Skip block types the netlist does not use.

size_t available = count_available_instances(type, device_grid);
float utilization = (available != 0) ? 100.0f * estimated / available : 0.0f;

auto mul_itr = type_th_multiplier.find(&type);
float th_multiplier = (mul_itr != type_th_multiplier.end()) ? mul_itr->second : 1.0f;

VTR_LOG("%-20s %12zu %12zu %10.1f %10.2f\n",
type.name.c_str(), available, estimated, utilization, th_multiplier);
}
VTR_LOG("\n");
}

void APPackContext::adjust_for_device_size_estimate(
const std::map<t_logical_block_type_ptr, size_t>& estimated_type_instance_counts,
const std::vector<t_logical_block_type>& logical_block_types,
const DeviceGrid& device_grid) {

// This reaction only makes sense when APPack is in use; the managers it
// touches are not initialized otherwise.
if (!appack_options.use_appack)
return;

// Max distance threshold multiplier applied to each block type, recorded for
// logging. Types not present here were left unchanged (multiplier of 1.0).
std::map<t_logical_block_type_ptr, float> type_th_multiplier;

for (const t_logical_block_type& type : logical_block_types) {
if (is_empty_type(&type))
continue;

size_t num_total_instances = count_available_instances(type, device_grid);
if (num_total_instances == 0)
continue; // No capacity for this type on the device; ignoring for now.

auto itr = estimated_type_instance_counts.find(&type);
size_t estimated_instances = (itr != estimated_type_instance_counts.end()) ? itr->second : 0;

// Compute the utilization of this block type.
float utilization = static_cast<float>(estimated_instances) / static_cast<float>(num_total_instances);
if (utilization < device_size_min_utilization_for_th_bump)
continue; // Comfortably fits.

// Linearly scale the multiplier applied to this type's normal max
// distance threshold from 1x to device_size_max_dist_th_scale_multiplier
// as utilization goes from device_size_min_utilization_for_th_bump to
// device_size_severe_utilization_cutoff.
float utilization_clamped = std::min(utilization, device_size_severe_utilization_cutoff);
float th_multiplier = 1.0f + (utilization_clamped - device_size_min_utilization_for_th_bump) / (device_size_severe_utilization_cutoff - device_size_min_utilization_for_th_bump) * (device_size_max_dist_th_scale_multiplier - 1.0f);
float base_max_dist_th = max_distance_threshold_manager.get_max_dist_threshold(type);
max_distance_threshold_manager.set_max_dist_threshold(type, base_max_dist_th * th_multiplier);

type_th_multiplier[&type] = th_multiplier;
}

log_device_size_adjustments(estimated_type_instance_counts, type_th_multiplier,
logical_block_types, device_grid);

// TODO: This should be capable of turning on unrelated clustering as well if the
// utilization is high enough. I chose to keep that out for now as I explore
// when it is best to use unrelated clustering. From many experiments, I have
// found that it is always better to turn off unrelated clustering if you can.

return;
}
51 changes: 50 additions & 1 deletion vpr/src/pack/appack_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
* information used to configure APPack in the packer.
*/

#include <cstddef>
#include <map>
#include <vector>
#include "appack_max_dist_th_manager.h"
#include "appack_unrelated_clustering_manager.h"
#include "device_grid.h"
Expand Down Expand Up @@ -78,6 +81,15 @@ struct t_appack_options {
// TODO: Investigate adding flat placement info to seed selection.
};

/**
* @brief Result of APPackContext::adjust_for_device_size_estimate.
*/
struct t_appack_device_size_adjustment {
/// @brief Whether unrelated clustering should be enabled globally (for all
/// block types) from the start of packing.
bool allow_unrelated_clustering = false;
};

/**
* @brief State relating to APPack.
*
Expand All @@ -104,7 +116,8 @@ struct APPackContext : public Context {
device_grid);

unrelated_clustering_manager.init(ap_opts.appack_unrelated_clustering_args,
logical_block_types);
logical_block_types,
device_grid);
}
}

Expand All @@ -126,4 +139,40 @@ struct APPackContext : public Context {
// how far we should search for unrelated candidates and how many attempts
// we should perform.
APPackUnrelatedClusteringManager unrelated_clustering_manager;

// ============ Device size estimate reaction ========================== //
// Tuning constants for adjust_for_device_size_estimate. "Utilization" here
// means a block type's estimated instance count (from the pre-packing
// device size estimate) divided by the number of instances available on
// the device.

/// @brief Minimum estimated utilization of a block type before its max
/// candidate distance threshold is widened.
static constexpr float device_size_min_utilization_for_th_bump = 0.5f;

/// @brief Largest multiplier applied to a block type's max candidate
/// distance threshold. Reached once the estimated utilization is at
/// (or above) device_size_severe_utilization_cutoff.
static constexpr float device_size_max_dist_th_scale_multiplier = 10.0f;

/// @brief Estimated utilization at (or above) which a block type is
/// considered severely over capacity.
static constexpr float device_size_severe_utilization_cutoff = 1.5f;

/**
* @brief Adjusts the APPack parameters according to how dense the device is
* expected to be.
*
* @param estimated_type_instance_counts
* Estimated number of instances of each logical block type needed by
* the netlist, computed before packing.
* @param logical_block_types
* All logical block types in the architecture.
* @param device_grid
* The device grid, used to count available instances of each type.
*/
void adjust_for_device_size_estimate(
const std::map<t_logical_block_type_ptr, size_t>& estimated_type_instance_counts,
const std::vector<t_logical_block_type>& logical_block_types,
const DeviceGrid& device_grid);
};
29 changes: 16 additions & 13 deletions vpr/src/pack/appack_max_dist_th_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ void APPackMaxDistThManager::init(const std::vector<std::string>& max_dist_ths,
const DeviceGrid& device_grid) {
// Compute the max device distance based on the width and height of the
// device. This is the L1 (manhattan) distance.
max_distance_on_device_ = device_grid.width() + device_grid.height();
float layer_span_contr = device_grid.get_num_layers() > 1 ? device_grid.get_num_layers() : 0;
max_distance_on_device_ = device_grid.width() + device_grid.height() + layer_span_contr;

// Automatically set the max distance thresholds.
auto_set_max_distance_thresholds(logical_block_types, device_grid);
Expand All @@ -38,18 +39,6 @@ void APPackMaxDistThManager::init(const std::vector<std::string>& max_dist_ths,

// Set the initialized flag to true.
is_initialized_ = true;

// Log the max distance thresholds for each logical block type. This is
// similar to how the input and output pin utilizations are printed.
VTR_LOG("APPack is using max distance thresholds: ");
for (const t_logical_block_type& lb_ty : logical_block_types) {
if (lb_ty.is_empty())
continue;
VTR_LOG("%s:%g ",
lb_ty.name.c_str(),
get_max_dist_threshold(lb_ty));
}
VTR_LOG("\n");
}

void APPackMaxDistThManager::auto_set_max_distance_thresholds(const std::vector<t_logical_block_type>& logical_block_types,
Expand Down Expand Up @@ -143,3 +132,17 @@ void APPackMaxDistThManager::set_max_distance_thresholds_from_strings(
logical_block_dist_thresholds_[lb_ty_index] = logical_block_max_dist_th;
}
}

void APPackMaxDistThManager::print_max_dist_thresholds(const std::vector<t_logical_block_type>& logical_block_types) const {
// Log the max distance thresholds for each logical block type. This is
// similar to how the input and output pin utilizations are printed.
VTR_LOG("APPack is using max distance thresholds: ");
for (const t_logical_block_type& lb_ty : logical_block_types) {
if (lb_ty.is_empty())
continue;
VTR_LOG("%s:%g ",
lb_ty.name.c_str(),
get_max_dist_threshold(lb_ty));
}
VTR_LOG("\n");
}
Loading
Loading