Skip to content

[Packer] Reorganized Iterative Packer Algorithm #3162

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
Merged
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
222 changes: 181 additions & 41 deletions vpr/src/pack/pack.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,122 @@
#include "vtr_assert.h"
#include "vtr_log.h"

namespace {

/**
* @brief Enumeration for the state of the packer.
*
* If the packer fails to find a dense enough packing, depending on how the
* packer failed, the packer may iteratively retry packing with different
* settings to try and find a denser packing. These states represent a
* different type of iteration.
*/
enum class e_packer_state {
/// @brief Default packer state.
DEFAULT,
/// @brief Succcess state for the packer. The packing looks feasible to
/// fit on the device (does not exceed number of blocks of each
/// type in the grid) and meets floorplanning constraints.
SUCCESS,
/// @brief Standard fallback where there are no region constraints. Turns
/// on unrelated clustering and balanced packing if it can.
UNRELATED_AND_BALANCED,
/// @brief Region constraints: Turns on attraction groups for overfilled regions.
ATTRACTION_GROUPS,
/// @brief Region constraints: Turns on more attraction groups for overfilled regions.
MORE_ATTRACTION_GROUPS,
/// @brief Region constraints: Turns on more attraction groups for all regions.
ATTRACTION_GROUPS_ALL_REGIONS,
/// @brief Region constraints: Turns on more attraction groups for all regions
/// and increases the target density of "clb" blocks.
/// TODO: This should increase the target density of all overused blocks.
ATTRACTION_GROUPS_ALL_REGIONS_AND_INCREASED_TARGET_DENSITY,
/// @brief The failure state.
FAILURE
};

} // namespace

static bool try_size_device_grid(const t_arch& arch,
const std::map<t_logical_block_type_ptr, size_t>& num_type_instances,
float target_device_utilization,
const std::string& device_layout_name);

/**
* @brief The packer iteratively re-packes the netlist if it fails to find a
* valid clustering. Each iteration is a state the packer is in, where
* each state uses a different set of options. This method gets the next
* state of the packer given the current state of the packer.
*
* @param current_packer_state
* The current state of the packer (the state used to make the most recent
* clustering).
* @param fits_on_device
* Whether the current clustering fits on the device or not.
* @param floorplan_regions_overfull
* Whether the current clustering has overfilled regions.
* @param floorplan_not_fitting
* Whether the current clustering is fitting on the current floorplan.
*/
static e_packer_state get_next_packer_state(e_packer_state current_packer_state,
bool fits_on_device,
bool floorplan_regions_overfull,
bool floorplan_not_fitting) {
// Next packer state logic
e_packer_state next_packer_state = e_packer_state::FAILURE;
if (fits_on_device && !floorplan_regions_overfull) {
// If everything fits on the device and the floorplan regions are
// not overfilled, the next state is success.
next_packer_state = e_packer_state::SUCCESS;
} else {
if (floorplan_not_fitting) {
// If there are overfilled region constraints.

// When running with tight floorplan constraints, some regions may become overfull with clusters (i.e.
// the number of blocks assigned to the region exceeds the number of blocks available). When this occurs, we
// cluster more densely to be able to adhere to the floorplan constraints. However, we do not want to cluster more
// densely unnecessarily, as this can negatively impact wirelength. So, we have iterative approach. We check at the end
// of every iteration if any floorplan regions are overfull. In the first iteration, we run
// with no attraction groups (not packing more densely). If regions are overfull at the end of the first iteration,
// we create attraction groups for partitions with overfull regions (pack those atoms more densely). We continue this way
// until the last iteration, when we create attraction groups for every partition, if needed.

switch (current_packer_state) {
case e_packer_state::DEFAULT:
next_packer_state = e_packer_state::ATTRACTION_GROUPS;
break;
case e_packer_state::UNRELATED_AND_BALANCED:
case e_packer_state::ATTRACTION_GROUPS:
next_packer_state = e_packer_state::MORE_ATTRACTION_GROUPS;
break;
case e_packer_state::MORE_ATTRACTION_GROUPS:
next_packer_state = e_packer_state::ATTRACTION_GROUPS_ALL_REGIONS;
break;
case e_packer_state::ATTRACTION_GROUPS_ALL_REGIONS:
next_packer_state = e_packer_state::ATTRACTION_GROUPS_ALL_REGIONS_AND_INCREASED_TARGET_DENSITY;
break;
case e_packer_state::ATTRACTION_GROUPS_ALL_REGIONS_AND_INCREASED_TARGET_DENSITY:
default:
next_packer_state = e_packer_state::FAILURE;
break;
}
} else {
// If there are no overfilled region constraints, but some block
// types on the grid are overfilled.
switch (current_packer_state) {
case e_packer_state::DEFAULT:
next_packer_state = e_packer_state::UNRELATED_AND_BALANCED;
break;
default:
next_packer_state = e_packer_state::FAILURE;
break;
}
}
}

return next_packer_state;
}

bool try_pack(const t_packer_opts& packer_opts,
const t_analysis_opts& analysis_opts,
const t_ap_opts& ap_opts,
Expand Down Expand Up @@ -145,7 +256,10 @@ bool try_pack(const t_packer_opts& packer_opts,

g_vpr_ctx.mutable_atom().mutable_lookup().set_atom_pb_bimap_lock(true);

while (true) {
// The current state of the packer during iterative packing.
e_packer_state current_packer_state = e_packer_state::DEFAULT;

while (current_packer_state != e_packer_state::SUCCESS && current_packer_state != e_packer_state::FAILURE) {
//Cluster the netlist
// num_used_type_instances: A map used to save the number of used
// instances from each logical block type.
Expand All @@ -157,7 +271,7 @@ bool try_pack(const t_packer_opts& packer_opts,
attraction_groups,
mutable_device_ctx);

//Try to size/find a device
// Try to size/find a device
bool fits_on_device = try_size_device_grid(arch, num_used_type_instances, packer_opts.target_device_utilization, packer_opts.device_layout);

/* We use this bool to determine the cause for the clustering not being dense enough. If the clustering
Expand All @@ -170,48 +284,56 @@ bool try_pack(const t_packer_opts& packer_opts,

bool floorplan_not_fitting = (floorplan_regions_overfull || g_vpr_ctx.floorplanning().constraints.get_num_partitions() > 0);

if (fits_on_device && !floorplan_regions_overfull) {
break; //Done
} else if (pack_iteration == 1 && !floorplan_not_fitting) {
//1st pack attempt was unsuccessful (i.e. not dense enough) and we have control of unrelated clustering
//
//Turn it on to increase packing density
if (packer_opts.allow_unrelated_clustering == e_unrelated_clustering::AUTO) {
VTR_ASSERT(allow_unrelated_clustering == false);
allow_unrelated_clustering = true;
// Next packer state logic
e_packer_state next_packer_state = get_next_packer_state(current_packer_state,
fits_on_device,
floorplan_regions_overfull,
floorplan_not_fitting);

// Set up for the options used for the next packer state.
// NOTE: This must be done here (and not at the start of the next packer
// iteration) since we need to know information about the current
// clustering to change the options for the next iteration.
switch (next_packer_state) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a little bit of a safety issue here in that if in the future someone decides to skip one of the stages and go straight to the next stage (by modifying the previous section) this code might leave the packer in an invalid state. Essentially there's a coupling between the state diagram/transitions in the state machine and the effect of each state.

This isn't a massive issue and it's probably tricky to avoid it since I bet the attraction_groups methods are not idempotent so if you feel like it would be too much work, it's okay.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, but this is not something I am able to fix right now. The options used for the next iteration of the packer requires information from the previous iteration. It is much cleaner to just set it up here. I am planning on leaving the way it is.

case e_packer_state::UNRELATED_AND_BALANCED: {
// 1st pack attempt was unsuccessful (i.e. not dense enough) and we have control of unrelated clustering
//
// Turn it on to increase packing density
// TODO: This will have no affect if unrelated clustering and
// balance block type utilization is not auto. Should update
// the next state logic.
if (packer_opts.allow_unrelated_clustering == e_unrelated_clustering::AUTO) {
VTR_ASSERT(allow_unrelated_clustering == false);
allow_unrelated_clustering = true;
}
if (packer_opts.balance_block_type_utilization == e_balance_block_type_util::AUTO) {
VTR_ASSERT(balance_block_type_util == false);
balance_block_type_util = true;
}
VTR_LOG("Packing failed to fit on device. Re-packing with: unrelated_logic_clustering=%s balance_block_type_util=%s\n",
(allow_unrelated_clustering ? "true" : "false"),
(balance_block_type_util ? "true" : "false"));
break;
}
if (packer_opts.balance_block_type_utilization == e_balance_block_type_util::AUTO) {
VTR_ASSERT(balance_block_type_util == false);
balance_block_type_util = true;
case e_packer_state::ATTRACTION_GROUPS: {
VTR_LOG("Floorplan regions are overfull: trying to pack again using cluster attraction groups. \n");
attraction_groups.create_att_groups_for_overfull_regions(overfull_partition_regions);
attraction_groups.set_att_group_pulls(1);
break;
}
VTR_LOG("Packing failed to fit on device. Re-packing with: unrelated_logic_clustering=%s balance_block_type_util=%s\n",
(allow_unrelated_clustering ? "true" : "false"),
(balance_block_type_util ? "true" : "false"));
/*
* When running with tight floorplan constraints, some regions may become overfull with clusters (i.e.
* the number of blocks assigned to the region exceeds the number of blocks available). When this occurs, we
* cluster more densely to be able to adhere to the floorplan constraints. However, we do not want to cluster more
* densely unnecessarily, as this can negatively impact wirelength. So, we have iterative approach. We check at the end
* of every iteration if any floorplan regions are overfull. In the first iteration, we run
* with no attraction groups (not packing more densely). If regions are overfull at the end of the first iteration,
* we create attraction groups for partitions with overfull regions (pack those atoms more densely). We continue this way
* until the last iteration, when we create attraction groups for every partition, if needed.
*/
} else if (pack_iteration == 1 && floorplan_not_fitting) {
VTR_LOG("Floorplan regions are overfull: trying to pack again using cluster attraction groups. \n");
attraction_groups.create_att_groups_for_overfull_regions(overfull_partition_regions);
attraction_groups.set_att_group_pulls(1);

} else if (pack_iteration >= 2 && pack_iteration < 5 && floorplan_not_fitting) {
if (pack_iteration == 2) {
case e_packer_state::MORE_ATTRACTION_GROUPS: {
VTR_LOG("Floorplan regions are overfull: trying to pack again with more attraction groups exploration. \n");
attraction_groups.create_att_groups_for_overfull_regions(overfull_partition_regions);
VTR_LOG("Pack iteration is %d\n", pack_iteration);
} else if (pack_iteration == 3) {
break;
}
case e_packer_state::ATTRACTION_GROUPS_ALL_REGIONS: {
attraction_groups.create_att_groups_for_all_regions();
VTR_LOG("Floorplan regions are overfull: trying to pack again with more attraction groups exploration. \n");
VTR_LOG("Pack iteration is %d\n", pack_iteration);
} else if (pack_iteration == 4) {
break;
}
case e_packer_state::ATTRACTION_GROUPS_ALL_REGIONS_AND_INCREASED_TARGET_DENSITY: {
attraction_groups.create_att_groups_for_all_regions();
VTR_LOG("Floorplan regions are overfull: trying to pack again with more attraction groups exploration and higher target pin utilization. \n");
VTR_LOG("Pack iteration is %d\n", pack_iteration);
Expand All @@ -224,9 +346,18 @@ bool try_pack(const t_packer_opts& packer_opts,
// do it for all block types. Doing it only for a clb
// string is dangerous -VB.
cluster_legalizer.get_target_external_pin_util().set_block_pin_util("clb", pin_util);
break;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we should fix this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should really check which types of blocks are overused and increase their target_external_pin_util.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I already do this on the AP changes I am working on now. It will be very easy to bring in later in a future PR.

}
case e_packer_state::DEFAULT:
case e_packer_state::SUCCESS:
case e_packer_state::FAILURE:
default:
// Nothing to set up.
break;
}

} else { //Unable to pack densely enough: Give Up
// Raise an error if the packer failed to pack.
if (next_packer_state == e_packer_state::FAILURE) {
if (floorplan_regions_overfull) {
VPR_FATAL_ERROR(VPR_ERROR_OTHER,
"Failed to find pack clusters densely enough to fit in the designated floorplan regions.\n"
Expand Down Expand Up @@ -255,14 +386,23 @@ bool try_pack(const t_packer_opts& packer_opts,
VPR_FATAL_ERROR(VPR_ERROR_OTHER, "Failed to find device which satisfies resource requirements required: %s (available %s)", resource_reqs.c_str(), resource_avail.c_str());
}

//Reset floorplanning constraints for re-packing
g_vpr_ctx.mutable_floorplanning().cluster_constraints.clear();
// If the packer was unsuccessful, reset the packed solution and try again.
if (next_packer_state != e_packer_state::SUCCESS) {
// Reset floorplanning constraints for re-packing
g_vpr_ctx.mutable_floorplanning().cluster_constraints.clear();

// Reset the cluster legalizer for re-clustering.
cluster_legalizer.reset();
}

// Set the current state to the next state.
current_packer_state = next_packer_state;

// Reset the cluster legalizer for re-clustering.
cluster_legalizer.reset();
++pack_iteration;
}

VTR_ASSERT(current_packer_state == e_packer_state::SUCCESS);

/* Packing iterative improvement can be done here */
/* Use the re-cluster API to edit it */
/******************* Start *************************/
Expand Down