diff --git a/doc/src/vpr/command_line_usage.rst b/doc/src/vpr/command_line_usage.rst index d596300cda..920f91e712 100644 --- a/doc/src/vpr/command_line_usage.rst +++ b/doc/src/vpr/command_line_usage.rst @@ -408,6 +408,50 @@ Use the options below to override this default naming behaviour. Prefix for output files +.. option:: --read_flat_place + + Reads a file containing the locations of each atom on the FPGA. + This is used by the packer to better cluster atoms together. + + The flat placement file (which often ends in ``.fplace``) is a text file + where each line describes the location of an atom. Each line in the flat + placement file should have the following syntax: + + .. code-block:: none + + + + For example: + + .. code-block:: none + + n523 6 8 0 0 3 + n522 6 8 0 0 5 + n520 6 8 0 0 2 + n518 6 8 0 0 16 + + The position of the atom on the FPGA is given by 3 floating point values + (``x``, ``y``, ``layer``). We allow for the positions of atom to be not + quite legal (ok to be off-grid) since this flat placement will be fed into + the packer and placer, which will snap the positions to grid locations. By + allowing for off-grid positions, the packer can better trade-off where to + move atom blocks if they cannot be placed at the given position. + For 2D FPGA architectures, the ``layer`` should be 0. + + The ``sub_tile`` is a clustered placement construct: which cluster-level + location at a given (x, y, layer) should these atoms go at (relevant when + multiple clusters can be stacked there). A sub-tile of -1 may be used when + the sub-tile of an atom is unkown (allowing the packing algorithm to choose + any sub-tile at the given (x, y, layer) location). + + The ``site_idx`` is an optional index into a linearized list of primitive + locations within a cluster-level block which may be used as a hint to + reconstruct clusters. + + .. warning:: + + This interface is currently experimental and under active development. + .. option:: --write_flat_place Writes the post-placement locations of each atom into a flat placement file. diff --git a/vpr/src/base/FlatPlacementInfo.h b/vpr/src/base/FlatPlacementInfo.h new file mode 100644 index 0000000000..4f3ca3e5e8 --- /dev/null +++ b/vpr/src/base/FlatPlacementInfo.h @@ -0,0 +1,84 @@ +/** + * @file + * @author Alex Singer + * @date January 2025 + * @brief Declaration of the FlatPlacementInfo object, which is used to store + * flat placement information used by the packer. + */ + +#pragma once + +#include "atom_netlist.h" +#include "vtr_vector.h" + +/** + * @brief Flat placement storage class. + * + * This stores placement information for each atom in the netlist. It contains + * any information that may be used by the packer to better create clusters. + */ +class FlatPlacementInfo { +public: + /// @brief Identifier for an undefined position. + static constexpr float UNDEFINED_POS = -1.f; + /// @brief Identifier for an undefined sub tile. + static constexpr int UNDEFINED_SUB_TILE = -1; + /// @brief Identifier for an undefined site idx. + static constexpr int UNDEFINED_SITE_IDX = -1; + + // The following three floating point numbers describe the flat position of + // an atom block. These are floats instead of integers to allow for flat + // placements which are not quite legal (ok to be off-grid). This allows + // the flat placement to encode information about where atom blocks would + // want to go if they cannot be placed at the grid position they are at. + // (for example, a block placed at (0.9, 0.9) wants to be at tile (0, 0), + // but if thats not possible it would prefer (1, 1) over anything else. + + /// @brief The x-positions of each atom block. Is UNDEFINED_POS if undefined. + vtr::vector blk_x_pos; + /// @brief The y-positions of each atom block. Is UNDEFINED_POS if undefined. + vtr::vector blk_y_pos; + /// @brief The layer of each atom block. Is UNDEFINED_POS if undefined. + vtr::vector blk_layer; + + /// @brief The sub tile location of each atom block. Is UNDEFINED_SUB_TILE + /// if undefined. + vtr::vector blk_sub_tile; + /// @brief The flat site idx of each atom block. This is an optional index + /// into a linearized list of primitive locations within a cluster- + /// level block. Is UNDEFINED_SITE_IDX if undefined. + vtr::vector blk_site_idx; + + /// @brief A flag to signify if this object has been constructed with data + /// or not. This makes it easier to detect if a flat placement exists + /// or not. Is true when a placement has been loaded into this + /// object, false otherwise. + bool valid; + + /** + * @brief Default constructor of this class. + * + * Initializes the data structure to invalid so it can be easily checked to + * be uninitialized. + */ + FlatPlacementInfo() : valid(false) {} + + /** + * @brief Constructs the flat placement with undefined positions for each + * atom block in the atom netlist. + * + * The valid flag is set to true here, since this structure is now + * initialized with data and can be used. + * + * @param atom_netlist + * The netlist of atom blocks in the circuit. + */ + FlatPlacementInfo(const AtomNetlist& atom_netlist) + : blk_x_pos(atom_netlist.blocks().size(), UNDEFINED_POS), + blk_y_pos(atom_netlist.blocks().size(), UNDEFINED_POS), + blk_layer(atom_netlist.blocks().size(), UNDEFINED_POS), + blk_sub_tile(atom_netlist.blocks().size(), UNDEFINED_SUB_TILE), + blk_site_idx(atom_netlist.blocks().size(), UNDEFINED_SITE_IDX), + valid(true) {} +}; + diff --git a/vpr/src/base/SetupVPR.cpp b/vpr/src/base/SetupVPR.cpp index 609b85df75..9c0fb69ba9 100644 --- a/vpr/src/base/SetupVPR.cpp +++ b/vpr/src/base/SetupVPR.cpp @@ -132,6 +132,7 @@ void SetupVPR(const t_options* options, fileNameOpts->read_vpr_constraints_file = options->read_vpr_constraints_file; fileNameOpts->write_vpr_constraints_file = options->write_vpr_constraints_file; fileNameOpts->write_constraints_file = options->write_constraints_file; + fileNameOpts->read_flat_place_file = options->read_flat_place_file; fileNameOpts->write_flat_place_file = options->write_flat_place_file; fileNameOpts->write_block_usage = options->write_block_usage; diff --git a/vpr/src/base/load_flat_place.cpp b/vpr/src/base/load_flat_place.cpp index f34d1f9468..04a141e3b4 100644 --- a/vpr/src/base/load_flat_place.cpp +++ b/vpr/src/base/load_flat_place.cpp @@ -8,11 +8,16 @@ #include "load_flat_place.h" +#include #include +#include "atom_netlist.h" #include "clustered_netlist.h" +#include "FlatPlacementInfo.h" #include "globals.h" #include "vpr_context.h" +#include "vpr_error.h" #include "vpr_types.h" +#include "vtr_log.h" /** * @brief Prints flat placement file entries for the atoms in one placed @@ -45,9 +50,10 @@ static void print_flat_cluster(FILE* fp, t_pb_graph_node* atom_pbgn = atom_ctx.lookup.atom_pb(atom)->pb_graph_node; // Print the flat placement information for this atom. - fprintf(fp, "%s %d %d %d %d #%zu %s\n", + fprintf(fp, "%s %d %d %d %d %d #%zu %s\n", atom_ctx.nlist.block_name(atom).c_str(), - blk_loc.x, blk_loc.y, blk_loc.sub_tile, + blk_loc.x, blk_loc.y, blk_loc.layer, + blk_loc.sub_tile, atom_pbgn->flat_site_index, static_cast(blk_id), atom_pbgn->pb_type->name); @@ -77,6 +83,86 @@ void write_flat_placement(const char* flat_place_file_path, fclose(fp); } +FlatPlacementInfo read_flat_placement(const std::string& read_flat_place_file_path, + const AtomNetlist& atom_netlist) { + // Try to open the file, crash if we cannot open the file. + std::ifstream flat_place_file(read_flat_place_file_path); + if (!flat_place_file.is_open()) { + VPR_ERROR(VPR_ERROR_OTHER, "Unable to open flat placement file: %s\n", + read_flat_place_file_path.c_str()); + } + + // Create a FlatPlacementInfo object to hold the flat placement. + FlatPlacementInfo flat_placement_info(atom_netlist); + + // Read each line of the flat placement file. + unsigned line_num = 0; + std::string line; + while (std::getline(flat_place_file, line)) { + // Split the line into tokens (using spaces, tabs, etc. as delimiters). + std::vector tokens = vtr::split(line); + // Skip empty lines + if (tokens.empty()) + continue; + // Skip lines that are only comments. + if (tokens[0][0] == '#') + continue; + // Skip lines with too few arguments. + // Required arguments: + // - Atom name + // - Atom x-pos + // - Atom y-pos + // - Atom layer + // - Atom sub-tile + if (tokens.size() < 5) { + VTR_LOG_WARN("Flat placement file, line %d has too few arguments. " + "Requires at least: \n", + line_num); + continue; + } + + // Get the atom name, which should be the first argument. + AtomBlockId atom_blk_id = atom_netlist.find_block(tokens[0]); + if (!atom_blk_id.is_valid()) { + VTR_LOG_WARN("Flat placement file, line %d atom name does not match " + "any atoms in the atom netlist.\n", + line_num); + continue; + } + + // Check if this atom already has a flat placement + // Using the x_pos and y_pos as identifiers. + if (flat_placement_info.blk_x_pos[atom_blk_id] != FlatPlacementInfo::UNDEFINED_POS || + flat_placement_info.blk_y_pos[atom_blk_id] != FlatPlacementInfo::UNDEFINED_POS) { + VTR_LOG_WARN("Flat placement file, line %d, atom %s has multiple " + "placement definitions in the flat placement file.\n", + line_num, atom_netlist.block_name(atom_blk_id).c_str()); + continue; + } + + // Get the (x, y, layer) position of the atom. These functions have + // error checking built in. We parse these as floats to allow for + // reading in more global atom positions. + flat_placement_info.blk_x_pos[atom_blk_id] = vtr::atof(tokens[1]); + flat_placement_info.blk_y_pos[atom_blk_id] = vtr::atof(tokens[2]); + flat_placement_info.blk_layer[atom_blk_id] = vtr::atof(tokens[3]); + + // Parse the sub-tile as an integer. + flat_placement_info.blk_sub_tile[atom_blk_id] = vtr::atoi(tokens[4]); + + // If a site index is given, parse the site index as an integer. + if (tokens.size() >= 6 && tokens[5][0] != '#') + flat_placement_info.blk_site_idx[atom_blk_id] = vtr::atoi(tokens[5]); + + // Ignore any further tokens. + + line_num++; + } + + // Return the flat placement info loaded from the file. + return flat_placement_info; +} + /* ingests and legalizes a flat placement file */ bool load_flat_placement(t_vpr_setup& vpr_setup, const t_arch& arch) { VTR_LOG("load_flat_placement(); when implemented, this function:"); diff --git a/vpr/src/base/load_flat_place.h b/vpr/src/base/load_flat_place.h index c70314bb7f..683208bcf0 100644 --- a/vpr/src/base/load_flat_place.h +++ b/vpr/src/base/load_flat_place.h @@ -10,14 +10,18 @@ #pragma once +#include #include #include "vtr_vector_map.h" #include "vtr_vector.h" // Forward declarations class AtomBlockId; +class AtomNetlist; class ClusterBlockId; class ClusteredNetlist; +class FlatPlacementInfo; +class Prepacker; struct t_arch; struct t_block_loc; struct t_vpr_setup; @@ -40,6 +44,18 @@ void write_flat_placement(const char* flat_place_file_path, const vtr::vector_map &block_locs, const vtr::vector>& atoms_lookup); +/** + * @brief Reads a flat placement file generated from a previous run of VTR or + * externally generated. + * + * @param read_flat_place_file_path + * Path to the file to read the flat placement from. + * @param atom_netlist + * The netlist of atom blocks in the circuit. + */ +FlatPlacementInfo read_flat_placement(const std::string& read_flat_place_file_path, + const AtomNetlist& atom_netlist); + /** * @brief A function that loads and legalizes a flat placement file */ diff --git a/vpr/src/base/read_options.cpp b/vpr/src/base/read_options.cpp index 78124dd85c..ca4160f861 100644 --- a/vpr/src/base/read_options.cpp +++ b/vpr/src/base/read_options.cpp @@ -1652,6 +1652,11 @@ argparse::ArgumentParser create_arg_parser(const std::string& prog_name, t_optio .default_value("fix_clusters.out") .show_in(argparse::ShowIn::HELP_ONLY); + file_grp.add_argument(args.read_flat_place_file, "--read_flat_place") + .help( + "Reads VPR's (or reconstructed external) placement solution in flat placement file format; this file lists cluster and intra-cluster placement coordinates for each atom and can be used to reconstruct a clustering and placement solution.") + .show_in(argparse::ShowIn::HELP_ONLY); + file_grp.add_argument(args.write_flat_place_file, "--write_flat_place") .help( "VPR's (or reconstructed external) placement solution in flat placement file format; this file lists cluster and intra-cluster placement coordinates for each atom and can be used to reconstruct a clustering and placement solution.") diff --git a/vpr/src/base/read_options.h b/vpr/src/base/read_options.h index b43e3734de..55dc3d50c0 100644 --- a/vpr/src/base/read_options.h +++ b/vpr/src/base/read_options.h @@ -33,6 +33,7 @@ struct t_options { argparse::ArgValue read_vpr_constraints_file; argparse::ArgValue write_vpr_constraints_file; argparse::ArgValue write_constraints_file; + argparse::ArgValue read_flat_place_file; argparse::ArgValue write_flat_place_file; argparse::ArgValue write_placement_delay_lookup; diff --git a/vpr/src/base/vpr_api.cpp b/vpr/src/base/vpr_api.cpp index 7b0150bee4..1e3a4c390e 100644 --- a/vpr/src/base/vpr_api.cpp +++ b/vpr/src/base/vpr_api.cpp @@ -15,6 +15,7 @@ #include #include +#include "FlatPlacementInfo.h" #include "cluster_util.h" #include "verify_placement.h" #include "vpr_context.h" @@ -687,10 +688,18 @@ bool vpr_pack(t_vpr_setup& vpr_setup, const t_arch& arch) { + wtoi_switch_del); /* multiply by 4 to get a more conservative estimate */ } + // Read in the flat placement if a flat placement file is provided. + FlatPlacementInfo flat_placement_info; + if (!vpr_setup.FileNameOpts.read_flat_place_file.empty()) { + flat_placement_info = read_flat_placement( + vpr_setup.FileNameOpts.read_flat_place_file, + g_vpr_ctx.atom().nlist); + } + return try_pack(&vpr_setup.PackerOpts, &vpr_setup.AnalysisOpts, &arch, vpr_setup.user_models, vpr_setup.library_models, inter_cluster_delay, - vpr_setup.PackerRRGraph); + vpr_setup.PackerRRGraph, flat_placement_info); } void vpr_load_packing(t_vpr_setup& vpr_setup, const t_arch& arch) { diff --git a/vpr/src/base/vpr_types.h b/vpr/src/base/vpr_types.h index e33c1ac004..2e4ee9c255 100644 --- a/vpr/src/base/vpr_types.h +++ b/vpr/src/base/vpr_types.h @@ -755,6 +755,7 @@ struct t_file_name_opts { std::string read_vpr_constraints_file; std::string write_vpr_constraints_file; std::string write_constraints_file; + std::string read_flat_place_file; std::string write_flat_place_file; std::string write_block_usage; bool verify_file_digests; diff --git a/vpr/src/pack/pack.cpp b/vpr/src/pack/pack.cpp index e9ba3f7f7e..b71505246f 100644 --- a/vpr/src/pack/pack.cpp +++ b/vpr/src/pack/pack.cpp @@ -1,5 +1,8 @@ -#include +#include "pack.h" + +#include +#include "FlatPlacementInfo.h" #include "SetupGrid.h" #include "attraction_groups.h" #include "cluster_legalizer.h" @@ -7,8 +10,8 @@ #include "constraints_report.h" #include "globals.h" #include "greedy_clusterer.h" -#include "pack.h" #include "prepack.h" +#include "verify_flat_placement.h" #include "vpr_context.h" #include "vpr_error.h" #include "vpr_types.h" @@ -26,7 +29,8 @@ bool try_pack(t_packer_opts* packer_opts, const t_model* user_models, const t_model* library_models, float interc_delay, - std::vector* lb_type_rr_graphs) { + std::vector* lb_type_rr_graphs, + const FlatPlacementInfo& flat_placement_info) { const AtomContext& atom_ctx = g_vpr_ctx.atom(); const DeviceContext& device_ctx = g_vpr_ctx.device(); // The clusterer modifies the device context by increasing the size of the @@ -69,6 +73,24 @@ bool try_pack(t_packer_opts* packer_opts, VTR_LOG("%d attraction groups were created during prepacking.\n", attraction_groups.num_attraction_groups()); VTR_LOG("Finish prepacking.\n"); + // Verify that the Flat Placement is valid for packing. + if (flat_placement_info.valid) { + unsigned num_errors = verify_flat_placement_for_packing(flat_placement_info, + atom_ctx.nlist, + prepacker); + if (num_errors == 0) { + VTR_LOG("Completed flat placement consistency check successfully.\n"); + } else { + // TODO: In the future, we can just erase the flat placement and + // continue. It depends on what we want to happen if the + // flat placement is not valid. + VPR_ERROR(VPR_ERROR_PACK, + "%u errors found while performing flat placement " + "consistency check. Aborting program.\n", + num_errors); + } + } + if (packer_opts->auto_compute_inter_cluster_net_delay) { packer_opts->inter_cluster_net_delay = interc_delay; VTR_LOG("Using inter-cluster delay: %g\n", packer_opts->inter_cluster_net_delay); diff --git a/vpr/src/pack/pack.h b/vpr/src/pack/pack.h index 842feb0aac..00fba353da 100644 --- a/vpr/src/pack/pack.h +++ b/vpr/src/pack/pack.h @@ -5,19 +5,43 @@ #include class AtomNetId; +class FlatPlacementInfo; struct t_analysis_opts; struct t_arch; struct t_lb_type_rr_node; struct t_model; struct t_packer_opts; +/** + * @brief Try to pack the atom netlist into legal clusters on the given + * architecture. Will return true if successful, false otherwise. + * + * @param packer_opts + * Options passed by the user to configure the packing algorithm. + * @param analysis_opts + * Options passed by the user to configure how analysis is + * performed in the packer. + * @param arch + * A pointer to the architecture to create clusters for. + * @param user_models + * A list of architecture models provided by the architecture file. + * @param library_models + * A list of architecture models provided by the library. + * @param interc_delay + * @param lb_type_rr_graphs + * @param flat_placement_info + * Flat (primitive-level) placement information that may be + * provided by the user as a hint for packing. Will be invalid if + * there is no flat placement information provided. + */ bool try_pack(t_packer_opts* packer_opts, const t_analysis_opts* analysis_opts, const t_arch* arch, const t_model* user_models, const t_model* library_models, float interc_delay, - std::vector* lb_type_rr_graphs); + std::vector* lb_type_rr_graphs, + const FlatPlacementInfo& flat_placement_info); float get_arch_switch_info(short switch_index, int switch_fanin, float& Tdel_switch, float& R_switch, float& Cout_switch); diff --git a/vpr/src/pack/verify_flat_placement.cpp b/vpr/src/pack/verify_flat_placement.cpp new file mode 100644 index 0000000000..00e4b25987 --- /dev/null +++ b/vpr/src/pack/verify_flat_placement.cpp @@ -0,0 +1,101 @@ + +#include "verify_flat_placement.h" +#include "FlatPlacementInfo.h" +#include "atom_netlist.h" +#include "atom_netlist_fwd.h" +#include "prepack.h" +#include "vpr_types.h" +#include "vtr_log.h" + +unsigned verify_flat_placement_for_packing(const FlatPlacementInfo& flat_placement_info, + const AtomNetlist& atom_netlist, + const Prepacker& prepacker) { + unsigned num_errors = 0; + + // Quick check to ensure that the flat placement info has the correct size + // for each piece of information. + if (flat_placement_info.blk_x_pos.size() != atom_netlist.blocks().size() || + flat_placement_info.blk_y_pos.size() != atom_netlist.blocks().size() || + flat_placement_info.blk_layer.size() != atom_netlist.blocks().size() || + flat_placement_info.blk_sub_tile.size() != atom_netlist.blocks().size() || + flat_placement_info.blk_site_idx.size() != atom_netlist.blocks().size()) { + VTR_LOG_ERROR( + "The number of blocks in the flat placement does not match the " + "number of blocks in the atom netlist.\n"); + num_errors++; + // Return here since this error can cause issues below. + return num_errors; + } + + // 1. Verify that every atom has an (x, y, layer) position on the device. + // + // TODO: In the future, we may be able to allow some blocks to have + // undefined positions. + for (AtomBlockId blk_id : atom_netlist.blocks()) { + if (flat_placement_info.blk_x_pos[blk_id] == FlatPlacementInfo::UNDEFINED_POS || + flat_placement_info.blk_y_pos[blk_id] == FlatPlacementInfo::UNDEFINED_POS || + flat_placement_info.blk_layer[blk_id] == FlatPlacementInfo::UNDEFINED_POS) { + VTR_LOG_ERROR( + "Atom block %s has an undefined position in the flat placement.\n", + atom_netlist.block_name(blk_id).c_str()); + num_errors++; + } + } + + // 2. Verify that every atom block has non-negative position values. + // + // Since the device may not be sized yet, we cannot check if the positions + // are within the bounds of the device, but if any position value is + // negative (and is not undefined) we know that it is invalid. + for (AtomBlockId blk_id : atom_netlist.blocks()) { + float blk_x_pos = flat_placement_info.blk_x_pos[blk_id]; + float blk_y_pos = flat_placement_info.blk_y_pos[blk_id]; + float blk_layer = flat_placement_info.blk_layer[blk_id]; + int blk_sub_tile = flat_placement_info.blk_sub_tile[blk_id]; + int blk_site_idx = flat_placement_info.blk_site_idx[blk_id]; + if ((blk_x_pos < 0.f && blk_x_pos != FlatPlacementInfo::UNDEFINED_POS) || + (blk_y_pos < 0.f && blk_y_pos != FlatPlacementInfo::UNDEFINED_POS) || + (blk_layer < 0.f && blk_layer != FlatPlacementInfo::UNDEFINED_POS) || + (blk_sub_tile < 0 && blk_sub_tile != FlatPlacementInfo::UNDEFINED_SUB_TILE) || + (blk_site_idx < 0 && blk_site_idx != FlatPlacementInfo::UNDEFINED_SITE_IDX)) { + VTR_LOG_ERROR( + "Atom block %s is placed at an invalid position on the FPGA.\n", + atom_netlist.block_name(blk_id).c_str()); + num_errors++; + } + } + + // 3. Verify that every atom in each molecule has the same position. + // + // TODO: In the future, we can support if some of the atoms are undefined, + // but that can be fixed-up before calling this method. + std::vector molecules = prepacker.get_molecules_vector(); + for (t_pack_molecule* mol : molecules) { + AtomBlockId root_blk_id = mol->atom_block_ids[mol->root]; + float root_pos_x = flat_placement_info.blk_x_pos[root_blk_id]; + float root_pos_y = flat_placement_info.blk_y_pos[root_blk_id]; + float root_layer = flat_placement_info.blk_layer[root_blk_id]; + int root_sub_tile = flat_placement_info.blk_sub_tile[root_blk_id]; + for (AtomBlockId mol_blk_id : mol->atom_block_ids) { + if (!mol_blk_id.is_valid()) + continue; + if (flat_placement_info.blk_x_pos[mol_blk_id] != root_pos_x || + flat_placement_info.blk_y_pos[mol_blk_id] != root_pos_y || + flat_placement_info.blk_layer[mol_blk_id] != root_layer || + flat_placement_info.blk_sub_tile[mol_blk_id] != root_sub_tile) { + VTR_LOG_ERROR( + "Molecule with root atom block %s contains atom block %s " + "which is not at the same position as the root atom " + "block.\n", + atom_netlist.block_name(root_blk_id).c_str(), + atom_netlist.block_name(mol_blk_id).c_str()); + num_errors++; + } + } + } + + // TODO: May want to verify that the layer is all 0 in the case of 2D FPGAs. + + return num_errors; +} + diff --git a/vpr/src/pack/verify_flat_placement.h b/vpr/src/pack/verify_flat_placement.h new file mode 100644 index 0000000000..c550853d95 --- /dev/null +++ b/vpr/src/pack/verify_flat_placement.h @@ -0,0 +1,38 @@ +/** + * @file + * @author Alex Singer + * @date January 2025 + * @brief Independent verify methods to check invariants on the flat + * placement that has been passed into the packer. This checks for + * invalid data so this does not have to be checked during packing. + */ + +#pragma once + +// Forward declarations +class FlatPlacementInfo; +class AtomNetlist; +class Prepacker; + +/** + * @brief Verify the flat placement for use in the packer. + * + * This method will check the following invariants: + * 1. Every atom has a defined x and y position. + * 2. Every atom has non-negative placement information values. + * 3. Every molecule has atoms that have the same placement information. + * + * This method will log error messages for each issue it finds and will return + * a count of the number of errors. + * + * @param flat_placement_info + * The flat placement to verify. + * @param atom_netlist + * The netlist of atoms in the circuits. + * @param prepacker + * The prepacker object used to prepack the atoms into molecules. + */ +unsigned verify_flat_placement_for_packing(const FlatPlacementInfo& flat_placement_info, + const AtomNetlist& atom_netlist, + const Prepacker& prepacker); +