diff --git a/vpr/src/analytical_place/partial_placement.cpp b/vpr/src/analytical_place/partial_placement.cpp new file mode 100644 index 00000000000..491764bf186 --- /dev/null +++ b/vpr/src/analytical_place/partial_placement.cpp @@ -0,0 +1,102 @@ +/** + * @file + * @author Alex Singer + * @date September 2024 + * @brief The definitions of the PartialPlacement methods + */ + +#include "partial_placement.h" +#include +#include +#include "ap_netlist.h" + +bool PartialPlacement::verify_locs(const APNetlist& netlist, + size_t grid_width, + size_t grid_height) { + // Make sure all of the loc values are there. + if (block_x_locs.size() != netlist.blocks().size()) + return false; + if (block_y_locs.size() != netlist.blocks().size()) + return false; + // Make sure all locs are on the device and fixed locs are correct + for (APBlockId blk_id : netlist.blocks()) { + double x_pos = block_x_locs[blk_id]; + double y_pos = block_y_locs[blk_id]; + if (std::isnan(x_pos) || + x_pos < 0.0 || + x_pos >= grid_width) + return false; + if (std::isnan(y_pos) || + y_pos < 0.0 || + y_pos >= grid_height) + return false; + if (netlist.block_mobility(blk_id) == APBlockMobility::FIXED) { + const APFixedBlockLoc& fixed_loc = netlist.block_loc(blk_id); + if (fixed_loc.x != -1 && x_pos != fixed_loc.x) + return false; + if (fixed_loc.y != -1 && y_pos != fixed_loc.y) + return false; + } + } + // If all previous checks passed, return true + return true; +} + +bool PartialPlacement::verify_layer_nums(const APNetlist& netlist, + size_t grid_num_layers) { + // Make sure all of the layer nums are there + if (block_layer_nums.size() != netlist.blocks().size()) + return false; + // Make sure all layer_nums are on the device and fixed locs are correct. + for (APBlockId blk_id : netlist.blocks()) { + double layer_num = block_layer_nums[blk_id]; + if (layer_num < 0.0 || layer_num >= static_cast(grid_num_layers)) + return false; + if (netlist.block_mobility(blk_id) == APBlockMobility::FIXED) { + const APFixedBlockLoc& fixed_loc = netlist.block_loc(blk_id); + if (fixed_loc.layer_num != -1 && layer_num != fixed_loc.layer_num) + return false; + } + } + // If all previous checks passed, return true + return true; +} + +bool PartialPlacement::verify_sub_tiles(const APNetlist& netlist) { + // Make sure all of the sub tiles are there + if (block_sub_tiles.size() != netlist.blocks().size()) + return false; + // For now, we do not really do much with the sub_tile information. Ideally + // we should check that all blocks have a sub_tile that actually exists + // (a tile actually has a sub_tile of that idx); however, this may be + // challenging to enforce. For now, just ensure that the number is + // non-negative (implying a choice was made). + for (APBlockId blk_id : netlist.blocks()) { + int sub_tile = block_sub_tiles[blk_id]; + if (sub_tile < 0) + return false; + if (netlist.block_mobility(blk_id) == APBlockMobility::FIXED) { + const APFixedBlockLoc& fixed_loc = netlist.block_loc(blk_id); + if (fixed_loc.sub_tile != -1 && sub_tile != fixed_loc.sub_tile) + return false; + } + } + // If all previous checks passed, return true + return true; +} + +bool PartialPlacement::verify(const APNetlist& netlist, + size_t grid_width, + size_t grid_height, + size_t grid_num_layers) { + // Check that all the other verify methods passed. + if (!verify_locs(netlist, grid_width, grid_height)) + return false; + if (!verify_layer_nums(netlist, grid_num_layers)) + return false; + if (!verify_sub_tiles(netlist)) + return false; + // If all other verify methods passed, then the placement is valid. + return true; +} + diff --git a/vpr/src/analytical_place/partial_placement.h b/vpr/src/analytical_place/partial_placement.h new file mode 100644 index 00000000000..304e94c0784 --- /dev/null +++ b/vpr/src/analytical_place/partial_placement.h @@ -0,0 +1,151 @@ +/** + * @file + * @author Alex Singer + * @date September 2024 + * @brief The declaration of the PartialPlacement object + * + * The partial placement object stores the placement of the APNetlist blocks + * during different stages of the Analytical Placement flow. The Partial + * Placement need not represent a legal placement; however, the placed blocks + * will always be on the device and will respect fixed block locations. + */ + +#pragma once + +#include "ap_netlist.h" +#include "vtr_vector.h" + +/** + * @brief A partial placement during the Analytical Placement flow + * + * The goal of this class is to contain positional information about the blocks + * that gets passed around during analytical placement. + * + * The placement of a given APBlock is given by the following values: + * - x_loc The x-position of the APBlock on the device grid + * - y_loc The y-position of the APBlock on the device grid + * - layer_num The layer on the device grid this block is on + * - sub_tile The sub-tile this APBlock wants to be a part of + * TODO: Not sure if we want to keep the sub tile, but it may be useful for + * transferring information from the PartialLegalizer to the FullLegalizer. + * + * x_loc, y_loc, and layer_num are represented by doubles since APBlocks can be + * placed in continuous space, rather than constrained to the integer device + * grid of legal locations. These are doubles rather than floats since they + * better fit the matrix solver we currently use. + * + * layer_num is represented by an int since it is not decided by an analytical + * solver, but rather by the legalizers; so it does not need to be in continuous + * space. + * + * This object assumes that the APNetlist object is static during the operation + * of Analytical Placement (no blocks are added or removed). + * + * Note: The placement information was stored in this object as a struct of + * arrays instead of an array of structs since it is assumed that the + * main operations being performed on this placement will be analytical; + * thus the x positions and y positions will likely be stored and computed + * as separate continuous vectors. + */ +struct PartialPlacement { + /// @brief The x locations of all the APBlocks + vtr::vector block_x_locs; + /// @brief The y locations of all the APBlocks + vtr::vector block_y_locs; + /// @brief The layers of the device of all the APBlocks + vtr::vector block_layer_nums; + /// @brief The sub tiles of all the APBlocks + vtr::vector block_sub_tiles; + + // Remove the default constructor. Need to construct this using an APNetlist. + PartialPlacement() = delete; + + /** + * @brief Construct the partial placement + * + * Uses the APNetlist object to allocate space for the locations of all + * APBlocks. + * + * @param netlist The APNetlist which contains the blocks to be placed. + */ + PartialPlacement(const APNetlist& netlist) + : block_x_locs(netlist.blocks().size(), -1.0), + block_y_locs(netlist.blocks().size(), -1.0), + block_layer_nums(netlist.blocks().size(), 0.0), + block_sub_tiles(netlist.blocks().size(), 0) { + // Note: All blocks are initialized to: + // x_loc = -1.0 + // y_loc = -1.0 + // layer_num = 0.0 + // sub_tile = 0 + // Load the fixed block locations + for (APBlockId blk_id : netlist.blocks()) { + if (netlist.block_mobility(blk_id) != APBlockMobility::FIXED) + continue; + const APFixedBlockLoc &loc = netlist.block_loc(blk_id); + if (loc.x != -1) + block_x_locs[blk_id] = loc.x; + if (loc.y != -1) + block_y_locs[blk_id] = loc.y; + if (loc.layer_num != -1) + block_layer_nums[blk_id] = loc.layer_num; + if (loc.sub_tile != -1) + block_sub_tiles[blk_id] = loc.sub_tile; + } + } + + /** + * @brief Verify the block_x_locs and block_y_locs vectors + * + * Currently ensures: + * - All blocks have locs + * - All blocks are on the device + * - All fixed locs are correct + * + * @param netlist The APNetlist used to generate this placement + * @param grid_width The width of the device grid + * @param grid_height The height of the device grid + */ + bool verify_locs(const APNetlist& netlist, + size_t grid_width, + size_t grid_height); + + /** + * @brief Verify the block_layer_nums vector + * + * Currently ensures: + * - All blocks have layer_nums + * - All blocks are on the device + * - All fixed layers are correct + * + * @param netlist The APNetlist used to generate this placement + * @param grid_num_layers The number of layers in the device grid + */ + bool verify_layer_nums(const APNetlist& netlist, size_t grid_num_layers); + + /** + * @brief Verify the sub_tiles + * + * Currently ensures: + * - All sub_tiles are non-negative (implies unset) + * + * @param netlist The APNetlist used to generate this placement + */ + bool verify_sub_tiles(const APNetlist& netlist); + + /** + * @brief Verify the entire partial placement object + * + * Runs all of the verification checks above. + * + * @param netlist The APNetlist used to generate this placement + * @param grid_width The width of the device grid + * @param grid_height The height of the device grid + * @param grid_num_layers The number of layers in the device grid + */ + bool verify(const APNetlist& netlist, + size_t grid_width, + size_t grid_height, + size_t grid_num_layers); +}; + diff --git a/vpr/test/test_ap_partial_placement.cpp b/vpr/test/test_ap_partial_placement.cpp new file mode 100644 index 00000000000..c460df48227 --- /dev/null +++ b/vpr/test/test_ap_partial_placement.cpp @@ -0,0 +1,134 @@ +/** + * @file + * @author Alex Singer + * @date September 2024 + * @brief Unit tests for the PartialPlacement object + * + * Very quick functionality checks to make sure that the methods inside of the + * PartialPlacement object are working as expected. + */ + +#include "catch2/catch_test_macros.hpp" + +#include "ap_netlist.h" +#include "partial_placement.h" +#include "vpr_types.h" + +namespace { + +TEST_CASE("test_ap_partial_placement_verify", "[vpr_ap]") { + // Create a test netlist object. + APNetlist test_netlist("test_netlist"); + // Create a few molecules. + t_pack_molecule mol_a; + t_pack_molecule mol_b; + t_pack_molecule mol_c; + // Create blocks for these molecules. + APBlockId block_id_a = test_netlist.create_block("BlockA", &mol_a); + APBlockId block_id_b = test_netlist.create_block("BlockB", &mol_b); + APBlockId block_id_c = test_netlist.create_block("BlockC", &mol_c); + // Fix BlockC. + APFixedBlockLoc fixed_block_loc; + fixed_block_loc.x = 12; + fixed_block_loc.y = 42; + fixed_block_loc.layer_num = 2; + fixed_block_loc.sub_tile = 1; + test_netlist.set_block_loc(block_id_c, fixed_block_loc); + + // Create the PartialPlacement object. + PartialPlacement test_placement(test_netlist); + + SECTION("Test constructor places fixed blocks correctly") { + REQUIRE(test_placement.block_x_locs[block_id_c] == fixed_block_loc.x); + REQUIRE(test_placement.block_y_locs[block_id_c] == fixed_block_loc.y); + REQUIRE(test_placement.block_layer_nums[block_id_c] == fixed_block_loc.layer_num); + REQUIRE(test_placement.block_sub_tiles[block_id_c] == fixed_block_loc.sub_tile); + } + + // Place the blocks + // Place BlockA + test_placement.block_x_locs[block_id_a] = 0; + test_placement.block_y_locs[block_id_a] = 0; + test_placement.block_layer_nums[block_id_a] = 0; + test_placement.block_sub_tiles[block_id_a] = 0; + // Place BlockB + test_placement.block_x_locs[block_id_b] = 0; + test_placement.block_y_locs[block_id_b] = 0; + test_placement.block_layer_nums[block_id_b] = 0; + test_placement.block_sub_tiles[block_id_b] = 0; + + SECTION("Test verify returns true when the placement is valid") { + // NOTE: Using a very large device. + REQUIRE(test_placement.verify(test_netlist, 100, 100, 100)); + // Picking sizes that just fit. + REQUIRE(test_placement.verify(test_netlist, 13, 100, 100)); + REQUIRE(test_placement.verify(test_netlist, 100, 43, 100)); + REQUIRE(test_placement.verify(test_netlist, 100, 100, 3)); + REQUIRE(test_placement.verify(test_netlist, 13, 43, 3)); + } + + SECTION("Test verify methods all return true when verify returns true") { + REQUIRE(test_placement.verify_locs(test_netlist, 100, 100)); + REQUIRE(test_placement.verify_layer_nums(test_netlist, 100)); + REQUIRE(test_placement.verify_sub_tiles(test_netlist)); + } + + SECTION("Test verify returns false when blocks are outside grid") { + // NOTE: Picking device sizes that are just small enough that BlockC + // is off the device. + REQUIRE(!test_placement.verify_locs(test_netlist, 100, 1)); + REQUIRE(!test_placement.verify_locs(test_netlist, 1, 100)); + REQUIRE(!test_placement.verify_layer_nums(test_netlist, 1)); + // Make sure that the verify method also catches these cases + REQUIRE(!test_placement.verify(test_netlist, 100, 1, 100)); + REQUIRE(!test_placement.verify(test_netlist, 1, 100, 100)); + REQUIRE(!test_placement.verify(test_netlist, 100, 100, 1)); + // Move BlockA off the grid into the negative direction + test_placement.block_x_locs[block_id_a] = -1; + REQUIRE(!test_placement.verify_locs(test_netlist, 100, 100)); + REQUIRE(!test_placement.verify(test_netlist, 100, 100, 100)); + test_placement.block_x_locs[block_id_a] = 0; + test_placement.block_y_locs[block_id_a] = -1; + REQUIRE(!test_placement.verify_locs(test_netlist, 100, 100)); + REQUIRE(!test_placement.verify(test_netlist, 100, 100, 100)); + test_placement.block_y_locs[block_id_a] = 0; + test_placement.block_layer_nums[block_id_a] = -1; + REQUIRE(!test_placement.verify_layer_nums(test_netlist, 100)); + REQUIRE(!test_placement.verify(test_netlist, 100, 100, 100)); + test_placement.block_layer_nums[block_id_a] = 0; + test_placement.block_sub_tiles[block_id_a] = -1; + REQUIRE(!test_placement.verify_sub_tiles(test_netlist)); + REQUIRE(!test_placement.verify(test_netlist, 100, 100, 100)); + test_placement.block_sub_tiles[block_id_a] = 0; + // Make sure everything is valid again + REQUIRE(test_placement.verify(test_netlist, 100, 100, 100)); + } + + SECTION("Test verify returns false when fixed blocks are moved") { + // Move BlockC's x-coordinate + test_placement.block_x_locs[block_id_c] = 0; + REQUIRE(!test_placement.verify_locs(test_netlist, 100, 100)); + REQUIRE(!test_placement.verify(test_netlist, 100, 100, 100)); + test_placement.block_x_locs[block_id_c] = fixed_block_loc.x; + // Move BlockC's y-coordinate + test_placement.block_y_locs[block_id_c] = 0; + REQUIRE(!test_placement.verify_locs(test_netlist, 100, 100)); + REQUIRE(!test_placement.verify(test_netlist, 100, 100, 100)); + test_placement.block_y_locs[block_id_c] = fixed_block_loc.y; + // Move BlockC's layer num + test_placement.block_layer_nums[block_id_c] = 0; + REQUIRE(!test_placement.verify_layer_nums(test_netlist, 100)); + REQUIRE(!test_placement.verify(test_netlist, 100, 100, 100)); + test_placement.block_layer_nums[block_id_c] = fixed_block_loc.layer_num; + // Move BlockC's sub tile + test_placement.block_sub_tiles[block_id_c] = 0; + REQUIRE(!test_placement.verify_sub_tiles(test_netlist)); + REQUIRE(!test_placement.verify(test_netlist, 100, 100, 100)); + test_placement.block_sub_tiles[block_id_c] = fixed_block_loc.sub_tile; + // Make sure everything was put back correctly. + REQUIRE(test_placement.verify(test_netlist, 100, 100, 100)); + } +} + +} // namespace +