|
29 | 29 | #include <limits>
|
30 | 30 | #include <optional>
|
31 | 31 | #include <queue>
|
| 32 | +#include <vector> |
32 | 33 |
|
33 | 34 | #ifdef VERBOSE
|
34 | 35 | void print_clb_placement(const char* fname);
|
@@ -1628,27 +1629,20 @@ static void print_ap_initial_placer_status(unsigned iteration,
|
1628 | 1629 | }
|
1629 | 1630 |
|
1630 | 1631 | /**
|
1631 |
| - * @brief Places all blocks in the clustered netlist as close to the global |
1632 |
| - * placement produced by the AP flow. |
1633 |
| - * |
1634 |
| - * This function will place clusters in passes. In the first pass, it will try |
1635 |
| - * to place clusters exactly where their global placement is (according to the |
1636 |
| - * atoms contained in the cluster). In the second pass, all unplaced clusters |
1637 |
| - * will try to be placed within 1 tile of where they wanted to be placed. |
1638 |
| - * Subsequent passes will then try to place clusters at exponentially farther |
1639 |
| - * distances. |
| 1632 | + * @brief Collects unplaced clusters and sorts such that clusters which should |
| 1633 | + * be placed first appear early in the list. |
1640 | 1634 | *
|
1641 | 1635 | * The clusters are sorted based on how many clusters are in the macro that
|
1642 | 1636 | * contains this cluster and the standard deviation of the placement of atoms
|
1643 | 1637 | * within the cluster. Large macros with low standard deviation will be placed
|
1644 | 1638 | * first.
|
1645 | 1639 | */
|
1646 |
| -static inline void place_all_blocks_ap(enum e_pad_loc_type pad_loc_type, |
1647 |
| - BlkLocRegistry& blk_loc_registry, |
1648 |
| - const PlaceMacros& place_macros, |
1649 |
| - const FlatPlacementInfo& flat_placement_info) { |
1650 |
| - const ClusteredNetlist& cluster_netlist = g_vpr_ctx.clustering().clb_nlist; |
1651 |
| - const DeviceGrid& device_grid = g_vpr_ctx.device().grid; |
| 1640 | +static inline std::vector<ClusterBlockId> get_sorted_clusters_to_place( |
| 1641 | + BlkLocRegistry& blk_loc_registry, |
| 1642 | + const PlaceMacros& place_macros, |
| 1643 | + const ClusteredNetlist& cluster_netlist, |
| 1644 | + const FlatPlacementInfo& flat_placement_info) { |
| 1645 | + |
1652 | 1646 | const auto& cluster_constraints = g_vpr_ctx.floorplanning().cluster_constraints;
|
1653 | 1647 |
|
1654 | 1648 | // Create a list of clusters to place.
|
@@ -1728,6 +1722,29 @@ static inline void place_all_blocks_ap(enum e_pad_loc_type pad_loc_type,
|
1728 | 1722 | return cluster_score[lhs] > cluster_score[rhs];
|
1729 | 1723 | });
|
1730 | 1724 |
|
| 1725 | + return clusters_to_place; |
| 1726 | +} |
| 1727 | + |
| 1728 | +/** |
| 1729 | + * @brief Tries to place all of the given clusters as closed to their flat |
| 1730 | + * placement as possible (minimum displacement from flat placement). |
| 1731 | + * |
| 1732 | + * This function will place clusters in passes. In the first pass, it will try |
| 1733 | + * to place clusters exactly where their global placement is (according to the |
| 1734 | + * atoms contained in the cluster). In the second pass, all unplaced clusters |
| 1735 | + * will try to be placed within 1 tile of where they wanted to be placed. |
| 1736 | + * Subsequent passes will then try to place clusters at exponentially farther |
| 1737 | + * distances. |
| 1738 | + */ |
| 1739 | +static inline void place_blocks_min_displacement(std::vector<ClusterBlockId>& clusters_to_place, |
| 1740 | + enum e_pad_loc_type pad_loc_type, |
| 1741 | + BlkLocRegistry& blk_loc_registry, |
| 1742 | + const PlaceMacros& place_macros, |
| 1743 | + const ClusteredNetlist& cluster_netlist, |
| 1744 | + const FlatPlacementInfo& flat_placement_info) { |
| 1745 | + |
| 1746 | + const DeviceGrid& device_grid = g_vpr_ctx.device().grid; |
| 1747 | + |
1731 | 1748 | // Compute the max L1 distance on the device. If we cannot find a location
|
1732 | 1749 | // to place a cluster within this distance, then no legal location exists.
|
1733 | 1750 | float max_distance_on_device = device_grid.width() + device_grid.height();
|
@@ -1844,13 +1861,126 @@ static inline void place_all_blocks_ap(enum e_pad_loc_type pad_loc_type,
|
1844 | 1861 | iter++;
|
1845 | 1862 | }
|
1846 | 1863 |
|
| 1864 | + if (clusters_to_place.size() > 0) { |
| 1865 | + VTR_LOG("Unable to place all clusters.\n"); |
| 1866 | + VTR_LOG("Clusters left unplaced:\n"); |
| 1867 | + for (ClusterBlockId blk_id : clusters_to_place) { |
| 1868 | + VTR_LOG("\t%s\n", cluster_netlist.block_name(blk_id).c_str()); |
| 1869 | + } |
| 1870 | + } |
| 1871 | + |
1847 | 1872 | // Check if anything has not been placed, if so just crash for now.
|
1848 | 1873 | // TODO: Should fall back on the original initial placer. Unless there is a
|
1849 | 1874 | // bug in the code above, it could be that it is challenging to place
|
1850 | 1875 | // for this circuit.
|
1851 | 1876 | VTR_ASSERT(clusters_to_place.size() == 0);
|
1852 | 1877 | }
|
1853 | 1878 |
|
| 1879 | +/** |
| 1880 | + * @brief Places all blocks in the clustered netlist as close to the global |
| 1881 | + * placement produced by the AP flow. |
| 1882 | + * |
| 1883 | + * This function places the blocks in stages. The goal of this stage-based |
| 1884 | + * approach is to place clusters which are challenging to place first. Within |
| 1885 | + * each stage, the clusters are ordered based on heuristics such that the most |
| 1886 | + * impactful clusters get first dibs on placement. |
| 1887 | + */ |
| 1888 | +static inline void place_all_blocks_ap(enum e_pad_loc_type pad_loc_type, |
| 1889 | + BlkLocRegistry& blk_loc_registry, |
| 1890 | + const PlaceMacros& place_macros, |
| 1891 | + const FlatPlacementInfo& flat_placement_info) { |
| 1892 | + |
| 1893 | + const ClusteredNetlist& cluster_netlist = g_vpr_ctx.clustering().clb_nlist; |
| 1894 | + |
| 1895 | + // Get a list of clusters to place, sorted based on different heuristics |
| 1896 | + // to try to give more important clusters first dibs on the placement. |
| 1897 | + std::vector<ClusterBlockId> sorted_cluster_list = get_sorted_clusters_to_place(blk_loc_registry, |
| 1898 | + place_macros, |
| 1899 | + cluster_netlist, |
| 1900 | + flat_placement_info); |
| 1901 | + |
| 1902 | + // 1: Get the constrained clusters and place them first. For now, we place |
| 1903 | + // constrained clusters first to prevent other clusters from taking their |
| 1904 | + // spot if they are constrained to one and only one site. |
| 1905 | + // TODO: This gives clusters with region constraints VIP access to the |
| 1906 | + // placement. This may not give the best results. This should be |
| 1907 | + // investigated more once region constraints are more supported in the |
| 1908 | + // AP flow. |
| 1909 | + std::vector<ClusterBlockId> constrained_clusters; |
| 1910 | + constrained_clusters.reserve(sorted_cluster_list.size()); |
| 1911 | + for (ClusterBlockId blk_id : sorted_cluster_list) { |
| 1912 | + if (is_cluster_constrained(blk_id)) |
| 1913 | + constrained_clusters.push_back(blk_id); |
| 1914 | + } |
| 1915 | + |
| 1916 | + if (constrained_clusters.size() > 0) { |
| 1917 | + VTR_LOG("Placing constrained clusters...\n"); |
| 1918 | + place_blocks_min_displacement(constrained_clusters, |
| 1919 | + pad_loc_type, |
| 1920 | + blk_loc_registry, |
| 1921 | + place_macros, |
| 1922 | + cluster_netlist, |
| 1923 | + flat_placement_info); |
| 1924 | + VTR_LOG("\n"); |
| 1925 | + } |
| 1926 | + |
| 1927 | + // 2. Get all of the large macros and place them next. Large macros have a |
| 1928 | + // hard time finding a place to go since they take up so much space. They |
| 1929 | + // also can have a larger impact on the quality of the placement, so we |
| 1930 | + // give them dibs on placement early. |
| 1931 | + std::vector<ClusterBlockId> large_macro_clusters; |
| 1932 | + large_macro_clusters.reserve(sorted_cluster_list.size()); |
| 1933 | + for (ClusterBlockId blk_id : sorted_cluster_list) { |
| 1934 | + // If this block has been placed, skip it. |
| 1935 | + if (is_block_placed(blk_id, blk_loc_registry.block_locs())) |
| 1936 | + continue; |
| 1937 | + |
| 1938 | + // Get the size of the macro this block is a part of. |
| 1939 | + t_pl_macro pl_macro = get_or_create_macro(blk_id, place_macros); |
| 1940 | + size_t macro_size = pl_macro.members.size(); |
| 1941 | + if (macro_size > 1) { |
| 1942 | + // If the size of the macro is larger than 1 (there is more than |
| 1943 | + // one cluster in this macro) add to the list. |
| 1944 | + large_macro_clusters.push_back(blk_id); |
| 1945 | + } |
| 1946 | + } |
| 1947 | + |
| 1948 | + if (large_macro_clusters.size() > 0) { |
| 1949 | + VTR_LOG("Placing clusters that are part of larger macros...\n"); |
| 1950 | + place_blocks_min_displacement(large_macro_clusters, |
| 1951 | + pad_loc_type, |
| 1952 | + blk_loc_registry, |
| 1953 | + place_macros, |
| 1954 | + cluster_netlist, |
| 1955 | + flat_placement_info); |
| 1956 | + VTR_LOG("\n"); |
| 1957 | + } |
| 1958 | + |
| 1959 | + // 3. Place the rest of the clusters. These clusters will be unconstrained |
| 1960 | + // and be of small size; so they are more free to fill in the gaps left |
| 1961 | + // behind. |
| 1962 | + std::vector<ClusterBlockId> clusters_to_place; |
| 1963 | + clusters_to_place.reserve(sorted_cluster_list.size()); |
| 1964 | + for (ClusterBlockId blk_id : sorted_cluster_list) { |
| 1965 | + // If this block has been placed, skip it. |
| 1966 | + if (is_block_placed(blk_id, blk_loc_registry.block_locs())) |
| 1967 | + continue; |
| 1968 | + |
| 1969 | + clusters_to_place.push_back(blk_id); |
| 1970 | + } |
| 1971 | + |
| 1972 | + if (clusters_to_place.size() > 0) { |
| 1973 | + VTR_LOG("Placing general clusters...\n"); |
| 1974 | + place_blocks_min_displacement(clusters_to_place, |
| 1975 | + pad_loc_type, |
| 1976 | + blk_loc_registry, |
| 1977 | + place_macros, |
| 1978 | + cluster_netlist, |
| 1979 | + flat_placement_info); |
| 1980 | + VTR_LOG("\n"); |
| 1981 | + } |
| 1982 | +} |
| 1983 | + |
1854 | 1984 | void initial_placement(const t_placer_opts& placer_opts,
|
1855 | 1985 | const char* constraints_file,
|
1856 | 1986 | const t_noc_opts& noc_opts,
|
|
0 commit comments