diff --git a/vpr/src/base/read_place.cpp b/vpr/src/base/read_place.cpp index b4a8b972d71..68b51ef5b02 100644 --- a/vpr/src/base/read_place.cpp +++ b/vpr/src/base/read_place.cpp @@ -15,6 +15,7 @@ #include "hash.h" #include "read_place.h" #include "read_xml_arch_file.h" +#include "place_util.h" void read_place_header( std::ifstream& placement_file, @@ -173,18 +174,12 @@ void read_place_body(std::ifstream& placement_file, const char* place_file, bool is_place_file) { auto& cluster_ctx = g_vpr_ctx.clustering(); - auto& device_ctx = g_vpr_ctx.device(); auto& place_ctx = g_vpr_ctx.mutable_placement(); auto& atom_ctx = g_vpr_ctx.atom(); std::string line; int lineno = 0; - if (place_ctx.block_locs.size() != cluster_ctx.clb_nlist.blocks().size()) { - //Resize if needed - place_ctx.block_locs.resize(cluster_ctx.clb_nlist.blocks().size()); - } - //used to count how many times a block has been seen in the place/constraints file so duplicate blocks can be detected vtr::vector_map seen_blocks; @@ -236,45 +231,26 @@ void read_place_body(std::ifstream& placement_file, //Check if block is listed multiple times with conflicting locations in constraints file if (seen_blocks[blk_id] > 0) { if (block_x != place_ctx.block_locs[blk_id].loc.x || block_y != place_ctx.block_locs[blk_id].loc.y || sub_tile_index != place_ctx.block_locs[blk_id].loc.sub_tile) { + std::string cluster_name = cluster_ctx.clb_nlist.block_name(blk_id); VPR_THROW(VPR_ERROR_PLACE, - "The location of cluster %d is specified %d times in the constraints file with conflicting locations. \n" + "The location of cluster %s (#%d) is specified %d times in the constraints file with conflicting locations. \n" "Its location was last specified with block %s. \n", - blk_id, seen_blocks[blk_id] + 1, c_block_name); + cluster_name.c_str(), blk_id, seen_blocks[blk_id] + 1, c_block_name); } } - //Check if block location is out of range of grid dimensions - if (block_x < 0 || block_x > int(device_ctx.grid.width() - 1) - || block_y < 0 || block_y > int(device_ctx.grid.height() - 1)) { - VPR_THROW(VPR_ERROR_PLACE, "Block %s with ID %d is out of range at location (%d, %d). \n", c_block_name, blk_id, block_x, block_y); - } - - //Set the location - place_ctx.block_locs[blk_id].loc.x = block_x; - place_ctx.block_locs[blk_id].loc.y = block_y; - place_ctx.block_locs[blk_id].loc.sub_tile = sub_tile_index; + t_pl_loc loc; + loc.x = block_x; + loc.y = block_y; + loc.sub_tile = sub_tile_index; - //Check if block is at an illegal location - - auto physical_tile = device_ctx.grid[block_x][block_y].type; - auto logical_block = cluster_ctx.clb_nlist.block_type(blk_id); - - if (sub_tile_index >= physical_tile->capacity || sub_tile_index < 0) { - VPR_THROW(VPR_ERROR_PLACE, "Block %s subtile number (%d) is out of range. \n", c_block_name, sub_tile_index); + if (seen_blocks[blk_id] == 0) { + set_block_location(blk_id, loc); } - if (!is_sub_tile_compatible(physical_tile, logical_block, place_ctx.block_locs[blk_id].loc.sub_tile)) { - VPR_THROW(VPR_ERROR_PLACE, "Attempt to place block %s with ID %d at illegal location (%d, %d). \n", c_block_name, blk_id, block_x, block_y); - } - - //need to lock down blocks and mark grid block usage if it is a constraints file - //for a place file, grid usage is marked during initial placement instead + //need to lock down blocks if it is a constraints file if (!is_place_file) { place_ctx.block_locs[blk_id].is_fixed = true; - place_ctx.grid_blocks[block_x][block_y].blocks[sub_tile_index] = blk_id; - if (seen_blocks[blk_id] == 0) { - place_ctx.grid_blocks[block_x][block_y].usage++; - } } //mark the block as seen diff --git a/vpr/src/base/region.cpp b/vpr/src/base/region.cpp index ed5b79c068b..594ec76564e 100644 --- a/vpr/src/base/region.cpp +++ b/vpr/src/base/region.cpp @@ -3,10 +3,10 @@ Region::Region() { sub_tile = NO_SUBTILE; - //default rect for a region is (-1, -1, -1, -1) + //default rect for a region is (999, 999, -1, -1) //these values indicate an empty rectangle, they are set as default values to help catch uninitialized use - region_bounds.set_xmin(-1); - region_bounds.set_ymin(-1); + region_bounds.set_xmin(999); + region_bounds.set_ymin(999); region_bounds.set_xmax(-1); region_bounds.set_ymax(-1); } @@ -30,12 +30,8 @@ void Region::set_sub_tile(int _sub_tile) { sub_tile = _sub_tile; } -bool Region::locked() { - return region_bounds.xmin() == region_bounds.xmax() && region_bounds.ymin() == region_bounds.ymax() && sub_tile != NO_SUBTILE; -} - bool Region::empty() { - return region_bounds.empty(); + return (region_bounds.xmax() < region_bounds.xmin() || region_bounds.ymax() < region_bounds.ymin()); } bool Region::is_loc_in_reg(t_pl_loc loc) { diff --git a/vpr/src/base/region.h b/vpr/src/base/region.h index 9069ba180f6..415060ada6a 100644 --- a/vpr/src/base/region.h +++ b/vpr/src/base/region.h @@ -44,15 +44,11 @@ class Region { void set_sub_tile(int _sub_tile); /** - * @brief Return whether the region is empty, based on whether the region rectangle is empty + * @brief Return whether the region is empty (i. e. the region bounds rectangle + * covers no area) */ bool empty(); - /** - * @brief Checks whether a block is locked down to a specific x, y, subtile location - */ - bool locked(); - /** * @brief Check if the location is in the region (at a valid x, y, subtile location within the region bounds, inclusive) * If the region has no subtile specified, then the location subtile does not have to match. If it does, the location diff --git a/vpr/src/base/vpr_api.cpp b/vpr/src/base/vpr_api.cpp index 5c1587d9c91..54f053ec6a7 100644 --- a/vpr/src/base/vpr_api.cpp +++ b/vpr/src/base/vpr_api.cpp @@ -70,6 +70,7 @@ #include "output_clustering.h" #include "vpr_constraints_reader.h" #include "place_constraints.h" +#include "place_util.h" #include "vpr_constraints_writer.h" @@ -684,6 +685,9 @@ void vpr_load_placement(t_vpr_setup& vpr_setup, const t_arch& arch) { auto& place_ctx = g_vpr_ctx.mutable_placement(); const auto& filename_opts = vpr_setup.FileNameOpts; + //Initialize placement data structures, which will be filled when loading placement + init_placement_context(); + //Load an existing placement from a file read_place(filename_opts.NetFile.c_str(), filename_opts.PlaceFile.c_str(), filename_opts.verify_file_digests, device_ctx.grid); diff --git a/vpr/src/place/initial_placement.cpp b/vpr/src/place/initial_placement.cpp index 8bda8f3d3a2..23fd8683b18 100644 --- a/vpr/src/place/initial_placement.cpp +++ b/vpr/src/place/initial_placement.cpp @@ -479,6 +479,10 @@ void initial_placement(enum e_pad_loc_type pad_loc_type, const char* constraints read_constraints(constraints_file); } + /*Mark the blocks that have already been locked to one spot via floorplan constraints + * as fixed so they do not get moved during initial placement or during simulated annealing*/ + mark_fixed_blocks(); + initial_placement_pl_macros(MAX_NUM_TRIES_TO_PLACE_MACROS_RANDOMLY, free_locations); // All the macros are placed, update the legal_pos[][] array and free_locations[] array diff --git a/vpr/src/place/place.cpp b/vpr/src/place/place.cpp index aabd02c95a9..f6a2fa9762b 100644 --- a/vpr/src/place/place.cpp +++ b/vpr/src/place/place.cpp @@ -2644,8 +2644,8 @@ static int check_block_placement_consistency() { for (size_t i = 0; i < device_ctx.grid.width(); i++) for (size_t j = 0; j < device_ctx.grid.height(); j++) { if (place_ctx.grid_blocks[i][j].usage > device_ctx.grid[i][j].type->capacity) { - VTR_LOG_ERROR("Block at grid location (%zu,%zu) overused. Usage is %d.\n", - i, j, place_ctx.grid_blocks[i][j].usage); + VTR_LOG_ERROR("%d blocks were placed at grid location (%zu,%zu), but location capacity is %d.\n", + place_ctx.grid_blocks[i][j].usage, i, j, device_ctx.grid[i][j].type->capacity); error++; } int usage_check = 0; @@ -2674,8 +2674,8 @@ static int check_block_placement_consistency() { bdone[bnum]++; } if (usage_check != place_ctx.grid_blocks[i][j].usage) { - VTR_LOG_ERROR("Location (%zu,%zu) usage is %d, but has actual usage %d.\n", - i, j, place_ctx.grid_blocks[i][j].usage, usage_check); + VTR_LOG_ERROR("%d block(s) were placed at location (%zu,%zu), but location contains %d block(s).\n", + place_ctx.grid_blocks[i][j].usage, i, j, usage_check); error++; } } @@ -2683,7 +2683,7 @@ static int check_block_placement_consistency() { /* Check that every block exists in the device_ctx.grid and cluster_ctx.blocks arrays somewhere. */ for (auto blk_id : cluster_ctx.clb_nlist.blocks()) if (bdone[blk_id] != 1) { - VTR_LOG_ERROR("Block %zu listed %d times in data structures.\n", + VTR_LOG_ERROR("Block %zu listed %d times in device context grid.\n", size_t(blk_id), bdone[blk_id]); error++; } diff --git a/vpr/src/place/place_constraints.cpp b/vpr/src/place/place_constraints.cpp index 1a07b65737b..43a8231fe3c 100644 --- a/vpr/src/place/place_constraints.cpp +++ b/vpr/src/place/place_constraints.cpp @@ -10,6 +10,7 @@ #include "globals.h" #include "place_constraints.h" +#include "place_util.h" /*checks that each block's location is compatible with its floorplanning constraints if it has any*/ int check_placement_floorplanning() { @@ -257,3 +258,158 @@ void load_cluster_constraints() { } } } + +void mark_fixed_blocks() { + auto& cluster_ctx = g_vpr_ctx.clustering(); + auto& place_ctx = g_vpr_ctx.mutable_placement(); + auto& floorplanning_ctx = g_vpr_ctx.floorplanning(); + + for (auto blk_id : cluster_ctx.clb_nlist.blocks()) { + if (!is_cluster_constrained(blk_id)) { + continue; + } + PartitionRegion pr = floorplanning_ctx.cluster_constraints[blk_id]; + auto block_type = cluster_ctx.clb_nlist.block_type(blk_id); + t_pl_loc loc; + + /* + * If the block can be placed in exactly one + * legal (x, y, subtile) location, place it now + * and mark it as fixed. + */ + if (is_pr_size_one(pr, block_type, loc)) { + set_block_location(blk_id, loc); + + place_ctx.block_locs[blk_id].is_fixed = true; + } + } +} + +/* + * Returns 0, 1, or 2 depending on the number of tiles covered. + * Will not return a value above 2 because as soon as num_tiles is above 1, + * it is known that the block that is assigned to this region will not be fixed, and so + * num_tiles is immediately returned. + * Updates the location passed in because if num_tiles turns out to be 1 after checking the + * region, the location that was set will be used as the location to which the block + * will be fixed. + */ +int region_tile_cover(const Region& reg, t_logical_block_type_ptr block_type, t_pl_loc& loc) { + auto& device_ctx = g_vpr_ctx.device(); + vtr::Rect rb = reg.get_region_rect(); + int num_tiles = 0; + + for (int x = rb.xmin(); x <= rb.xmax(); x++) { + for (int y = rb.ymin(); y <= rb.ymax(); y++) { + auto& tile = device_ctx.grid[x][y].type; + + /* + * If the tile at the grid location is not compatible with the cluster block + * type, do not count this tile for num_tiles + */ + if (!is_tile_compatible(tile, block_type)) { + continue; + } + + /* + * If the region passed has a specific subtile set, increment + * the number of tiles set the location using the x, y, subtile + * values if the subtile is compatible at this location + */ + if (reg.get_sub_tile() != NO_SUBTILE) { + if (is_sub_tile_compatible(tile, block_type, reg.get_sub_tile())) { + num_tiles++; + loc.x = x; + loc.y = y; + loc.sub_tile = reg.get_sub_tile(); + if (num_tiles > 1) { + return num_tiles; + } + } + + /* + * If the region passed in does not have a subtile set, set the + * subtile to the first possible slot found at this location. + */ + } else if (reg.get_sub_tile() == NO_SUBTILE) { + int num_compatible_st = 0; + + for (int z = 0; z < tile->capacity; z++) { + if (is_sub_tile_compatible(tile, block_type, z)) { + num_tiles++; + num_compatible_st++; + if (num_compatible_st == 1) { //set loc.sub_tile to the first compatible subtile value found + loc.x = x; + loc.y = y; + loc.sub_tile = z; + } + if (num_tiles > 1) { + return num_tiles; + } + } + } + } + } + } + + return num_tiles; +} + +/* + * Used when marking fixed blocks to check whether the ParitionRegion associated with a block + * covers one tile. If it covers one tile, it is marked as fixed. If it covers 0 tiles or + * more than one tile, it will not be marked as fixed. As soon as it is known that the + * PartitionRegion covers more than one tile, there is no need to check further regions + * and the routine will return false. + */ +bool is_pr_size_one(PartitionRegion& pr, t_logical_block_type_ptr block_type, t_pl_loc& loc) { + auto& device_ctx = g_vpr_ctx.device(); + std::vector regions = pr.get_partition_region(); + bool pr_size_one; + int pr_size = 0; + int reg_size; + + Region intersect_reg; + intersect_reg.set_region_rect(0, 0, device_ctx.grid.width() - 1, device_ctx.grid.height() - 1); + Region current_reg; + + for (unsigned int i = 0; i < regions.size(); i++) { + reg_size = region_tile_cover(regions[i], block_type, loc); + + /* + * If multiple regions in the PartitionRegion all have size 1, + * the block may still be marked as locked, in the case that + * they all cover the exact same x, y, subtile location. To check whether this + * is the case, whenever there is a size 1 region, it is intersected + * with the previous size 1 regions to see whether it covers the same location. + * If there is an intersection, it does cover the same location, and so pr_size is + * not incremented (unless this is the first size 1 region encountered). + */ + if (reg_size == 1) { + //get the exact x, y, subtile location covered by the current region (regions[i]) + current_reg.set_region_rect(loc.x, loc.y, loc.x, loc.y); + current_reg.set_sub_tile(loc.sub_tile); + intersect_reg = intersection(intersect_reg, current_reg); + + if (i == 0 || intersect_reg.empty()) { + pr_size = pr_size + reg_size; + if (pr_size > 1) { + break; + } + } + } else { + pr_size = pr_size + reg_size; + if (pr_size > 1) { + break; + } + } + } + + if (pr_size == 1) { + pr_size_one = true; + } else { //pr_size = 0 or pr_size > 1 + pr_size_one = false; + } + + return pr_size_one; +} diff --git a/vpr/src/place/place_constraints.h b/vpr/src/place/place_constraints.h index d6287287689..ccbe4c9f64e 100644 --- a/vpr/src/place/place_constraints.h +++ b/vpr/src/place/place_constraints.h @@ -88,4 +88,29 @@ inline bool floorplan_legal(const t_pl_blocks_to_be_moved& blocks_affected) { */ void load_cluster_constraints(); +/* + * Marks blocks as fixed if they have a constraint region that specifies exactly one x, y, + * subtile location as legal. + * Marking them as fixed indicates that they cannot be moved during initial placement and simulated annealing + */ +void mark_fixed_blocks(); + +/* + * Returns the number of tiles covered by a floorplan region. + * The return value of this routine will either be 0, 1, or 2. This + * is because this routine is used to check whether the region covers no tile, + * one tile, or more than one tile, and so as soon as it is seen that the number of tiles + * covered is 2, no further information is needed. + */ +int region_tile_cover(const Region& reg, t_logical_block_type_ptr block_type, t_pl_loc& loc); + +/* + * Returns a bool that indicates if the PartitionRegion covers exactly one compatible location. + * Used to decide whether to mark a block with the .is_fixed flag based on its floorplan + * region. + * block_type is used to determine whether the PartitionRegion is compatible with the cluster block type + * and loc is updated with the location covered by the PartitionRegion + */ +bool is_pr_size_one(PartitionRegion& pr, t_logical_block_type_ptr block_type, t_pl_loc& loc); + #endif /* VPR_SRC_PLACE_PLACE_CONSTRAINTS_H_ */ diff --git a/vpr/src/place/place_util.cpp b/vpr/src/place/place_util.cpp index 74805f3d96e..e6e0ab6cad1 100644 --- a/vpr/src/place/place_util.cpp +++ b/vpr/src/place/place_util.cpp @@ -427,3 +427,38 @@ void alloc_and_load_legal_placement_locations(std::vector int(device_ctx.grid.width() - 1) + || location.y < 0 || location.y > int(device_ctx.grid.height() - 1)) { + VPR_THROW(VPR_ERROR_PLACE, "Block %s with ID %d is out of range at location (%d, %d). \n", block_name.c_str(), blk_id, location.x, location.y); + } + + //Set the location of the block + place_ctx.block_locs[blk_id].loc.x = location.x; + place_ctx.block_locs[blk_id].loc.y = location.y; + place_ctx.block_locs[blk_id].loc.sub_tile = location.sub_tile; + + //Check if block is at an illegal location + auto physical_tile = device_ctx.grid[location.x][location.y].type; + auto logical_block = cluster_ctx.clb_nlist.block_type(blk_id); + + if (location.sub_tile >= physical_tile->capacity || location.sub_tile < 0) { + VPR_THROW(VPR_ERROR_PLACE, "Block %s subtile number (%d) is out of range. \n", block_name.c_str(), location.sub_tile); + } + + if (!is_sub_tile_compatible(physical_tile, logical_block, place_ctx.block_locs[blk_id].loc.sub_tile)) { + VPR_THROW(VPR_ERROR_PLACE, "Attempt to place block %s with ID %d at illegal location (%d, %d). \n", block_name.c_str(), blk_id, location.x, location.y); + } + + //Mark the grid location and usage of the block + place_ctx.grid_blocks[location.x][location.y].blocks[location.sub_tile] = blk_id; + place_ctx.grid_blocks[location.x][location.y].usage++; +} diff --git a/vpr/src/place/place_util.h b/vpr/src/place/place_util.h index 2ed0bf2e714..30648dc895e 100644 --- a/vpr/src/place/place_util.h +++ b/vpr/src/place/place_util.h @@ -218,4 +218,7 @@ void load_grid_blocks_from_block_locs(); ///@brief Builds legal_pos structure. legal_pos[type->index] is an array that gives every legal value of (x,y,z) that can accommodate a block. void alloc_and_load_legal_placement_locations(std::vector>>& legal_pos); + +///@brief Performs error checking to see if location is legal for block type, and sets the location and grid usage of the block if it is legal. +void set_block_location(ClusterBlockId blk_id, const t_pl_loc& location); #endif diff --git a/vpr/test/test_vpr_constraints.cpp b/vpr/test/test_vpr_constraints.cpp index b0b1ad2afe6..c687c5f6b7e 100644 --- a/vpr/test/test_vpr_constraints.cpp +++ b/vpr/test/test_vpr_constraints.cpp @@ -29,14 +29,14 @@ TEST_CASE("Region", "[vpr]") { REQUIRE(rect.ymax() == 4); REQUIRE(r1.get_sub_tile() == 2); - //checking that default constructor creates an empty rectangle (-1,-1,-1,-1) + //checking that default constructor creates an empty rectangle (999, 999,-1,-1) Region def_region; bool is_def_empty = false; vtr::Rect def_rect = def_region.get_region_rect(); is_def_empty = def_rect.empty(); REQUIRE(is_def_empty == true); - REQUIRE(def_rect.xmin() == -1); + REQUIRE(def_rect.xmin() == 999); REQUIRE(def_region.get_sub_tile() == -1); } @@ -394,38 +394,6 @@ TEST_CASE("PartRegionIntersect6", "[vpr]") { REQUIRE(regions[3].get_region_rect() == int_r2r4); } -//Test the locked member function of the Region class -TEST_CASE("RegionLocked", "[vpr]") { - Region r1; - bool is_r1_locked = false; - - //set the region to a specific x, y, subtile location - region is locked - r1.set_region_rect(2, 3, 2, 3); //point (2,3) to point (2,3) - locking to specific x, y location - r1.set_sub_tile(3); //locking down to subtile 3 - - is_r1_locked = r1.locked(); - - REQUIRE(is_r1_locked == true); - - //do not set region to specific x, y location - region is not locked even if a subtile is specified - r1.set_region_rect(2, 3, 5, 6); //point (2,3) to point (5,6) - not locking to specific x, y location - r1.set_sub_tile(3); //locking down to subtile 3 - - is_r1_locked = r1.locked(); - - REQUIRE(is_r1_locked == false); - - //do not specify a subtile for the region - region is not locked even if it is set at specific x, y location - Region r2; - bool is_r2_locked = true; - - r2.set_region_rect(2, 3, 2, 3); - - is_r2_locked = r2.locked(); - - 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.