Skip to content

Add constraints propagation to update the PartitionRegions of blocks … #1751

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Jun 15, 2021
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 15 additions & 8 deletions vpr/src/place/initial_placement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,20 +82,22 @@ static int check_macro_can_be_placed(t_pl_macro pl_macro, int itype, t_pl_loc he
// Every macro can be placed until proven otherwise
int macro_can_be_placed = true;

//Check whether macro contains blocks with floorplan constraints
bool macro_constrained = is_macro_constrained(pl_macro);
PartitionRegion macro_pr;

if (macro_constrained) {
macro_pr = constrained_macro_locs(pl_macro);
}

// Check whether all the members can be placed
for (size_t imember = 0; imember < pl_macro.members.size(); imember++) {
t_pl_loc member_pos = head_pos + pl_macro.members[imember].offset;

//if the macro is constrained, check if the member position is within the PartitionRegion for the macro
if (macro_constrained) {
bool member_loc_good = macro_pr.is_loc_in_part_reg(member_pos);
/*
* If the macro is constrained, check that the head member is in a legal position from
* a floorplanning perspective. It is enough to do this check for the head member alone,
* because constraints propagation was performed to calculate smallest floorplan region for the head
* macro, based on the constraints on all of the blocks in the macro. So, if the head macro is in a
* legal floorplan location, all other blocks in the macro will be as well.
*/
if (macro_constrained && imember == 0) {
bool member_loc_good = cluster_floorplanning_legal(pl_macro.members[imember].blk_index, member_pos);
if (!member_loc_good) {
macro_can_be_placed = false;
break;
Expand Down Expand Up @@ -417,6 +419,11 @@ void initial_placement(enum e_pad_loc_type pad_loc_type, const char* constraints
vtr::vector<ClusterBlockId, t_block_score> block_scores = assign_block_scores();
std::vector<ClusterBlockId> sorted_blocks = sort_blocks(block_scores);

/* Go through cluster blocks to calculate the tightest placement
* floorplan constraint for each constrained block
*/
propagate_place_constraints();

// Loading legal placement locations
zero_initialize_grid_blocks();
alloc_and_load_legal_placement_locations(legal_pos);
Expand Down
105 changes: 90 additions & 15 deletions vpr/src/place/place_constraints.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ bool is_macro_constrained(const t_pl_macro& pl_macro) {
}

/*Returns PartitionRegion of where the head of the macro could go*/
PartitionRegion constrained_macro_locs(const t_pl_macro& pl_macro) {
PartitionRegion macro_pr;
PartitionRegion update_macro_head_pr(const t_pl_macro& pl_macro, const PartitionRegion& grid_pr) {
PartitionRegion macro_head_pr;
bool is_member_constrained = false;
int num_constrained_members = 0;
auto& floorplanning_ctx = g_vpr_ctx.floorplanning();
Expand All @@ -80,39 +80,114 @@ PartitionRegion constrained_macro_locs(const t_pl_macro& pl_macro) {

vtr::Rect<int> reg_rect = block_regions[i].get_region_rect();

t_pl_loc min_pl_loc(reg_rect.xmin(), reg_rect.ymin(), block_regions[i].get_sub_tile());

t_pl_loc modified_min_pl_loc = min_pl_loc + offset;

t_pl_loc max_pl_loc(reg_rect.xmax(), reg_rect.ymax(), block_regions[i].get_sub_tile());
modified_reg.set_region_rect(reg_rect.xmin() - offset.x, reg_rect.ymin() - offset.y, reg_rect.xmax() - offset.x, reg_rect.ymax() - offset.y);

t_pl_loc modified_max_pl_loc = max_pl_loc + offset;

modified_reg.set_region_rect(modified_min_pl_loc.x, modified_min_pl_loc.y, modified_max_pl_loc.x, modified_max_pl_loc.y);
//check that subtile is not an invalid value before changing, otherwise it just stays -1
if (block_regions[i].get_sub_tile() != -1) {
modified_reg.set_sub_tile(modified_min_pl_loc.sub_tile);
if (block_regions[i].get_sub_tile() != NO_SUBTILE) {
modified_reg.set_sub_tile(block_regions[i].get_sub_tile() - offset.sub_tile);
}

modified_pr.add_to_part_region(modified_reg);
}

if (num_constrained_members == 1) {
macro_pr = modified_pr;
macro_head_pr = modified_pr;
} else {
macro_pr = intersection(macro_pr, modified_pr);
macro_head_pr = intersection(macro_head_pr, modified_pr);
}
}
}

//intersect to ensure the head pr does not go outside of grid dimensions
macro_head_pr = intersection(macro_head_pr, grid_pr);

//if the intersection is empty, no way to place macro members together, give an error
if (macro_head_pr.empty()) {
print_macro_constraint_error(pl_macro);
}

return macro_head_pr;
}

PartitionRegion update_macro_member_pr(PartitionRegion& head_pr, const t_pl_offset& offset, const PartitionRegion& grid_pr, const t_pl_macro& pl_macro) {
std::vector<Region> block_regions = head_pr.get_partition_region();
PartitionRegion macro_pr;

for (unsigned int i = 0; i < block_regions.size(); i++) {
Region modified_reg;

vtr::Rect<int> reg_rect = block_regions[i].get_region_rect();

modified_reg.set_region_rect(reg_rect.xmin() + offset.x, reg_rect.ymin() + offset.y, reg_rect.xmax() + offset.x, reg_rect.ymax() + offset.y);

//check that subtile is not an invalid value before changing, otherwise it just stays -1
if (block_regions[i].get_sub_tile() != NO_SUBTILE) {
modified_reg.set_sub_tile(block_regions[i].get_sub_tile() + offset.sub_tile);
}

macro_pr.add_to_part_region(modified_reg);
}

//intersect to ensure the macro pr does not go outside of grid dimensions
macro_pr = intersection(macro_pr, grid_pr);

//if the intersection is empty, no way to place macro members together, give an error
if (macro_pr.empty()) {
VPR_ERROR(VPR_ERROR_PLACE, " \n Feasible floorplanning constraints could not be calculated for the placement macro.\n");
print_macro_constraint_error(pl_macro);
}

return macro_pr;
}

void print_macro_constraint_error(const t_pl_macro& pl_macro) {
VTR_LOG(
"Feasible floorplanning constraints could not be calculated for the placement macro. \n"
"The placement macro contains the following blocks: \n");
for (unsigned int i = 0; i < pl_macro.members.size(); i++) {
VTR_LOG("Block #%zu ", size_t(pl_macro.members[i].blk_index));
}
VTR_LOG("\n");
VPR_ERROR(VPR_ERROR_PLACE, " \n Check that the above-mentioned placement macro blocks have compatible floorplan constraints.\n");
}

void propagate_place_constraints() {
auto& place_ctx = g_vpr_ctx.placement();
auto& floorplanning_ctx = g_vpr_ctx.mutable_floorplanning();
auto& device_ctx = g_vpr_ctx.device();

//Create a PartitionRegion with grid dimensions
//Will be used to check that updated PartitionRegions are within grid bounds
int width = device_ctx.grid.width() - 1;
int height = device_ctx.grid.height() - 1;
Region grid_reg;
grid_reg.set_region_rect(0, 0, width, height);
PartitionRegion grid_pr;
grid_pr.add_to_part_region(grid_reg);

for (auto pl_macro : place_ctx.pl_macros) {
if (is_macro_constrained(pl_macro)) {
/*
* Get the PartitionRegion for the head of the macro
* based on the constraints of all blocks contained in the macro
*/
PartitionRegion macro_head_pr = update_macro_head_pr(pl_macro, grid_pr);

//Update PartitionRegions of all members of the macro
for (size_t imember = 0; imember < pl_macro.members.size(); imember++) {
ClusterBlockId iblk = pl_macro.members[imember].blk_index;
auto offset = pl_macro.members[imember].offset;

if (imember == 0) { //Update head PR
floorplanning_ctx.cluster_constraints[iblk] = macro_head_pr;
} else { //Update macro member PR
PartitionRegion macro_pr = update_macro_member_pr(macro_head_pr, offset, grid_pr, pl_macro);
floorplanning_ctx.cluster_constraints[iblk] = macro_pr;
}
}
}
}
}

/*returns true if location is compatible with floorplanning constraints, false if not*/
/*
* Even if the block passed in is from a macro, it will work because of the constraints
Expand Down
28 changes: 26 additions & 2 deletions vpr/src/place/place_constraints.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,33 @@ bool cluster_floorplanning_legal(ClusterBlockId blk_id, const t_pl_loc& loc);
bool is_macro_constrained(const t_pl_macro& pl_macro);

/*
* Returns region of valid locations for the head of the macro based on floorplan constraints
* Returns PartitionRegion for the head of the macro based on the floorplan constraints
* of all blocks in the macro. For example, if there was a macro of length two and each block has
* a constraint, this routine will shift and intersect the two constraint regions to calculate
* the tightest region constraint for the head macro.
*/
PartitionRegion constrained_macro_locs(const t_pl_macro& pl_macro);
PartitionRegion update_macro_head_pr(const t_pl_macro& pl_macro, const PartitionRegion& grid_pr);

/*
* Update the PartitionRegions of non-head members of a macro,
* based on the constraint that was calculated for the head region, head_pr.
* The constraint on the head region must be the tightest possible (i.e. implied by the
* entire macro) before this routine is called.
* For each macro member, the updated constraint is essentially the head constraint
* with the member's offset applied.
*/
PartitionRegion update_macro_member_pr(PartitionRegion& head_pr, const t_pl_offset& offset, const PartitionRegion& grid_pr, const t_pl_macro& pl_macro);

/*
* Updates the floorplan constraints information for all constrained macros.
* Updates the constraints to be the tightest constraints possible while adhering
* to the floorplan constraints of each macro member.
* This is done at the start of initial placement to ease floorplan legality checking
* while placing macros during initial placement.
*/
void propagate_place_constraints();

void print_macro_constraint_error(const t_pl_macro& pl_macro);

inline bool floorplan_legal(const t_pl_blocks_to_be_moved& blocks_affected) {
bool floorplan_legal;
Expand Down
29 changes: 29 additions & 0 deletions vpr/test/test_vpr_constraints.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "vpr_constraints.h"
#include "partition.h"
#include "region.h"
#include "place_constraints.h"

/**
* This file contains unit tests that check the functionality of all classes related to vpr constraints. These classes include
Expand Down Expand Up @@ -425,6 +426,34 @@ TEST_CASE("RegionLocked", "[vpr]") {
REQUIRE(is_r2_locked == false);
}

//Test calculation of macro constraints
TEST_CASE("MacroConstraints", "[vpr]") {
t_pl_macro pl_macro;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is pl_macro initialized to? Maybe you're relying on the default constructor? Better to initialize it I think, and describe what the test does (macro of size 1, macro of size 2, ??).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The pl_macro wasn't initialized to anything. It is only passed in to update_macro_member_pr so that if there is an error the error message can be printed with the macro. This won't happen in the case of this unit test, since the test is just checking that the PartRegion is updated properly for a particular case. I can initialize it to something though, and yes, I'll add some comments about what the test is doing.

PartitionRegion head_pr;
t_pl_offset offset(2, 1, 0);

Region reg;
reg.set_region_rect(5, 2, 9, 6);

head_pr.add_to_part_region(reg);

Region grid_reg;
grid_reg.set_region_rect(0, 0, 20, 20);
PartitionRegion grid_pr;
grid_pr.add_to_part_region(grid_reg);

PartitionRegion macro_pr = update_macro_member_pr(head_pr, offset, grid_pr, pl_macro);

std::vector<Region> mac_regions = macro_pr.get_partition_region();

vtr::Rect<int> mac_rect = mac_regions[0].get_region_rect();

REQUIRE(mac_rect.xmin() == 7);
REQUIRE(mac_rect.ymin() == 3);
REQUIRE(mac_rect.xmax() == 11);
REQUIRE(mac_rect.ymax() == 7);
}

static constexpr const char kArchFile[] = "test_read_arch_metadata.xml";

// Test that place constraints are not changed during placement
Expand Down