From 2b6a935494363f8be1a83a05253c9bd70106e400 Mon Sep 17 00:00:00 2001 From: AlexandreSinger Date: Fri, 20 Sep 2024 22:02:27 -0400 Subject: [PATCH] [AP] Added Partial Placement Object Added the partial placement object. This object is used during the Analytical Placement flow to store the positions of the APBlocks. This placement is not gaurenteed to be legal; however, it does gaurentee that the blocks are placed on the device and the fixed blocks are placed in valid locations. --- .../analytical_place/partial_placement.cpp | 102 ++++++++++++ vpr/src/analytical_place/partial_placement.h | 151 ++++++++++++++++++ vpr/test/test_ap_partial_placement.cpp | 134 ++++++++++++++++ 3 files changed, 387 insertions(+) create mode 100644 vpr/src/analytical_place/partial_placement.cpp create mode 100644 vpr/src/analytical_place/partial_placement.h create mode 100644 vpr/test/test_ap_partial_placement.cpp 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 +