Skip to content

Commit 99f2b7a

Browse files
authored
Merge pull request #1759 from verilog-to-routing/mark_fixed_blocks
Mark fixed blocks
2 parents f37d18e + 57ae843 commit 99f2b7a

11 files changed

+251
-88
lines changed

vpr/src/base/read_place.cpp

Lines changed: 11 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "hash.h"
1616
#include "read_place.h"
1717
#include "read_xml_arch_file.h"
18+
#include "place_util.h"
1819

1920
void read_place_header(
2021
std::ifstream& placement_file,
@@ -173,18 +174,12 @@ void read_place_body(std::ifstream& placement_file,
173174
const char* place_file,
174175
bool is_place_file) {
175176
auto& cluster_ctx = g_vpr_ctx.clustering();
176-
auto& device_ctx = g_vpr_ctx.device();
177177
auto& place_ctx = g_vpr_ctx.mutable_placement();
178178
auto& atom_ctx = g_vpr_ctx.atom();
179179

180180
std::string line;
181181
int lineno = 0;
182182

183-
if (place_ctx.block_locs.size() != cluster_ctx.clb_nlist.blocks().size()) {
184-
//Resize if needed
185-
place_ctx.block_locs.resize(cluster_ctx.clb_nlist.blocks().size());
186-
}
187-
188183
//used to count how many times a block has been seen in the place/constraints file so duplicate blocks can be detected
189184
vtr::vector_map<ClusterBlockId, int> seen_blocks;
190185

@@ -236,45 +231,26 @@ void read_place_body(std::ifstream& placement_file,
236231
//Check if block is listed multiple times with conflicting locations in constraints file
237232
if (seen_blocks[blk_id] > 0) {
238233
if (block_x != place_ctx.block_locs[blk_id].loc.x || block_y != place_ctx.block_locs[blk_id].loc.y || sub_tile_index != place_ctx.block_locs[blk_id].loc.sub_tile) {
234+
std::string cluster_name = cluster_ctx.clb_nlist.block_name(blk_id);
239235
VPR_THROW(VPR_ERROR_PLACE,
240-
"The location of cluster %d is specified %d times in the constraints file with conflicting locations. \n"
236+
"The location of cluster %s (#%d) is specified %d times in the constraints file with conflicting locations. \n"
241237
"Its location was last specified with block %s. \n",
242-
blk_id, seen_blocks[blk_id] + 1, c_block_name);
238+
cluster_name.c_str(), blk_id, seen_blocks[blk_id] + 1, c_block_name);
243239
}
244240
}
245241

246-
//Check if block location is out of range of grid dimensions
247-
if (block_x < 0 || block_x > int(device_ctx.grid.width() - 1)
248-
|| block_y < 0 || block_y > int(device_ctx.grid.height() - 1)) {
249-
VPR_THROW(VPR_ERROR_PLACE, "Block %s with ID %d is out of range at location (%d, %d). \n", c_block_name, blk_id, block_x, block_y);
250-
}
251-
252-
//Set the location
253-
place_ctx.block_locs[blk_id].loc.x = block_x;
254-
place_ctx.block_locs[blk_id].loc.y = block_y;
255-
place_ctx.block_locs[blk_id].loc.sub_tile = sub_tile_index;
242+
t_pl_loc loc;
243+
loc.x = block_x;
244+
loc.y = block_y;
245+
loc.sub_tile = sub_tile_index;
256246

257-
//Check if block is at an illegal location
258-
259-
auto physical_tile = device_ctx.grid[block_x][block_y].type;
260-
auto logical_block = cluster_ctx.clb_nlist.block_type(blk_id);
261-
262-
if (sub_tile_index >= physical_tile->capacity || sub_tile_index < 0) {
263-
VPR_THROW(VPR_ERROR_PLACE, "Block %s subtile number (%d) is out of range. \n", c_block_name, sub_tile_index);
247+
if (seen_blocks[blk_id] == 0) {
248+
set_block_location(blk_id, loc);
264249
}
265250

266-
if (!is_sub_tile_compatible(physical_tile, logical_block, place_ctx.block_locs[blk_id].loc.sub_tile)) {
267-
VPR_THROW(VPR_ERROR_PLACE, "Attempt to place block %s with ID %d at illegal location (%d, %d). \n", c_block_name, blk_id, block_x, block_y);
268-
}
269-
270-
//need to lock down blocks and mark grid block usage if it is a constraints file
271-
//for a place file, grid usage is marked during initial placement instead
251+
//need to lock down blocks if it is a constraints file
272252
if (!is_place_file) {
273253
place_ctx.block_locs[blk_id].is_fixed = true;
274-
place_ctx.grid_blocks[block_x][block_y].blocks[sub_tile_index] = blk_id;
275-
if (seen_blocks[blk_id] == 0) {
276-
place_ctx.grid_blocks[block_x][block_y].usage++;
277-
}
278254
}
279255

280256
//mark the block as seen

vpr/src/base/region.cpp

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33
Region::Region() {
44
sub_tile = NO_SUBTILE;
55

6-
//default rect for a region is (-1, -1, -1, -1)
6+
//default rect for a region is (999, 999, -1, -1)
77
//these values indicate an empty rectangle, they are set as default values to help catch uninitialized use
8-
region_bounds.set_xmin(-1);
9-
region_bounds.set_ymin(-1);
8+
region_bounds.set_xmin(999);
9+
region_bounds.set_ymin(999);
1010
region_bounds.set_xmax(-1);
1111
region_bounds.set_ymax(-1);
1212
}
@@ -30,12 +30,8 @@ void Region::set_sub_tile(int _sub_tile) {
3030
sub_tile = _sub_tile;
3131
}
3232

33-
bool Region::locked() {
34-
return region_bounds.xmin() == region_bounds.xmax() && region_bounds.ymin() == region_bounds.ymax() && sub_tile != NO_SUBTILE;
35-
}
36-
3733
bool Region::empty() {
38-
return region_bounds.empty();
34+
return (region_bounds.xmax() < region_bounds.xmin() || region_bounds.ymax() < region_bounds.ymin());
3935
}
4036

4137
bool Region::is_loc_in_reg(t_pl_loc loc) {

vpr/src/base/region.h

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,15 +44,11 @@ class Region {
4444
void set_sub_tile(int _sub_tile);
4545

4646
/**
47-
* @brief Return whether the region is empty, based on whether the region rectangle is empty
47+
* @brief Return whether the region is empty (i. e. the region bounds rectangle
48+
* covers no area)
4849
*/
4950
bool empty();
5051

51-
/**
52-
* @brief Checks whether a block is locked down to a specific x, y, subtile location
53-
*/
54-
bool locked();
55-
5652
/**
5753
* @brief Check if the location is in the region (at a valid x, y, subtile location within the region bounds, inclusive)
5854
* If the region has no subtile specified, then the location subtile does not have to match. If it does, the location

vpr/src/base/vpr_api.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@
7070
#include "output_clustering.h"
7171
#include "vpr_constraints_reader.h"
7272
#include "place_constraints.h"
73+
#include "place_util.h"
7374

7475
#include "vpr_constraints_writer.h"
7576

@@ -684,6 +685,9 @@ void vpr_load_placement(t_vpr_setup& vpr_setup, const t_arch& arch) {
684685
auto& place_ctx = g_vpr_ctx.mutable_placement();
685686
const auto& filename_opts = vpr_setup.FileNameOpts;
686687

688+
//Initialize placement data structures, which will be filled when loading placement
689+
init_placement_context();
690+
687691
//Load an existing placement from a file
688692
read_place(filename_opts.NetFile.c_str(), filename_opts.PlaceFile.c_str(), filename_opts.verify_file_digests, device_ctx.grid);
689693

vpr/src/place/initial_placement.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,10 @@ void initial_placement(enum e_pad_loc_type pad_loc_type, const char* constraints
479479
read_constraints(constraints_file);
480480
}
481481

482+
/*Mark the blocks that have already been locked to one spot via floorplan constraints
483+
* as fixed so they do not get moved during initial placement or during simulated annealing*/
484+
mark_fixed_blocks();
485+
482486
initial_placement_pl_macros(MAX_NUM_TRIES_TO_PLACE_MACROS_RANDOMLY, free_locations);
483487

484488
// All the macros are placed, update the legal_pos[][] array and free_locations[] array

vpr/src/place/place.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2644,8 +2644,8 @@ static int check_block_placement_consistency() {
26442644
for (size_t i = 0; i < device_ctx.grid.width(); i++)
26452645
for (size_t j = 0; j < device_ctx.grid.height(); j++) {
26462646
if (place_ctx.grid_blocks[i][j].usage > device_ctx.grid[i][j].type->capacity) {
2647-
VTR_LOG_ERROR("Block at grid location (%zu,%zu) overused. Usage is %d.\n",
2648-
i, j, place_ctx.grid_blocks[i][j].usage);
2647+
VTR_LOG_ERROR("%d blocks were placed at grid location (%zu,%zu), but location capacity is %d.\n",
2648+
place_ctx.grid_blocks[i][j].usage, i, j, device_ctx.grid[i][j].type->capacity);
26492649
error++;
26502650
}
26512651
int usage_check = 0;
@@ -2674,16 +2674,16 @@ static int check_block_placement_consistency() {
26742674
bdone[bnum]++;
26752675
}
26762676
if (usage_check != place_ctx.grid_blocks[i][j].usage) {
2677-
VTR_LOG_ERROR("Location (%zu,%zu) usage is %d, but has actual usage %d.\n",
2678-
i, j, place_ctx.grid_blocks[i][j].usage, usage_check);
2677+
VTR_LOG_ERROR("%d block(s) were placed at location (%zu,%zu), but location contains %d block(s).\n",
2678+
place_ctx.grid_blocks[i][j].usage, i, j, usage_check);
26792679
error++;
26802680
}
26812681
}
26822682

26832683
/* Check that every block exists in the device_ctx.grid and cluster_ctx.blocks arrays somewhere. */
26842684
for (auto blk_id : cluster_ctx.clb_nlist.blocks())
26852685
if (bdone[blk_id] != 1) {
2686-
VTR_LOG_ERROR("Block %zu listed %d times in data structures.\n",
2686+
VTR_LOG_ERROR("Block %zu listed %d times in device context grid.\n",
26872687
size_t(blk_id), bdone[blk_id]);
26882688
error++;
26892689
}

vpr/src/place/place_constraints.cpp

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
#include "globals.h"
1212
#include "place_constraints.h"
13+
#include "place_util.h"
1314

1415
/*checks that each block's location is compatible with its floorplanning constraints if it has any*/
1516
int check_placement_floorplanning() {
@@ -257,3 +258,158 @@ void load_cluster_constraints() {
257258
}
258259
}
259260
}
261+
262+
void mark_fixed_blocks() {
263+
auto& cluster_ctx = g_vpr_ctx.clustering();
264+
auto& place_ctx = g_vpr_ctx.mutable_placement();
265+
auto& floorplanning_ctx = g_vpr_ctx.floorplanning();
266+
267+
for (auto blk_id : cluster_ctx.clb_nlist.blocks()) {
268+
if (!is_cluster_constrained(blk_id)) {
269+
continue;
270+
}
271+
PartitionRegion pr = floorplanning_ctx.cluster_constraints[blk_id];
272+
auto block_type = cluster_ctx.clb_nlist.block_type(blk_id);
273+
t_pl_loc loc;
274+
275+
/*
276+
* If the block can be placed in exactly one
277+
* legal (x, y, subtile) location, place it now
278+
* and mark it as fixed.
279+
*/
280+
if (is_pr_size_one(pr, block_type, loc)) {
281+
set_block_location(blk_id, loc);
282+
283+
place_ctx.block_locs[blk_id].is_fixed = true;
284+
}
285+
}
286+
}
287+
288+
/*
289+
* Returns 0, 1, or 2 depending on the number of tiles covered.
290+
* Will not return a value above 2 because as soon as num_tiles is above 1,
291+
* it is known that the block that is assigned to this region will not be fixed, and so
292+
* num_tiles is immediately returned.
293+
* Updates the location passed in because if num_tiles turns out to be 1 after checking the
294+
* region, the location that was set will be used as the location to which the block
295+
* will be fixed.
296+
*/
297+
int region_tile_cover(const Region& reg, t_logical_block_type_ptr block_type, t_pl_loc& loc) {
298+
auto& device_ctx = g_vpr_ctx.device();
299+
vtr::Rect<int> rb = reg.get_region_rect();
300+
int num_tiles = 0;
301+
302+
for (int x = rb.xmin(); x <= rb.xmax(); x++) {
303+
for (int y = rb.ymin(); y <= rb.ymax(); y++) {
304+
auto& tile = device_ctx.grid[x][y].type;
305+
306+
/*
307+
* If the tile at the grid location is not compatible with the cluster block
308+
* type, do not count this tile for num_tiles
309+
*/
310+
if (!is_tile_compatible(tile, block_type)) {
311+
continue;
312+
}
313+
314+
/*
315+
* If the region passed has a specific subtile set, increment
316+
* the number of tiles set the location using the x, y, subtile
317+
* values if the subtile is compatible at this location
318+
*/
319+
if (reg.get_sub_tile() != NO_SUBTILE) {
320+
if (is_sub_tile_compatible(tile, block_type, reg.get_sub_tile())) {
321+
num_tiles++;
322+
loc.x = x;
323+
loc.y = y;
324+
loc.sub_tile = reg.get_sub_tile();
325+
if (num_tiles > 1) {
326+
return num_tiles;
327+
}
328+
}
329+
330+
/*
331+
* If the region passed in does not have a subtile set, set the
332+
* subtile to the first possible slot found at this location.
333+
*/
334+
} else if (reg.get_sub_tile() == NO_SUBTILE) {
335+
int num_compatible_st = 0;
336+
337+
for (int z = 0; z < tile->capacity; z++) {
338+
if (is_sub_tile_compatible(tile, block_type, z)) {
339+
num_tiles++;
340+
num_compatible_st++;
341+
if (num_compatible_st == 1) { //set loc.sub_tile to the first compatible subtile value found
342+
loc.x = x;
343+
loc.y = y;
344+
loc.sub_tile = z;
345+
}
346+
if (num_tiles > 1) {
347+
return num_tiles;
348+
}
349+
}
350+
}
351+
}
352+
}
353+
}
354+
355+
return num_tiles;
356+
}
357+
358+
/*
359+
* Used when marking fixed blocks to check whether the ParitionRegion associated with a block
360+
* covers one tile. If it covers one tile, it is marked as fixed. If it covers 0 tiles or
361+
* more than one tile, it will not be marked as fixed. As soon as it is known that the
362+
* PartitionRegion covers more than one tile, there is no need to check further regions
363+
* and the routine will return false.
364+
*/
365+
bool is_pr_size_one(PartitionRegion& pr, t_logical_block_type_ptr block_type, t_pl_loc& loc) {
366+
auto& device_ctx = g_vpr_ctx.device();
367+
std::vector<Region> regions = pr.get_partition_region();
368+
bool pr_size_one;
369+
int pr_size = 0;
370+
int reg_size;
371+
372+
Region intersect_reg;
373+
intersect_reg.set_region_rect(0, 0, device_ctx.grid.width() - 1, device_ctx.grid.height() - 1);
374+
Region current_reg;
375+
376+
for (unsigned int i = 0; i < regions.size(); i++) {
377+
reg_size = region_tile_cover(regions[i], block_type, loc);
378+
379+
/*
380+
* If multiple regions in the PartitionRegion all have size 1,
381+
* the block may still be marked as locked, in the case that
382+
* they all cover the exact same x, y, subtile location. To check whether this
383+
* is the case, whenever there is a size 1 region, it is intersected
384+
* with the previous size 1 regions to see whether it covers the same location.
385+
* If there is an intersection, it does cover the same location, and so pr_size is
386+
* not incremented (unless this is the first size 1 region encountered).
387+
*/
388+
if (reg_size == 1) {
389+
//get the exact x, y, subtile location covered by the current region (regions[i])
390+
current_reg.set_region_rect(loc.x, loc.y, loc.x, loc.y);
391+
current_reg.set_sub_tile(loc.sub_tile);
392+
intersect_reg = intersection(intersect_reg, current_reg);
393+
394+
if (i == 0 || intersect_reg.empty()) {
395+
pr_size = pr_size + reg_size;
396+
if (pr_size > 1) {
397+
break;
398+
}
399+
}
400+
} else {
401+
pr_size = pr_size + reg_size;
402+
if (pr_size > 1) {
403+
break;
404+
}
405+
}
406+
}
407+
408+
if (pr_size == 1) {
409+
pr_size_one = true;
410+
} else { //pr_size = 0 or pr_size > 1
411+
pr_size_one = false;
412+
}
413+
414+
return pr_size_one;
415+
}

vpr/src/place/place_constraints.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,4 +88,29 @@ inline bool floorplan_legal(const t_pl_blocks_to_be_moved& blocks_affected) {
8888
*/
8989
void load_cluster_constraints();
9090

91+
/*
92+
* Marks blocks as fixed if they have a constraint region that specifies exactly one x, y,
93+
* subtile location as legal.
94+
* Marking them as fixed indicates that they cannot be moved during initial placement and simulated annealing
95+
*/
96+
void mark_fixed_blocks();
97+
98+
/*
99+
* Returns the number of tiles covered by a floorplan region.
100+
* The return value of this routine will either be 0, 1, or 2. This
101+
* is because this routine is used to check whether the region covers no tile,
102+
* one tile, or more than one tile, and so as soon as it is seen that the number of tiles
103+
* covered is 2, no further information is needed.
104+
*/
105+
int region_tile_cover(const Region& reg, t_logical_block_type_ptr block_type, t_pl_loc& loc);
106+
107+
/*
108+
* Returns a bool that indicates if the PartitionRegion covers exactly one compatible location.
109+
* Used to decide whether to mark a block with the .is_fixed flag based on its floorplan
110+
* region.
111+
* block_type is used to determine whether the PartitionRegion is compatible with the cluster block type
112+
* and loc is updated with the location covered by the PartitionRegion
113+
*/
114+
bool is_pr_size_one(PartitionRegion& pr, t_logical_block_type_ptr block_type, t_pl_loc& loc);
115+
91116
#endif /* VPR_SRC_PLACE_PLACE_CONSTRAINTS_H_ */

0 commit comments

Comments
 (0)