diff --git a/vpr/src/pack/pack.cpp b/vpr/src/pack/pack.cpp index 57ee96a1a7..559f29173a 100644 --- a/vpr/src/pack/pack.cpp +++ b/vpr/src/pack/pack.cpp @@ -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& 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, @@ -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. @@ -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 @@ -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) { + 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); @@ -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; } + 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" @@ -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 *************************/