diff --git a/vpr/src/analytical_place/full_legalizer.cpp b/vpr/src/analytical_place/full_legalizer.cpp index 1383a73b99b..97a6b518c3a 100644 --- a/vpr/src/analytical_place/full_legalizer.cpp +++ b/vpr/src/analytical_place/full_legalizer.cpp @@ -7,7 +7,6 @@ #include "full_legalizer.h" -#include #include #include #include @@ -27,6 +26,7 @@ #include "physical_types.h" #include "place_constraints.h" #include "place_macro.h" +#include "verify_placement.h" #include "vpr_api.h" #include "vpr_context.h" #include "vpr_error.h" @@ -396,5 +396,16 @@ void FullLegalizer::legalize(const PartialPlacement& p_placement) { // Place the clusters based on where the atoms want to be placed. place_clusters(clb_nlist, p_placement); + + // Verify that the placement created by the full legalizer is valid. + unsigned num_placement_errors = verify_placement(g_vpr_ctx); + if (num_placement_errors == 0) { + VTR_LOG("Completed placement consistency check successfully.\n"); + } else { + VPR_ERROR(VPR_ERROR_AP, + "Completed placement consistency check, %u errors found.\n" + "Aborting program.\n", + num_placement_errors); + } } diff --git a/vpr/src/base/vpr_api.cpp b/vpr/src/base/vpr_api.cpp index e824f20f712..60490eecdc2 100644 --- a/vpr/src/base/vpr_api.cpp +++ b/vpr/src/base/vpr_api.cpp @@ -793,6 +793,9 @@ bool vpr_place_flow(const Netlist<>& net_list, t_vpr_setup& vpr_setup, const t_a vpr_load_placement(vpr_setup, arch); } + // FIXME: This synchronization is not consistent with the rest of + // placement. This requires it to happen after the placement is + // verified. See issue #2801 sync_grid_to_blocks(); post_place_sync(); } diff --git a/vpr/src/place/place.cpp b/vpr/src/place/place.cpp index e41fbcbd2ba..9a3884abc20 100644 --- a/vpr/src/place/place.cpp +++ b/vpr/src/place/place.cpp @@ -8,6 +8,9 @@ #include #include "NetPinTimingInvalidator.h" +#include "clustered_netlist.h" +#include "device_grid.h" +#include "verify_placement.h" #include "vtr_assert.h" #include "vtr_log.h" #include "vtr_util.h" @@ -228,11 +231,6 @@ static int check_placement_costs(const t_placer_costs& costs, PlacerState& placer_state, NetCostHandler& net_cost_handler); - -static int check_placement_consistency(const BlkLocRegistry& blk_loc_registry); -static int check_block_placement_consistency(const BlkLocRegistry& blk_loc_registry); -static int check_macro_placement_consistency(const BlkLocRegistry& blk_loc_registry); - static float starting_t(const t_annealing_state* state, t_placer_costs* costs, t_annealing_sched annealing_sched, @@ -1943,12 +1941,19 @@ static void check_place(const t_placer_costs& costs, * every block, blocks are in legal spots, etc. Also recomputes * * the final placement cost from scratch and makes sure it is * * within roundoff of what we think the cost is. */ + const ClusteredNetlist& clb_nlist = g_vpr_ctx.clustering().clb_nlist; + const DeviceGrid& device_grid = g_vpr_ctx.device().grid; + const auto& cluster_constraints = g_vpr_ctx.floorplanning().cluster_constraints; int error = 0; - error += check_placement_consistency(placer_state.blk_loc_registry()); + // Verify the placement invariants independent to the placement flow. + error += verify_placement(placer_state.blk_loc_registry(), + clb_nlist, + device_grid, + cluster_constraints); + error += check_placement_costs(costs, delay_model, criticalities, place_algorithm, placer_state, net_cost_handler); - error += check_placement_floorplanning(placer_state.block_locs()); if (noc_opts.noc) { // check the NoC costs during placement if the user is using the NoC supported flow @@ -2000,133 +2005,6 @@ static int check_placement_costs(const t_placer_costs& costs, return error; } -static int check_placement_consistency(const BlkLocRegistry& blk_loc_registry) { - return check_block_placement_consistency(blk_loc_registry) + check_macro_placement_consistency(blk_loc_registry); -} - -static int check_block_placement_consistency(const BlkLocRegistry& blk_loc_registry) { - auto& cluster_ctx = g_vpr_ctx.clustering(); - auto& device_ctx = g_vpr_ctx.device(); - const auto& block_locs = blk_loc_registry.block_locs(); - const auto& grid_blocks = blk_loc_registry.grid_blocks(); - - int error = 0; - - vtr::vector bdone(cluster_ctx.clb_nlist.blocks().size(), 0); - - /* Step through device grid and placement. Check it against blocks */ - for (int layer_num = 0; layer_num < (int)device_ctx.grid.get_num_layers(); layer_num++) { - for (int i = 0; i < (int)device_ctx.grid.width(); i++) { - for (int j = 0; j < (int)device_ctx.grid.height(); j++) { - const t_physical_tile_loc tile_loc(i, j, layer_num); - const auto& type = device_ctx.grid.get_physical_type(tile_loc); - if (grid_blocks.get_usage(tile_loc) > type->capacity) { - VTR_LOG_ERROR( - "%d blocks were placed at grid location (%d,%d,%d), but location capacity is %d.\n", - grid_blocks.get_usage(tile_loc), i, j, layer_num, type->capacity); - error++; - } - int usage_check = 0; - for (int k = 0; k < type->capacity; k++) { - ClusterBlockId bnum = grid_blocks.block_at_location({i, j, k, layer_num}); - if (bnum == ClusterBlockId::INVALID()) { - continue; - } - - auto logical_block = cluster_ctx.clb_nlist.block_type(bnum); - auto physical_tile = type; - t_pl_loc block_loc = block_locs[bnum].loc; - - if (physical_tile_type(block_loc) != physical_tile) { - VTR_LOG_ERROR( - "Block %zu type (%s) does not match grid location (%zu,%zu, %d) type (%s).\n", - size_t(bnum), logical_block->name.c_str(), i, j, layer_num, physical_tile->name.c_str()); - error++; - } - - auto& loc = block_locs[bnum].loc; - if (loc.x != i || loc.y != j || loc.layer != layer_num - || !is_sub_tile_compatible(physical_tile, logical_block, - loc.sub_tile)) { - VTR_LOG_ERROR( - "Block %zu's location is (%d,%d,%d,%d) but found in grid at (%d,%d,%d,%d).\n", - size_t(bnum), - loc.x, - loc.y, - loc.sub_tile, - loc.layer, - i, - j, - k, - layer_num); - error++; - } - ++usage_check; - bdone[bnum]++; - } - if (usage_check != grid_blocks.get_usage(tile_loc)) { - VTR_LOG_ERROR( - "%d block(s) were placed at location (%d,%d,%d), but location contains %d block(s).\n", - grid_blocks.get_usage(tile_loc), - tile_loc.x, - tile_loc.y, - tile_loc.layer_num, - usage_check); - error++; - } - } - } - } - - /* Check that every block exists in the device_ctx.grid and cluster_ctx.blocks arrays somewhere. */ - for (ClusterBlockId blk_id : cluster_ctx.clb_nlist.blocks()) - if (bdone[blk_id] != 1) { - VTR_LOG_ERROR("Block %zu listed %d times in device context grid.\n", - size_t(blk_id), bdone[blk_id]); - error++; - } - - return error; -} - -int check_macro_placement_consistency(const BlkLocRegistry& blk_loc_registry) { - const auto& pl_macros = blk_loc_registry.place_macros().macros(); - const auto& block_locs = blk_loc_registry.block_locs(); - const auto& grid_blocks = blk_loc_registry.grid_blocks(); - - int error = 0; - - /* Check the pl_macro placement are legal - blocks are in the proper relative position. */ - for (size_t imacro = 0; imacro < pl_macros.size(); imacro++) { - auto head_iblk = pl_macros[imacro].members[0].blk_index; - - for (size_t imember = 0; imember < pl_macros[imacro].members.size(); imember++) { - auto member_iblk = pl_macros[imacro].members[imember].blk_index; - - // Compute the supposed member's x,y,z location - t_pl_loc member_pos = block_locs[head_iblk].loc + pl_macros[imacro].members[imember].offset; - - // Check the blk_loc_registry.block_locs data structure first - if (block_locs[member_iblk].loc != member_pos) { - VTR_LOG_ERROR( - "Block %zu in pl_macro #%zu is not placed in the proper orientation.\n", - size_t(member_iblk), imacro); - error++; - } - - // Then check the blk_loc_registry.grid data structure - if (grid_blocks.block_at_location(member_pos) != member_iblk) { - VTR_LOG_ERROR( - "Block %zu in pl_macro #%zu is not placed in the proper orientation.\n", - size_t(member_iblk), imacro); - error++; - } - } // Finish going through all the members - } // Finish going through all the macros - - return error; -} - #ifdef VERBOSE void print_clb_placement(const char* fname) { /* Prints out the clb placements to a file. */ diff --git a/vpr/src/place/place_constraints.cpp b/vpr/src/place/place_constraints.cpp index f53efc0f4ef..2fee09e9d7b 100644 --- a/vpr/src/place/place_constraints.cpp +++ b/vpr/src/place/place_constraints.cpp @@ -13,21 +13,6 @@ #include "place_util.h" #include "vpr_context.h" -int check_placement_floorplanning(const vtr::vector_map& block_locs) { - int error = 0; - auto& cluster_ctx = g_vpr_ctx.clustering(); - - for (ClusterBlockId blk_id : cluster_ctx.clb_nlist.blocks()) { - t_pl_loc loc = block_locs[blk_id].loc; - if (!cluster_floorplanning_legal(blk_id, loc)) { - error++; - VTR_LOG_ERROR("Block %zu is not in correct floorplanning region.\n", size_t(blk_id)); - } - } - - return error; -} - bool is_cluster_constrained(ClusterBlockId blk_id) { auto& floorplanning_ctx = g_vpr_ctx.floorplanning(); const PartitionRegion& pr = floorplanning_ctx.cluster_constraints[blk_id]; diff --git a/vpr/src/place/place_constraints.h b/vpr/src/place/place_constraints.h index 78497dd20f8..9e045178e77 100644 --- a/vpr/src/place/place_constraints.h +++ b/vpr/src/place/place_constraints.h @@ -15,15 +15,6 @@ #include "place_macro.h" #include "grid_tile_lookup.h" -/** - * @brief Check that placement of each block is within the floorplan constraint region - * of that block (if the block has any constraints). - * - * @param block_locs Contains the location where each clustered block is placed. - * @return int The number of errors (inconsistencies in adherence to floorplanning constraints). - */ -int check_placement_floorplanning(const vtr::vector_map& block_locs); - /** * @brief Check if the block has floorplanning constraints. * diff --git a/vpr/src/place/verify_placement.cpp b/vpr/src/place/verify_placement.cpp new file mode 100644 index 00000000000..7fca36d6d19 --- /dev/null +++ b/vpr/src/place/verify_placement.cpp @@ -0,0 +1,254 @@ +/** + * @file + * @author Alex Singer + * @date October 2024 + * @brief Definitions of the independent placement verifier. + * + * Most of this code originally came from place.cpp and place_constraints.cpp + * + * By design, these methods should not use any global variables and anything + * that it does not assume should be re-computed from scratch. This ensures that + * the placement is actually valid. + */ + +#include "verify_placement.h" +#include +#include "blk_loc_registry.h" +#include "clustered_netlist.h" +#include "device_grid.h" +#include "partition_region.h" +#include "physical_types.h" +#include "place_macro.h" +#include "vpr_context.h" +#include "vpr_types.h" +#include "vpr_utils.h" +#include "vtr_log.h" +#include "vtr_vector.h" + +/** + * @brief Check the the block placement and grid blocks are consistent with each + * other and are both valid. + * + * This method checks: + * - The grid blocks matches the block locations. + * - Blocks are not in invalid grid locations. + * - All clusters are placed. + * + * @param blk_loc_registry A registry containing the block locations and + * the blocks at each grid location. + * @param clb_nlist The clustered netlist being verified. + * @param device_grid The device grid being verified over. + * + * @return The number of errors in the block placement. + */ +static unsigned check_block_placement_consistency(const BlkLocRegistry& blk_loc_registry, + const ClusteredNetlist& clb_nlist, + const DeviceGrid& device_grid) { + const auto& block_locs = blk_loc_registry.block_locs(); + const auto& grid_blocks = blk_loc_registry.grid_blocks(); + + unsigned num_errors = 0; + + vtr::vector bdone(clb_nlist.blocks().size(), 0); + + /* Step through device grid and placement. Check it against blocks */ + for (int layer_num = 0; layer_num < (int)device_grid.get_num_layers(); layer_num++) { + for (int i = 0; i < (int)device_grid.width(); i++) { + for (int j = 0; j < (int)device_grid.height(); j++) { + const t_physical_tile_loc tile_loc(i, j, layer_num); + const auto& type = device_grid.get_physical_type(tile_loc); + if (grid_blocks.get_usage(tile_loc) > type->capacity) { + VTR_LOG_ERROR( + "%d blocks were placed at grid location (%d,%d,%d), but location capacity is %d.\n", + grid_blocks.get_usage(tile_loc), i, j, layer_num, type->capacity); + num_errors++; + } + + int usage_check = 0; + for (int k = 0; k < type->capacity; k++) { + ClusterBlockId bnum = grid_blocks.block_at_location({i, j, k, layer_num}); + if (bnum == ClusterBlockId::INVALID()) { + continue; + } + + auto logical_block = clb_nlist.block_type(bnum); + auto physical_tile = type; + t_pl_loc block_loc = block_locs[bnum].loc; + + if (physical_tile_type(block_loc) != physical_tile) { + VTR_LOG_ERROR( + "Block %zu type (%s) does not match grid location (%zu,%zu, %d) type (%s).\n", + size_t(bnum), logical_block->name.c_str(), i, j, layer_num, physical_tile->name.c_str()); + num_errors++; + } + + auto& loc = block_locs[bnum].loc; + if (loc.x != i || loc.y != j || loc.layer != layer_num + || !is_sub_tile_compatible(physical_tile, logical_block, + loc.sub_tile)) { + VTR_LOG_ERROR( + "Block %zu's location is (%d,%d,%d,%d) but found in grid at (%d,%d,%d,%d).\n", + size_t(bnum), + loc.x, + loc.y, + loc.sub_tile, + loc.layer, + i, + j, + k, + layer_num); + num_errors++; + } + ++usage_check; + bdone[bnum]++; + } + if (usage_check != grid_blocks.get_usage(tile_loc)) { + VTR_LOG_ERROR( + "%d block(s) were placed at location (%d,%d,%d), but location contains %d block(s).\n", + grid_blocks.get_usage(tile_loc), + tile_loc.x, + tile_loc.y, + tile_loc.layer_num, + usage_check); + num_errors++; + } + } + } + } + + /* Check that every block exists in the device_ctx.grid and cluster_ctx.blocks arrays somewhere. */ + for (ClusterBlockId blk_id : clb_nlist.blocks()) + if (bdone[blk_id] != 1) { + VTR_LOG_ERROR("Block %zu listed %d times in device context grid.\n", + size_t(blk_id), bdone[blk_id]); + num_errors++; + } + + return num_errors; +} + +/** + * @brief Check that the macro placement is consistent + * + * This checks that the pl_macro placement is legal by making sure that blocks + * are in the proper relative positions. + * + * It is assumed in this method that the macros were initialized properly. + * FIXME: This needs to be verified somewhere in the flow. + * + * @param blk_loc_registry A registry containing the block locations and + * the blocks at each grid location. + * + * @return The number of errors in the macro placement. + */ +static unsigned check_macro_placement_consistency(const BlkLocRegistry& blk_loc_registry) { + const PlaceMacros& pl_macros = blk_loc_registry.place_macros(); + const auto& block_locs = blk_loc_registry.block_locs(); + const auto& grid_blocks = blk_loc_registry.grid_blocks(); + + unsigned num_errors = 0; + + /* Check the pl_macro placement are legal - blocks are in the proper relative position. */ + for (size_t imacro = 0; imacro < pl_macros.macros().size(); imacro++) { + auto head_iblk = pl_macros[imacro].members[0].blk_index; + + for (size_t imember = 0; imember < pl_macros[imacro].members.size(); imember++) { + auto member_iblk = pl_macros[imacro].members[imember].blk_index; + + // Compute the supposed member's x,y,z location + t_pl_loc member_pos = block_locs[head_iblk].loc + pl_macros[imacro].members[imember].offset; + + // Check the blk_loc_registry.block_locs data structure first + if (block_locs[member_iblk].loc != member_pos) { + VTR_LOG_ERROR( + "Block %zu in pl_macro #%zu is not placed in the proper orientation.\n", + size_t(member_iblk), imacro); + num_errors++; + } + + // Then check the blk_loc_registry.grid data structure + if (grid_blocks.block_at_location(member_pos) != member_iblk) { + VTR_LOG_ERROR( + "Block %zu in pl_macro #%zu is not placed in the proper orientation.\n", + size_t(member_iblk), imacro); + num_errors++; + } + } // Finish going through all the members + } // Finish going through all the macros + + return num_errors; +} + +/** + * @brief Check that the placement is following the floorplanning constraints. + * + * This checks that all constrained blocks are placed within their floorplanning + * constraints. + * + * @param blk_loc_registry A registry containing the block locations and + * the blocks at each grid location. + * @param cluster_constraints The constraints on the placement of each + * cluster. + * @param clb_nlist The clustered netlist being verified. + * + * @return The number of errors in the placement regarding floorplanning. + */ +static unsigned check_placement_floorplanning(const BlkLocRegistry& blk_loc_registry, + const vtr::vector& cluster_constraints, + const ClusteredNetlist& clb_nlist) { + const auto& block_locs = blk_loc_registry.block_locs(); + + unsigned num_errors = 0; + for (ClusterBlockId blk_id : clb_nlist.blocks()) { + const PartitionRegion& blk_pr = cluster_constraints[blk_id]; + // If the cluster is not constrained, no need to check. + if (blk_pr.empty()) + continue; + // Check if the block is placed in its constrained partition region. + const t_pl_loc& blk_loc = block_locs[blk_id].loc; + if (!blk_pr.is_loc_in_part_reg(blk_loc)) { + VTR_LOG_ERROR( + "Block %zu is not in correct floorplanning region.\n", + size_t(blk_id)); + num_errors++; + } + } + return num_errors; +} + +unsigned verify_placement(const BlkLocRegistry& blk_loc_registry, + const ClusteredNetlist& clb_nlist, + const DeviceGrid& device_grid, + const vtr::vector& cluster_constraints) { + // TODO: Verify the assumptions with an assert_debug. + // - Once a verify_clustering method is written, it would make sense to + // call it here in debug mode. + unsigned num_errors = 0; + + // Check that the block placement is valid. + num_errors += check_block_placement_consistency(blk_loc_registry, + clb_nlist, + device_grid); + + // Check that the pl_macros are observed. + // FIXME: Should we be checking the macro consistency at all? Does the + // router use the pl_macros? If not this should be removed from this + // method and only used when the macro placement is actually used. + num_errors += check_macro_placement_consistency(blk_loc_registry); + + // Check that the floorplanning is observed. + num_errors += check_placement_floorplanning(blk_loc_registry, + cluster_constraints, + clb_nlist); + + return num_errors; +} + +unsigned verify_placement(const VprContext& ctx) { + // Verify the placement within the given context. + return verify_placement(ctx.placement().blk_loc_registry(), + ctx.clustering().clb_nlist, + ctx.device().grid, + ctx.floorplanning().cluster_constraints); +} + diff --git a/vpr/src/place/verify_placement.h b/vpr/src/place/verify_placement.h new file mode 100644 index 00000000000..1bee823ea5e --- /dev/null +++ b/vpr/src/place/verify_placement.h @@ -0,0 +1,81 @@ +/** + * @file + * @author Alex Singer + * @date October 2024 + * @brief Independent verify methods to check invariants that ensure that the + * given placement is valid and can be used with the rest of the VPR + * flow. + * + * This methods were orginally part of methods found in the place.cpp file and + * the place_constraints.cpp files. Moved into here to put it all in one place. + * Since these methods should be completely independent of the place flow, it + * makes sense that they are in their own file. + */ + +#pragma once + +#include "vtr_vector.h" + +// Forward declarations +class BlkLocRegistry; +class ClusterBlockId; +class ClusteredNetlist; +class DeviceGrid; +class PartitionRegion; +class VprContext; + +/** + * @brief Verify the placement of the clustered blocks on the device. + * + * This is verifier is independent to how the placement was performed and check + * invariants that all placements must adhere to in order to be used in the + * rest of the VPR flow. The assumption is that if a placement passes this + * verifier it can be passed into routing without issue. + * + * By design, this verifier uses no global variables and tries to recompute + * anything that it does not assume. + * + * Invariants (are checked by this function): + * - All clusters are placed. + * - All clusters are placed in a tile, sub-tile that it can exist in. + * - The cluster locations are consistent with the clusters in each block. + * - The placement macros are consistent such that all blocks are in the + * proper relative positions. + * - All clusters are placed in legal positions according to the floorplanning + * constraints. + * + * Assumptions (are not checked by this function): + * - The clustered netlist is valid. + * - The device grid is valid. + * - The cluster constraints are valid. + * - The macros are valid. + * + * @param blk_loc_registry A registry containing the current placement of + * the clusters. + * @param clb_nlist The clustered netlist being verified. + * @param device_grid The device grid being verified over. + * @param cluster_constraints The constrained regions that each cluster is + * constrained to. + * + * @return The number of errors found in the placement. Will also print error + * log messages for each error found. + */ +unsigned verify_placement(const BlkLocRegistry& blk_loc_registry, + const ClusteredNetlist& clb_nlist, + const DeviceGrid& device_grid, + const vtr::vector& cluster_constraints); + +/** + * @brief Verifies the placement as it appears in the global context. + * + * This performs the verification in the method above, but performs it on the + * global VPR context itself. This verifies that the actual placement being + * used through the rest of the flow is valid. + * + * NOTE: The above method is in-case the user wishes to verify a temporary + * placement before updating the actual global VPR context. + * + * @param ctx The global VPR context variable found in g_vpr_ctx. + */ +unsigned verify_placement(const VprContext& ctx); +