From 14f17e8034f933e4314c737d8446d9b3ff5522cb Mon Sep 17 00:00:00 2001 From: Arthur Koucher Date: Mon, 1 Jul 2024 11:10:37 -0300 Subject: [PATCH 01/30] mpl2: 1) remove functionalities that don't belong to multilevel autoclustering from setDefaultThresholds 2)draft for new clustering engine class and structures 3)add new files and incorporate on cmake Signed-off-by: Arthur Koucher --- src/mpl2/CMakeLists.txt | 1 + src/mpl2/src/clusterEngine.cpp | 143 ++++++++++++++++++++ src/mpl2/src/clusterEngine.h | 101 ++++++++++++++ src/mpl2/src/hier_rtlmp.cpp | 208 +++++++++++++++++------------ src/mpl2/src/hier_rtlmp.h | 8 ++ src/mpl2/test/macro_only.ok | 3 +- src/mpl2/test/no_unfixed_macros.ok | 3 +- 7 files changed, 377 insertions(+), 90 deletions(-) create mode 100644 src/mpl2/src/clusterEngine.cpp create mode 100644 src/mpl2/src/clusterEngine.h diff --git a/src/mpl2/CMakeLists.txt b/src/mpl2/CMakeLists.txt index 9406a52806a..ceb323f0004 100644 --- a/src/mpl2/CMakeLists.txt +++ b/src/mpl2/CMakeLists.txt @@ -45,6 +45,7 @@ add_library(mpl2_lib src/SACoreHardMacro.cpp src/SACoreSoftMacro.cpp src/bus_synthesis.cpp + src/clusterEngine.cpp ) target_include_directories(mpl2_lib diff --git a/src/mpl2/src/clusterEngine.cpp b/src/mpl2/src/clusterEngine.cpp new file mode 100644 index 00000000000..8c0cb7bb9f3 --- /dev/null +++ b/src/mpl2/src/clusterEngine.cpp @@ -0,0 +1,143 @@ +/////////////////////////////////////////////////////////////////////////////// +// BSD 3-Clause License +// +// Copyright (c) 2021, The Regents of the University of California +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +/////////////////////////////////////////////////////////////////////////////// + +#include "clusterEngine.h" + +#include "object.h" + +namespace mpl2 { +using utl::MPL; + +ClusteringEngine::ClusteringEngine(odb::dbBlock* block, utl::Logger* logger) + : block_(block), logger_(logger), id_(0) +{ +} + +void ClusteringEngine::buildPhysicalHierarchy() +{ + initTree(); + + setDefaultThresholds(); +} + +void ClusteringEngine::setDesignMetrics(Metrics* design_metrics) +{ + design_metrics_ = design_metrics; +} + +void ClusteringEngine::setTargetStructure(PhysicalHierarchy* tree) +{ + tree_ = tree; +} + +void ClusteringEngine::initTree() +{ + setDefaultThresholds(); + + tree_->root = new Cluster(id_, std::string("root"), logger_); + tree_->root->addDbModule(block_->getTopModule()); + tree_->root->setMetrics(*design_metrics_); + + tree_->maps.id_to_cluster[id_++] = tree_->root; + + // Associate all instances to root + for (odb::dbInst* inst : block_->getInsts()) { + tree_->maps.inst_to_cluster_id[inst] = id_; + } +} + +void ClusteringEngine::setDefaultThresholds() +{ + if (tree_->base_thresholds.max_macro <= 0 + || tree_->base_thresholds.min_macro <= 0 + || tree_->base_thresholds.max_std_cell <= 0 + || tree_->base_thresholds.min_std_cell <= 0) { + // Set base values for std cell lower/upper thresholds + const int min_num_std_cells_allowed = 1000; + tree_->base_thresholds.min_std_cell + = std::floor(design_metrics_->getNumStdCell() + / std::pow(tree_->coarsening_ratio, tree_->max_level)); + if (tree_->base_thresholds.min_std_cell <= min_num_std_cells_allowed) { + tree_->base_thresholds.min_std_cell = min_num_std_cells_allowed; + } + tree_->base_thresholds.max_std_cell + = tree_->base_thresholds.min_std_cell * tree_->coarsening_ratio / 2.0; + + // Set base values for macros lower/upper thresholds + tree_->base_thresholds.min_macro + = std::floor(design_metrics_->getNumMacro() + / std::pow(tree_->coarsening_ratio, tree_->max_level)); + if (tree_->base_thresholds.min_macro <= 0) { + tree_->base_thresholds.min_macro = 1; + } + tree_->base_thresholds.max_macro + = tree_->base_thresholds.min_macro * tree_->coarsening_ratio / 2.0; + + // From original implementation: Reset maximum level based on number + // of macros. + const int min_num_macros_for_multilevel = 150; + if (design_metrics_->getNumMacro() <= min_num_macros_for_multilevel) { + tree_->max_level = 1; + debugPrint( + logger_, + MPL, + "multilevel_autoclustering", + 1, + "Number of macros is below {}. Resetting number of levels to {}", + min_num_macros_for_multilevel, + tree_->max_level); + } + } + + // Set sizes for root + unsigned coarsening_factor + = std::pow(tree_->coarsening_ratio, tree_->max_level - 1); + tree_->base_thresholds.max_macro *= coarsening_factor; + tree_->base_thresholds.min_macro *= coarsening_factor; + tree_->base_thresholds.max_std_cell *= coarsening_factor; + tree_->base_thresholds.min_std_cell *= coarsening_factor; + + debugPrint( + logger_, + MPL, + "multilevel_autoclustering", + 1, + "num level: {}, max_macro: {}, min_macro: {}, max_inst:{}, min_inst:{}", + tree_->max_level, + tree_->base_thresholds.max_macro, + tree_->base_thresholds.min_macro, + tree_->base_thresholds.max_std_cell, + tree_->base_thresholds.min_std_cell); +} + +} // namespace mpl2 \ No newline at end of file diff --git a/src/mpl2/src/clusterEngine.h b/src/mpl2/src/clusterEngine.h new file mode 100644 index 00000000000..7d1ea745cea --- /dev/null +++ b/src/mpl2/src/clusterEngine.h @@ -0,0 +1,101 @@ +/////////////////////////////////////////////////////////////////////////////// +// BSD 3-Clause License +// +// Copyright (c) 2021, The Regents of the University of California +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +/////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include + +namespace utl { +class Logger; +} + +namespace odb { +class dbBlock; +class dbInst; +} // namespace odb + +namespace mpl2 { +class Metrics; +class Cluster; + +struct SizeThresholds +{ + int max_macro = 0; + int min_macro = 0; + int max_std_cell = 0; + int min_std_cell = 0; +}; + +struct PhysicalHierarchyMaps +{ + std::map id_to_cluster; + std::unordered_map inst_to_cluster_id; +}; + +struct PhysicalHierarchy +{ + Cluster* root; + PhysicalHierarchyMaps maps; + + SizeThresholds base_thresholds; + float coarsening_ratio = 0; + int max_level = 0; // Max level might be reset when clustering. +}; + +class ClusteringEngine +{ + public: + ClusteringEngine(odb::dbBlock* block, utl::Logger* logger); + + void setDesignMetrics(Metrics* design_metrics); + void setTargetStructure(PhysicalHierarchy* tree); + + void buildPhysicalHierarchy(); + + private: + void initTree(); + void setDefaultThresholds(); + + odb::dbBlock* block_; + utl::Logger* logger_; + Metrics* design_metrics_; + + PhysicalHierarchy* tree_; + + // Criado na engine + int id_; + SizeThresholds level_thresholds_; +}; + +} // namespace mpl2 \ No newline at end of file diff --git a/src/mpl2/src/hier_rtlmp.cpp b/src/mpl2/src/hier_rtlmp.cpp index d5a40c899c5..0cafe230157 100644 --- a/src/mpl2/src/hier_rtlmp.cpp +++ b/src/mpl2/src/hier_rtlmp.cpp @@ -67,12 +67,13 @@ HierRTLMP::HierRTLMP(sta::dbNetwork* network, sta::dbSta* sta, utl::Logger* logger, par::PartitionMgr* tritonpart) + : network_(network), + db_(db), + sta_(sta), + logger_(logger), + tritonpart_(tritonpart) /*, + clustering_engine_(nullptr) */ { - network_ = network; - db_ = db; - sta_ = sta; - logger_ = logger; - tritonpart_ = tritonpart; } /////////////////////////////////////////////////////////////////////////// @@ -155,6 +156,11 @@ void HierRTLMP::setClusterSize(int max_num_macro, min_num_macro_base_ = min_num_macro; max_num_inst_base_ = max_num_inst; min_num_inst_base_ = min_num_inst; +/* + tree_.base_thresholds.max_macro = max_num_macro; + tree_.base_thresholds.min_macro = min_num_macro; + tree_.base_thresholds.max_std_cell = max_num_inst; + tree_.base_thresholds.min_std_cell = min_num_inst;*/ } void HierRTLMP::setClusterSizeTolerance(float tolerance) @@ -165,11 +171,13 @@ void HierRTLMP::setClusterSizeTolerance(float tolerance) void HierRTLMP::setMaxNumLevel(int max_num_level) { max_num_level_ = max_num_level; + /*tree_.max_level = max_num_level;*/ } void HierRTLMP::setClusterSizeRatioPerLevel(float coarsening_ratio) { coarsening_ratio_ = coarsening_ratio; + /*tree_.coarsening_ratio = coarsening_ratio;*/ } void HierRTLMP::setLargeNetThreshold(int large_net_threshold) @@ -216,61 +224,6 @@ void HierRTLMP::setReportDirectory(const char* report_directory) // Set defaults for min/max number of instances and macros if not set by user. void HierRTLMP::setDefaultThresholds() { - debugPrint(logger_, - MPL, - "multilevel_autoclustering", - 1, - "Setting default threholds..."); - - std::string snap_layer_name; - - for (auto& macro : hard_macro_map_) { - odb::dbMaster* master = macro.first->getMaster(); - for (odb::dbMTerm* mterm : master->getMTerms()) { - if (mterm->getSigType() == odb::dbSigType::SIGNAL) { - for (odb::dbMPin* mpin : mterm->getMPins()) { - for (odb::dbBox* box : mpin->getGeometry()) { - odb::dbTechLayer* layer = box->getTechLayer(); - snap_layer_name = layer->getName(); - } - } - } - } - - break; - } - - // update weight - if (dynamic_congestion_weight_flag_ == true) { - std::vector layers; - int tot_num_layer = 0; - for (odb::dbTechLayer* layer : db_->getTech()->getLayers()) { - if (layer->getType() == odb::dbTechLayerType::ROUTING) { - layers.push_back(layer->getName()); - tot_num_layer++; - } - } - snap_layer_ = 0; - for (int i = 0; i < layers.size(); i++) { - if (layers[i] == snap_layer_name) { - snap_layer_ = i + 1; - break; - } - } - if (snap_layer_ <= 0) { - congestion_weight_ = 0.0; - } else { - congestion_weight_ = 1.0 * snap_layer_ / layers.size(); - } - debugPrint(logger_, - MPL, - "multilevel_autoclustering", - 1, - "snap_layer : {} congestion_weight : {}", - snap_layer_, - congestion_weight_); - } - if (max_num_macro_base_ <= 0 || min_num_macro_base_ <= 0 || max_num_inst_base_ <= 0 || min_num_inst_base_ <= 0) { min_num_inst_base_ @@ -298,18 +251,6 @@ void HierRTLMP::setDefaultThresholds() } } - // for num_level = 1, we recommand to increase the macro_blockage_weight to - // half of the outline_weight - if (max_num_level_ == 1) { - macro_blockage_weight_ = outline_weight_ / 2.0; - debugPrint(logger_, - MPL, - "multilevel_autoclustering", - 1, - "Reset macro_blockage_weight : {}", - macro_blockage_weight_); - } - // Set sizes for root level based on coarsening_factor and the number of // physical hierarchy levels unsigned coarsening_factor = std::pow(coarsening_ratio_, max_num_level_ - 1); @@ -329,21 +270,6 @@ void HierRTLMP::setDefaultThresholds() min_num_macro_base_, max_num_inst_base_, min_num_inst_base_); - - if (logger_->debugCheck(MPL, "multilevel_autoclustering", 1)) { - logger_->report("\nPrint Default Parameters\n"); - logger_->report("area_weight_ = {}", area_weight_); - logger_->report("outline_weight_ = {}", outline_weight_); - logger_->report("wirelength_weight_ = {}", wirelength_weight_); - logger_->report("guidance_weight_ = {}", guidance_weight_); - logger_->report("fence_weight_ = {}", fence_weight_); - logger_->report("boundary_weight_ = {}", boundary_weight_); - logger_->report("notch_weight_ = {}", notch_weight_); - logger_->report("macro_blockage_weight_ = {}", macro_blockage_weight_); - logger_->report("halo_width_ = {}", halo_width_); - logger_->report("halo_height_ = {}", halo_height_); - logger_->report("bus_planning_on_ = {}\n", bus_planning_on_); - } } // Top Level Function @@ -380,7 +306,13 @@ void HierRTLMP::run() graphics_->startFine(); } + adjustMacroBlockageWeight(); + if (logger_->debugCheck(MPL, "hierarchical_macro_placement", 1)) { + reportSAWeights(); + } + if (bus_planning_on_) { + adjustCongestionWeight(); runHierarchicalMacroPlacement(root_cluster_); } else { runHierarchicalMacroPlacementWithoutBusPlanning(root_cluster_); @@ -481,11 +413,12 @@ void HierRTLMP::reportLogicalHierarchyInformation(float core_area, float core_util) { logger_->report( - "Traversed logical hierarchy\n" "\tNumber of std cell instances: {}\n" "\tArea of std cell instances: {:.2f}\n" "\tNumber of macros: {}\n" "\tArea of macros: {:.2f}\n" + "\tHalo width: {:.2f}\n" + "\tHalo height: {:.2f}\n" "\tArea of macros with halos: {:.2f}\n" "\tArea of std cell instances + Area of macros: {:.2f}\n" "\tCore area: {:.2f}\n" @@ -496,6 +429,8 @@ void HierRTLMP::reportLogicalHierarchyInformation(float core_area, metrics_->getStdCellArea(), metrics_->getNumMacro(), metrics_->getMacroArea(), + halo_width_, + halo_height_, macro_with_halo_area_, metrics_->getStdCellArea() + metrics_->getMacroArea(), core_area, @@ -525,6 +460,14 @@ void HierRTLMP::initPhysicalHierarchy() // Transform the logical hierarchy into a physical hierarchy. void HierRTLMP::runMultilevelAutoclustering() { + /* + clustering_engine_ = std::make_unique(block_, logger_); + clustering_engine_->setDesignMetrics(metrics_); + clustering_engine_->setTargetStructure(&tree_); + + clustering_engine_->buildPhysicalHierarchy(); + */ + initPhysicalHierarchy(); createIOClusters(); @@ -3238,6 +3181,62 @@ void HierRTLMP::setPlacementBlockages() } } +void HierRTLMP::adjustCongestionWeight() +{ + // TODO: Isso aqui faz sentido? E se nós usarmos pinos de sinal + // em dois layers? Qual é o snap layer nesse caso? O de baixo + // ou o de cima? + // Should we alow setting the snap layer manually? + std::string snap_layer_name; + + for (auto& macro : hard_macro_map_) { + odb::dbMaster* master = macro.first->getMaster(); + for (odb::dbMTerm* mterm : master->getMTerms()) { + if (mterm->getSigType() == odb::dbSigType::SIGNAL) { + for (odb::dbMPin* mpin : mterm->getMPins()) { + for (odb::dbBox* box : mpin->getGeometry()) { + odb::dbTechLayer* layer = box->getTechLayer(); + snap_layer_name = layer->getName(); + } + } + } + } + + break; + } + + // update weight + if (dynamic_congestion_weight_flag_) { + std::vector layers; + int tot_num_layer = 0; + for (odb::dbTechLayer* layer : db_->getTech()->getLayers()) { + if (layer->getType() == odb::dbTechLayerType::ROUTING) { + layers.push_back(layer->getName()); + tot_num_layer++; + } + } + snap_layer_ = 0; + for (int i = 0; i < layers.size(); i++) { + if (layers[i] == snap_layer_name) { + snap_layer_ = i + 1; + break; + } + } + if (snap_layer_ <= 0) { + congestion_weight_ = 0.0; + } else { + congestion_weight_ = 1.0 * snap_layer_ / layers.size(); + } + debugPrint(logger_, + MPL, + "hierarchical_macro_placement", + 1, + "Adjusting congestion weight to {} - Snap layer is {}", + congestion_weight_, + snap_layer_); + } +} + // Cluster Placement Engine Starts ........................................... // The cluster placement is done in a top-down manner // (Preorder DFS) @@ -4207,6 +4206,39 @@ void HierRTLMP::mergeNets(std::vector& nets) } } +// Recommendation from the original implementation: +// For single level, increase macro blockage weight to +// half of the outline weight. +void HierRTLMP::adjustMacroBlockageWeight() +{ + if (max_num_level_ == 1) { + float new_macro_blockage_weight = outline_weight_ / 2.0; + debugPrint(logger_, + MPL, + "multilevel_autoclustering", + 1, + "Number of levels is {}, Changing macro blockage weight from {} " + "to {} (half of the outline weight)", + max_num_level_, + macro_blockage_weight_, + new_macro_blockage_weight); + macro_blockage_weight_ = new_macro_blockage_weight; + } +} + +void HierRTLMP::reportSAWeights() +{ + logger_->report("\nSimmulated Annealing Weights:\n"); + logger_->report("Area = \t{}", area_weight_); + logger_->report("Outline = \t{}", outline_weight_); + logger_->report("WL = \t{}", wirelength_weight_); + logger_->report("Guidance = {}", guidance_weight_); + logger_->report("Fence = {}", fence_weight_); + logger_->report("Boundary = {}", boundary_weight_); + logger_->report("Notch = {}", notch_weight_); + logger_->report("Macro Blockage = {}", macro_blockage_weight_); +} + // Multilevel macro placement without bus planning void HierRTLMP::runHierarchicalMacroPlacementWithoutBusPlanning(Cluster* parent) { diff --git a/src/mpl2/src/hier_rtlmp.h b/src/mpl2/src/hier_rtlmp.h index 09c62fe4464..593dd396fb4 100644 --- a/src/mpl2/src/hier_rtlmp.h +++ b/src/mpl2/src/hier_rtlmp.h @@ -43,6 +43,7 @@ #include #include "Mpl2Observer.h" +/*#include "clusterEngine.h"*/ namespace odb { class dbBTerm; @@ -266,6 +267,8 @@ class HierRTLMP // Hierarchical Macro Placement 1st stage: Cluster Placement void runHierarchicalMacroPlacement(Cluster* parent); + void adjustMacroBlockageWeight(); + void reportSAWeights(); void runHierarchicalMacroPlacementWithoutBusPlanning(Cluster* parent); void runEnhancedHierarchicalMacroPlacement(Cluster* parent); @@ -319,6 +322,7 @@ class HierRTLMP // Bus Planning void callBusPlanning(std::vector& shaped_macros, std::vector& nets_old); + void adjustCongestionWeight(); static bool isIgnoredMaster(odb::dbMaster* master); @@ -332,6 +336,8 @@ class HierRTLMP utl::Logger* logger_ = nullptr; par::PartitionMgr* tritonpart_ = nullptr; + /*std::unique_ptr clustering_engine_;*/ + // flag variables const bool dynamic_congestion_weight_flag_ = false; // Our experiments show that for most testcases, turn off bus planning @@ -496,6 +502,8 @@ class HierRTLMP bool design_has_only_macros_ = false; bool design_has_unfixed_macros_ = true; + /*PhysicalHierarchy tree_;*/ + std::unique_ptr graphics_; }; diff --git a/src/mpl2/test/macro_only.ok b/src/mpl2/test/macro_only.ok index 8fff98051aa..8adae8e6d22 100644 --- a/src/mpl2/test/macro_only.ok +++ b/src/mpl2/test/macro_only.ok @@ -4,11 +4,12 @@ [INFO ODB-0128] Design: macro_only [INFO ODB-0253] Updated 10 components. Floorplan Outline: (0.0, 0.0) (450.0, 450.0), Core Outline: (4.94, 4.2) (444.98, 443.8) -Traversed logical hierarchy Number of std cell instances: 0 Area of std cell instances: 0.00 Number of macros: 10 Area of macros: 166000.00 + Halo width: 4.00 + Halo height: 4.00 Area of macros with halos: 187920.00 Area of std cell instances + Area of macros: 166000.00 Core area: 193441.58 diff --git a/src/mpl2/test/no_unfixed_macros.ok b/src/mpl2/test/no_unfixed_macros.ok index a799d4f09a9..940c23b4a49 100644 --- a/src/mpl2/test/no_unfixed_macros.ok +++ b/src/mpl2/test/no_unfixed_macros.ok @@ -5,11 +5,12 @@ [INFO ODB-0253] Updated 10 components. [INFO ODB-0254] Updated 12 nets and 24 connections. Floorplan Outline: (0.0, 0.0) (450.0, 450.0), Core Outline: (4.94, 4.2) (444.98, 443.8) -Traversed logical hierarchy Number of std cell instances: 0 Area of std cell instances: 0.00 Number of macros: 10 Area of macros: 166000.00 + Halo width: 4.00 + Halo height: 4.00 Area of macros with halos: 187920.00 Area of std cell instances + Area of macros: 166000.00 Core area: 193441.58 From 9711bf3b55fbebe316fc84032730f8733b3119b4 Mon Sep 17 00:00:00 2001 From: Arthur Koucher Date: Mon, 1 Jul 2024 12:19:20 -0300 Subject: [PATCH 02/30] mpl2: create constructor for structs, incorporate bundled io creation into new class Signed-off-by: Arthur Koucher --- src/mpl2/src/clusterEngine.cpp | 201 ++++++++++++++++++++++++++++++++- src/mpl2/src/clusterEngine.h | 36 +++++- src/mpl2/src/hier_rtlmp.cpp | 12 +- src/mpl2/src/hier_rtlmp.h | 4 +- 4 files changed, 240 insertions(+), 13 deletions(-) diff --git a/src/mpl2/src/clusterEngine.cpp b/src/mpl2/src/clusterEngine.cpp index 8c0cb7bb9f3..a9c97190289 100644 --- a/src/mpl2/src/clusterEngine.cpp +++ b/src/mpl2/src/clusterEngine.cpp @@ -46,8 +46,9 @@ ClusteringEngine::ClusteringEngine(odb::dbBlock* block, utl::Logger* logger) void ClusteringEngine::buildPhysicalHierarchy() { initTree(); - setDefaultThresholds(); + + createIOClusters(); } void ClusteringEngine::setDesignMetrics(Metrics* design_metrics) @@ -140,4 +141,202 @@ void ClusteringEngine::setDefaultThresholds() tree_->base_thresholds.min_std_cell); } +void ClusteringEngine::createIOClusters() +{ + mapIOPads(); + + debugPrint(logger_, + MPL, + "multilevel_autoclustering", + 1, + "Creating bundledIO clusters..."); + + const odb::Rect die = block_->getDieArea(); + + // Get the floorplan information and get the range of bundled IO regions + odb::Rect die_box = block_->getCoreArea(); + int core_lx = die_box.xMin(); + int core_ly = die_box.yMin(); + int core_ux = die_box.xMax(); + int core_uy = die_box.yMax(); + const int x_base = (die.xMax() - die.xMin()) / tree_->bundled_ios_per_edge; + const int y_base = (die.yMax() - die.yMin()) / tree_->bundled_ios_per_edge; + int cluster_id_base = id_; + + // Map all the BTerms / Pads to Bundled IOs (cluster) + std::vector prefix_vec; + prefix_vec.emplace_back("L"); + prefix_vec.emplace_back("T"); + prefix_vec.emplace_back("R"); + prefix_vec.emplace_back("B"); + std::map cluster_io_map; + for (int i = 0; i < 4; + i++) { // four boundaries (Left, Top, Right and Bottom in order) + for (int j = 0; j < tree_->bundled_ios_per_edge; j++) { + std::string cluster_name = prefix_vec[i] + std::to_string(j); + Cluster* cluster = new Cluster(id_, cluster_name, logger_); + tree_->root->addChild(cluster); + cluster->setParent(tree_->root); + cluster_io_map[id_] = false; + tree_->maps.id_to_cluster[id_++] = cluster; + int x = 0.0; + int y = 0.0; + int width = 0; + int height = 0; + if (i == 0) { // Left boundary + x = die.xMin(); + y = die.yMin() + y_base * j; + height = y_base; + } else if (i == 1) { // Top boundary + x = die.xMin() + x_base * j; + y = die.yMax(); + width = x_base; + } else if (i == 2) { // Right boundary + x = die.xMax(); + y = die.yMax() - y_base * (j + 1); + height = y_base; + } else { // Bottom boundary + x = die.xMax() - x_base * (j + 1); + y = die.yMin(); + width = x_base; + } + + // set the cluster to a IO cluster + cluster->setAsIOCluster(std::pair(block_->dbuToMicrons(x), + block_->dbuToMicrons(y)), + block_->dbuToMicrons(width), + block_->dbuToMicrons(height)); + } + } + + // Map all the BTerms to bundled IOs + for (auto term : block_->getBTerms()) { + int lx = std::numeric_limits::max(); + int ly = std::numeric_limits::max(); + int ux = 0; + int uy = 0; + // If the design has IO pads, these block terms + // will not have block pins. + // Otherwise, the design will have IO pins. + for (const auto pin : term->getBPins()) { + for (const auto box : pin->getBoxes()) { + lx = std::min(lx, box->xMin()); + ly = std::min(ly, box->yMin()); + ux = std::max(ux, box->xMax()); + uy = std::max(uy, box->yMax()); + } + } + // remove power pins + if (term->getSigType().isSupply()) { + continue; + } + + // If the term has a connected pad, get the bbox from the pad inst + if (tree_->maps.bterm_to_inst.find(term) + != tree_->maps.bterm_to_inst.end()) { + lx = tree_->maps.bterm_to_inst[term]->getBBox()->xMin(); + ly = tree_->maps.bterm_to_inst[term]->getBBox()->yMin(); + ux = tree_->maps.bterm_to_inst[term]->getBBox()->xMax(); + uy = tree_->maps.bterm_to_inst[term]->getBBox()->yMax(); + if (lx <= core_lx) { + lx = die.xMin(); + } + if (ly <= core_ly) { + ly = die.yMin(); + } + if (ux >= core_ux) { + ux = die.xMax(); + } + if (uy >= core_uy) { + uy = die.yMax(); + } + } + // calculate cluster id based on the location of IO Pins / Pads + int cluster_id = -1; + if (lx <= die.xMin()) { + // The IO is on the left boundary + cluster_id = cluster_id_base + + std::floor(((ly + uy) / 2.0 - die.yMin()) / y_base); + } else if (uy >= die.yMax()) { + // The IO is on the top boundary + cluster_id = cluster_id_base + tree_->bundled_ios_per_edge + + std::floor(((lx + ux) / 2.0 - die.xMin()) / x_base); + } else if (ux >= die.xMax()) { + // The IO is on the right boundary + cluster_id = cluster_id_base + tree_->bundled_ios_per_edge * 2 + + std::floor((die.yMax() - (ly + uy) / 2.0) / y_base); + } else if (ly <= die.yMin()) { + // The IO is on the bottom boundary + cluster_id = cluster_id_base + tree_->bundled_ios_per_edge * 3 + + std::floor((die.xMax() - (lx + ux) / 2.0) / x_base); + } + + // Check if the IO pins / Pads exist + if (cluster_id == -1) { + logger_->error( + MPL, + 2, + "Floorplan has not been initialized? Pin location error for {}.", + term->getName()); + } else { + tree_->maps.bterm_to_cluster_id[term] = cluster_id; + } + + cluster_io_map[cluster_id] = true; + } + + // delete the IO clusters that do not have any pins assigned to them + for (auto& [cluster_id, flag] : cluster_io_map) { + if (!flag) { + debugPrint(logger_, + MPL, + "multilevel_autoclustering", + 1, + "Remove IO Cluster with no pins: {}, id: {}", + tree_->maps.id_to_cluster[cluster_id]->getName(), + cluster_id); + tree_->maps.id_to_cluster[cluster_id]->getParent()->removeChild( + tree_->maps.id_to_cluster[cluster_id]); + delete tree_->maps.id_to_cluster[cluster_id]; + tree_->maps.id_to_cluster.erase(cluster_id); + } + } + + // At this point the cluster map has only the root (id = 0) and bundledIOs + if (tree_->maps.id_to_cluster.size() == 1) { + logger_->warn(MPL, 26, "Design has no IO pins!"); + tree_->has_io_clusters = false; + } +} + +void ClusteringEngine::mapIOPads() +{ + bool design_has_io_pads = false; + for (auto inst : block_->getInsts()) { + if (inst->getMaster()->isPad()) { + design_has_io_pads = true; + break; + } + } + + if (!design_has_io_pads) { + return; + } + + for (odb::dbNet* net : block_->getNets()) { + if (net->getBTerms().size() == 0) { + continue; + } + + // If the design has IO pads, there is a net + // connecting the IO pin and IO pad instance + for (odb::dbBTerm* bterm : net->getBTerms()) { + for (odb::dbITerm* iterm : net->getITerms()) { + odb::dbInst* inst = iterm->getInst(); + tree_->maps.bterm_to_inst[bterm] = inst; + } + } + } +} + } // namespace mpl2 \ No newline at end of file diff --git a/src/mpl2/src/clusterEngine.h b/src/mpl2/src/clusterEngine.h index 7d1ea745cea..c6449fbd4cc 100644 --- a/src/mpl2/src/clusterEngine.h +++ b/src/mpl2/src/clusterEngine.h @@ -43,6 +43,7 @@ class Logger; namespace odb { class dbBlock; class dbInst; +class dbBTerm; } // namespace odb namespace mpl2 { @@ -51,26 +52,47 @@ class Cluster; struct SizeThresholds { - int max_macro = 0; - int min_macro = 0; - int max_std_cell = 0; - int min_std_cell = 0; + SizeThresholds() + : max_macro(0), min_macro(0), max_std_cell(0), min_std_cell(0) + { + } + + int max_macro; + int min_macro; + int max_std_cell; + int min_std_cell; }; struct PhysicalHierarchyMaps { std::map id_to_cluster; std::unordered_map inst_to_cluster_id; + std::unordered_map bterm_to_cluster_id; + + // Only for designs with IO Pads + std::map bterm_to_inst; }; struct PhysicalHierarchy { + PhysicalHierarchy() + : root(nullptr), + coarsening_ratio(0), + max_level(0), + bundled_ios_per_edge(0), + has_io_clusters(true) + { + } + Cluster* root; PhysicalHierarchyMaps maps; SizeThresholds base_thresholds; - float coarsening_ratio = 0; - int max_level = 0; // Max level might be reset when clustering. + float coarsening_ratio; + int max_level; + int bundled_ios_per_edge; + + bool has_io_clusters; }; class ClusteringEngine @@ -86,6 +108,8 @@ class ClusteringEngine private: void initTree(); void setDefaultThresholds(); + void createIOClusters(); + void mapIOPads(); odb::dbBlock* block_; utl::Logger* logger_; diff --git a/src/mpl2/src/hier_rtlmp.cpp b/src/mpl2/src/hier_rtlmp.cpp index 0cafe230157..1db015d65ef 100644 --- a/src/mpl2/src/hier_rtlmp.cpp +++ b/src/mpl2/src/hier_rtlmp.cpp @@ -145,6 +145,8 @@ void HierRTLMP::setHaloHeight(float halo_height) void HierRTLMP::setNumBundledIOsPerBoundary(int num_bundled_ios) { num_bundled_IOs_ = num_bundled_ios; + + tree_.bundled_ios_per_edge = num_bundled_ios; } void HierRTLMP::setClusterSize(int max_num_macro, @@ -156,11 +158,11 @@ void HierRTLMP::setClusterSize(int max_num_macro, min_num_macro_base_ = min_num_macro; max_num_inst_base_ = max_num_inst; min_num_inst_base_ = min_num_inst; -/* + tree_.base_thresholds.max_macro = max_num_macro; tree_.base_thresholds.min_macro = min_num_macro; tree_.base_thresholds.max_std_cell = max_num_inst; - tree_.base_thresholds.min_std_cell = min_num_inst;*/ + tree_.base_thresholds.min_std_cell = min_num_inst; } void HierRTLMP::setClusterSizeTolerance(float tolerance) @@ -171,13 +173,15 @@ void HierRTLMP::setClusterSizeTolerance(float tolerance) void HierRTLMP::setMaxNumLevel(int max_num_level) { max_num_level_ = max_num_level; - /*tree_.max_level = max_num_level;*/ + + tree_.max_level = max_num_level; } void HierRTLMP::setClusterSizeRatioPerLevel(float coarsening_ratio) { coarsening_ratio_ = coarsening_ratio; - /*tree_.coarsening_ratio = coarsening_ratio;*/ + + tree_.coarsening_ratio = coarsening_ratio; } void HierRTLMP::setLargeNetThreshold(int large_net_threshold) diff --git a/src/mpl2/src/hier_rtlmp.h b/src/mpl2/src/hier_rtlmp.h index 593dd396fb4..2ad6e74153c 100644 --- a/src/mpl2/src/hier_rtlmp.h +++ b/src/mpl2/src/hier_rtlmp.h @@ -43,7 +43,7 @@ #include #include "Mpl2Observer.h" -/*#include "clusterEngine.h"*/ +#include "clusterEngine.h" namespace odb { class dbBTerm; @@ -502,7 +502,7 @@ class HierRTLMP bool design_has_only_macros_ = false; bool design_has_unfixed_macros_ = true; - /*PhysicalHierarchy tree_;*/ + PhysicalHierarchy tree_; std::unique_ptr graphics_; }; From bd6b0b0f7b0cc5eeffdb299813e34dbf91666dd4 Mon Sep 17 00:00:00 2001 From: Arthur Koucher Date: Wed, 3 Jul 2024 11:36:10 -0300 Subject: [PATCH 03/30] mpl2: convert IDs so that the changes can be compiled Signed-off-by: Arthur Koucher --- src/mpl2/src/hier_rtlmp.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mpl2/src/hier_rtlmp.cpp b/src/mpl2/src/hier_rtlmp.cpp index 1db015d65ef..e00f2129757 100644 --- a/src/mpl2/src/hier_rtlmp.cpp +++ b/src/mpl2/src/hier_rtlmp.cpp @@ -886,7 +886,7 @@ void HierRTLMP::createIOClusters() if (cluster_id == -1) { logger_->error( MPL, - 2, + 102, "Floorplan has not been initialized? Pin location error for {}.", term->getName()); } else { @@ -915,7 +915,7 @@ void HierRTLMP::createIOClusters() // At this point the cluster map has only the root (id = 0) and bundledIOs if (cluster_map_.size() == 1) { - logger_->warn(MPL, 26, "Design has no IO pins!"); + logger_->warn(MPL, 126, "Design has no IO pins!"); design_has_io_clusters_ = false; } } From 94b624eda1b21b6117a512c88771a3112c0c70f8 Mon Sep 17 00:00:00 2001 From: Arthur Koucher Date: Wed, 3 Jul 2024 14:19:20 -0300 Subject: [PATCH 04/30] mpl2: incorporate dataflow Signed-off-by: Arthur Koucher --- src/mpl2/src/clusterEngine.cpp | 373 ++++++++++++++++++++++++++++++++- src/mpl2/src/clusterEngine.h | 63 +++++- src/mpl2/src/hier_rtlmp.cpp | 11 +- src/mpl2/src/hier_rtlmp.h | 3 +- 4 files changed, 438 insertions(+), 12 deletions(-) diff --git a/src/mpl2/src/clusterEngine.cpp b/src/mpl2/src/clusterEngine.cpp index a9c97190289..854a05c26ab 100644 --- a/src/mpl2/src/clusterEngine.cpp +++ b/src/mpl2/src/clusterEngine.cpp @@ -33,13 +33,17 @@ #include "clusterEngine.h" +#include "db_sta/dbNetwork.hh" #include "object.h" +#include "sta/Liberty.hh" namespace mpl2 { using utl::MPL; -ClusteringEngine::ClusteringEngine(odb::dbBlock* block, utl::Logger* logger) - : block_(block), logger_(logger), id_(0) +ClusteringEngine::ClusteringEngine(odb::dbBlock* block, + sta::dbNetwork* network, + utl::Logger* logger) + : block_(block), network_(network), logger_(logger), id_(0) { } @@ -49,6 +53,7 @@ void ClusteringEngine::buildPhysicalHierarchy() setDefaultThresholds(); createIOClusters(); + createDataFlow(); } void ClusteringEngine::setDesignMetrics(Metrics* design_metrics) @@ -339,4 +344,368 @@ void ClusteringEngine::mapIOPads() } } +// Dataflow is used to improve quality of macro placement. +// Here we model each std cell instance, IO pin and macro pin as vertices. +void ClusteringEngine::createDataFlow() +{ + debugPrint( + logger_, MPL, "multilevel_autoclustering", 1, "Creating dataflow..."); + if (data_flow.register_dist <= 0) { + return; + } + + // create vertex id property for std cell, IO pin and macro pin + std::map io_pin_vertex; + std::map std_cell_vertex; + std::map macro_pin_vertex; + + std::vector stop_flag_vec; + // assign vertex_id property of each Bterm + // All boundary terms are marked as sequential stopping pts + for (odb::dbBTerm* term : block_->getBTerms()) { + odb::dbIntProperty::create(term, "vertex_id", stop_flag_vec.size()); + io_pin_vertex[stop_flag_vec.size()] = term; + stop_flag_vec.push_back(true); + } + + // assign vertex_id property of each instance + for (auto inst : block_->getInsts()) { + odb::dbMaster* master = inst->getMaster(); + if (isIgnoredMaster(master) || master->isBlock()) { + continue; + } + + const sta::LibertyCell* liberty_cell = network_->libertyCell(inst); + if (!liberty_cell) { + continue; + } + + // Mark registers + odb::dbIntProperty::create(inst, "vertex_id", stop_flag_vec.size()); + std_cell_vertex[stop_flag_vec.size()] = inst; + + if (liberty_cell->hasSequentials()) { + stop_flag_vec.push_back(true); + } else { + stop_flag_vec.push_back(false); + } + } + // assign vertex_id property of each macro pin + // all macro pins are flagged as sequential stopping pt + for (auto& [macro, hard_macro] : tree_->maps.inst_to_hard) { + for (odb::dbITerm* pin : macro->getITerms()) { + if (pin->getSigType() != odb::dbSigType::SIGNAL) { + continue; + } + odb::dbIntProperty::create(pin, "vertex_id", stop_flag_vec.size()); + macro_pin_vertex[stop_flag_vec.size()] = pin; + stop_flag_vec.push_back(true); + } + } + + // + // Num of vertices will be # of boundary pins + number of logical std cells + + // number of macro pins) + // + debugPrint(logger_, + MPL, + "multilevel_autoclustering", + 1, + "Number of vertices: {}", + stop_flag_vec.size()); + + // create hypergraphs + std::vector> vertices(stop_flag_vec.size()); + std::vector> backward_vertices(stop_flag_vec.size()); + std::vector> hyperedges; // dircted hypergraph + // traverse the netlist + for (odb::dbNet* net : block_->getNets()) { + // ignore all the power net + if (net->getSigType().isSupply()) { + continue; + } + int driver_id = -1; // driver vertex id + std::set loads_id; // load vertex id + bool ignore = false; + // check the connected instances + for (odb::dbITerm* iterm : net->getITerms()) { + odb::dbInst* inst = iterm->getInst(); + odb::dbMaster* master = inst->getMaster(); + // We ignore nets connecting ignored masters + if (isIgnoredMaster(master)) { + ignore = true; + break; + } + int vertex_id = -1; + if (master->isBlock()) { + vertex_id = odb::dbIntProperty::find(iterm, "vertex_id")->getValue(); + } else { + vertex_id = odb::dbIntProperty::find(inst, "vertex_id")->getValue(); + } + if (iterm->getIoType() == odb::dbIoType::OUTPUT) { + driver_id = vertex_id; + } else { + loads_id.insert(vertex_id); + } + } + if (ignore) { + continue; // the nets with Pads should be ignored + } + + // check the connected IO pins of the net + for (odb::dbBTerm* bterm : net->getBTerms()) { + const int vertex_id + = odb::dbIntProperty::find(bterm, "vertex_id")->getValue(); + if (bterm->getIoType() == odb::dbIoType::INPUT) { + driver_id = vertex_id; + } else { + loads_id.insert(vertex_id); + } + } + + // + // Skip high fanout nets or nets that do not have valid driver or loads + // + if (driver_id < 0 || loads_id.empty() + || loads_id.size() > tree_->large_net_threshold) { + continue; + } + + // Create the hyperedge + std::vector hyperedge{driver_id}; + for (auto& load : loads_id) { + if (load != driver_id) { + hyperedge.push_back(load); + } + } + vertices[driver_id].push_back(hyperedges.size()); + for (int i = 1; i < hyperedge.size(); i++) { + backward_vertices[hyperedge[i]].push_back(hyperedges.size()); + } + hyperedges.push_back(hyperedge); + } // end net traversal + + debugPrint( + logger_, MPL, "multilevel_autoclustering", 1, "Created hypergraph"); + + // traverse hypergraph to build dataflow + for (auto [src, src_pin] : io_pin_vertex) { + int idx = 0; + std::vector visited(vertices.size(), false); + std::vector> insts(data_flow.register_dist); + dataFlowDFSIOPin(src, + idx, + insts, + io_pin_vertex, + std_cell_vertex, + macro_pin_vertex, + stop_flag_vec, + visited, + vertices, + hyperedges, + false); + dataFlowDFSIOPin(src, + idx, + insts, + io_pin_vertex, + std_cell_vertex, + macro_pin_vertex, + stop_flag_vec, + visited, + backward_vertices, + hyperedges, + true); + data_flow.io_to_regs.emplace_back(src_pin, insts); + } + + for (auto [src, src_pin] : macro_pin_vertex) { + int idx = 0; + std::vector visited(vertices.size(), false); + std::vector> std_cells(data_flow.register_dist); + std::vector> macros(data_flow.register_dist); + dataFlowDFSMacroPin(src, + idx, + std_cells, + macros, + io_pin_vertex, + std_cell_vertex, + macro_pin_vertex, + stop_flag_vec, + visited, + vertices, + hyperedges, + false); + dataFlowDFSMacroPin(src, + idx, + std_cells, + macros, + io_pin_vertex, + std_cell_vertex, + macro_pin_vertex, + stop_flag_vec, + visited, + backward_vertices, + hyperedges, + true); + data_flow.macro_pin_to_regs.emplace_back(src_pin, std_cells); + data_flow.macro_pin_to_macros.emplace_back(src_pin, macros); + } +} + +/* static */ +bool ClusteringEngine::isIgnoredMaster(odb::dbMaster* master) +{ + // IO corners are sometimes marked as end caps + return master->isPad() || master->isCover() || master->isEndCap(); +} + +// Forward or Backward DFS search to find sequential paths from/to IO pins based +// on hop count to macro pins +void ClusteringEngine::dataFlowDFSIOPin( + int parent, + int idx, + std::vector>& insts, + std::map& io_pin_vertex, + std::map& std_cell_vertex, + std::map& macro_pin_vertex, + std::vector& stop_flag_vec, + std::vector& visited, + std::vector>& vertices, + std::vector>& hyperedges, + bool backward_search) +{ + visited[parent] = true; + if (stop_flag_vec[parent]) { + if (parent < io_pin_vertex.size()) { + ; // currently we do not consider IO pin to IO pin connection + } else if (parent < io_pin_vertex.size() + std_cell_vertex.size()) { + insts[idx].insert(std_cell_vertex[parent]); + } else { + insts[idx].insert(macro_pin_vertex[parent]->getInst()); + } + idx++; + } + + if (idx >= data_flow.register_dist) { + return; + } + + if (!backward_search) { + for (auto& hyperedge : vertices[parent]) { + for (auto& vertex : hyperedges[hyperedge]) { + // we do not consider pin to pin + if (visited[vertex] || vertex < io_pin_vertex.size()) { + continue; + } + dataFlowDFSIOPin(vertex, + idx, + insts, + io_pin_vertex, + std_cell_vertex, + macro_pin_vertex, + stop_flag_vec, + visited, + vertices, + hyperedges, + backward_search); + } + } + } else { + for (auto& hyperedge : vertices[parent]) { + const int vertex = hyperedges[hyperedge][0]; // driver vertex + // we do not consider pin to pin + if (visited[vertex] || vertex < io_pin_vertex.size()) { + continue; + } + dataFlowDFSIOPin(vertex, + idx, + insts, + io_pin_vertex, + std_cell_vertex, + macro_pin_vertex, + stop_flag_vec, + visited, + vertices, + hyperedges, + backward_search); + } + } +} + +// Forward or Backward DFS search to find sequential paths between Macros based +// on hop count +void ClusteringEngine::dataFlowDFSMacroPin( + int parent, + int idx, + std::vector>& std_cells, + std::vector>& macros, + std::map& io_pin_vertex, + std::map& std_cell_vertex, + std::map& macro_pin_vertex, + std::vector& stop_flag_vec, + std::vector& visited, + std::vector>& vertices, + std::vector>& hyperedges, + bool backward_search) +{ + visited[parent] = true; + if (stop_flag_vec[parent]) { + if (parent < io_pin_vertex.size()) { + ; // the connection between IO and macro pins have been considers + } else if (parent < io_pin_vertex.size() + std_cell_vertex.size()) { + std_cells[idx].insert(std_cell_vertex[parent]); + } else { + macros[idx].insert(macro_pin_vertex[parent]->getInst()); + } + idx++; + } + + if (idx >= data_flow.register_dist) { + return; + } + + if (!backward_search) { + for (auto& hyperedge : vertices[parent]) { + for (auto& vertex : hyperedges[hyperedge]) { + // we do not consider pin to pin + if (visited[vertex] || vertex < io_pin_vertex.size()) { + continue; + } + dataFlowDFSMacroPin(vertex, + idx, + std_cells, + macros, + io_pin_vertex, + std_cell_vertex, + macro_pin_vertex, + stop_flag_vec, + visited, + vertices, + hyperedges, + backward_search); + } + } + } else { + for (auto& hyperedge : vertices[parent]) { + const int vertex = hyperedges[hyperedge][0]; + // we do not consider pin to pin + if (visited[vertex] || vertex < io_pin_vertex.size()) { + continue; + } + dataFlowDFSMacroPin(vertex, + idx, + std_cells, + macros, + io_pin_vertex, + std_cell_vertex, + macro_pin_vertex, + stop_flag_vec, + visited, + vertices, + hyperedges, + backward_search); + } + } +} + } // namespace mpl2 \ No newline at end of file diff --git a/src/mpl2/src/clusterEngine.h b/src/mpl2/src/clusterEngine.h index c6449fbd4cc..5390c73927f 100644 --- a/src/mpl2/src/clusterEngine.h +++ b/src/mpl2/src/clusterEngine.h @@ -34,21 +34,44 @@ #pragma once #include +#include #include +#include namespace utl { class Logger; } +namespace sta { +class dbNetwork; +} + namespace odb { class dbBlock; class dbInst; class dbBTerm; +class dbMaster; +class dbITerm; } // namespace odb namespace mpl2 { class Metrics; class Cluster; +class HardMacro; + +struct DataFlow +{ + const int register_dist = 5; + const float factor = 2.0; + const float weight = 1; + + std::vector>>> + macro_pin_to_regs; + std::vector>>> + io_to_regs; + std::vector>>> + macro_pin_to_macros; +}; struct SizeThresholds { @@ -66,6 +89,7 @@ struct SizeThresholds struct PhysicalHierarchyMaps { std::map id_to_cluster; + std::map inst_to_hard; std::unordered_map inst_to_cluster_id; std::unordered_map bterm_to_cluster_id; @@ -80,6 +104,7 @@ struct PhysicalHierarchy coarsening_ratio(0), max_level(0), bundled_ios_per_edge(0), + large_net_threshold(0), has_io_clusters(true) { } @@ -91,6 +116,7 @@ struct PhysicalHierarchy float coarsening_ratio; int max_level; int bundled_ios_per_edge; + int large_net_threshold; // used to ignore global nets bool has_io_clusters; }; @@ -98,7 +124,9 @@ struct PhysicalHierarchy class ClusteringEngine { public: - ClusteringEngine(odb::dbBlock* block, utl::Logger* logger); + ClusteringEngine(odb::dbBlock* block, + sta::dbNetwork* network, + utl::Logger* logger); void setDesignMetrics(Metrics* design_metrics); void setTargetStructure(PhysicalHierarchy* tree); @@ -111,15 +139,44 @@ class ClusteringEngine void createIOClusters(); void mapIOPads(); + // Methods for data flow + void createDataFlow(); + void dataFlowDFSIOPin(int parent, + int idx, + std::vector>& insts, + std::map& io_pin_vertex, + std::map& std_cell_vertex, + std::map& macro_pin_vertex, + std::vector& stop_flag_vec, + std::vector& visited, + std::vector>& vertices, + std::vector>& hyperedges, + bool backward_search); + void dataFlowDFSMacroPin(int parent, + int idx, + std::vector>& std_cells, + std::vector>& macros, + std::map& io_pin_vertex, + std::map& std_cell_vertex, + std::map& macro_pin_vertex, + std::vector& stop_flag_vec, + std::vector& visited, + std::vector>& vertices, + std::vector>& hyperedges, + bool backward_search); + + static bool isIgnoredMaster(odb::dbMaster* master); + odb::dbBlock* block_; + sta::dbNetwork* network_; utl::Logger* logger_; - Metrics* design_metrics_; + Metrics* design_metrics_; PhysicalHierarchy* tree_; - // Criado na engine int id_; SizeThresholds level_thresholds_; + DataFlow data_flow; }; } // namespace mpl2 \ No newline at end of file diff --git a/src/mpl2/src/hier_rtlmp.cpp b/src/mpl2/src/hier_rtlmp.cpp index e00f2129757..be475a292ae 100644 --- a/src/mpl2/src/hier_rtlmp.cpp +++ b/src/mpl2/src/hier_rtlmp.cpp @@ -71,8 +71,8 @@ HierRTLMP::HierRTLMP(sta::dbNetwork* network, db_(db), sta_(sta), logger_(logger), - tritonpart_(tritonpart) /*, - clustering_engine_(nullptr) */ + tritonpart_(tritonpart), + clustering_engine_(nullptr) { } @@ -187,6 +187,8 @@ void HierRTLMP::setClusterSizeRatioPerLevel(float coarsening_ratio) void HierRTLMP::setLargeNetThreshold(int large_net_threshold) { large_net_threshold_ = large_net_threshold; + + tree_.large_net_threshold = large_net_threshold; } void HierRTLMP::setSignatureNetThreshold(int signature_net_threshold) @@ -464,13 +466,12 @@ void HierRTLMP::initPhysicalHierarchy() // Transform the logical hierarchy into a physical hierarchy. void HierRTLMP::runMultilevelAutoclustering() { - /* - clustering_engine_ = std::make_unique(block_, logger_); + clustering_engine_ + = std::make_unique(block_, network_, logger_); clustering_engine_->setDesignMetrics(metrics_); clustering_engine_->setTargetStructure(&tree_); clustering_engine_->buildPhysicalHierarchy(); - */ initPhysicalHierarchy(); diff --git a/src/mpl2/src/hier_rtlmp.h b/src/mpl2/src/hier_rtlmp.h index 2ad6e74153c..f31d3e60b32 100644 --- a/src/mpl2/src/hier_rtlmp.h +++ b/src/mpl2/src/hier_rtlmp.h @@ -335,8 +335,7 @@ class HierRTLMP sta::dbSta* sta_ = nullptr; utl::Logger* logger_ = nullptr; par::PartitionMgr* tritonpart_ = nullptr; - - /*std::unique_ptr clustering_engine_;*/ + std::unique_ptr clustering_engine_; // flag variables const bool dynamic_congestion_weight_flag_ = false; From 4cf64c252b86c7c188a6c41cdc9838247f606f22 Mon Sep 17 00:00:00 2001 From: Arthur Koucher Date: Thu, 4 Jul 2024 17:47:22 -0300 Subject: [PATCH 05/30] mpl2: 1) move clustering methods to new engine 2) remove duplicated variables 3) make needed methods public Signed-off-by: Arthur Koucher --- src/mpl2/src/clusterEngine.cpp | 1365 ++++++++++++++++- src/mpl2/src/clusterEngine.h | 136 +- src/mpl2/src/hier_rtlmp.cpp | 2513 ++++---------------------------- src/mpl2/src/hier_rtlmp.h | 172 +-- 4 files changed, 1714 insertions(+), 2472 deletions(-) diff --git a/src/mpl2/src/clusterEngine.cpp b/src/mpl2/src/clusterEngine.cpp index 854a05c26ab..8bc04367373 100644 --- a/src/mpl2/src/clusterEngine.cpp +++ b/src/mpl2/src/clusterEngine.cpp @@ -33,27 +33,57 @@ #include "clusterEngine.h" +#include + #include "db_sta/dbNetwork.hh" #include "object.h" #include "sta/Liberty.hh" +#include "par/PartitionMgr.h" namespace mpl2 { using utl::MPL; ClusteringEngine::ClusteringEngine(odb::dbBlock* block, sta::dbNetwork* network, - utl::Logger* logger) - : block_(block), network_(network), logger_(logger), id_(0) + utl::Logger* logger, + par::PartitionMgr* triton_part) + : block_(block), + network_(network), + logger_(logger), + triton_part_(triton_part), + level_(0), + id_(0) { } -void ClusteringEngine::buildPhysicalHierarchy() +void ClusteringEngine::run() { initTree(); - setDefaultThresholds(); + setBaseThresholds(); createIOClusters(); createDataFlow(); + + if (design_metrics_->getNumStdCell() == 0) { + logger_->warn(MPL, 25, "Design has no standard cells!"); + treatEachMacroAsSingleCluster(); + } else { + multilevelAutocluster(tree_->root); + + std::vector> mixed_leaves; + fetchMixedLeaves(tree_->root, mixed_leaves); + breakMixedLeaves(mixed_leaves); + } + + if (logger_->debugCheck(MPL, "multilevel_autoclustering", 1)) { + logger_->report("\nPrint Physical Hierarchy\n"); + printPhysicalHierarchyTree(tree_->root, 0); + } + + // Map the macros in each cluster to their HardMacro objects + for (auto& [cluster_id, cluster] : tree_->maps.id_to_cluster) { + mapMacroInCluster2HardMacro(cluster); + } } void ClusteringEngine::setDesignMetrics(Metrics* design_metrics) @@ -61,15 +91,13 @@ void ClusteringEngine::setDesignMetrics(Metrics* design_metrics) design_metrics_ = design_metrics; } -void ClusteringEngine::setTargetStructure(PhysicalHierarchy* tree) +void ClusteringEngine::setTree(PhysicalHierarchy* tree) { tree_ = tree; } void ClusteringEngine::initTree() { - setDefaultThresholds(); - tree_->root = new Cluster(id_, std::string("root"), logger_); tree_->root->addDbModule(block_->getTopModule()); tree_->root->setMetrics(*design_metrics_); @@ -82,32 +110,32 @@ void ClusteringEngine::initTree() } } -void ClusteringEngine::setDefaultThresholds() +void ClusteringEngine::setBaseThresholds() { - if (tree_->base_thresholds.max_macro <= 0 - || tree_->base_thresholds.min_macro <= 0 - || tree_->base_thresholds.max_std_cell <= 0 - || tree_->base_thresholds.min_std_cell <= 0) { + if (tree_->base_max_macro <= 0 + || tree_->base_min_macro <= 0 + || tree_->base_max_std_cell <= 0 + || tree_->base_min_std_cell <= 0) { // Set base values for std cell lower/upper thresholds const int min_num_std_cells_allowed = 1000; - tree_->base_thresholds.min_std_cell + tree_->base_min_std_cell = std::floor(design_metrics_->getNumStdCell() - / std::pow(tree_->coarsening_ratio, tree_->max_level)); - if (tree_->base_thresholds.min_std_cell <= min_num_std_cells_allowed) { - tree_->base_thresholds.min_std_cell = min_num_std_cells_allowed; + / std::pow(tree_->cluster_size_ratio, tree_->max_level)); + if (tree_->base_min_std_cell <= min_num_std_cells_allowed) { + tree_->base_min_std_cell = min_num_std_cells_allowed; } - tree_->base_thresholds.max_std_cell - = tree_->base_thresholds.min_std_cell * tree_->coarsening_ratio / 2.0; + tree_->base_max_std_cell + = tree_->base_min_std_cell * tree_->cluster_size_ratio / 2.0; // Set base values for macros lower/upper thresholds - tree_->base_thresholds.min_macro + tree_->base_min_macro = std::floor(design_metrics_->getNumMacro() - / std::pow(tree_->coarsening_ratio, tree_->max_level)); - if (tree_->base_thresholds.min_macro <= 0) { - tree_->base_thresholds.min_macro = 1; + / std::pow(tree_->cluster_size_ratio, tree_->max_level)); + if (tree_->base_min_macro <= 0) { + tree_->base_min_macro = 1; } - tree_->base_thresholds.max_macro - = tree_->base_thresholds.min_macro * tree_->coarsening_ratio / 2.0; + tree_->base_max_macro + = tree_->base_min_macro * tree_->cluster_size_ratio / 2.0; // From original implementation: Reset maximum level based on number // of macros. @@ -127,11 +155,11 @@ void ClusteringEngine::setDefaultThresholds() // Set sizes for root unsigned coarsening_factor - = std::pow(tree_->coarsening_ratio, tree_->max_level - 1); - tree_->base_thresholds.max_macro *= coarsening_factor; - tree_->base_thresholds.min_macro *= coarsening_factor; - tree_->base_thresholds.max_std_cell *= coarsening_factor; - tree_->base_thresholds.min_std_cell *= coarsening_factor; + = std::pow(tree_->cluster_size_ratio, tree_->max_level - 1); + tree_->base_max_macro *= coarsening_factor; + tree_->base_min_macro *= coarsening_factor; + tree_->base_max_std_cell *= coarsening_factor; + tree_->base_min_std_cell *= coarsening_factor; debugPrint( logger_, @@ -140,10 +168,10 @@ void ClusteringEngine::setDefaultThresholds() 1, "num level: {}, max_macro: {}, min_macro: {}, max_inst:{}, min_inst:{}", tree_->max_level, - tree_->base_thresholds.max_macro, - tree_->base_thresholds.min_macro, - tree_->base_thresholds.max_std_cell, - tree_->base_thresholds.min_std_cell); + tree_->base_max_macro, + tree_->base_min_macro, + tree_->base_max_std_cell, + tree_->base_min_std_cell); } void ClusteringEngine::createIOClusters() @@ -708,4 +736,1275 @@ void ClusteringEngine::dataFlowDFSMacroPin( } } +void ClusteringEngine::updateDataFlow() +{ + // bterm, macros or ffs + for (const auto& [bterm, insts] : data_flow.io_to_regs) { + if (tree_->maps.bterm_to_cluster_id.find(bterm) + == tree_->maps.bterm_to_cluster_id.end()) { + continue; + } + + const int driver_id = tree_->maps.bterm_to_cluster_id.at(bterm); + + for (int i = 0; i < data_flow.register_dist; i++) { + const float weight = data_flow.weight / std::pow(data_flow.factor, i); + std::set sink_clusters; + + for (auto& inst : insts[i]) { + const int cluster_id = tree_->maps.inst_to_cluster_id.at(inst); + sink_clusters.insert(cluster_id); + } + + for (auto& sink : sink_clusters) { + tree_->maps.id_to_cluster[driver_id]->addConnection(sink, weight); + tree_->maps.id_to_cluster[sink]->addConnection(driver_id, weight); + } + } + } + + // macros to ffs + for (const auto& [iterm, insts] : data_flow.macro_pin_to_regs) { + const int driver_id = tree_->maps.inst_to_cluster_id.at(iterm->getInst()); + + for (int i = 0; i < data_flow.register_dist; i++) { + const float weight = data_flow.weight / std::pow(data_flow.factor, i); + std::set sink_clusters; + + for (auto& inst : insts[i]) { + const int cluster_id = tree_->maps.inst_to_cluster_id.at(inst); + sink_clusters.insert(cluster_id); + } + + for (auto& sink : sink_clusters) { + tree_->maps.id_to_cluster[driver_id]->addConnection(sink, weight); + tree_->maps.id_to_cluster[sink]->addConnection(driver_id, weight); + } + } + } + + // macros to macros + for (const auto& [iterm, insts] : data_flow.macro_pin_to_macros) { + const int driver_id = tree_->maps.inst_to_cluster_id.at(iterm->getInst()); + + for (int i = 0; i < data_flow.register_dist; i++) { + const float weight = data_flow.weight / std::pow(data_flow.factor, i); + std::set sink_clusters; + + for (auto& inst : insts[i]) { + const int cluster_id = tree_->maps.inst_to_cluster_id.at(inst); + sink_clusters.insert(cluster_id); + } + + for (auto& sink : sink_clusters) { + tree_->maps.id_to_cluster[driver_id]->addConnection(sink, weight); + } + } + } +} + +void ClusteringEngine::treatEachMacroAsSingleCluster() +{ + auto module = block_->getTopModule(); + for (odb::dbInst* inst : module->getInsts()) { + odb::dbMaster* master = inst->getMaster(); + + if (isIgnoredMaster(master)) { + continue; + } + + if (master->isBlock()) { + std::string cluster_name = inst->getName(); + Cluster* cluster = new Cluster(id_, cluster_name, logger_); + cluster->addLeafMacro(inst); + incorporateNewCluster(cluster, tree_->root); + cluster->setClusterType(HardMacroCluster); + + debugPrint(logger_, + MPL, + "multilevel_autoclustering", + 1, + "model {} as a cluster.", + cluster_name); + } + + if (!tree_->has_io_clusters) { + tree_->has_only_macros = true; + } + } +} + +void ClusteringEngine::incorporateNewCluster(Cluster* cluster, Cluster* parent) +{ + updateInstancesAssociation(cluster); + setClusterMetrics(cluster); + tree_->maps.id_to_cluster[id_++] = cluster; + + // modify physical hierarchy + cluster->setParent(parent); + parent->addChild(cluster); +} + +void ClusteringEngine::updateInstancesAssociation(Cluster* cluster) +{ + int cluster_id = cluster->getId(); + ClusterType cluster_type = cluster->getClusterType(); + if (cluster_type == HardMacroCluster || cluster_type == MixedCluster) { + for (auto& inst : cluster->getLeafMacros()) { + tree_->maps.inst_to_cluster_id[inst] = cluster_id; + } + } + + if (cluster_type == StdCellCluster || cluster_type == MixedCluster) { + for (auto& inst : cluster->getLeafStdCells()) { + tree_->maps.inst_to_cluster_id[inst] = cluster_id; + } + } + + // Note: macro clusters have no module. + if (cluster_type == StdCellCluster) { + for (auto& module : cluster->getDbModules()) { + updateInstancesAssociation(module, cluster_id, false); + } + } else if (cluster_type == MixedCluster) { + for (auto& module : cluster->getDbModules()) { + updateInstancesAssociation(module, cluster_id, true); + } + } +} + +// Unlike macros, std cells are always considered when when updating +// the inst -> cluster map with the data from a module. +void ClusteringEngine::updateInstancesAssociation( + odb::dbModule* module, + int cluster_id, + bool include_macro) +{ + if (include_macro) { + for (odb::dbInst* inst : module->getInsts()) { + tree_->maps.inst_to_cluster_id[inst] = cluster_id; + } + } else { // only consider standard cells + for (odb::dbInst* inst : module->getInsts()) { + odb::dbMaster* master = inst->getMaster(); + if (isIgnoredMaster(master) || master->isBlock()) { + continue; + } + + tree_->maps.inst_to_cluster_id[inst] = cluster_id; + } + } + for (odb::dbModInst* inst : module->getChildren()) { + updateInstancesAssociation(inst->getMaster(), cluster_id, include_macro); + } +} + +void ClusteringEngine::setClusterMetrics(Cluster* cluster) +{ + float std_cell_area = 0.0f; + for (odb::dbInst* std_cell : cluster->getLeafStdCells()) { + std_cell_area += computeMicronArea(std_cell); + } + + float macro_area = 0.0f; + for (odb::dbInst* macro : cluster->getLeafMacros()) { + macro_area += computeMicronArea(macro); + } + + const unsigned int num_std_cell = cluster->getLeafStdCells().size(); + const unsigned int num_macro = cluster->getLeafMacros().size(); + + Metrics metrics(num_std_cell, num_macro, std_cell_area, macro_area); + + for (auto& module : cluster->getDbModules()) { + metrics.addMetrics(*tree_->maps.module_to_metrics[module]); + } + + debugPrint(logger_, + MPL, + "multilevel_autoclustering", + 1, + "Setting Cluster Metrics for {}: Num Macros: {} Num Std Cells: {}", + cluster->getName(), + metrics.getNumMacro(), + metrics.getNumStdCell()); + + if (cluster->getClusterType() == HardMacroCluster) { + cluster->setMetrics( + Metrics(0, metrics.getNumMacro(), 0.0, metrics.getMacroArea())); + } else if (cluster->getClusterType() == StdCellCluster) { + cluster->setMetrics( + Metrics(metrics.getNumStdCell(), 0, metrics.getStdCellArea(), 0.0)); + } else { + cluster->setMetrics(metrics); + } +} + +float ClusteringEngine::computeMicronArea(odb::dbInst* inst) +{ + const float width = static_cast( + block_->dbuToMicrons(inst->getBBox()->getBox().dx())); + const float height = static_cast( + block_->dbuToMicrons(inst->getBBox()->getBox().dy())); + + return width * height; +} + +// Post-order DFS for clustering +void ClusteringEngine::multilevelAutocluster(Cluster* parent) +{ + bool force_split_root = false; + if (level_ == 0) { + const int leaf_max_std_cell + = tree_->base_max_std_cell / std::pow(tree_->cluster_size_ratio, tree_->max_level - 1) + * (1 + size_tolerance_); + if (parent->getNumStdCell() < leaf_max_std_cell) { + force_split_root = true; + debugPrint(logger_, + MPL, + "multilevel_autoclustering", + 1, + "Root number of std cells ({}) is below leaf cluster max " + "({}). Root will be force split.", + parent->getNumStdCell(), + leaf_max_std_cell); + } + } + + if (level_ >= tree_->max_level) { + return; + } + debugPrint(logger_, + MPL, + "multilevel_autoclustering", + 1, + "Current cluster: {} - Level: {} - Macros: {} - Std Cells: {}", + parent->getName(), + level_, + parent->getNumMacro(), + parent->getNumStdCell()); + + level_++; + updateSizeThresholds(); + + if (force_split_root + || (parent->getNumStdCell() > max_std_cell_)) { + breakCluster(parent); + updateSubTree(parent); + + for (auto& child : parent->getChildren()) { + updateInstancesAssociation(child); + } + + for (auto& child : parent->getChildren()) { + debugPrint(logger_, + MPL, + "multilevel_autoclustering", + 1, + "\tChild Cluster: {}", + child->getName()); + multilevelAutocluster(child); + } + } else { + multilevelAutocluster(parent); + } + + updateInstancesAssociation(parent); + level_--; +} + +void ClusteringEngine::updateSizeThresholds() +{ + const double coarse_factor + = std::pow(tree_->cluster_size_ratio, level_ - 1); + + // A high cluster size ratio per level helps the + // clustering process converge fast + max_macro_ = tree_->base_max_macro / coarse_factor; + min_macro_ = tree_->base_min_macro / coarse_factor; + max_std_cell_ = tree_->base_max_std_cell / coarse_factor; + min_std_cell_ = tree_->base_min_std_cell / coarse_factor; + + // We define the tolerance to improve the robustness of our hierarchical + // clustering + max_macro_ *= (1 + size_tolerance_); + min_macro_ *= (1 - size_tolerance_); + max_std_cell_ *= (1 + size_tolerance_); + min_std_cell_ *= (1 - size_tolerance_); + + if (min_macro_ <= 0) { + min_macro_ = 1; + max_macro_ = min_macro_ * tree_->cluster_size_ratio / 2.0; + } + + if (min_std_cell_ <= 0) { + min_std_cell_ = 100; + max_std_cell_ = min_std_cell_ * tree_->cluster_size_ratio / 2.0; + } +} + +// We expand the parent cluster into a subtree based on logical +// hierarchy in a DFS manner. During the expansion process, +// we merge small clusters in the same logical hierarchy +void ClusteringEngine::breakCluster(Cluster* parent) +{ + debugPrint(logger_, + MPL, + "multilevel_autoclustering", + 1, + "Breaking Cluster: {}", + parent->getName()); + + if (parent->isEmpty()) { + return; + } + + if (parent->correspondsToLogicalModule()) { + odb::dbModule* module = parent->getDbModules().front(); + // Flat module that will be partitioned with TritonPart when updating + // the subtree later on. + if (module->getChildren().size() == 0) { + if (parent == tree_->root) { + createFlatCluster(module, parent); + } else { + addModuleInstsToCluster(parent, module); + parent->clearDbModules(); + updateInstancesAssociation(parent); + } + return; + } + + for (odb::dbModInst* child : module->getChildren()) { + createCluster(child->getMaster(), parent); + } + createFlatCluster(module, parent); + } else { + // Parent is a cluster generated by merging small clusters: + // It may have a few logical modules or many glue insts. + for (auto& module : parent->getDbModules()) { + createCluster(module, parent); + } + + if (!parent->getLeafStdCells().empty() + || !parent->getLeafMacros().empty()) { + createCluster(parent); + } + } + + // Recursively break down non-flat large clusters with logical modules + for (auto& child : parent->getChildren()) { + if (!child->getDbModules().empty()) { + if (child->getNumStdCell() > max_std_cell_ + || child->getNumMacro() > max_macro_) { + breakCluster(child); + } + } + } + + // Merge small clusters + std::vector candidate_clusters; + for (auto& cluster : parent->getChildren()) { + if (!cluster->isIOCluster() && cluster->getNumStdCell() < min_std_cell_ + && cluster->getNumMacro() < min_macro_) { + candidate_clusters.push_back(cluster); + } + } + + mergeClusters(candidate_clusters); + + // Update the cluster_id + // This is important to maintain the clustering results + updateInstancesAssociation(parent); +} + +// This cluster won't be associated with the module. It will only +// contain its macros and std cells as leaves. +void ClusteringEngine::createFlatCluster(odb::dbModule* module, Cluster* parent) +{ + std::string cluster_name + = std::string("(") + parent->getName() + ")_glue_logic"; + Cluster* cluster = new Cluster(id_, cluster_name, logger_); + addModuleInstsToCluster(cluster, module); + + if (cluster->getLeafStdCells().empty() && cluster->getLeafMacros().empty()) { + delete cluster; + cluster = nullptr; + } else { + incorporateNewCluster(cluster, parent); + } +} + +void ClusteringEngine::addModuleInstsToCluster(Cluster* cluster, odb::dbModule* module) +{ + for (odb::dbInst* inst : module->getInsts()) { + odb::dbMaster* master = inst->getMaster(); + if (isIgnoredMaster(master)) { + continue; + } + cluster->addLeafInst(inst); + } +} + +void ClusteringEngine::createCluster(odb::dbModule* module, Cluster* parent) +{ + std::string cluster_name = module->getHierarchicalName(); + Cluster* cluster = new Cluster(id_, cluster_name, logger_); + cluster->addDbModule(module); + incorporateNewCluster(cluster, parent); +} + +void ClusteringEngine::createCluster(Cluster* parent) +{ + std::string cluster_name + = std::string("(") + parent->getName() + ")_glue_logic"; + Cluster* cluster = new Cluster(id_, cluster_name, logger_); + for (auto& inst : parent->getLeafStdCells()) { + cluster->addLeafStdCell(inst); + } + for (auto& inst : parent->getLeafMacros()) { + cluster->addLeafMacro(inst); + } + + incorporateNewCluster(cluster, parent); +} + +// This function has two purposes: +// 1) remove all the internal clusters between parent and leaf clusters in its +// subtree 2) Call TritonPart to partition large flat clusters (a cluster with +// no logical modules) +void ClusteringEngine::updateSubTree(Cluster* parent) +{ + std::vector children_clusters; + std::vector internal_clusters; + std::queue wavefront; + for (auto child : parent->getChildren()) { + wavefront.push(child); + } + + while (!wavefront.empty()) { + Cluster* cluster = wavefront.front(); + wavefront.pop(); + if (cluster->getChildren().empty()) { + children_clusters.push_back(cluster); + } else { + internal_clusters.push_back(cluster); + for (auto child : cluster->getChildren()) { + wavefront.push(child); + } + } + } + + // delete all the internal clusters + for (auto& cluster : internal_clusters) { + tree_->maps.id_to_cluster.erase(cluster->getId()); + delete cluster; + } + + parent->removeChildren(); + parent->addChildren(children_clusters); + for (auto& cluster : children_clusters) { + cluster->setParent(parent); + if (cluster->getNumStdCell() > max_std_cell_) { + breakLargeFlatCluster(cluster); + } + } +} + +// Break large flat clusters with TritonPart +// Binary coding method to differentiate partitions: +// cluster -> cluster_0, cluster_1 +// cluster_0 -> cluster_0_0, cluster_0_1 +// cluster_1 -> cluster_1_0, cluster_1_1 [...] +void ClusteringEngine::breakLargeFlatCluster(Cluster* parent) +{ + // Check if the cluster is a large flat cluster + if (!parent->getDbModules().empty() + || parent->getLeafStdCells().size() < max_std_cell_) { + return; + } + updateInstancesAssociation(parent); + + std::map cluster_vertex_id_map; + std::vector vertex_weight; + int vertex_id = 0; + for (auto& [cluster_id, cluster] : tree_->maps.id_to_cluster) { + cluster_vertex_id_map[cluster_id] = vertex_id++; + vertex_weight.push_back(0.0f); + } + const int num_other_cluster_vertices = vertex_id; + + std::vector insts; + std::map inst_vertex_id_map; + for (auto& macro : parent->getLeafMacros()) { + inst_vertex_id_map[macro] = vertex_id++; + vertex_weight.push_back(computeMicronArea(macro)); + insts.push_back(macro); + } + for (auto& std_cell : parent->getLeafStdCells()) { + inst_vertex_id_map[std_cell] = vertex_id++; + vertex_weight.push_back(computeMicronArea(std_cell)); + insts.push_back(std_cell); + } + + std::vector> hyperedges; + for (odb::dbNet* net : block_->getNets()) { + if (net->getSigType().isSupply()) { + continue; + } + + int driver_id = -1; + std::set loads_id; + bool ignore = false; + for (odb::dbITerm* iterm : net->getITerms()) { + odb::dbInst* inst = iterm->getInst(); + odb::dbMaster* master = inst->getMaster(); + if (isIgnoredMaster(master)) { + ignore = true; + break; + } + + const int cluster_id = tree_->maps.inst_to_cluster_id.at(inst); + int vertex_id = (cluster_id != parent->getId()) + ? cluster_vertex_id_map[cluster_id] + : inst_vertex_id_map[inst]; + if (iterm->getIoType() == odb::dbIoType::OUTPUT) { + driver_id = vertex_id; + } else { + loads_id.insert(vertex_id); + } + } + + if (ignore) { + continue; + } + + for (odb::dbBTerm* bterm : net->getBTerms()) { + const int cluster_id = tree_->maps.bterm_to_cluster_id.at(bterm); + if (bterm->getIoType() == odb::dbIoType::INPUT) { + driver_id = cluster_vertex_id_map[cluster_id]; + } else { + loads_id.insert(cluster_vertex_id_map[cluster_id]); + } + } + loads_id.insert(driver_id); + if (driver_id != -1 && loads_id.size() > 1 + && loads_id.size() < tree_->large_net_threshold) { + std::vector hyperedge; + hyperedge.insert(hyperedge.end(), loads_id.begin(), loads_id.end()); + hyperedges.push_back(hyperedge); + } + } + + const int seed = 0; + const float balance_constraint = 1.0; + const int num_parts = 2; // We use two-way partitioning here + const int num_vertices = static_cast(vertex_weight.size()); + std::vector hyperedge_weights(hyperedges.size(), 1.0f); + + debugPrint(logger_, + MPL, + "multilevel_autoclustering", + 1, + "Breaking flat cluster {} with TritonPart", + parent->getName()); + + std::vector part + = triton_part_->PartitionKWaySimpleMode(num_parts, + balance_constraint, + seed, + hyperedges, + vertex_weight, + hyperedge_weights); + + parent->clearLeafStdCells(); + parent->clearLeafMacros(); + + const std::string cluster_name = parent->getName(); + parent->setName(cluster_name + std::string("_0")); + Cluster* cluster_part_1 + = new Cluster(id_, cluster_name + std::string("_1"), logger_); + + for (int i = num_other_cluster_vertices; i < num_vertices; i++) { + odb::dbInst* inst = insts[i - num_other_cluster_vertices]; + if (part[i] == 0) { + parent->addLeafInst(inst); + } else { + cluster_part_1->addLeafInst(inst); + } + } + + updateInstancesAssociation(parent); + setClusterMetrics(parent); + incorporateNewCluster(cluster_part_1, parent->getParent()); + + // Recursive break the cluster + // until the size of the cluster is less than max_num_inst_ + breakLargeFlatCluster(parent); + breakLargeFlatCluster(cluster_part_1); +} + +// Recursively merge small clusters with the same parent cluster: +// Example process based on connection signature: +// Iter1 : A, B, C, D, E, F +// Iter2 : A + C, B + D, E, F +// Iter3 : A + C + F, B + D, E +// End if there is no same connection signature. +// During the merging process, we support two types of merging +// Type 1: merging small clusters to their closely connected clusters +// For example, if a small cluster A is closely connected to a +// well-formed cluster B, (there are also other well-formed clusters +// C, D), A is only connected to B and A has no connection with C, D +// Type 2: merging small clusters with the same connection signature +// For example, if we merge small clusters A and B, A and B will have +// exactly the same connections relative to all other clusters (both +// small clusters and well-formed clusters). In this case, if A and B +// have the same connection signature, A and C have the same connection +// signature, then B and C also have the same connection signature. +// Note in both types, we only merge clusters with the same parent cluster. +void ClusteringEngine::mergeClusters(std::vector& candidate_clusters) +{ + if (candidate_clusters.empty()) { + return; + } + + int merge_iter = 0; + debugPrint(logger_, + MPL, + "multilevel_autoclustering", + 1, + "Merge Cluster Iter: {}", + merge_iter++); + for (auto& cluster : candidate_clusters) { + debugPrint(logger_, + MPL, + "multilevel_autoclustering", + 1, + "Cluster: {}, num std cell: {}, num macros: {}", + cluster->getName(), + cluster->getNumStdCell(), + cluster->getNumMacro()); + } + + int num_candidate_clusters = candidate_clusters.size(); + while (true) { + updateConnections(); // update the connections between clusters + + std::vector cluster_class(num_candidate_clusters, -1); // merge flag + std::vector candidate_clusters_id; // store cluster id + candidate_clusters_id.reserve(candidate_clusters.size()); + for (auto& cluster : candidate_clusters) { + candidate_clusters_id.push_back(cluster->getId()); + } + // Firstly we perform Type 1 merge + for (int i = 0; i < num_candidate_clusters; i++) { + const int cluster_id = candidate_clusters[i]->getCloseCluster( + candidate_clusters_id, tree_->min_net_count_for_connection); + debugPrint( + logger_, + MPL, + "multilevel_autoclustering", + 1, + "Candidate cluster: {} - {}", + candidate_clusters[i]->getName(), + (cluster_id != -1 ? tree_->maps.id_to_cluster[cluster_id]->getName() : " ")); + if (cluster_id != -1 && !tree_->maps.id_to_cluster[cluster_id]->isIOCluster()) { + Cluster*& cluster = tree_->maps.id_to_cluster[cluster_id]; + bool delete_flag = false; + if (cluster->mergeCluster(*candidate_clusters[i], delete_flag)) { + if (delete_flag) { + tree_->maps.id_to_cluster.erase(candidate_clusters[i]->getId()); + delete candidate_clusters[i]; + } + updateInstancesAssociation(cluster); + setClusterMetrics(cluster); + cluster_class[i] = cluster->getId(); + } + } + } + + // Then we perform Type 2 merge + std::vector new_candidate_clusters; + for (int i = 0; i < num_candidate_clusters; i++) { + if (cluster_class[i] == -1) { // the cluster has not been merged + // new_candidate_clusters.push_back(candidate_clusters[i]); + for (int j = i + 1; j < num_candidate_clusters; j++) { + if (cluster_class[j] != -1) { + continue; + } + bool flag = candidate_clusters[i]->isSameConnSignature( + *candidate_clusters[j], tree_->min_net_count_for_connection); + if (flag) { + cluster_class[j] = i; + bool delete_flag = false; + if (candidate_clusters[i]->mergeCluster(*candidate_clusters[j], + delete_flag)) { + if (delete_flag) { + tree_->maps.id_to_cluster.erase(candidate_clusters[j]->getId()); + delete candidate_clusters[j]; + } + updateInstancesAssociation(candidate_clusters[i]); + setClusterMetrics(candidate_clusters[i]); + } + } + } + } + } + + // Then we perform Type 3 merge: merge all dust cluster + const int dust_cluster_std_cell = 10; + for (int i = 0; i < num_candidate_clusters; i++) { + if (cluster_class[i] == -1) { // the cluster has not been merged + new_candidate_clusters.push_back(candidate_clusters[i]); + if (candidate_clusters[i]->getNumStdCell() <= dust_cluster_std_cell + && candidate_clusters[i]->getNumMacro() == 0) { + for (int j = i + 1; j < num_candidate_clusters; j++) { + if (cluster_class[j] != -1 + || candidate_clusters[j]->getNumMacro() > 0 + || candidate_clusters[j]->getNumStdCell() + > dust_cluster_std_cell) { + continue; + } + cluster_class[j] = i; + bool delete_flag = false; + if (candidate_clusters[i]->mergeCluster(*candidate_clusters[j], + delete_flag)) { + if (delete_flag) { + tree_->maps.id_to_cluster.erase(candidate_clusters[j]->getId()); + delete candidate_clusters[j]; + } + updateInstancesAssociation(candidate_clusters[i]); + setClusterMetrics(candidate_clusters[i]); + } + } + } + } + } + + // Update the candidate clusters + // Some clusters have become well-formed clusters + candidate_clusters.clear(); + for (auto& cluster : new_candidate_clusters) { + if (cluster->getNumStdCell() < min_std_cell_ + && cluster->getNumMacro() < min_macro_) { + candidate_clusters.push_back(cluster); + } + } + + // If no more clusters have been merged, exit the merging loop + if (num_candidate_clusters == new_candidate_clusters.size()) { + break; + } + + num_candidate_clusters = candidate_clusters.size(); + + debugPrint(logger_, + MPL, + "multilevel_autoclustering", + 1, + "Merge Cluster Iter: {}", + merge_iter++); + for (auto& cluster : candidate_clusters) { + debugPrint(logger_, + MPL, + "multilevel_autoclustering", + 1, + "Cluster: {}", + cluster->getName()); + } + // merge small clusters + if (candidate_clusters.empty()) { + break; + } + } + debugPrint(logger_, + MPL, + "multilevel_autoclustering", + 1, + "Finished merging clusters"); +} + +void ClusteringEngine::updateConnections() +{ + for (auto& [cluster_id, cluster] : tree_->maps.id_to_cluster) { + cluster->initConnection(); + } + + for (odb::dbNet* net : block_->getNets()) { + if (net->getSigType().isSupply()) { + continue; + } + + int driver_cluster_id = -1; + std::vector load_clusters_ids; + bool net_has_pad_or_cover = false; + + for (odb::dbITerm* iterm : net->getITerms()) { + odb::dbInst* inst = iterm->getInst(); + odb::dbMaster* master = inst->getMaster(); + + if (isIgnoredMaster(master)) { + net_has_pad_or_cover = true; + break; + } + + const int cluster_id = tree_->maps.inst_to_cluster_id.at(inst); + + if (iterm->getIoType() == odb::dbIoType::OUTPUT) { + driver_cluster_id = cluster_id; + } else { + load_clusters_ids.push_back(cluster_id); + } + } + + if (net_has_pad_or_cover) { + continue; + } + + bool net_has_io_pin = false; + + for (odb::dbBTerm* bterm : net->getBTerms()) { + const int cluster_id = tree_->maps.bterm_to_cluster_id.at(bterm); + net_has_io_pin = true; + + if (bterm->getIoType() == odb::dbIoType::INPUT) { + driver_cluster_id = cluster_id; + } else { + load_clusters_ids.push_back(cluster_id); + } + } + + if (driver_cluster_id != -1 && !load_clusters_ids.empty() + && load_clusters_ids.size() < tree_->large_net_threshold) { + const float weight = net_has_io_pin ? tree_->virtual_weight : 1.0; + + for (const int load_cluster_id : load_clusters_ids) { + if (load_cluster_id != driver_cluster_id) { /* undirected connection */ + tree_->maps.id_to_cluster[driver_cluster_id]->addConnection( + load_cluster_id, weight); + tree_->maps.id_to_cluster[load_cluster_id]->addConnection( + driver_cluster_id, weight); + } + } + } + } +} + +// Traverse the physical hierarchy tree in a DFS manner (post-order) +void ClusteringEngine::fetchMixedLeaves( + Cluster* parent, + std::vector>& mixed_leaves) +{ + if (parent->getChildren().empty() || parent->getNumMacro() == 0) { + return; + } + + std::vector sister_mixed_leaves; + + for (auto& child : parent->getChildren()) { + updateInstancesAssociation(child); + if (child->getNumMacro() > 0) { + if (child->getChildren().empty()) { + sister_mixed_leaves.push_back(child); + } else { + fetchMixedLeaves(child, mixed_leaves); + } + } else { + child->setClusterType(StdCellCluster); + } + } + + // We push the leaves after finishing searching the children so + // that each vector of clusters represents the children of one + // parent. + mixed_leaves.push_back(sister_mixed_leaves); +} + +void ClusteringEngine::breakMixedLeaves( + const std::vector>& mixed_leaves) +{ + for (const std::vector& sister_mixed_leaves : mixed_leaves) { + if (!sister_mixed_leaves.empty()) { + Cluster* parent = sister_mixed_leaves.front()->getParent(); + + for (Cluster* mixed_leaf : sister_mixed_leaves) { + breakMixedLeaf(mixed_leaf); + } + + updateInstancesAssociation(parent); + } + } +} + +// Break mixed leaf into standard-cell and hard-macro clusters. +// Merge macros based on connection signature and footprint. +// Based on types of designs, we support two types of breaking up: +// 1) Replace cluster A by A1, A2, A3 +// 2) Create a subtree: +// A -> A +// | | | +// A1 A2 A3 +void ClusteringEngine::breakMixedLeaf(Cluster* mixed_leaf) +{ + Cluster* parent = mixed_leaf; + const float macro_dominated_cluster_ratio = 0.1; + + // Split by replacement if macro dominated. + if (mixed_leaf->getNumStdCell() * macro_dominated_cluster_ratio + < mixed_leaf->getNumMacro()) { + parent = mixed_leaf->getParent(); + } + + mapMacroInCluster2HardMacro(mixed_leaf); + + std::vector hard_macros = mixed_leaf->getHardMacros(); + std::vector macro_clusters; + + createOneClusterForEachMacro(parent, hard_macros, macro_clusters); + + std::vector size_class(hard_macros.size(), -1); + classifyMacrosBySize(hard_macros, size_class); + + updateConnections(); + + std::vector signature_class(hard_macros.size(), -1); + classifyMacrosByConnSignature(macro_clusters, signature_class); + + std::vector interconn_class(hard_macros.size(), -1); + classifyMacrosByInterconn(macro_clusters, interconn_class); + + std::vector macro_class(hard_macros.size(), -1); + groupSingleMacroClusters(macro_clusters, + size_class, + signature_class, + interconn_class, + macro_class); + + mixed_leaf->clearHardMacros(); + + // IMPORTANT: Restore the structure of physical hierarchical tree. Thus the + // order of leaf clusters will not change the final macro grouping results. + updateInstancesAssociation(mixed_leaf); + + // Never use SetInstProperty in the following lines for the reason above! + std::vector virtual_conn_clusters; + + // Deal with the std cells + if (parent == mixed_leaf) { + addStdCellClusterToSubTree(parent, mixed_leaf, virtual_conn_clusters); + } else { + replaceByStdCellCluster(mixed_leaf, virtual_conn_clusters); + } + + // Deal with the macros + for (int i = 0; i < macro_class.size(); i++) { + if (macro_class[i] != i) { + continue; // this macro cluster has been merged + } + + macro_clusters[i]->setClusterType(HardMacroCluster); + + if (interconn_class[i] != -1) { + macro_clusters[i]->setAsArrayOfInterconnectedMacros(); + } + + setClusterMetrics(macro_clusters[i]); + virtual_conn_clusters.push_back(mixed_leaf->getId()); + } + + // add virtual connections + for (int i = 0; i < virtual_conn_clusters.size(); i++) { + for (int j = i + 1; j < virtual_conn_clusters.size(); j++) { + parent->addVirtualConnection(virtual_conn_clusters[i], + virtual_conn_clusters[j]); + } + } +} + +// Map all the macros into their HardMacro objects for all the clusters +void ClusteringEngine::mapMacroInCluster2HardMacro(Cluster* cluster) +{ + if (cluster->getClusterType() == StdCellCluster) { + return; + } + + std::vector hard_macros; + for (const auto& inst : cluster->getLeafMacros()) { + hard_macros.push_back(tree_->maps.inst_to_hard[inst]); + } + for (const auto& module : cluster->getDbModules()) { + getHardMacros(module, hard_macros); + } + cluster->specifyHardMacros(hard_macros); +} + +// Get all the hard macros in a logical module +void ClusteringEngine::getHardMacros(odb::dbModule* module, + std::vector& hard_macros) +{ + for (odb::dbInst* inst : module->getInsts()) { + odb::dbMaster* master = inst->getMaster(); + + if (isIgnoredMaster(master)) { + continue; + } + + if (master->isBlock()) { + hard_macros.push_back(tree_->maps.inst_to_hard[inst]); + } + } + + for (odb::dbModInst* inst : module->getChildren()) { + getHardMacros(inst->getMaster(), hard_macros); + } +} + +void ClusteringEngine::createOneClusterForEachMacro( + Cluster* parent, + const std::vector& hard_macros, + std::vector& macro_clusters) +{ + for (auto& hard_macro : hard_macros) { + std::string cluster_name = hard_macro->getName(); + Cluster* single_macro_cluster + = new Cluster(id_, cluster_name, logger_); + single_macro_cluster->addLeafMacro(hard_macro->getInst()); + incorporateNewCluster(single_macro_cluster, parent); + + macro_clusters.push_back(single_macro_cluster); + } +} + +void ClusteringEngine::classifyMacrosBySize(const std::vector& hard_macros, + std::vector& size_class) +{ + for (int i = 0; i < hard_macros.size(); i++) { + if (size_class[i] == -1) { + for (int j = i + 1; j < hard_macros.size(); j++) { + if ((size_class[j] == -1) && ((*hard_macros[i]) == (*hard_macros[j]))) { + size_class[j] = i; + } + } + } + } + + for (int i = 0; i < hard_macros.size(); i++) { + size_class[i] = (size_class[i] == -1) ? i : size_class[i]; + } +} + +void ClusteringEngine::classifyMacrosByConnSignature( + const std::vector& macro_clusters, + std::vector& signature_class) +{ + for (int i = 0; i < macro_clusters.size(); i++) { + if (signature_class[i] == -1) { + signature_class[i] = i; + for (int j = i + 1; j < macro_clusters.size(); j++) { + if (signature_class[j] != -1) { + continue; + } + + if (macro_clusters[i]->isSameConnSignature(*macro_clusters[j], + tree_->min_net_count_for_connection)) { + signature_class[j] = i; + } + } + } + } + + if (logger_->debugCheck(MPL, "multilevel_autoclustering", 2)) { + logger_->report("\nPrint Connection Signature\n"); + for (auto& cluster : macro_clusters) { + logger_->report("Macro Signature: {}", cluster->getName()); + for (auto& [cluster_id, weight] : cluster->getConnection()) { + logger_->report(" {} {} ", tree_->maps.id_to_cluster[cluster_id]->getName(), weight); + } + } + } +} + +void ClusteringEngine::classifyMacrosByInterconn( + const std::vector& macro_clusters, + std::vector& interconn_class) +{ + for (int i = 0; i < macro_clusters.size(); i++) { + if (interconn_class[i] == -1) { + interconn_class[i] = i; + for (int j = 0; j < macro_clusters.size(); j++) { + if (macro_clusters[i]->hasMacroConnectionWith( + *macro_clusters[j], tree_->min_net_count_for_connection)) { + if (interconn_class[j] != -1) { + interconn_class[i] = interconn_class[j]; + break; + } + + interconn_class[j] = i; + } + } + } + } +} + +// We determine if the macros belong to the same class based on: +// 1. Size && and Interconnection (Directly connected macro clusters +// should be grouped) +// 2. Size && Connection Signature (Macros with same connection +// signature should be grouped) +void ClusteringEngine::groupSingleMacroClusters( + const std::vector& macro_clusters, + const std::vector& size_class, + const std::vector& signature_class, + std::vector& interconn_class, + std::vector& macro_class) +{ + for (int i = 0; i < macro_clusters.size(); i++) { + if (macro_class[i] != -1) { + continue; + } + macro_class[i] = i; + + for (int j = i + 1; j < macro_clusters.size(); j++) { + if (macro_class[j] != -1) { + continue; + } + + if (size_class[i] == size_class[j]) { + if (interconn_class[i] == interconn_class[j]) { + macro_class[j] = i; + + debugPrint(logger_, + MPL, + "multilevel_autoclustering", + 1, + "Merging interconnected macro clusters {} and {}", + macro_clusters[j]->getName(), + macro_clusters[i]->getName()); + + mergeMacroClustersWithinSameClass(macro_clusters[i], + macro_clusters[j]); + } else { + // We need this so we can distinguish arrays of interconnected macros + // from grouped macro clusters with same signature. + interconn_class[i] = -1; + + if (signature_class[i] == signature_class[j]) { + macro_class[j] = i; + + debugPrint(logger_, + MPL, + "multilevel_autoclustering", + 1, + "Merging same signature clusters {} and {}.", + macro_clusters[j]->getName(), + macro_clusters[i]->getName()); + + mergeMacroClustersWithinSameClass(macro_clusters[i], + macro_clusters[j]); + } + } + } + } + } +} + +void ClusteringEngine::mergeMacroClustersWithinSameClass(Cluster* target, + Cluster* source) +{ + bool delete_merged = false; + target->mergeCluster(*source, delete_merged); + + if (delete_merged) { + tree_->maps.id_to_cluster.erase(source->getId()); + delete source; + source = nullptr; + } +} + +void ClusteringEngine::addStdCellClusterToSubTree( + Cluster* parent, + Cluster* mixed_leaf, + std::vector& virtual_conn_clusters) +{ + std::string std_cell_cluster_name = mixed_leaf->getName(); + Cluster* std_cell_cluster + = new Cluster(id_, std_cell_cluster_name, logger_); + + std_cell_cluster->copyInstances(*mixed_leaf); + std_cell_cluster->clearLeafMacros(); + std_cell_cluster->setClusterType(StdCellCluster); + + setClusterMetrics(std_cell_cluster); + + tree_->maps.id_to_cluster[id_++] = std_cell_cluster; + + // modify the physical hierachy tree + std_cell_cluster->setParent(parent); + parent->addChild(std_cell_cluster); + virtual_conn_clusters.push_back(std_cell_cluster->getId()); +} + +// We don't modify the physical hierarchy when spliting by replacement +void ClusteringEngine::replaceByStdCellCluster(Cluster* mixed_leaf, + std::vector& virtual_conn_clusters) +{ + mixed_leaf->clearLeafMacros(); + mixed_leaf->setClusterType(StdCellCluster); + + setClusterMetrics(mixed_leaf); + + virtual_conn_clusters.push_back(mixed_leaf->getId()); +} + +// Print Physical Hierarchy tree in a DFS manner +void ClusteringEngine::printPhysicalHierarchyTree(Cluster* parent, int level) +{ + std::string line; + for (int i = 0; i < level; i++) { + line += "+---"; + } + line += fmt::format( + "{} ({}) num_macro : {} num_std_cell : {}" + " macro_area : {} std_cell_area : {} cluster type: {} {}", + parent->getName(), + parent->getId(), + parent->getNumMacro(), + parent->getNumStdCell(), + parent->getMacroArea(), + parent->getStdCellArea(), + parent->getIsLeafString(), + parent->getClusterTypeString()); + logger_->report("{}", line); + + for (auto& cluster : parent->getChildren()) { + printPhysicalHierarchyTree(cluster, level + 1); + } +} + +void ClusteringEngine::createClusterForEachMacro( + const std::vector& hard_macros, + std::vector& sa_macros, + std::vector& macro_clusters, + std::map& cluster_to_macro, + std::set& masters) +{ + int macro_id = 0; + std::string cluster_name; + + for (auto& hard_macro : hard_macros) { + macro_id = sa_macros.size(); + cluster_name = hard_macro->getName(); + + Cluster* macro_cluster = new Cluster(id_, cluster_name, logger_); + macro_cluster->addLeafMacro(hard_macro->getInst()); + updateInstancesAssociation(macro_cluster); + + sa_macros.push_back(*hard_macro); + macro_clusters.push_back(macro_cluster); + cluster_to_macro[id_] = macro_id; + masters.insert(hard_macro->getInst()->getMaster()); + + tree_->maps.id_to_cluster[id_++] = macro_cluster; + } +} + } // namespace mpl2 \ No newline at end of file diff --git a/src/mpl2/src/clusterEngine.h b/src/mpl2/src/clusterEngine.h index 5390c73927f..74e1c4277fa 100644 --- a/src/mpl2/src/clusterEngine.h +++ b/src/mpl2/src/clusterEngine.h @@ -38,6 +38,10 @@ #include #include +namespace par { +class PartitionMgr; +} + namespace utl { class Logger; } @@ -52,6 +56,7 @@ class dbInst; class dbBTerm; class dbMaster; class dbITerm; +class dbModule; } // namespace odb namespace mpl2 { @@ -73,26 +78,15 @@ struct DataFlow macro_pin_to_macros; }; -struct SizeThresholds -{ - SizeThresholds() - : max_macro(0), min_macro(0), max_std_cell(0), min_std_cell(0) - { - } - - int max_macro; - int min_macro; - int max_std_cell; - int min_std_cell; -}; - struct PhysicalHierarchyMaps { std::map id_to_cluster; - std::map inst_to_hard; std::unordered_map inst_to_cluster_id; std::unordered_map bterm_to_cluster_id; + std::map inst_to_hard; + std::map module_to_metrics; + // Only for designs with IO Pads std::map bterm_to_inst; }; @@ -101,24 +95,42 @@ struct PhysicalHierarchy { PhysicalHierarchy() : root(nullptr), - coarsening_ratio(0), + base_max_macro(0), + base_min_macro(0), + base_max_std_cell(0), + base_min_std_cell(0), + cluster_size_ratio(0.0f), + cluster_size_tolerance(0.0f), + virtual_weight(0.0f), max_level(0), bundled_ios_per_edge(0), large_net_threshold(0), - has_io_clusters(true) + min_net_count_for_connection(0), + has_io_clusters(true), + has_only_macros(false), + has_std_cells(true) { } Cluster* root; PhysicalHierarchyMaps maps; - SizeThresholds base_thresholds; - float coarsening_ratio; + int base_max_macro; + int base_min_macro; + int base_max_std_cell; + int base_min_std_cell; + + float cluster_size_ratio; + float cluster_size_tolerance; + float virtual_weight; // between std cell part and macro part int max_level; int bundled_ios_per_edge; int large_net_threshold; // used to ignore global nets + int min_net_count_for_connection; bool has_io_clusters; + bool has_only_macros; + bool has_std_cells; }; class ClusteringEngine @@ -126,18 +138,82 @@ class ClusteringEngine public: ClusteringEngine(odb::dbBlock* block, sta::dbNetwork* network, - utl::Logger* logger); + utl::Logger* logger, + par::PartitionMgr* triton_part); + + void run(); void setDesignMetrics(Metrics* design_metrics); - void setTargetStructure(PhysicalHierarchy* tree); + void setTree(PhysicalHierarchy* tree); + + // Methods to update the tree as the hierarchical + // macro placement runs. + void updateConnections(); + void updateDataFlow(); + void updateInstancesAssociation(Cluster* cluster); + void updateInstancesAssociation(odb::dbModule* module, + int cluster_id, + bool include_macro); + + // Needed for macro placement + void createClusterForEachMacro(const std::vector& hard_macros, + std::vector& sa_macros, + std::vector& macro_clusters, + std::map& cluster_to_macro, + std::set& masters); + + // Keep this auxiliary method here temporarily until + // we remove the micron code. + float computeMicronArea(odb::dbInst* inst); - void buildPhysicalHierarchy(); + static bool isIgnoredMaster(odb::dbMaster* master); private: void initTree(); - void setDefaultThresholds(); + void setBaseThresholds(); void createIOClusters(); void mapIOPads(); + void treatEachMacroAsSingleCluster(); + void incorporateNewCluster(Cluster* cluster, Cluster* parent); + void setClusterMetrics(Cluster* cluster); + void multilevelAutocluster(Cluster* parent); + void updateSizeThresholds(); + void breakCluster(Cluster* parent); + void createFlatCluster(odb::dbModule* module, Cluster* parent); + void addModuleInstsToCluster(Cluster* cluster, odb::dbModule* module); + void createCluster(odb::dbModule* module, Cluster* parent); + void createCluster(Cluster* parent); + void updateSubTree(Cluster* parent); + void breakLargeFlatCluster(Cluster* parent); + void mergeClusters(std::vector& candidate_clusters); + void fetchMixedLeaves(Cluster* parent, + std::vector>& mixed_leaves); + void breakMixedLeaves(const std::vector>& mixed_leaves); + void breakMixedLeaf(Cluster* mixed_leaf); + void mapMacroInCluster2HardMacro(Cluster* cluster); + void getHardMacros(odb::dbModule* module, + std::vector& hard_macros); + void createOneClusterForEachMacro(Cluster* parent, + const std::vector& hard_macros, + std::vector& macro_clusters); + void classifyMacrosBySize(const std::vector& hard_macros, + std::vector& size_class); + void classifyMacrosByConnSignature( + const std::vector& macro_clusters, + std::vector& signature_class); + void classifyMacrosByInterconn(const std::vector& macro_clusters, + std::vector& interconn_class); + void groupSingleMacroClusters(const std::vector& macro_clusters, + const std::vector& size_class, + const std::vector& signature_class, + std::vector& interconn_class, + std::vector& macro_class); + void mergeMacroClustersWithinSameClass(Cluster* target, Cluster* source); + void addStdCellClusterToSubTree(Cluster* parent, + Cluster* mixed_leaf, + std::vector& virtual_conn_clusters); + void replaceByStdCellCluster(Cluster* mixed_leaf, + std::vector& virtual_conn_clusters); // Methods for data flow void createDataFlow(); @@ -165,18 +241,28 @@ class ClusteringEngine std::vector>& hyperedges, bool backward_search); - static bool isIgnoredMaster(odb::dbMaster* master); + void printPhysicalHierarchyTree(Cluster* parent, int level); odb::dbBlock* block_; sta::dbNetwork* network_; utl::Logger* logger_; + par::PartitionMgr* triton_part_; Metrics* design_metrics_; PhysicalHierarchy* tree_; - int id_; - SizeThresholds level_thresholds_; + int level_; // Current level + int id_; // Current "highest" id + + // Size limits of the current level + int max_macro_; + int min_macro_; + int max_std_cell_; + int min_std_cell_; + DataFlow data_flow; + + const float size_tolerance_ = 0.1; }; } // namespace mpl2 \ No newline at end of file diff --git a/src/mpl2/src/hier_rtlmp.cpp b/src/mpl2/src/hier_rtlmp.cpp index be475a292ae..7da7004991f 100644 --- a/src/mpl2/src/hier_rtlmp.cpp +++ b/src/mpl2/src/hier_rtlmp.cpp @@ -144,8 +144,6 @@ void HierRTLMP::setHaloHeight(float halo_height) // Options related to clustering void HierRTLMP::setNumBundledIOsPerBoundary(int num_bundled_ios) { - num_bundled_IOs_ = num_bundled_ios; - tree_.bundled_ios_per_edge = num_bundled_ios; } @@ -154,46 +152,35 @@ void HierRTLMP::setClusterSize(int max_num_macro, int max_num_inst, int min_num_inst) { - max_num_macro_base_ = max_num_macro; - min_num_macro_base_ = min_num_macro; - max_num_inst_base_ = max_num_inst; - min_num_inst_base_ = min_num_inst; - - tree_.base_thresholds.max_macro = max_num_macro; - tree_.base_thresholds.min_macro = min_num_macro; - tree_.base_thresholds.max_std_cell = max_num_inst; - tree_.base_thresholds.min_std_cell = min_num_inst; + tree_.base_max_macro = max_num_macro; + tree_.base_min_macro = min_num_macro; + tree_.base_max_std_cell = max_num_inst; + tree_.base_min_std_cell = min_num_inst; } void HierRTLMP::setClusterSizeTolerance(float tolerance) { - tolerance_ = tolerance; + tree_.cluster_size_tolerance = tolerance; } void HierRTLMP::setMaxNumLevel(int max_num_level) { - max_num_level_ = max_num_level; - tree_.max_level = max_num_level; } void HierRTLMP::setClusterSizeRatioPerLevel(float coarsening_ratio) { - coarsening_ratio_ = coarsening_ratio; - - tree_.coarsening_ratio = coarsening_ratio; + tree_.cluster_size_ratio = coarsening_ratio; } void HierRTLMP::setLargeNetThreshold(int large_net_threshold) { - large_net_threshold_ = large_net_threshold; - tree_.large_net_threshold = large_net_threshold; } void HierRTLMP::setSignatureNetThreshold(int signature_net_threshold) { - signature_net_threshold_ = signature_net_threshold; + tree_.min_net_count_for_connection = signature_net_threshold; } void HierRTLMP::setPinAccessThreshold(float pin_access_th) @@ -227,57 +214,6 @@ void HierRTLMP::setReportDirectory(const char* report_directory) report_directory_ = report_directory; } -// Set defaults for min/max number of instances and macros if not set by user. -void HierRTLMP::setDefaultThresholds() -{ - if (max_num_macro_base_ <= 0 || min_num_macro_base_ <= 0 - || max_num_inst_base_ <= 0 || min_num_inst_base_ <= 0) { - min_num_inst_base_ - = std::floor(metrics_->getNumStdCell() - / std::pow(coarsening_ratio_, max_num_level_)); - if (min_num_inst_base_ <= 1000) { - min_num_inst_base_ = 1000; // lower bound - } - max_num_inst_base_ = min_num_inst_base_ * coarsening_ratio_ / 2.0; - min_num_macro_base_ = std::floor( - metrics_->getNumMacro() / std::pow(coarsening_ratio_, max_num_level_)); - if (min_num_macro_base_ <= 0) { - min_num_macro_base_ = 1; // lowerbound - } - max_num_macro_base_ = min_num_macro_base_ * coarsening_ratio_ / 2.0; - if (metrics_->getNumMacro() <= 150) { - // If the number of macros is less than the threshold value, reset to - // single level. - max_num_level_ = 1; - debugPrint(logger_, - MPL, - "multilevel_autoclustering", - 1, - "Reset number of levels to 1"); - } - } - - // Set sizes for root level based on coarsening_factor and the number of - // physical hierarchy levels - unsigned coarsening_factor = std::pow(coarsening_ratio_, max_num_level_ - 1); - max_num_macro_base_ = max_num_macro_base_ * coarsening_factor; - min_num_macro_base_ = min_num_macro_base_ * coarsening_factor; - max_num_inst_base_ = max_num_inst_base_ * coarsening_factor; - min_num_inst_base_ = min_num_inst_base_ * coarsening_factor; - - debugPrint( - logger_, - MPL, - "multilevel_autoclustering", - 1, - "num level: {}, max_macro: {}, min_macro: {}, max_inst:{}, min_inst:{}", - max_num_level_, - max_num_macro_base_, - min_num_macro_base_, - max_num_inst_base_, - min_num_inst_base_); -} - // Top Level Function // The flow of our MacroPlacer is divided into 6 stages. // 1) Multilevel Autoclustering: @@ -299,19 +235,26 @@ void HierRTLMP::setDefaultThresholds() void HierRTLMP::run() { initMacroPlacer(); - if (!design_has_unfixed_macros_) { logger_->info(MPL, 17, "No unfixed macros. Skipping macro placement."); return; } runMultilevelAutoclustering(); + + if (graphics_) { + graphics_->finishedClustering(tree_.root); + } + + if (!tree_.has_std_cells) { + resetSAParameters(); + } + runCoarseShaping(); if (graphics_) { graphics_->startFine(); } - adjustMacroBlockageWeight(); if (logger_->debugCheck(MPL, "hierarchical_macro_placement", 1)) { reportSAWeights(); @@ -319,2154 +262,259 @@ void HierRTLMP::run() if (bus_planning_on_) { adjustCongestionWeight(); - runHierarchicalMacroPlacement(root_cluster_); - } else { - runHierarchicalMacroPlacementWithoutBusPlanning(root_cluster_); - } - - if (graphics_) { - graphics_->setMaxLevel(max_num_level_); - graphics_->drawResult(); - } - - Pusher pusher(logger_, root_cluster_, block_, boundary_to_io_blockage_); - pusher.pushMacrosToCoreBoundaries(); - - updateMacrosOnDb(); - - generateTemporaryStdCellsPlacement(root_cluster_); - correctAllMacrosOrientation(); - - commitMacroPlacementToDb(); - - writeMacroPlacement(macro_placement_file_); - clear(); -} - -//////////////////////////////////////////////////////////////////////// -// Private functions -//////////////////////////////////////////////////////////////////////// - -void HierRTLMP::initMacroPlacer() -{ - block_ = db_->getChip()->getBlock(); - - odb::Rect die = block_->getDieArea(); - odb::Rect core_box = block_->getCoreArea(); - - float core_lx = block_->dbuToMicrons(core_box.xMin()); - float core_ly = block_->dbuToMicrons(core_box.yMin()); - float core_ux = block_->dbuToMicrons(core_box.xMax()); - float core_uy = block_->dbuToMicrons(core_box.yMax()); - - logger_->report( - "Floorplan Outline: ({}, {}) ({}, {}), Core Outline: ({}, {}) ({}, {})", - block_->dbuToMicrons(die.xMin()), - block_->dbuToMicrons(die.yMin()), - block_->dbuToMicrons(die.xMax()), - block_->dbuToMicrons(die.yMax()), - core_lx, - core_ly, - core_ux, - core_uy); - - float core_area = (core_ux - core_lx) * (core_uy - core_ly); - - computeMetricsForModules(core_area); -} - -void HierRTLMP::computeMetricsForModules(float core_area) -{ - metrics_ = computeMetrics(block_->getTopModule()); - - float util - = (metrics_->getStdCellArea() + metrics_->getMacroArea()) / core_area; - float core_util - = metrics_->getStdCellArea() / (core_area - metrics_->getMacroArea()); - - // Check if placement is feasible in the core area when considering - // the macro halos - int unfixed_macros = 0; - for (auto inst : block_->getInsts()) { - auto master = inst->getMaster(); - if (master->isBlock()) { - const auto width - = block_->dbuToMicrons(master->getWidth()) + 2 * halo_width_; - const auto height - = block_->dbuToMicrons(master->getHeight()) + 2 * halo_width_; - macro_with_halo_area_ += width * height; - unfixed_macros += !inst->getPlacementStatus().isFixed(); - } - } - reportLogicalHierarchyInformation(core_area, util, core_util); - - if (unfixed_macros == 0) { - design_has_unfixed_macros_ = false; - return; - } - - if (macro_with_halo_area_ + metrics_->getStdCellArea() > core_area) { - logger_->error(MPL, - 16, - "The instance area with halos {} exceeds the core area {}", - macro_with_halo_area_ + metrics_->getStdCellArea(), - core_area); - } -} - -void HierRTLMP::reportLogicalHierarchyInformation(float core_area, - float util, - float core_util) -{ - logger_->report( - "\tNumber of std cell instances: {}\n" - "\tArea of std cell instances: {:.2f}\n" - "\tNumber of macros: {}\n" - "\tArea of macros: {:.2f}\n" - "\tHalo width: {:.2f}\n" - "\tHalo height: {:.2f}\n" - "\tArea of macros with halos: {:.2f}\n" - "\tArea of std cell instances + Area of macros: {:.2f}\n" - "\tCore area: {:.2f}\n" - "\tDesign Utilization: {:.2f}\n" - "\tCore Utilization: {:.2f}\n" - "\tManufacturing Grid: {}\n", - metrics_->getNumStdCell(), - metrics_->getStdCellArea(), - metrics_->getNumMacro(), - metrics_->getMacroArea(), - halo_width_, - halo_height_, - macro_with_halo_area_, - metrics_->getStdCellArea() + metrics_->getMacroArea(), - core_area, - util, - core_util, - block_->getTech()->getManufacturingGrid()); -} - -void HierRTLMP::initPhysicalHierarchy() -{ - setDefaultThresholds(); - - cluster_id_ = 0; - - root_cluster_ = new Cluster(cluster_id_, std::string("root"), logger_); - root_cluster_->addDbModule(block_->getTopModule()); - root_cluster_->setMetrics(*metrics_); - - cluster_map_[cluster_id_++] = root_cluster_; - - // Associate all instances to root - for (odb::dbInst* inst : block_->getInsts()) { - inst_to_cluster_[inst] = cluster_id_; - } -} - -// Transform the logical hierarchy into a physical hierarchy. -void HierRTLMP::runMultilevelAutoclustering() -{ - clustering_engine_ - = std::make_unique(block_, network_, logger_); - clustering_engine_->setDesignMetrics(metrics_); - clustering_engine_->setTargetStructure(&tree_); - - clustering_engine_->buildPhysicalHierarchy(); - - initPhysicalHierarchy(); - - createIOClusters(); - createDataFlow(); - - if (metrics_->getNumStdCell() == 0) { - logger_->warn(MPL, 25, "Design has no standard cells!"); - - treatEachMacroAsSingleCluster(); - resetSAParameters(); - + runHierarchicalMacroPlacement(tree_.root); } else { - multilevelAutocluster(root_cluster_); - - std::vector> mixed_leaves; - fetchMixedLeaves(root_cluster_, mixed_leaves); - breakMixedLeaves(mixed_leaves); - - if (graphics_) { - graphics_->finishedClustering(root_cluster_); - } - } - - if (logger_->debugCheck(MPL, "multilevel_autoclustering", 1)) { - logger_->report("\nPrint Physical Hierarchy\n"); - printPhysicalHierarchyTree(root_cluster_, 0); - } - - // Map the macros in each cluster to their HardMacro objects - for (auto& [cluster_id, cluster] : cluster_map_) { - mapMacroInCluster2HardMacro(cluster); - } -} - -/* static */ -bool HierRTLMP::isIgnoredMaster(odb::dbMaster* master) -{ - // IO corners are sometimes marked as end caps - return master->isPad() || master->isCover() || master->isEndCap(); -} - -void HierRTLMP::treatEachMacroAsSingleCluster() -{ - auto module = block_->getTopModule(); - for (odb::dbInst* inst : module->getInsts()) { - odb::dbMaster* master = inst->getMaster(); - - if (isIgnoredMaster(master)) { - continue; - } - - if (master->isBlock()) { - std::string cluster_name = inst->getName(); - Cluster* cluster = new Cluster(cluster_id_, cluster_name, logger_); - cluster->addLeafMacro(inst); - incorporateNewClusterToTree(cluster, root_cluster_); - cluster->setClusterType(HardMacroCluster); - - debugPrint(logger_, - MPL, - "multilevel_autoclustering", - 1, - "model {} as a cluster.", - cluster_name); - } - - if (!design_has_io_clusters_) { - design_has_only_macros_ = true; - } - } -} - -void HierRTLMP::resetSAParameters() -{ - pos_swap_prob_ = 0.2; - neg_swap_prob_ = 0.2; - double_swap_prob_ = 0.2; - exchange_swap_prob_ = 0.2; - flip_prob_ = 0.2; - resize_prob_ = 0.0; - guidance_weight_ = 0.0; - fence_weight_ = 0.0; - boundary_weight_ = 0.0; - notch_weight_ = 0.0; - macro_blockage_weight_ = 0.0; -} - -void HierRTLMP::runCoarseShaping() -{ - setRootShapes(); - - if (design_has_only_macros_) { - logger_->warn(MPL, 27, "Design has only macros!"); - root_cluster_->setClusterType(HardMacroCluster); - return; - } - - if (graphics_) { - graphics_->startCoarse(); - } - - calculateChildrenTilings(root_cluster_); - - setIOClustersBlockages(); - setPlacementBlockages(); -} - -void HierRTLMP::setRootShapes() -{ - SoftMacro* root_soft_macro = new SoftMacro(root_cluster_); - - const float core_lx - = static_cast(block_->dbuToMicrons(block_->getCoreArea().xMin())); - const float root_lx = std::max(core_lx, global_fence_lx_); - - const float core_ly - = static_cast(block_->dbuToMicrons(block_->getCoreArea().yMin())); - const float root_ly = std::max(core_ly, global_fence_ly_); - - const float core_ux - = static_cast(block_->dbuToMicrons(block_->getCoreArea().xMax())); - const float root_ux = std::min(core_ux, global_fence_ux_); - - const float core_uy - = static_cast(block_->dbuToMicrons(block_->getCoreArea().yMax())); - const float root_uy = std::min(core_uy, global_fence_uy_); - - const float root_area = (root_ux - root_lx) * (root_uy - root_ly); - const float root_width = root_ux - root_lx; - const std::vector> root_width_list - = {std::pair(root_width, root_width)}; - - root_soft_macro->setShapes(root_width_list, root_area); - root_soft_macro->setWidth(root_width); // This will set height automatically - root_soft_macro->setX(root_lx); - root_soft_macro->setY(root_ly); - root_cluster_->setSoftMacro(root_soft_macro); -} - -// Traverse Logical Hierarchy -// Recursive function to collect the design metrics (number of std cells, -// area of std cells, number of macros and area of macros) in the logical -// hierarchy -Metrics* HierRTLMP::computeMetrics(odb::dbModule* module) -{ - unsigned int num_std_cell = 0; - float std_cell_area = 0.0; - unsigned int num_macro = 0; - float macro_area = 0.0; - - for (odb::dbInst* inst : module->getInsts()) { - odb::dbMaster* master = inst->getMaster(); - - if (isIgnoredMaster(master)) { - continue; - } - - float inst_area = computeMicronArea(inst); - - if (master->isBlock()) { // a macro - num_macro += 1; - macro_area += inst_area; - - // add hard macro to corresponding map - HardMacro* macro = new HardMacro(inst, halo_width_, halo_height_); - hard_macro_map_[inst] = macro; - } else { - num_std_cell += 1; - std_cell_area += inst_area; - } - } - - // Be careful about the relationship between - // odb::dbModule and odb::dbInst - // odb::dbModule and odb::dbModInst - // recursively traverse the hierarchical module instances - for (odb::dbModInst* inst : module->getChildren()) { - Metrics* metrics = computeMetrics(inst->getMaster()); - num_std_cell += metrics->getNumStdCell(); - std_cell_area += metrics->getStdCellArea(); - num_macro += metrics->getNumMacro(); - macro_area += metrics->getMacroArea(); - } - - Metrics* metrics - = new Metrics(num_std_cell, num_macro, std_cell_area, macro_area); - - logical_module_map_[module] = metrics; - - return metrics; -} - -float HierRTLMP::computeMicronArea(odb::dbInst* inst) -{ - const float width = static_cast( - block_->dbuToMicrons(inst->getBBox()->getBox().dx())); - const float height = static_cast( - block_->dbuToMicrons(inst->getBBox()->getBox().dy())); - - return width * height; -} - -void HierRTLMP::setClusterMetrics(Cluster* cluster) -{ - float std_cell_area = 0.0f; - for (odb::dbInst* std_cell : cluster->getLeafStdCells()) { - std_cell_area += computeMicronArea(std_cell); - } - - float macro_area = 0.0f; - for (odb::dbInst* macro : cluster->getLeafMacros()) { - macro_area += computeMicronArea(macro); - } - - const unsigned int num_std_cell = cluster->getLeafStdCells().size(); - const unsigned int num_macro = cluster->getLeafMacros().size(); - - Metrics metrics(num_std_cell, num_macro, std_cell_area, macro_area); - - for (auto& module : cluster->getDbModules()) { - metrics.addMetrics(*logical_module_map_[module]); - } - - debugPrint(logger_, - MPL, - "multilevel_autoclustering", - 1, - "Setting Cluster Metrics for {}: Num Macros: {} Num Std Cells: {}", - cluster->getName(), - metrics.getNumMacro(), - metrics.getNumStdCell()); - - if (cluster->getClusterType() == HardMacroCluster) { - cluster->setMetrics( - Metrics(0, metrics.getNumMacro(), 0.0, metrics.getMacroArea())); - } else if (cluster->getClusterType() == StdCellCluster) { - cluster->setMetrics( - Metrics(metrics.getNumStdCell(), 0, metrics.getStdCellArea(), 0.0)); - } else { - cluster->setMetrics(metrics); - } -} - -// Handle IOs: Map IOs to Pads for designs with IO pads -void HierRTLMP::mapIOPads() -{ - // Check if this design has IO pads - bool is_pad_design = false; - for (auto inst : block_->getInsts()) { - if (inst->getMaster()->isPad()) { - is_pad_design = true; - break; - } - } - - if (!is_pad_design) { - return; - } - - for (odb::dbNet* net : block_->getNets()) { - if (net->getBTerms().size() == 0) { - continue; - } - - // - // If the design has IO pads, there is a net - // connecting the IO pin and IO pad instance - // - for (odb::dbBTerm* bterm : net->getBTerms()) { - for (odb::dbITerm* iterm : net->getITerms()) { - odb::dbInst* inst = iterm->getInst(); - io_pad_map_[bterm] = inst; - } - } - } -} - -// Model IO pins as bundled IO clusters under the root node. -// IO clusters are created in the following the order : L, T, R, B. -// We will have num_bundled_IOs_ x 4 clusters for bundled IOs. -// Bundled IOs are only created for pins in the region. -void HierRTLMP::createIOClusters() -{ - mapIOPads(); - - debugPrint(logger_, - MPL, - "multilevel_autoclustering", - 1, - "Creating bundledIO clusters..."); - - const odb::Rect die = block_->getDieArea(); - - // Get the floorplan information and get the range of bundled IO regions - odb::Rect die_box = block_->getCoreArea(); - int core_lx = die_box.xMin(); - int core_ly = die_box.yMin(); - int core_ux = die_box.xMax(); - int core_uy = die_box.yMax(); - const int x_base = (die.xMax() - die.xMin()) / num_bundled_IOs_; - const int y_base = (die.yMax() - die.yMin()) / num_bundled_IOs_; - int cluster_id_base = cluster_id_; - - // Map all the BTerms / Pads to Bundled IOs (cluster) - std::vector prefix_vec; - prefix_vec.emplace_back("L"); - prefix_vec.emplace_back("T"); - prefix_vec.emplace_back("R"); - prefix_vec.emplace_back("B"); - std::map cluster_io_map; - for (int i = 0; i < 4; - i++) { // four boundaries (Left, Top, Right and Bottom in order) - for (int j = 0; j < num_bundled_IOs_; j++) { - std::string cluster_name = prefix_vec[i] + std::to_string(j); - Cluster* cluster = new Cluster(cluster_id_, cluster_name, logger_); - root_cluster_->addChild(cluster); - cluster->setParent(root_cluster_); - cluster_io_map[cluster_id_] = false; - cluster_map_[cluster_id_++] = cluster; - int x = 0.0; - int y = 0.0; - int width = 0; - int height = 0; - if (i == 0) { // Left boundary - x = die.xMin(); - y = die.yMin() + y_base * j; - height = y_base; - } else if (i == 1) { // Top boundary - x = die.xMin() + x_base * j; - y = die.yMax(); - width = x_base; - } else if (i == 2) { // Right boundary - x = die.xMax(); - y = die.yMax() - y_base * (j + 1); - height = y_base; - } else { // Bottom boundary - x = die.xMax() - x_base * (j + 1); - y = die.yMin(); - width = x_base; - } - - // set the cluster to a IO cluster - cluster->setAsIOCluster(std::pair(block_->dbuToMicrons(x), - block_->dbuToMicrons(y)), - block_->dbuToMicrons(width), - block_->dbuToMicrons(height)); - } - } - - // Map all the BTerms to bundled IOs - for (auto term : block_->getBTerms()) { - int lx = std::numeric_limits::max(); - int ly = std::numeric_limits::max(); - int ux = 0; - int uy = 0; - // If the design has IO pads, these block terms - // will not have block pins. - // Otherwise, the design will have IO pins. - for (const auto pin : term->getBPins()) { - for (const auto box : pin->getBoxes()) { - lx = std::min(lx, box->xMin()); - ly = std::min(ly, box->yMin()); - ux = std::max(ux, box->xMax()); - uy = std::max(uy, box->yMax()); - } - } - // remove power pins - if (term->getSigType().isSupply()) { - continue; - } - - // If the term has a connected pad, get the bbox from the pad inst - if (io_pad_map_.find(term) != io_pad_map_.end()) { - lx = io_pad_map_[term]->getBBox()->xMin(); - ly = io_pad_map_[term]->getBBox()->yMin(); - ux = io_pad_map_[term]->getBBox()->xMax(); - uy = io_pad_map_[term]->getBBox()->yMax(); - if (lx <= core_lx) { - lx = die.xMin(); - } - if (ly <= core_ly) { - ly = die.yMin(); - } - if (ux >= core_ux) { - ux = die.xMax(); - } - if (uy >= core_uy) { - uy = die.yMax(); - } - } - // calculate cluster id based on the location of IO Pins / Pads - int cluster_id = -1; - if (lx <= die.xMin()) { - // The IO is on the left boundary - cluster_id = cluster_id_base - + std::floor(((ly + uy) / 2.0 - die.yMin()) / y_base); - } else if (uy >= die.yMax()) { - // The IO is on the top boundary - cluster_id = cluster_id_base + num_bundled_IOs_ - + std::floor(((lx + ux) / 2.0 - die.xMin()) / x_base); - } else if (ux >= die.xMax()) { - // The IO is on the right boundary - cluster_id = cluster_id_base + num_bundled_IOs_ * 2 - + std::floor((die.yMax() - (ly + uy) / 2.0) / y_base); - } else if (ly <= die.yMin()) { - // The IO is on the bottom boundary - cluster_id = cluster_id_base + num_bundled_IOs_ * 3 - + std::floor((die.xMax() - (lx + ux) / 2.0) / x_base); - } - - // Check if the IO pins / Pads exist - if (cluster_id == -1) { - logger_->error( - MPL, - 102, - "Floorplan has not been initialized? Pin location error for {}.", - term->getName()); - } else { - bterm_to_cluster_[term] = cluster_id; - } - - cluster_io_map[cluster_id] = true; - } - - // delete the IO clusters that do not have any pins assigned to them - for (auto& [cluster_id, flag] : cluster_io_map) { - if (!flag) { - debugPrint(logger_, - MPL, - "multilevel_autoclustering", - 1, - "Remove IO Cluster with no pins: {}, id: {}", - cluster_map_[cluster_id]->getName(), - cluster_id); - cluster_map_[cluster_id]->getParent()->removeChild( - cluster_map_[cluster_id]); - delete cluster_map_[cluster_id]; - cluster_map_.erase(cluster_id); - } - } - - // At this point the cluster map has only the root (id = 0) and bundledIOs - if (cluster_map_.size() == 1) { - logger_->warn(MPL, 126, "Design has no IO pins!"); - design_has_io_clusters_ = false; - } -} - -// Create physical hierarchy tree in a post-order DFS manner -// Recursive call for creating the physical hierarchy tree -void HierRTLMP::multilevelAutocluster(Cluster* parent) -{ - bool force_split_root = false; - if (level_ == 0) { - const int leaf_max_std_cell - = max_num_inst_base_ / std::pow(coarsening_ratio_, max_num_level_ - 1) - * (1 + tolerance_); - if (parent->getNumStdCell() < leaf_max_std_cell) { - force_split_root = true; - debugPrint(logger_, - MPL, - "multilevel_autoclustering", - 1, - "Root number of std cells ({}) is below leaf cluster max " - "({}). Root will be force split.", - parent->getNumStdCell(), - leaf_max_std_cell); - } - } - - if (level_ >= max_num_level_) { - return; - } - debugPrint(logger_, - MPL, - "multilevel_autoclustering", - 1, - "Current cluster: {} - Level: {} - Macros: {} - Std Cells: {}", - parent->getName(), - level_, - parent->getNumMacro(), - parent->getNumStdCell()); - - level_++; - updateSizeThresholds(); - - if (force_split_root || (parent->getNumStdCell() > max_num_inst_)) { - breakCluster(parent); - updateSubTree(parent); - - for (auto& child : parent->getChildren()) { - updateInstancesAssociation(child); - } - - for (auto& child : parent->getChildren()) { - debugPrint(logger_, - MPL, - "multilevel_autoclustering", - 1, - "\tChild Cluster: {}", - child->getName()); - multilevelAutocluster(child); - } - } else { - multilevelAutocluster(parent); - } - - updateInstancesAssociation(parent); - level_--; -} - -void HierRTLMP::updateSizeThresholds() -{ - const double coarse_factor = std::pow(coarsening_ratio_, level_ - 1); - - // a large coarsening_ratio_ helps the clustering process converge fast - max_num_macro_ = max_num_macro_base_ / coarse_factor; - min_num_macro_ = min_num_macro_base_ / coarse_factor; - max_num_inst_ = max_num_inst_base_ / coarse_factor; - min_num_inst_ = min_num_inst_base_ / coarse_factor; - - // We define the tolerance to improve the robustness of our hierarchical - // clustering - max_num_inst_ *= (1 + tolerance_); - min_num_inst_ *= (1 - tolerance_); - max_num_macro_ *= (1 + tolerance_); - min_num_macro_ *= (1 - tolerance_); - - if (min_num_macro_ <= 0) { - min_num_macro_ = 1; - max_num_macro_ = min_num_macro_ * coarsening_ratio_ / 2.0; - } - - if (min_num_inst_ <= 0) { - min_num_inst_ = 100; - max_num_inst_ = min_num_inst_ * coarsening_ratio_ / 2.0; - } -} - -void HierRTLMP::updateInstancesAssociation(Cluster* cluster) -{ - int cluster_id = cluster->getId(); - ClusterType cluster_type = cluster->getClusterType(); - if (cluster_type == HardMacroCluster || cluster_type == MixedCluster) { - for (auto& inst : cluster->getLeafMacros()) { - inst_to_cluster_[inst] = cluster_id; - } - } - - if (cluster_type == StdCellCluster || cluster_type == MixedCluster) { - for (auto& inst : cluster->getLeafStdCells()) { - inst_to_cluster_[inst] = cluster_id; - } - } - - // Note: macro clusters have no module. - if (cluster_type == StdCellCluster) { - for (auto& module : cluster->getDbModules()) { - updateInstancesAssociation(module, cluster_id, false); - } - } else if (cluster_type == MixedCluster) { - for (auto& module : cluster->getDbModules()) { - updateInstancesAssociation(module, cluster_id, true); - } - } -} - -// Unlike macros, std cells are always considered when when updating -// the inst -> cluster map with the data from a module. -void HierRTLMP::updateInstancesAssociation(odb::dbModule* module, - int cluster_id, - bool include_macro) -{ - if (include_macro) { - for (odb::dbInst* inst : module->getInsts()) { - inst_to_cluster_[inst] = cluster_id; - } - } else { // only consider standard cells - for (odb::dbInst* inst : module->getInsts()) { - odb::dbMaster* master = inst->getMaster(); - - if (isIgnoredMaster(master) || master->isBlock()) { - continue; - } - - inst_to_cluster_[inst] = cluster_id; - } - } - for (odb::dbModInst* inst : module->getChildren()) { - updateInstancesAssociation(inst->getMaster(), cluster_id, include_macro); - } -} - -// We expand the parent cluster into a subtree based on logical -// hierarchy in a DFS manner. During the expansion process, -// we merge small clusters in the same logical hierarchy -void HierRTLMP::breakCluster(Cluster* parent) -{ - debugPrint(logger_, - MPL, - "multilevel_autoclustering", - 1, - "Breaking Cluster: {}", - parent->getName()); - - if (parent->isEmpty()) { - return; - } - - if (parent->correspondsToLogicalModule()) { - odb::dbModule* module = parent->getDbModules().front(); - // Flat module that will be partitioned with TritonPart when updating - // the subtree later on. - if (module->getChildren().size() == 0) { - if (parent == root_cluster_) { - createFlatCluster(module, parent); - } else { - addModuleInstsToCluster(parent, module); - parent->clearDbModules(); - updateInstancesAssociation(parent); - } - return; - } - - for (odb::dbModInst* child : module->getChildren()) { - createCluster(child->getMaster(), parent); - } - createFlatCluster(module, parent); - } else { - // Parent is a cluster generated by merging small clusters: - // It may have a few logical modules or many glue insts. - for (auto& module : parent->getDbModules()) { - createCluster(module, parent); - } - - if (!parent->getLeafStdCells().empty() - || !parent->getLeafMacros().empty()) { - createCluster(parent); - } - } - - // Recursively break down non-flat large clusters with logical modules - for (auto& child : parent->getChildren()) { - if (!child->getDbModules().empty()) { - if (child->getNumStdCell() > max_num_inst_ - || child->getNumMacro() > max_num_macro_) { - breakCluster(child); - } - } - } - - // Merge small clusters - std::vector candidate_clusters; - for (auto& cluster : parent->getChildren()) { - if (!cluster->isIOCluster() && cluster->getNumStdCell() < min_num_inst_ - && cluster->getNumMacro() < min_num_macro_) { - candidate_clusters.push_back(cluster); - } - } - - mergeClusters(candidate_clusters); - - // Update the cluster_id - // This is important to maintain the clustering results - updateInstancesAssociation(parent); -} - -// This cluster won't be associated with the module. It will only -// contain its macros and std cells as leaves. -void HierRTLMP::createFlatCluster(odb::dbModule* module, Cluster* parent) -{ - std::string cluster_name - = std::string("(") + parent->getName() + ")_glue_logic"; - Cluster* cluster = new Cluster(cluster_id_, cluster_name, logger_); - addModuleInstsToCluster(cluster, module); - - if (cluster->getLeafStdCells().empty() && cluster->getLeafMacros().empty()) { - delete cluster; - cluster = nullptr; - } else { - incorporateNewClusterToTree(cluster, parent); - } -} - -void HierRTLMP::createCluster(Cluster* parent) -{ - std::string cluster_name - = std::string("(") + parent->getName() + ")_glue_logic"; - Cluster* cluster = new Cluster(cluster_id_, cluster_name, logger_); - for (auto& inst : parent->getLeafStdCells()) { - cluster->addLeafStdCell(inst); - } - for (auto& inst : parent->getLeafMacros()) { - cluster->addLeafMacro(inst); - } - - incorporateNewClusterToTree(cluster, parent); -} - -void HierRTLMP::createCluster(odb::dbModule* module, Cluster* parent) -{ - std::string cluster_name = module->getHierarchicalName(); - Cluster* cluster = new Cluster(cluster_id_, cluster_name, logger_); - cluster->addDbModule(module); - incorporateNewClusterToTree(cluster, parent); -} - -void HierRTLMP::addModuleInstsToCluster(Cluster* cluster, odb::dbModule* module) -{ - for (odb::dbInst* inst : module->getInsts()) { - odb::dbMaster* master = inst->getMaster(); - if (isIgnoredMaster(master)) { - continue; - } - cluster->addLeafInst(inst); - } -} - -void HierRTLMP::incorporateNewClusterToTree(Cluster* cluster, Cluster* parent) -{ - updateInstancesAssociation(cluster); - setClusterMetrics(cluster); - cluster_map_[cluster_id_++] = cluster; - - // modify physical hierarchy - cluster->setParent(parent); - parent->addChild(cluster); -} - -// Merge small clusters with the same parent cluster -// Recursively merge clusters -// Here is an example process based on connection signature -// Iter1 : A, B, C, D, E, F -// Iter2 : A + C, B + D, E, F -// Iter3 : A + C + F, B + D, E -// End if there is no same connection signature -// During the merging process, we support two types of merging -// Type 1: merging small clusters to their closely connected clusters -// For example, if a small cluster A is closely connected to a -// well-formed cluster B, (there are also other well-formed clusters -// C, D), A is only connected to B and A has no connection with C, D -// Type 2: merging small clusters with the same connection signature -// For example, if we merge small clusters A and B, A and B will have -// exactly the same connections relative to all other clusters (both -// small clusters and well-formed clusters). In this case, if A and B -// have the same connection signature, A and C have the same connection -// signature, then B and C also have the same connection signature. -// Note in both types, we only merge clusters with the same parent cluster -void HierRTLMP::mergeClusters(std::vector& candidate_clusters) -{ - if (candidate_clusters.empty()) { - return; - } - - int merge_iter = 0; - debugPrint(logger_, - MPL, - "multilevel_autoclustering", - 1, - "Merge Cluster Iter: {}", - merge_iter++); - for (auto& cluster : candidate_clusters) { - debugPrint(logger_, - MPL, - "multilevel_autoclustering", - 1, - "Cluster: {}, num std cell: {}, num macros: {}", - cluster->getName(), - cluster->getNumStdCell(), - cluster->getNumMacro()); - } - - int num_candidate_clusters = candidate_clusters.size(); - while (true) { - calculateConnection(); // update the connections between clusters - - std::vector cluster_class(num_candidate_clusters, -1); // merge flag - std::vector candidate_clusters_id; // store cluster id - candidate_clusters_id.reserve(candidate_clusters.size()); - for (auto& cluster : candidate_clusters) { - candidate_clusters_id.push_back(cluster->getId()); - } - // Firstly we perform Type 1 merge - for (int i = 0; i < num_candidate_clusters; i++) { - const int cluster_id = candidate_clusters[i]->getCloseCluster( - candidate_clusters_id, signature_net_threshold_); - debugPrint( - logger_, - MPL, - "multilevel_autoclustering", - 1, - "Candidate cluster: {} - {}", - candidate_clusters[i]->getName(), - (cluster_id != -1 ? cluster_map_[cluster_id]->getName() : " ")); - if (cluster_id != -1 && !cluster_map_[cluster_id]->isIOCluster()) { - Cluster*& cluster = cluster_map_[cluster_id]; - bool delete_flag = false; - if (cluster->mergeCluster(*candidate_clusters[i], delete_flag)) { - if (delete_flag) { - cluster_map_.erase(candidate_clusters[i]->getId()); - delete candidate_clusters[i]; - } - updateInstancesAssociation(cluster); - setClusterMetrics(cluster); - cluster_class[i] = cluster->getId(); - } - } - } - - // Then we perform Type 2 merge - std::vector new_candidate_clusters; - for (int i = 0; i < num_candidate_clusters; i++) { - if (cluster_class[i] == -1) { // the cluster has not been merged - // new_candidate_clusters.push_back(candidate_clusters[i]); - for (int j = i + 1; j < num_candidate_clusters; j++) { - if (cluster_class[j] != -1) { - continue; - } - bool flag = candidate_clusters[i]->isSameConnSignature( - *candidate_clusters[j], signature_net_threshold_); - if (flag) { - cluster_class[j] = i; - bool delete_flag = false; - if (candidate_clusters[i]->mergeCluster(*candidate_clusters[j], - delete_flag)) { - if (delete_flag) { - cluster_map_.erase(candidate_clusters[j]->getId()); - delete candidate_clusters[j]; - } - updateInstancesAssociation(candidate_clusters[i]); - setClusterMetrics(candidate_clusters[i]); - } - } - } - } - } - - // Then we perform Type 3 merge: merge all dust cluster - const int dust_cluster_std_cell = 10; - for (int i = 0; i < num_candidate_clusters; i++) { - if (cluster_class[i] == -1) { // the cluster has not been merged - new_candidate_clusters.push_back(candidate_clusters[i]); - if (candidate_clusters[i]->getNumStdCell() <= dust_cluster_std_cell - && candidate_clusters[i]->getNumMacro() == 0) { - for (int j = i + 1; j < num_candidate_clusters; j++) { - if (cluster_class[j] != -1 - || candidate_clusters[j]->getNumMacro() > 0 - || candidate_clusters[j]->getNumStdCell() - > dust_cluster_std_cell) { - continue; - } - cluster_class[j] = i; - bool delete_flag = false; - if (candidate_clusters[i]->mergeCluster(*candidate_clusters[j], - delete_flag)) { - if (delete_flag) { - cluster_map_.erase(candidate_clusters[j]->getId()); - delete candidate_clusters[j]; - } - updateInstancesAssociation(candidate_clusters[i]); - setClusterMetrics(candidate_clusters[i]); - } - } - } - } - } - - // Update the candidate clusters - // Some clusters have become well-formed clusters - candidate_clusters.clear(); - for (auto& cluster : new_candidate_clusters) { - if (cluster->getNumStdCell() < min_num_inst_ - && cluster->getNumMacro() < min_num_macro_) { - candidate_clusters.push_back(cluster); - } - } - - // If no more clusters have been merged, exit the merging loop - if (num_candidate_clusters == new_candidate_clusters.size()) { - break; - } - - num_candidate_clusters = candidate_clusters.size(); - - debugPrint(logger_, - MPL, - "multilevel_autoclustering", - 1, - "Merge Cluster Iter: {}", - merge_iter++); - for (auto& cluster : candidate_clusters) { - debugPrint(logger_, - MPL, - "multilevel_autoclustering", - 1, - "Cluster: {}", - cluster->getName()); - } - // merge small clusters - if (candidate_clusters.empty()) { - break; - } - } - debugPrint(logger_, - MPL, - "multilevel_autoclustering", - 1, - "Finished merging clusters"); -} - -void HierRTLMP::calculateConnection() -{ - for (auto& [cluster_id, cluster] : cluster_map_) { - cluster->initConnection(); - } - - for (odb::dbNet* net : block_->getNets()) { - if (net->getSigType().isSupply()) { - continue; - } - - int driver_cluster_id = -1; - std::vector load_clusters_ids; - bool net_has_pad_or_cover = false; - - for (odb::dbITerm* iterm : net->getITerms()) { - odb::dbInst* inst = iterm->getInst(); - odb::dbMaster* master = inst->getMaster(); - - if (isIgnoredMaster(master)) { - net_has_pad_or_cover = true; - break; - } - - const int cluster_id = inst_to_cluster_.at(inst); - - if (iterm->getIoType() == odb::dbIoType::OUTPUT) { - driver_cluster_id = cluster_id; - } else { - load_clusters_ids.push_back(cluster_id); - } - } - - if (net_has_pad_or_cover) { - continue; - } - - bool net_has_io_pin = false; - - for (odb::dbBTerm* bterm : net->getBTerms()) { - const int cluster_id = bterm_to_cluster_.at(bterm); - net_has_io_pin = true; - - if (bterm->getIoType() == odb::dbIoType::INPUT) { - driver_cluster_id = cluster_id; - } else { - load_clusters_ids.push_back(cluster_id); - } - } - - if (driver_cluster_id != -1 && !load_clusters_ids.empty() - && load_clusters_ids.size() < large_net_threshold_) { - const float weight = net_has_io_pin ? virtual_weight_ : 1.0; - - for (const int load_cluster_id : load_clusters_ids) { - if (load_cluster_id != driver_cluster_id) { /* undirected connection */ - cluster_map_[driver_cluster_id]->addConnection(load_cluster_id, - weight); - cluster_map_[load_cluster_id]->addConnection(driver_cluster_id, - weight); - } - } - } - } -} - -// Dataflow is used to improve quality of macro placement. -// Here we model each std cell instance, IO pin and macro pin as vertices. -void HierRTLMP::createDataFlow() -{ - debugPrint( - logger_, MPL, "multilevel_autoclustering", 1, "Creating dataflow..."); - if (max_num_ff_dist_ <= 0) { - return; - } - // create vertex id property for std cell, IO pin and macro pin - std::map io_pin_vertex; - std::map std_cell_vertex; - std::map macro_pin_vertex; - - std::vector stop_flag_vec; - // assign vertex_id property of each Bterm - // All boundary terms are marked as sequential stopping pts - for (odb::dbBTerm* term : block_->getBTerms()) { - odb::dbIntProperty::create(term, "vertex_id", stop_flag_vec.size()); - io_pin_vertex[stop_flag_vec.size()] = term; - stop_flag_vec.push_back(true); - } - - // assign vertex_id property of each instance - for (auto inst : block_->getInsts()) { - odb::dbMaster* master = inst->getMaster(); - if (isIgnoredMaster(master) || master->isBlock()) { - continue; - } - - const sta::LibertyCell* liberty_cell = network_->libertyCell(inst); - if (!liberty_cell) { - continue; - } - - // Mark registers - odb::dbIntProperty::create(inst, "vertex_id", stop_flag_vec.size()); - std_cell_vertex[stop_flag_vec.size()] = inst; - - if (liberty_cell->hasSequentials()) { - stop_flag_vec.push_back(true); - } else { - stop_flag_vec.push_back(false); - } - } - // assign vertex_id property of each macro pin - // all macro pins are flagged as sequential stopping pt - for (auto& [macro, hard_macro] : hard_macro_map_) { - for (odb::dbITerm* pin : macro->getITerms()) { - if (pin->getSigType() != odb::dbSigType::SIGNAL) { - continue; - } - odb::dbIntProperty::create(pin, "vertex_id", stop_flag_vec.size()); - macro_pin_vertex[stop_flag_vec.size()] = pin; - stop_flag_vec.push_back(true); - } - } - - // - // Num of vertices will be # of boundary pins + number of logical std cells + - // number of macro pins) - // - debugPrint(logger_, - MPL, - "multilevel_autoclustering", - 1, - "Number of vertices: {}", - stop_flag_vec.size()); - - // create hypergraphs - std::vector> vertices(stop_flag_vec.size()); - std::vector> backward_vertices(stop_flag_vec.size()); - std::vector> hyperedges; // dircted hypergraph - // traverse the netlist - for (odb::dbNet* net : block_->getNets()) { - // ignore all the power net - if (net->getSigType().isSupply()) { - continue; - } - int driver_id = -1; // driver vertex id - std::set loads_id; // load vertex id - bool ignore = false; - // check the connected instances - for (odb::dbITerm* iterm : net->getITerms()) { - odb::dbInst* inst = iterm->getInst(); - odb::dbMaster* master = inst->getMaster(); - // We ignore nets connecting ignored masters - if (isIgnoredMaster(master)) { - ignore = true; - break; - } - int vertex_id = -1; - if (master->isBlock()) { - vertex_id = odb::dbIntProperty::find(iterm, "vertex_id")->getValue(); - } else { - vertex_id = odb::dbIntProperty::find(inst, "vertex_id")->getValue(); - } - if (iterm->getIoType() == odb::dbIoType::OUTPUT) { - driver_id = vertex_id; - } else { - loads_id.insert(vertex_id); - } - } - if (ignore) { - continue; // the nets with Pads should be ignored - } - - // check the connected IO pins of the net - for (odb::dbBTerm* bterm : net->getBTerms()) { - const int vertex_id - = odb::dbIntProperty::find(bterm, "vertex_id")->getValue(); - if (bterm->getIoType() == odb::dbIoType::INPUT) { - driver_id = vertex_id; - } else { - loads_id.insert(vertex_id); - } - } - - // - // Skip high fanout nets or nets that do not have valid driver or loads - // - if (driver_id < 0 || loads_id.empty() - || loads_id.size() > large_net_threshold_) { - continue; - } - - // Create the hyperedge - std::vector hyperedge{driver_id}; - for (auto& load : loads_id) { - if (load != driver_id) { - hyperedge.push_back(load); - } - } - vertices[driver_id].push_back(hyperedges.size()); - for (int i = 1; i < hyperedge.size(); i++) { - backward_vertices[hyperedge[i]].push_back(hyperedges.size()); - } - hyperedges.push_back(hyperedge); - } // end net traversal - - debugPrint( - logger_, MPL, "multilevel_autoclustering", 1, "Created hypergraph"); - - // traverse hypergraph to build dataflow - for (auto [src, src_pin] : io_pin_vertex) { - int idx = 0; - std::vector visited(vertices.size(), false); - std::vector> insts(max_num_ff_dist_); - dataFlowDFSIOPin(src, - idx, - insts, - io_pin_vertex, - std_cell_vertex, - macro_pin_vertex, - stop_flag_vec, - visited, - vertices, - hyperedges, - false); - dataFlowDFSIOPin(src, - idx, - insts, - io_pin_vertex, - std_cell_vertex, - macro_pin_vertex, - stop_flag_vec, - visited, - backward_vertices, - hyperedges, - true); - io_ffs_conn_map_.emplace_back(src_pin, insts); - } - - for (auto [src, src_pin] : macro_pin_vertex) { - int idx = 0; - std::vector visited(vertices.size(), false); - std::vector> std_cells(max_num_ff_dist_); - std::vector> macros(max_num_ff_dist_); - dataFlowDFSMacroPin(src, - idx, - std_cells, - macros, - io_pin_vertex, - std_cell_vertex, - macro_pin_vertex, - stop_flag_vec, - visited, - vertices, - hyperedges, - false); - dataFlowDFSMacroPin(src, - idx, - std_cells, - macros, - io_pin_vertex, - std_cell_vertex, - macro_pin_vertex, - stop_flag_vec, - visited, - backward_vertices, - hyperedges, - true); - macro_ffs_conn_map_.emplace_back(src_pin, std_cells); - macro_macro_conn_map_.emplace_back(src_pin, macros); - } -} - -// -// Forward or Backward DFS search to find sequential paths from/to IO pins based -// on hop count to macro pins -// -void HierRTLMP::dataFlowDFSIOPin(int parent, - int idx, - std::vector>& insts, - std::map& io_pin_vertex, - std::map& std_cell_vertex, - std::map& macro_pin_vertex, - std::vector& stop_flag_vec, - std::vector& visited, - std::vector>& vertices, - std::vector>& hyperedges, - bool backward_flag) -{ - visited[parent] = true; - if (stop_flag_vec[parent]) { - if (parent < io_pin_vertex.size()) { - ; // currently we do not consider IO pin to IO pin connnection - } else if (parent < io_pin_vertex.size() + std_cell_vertex.size()) { - insts[idx].insert(std_cell_vertex[parent]); - } else { - insts[idx].insert(macro_pin_vertex[parent]->getInst()); - } - idx++; - } - - if (idx >= max_num_ff_dist_) { - return; - } - - if (!backward_flag) { - for (auto& hyperedge : vertices[parent]) { - for (auto& vertex : hyperedges[hyperedge]) { - // we do not consider pin to pin - if (visited[vertex] || vertex < io_pin_vertex.size()) { - continue; - } - dataFlowDFSIOPin(vertex, - idx, - insts, - io_pin_vertex, - std_cell_vertex, - macro_pin_vertex, - stop_flag_vec, - visited, - vertices, - hyperedges, - backward_flag); - } - } // finish hyperedges - } else { - for (auto& hyperedge : vertices[parent]) { - const int vertex = hyperedges[hyperedge][0]; // driver vertex - // we do not consider pin to pin - if (visited[vertex] || vertex < io_pin_vertex.size()) { - continue; - } - dataFlowDFSIOPin(vertex, - idx, - insts, - io_pin_vertex, - std_cell_vertex, - macro_pin_vertex, - stop_flag_vec, - visited, - vertices, - hyperedges, - backward_flag); - } // finish hyperedges - } // finish current vertex -} - -// -// Forward or Backward DFS search to find sequential paths between Macros based -// on hop count -// -void HierRTLMP::dataFlowDFSMacroPin( - int parent, - int idx, - std::vector>& std_cells, - std::vector>& macros, - std::map& io_pin_vertex, - std::map& std_cell_vertex, - std::map& macro_pin_vertex, - std::vector& stop_flag_vec, - std::vector& visited, - std::vector>& vertices, - std::vector>& hyperedges, - bool backward_flag) -{ - visited[parent] = true; - if (stop_flag_vec[parent]) { - if (parent < io_pin_vertex.size()) { - ; // the connection between IO and macro pins have been considers - } else if (parent < io_pin_vertex.size() + std_cell_vertex.size()) { - std_cells[idx].insert(std_cell_vertex[parent]); - } else { - macros[idx].insert(macro_pin_vertex[parent]->getInst()); - } - idx++; - } - - if (idx >= max_num_ff_dist_) { - return; - } - - if (!backward_flag) { - for (auto& hyperedge : vertices[parent]) { - for (auto& vertex : hyperedges[hyperedge]) { - // we do not consider pin to pin - if (visited[vertex] || vertex < io_pin_vertex.size()) { - continue; - } - dataFlowDFSMacroPin(vertex, - idx, - std_cells, - macros, - io_pin_vertex, - std_cell_vertex, - macro_pin_vertex, - stop_flag_vec, - visited, - vertices, - hyperedges, - backward_flag); - } - } // finish hyperedges - } else { - for (auto& hyperedge : vertices[parent]) { - const int vertex = hyperedges[hyperedge][0]; - // we do not consider pin to pin - if (visited[vertex] || vertex < io_pin_vertex.size()) { - continue; - } - dataFlowDFSMacroPin(vertex, - idx, - std_cells, - macros, - io_pin_vertex, - std_cell_vertex, - macro_pin_vertex, - stop_flag_vec, - visited, - vertices, - hyperedges, - backward_flag); - } // finish hyperedges - } -} - -void HierRTLMP::updateDataFlow() -{ - // bterm, macros or ffs - for (const auto& [bterm, insts] : io_ffs_conn_map_) { - if (bterm_to_cluster_.find(bterm) == bterm_to_cluster_.end()) { - continue; - } - - const int driver_id = bterm_to_cluster_.at(bterm); - - for (int i = 0; i < max_num_ff_dist_; i++) { - const float weight = dataflow_weight_ / std::pow(dataflow_factor_, i); - std::set sink_clusters; - - for (auto& inst : insts[i]) { - const int cluster_id = inst_to_cluster_.at(inst); - sink_clusters.insert(cluster_id); - } - - for (auto& sink : sink_clusters) { - cluster_map_[driver_id]->addConnection(sink, weight); - cluster_map_[sink]->addConnection(driver_id, weight); - } - } - } - - // macros to ffs - for (const auto& [iterm, insts] : macro_ffs_conn_map_) { - const int driver_id = inst_to_cluster_.at(iterm->getInst()); - - for (int i = 0; i < max_num_ff_dist_; i++) { - const float weight = dataflow_weight_ / std::pow(dataflow_factor_, i); - std::set sink_clusters; - - for (auto& inst : insts[i]) { - const int cluster_id = inst_to_cluster_.at(inst); - sink_clusters.insert(cluster_id); - } - - for (auto& sink : sink_clusters) { - cluster_map_[driver_id]->addConnection(sink, weight); - cluster_map_[sink]->addConnection(driver_id, weight); - } - } - } - - // macros to macros - for (const auto& [iterm, insts] : macro_macro_conn_map_) { - const int driver_id = inst_to_cluster_.at(iterm->getInst()); - - for (int i = 0; i < max_num_ff_dist_; i++) { - const float weight = dataflow_weight_ / std::pow(dataflow_factor_, i); - std::set sink_clusters; - - for (auto& inst : insts[i]) { - const int cluster_id = inst_to_cluster_.at(inst); - sink_clusters.insert(cluster_id); - } - - for (auto& sink : sink_clusters) { - cluster_map_[driver_id]->addConnection(sink, weight); - } - } - } -} - -// Print Connnection For all the clusters -void HierRTLMP::printConnection() -{ - std::string line; - line += "NUM_CLUSTERS : " + std::to_string(cluster_map_.size()) + "\n"; - for (auto& [cluster_id, cluster] : cluster_map_) { - const std::map connections = cluster->getConnection(); - if (connections.empty()) { - continue; - } - line += "cluster " + cluster->getName() + " : \n"; - for (auto [target, num_nets] : connections) { - line += "\t\t" + cluster_map_[target]->getName() + " "; - line += std::to_string(static_cast(num_nets)) + "\n"; - } - } - logger_->report(line); -} - -// Print All the clusters and their statics -void HierRTLMP::printClusters() -{ - std::string line; - line += "NUM_CLUSTERS : " + std::to_string(cluster_map_.size()) + "\n"; - for (auto& [cluster_id, cluster] : cluster_map_) { - line += cluster->getName() + " "; - line += std::to_string(cluster->getId()) + "\n"; - } - logger_->report(line); -} - -// This function has two purposes: -// 1) remove all the internal clusters between parent and leaf clusters in its -// subtree 2) Call TritonPart to partition large flat clusters (a cluster with -// no logical modules) -void HierRTLMP::updateSubTree(Cluster* parent) -{ - std::vector children_clusters; - std::vector internal_clusters; - std::queue wavefront; - for (auto child : parent->getChildren()) { - wavefront.push(child); - } - - while (!wavefront.empty()) { - Cluster* cluster = wavefront.front(); - wavefront.pop(); - if (cluster->getChildren().empty()) { - children_clusters.push_back(cluster); - } else { - internal_clusters.push_back(cluster); - for (auto child : cluster->getChildren()) { - wavefront.push(child); - } - } - } - - // delete all the internal clusters - for (auto& cluster : internal_clusters) { - cluster_map_.erase(cluster->getId()); - delete cluster; - } - - parent->removeChildren(); - parent->addChildren(children_clusters); - for (auto& cluster : children_clusters) { - cluster->setParent(parent); - if (cluster->getNumStdCell() > max_num_inst_) { - breakLargeFlatCluster(cluster); - } - } -} - -// Break large flat clusters with TritonPart -// Binary coding method to differentiate partitions: -// cluster -> cluster_0, cluster_1 -// cluster_0 -> cluster_0_0, cluster_0_1 -// cluster_1 -> cluster_1_0, cluster_1_1 [...] -void HierRTLMP::breakLargeFlatCluster(Cluster* parent) -{ - // Check if the cluster is a large flat cluster - if (!parent->getDbModules().empty() - || parent->getLeafStdCells().size() < max_num_inst_) { - return; - } - updateInstancesAssociation(parent); - - std::map cluster_vertex_id_map; - std::vector vertex_weight; - int vertex_id = 0; - for (auto& [cluster_id, cluster] : cluster_map_) { - cluster_vertex_id_map[cluster_id] = vertex_id++; - vertex_weight.push_back(0.0f); - } - const int num_other_cluster_vertices = vertex_id; - - std::vector insts; - std::map inst_vertex_id_map; - for (auto& macro : parent->getLeafMacros()) { - inst_vertex_id_map[macro] = vertex_id++; - vertex_weight.push_back(computeMicronArea(macro)); - insts.push_back(macro); - } - for (auto& std_cell : parent->getLeafStdCells()) { - inst_vertex_id_map[std_cell] = vertex_id++; - vertex_weight.push_back(computeMicronArea(std_cell)); - insts.push_back(std_cell); - } - - std::vector> hyperedges; - for (odb::dbNet* net : block_->getNets()) { - if (net->getSigType().isSupply()) { - continue; - } - - int driver_id = -1; - std::set loads_id; - bool ignore = false; - for (odb::dbITerm* iterm : net->getITerms()) { - odb::dbInst* inst = iterm->getInst(); - odb::dbMaster* master = inst->getMaster(); - if (isIgnoredMaster(master)) { - ignore = true; - break; - } - - const int cluster_id = inst_to_cluster_.at(inst); - int vertex_id = (cluster_id != parent->getId()) - ? cluster_vertex_id_map[cluster_id] - : inst_vertex_id_map[inst]; - if (iterm->getIoType() == odb::dbIoType::OUTPUT) { - driver_id = vertex_id; - } else { - loads_id.insert(vertex_id); - } - } - - if (ignore) { - continue; - } - - for (odb::dbBTerm* bterm : net->getBTerms()) { - const int cluster_id = bterm_to_cluster_.at(bterm); - if (bterm->getIoType() == odb::dbIoType::INPUT) { - driver_id = cluster_vertex_id_map[cluster_id]; - } else { - loads_id.insert(cluster_vertex_id_map[cluster_id]); - } - } - loads_id.insert(driver_id); - if (driver_id != -1 && loads_id.size() > 1 - && loads_id.size() < large_net_threshold_) { - std::vector hyperedge; - hyperedge.insert(hyperedge.end(), loads_id.begin(), loads_id.end()); - hyperedges.push_back(hyperedge); - } - } - - const int seed = 0; - const float balance_constraint = 1.0; - const int num_parts = 2; // We use two-way partitioning here - const int num_vertices = static_cast(vertex_weight.size()); - std::vector hyperedge_weights(hyperedges.size(), 1.0f); - - debugPrint(logger_, - MPL, - "multilevel_autoclustering", - 1, - "Breaking flat cluster {} with TritonPart", - parent->getName()); - - std::vector part - = tritonpart_->PartitionKWaySimpleMode(num_parts, - balance_constraint, - seed, - hyperedges, - vertex_weight, - hyperedge_weights); - - parent->clearLeafStdCells(); - parent->clearLeafMacros(); - - const std::string cluster_name = parent->getName(); - parent->setName(cluster_name + std::string("_0")); - Cluster* cluster_part_1 - = new Cluster(cluster_id_, cluster_name + std::string("_1"), logger_); - - for (int i = num_other_cluster_vertices; i < num_vertices; i++) { - odb::dbInst* inst = insts[i - num_other_cluster_vertices]; - if (part[i] == 0) { - parent->addLeafInst(inst); - } else { - cluster_part_1->addLeafInst(inst); - } - } - - updateInstancesAssociation(parent); - setClusterMetrics(parent); - incorporateNewClusterToTree(cluster_part_1, parent->getParent()); - - // Recursive break the cluster - // until the size of the cluster is less than max_num_inst_ - breakLargeFlatCluster(parent); - breakLargeFlatCluster(cluster_part_1); -} - -// Traverse the physical hierarchy tree in a DFS manner (post-order) -void HierRTLMP::fetchMixedLeaves( - Cluster* parent, - std::vector>& mixed_leaves) -{ - if (parent->getChildren().empty() || parent->getNumMacro() == 0) { - return; - } - - std::vector sister_mixed_leaves; - - for (auto& child : parent->getChildren()) { - updateInstancesAssociation(child); - if (child->getNumMacro() > 0) { - if (child->getChildren().empty()) { - sister_mixed_leaves.push_back(child); - } else { - fetchMixedLeaves(child, mixed_leaves); - } - } else { - child->setClusterType(StdCellCluster); - } - } - - // We push the leaves after finishing searching the children so - // that each vector of clusters represents the children of one - // parent. - mixed_leaves.push_back(sister_mixed_leaves); -} - -void HierRTLMP::breakMixedLeaves( - const std::vector>& mixed_leaves) -{ - for (const std::vector& sister_mixed_leaves : mixed_leaves) { - if (!sister_mixed_leaves.empty()) { - Cluster* parent = sister_mixed_leaves.front()->getParent(); - - for (Cluster* mixed_leaf : sister_mixed_leaves) { - breakMixedLeaf(mixed_leaf); - } - - updateInstancesAssociation(parent); - } - } -} - -// Break mixed leaf into standard-cell and hard-macro clusters. -// Merge macros based on connection signature and footprint. -// Based on types of designs, we support two types of breaking up: -// 1) Replace cluster A by A1, A2, A3 -// 2) Create a subtree: -// A -> A -// | | | -// A1 A2 A3 -void HierRTLMP::breakMixedLeaf(Cluster* mixed_leaf) -{ - Cluster* parent = mixed_leaf; - - // Split by replacement if macro dominated. - if (mixed_leaf->getNumStdCell() * macro_dominated_cluster_threshold_ - < mixed_leaf->getNumMacro()) { - parent = mixed_leaf->getParent(); + runHierarchicalMacroPlacementWithoutBusPlanning(tree_.root); } - mapMacroInCluster2HardMacro(mixed_leaf); - - std::vector hard_macros = mixed_leaf->getHardMacros(); - std::vector macro_clusters; + if (graphics_) { + graphics_->setMaxLevel(tree_.max_level); + graphics_->drawResult(); + } - createOneClusterForEachMacro(parent, hard_macros, macro_clusters); + Pusher pusher(logger_, tree_.root, block_, boundary_to_io_blockage_); + pusher.pushMacrosToCoreBoundaries(); - std::vector size_class(hard_macros.size(), -1); - classifyMacrosBySize(hard_macros, size_class); + updateMacrosOnDb(); - calculateConnection(); + generateTemporaryStdCellsPlacement(tree_.root); + correctAllMacrosOrientation(); - std::vector signature_class(hard_macros.size(), -1); - classifyMacrosByConnSignature(macro_clusters, signature_class); + commitMacroPlacementToDb(); - std::vector interconn_class(hard_macros.size(), -1); - classifyMacrosByInterconn(macro_clusters, interconn_class); + writeMacroPlacement(macro_placement_file_); + clear(); +} - std::vector macro_class(hard_macros.size(), -1); - groupSingleMacroClusters(macro_clusters, - size_class, - signature_class, - interconn_class, - macro_class); +//////////////////////////////////////////////////////////////////////// +// Private functions +//////////////////////////////////////////////////////////////////////// - mixed_leaf->clearHardMacros(); +void HierRTLMP::initMacroPlacer() +{ + block_ = db_->getChip()->getBlock(); + clustering_engine_ = std::make_unique( + block_, network_, logger_, tritonpart_); - // IMPORTANT: Restore the structure of physical hierarchical tree. Thus the - // order of leaf clusters will not change the final macro grouping results. - updateInstancesAssociation(mixed_leaf); + odb::Rect die = block_->getDieArea(); + odb::Rect core_box = block_->getCoreArea(); - // Never use SetInstProperty in the following lines for the reason above! - std::vector virtual_conn_clusters; + float core_lx = block_->dbuToMicrons(core_box.xMin()); + float core_ly = block_->dbuToMicrons(core_box.yMin()); + float core_ux = block_->dbuToMicrons(core_box.xMax()); + float core_uy = block_->dbuToMicrons(core_box.yMax()); - // Deal with the std cells - if (parent == mixed_leaf) { - addStdCellClusterToSubTree(parent, mixed_leaf, virtual_conn_clusters); - } else { - replaceByStdCellCluster(mixed_leaf, virtual_conn_clusters); - } + logger_->report( + "Floorplan Outline: ({}, {}) ({}, {}), Core Outline: ({}, {}) ({}, {})", + block_->dbuToMicrons(die.xMin()), + block_->dbuToMicrons(die.yMin()), + block_->dbuToMicrons(die.xMax()), + block_->dbuToMicrons(die.yMax()), + core_lx, + core_ly, + core_ux, + core_uy); - // Deal with the macros - for (int i = 0; i < macro_class.size(); i++) { - if (macro_class[i] != i) { - continue; // this macro cluster has been merged - } + float core_area = (core_ux - core_lx) * (core_uy - core_ly); - macro_clusters[i]->setClusterType(HardMacroCluster); + computeMetricsForModules(core_area); +} - if (interconn_class[i] != -1) { - macro_clusters[i]->setAsArrayOfInterconnectedMacros(); - } +void HierRTLMP::computeMetricsForModules(float core_area) +{ + metrics_ = computeMetrics(block_->getTopModule()); - setClusterMetrics(macro_clusters[i]); - virtual_conn_clusters.push_back(mixed_leaf->getId()); - } + float util + = (metrics_->getStdCellArea() + metrics_->getMacroArea()) / core_area; + float core_util + = metrics_->getStdCellArea() / (core_area - metrics_->getMacroArea()); - // add virtual connections - for (int i = 0; i < virtual_conn_clusters.size(); i++) { - for (int j = i + 1; j < virtual_conn_clusters.size(); j++) { - parent->addVirtualConnection(virtual_conn_clusters[i], - virtual_conn_clusters[j]); + // Check if placement is feasible in the core area when considering + // the macro halos + int unfixed_macros = 0; + for (auto inst : block_->getInsts()) { + auto master = inst->getMaster(); + if (master->isBlock()) { + const auto width + = block_->dbuToMicrons(master->getWidth()) + 2 * halo_width_; + const auto height + = block_->dbuToMicrons(master->getHeight()) + 2 * halo_width_; + macro_with_halo_area_ += width * height; + unfixed_macros += !inst->getPlacementStatus().isFixed(); } } -} + reportLogicalHierarchyInformation(core_area, util, core_util); -// Map all the macros into their HardMacro objects for all the clusters -void HierRTLMP::mapMacroInCluster2HardMacro(Cluster* cluster) -{ - if (cluster->getClusterType() == StdCellCluster) { + if (unfixed_macros == 0) { + design_has_unfixed_macros_ = false; return; } - std::vector hard_macros; - for (const auto& macro : cluster->getLeafMacros()) { - hard_macros.push_back(hard_macro_map_[macro]); - } - for (const auto& module : cluster->getDbModules()) { - getHardMacros(module, hard_macros); + if (macro_with_halo_area_ + metrics_->getStdCellArea() > core_area) { + logger_->error(MPL, + 16, + "The instance area with halos {} exceeds the core area {}", + macro_with_halo_area_ + metrics_->getStdCellArea(), + core_area); } - cluster->specifyHardMacros(hard_macros); } -// Get all the hard macros in a logical module -void HierRTLMP::getHardMacros(odb::dbModule* module, - std::vector& hard_macros) +void HierRTLMP::reportLogicalHierarchyInformation(float core_area, + float util, + float core_util) { - for (odb::dbInst* inst : module->getInsts()) { - odb::dbMaster* master = inst->getMaster(); - - if (isIgnoredMaster(master)) { - continue; - } - - if (master->isBlock()) { - hard_macros.push_back(hard_macro_map_[inst]); - } - } - - for (odb::dbModInst* inst : module->getChildren()) { - getHardMacros(inst->getMaster(), hard_macros); - } + logger_->report( + "\tNumber of std cell instances: {}\n" + "\tArea of std cell instances: {:.2f}\n" + "\tNumber of macros: {}\n" + "\tArea of macros: {:.2f}\n" + "\tHalo width: {:.2f}\n" + "\tHalo height: {:.2f}\n" + "\tArea of macros with halos: {:.2f}\n" + "\tArea of std cell instances + Area of macros: {:.2f}\n" + "\tCore area: {:.2f}\n" + "\tDesign Utilization: {:.2f}\n" + "\tCore Utilization: {:.2f}\n" + "\tManufacturing Grid: {}\n", + metrics_->getNumStdCell(), + metrics_->getStdCellArea(), + metrics_->getNumMacro(), + metrics_->getMacroArea(), + halo_width_, + halo_height_, + macro_with_halo_area_, + metrics_->getStdCellArea() + metrics_->getMacroArea(), + core_area, + util, + core_util, + block_->getTech()->getManufacturingGrid()); } -void HierRTLMP::createOneClusterForEachMacro( - Cluster* parent, - const std::vector& hard_macros, - std::vector& macro_clusters) +// Transform the logical hierarchy into a physical hierarchy. +void HierRTLMP::runMultilevelAutoclustering() { - for (auto& hard_macro : hard_macros) { - std::string cluster_name = hard_macro->getName(); - Cluster* single_macro_cluster - = new Cluster(cluster_id_, cluster_name, logger_); - single_macro_cluster->addLeafMacro(hard_macro->getInst()); - incorporateNewClusterToTree(single_macro_cluster, parent); - - macro_clusters.push_back(single_macro_cluster); - } + clustering_engine_->setDesignMetrics(metrics_); + clustering_engine_->setTree(&tree_); + clustering_engine_->run(); } -void HierRTLMP::classifyMacrosBySize(const std::vector& hard_macros, - std::vector& size_class) +void HierRTLMP::resetSAParameters() { - for (int i = 0; i < hard_macros.size(); i++) { - if (size_class[i] == -1) { - for (int j = i + 1; j < hard_macros.size(); j++) { - if ((size_class[j] == -1) && ((*hard_macros[i]) == (*hard_macros[j]))) { - size_class[j] = i; - } - } - } - } - - for (int i = 0; i < hard_macros.size(); i++) { - size_class[i] = (size_class[i] == -1) ? i : size_class[i]; - } + pos_swap_prob_ = 0.2; + neg_swap_prob_ = 0.2; + double_swap_prob_ = 0.2; + exchange_swap_prob_ = 0.2; + flip_prob_ = 0.2; + resize_prob_ = 0.0; + guidance_weight_ = 0.0; + fence_weight_ = 0.0; + boundary_weight_ = 0.0; + notch_weight_ = 0.0; + macro_blockage_weight_ = 0.0; } -void HierRTLMP::classifyMacrosByInterconn( - const std::vector& macro_clusters, - std::vector& interconn_class) +void HierRTLMP::runCoarseShaping() { - for (int i = 0; i < macro_clusters.size(); i++) { - if (interconn_class[i] == -1) { - interconn_class[i] = i; - for (int j = 0; j < macro_clusters.size(); j++) { - if (macro_clusters[i]->hasMacroConnectionWith( - *macro_clusters[j], signature_net_threshold_)) { - if (interconn_class[j] != -1) { - interconn_class[i] = interconn_class[j]; - break; - } + setRootShapes(); - interconn_class[j] = i; - } - } - } + if (tree_.has_only_macros) { + logger_->warn(MPL, 27, "Design has only macros!"); + tree_.root->setClusterType(HardMacroCluster); + return; } -} - -void HierRTLMP::classifyMacrosByConnSignature( - const std::vector& macro_clusters, - std::vector& signature_class) -{ - for (int i = 0; i < macro_clusters.size(); i++) { - if (signature_class[i] == -1) { - signature_class[i] = i; - for (int j = i + 1; j < macro_clusters.size(); j++) { - if (signature_class[j] != -1) { - continue; - } - if (macro_clusters[i]->isSameConnSignature(*macro_clusters[j], - signature_net_threshold_)) { - signature_class[j] = i; - } - } - } + if (graphics_) { + graphics_->startCoarse(); } - if (logger_->debugCheck(MPL, "multilevel_autoclustering", 2)) { - logger_->report("\nPrint Connection Signature\n"); - for (auto& cluster : macro_clusters) { - logger_->report("Macro Signature: {}", cluster->getName()); - for (auto& [cluster_id, weight] : cluster->getConnection()) { - logger_->report(" {} {} ", cluster_map_[cluster_id]->getName(), weight); - } - } - } + calculateChildrenTilings(tree_.root); + + setIOClustersBlockages(); + setPlacementBlockages(); } -// We determine if the macros belong to the same class based on: -// 1. Size && and Interconnection (Directly connected macro clusters -// should be grouped) -// 2. Size && Connection Signature (Macros with same connection -// signature should be grouped) -void HierRTLMP::groupSingleMacroClusters( - const std::vector& macro_clusters, - const std::vector& size_class, - const std::vector& signature_class, - std::vector& interconn_class, - std::vector& macro_class) +void HierRTLMP::setRootShapes() { - for (int i = 0; i < macro_clusters.size(); i++) { - if (macro_class[i] != -1) { - continue; - } - macro_class[i] = i; + SoftMacro* root_soft_macro = new SoftMacro(tree_.root); - for (int j = i + 1; j < macro_clusters.size(); j++) { - if (macro_class[j] != -1) { - continue; - } + const float core_lx + = static_cast(block_->dbuToMicrons(block_->getCoreArea().xMin())); + const float root_lx = std::max(core_lx, global_fence_lx_); - if (size_class[i] == size_class[j]) { - if (interconn_class[i] == interconn_class[j]) { - macro_class[j] = i; + const float core_ly + = static_cast(block_->dbuToMicrons(block_->getCoreArea().yMin())); + const float root_ly = std::max(core_ly, global_fence_ly_); - debugPrint(logger_, - MPL, - "multilevel_autoclustering", - 1, - "Merging interconnected macro clusters {} and {}", - macro_clusters[j]->getName(), - macro_clusters[i]->getName()); + const float core_ux + = static_cast(block_->dbuToMicrons(block_->getCoreArea().xMax())); + const float root_ux = std::min(core_ux, global_fence_ux_); - mergeMacroClustersWithinSameClass(macro_clusters[i], - macro_clusters[j]); - } else { - // We need this so we can distinguish arrays of interconnected macros - // from grouped macro clusters with same signature. - interconn_class[i] = -1; - - if (signature_class[i] == signature_class[j]) { - macro_class[j] = i; - - debugPrint(logger_, - MPL, - "multilevel_autoclustering", - 1, - "Merging same signature clusters {} and {}.", - macro_clusters[j]->getName(), - macro_clusters[i]->getName()); - - mergeMacroClustersWithinSameClass(macro_clusters[i], - macro_clusters[j]); - } - } - } - } - } -} + const float core_uy + = static_cast(block_->dbuToMicrons(block_->getCoreArea().yMax())); + const float root_uy = std::min(core_uy, global_fence_uy_); -void HierRTLMP::mergeMacroClustersWithinSameClass(Cluster* target, - Cluster* source) -{ - bool delete_merged = false; - target->mergeCluster(*source, delete_merged); + const float root_area = (root_ux - root_lx) * (root_uy - root_ly); + const float root_width = root_ux - root_lx; + const std::vector> root_width_list + = {std::pair(root_width, root_width)}; - if (delete_merged) { - cluster_map_.erase(source->getId()); - delete source; - } + root_soft_macro->setShapes(root_width_list, root_area); + root_soft_macro->setWidth(root_width); // This will set height automatically + root_soft_macro->setX(root_lx); + root_soft_macro->setY(root_ly); + tree_.root->setSoftMacro(root_soft_macro); } -void HierRTLMP::addStdCellClusterToSubTree( - Cluster* parent, - Cluster* mixed_leaf, - std::vector& virtual_conn_clusters) +// Traverse Logical Hierarchy +// Recursive function to collect the design metrics (number of std cells, +// area of std cells, number of macros and area of macros) in the logical +// hierarchy +Metrics* HierRTLMP::computeMetrics(odb::dbModule* module) { - std::string std_cell_cluster_name = mixed_leaf->getName(); - Cluster* std_cell_cluster - = new Cluster(cluster_id_, std_cell_cluster_name, logger_); - - std_cell_cluster->copyInstances(*mixed_leaf); - std_cell_cluster->clearLeafMacros(); - std_cell_cluster->setClusterType(StdCellCluster); + unsigned int num_std_cell = 0; + float std_cell_area = 0.0; + unsigned int num_macro = 0; + float macro_area = 0.0; - setClusterMetrics(std_cell_cluster); + for (odb::dbInst* inst : module->getInsts()) { + odb::dbMaster* master = inst->getMaster(); - cluster_map_[cluster_id_++] = std_cell_cluster; + if (ClusteringEngine::isIgnoredMaster(master)) { + continue; + } - // modify the physical hierachy tree - std_cell_cluster->setParent(parent); - parent->addChild(std_cell_cluster); - virtual_conn_clusters.push_back(std_cell_cluster->getId()); -} + float inst_area = clustering_engine_->computeMicronArea(inst); -// We don't modify the physical hierarchy when spliting by replacement -void HierRTLMP::replaceByStdCellCluster(Cluster* mixed_leaf, - std::vector& virtual_conn_clusters) -{ - mixed_leaf->clearLeafMacros(); - mixed_leaf->setClusterType(StdCellCluster); + if (master->isBlock()) { // a macro + num_macro += 1; + macro_area += inst_area; - setClusterMetrics(mixed_leaf); + // add hard macro to corresponding map + HardMacro* macro = new HardMacro(inst, halo_width_, halo_height_); + tree_.maps.inst_to_hard[inst] = macro; + } else { + num_std_cell += 1; + std_cell_area += inst_area; + } + } - virtual_conn_clusters.push_back(mixed_leaf->getId()); -} + // Be careful about the relationship between + // odb::dbModule and odb::dbInst + // odb::dbModule and odb::dbModInst + // recursively traverse the hierarchical module instances + for (odb::dbModInst* inst : module->getChildren()) { + Metrics* metrics = computeMetrics(inst->getMaster()); + num_std_cell += metrics->getNumStdCell(); + std_cell_area += metrics->getStdCellArea(); + num_macro += metrics->getNumMacro(); + macro_area += metrics->getMacroArea(); + } -// Print Physical Hierarchy tree in a DFS manner -void HierRTLMP::printPhysicalHierarchyTree(Cluster* parent, int level) -{ - std::string line; - for (int i = 0; i < level; i++) { - line += "+---"; - } - line += fmt::format( - "{} ({}) num_macro : {} num_std_cell : {}" - " macro_area : {} std_cell_area : {} cluster type: {} {}", - parent->getName(), - parent->getId(), - parent->getNumMacro(), - parent->getNumStdCell(), - parent->getMacroArea(), - parent->getStdCellArea(), - parent->getIsLeafString(), - parent->getClusterTypeString()); - logger_->report("{}", line); + Metrics* metrics + = new Metrics(num_std_cell, num_macro, std_cell_area, macro_area); + tree_.maps.module_to_metrics[module] = metrics; - for (auto& cluster : parent->getChildren()) { - printPhysicalHierarchyTree(cluster, level + 1); - } + return metrics; } // Compare two intervals according to the product @@ -2564,7 +612,7 @@ void HierRTLMP::calculateChildrenTilings(Cluster* parent) + exchange_swap_prob_ + resize_prob_; const Rect outline( - 0, 0, root_cluster_->getWidth(), root_cluster_->getHeight()); + 0, 0, tree_.root->getWidth(), tree_.root->getHeight()); const int num_perturb_per_step = (macros.size() > num_perturb_per_step_ / 10) ? macros.size() @@ -2594,7 +642,7 @@ void HierRTLMP::calculateChildrenTilings(Cluster* parent) graphics_->setOutline(micronsToDbu(new_outline)); } SACoreSoftMacro* sa - = new SACoreSoftMacro(root_cluster_, + = new SACoreSoftMacro(tree_.root, new_outline, macros, 1.0, // area weight @@ -2660,7 +708,7 @@ void HierRTLMP::calculateChildrenTilings(Cluster* parent) graphics_->setOutline(micronsToDbu(new_outline)); } SACoreSoftMacro* sa - = new SACoreSoftMacro(root_cluster_, + = new SACoreSoftMacro(tree_.root, new_outline, macros, 1.0, // area weight @@ -2795,7 +843,7 @@ void HierRTLMP::calculateMacroTilings(Cluster* cluster) + exchange_swap_prob_; const Rect outline( - 0, 0, root_cluster_->getWidth(), root_cluster_->getHeight()); + 0, 0, tree_.root->getWidth(), tree_.root->getHeight()); // update macros std::vector macros; @@ -3012,17 +1060,17 @@ void HierRTLMP::setTightPackingTilings(Cluster* macro_array) void HierRTLMP::setIOClustersBlockages() { - if (!io_pad_map_.empty()) { + if (!tree_.maps.bterm_to_inst.empty()) { return; } IOSpans io_spans = computeIOSpans(); const float depth = computeIOBlockagesDepth(io_spans); - const Rect root(root_cluster_->getX(), - root_cluster_->getY(), - root_cluster_->getX() + root_cluster_->getWidth(), - root_cluster_->getY() + root_cluster_->getHeight()); + const Rect root(tree_.root->getX(), + tree_.root->getY(), + tree_.root->getX() + tree_.root->getWidth(), + tree_.root->getY() + tree_.root->getHeight()); // Note that the range can be larger than the respective core dimension. // As SA only sees what is inside its current outline, this is not a problem. @@ -3150,7 +1198,7 @@ float HierRTLMP::computeIOBlockagesDepth(const IOSpans& io_spans) } float std_cell_area = 0.0; - for (auto& cluster : root_cluster_->getChildren()) { + for (auto& cluster : tree_.root->getChildren()) { if (cluster->getClusterType() == StdCellCluster) { std_cell_area += cluster->getArea(); } @@ -3158,7 +1206,7 @@ float HierRTLMP::computeIOBlockagesDepth(const IOSpans& io_spans) const float macro_dominance_factor = macro_with_halo_area_ - / (root_cluster_->getWidth() * root_cluster_->getHeight()); + / (tree_.root->getWidth() * tree_.root->getHeight()); const float depth = (std_cell_area / sum_length) * std::pow((1 - macro_dominance_factor), 2); @@ -3188,13 +1236,9 @@ void HierRTLMP::setPlacementBlockages() void HierRTLMP::adjustCongestionWeight() { - // TODO: Isso aqui faz sentido? E se nós usarmos pinos de sinal - // em dois layers? Qual é o snap layer nesse caso? O de baixo - // ou o de cima? - // Should we alow setting the snap layer manually? std::string snap_layer_name; - for (auto& macro : hard_macro_map_) { + for (auto& macro : tree_.maps.inst_to_hard) { odb::dbMaster* master = macro.first->getMaster(); for (odb::dbMTerm* mterm : master->getMTerms()) { if (mterm->getSigType() == odb::dbSigType::SIGNAL) { @@ -3275,7 +1319,7 @@ void HierRTLMP::runHierarchicalMacroPlacement(Cluster* parent) } for (auto& cluster : parent->getChildren()) { - updateInstancesAssociation(cluster); + clustering_engine_->updateInstancesAssociation(cluster); } // Place children clusters // map children cluster to soft macro @@ -3336,7 +1380,7 @@ void HierRTLMP::runHierarchicalMacroPlacement(Cluster* parent) // for other clusters soft_macro_id_map[cluster->getName()] = macros.size(); SoftMacro* soft_macro = new SoftMacro(cluster); - updateInstancesAssociation(cluster); // we need this step to calculate nets + clustering_engine_->updateInstancesAssociation(cluster); // we need this step to calculate nets macros.push_back(*soft_macro); cluster->setSoftMacro(soft_macro); // merge fences and guides for hard macros within cluster @@ -3370,7 +1414,7 @@ void HierRTLMP::runHierarchicalMacroPlacement(Cluster* parent) } } - calculateConnection(); + clustering_engine_->updateConnections(); debugPrint(logger_, MPL, "hierarchical_macro_placement", @@ -3408,9 +1452,9 @@ void HierRTLMP::runHierarchicalMacroPlacement(Cluster* parent) // add the virtual connections (the weight related to IOs and macros belong to // the same cluster) for (const auto& [cluster1, cluster2] : parent->getVirtualConnections()) { - BundledNet net(soft_macro_id_map[cluster_map_[cluster1]->getName()], - soft_macro_id_map[cluster_map_[cluster2]->getName()], - virtual_weight_); + BundledNet net(soft_macro_id_map[tree_.maps.id_to_cluster[cluster1]->getName()], + soft_macro_id_map[tree_.maps.id_to_cluster[cluster2]->getName()], + tree_.virtual_weight); net.src_cluster_id = cluster1; net.target_cluster_id = cluster2; nets.push_back(net); @@ -3427,13 +1471,13 @@ void HierRTLMP::runHierarchicalMacroPlacement(Cluster* parent) 2, " Cluster connection: {} {} {} ", cluster->getName(), - cluster_map_[cluster_id]->getName(), + tree_.maps.id_to_cluster[cluster_id]->getName(), weight); - const std::string name = cluster_map_[cluster_id]->getName(); + const std::string name = tree_.maps.id_to_cluster[cluster_id]->getName(); if (soft_macro_id_map.find(name) == soft_macro_id_map.end()) { float new_weight = weight; if (macros[soft_macro_id_map[src_name]].isStdCellCluster()) { - new_weight *= virtual_weight_; + new_weight *= tree_.virtual_weight; } // if the cluster_id is out of the parent cluster BundledNet net( @@ -3761,7 +1805,7 @@ void HierRTLMP::runHierarchicalMacroPlacement(Cluster* parent) // Note that the weight are not necessaries summarized to 1.0, i.e., not // normalized. SACoreSoftMacro* sa - = new SACoreSoftMacro(root_cluster_, + = new SACoreSoftMacro(tree_.root, outline, shaped_macros, area_weight_, @@ -4019,7 +2063,7 @@ void HierRTLMP::runHierarchicalMacroPlacement(Cluster* parent) // of 1.0. Note that the weight are not necessaries summarized to 1.0, // i.e., not normalized. SACoreSoftMacro* sa - = new SACoreSoftMacro(root_cluster_, + = new SACoreSoftMacro(tree_.root, outline, shaped_macros, area_weight_, @@ -4174,7 +2218,7 @@ void HierRTLMP::runHierarchicalMacroPlacement(Cluster* parent) sa_containers.clear(); - updateInstancesAssociation(parent); + clustering_engine_->updateInstancesAssociation(parent); } // Merge nets to reduce runtime @@ -4216,7 +2260,7 @@ void HierRTLMP::mergeNets(std::vector& nets) // half of the outline weight. void HierRTLMP::adjustMacroBlockageWeight() { - if (max_num_level_ == 1) { + if (tree_.max_level == 1) { float new_macro_blockage_weight = outline_weight_ / 2.0; debugPrint(logger_, MPL, @@ -4224,7 +2268,7 @@ void HierRTLMP::adjustMacroBlockageWeight() 1, "Number of levels is {}, Changing macro blockage weight from {} " "to {} (half of the outline weight)", - max_num_level_, + tree_.max_level, macro_blockage_weight_, new_macro_blockage_weight); macro_blockage_weight_ = new_macro_blockage_weight; @@ -4261,7 +2305,7 @@ void HierRTLMP::runHierarchicalMacroPlacementWithoutBusPlanning(Cluster* parent) } for (auto& cluster : parent->getChildren()) { - updateInstancesAssociation(cluster); + clustering_engine_->updateInstancesAssociation(cluster); } // Place children clusters // map children cluster to soft macro @@ -4322,7 +2366,7 @@ void HierRTLMP::runHierarchicalMacroPlacementWithoutBusPlanning(Cluster* parent) // for other clusters soft_macro_id_map[cluster->getName()] = macros.size(); SoftMacro* soft_macro = new SoftMacro(cluster); - updateInstancesAssociation(cluster); // we need this step to calculate nets + clustering_engine_->updateInstancesAssociation(cluster); // we need this step to calculate nets macros.push_back(*soft_macro); cluster->setSoftMacro(soft_macro); // merge fences and guides for hard macros within cluster @@ -4411,13 +2455,13 @@ void HierRTLMP::runHierarchicalMacroPlacementWithoutBusPlanning(Cluster* parent) } // update the connnection - calculateConnection(); + clustering_engine_->updateConnections(); debugPrint(logger_, MPL, "hierarchical_macro_placement", 1, "Finished calculating connection"); - updateDataFlow(); + clustering_engine_->updateDataFlow(); debugPrint(logger_, MPL, "hierarchical_macro_placement", @@ -4427,9 +2471,9 @@ void HierRTLMP::runHierarchicalMacroPlacementWithoutBusPlanning(Cluster* parent) // add the virtual connections (the weight related to IOs and macros belong to // the same cluster) for (const auto& [cluster1, cluster2] : parent->getVirtualConnections()) { - BundledNet net(soft_macro_id_map[cluster_map_[cluster1]->getName()], - soft_macro_id_map[cluster_map_[cluster2]->getName()], - virtual_weight_); + BundledNet net(soft_macro_id_map[tree_.maps.id_to_cluster[cluster1]->getName()], + soft_macro_id_map[tree_.maps.id_to_cluster[cluster2]->getName()], + tree_.virtual_weight); net.src_cluster_id = cluster1; net.target_cluster_id = cluster2; nets.push_back(net); @@ -4446,9 +2490,9 @@ void HierRTLMP::runHierarchicalMacroPlacementWithoutBusPlanning(Cluster* parent) 2, " Cluster connection: {} {} {} ", cluster->getName(), - cluster_map_[cluster_id]->getName(), + tree_.maps.id_to_cluster[cluster_id]->getName(), weight); - const std::string name = cluster_map_[cluster_id]->getName(); + const std::string name = tree_.maps.id_to_cluster[cluster_id]->getName(); if (src_id > cluster_id) { BundledNet net( soft_macro_id_map[src_name], soft_macro_id_map[name], weight); @@ -4592,7 +2636,7 @@ void HierRTLMP::runHierarchicalMacroPlacementWithoutBusPlanning(Cluster* parent) // Note that the weight are not necessaries summarized to 1.0, i.e., not // normalized. SACoreSoftMacro* sa - = new SACoreSoftMacro(root_cluster_, + = new SACoreSoftMacro(tree_.root, outline, shaped_macros, area_weight_, @@ -4738,7 +2782,7 @@ void HierRTLMP::runHierarchicalMacroPlacementWithoutBusPlanning(Cluster* parent) sa_containers.clear(); - updateInstancesAssociation(parent); + clustering_engine_->updateInstancesAssociation(parent); } // This function is used in cases with very high density, in which it may @@ -4823,7 +2867,7 @@ void HierRTLMP::runEnhancedHierarchicalMacroPlacement(Cluster* parent) // for other clusters soft_macro_id_map[cluster->getName()] = macros.size(); SoftMacro* soft_macro = new SoftMacro(cluster); - updateInstancesAssociation(cluster); // we need this step to calculate nets + clustering_engine_->updateInstancesAssociation(cluster); // we need this step to calculate nets macros.push_back(*soft_macro); cluster->setSoftMacro(soft_macro); // merge fences and guides for hard macros within cluster @@ -4911,13 +2955,13 @@ void HierRTLMP::runEnhancedHierarchicalMacroPlacement(Cluster* parent) } // update the connnection - calculateConnection(); + clustering_engine_->updateConnections(); debugPrint(logger_, MPL, "hierarchical_macro_placement", 1, "Finished calculating connection"); - updateDataFlow(); + clustering_engine_->updateDataFlow(); debugPrint(logger_, MPL, "hierarchical_macro_placement", @@ -4927,9 +2971,9 @@ void HierRTLMP::runEnhancedHierarchicalMacroPlacement(Cluster* parent) // add the virtual connections (the weight related to IOs and macros belong to // the same cluster) for (const auto& [cluster1, cluster2] : parent->getVirtualConnections()) { - BundledNet net(soft_macro_id_map[cluster_map_[cluster1]->getName()], - soft_macro_id_map[cluster_map_[cluster2]->getName()], - virtual_weight_); + BundledNet net(soft_macro_id_map[tree_.maps.id_to_cluster[cluster1]->getName()], + soft_macro_id_map[tree_.maps.id_to_cluster[cluster2]->getName()], + tree_.virtual_weight); net.src_cluster_id = cluster1; net.target_cluster_id = cluster2; nets.push_back(net); @@ -4946,9 +2990,9 @@ void HierRTLMP::runEnhancedHierarchicalMacroPlacement(Cluster* parent) 2, " Cluster connection: {} {} {} ", cluster->getName(), - cluster_map_[cluster_id]->getName(), + tree_.maps.id_to_cluster[cluster_id]->getName(), weight); - const std::string name = cluster_map_[cluster_id]->getName(); + const std::string name = tree_.maps.id_to_cluster[cluster_id]->getName(); if (src_id > cluster_id) { BundledNet net( soft_macro_id_map[src_name], soft_macro_id_map[name], weight); @@ -5078,7 +3122,7 @@ void HierRTLMP::runEnhancedHierarchicalMacroPlacement(Cluster* parent) // Note that the weight are not necessaries summarized to 1.0, i.e., not // normalized. SACoreSoftMacro* sa - = new SACoreSoftMacro(root_cluster_, + = new SACoreSoftMacro(tree_.root, outline, shaped_macros, area_weight_, @@ -5490,7 +3534,7 @@ void HierRTLMP::placeMacros(Cluster* cluster) std::vector macro_clusters; // needed to calculate connections std::map cluster_to_macro; std::set masters; - createClusterForEachMacro( + clustering_engine_->createClusterForEachMacro( hard_macros, sa_macros, macro_clusters, cluster_to_macro, masters); const Rect outline(cluster->getX(), @@ -5502,7 +3546,7 @@ void HierRTLMP::placeMacros(Cluster* cluster) std::map guides; computeFencesAndGuides(hard_macros, outline, fences, guides); - calculateConnection(); + clustering_engine_->updateConnections(); createFixedTerminals(outline, macro_clusters, cluster_to_macro, sa_macros); @@ -5664,7 +3708,7 @@ void HierRTLMP::placeMacros(Cluster* cluster) sa_containers.clear(); - updateInstancesAssociation(cluster); + clustering_engine_->updateInstancesAssociation(cluster); } // Suppose we have a 2x2 array such as: @@ -5708,33 +3752,6 @@ void HierRTLMP::setArrayTilingSequencePair(Cluster* cluster, } } -void HierRTLMP::createClusterForEachMacro( - const std::vector& hard_macros, - std::vector& sa_macros, - std::vector& macro_clusters, - std::map& cluster_to_macro, - std::set& masters) -{ - int macro_id = 0; - std::string cluster_name; - - for (auto& hard_macro : hard_macros) { - macro_id = sa_macros.size(); - cluster_name = hard_macro->getName(); - - Cluster* macro_cluster = new Cluster(cluster_id_, cluster_name, logger_); - macro_cluster->addLeafMacro(hard_macro->getInst()); - updateInstancesAssociation(macro_cluster); - - sa_macros.push_back(*hard_macro); - macro_clusters.push_back(macro_cluster); - cluster_to_macro[cluster_id_] = macro_id; - masters.insert(hard_macro->getInst()->getMaster()); - - cluster_map_[cluster_id_++] = macro_cluster; - } -} - void HierRTLMP::computeFencesAndGuides( const std::vector& hard_macros, const Rect& outline, @@ -5773,7 +3790,7 @@ void HierRTLMP::createFixedTerminals( if (cluster_to_macro.find(cluster_id) != cluster_to_macro.end()) { continue; } - auto& temp_cluster = cluster_map_[cluster_id]; + auto& temp_cluster = tree_.maps.id_to_cluster[cluster_id]; // model other cluster as a fixed macro with zero size cluster_to_macro[cluster_id] = sa_macros.size(); @@ -6150,7 +4167,7 @@ void HierRTLMP::correctAllMacrosOrientation() void HierRTLMP::updateMacrosOnDb() { - for (const auto& [inst, hard_macro] : hard_macro_map_) { + for (const auto& [inst, hard_macro] : tree_.maps.inst_to_hard) { updateMacroOnDb(hard_macro); } } @@ -6179,7 +4196,7 @@ void HierRTLMP::commitMacroPlacementToDb() { Snapper snapper; - for (auto& [inst, hard_macro] : hard_macro_map_) { + for (auto& [inst, hard_macro] : tree_.maps.inst_to_hard) { if (!inst) { continue; } @@ -6213,20 +4230,20 @@ void HierRTLMP::writeMacroPlacement(const std::string& file_name) void HierRTLMP::clear() { - for (auto& [module, metrics] : logical_module_map_) { + for (auto& [module, metrics] : tree_.maps.module_to_metrics) { delete metrics; } - logical_module_map_.clear(); + tree_.maps.module_to_metrics.clear(); - for (auto& [inst, hard_macro] : hard_macro_map_) { + for (auto& [inst, hard_macro] : tree_.maps.inst_to_hard) { delete hard_macro; } - hard_macro_map_.clear(); + tree_.maps.inst_to_hard.clear(); - for (auto& [cluster_id, cluster] : cluster_map_) { + for (auto& [cluster_id, cluster] : tree_.maps.id_to_cluster) { delete cluster; } - cluster_map_.clear(); + tree_.maps.id_to_cluster.clear(); if (graphics_) { graphics_->eraseDrawing(); diff --git a/src/mpl2/src/hier_rtlmp.h b/src/mpl2/src/hier_rtlmp.h index f31d3e60b32..1e2a1614d17 100644 --- a/src/mpl2/src/hier_rtlmp.h +++ b/src/mpl2/src/hier_rtlmp.h @@ -36,22 +36,16 @@ #pragma once #include -#include #include -#include #include -#include #include "Mpl2Observer.h" #include "clusterEngine.h" namespace odb { -class dbBTerm; class dbBlock; class dbDatabase; -class dbITerm; class dbInst; -class dbMaster; class dbModule; } // namespace odb @@ -150,43 +144,15 @@ class HierRTLMP void reportLogicalHierarchyInformation(float core_area, float util, float core_util); - void updateDataFlow(); - void dataFlowDFSIOPin(int parent, - int idx, - std::vector>& insts, - std::map& io_pin_vertex, - std::map& std_cell_vertex, - std::map& macro_pin_vertex, - std::vector& stop_flag_vec, - std::vector& visited, - std::vector>& vertices, - std::vector>& hyperedges, - bool backward_flag); - void dataFlowDFSMacroPin(int parent, - int idx, - std::vector>& std_cells, - std::vector>& macros, - std::map& io_pin_vertex, - std::map& std_cell_vertex, - std::map& macro_pin_vertex, - std::vector& stop_flag_vec, - std::vector& visited, - std::vector>& vertices, - std::vector>& hyperedges, - bool backward_flag); + void resetSAParameters(); + Metrics* computeMetrics(odb::dbModule* module); - float computeMicronArea(odb::dbInst* inst); - void setClusterMetrics(Cluster* cluster); - void calculateConnection(); - void getHardMacros(odb::dbModule* module, - std::vector& hard_macros); + void updateMacrosOnDb(); void updateMacroOnDb(const HardMacro* hard_macro); void commitMacroPlacementToDb(); void clear(); - void printConnection(); - void printClusters(); void FDPlacement(std::vector& blocks, const std::vector& nets, float outline_width, @@ -195,57 +161,6 @@ class HierRTLMP // Multilevel Autoclustering void runMultilevelAutoclustering(); - void initPhysicalHierarchy(); - void setDefaultThresholds(); - void createIOClusters(); - void mapIOPads(); - void createDataFlow(); - void treatEachMacroAsSingleCluster(); - void resetSAParameters(); - void multilevelAutocluster(Cluster* parent); - void updateSizeThresholds(); - void printPhysicalHierarchyTree(Cluster* parent, int level); - void updateInstancesAssociation(Cluster* cluster); - void updateInstancesAssociation(odb::dbModule* module, - int cluster_id, - bool include_macro); - void breakCluster(Cluster* parent); - void createFlatCluster(odb::dbModule* module, Cluster* parent); - void createCluster(Cluster* parent); - void createCluster(odb::dbModule* module, Cluster* parent); - void addModuleInstsToCluster(Cluster* cluster, odb::dbModule* module); - void incorporateNewClusterToTree(Cluster* cluster, Cluster* parent); - void mergeClusters(std::vector& candidate_clusters); - void updateSubTree(Cluster* parent); - void breakLargeFlatCluster(Cluster* parent); - - void fetchMixedLeaves(Cluster* parent, - std::vector>& mixed_leaves); - void breakMixedLeaves(const std::vector>& mixed_leaves); - - void breakMixedLeaf(Cluster* mixed_leaf); - void mapMacroInCluster2HardMacro(Cluster* cluster); - void createOneClusterForEachMacro(Cluster* parent, - const std::vector& hard_macros, - std::vector& macro_clusters); - void classifyMacrosBySize(const std::vector& hard_macros, - std::vector& size_class); - void classifyMacrosByInterconn(const std::vector& macro_clusters, - std::vector& interconn_class); - void classifyMacrosByConnSignature( - const std::vector& macro_clusters, - std::vector& signature_class); - void groupSingleMacroClusters(const std::vector& macro_clusters, - const std::vector& size_class, - const std::vector& signature_class, - std::vector& interconn_class, - std::vector& macro_class); - void mergeMacroClustersWithinSameClass(Cluster* target, Cluster* source); - void addStdCellClusterToSubTree(Cluster* parent, - Cluster* mixed_leaf, - std::vector& virtual_conn_clusters); - void replaceByStdCellCluster(Cluster* mixed_leaf, - std::vector& virtual_conn_clusters); // Coarse Shaping void runCoarseShaping(); @@ -289,11 +204,6 @@ class HierRTLMP // Hierarchical Macro Placement 2nd stage: Macro Placement void placeMacros(Cluster* cluster); - void createClusterForEachMacro(const std::vector& hard_macros, - std::vector& sa_macros, - std::vector& macro_clusters, - std::map& cluster_to_macro, - std::set& masters); void computeFencesAndGuides(const std::vector& hard_macros, const Rect& outline, std::map& fences, @@ -324,8 +234,6 @@ class HierRTLMP std::vector& nets_old); void adjustCongestionWeight(); - static bool isIgnoredMaster(odb::dbMaster* master); - // Aux for conversion odb::Rect micronsToDbu(const Rect& micron_rect); @@ -337,6 +245,8 @@ class HierRTLMP par::PartitionMgr* tritonpart_ = nullptr; std::unique_ptr clustering_engine_; + PhysicalHierarchy tree_; + // flag variables const bool dynamic_congestion_weight_flag_ = false; // Our experiments show that for most testcases, turn off bus planning @@ -410,10 +320,6 @@ class HierRTLMP const int max_num_step_ = 2000; const int num_perturb_per_step_ = 500; - // the virtual weight between std cell part and corresponding macro part - // to force them stay together - float virtual_weight_ = 10.0; - // probability of each action float pos_swap_prob_ = 0.2; float neg_swap_prob_ = 0.2; @@ -425,84 +331,18 @@ class HierRTLMP // design-related variables float macro_with_halo_area_ = 0.0; - // dataflow parameters and store the dataflow - int max_num_ff_dist_ = 5; // maximum number of FF distances between - float dataflow_factor_ = 2.0; - float dataflow_weight_ = 1; - std::vector>>> - macro_ffs_conn_map_; - std::vector>>> - io_ffs_conn_map_; - std::vector>>> - macro_macro_conn_map_; - // statistics of the design Metrics* metrics_ = nullptr; - std::map logical_module_map_; - std::map hard_macro_map_; - - // user-specified variables - int num_bundled_IOs_ = 3; // number of bundled IOs on each boundary - int max_num_macro_base_ = 0; - int min_num_macro_base_ = 0; - int max_num_inst_base_ = 0; - int min_num_inst_base_ = 0; - - // we define the tolerance for num_insts to improve the - // robustness of the clustering engine - float tolerance_ = 0.1; - int max_num_macro_ = 0; - int min_num_macro_ = 0; - int max_num_inst_ = 0; - int min_num_inst_ = 0; - - // Multilevel support - int max_num_level_ = 2; - int level_ = 0; - float coarsening_ratio_ = 5.0; - - // minimum number of connections between two clusters - // for them to be identified as connected - int signature_net_threshold_ = 20; - int large_net_threshold_ = 100; // ignore global nets when clustering + const int bus_net_threshold_ = 32; // only for bus planning float congestion_weight_ = 0.5; // for balance timing and congestion - const float macro_dominated_cluster_threshold_ = 0.01; - // since we convert from the database unit to the micrometer // during calculation, we may loss some accuracy. const float conversion_tolerance_ = 0.01; - // Physical hierarchy tree - int cluster_id_ = 0; - // root cluster does not correspond to top design - // it's a special node in the physical hierachy tree - // Our physical hierarchy tree is as following: - // root_cluster_ (top design) - // * * * * * * - // L T M1 M2 B T (L, T, B, T are clusters for - // bundled IOs) - // * * - // M3 M4 (M1, M2, M3 and M4 are clusters for logical - // modules - Cluster* root_cluster_ = nullptr; // cluster_id = 0 for root cluster - std::map cluster_map_; // cluster_id, cluster - std::unordered_map inst_to_cluster_; // inst, id - std::unordered_map bterm_to_cluster_; // io pin, id - - // All the bundled IOs are children of root_cluster_ - // Bundled IO (Pads) - // In the bundled IO clusters, we don't store the ios in their corresponding - // clusters However, we store the instances in their corresponding clusters - // map IO pins to Pads (for designs with IO pads) - std::map io_pad_map_; - bool design_has_io_clusters_ = true; - bool design_has_only_macros_ = false; bool design_has_unfixed_macros_ = true; - PhysicalHierarchy tree_; - std::unique_ptr graphics_; }; From e07639a06233ef03df5a3b93c98b7082797b4a46 Mon Sep 17 00:00:00 2001 From: Arthur Koucher Date: Thu, 4 Jul 2024 20:07:13 -0300 Subject: [PATCH 06/30] mpl2: moving logical data extraction to clustering engine Signed-off-by: Arthur Koucher --- src/mpl2/src/clusterEngine.cpp | 144 +++++++++++++++++++++++++ src/mpl2/src/clusterEngine.h | 28 +++-- src/mpl2/src/hier_rtlmp.cpp | 190 +++++---------------------------- src/mpl2/src/hier_rtlmp.h | 19 +--- 4 files changed, 191 insertions(+), 190 deletions(-) diff --git a/src/mpl2/src/clusterEngine.cpp b/src/mpl2/src/clusterEngine.cpp index 8bc04367373..9d14b74cb2b 100644 --- a/src/mpl2/src/clusterEngine.cpp +++ b/src/mpl2/src/clusterEngine.cpp @@ -96,6 +96,150 @@ void ClusteringEngine::setTree(PhysicalHierarchy* tree) tree_ = tree; } +// Fetch the design's logical data +void ClusteringEngine::fetchDesignMetrics() +{ + design_metrics_ = computeMetrics(block_->getTopModule()); + + odb::Rect die = block_->getDieArea(); + odb::Rect core_box = block_->getCoreArea(); + + float core_lx = block_->dbuToMicrons(core_box.xMin()); + float core_ly = block_->dbuToMicrons(core_box.yMin()); + float core_ux = block_->dbuToMicrons(core_box.xMax()); + float core_uy = block_->dbuToMicrons(core_box.yMax()); + + logger_->report( + "Floorplan Outline: ({}, {}) ({}, {}), Core Outline: ({}, {}) ({}, {})", + block_->dbuToMicrons(die.xMin()), + block_->dbuToMicrons(die.yMin()), + block_->dbuToMicrons(die.xMax()), + block_->dbuToMicrons(die.yMax()), + core_lx, + core_ly, + core_ux, + core_uy); + + float core_area = (core_ux - core_lx) * (core_uy - core_ly); + float util + = (design_metrics_->getStdCellArea() + design_metrics_->getMacroArea()) / core_area; + float core_util + = design_metrics_->getStdCellArea() / (core_area - design_metrics_->getMacroArea()); + + // Check if placement is feasible in the core area when considering + // the macro halos + int unfixed_macros = 0; + for (auto inst : block_->getInsts()) { + auto master = inst->getMaster(); + if (master->isBlock()) { + const auto width + = block_->dbuToMicrons(master->getWidth()) + 2 * tree_->halo_width; + const auto height + = block_->dbuToMicrons(master->getHeight()) + 2 * tree_->halo_width; + tree_->macro_with_halo_area += width * height; + unfixed_macros += !inst->getPlacementStatus().isFixed(); + } + } + + reportLogicalHierarchyInformation(core_area, util, core_util); + + if (unfixed_macros == 0) { + tree_->has_unfixed_macros = false; + return; + } + + if (tree_->macro_with_halo_area + design_metrics_->getStdCellArea() > core_area) { + logger_->error(MPL, + 16, + "The instance area with halos {} exceeds the core area {}", + tree_->macro_with_halo_area + design_metrics_->getStdCellArea(), + core_area); + } +} + +// Traverse Logical Hierarchy +// Recursive function to collect the design metrics (number of std cells, +// area of std cells, number of macros and area of macros) in the logical +// hierarchy +Metrics* ClusteringEngine::computeMetrics(odb::dbModule* module) +{ + unsigned int num_std_cell = 0; + float std_cell_area = 0.0; + unsigned int num_macro = 0; + float macro_area = 0.0; + + for (odb::dbInst* inst : module->getInsts()) { + odb::dbMaster* master = inst->getMaster(); + + if (ClusteringEngine::isIgnoredMaster(master)) { + continue; + } + + float inst_area = computeMicronArea(inst); + + if (master->isBlock()) { // a macro + num_macro += 1; + macro_area += inst_area; + + // add hard macro to corresponding map + HardMacro* macro = new HardMacro(inst, tree_->halo_width, tree_->halo_width); + tree_->maps.inst_to_hard[inst] = macro; + } else { + num_std_cell += 1; + std_cell_area += inst_area; + } + } + + // Be careful about the relationship between + // odb::dbModule and odb::dbInst + // odb::dbModule and odb::dbModInst + // recursively traverse the hierarchical module instances + for (odb::dbModInst* inst : module->getChildren()) { + Metrics* metrics = computeMetrics(inst->getMaster()); + num_std_cell += metrics->getNumStdCell(); + std_cell_area += metrics->getStdCellArea(); + num_macro += metrics->getNumMacro(); + macro_area += metrics->getMacroArea(); + } + + Metrics* metrics + = new Metrics(num_std_cell, num_macro, std_cell_area, macro_area); + tree_->maps.module_to_metrics[module] = metrics; + + return metrics; +} + +void ClusteringEngine::reportLogicalHierarchyInformation(float core_area, + float util, + float core_util) +{ + logger_->report( + "\tNumber of std cell instances: {}\n" + "\tArea of std cell instances: {:.2f}\n" + "\tNumber of macros: {}\n" + "\tArea of macros: {:.2f}\n" + "\tHalo width: {:.2f}\n" + "\tHalo height: {:.2f}\n" + "\tArea of macros with halos: {:.2f}\n" + "\tArea of std cell instances + Area of macros: {:.2f}\n" + "\tCore area: {:.2f}\n" + "\tDesign Utilization: {:.2f}\n" + "\tCore Utilization: {:.2f}\n" + "\tManufacturing Grid: {}\n", + design_metrics_->getNumStdCell(), + design_metrics_->getStdCellArea(), + design_metrics_->getNumMacro(), + design_metrics_->getMacroArea(), + tree_->halo_width, + tree_->halo_height, + tree_->macro_with_halo_area, + design_metrics_->getStdCellArea() + design_metrics_->getMacroArea(), + core_area, + util, + core_util, + block_->getTech()->getManufacturingGrid()); +} + void ClusteringEngine::initTree() { tree_->root = new Cluster(id_, std::string("root"), logger_); diff --git a/src/mpl2/src/clusterEngine.h b/src/mpl2/src/clusterEngine.h index 74e1c4277fa..c0ff7f11b92 100644 --- a/src/mpl2/src/clusterEngine.h +++ b/src/mpl2/src/clusterEngine.h @@ -99,16 +99,20 @@ struct PhysicalHierarchy base_min_macro(0), base_max_std_cell(0), base_min_std_cell(0), - cluster_size_ratio(0.0f), - cluster_size_tolerance(0.0f), - virtual_weight(0.0f), max_level(0), bundled_ios_per_edge(0), large_net_threshold(0), min_net_count_for_connection(0), + cluster_size_ratio(0.0f), + cluster_size_tolerance(0.0f), + virtual_weight(0.0f), + halo_width(0.0f), + halo_height(0.0f), + macro_with_halo_area(0.0f), has_io_clusters(true), has_only_macros(false), - has_std_cells(true) + has_std_cells(true), + has_unfixed_macros(true) { } @@ -120,17 +124,22 @@ struct PhysicalHierarchy int base_max_std_cell; int base_min_std_cell; - float cluster_size_ratio; - float cluster_size_tolerance; - float virtual_weight; // between std cell part and macro part int max_level; int bundled_ios_per_edge; int large_net_threshold; // used to ignore global nets int min_net_count_for_connection; + float cluster_size_ratio; + float cluster_size_tolerance; + float virtual_weight; // between std cell part and macro part + + float halo_width; + float halo_height; + float macro_with_halo_area; bool has_io_clusters; bool has_only_macros; bool has_std_cells; + bool has_unfixed_macros; }; class ClusteringEngine @@ -142,6 +151,7 @@ class ClusteringEngine par::PartitionMgr* triton_part); void run(); + void fetchDesignMetrics(); void setDesignMetrics(Metrics* design_metrics); void setTree(PhysicalHierarchy* tree); @@ -169,6 +179,10 @@ class ClusteringEngine static bool isIgnoredMaster(odb::dbMaster* master); private: + Metrics* computeMetrics(odb::dbModule* module); + void reportLogicalHierarchyInformation(float core_area, + float util, + float core_util); void initTree(); void setBaseThresholds(); void createIOClusters(); diff --git a/src/mpl2/src/hier_rtlmp.cpp b/src/mpl2/src/hier_rtlmp.cpp index 7da7004991f..190577c1390 100644 --- a/src/mpl2/src/hier_rtlmp.cpp +++ b/src/mpl2/src/hier_rtlmp.cpp @@ -133,12 +133,12 @@ void HierRTLMP::setGlobalFence(float fence_lx, void HierRTLMP::setHaloWidth(float halo_width) { - halo_width_ = halo_width; + tree_.halo_width = halo_width; } void HierRTLMP::setHaloHeight(float halo_height) { - halo_height_ = halo_height; + tree_.halo_height = halo_height; } // Options related to clustering @@ -234,16 +234,12 @@ void HierRTLMP::setReportDirectory(const char* report_directory) // Attempts macro flipping to improve WR. void HierRTLMP::run() { - initMacroPlacer(); - if (!design_has_unfixed_macros_) { - logger_->info(MPL, 17, "No unfixed macros. Skipping macro placement."); - return; - } + block_ = db_->getChip()->getBlock(); runMultilevelAutoclustering(); - - if (graphics_) { - graphics_->finishedClustering(tree_.root); + if (skip_macro_placement_) { + logger_->info(MPL, 17, "Skipping macro placement."); + return; } if (!tree_.has_std_cells) { @@ -290,112 +286,28 @@ void HierRTLMP::run() // Private functions //////////////////////////////////////////////////////////////////////// -void HierRTLMP::initMacroPlacer() +// Transform the logical hierarchy into a physical hierarchy. +void HierRTLMP::runMultilevelAutoclustering() { - block_ = db_->getChip()->getBlock(); clustering_engine_ = std::make_unique( block_, network_, logger_, tritonpart_); - odb::Rect die = block_->getDieArea(); - odb::Rect core_box = block_->getCoreArea(); - - float core_lx = block_->dbuToMicrons(core_box.xMin()); - float core_ly = block_->dbuToMicrons(core_box.yMin()); - float core_ux = block_->dbuToMicrons(core_box.xMax()); - float core_uy = block_->dbuToMicrons(core_box.yMax()); - - logger_->report( - "Floorplan Outline: ({}, {}) ({}, {}), Core Outline: ({}, {}) ({}, {})", - block_->dbuToMicrons(die.xMin()), - block_->dbuToMicrons(die.yMin()), - block_->dbuToMicrons(die.xMax()), - block_->dbuToMicrons(die.yMax()), - core_lx, - core_ly, - core_ux, - core_uy); - - float core_area = (core_ux - core_lx) * (core_uy - core_ly); - - computeMetricsForModules(core_area); -} + // Set target structures + clustering_engine_->setDesignMetrics(metrics_); + clustering_engine_->setTree(&tree_); -void HierRTLMP::computeMetricsForModules(float core_area) -{ - metrics_ = computeMetrics(block_->getTopModule()); - - float util - = (metrics_->getStdCellArea() + metrics_->getMacroArea()) / core_area; - float core_util - = metrics_->getStdCellArea() / (core_area - metrics_->getMacroArea()); - - // Check if placement is feasible in the core area when considering - // the macro halos - int unfixed_macros = 0; - for (auto inst : block_->getInsts()) { - auto master = inst->getMaster(); - if (master->isBlock()) { - const auto width - = block_->dbuToMicrons(master->getWidth()) + 2 * halo_width_; - const auto height - = block_->dbuToMicrons(master->getHeight()) + 2 * halo_width_; - macro_with_halo_area_ += width * height; - unfixed_macros += !inst->getPlacementStatus().isFixed(); - } - } - reportLogicalHierarchyInformation(core_area, util, core_util); - - if (unfixed_macros == 0) { - design_has_unfixed_macros_ = false; + clustering_engine_->fetchDesignMetrics(); + if (!tree_.has_unfixed_macros) { + logger_->info(MPL, 17, "No unfixed macros."); + skip_macro_placement_ = true; return; } - if (macro_with_halo_area_ + metrics_->getStdCellArea() > core_area) { - logger_->error(MPL, - 16, - "The instance area with halos {} exceeds the core area {}", - macro_with_halo_area_ + metrics_->getStdCellArea(), - core_area); - } -} - -void HierRTLMP::reportLogicalHierarchyInformation(float core_area, - float util, - float core_util) -{ - logger_->report( - "\tNumber of std cell instances: {}\n" - "\tArea of std cell instances: {:.2f}\n" - "\tNumber of macros: {}\n" - "\tArea of macros: {:.2f}\n" - "\tHalo width: {:.2f}\n" - "\tHalo height: {:.2f}\n" - "\tArea of macros with halos: {:.2f}\n" - "\tArea of std cell instances + Area of macros: {:.2f}\n" - "\tCore area: {:.2f}\n" - "\tDesign Utilization: {:.2f}\n" - "\tCore Utilization: {:.2f}\n" - "\tManufacturing Grid: {}\n", - metrics_->getNumStdCell(), - metrics_->getStdCellArea(), - metrics_->getNumMacro(), - metrics_->getMacroArea(), - halo_width_, - halo_height_, - macro_with_halo_area_, - metrics_->getStdCellArea() + metrics_->getMacroArea(), - core_area, - util, - core_util, - block_->getTech()->getManufacturingGrid()); -} - -// Transform the logical hierarchy into a physical hierarchy. -void HierRTLMP::runMultilevelAutoclustering() -{ - clustering_engine_->setDesignMetrics(metrics_); - clustering_engine_->setTree(&tree_); clustering_engine_->run(); + + if (graphics_) { + graphics_->finishedClustering(tree_.root); + } } void HierRTLMP::resetSAParameters() @@ -465,58 +377,6 @@ void HierRTLMP::setRootShapes() tree_.root->setSoftMacro(root_soft_macro); } -// Traverse Logical Hierarchy -// Recursive function to collect the design metrics (number of std cells, -// area of std cells, number of macros and area of macros) in the logical -// hierarchy -Metrics* HierRTLMP::computeMetrics(odb::dbModule* module) -{ - unsigned int num_std_cell = 0; - float std_cell_area = 0.0; - unsigned int num_macro = 0; - float macro_area = 0.0; - - for (odb::dbInst* inst : module->getInsts()) { - odb::dbMaster* master = inst->getMaster(); - - if (ClusteringEngine::isIgnoredMaster(master)) { - continue; - } - - float inst_area = clustering_engine_->computeMicronArea(inst); - - if (master->isBlock()) { // a macro - num_macro += 1; - macro_area += inst_area; - - // add hard macro to corresponding map - HardMacro* macro = new HardMacro(inst, halo_width_, halo_height_); - tree_.maps.inst_to_hard[inst] = macro; - } else { - num_std_cell += 1; - std_cell_area += inst_area; - } - } - - // Be careful about the relationship between - // odb::dbModule and odb::dbInst - // odb::dbModule and odb::dbModInst - // recursively traverse the hierarchical module instances - for (odb::dbModInst* inst : module->getChildren()) { - Metrics* metrics = computeMetrics(inst->getMaster()); - num_std_cell += metrics->getNumStdCell(); - std_cell_area += metrics->getStdCellArea(); - num_macro += metrics->getNumMacro(); - macro_area += metrics->getMacroArea(); - } - - Metrics* metrics - = new Metrics(num_std_cell, num_macro, std_cell_area, macro_area); - tree_.maps.module_to_metrics[module] = metrics; - - return metrics; -} - // Compare two intervals according to the product static bool comparePairProduct(const std::pair& p1, const std::pair& p2) @@ -1205,7 +1065,7 @@ float HierRTLMP::computeIOBlockagesDepth(const IOSpans& io_spans) } const float macro_dominance_factor - = macro_with_halo_area_ + = tree_.macro_with_halo_area / (tree_.root->getWidth() * tree_.root->getHeight()); const float depth = (std_cell_area / sum_length) * std::pow((1 - macro_dominance_factor), 2); @@ -2266,7 +2126,7 @@ void HierRTLMP::adjustMacroBlockageWeight() MPL, "multilevel_autoclustering", 1, - "Number of levels is {}, Changing macro blockage weight from {} " + "Max level is {}, Changing macro blockage weight from {} " "to {} (half of the outline weight)", tree_.max_level, macro_blockage_weight_, @@ -2278,14 +2138,14 @@ void HierRTLMP::adjustMacroBlockageWeight() void HierRTLMP::reportSAWeights() { logger_->report("\nSimmulated Annealing Weights:\n"); - logger_->report("Area = \t{}", area_weight_); - logger_->report("Outline = \t{}", outline_weight_); - logger_->report("WL = \t{}", wirelength_weight_); + logger_->report("Area = {}", area_weight_); + logger_->report("Outline = {}", outline_weight_); + logger_->report("WL = {}", wirelength_weight_); logger_->report("Guidance = {}", guidance_weight_); logger_->report("Fence = {}", fence_weight_); logger_->report("Boundary = {}", boundary_weight_); logger_->report("Notch = {}", notch_weight_); - logger_->report("Macro Blockage = {}", macro_blockage_weight_); + logger_->report("Macro Blockage = {}\n", macro_blockage_weight_); } // Multilevel macro placement without bus planning diff --git a/src/mpl2/src/hier_rtlmp.h b/src/mpl2/src/hier_rtlmp.h index 1e2a1614d17..5b0d710acb2 100644 --- a/src/mpl2/src/hier_rtlmp.h +++ b/src/mpl2/src/hier_rtlmp.h @@ -138,28 +138,17 @@ class HierRTLMP private: using IOSpans = std::map>; - // General Hier-RTLMP flow functions - void initMacroPlacer(); - void computeMetricsForModules(float core_area); - void reportLogicalHierarchyInformation(float core_area, - float util, - float core_util); void resetSAParameters(); - - Metrics* computeMetrics(odb::dbModule* module); - void updateMacrosOnDb(); void updateMacroOnDb(const HardMacro* hard_macro); void commitMacroPlacementToDb(); void clear(); - void FDPlacement(std::vector& blocks, const std::vector& nets, float outline_width, float outline_height, const std::string& file_name); - // Multilevel Autoclustering void runMultilevelAutoclustering(); // Coarse Shaping @@ -270,9 +259,6 @@ class HierRTLMP float global_fence_ux_ = 0.0; float global_fence_uy_ = 0.0; - float halo_width_ = 0.0; - float halo_height_ = 0.0; - const int num_runs_ = 10; // number of runs for SA int num_threads_ = 10; // number of threads const int random_seed_ = 0; // random seed for deterministic @@ -328,9 +314,6 @@ class HierRTLMP float flip_prob_ = 0.4; float resize_prob_ = 0.4; - // design-related variables - float macro_with_halo_area_ = 0.0; - // statistics of the design Metrics* metrics_ = nullptr; @@ -341,7 +324,7 @@ class HierRTLMP // during calculation, we may loss some accuracy. const float conversion_tolerance_ = 0.01; - bool design_has_unfixed_macros_ = true; + bool skip_macro_placement_ = false; std::unique_ptr graphics_; }; From 11b42eed13fb5710837d6c2a406d9630bcd0d57d Mon Sep 17 00:00:00 2001 From: Arthur Koucher Date: Thu, 4 Jul 2024 20:12:34 -0300 Subject: [PATCH 07/30] mpl2: fix message id and move block initialization from run to public init Signed-off-by: Arthur Koucher --- src/mpl2/src/hier_rtlmp.cpp | 9 ++++++--- src/mpl2/src/hier_rtlmp.h | 1 + src/mpl2/src/rtl_mp.cpp | 2 ++ 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/mpl2/src/hier_rtlmp.cpp b/src/mpl2/src/hier_rtlmp.cpp index 190577c1390..07eb2d0b471 100644 --- a/src/mpl2/src/hier_rtlmp.cpp +++ b/src/mpl2/src/hier_rtlmp.cpp @@ -234,11 +234,9 @@ void HierRTLMP::setReportDirectory(const char* report_directory) // Attempts macro flipping to improve WR. void HierRTLMP::run() { - block_ = db_->getChip()->getBlock(); - runMultilevelAutoclustering(); if (skip_macro_placement_) { - logger_->info(MPL, 17, "Skipping macro placement."); + logger_->info(MPL, 13, "Skipping macro placement."); return; } @@ -282,6 +280,11 @@ void HierRTLMP::run() clear(); } +void HierRTLMP::init() +{ + block_ = db_->getChip()->getBlock(); +} + //////////////////////////////////////////////////////////////////////// // Private functions //////////////////////////////////////////////////////////////////////// diff --git a/src/mpl2/src/hier_rtlmp.h b/src/mpl2/src/hier_rtlmp.h index 5b0d710acb2..7d9578af13e 100644 --- a/src/mpl2/src/hier_rtlmp.h +++ b/src/mpl2/src/hier_rtlmp.h @@ -89,6 +89,7 @@ class HierRTLMP par::PartitionMgr* tritonpart); ~HierRTLMP(); + void init(); void run(); // Interfaces functions for setting options diff --git a/src/mpl2/src/rtl_mp.cpp b/src/mpl2/src/rtl_mp.cpp index 2868b8c9231..ab90ebfc487 100644 --- a/src/mpl2/src/rtl_mp.cpp +++ b/src/mpl2/src/rtl_mp.cpp @@ -121,6 +121,8 @@ bool MacroPlacer2::place(const int num_threads, hier_rtlmp_->setBusPlanningOn(bus_planning_on); hier_rtlmp_->setReportDirectory(report_directory); hier_rtlmp_->setNumThreads(num_threads); + + hier_rtlmp_->init(); hier_rtlmp_->run(); return true; From c772db5667e7c6704fba30b10e37435bb257377b Mon Sep 17 00:00:00 2001 From: Arthur Koucher Date: Thu, 4 Jul 2024 20:20:04 -0300 Subject: [PATCH 08/30] mpl2: make aux methods private Signed-off-by: Arthur Koucher --- src/mpl2/src/clusterEngine.h | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/mpl2/src/clusterEngine.h b/src/mpl2/src/clusterEngine.h index c0ff7f11b92..fc33931e402 100644 --- a/src/mpl2/src/clusterEngine.h +++ b/src/mpl2/src/clusterEngine.h @@ -172,12 +172,6 @@ class ClusteringEngine std::map& cluster_to_macro, std::set& masters); - // Keep this auxiliary method here temporarily until - // we remove the micron code. - float computeMicronArea(odb::dbInst* inst); - - static bool isIgnoredMaster(odb::dbMaster* master); - private: Metrics* computeMetrics(odb::dbModule* module); void reportLogicalHierarchyInformation(float core_area, @@ -256,6 +250,9 @@ class ClusteringEngine bool backward_search); void printPhysicalHierarchyTree(Cluster* parent, int level); + float computeMicronArea(odb::dbInst* inst); + + static bool isIgnoredMaster(odb::dbMaster* master); odb::dbBlock* block_; sta::dbNetwork* network_; From e3d00d1aede44fda7769060c937726abdcf342f1 Mon Sep 17 00:00:00 2001 From: Arthur Koucher Date: Thu, 4 Jul 2024 20:27:05 -0300 Subject: [PATCH 09/30] mpl2: top-level function cleanup Signed-off-by: Arthur Koucher --- src/mpl2/src/hier_rtlmp.cpp | 45 ++++++++++++++++++++----------------- src/mpl2/src/hier_rtlmp.h | 6 +++-- 2 files changed, 29 insertions(+), 22 deletions(-) diff --git a/src/mpl2/src/hier_rtlmp.cpp b/src/mpl2/src/hier_rtlmp.cpp index 07eb2d0b471..01415c5dafe 100644 --- a/src/mpl2/src/hier_rtlmp.cpp +++ b/src/mpl2/src/hier_rtlmp.cpp @@ -245,26 +245,7 @@ void HierRTLMP::run() } runCoarseShaping(); - - if (graphics_) { - graphics_->startFine(); - } - adjustMacroBlockageWeight(); - if (logger_->debugCheck(MPL, "hierarchical_macro_placement", 1)) { - reportSAWeights(); - } - - if (bus_planning_on_) { - adjustCongestionWeight(); - runHierarchicalMacroPlacement(tree_.root); - } else { - runHierarchicalMacroPlacementWithoutBusPlanning(tree_.root); - } - - if (graphics_) { - graphics_->setMaxLevel(tree_.max_level); - graphics_->drawResult(); - } + runHierarchicalMacroPlacement(); Pusher pusher(logger_, tree_.root, block_, boundary_to_io_blockage_); pusher.pushMacrosToCoreBoundaries(); @@ -313,6 +294,30 @@ void HierRTLMP::runMultilevelAutoclustering() } } +void HierRTLMP::runHierarchicalMacroPlacement() +{ + if (graphics_) { + graphics_->startFine(); + } + + adjustMacroBlockageWeight(); + if (logger_->debugCheck(MPL, "hierarchical_macro_placement", 1)) { + reportSAWeights(); + } + + if (bus_planning_on_) { + adjustCongestionWeight(); + runHierarchicalMacroPlacement(tree_.root); + } else { + runHierarchicalMacroPlacementWithoutBusPlanning(tree_.root); + } + + if (graphics_) { + graphics_->setMaxLevel(tree_.max_level); + graphics_->drawResult(); + } +} + void HierRTLMP::resetSAParameters() { pos_swap_prob_ = 0.2; diff --git a/src/mpl2/src/hier_rtlmp.h b/src/mpl2/src/hier_rtlmp.h index 7d9578af13e..9b9d7c9e61f 100644 --- a/src/mpl2/src/hier_rtlmp.h +++ b/src/mpl2/src/hier_rtlmp.h @@ -139,7 +139,11 @@ class HierRTLMP private: using IOSpans = std::map>; + void runMultilevelAutoclustering(); + void runHierarchicalMacroPlacement(); + void resetSAParameters(); + void updateMacrosOnDb(); void updateMacroOnDb(const HardMacro* hard_macro); void commitMacroPlacementToDb(); @@ -150,8 +154,6 @@ class HierRTLMP float outline_height, const std::string& file_name); - void runMultilevelAutoclustering(); - // Coarse Shaping void runCoarseShaping(); void setRootShapes(); From 3384b08455348352e755261268226dc5efd13174 Mon Sep 17 00:00:00 2001 From: Arthur Koucher Date: Fri, 5 Jul 2024 15:50:49 -0300 Subject: [PATCH 10/30] mpl2: fix virtual weight Signed-off-by: Arthur Koucher --- src/mpl2/src/clusterEngine.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/mpl2/src/clusterEngine.h b/src/mpl2/src/clusterEngine.h index fc33931e402..ce7811d6483 100644 --- a/src/mpl2/src/clusterEngine.h +++ b/src/mpl2/src/clusterEngine.h @@ -105,7 +105,6 @@ struct PhysicalHierarchy min_net_count_for_connection(0), cluster_size_ratio(0.0f), cluster_size_tolerance(0.0f), - virtual_weight(0.0f), halo_width(0.0f), halo_height(0.0f), macro_with_halo_area(0.0f), @@ -130,7 +129,7 @@ struct PhysicalHierarchy int min_net_count_for_connection; float cluster_size_ratio; float cluster_size_tolerance; - float virtual_weight; // between std cell part and macro part + const float virtual_weight = 10.0f;; // between std cell part and macro part float halo_width; float halo_height; From 998b07a40ed7b72d69d1943734a645d93b3c3e89 Mon Sep 17 00:00:00 2001 From: Arthur Koucher Date: Fri, 5 Jul 2024 16:01:46 -0300 Subject: [PATCH 11/30] mpl2: comment Signed-off-by: Arthur Koucher --- src/mpl2/src/clusterEngine.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/mpl2/src/clusterEngine.h b/src/mpl2/src/clusterEngine.h index ce7811d6483..30afd632ea3 100644 --- a/src/mpl2/src/clusterEngine.h +++ b/src/mpl2/src/clusterEngine.h @@ -129,7 +129,11 @@ struct PhysicalHierarchy int min_net_count_for_connection; float cluster_size_ratio; float cluster_size_tolerance; - const float virtual_weight = 10.0f;; // between std cell part and macro part + + // Virtual connection weight between each macro cluster + // and its corresponding standard-cell cluster to bias + // the macro placer to place them together. + const float virtual_weight = 10.0f; float halo_width; float halo_height; From 200f8b7b78231db386ae80d6bb470b8f57520b52 Mon Sep 17 00:00:00 2001 From: Arthur Koucher Date: Fri, 5 Jul 2024 16:02:57 -0300 Subject: [PATCH 12/30] mpl2: format Signed-off-by: Arthur Koucher --- src/mpl2/src/clusterEngine.cpp | 99 ++++++++++++++++++---------------- src/mpl2/src/clusterEngine.h | 6 +-- src/mpl2/src/hier_rtlmp.cpp | 38 +++++++------ src/mpl2/src/hier_rtlmp.h | 2 +- 4 files changed, 77 insertions(+), 68 deletions(-) diff --git a/src/mpl2/src/clusterEngine.cpp b/src/mpl2/src/clusterEngine.cpp index 9d14b74cb2b..d6e6ec68dad 100644 --- a/src/mpl2/src/clusterEngine.cpp +++ b/src/mpl2/src/clusterEngine.cpp @@ -37,8 +37,8 @@ #include "db_sta/dbNetwork.hh" #include "object.h" -#include "sta/Liberty.hh" #include "par/PartitionMgr.h" +#include "sta/Liberty.hh" namespace mpl2 { using utl::MPL; @@ -122,9 +122,10 @@ void ClusteringEngine::fetchDesignMetrics() float core_area = (core_ux - core_lx) * (core_uy - core_ly); float util - = (design_metrics_->getStdCellArea() + design_metrics_->getMacroArea()) / core_area; - float core_util - = design_metrics_->getStdCellArea() / (core_area - design_metrics_->getMacroArea()); + = (design_metrics_->getStdCellArea() + design_metrics_->getMacroArea()) + / core_area; + float core_util = design_metrics_->getStdCellArea() + / (core_area - design_metrics_->getMacroArea()); // Check if placement is feasible in the core area when considering // the macro halos @@ -148,12 +149,14 @@ void ClusteringEngine::fetchDesignMetrics() return; } - if (tree_->macro_with_halo_area + design_metrics_->getStdCellArea() > core_area) { - logger_->error(MPL, - 16, - "The instance area with halos {} exceeds the core area {}", - tree_->macro_with_halo_area + design_metrics_->getStdCellArea(), - core_area); + if (tree_->macro_with_halo_area + design_metrics_->getStdCellArea() + > core_area) { + logger_->error( + MPL, + 16, + "The instance area with halos {} exceeds the core area {}", + tree_->macro_with_halo_area + design_metrics_->getStdCellArea(), + core_area); } } @@ -182,7 +185,8 @@ Metrics* ClusteringEngine::computeMetrics(odb::dbModule* module) macro_area += inst_area; // add hard macro to corresponding map - HardMacro* macro = new HardMacro(inst, tree_->halo_width, tree_->halo_width); + HardMacro* macro + = new HardMacro(inst, tree_->halo_width, tree_->halo_width); tree_->maps.inst_to_hard[inst] = macro; } else { num_std_cell += 1; @@ -210,8 +214,8 @@ Metrics* ClusteringEngine::computeMetrics(odb::dbModule* module) } void ClusteringEngine::reportLogicalHierarchyInformation(float core_area, - float util, - float core_util) + float util, + float core_util) { logger_->report( "\tNumber of std cell instances: {}\n" @@ -256,10 +260,8 @@ void ClusteringEngine::initTree() void ClusteringEngine::setBaseThresholds() { - if (tree_->base_max_macro <= 0 - || tree_->base_min_macro <= 0 - || tree_->base_max_std_cell <= 0 - || tree_->base_min_std_cell <= 0) { + if (tree_->base_max_macro <= 0 || tree_->base_min_macro <= 0 + || tree_->base_max_std_cell <= 0 || tree_->base_min_std_cell <= 0) { // Set base values for std cell lower/upper thresholds const int min_num_std_cells_allowed = 1000; tree_->base_min_std_cell @@ -1019,10 +1021,9 @@ void ClusteringEngine::updateInstancesAssociation(Cluster* cluster) // Unlike macros, std cells are always considered when when updating // the inst -> cluster map with the data from a module. -void ClusteringEngine::updateInstancesAssociation( - odb::dbModule* module, - int cluster_id, - bool include_macro) +void ClusteringEngine::updateInstancesAssociation(odb::dbModule* module, + int cluster_id, + bool include_macro) { if (include_macro) { for (odb::dbInst* inst : module->getInsts()) { @@ -1100,7 +1101,8 @@ void ClusteringEngine::multilevelAutocluster(Cluster* parent) bool force_split_root = false; if (level_ == 0) { const int leaf_max_std_cell - = tree_->base_max_std_cell / std::pow(tree_->cluster_size_ratio, tree_->max_level - 1) + = tree_->base_max_std_cell + / std::pow(tree_->cluster_size_ratio, tree_->max_level - 1) * (1 + size_tolerance_); if (parent->getNumStdCell() < leaf_max_std_cell) { force_split_root = true; @@ -1131,8 +1133,7 @@ void ClusteringEngine::multilevelAutocluster(Cluster* parent) level_++; updateSizeThresholds(); - if (force_split_root - || (parent->getNumStdCell() > max_std_cell_)) { + if (force_split_root || (parent->getNumStdCell() > max_std_cell_)) { breakCluster(parent); updateSubTree(parent); @@ -1159,10 +1160,9 @@ void ClusteringEngine::multilevelAutocluster(Cluster* parent) void ClusteringEngine::updateSizeThresholds() { - const double coarse_factor - = std::pow(tree_->cluster_size_ratio, level_ - 1); + const double coarse_factor = std::pow(tree_->cluster_size_ratio, level_ - 1); - // A high cluster size ratio per level helps the + // A high cluster size ratio per level helps the // clustering process converge fast max_macro_ = tree_->base_max_macro / coarse_factor; min_macro_ = tree_->base_min_macro / coarse_factor; @@ -1278,7 +1278,8 @@ void ClusteringEngine::createFlatCluster(odb::dbModule* module, Cluster* parent) } } -void ClusteringEngine::addModuleInstsToCluster(Cluster* cluster, odb::dbModule* module) +void ClusteringEngine::addModuleInstsToCluster(Cluster* cluster, + odb::dbModule* module) { for (odb::dbInst* inst : module->getInsts()) { odb::dbMaster* master = inst->getMaster(); @@ -1454,11 +1455,11 @@ void ClusteringEngine::breakLargeFlatCluster(Cluster* parent) std::vector part = triton_part_->PartitionKWaySimpleMode(num_parts, - balance_constraint, - seed, - hyperedges, - vertex_weight, - hyperedge_weights); + balance_constraint, + seed, + hyperedges, + vertex_weight, + hyperedge_weights); parent->clearLeafStdCells(); parent->clearLeafMacros(); @@ -1550,8 +1551,10 @@ void ClusteringEngine::mergeClusters(std::vector& candidate_clusters) 1, "Candidate cluster: {} - {}", candidate_clusters[i]->getName(), - (cluster_id != -1 ? tree_->maps.id_to_cluster[cluster_id]->getName() : " ")); - if (cluster_id != -1 && !tree_->maps.id_to_cluster[cluster_id]->isIOCluster()) { + (cluster_id != -1 ? tree_->maps.id_to_cluster[cluster_id]->getName() + : " ")); + if (cluster_id != -1 + && !tree_->maps.id_to_cluster[cluster_id]->isIOCluster()) { Cluster*& cluster = tree_->maps.id_to_cluster[cluster_id]; bool delete_flag = false; if (cluster->mergeCluster(*candidate_clusters[i], delete_flag)) { @@ -1909,8 +1912,7 @@ void ClusteringEngine::createOneClusterForEachMacro( { for (auto& hard_macro : hard_macros) { std::string cluster_name = hard_macro->getName(); - Cluster* single_macro_cluster - = new Cluster(id_, cluster_name, logger_); + Cluster* single_macro_cluster = new Cluster(id_, cluster_name, logger_); single_macro_cluster->addLeafMacro(hard_macro->getInst()); incorporateNewCluster(single_macro_cluster, parent); @@ -1918,8 +1920,9 @@ void ClusteringEngine::createOneClusterForEachMacro( } } -void ClusteringEngine::classifyMacrosBySize(const std::vector& hard_macros, - std::vector& size_class) +void ClusteringEngine::classifyMacrosBySize( + const std::vector& hard_macros, + std::vector& size_class) { for (int i = 0; i < hard_macros.size(); i++) { if (size_class[i] == -1) { @@ -1948,8 +1951,8 @@ void ClusteringEngine::classifyMacrosByConnSignature( continue; } - if (macro_clusters[i]->isSameConnSignature(*macro_clusters[j], - tree_->min_net_count_for_connection)) { + if (macro_clusters[i]->isSameConnSignature( + *macro_clusters[j], tree_->min_net_count_for_connection)) { signature_class[j] = i; } } @@ -1961,7 +1964,9 @@ void ClusteringEngine::classifyMacrosByConnSignature( for (auto& cluster : macro_clusters) { logger_->report("Macro Signature: {}", cluster->getName()); for (auto& [cluster_id, weight] : cluster->getConnection()) { - logger_->report(" {} {} ", tree_->maps.id_to_cluster[cluster_id]->getName(), weight); + logger_->report(" {} {} ", + tree_->maps.id_to_cluster[cluster_id]->getName(), + weight); } } } @@ -2052,7 +2057,7 @@ void ClusteringEngine::groupSingleMacroClusters( } void ClusteringEngine::mergeMacroClustersWithinSameClass(Cluster* target, - Cluster* source) + Cluster* source) { bool delete_merged = false; target->mergeCluster(*source, delete_merged); @@ -2070,8 +2075,7 @@ void ClusteringEngine::addStdCellClusterToSubTree( std::vector& virtual_conn_clusters) { std::string std_cell_cluster_name = mixed_leaf->getName(); - Cluster* std_cell_cluster - = new Cluster(id_, std_cell_cluster_name, logger_); + Cluster* std_cell_cluster = new Cluster(id_, std_cell_cluster_name, logger_); std_cell_cluster->copyInstances(*mixed_leaf); std_cell_cluster->clearLeafMacros(); @@ -2088,8 +2092,9 @@ void ClusteringEngine::addStdCellClusterToSubTree( } // We don't modify the physical hierarchy when spliting by replacement -void ClusteringEngine::replaceByStdCellCluster(Cluster* mixed_leaf, - std::vector& virtual_conn_clusters) +void ClusteringEngine::replaceByStdCellCluster( + Cluster* mixed_leaf, + std::vector& virtual_conn_clusters) { mixed_leaf->clearLeafMacros(); mixed_leaf->setClusterType(StdCellCluster); diff --git a/src/mpl2/src/clusterEngine.h b/src/mpl2/src/clusterEngine.h index 30afd632ea3..86c9cf2aa11 100644 --- a/src/mpl2/src/clusterEngine.h +++ b/src/mpl2/src/clusterEngine.h @@ -131,7 +131,7 @@ struct PhysicalHierarchy float cluster_size_tolerance; // Virtual connection weight between each macro cluster - // and its corresponding standard-cell cluster to bias + // and its corresponding standard-cell cluster to bias // the macro placer to place them together. const float virtual_weight = 10.0f; @@ -265,8 +265,8 @@ class ClusteringEngine Metrics* design_metrics_; PhysicalHierarchy* tree_; - int level_; // Current level - int id_; // Current "highest" id + int level_; // Current level + int id_; // Current "highest" id // Size limits of the current level int max_macro_; diff --git a/src/mpl2/src/hier_rtlmp.cpp b/src/mpl2/src/hier_rtlmp.cpp index 01415c5dafe..cdee9b82fa7 100644 --- a/src/mpl2/src/hier_rtlmp.cpp +++ b/src/mpl2/src/hier_rtlmp.cpp @@ -288,7 +288,7 @@ void HierRTLMP::runMultilevelAutoclustering() } clustering_engine_->run(); - + if (graphics_) { graphics_->finishedClustering(tree_.root); } @@ -479,8 +479,7 @@ void HierRTLMP::calculateChildrenTilings(Cluster* parent) const float action_sum = pos_swap_prob_ + neg_swap_prob_ + double_swap_prob_ + exchange_swap_prob_ + resize_prob_; - const Rect outline( - 0, 0, tree_.root->getWidth(), tree_.root->getHeight()); + const Rect outline(0, 0, tree_.root->getWidth(), tree_.root->getHeight()); const int num_perturb_per_step = (macros.size() > num_perturb_per_step_ / 10) ? macros.size() @@ -710,8 +709,7 @@ void HierRTLMP::calculateMacroTilings(Cluster* cluster) const float action_sum = pos_swap_prob_ + neg_swap_prob_ + double_swap_prob_ + exchange_swap_prob_; - const Rect outline( - 0, 0, tree_.root->getWidth(), tree_.root->getHeight()); + const Rect outline(0, 0, tree_.root->getWidth(), tree_.root->getHeight()); // update macros std::vector macros; @@ -1248,7 +1246,8 @@ void HierRTLMP::runHierarchicalMacroPlacement(Cluster* parent) // for other clusters soft_macro_id_map[cluster->getName()] = macros.size(); SoftMacro* soft_macro = new SoftMacro(cluster); - clustering_engine_->updateInstancesAssociation(cluster); // we need this step to calculate nets + clustering_engine_->updateInstancesAssociation( + cluster); // we need this step to calculate nets macros.push_back(*soft_macro); cluster->setSoftMacro(soft_macro); // merge fences and guides for hard macros within cluster @@ -1320,9 +1319,10 @@ void HierRTLMP::runHierarchicalMacroPlacement(Cluster* parent) // add the virtual connections (the weight related to IOs and macros belong to // the same cluster) for (const auto& [cluster1, cluster2] : parent->getVirtualConnections()) { - BundledNet net(soft_macro_id_map[tree_.maps.id_to_cluster[cluster1]->getName()], - soft_macro_id_map[tree_.maps.id_to_cluster[cluster2]->getName()], - tree_.virtual_weight); + BundledNet net( + soft_macro_id_map[tree_.maps.id_to_cluster[cluster1]->getName()], + soft_macro_id_map[tree_.maps.id_to_cluster[cluster2]->getName()], + tree_.virtual_weight); net.src_cluster_id = cluster1; net.target_cluster_id = cluster2; nets.push_back(net); @@ -2234,7 +2234,8 @@ void HierRTLMP::runHierarchicalMacroPlacementWithoutBusPlanning(Cluster* parent) // for other clusters soft_macro_id_map[cluster->getName()] = macros.size(); SoftMacro* soft_macro = new SoftMacro(cluster); - clustering_engine_->updateInstancesAssociation(cluster); // we need this step to calculate nets + clustering_engine_->updateInstancesAssociation( + cluster); // we need this step to calculate nets macros.push_back(*soft_macro); cluster->setSoftMacro(soft_macro); // merge fences and guides for hard macros within cluster @@ -2339,9 +2340,10 @@ void HierRTLMP::runHierarchicalMacroPlacementWithoutBusPlanning(Cluster* parent) // add the virtual connections (the weight related to IOs and macros belong to // the same cluster) for (const auto& [cluster1, cluster2] : parent->getVirtualConnections()) { - BundledNet net(soft_macro_id_map[tree_.maps.id_to_cluster[cluster1]->getName()], - soft_macro_id_map[tree_.maps.id_to_cluster[cluster2]->getName()], - tree_.virtual_weight); + BundledNet net( + soft_macro_id_map[tree_.maps.id_to_cluster[cluster1]->getName()], + soft_macro_id_map[tree_.maps.id_to_cluster[cluster2]->getName()], + tree_.virtual_weight); net.src_cluster_id = cluster1; net.target_cluster_id = cluster2; nets.push_back(net); @@ -2735,7 +2737,8 @@ void HierRTLMP::runEnhancedHierarchicalMacroPlacement(Cluster* parent) // for other clusters soft_macro_id_map[cluster->getName()] = macros.size(); SoftMacro* soft_macro = new SoftMacro(cluster); - clustering_engine_->updateInstancesAssociation(cluster); // we need this step to calculate nets + clustering_engine_->updateInstancesAssociation( + cluster); // we need this step to calculate nets macros.push_back(*soft_macro); cluster->setSoftMacro(soft_macro); // merge fences and guides for hard macros within cluster @@ -2839,9 +2842,10 @@ void HierRTLMP::runEnhancedHierarchicalMacroPlacement(Cluster* parent) // add the virtual connections (the weight related to IOs and macros belong to // the same cluster) for (const auto& [cluster1, cluster2] : parent->getVirtualConnections()) { - BundledNet net(soft_macro_id_map[tree_.maps.id_to_cluster[cluster1]->getName()], - soft_macro_id_map[tree_.maps.id_to_cluster[cluster2]->getName()], - tree_.virtual_weight); + BundledNet net( + soft_macro_id_map[tree_.maps.id_to_cluster[cluster1]->getName()], + soft_macro_id_map[tree_.maps.id_to_cluster[cluster2]->getName()], + tree_.virtual_weight); net.src_cluster_id = cluster1; net.target_cluster_id = cluster2; nets.push_back(net); diff --git a/src/mpl2/src/hier_rtlmp.h b/src/mpl2/src/hier_rtlmp.h index 9b9d7c9e61f..5ba53761b30 100644 --- a/src/mpl2/src/hier_rtlmp.h +++ b/src/mpl2/src/hier_rtlmp.h @@ -142,7 +142,7 @@ class HierRTLMP void runMultilevelAutoclustering(); void runHierarchicalMacroPlacement(); - void resetSAParameters(); + void resetSAParameters(); void updateMacrosOnDb(); void updateMacroOnDb(const HardMacro* hard_macro); From 9e79d4de51f7f03bb50d15265cc3b5010f75785c Mon Sep 17 00:00:00 2001 From: Arthur Koucher Date: Fri, 5 Jul 2024 16:59:01 -0300 Subject: [PATCH 13/30] mpl2: fix macro dominated ratio Signed-off-by: Arthur Koucher --- src/mpl2/src/clusterEngine.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mpl2/src/clusterEngine.cpp b/src/mpl2/src/clusterEngine.cpp index d6e6ec68dad..b1cffe72797 100644 --- a/src/mpl2/src/clusterEngine.cpp +++ b/src/mpl2/src/clusterEngine.cpp @@ -1793,7 +1793,7 @@ void ClusteringEngine::breakMixedLeaves( void ClusteringEngine::breakMixedLeaf(Cluster* mixed_leaf) { Cluster* parent = mixed_leaf; - const float macro_dominated_cluster_ratio = 0.1; + const float macro_dominated_cluster_ratio = 0.01; // Split by replacement if macro dominated. if (mixed_leaf->getNumStdCell() * macro_dominated_cluster_ratio From 48445bc5b9dfd634cb906cea5650699ca83fe219 Mon Sep 17 00:00:00 2001 From: Arthur Koucher Date: Fri, 5 Jul 2024 19:03:12 -0300 Subject: [PATCH 14/30] mpl2: change debug groups for weight adjusting methods Signed-off-by: Arthur Koucher --- src/mpl2/src/hier_rtlmp.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/mpl2/src/hier_rtlmp.cpp b/src/mpl2/src/hier_rtlmp.cpp index cdee9b82fa7..3a72099fc6a 100644 --- a/src/mpl2/src/hier_rtlmp.cpp +++ b/src/mpl2/src/hier_rtlmp.cpp @@ -1144,7 +1144,7 @@ void HierRTLMP::adjustCongestionWeight() } debugPrint(logger_, MPL, - "hierarchical_macro_placement", + "bus_planning", 1, "Adjusting congestion weight to {} - Snap layer is {}", congestion_weight_, @@ -2132,9 +2132,9 @@ void HierRTLMP::adjustMacroBlockageWeight() float new_macro_blockage_weight = outline_weight_ / 2.0; debugPrint(logger_, MPL, - "multilevel_autoclustering", + "hierarchical_macro_placement", 1, - "Max level is {}, Changing macro blockage weight from {} " + "Tree max level is {}, Changing macro blockage weight from {} " "to {} (half of the outline weight)", tree_.max_level, macro_blockage_weight_, From 7c6ad6b4281992da608920d5669d08dea385f7a3 Mon Sep 17 00:00:00 2001 From: Arthur Koucher Date: Sat, 6 Jul 2024 12:06:57 -0300 Subject: [PATCH 15/30] mpl2: modernize struct initialization Signed-off-by: Arthur Koucher --- src/mpl2/src/clusterEngine.h | 62 ++++++++++++------------------------ 1 file changed, 20 insertions(+), 42 deletions(-) diff --git a/src/mpl2/src/clusterEngine.h b/src/mpl2/src/clusterEngine.h index 86c9cf2aa11..f8af606ccda 100644 --- a/src/mpl2/src/clusterEngine.h +++ b/src/mpl2/src/clusterEngine.h @@ -93,56 +93,34 @@ struct PhysicalHierarchyMaps struct PhysicalHierarchy { - PhysicalHierarchy() - : root(nullptr), - base_max_macro(0), - base_min_macro(0), - base_max_std_cell(0), - base_min_std_cell(0), - max_level(0), - bundled_ios_per_edge(0), - large_net_threshold(0), - min_net_count_for_connection(0), - cluster_size_ratio(0.0f), - cluster_size_tolerance(0.0f), - halo_width(0.0f), - halo_height(0.0f), - macro_with_halo_area(0.0f), - has_io_clusters(true), - has_only_macros(false), - has_std_cells(true), - has_unfixed_macros(true) - { - } - - Cluster* root; + Cluster* root{nullptr}; PhysicalHierarchyMaps maps; - int base_max_macro; - int base_min_macro; - int base_max_std_cell; - int base_min_std_cell; + float halo_width{0.0f}; + float halo_height{0.0f}; + float macro_with_halo_area{0.0f}; - int max_level; - int bundled_ios_per_edge; - int large_net_threshold; // used to ignore global nets - int min_net_count_for_connection; - float cluster_size_ratio; - float cluster_size_tolerance; + bool has_io_clusters{true}; + bool has_only_macros{false}; + bool has_std_cells{true}; + bool has_unfixed_macros{true}; + + int base_max_macro{0}; + int base_min_macro{0}; + int base_max_std_cell{0}; + int base_min_std_cell{0}; + + int max_level{0}; + int bundled_ios_per_edge{0}; + int large_net_threshold{0}; // used to ignore global nets + int min_net_count_for_connection{0}; + float cluster_size_ratio{0.0f}; + float cluster_size_tolerance{0.0f}; // Virtual connection weight between each macro cluster // and its corresponding standard-cell cluster to bias // the macro placer to place them together. const float virtual_weight = 10.0f; - - float halo_width; - float halo_height; - float macro_with_halo_area; - - bool has_io_clusters; - bool has_only_macros; - bool has_std_cells; - bool has_unfixed_macros; }; class ClusteringEngine From b5bd9687d53173bd72ad46e6477bad08a7aaaa87 Mon Sep 17 00:00:00 2001 From: Arthur Koucher Date: Sat, 6 Jul 2024 12:13:16 -0300 Subject: [PATCH 16/30] mpl2: remove unused variable Signed-off-by: Arthur Koucher --- src/mpl2/src/hier_rtlmp.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/mpl2/src/hier_rtlmp.cpp b/src/mpl2/src/hier_rtlmp.cpp index 3a72099fc6a..db85059b4f9 100644 --- a/src/mpl2/src/hier_rtlmp.cpp +++ b/src/mpl2/src/hier_rtlmp.cpp @@ -1123,11 +1123,9 @@ void HierRTLMP::adjustCongestionWeight() // update weight if (dynamic_congestion_weight_flag_) { std::vector layers; - int tot_num_layer = 0; for (odb::dbTechLayer* layer : db_->getTech()->getLayers()) { if (layer->getType() == odb::dbTechLayerType::ROUTING) { layers.push_back(layer->getName()); - tot_num_layer++; } } snap_layer_ = 0; From 41d6bf52c7c816a071c36162d9c13e60ba056d2c Mon Sep 17 00:00:00 2001 From: Arthur Koucher Date: Mon, 8 Jul 2024 11:29:05 -0300 Subject: [PATCH 17/30] mpl2: update ok with new messages Signed-off-by: Arthur Koucher --- src/mpl2/test/no_unfixed_macros.ok | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/mpl2/test/no_unfixed_macros.ok b/src/mpl2/test/no_unfixed_macros.ok index 940c23b4a49..a1b144a4d5e 100644 --- a/src/mpl2/test/no_unfixed_macros.ok +++ b/src/mpl2/test/no_unfixed_macros.ok @@ -18,5 +18,6 @@ Floorplan Outline: (0.0, 0.0) (450.0, 450.0), Core Outline: (4.94, 4.2) (444.98 Core Utilization: 0.00 Manufacturing Grid: 10 -[INFO MPL-0017] No unfixed macros. Skipping macro placement. +[INFO MPL-0017] No unfixed macros. +[INFO MPL-0013] Skipping macro placement. No differences found. From 458dfe61058b02161b00e39b726685e73826d796 Mon Sep 17 00:00:00 2001 From: Arthur Koucher Date: Mon, 8 Jul 2024 16:06:47 -0300 Subject: [PATCH 18/30] mpl2: ensure SA parameters are reset (fix macro_only results divergence) Signed-off-by: Arthur Koucher --- src/mpl2/src/clusterEngine.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/mpl2/src/clusterEngine.cpp b/src/mpl2/src/clusterEngine.cpp index b1cffe72797..ae5275c4d71 100644 --- a/src/mpl2/src/clusterEngine.cpp +++ b/src/mpl2/src/clusterEngine.cpp @@ -66,6 +66,7 @@ void ClusteringEngine::run() if (design_metrics_->getNumStdCell() == 0) { logger_->warn(MPL, 25, "Design has no standard cells!"); + tree_->has_std_cells = false; treatEachMacroAsSingleCluster(); } else { multilevelAutocluster(tree_->root); From 8cc0d3b36c96927052884db1faab8b6148a22a10 Mon Sep 17 00:00:00 2001 From: Arthur Koucher Date: Mon, 8 Jul 2024 18:10:00 -0300 Subject: [PATCH 19/30] mpl2: initialize engine variables in member definition Signed-off-by: Arthur Koucher --- src/mpl2/src/clusterEngine.cpp | 4 +--- src/mpl2/src/clusterEngine.h | 12 ++++++------ 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/mpl2/src/clusterEngine.cpp b/src/mpl2/src/clusterEngine.cpp index ae5275c4d71..92c863f1206 100644 --- a/src/mpl2/src/clusterEngine.cpp +++ b/src/mpl2/src/clusterEngine.cpp @@ -50,9 +50,7 @@ ClusteringEngine::ClusteringEngine(odb::dbBlock* block, : block_(block), network_(network), logger_(logger), - triton_part_(triton_part), - level_(0), - id_(0) + triton_part_(triton_part) { } diff --git a/src/mpl2/src/clusterEngine.h b/src/mpl2/src/clusterEngine.h index f8af606ccda..cfb5fbdd0ab 100644 --- a/src/mpl2/src/clusterEngine.h +++ b/src/mpl2/src/clusterEngine.h @@ -243,14 +243,14 @@ class ClusteringEngine Metrics* design_metrics_; PhysicalHierarchy* tree_; - int level_; // Current level - int id_; // Current "highest" id + int level_{0}; // Current level + int id_{0}; // Current "highest" id // Size limits of the current level - int max_macro_; - int min_macro_; - int max_std_cell_; - int min_std_cell_; + int max_macro_{0}; + int min_macro_{0}; + int max_std_cell_{0}; + int min_std_cell_{0}; DataFlow data_flow; From 076adb3ec6c6b2d42604ff2b4ae0e2dffac61a74 Mon Sep 17 00:00:00 2001 From: Arthur Koucher Date: Mon, 8 Jul 2024 18:30:56 -0300 Subject: [PATCH 20/30] mpl2: remove unneeded initialization Signed-off-by: Arthur Koucher --- src/mpl2/src/hier_rtlmp.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/mpl2/src/hier_rtlmp.cpp b/src/mpl2/src/hier_rtlmp.cpp index db85059b4f9..7154b14e518 100644 --- a/src/mpl2/src/hier_rtlmp.cpp +++ b/src/mpl2/src/hier_rtlmp.cpp @@ -71,8 +71,7 @@ HierRTLMP::HierRTLMP(sta::dbNetwork* network, db_(db), sta_(sta), logger_(logger), - tritonpart_(tritonpart), - clustering_engine_(nullptr) + tritonpart_(tritonpart) { } From 97341d75e17f0792c492e4ad82bdc3bc3e1d910a Mon Sep 17 00:00:00 2001 From: Arthur Koucher Date: Tue, 9 Jul 2024 13:58:50 -0300 Subject: [PATCH 21/30] mpl2: refactor dataflow creation Signed-off-by: Arthur Koucher --- src/mpl2/src/clusterEngine.cpp | 349 +++++++++++++++------------------ src/mpl2/src/clusterEngine.h | 36 ++-- 2 files changed, 184 insertions(+), 201 deletions(-) diff --git a/src/mpl2/src/clusterEngine.cpp b/src/mpl2/src/clusterEngine.cpp index 92c863f1206..d7e27d60e21 100644 --- a/src/mpl2/src/clusterEngine.cpp +++ b/src/mpl2/src/clusterEngine.cpp @@ -521,27 +521,83 @@ void ClusteringEngine::mapIOPads() // Here we model each std cell instance, IO pin and macro pin as vertices. void ClusteringEngine::createDataFlow() { - debugPrint( - logger_, MPL, "multilevel_autoclustering", 1, "Creating dataflow..."); - if (data_flow.register_dist <= 0) { - return; + // Create vertices IDs. + VerticesMaps vertices_maps; + computeIOVertices(vertices_maps); + computeStdCellVertices(vertices_maps); + computeMacroPinVertices(vertices_maps); + + const int num_of_vertices = static_cast(vertices_maps.stoppers.size()); + debugPrint(logger_, + MPL, + "multilevel_autoclustering", + 1, + "Number of vertices: {}", + num_of_vertices); + + std::vector> vertices(num_of_vertices); + std::vector> backward_vertices(num_of_vertices); + std::vector> hyperedges; // directed hypergraph + createHypergraph(vertices, backward_vertices, hyperedges); + + // Traverse hypergraph to build dataflow. + for (auto [src, src_pin] : vertices_maps.id_to_bterm) { + int idx = 0; + std::vector visited(vertices.size(), false); + std::vector> insts(data_flow.max_num_of_hops); + dataFlowDFSIOPin( + src, idx, vertices_maps, insts, visited, vertices, hyperedges, false); + dataFlowDFSIOPin(src, + idx, + vertices_maps, + insts, + visited, + backward_vertices, + hyperedges, + true); + data_flow.io_to_regs.emplace_back(src_pin, insts); } - // create vertex id property for std cell, IO pin and macro pin - std::map io_pin_vertex; - std::map std_cell_vertex; - std::map macro_pin_vertex; + for (auto [src, src_pin] : vertices_maps.id_to_macro_pin) { + int idx = 0; + std::vector visited(vertices.size(), false); + std::vector> std_cells(data_flow.max_num_of_hops); + std::vector> macros(data_flow.max_num_of_hops); + dataFlowDFSMacroPin(src, + idx, + vertices_maps, + std_cells, + macros, + visited, + vertices, + hyperedges, + false); + dataFlowDFSMacroPin(src, + idx, + vertices_maps, + std_cells, + macros, + visited, + backward_vertices, + hyperedges, + true); + data_flow.macro_pin_to_regs.emplace_back(src_pin, std_cells); + data_flow.macro_pin_to_macros.emplace_back(src_pin, macros); + } +} - std::vector stop_flag_vec; - // assign vertex_id property of each Bterm - // All boundary terms are marked as sequential stopping pts - for (odb::dbBTerm* term : block_->getBTerms()) { - odb::dbIntProperty::create(term, "vertex_id", stop_flag_vec.size()); - io_pin_vertex[stop_flag_vec.size()] = term; - stop_flag_vec.push_back(true); +void ClusteringEngine::computeIOVertices(VerticesMaps& vertices_maps) +{ + for (odb::dbBTerm* bterm : block_->getBTerms()) { + const int id = static_cast(vertices_maps.stoppers.size()); + odb::dbIntProperty::create(bterm, "vertex_id", id); + vertices_maps.id_to_bterm[id] = bterm; + vertices_maps.stoppers.push_back(true); } +} - // assign vertex_id property of each instance +void ClusteringEngine::computeStdCellVertices(VerticesMaps& vertices_maps) +{ for (auto inst : block_->getInsts()) { odb::dbMaster* master = inst->getMaster(); if (isIgnoredMaster(master) || master->isBlock()) { @@ -553,79 +609,75 @@ void ClusteringEngine::createDataFlow() continue; } - // Mark registers - odb::dbIntProperty::create(inst, "vertex_id", stop_flag_vec.size()); - std_cell_vertex[stop_flag_vec.size()] = inst; + const int id = static_cast(vertices_maps.stoppers.size()); + + // Registers are stoppers. + odb::dbIntProperty::create(inst, "vertex_id", id); + vertices_maps.id_to_std_cell[id] = inst; if (liberty_cell->hasSequentials()) { - stop_flag_vec.push_back(true); + vertices_maps.stoppers.push_back(true); } else { - stop_flag_vec.push_back(false); + vertices_maps.stoppers.push_back(false); } } - // assign vertex_id property of each macro pin - // all macro pins are flagged as sequential stopping pt +} + +void ClusteringEngine::computeMacroPinVertices(VerticesMaps& vertices_maps) +{ for (auto& [macro, hard_macro] : tree_->maps.inst_to_hard) { for (odb::dbITerm* pin : macro->getITerms()) { if (pin->getSigType() != odb::dbSigType::SIGNAL) { continue; } - odb::dbIntProperty::create(pin, "vertex_id", stop_flag_vec.size()); - macro_pin_vertex[stop_flag_vec.size()] = pin; - stop_flag_vec.push_back(true); + + const int id = static_cast(vertices_maps.stoppers.size()); + odb::dbIntProperty::create(pin, "vertex_id", id); + vertices_maps.id_to_macro_pin[id] = pin; + vertices_maps.stoppers.push_back(true); } } +} - // - // Num of vertices will be # of boundary pins + number of logical std cells + - // number of macro pins) - // - debugPrint(logger_, - MPL, - "multilevel_autoclustering", - 1, - "Number of vertices: {}", - stop_flag_vec.size()); - - // create hypergraphs - std::vector> vertices(stop_flag_vec.size()); - std::vector> backward_vertices(stop_flag_vec.size()); - std::vector> hyperedges; // dircted hypergraph - // traverse the netlist +void ClusteringEngine::createHypergraph( + std::vector>& vertices, + std::vector>& backward_vertices, + std::vector>& hyperedges) +{ for (odb::dbNet* net : block_->getNets()) { - // ignore all the power net if (net->getSigType().isSupply()) { continue; } - int driver_id = -1; // driver vertex id - std::set loads_id; // load vertex id + + int driver_id = -1; + std::set loads_id; bool ignore = false; - // check the connected instances for (odb::dbITerm* iterm : net->getITerms()) { odb::dbInst* inst = iterm->getInst(); odb::dbMaster* master = inst->getMaster(); - // We ignore nets connecting ignored masters if (isIgnoredMaster(master)) { ignore = true; break; } + int vertex_id = -1; if (master->isBlock()) { vertex_id = odb::dbIntProperty::find(iterm, "vertex_id")->getValue(); } else { vertex_id = odb::dbIntProperty::find(inst, "vertex_id")->getValue(); } + if (iterm->getIoType() == odb::dbIoType::OUTPUT) { driver_id = vertex_id; } else { loads_id.insert(vertex_id); } } + if (ignore) { - continue; // the nets with Pads should be ignored + continue; } - // check the connected IO pins of the net for (odb::dbBTerm* bterm : net->getBTerms()) { const int vertex_id = odb::dbIntProperty::find(bterm, "vertex_id")->getValue(); @@ -636,15 +688,12 @@ void ClusteringEngine::createDataFlow() } } - // // Skip high fanout nets or nets that do not have valid driver or loads - // if (driver_id < 0 || loads_id.empty() || loads_id.size() > tree_->large_net_threshold) { continue; } - // Create the hyperedge std::vector hyperedge{driver_id}; for (auto& load : loads_id) { if (load != driver_id) { @@ -656,72 +705,6 @@ void ClusteringEngine::createDataFlow() backward_vertices[hyperedge[i]].push_back(hyperedges.size()); } hyperedges.push_back(hyperedge); - } // end net traversal - - debugPrint( - logger_, MPL, "multilevel_autoclustering", 1, "Created hypergraph"); - - // traverse hypergraph to build dataflow - for (auto [src, src_pin] : io_pin_vertex) { - int idx = 0; - std::vector visited(vertices.size(), false); - std::vector> insts(data_flow.register_dist); - dataFlowDFSIOPin(src, - idx, - insts, - io_pin_vertex, - std_cell_vertex, - macro_pin_vertex, - stop_flag_vec, - visited, - vertices, - hyperedges, - false); - dataFlowDFSIOPin(src, - idx, - insts, - io_pin_vertex, - std_cell_vertex, - macro_pin_vertex, - stop_flag_vec, - visited, - backward_vertices, - hyperedges, - true); - data_flow.io_to_regs.emplace_back(src_pin, insts); - } - - for (auto [src, src_pin] : macro_pin_vertex) { - int idx = 0; - std::vector visited(vertices.size(), false); - std::vector> std_cells(data_flow.register_dist); - std::vector> macros(data_flow.register_dist); - dataFlowDFSMacroPin(src, - idx, - std_cells, - macros, - io_pin_vertex, - std_cell_vertex, - macro_pin_vertex, - stop_flag_vec, - visited, - vertices, - hyperedges, - false); - dataFlowDFSMacroPin(src, - idx, - std_cells, - macros, - io_pin_vertex, - std_cell_vertex, - macro_pin_vertex, - stop_flag_vec, - visited, - backward_vertices, - hyperedges, - true); - data_flow.macro_pin_to_regs.emplace_back(src_pin, std_cells); - data_flow.macro_pin_to_macros.emplace_back(src_pin, macros); } } @@ -737,29 +720,27 @@ bool ClusteringEngine::isIgnoredMaster(odb::dbMaster* master) void ClusteringEngine::dataFlowDFSIOPin( int parent, int idx, + const VerticesMaps& vertices_maps, std::vector>& insts, - std::map& io_pin_vertex, - std::map& std_cell_vertex, - std::map& macro_pin_vertex, - std::vector& stop_flag_vec, std::vector& visited, std::vector>& vertices, std::vector>& hyperedges, bool backward_search) { visited[parent] = true; - if (stop_flag_vec[parent]) { - if (parent < io_pin_vertex.size()) { + if (vertices_maps.stoppers[parent]) { + if (parent < vertices_maps.id_to_bterm.size()) { ; // currently we do not consider IO pin to IO pin connection - } else if (parent < io_pin_vertex.size() + std_cell_vertex.size()) { - insts[idx].insert(std_cell_vertex[parent]); + } else if (parent < vertices_maps.id_to_bterm.size() + + vertices_maps.id_to_std_cell.size()) { + insts[idx].insert(vertices_maps.id_to_std_cell.at(parent)); } else { - insts[idx].insert(macro_pin_vertex[parent]->getInst()); + insts[idx].insert(vertices_maps.id_to_macro_pin.at(parent)->getInst()); } idx++; } - if (idx >= data_flow.register_dist) { + if (idx >= data_flow.max_num_of_hops) { return; } @@ -767,16 +748,13 @@ void ClusteringEngine::dataFlowDFSIOPin( for (auto& hyperedge : vertices[parent]) { for (auto& vertex : hyperedges[hyperedge]) { // we do not consider pin to pin - if (visited[vertex] || vertex < io_pin_vertex.size()) { + if (visited[vertex] || vertex < vertices_maps.id_to_bterm.size()) { continue; } dataFlowDFSIOPin(vertex, idx, + vertices_maps, insts, - io_pin_vertex, - std_cell_vertex, - macro_pin_vertex, - stop_flag_vec, visited, vertices, hyperedges, @@ -787,16 +765,13 @@ void ClusteringEngine::dataFlowDFSIOPin( for (auto& hyperedge : vertices[parent]) { const int vertex = hyperedges[hyperedge][0]; // driver vertex // we do not consider pin to pin - if (visited[vertex] || vertex < io_pin_vertex.size()) { + if (visited[vertex] || vertex < vertices_maps.id_to_bterm.size()) { continue; } dataFlowDFSIOPin(vertex, idx, + vertices_maps, insts, - io_pin_vertex, - std_cell_vertex, - macro_pin_vertex, - stop_flag_vec, visited, vertices, hyperedges, @@ -810,30 +785,28 @@ void ClusteringEngine::dataFlowDFSIOPin( void ClusteringEngine::dataFlowDFSMacroPin( int parent, int idx, + const VerticesMaps& vertices_maps, std::vector>& std_cells, std::vector>& macros, - std::map& io_pin_vertex, - std::map& std_cell_vertex, - std::map& macro_pin_vertex, - std::vector& stop_flag_vec, std::vector& visited, std::vector>& vertices, std::vector>& hyperedges, bool backward_search) { visited[parent] = true; - if (stop_flag_vec[parent]) { - if (parent < io_pin_vertex.size()) { + if (vertices_maps.stoppers[parent]) { + if (parent < vertices_maps.id_to_bterm.size()) { ; // the connection between IO and macro pins have been considers - } else if (parent < io_pin_vertex.size() + std_cell_vertex.size()) { - std_cells[idx].insert(std_cell_vertex[parent]); + } else if (parent < vertices_maps.id_to_bterm.size() + + vertices_maps.id_to_std_cell.size()) { + std_cells[idx].insert(vertices_maps.id_to_std_cell.at(parent)); } else { - macros[idx].insert(macro_pin_vertex[parent]->getInst()); + macros[idx].insert(vertices_maps.id_to_macro_pin.at(parent)->getInst()); } idx++; } - if (idx >= data_flow.register_dist) { + if (idx >= data_flow.max_num_of_hops) { return; } @@ -841,17 +814,14 @@ void ClusteringEngine::dataFlowDFSMacroPin( for (auto& hyperedge : vertices[parent]) { for (auto& vertex : hyperedges[hyperedge]) { // we do not consider pin to pin - if (visited[vertex] || vertex < io_pin_vertex.size()) { + if (visited[vertex] || vertex < vertices_maps.id_to_bterm.size()) { continue; } dataFlowDFSMacroPin(vertex, idx, + vertices_maps, std_cells, macros, - io_pin_vertex, - std_cell_vertex, - macro_pin_vertex, - stop_flag_vec, visited, vertices, hyperedges, @@ -862,17 +832,14 @@ void ClusteringEngine::dataFlowDFSMacroPin( for (auto& hyperedge : vertices[parent]) { const int vertex = hyperedges[hyperedge][0]; // we do not consider pin to pin - if (visited[vertex] || vertex < io_pin_vertex.size()) { + if (visited[vertex] || vertex < vertices_maps.id_to_bterm.size()) { continue; } dataFlowDFSMacroPin(vertex, idx, + vertices_maps, std_cells, macros, - io_pin_vertex, - std_cell_vertex, - macro_pin_vertex, - stop_flag_vec, visited, vertices, hyperedges, @@ -892,18 +859,12 @@ void ClusteringEngine::updateDataFlow() const int driver_id = tree_->maps.bterm_to_cluster_id.at(bterm); - for (int i = 0; i < data_flow.register_dist; i++) { - const float weight = data_flow.weight / std::pow(data_flow.factor, i); - std::set sink_clusters; - - for (auto& inst : insts[i]) { - const int cluster_id = tree_->maps.inst_to_cluster_id.at(inst); - sink_clusters.insert(cluster_id); - } - + for (int hops = 0; hops < data_flow.max_num_of_hops; hops++) { + std::set sink_clusters = computeSinks(insts[hops]); + const float conn_weight = computeConnWeight(hops); for (auto& sink : sink_clusters) { - tree_->maps.id_to_cluster[driver_id]->addConnection(sink, weight); - tree_->maps.id_to_cluster[sink]->addConnection(driver_id, weight); + tree_->maps.id_to_cluster[driver_id]->addConnection(sink, conn_weight); + tree_->maps.id_to_cluster[sink]->addConnection(driver_id, conn_weight); } } } @@ -912,18 +873,12 @@ void ClusteringEngine::updateDataFlow() for (const auto& [iterm, insts] : data_flow.macro_pin_to_regs) { const int driver_id = tree_->maps.inst_to_cluster_id.at(iterm->getInst()); - for (int i = 0; i < data_flow.register_dist; i++) { - const float weight = data_flow.weight / std::pow(data_flow.factor, i); - std::set sink_clusters; - - for (auto& inst : insts[i]) { - const int cluster_id = tree_->maps.inst_to_cluster_id.at(inst); - sink_clusters.insert(cluster_id); - } - + for (int hops = 0; hops < data_flow.max_num_of_hops; hops++) { + std::set sink_clusters = computeSinks(insts[hops]); + const float conn_weight = computeConnWeight(hops); for (auto& sink : sink_clusters) { - tree_->maps.id_to_cluster[driver_id]->addConnection(sink, weight); - tree_->maps.id_to_cluster[sink]->addConnection(driver_id, weight); + tree_->maps.id_to_cluster[driver_id]->addConnection(sink, conn_weight); + tree_->maps.id_to_cluster[sink]->addConnection(driver_id, conn_weight); } } } @@ -932,22 +887,36 @@ void ClusteringEngine::updateDataFlow() for (const auto& [iterm, insts] : data_flow.macro_pin_to_macros) { const int driver_id = tree_->maps.inst_to_cluster_id.at(iterm->getInst()); - for (int i = 0; i < data_flow.register_dist; i++) { - const float weight = data_flow.weight / std::pow(data_flow.factor, i); - std::set sink_clusters; - - for (auto& inst : insts[i]) { - const int cluster_id = tree_->maps.inst_to_cluster_id.at(inst); - sink_clusters.insert(cluster_id); - } - + for (int hops = 0; hops < data_flow.max_num_of_hops; hops++) { + std::set sink_clusters = computeSinks(insts[hops]); + const float conn_weight = computeConnWeight(hops); for (auto& sink : sink_clusters) { - tree_->maps.id_to_cluster[driver_id]->addConnection(sink, weight); + tree_->maps.id_to_cluster[driver_id]->addConnection(sink, conn_weight); } } } } +float ClusteringEngine::computeConnWeight(const int hops) +{ + const float base_remoteness_factor = 2.0; + const float base_connection_weight = 1; + const float remoteness_factor = std::pow(base_remoteness_factor, hops); + + return base_connection_weight / remoteness_factor; +} + +std::set ClusteringEngine::computeSinks( + const std::set& insts) +{ + std::set sink_clusters; + for (auto& inst : insts) { + const int cluster_id = tree_->maps.inst_to_cluster_id.at(inst); + sink_clusters.insert(cluster_id); + } + return sink_clusters; +} + void ClusteringEngine::treatEachMacroAsSingleCluster() { auto module = block_->getTopModule(); diff --git a/src/mpl2/src/clusterEngine.h b/src/mpl2/src/clusterEngine.h index cfb5fbdd0ab..fcd1a0e9917 100644 --- a/src/mpl2/src/clusterEngine.h +++ b/src/mpl2/src/clusterEngine.h @@ -64,11 +64,23 @@ class Metrics; class Cluster; class HardMacro; +// For data flow computation. +struct VerticesMaps +{ + std::map id_to_bterm; + std::map id_to_std_cell; + std::map id_to_macro_pin; + + // Each index represents a vertex in which + // the flow is interrupted. + std::vector stoppers; +}; + struct DataFlow { - const int register_dist = 5; - const float factor = 2.0; - const float weight = 1; + // The register distance between two macros to + // them to be considered connected. + const int max_num_of_hops = 5; std::vector>>> macro_pin_to_regs; @@ -206,29 +218,31 @@ class ClusteringEngine // Methods for data flow void createDataFlow(); + void computeIOVertices(VerticesMaps& vertices_maps); + void computeStdCellVertices(VerticesMaps& vertices_maps); + void computeMacroPinVertices(VerticesMaps& vertices_maps); + void createHypergraph(std::vector>& vertices, + std::vector>& backward_vertices, + std::vector>& hyperedges); void dataFlowDFSIOPin(int parent, int idx, + const VerticesMaps& vertices_maps, std::vector>& insts, - std::map& io_pin_vertex, - std::map& std_cell_vertex, - std::map& macro_pin_vertex, - std::vector& stop_flag_vec, std::vector& visited, std::vector>& vertices, std::vector>& hyperedges, bool backward_search); void dataFlowDFSMacroPin(int parent, int idx, + const VerticesMaps& vertices_maps, std::vector>& std_cells, std::vector>& macros, - std::map& io_pin_vertex, - std::map& std_cell_vertex, - std::map& macro_pin_vertex, - std::vector& stop_flag_vec, std::vector& visited, std::vector>& vertices, std::vector>& hyperedges, bool backward_search); + std::set computeSinks(const std::set& insts); + float computeConnWeight(int hops); void printPhysicalHierarchyTree(Cluster* parent, int level); float computeMicronArea(odb::dbInst* inst); From a37503acc5e899db5728bff6b7bad0d8b304c688 Mon Sep 17 00:00:00 2001 From: Arthur Koucher Date: Tue, 9 Jul 2024 16:04:36 -0300 Subject: [PATCH 22/30] mpl2: final refactor for creating data flow Signed-off-by: Arthur Koucher --- src/mpl2/src/clusterEngine.cpp | 126 ++++++++++++++------------------- src/mpl2/src/clusterEngine.h | 18 +++-- 2 files changed, 66 insertions(+), 78 deletions(-) diff --git a/src/mpl2/src/clusterEngine.cpp b/src/mpl2/src/clusterEngine.cpp index d7e27d60e21..fc2fe5a7d8a 100644 --- a/src/mpl2/src/clusterEngine.cpp +++ b/src/mpl2/src/clusterEngine.cpp @@ -522,70 +522,55 @@ void ClusteringEngine::mapIOPads() void ClusteringEngine::createDataFlow() { // Create vertices IDs. - VerticesMaps vertices_maps; - computeIOVertices(vertices_maps); - computeStdCellVertices(vertices_maps); - computeMacroPinVertices(vertices_maps); - + VerticesMaps vertices_maps = computeVertices(); const int num_of_vertices = static_cast(vertices_maps.stoppers.size()); - debugPrint(logger_, - MPL, - "multilevel_autoclustering", - 1, - "Number of vertices: {}", - num_of_vertices); - std::vector> vertices(num_of_vertices); - std::vector> backward_vertices(num_of_vertices); - std::vector> hyperedges; // directed hypergraph - createHypergraph(vertices, backward_vertices, hyperedges); + DataFlowHypergraph hypergraph = computeHypergraph(num_of_vertices); // Traverse hypergraph to build dataflow. for (auto [src, src_pin] : vertices_maps.id_to_bterm) { int idx = 0; - std::vector visited(vertices.size(), false); + std::vector visited(num_of_vertices, false); std::vector> insts(data_flow.max_num_of_hops); dataFlowDFSIOPin( - src, idx, vertices_maps, insts, visited, vertices, hyperedges, false); - dataFlowDFSIOPin(src, - idx, - vertices_maps, - insts, - visited, - backward_vertices, - hyperedges, - true); + src, idx, vertices_maps, hypergraph, insts, visited, false); + dataFlowDFSIOPin(src, idx, vertices_maps, hypergraph, insts, visited, true); + data_flow.io_to_regs.emplace_back(src_pin, insts); } for (auto [src, src_pin] : vertices_maps.id_to_macro_pin) { int idx = 0; - std::vector visited(vertices.size(), false); + std::vector visited(num_of_vertices, false); std::vector> std_cells(data_flow.max_num_of_hops); std::vector> macros(data_flow.max_num_of_hops); - dataFlowDFSMacroPin(src, - idx, - vertices_maps, - std_cells, - macros, - visited, - vertices, - hyperedges, - false); - dataFlowDFSMacroPin(src, - idx, - vertices_maps, - std_cells, - macros, - visited, - backward_vertices, - hyperedges, - true); + dataFlowDFSMacroPin( + src, idx, vertices_maps, hypergraph, std_cells, macros, visited, false); + dataFlowDFSMacroPin( + src, idx, vertices_maps, hypergraph, std_cells, macros, visited, true); + data_flow.macro_pin_to_regs.emplace_back(src_pin, std_cells); data_flow.macro_pin_to_macros.emplace_back(src_pin, macros); } } +VerticesMaps ClusteringEngine::computeVertices() +{ + VerticesMaps vertices_maps; + computeIOVertices(vertices_maps); + computeStdCellVertices(vertices_maps); + computeMacroPinVertices(vertices_maps); + + debugPrint(logger_, + MPL, + "multilevel_autoclustering", + 1, + "Number of vertices: {}", + vertices_maps.stoppers.size()); + + return vertices_maps; +} + void ClusteringEngine::computeIOVertices(VerticesMaps& vertices_maps) { for (odb::dbBTerm* bterm : block_->getBTerms()) { @@ -639,11 +624,13 @@ void ClusteringEngine::computeMacroPinVertices(VerticesMaps& vertices_maps) } } -void ClusteringEngine::createHypergraph( - std::vector>& vertices, - std::vector>& backward_vertices, - std::vector>& hyperedges) +DataFlowHypergraph ClusteringEngine::computeHypergraph( + const int num_of_vertices) { + DataFlowHypergraph graph; + graph.vertices.resize(num_of_vertices); + graph.backward_vertices.resize(num_of_vertices); + for (odb::dbNet* net : block_->getNets()) { if (net->getSigType().isSupply()) { continue; @@ -700,12 +687,14 @@ void ClusteringEngine::createHypergraph( hyperedge.push_back(load); } } - vertices[driver_id].push_back(hyperedges.size()); + graph.vertices[driver_id].push_back(graph.hyperedges.size()); for (int i = 1; i < hyperedge.size(); i++) { - backward_vertices[hyperedge[i]].push_back(hyperedges.size()); + graph.backward_vertices[hyperedge[i]].push_back(graph.hyperedges.size()); } - hyperedges.push_back(hyperedge); + graph.hyperedges.push_back(hyperedge); } + + return graph; } /* static */ @@ -721,10 +710,9 @@ void ClusteringEngine::dataFlowDFSIOPin( int parent, int idx, const VerticesMaps& vertices_maps, + const DataFlowHypergraph& hypergraph, std::vector>& insts, std::vector& visited, - std::vector>& vertices, - std::vector>& hyperedges, bool backward_search) { visited[parent] = true; @@ -745,8 +733,8 @@ void ClusteringEngine::dataFlowDFSIOPin( } if (!backward_search) { - for (auto& hyperedge : vertices[parent]) { - for (auto& vertex : hyperedges[hyperedge]) { + for (auto& hyperedge : hypergraph.vertices[parent]) { + for (auto& vertex : hypergraph.hyperedges[hyperedge]) { // we do not consider pin to pin if (visited[vertex] || vertex < vertices_maps.id_to_bterm.size()) { continue; @@ -754,16 +742,16 @@ void ClusteringEngine::dataFlowDFSIOPin( dataFlowDFSIOPin(vertex, idx, vertices_maps, + hypergraph, insts, visited, - vertices, - hyperedges, backward_search); } } } else { - for (auto& hyperedge : vertices[parent]) { - const int vertex = hyperedges[hyperedge][0]; // driver vertex + for (auto& hyperedge : hypergraph.vertices[parent]) { + const int vertex + = hypergraph.hyperedges[hyperedge].front(); // driver vertex // we do not consider pin to pin if (visited[vertex] || vertex < vertices_maps.id_to_bterm.size()) { continue; @@ -771,10 +759,9 @@ void ClusteringEngine::dataFlowDFSIOPin( dataFlowDFSIOPin(vertex, idx, vertices_maps, + hypergraph, insts, visited, - vertices, - hyperedges, backward_search); } } @@ -786,11 +773,10 @@ void ClusteringEngine::dataFlowDFSMacroPin( int parent, int idx, const VerticesMaps& vertices_maps, + const DataFlowHypergraph& hypergraph, std::vector>& std_cells, std::vector>& macros, std::vector& visited, - std::vector>& vertices, - std::vector>& hyperedges, bool backward_search) { visited[parent] = true; @@ -811,8 +797,8 @@ void ClusteringEngine::dataFlowDFSMacroPin( } if (!backward_search) { - for (auto& hyperedge : vertices[parent]) { - for (auto& vertex : hyperedges[hyperedge]) { + for (auto& hyperedge : hypergraph.vertices[parent]) { + for (auto& vertex : hypergraph.hyperedges[hyperedge]) { // we do not consider pin to pin if (visited[vertex] || vertex < vertices_maps.id_to_bterm.size()) { continue; @@ -820,17 +806,16 @@ void ClusteringEngine::dataFlowDFSMacroPin( dataFlowDFSMacroPin(vertex, idx, vertices_maps, + hypergraph, std_cells, macros, visited, - vertices, - hyperedges, backward_search); } } } else { - for (auto& hyperedge : vertices[parent]) { - const int vertex = hyperedges[hyperedge][0]; + for (auto& hyperedge : hypergraph.vertices[parent]) { + const int vertex = hypergraph.hyperedges[hyperedge].front(); // we do not consider pin to pin if (visited[vertex] || vertex < vertices_maps.id_to_bterm.size()) { continue; @@ -838,11 +823,10 @@ void ClusteringEngine::dataFlowDFSMacroPin( dataFlowDFSMacroPin(vertex, idx, vertices_maps, + hypergraph, std_cells, macros, visited, - vertices, - hyperedges, backward_search); } } diff --git a/src/mpl2/src/clusterEngine.h b/src/mpl2/src/clusterEngine.h index fcd1a0e9917..e3c2db6913e 100644 --- a/src/mpl2/src/clusterEngine.h +++ b/src/mpl2/src/clusterEngine.h @@ -64,6 +64,13 @@ class Metrics; class Cluster; class HardMacro; +struct DataFlowHypergraph +{ + std::vector> vertices; + std::vector> backward_vertices; + std::vector> hyperedges; +}; + // For data flow computation. struct VerticesMaps { @@ -218,28 +225,25 @@ class ClusteringEngine // Methods for data flow void createDataFlow(); + VerticesMaps computeVertices(); void computeIOVertices(VerticesMaps& vertices_maps); void computeStdCellVertices(VerticesMaps& vertices_maps); void computeMacroPinVertices(VerticesMaps& vertices_maps); - void createHypergraph(std::vector>& vertices, - std::vector>& backward_vertices, - std::vector>& hyperedges); + DataFlowHypergraph computeHypergraph(int num_of_vertices); void dataFlowDFSIOPin(int parent, int idx, const VerticesMaps& vertices_maps, + const DataFlowHypergraph& hypergraph, std::vector>& insts, std::vector& visited, - std::vector>& vertices, - std::vector>& hyperedges, bool backward_search); void dataFlowDFSMacroPin(int parent, int idx, const VerticesMaps& vertices_maps, + const DataFlowHypergraph& hypergraph, std::vector>& std_cells, std::vector>& macros, std::vector& visited, - std::vector>& vertices, - std::vector>& hyperedges, bool backward_search); std::set computeSinks(const std::set& insts); float computeConnWeight(int hops); From 9e808ca71f124bca76b66d9618937d0e61f21f01 Mon Sep 17 00:00:00 2001 From: Arthur Koucher Date: Tue, 9 Jul 2024 16:18:48 -0300 Subject: [PATCH 23/30] mpl2: rename functions for getting the design's logical data Signed-off-by: Arthur Koucher --- src/mpl2/src/clusterEngine.cpp | 22 ++++++---------------- src/mpl2/src/clusterEngine.h | 4 ++-- src/mpl2/src/hier_rtlmp.cpp | 2 +- 3 files changed, 9 insertions(+), 19 deletions(-) diff --git a/src/mpl2/src/clusterEngine.cpp b/src/mpl2/src/clusterEngine.cpp index fc2fe5a7d8a..2449f1030b4 100644 --- a/src/mpl2/src/clusterEngine.cpp +++ b/src/mpl2/src/clusterEngine.cpp @@ -95,10 +95,9 @@ void ClusteringEngine::setTree(PhysicalHierarchy* tree) tree_ = tree; } -// Fetch the design's logical data -void ClusteringEngine::fetchDesignMetrics() +void ClusteringEngine::computeDesignMetrics() { - design_metrics_ = computeMetrics(block_->getTopModule()); + design_metrics_ = computeModuleMetrics(block_->getTopModule()); odb::Rect die = block_->getDieArea(); odb::Rect core_box = block_->getCoreArea(); @@ -159,11 +158,7 @@ void ClusteringEngine::fetchDesignMetrics() } } -// Traverse Logical Hierarchy -// Recursive function to collect the design metrics (number of std cells, -// area of std cells, number of macros and area of macros) in the logical -// hierarchy -Metrics* ClusteringEngine::computeMetrics(odb::dbModule* module) +Metrics* ClusteringEngine::computeModuleMetrics(odb::dbModule* module) { unsigned int num_std_cell = 0; float std_cell_area = 0.0; @@ -172,8 +167,7 @@ Metrics* ClusteringEngine::computeMetrics(odb::dbModule* module) for (odb::dbInst* inst : module->getInsts()) { odb::dbMaster* master = inst->getMaster(); - - if (ClusteringEngine::isIgnoredMaster(master)) { + if (isIgnoredMaster(master)) { continue; } @@ -193,12 +187,8 @@ Metrics* ClusteringEngine::computeMetrics(odb::dbModule* module) } } - // Be careful about the relationship between - // odb::dbModule and odb::dbInst - // odb::dbModule and odb::dbModInst - // recursively traverse the hierarchical module instances - for (odb::dbModInst* inst : module->getChildren()) { - Metrics* metrics = computeMetrics(inst->getMaster()); + for (odb::dbModInst* child_module_inst : module->getChildren()) { + Metrics* metrics = computeModuleMetrics(child_module_inst->getMaster()); num_std_cell += metrics->getNumStdCell(); std_cell_area += metrics->getStdCellArea(); num_macro += metrics->getNumMacro(); diff --git a/src/mpl2/src/clusterEngine.h b/src/mpl2/src/clusterEngine.h index e3c2db6913e..39eced0f1e1 100644 --- a/src/mpl2/src/clusterEngine.h +++ b/src/mpl2/src/clusterEngine.h @@ -151,7 +151,7 @@ class ClusteringEngine par::PartitionMgr* triton_part); void run(); - void fetchDesignMetrics(); + void computeDesignMetrics(); void setDesignMetrics(Metrics* design_metrics); void setTree(PhysicalHierarchy* tree); @@ -173,7 +173,7 @@ class ClusteringEngine std::set& masters); private: - Metrics* computeMetrics(odb::dbModule* module); + Metrics* computeModuleMetrics(odb::dbModule* module); void reportLogicalHierarchyInformation(float core_area, float util, float core_util); diff --git a/src/mpl2/src/hier_rtlmp.cpp b/src/mpl2/src/hier_rtlmp.cpp index 7154b14e518..94ab2aae082 100644 --- a/src/mpl2/src/hier_rtlmp.cpp +++ b/src/mpl2/src/hier_rtlmp.cpp @@ -279,7 +279,7 @@ void HierRTLMP::runMultilevelAutoclustering() clustering_engine_->setDesignMetrics(metrics_); clustering_engine_->setTree(&tree_); - clustering_engine_->fetchDesignMetrics(); + clustering_engine_->computeDesignMetrics(); if (!tree_.has_unfixed_macros) { logger_->info(MPL, 17, "No unfixed macros."); skip_macro_placement_ = true; From 2101d152eb54907fc33f6e1ec57403a67aae6287 Mon Sep 17 00:00:00 2001 From: Arthur Koucher Date: Tue, 9 Jul 2024 20:05:23 -0300 Subject: [PATCH 24/30] mpl2: separate design metrics population from engine initialization and update ok Signed-off-by: Arthur Koucher --- src/mpl2/src/clusterEngine.cpp | 119 +++++++++++++++++------------ src/mpl2/src/clusterEngine.h | 12 +-- src/mpl2/src/hier_rtlmp.cpp | 6 +- src/mpl2/test/no_unfixed_macros.ok | 14 ---- 4 files changed, 79 insertions(+), 72 deletions(-) diff --git a/src/mpl2/src/clusterEngine.cpp b/src/mpl2/src/clusterEngine.cpp index 2449f1030b4..7c58f1dd24b 100644 --- a/src/mpl2/src/clusterEngine.cpp +++ b/src/mpl2/src/clusterEngine.cpp @@ -56,7 +56,14 @@ ClusteringEngine::ClusteringEngine(odb::dbBlock* block, void ClusteringEngine::run() { - initTree(); + design_metrics_ = computeModuleMetrics(block_->getTopModule()); + init(); + + if (!tree_->has_unfixed_macros) { + return; + } + + createRoot(); setBaseThresholds(); createIOClusters(); @@ -95,17 +102,39 @@ void ClusteringEngine::setTree(PhysicalHierarchy* tree) tree_ = tree; } -void ClusteringEngine::computeDesignMetrics() +// Check if macro placement is both needed and feasible. +// Also report some design data relevant for the user. +void ClusteringEngine::init() { - design_metrics_ = computeModuleMetrics(block_->getTopModule()); + const std::vector unfixed_macros = getUnfixedMacros(); + if (unfixed_macros.empty()) { + tree_->has_unfixed_macros = false; + logger_->info(MPL, 17, "No unfixed macros."); + return; + } + + tree_->macro_with_halo_area = computeMacroWithHaloArea(unfixed_macros); - odb::Rect die = block_->getDieArea(); - odb::Rect core_box = block_->getCoreArea(); + const odb::Rect die = block_->getDieArea(); + const odb::Rect core_box = block_->getCoreArea(); + + const float core_lx = block_->dbuToMicrons(core_box.xMin()); + const float core_ly = block_->dbuToMicrons(core_box.yMin()); + const float core_ux = block_->dbuToMicrons(core_box.xMax()); + const float core_uy = block_->dbuToMicrons(core_box.yMax()); - float core_lx = block_->dbuToMicrons(core_box.xMin()); - float core_ly = block_->dbuToMicrons(core_box.yMin()); - float core_ux = block_->dbuToMicrons(core_box.xMax()); - float core_uy = block_->dbuToMicrons(core_box.yMax()); + const float core_area = (core_ux - core_lx) * (core_uy - core_ly); + const float inst_area_with_halos + = tree_->macro_with_halo_area + design_metrics_->getStdCellArea(); + + if (inst_area_with_halos > core_area) { + logger_->error(MPL, + 16, + "The instance area considering the macros' halos {} exceeds " + "the core area {}", + inst_area_with_halos, + core_area); + } logger_->report( "Floorplan Outline: ({}, {}) ({}, {}), Core Outline: ({}, {}) ({}, {})", @@ -118,44 +147,33 @@ void ClusteringEngine::computeDesignMetrics() core_ux, core_uy); - float core_area = (core_ux - core_lx) * (core_uy - core_ly); - float util - = (design_metrics_->getStdCellArea() + design_metrics_->getMacroArea()) - / core_area; - float core_util = design_metrics_->getStdCellArea() - / (core_area - design_metrics_->getMacroArea()); - - // Check if placement is feasible in the core area when considering - // the macro halos - int unfixed_macros = 0; - for (auto inst : block_->getInsts()) { - auto master = inst->getMaster(); - if (master->isBlock()) { - const auto width - = block_->dbuToMicrons(master->getWidth()) + 2 * tree_->halo_width; - const auto height - = block_->dbuToMicrons(master->getHeight()) + 2 * tree_->halo_width; - tree_->macro_with_halo_area += width * height; - unfixed_macros += !inst->getPlacementStatus().isFixed(); - } - } - - reportLogicalHierarchyInformation(core_area, util, core_util); + reportDesignData(core_area); +} - if (unfixed_macros == 0) { - tree_->has_unfixed_macros = false; - return; - } +float ClusteringEngine::computeMacroWithHaloArea( + const std::vector& unfixed_macros) +{ + float macro_with_halo_area = 0.0f; + for (odb::dbInst* unfixed_macro : unfixed_macros) { + odb::dbMaster* master = unfixed_macro->getMaster(); + const float width + = block_->dbuToMicrons(master->getWidth()) + 2 * tree_->halo_width; + const float height + = block_->dbuToMicrons(master->getHeight()) + 2 * tree_->halo_height; + macro_with_halo_area += width * height; + } + return macro_with_halo_area; +} - if (tree_->macro_with_halo_area + design_metrics_->getStdCellArea() - > core_area) { - logger_->error( - MPL, - 16, - "The instance area with halos {} exceeds the core area {}", - tree_->macro_with_halo_area + design_metrics_->getStdCellArea(), - core_area); +std::vector ClusteringEngine::getUnfixedMacros() +{ + std::vector unfixed_macros; + for (odb::dbInst* inst : block_->getInsts()) { + if (inst->isBlock() && !inst->getPlacementStatus().isFixed()) { + unfixed_macros.push_back(inst); + } } + return unfixed_macros; } Metrics* ClusteringEngine::computeModuleMetrics(odb::dbModule* module) @@ -202,10 +220,14 @@ Metrics* ClusteringEngine::computeModuleMetrics(odb::dbModule* module) return metrics; } -void ClusteringEngine::reportLogicalHierarchyInformation(float core_area, - float util, - float core_util) +void ClusteringEngine::reportDesignData(const float core_area) { + float util + = (design_metrics_->getStdCellArea() + design_metrics_->getMacroArea()) + / core_area; + float core_util = design_metrics_->getStdCellArea() + / (core_area - design_metrics_->getMacroArea()); + logger_->report( "\tNumber of std cell instances: {}\n" "\tArea of std cell instances: {:.2f}\n" @@ -233,7 +255,7 @@ void ClusteringEngine::reportLogicalHierarchyInformation(float core_area, block_->getTech()->getManufacturingGrid()); } -void ClusteringEngine::initTree() +void ClusteringEngine::createRoot() { tree_->root = new Cluster(id_, std::string("root"), logger_); tree_->root->addDbModule(block_->getTopModule()); @@ -1678,7 +1700,6 @@ void ClusteringEngine::updateConnections() } } -// Traverse the physical hierarchy tree in a DFS manner (post-order) void ClusteringEngine::fetchMixedLeaves( Cluster* parent, std::vector>& mixed_leaves) diff --git a/src/mpl2/src/clusterEngine.h b/src/mpl2/src/clusterEngine.h index 39eced0f1e1..43aff5daaee 100644 --- a/src/mpl2/src/clusterEngine.h +++ b/src/mpl2/src/clusterEngine.h @@ -151,7 +151,6 @@ class ClusteringEngine par::PartitionMgr* triton_part); void run(); - void computeDesignMetrics(); void setDesignMetrics(Metrics* design_metrics); void setTree(PhysicalHierarchy* tree); @@ -173,11 +172,14 @@ class ClusteringEngine std::set& masters); private: + void init(); Metrics* computeModuleMetrics(odb::dbModule* module); - void reportLogicalHierarchyInformation(float core_area, - float util, - float core_util); - void initTree(); + std::vector getUnfixedMacros(); + bool macroPlacementIsFeasible(const float core_area); + float computeMacroWithHaloArea( + const std::vector& unfixed_macros); + void reportDesignData(float core_area); + void createRoot(); void setBaseThresholds(); void createIOClusters(); void mapIOPads(); diff --git a/src/mpl2/src/hier_rtlmp.cpp b/src/mpl2/src/hier_rtlmp.cpp index 94ab2aae082..6d0bbc74739 100644 --- a/src/mpl2/src/hier_rtlmp.cpp +++ b/src/mpl2/src/hier_rtlmp.cpp @@ -279,15 +279,13 @@ void HierRTLMP::runMultilevelAutoclustering() clustering_engine_->setDesignMetrics(metrics_); clustering_engine_->setTree(&tree_); - clustering_engine_->computeDesignMetrics(); + clustering_engine_->run(); + if (!tree_.has_unfixed_macros) { - logger_->info(MPL, 17, "No unfixed macros."); skip_macro_placement_ = true; return; } - clustering_engine_->run(); - if (graphics_) { graphics_->finishedClustering(tree_.root); } diff --git a/src/mpl2/test/no_unfixed_macros.ok b/src/mpl2/test/no_unfixed_macros.ok index a1b144a4d5e..2ce52e8635f 100644 --- a/src/mpl2/test/no_unfixed_macros.ok +++ b/src/mpl2/test/no_unfixed_macros.ok @@ -4,20 +4,6 @@ [INFO ODB-0128] Design: macro_only [INFO ODB-0253] Updated 10 components. [INFO ODB-0254] Updated 12 nets and 24 connections. -Floorplan Outline: (0.0, 0.0) (450.0, 450.0), Core Outline: (4.94, 4.2) (444.98, 443.8) - Number of std cell instances: 0 - Area of std cell instances: 0.00 - Number of macros: 10 - Area of macros: 166000.00 - Halo width: 4.00 - Halo height: 4.00 - Area of macros with halos: 187920.00 - Area of std cell instances + Area of macros: 166000.00 - Core area: 193441.58 - Design Utilization: 0.86 - Core Utilization: 0.00 - Manufacturing Grid: 10 - [INFO MPL-0017] No unfixed macros. [INFO MPL-0013] Skipping macro placement. No differences found. From 47d0bbc1b3d17175b9263c49f2582a2b9b2bbbd7 Mon Sep 17 00:00:00 2001 From: Arthur Koucher Date: Wed, 10 Jul 2024 09:48:14 -0300 Subject: [PATCH 25/30] mpl2: const and remove some auto Signed-off-by: Arthur Koucher --- src/mpl2/src/clusterEngine.cpp | 101 +++++++++++++++------------------ 1 file changed, 46 insertions(+), 55 deletions(-) diff --git a/src/mpl2/src/clusterEngine.cpp b/src/mpl2/src/clusterEngine.cpp index 7c58f1dd24b..214d45c648e 100644 --- a/src/mpl2/src/clusterEngine.cpp +++ b/src/mpl2/src/clusterEngine.cpp @@ -311,7 +311,7 @@ void ClusteringEngine::setBaseThresholds() } // Set sizes for root - unsigned coarsening_factor + const unsigned coarsening_factor = std::pow(tree_->cluster_size_ratio, tree_->max_level - 1); tree_->base_max_macro *= coarsening_factor; tree_->base_min_macro *= coarsening_factor; @@ -334,24 +334,18 @@ void ClusteringEngine::setBaseThresholds() void ClusteringEngine::createIOClusters() { mapIOPads(); - debugPrint(logger_, MPL, "multilevel_autoclustering", 1, "Creating bundledIO clusters..."); - const odb::Rect die = block_->getDieArea(); - // Get the floorplan information and get the range of bundled IO regions - odb::Rect die_box = block_->getCoreArea(); - int core_lx = die_box.xMin(); - int core_ly = die_box.yMin(); - int core_ux = die_box.xMax(); - int core_uy = die_box.yMax(); + const odb::Rect die = block_->getDieArea(); + const odb::Rect core = block_->getCoreArea(); const int x_base = (die.xMax() - die.xMin()) / tree_->bundled_ios_per_edge; const int y_base = (die.yMax() - die.yMin()) / tree_->bundled_ios_per_edge; - int cluster_id_base = id_; + const int cluster_id_base = id_; // Map all the BTerms / Pads to Bundled IOs (cluster) std::vector prefix_vec; @@ -363,7 +357,7 @@ void ClusteringEngine::createIOClusters() for (int i = 0; i < 4; i++) { // four boundaries (Left, Top, Right and Bottom in order) for (int j = 0; j < tree_->bundled_ios_per_edge; j++) { - std::string cluster_name = prefix_vec[i] + std::to_string(j); + const std::string cluster_name = prefix_vec[i] + std::to_string(j); Cluster* cluster = new Cluster(id_, cluster_name, logger_); tree_->root->addChild(cluster); cluster->setParent(tree_->root); @@ -428,16 +422,16 @@ void ClusteringEngine::createIOClusters() ly = tree_->maps.bterm_to_inst[term]->getBBox()->yMin(); ux = tree_->maps.bterm_to_inst[term]->getBBox()->xMax(); uy = tree_->maps.bterm_to_inst[term]->getBBox()->yMax(); - if (lx <= core_lx) { + if (lx <= core.xMin()) { lx = die.xMin(); } - if (ly <= core_ly) { + if (ly <= core.yMin()) { ly = die.yMin(); } - if (ux >= core_ux) { + if (ux >= core.xMax()) { ux = die.xMax(); } - if (uy >= core_uy) { + if (uy >= core.yMax()) { uy = die.yMax(); } } @@ -534,10 +528,10 @@ void ClusteringEngine::mapIOPads() void ClusteringEngine::createDataFlow() { // Create vertices IDs. - VerticesMaps vertices_maps = computeVertices(); + const VerticesMaps vertices_maps = computeVertices(); const int num_of_vertices = static_cast(vertices_maps.stoppers.size()); - DataFlowHypergraph hypergraph = computeHypergraph(num_of_vertices); + const DataFlowHypergraph hypergraph = computeHypergraph(num_of_vertices); // Traverse hypergraph to build dataflow. for (auto [src, src_pin] : vertices_maps.id_to_bterm) { @@ -595,7 +589,7 @@ void ClusteringEngine::computeIOVertices(VerticesMaps& vertices_maps) void ClusteringEngine::computeStdCellVertices(VerticesMaps& vertices_maps) { - for (auto inst : block_->getInsts()) { + for (odb::dbInst* inst : block_->getInsts()) { odb::dbMaster* master = inst->getMaster(); if (isIgnoredMaster(master) || master->isBlock()) { continue; @@ -719,7 +713,7 @@ bool ClusteringEngine::isIgnoredMaster(odb::dbMaster* master) // Forward or Backward DFS search to find sequential paths from/to IO pins based // on hop count to macro pins void ClusteringEngine::dataFlowDFSIOPin( - int parent, + const int parent, int idx, const VerticesMaps& vertices_maps, const DataFlowHypergraph& hypergraph, @@ -782,7 +776,7 @@ void ClusteringEngine::dataFlowDFSIOPin( // Forward or Backward DFS search to find sequential paths between Macros based // on hop count void ClusteringEngine::dataFlowDFSMacroPin( - int parent, + const int parent, int idx, const VerticesMaps& vertices_maps, const DataFlowHypergraph& hypergraph, @@ -915,16 +909,15 @@ std::set ClusteringEngine::computeSinks( void ClusteringEngine::treatEachMacroAsSingleCluster() { - auto module = block_->getTopModule(); + odb::dbModule* module = block_->getTopModule(); for (odb::dbInst* inst : module->getInsts()) { odb::dbMaster* master = inst->getMaster(); - if (isIgnoredMaster(master)) { continue; } if (master->isBlock()) { - std::string cluster_name = inst->getName(); + const std::string cluster_name = inst->getName(); Cluster* cluster = new Cluster(id_, cluster_name, logger_); cluster->addLeafMacro(inst); incorporateNewCluster(cluster, tree_->root); @@ -957,27 +950,27 @@ void ClusteringEngine::incorporateNewCluster(Cluster* cluster, Cluster* parent) void ClusteringEngine::updateInstancesAssociation(Cluster* cluster) { - int cluster_id = cluster->getId(); - ClusterType cluster_type = cluster->getClusterType(); + const int cluster_id = cluster->getId(); + const ClusterType cluster_type = cluster->getClusterType(); if (cluster_type == HardMacroCluster || cluster_type == MixedCluster) { - for (auto& inst : cluster->getLeafMacros()) { + for (odb::dbInst* inst : cluster->getLeafMacros()) { tree_->maps.inst_to_cluster_id[inst] = cluster_id; } } if (cluster_type == StdCellCluster || cluster_type == MixedCluster) { - for (auto& inst : cluster->getLeafStdCells()) { + for (odb::dbInst* inst : cluster->getLeafStdCells()) { tree_->maps.inst_to_cluster_id[inst] = cluster_id; } } // Note: macro clusters have no module. if (cluster_type == StdCellCluster) { - for (auto& module : cluster->getDbModules()) { + for (odb::dbModule* module : cluster->getDbModules()) { updateInstancesAssociation(module, cluster_id, false); } } else if (cluster_type == MixedCluster) { - for (auto& module : cluster->getDbModules()) { + for (odb::dbModule* module : cluster->getDbModules()) { updateInstancesAssociation(module, cluster_id, true); } } @@ -1003,8 +996,8 @@ void ClusteringEngine::updateInstancesAssociation(odb::dbModule* module, tree_->maps.inst_to_cluster_id[inst] = cluster_id; } } - for (odb::dbModInst* inst : module->getChildren()) { - updateInstancesAssociation(inst->getMaster(), cluster_id, include_macro); + for (odb::dbModInst* child_module_inst : module->getChildren()) { + updateInstancesAssociation(child_module_inst->getMaster(), cluster_id, include_macro); } } @@ -1182,14 +1175,14 @@ void ClusteringEngine::breakCluster(Cluster* parent) return; } - for (odb::dbModInst* child : module->getChildren()) { - createCluster(child->getMaster(), parent); + for (odb::dbModInst* child_module_inst : module->getChildren()) { + createCluster(child_module_inst->getMaster(), parent); } createFlatCluster(module, parent); } else { // Parent is a cluster generated by merging small clusters: // It may have a few logical modules or many glue insts. - for (auto& module : parent->getDbModules()) { + for (odb::dbModule* module : parent->getDbModules()) { createCluster(module, parent); } @@ -1200,7 +1193,7 @@ void ClusteringEngine::breakCluster(Cluster* parent) } // Recursively break down non-flat large clusters with logical modules - for (auto& child : parent->getChildren()) { + for (Cluster* child : parent->getChildren()) { if (!child->getDbModules().empty()) { if (child->getNumStdCell() > max_std_cell_ || child->getNumMacro() > max_macro_) { @@ -1211,7 +1204,7 @@ void ClusteringEngine::breakCluster(Cluster* parent) // Merge small clusters std::vector candidate_clusters; - for (auto& cluster : parent->getChildren()) { + for (Cluster* cluster : parent->getChildren()) { if (!cluster->isIOCluster() && cluster->getNumStdCell() < min_std_cell_ && cluster->getNumMacro() < min_macro_) { candidate_clusters.push_back(cluster); @@ -1229,7 +1222,7 @@ void ClusteringEngine::breakCluster(Cluster* parent) // contain its macros and std cells as leaves. void ClusteringEngine::createFlatCluster(odb::dbModule* module, Cluster* parent) { - std::string cluster_name + const std::string cluster_name = std::string("(") + parent->getName() + ")_glue_logic"; Cluster* cluster = new Cluster(id_, cluster_name, logger_); addModuleInstsToCluster(cluster, module); @@ -1256,7 +1249,7 @@ void ClusteringEngine::addModuleInstsToCluster(Cluster* cluster, void ClusteringEngine::createCluster(odb::dbModule* module, Cluster* parent) { - std::string cluster_name = module->getHierarchicalName(); + const std::string cluster_name = module->getHierarchicalName(); Cluster* cluster = new Cluster(id_, cluster_name, logger_); cluster->addDbModule(module); incorporateNewCluster(cluster, parent); @@ -1264,14 +1257,14 @@ void ClusteringEngine::createCluster(odb::dbModule* module, Cluster* parent) void ClusteringEngine::createCluster(Cluster* parent) { - std::string cluster_name + const std::string cluster_name = std::string("(") + parent->getName() + ")_glue_logic"; Cluster* cluster = new Cluster(id_, cluster_name, logger_); - for (auto& inst : parent->getLeafStdCells()) { - cluster->addLeafStdCell(inst); + for (odb::dbInst* std_cell : parent->getLeafStdCells()) { + cluster->addLeafStdCell(std_cell); } - for (auto& inst : parent->getLeafMacros()) { - cluster->addLeafMacro(inst); + for (odb::dbInst* macro : parent->getLeafMacros()) { + cluster->addLeafMacro(macro); } incorporateNewCluster(cluster, parent); @@ -1286,7 +1279,7 @@ void ClusteringEngine::updateSubTree(Cluster* parent) std::vector children_clusters; std::vector internal_clusters; std::queue wavefront; - for (auto child : parent->getChildren()) { + for (Cluster* child : parent->getChildren()) { wavefront.push(child); } @@ -1297,21 +1290,21 @@ void ClusteringEngine::updateSubTree(Cluster* parent) children_clusters.push_back(cluster); } else { internal_clusters.push_back(cluster); - for (auto child : cluster->getChildren()) { + for (Cluster* child : cluster->getChildren()) { wavefront.push(child); } } } // delete all the internal clusters - for (auto& cluster : internal_clusters) { + for (Cluster* cluster : internal_clusters) { tree_->maps.id_to_cluster.erase(cluster->getId()); delete cluster; } parent->removeChildren(); parent->addChildren(children_clusters); - for (auto& cluster : children_clusters) { + for (Cluster* cluster : children_clusters) { cluster->setParent(parent); if (cluster->getNumStdCell() > max_std_cell_) { breakLargeFlatCluster(cluster); @@ -1594,7 +1587,7 @@ void ClusteringEngine::mergeClusters(std::vector& candidate_clusters) // Update the candidate clusters // Some clusters have become well-formed clusters candidate_clusters.clear(); - for (auto& cluster : new_candidate_clusters) { + for (Cluster* cluster : new_candidate_clusters) { if (cluster->getNumStdCell() < min_std_cell_ && cluster->getNumMacro() < min_macro_) { candidate_clusters.push_back(cluster); @@ -1652,7 +1645,6 @@ void ClusteringEngine::updateConnections() for (odb::dbITerm* iterm : net->getITerms()) { odb::dbInst* inst = iterm->getInst(); odb::dbMaster* master = inst->getMaster(); - if (isIgnoredMaster(master)) { net_has_pad_or_cover = true; break; @@ -1710,7 +1702,7 @@ void ClusteringEngine::fetchMixedLeaves( std::vector sister_mixed_leaves; - for (auto& child : parent->getChildren()) { + for (Cluster* child : parent->getChildren()) { updateInstancesAssociation(child); if (child->getNumMacro() > 0) { if (child->getChildren().empty()) { @@ -1735,7 +1727,6 @@ void ClusteringEngine::breakMixedLeaves( for (const std::vector& sister_mixed_leaves : mixed_leaves) { if (!sister_mixed_leaves.empty()) { Cluster* parent = sister_mixed_leaves.front()->getParent(); - for (Cluster* mixed_leaf : sister_mixed_leaves) { breakMixedLeaf(mixed_leaf); } @@ -1863,8 +1854,8 @@ void ClusteringEngine::getHardMacros(odb::dbModule* module, } } - for (odb::dbModInst* inst : module->getChildren()) { - getHardMacros(inst->getMaster(), hard_macros); + for (odb::dbModInst* child_module_inst : module->getChildren()) { + getHardMacros(child_module_inst->getMaster(), hard_macros); } } @@ -1874,7 +1865,7 @@ void ClusteringEngine::createOneClusterForEachMacro( std::vector& macro_clusters) { for (auto& hard_macro : hard_macros) { - std::string cluster_name = hard_macro->getName(); + const std::string cluster_name = hard_macro->getName(); Cluster* single_macro_cluster = new Cluster(id_, cluster_name, logger_); single_macro_cluster->addLeafMacro(hard_macro->getInst()); incorporateNewCluster(single_macro_cluster, parent); @@ -1924,7 +1915,7 @@ void ClusteringEngine::classifyMacrosByConnSignature( if (logger_->debugCheck(MPL, "multilevel_autoclustering", 2)) { logger_->report("\nPrint Connection Signature\n"); - for (auto& cluster : macro_clusters) { + for (Cluster* cluster : macro_clusters) { logger_->report("Macro Signature: {}", cluster->getName()); for (auto& [cluster_id, weight] : cluster->getConnection()) { logger_->report(" {} {} ", From 714b6de17d11c556ee8490df8b05c79a6870cfa4 Mon Sep 17 00:00:00 2001 From: Arthur Koucher Date: Wed, 10 Jul 2024 15:22:18 -0300 Subject: [PATCH 26/30] mpl2: use backward vertices for backward searching when traversing hypergraph to create dataflow Signed-off-by: Arthur Koucher --- src/mpl2/src/clusterEngine.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/mpl2/src/clusterEngine.cpp b/src/mpl2/src/clusterEngine.cpp index 214d45c648e..bdca3f358ac 100644 --- a/src/mpl2/src/clusterEngine.cpp +++ b/src/mpl2/src/clusterEngine.cpp @@ -755,7 +755,7 @@ void ClusteringEngine::dataFlowDFSIOPin( } } } else { - for (auto& hyperedge : hypergraph.vertices[parent]) { + for (auto& hyperedge : hypergraph.backward_vertices[parent]) { const int vertex = hypergraph.hyperedges[hyperedge].front(); // driver vertex // we do not consider pin to pin @@ -820,7 +820,7 @@ void ClusteringEngine::dataFlowDFSMacroPin( } } } else { - for (auto& hyperedge : hypergraph.vertices[parent]) { + for (auto& hyperedge : hypergraph.backward_vertices[parent]) { const int vertex = hypergraph.hyperedges[hyperedge].front(); // we do not consider pin to pin if (visited[vertex] || vertex < vertices_maps.id_to_bterm.size()) { @@ -997,7 +997,8 @@ void ClusteringEngine::updateInstancesAssociation(odb::dbModule* module, } } for (odb::dbModInst* child_module_inst : module->getChildren()) { - updateInstancesAssociation(child_module_inst->getMaster(), cluster_id, include_macro); + updateInstancesAssociation( + child_module_inst->getMaster(), cluster_id, include_macro); } } From 650de56fe3c2771becac9022d5f0e1bd1e83787d Mon Sep 17 00:00:00 2001 From: Arthur Koucher Date: Wed, 10 Jul 2024 15:25:50 -0300 Subject: [PATCH 27/30] mpl2: comment cleanup Signed-off-by: Arthur Koucher --- src/mpl2/src/clusterEngine.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/mpl2/src/clusterEngine.cpp b/src/mpl2/src/clusterEngine.cpp index bdca3f358ac..43a06700948 100644 --- a/src/mpl2/src/clusterEngine.cpp +++ b/src/mpl2/src/clusterEngine.cpp @@ -1272,9 +1272,8 @@ void ClusteringEngine::createCluster(Cluster* parent) } // This function has two purposes: -// 1) remove all the internal clusters between parent and leaf clusters in its -// subtree 2) Call TritonPart to partition large flat clusters (a cluster with -// no logical modules) +// 1) Remove internal clusters between parent and leaf clusters in its subtree. +// 2) Call TritonPart to partition large flat clusters. void ClusteringEngine::updateSubTree(Cluster* parent) { std::vector children_clusters; @@ -1297,7 +1296,6 @@ void ClusteringEngine::updateSubTree(Cluster* parent) } } - // delete all the internal clusters for (Cluster* cluster : internal_clusters) { tree_->maps.id_to_cluster.erase(cluster->getId()); delete cluster; From 6a162053d75cdebc5f523f8af9b6dfa85fe42c68 Mon Sep 17 00:00:00 2001 From: Arthur Koucher Date: Wed, 10 Jul 2024 19:08:28 -0300 Subject: [PATCH 28/30] mpl2: remove unneeded type handling Signed-off-by: Arthur Koucher --- src/mpl2/src/clusterEngine.cpp | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/src/mpl2/src/clusterEngine.cpp b/src/mpl2/src/clusterEngine.cpp index 43a06700948..a6b970de720 100644 --- a/src/mpl2/src/clusterEngine.cpp +++ b/src/mpl2/src/clusterEngine.cpp @@ -1018,10 +1018,10 @@ void ClusteringEngine::setClusterMetrics(Cluster* cluster) const unsigned int num_macro = cluster->getLeafMacros().size(); Metrics metrics(num_std_cell, num_macro, std_cell_area, macro_area); - for (auto& module : cluster->getDbModules()) { metrics.addMetrics(*tree_->maps.module_to_metrics[module]); } + cluster->setMetrics(metrics); debugPrint(logger_, MPL, @@ -1031,16 +1031,6 @@ void ClusteringEngine::setClusterMetrics(Cluster* cluster) cluster->getName(), metrics.getNumMacro(), metrics.getNumStdCell()); - - if (cluster->getClusterType() == HardMacroCluster) { - cluster->setMetrics( - Metrics(0, metrics.getNumMacro(), 0.0, metrics.getMacroArea())); - } else if (cluster->getClusterType() == StdCellCluster) { - cluster->setMetrics( - Metrics(metrics.getNumStdCell(), 0, metrics.getStdCellArea(), 0.0)); - } else { - cluster->setMetrics(metrics); - } } float ClusteringEngine::computeMicronArea(odb::dbInst* inst) From d5f4ff27a1fcdacae6e6b1edfedd7746b9500882 Mon Sep 17 00:00:00 2001 From: Arthur Koucher Date: Thu, 11 Jul 2024 11:17:16 -0300 Subject: [PATCH 29/30] mpl2: remove declaration Signed-off-by: Arthur Koucher --- src/mpl2/src/clusterEngine.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/mpl2/src/clusterEngine.h b/src/mpl2/src/clusterEngine.h index 43aff5daaee..d165a662a68 100644 --- a/src/mpl2/src/clusterEngine.h +++ b/src/mpl2/src/clusterEngine.h @@ -175,7 +175,6 @@ class ClusteringEngine void init(); Metrics* computeModuleMetrics(odb::dbModule* module); std::vector getUnfixedMacros(); - bool macroPlacementIsFeasible(const float core_area); float computeMacroWithHaloArea( const std::vector& unfixed_macros); void reportDesignData(float core_area); From 375d6c8b7e1e5353247604f3963f54c8d32c6d4c Mon Sep 17 00:00:00 2001 From: Arthur Koucher Date: Mon, 15 Jul 2024 17:52:39 -0300 Subject: [PATCH 30/30] mpl2: fix target structures pointers init / rename dataflow variables Signed-off-by: Arthur Koucher --- src/mpl2/src/clusterEngine.cpp | 28 ++++++++++++++-------------- src/mpl2/src/clusterEngine.h | 32 +++++++++++++++++++++----------- 2 files changed, 35 insertions(+), 25 deletions(-) diff --git a/src/mpl2/src/clusterEngine.cpp b/src/mpl2/src/clusterEngine.cpp index a6b970de720..bab4ee6898e 100644 --- a/src/mpl2/src/clusterEngine.cpp +++ b/src/mpl2/src/clusterEngine.cpp @@ -537,26 +537,26 @@ void ClusteringEngine::createDataFlow() for (auto [src, src_pin] : vertices_maps.id_to_bterm) { int idx = 0; std::vector visited(num_of_vertices, false); - std::vector> insts(data_flow.max_num_of_hops); + std::vector> insts(max_num_of_hops_); dataFlowDFSIOPin( src, idx, vertices_maps, hypergraph, insts, visited, false); dataFlowDFSIOPin(src, idx, vertices_maps, hypergraph, insts, visited, true); - data_flow.io_to_regs.emplace_back(src_pin, insts); + data_connections_.io_and_regs.emplace_back(src_pin, insts); } for (auto [src, src_pin] : vertices_maps.id_to_macro_pin) { int idx = 0; std::vector visited(num_of_vertices, false); - std::vector> std_cells(data_flow.max_num_of_hops); - std::vector> macros(data_flow.max_num_of_hops); + std::vector> std_cells(max_num_of_hops_); + std::vector> macros(max_num_of_hops_); dataFlowDFSMacroPin( src, idx, vertices_maps, hypergraph, std_cells, macros, visited, false); dataFlowDFSMacroPin( src, idx, vertices_maps, hypergraph, std_cells, macros, visited, true); - data_flow.macro_pin_to_regs.emplace_back(src_pin, std_cells); - data_flow.macro_pin_to_macros.emplace_back(src_pin, macros); + data_connections_.macro_pins_and_regs.emplace_back(src_pin, std_cells); + data_connections_.macro_pins_and_macros.emplace_back(src_pin, macros); } } @@ -734,7 +734,7 @@ void ClusteringEngine::dataFlowDFSIOPin( idx++; } - if (idx >= data_flow.max_num_of_hops) { + if (idx >= max_num_of_hops_) { return; } @@ -798,7 +798,7 @@ void ClusteringEngine::dataFlowDFSMacroPin( idx++; } - if (idx >= data_flow.max_num_of_hops) { + if (idx >= max_num_of_hops_) { return; } @@ -841,7 +841,7 @@ void ClusteringEngine::dataFlowDFSMacroPin( void ClusteringEngine::updateDataFlow() { // bterm, macros or ffs - for (const auto& [bterm, insts] : data_flow.io_to_regs) { + for (const auto& [bterm, insts] : data_connections_.io_and_regs) { if (tree_->maps.bterm_to_cluster_id.find(bterm) == tree_->maps.bterm_to_cluster_id.end()) { continue; @@ -849,7 +849,7 @@ void ClusteringEngine::updateDataFlow() const int driver_id = tree_->maps.bterm_to_cluster_id.at(bterm); - for (int hops = 0; hops < data_flow.max_num_of_hops; hops++) { + for (int hops = 0; hops < max_num_of_hops_; hops++) { std::set sink_clusters = computeSinks(insts[hops]); const float conn_weight = computeConnWeight(hops); for (auto& sink : sink_clusters) { @@ -860,10 +860,10 @@ void ClusteringEngine::updateDataFlow() } // macros to ffs - for (const auto& [iterm, insts] : data_flow.macro_pin_to_regs) { + for (const auto& [iterm, insts] : data_connections_.macro_pins_and_regs) { const int driver_id = tree_->maps.inst_to_cluster_id.at(iterm->getInst()); - for (int hops = 0; hops < data_flow.max_num_of_hops; hops++) { + for (int hops = 0; hops < max_num_of_hops_; hops++) { std::set sink_clusters = computeSinks(insts[hops]); const float conn_weight = computeConnWeight(hops); for (auto& sink : sink_clusters) { @@ -874,10 +874,10 @@ void ClusteringEngine::updateDataFlow() } // macros to macros - for (const auto& [iterm, insts] : data_flow.macro_pin_to_macros) { + for (const auto& [iterm, insts] : data_connections_.macro_pins_and_macros) { const int driver_id = tree_->maps.inst_to_cluster_id.at(iterm->getInst()); - for (int hops = 0; hops < data_flow.max_num_of_hops; hops++) { + for (int hops = 0; hops < max_num_of_hops_; hops++) { std::set sink_clusters = computeSinks(insts[hops]); const float conn_weight = computeConnWeight(hops); for (auto& sink : sink_clusters) { diff --git a/src/mpl2/src/clusterEngine.h b/src/mpl2/src/clusterEngine.h index d165a662a68..bb72d1fbbb7 100644 --- a/src/mpl2/src/clusterEngine.h +++ b/src/mpl2/src/clusterEngine.h @@ -85,16 +85,22 @@ struct VerticesMaps struct DataFlow { - // The register distance between two macros to - // them to be considered connected. - const int max_num_of_hops = 5; - + // Macro Pins --> Registers + // Registers --> Macro Pins std::vector>>> - macro_pin_to_regs; + macro_pins_and_regs; + + // IO --> Register + // Register --> IO + // IO --> Macro Pin + // Macro Pin --> IO std::vector>>> - io_to_regs; + io_and_regs; + + // Macro Pin --> Macros + // Macros --> Macro Pin std::vector>>> - macro_pin_to_macros; + macro_pins_and_macros; }; struct PhysicalHierarchyMaps @@ -259,8 +265,8 @@ class ClusteringEngine utl::Logger* logger_; par::PartitionMgr* triton_part_; - Metrics* design_metrics_; - PhysicalHierarchy* tree_; + Metrics* design_metrics_{nullptr}; + PhysicalHierarchy* tree_{nullptr}; int level_{0}; // Current level int id_{0}; // Current "highest" id @@ -270,10 +276,14 @@ class ClusteringEngine int min_macro_{0}; int max_std_cell_{0}; int min_std_cell_{0}; + const float size_tolerance_ = 0.1; - DataFlow data_flow; + // Variables for data flow + DataFlow data_connections_; - const float size_tolerance_ = 0.1; + // The register distance between two macros for + // them to be considered connected when creating data flow. + const int max_num_of_hops_ = 5; }; } // namespace mpl2 \ No newline at end of file