diff --git a/doc/src/vpr/command_line_usage.rst b/doc/src/vpr/command_line_usage.rst index 78338d75c5..699413725e 100644 --- a/doc/src/vpr/command_line_usage.rst +++ b/doc/src/vpr/command_line_usage.rst @@ -1336,6 +1336,15 @@ Analytical Placement is generally split into three stages: **Default:** ``1`` +.. option:: --ap_generate_mass_report {on | off} + + Controls whether to generate a report on how the partial legalizer + within the AP flow calculates the mass of primitives and the + capacity of tiles on the device. This report is useful when + debugging the partial legalizer. + + **Default:** ``off`` + .. _router_options: diff --git a/vpr/src/analytical_place/analytical_placement_flow.cpp b/vpr/src/analytical_place/analytical_placement_flow.cpp index cd07b60119..ec8712931a 100644 --- a/vpr/src/analytical_place/analytical_placement_flow.cpp +++ b/vpr/src/analytical_place/analytical_placement_flow.cpp @@ -144,6 +144,7 @@ static PartialPlacement run_global_placer(const t_ap_opts& ap_opts, device_ctx.physical_tile_types, pre_cluster_timing_manager, ap_opts.ap_timing_tradeoff, + ap_opts.generate_mass_report, ap_opts.num_threads, ap_opts.log_verbosity); return global_placer->place(); diff --git a/vpr/src/analytical_place/ap_mass_report.cpp b/vpr/src/analytical_place/ap_mass_report.cpp new file mode 100644 index 0000000000..30f3be6322 --- /dev/null +++ b/vpr/src/analytical_place/ap_mass_report.cpp @@ -0,0 +1,542 @@ +/** + * @file + * @author Alex Singer + * @date May 2025 + * @brief Implementation of the AP mass report generator. + */ + +#include "ap_mass_report.h" +#include +#include +#include +#include +#include +#include +#include "ap_netlist.h" +#include "ap_netlist_fwd.h" +#include "globals.h" +#include "logic_types.h" +#include "physical_types.h" +#include "physical_types_util.h" +#include "primitive_vector.h" +#include "vpr_context.h" +#include "vpr_error.h" +#include "vtr_time.h" +#include "vtr_vector.h" + +namespace { + +/** + * @brief A node in a printing tree. + */ +struct PrintingTreeNode { + /// @brief The name of this node. This will be printed when the tree is printed. + std::string name; + /// @brief The children of this node in the tree. + std::vector children; +}; + +/** + * @brief A printing tree. This tree contains basic information which will be + * used to print data in a good-looking form. + */ +struct PrintingTree { + /// @brief The root node of the tree. + PrintingTreeNode root; +}; + +/** + * @brief Recursively print the given tree node. + * + * @param node + * The node to print. + * @param os + * The output file stream to print the node to. + * @param prefix + * The prefix that all children of this node will print before their names. + */ +void print_tree_node_recur(const PrintingTreeNode& node, + std::ofstream& os, + const std::string& prefix) { + // Print the name of the node here and start a new line. + os << node.name << "\n"; + + // Print the children of this node. + size_t num_children = node.children.size(); + for (size_t child_idx = 0; child_idx < num_children; child_idx++) { + if (child_idx != num_children - 1) { + // If this is not the last child, print a vertical line which will + // be connected by the next child. This will be printed just before + // the node name of this child. + os << prefix << "├── "; + // Print the child node and update the prefix for any of its children. + // This prefix will connect the lines in a good looking way. + print_tree_node_recur(node.children[child_idx], os, prefix + "│ "); + } else { + // If this is the last child, we print an L shape to signify that + // there are no further children. + os << prefix << "└── "; + // Print the child node, and set the prefix to basically just be + // an indent. + print_tree_node_recur(node.children[child_idx], os, prefix + " "); + } + } +} + +/** + * @brief Helper function to print an entire printing tree. + * + * This method begins the recursion of printing using the proper prefix. + * + * @param tree + * The tree to print. + * @param os + * The output file stream to print to. + */ +void print_tree(const PrintingTree& tree, std::ofstream& os) { + print_tree_node_recur(tree.root, os, ""); +} + +/** + * @brief Generate the printing tree node for the given pb type. + * + * @param pb_type + * The pb type to generate the tree node for. + * @param models + * The logical models in the architecture. + */ +PrintingTreeNode gen_pb_printing_tree_node(const t_pb_type* pb_type, const LogicalModels& models); + +/** + * @brief Generate the printing tree node for the given mode. + * + * @param mode + * The mode to generate the tree node for. + * @param models + * The logical models in the architecture. + */ +PrintingTreeNode gen_mode_printing_tree_node(const t_mode& mode, const LogicalModels& models) { + // Create the node with the mode name. + PrintingTreeNode mode_node; + mode_node.name = std::string(mode.name) + " (mode)"; + + // Create the children. There will be one child for each pb in this mode. + mode_node.children.reserve(mode.num_pb_type_children); + for (int pb_child_idx = 0; pb_child_idx < mode.num_pb_type_children; pb_child_idx++) { + // Generate the child pb's node. + const t_pb_type& pb_type = mode.pb_type_children[pb_child_idx]; + PrintingTreeNode pb_node = gen_pb_printing_tree_node(&pb_type, models); + // Insert the node into the list of children. + mode_node.children.emplace_back(std::move(pb_node)); + } + + return mode_node; +} + +PrintingTreeNode gen_pb_printing_tree_node(const t_pb_type* pb_type, const LogicalModels& models) { + // Create the node with the pb name and the number of pbs. + PrintingTreeNode pb_node; + pb_node.name = std::string(pb_type->name) + " [" + std::to_string(pb_type->num_pb) + "]"; + if (!pb_type->is_primitive()) { + pb_node.name += " (pb_type)"; + } else { + // If this pb type is a primitive, print the name of the model as well. + LogicalModelId model_id = pb_type->model_id; + std::string model_name = models.model_name(model_id); + pb_node.name += " (primitive pb_type | model: " + model_name + ")"; + } + + // Create the children. There will be one child for each mode of this pb. + pb_node.children.reserve(pb_type->num_modes); + for (int mode_idx = 0; mode_idx < pb_type->num_modes; mode_idx++) { + // Generate the child mode's node. + const t_mode& mode = pb_type->modes[mode_idx]; + PrintingTreeNode mode_node = gen_mode_printing_tree_node(mode, models); + // Insert the node into the list of children. + pb_node.children.emplace_back(std::move(mode_node)); + } + + return pb_node; +} + +/** + * @brief Print the logical block type graph. + * + * This graph is a forest, as such we can print the forest in a pretty way in + * a text file. + */ +void print_logical_block_graph(std::ofstream& os, + const std::vector& logical_block_types, + const LogicalModels& models) { + // Generate the internal complex block trees. + // This is a DFS traversal of each complex block root in the forest. + os << "=================================================================\n"; + os << "Logical (Complex) Block Graph:\n"; + os << "=================================================================\n"; + os << "\n"; + + for (const t_logical_block_type& block_type : logical_block_types) { + // Set the root of the complex block tree to be the name of this logical + // block type. + PrintingTree complex_block_tree; + complex_block_tree.root.name = block_type.name + " (root logical block)"; + + // If this block has a pb type, generate the pb printing node (including + // its children) and add to the list of children. + if (block_type.pb_type != nullptr) { + PrintingTreeNode root_pb_node = gen_pb_printing_tree_node(block_type.pb_type, models); + complex_block_tree.root.children.emplace_back(std::move(root_pb_node)); + } + + // Print the tree to the file. + print_tree(complex_block_tree, os); + os << "\n"; + } +} + +/** + * @brief Print information on the physical tiles and how they relate to the + * logical block types. + */ +void print_physical_tiles(std::ofstream& os, + const std::vector& physical_tile_types) { + // Generate the physical tile relationships with the complex blocks. + os << "=================================================================\n"; + os << "Physical Tile Graph:\n"; + os << "=================================================================\n"; + os << "\n"; + + for (const t_physical_tile_type& tile_type : physical_tile_types) { + // Create a printing tree with a root name of the tile. + PrintingTree tile_tree; + tile_tree.root.name = tile_type.name + " (tile)"; + + // Create a child for each sub tile. + tile_tree.root.children.reserve(tile_type.sub_tiles.size()); + for (const auto& sub_tile : tile_type.sub_tiles) { + // Create a sub tile node with the name of the sub tile and the capacity. + PrintingTreeNode sub_tile_node; + sub_tile_node.name = sub_tile.name + " [" + std::to_string(sub_tile.capacity.total()) + "] (sub-tile)"; + + // Create a child for each equivalent site. + sub_tile_node.children.reserve(sub_tile.equivalent_sites.size()); + for (const auto& block_type : sub_tile.equivalent_sites) { + PrintingTreeNode block_type_node; + block_type_node.name = block_type->name + " (equiv-site)"; + + // Add this equivalent site to the parent sub-tile. + sub_tile_node.children.push_back(std::move(block_type_node)); + } + + // Add this sub-tile to the tile node. + tile_tree.root.children.push_back(std::move(sub_tile_node)); + } + + // Print the tree for this tile. + print_tree(tile_tree, os); + os << "\n"; + } +} + +/** + * @brief Prints all of the non-zero dimensions of the given primitive vector. + * + * @param os + * The output file stream to print the primtive vector to. + * @param primitive_vec + * The primitive vector to print. + * @param models + * All models in the architecture (used to get the name of the model. + * @param prefix + * The prefix to print ahead of each entry in the primitive vector print. + */ +void print_primitive_vector(std::ofstream& os, + const PrimitiveVector& primitive_vec, + const LogicalModels& models, + const std::string& prefix) { + std::vector contained_models = primitive_vec.get_non_zero_dims(); + + // Get the max name length of the contained models for pretty printing. + size_t max_model_name_len = 0; + for (int model_id : contained_models) { + std::string model_name = models.model_name((LogicalModelId)model_id); + max_model_name_len = std::max(max_model_name_len, + model_name.size()); + } + + // Print the capacity of each model. + for (int model_id : contained_models) { + std::string model_name = models.model_name((LogicalModelId)model_id); + os << prefix << std::setw(max_model_name_len) << model_name; + os << ": " << primitive_vec.get_dim_val(model_id); + os << "\n"; + } +} + +/** + * @brief Print information of the capacity of each logical block type. + */ +void print_logical_block_type_capacities(std::ofstream& os, + const std::vector& logical_block_types, + const std::vector& logical_block_type_capacities, + const LogicalModels& models) { + os << "=================================================================\n"; + os << "Logical Block Type Capacities:\n"; + os << "=================================================================\n"; + os << "\n"; + + // For each logical block type, print the capacity (in primitive-vector + // space). + for (const t_logical_block_type& block_type : logical_block_types) { + // Print the name of the block. + os << block_type.name << ":\n"; + + // Print the capacity of the logical block type. + const PrimitiveVector& cap = logical_block_type_capacities[block_type.index]; + print_primitive_vector(os, cap, models, "\t"); + os << "\n"; + } +} + +/** + * @brief Print information of the capacity of each physical tile on the device. + */ +void print_physical_tile_type_capacities(std::ofstream& os, + const std::vector& physical_tile_types, + const std::vector& physical_tile_type_capacities, + const LogicalModels& models) { + os << "=================================================================\n"; + os << "Physical Tile Type Capacities:\n"; + os << "=================================================================\n"; + os << "\n"; + + // For each physical tile type, print the capacity (in primitive-vector + // space). + for (const t_physical_tile_type& tile_type : physical_tile_types) { + // Print the name of the tile. + os << tile_type.name << ":\n"; + + // Print the capacity of the tile type. + const PrimitiveVector& cap = physical_tile_type_capacities[tile_type.index]; + print_primitive_vector(os, cap, models, "\t"); + os << "\n"; + } +} + +/** + * @brief Helper method for computing the total primitive vector mass of the + * given AP netlist. + */ +PrimitiveVector calc_total_netlist_mass(const APNetlist& ap_netlist, + const vtr::vector& block_mass) { + PrimitiveVector total_netlist_mass; + for (APBlockId ap_blk_id : ap_netlist.blocks()) { + total_netlist_mass += block_mass[ap_blk_id]; + } + + return total_netlist_mass; +} + +/** + * @brief Print information on how much mass the AP netlist uses relative to + * the overall device. + */ +void print_netlist_mass_utilization(std::ofstream& os, + const APNetlist& ap_netlist, + const vtr::vector& block_mass, + const std::vector& physical_tile_type_capacities, + const DeviceGrid& device_grid, + const LogicalModels& models) { + os << "=================================================================\n"; + os << "Netlist Mass Utilization:\n"; + os << "=================================================================\n"; + os << "\n"; + + // Get the capacity of all the physical tiles in the grid. + PrimitiveVector total_grid_capacity; + size_t grid_width = device_grid.width(); + size_t grid_height = device_grid.height(); + size_t layer = 0; + for (size_t x = 0; x < grid_width; x++) { + for (size_t y = 0; y < grid_height; y++) { + t_physical_tile_loc tile_loc(x, y, layer); + if (device_grid.get_width_offset(tile_loc) != 0 || device_grid.get_height_offset(tile_loc) != 0) + continue; + + auto tile_type = device_grid.get_physical_type(tile_loc); + total_grid_capacity += physical_tile_type_capacities[tile_type->index]; + } + } + + // Get the mass of all blocks in the netlist. + PrimitiveVector total_netlist_mass = calc_total_netlist_mass(ap_netlist, block_mass); + + PrimitiveVector netlist_per_model_counts; + for (APBlockId ap_blk_id : ap_netlist.blocks()) { + for (int dim : block_mass[ap_blk_id].get_non_zero_dims()) { + netlist_per_model_counts.add_val_to_dim(1, dim); + } + } + + // Get the max string length of any model to make the printing prettier. + size_t max_model_name_len = 0; + for (LogicalModelId model_id : models.all_models()) { + max_model_name_len = std::max(max_model_name_len, std::strlen(models.model_name(model_id).c_str())); + } + + // Print a breakdown of the mass utilization of the netlist. + os << std::setw(max_model_name_len) << "Model"; + os << ": Total Netlist Mass | Total Grid Mass | Mass Utilization\n"; + for (LogicalModelId model_id : models.all_models()) { + float model_netlist_mass = total_netlist_mass.get_dim_val((size_t)model_id); + float model_grid_capacity = total_grid_capacity.get_dim_val((size_t)model_id); + os << std::setw(max_model_name_len) << models.model_name(model_id); + os << ": " << std::setw(18) << model_netlist_mass; + os << " | " << std::setw(15) << model_grid_capacity; + os << " | " << std::setw(16) << model_netlist_mass / model_grid_capacity; + os << "\n"; + } + os << "\n"; + + os << std::setw(max_model_name_len) << "Model"; + os << ": Total Netlist Mass | Number of Blocks | Average Mass per Block\n"; + for (LogicalModelId model_id : models.all_models()) { + float model_netlist_mass = total_netlist_mass.get_dim_val((size_t)model_id); + float num_blocks = netlist_per_model_counts.get_dim_val((size_t)model_id); + float average_mass_per_block = 0.0f; + if (num_blocks > 0.0f) { + average_mass_per_block = model_netlist_mass / num_blocks; + } + os << std::setw(max_model_name_len) << models.model_name(model_id); + os << ": " << std::setw(18) << model_netlist_mass; + os << " | " << std::setw(16) << num_blocks; + os << " | " << std::setw(22) << average_mass_per_block; + os << "\n"; + } + os << "\n"; +} + +/** + * @brief Uses the mass of the netlist and the mass of the logical block types + * to predict the device utilization for the given device grid. + */ +void print_expected_device_utilization(std::ofstream& os, + const APNetlist& ap_netlist, + const vtr::vector& block_mass, + const std::vector& logical_block_types, + const std::vector& logical_block_type_capacities, + const DeviceGrid& device_grid) { + os << "=================================================================\n"; + os << "Expected Device Utilization:\n"; + os << "=================================================================\n"; + os << "\n"; + + // Get the total mass of the netlist. + PrimitiveVector total_netlist_mass = calc_total_netlist_mass(ap_netlist, block_mass); + + // Get the expected number of instances of each logical block type. + std::vector num_type_instances(logical_block_types.size(), 0); + for (const t_logical_block_type& block_type : logical_block_types) { + // For each logical block type, estimate the number of blocks of that type + // We can estimate this value as being the maximum required number of + // instances to support the most utilized model. + const PrimitiveVector block_type_cap = logical_block_type_capacities[block_type.index]; + unsigned num_blocks_of_this_type = 0; + for (int model_id : block_type_cap.get_non_zero_dims()) { + float netlist_model_mass = total_netlist_mass.get_dim_val(model_id); + float model_mass_per_block = block_type_cap.get_dim_val(model_id); + unsigned num_blocks_needed_for_model = std::ceil(netlist_model_mass / model_mass_per_block); + num_blocks_of_this_type = std::max(num_blocks_of_this_type, num_blocks_needed_for_model); + } + + num_type_instances[block_type.index] = num_blocks_of_this_type; + } + + // Get the max logical block type name length for pretty printing. + size_t max_logical_block_name_len = 0; + for (const t_logical_block_type& block_type : logical_block_types) { + max_logical_block_name_len = std::max(max_logical_block_name_len, block_type.name.size()); + } + + // Print the expected number of logical blocks and the expected block utilization. + // Note: These may be innacurate if a model appears in multiple different + // logical blocks. + // TODO: Investigate resolving this issue. + os << "Expected number of logical blocks:\n"; + for (const t_logical_block_type& block_type : logical_block_types) { + if (block_type.is_empty()) + continue; + os << "\t" << std::setw(max_logical_block_name_len) << block_type.name; + os << ": " << num_type_instances[block_type.index]; + os << "\n"; + } + os << "\n"; + + os << "Expected block utilization:\n"; + for (const t_logical_block_type& block_type : logical_block_types) { + if (block_type.is_empty()) + continue; + // Get the number of instances of this logical block in this device. + size_t num_inst = device_grid.num_instances(pick_physical_type(&block_type), -1); + + // Estimate the utilization as being the expected number of instances + // divided by the number of instances possible on the device. + float util = 0.0f; + if (num_inst > 0) { + util = static_cast(num_type_instances[block_type.index]); + util /= static_cast(num_inst); + } + os << "\t" << std::setw(max_logical_block_name_len) << block_type.name; + os << ": " << util; + os << "\n"; + } + os << "\n"; +} + +} // namespace + +void generate_ap_mass_report(const std::vector& logical_block_type_capacities, + const std::vector& physical_tile_type_capacities, + const vtr::vector& block_mass, + const APNetlist& ap_netlist) { + + vtr::ScopedStartFinishTimer timer("Generating AP Mass Report"); + + // Load device data which is used to calculate the data in the report. + const DeviceContext& device_ctx = g_vpr_ctx.device(); + const std::vector& physical_tile_types = device_ctx.physical_tile_types; + const std::vector& logical_block_types = device_ctx.logical_block_types; + const LogicalModels& models = device_ctx.arch->models; + const DeviceGrid& device_grid = device_ctx.grid; + + // Open the AP mass report file as an output file stream. + std::string mass_report_file_name = "ap_mass.rpt"; + std::ofstream os(mass_report_file_name); + if (!os.is_open()) { + VPR_FATAL_ERROR(VPR_ERROR_AP, + "Unable to open AP mass report file"); + return; + } + + // Print the logical block graph. + print_logical_block_graph(os, logical_block_types, models); + + // Print information on the physical tiles. + print_physical_tiles(os, physical_tile_types); + + // TODO: Print a lookup between the model names and IDs + + // Print the computed capacities of each logical block type. + print_logical_block_type_capacities(os, logical_block_types, logical_block_type_capacities, models); + + // Print the computed capacities of each physical tile type. + print_physical_tile_type_capacities(os, physical_tile_types, physical_tile_type_capacities, models); + + // Print information on how much mass is utilized by the netlist relative + // to the device. + print_netlist_mass_utilization(os, ap_netlist, block_mass, physical_tile_type_capacities, device_grid, models); + + // Print the expected device utilization, given the mass of the netlist + // and the capacity of the device grid. + print_expected_device_utilization(os, ap_netlist, block_mass, logical_block_types, logical_block_type_capacities, device_grid); +} diff --git a/vpr/src/analytical_place/ap_mass_report.h b/vpr/src/analytical_place/ap_mass_report.h new file mode 100644 index 0000000000..f5a9eb0f0e --- /dev/null +++ b/vpr/src/analytical_place/ap_mass_report.h @@ -0,0 +1,33 @@ +#pragma once +/** + * @file + * @author Alex Singer + * @date May 2025 + * @brief Methods for generating mass reports. Mass reports contain information + * on the AP partial legalizer's internal representation of mass for + * primitives and tiles. + */ + +#include +#include +#include "vtr_vector.h" + +// Forward declarations +class PrimitiveVector; + +/** + * @brief Generate a mass report for the given AP netlist with the given block + * masses and tile / logic block capacities. + * + * @param logical_block_type_capacities + * The primitive-vector capacity of each logical block on the device. + * @param physical_tile_type_capacities + * The primitive-vector capacity of each tile on the device. + * @param block_mass + * The mass of each AP block in the AP netlist. + * @param ap_netlist + */ +void generate_ap_mass_report(const std::vector& logical_block_type_capacities, + const std::vector& physical_tile_type_capacities, + const vtr::vector& block_mass, + const APNetlist& ap_netlist); diff --git a/vpr/src/analytical_place/flat_placement_density_manager.cpp b/vpr/src/analytical_place/flat_placement_density_manager.cpp index f4c37b191c..71fc33decf 100644 --- a/vpr/src/analytical_place/flat_placement_density_manager.cpp +++ b/vpr/src/analytical_place/flat_placement_density_manager.cpp @@ -307,3 +307,7 @@ void FlatPlacementDensityManager::print_bin_grid() const { } VTR_LOG("\n"); } + +void FlatPlacementDensityManager::generate_mass_report() const { + mass_calculator_.generate_mass_report(ap_netlist_); +} diff --git a/vpr/src/analytical_place/flat_placement_density_manager.h b/vpr/src/analytical_place/flat_placement_density_manager.h index 40b9e7fa9b..403d36dd20 100644 --- a/vpr/src/analytical_place/flat_placement_density_manager.h +++ b/vpr/src/analytical_place/flat_placement_density_manager.h @@ -235,6 +235,12 @@ class FlatPlacementDensityManager { */ void print_bin_grid() const; + /** + * @brief Generate a report on the mass calculations within the density + * manager class. + */ + void generate_mass_report() const; + private: /// @brief The AP netlist of blocks which are filling the bins. const APNetlist& ap_netlist_; diff --git a/vpr/src/analytical_place/flat_placement_mass_calculator.cpp b/vpr/src/analytical_place/flat_placement_mass_calculator.cpp index 3581c4ce86..b50a33b57e 100644 --- a/vpr/src/analytical_place/flat_placement_mass_calculator.cpp +++ b/vpr/src/analytical_place/flat_placement_mass_calculator.cpp @@ -7,9 +7,9 @@ #include "flat_placement_mass_calculator.h" #include +#include "ap_mass_report.h" #include "ap_netlist.h" #include "atom_netlist.h" -#include "globals.h" #include "logic_types.h" #include "physical_types.h" #include "prepack.h" @@ -173,53 +173,6 @@ static PrimitiveVector calc_block_mass(APBlockId blk_id, return mass; } -/** - * @brief Debug printing method to print the capacities of all logical blocks - * and physical tile types. - */ -static void print_capacities(const std::vector& logical_block_type_capacities, - const std::vector& physical_tile_type_capacities, - const std::vector& logical_block_types, - const std::vector& physical_tile_types) { - // TODO: Pass these into this function. - const LogicalModels& models = g_vpr_ctx.device().arch->models; - - // Print the capacities. - VTR_LOG("Logical Block Type Capacities:\n"); - VTR_LOG("------------------------------\n"); - VTR_LOG("name\t"); - for (LogicalModelId model_id : models.all_models()) { - VTR_LOG("%s\t", models.get_model(model_id).name); - } - VTR_LOG("\n"); - for (const t_logical_block_type& block_type : logical_block_types) { - const PrimitiveVector& capacity = logical_block_type_capacities[block_type.index]; - VTR_LOG("%s\t", block_type.name.c_str()); - for (LogicalModelId model_id : models.all_models()) { - VTR_LOG("%.2f\t", capacity.get_dim_val((size_t)model_id)); - } - VTR_LOG("\n"); - } - VTR_LOG("\n"); - VTR_LOG("Physical Tile Type Capacities:\n"); - VTR_LOG("------------------------------\n"); - VTR_LOG("name\t"); - for (LogicalModelId model_id : models.all_models()) { - VTR_LOG("%s\t", models.get_model(model_id).name); - } - VTR_LOG("\n"); - for (const t_physical_tile_type& tile_type : physical_tile_types) { - const PrimitiveVector& capacity = physical_tile_type_capacities[tile_type.index]; - VTR_LOG("%s\t", tile_type.name.c_str()); - for (LogicalModelId model_id : models.all_models()) { - VTR_LOG("%.2f\t", capacity.get_dim_val((size_t)model_id)); - } - VTR_LOG("\n"); - } - VTR_LOG("\n"); - // TODO: Print the masses of each model. -} - FlatPlacementMassCalculator::FlatPlacementMassCalculator(const APNetlist& ap_netlist, const Prepacker& prepacker, const AtomNetlist& atom_netlist, @@ -250,12 +203,11 @@ FlatPlacementMassCalculator::FlatPlacementMassCalculator(const APNetlist& ap_net atom_netlist); } VTR_LOGV(log_verbosity_ >= 10, "Finished pre-computing the block masses.\n"); +} - // Print the precomputed block capacities. This can be helpful for debugging. - if (log_verbosity_ > 1) { - print_capacities(logical_block_type_capacity_, - physical_tile_type_capacity_, - logical_block_types, - physical_tile_types); - } +void FlatPlacementMassCalculator::generate_mass_report(const APNetlist& ap_netlist) const { + generate_ap_mass_report(logical_block_type_capacity_, + physical_tile_type_capacity_, + block_mass_, + ap_netlist); } diff --git a/vpr/src/analytical_place/flat_placement_mass_calculator.h b/vpr/src/analytical_place/flat_placement_mass_calculator.h index aea36c464f..d72d04288a 100644 --- a/vpr/src/analytical_place/flat_placement_mass_calculator.h +++ b/vpr/src/analytical_place/flat_placement_mass_calculator.h @@ -96,6 +96,12 @@ class FlatPlacementMassCalculator { return block_mass_[blk_id]; } + /** + * @brief Generate a report on the mass and capacities calculated by this + * class. + */ + void generate_mass_report(const APNetlist& ap_netlist) const; + private: /// @brief The capacity of each physical tile type, indexed by the index /// of the physical_tile_type. diff --git a/vpr/src/analytical_place/global_placer.cpp b/vpr/src/analytical_place/global_placer.cpp index ebf506a343..6912eb2a62 100644 --- a/vpr/src/analytical_place/global_placer.cpp +++ b/vpr/src/analytical_place/global_placer.cpp @@ -37,6 +37,7 @@ std::unique_ptr make_global_placer(e_ap_analytical_solver analytic const std::vector& physical_tile_types, const PreClusterTimingManager& pre_cluster_timing_manager, float ap_timing_tradeoff, + bool generate_mass_report, unsigned num_threads, int log_verbosity) { return std::make_unique(analytical_solver_type, @@ -49,6 +50,7 @@ std::unique_ptr make_global_placer(e_ap_analytical_solver analytic physical_tile_types, pre_cluster_timing_manager, ap_timing_tradeoff, + generate_mass_report, num_threads, log_verbosity); } @@ -63,6 +65,7 @@ SimPLGlobalPlacer::SimPLGlobalPlacer(e_ap_analytical_solver analytical_solver_ty const std::vector& physical_tile_types, const PreClusterTimingManager& pre_cluster_timing_manager, float ap_timing_tradeoff, + bool generate_mass_report, unsigned num_threads, int log_verbosity) : GlobalPlacer(ap_netlist, log_verbosity) { @@ -90,6 +93,8 @@ SimPLGlobalPlacer::SimPLGlobalPlacer(e_ap_analytical_solver analytical_solver_ty logical_block_types, physical_tile_types, log_verbosity_); + if (generate_mass_report) + density_manager_->generate_mass_report(); // Build the partial legalizer VTR_LOGV(log_verbosity_ >= 10, "\tBuilding the partial legalizer...\n"); diff --git a/vpr/src/analytical_place/global_placer.h b/vpr/src/analytical_place/global_placer.h index 4b1bb21b2f..5d0abb1212 100644 --- a/vpr/src/analytical_place/global_placer.h +++ b/vpr/src/analytical_place/global_placer.h @@ -82,6 +82,7 @@ std::unique_ptr make_global_placer(e_ap_analytical_solver analytic const std::vector& physical_tile_types, const PreClusterTimingManager& pre_cluster_timing_manager, float ap_timing_tradeoff, + bool generate_mass_report, unsigned num_threads, int log_verbosity); @@ -148,6 +149,7 @@ class SimPLGlobalPlacer : public GlobalPlacer { const std::vector& physical_tile_types, const PreClusterTimingManager& pre_cluster_timing_manager, float ap_timing_tradeoff, + bool generate_mass_report, unsigned num_threads, int log_verbosity); diff --git a/vpr/src/base/SetupVPR.cpp b/vpr/src/base/SetupVPR.cpp index 676f6378bd..6a41cc633c 100644 --- a/vpr/src/base/SetupVPR.cpp +++ b/vpr/src/base/SetupVPR.cpp @@ -561,6 +561,7 @@ void SetupAPOpts(const t_options& options, apOpts.appack_max_dist_th = options.appack_max_dist_th.value(); apOpts.num_threads = options.num_workers.value(); apOpts.log_verbosity = options.ap_verbosity.value(); + apOpts.generate_mass_report = options.ap_generate_mass_report.value(); } /** diff --git a/vpr/src/base/read_options.cpp b/vpr/src/base/read_options.cpp index 75d6340973..a9da9c8486 100644 --- a/vpr/src/base/read_options.cpp +++ b/vpr/src/base/read_options.cpp @@ -1983,6 +1983,15 @@ argparse::ArgumentParser create_arg_parser(const std::string& prog_name, t_optio .default_value("1") .show_in(argparse::ShowIn::HELP_ONLY); + ap_grp.add_argument(args.ap_generate_mass_report, "--ap_generate_mass_report") + .help( + "Controls whether to generate a report on how the partial legalizer " + "within the AP flow calculates the mass of primitives and the " + "capacity of tiles on the device. This report is useful when " + "debugging the partial legalizer.") + .default_value("off") + .show_in(argparse::ShowIn::HELP_ONLY); + auto& pack_grp = parser.add_argument_group("packing options"); pack_grp.add_argument(args.connection_driven_clustering, "--connection_driven_clustering") diff --git a/vpr/src/base/read_options.h b/vpr/src/base/read_options.h index 73577e8597..949fefdde6 100644 --- a/vpr/src/base/read_options.h +++ b/vpr/src/base/read_options.h @@ -106,6 +106,7 @@ struct t_options { argparse::ArgValue> appack_max_dist_th; argparse::ArgValue ap_verbosity; argparse::ArgValue ap_timing_tradeoff; + argparse::ArgValue ap_generate_mass_report; /* Clustering options */ argparse::ArgValue connection_driven_clustering; diff --git a/vpr/src/base/vpr_types.h b/vpr/src/base/vpr_types.h index 02401b339f..1f8a19623b 100644 --- a/vpr/src/base/vpr_types.h +++ b/vpr/src/base/vpr_types.h @@ -1103,6 +1103,8 @@ struct t_placer_opts { * @param log_verbosity * The verbosity level of log messages in the AP flow, with higher * values leading to more verbose messages. + * @param generate_mass_report + * Whether to generate a mass report during global placement or not. */ struct t_ap_opts { e_stage_action doAP; @@ -1122,6 +1124,8 @@ struct t_ap_opts { unsigned num_threads; int log_verbosity; + + bool generate_mass_report; }; /****************************************************************** diff --git a/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_ap/gen_mass_report/config/config.txt b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_ap/gen_mass_report/config/config.txt new file mode 100644 index 0000000000..2ecd91cf0e --- /dev/null +++ b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_ap/gen_mass_report/config/config.txt @@ -0,0 +1,28 @@ +############################################## +# Configuration file for running experiments +############################################## + +# Path to directory of circuits to use +circuits_dir=benchmarks/verilog + +# Path to directory of architectures to use +archs_dir=arch/timing/fixed_size + +# Add circuits to list to sweep +circuit_list_add=single_wire.v + +# Add architectures to list to sweep +arch_list_add=fixed_k6_frac_N8_22nm.xml + +# Parse info and how to parse +parse_file=vpr_standard.txt + +# How to parse QoR info +qor_parse_file=qor_standard.txt + +# Pass requirements +pass_requirements_file=pass_requirements_ap.txt + +# Script parameters +script_params_common=-track_memory_usage --analytical_place --route --ap_generate_mass_report on + diff --git a/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_ap/gen_mass_report/config/golden_results.txt b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_ap/gen_mass_report/config/golden_results.txt new file mode 100644 index 0000000000..1ce7736a27 --- /dev/null +++ b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_ap/gen_mass_report/config/golden_results.txt @@ -0,0 +1,2 @@ +arch circuit script_params vtr_flow_elapsed_time vtr_max_mem_stage vtr_max_mem error odin_synth_time max_odin_mem parmys_synth_time max_parmys_mem abc_depth abc_synth_time abc_cec_time abc_sec_time max_abc_mem ace_time max_ace_mem num_clb num_io num_memories num_mult vpr_status vpr_revision vpr_build_info vpr_compiler vpr_compiled hostname rundir max_vpr_mem num_primary_inputs num_primary_outputs num_pre_packed_nets num_pre_packed_blocks num_netlist_clocks num_post_packed_nets num_post_packed_blocks device_width device_height device_grid_tiles device_limiting_resources device_name pack_mem pack_time initial_placed_wirelength_est placed_wirelength_est total_swap accepted_swap rejected_swap aborted_swap place_mem place_time place_quench_time initial_placed_CPD_est placed_CPD_est placed_setup_TNS_est placed_setup_WNS_est placed_geomean_nonvirtual_intradomain_critical_path_delay_est place_delay_matrix_lookup_time place_quench_timing_analysis_time place_quench_sta_time place_total_timing_analysis_time place_total_sta_time ap_mem ap_time ap_full_legalizer_mem ap_full_legalizer_time min_chan_width routed_wirelength min_chan_width_route_success_iteration logic_block_area_total logic_block_area_used min_chan_width_routing_area_total min_chan_width_routing_area_per_tile min_chan_width_route_time min_chan_width_total_timing_analysis_time min_chan_width_total_sta_time crit_path_num_rr_graph_nodes crit_path_num_rr_graph_edges crit_path_collapsed_nodes crit_path_routed_wirelength crit_path_route_success_iteration crit_path_total_nets_routed crit_path_total_connections_routed crit_path_total_heap_pushes crit_path_total_heap_pops critical_path_delay geomean_nonvirtual_intradomain_critical_path_delay setup_TNS setup_WNS hold_TNS hold_WNS crit_path_routing_area_total crit_path_routing_area_per_tile router_lookahead_computation_time crit_path_route_time crit_path_create_rr_graph_time crit_path_create_intra_cluster_rr_graph_time crit_path_tile_lookahead_computation_time crit_path_router_lookahead_computation_time crit_path_total_timing_analysis_time crit_path_total_sta_time +fixed_k6_frac_N8_22nm.xml single_wire.v common 1.34 vpr 76.20 MiB -1 -1 0.07 20448 1 0.02 -1 -1 33176 -1 -1 0 1 0 0 success v8.0.0-12842-ge95d71165b-dirty release VTR_ASSERT_LEVEL=3 GNU 13.3.0 on Linux-6.8.0-58-generic x86_64 2025-05-29T18:09:19 srivatsan-Precision-Tower-5810 /home/alex/vtr-verilog-to-routing 78028 1 1 1 0 0 1 2 17 17 289 -1 unnamed_device -1 -1 2 2 3 0 3 0 76.2 MiB 0.46 0.00 0.2714 0.2714 -0.2714 -0.2714 nan 0.38 9.051e-06 5.955e-06 6.3309e-05 4.4576e-05 76.2 MiB 0.46 76.2 MiB 0.07 8 4 1 6.79088e+06 0 166176. 575.005 0.14 0.000829136 0.000770258 20206 45088 -1 4 1 1 1 21 11 0.2714 nan -0.2714 -0.2714 0 0 202963. 702.294 0.01 0.00 0.04 -1 -1 0.01 0.000785867 0.000731474