forked from verilog-to-routing/vtr-verilog-to-routing
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathinitial_placement.cpp
More file actions
2086 lines (1803 loc) · 101 KB
/
Copy pathinitial_placement.cpp
File metadata and controls
2086 lines (1803 loc) · 101 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#include "clustered_netlist.h"
#include "flat_placement_types.h"
#include "atom_netlist_fwd.h"
#include "flat_placement_utils.h"
#include "physical_types_util.h"
#include "place_macro.h"
#include "vtr_assert.h"
#include "vtr_geometry.h"
#include "vtr_ndmatrix.h"
#include "vtr_random.h"
#include "vtr_time.h"
#include "vpr_types.h"
#include "globals.h"
#include "read_place.h"
#include "initial_placement.h"
#include "initial_noc_placment.h"
#include "vpr_utils.h"
#include "place_util.h"
#include "place_constraints.h"
#include "move_utils.h"
#include "region.h"
#include "noc_place_utils.h"
#include "vtr_vector.h"
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <iterator>
#include <limits>
#include <optional>
#include <queue>
#include <vector>
#ifdef VERBOSE
void print_clb_placement(const char* fname);
#endif
// Number of iterations that initial placement tries to place all blocks before throwing an error
static constexpr int MAX_INIT_PLACE_ATTEMPTS = 2;
// The amount of weight that will be added to previous unplaced block scores to ensure that failed blocks would be placed earlier next iteration
static constexpr int SORT_WEIGHT_PER_FAILED_BLOCK = 10;
// The amount of weight that will be added to each tile which is outside the floorplanning constraints
static constexpr int SORT_WEIGHT_PER_TILES_OUTSIDE_OF_PR = 100;
// The range limit to be used when searching for a neighbor in the centroid placement.
// The neighbor location should be within the defined range to the calculated centroid location.
static constexpr int CENTROID_NEIGHBOR_SEARCH_RLIM = 15;
/**
* @brief Control routine for placing a macro.
* First iteration of place_marco performs the following steps to place a macro:
* 1) try_centroid_placement : tries to find a location based on the macro's logical connections.
* 2) try_place_macro_randomly : if no smart location found in the centroid placement, the function tries
* to place it randomly for the max number of tries.
* 3) try_place_macro_exhaustively : if neither placement algorithms work, the function will find a location
* for the macro by exhaustively searching all available locations.
* If first iteration failed, next iteration calls dense placement for specific block types.
*
* @param macros_max_num_tries Max number of tries for initial placement before switching to exhaustive placement.
* @param pl_macro The macro to be placed.
* @param pad_loc_type Used to check whether an io block needs to be marked as fixed.
* @param blk_types_empty_locs_in_grid First location (lowest y) and number of remaining blocks in each column for the blk_id type.
* @param block_scores The block_scores (ranking of what to place next) for unplaced blocks connected to this macro should be updated.
* @param blk_loc_registry Placement block location information. To be filled with the location
* where pl_macro is placed.
* @param rng A random number generator.
*
* @return true if macro was placed, false if not.
*/
static bool place_macro(int macros_max_num_tries,
const t_pl_macro& pl_macro,
e_pad_loc_type pad_loc_type,
std::vector<t_grid_empty_locs_block_type>* blk_types_empty_locs_in_grid,
vtr::vector<ClusterBlockId, t_block_score>& block_scores,
BlkLocRegistry& blk_loc_registry,
const FlatPlacementInfo& flat_placement_info,
vtr::RngContainer& rng);
/*
* Assign scores to each block based on macro size and floorplanning constraints.
* Used for relative placement, so that the blocks that are more difficult to place can be placed first during initial placement.
* A higher score indicates that the block is more difficult to place.
*/
static vtr::vector<ClusterBlockId, t_block_score> assign_block_scores(const PlaceMacros& place_macros);
/**
* @brief Tries to find y coordinate for macro head location based on macro direction
*
* @param first_macro_loc The first available location that can place the macro blocks.
* @param pl_macro The macro to be placed.
* @param blk_loc_registry Placement block location information. To be filled with the location
* where pl_macro is placed.
*
* @return y coordinate of the location that macro head should be placed
*/
static int get_y_loc_based_on_macro_direction(t_grid_empty_locs_block_type first_macro_loc,
const t_pl_macro& pl_macro);
/**
* @brief Tries to get the first available location of a specific block type that can accommodate macro blocks
*
* @param loc The first available location that can place the macro blocks.
* @param pl_macro The macro to be placed.
* @param blk_types_empty_locs_in_grid first location (lowest y) and number of remaining blocks in each column for the blk_id type
*
* @return index to a column of blk_types_empty_locs_in_grid that can accommodate pl_macro and location of first available location returned by reference
*/
static int get_blk_type_first_loc(t_pl_loc& loc, const t_pl_macro& pl_macro, std::vector<t_grid_empty_locs_block_type>* blk_types_empty_locs_in_grid);
/**
* @brief Updates the first available location (lowest y) and number of remaining blocks in the column that dense placement used to place the macro.
*
* @param blk_type_column_index Index to a column in blk_types_empty_locs_in_grid that placed pl_macro in itself.
* @param block_type Logical block type of the macro blocks.
* @param pl_macro The macro to be placed.
* @param blk_types_empty_locs_in_grid first location (lowest y) and number of remaining blocks in each column for the blk_id type
*
*/
static void update_blk_type_first_loc(int blk_type_column_index,
t_logical_block_type_ptr block_type,
const t_pl_macro& pl_macro,
std::vector<t_grid_empty_locs_block_type>* blk_types_empty_locs_in_grid);
/**
* @brief Initializes empty locations of the grid with a specific block type into vector for dense initial placement
*
* @param block_type_index block type index that failed in previous initial placement iterations
*
* @return first location (lowest y) and number of remaining blocks in each column for the block_type_index
*/
static std::vector<t_grid_empty_locs_block_type> init_blk_types_empty_locations(int block_type_index);
/**
* @brief Helper function used when IO locations are to be randomly locked
*
* @param pl_macro The macro to be fixed.
* @param loc The location at which the head of the macro is placed.
* @param pad_loc_type Used to check whether an io block needs to be marked as fixed.
* @param block_locs Clustered block locations used to mark the IO blocks that are to be placed
* randomly as fixed.
*/
static inline void fix_IO_block_types(const t_pl_macro& pl_macro,
t_pl_loc loc,
e_pad_loc_type pad_loc_type,
vtr::vector_map<ClusterBlockId, t_block_loc>& block_locs);
/**
* @brief Determine whether a specific macro can be placed in a specific location.
*
* @param loc The location at which the macro head member is placed.
* @param pr The PartitionRegion of the macro head member - represents its floorplanning constraints, is the size of
* the whole chip if the macro is not constrained.
* @param block_type Logical block type of the macro head member.
*
* @return True if the location is legal for the macro head member, false otherwise.
*/
static bool is_loc_legal(const t_pl_loc& loc,
const PartitionRegion& pr,
t_logical_block_type_ptr block_type);
/**
* @brief Helper function to choose a subtile in specified location if the type is compatible and an available one exists.
*
* @param centroid The centroid location at which the subtile will be selected using its x, y, and layer.
* @param block_type Logical block type we would like to place here.
* @param block_loc_registry Information on where other blocks have been placed.
* @param pr The PartitionRegion of the block we are trying to place - represents its floorplanning constraints;
* it is the size of the whole chip if the block is not constrained.
* @param rng A random number generator to select a subtile from the available and compatible ones.
*
* @return True if the location is on the chip, legal, and at least one available subtile is found at that location;
* false otherwise.
*/
static bool find_subtile_in_location(t_pl_loc& centroid,
t_logical_block_type_ptr block_type,
const BlkLocRegistry& blk_loc_registry,
const PartitionRegion& pr,
vtr::RngContainer& rng);
/**
* @brief Calculates a centroid location for a block based on its placed connections.
*
* @param pl_macro The macro to be placed.
* @param centroid specified location (x,y,subtile) for the pl_macro head member.
* @param blk_loc_registry Placement block location information. To be filled with the location
* where pl_macro is placed.
*
* @return a vector of blocks that are connected to this block but not yet placed so their scores can later be updated.
*/
static std::vector<ClusterBlockId> find_centroid_loc(const t_pl_macro& pl_macro,
t_pl_loc& centroid,
const BlkLocRegistry& blk_loc_registry);
/**
* @brief Tries to find a nearest location to the centroid location if calculated centroid location is not legal or is occupied.
*
* @param centroid_loc Calculated location in try_centroid_placement function for the block.
* @param block_type Logical block type of the macro blocks.
* @param search_for_empty If set, the function tries to find an empty location.
* @param blk_loc_registry Placement block location information. To be filled with the location
* where pl_macro is placed.
*
* @return true if the function can find any location near the centroid one, false otherwise.
*/
static bool find_centroid_neighbor(ClusterBlockId block_id,
t_pl_loc& centroid_loc,
t_logical_block_type_ptr block_type,
bool search_for_empty,
int r_lim,
const BlkLocRegistry& blk_loc_registry,
vtr::RngContainer& rng);
/**
* @brief tries to place a macro at a centroid location of its placed connections.
*
* @param block_id The block to be placed.
* @param pl_macro The macro to be placed.
* @param pr The PartitionRegion of the macro - represents its floorplanning constraints, is the size of the whole chip if the macro is not
* constrained.
* @param block_type Logical block type of the macro blocks.
* @param pad_loc_type Used to check whether an io block needs to be marked as fixed.
* @param block_scores The block_scores (ranking of what to place next) for unplaced blocks connected to this macro are updated in this routine.
* @param blk_loc_registry Placement block location information. To be filled with the location
* where pl_macro is placed.
* @param rng A random number generator for choosing a compatible subtile randomly.
*
* @return true if the macro gets placed, false if not.
*/
static bool try_centroid_placement(ClusterBlockId block_id,
const t_pl_macro& pl_macro,
const PartitionRegion& pr,
t_logical_block_type_ptr block_type,
e_pad_loc_type pad_loc_type,
vtr::vector<ClusterBlockId, t_block_score>& block_scores,
BlkLocRegistry& blk_loc_registry,
const FlatPlacementInfo& flat_placement_info,
vtr::RngContainer& rng);
/**
* @brief Looks for a valid placement location for macro in second iteration, tries to place as many macros as possible in one column
* and avoids fragmenting the available locations in one column.
*
* @param pl_macro The macro to be placed.
* @param pr The PartitionRegion of the macro - represents its floorplanning constraints, is the size of the whole chip if the macro is not
* constrained.
* @param block_type Logical block type of the macro blocks.
* @param pad_loc_type Used to check whether an io block needs to be marked as fixed.
* @param blk_types_empty_locs_in_grid first location (lowest y) and number of remaining blocks in each column for the blk_id type
* @param blk_loc_registry Placement block location information. To be filled with the location
* where pl_macro is placed.
*
* @return true if the macro gets placed, false if not.
*/
static bool try_dense_placement(const t_pl_macro& pl_macro,
const PartitionRegion& pr,
t_logical_block_type_ptr block_type,
e_pad_loc_type pad_loc_type,
std::vector<t_grid_empty_locs_block_type>* blk_types_empty_locs_in_grid,
BlkLocRegistry& blk_loc_registry);
/**
* @brief Tries for MAX_INIT_PLACE_ATTEMPTS times to place all blocks considering their floorplanning constraints and the device size
*
* @param pad_loc_type Used to check whether an io block needs to be marked as fixed.
* @param constraints_file Used to read block locations if any constraints is available.
* @param blk_loc_registry Placement block location information. To be filled with the location
* where pl_macro is placed.
* @param rng A random number generator.
*/
static void place_all_blocks(const t_placer_opts& placer_opts,
vtr::vector<ClusterBlockId, t_block_score>& block_scores,
e_pad_loc_type pad_loc_type,
const char* constraints_file,
BlkLocRegistry& blk_loc_registry,
const PlaceMacros& place_macros,
const FlatPlacementInfo& flat_placement_info,
vtr::RngContainer& rng);
/**
* @brief If any blocks remain unplaced after all initial placement iterations, this routine
* throws an error indicating that initial placement can not be done with the current device size or
* floorplanning constraints.
*/
static void check_initial_placement_legality(const BlkLocRegistry& blk_loc_registry);
static void check_initial_placement_legality(const BlkLocRegistry& blk_loc_registry) {
const auto& cluster_ctx = g_vpr_ctx.clustering();
const auto& device_ctx = g_vpr_ctx.device();
const auto& block_locs = blk_loc_registry.block_locs();
int unplaced_blocks = 0;
for (ClusterBlockId blk_id : cluster_ctx.clb_nlist.blocks()) {
if (block_locs[blk_id].loc.x == INVALID_X) {
VTR_LOG("Block %s (# %d) of type %s could not be placed during initial placement iteration %d\n",
cluster_ctx.clb_nlist.block_name(blk_id).c_str(),
blk_id,
cluster_ctx.clb_nlist.block_type(blk_id)->name.c_str(),
MAX_INIT_PLACE_ATTEMPTS - 1);
unplaced_blocks++;
}
}
if (unplaced_blocks > 0) {
VPR_FATAL_ERROR(VPR_ERROR_PLACE,
"%d blocks could not be placed during initial placement; no spaces were available for them on the grid.\n"
"If VPR was run with floorplan constraints, the constraints may be too tight.\n",
unplaced_blocks);
}
for (auto movable_blk_id : blk_loc_registry.movable_blocks()) {
if (block_locs[movable_blk_id].is_fixed) {
VPR_FATAL_ERROR(VPR_ERROR_PLACE, "Fixed block was mistakenly marked as movable during initial placement.\n");
}
}
for (const auto& logical_block_type : device_ctx.logical_block_types) {
const auto& movable_blocks_of_type = blk_loc_registry.movable_blocks_per_type()[logical_block_type.index];
for (const auto& movable_blk_id : movable_blocks_of_type) {
if (block_locs[movable_blk_id].is_fixed) {
VPR_FATAL_ERROR(VPR_ERROR_PLACE, "Fixed block %d of logical type %s was mistakenly marked as movable during initial placement.\n",
(size_t)movable_blk_id, logical_block_type.name.c_str());
}
if (cluster_ctx.clb_nlist.block_type(movable_blk_id)->index != logical_block_type.index) {
VPR_FATAL_ERROR(VPR_ERROR_PLACE, "Clustered block %d of logical type %s was mistakenly marked as logical type %s.\n",
(size_t)movable_blk_id,
cluster_ctx.clb_nlist.block_type(movable_blk_id)->name.c_str(),
logical_block_type.name.c_str());
}
}
}
}
bool is_block_placed(ClusterBlockId blk_id,
const vtr::vector_map<ClusterBlockId, t_block_loc>& block_locs) {
return (block_locs[blk_id].loc.x != INVALID_X);
}
static bool is_loc_legal(const t_pl_loc& loc,
const PartitionRegion& pr,
t_logical_block_type_ptr block_type) {
const auto& grid = g_vpr_ctx.device().grid;
bool legal = false;
//Check if the location is within its constraint region
for (const auto& reg : pr.get_regions()) {
const vtr::Rect<int>& reg_rect = reg.get_rect();
const auto [layer_low, layer_high] = reg.get_layer_range();
if (loc.layer > layer_high || loc.layer < layer_low) {
continue;
}
if (reg_rect.coincident({loc.x, loc.y})) {
//check if the location is compatible with the block type
const auto& type = grid.get_physical_type({loc.x, loc.y, loc.layer});
int height_offset = grid.get_height_offset({loc.x, loc.y, loc.layer});
int width_offset = grid.get_width_offset({loc.x, loc.y, loc.layer});
if (is_tile_compatible(type, block_type)) {
//Check if the location is an anchor position
if (height_offset == 0 && width_offset == 0) {
legal = true;
break;
}
}
}
}
return legal;
}
bool find_subtile_in_location(t_pl_loc& centroid,
t_logical_block_type_ptr block_type,
const BlkLocRegistry& blk_loc_registry,
const PartitionRegion& pr,
vtr::RngContainer& rng) {
//check if the location is on chip and legal, if yes try to update subtile
if (is_loc_on_chip({centroid.x, centroid.y, centroid.layer}) && is_loc_legal(centroid, pr, block_type)) {
//find the compatible subtiles
const auto& device_ctx = g_vpr_ctx.device();
const auto& compressed_block_grid = g_vpr_ctx.placement().compressed_block_grids[block_type->index];
const auto& type = device_ctx.grid.get_physical_type({centroid.x, centroid.y, centroid.layer});
const auto& compatible_sub_tiles = compressed_block_grid.compatible_sub_tile_num(type->index);
//filter out the occupied subtiles
const GridBlock& grid_blocks = blk_loc_registry.grid_blocks();
std::vector<int> available_sub_tiles;
available_sub_tiles.reserve(compatible_sub_tiles.size());
for (int sub_tile : compatible_sub_tiles) {
t_pl_loc pos = {centroid.x, centroid.y, sub_tile, centroid.layer};
if (!grid_blocks.block_at_location(pos)) {
available_sub_tiles.push_back(sub_tile);
}
}
if (!available_sub_tiles.empty()) {
centroid.sub_tile = available_sub_tiles[rng.irand((int)available_sub_tiles.size() - 1)];
return true;
}
}
return false;
}
static bool find_centroid_neighbor(ClusterBlockId block_id,
t_pl_loc& centroid_loc,
t_logical_block_type_ptr block_type,
bool search_for_empty,
int rlim,
const BlkLocRegistry& blk_loc_registry,
vtr::RngContainer& rng) {
const auto& compressed_block_grid = g_vpr_ctx.placement().compressed_block_grids[block_type->index];
const int num_layers = g_vpr_ctx.device().grid.get_num_layers();
const int centroid_loc_layer_num = centroid_loc.layer;
//Determine centroid location in the compressed space of the current block
auto compressed_centroid_loc = get_compressed_loc_approx(compressed_block_grid,
centroid_loc,
num_layers);
// If no compressed location can be found on this layer, return false.
// TODO: Maybe search in the layers above or below.
const t_physical_tile_loc& compressed_loc_on_layer = compressed_centroid_loc[centroid_loc.layer];
if (compressed_loc_on_layer.x == OPEN && compressed_loc_on_layer.y == OPEN && compressed_loc_on_layer.layer_num == OPEN)
return false;
//range limit (rlim) set a limit for the neighbor search in the centroid placement
//the neighbor location should be within the defined range to calculated centroid location
int first_rlim = rlim;
auto search_range = get_compressed_grid_target_search_range(compressed_block_grid,
compressed_loc_on_layer,
first_rlim);
int delta_cx = search_range.xmax - search_range.xmin;
bool block_constrained = is_cluster_constrained(block_id);
if (block_constrained) {
bool intersect = intersect_range_limit_with_floorplan_constraints(block_id,
search_range,
delta_cx,
centroid_loc_layer_num);
if (!intersect) {
return false;
}
}
//Block has not been placed yet, so the "from" coords will be (-1, -1)
int cx_from = OPEN;
int cy_from = OPEN;
int layer_from = centroid_loc_layer_num;
t_physical_tile_loc to_compressed_loc;
bool legal = find_compatible_compressed_loc_in_range(block_type,
delta_cx,
{cx_from, cy_from, layer_from},
search_range,
to_compressed_loc,
/*is_median=*/false,
centroid_loc_layer_num,
search_for_empty,
blk_loc_registry,
rng,
block_constrained);
if (!legal) {
return false;
}
compressed_grid_to_loc(block_type, to_compressed_loc, centroid_loc, rng);
return legal;
}
static std::vector<ClusterBlockId> find_centroid_loc(const t_pl_macro& pl_macro,
t_pl_loc& centroid,
const BlkLocRegistry& blk_loc_registry) {
const auto& cluster_ctx = g_vpr_ctx.clustering();
const auto& block_locs = blk_loc_registry.block_locs();
float acc_weight = 0;
float acc_x = 0;
float acc_y = 0;
bool find_layer = false;
std::vector<int> layer_count(g_vpr_ctx.device().grid.get_num_layers(), 0);
ClusterBlockId head_blk = pl_macro.members.at(0).blk_index;
// For now, we put the macro in the same layer as the head block
int head_layer_num = block_locs[head_blk].loc.layer;
// If block is placed, we use the layer of the block. Otherwise, the layer will be determined later
if (head_layer_num == OPEN) {
find_layer = true;
}
std::vector<ClusterBlockId> connected_blocks_to_update;
//iterate over the from block pins
for (ClusterPinId pin_id : cluster_ctx.clb_nlist.block_pins(head_blk)) {
ClusterNetId net_id = cluster_ctx.clb_nlist.pin_net(pin_id);
/* Ignore the special case nets which only connects a block to itself *
* Experimentally, it was found that this case greatly degrade QoR */
if (cluster_ctx.clb_nlist.net_sinks(net_id).size() == 1) {
ClusterBlockId source = cluster_ctx.clb_nlist.net_driver_block(net_id);
ClusterPinId sink_pin = *cluster_ctx.clb_nlist.net_sinks(net_id).begin();
ClusterBlockId sink = cluster_ctx.clb_nlist.pin_block(sink_pin);
if (sink == source) {
continue;
}
}
//if the pin is driver iterate over all the sinks
if (cluster_ctx.clb_nlist.pin_type(pin_id) == PinType::DRIVER) {
//ignore nets that are globally routed
if (cluster_ctx.clb_nlist.net_is_ignored(net_id)) {
continue;
}
for (ClusterPinId sink_pin_id : cluster_ctx.clb_nlist.net_sinks(net_id)) {
/* Ignore if one of the sinks is the block itself*/
if (pin_id == sink_pin_id)
continue;
if (!is_block_placed(cluster_ctx.clb_nlist.pin_block(sink_pin_id), block_locs)) {
//add unplaced block to connected_blocks_to_update vector to update its score later.
connected_blocks_to_update.push_back(cluster_ctx.clb_nlist.pin_block(sink_pin_id));
continue;
}
t_physical_tile_loc tile_loc = blk_loc_registry.get_coordinate_of_pin(sink_pin_id);
if (find_layer) {
VTR_ASSERT(tile_loc.layer_num != OPEN);
layer_count[tile_loc.layer_num]++;
}
acc_x += tile_loc.x;
acc_y += tile_loc.y;
acc_weight++;
}
}
//else the pin is sink --> only care about its driver
else {
ClusterPinId source_pin = cluster_ctx.clb_nlist.net_driver(net_id);
if (!is_block_placed(cluster_ctx.clb_nlist.pin_block(source_pin), block_locs)) {
//add unplaced block to connected_blocks_to_update vector to update its score later.
connected_blocks_to_update.push_back(cluster_ctx.clb_nlist.pin_block(source_pin));
continue;
}
t_physical_tile_loc tile_loc = blk_loc_registry.get_coordinate_of_pin(source_pin);
if (find_layer) {
VTR_ASSERT(tile_loc.layer_num != OPEN);
layer_count[tile_loc.layer_num]++;
}
acc_x += tile_loc.x;
acc_y += tile_loc.y;
acc_weight++;
}
}
//Calculate the centroid location
if (acc_weight > 0) {
centroid.x = acc_x / acc_weight;
centroid.y = acc_y / acc_weight;
if (find_layer) {
auto max_element = std::max_element(layer_count.begin(), layer_count.end());
VTR_ASSERT((*max_element) != 0);
centroid.layer = (int)std::distance(layer_count.begin(), max_element);
} else {
centroid.layer = head_layer_num;
}
}
return connected_blocks_to_update;
}
/**
* @brief Helper method for getting the flat position of an atom relative to a
* given offset.
*
* This method is useful for chained blocks where an atom may be a member of a
* chain with the given offset. This gives the atom's position relative to the
* head macro's tile location.
*/
static t_flat_pl_loc get_atom_relative_flat_loc(AtomBlockId atom_blk_id,
const t_pl_offset& offset,
const FlatPlacementInfo& flat_placement_info,
const DeviceGrid& device_grid) {
// Get the flat location of the atom and the offset.
t_flat_pl_loc atom_pos = flat_placement_info.get_pos(atom_blk_id);
t_flat_pl_loc flat_offset = t_flat_pl_loc((float)offset.x,
(float)offset.y,
(float)offset.layer);
// Get the position of the head macro of the chain that this atom is a part of.
atom_pos -= flat_offset;
// This may put the atom off device (due to the flat placement not being fully
// legal), so we clamp this to be within the device.
atom_pos.x = std::clamp(atom_pos.x, 0.0f, (float)device_grid.width() - 0.001f);
atom_pos.y = std::clamp(atom_pos.y, 0.0f, (float)device_grid.height() - 0.001f);
atom_pos.layer = std::clamp(atom_pos.layer, 0.0f, (float)device_grid.get_num_layers() - 0.001f);
return atom_pos;
}
// TODO: Should this return the unplaced_blocks_to_update_their_score?
static t_flat_pl_loc find_centroid_loc_from_flat_placement(const t_pl_macro& pl_macro,
const FlatPlacementInfo& flat_placement_info) {
// Use the flat placement to compute the centroid of the given macro.
// TODO: Instead of averaging, maybe use MODE (most frequently placed location).
const DeviceGrid& device_grid = g_vpr_ctx.device().grid;
unsigned acc_weight = 0;
t_flat_pl_loc centroid({0.0f, 0.0f, 0.0f});
for (const t_pl_macro_member& member : pl_macro.members) {
const auto& cluster_atoms = g_vpr_ctx.clustering().atoms_lookup[member.blk_index];
for (AtomBlockId atom_blk_id : cluster_atoms) {
// TODO: We can get away with using less information.
VTR_ASSERT(flat_placement_info.blk_x_pos[atom_blk_id] != FlatPlacementInfo::UNDEFINED_POS && flat_placement_info.blk_y_pos[atom_blk_id] != FlatPlacementInfo::UNDEFINED_POS && flat_placement_info.blk_layer[atom_blk_id] != FlatPlacementInfo::UNDEFINED_POS && flat_placement_info.blk_sub_tile[atom_blk_id] != FlatPlacementInfo::UNDEFINED_SUB_TILE);
// Accumulate the x, y, layer, and sub_tile for each atom in each
// member of the macro. The position should be relative to the head
// macro's position such that the centroid is where all blocks think
// the head macro should be.
t_flat_pl_loc atom_pos = get_atom_relative_flat_loc(atom_blk_id,
member.offset,
flat_placement_info,
device_grid);
centroid += atom_pos;
acc_weight++;
}
}
if (acc_weight > 0) {
centroid /= static_cast<float>(acc_weight);
}
// If the root cluster is constrained, project the centroid onto its
// partition region. This will move the centroid position to the closest
// position within the partition region.
ClusterBlockId head_cluster_id = pl_macro.members[0].blk_index;
if (is_cluster_constrained(head_cluster_id)) {
// Get the partition region of the head. This is the partition region
// that affects the entire macro.
const PartitionRegion& head_pr = g_vpr_ctx.floorplanning().cluster_constraints[head_cluster_id];
// For each region, find the closest point in that region to the centroid
// and save the closest of all regions.
t_flat_pl_loc best_projected_pos = centroid;
float best_distance = std::numeric_limits<float>::max();
VTR_ASSERT_MSG(centroid.layer == 0,
"3D FPGAs not supported for this part of the code yet");
for (const Region& region : head_pr.get_regions()) {
const vtr::Rect<int>& rect = region.get_rect();
// Note: We add 0.999 here since the partition region is in grid
// space, so it treats tile positions as having size 0x0 when
// they really are 1x1.
float proj_x = std::clamp<float>(centroid.x, rect.xmin(), rect.xmax() + 0.999);
float proj_y = std::clamp<float>(centroid.y, rect.ymin(), rect.ymax() + 0.999);
float dx = std::abs(proj_x - centroid.x);
float dy = std::abs(proj_y - centroid.y);
float dist = dx + dy;
if (dist < best_distance) {
best_projected_pos.x = proj_x;
best_projected_pos.y = proj_y;
best_distance = dist;
}
}
VTR_ASSERT_SAFE(best_distance != std::numeric_limits<float>::max());
// Return the point within the partition region that is closest to the
// original centroid.
return best_projected_pos;
}
return centroid;
}
/**
* @brief Returns the first available sub_tile (both compatible with the given
* compressed grid and is empty according the the blk_loc_registry) in
* the tile at the given grid_loc. Returns OPEN if no such sub_tile exists.
*/
static inline int get_first_available_sub_tile_at_grid_loc(const t_physical_tile_loc& grid_loc,
const BlkLocRegistry& blk_loc_registry,
const DeviceGrid& device_grid,
const t_compressed_block_grid& compressed_block_grid) {
// Get the compatible sub-tiles from the compressed grid for this physical
// tile type.
const t_physical_tile_type_ptr phy_type = device_grid.get_physical_type(grid_loc);
const auto& compatible_sub_tiles = compressed_block_grid.compatible_sub_tile_num(phy_type->index);
// Return the first empty sub-tile from this list.
for (int sub_tile : compatible_sub_tiles) {
if (blk_loc_registry.grid_blocks().is_sub_tile_empty(grid_loc, sub_tile)) {
return sub_tile;
}
}
// If one cannot be found, return OPEN.
return OPEN;
}
/**
* @brief Find the nearest compatible location for the given macro as close to
* the src_flat_loc as possible.
*
* This method uses a BFS to find the closest legal location for the macro.
*
* @param src_flat_loc
* The start location of the BFS. This is given as a flat placement to
* allow the search to trade-off different location options. For example,
* if src_loc was (1.6, 1.5), this tells the search that the cluster
* would prefer to be at tile (1, 1), but if it cannot go there and
* it had to go to one of the neighbors, it would prefer to be on the
* right.
* @param block_type
* The logical block type of the macro.
* @param macro
* The macro to place in the location.
* @param blk_loc_registry
*
* @return Returns the closest legal location found. All of the dimensions will
* be OPEN if a locations could not be found.
*/
static inline t_pl_loc find_nearest_compatible_loc(const t_flat_pl_loc& src_flat_loc,
float max_displacement_threshold,
t_logical_block_type_ptr block_type,
const t_pl_macro& pl_macro,
const BlkLocRegistry& blk_loc_registry) {
// This method performs a BFS over the compressed grid. This avoids searching
// locations which obviously cannot implement this macro.
const auto& compressed_block_grid = g_vpr_ctx.placement().compressed_block_grids[block_type->index];
const DeviceGrid& device_grid = g_vpr_ctx.device().grid;
const int num_layers = device_grid.get_num_layers();
// This method does not support 3D FPGAs yet. The search performed will only
// traverse the same layer as the src_loc.
VTR_ASSERT(num_layers == 1);
constexpr int layer = 0;
// Get the closest (approximately) compressed location to the src location.
// This does not need to be perfect (in fact I do not think it is), but the
// closer it is, the faster the BFS will find the best solution.
t_physical_tile_loc src_grid_loc(src_flat_loc.x, src_flat_loc.y, src_flat_loc.layer);
const t_physical_tile_loc compressed_src_loc = compressed_block_grid.grid_loc_to_compressed_loc_approx(src_grid_loc);
// Weighted-BFS search the compressed grid for an empty compatible subtile.
size_t num_rows = compressed_block_grid.get_num_rows(layer);
size_t num_cols = compressed_block_grid.get_num_columns(layer);
vtr::NdMatrix<bool, 2> visited({num_cols, num_rows}, false);
float best_dist = std::numeric_limits<float>::max();
t_pl_loc best_loc(OPEN, OPEN, OPEN, OPEN);
std::queue<t_physical_tile_loc> loc_queue;
loc_queue.push(compressed_src_loc);
while (!loc_queue.empty()) {
// Pop the top element off the queue.
t_physical_tile_loc loc = loc_queue.front();
loc_queue.pop();
// If this location has already been visited, skip it.
if (visited[loc.x][loc.y])
continue;
visited[loc.x][loc.y] = true;
// Get the minimum distance the cluster would need to move (relative to
// its global placement solution) to be within the tile at the given
// location.
// Note: In compressed space, distances are not what they appear. We are
// using the true grid positions to get the truly closest loc.
auto grid_loc = compressed_block_grid.compressed_loc_to_grid_loc(loc);
float grid_dist = get_manhattan_distance_to_tile(src_flat_loc,
grid_loc,
device_grid);
// If this distance is worst than the best we have seen.
// NOTE: This prune is always safe (i.e. it will never remove a better
// solution) since this is a spatial graph and our objective is
// positional distance. The un-visitied neighbors of a node should
// have a higher distance than the current node.
if (grid_dist >= best_dist)
continue;
// If this distance is beyond the max_displacement_threshold, drop this
// location.
if (grid_dist > max_displacement_threshold)
continue;
// In order to ensure our BFS finds the closest compatible location, we
// traverse compressed grid locations which may not actually be valid
// (i.e. no tile exists there). This is fine, we just need to check for
// them to ensure we never try to put a cluster there.
bool is_valid_compressed_loc = false;
const auto& compressed_col_blk_map = compressed_block_grid.get_column_block_map(loc.x, layer);
if (compressed_col_blk_map.count(loc.y) != 0)
is_valid_compressed_loc = true;
// If this distance is better than the best we have seen so far, try
// to see if this is a better solution.
if (is_valid_compressed_loc) {
// Get a sub-tile at this location if it is available.
int new_sub_tile = get_first_available_sub_tile_at_grid_loc(grid_loc,
blk_loc_registry,
device_grid,
compressed_block_grid);
if (new_sub_tile != OPEN) {
// If a sub-tile is available, set this to be the first sub-tile
// available and check if this site is legal for this macro.
// Note: We are using the fully legality check here to check for
// floorplanning constraints and compatibility for all
// members of the macro. This prevents some macros being
// placed where they obviously cannot be implemented.
t_pl_loc new_loc = t_pl_loc(grid_loc.x, grid_loc.y, new_sub_tile, grid_loc.layer_num);
bool site_legal_for_macro = macro_can_be_placed(pl_macro,
new_loc,
true /*check_all_legality*/,
blk_loc_registry);
if (site_legal_for_macro) {
// Update the best solition.
// Note: We need to keep searching since the compressed grid
// may present a location which is closer in compressed
// space earlier than a location which is closer in
// grid space.
best_dist = grid_dist;
best_loc = new_loc;
}
}
}
// Push the neighbors (in the compressed grid) onto the queue.
// This will push the neighbors left, right, above, and below the current
// location. Some of these locations may not exist or may have already
// been visited. The code above checks for these cases to prevent extra
// work and invalid lookups. This must be done this way to ensure that
// the closest location can be found efficiently.
if (loc.x > 0) {
t_physical_tile_loc new_comp_loc = t_physical_tile_loc(loc.x - 1,
loc.y,
loc.layer_num);
loc_queue.push(new_comp_loc);
}
if (loc.x < (int)num_cols - 1) {
t_physical_tile_loc new_comp_loc = t_physical_tile_loc(loc.x + 1,
loc.y,
loc.layer_num);
loc_queue.push(new_comp_loc);
}
if (loc.y > 0) {
t_physical_tile_loc new_comp_loc = t_physical_tile_loc(loc.x,
loc.y - 1,
loc.layer_num);
loc_queue.push(new_comp_loc);
}
if (loc.y < (int)num_rows - 1) {
t_physical_tile_loc new_comp_loc = t_physical_tile_loc(loc.x,
loc.y + 1,
loc.layer_num);
loc_queue.push(new_comp_loc);
}
}
return best_loc;
}
static bool try_centroid_placement(ClusterBlockId block_id,
const t_pl_macro& pl_macro,
const PartitionRegion& pr,
t_logical_block_type_ptr block_type,
e_pad_loc_type pad_loc_type,
vtr::vector<ClusterBlockId, t_block_score>& block_scores,
BlkLocRegistry& blk_loc_registry,
const FlatPlacementInfo& flat_placement_info,
vtr::RngContainer& rng) {
auto& block_locs = blk_loc_registry.mutable_block_locs();
t_pl_loc centroid_loc(OPEN, OPEN, OPEN, OPEN);
std::vector<ClusterBlockId> unplaced_blocks_to_update_their_score;
bool found_legal_subtile = false;
int rlim = CENTROID_NEIGHBOR_SEARCH_RLIM;
if (!flat_placement_info.valid) {
// If a flat placement is not provided, use the centroid of connected
// blocks which have already been placed.
unplaced_blocks_to_update_their_score = find_centroid_loc(pl_macro, centroid_loc, blk_loc_registry);
found_legal_subtile = find_subtile_in_location(centroid_loc, block_type, blk_loc_registry, pr, rng);
} else {
// If a flat placement is provided, use the flat placement to get the
// centroid location of the macro.
t_flat_pl_loc centroid_flat_loc = find_centroid_loc_from_flat_placement(pl_macro, flat_placement_info);
// Then find the nearest legal location to this centroid for this macro.
centroid_loc = find_nearest_compatible_loc(centroid_flat_loc,
static_cast<float>(rlim),
block_type,
pl_macro,
blk_loc_registry);
// FIXME: After this point, if the find_nearest_compatible_loc function
// could not find a valid location, then nothing should be able to.
// Also the location it returns will be on the chip and in the PR
// by construction. Could save time by skipping those checks if
// needed.
if (centroid_loc.x == OPEN) {
// If we cannot find a nearest block, fall back on the original
// find_centroid_loc function.
// FIXME: We should really just skip this block and come back
// to it later. We do not want it taking space from
// someone else!
unplaced_blocks_to_update_their_score = find_centroid_loc(pl_macro, centroid_loc, blk_loc_registry);
found_legal_subtile = find_subtile_in_location(centroid_loc, block_type, blk_loc_registry, pr, rng);
} else {
found_legal_subtile = true;
}
}
//no suggestion was available for this block type
if (!is_loc_on_chip({centroid_loc.x, centroid_loc.y, centroid_loc.layer})) {
return false;
}
//centroid suggestion was either occupied or does not match block type
//try to find a near location that meet these requirements
if (!found_legal_subtile) {
bool neighbor_legal_loc = find_centroid_neighbor(block_id, centroid_loc, block_type, false, rlim, blk_loc_registry, rng);
if (!neighbor_legal_loc) { //no neighbor candidate found
return false;
}
}
//no neighbor were found that meet all our requirements, should be placed with random placement
if (!is_loc_on_chip({centroid_loc.x, centroid_loc.y, centroid_loc.layer}) || !pr.is_loc_in_part_reg(centroid_loc)) {
return false;
}
auto& device_ctx = g_vpr_ctx.device();
int width_offset = device_ctx.grid.get_width_offset({centroid_loc.x, centroid_loc.y, centroid_loc.layer});
int height_offset = device_ctx.grid.get_height_offset({centroid_loc.x, centroid_loc.y, centroid_loc.layer});
VTR_ASSERT(width_offset == 0);
VTR_ASSERT(height_offset == 0);
bool legal = try_place_macro(pl_macro, centroid_loc, blk_loc_registry);
if (legal) {
fix_IO_block_types(pl_macro, centroid_loc, pad_loc_type, block_locs);
//after placing the current block, its connections' score must be updated.
for (ClusterBlockId blk_id : unplaced_blocks_to_update_their_score) {
block_scores[blk_id].number_of_placed_connections++;
}
}
return legal;
}
static int get_y_loc_based_on_macro_direction(t_grid_empty_locs_block_type first_macro_loc, const t_pl_macro& pl_macro) {
int y = first_macro_loc.first_avail_loc.y;
/* if the macro member offset is positive, it means that macro head should be placed at the first location of first_macro_loc.
* otherwise, macro head should be placed at the last available location to ensure macro_can_be_placed can check macro location correctly.
*/
if (pl_macro.members.size() > 1) {
if (pl_macro.members.at(1).offset.y < 0) {
y += (pl_macro.members.size() - 1) * abs(pl_macro.members.at(1).offset.y);
}
}
return y;
}
static void update_blk_type_first_loc(int blk_type_column_index,
t_logical_block_type_ptr block_type,
const t_pl_macro& pl_macro,
std::vector<t_grid_empty_locs_block_type>* blk_types_empty_locs_in_grid) {
//check if dense placement could place macro successfully
if (blk_type_column_index == -1 || blk_types_empty_locs_in_grid->size() <= (size_t)abs(blk_type_column_index)) {
return;
}
const auto& device_ctx = g_vpr_ctx.device();
//update the first available macro location in a specific column for the next macro
blk_types_empty_locs_in_grid->at(blk_type_column_index).first_avail_loc.y += device_ctx.physical_tile_types.at(block_type->index).height * pl_macro.members.size();
blk_types_empty_locs_in_grid->at(blk_type_column_index).num_of_empty_locs_in_y_axis -= pl_macro.members.size();
}
static int get_blk_type_first_loc(t_pl_loc& loc,
const t_pl_macro& pl_macro,
std::vector<t_grid_empty_locs_block_type>* blk_types_empty_locs_in_grid) {
//loop over all empty locations and choose first column that can accommodate macro blocks
for (unsigned int empty_loc_index = 0; empty_loc_index < blk_types_empty_locs_in_grid->size(); empty_loc_index++) {
auto first_empty_loc = blk_types_empty_locs_in_grid->at(empty_loc_index);
//if macro size is larger than available locations in the specific column, should go to next available column
if ((unsigned)first_empty_loc.num_of_empty_locs_in_y_axis < pl_macro.members.size()) {
continue;
}
//set the coordinate of first location that can accommodate macro blocks
loc.x = first_empty_loc.first_avail_loc.x;
loc.y = get_y_loc_based_on_macro_direction(first_empty_loc, pl_macro);
loc.layer = first_empty_loc.first_avail_loc.layer;
loc.sub_tile = first_empty_loc.first_avail_loc.sub_tile;