diff --git a/vpr/src/place/initial_placement.cpp b/vpr/src/place/initial_placement.cpp index 45784d6e698..d5662af4f47 100644 --- a/vpr/src/place/initial_placement.cpp +++ b/vpr/src/place/initial_placement.cpp @@ -82,20 +82,22 @@ static int check_macro_can_be_placed(t_pl_macro pl_macro, int itype, t_pl_loc he // Every macro can be placed until proven otherwise int macro_can_be_placed = true; + //Check whether macro contains blocks with floorplan constraints bool macro_constrained = is_macro_constrained(pl_macro); - PartitionRegion macro_pr; - - if (macro_constrained) { - macro_pr = constrained_macro_locs(pl_macro); - } // Check whether all the members can be placed for (size_t imember = 0; imember < pl_macro.members.size(); imember++) { t_pl_loc member_pos = head_pos + pl_macro.members[imember].offset; - //if the macro is constrained, check if the member position is within the PartitionRegion for the macro - if (macro_constrained) { - bool member_loc_good = macro_pr.is_loc_in_part_reg(member_pos); + /* + * If the macro is constrained, check that the head member is in a legal position from + * a floorplanning perspective. It is enough to do this check for the head member alone, + * because constraints propagation was performed to calculate smallest floorplan region for the head + * macro, based on the constraints on all of the blocks in the macro. So, if the head macro is in a + * legal floorplan location, all other blocks in the macro will be as well. + */ + if (macro_constrained && imember == 0) { + bool member_loc_good = cluster_floorplanning_legal(pl_macro.members[imember].blk_index, member_pos); if (!member_loc_good) { macro_can_be_placed = false; break; @@ -417,6 +419,11 @@ void initial_placement(enum e_pad_loc_type pad_loc_type, const char* constraints vtr::vector block_scores = assign_block_scores(); std::vector sorted_blocks = sort_blocks(block_scores); + /* Go through cluster blocks to calculate the tightest placement + * floorplan constraint for each constrained block + */ + propagate_place_constraints(); + // Loading legal placement locations zero_initialize_grid_blocks(); alloc_and_load_legal_placement_locations(legal_pos); diff --git a/vpr/src/place/place_constraints.cpp b/vpr/src/place/place_constraints.cpp index 5f6014a835f..1a07b65737b 100644 --- a/vpr/src/place/place_constraints.cpp +++ b/vpr/src/place/place_constraints.cpp @@ -54,8 +54,8 @@ bool is_macro_constrained(const t_pl_macro& pl_macro) { } /*Returns PartitionRegion of where the head of the macro could go*/ -PartitionRegion constrained_macro_locs(const t_pl_macro& pl_macro) { - PartitionRegion macro_pr; +PartitionRegion update_macro_head_pr(const t_pl_macro& pl_macro, const PartitionRegion& grid_pr) { + PartitionRegion macro_head_pr; bool is_member_constrained = false; int num_constrained_members = 0; auto& floorplanning_ctx = g_vpr_ctx.floorplanning(); @@ -80,39 +80,116 @@ PartitionRegion constrained_macro_locs(const t_pl_macro& pl_macro) { vtr::Rect reg_rect = block_regions[i].get_region_rect(); - t_pl_loc min_pl_loc(reg_rect.xmin(), reg_rect.ymin(), block_regions[i].get_sub_tile()); - - t_pl_loc modified_min_pl_loc = min_pl_loc + offset; - - t_pl_loc max_pl_loc(reg_rect.xmax(), reg_rect.ymax(), block_regions[i].get_sub_tile()); + modified_reg.set_region_rect(reg_rect.xmin() - offset.x, reg_rect.ymin() - offset.y, reg_rect.xmax() - offset.x, reg_rect.ymax() - offset.y); - t_pl_loc modified_max_pl_loc = max_pl_loc + offset; - - modified_reg.set_region_rect(modified_min_pl_loc.x, modified_min_pl_loc.y, modified_max_pl_loc.x, modified_max_pl_loc.y); //check that subtile is not an invalid value before changing, otherwise it just stays -1 - if (block_regions[i].get_sub_tile() != -1) { - modified_reg.set_sub_tile(modified_min_pl_loc.sub_tile); + if (block_regions[i].get_sub_tile() != NO_SUBTILE) { + modified_reg.set_sub_tile(block_regions[i].get_sub_tile() - offset.sub_tile); } modified_pr.add_to_part_region(modified_reg); } if (num_constrained_members == 1) { - macro_pr = modified_pr; + macro_head_pr = modified_pr; } else { - macro_pr = intersection(macro_pr, modified_pr); + macro_head_pr = intersection(macro_head_pr, modified_pr); } } } + //intersect to ensure the head pr does not go outside of grid dimensions + macro_head_pr = intersection(macro_head_pr, grid_pr); + + //if the intersection is empty, no way to place macro members together, give an error + if (macro_head_pr.empty()) { + print_macro_constraint_error(pl_macro); + } + + return macro_head_pr; +} + +PartitionRegion update_macro_member_pr(PartitionRegion& head_pr, const t_pl_offset& offset, const PartitionRegion& grid_pr, const t_pl_macro& pl_macro) { + std::vector block_regions = head_pr.get_partition_region(); + PartitionRegion macro_pr; + + for (unsigned int i = 0; i < block_regions.size(); i++) { + Region modified_reg; + + vtr::Rect reg_rect = block_regions[i].get_region_rect(); + + modified_reg.set_region_rect(reg_rect.xmin() + offset.x, reg_rect.ymin() + offset.y, reg_rect.xmax() + offset.x, reg_rect.ymax() + offset.y); + + //check that subtile is not an invalid value before changing, otherwise it just stays -1 + if (block_regions[i].get_sub_tile() != NO_SUBTILE) { + modified_reg.set_sub_tile(block_regions[i].get_sub_tile() + offset.sub_tile); + } + + macro_pr.add_to_part_region(modified_reg); + } + + //intersect to ensure the macro pr does not go outside of grid dimensions + macro_pr = intersection(macro_pr, grid_pr); + //if the intersection is empty, no way to place macro members together, give an error if (macro_pr.empty()) { - VPR_ERROR(VPR_ERROR_PLACE, " \n Feasible floorplanning constraints could not be calculated for the placement macro.\n"); + print_macro_constraint_error(pl_macro); } return macro_pr; } +void print_macro_constraint_error(const t_pl_macro& pl_macro) { + auto& cluster_ctx = g_vpr_ctx.clustering(); + VTR_LOG( + "Feasible floorplanning constraints could not be calculated for the placement macro. \n" + "The placement macro contains the following blocks: \n"); + for (unsigned int i = 0; i < pl_macro.members.size(); i++) { + std::string blk_name = cluster_ctx.clb_nlist.block_name((pl_macro.members[i].blk_index)); + VTR_LOG("Block %s (#%zu) ", blk_name.c_str(), size_t(pl_macro.members[i].blk_index)); + } + VTR_LOG("\n"); + VPR_ERROR(VPR_ERROR_PLACE, " \n Check that the above-mentioned placement macro blocks have compatible floorplan constraints.\n"); +} + +void propagate_place_constraints() { + auto& place_ctx = g_vpr_ctx.placement(); + auto& floorplanning_ctx = g_vpr_ctx.mutable_floorplanning(); + auto& device_ctx = g_vpr_ctx.device(); + + //Create a PartitionRegion with grid dimensions + //Will be used to check that updated PartitionRegions are within grid bounds + int width = device_ctx.grid.width() - 1; + int height = device_ctx.grid.height() - 1; + Region grid_reg; + grid_reg.set_region_rect(0, 0, width, height); + PartitionRegion grid_pr; + grid_pr.add_to_part_region(grid_reg); + + for (auto pl_macro : place_ctx.pl_macros) { + if (is_macro_constrained(pl_macro)) { + /* + * Get the PartitionRegion for the head of the macro + * based on the constraints of all blocks contained in the macro + */ + PartitionRegion macro_head_pr = update_macro_head_pr(pl_macro, grid_pr); + + //Update PartitionRegions of all members of the macro + for (size_t imember = 0; imember < pl_macro.members.size(); imember++) { + ClusterBlockId iblk = pl_macro.members[imember].blk_index; + auto offset = pl_macro.members[imember].offset; + + if (imember == 0) { //Update head PR + floorplanning_ctx.cluster_constraints[iblk] = macro_head_pr; + } else { //Update macro member PR + PartitionRegion macro_pr = update_macro_member_pr(macro_head_pr, offset, grid_pr, pl_macro); + floorplanning_ctx.cluster_constraints[iblk] = macro_pr; + } + } + } + } +} + /*returns true if location is compatible with floorplanning constraints, false if not*/ /* * Even if the block passed in is from a macro, it will work because of the constraints diff --git a/vpr/src/place/place_constraints.h b/vpr/src/place/place_constraints.h index b9121ebdcd7..d6287287689 100644 --- a/vpr/src/place/place_constraints.h +++ b/vpr/src/place/place_constraints.h @@ -34,9 +34,33 @@ bool cluster_floorplanning_legal(ClusterBlockId blk_id, const t_pl_loc& loc); bool is_macro_constrained(const t_pl_macro& pl_macro); /* - * Returns region of valid locations for the head of the macro based on floorplan constraints + * Returns PartitionRegion for the head of the macro based on the floorplan constraints + * of all blocks in the macro. For example, if there was a macro of length two and each block has + * a constraint, this routine will shift and intersect the two constraint regions to calculate + * the tightest region constraint for the head macro. */ -PartitionRegion constrained_macro_locs(const t_pl_macro& pl_macro); +PartitionRegion update_macro_head_pr(const t_pl_macro& pl_macro, const PartitionRegion& grid_pr); + +/* + * Update the PartitionRegions of non-head members of a macro, + * based on the constraint that was calculated for the head region, head_pr. + * The constraint on the head region must be the tightest possible (i.e. implied by the + * entire macro) before this routine is called. + * For each macro member, the updated constraint is essentially the head constraint + * with the member's offset applied. + */ +PartitionRegion update_macro_member_pr(PartitionRegion& head_pr, const t_pl_offset& offset, const PartitionRegion& grid_pr, const t_pl_macro& pl_macro); + +/* + * Updates the floorplan constraints information for all constrained macros. + * Updates the constraints to be the tightest constraints possible while adhering + * to the floorplan constraints of each macro member. + * This is done at the start of initial placement to ease floorplan legality checking + * while placing macros during initial placement. + */ +void propagate_place_constraints(); + +void print_macro_constraint_error(const t_pl_macro& pl_macro); inline bool floorplan_legal(const t_pl_blocks_to_be_moved& blocks_affected) { bool floorplan_legal; diff --git a/vpr/test/test_vpr_constraints.cpp b/vpr/test/test_vpr_constraints.cpp index c6dbb9a81e4..b0b1ad2afe6 100644 --- a/vpr/test/test_vpr_constraints.cpp +++ b/vpr/test/test_vpr_constraints.cpp @@ -6,6 +6,7 @@ #include "vpr_constraints.h" #include "partition.h" #include "region.h" +#include "place_constraints.h" /** * This file contains unit tests that check the functionality of all classes related to vpr constraints. These classes include @@ -425,6 +426,37 @@ TEST_CASE("RegionLocked", "[vpr]") { REQUIRE(is_r2_locked == false); } +//Test calculation of macro constraints +/* Checks that the PartitionRegion of a macro member is updated properly according + * to the head member's PartitionRegion that is passed in. + */ +TEST_CASE("MacroConstraints", "[vpr]") { + t_pl_macro pl_macro; + PartitionRegion head_pr; + t_pl_offset offset(2, 1, 0); + + Region reg; + reg.set_region_rect(5, 2, 9, 6); + + head_pr.add_to_part_region(reg); + + Region grid_reg; + grid_reg.set_region_rect(0, 0, 20, 20); + PartitionRegion grid_pr; + grid_pr.add_to_part_region(grid_reg); + + PartitionRegion macro_pr = update_macro_member_pr(head_pr, offset, grid_pr, pl_macro); + + std::vector mac_regions = macro_pr.get_partition_region(); + + vtr::Rect mac_rect = mac_regions[0].get_region_rect(); + + REQUIRE(mac_rect.xmin() == 7); + REQUIRE(mac_rect.ymin() == 3); + REQUIRE(mac_rect.xmax() == 11); + REQUIRE(mac_rect.ymax() == 7); +} + static constexpr const char kArchFile[] = "test_read_arch_metadata.xml"; // Test that place constraints are not changed during placement