diff --git a/doc/src/vpr/command_line_usage.rst b/doc/src/vpr/command_line_usage.rst index 26047cf70f..3f388eebed 100644 --- a/doc/src/vpr/command_line_usage.rst +++ b/doc/src/vpr/command_line_usage.rst @@ -1281,6 +1281,40 @@ Analytical Placement is generally split into three stages: **Default:** ``0.5`` +.. option:: --ap_partial_legalizer_target_density { auto | :, } + + Sets the target density of different physical tiles on the FPGA device + for the partial legalizer in the AP flow. The partial legalizer will + try to fill tiles up to (but not beyond) this target density. This + is used as a guide, the legalizer may not follow this if it must fill + the tile more. + + The partial legalizer uses an abstraction called "mass" to describe the resources + used by a set of primitives in the netlist and the capacity of resources in a + given tile. For primitives like LUTs, FFs, and DSPs this mass can be thought of + as the number of pins used (but not exactly). For memories, this mass can be + thought of as the number of bits stored. This target density parameter lowers + the mass capacity of tiles. + + When this option is set ot auto, VPR will select good values for the + target density of tiles. + + reasonable values are between 0.0 and 1.0, with negative values not being allowed. + + This option is similar to appack_max_dist_th, where a regex string + is used to set the target density of different physical tiles. + + For example: + + .. code-block:: none + + --ap_partial_legalizer_target_density .*:0.9 "clb|memory:0.8" + + Would set the target density of all physical tiles to be 0.9, except for the clb and + memory tiles, which will be set to a target density of 0.8. + + **Default:** ``auto`` + .. option:: --appack_max_dist_th { auto | :, } Sets the maximum candidate distance thresholds for the logical block types diff --git a/vpr/src/analytical_place/analytical_placement_flow.cpp b/vpr/src/analytical_place/analytical_placement_flow.cpp index 83b9b843a6..e54d016687 100644 --- a/vpr/src/analytical_place/analytical_placement_flow.cpp +++ b/vpr/src/analytical_place/analytical_placement_flow.cpp @@ -156,6 +156,7 @@ static PartialPlacement run_global_placer(const t_ap_opts& ap_opts, place_delay_model, ap_opts.ap_timing_tradeoff, ap_opts.generate_mass_report, + ap_opts.ap_partial_legalizer_target_density, ap_opts.num_threads, ap_opts.log_verbosity); return global_placer->place(); diff --git a/vpr/src/analytical_place/ap_argparse_utils.cpp b/vpr/src/analytical_place/ap_argparse_utils.cpp new file mode 100644 index 0000000000..c58863c25d --- /dev/null +++ b/vpr/src/analytical_place/ap_argparse_utils.cpp @@ -0,0 +1,156 @@ +/** + * @file + * @author Alex Singer + * @date June 2025 + * @brief Implementation of utility functions used for parsing AP arguments. + */ +#include "ap_argparse_utils.h" +#include +#include +#include +#include +#include "vtr_assert.h" +#include "vtr_log.h" +#include "vpr_error.h" + +/** + * @brief Helper method to convert a string into a float with error checking. + */ +static float str_to_float_or_error(const std::string& str); + +/** + * @brief Parse the given key, value string argument. The string is expected to + * be of the form: + * ":,," + * + * This method returns a tuple containing the regex string and a vector of the + * values. The vector will be of the length expected_num_vals_per_key. + */ +static std::tuple> +parse_key_val_arg(const std::string& arg, unsigned expected_num_vals_per_key); + +std::unordered_map> +key_to_float_argument_parser(const std::vector& arg_vals, + const std::vector& valid_keys, + unsigned expected_num_vals_per_key) { + + // Create the key to float map which will be returned from this method. + std::unordered_map> key_to_float_map; + + // Go through each of the arguments to parse. + for (const std::string& arg_val : arg_vals) { + // Parse this argument. + // Key is the regex string, vals is the vector of values. + auto [key, vals] = parse_key_val_arg(arg_val, expected_num_vals_per_key); + + // Create a regex object to be used to match for valid keys. + std::regex key_regex(key); + + // Go through each valid key and find which ones match the regex. + bool found_match = false; + for (const std::string& valid_key : valid_keys) { + bool is_match = std::regex_match(valid_key, key_regex); + if (!is_match) + continue; + + // If this key matches the regex, set the map to the given values. + key_to_float_map[valid_key] = vals; + found_match = true; + } + + // If no match is found for this key regex, raise a warning to the user. + // They may have made a mistake and may want to be warned about it. + if (!found_match) { + VTR_LOG_WARN("Unable to find a valid key that matches regex pattern: %s\n", + key.c_str()); + } + } + + // Return the map. + return key_to_float_map; +} + +static std::tuple> +parse_key_val_arg(const std::string& arg, unsigned expected_num_vals_per_key) { + // Verify the format of the string. It must have one and only one colon. + unsigned colon_count = 0; + for (char c : arg) { + if (c == ':') + colon_count++; + } + if (colon_count != 1) { + VTR_LOG_ERROR("Invalid argument string: %s\n", + arg.c_str()); + VPR_FATAL_ERROR(VPR_ERROR_PACK, + "Error when parsing argument string"); + } + + // Split the string along the colon. + auto del_pos = arg.find(':'); + std::string key_regex_str = arg.substr(0, del_pos); + std::string val_list_str = arg.substr(del_pos + 1, std::string::npos); + + // Verify that there are a correct number of commas given the expected number + // of values. + unsigned comma_count = 0; + for (char c : val_list_str) { + if (c == ',') + comma_count++; + } + if (comma_count != expected_num_vals_per_key - 1) { + VTR_LOG_ERROR("Invalid argument string (too many commas): %s\n", + arg.c_str()); + VPR_FATAL_ERROR(VPR_ERROR_PACK, + "Error when parsing argument string"); + } + + // Collect the comma seperated values into a vector. + std::vector vals; + vals.reserve(expected_num_vals_per_key); + + // As we are reading each comma-seperated value, keep track of the current + // part of the string we are reading. We read from left to right. + std::string acc_val_list_str = val_list_str; + + // For each expected value up to the last one, parse the current value before + // the comma. + VTR_ASSERT(expected_num_vals_per_key > 0); + for (unsigned i = 0; i < expected_num_vals_per_key - 1; i++) { + // Split the string before and after the comma. + auto comma_pos = acc_val_list_str.find(","); + VTR_ASSERT(comma_pos != std::string::npos); + std::string current_val_str = val_list_str.substr(0, comma_pos); + // Send the string after the comma to the next iteration. + acc_val_list_str = val_list_str.substr(comma_pos + 1, std::string::npos); + + // Cast the string before the comma into a float and store it. + float current_val = str_to_float_or_error(current_val_str); + vals.push_back(current_val); + } + + // Parse the last value in the list. This one should not have a comma in it. + VTR_ASSERT(acc_val_list_str.find(",") == std::string::npos); + float last_val = str_to_float_or_error(acc_val_list_str); + vals.push_back(last_val); + + // Return the results as a tuple. + return std::make_tuple(key_regex_str, vals); +} + +static float str_to_float_or_error(const std::string& str) { + float val = -1; + try { + val = std::stof(str); + } catch (const std::invalid_argument& e) { + VTR_LOG_ERROR("Error while parsing float arg value: %s\n" + "Failed with invalid argument: %s\n", + str.c_str(), + e.what()); + } catch (const std::out_of_range& e) { + VTR_LOG_ERROR("Error while parsing float arg value: %s\n" + "Failed with out of range: %s\n", + str.c_str(), + e.what()); + } + return val; +} diff --git a/vpr/src/analytical_place/ap_argparse_utils.h b/vpr/src/analytical_place/ap_argparse_utils.h new file mode 100644 index 0000000000..ba48203592 --- /dev/null +++ b/vpr/src/analytical_place/ap_argparse_utils.h @@ -0,0 +1,44 @@ +#pragma once +/** + * @file + * @author Alex Singer + * @date June 2025 + * @brief Delcarations of utility functions used to parse AP options. + */ + +#include +#include +#include + +/** + * @brief Parser method for parsing a list of arguments of the form: + * ":,,,..." + * + * This method will will return a map containing the value for each key matched. + * The map will not contain an entry for a key that was not set by the arguments. + * + * Example usage: + * // Create a list of valid keys. + * std::vector valid_keys = {"foo", "bar"} + * + * // User passed regex args. Sets all values to {0.5, 0.5, 0.5} THEN sets + * // "foo" specifically to {0.1, 0.2, 0.3}. + * // NOTE: Arguments are read left to right (first to last). + * std::vector arg_vals = {".*:0.5,0.5,0.5", "foo:0.1,0.2,0.3"} + * + * auto key_to_val_map = key_to_float_argument_parser(arg_vals, + * valid_keys, + * 3); + * // Map will contain {0.1, 0.2, 0.3} for "foo" and {0.5, 0.5, 0.5} for "bar" + * + * @param arg_vals + * The list of arguments to parse. + * @param valid_keys + * A list of valid keys that the argument regex patterns can match for. + * @param expected_num_vals_per_key + * The expected number of floating point values per key. + */ +std::unordered_map> +key_to_float_argument_parser(const std::vector& arg_vals, + const std::vector& valid_keys, + unsigned expected_num_vals_per_key = 1); diff --git a/vpr/src/analytical_place/flat_placement_density_manager.cpp b/vpr/src/analytical_place/flat_placement_density_manager.cpp index 2899981943..93f7021c22 100644 --- a/vpr/src/analytical_place/flat_placement_density_manager.cpp +++ b/vpr/src/analytical_place/flat_placement_density_manager.cpp @@ -7,6 +7,8 @@ #include "flat_placement_density_manager.h" #include +#include +#include "ap_argparse_utils.h" #include "ap_netlist.h" #include "ap_netlist_fwd.h" #include "atom_netlist.h" @@ -18,6 +20,8 @@ #include "prepack.h" #include "primitive_dim_manager.h" #include "primitive_vector.h" +#include "vpr_error.h" +#include "vpr_utils.h" #include "vtr_assert.h" #include "vtr_geometry.h" #include "vtr_vector.h" @@ -45,6 +49,60 @@ static PrimitiveVector calc_bin_underfill(const PrimitiveVector& bin_utilization return underfill; } +/** + * @brief Get the physical type target densities given the user arguments. + * + * This will automatically select good target densisities, but will allow the + * user to override these values from the command line. + * + * @param target_density_arg_strs + * The command-line arguments provided by the user. + * @param physical_tile_types + * A vector of all physical tile types in the architecture. + */ +static std::vector get_physical_type_target_densities(const std::vector& target_density_arg_strs, + const std::vector& physical_tile_types) { + // Get the target densisty of each physical block type. + // TODO: Create auto feature to automatically select target densities based + // on properties of the architecture. Need to sweep to find reasonable + // values. + std::vector phy_ty_target_density(physical_tile_types.size(), 1.0f); + + // Set to auto if no user args are provided. + if (target_density_arg_strs.size() == 0) + return phy_ty_target_density; + if (target_density_arg_strs.size() == 1 && target_density_arg_strs[0] == "auto") + return phy_ty_target_density; + + // Parse the user args. The physical type names are expected to be used as keys. + std::vector phy_ty_names; + phy_ty_names.reserve(physical_tile_types.size()); + std::unordered_map phy_ty_name_to_index; + for (const t_physical_tile_type& phy_ty : physical_tile_types) { + phy_ty_names.push_back(phy_ty.name); + phy_ty_name_to_index[phy_ty.name] = phy_ty.index; + } + auto phy_ty_name_to_tar_density = key_to_float_argument_parser(target_density_arg_strs, + phy_ty_names, + 1); + + // Update the target densities based on the user args. + for (const auto& phy_ty_name_to_density_pair : phy_ty_name_to_tar_density) { + const std::string& phy_ty_name = phy_ty_name_to_density_pair.first; + VTR_ASSERT(phy_ty_name_to_density_pair.second.size() == 1); + float target_density = phy_ty_name_to_density_pair.second[0]; + if (target_density < 0.0f) { + VPR_FATAL_ERROR(VPR_ERROR_AP, + "Cannot have negative target density"); + } + + int phy_ty_index = phy_ty_name_to_index[phy_ty_name]; + phy_ty_target_density[phy_ty_index] = target_density; + } + + return phy_ty_target_density; +} + FlatPlacementDensityManager::FlatPlacementDensityManager(const APNetlist& ap_netlist, const Prepacker& prepacker, const AtomNetlist& atom_netlist, @@ -52,6 +110,7 @@ FlatPlacementDensityManager::FlatPlacementDensityManager(const APNetlist& ap_net const std::vector& logical_block_types, const std::vector& physical_tile_types, const LogicalModels& models, + const std::vector& target_density_arg_strs, int log_verbosity) : ap_netlist_(ap_netlist) , bins_(ap_netlist) @@ -62,6 +121,15 @@ FlatPlacementDensityManager::FlatPlacementDensityManager(const APNetlist& ap_net std::tie(num_layers, width, height) = device_grid.dim_sizes(); bin_spatial_lookup_.resize({num_layers, width, height}); + // Get the target densisty of each physical block type. + std::vector phy_ty_target_densities = get_physical_type_target_densities(target_density_arg_strs, + physical_tile_types); + VTR_LOG("Partial legalizer is using target densities:"); + for (const t_physical_tile_type& phy_ty : physical_tile_types) { + VTR_LOG(" %s:%.1f", phy_ty.name.c_str(), phy_ty_target_densities[phy_ty.index]); + } + VTR_LOG("\n"); + // Create a bin for each tile. This will create one bin for each root tile // location. vtr::vector_map bin_phy_tile_type_idx; @@ -96,6 +164,12 @@ FlatPlacementDensityManager::FlatPlacementDensityManager(const APNetlist& ap_net // Store the index of the physical tile type into a map to be // used to compute the capacity. bin_phy_tile_type_idx.insert(new_bin_id, tile_type->index); + + // Set the target density for this bin based on the physical + // tile type.. + float target_density = phy_ty_target_densities[tile_type->index]; + bin_target_density_.push_back(target_density); + VTR_ASSERT(bin_target_density_[new_bin_id] = target_density); } } } diff --git a/vpr/src/analytical_place/flat_placement_density_manager.h b/vpr/src/analytical_place/flat_placement_density_manager.h index 73f27b4efd..c030f773f5 100644 --- a/vpr/src/analytical_place/flat_placement_density_manager.h +++ b/vpr/src/analytical_place/flat_placement_density_manager.h @@ -69,6 +69,7 @@ class FlatPlacementDensityManager { * @param logical_block_types * @param physical_tile_types * @param models + * @param target_density_arg_strs * @param log_verbosity */ FlatPlacementDensityManager(const APNetlist& ap_netlist, @@ -78,6 +79,7 @@ class FlatPlacementDensityManager { const std::vector& logical_block_types, const std::vector& physical_tile_types, const LogicalModels& models, + const std::vector& target_density_arg_strs, int log_verbosity); /** @@ -168,6 +170,14 @@ class FlatPlacementDensityManager { return bin_underfill_[bin_id]; } + /** + * @brief Returns the target density for the given bin. + */ + inline float get_bin_target_density(FlatPlacementBinId bin_id) const { + VTR_ASSERT_SAFE(bin_id.is_valid()); + return bin_target_density_[bin_id]; + } + /** * @brief Returns true of the given bin is overfilled (it contains too much * mass and is over capacity). @@ -292,6 +302,9 @@ class FlatPlacementDensityManager { /// 1 in this vector, and 0 otherwise. PrimitiveVector used_dims_mask_; + /// @brief The target density of each bin. + vtr::vector bin_target_density_; + /// @brief The verbosity of log messages in this class. const int log_verbosity_; }; diff --git a/vpr/src/analytical_place/global_placer.cpp b/vpr/src/analytical_place/global_placer.cpp index 9b9e3ceeb4..b44118ceb5 100644 --- a/vpr/src/analytical_place/global_placer.cpp +++ b/vpr/src/analytical_place/global_placer.cpp @@ -44,6 +44,7 @@ std::unique_ptr make_global_placer(e_ap_analytical_solver analytic std::shared_ptr place_delay_model, float ap_timing_tradeoff, bool generate_mass_report, + const std::vector& target_density_arg_strs, unsigned num_threads, int log_verbosity) { return std::make_unique(analytical_solver_type, @@ -59,6 +60,7 @@ std::unique_ptr make_global_placer(e_ap_analytical_solver analytic place_delay_model, ap_timing_tradeoff, generate_mass_report, + target_density_arg_strs, num_threads, log_verbosity); } @@ -76,6 +78,7 @@ SimPLGlobalPlacer::SimPLGlobalPlacer(e_ap_analytical_solver analytical_solver_ty std::shared_ptr place_delay_model, float ap_timing_tradeoff, bool generate_mass_report, + const std::vector& target_density_arg_strs, unsigned num_threads, int log_verbosity) : GlobalPlacer(ap_netlist, log_verbosity) @@ -105,6 +108,7 @@ SimPLGlobalPlacer::SimPLGlobalPlacer(e_ap_analytical_solver analytical_solver_ty logical_block_types, physical_tile_types, models, + target_density_arg_strs, log_verbosity_); if (generate_mass_report) density_manager_->generate_mass_report(); diff --git a/vpr/src/analytical_place/global_placer.h b/vpr/src/analytical_place/global_placer.h index 033db223b1..f87dec400b 100644 --- a/vpr/src/analytical_place/global_placer.h +++ b/vpr/src/analytical_place/global_placer.h @@ -87,6 +87,7 @@ std::unique_ptr make_global_placer(e_ap_analytical_solver analytic std::shared_ptr place_delay_model, float ap_timing_tradeoff, bool generate_mass_report, + const std::vector& target_density_arg_strs, unsigned num_threads, int log_verbosity); @@ -164,6 +165,7 @@ class SimPLGlobalPlacer : public GlobalPlacer { std::shared_ptr place_delay_model, float ap_timing_tradeoff, bool generate_mass_report, + const std::vector& target_density_arg_strs, unsigned num_threads, int log_verbosity); diff --git a/vpr/src/analytical_place/partial_legalizer.cpp b/vpr/src/analytical_place/partial_legalizer.cpp index f80ec36819..317aa2732b 100644 --- a/vpr/src/analytical_place/partial_legalizer.cpp +++ b/vpr/src/analytical_place/partial_legalizer.cpp @@ -694,11 +694,18 @@ PerPrimitiveDimPrefixSum2D::PerPrimitiveDimPrefixSum2D(const FlatPlacementDensit const PrimitiveDimManager& dim_manager = density_manager.mass_calculator().get_dim_manager(); dim_prefix_sum_.resize(dim_manager.dims().size()); for (PrimitiveVectorDim dim : density_manager.get_used_dims_mask().get_non_zero_dims()) { - dim_prefix_sum_[dim] = vtr::PrefixSum2D( + dim_prefix_sum_[dim] = vtr::PrefixSum2D( width, height, [&](size_t x, size_t y) { - return lookup(dim, x, y); + // Convert the floating point value into fixed point to prevent + // error accumulation in the prefix sum. + // Note: We ceil here since we do not want to lose information + // on numbers that get very close to 0. + float val = lookup(dim, x, y); + VTR_ASSERT_SAFE_MSG(val >= 0.0f, + "PerPrimitiveDimPrefixSum2D expected to only hold positive values"); + return std::ceil(val * fractional_scale_); }); } } @@ -707,10 +714,14 @@ float PerPrimitiveDimPrefixSum2D::get_dim_sum(PrimitiveVectorDim dim, const vtr::Rect& region) const { VTR_ASSERT_SAFE(dim.is_valid()); // Get the sum over the given region. - return dim_prefix_sum_[dim].get_sum(region.xmin(), - region.ymin(), - region.xmax() - 1, - region.ymax() - 1); + uint64_t sum = dim_prefix_sum_[dim].get_sum(region.xmin(), + region.ymin(), + region.xmax() - 1, + region.ymax() - 1); + + // The sum is stored as a fixed point number. Cast into float by casting to + // a float and dividing by the fractional scale. + return static_cast(sum) / fractional_scale_; } PrimitiveVector PerPrimitiveDimPrefixSum2D::get_sum(const std::vector& dims, @@ -846,13 +857,22 @@ BiPartitioningPartialLegalizer::BiPartitioningPartialLegalizer( // Get the capacity of the bin for this dim. float cap = density_manager_->get_bin_capacity(bin_id).get_dim_val(dim); VTR_ASSERT_SAFE(cap >= 0.0f); + + // Update the capacity with the target density. By multiplying by the + // target density, we make the capacity appear smaller than it actually + // is during partial legalization. + float target_density = density_manager_->get_bin_target_density(bin_id); + cap *= target_density; + // Bins may be large, but the prefix sum assumes a 1x1 grid of // values. Normalize by the area of the bin to turn this into // a 1x1 bin equivalent. const vtr::Rect& bin_region = density_manager_->flat_placement_bins().bin_region(bin_id); float bin_area = bin_region.width() * bin_region.height(); - VTR_ASSERT_SAFE(!vtr::isclose(bin_area, 0.f)); - return cap / bin_area; + VTR_ASSERT_SAFE(!vtr::isclose(bin_area, 0.0f)); + cap /= bin_area; + + return cap; }); num_windows_partitioned_ = 0; diff --git a/vpr/src/analytical_place/partial_legalizer.h b/vpr/src/analytical_place/partial_legalizer.h index 30932b30bd..4349faca5e 100644 --- a/vpr/src/analytical_place/partial_legalizer.h +++ b/vpr/src/analytical_place/partial_legalizer.h @@ -308,6 +308,16 @@ struct PartitionedWindow { * regions. */ class PerPrimitiveDimPrefixSum2D { + // This class stores the prefix sum as a fixed-point number instead of a + // floating point number. This is to prevent error accumulation which can + // occur due to numerical imprecisions. This variable selects how many bits + // the fractional component of the fixed-point number should have. + static constexpr unsigned num_fractional_bits_ = 10; + + // When converting to/from fixed-point, we need to scale/shrink by 2 to the + // power of the number of fractional bits. + static constexpr float fractional_scale_ = 2 << num_fractional_bits_; + public: PerPrimitiveDimPrefixSum2D() = default; @@ -336,8 +346,9 @@ class PerPrimitiveDimPrefixSum2D { const vtr::Rect& region) const; private: - /// @brief Per-Dim Prefix Sums - vtr::vector> dim_prefix_sum_; + /// @brief Per-Dim Prefix Sums. These are stored as fixed-point numbers to + /// prevent error accumulations due to numerical imprecisions. + vtr::vector> dim_prefix_sum_; }; /// @brief A unique ID of a group of primitive dims created by the PrimitiveDimGrouper class. diff --git a/vpr/src/base/SetupVPR.cpp b/vpr/src/base/SetupVPR.cpp index a6c5232752..2d06d0c25f 100644 --- a/vpr/src/base/SetupVPR.cpp +++ b/vpr/src/base/SetupVPR.cpp @@ -559,6 +559,7 @@ void SetupAPOpts(const t_options& options, apOpts.detailed_placer_type = options.ap_detailed_placer.value(); apOpts.ap_timing_tradeoff = options.ap_timing_tradeoff.value(); apOpts.ap_high_fanout_threshold = options.ap_high_fanout_threshold.value(); + apOpts.ap_partial_legalizer_target_density = options.ap_partial_legalizer_target_density.value(); 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(); diff --git a/vpr/src/base/read_options.cpp b/vpr/src/base/read_options.cpp index d2f452af6a..2030ff1e7d 100644 --- a/vpr/src/base/read_options.cpp +++ b/vpr/src/base/read_options.cpp @@ -1957,6 +1957,24 @@ argparse::ArgumentParser create_arg_parser(const std::string& prog_name, t_optio .default_value("256") .show_in(argparse::ShowIn::HELP_ONLY); + ap_grp.add_argument(args.ap_partial_legalizer_target_density, "--ap_partial_legalizer_target_density") + .help( + "Sets the target density of different physical tiles on the FPGA device " + "for the partial legalizer in the AP flow. The partial legalizer will " + "try to fill tiles up to (but not beyond) this target density. This " + "is used as a guide, the legalizer may not follow this if it must fill " + "the tile more." + "\n" + "When this option is set ot auto, VPR will select good values for the " + "target density of tiles." + "\n" + "This option is similar to appack_max_dist_th, where a regex string " + "is used to set the target density of different physical tiles. See " + "the documentation for more information.") + .nargs('+') + .default_value({"auto"}) + .show_in(argparse::ShowIn::HELP_ONLY); + ap_grp.add_argument(args.appack_max_dist_th, "--appack_max_dist_th") .help( "Sets the maximum candidate distance thresholds for the logical block types" diff --git a/vpr/src/base/read_options.h b/vpr/src/base/read_options.h index 854f283a26..7612f29a81 100644 --- a/vpr/src/base/read_options.h +++ b/vpr/src/base/read_options.h @@ -103,6 +103,7 @@ struct t_options { argparse::ArgValue ap_partial_legalizer; argparse::ArgValue ap_full_legalizer; argparse::ArgValue ap_detailed_placer; + argparse::ArgValue> ap_partial_legalizer_target_density; argparse::ArgValue> appack_max_dist_th; argparse::ArgValue ap_verbosity; argparse::ArgValue ap_timing_tradeoff; diff --git a/vpr/src/base/vpr_types.h b/vpr/src/base/vpr_types.h index 34d4791430..2101faab25 100644 --- a/vpr/src/base/vpr_types.h +++ b/vpr/src/base/vpr_types.h @@ -1105,6 +1105,9 @@ struct t_placer_opts { * @param ap_high_fanout_threshold; * The threshold to ignore nets with higher fanout than that * value while constructing the solver. + * @param ap_partial_legalizer_target_density + * Vector of strings passed by the user to configure the target + * density of different physical tiles on the device. * @param appack_max_dist_th * Array of string passed by the user to configure the max candidate * distance thresholds. @@ -1131,6 +1134,8 @@ struct t_ap_opts { int ap_high_fanout_threshold; + std::vector ap_partial_legalizer_target_density; + std::vector appack_max_dist_th; unsigned num_threads; diff --git a/vpr/src/pack/appack_max_dist_th_manager.cpp b/vpr/src/pack/appack_max_dist_th_manager.cpp index c5c9b685b7..4d602bed00 100644 --- a/vpr/src/pack/appack_max_dist_th_manager.cpp +++ b/vpr/src/pack/appack_max_dist_th_manager.cpp @@ -6,10 +6,10 @@ */ #include "appack_max_dist_th_manager.h" -#include -#include -#include +#include +#include #include +#include "ap_argparse_utils.h" #include "device_grid.h" #include "physical_types.h" #include "physical_types_util.h" @@ -18,20 +18,6 @@ #include "vtr_assert.h" #include "vtr_log.h" -/** - * @brief Helper method to convert a string into a float with error checking. - */ -static float str_to_float_or_error(const std::string& str); - -/** - * @brief Helper method to parse one term of the user-provided max distance - * threshold string. - * - * This method decomposes the user string of the form ":," - * into its three components. - */ -static std::tuple parse_max_dist_th(const std::string& max_dist_th); - /** * @brief Recursive helper method to deduce if the given pb_type is or contains * pb_types which are of the memory class. @@ -155,117 +141,37 @@ void APPackMaxDistThManager::set_max_distance_thresholds_from_strings( const std::vector& logical_block_types, const DeviceGrid& device_grid) { - // Go through each of the user-provided strings. - for (const std::string& max_dist_th : max_dist_ths) { - // If any of them are the word "auto", this was a user error and should - // be flagged. - // TODO: Maybe move this and other semantic checks up to the checker of - // VPR's command line. - if (max_dist_th == "auto") { - VPR_FATAL_ERROR(VPR_ERROR_PACK, - "APPack: Cannot provide both auto and other max distance threshold strings"); - } + std::vector lb_type_names; + std::unordered_map lb_type_name_to_index; + for (const t_logical_block_type& lb_ty : logical_block_types) { + lb_type_names.push_back(lb_ty.name); + lb_type_name_to_index[lb_ty.name] = lb_ty.index; + } - // Parse the string for the regex, scale, and offset. - std::string logical_block_regex_str; - float logical_block_max_dist_th_scale; - float logical_block_max_dist_th_offset; - std::tie(logical_block_regex_str, - logical_block_max_dist_th_scale, - logical_block_max_dist_th_offset) = parse_max_dist_th(max_dist_th); + auto lb_to_floats_map = key_to_float_argument_parser(max_dist_ths, lb_type_names, 2); - // Setup the regex for the logical blocks the user wants to set the - // thresholds for. - std::regex logical_block_regex(logical_block_regex_str); + for (const auto& lb_name_to_floats_pair : lb_to_floats_map) { + const std::string& lb_name = lb_name_to_floats_pair.first; + const std::vector& lb_floats = lb_name_to_floats_pair.second; + VTR_ASSERT(lb_floats.size() == 2); + float logical_block_max_dist_th_scale = lb_floats[0]; + float logical_block_max_dist_th_offset = lb_floats[1]; + + if (logical_block_max_dist_th_scale < 0.0) { + VPR_FATAL_ERROR(VPR_ERROR_PACK, + "APPack: Cannot have negative max distance threshold scale"); + } + if (logical_block_max_dist_th_offset < 0.0) { + VPR_FATAL_ERROR(VPR_ERROR_PACK, + "APPack: Cannot have negative max distance threshold offset"); + } // Compute the max distance threshold the user selected. float max_device_distance = device_grid.width() + device_grid.height(); float logical_block_max_dist_th = std::max(max_device_distance * logical_block_max_dist_th_scale, logical_block_max_dist_th_offset); - // Search through all logical blocks and set the thresholds of any matches - // to the threshold the user selected. - bool found_match = false; - for (const t_logical_block_type& lb_ty : logical_block_types) { - bool is_match = std::regex_match(lb_ty.name, logical_block_regex); - if (!is_match) - continue; - - logical_block_dist_thresholds_[lb_ty.index] = logical_block_max_dist_th; - found_match = true; - } - // If no match is found, send a warning to the user. - if (!found_match) { - VTR_LOG_WARN("Unable to find logical block type for max distance threshold regex string: %s\n", - logical_block_regex_str.c_str()); - } - } -} - -static std::tuple parse_max_dist_th(const std::string& max_dist_th) { - // Verify the format of the string. It must have one and only one colon. - unsigned colon_count = 0; - for (char c : max_dist_th) { - if (c == ':') - colon_count++; - } - if (colon_count != 1) { - VTR_LOG_ERROR("Invalid max distance threshold string: %s\n", - max_dist_th.c_str()); - VPR_FATAL_ERROR(VPR_ERROR_PACK, - "Error when parsing APPack max distance threshold string"); - } - - // Split the string along the colon. - auto del_pos = max_dist_th.find(':'); - std::string logical_block_regex_str = max_dist_th.substr(0, del_pos); - std::string lb_max_dist_th_str = max_dist_th.substr(del_pos + 1, std::string::npos); - - // Split along the comma for the scale/offset. - // Verify that the comma only appears once in the scale/offset string. - unsigned comma_count = 0; - for (char c : lb_max_dist_th_str) { - if (c == ',') - comma_count++; - } - if (comma_count != 1) { - VTR_LOG_ERROR("Invalid max distance threshold string: %s\n", - max_dist_th.c_str()); - VPR_FATAL_ERROR(VPR_ERROR_PACK, - "Error when parsing APPack max distance threshold string"); - } - - // Split the string along the comma. - auto comma_pos = lb_max_dist_th_str.find(','); - std::string lb_max_dist_th_scale_str = lb_max_dist_th_str.substr(0, comma_pos); - std::string lb_max_dist_th_offset_str = lb_max_dist_th_str.substr(comma_pos + 1, std::string::npos); - - // Convert the scale and offset into floats (error checking to be safe). - float lb_max_dist_th_scale = str_to_float_or_error(lb_max_dist_th_scale_str); - float lb_max_dist_th_offset = str_to_float_or_error(lb_max_dist_th_offset_str); - - // Return the results as a tuple. - return std::make_tuple(logical_block_regex_str, lb_max_dist_th_scale, lb_max_dist_th_offset); -} - -static float str_to_float_or_error(const std::string& str) { - float val = -1; - try { - val = std::stof(str); - } catch (const std::invalid_argument& e) { - VTR_LOG_ERROR("Error while parsing max distance threshold value: %s\n" - "Failed with invalid argument: %s\n", - str.c_str(), - e.what()); - } catch (const std::out_of_range& e) { - VTR_LOG_ERROR("Error while parsing max distance threshold value: %s\n" - "Failed with out of range: %s\n", - str.c_str(), - e.what()); - } - if (val < 0.0f) { - VPR_FATAL_ERROR(VPR_ERROR_PACK, - "Error when parsing APPack max distance threshold string"); + int lb_ty_index = lb_type_name_to_index[lb_name]; + logical_block_dist_thresholds_[lb_ty_index] = logical_block_max_dist_th; } - return val; } diff --git a/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_ap/no_fixed_blocks/config/config.txt b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_ap/no_fixed_blocks/config/config.txt index 7678dea6b0..ed380bf5fa 100755 --- a/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_ap/no_fixed_blocks/config/config.txt +++ b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_ap/no_fixed_blocks/config/config.txt @@ -35,7 +35,7 @@ circuit_constraint_list_add=(stereovision3.v, route_chan_width=44) circuit_constraint_list_add=(ch_intrinsics.v, route_chan_width=52) circuit_constraint_list_add=(spree.v, route_chan_width=78) circuit_constraint_list_add=(boundtop.v, route_chan_width=50) -circuit_constraint_list_add=(or1200.v, route_chan_width=118) +circuit_constraint_list_add=(or1200.v, route_chan_width=124) # Parse info and how to parse parse_file=vpr_fixed_chan_width.txt diff --git a/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_ap/no_fixed_blocks/config/golden_results.txt b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_ap/no_fixed_blocks/config/golden_results.txt index 5bce89c0fd..a3ab75a588 100644 --- a/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_ap/no_fixed_blocks/config/golden_results.txt +++ b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_ap/no_fixed_blocks/config/golden_results.txt @@ -1,6 +1,6 @@ - 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 routed_wirelength avg_routed_wirelength routed_wiresegment avg_routed_wiresegment total_nets_routed total_connections_routed total_heap_pushes total_heap_pops logic_block_area_total logic_block_area_used routing_area_total routing_area_per_tile crit_path_route_success_iteration num_rr_graph_nodes num_rr_graph_edges collapsed_nodes critical_path_delay geomean_nonvirtual_intradomain_critical_path_delay setup_TNS setup_WNS hold_TNS hold_WNS create_rr_graph_time create_intra_cluster_rr_graph_time adding_internal_edges route_mem crit_path_route_time crit_path_total_timing_analysis_time crit_path_total_sta_time router_lookahead_mem tile_lookahead_computation_time router_lookahead_computation_time - k6_frac_N10_frac_chain_mem32K_40nm.xml boundtop.v common 9.58 vpr 82.57 MiB -1 -1 6.82 42428 3 0.61 -1 -1 37568 -1 -1 46 196 1 0 success v8.0.0-13084-g071ad3865 release IPO VTR_ASSERT_LEVEL=2 GNU 13.3.0 on Linux-6.8.0-60-generic x86_64 2025-06-17T09:37:40 betzgrp-wintermute /home/pooladam/vtr-verilog-to-routing 84552 196 193 798 0 1 604 436 20 20 400 -1 vtr_extra_small -1 -1 3760.61 3044 33476 4713 27728 1035 82.6 MiB 1.33 0.01 2.93578 2.81317 -1151 -2.81317 2.81317 0.00 0.00180736 0.00165078 0.0492256 0.0457389 82.6 MiB 1.33 82.6 MiB 0.70 5106 8.58151 1525 2.56303 1759 2700 156727 46750 2.07112e+07 3.02712e+06 1.26946e+06 3173.65 11 38988 203232 -1 3.15897 3.15897 -1301.64 -3.15897 0 0 0.13 -1 -1 82.6 MiB 0.10 0.206096 0.192796 39.7 MiB -1 0.04 - k6_frac_N10_frac_chain_mem32K_40nm.xml ch_intrinsics.v common 1.25 vpr 75.55 MiB -1 -1 0.19 21308 3 0.07 -1 -1 33104 -1 -1 68 99 1 0 success v8.0.0-13084-g071ad3865 release IPO VTR_ASSERT_LEVEL=2 GNU 13.3.0 on Linux-6.8.0-60-generic x86_64 2025-06-17T09:37:40 betzgrp-wintermute /home/pooladam/vtr-verilog-to-routing 77368 99 130 264 0 1 226 298 20 20 400 -1 vtr_extra_small -1 -1 885.731 708 16218 4754 9792 1672 75.6 MiB 0.51 0.00 1.87109 1.87109 -121.775 -1.87109 1.87109 0.00 0.000575195 0.000536795 0.0131266 0.0123694 75.6 MiB 0.51 75.6 MiB 0.26 1253 7.50299 388 2.32335 376 577 30318 8963 2.07112e+07 4.21279e+06 1.31074e+06 3276.84 10 39388 210115 -1 1.99059 1.99059 -137.535 -1.99059 0 0 0.15 -1 -1 75.6 MiB 0.03 0.0573551 0.0537716 38.4 MiB -1 0.05 - k6_frac_N10_frac_chain_mem32K_40nm.xml or1200.v common 19.28 vpr 134.58 MiB -1 -1 2.70 59328 8 2.91 -1 -1 40540 -1 -1 247 385 2 1 success v8.0.0-13084-g071ad3865 release IPO VTR_ASSERT_LEVEL=2 GNU 13.3.0 on Linux-6.8.0-60-generic x86_64 2025-06-17T09:37:40 betzgrp-wintermute /home/pooladam/vtr-verilog-to-routing 137812 385 362 3328 0 1 2408 997 30 30 900 -1 vtr_small -1 -1 37706.7 32316 309633 71864 234222 3547 134.6 MiB 10.53 0.04 10.8573 9.10491 -10763.7 -9.10491 9.10491 0.00 0.0110376 0.0102668 0.852951 0.784901 134.6 MiB 10.53 134.6 MiB 5.41 44145 18.4476 11407 4.76682 10587 34974 1884640 335487 4.8774e+07 1.48038e+07 6.56785e+06 7297.61 18 120772 1084977 -1 9.39133 9.39133 -11054.2 -9.39133 0 0 0.85 -1 -1 134.6 MiB 1.07 1.8929 1.75049 83.0 MiB -1 0.23 - k6_frac_N10_frac_chain_mem32K_40nm.xml spree.v common 5.96 vpr 86.36 MiB -1 -1 1.56 32544 16 0.44 -1 -1 34372 -1 -1 62 45 3 1 success v8.0.0-13084-g071ad3865 release IPO VTR_ASSERT_LEVEL=2 GNU 13.3.0 on Linux-6.8.0-60-generic x86_64 2025-06-17T09:37:40 betzgrp-wintermute /home/pooladam/vtr-verilog-to-routing 88436 45 32 944 0 1 766 143 20 20 400 -1 vtr_extra_small -1 -1 7012.23 6542 6857 949 5832 76 86.4 MiB 2.96 0.00 11.8281 11.6952 -7166.94 -11.6952 11.6952 0.00 0.00194449 0.00171015 0.066214 0.0598273 86.4 MiB 2.96 86.4 MiB 1.92 10810 14.1678 2765 3.62385 3021 8126 667447 163597 2.07112e+07 5.38143e+06 1.91495e+06 4787.38 15 44576 305072 -1 12.2006 12.2006 -7360.63 -12.2006 0 0 0.21 -1 -1 86.4 MiB 0.23 0.319432 0.291044 44.2 MiB -1 0.07 - k6_frac_N10_frac_chain_mem32K_40nm.xml stereovision3.v common 1.44 vpr 74.96 MiB -1 -1 0.31 24764 5 0.12 -1 -1 32640 -1 -1 15 11 0 0 success v8.0.0-13084-g071ad3865 release IPO VTR_ASSERT_LEVEL=2 GNU 13.3.0 on Linux-6.8.0-60-generic x86_64 2025-06-17T09:37:40 betzgrp-wintermute /home/pooladam/vtr-verilog-to-routing 76760 11 2 140 0 2 84 28 20 20 400 -1 vtr_extra_small -1 -1 322.099 315 112 28 73 11 75.0 MiB 0.38 0.00 2.10685 2.10685 -167.1 -2.10685 1.95087 0.00 0.000412674 0.000356264 0.00455256 0.00433906 75.0 MiB 0.38 75.0 MiB 0.24 493 6.32051 127 1.62821 160 274 5744 1513 2.07112e+07 808410 1.12964e+06 2824.09 10 37792 180905 -1 2.11477 2.00072 -170.487 -2.11477 0 0 0.12 -1 -1 75.0 MiB 0.02 0.0422746 0.0387358 37.1 MiB -1 0.04 +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 routed_wirelength avg_routed_wirelength routed_wiresegment avg_routed_wiresegment total_nets_routed total_connections_routed total_heap_pushes total_heap_pops logic_block_area_total logic_block_area_used routing_area_total routing_area_per_tile crit_path_route_success_iteration num_rr_graph_nodes num_rr_graph_edges collapsed_nodes critical_path_delay geomean_nonvirtual_intradomain_critical_path_delay setup_TNS setup_WNS hold_TNS hold_WNS create_rr_graph_time create_intra_cluster_rr_graph_time adding_internal_edges route_mem crit_path_route_time crit_path_total_timing_analysis_time crit_path_total_sta_time router_lookahead_mem tile_lookahead_computation_time router_lookahead_computation_time +k6_frac_N10_frac_chain_mem32K_40nm.xml boundtop.v common 10.42 vpr 84.87 MiB -1 -1 7.24 52056 3 0.63 -1 -1 39008 -1 -1 48 196 1 0 success v8.0.0-13089-g96c4fc94e release VTR_ASSERT_LEVEL=3 GNU 13.3.0 on Linux-6.8.0-58-generic x86_64 2025-06-17T18:59:40 srivatsan-Precision-Tower-5810 /home/alex/vtr-verilog-to-routing 86908 196 193 798 0 1 605 438 20 20 400 -1 vtr_extra_small -1 -1 3997.05 3285 73566 12097 59046 2423 84.9 MiB 1.70 0.01 2.82664 2.65254 -1140.6 -2.65254 2.65254 0.00 0.00198171 0.00172432 0.103308 0.0907731 84.9 MiB 1.70 84.9 MiB 0.88 5408 9.07383 1597 2.67953 1723 2368 159040 47645 2.07112e+07 3.13491e+06 1.26946e+06 3173.65 10 38988 203232 -1 3.17051 3.17051 -1227.06 -3.17051 0 0 0.18 -1 -1 84.9 MiB 0.10 0.270353 0.241577 41.4 MiB -1 0.06 +k6_frac_N10_frac_chain_mem32K_40nm.xml ch_intrinsics.v common 1.84 vpr 78.14 MiB -1 -1 0.36 30392 3 0.11 -1 -1 37180 -1 -1 68 99 1 0 success v8.0.0-13089-g96c4fc94e release VTR_ASSERT_LEVEL=3 GNU 13.3.0 on Linux-6.8.0-58-generic x86_64 2025-06-17T18:59:40 srivatsan-Precision-Tower-5810 /home/alex/vtr-verilog-to-routing 80012 99 130 264 0 1 226 298 20 20 400 -1 vtr_extra_small -1 -1 885.731 708 16218 4754 9792 1672 78.1 MiB 0.85 0.01 1.87109 1.87109 -121.775 -1.87109 1.87109 0.00 0.00106913 0.000942715 0.0226412 0.020327 78.1 MiB 0.85 78.1 MiB 0.42 1253 7.50299 388 2.32335 376 577 30318 8963 2.07112e+07 4.21279e+06 1.31074e+06 3276.84 10 39388 210115 -1 1.99059 1.99059 -137.535 -1.99059 0 0 0.19 -1 -1 78.1 MiB 0.05 0.095672 0.0864075 40.2 MiB -1 0.08 +k6_frac_N10_frac_chain_mem32K_40nm.xml or1200.v common 23.36 vpr 136.85 MiB -1 -1 2.84 66136 8 3.15 -1 -1 45136 -1 -1 250 385 2 1 success v8.0.0-13089-g96c4fc94e release VTR_ASSERT_LEVEL=3 GNU 13.3.0 on Linux-6.8.0-58-generic x86_64 2025-06-17T18:59:40 srivatsan-Precision-Tower-5810 /home/alex/vtr-verilog-to-routing 140132 385 362 3328 0 1 2406 1000 30 30 900 -1 vtr_small -1 -1 37338.5 30254 340864 75209 262031 3624 136.8 MiB 14.12 0.08 10.9083 9.279 -10729.1 -9.279 9.279 0.00 0.00962194 0.00850512 0.739236 0.650392 136.8 MiB 14.12 136.8 MiB 7.41 40781 17.0560 10534 4.40569 9354 30561 1592054 287754 4.8774e+07 1.49655e+07 6.84542e+06 7606.03 16 123468 1137417 -1 9.3335 9.3335 -10939.7 -9.3335 0 0 1.27 -1 -1 136.8 MiB 0.80 1.80362 1.62001 86.4 MiB -1 0.29 +k6_frac_N10_frac_chain_mem32K_40nm.xml spree.v common 6.71 vpr 88.48 MiB -1 -1 1.73 42548 16 0.45 -1 -1 38756 -1 -1 60 45 3 1 success v8.0.0-13089-g96c4fc94e release VTR_ASSERT_LEVEL=3 GNU 13.3.0 on Linux-6.8.0-58-generic x86_64 2025-06-17T18:59:40 srivatsan-Precision-Tower-5810 /home/alex/vtr-verilog-to-routing 90608 45 32 944 0 1 766 141 20 20 400 -1 vtr_extra_small -1 -1 7841.8 6489 7827 1250 6283 294 88.5 MiB 3.50 0.01 12.1051 11.0146 -6876.8 -11.0146 11.0146 0.00 0.00222847 0.00181942 0.0703686 0.0597694 88.5 MiB 3.50 88.5 MiB 2.41 10653 13.9620 2897 3.79685 3514 9994 754010 198329 2.07112e+07 5.27364e+06 1.91495e+06 4787.38 14 44576 305072 -1 11.3646 11.3646 -7213.07 -11.3646 0 0 0.30 -1 -1 88.5 MiB 0.26 0.33906 0.296281 45.7 MiB -1 0.08 +k6_frac_N10_frac_chain_mem32K_40nm.xml stereovision3.v common 2.13 vpr 77.10 MiB -1 -1 0.59 33840 5 0.17 -1 -1 36828 -1 -1 15 11 0 0 success v8.0.0-13089-g96c4fc94e release VTR_ASSERT_LEVEL=3 GNU 13.3.0 on Linux-6.8.0-58-generic x86_64 2025-06-17T18:59:40 srivatsan-Precision-Tower-5810 /home/alex/vtr-verilog-to-routing 78952 11 2 140 0 2 84 28 20 20 400 -1 vtr_extra_small -1 -1 322.099 315 112 28 73 11 77.1 MiB 0.54 0.00 2.10685 2.10685 -167.1 -2.10685 1.95087 0.00 0.00050932 0.000417877 0.00464267 0.00430535 77.1 MiB 0.54 77.1 MiB 0.27 493 6.32051 127 1.62821 160 274 5744 1513 2.07112e+07 808410 1.12964e+06 2824.09 10 37792 180905 -1 2.11477 2.00072 -170.487 -2.11477 0 0 0.28 -1 -1 77.1 MiB 0.02 0.0658957 0.0582486 38.7 MiB -1 0.10