Skip to content

Commit

Permalink
Merge pull request verilog-to-routing#2742 from AlexandreSinger/featu…
Browse files Browse the repository at this point in the history
…re-ap-partial-placement-upstreaming

[AP] Added Partial Placement Object
  • Loading branch information
vaughnbetz authored Sep 27, 2024
2 parents 372f0dc + 2b6a935 commit 40797ae
Show file tree
Hide file tree
Showing 3 changed files with 387 additions and 0 deletions.
102 changes: 102 additions & 0 deletions vpr/src/analytical_place/partial_placement.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/**
* @file
* @author Alex Singer
* @date September 2024
* @brief The definitions of the PartialPlacement methods
*/

#include "partial_placement.h"
#include <cmath>
#include <cstddef>
#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<double>(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;
}

151 changes: 151 additions & 0 deletions vpr/src/analytical_place/partial_placement.h
Original file line number Diff line number Diff line change
@@ -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<APBlockId, double> block_x_locs;
/// @brief The y locations of all the APBlocks
vtr::vector<APBlockId, double> block_y_locs;
/// @brief The layers of the device of all the APBlocks
vtr::vector<APBlockId, double> block_layer_nums;
/// @brief The sub tiles of all the APBlocks
vtr::vector<APBlockId, int> 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);
};

134 changes: 134 additions & 0 deletions vpr/test/test_ap_partial_placement.cpp
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit 40797ae

Please sign in to comment.