From 2934ebda0e7411a2f58d3d7330c8b0da811e5109 Mon Sep 17 00:00:00 2001 From: Amir Poolad Date: Tue, 11 Mar 2025 19:55:12 -0400 Subject: [PATCH 01/14] Remove usage of atom to pb lookup from packing Adds new AtomPBLookUp class that holds the atom to/from lookup data in the cluster legalizer class. The packing stage now uses that instead of the global context. At the end of packing, AtomPBLookUp gets copied to the global context and everything goes on as normal. --- vpr/src/base/atom_lookup.cpp | 4 + vpr/src/base/atom_lookup.h | 12 + vpr/src/pack/atom_pb_lookup.h | 37 +++ vpr/src/pack/cluster_legalizer.cpp | 312 ++++++++++----------- vpr/src/pack/cluster_legalizer.h | 222 ++++++++++++++- vpr/src/pack/cluster_router.cpp | 33 +-- vpr/src/pack/cluster_router.h | 4 +- vpr/src/pack/greedy_candidate_selector.cpp | 20 +- vpr/src/pack/pack.cpp | 14 +- vpr/src/util/vpr_utils.cpp | 37 ++- vpr/src/util/vpr_utils.h | 5 +- 11 files changed, 482 insertions(+), 218 deletions(-) create mode 100644 vpr/src/pack/atom_pb_lookup.h diff --git a/vpr/src/base/atom_lookup.cpp b/vpr/src/base/atom_lookup.cpp index eb597ff8abd..2d95e9d3e79 100644 --- a/vpr/src/base/atom_lookup.cpp +++ b/vpr/src/base/atom_lookup.cpp @@ -8,6 +8,7 @@ * PB */ const t_pb* AtomLookup::atom_pb(const AtomBlockId blk_id) const { + VTR_ASSERT(!lock_atom_pb); auto iter = atom_to_pb_.find(blk_id); if (iter == atom_to_pb_.end()) { //Not found @@ -17,6 +18,7 @@ const t_pb* AtomLookup::atom_pb(const AtomBlockId blk_id) const { } AtomBlockId AtomLookup::pb_atom(const t_pb* pb) const { + VTR_ASSERT(!lock_atom_pb); auto iter = atom_to_pb_.find(pb); if (iter == atom_to_pb_.inverse_end()) { //Not found @@ -26,6 +28,7 @@ AtomBlockId AtomLookup::pb_atom(const t_pb* pb) const { } const t_pb_graph_node* AtomLookup::atom_pb_graph_node(const AtomBlockId blk_id) const { + VTR_ASSERT(!lock_atom_pb); const t_pb* pb = atom_pb(blk_id); if (pb) { //Found @@ -35,6 +38,7 @@ const t_pb_graph_node* AtomLookup::atom_pb_graph_node(const AtomBlockId blk_id) } void AtomLookup::set_atom_pb(const AtomBlockId blk_id, const t_pb* pb) { + VTR_ASSERT(!lock_atom_pb); //If either of blk_id or pb are not valid, //remove any mapping diff --git a/vpr/src/base/atom_lookup.h b/vpr/src/base/atom_lookup.h index fdf17cddd46..ba33be7e383 100644 --- a/vpr/src/base/atom_lookup.h +++ b/vpr/src/base/atom_lookup.h @@ -25,6 +25,8 @@ class AtomLookup { typedef vtr::Range pin_tnode_range; + bool lock_atom_pb = false; + public: /* * PBs @@ -110,6 +112,16 @@ class AtomLookup { ///@brief Sets the bi-directional mapping between an atom netlist pin and timing graph node void set_atom_pin_tnode(const AtomPinId pin, const tatum::NodeId node, BlockTnode block_tnode_type); + // Getter function for atom_to_pb_ + inline const vtr::bimap &atom_to_pb() const{ + return atom_to_pb_; + } + + // Setter function for atom_to_pb_ + void set_atom_to_pb(const vtr::bimap &atom_to_pb){ + atom_to_pb_ = atom_to_pb; + } + private: //Types private: vtr::bimap atom_to_pb_; diff --git a/vpr/src/pack/atom_pb_lookup.h b/vpr/src/pack/atom_pb_lookup.h new file mode 100644 index 00000000000..df643cc1a14 --- /dev/null +++ b/vpr/src/pack/atom_pb_lookup.h @@ -0,0 +1,37 @@ +#pragma once + +#include "vpr_types.h" + +// Forward declaration +class t_pb_graph_node; + +class AtomPBLookUp { + public: + AtomPBLookUp() = default; + AtomPBLookUp(const vtr::bimap &atom_to_pb); + + + /** + * @brief Returns the leaf pb associated with the atom blk_id + * @note this is the lowest level pb which corresponds directly to the atom block + */ + const t_pb* atom_pb(const AtomBlockId blk_id) const; + + ///@brief Returns the atom block id associated with pb + AtomBlockId pb_atom(const t_pb* pb) const; + + ///@brief Conveneince wrapper around atom_pb to access the associated graph node + const t_pb_graph_node* atom_pb_graph_node(const AtomBlockId blk_id) const; + + /** + * @brief Sets the bidirectional mapping between an atom and pb + * + * If either blk_id or pb are not valid any, existing mapping is removed + */ + void set_atom_pb(const AtomBlockId blk_id, const t_pb* pb); + + const vtr::bimap &atom_to_pb() const {return atom_to_pb_;} + + private: + vtr::bimap atom_to_pb_; + }; diff --git a/vpr/src/pack/cluster_legalizer.cpp b/vpr/src/pack/cluster_legalizer.cpp index 19f3c2afd55..4b7be325eef 100644 --- a/vpr/src/pack/cluster_legalizer.cpp +++ b/vpr/src/pack/cluster_legalizer.cpp @@ -38,12 +38,7 @@ #include "vtr_vector.h" #include "vtr_vector_map.h" -/* - * @brief Allocates the stats stored within the pb of a cluster. - * - * Used to store information used during clustering. - */ -static void alloc_and_load_pb_stats(t_pb* pb) { +void ClusterLegalizer::alloc_and_load_pb_stats(t_pb* pb) { /* Call this routine when starting to fill up a new cluster. It resets * * the gain vector, etc. */ @@ -57,17 +52,13 @@ static void alloc_and_load_pb_stats(t_pb* pb) { pb->pb_stats->num_child_blocks_in_pb = 0; } -/* - * @brief Check the atom blocks of a cluster pb. Used in the verify method. - */ -/* TODO: May want to check that all atom blocks are actually reached */ -static void check_cluster_atom_blocks(t_pb* pb, std::unordered_set& blocks_checked) { +void ClusterLegalizer::check_cluster_atom_blocks(t_pb* pb, std::unordered_set& blocks_checked) { const AtomContext& atom_ctx = g_vpr_ctx.atom(); const t_pb_type* pb_type = pb->pb_graph_node->pb_type; if (pb_type->num_modes == 0) { /* primitive */ - AtomBlockId blk_id = atom_ctx.lookup().pb_atom(pb); + AtomBlockId blk_id = atom_pb_lookup().pb_atom(pb); if (blk_id) { if (blocks_checked.count(blk_id)) { VPR_FATAL_ERROR(VPR_ERROR_PACK, @@ -75,7 +66,7 @@ static void check_cluster_atom_blocks(t_pb* pb, std::unordered_set& pb->name, atom_ctx.netlist().block_name(blk_id).c_str()); } blocks_checked.insert(blk_id); - if (pb != atom_ctx.lookup().atom_pb(blk_id)) { + if (pb != atom_pb_lookup().atom_pb(blk_id)) { VPR_FATAL_ERROR(VPR_ERROR_PACK, "pb %s contains atom block %s but atom block does not link to pb.\n", pb->name, atom_ctx.netlist().block_name(blk_id).c_str()); @@ -98,9 +89,7 @@ static void check_cluster_atom_blocks(t_pb* pb, std::unordered_set& } } -/// @brief Recursively frees the pb stats of the given pb, without freeing the -/// pb itself. -static void free_pb_stats_recursive(t_pb* pb) { +void ClusterLegalizer::free_pb_stats_recursive(t_pb* pb) { /* Releases all the memory used by clustering data structures. */ if (pb) { if (pb->pb_graph_node != nullptr) { @@ -118,27 +107,7 @@ static void free_pb_stats_recursive(t_pb* pb) { } } -/** - * @brief Checks whether an atom block can be added to a clustered block - * without violating floorplanning constraints. It also updates the - * clustered block's floorplanning region by taking the intersection of - * its current region and the floorplanning region of the given atom block. - * - * @param atom_blk_id A unique ID for the candidate atom block to - * be added to the growing cluster. - * @param cluster_pr The floorplanning regions of the clustered - * block. This function may update the given - * region. - * @param constraints The set of user-given place constraints. - * @param log_verbosity Controls the detail level of log information - * printed by this function. - * @param cluster_pr_needs_update Indicates whether the floorplanning region - * of the clustered block have updated. - * - * @return True if adding the given atom block to the clustered block does not - * violated any floorplanning constraints. - */ -static bool check_cluster_floorplanning(AtomBlockId atom_blk_id, +bool ClusterLegalizer::check_cluster_floorplanning(AtomBlockId atom_blk_id, PartitionRegion& cluster_pr, const UserPlaceConstraints& constraints, int log_verbosity, @@ -194,27 +163,7 @@ static bool check_cluster_floorplanning(AtomBlockId atom_blk_id, return true; } -/** - * @brief Checks if an atom block can be added to a clustered block without - * violating NoC group constraints. For passing this check, either both - * clustered and atom blocks must belong to the same NoC group, or at - * least one of them should not belong to any NoC group. If the atom block - * is associated with a NoC group while the clustered block does not - * belong to any NoC groups, the NoC group ID of the atom block is assigned - * to the clustered block when the atom is added to it. - * - * @param atom_blk_id A unique ID for the candidate atom block to be - * added to the growing cluster. - * @param cluster_noc_grp_id The NoC group ID of the clustered block. This - * function may update this ID. - * @param atom_noc_grp_ids A mapping from atoms to NoC group IDs. - * @param log_verbosity Controls the detail level of log information - * printed by this function. - * - * @return True if adding the atom block the cluster does not violate NoC group - * constraints. - */ -static bool check_cluster_noc_group(AtomBlockId atom_blk_id, +bool ClusterLegalizer::check_cluster_noc_group(AtomBlockId atom_blk_id, NocGroupId& cluster_noc_grp_id, const vtr::vector& atom_noc_grp_ids, int log_verbosity) { @@ -247,13 +196,7 @@ static bool check_cluster_noc_group(AtomBlockId atom_blk_id, return false; } -/** - * @brief This function takes the root block of a chain molecule and a proposed - * placement primitive for this block. The function then checks if this - * chain root block has a placement constraint (such as being driven from - * outside the cluster) and returns the status of the placement accordingly. - */ -static enum e_block_pack_status check_chain_root_placement_feasibility( +enum e_block_pack_status ClusterLegalizer::check_chain_root_placement_feasibility( const t_pb_graph_node* pb_graph_node, const t_chain_info& prepack_chain_info, const t_clustering_chain_info& clustering_chain_info, @@ -313,16 +256,7 @@ static enum e_block_pack_status check_chain_root_placement_feasibility( return block_pack_status; } -/* - * @brief Check that the two atom blocks blk_id and sibling_blk_id (which should - * both be memory slices) are feasible, in the sense that they have - * precicely the same net connections (with the exception of nets in data - * port classes). - * - * Note that this routine does not check pin feasibility against the cur_pb_type; so - * primitive_type_feasible() should also be called on blk_id before concluding it is feasible. - */ -static bool primitive_memory_sibling_feasible(const AtomBlockId blk_id, const t_pb_type* cur_pb_type, const AtomBlockId sibling_blk_id) { +bool ClusterLegalizer::primitive_memory_sibling_feasible(const AtomBlockId blk_id, const t_pb_type* cur_pb_type, const AtomBlockId sibling_blk_id) { const AtomContext& atom_ctx = g_vpr_ctx.atom(); VTR_ASSERT(cur_pb_type->class_type == MEMORY_CLASS); @@ -390,17 +324,14 @@ static bool primitive_memory_sibling_feasible(const AtomBlockId blk_id, const t_ return true; } -/* - * @brief Check if the given atom is feasible in the given pb. - */ -static bool primitive_feasible(const AtomBlockId blk_id, t_pb* cur_pb) { +bool ClusterLegalizer::primitive_feasible(const AtomBlockId blk_id, t_pb* cur_pb) { const AtomContext& atom_ctx = g_vpr_ctx.atom(); const t_pb_type* cur_pb_type = cur_pb->pb_graph_node->pb_type; VTR_ASSERT(cur_pb_type->num_modes == 0); /* primitive */ - AtomBlockId cur_pb_blk_id = atom_ctx.lookup().pb_atom(cur_pb); + AtomBlockId cur_pb_blk_id = atom_pb_lookup().pb_atom(cur_pb); if (cur_pb_blk_id && cur_pb_blk_id != blk_id) { /* This pb already has a different logical block */ return false; @@ -411,7 +342,8 @@ static bool primitive_feasible(const AtomBlockId blk_id, t_pb* cur_pb) { * - all siblings must share all nets, including open nets, with the exception of data nets */ /* find sibling if one exists */ - AtomBlockId sibling_memory_blk_id = find_memory_sibling(cur_pb); + const t_pb *sibling_memory_pb = find_memory_sibling(cur_pb); + AtomBlockId sibling_memory_blk_id = atom_pb_lookup().pb_atom(sibling_memory_pb); if (sibling_memory_blk_id) { //There is a sibling, see if the current block is feasible with it @@ -426,11 +358,8 @@ static bool primitive_feasible(const AtomBlockId blk_id, t_pb* cur_pb) { return primitive_type_feasible(blk_id, cur_pb_type); } -/** - * @brief Try to place atom block into current primitive location - */ -static enum e_block_pack_status -try_place_atom_block_rec(const t_pb_graph_node* pb_graph_node, +enum e_block_pack_status +ClusterLegalizer::try_place_atom_block_rec(const t_pb_graph_node* pb_graph_node, const AtomBlockId blk_id, t_pb* cb, t_pb** parent, @@ -514,8 +443,8 @@ try_place_atom_block_rec(const t_pb_graph_node* pb_graph_node, bool is_primitive = (pb_type->num_modes == 0); if (is_primitive) { - VTR_ASSERT(!atom_ctx.lookup().pb_atom(pb) - && atom_ctx.lookup().atom_pb(blk_id) == nullptr + VTR_ASSERT(!atom_pb_lookup().pb_atom(pb) + && atom_pb_lookup().atom_pb(blk_id) == nullptr && atom_cluster[blk_id] == LegalizationClusterId::INVALID()); /* try pack to location */ VTR_ASSERT(pb->name == nullptr); @@ -528,9 +457,9 @@ try_place_atom_block_rec(const t_pb_graph_node* pb_graph_node, // TODO: It would be a good idea to remove the use of this global // variables to prevent external users from modifying this by // mistake. - mutable_atom_ctx.mutable_lookup().set_atom_pb(blk_id, pb); + mutable_atom_pb_lookup().set_atom_pb(blk_id, pb); - add_atom_as_target(router_data, blk_id); + add_atom_as_target(router_data, blk_id, atom_pb_lookup()); if (!primitive_feasible(blk_id, pb)) { /* failed location feasibility check, revert pack */ block_pack_status = e_block_pack_status::BLK_FAILED_FEASIBLE; @@ -566,11 +495,7 @@ try_place_atom_block_rec(const t_pb_graph_node* pb_graph_node, return block_pack_status; } -/* - * @brief Resets nets used at different pin classes for determining pin - * feasibility. - */ -static void reset_lookahead_pins_used(t_pb* cur_pb) { +void ClusterLegalizer::reset_lookahead_pins_used(t_pb* cur_pb) { const t_pb_type* pb_type = cur_pb->pb_graph_node->pb_type; if (cur_pb->pb_stats == nullptr) { return; /* No pins used, no need to continue */ @@ -597,17 +522,13 @@ static void reset_lookahead_pins_used(t_pb* cur_pb) { } } -/* - * @brief Checks if the sinks of the given net are reachable from the driver - * pb gpin. - */ -static int net_sinks_reachable_in_cluster(const t_pb_graph_pin* driver_pb_gpin, const int depth, const AtomNetId net_id) { +int ClusterLegalizer::net_sinks_reachable_in_cluster(const t_pb_graph_pin* driver_pb_gpin, const int depth, const AtomNetId net_id) { const AtomContext& atom_ctx = g_vpr_ctx.atom(); //Record the sink pb graph pins we are looking for std::unordered_set sink_pb_gpins; for (const AtomPinId pin_id : atom_ctx.netlist().net_sinks(net_id)) { - const t_pb_graph_pin* sink_pb_gpin = find_pb_graph_pin(atom_ctx.netlist(), atom_ctx.lookup(), pin_id); + const t_pb_graph_pin* sink_pb_gpin = find_pb_graph_pin(atom_ctx.netlist(), atom_pb_lookup(), pin_id); VTR_ASSERT(sink_pb_gpin); sink_pb_gpins.insert(sink_pb_gpin); @@ -629,10 +550,7 @@ static int net_sinks_reachable_in_cluster(const t_pb_graph_pin* driver_pb_gpin, return false; } -/** - * @brief Returns the pb_graph_pin of the atom pin defined by the driver_pin_id in the driver_pb - */ -static t_pb_graph_pin* get_driver_pb_graph_pin(const t_pb* driver_pb, const AtomPinId driver_pin_id) { +t_pb_graph_pin* ClusterLegalizer::get_driver_pb_graph_pin(const t_pb* driver_pb, const AtomPinId driver_pin_id) { const AtomContext& atom_ctx = g_vpr_ctx.atom(); const auto driver_pb_type = driver_pb->pb_graph_node->pb_type; @@ -656,15 +574,7 @@ static t_pb_graph_pin* get_driver_pb_graph_pin(const t_pb* driver_pb, const Atom return nullptr; } -/** - * @brief Given a pin and its assigned net, mark all pin classes that are affected. - * Check if connecting this pin to it's driver pin or to all sink pins will - * require leaving a pb_block starting from the parent pb_block of the - * primitive till the root block (depth = 0). If leaving a pb_block is - * required add this net to the pin class (to increment the number of used - * pins from this class) that should be used to leave the pb_block. - */ -static void compute_and_mark_lookahead_pins_used_for_pin(const t_pb_graph_pin* pb_graph_pin, +void ClusterLegalizer::compute_and_mark_lookahead_pins_used_for_pin(const t_pb_graph_pin* pb_graph_pin, const t_pb* primitive_pb, const AtomNetId net_id, const vtr::vector_map& atom_cluster) { @@ -684,9 +594,9 @@ static void compute_and_mark_lookahead_pins_used_for_pin(const t_pb_graph_pin* p // find the driver of the input net connected to the pin being studied const auto driver_pin_id = atom_ctx.netlist().net_driver(net_id); // find the id of the atom occupying the input primitive_pb - const auto prim_blk_id = atom_ctx.lookup().pb_atom(primitive_pb); + const auto prim_blk_id = atom_pb_lookup().pb_atom(primitive_pb); // find the pb block occupied by the driving atom - const auto driver_pb = atom_ctx.lookup().atom_pb(driver_blk_id); + const auto driver_pb = atom_pb_lookup().atom_pb(driver_blk_id); // pb_graph_pin driving net_id in the driver pb block t_pb_graph_pin* output_pb_graph_pin = nullptr; // if the driver block is in the same clb as the input primitive block @@ -789,33 +699,23 @@ static void compute_and_mark_lookahead_pins_used_for_pin(const t_pb_graph_pin* p } } - -/* - * @brief Determine if pins of speculatively packed pb are legal - */ -static void compute_and_mark_lookahead_pins_used(const AtomBlockId blk_id, +void ClusterLegalizer::compute_and_mark_lookahead_pins_used(const AtomBlockId blk_id, const vtr::vector_map& atom_cluster) { const AtomContext& atom_ctx = g_vpr_ctx.atom(); - const t_pb* cur_pb = atom_ctx.lookup().atom_pb(blk_id); + const t_pb* cur_pb = atom_pb_lookup().atom_pb(blk_id); VTR_ASSERT(cur_pb != nullptr); /* Walk through inputs, outputs, and clocks marking pins off of the same class */ for (auto pin_id : atom_ctx.netlist().block_pins(blk_id)) { auto net_id = atom_ctx.netlist().pin_net(pin_id); - const t_pb_graph_pin* pb_graph_pin = find_pb_graph_pin(atom_ctx.netlist(), atom_ctx.lookup(), pin_id); + const t_pb_graph_pin* pb_graph_pin = find_pb_graph_pin(atom_ctx.netlist(), atom_pb_lookup(), pin_id); compute_and_mark_lookahead_pins_used_for_pin(pb_graph_pin, cur_pb, net_id, atom_cluster); } } -/* - * @brief Determine if speculatively packed cur_pb is pin feasible - * - * Runtime is actually not that bad for this. It's worst case O(k^2) where k is the - * number of pb_graph pins. Can use hash tables or make incremental if becomes an issue. - */ -static void try_update_lookahead_pins_used(t_pb* cur_pb, +void ClusterLegalizer::try_update_lookahead_pins_used(t_pb* cur_pb, const vtr::vector_map& atom_cluster) { const AtomContext& atom_ctx = g_vpr_ctx.atom(); @@ -834,18 +734,14 @@ static void try_update_lookahead_pins_used(t_pb* cur_pb, } else { // find if this child (primitive) pb block has an atom mapped to it, // if yes compute and mark lookahead pins used for that pb block - AtomBlockId blk_id = atom_ctx.lookup().pb_atom(cur_pb); + AtomBlockId blk_id = atom_pb_lookup().pb_atom(cur_pb); if (pb_type->blif_model != nullptr && blk_id) { compute_and_mark_lookahead_pins_used(blk_id, atom_cluster); } } } -/* - * @brief Check if the number of available inputs/outputs for a pin class is - * sufficient for speculatively packed blocks. - */ -static bool check_lookahead_pins_used(t_pb* cur_pb, t_ext_pin_util max_external_pin_util) { +bool ClusterLegalizer::check_lookahead_pins_used(t_pb* cur_pb, t_ext_pin_util max_external_pin_util) { const t_pb_type* pb_type = cur_pb->pb_graph_node->pb_type; if (pb_type->num_modes > 0 && cur_pb->name) { @@ -961,10 +857,7 @@ void ClusterLegalizer::reset_molecule_info(PackMoleculeId mol_id) { } } -/* - * @brief Revert trial atom block iblock and free up memory space accordingly. - */ -static void revert_place_atom_block(const AtomBlockId blk_id, +void ClusterLegalizer::revert_place_atom_block(const AtomBlockId blk_id, t_lb_router_data* router_data, vtr::vector_map& atom_cluster) { const AtomContext& atom_ctx = g_vpr_ctx.atom(); @@ -975,7 +868,7 @@ static void revert_place_atom_block(const AtomBlockId blk_id, // //In general most code works fine accessing cosnt t_pb*, //which is why we store them as such in atom_ctx.lookup() - t_pb* pb = const_cast(atom_ctx.lookup().atom_pb(blk_id)); + t_pb* pb = const_cast(atom_pb_lookup().atom_pb(blk_id)); if (pb != nullptr) { /* When freeing molecules, the current block might already have been freed by a prior revert @@ -1008,13 +901,10 @@ static void revert_place_atom_block(const AtomBlockId blk_id, //Update the atom netlist mapping atom_cluster[blk_id] = LegalizationClusterId::INVALID(); - mutable_atom_ctx.mutable_lookup().set_atom_pb(blk_id, nullptr); + mutable_atom_pb_lookup().set_atom_pb(blk_id, nullptr); } -/* - * @brief Speculation successful, commit input/output pins used. - */ -static void commit_lookahead_pins_used(t_pb* cur_pb) { +void ClusterLegalizer::commit_lookahead_pins_used(t_pb* cur_pb) { const t_pb_type* pb_type = cur_pb->pb_graph_node->pb_type; if (pb_type->num_modes > 0 && cur_pb->name) { @@ -1046,22 +936,7 @@ static void commit_lookahead_pins_used(t_pb* cur_pb) { } } -/** - * @brief Cleans up a pb after unsuccessful molecule packing - * - * Recursively frees pbs from a t_pb tree. The given root pb itself is not - * deleted. - * - * If a pb object has its children allocated then before freeing them the - * function checks if there is no atom that corresponds to any of them. The - * check is performed only for leaf (primitive) pbs. The function recurses for - * non-primitive pbs. - * - * The cleaning itself includes deleting all child pbs, resetting mode of the - * pb and also freeing its name. This prepares the pb for another round of - * molecule packing tryout. - */ -static bool cleanup_pb(t_pb* pb) { +bool ClusterLegalizer::cleanup_pb(t_pb* pb) { bool can_free = true; /* Recursively check if there are any children with already assigned atoms */ @@ -1305,7 +1180,7 @@ e_block_pack_status ClusterLegalizer::try_pack_molecule(PackMoleculeId molecule_ /* Chained molecules often take up lots of area and are important, * if a chain is packed in, want to rename logic block to match chain name */ AtomBlockId chain_root_blk_id = molecule.atom_block_ids[molecule.pack_pattern->root_block->block_id]; - t_pb* cur_pb = atom_ctx.lookup().atom_pb(chain_root_blk_id)->parent_pb; + t_pb* cur_pb = atom_pb_lookup().atom_pb(chain_root_blk_id)->parent_pb; while (cur_pb != nullptr) { free(cur_pb->name); cur_pb->name = vtr::strdup(atom_ctx.netlist().block_name(chain_root_blk_id).c_str()); @@ -1350,7 +1225,7 @@ e_block_pack_status ClusterLegalizer::try_pack_molecule(PackMoleculeId molecule_ atom_cluster_[atom_blk_id] = cluster_id; // Update the num child blocks in pb - const t_pb* atom_pb = atom_ctx.lookup().atom_pb(atom_blk_id); + const t_pb* atom_pb = atom_pb_lookup().atom_pb(atom_blk_id); VTR_ASSERT_SAFE(atom_pb != nullptr); t_pb* cur_pb = atom_pb->parent_pb; while (cur_pb != nullptr) { @@ -1369,7 +1244,7 @@ e_block_pack_status ClusterLegalizer::try_pack_molecule(PackMoleculeId molecule_ for (size_t i = 0; i < failed_location; i++) { AtomBlockId atom_blk_id = molecule.atom_block_ids[i]; if (atom_blk_id) { - remove_atom_from_target(cluster.router_data, atom_blk_id); + remove_atom_from_target(cluster.router_data, atom_blk_id, atom_pb_lookup()); } } for (size_t i = 0; i < failed_location; i++) { @@ -1604,7 +1479,8 @@ ClusterLegalizer::ClusterLegalizer(const AtomNetlist& atom_netlist, const t_pack_high_fanout_thresholds& high_fanout_thresholds, ClusterLegalizationStrategy cluster_legalization_strategy, bool enable_pin_feasibility_filter, - int log_verbosity) : prepacker_(prepacker) { + int log_verbosity) : prepacker_(prepacker) + { // Verify that the inputs are valid. VTR_ASSERT_SAFE(lb_type_rr_graphs != nullptr); @@ -1632,6 +1508,7 @@ ClusterLegalizer::ClusterLegalizer(const AtomNetlist& atom_netlist, cluster_legalization_strategy_ = cluster_legalization_strategy; enable_pin_feasibility_filter_ = enable_pin_feasibility_filter; log_verbosity_ = log_verbosity; + atom_pb_lookup_ = AtomPBLookUp(g_vpr_ctx.atom().lookup().atom_to_pb()); } void ClusterLegalizer::reset() { @@ -1657,7 +1534,7 @@ void ClusterLegalizer::verify() { */ for (auto blk_id : atom_ctx.netlist().blocks()) { //Each atom should be part of a pb - const t_pb* atom_pb = atom_ctx.lookup().atom_pb(blk_id); + const t_pb* atom_pb = atom_pb_lookup().atom_pb(blk_id); if (!atom_pb) { VPR_FATAL_ERROR(VPR_ERROR_PACK, "Atom block %s is not mapped to a pb\n", @@ -1665,7 +1542,7 @@ void ClusterLegalizer::verify() { } //Check the reverse mapping is consistent - if (atom_ctx.lookup().pb_atom(atom_pb) != blk_id) { + if (atom_pb_lookup().pb_atom(atom_pb) != blk_id) { VPR_FATAL_ERROR(VPR_ERROR_PACK, "pb %s does not contain atom block %s but atom block %s maps to pb.\n", atom_pb->name, @@ -1772,6 +1649,59 @@ void ClusterLegalizer::finalize() { } } +void ClusterLegalizer::free_pb(t_pb* pb) { + if (pb == nullptr) { + return; + } + + const t_pb_type* pb_type; + int i, j, mode; + + pb_type = pb->pb_graph_node->pb_type; + + if (pb->name) { + free(pb->name); + pb->name = nullptr; + } + + if (pb_type->blif_model == nullptr) { + mode = pb->mode; + for (i = 0; i < pb_type->modes[mode].num_pb_type_children && pb->child_pbs != nullptr; i++) { + for (j = 0; j < pb_type->modes[mode].pb_type_children[i].num_pb && pb->child_pbs[i] != nullptr; j++) { + if (pb->child_pbs[i][j].name != nullptr || pb->child_pbs[i][j].child_pbs != nullptr) { + free_pb(&pb->child_pbs[i][j]); + } + } + if (pb->child_pbs[i]) { + //Free children (num_pb) + delete[] pb->child_pbs[i]; + } + } + if (pb->child_pbs) { + //Free child pointers (modes) + delete[] pb->child_pbs; + } + + pb->child_pbs = nullptr; + + } else { + /* Primitive */ + auto& atom_ctx = g_vpr_ctx.mutable_atom(); + auto blk_id = atom_pb_lookup().pb_atom(pb); + if (blk_id) { + //Update atom netlist mapping + atom_ctx.mutable_lookup().set_atom_clb(blk_id, ClusterBlockId::INVALID()); + atom_pb_lookup_.set_atom_pb(blk_id, nullptr); + } + atom_pb_lookup_.set_atom_pb(AtomBlockId::INVALID(), pb); + } + + if (pb && pb->pb_stats != nullptr) { + delete pb->pb_stats; + pb->pb_stats = nullptr; + } +} + ClusterLegalizer::~ClusterLegalizer() { // Destroy all clusters (no need to compress). for (LegalizationClusterId cluster_id : legalization_cluster_ids_) { @@ -1781,3 +1711,49 @@ ClusterLegalizer::~ClusterLegalizer() { } } +AtomPBLookUp::AtomPBLookUp(const vtr::bimap& atom_to_pb) { + atom_to_pb_ = atom_to_pb; +} + +const t_pb* AtomPBLookUp::atom_pb(const AtomBlockId blk_id) const { + auto iter = atom_to_pb_.find(blk_id); + if (iter == atom_to_pb_.end()) { + //Not found + return nullptr; + } + return iter->second; +} + +AtomBlockId AtomPBLookUp::pb_atom(const t_pb* pb) const { + auto iter = atom_to_pb_.find(pb); + if (iter == atom_to_pb_.inverse_end()) { + //Not found + return AtomBlockId::INVALID(); + } + return iter->second; +} + +const t_pb_graph_node* AtomPBLookUp::atom_pb_graph_node(const AtomBlockId blk_id) const { + const t_pb* pb = atom_pb(blk_id); + if (pb) { + //Found + return pb->pb_graph_node; + } + return nullptr; +} + +void AtomPBLookUp::set_atom_pb(const AtomBlockId blk_id, const t_pb* pb) { + //If either of blk_id or pb are not valid, + //remove any mapping + + if (!blk_id && pb) { + //Remove + atom_to_pb_.erase(pb); + } else if (blk_id && !pb) { + //Remove + atom_to_pb_.erase(blk_id); + } else if (blk_id && pb) { + //If both are valid store the mapping + atom_to_pb_.update(blk_id, pb); + } +} diff --git a/vpr/src/pack/cluster_legalizer.h b/vpr/src/pack/cluster_legalizer.h index 992f567ddbf..4e3c628f7d6 100644 --- a/vpr/src/pack/cluster_legalizer.h +++ b/vpr/src/pack/cluster_legalizer.h @@ -22,12 +22,14 @@ #include "vtr_strong_id.h" #include "vtr_vector.h" #include "vtr_vector_map.h" +#include "atom_pb_lookup.h" // Forward declarations class Prepacker; class t_intra_cluster_placement_stats; class t_pb_graph_node; struct t_lb_router_data; +class UserPlaceConstraints; // A special ID to identify the legalization clusters. This is separate from the // ClusterBlockId since this legalizer is not necessarily tied to the Clustered @@ -190,6 +192,7 @@ struct LegalizationCluster { * * // new_cluster_id now contains a fully legalized cluster. */ + class ClusterLegalizer { public: // Iterator for the legalization cluster IDs @@ -235,6 +238,200 @@ class ClusterLegalizer { */ void reset_molecule_info(PackMoleculeId mol_id); + /* + * @brief Allocates the stats stored within the pb of a cluster. + * + * Used to store information used during clustering. + */ + void alloc_and_load_pb_stats(t_pb* pb); + + /* + * @brief Check the atom blocks of a cluster pb. Used in the verify method. + */ + /* TODO: May want to check that all atom blocks are actually reached */ + void check_cluster_atom_blocks(t_pb* pb, std::unordered_set& blocks_checked); + + /// @brief Recursively frees the pb stats of the given pb, without freeing the + /// pb itself. + void free_pb_stats_recursive(t_pb* pb); + + /** + * @brief Checks whether an atom block can be added to a clustered block + * without violating floorplanning constraints. It also updates the + * clustered block's floorplanning region by taking the intersection of + * its current region and the floorplanning region of the given atom block. + * + * @param atom_blk_id A unique ID for the candidate atom block to + * be added to the growing cluster. + * @param cluster_pr The floorplanning regions of the clustered + * block. This function may update the given + * region. + * @param constraints The set of user-given place constraints. + * @param log_verbosity Controls the detail level of log information + * printed by this function. + * @param cluster_pr_needs_update Indicates whether the floorplanning region + * of the clustered block have updated. + * + * @return True if adding the given atom block to the clustered block does not + * violated any floorplanning constraints. + */ + bool check_cluster_floorplanning(AtomBlockId atom_blk_id, + PartitionRegion& cluster_pr, + const UserPlaceConstraints& constraints, + int log_verbosity, + bool& cluster_pr_needs_update); + + /** + * @brief Checks if an atom block can be added to a clustered block without + * violating NoC group constraints. For passing this check, either both + * clustered and atom blocks must belong to the same NoC group, or at + * least one of them should not belong to any NoC group. If the atom block + * is associated with a NoC group while the clustered block does not + * belong to any NoC groups, the NoC group ID of the atom block is assigned + * to the clustered block when the atom is added to it. + * + * @param atom_blk_id A unique ID for the candidate atom block to be + * added to the growing cluster. + * @param cluster_noc_grp_id The NoC group ID of the clustered block. This + * function may update this ID. + * @param atom_noc_grp_ids A mapping from atoms to NoC group IDs. + * @param log_verbosity Controls the detail level of log information + * printed by this function. + * + * @return True if adding the atom block the cluster does not violate NoC group + * constraints. + */ + bool check_cluster_noc_group(AtomBlockId atom_blk_id, + NocGroupId& cluster_noc_grp_id, + const vtr::vector& atom_noc_grp_ids, + int log_verbosity); + + /** + * @brief This function takes the root block of a chain molecule and a proposed + * placement primitive for this block. The function then checks if this + * chain root block has a placement constraint (such as being driven from + * outside the cluster) and returns the status of the placement accordingly. + */ + enum e_block_pack_status check_chain_root_placement_feasibility( + const t_pb_graph_node* pb_graph_node, + const t_chain_info& prepack_chain_info, + const t_clustering_chain_info& clustering_chain_info, + t_pack_patterns* mol_pack_patterns, + const AtomBlockId blk_id); + + /* + * @brief Check that the two atom blocks blk_id and sibling_blk_id (which should + * both be memory slices) are feasible, in the sense that they have + * precicely the same net connections (with the exception of nets in data + * port classes). + * + * Note that this routine does not check pin feasibility against the cur_pb_type; so + * primitive_type_feasible() should also be called on blk_id before concluding it is feasible. + */ + bool primitive_memory_sibling_feasible(const AtomBlockId blk_id, const t_pb_type* cur_pb_type, const AtomBlockId sibling_blk_id); + + /* + * @brief Check if the given atom is feasible in the given pb. + */ + bool primitive_feasible(const AtomBlockId blk_id, t_pb* cur_pb); + + /** + * @brief Try to place atom block into current primitive location + */ + enum e_block_pack_status + try_place_atom_block_rec(const t_pb_graph_node* pb_graph_node, + const AtomBlockId blk_id, + t_pb* cb, + t_pb** parent, + const LegalizationClusterId cluster_id, + vtr::vector_map& atom_cluster, + const PackMoleculeId molecule_id, + t_lb_router_data* router_data, + int verbosity, + const Prepacker& prepacker, + const vtr::vector_map& clustering_chain_info); + + /* + * @brief Resets nets used at different pin classes for determining pin + * feasibility. + */ + void reset_lookahead_pins_used(t_pb* cur_pb); + + /* + * @brief Checks if the sinks of the given net are reachable from the driver + * pb gpin. + */ + int net_sinks_reachable_in_cluster(const t_pb_graph_pin* driver_pb_gpin, const int depth, const AtomNetId net_id); + + /** + * @brief Returns the pb_graph_pin of the atom pin defined by the driver_pin_id in the driver_pb + */ + t_pb_graph_pin* get_driver_pb_graph_pin(const t_pb* driver_pb, const AtomPinId driver_pin_id); + + /** + * @brief Given a pin and its assigned net, mark all pin classes that are affected. + * Check if connecting this pin to it's driver pin or to all sink pins will + * require leaving a pb_block starting from the parent pb_block of the + * primitive till the root block (depth = 0). If leaving a pb_block is + * required add this net to the pin class (to increment the number of used + * pins from this class) that should be used to leave the pb_block. + */ + void compute_and_mark_lookahead_pins_used_for_pin(const t_pb_graph_pin* pb_graph_pin, + const t_pb* primitive_pb, + const AtomNetId net_id, + const vtr::vector_map& atom_cluster); + + /* + * @brief Determine if pins of speculatively packed pb are legal + */ + void compute_and_mark_lookahead_pins_used(const AtomBlockId blk_id, + const vtr::vector_map& atom_cluster); + + /* + * @brief Determine if speculatively packed cur_pb is pin feasible + * + * Runtime is actually not that bad for this. It's worst case O(k^2) where k is the + * number of pb_graph pins. Can use hash tables or make incremental if becomes an issue. + */ + void try_update_lookahead_pins_used(t_pb* cur_pb, + const vtr::vector_map& atom_cluster); + + /* + * @brief Check if the number of available inputs/outputs for a pin class is + * sufficient for speculatively packed blocks. + */ + bool check_lookahead_pins_used(t_pb* cur_pb, t_ext_pin_util max_external_pin_util); + + /* + * @brief Revert trial atom block iblock and free up memory space accordingly. + */ + void revert_place_atom_block(const AtomBlockId blk_id, + t_lb_router_data* router_data, + vtr::vector_map& atom_cluster); + + /* + * @brief Speculation successful, commit input/output pins used. + */ + void commit_lookahead_pins_used(t_pb* cur_pb); + + /** + * @brief Cleans up a pb after unsuccessful molecule packing + * + * Recursively frees pbs from a t_pb tree. The given root pb itself is not + * deleted. + * + * If a pb object has its children allocated then before freeing them the + * function checks if there is no atom that corresponds to any of them. The + * check is performed only for leaf (primitive) pbs. The function recurses for + * non-primitive pbs. + * + * The cleaning itself includes deleting all child pbs, resetting mode of the + * pb and also freeing its name. This prepares the pb for another round of + * molecule packing tryout. + */ + bool cleanup_pb(t_pb* pb); + + public: // Explicitly deleted default constructor. Need to use other constructor to @@ -515,6 +712,28 @@ class ClusterLegalizer { log_verbosity_ = verbosity; } + /* + * @brief Determine if atom block is in cluster block. + * + */ + bool is_atom_blk_in_cluster_block(const AtomBlockId blk_id, const AtomBlockId clustered_blk_id) const { + const t_pb* cur_pb = atom_pb_lookup_.atom_pb(blk_id); + const t_pb* pb = atom_pb_lookup_.atom_pb(clustered_blk_id); + while (cur_pb) { + if (cur_pb == pb) { + return true; + } + cur_pb = cur_pb->parent_pb; + } + return false; + } + + void free_pb(t_pb* pb); + + inline const AtomPBLookUp &atom_pb_lookup() const {return atom_pb_lookup_;} + inline AtomPBLookUp &mutable_atom_pb_lookup() {return atom_pb_lookup_;} + + /// @brief Destructor of the class. Frees allocated data. ~ClusterLegalizer(); @@ -582,5 +801,6 @@ class ClusterLegalizer { /// @brief The prepacker object that stores the molecules which will be /// legalized into clusters. const Prepacker& prepacker_; -}; + AtomPBLookUp atom_pb_lookup_; +}; \ No newline at end of file diff --git a/vpr/src/pack/cluster_router.cpp b/vpr/src/pack/cluster_router.cpp index 6bd4095736b..93739027149 100644 --- a/vpr/src/pack/cluster_router.cpp +++ b/vpr/src/pack/cluster_router.cpp @@ -34,6 +34,7 @@ #include "pb_type_graph.h" #include "lb_type_rr_graph.h" #include "cluster_router.h" +#include "atom_pb_lookup.h" /* #define PRINT_INTRA_LB_ROUTE */ @@ -74,10 +75,10 @@ class reservable_pq : public std::priority_queue { ******************************************************************************************/ static void free_lb_net_rt(t_lb_trace* lb_trace); static void free_lb_trace(t_lb_trace* lb_trace); -static void add_pin_to_rt_terminals(t_lb_router_data* router_data, const AtomPinId pin_id); -static void remove_pin_from_rt_terminals(t_lb_router_data* router_data, const AtomPinId pin_id); +static void add_pin_to_rt_terminals(t_lb_router_data* router_data, const AtomPinId pin_id, const AtomPBLookUp &atom_to_pb); +static void remove_pin_from_rt_terminals(t_lb_router_data* router_data, const AtomPinId pin_id, const AtomPBLookUp &atom_to_pb); -static void fix_duplicate_equivalent_pins(t_lb_router_data* router_data); +static void fix_duplicate_equivalent_pins(t_lb_router_data* router_data, const AtomPBLookUp &atom_to_pb); static void commit_remove_rt(t_lb_trace* rt, t_lb_router_data* router_data, e_commit_remove op, std::unordered_map* mode_map, t_mode_selection_status* mode_status); static bool is_skip_route_net(t_lb_trace* rt, t_lb_router_data* router_data); @@ -248,7 +249,7 @@ static bool check_edge_for_route_conflicts(std::unordered_map& atoms_added = *router_data->atoms_added; - const t_pb* pb = atom_ctx.lookup().atom_pb(blk_id); + const t_pb* pb = atom_to_pb.atom_pb(blk_id); if (atoms_added.count(blk_id) == 0) { return; @@ -288,7 +289,7 @@ void remove_atom_from_target(t_lb_router_data* router_data, const AtomBlockId bl set_reset_pb_modes(router_data, pb, false); for (auto pin_id : atom_ctx.netlist().block_pins(blk_id)) { - remove_pin_from_rt_terminals(router_data, pin_id); + remove_pin_from_rt_terminals(router_data, pin_id, atom_to_pb); } atoms_added.erase(blk_id); @@ -625,7 +626,7 @@ static void free_lb_trace(t_lb_trace* lb_trace) { /* Given a pin of a net, assign route tree terminals for it * Assumes that pin is not already assigned */ -static void add_pin_to_rt_terminals(t_lb_router_data* router_data, const AtomPinId pin_id) { +static void add_pin_to_rt_terminals(t_lb_router_data* router_data, const AtomPinId pin_id, const AtomPBLookUp &atom_to_pb) { std::vector& lb_nets = *router_data->intra_lb_nets; std::vector& lb_type_graph = *router_data->lb_type_graph; t_logical_block_type_ptr lb_type = router_data->lb_type; @@ -633,7 +634,7 @@ static void add_pin_to_rt_terminals(t_lb_router_data* router_data, const AtomPin unsigned int ipos; auto& atom_ctx = g_vpr_ctx.atom(); - const t_pb_graph_pin* pb_graph_pin = find_pb_graph_pin(atom_ctx.netlist(), atom_ctx.lookup(), pin_id); + const t_pb_graph_pin* pb_graph_pin = find_pb_graph_pin(atom_ctx.netlist(), atom_to_pb, pin_id); VTR_ASSERT(pb_graph_pin); AtomPortId port_id = atom_ctx.netlist().pin_port(pin_id); @@ -792,7 +793,7 @@ static void add_pin_to_rt_terminals(t_lb_router_data* router_data, const AtomPin /* Given a pin of a net, remove route tree terminals from it */ -static void remove_pin_from_rt_terminals(t_lb_router_data* router_data, const AtomPinId pin_id) { +static void remove_pin_from_rt_terminals(t_lb_router_data* router_data, const AtomPinId pin_id, const AtomPBLookUp &atom_to_pb) { std::vector& lb_nets = *router_data->intra_lb_nets; std::vector& lb_type_graph = *router_data->lb_type_graph; t_logical_block_type_ptr lb_type = router_data->lb_type; @@ -800,7 +801,7 @@ static void remove_pin_from_rt_terminals(t_lb_router_data* router_data, const At unsigned int ipos; auto& atom_ctx = g_vpr_ctx.atom(); - const t_pb_graph_pin* pb_graph_pin = find_pb_graph_pin(atom_ctx.netlist(), atom_ctx.lookup(), pin_id); + const t_pb_graph_pin* pb_graph_pin = find_pb_graph_pin(atom_ctx.netlist(), atom_to_pb, pin_id); AtomPortId port_id = atom_ctx.netlist().pin_port(pin_id); AtomNetId net_id = atom_ctx.netlist().pin_net(pin_id); @@ -917,7 +918,7 @@ static void remove_pin_from_rt_terminals(t_lb_router_data* router_data, const At //To work around this, we fix all but one of these duplicate connections to route to specific pins, //(instead of the common sink). This ensures a legal routing is produced and that the duplicate pins //are not 'missing' in the clustered netlist. -static void fix_duplicate_equivalent_pins(t_lb_router_data* router_data) { +static void fix_duplicate_equivalent_pins(t_lb_router_data* router_data, const AtomPBLookUp &atom_to_pb) { auto& atom_ctx = g_vpr_ctx.atom(); std::vector& lb_type_graph = *router_data->lb_type_graph; @@ -943,7 +944,7 @@ static void fix_duplicate_equivalent_pins(t_lb_router_data* router_data) { AtomPinId atom_pin = lb_nets[ilb_net].atom_pins[iterm]; VTR_ASSERT(atom_pin); - const t_pb_graph_pin* pb_graph_pin = find_pb_graph_pin(atom_ctx.netlist(), atom_ctx.lookup(), atom_pin); + const t_pb_graph_pin* pb_graph_pin = find_pb_graph_pin(atom_ctx.netlist(), atom_to_pb, atom_pin); VTR_ASSERT(pb_graph_pin); if (pb_graph_pin->port->equivalent == PortEquivalence::NONE) continue; //Only need to remap equivalent ports diff --git a/vpr/src/pack/cluster_router.h b/vpr/src/pack/cluster_router.h index 4f88f1c0b6a..8e1b0520596 100644 --- a/vpr/src/pack/cluster_router.h +++ b/vpr/src/pack/cluster_router.h @@ -16,8 +16,8 @@ void free_router_data(t_lb_router_data* router_data); void free_intra_lb_nets(std::vector* intra_lb_nets); /* Routing Functions */ -void add_atom_as_target(t_lb_router_data* router_data, const AtomBlockId blk_id); -void remove_atom_from_target(t_lb_router_data* router_data, const AtomBlockId blk_id); +void add_atom_as_target(t_lb_router_data* router_data, const AtomBlockId blk_id, const AtomPBLookUp &atom_to_pb); +void remove_atom_from_target(t_lb_router_data* router_data, const AtomBlockId blk_id, const AtomPBLookUp &atom_to_pb); void set_reset_pb_modes(t_lb_router_data* router_data, const t_pb* pb, const bool set); bool try_intra_lb_route(t_lb_router_data* router_data, int verbosity, t_mode_selection_status* mode_status); void reset_intra_lb_route(t_lb_router_data* router_data); diff --git a/vpr/src/pack/greedy_candidate_selector.cpp b/vpr/src/pack/greedy_candidate_selector.cpp index 46bada6237d..fb42df475e1 100644 --- a/vpr/src/pack/greedy_candidate_selector.cpp +++ b/vpr/src/pack/greedy_candidate_selector.cpp @@ -374,24 +374,6 @@ void GreedyCandidateSelector::mark_and_update_partial_gain( cluster_gain_stats.num_pins_of_net_in_pb[net_id]++; } -/* - * @brief Determine if atom block is in pb. - * - * TODO: This would make more sense in the cluster legalizer class. - */ -static bool is_atom_blk_in_pb(const AtomBlockId blk_id, const t_pb* pb) { - const AtomContext& atom_ctx = g_vpr_ctx.atom(); - - const t_pb* cur_pb = atom_ctx.lookup().atom_pb(blk_id); - while (cur_pb) { - if (cur_pb == pb) { - return true; - } - cur_pb = cur_pb->parent_pb; - } - return false; -} - void GreedyCandidateSelector::update_connection_gain_values( ClusterGainStats& cluster_gain_stats, AtomNetId net_id, @@ -416,7 +398,7 @@ void GreedyCandidateSelector::update_connection_gain_values( for (AtomPinId pin_id : atom_netlist_.net_pins(net_id)) { AtomBlockId blk_id = atom_netlist_.pin_block(pin_id); if (cluster_legalizer.get_atom_cluster(blk_id) == legalization_cluster_id - && is_atom_blk_in_pb(blk_id, atom_ctx.lookup().atom_pb(clustered_blk_id))) { + && cluster_legalizer.is_atom_blk_in_cluster_block(blk_id, clustered_blk_id)) { num_internal_connections++; } else if (!cluster_legalizer.is_atom_clustered(blk_id)) { num_open_connections++; diff --git a/vpr/src/pack/pack.cpp b/vpr/src/pack/pack.cpp index f4a7985afec..ab5ac3dd849 100644 --- a/vpr/src/pack/pack.cpp +++ b/vpr/src/pack/pack.cpp @@ -63,6 +63,7 @@ bool try_pack(t_packer_opts* packer_opts, // device if needed. DeviceContext& mutable_device_ctx = g_vpr_ctx.mutable_device(); + std::unordered_set is_clock, is_global; VTR_LOG("Begin packing '%s'.\n", packer_opts->circuit_file_name.c_str()); @@ -154,7 +155,6 @@ bool try_pack(t_packer_opts* packer_opts, } int pack_iteration = 1; - // Initialize the cluster legalizer. ClusterLegalizer cluster_legalizer(atom_ctx.netlist(), prepacker, @@ -164,10 +164,8 @@ bool try_pack(t_packer_opts* packer_opts, ClusterLegalizationStrategy::SKIP_INTRA_LB_ROUTE, packer_opts->enable_pin_feasibility_filter, packer_opts->pack_verbosity); - VTR_LOG("Packing with pin utilization targets: %s\n", cluster_legalizer.get_target_external_pin_util().to_string().c_str()); VTR_LOG("Packing with high fanout thresholds: %s\n", high_fanout_thresholds.to_string().c_str()); - // Initialize the greedy clusterer. GreedyClusterer clusterer(*packer_opts, *analysis_opts, @@ -178,6 +176,8 @@ bool try_pack(t_packer_opts* packer_opts, is_global, flat_placement_info); + g_vpr_ctx.mutable_atom().mutable_lookup().lock_atom_pb = true; + while (true) { //Cluster the netlist // num_used_type_instances: A map used to save the number of used @@ -289,9 +289,10 @@ bool try_pack(t_packer_opts* packer_opts, } //Reset clustering for re-packing + // g_vpr_ctx.mutable_atom().mutable_lookup().lock_atom_pb = false; for (auto blk : g_vpr_ctx.atom().netlist().blocks()) { g_vpr_ctx.mutable_atom().mutable_lookup().set_atom_clb(blk, ClusterBlockId::INVALID()); - g_vpr_ctx.mutable_atom().mutable_lookup().set_atom_pb(blk, nullptr); + cluster_legalizer.mutable_atom_pb_lookup().set_atom_pb(blk, nullptr); } for (auto net : g_vpr_ctx.atom().netlist().nets()) { g_vpr_ctx.mutable_atom().mutable_lookup().remove_atom_net(net); @@ -301,7 +302,7 @@ bool try_pack(t_packer_opts* packer_opts, // Reset the cluster legalizer for re-clustering. cluster_legalizer.reset(); - + // g_vpr_ctx.mutable_atom().mutable_lookup().lock_atom_pb = true; ++pack_iteration; } @@ -319,7 +320,8 @@ bool try_pack(t_packer_opts* packer_opts, * } */ /******************** End **************************/ - + g_vpr_ctx.mutable_atom().mutable_lookup().lock_atom_pb = false; + g_vpr_ctx.mutable_atom().mutable_lookup().set_atom_to_pb(cluster_legalizer.atom_pb_lookup().atom_to_pb()); //check clustering and output it check_and_output_clustering(cluster_legalizer, *packer_opts, is_clock, &arch); diff --git a/vpr/src/util/vpr_utils.cpp b/vpr/src/util/vpr_utils.cpp index 94541f5523b..70c0c8957fb 100644 --- a/vpr/src/util/vpr_utils.cpp +++ b/vpr/src/util/vpr_utils.cpp @@ -16,6 +16,7 @@ #include "globals.h" #include "vpr_utils.h" #include "cluster_placement.h" +#include "cluster_legalizer.h" #include "device_grid.h" #include "user_route_constraints.h" #include "grid_block.h" @@ -889,8 +890,7 @@ bool primitive_type_feasible(const AtomBlockId blk_id, const t_pb_type* cur_pb_t //Returns the sibling atom of a memory slice pb // Note that the pb must be part of a MEMORY_CLASS -AtomBlockId find_memory_sibling(const t_pb* pb) { - auto& atom_ctx = g_vpr_ctx.atom(); +const t_pb* find_memory_sibling(const t_pb* pb) { const t_pb_type* pb_type = pb->pb_graph_node->pb_type; @@ -902,10 +902,10 @@ AtomBlockId find_memory_sibling(const t_pb* pb) { const t_pb* sibling_pb = &memory_class_pb->child_pbs[pb->mode][isibling]; if (sibling_pb->name != nullptr) { - return atom_ctx.lookup().pb_atom(sibling_pb); + return sibling_pb; } } - return AtomBlockId::INVALID(); + return nullptr; } /** @@ -978,7 +978,7 @@ AtomPinId find_atom_pin(ClusterBlockId blk_id, const t_pb_graph_pin* pb_gpin) { return atom_pin; } -//Retrieves the pb_graph_pin associated with an AtomPinId +// Retrieves the pb_graph_pin associated with an AtomPinId // Currently this function just wraps get_pb_graph_node_pin_from_model_port_pin() // in a more convenient interface. const t_pb_graph_pin* find_pb_graph_pin(const AtomNetlist& netlist, const AtomLookup& netlist_lookup, const AtomPinId pin_id) { @@ -1003,6 +1003,33 @@ const t_pb_graph_pin* find_pb_graph_pin(const AtomNetlist& netlist, const AtomLo return get_pb_graph_node_pin_from_model_port_pin(model_port, ipin, pb_gnode); } + +// TODO: Code duplication here. Could probably use a better pack-related abstraction +// to avoid all this, as this function is only used in vpr/src/pack +// Retrieves the pb_graph_pin associated with an AtomPinId +const t_pb_graph_pin* find_pb_graph_pin(const AtomNetlist& netlist, const AtomPBLookUp& atom_pb_lookup, const AtomPinId pin_id) { + VTR_ASSERT(pin_id); + + //Get the graph node + AtomBlockId blk_id = netlist.pin_block(pin_id); + const t_pb_graph_node* pb_gnode = atom_pb_lookup.atom_pb_graph_node(blk_id); + VTR_ASSERT(pb_gnode); + + //The graph node and pin/block should agree on the model they represent + VTR_ASSERT(netlist.block_model(blk_id) == pb_gnode->pb_type->model); + + //Get the pin index + AtomPortId port_id = netlist.pin_port(pin_id); + int ipin = netlist.pin_port_bit(pin_id); + + //Get the model port + const t_model_ports* model_port = netlist.port_model(port_id); + VTR_ASSERT(model_port); + + return get_pb_graph_node_pin_from_model_port_pin(model_port, ipin, pb_gnode); +} + + t_pb_graph_pin* get_pb_graph_node_pin_from_pb_graph_node(t_pb_graph_node* pb_graph_node, int ipin) { int i, count; diff --git a/vpr/src/util/vpr_utils.h b/vpr/src/util/vpr_utils.h index 23d69eae471..5386f4cf661 100644 --- a/vpr/src/util/vpr_utils.h +++ b/vpr/src/util/vpr_utils.h @@ -7,6 +7,7 @@ #include "rr_graph_utils.h" #include "vpr_types.h" #include "vtr_vector.h" +#include "atom_pb_lookup.h" #include #include @@ -166,6 +167,8 @@ const t_port* find_pb_graph_port(const t_pb_graph_node* pb_gnode, const std::str //Returns the graph pin matching name at pin index const t_pb_graph_pin* find_pb_graph_pin(const t_pb_graph_node* pb_gnode, const std::string& port_name, int index); +const t_pb_graph_pin* find_pb_graph_pin(const AtomNetlist& netlist, const AtomPBLookUp& atom_pb_lookup, const AtomPinId pin_id); + AtomPinId find_atom_pin(ClusterBlockId blk_id, const t_pb_graph_pin* pb_gpin); //Returns the logical block type which is most common in the device grid @@ -223,7 +226,7 @@ void free_pb(t_pb* pb); void print_switch_usage(); void print_usage_by_wire_length(); -AtomBlockId find_memory_sibling(const t_pb* pb); +const t_pb* find_memory_sibling(const t_pb* pb); int get_atom_pin_class_num(const AtomPinId atom_pin_id); From 00aad1ba1299396b95f78fb172b7b34352c1c12b Mon Sep 17 00:00:00 2001 From: Amir Poolad Date: Thu, 13 Mar 2025 11:36:44 -0400 Subject: [PATCH 02/14] Remove unused variables in cluster_legalizer.cpp --- vpr/src/pack/cluster_legalizer.cpp | 3483 ++++++++++++++-------------- 1 file changed, 1738 insertions(+), 1745 deletions(-) diff --git a/vpr/src/pack/cluster_legalizer.cpp b/vpr/src/pack/cluster_legalizer.cpp index 4b7be325eef..c338ff1bf97 100644 --- a/vpr/src/pack/cluster_legalizer.cpp +++ b/vpr/src/pack/cluster_legalizer.cpp @@ -10,1750 +10,1743 @@ * was moved into the ClusterLegalizer class. */ -#include "cluster_legalizer.h" -#include -#include -#include -#include -#include "atom_lookup.h" -#include "atom_netlist.h" -#include "cad_types.h" -#include "cluster_placement.h" -#include "cluster_router.h" -#include "globals.h" -#include "logic_types.h" -#include "netlist_utils.h" -#include "noc_aware_cluster_util.h" -#include "noc_data_types.h" -#include "pack_types.h" -#include "partition.h" -#include "partition_region.h" -#include "physical_types.h" -#include "prepack.h" -#include "user_place_constraints.h" -#include "vpr_context.h" -#include "vpr_types.h" -#include "vpr_utils.h" -#include "vtr_assert.h" -#include "vtr_vector.h" -#include "vtr_vector_map.h" - -void ClusterLegalizer::alloc_and_load_pb_stats(t_pb* pb) { - /* Call this routine when starting to fill up a new cluster. It resets * - * the gain vector, etc. */ - - pb->pb_stats = new t_pb_stats; - - pb->pb_stats->input_pins_used = std::vector>(pb->pb_graph_node->num_input_pin_class); - pb->pb_stats->output_pins_used = std::vector>(pb->pb_graph_node->num_output_pin_class); - pb->pb_stats->lookahead_input_pins_used = std::vector>(pb->pb_graph_node->num_input_pin_class); - pb->pb_stats->lookahead_output_pins_used = std::vector>(pb->pb_graph_node->num_output_pin_class); - - pb->pb_stats->num_child_blocks_in_pb = 0; -} - -void ClusterLegalizer::check_cluster_atom_blocks(t_pb* pb, std::unordered_set& blocks_checked) { - const AtomContext& atom_ctx = g_vpr_ctx.atom(); - - const t_pb_type* pb_type = pb->pb_graph_node->pb_type; - if (pb_type->num_modes == 0) { - /* primitive */ - AtomBlockId blk_id = atom_pb_lookup().pb_atom(pb); - if (blk_id) { - if (blocks_checked.count(blk_id)) { - VPR_FATAL_ERROR(VPR_ERROR_PACK, - "pb %s contains atom block %s but atom block is already contained in another pb.\n", - pb->name, atom_ctx.netlist().block_name(blk_id).c_str()); - } - blocks_checked.insert(blk_id); - if (pb != atom_pb_lookup().atom_pb(blk_id)) { - VPR_FATAL_ERROR(VPR_ERROR_PACK, - "pb %s contains atom block %s but atom block does not link to pb.\n", - pb->name, atom_ctx.netlist().block_name(blk_id).c_str()); - } - } - } else { - /* this is a container pb, all container pbs must contain children */ - bool has_child = false; - for (int i = 0; i < pb_type->modes[pb->mode].num_pb_type_children; i++) { - for (int j = 0; j < pb_type->modes[pb->mode].pb_type_children[i].num_pb; j++) { - if (pb->child_pbs[i] != nullptr) { - if (pb->child_pbs[i][j].name != nullptr) { - has_child = true; - check_cluster_atom_blocks(&pb->child_pbs[i][j], blocks_checked); - } - } - } - } - VTR_ASSERT(has_child); - } -} - -void ClusterLegalizer::free_pb_stats_recursive(t_pb* pb) { - /* Releases all the memory used by clustering data structures. */ - if (pb) { - if (pb->pb_graph_node != nullptr) { - if (!pb->pb_graph_node->is_primitive()) { - for (int i = 0; i < pb->pb_graph_node->pb_type->modes[pb->mode].num_pb_type_children; i++) { - for (int j = 0; j < pb->pb_graph_node->pb_type->modes[pb->mode].pb_type_children[i].num_pb; j++) { - if (pb->child_pbs && pb->child_pbs[i]) { - free_pb_stats_recursive(&pb->child_pbs[i][j]); - } - } - } - } - } - free_pb_stats(pb); - } -} - -bool ClusterLegalizer::check_cluster_floorplanning(AtomBlockId atom_blk_id, - PartitionRegion& cluster_pr, - const UserPlaceConstraints& constraints, - int log_verbosity, - bool& cluster_pr_needs_update) { - // Get the partition ID of the atom. - PartitionId part_id = constraints.get_atom_partition(atom_blk_id); - // If the partition ID is invalid, then it can be put in the cluster - // regardless of what the cluster's PartitionRegion is since it is not - // constrained. - if (!part_id.is_valid()) { - VTR_LOGV(log_verbosity > 3, - "\t\t\t Intersect: Atom block %d has no floorplanning constraints\n", + #include "cluster_legalizer.h" + #include + #include + #include + #include + #include "atom_lookup.h" + #include "atom_netlist.h" + #include "cad_types.h" + #include "cluster_placement.h" + #include "cluster_router.h" + #include "globals.h" + #include "logic_types.h" + #include "netlist_utils.h" + #include "noc_aware_cluster_util.h" + #include "noc_data_types.h" + #include "pack_types.h" + #include "partition.h" + #include "partition_region.h" + #include "physical_types.h" + #include "prepack.h" + #include "user_place_constraints.h" + #include "vpr_context.h" + #include "vpr_types.h" + #include "vpr_utils.h" + #include "vtr_assert.h" + #include "vtr_vector.h" + #include "vtr_vector_map.h" + + void ClusterLegalizer::alloc_and_load_pb_stats(t_pb* pb) { + /* Call this routine when starting to fill up a new cluster. It resets * + * the gain vector, etc. */ + + pb->pb_stats = new t_pb_stats; + + pb->pb_stats->input_pins_used = std::vector>(pb->pb_graph_node->num_input_pin_class); + pb->pb_stats->output_pins_used = std::vector>(pb->pb_graph_node->num_output_pin_class); + pb->pb_stats->lookahead_input_pins_used = std::vector>(pb->pb_graph_node->num_input_pin_class); + pb->pb_stats->lookahead_output_pins_used = std::vector>(pb->pb_graph_node->num_output_pin_class); + + pb->pb_stats->num_child_blocks_in_pb = 0; + } + + void ClusterLegalizer::check_cluster_atom_blocks(t_pb* pb, std::unordered_set& blocks_checked) { + const AtomContext& atom_ctx = g_vpr_ctx.atom(); + + const t_pb_type* pb_type = pb->pb_graph_node->pb_type; + if (pb_type->num_modes == 0) { + /* primitive */ + AtomBlockId blk_id = atom_pb_lookup().pb_atom(pb); + if (blk_id) { + if (blocks_checked.count(blk_id)) { + VPR_FATAL_ERROR(VPR_ERROR_PACK, + "pb %s contains atom block %s but atom block is already contained in another pb.\n", + pb->name, atom_ctx.netlist().block_name(blk_id).c_str()); + } + blocks_checked.insert(blk_id); + if (pb != atom_pb_lookup().atom_pb(blk_id)) { + VPR_FATAL_ERROR(VPR_ERROR_PACK, + "pb %s contains atom block %s but atom block does not link to pb.\n", + pb->name, atom_ctx.netlist().block_name(blk_id).c_str()); + } + } + } else { + /* this is a container pb, all container pbs must contain children */ + bool has_child = false; + for (int i = 0; i < pb_type->modes[pb->mode].num_pb_type_children; i++) { + for (int j = 0; j < pb_type->modes[pb->mode].pb_type_children[i].num_pb; j++) { + if (pb->child_pbs[i] != nullptr) { + if (pb->child_pbs[i][j].name != nullptr) { + has_child = true; + check_cluster_atom_blocks(&pb->child_pbs[i][j], blocks_checked); + } + } + } + } + VTR_ASSERT(has_child); + } + } + + void ClusterLegalizer::free_pb_stats_recursive(t_pb* pb) { + /* Releases all the memory used by clustering data structures. */ + if (pb) { + if (pb->pb_graph_node != nullptr) { + if (!pb->pb_graph_node->is_primitive()) { + for (int i = 0; i < pb->pb_graph_node->pb_type->modes[pb->mode].num_pb_type_children; i++) { + for (int j = 0; j < pb->pb_graph_node->pb_type->modes[pb->mode].pb_type_children[i].num_pb; j++) { + if (pb->child_pbs && pb->child_pbs[i]) { + free_pb_stats_recursive(&pb->child_pbs[i][j]); + } + } + } + } + } + free_pb_stats(pb); + } + } + + bool ClusterLegalizer::check_cluster_floorplanning(AtomBlockId atom_blk_id, + PartitionRegion& cluster_pr, + const UserPlaceConstraints& constraints, + int log_verbosity, + bool& cluster_pr_needs_update) { + // Get the partition ID of the atom. + PartitionId part_id = constraints.get_atom_partition(atom_blk_id); + // If the partition ID is invalid, then it can be put in the cluster + // regardless of what the cluster's PartitionRegion is since it is not + // constrained. + if (!part_id.is_valid()) { + VTR_LOGV(log_verbosity > 3, + "\t\t\t Intersect: Atom block %d has no floorplanning constraints\n", + atom_blk_id); + cluster_pr_needs_update = false; + return true; + } + + // Get the Atom and Cluster Partition Regions + const PartitionRegion& atom_pr = constraints.get_partition_pr(part_id); + + // If the Cluster's PartitionRegion is empty, then this atom's PR becomes + // the Cluster's new PartitionRegion. + if (cluster_pr.empty()) { + VTR_LOGV(log_verbosity > 3, + "\t\t\t Intersect: Atom block %d has floorplanning constraints\n", atom_blk_id); - cluster_pr_needs_update = false; - return true; - } - - // Get the Atom and Cluster Partition Regions - const PartitionRegion& atom_pr = constraints.get_partition_pr(part_id); - - // If the Cluster's PartitionRegion is empty, then this atom's PR becomes - // the Cluster's new PartitionRegion. - if (cluster_pr.empty()) { - VTR_LOGV(log_verbosity > 3, - "\t\t\t Intersect: Atom block %d has floorplanning constraints\n", - atom_blk_id); - cluster_pr = atom_pr; - cluster_pr_needs_update = true; - return true; - } - - // The Cluster's new PartitionRegion is the intersection of the Cluster's - // original PartitionRegion and the atom's PartitionRegion. - update_cluster_part_reg(cluster_pr, atom_pr); - - // If the intersection is empty, then the atom cannot be placed in this - // Cluster due to floorplanning constraints. - if (cluster_pr.empty()) { - VTR_LOGV(log_verbosity > 3, - "\t\t\t Intersect: Atom block %d failed floorplanning check for cluster\n", - atom_blk_id); - cluster_pr_needs_update = false; - return false; - } - - // If the Cluster's new PartitionRegion is non-empty, then this atom passes - // the floorplanning constraints and the cluster's PartitionRegion should be - // updated. - cluster_pr_needs_update = true; - VTR_LOGV(log_verbosity > 3, - "\t\t\t Intersect: Atom block %d passed cluster, cluster PR was updated with intersection result \n", - atom_blk_id); - return true; -} - -bool ClusterLegalizer::check_cluster_noc_group(AtomBlockId atom_blk_id, - NocGroupId& cluster_noc_grp_id, - const vtr::vector& atom_noc_grp_ids, - int log_verbosity) { - const NocGroupId atom_noc_grp_id = atom_noc_grp_ids.empty() ? NocGroupId::INVALID() : atom_noc_grp_ids[atom_blk_id]; - - if (!cluster_noc_grp_id.is_valid()) { - // If the cluster does not have a NoC group, assign the atom's NoC group - // to the cluster. - VTR_LOGV(log_verbosity > 3, - "\t\t\t NoC Group: Atom block %d passed cluster, cluster's NoC group was updated with the atom's group %d\n", - atom_blk_id, (size_t)atom_noc_grp_id); - cluster_noc_grp_id = atom_noc_grp_id; - return true; - } - - if (cluster_noc_grp_id == atom_noc_grp_id) { - // If the cluster has the same NoC group ID as the atom, they are - // compatible. - VTR_LOGV(log_verbosity > 3, - "\t\t\t NoC Group: Atom block %d passed cluster, cluster's NoC group was compatible with the atom's group %d\n", - atom_blk_id, (size_t)atom_noc_grp_id); - return true; - } - - // If the cluster belongs to a different NoC group than the atom's group, - // they are incompatible. - VTR_LOGV(log_verbosity > 3, - "\t\t\t NoC Group: Atom block %d failed NoC group check for cluster. Cluster's NoC group: %d, atom's NoC group: %d\n", - atom_blk_id, (size_t)cluster_noc_grp_id, (size_t)atom_noc_grp_id); - return false; -} - -enum e_block_pack_status ClusterLegalizer::check_chain_root_placement_feasibility( - const t_pb_graph_node* pb_graph_node, - const t_chain_info& prepack_chain_info, - const t_clustering_chain_info& clustering_chain_info, - t_pack_patterns* mol_pack_patterns, - const AtomBlockId blk_id) { - const AtomContext& atom_ctx = g_vpr_ctx.atom(); - - enum e_block_pack_status block_pack_status = e_block_pack_status::BLK_PASSED; - - bool is_long_chain = prepack_chain_info.is_long_chain; - - const auto& chain_root_pins = mol_pack_patterns->chain_root_pins; - - t_model_ports* root_port = chain_root_pins[0][0]->port->model_port; - AtomNetId chain_net_id; - auto port_id = atom_ctx.netlist().find_atom_port(blk_id, root_port); - - if (port_id) { - chain_net_id = atom_ctx.netlist().port_net(port_id, chain_root_pins[0][0]->pin_number); - } - - // if this block is part of a long chain or it is driven by a cluster - // input pin we need to check the placement legality of this block - // Depending on the logic synthesis even small chains that can fit within one - // cluster might need to start at the top of the cluster as their input can be - // driven by a global gnd or vdd. Therefore even if this is not a long chain - // but its input pin is driven by a net, the placement legality is checked. - if (is_long_chain || chain_net_id) { - auto chain_id = clustering_chain_info.chain_id; - // if this chain has a chain id assigned to it (implies is_long_chain too) - if (chain_id != -1) { - // the chosen primitive should be a valid starting point for the chain - // long chains should only be placed at the top of the chain tieOff = 0 - if (pb_graph_node != chain_root_pins[chain_id][0]->parent_node) { - block_pack_status = e_block_pack_status::BLK_FAILED_FEASIBLE; - } - // the chain doesn't have an assigned chain_id yet - } else { - block_pack_status = e_block_pack_status::BLK_FAILED_FEASIBLE; - for (const auto& chain : chain_root_pins) { - for (auto tieOff : chain) { - // check if this chosen primitive is one of the possible - // starting points for this chain. - if (pb_graph_node == tieOff->parent_node) { - // this location matches with the one of the dedicated chain - // input from outside logic block, therefore it is feasible - block_pack_status = e_block_pack_status::BLK_PASSED; - break; - } - // long chains should only be placed at the top of the chain tieOff = 0 - if (is_long_chain) break; - } - } - } - } - - return block_pack_status; -} - -bool ClusterLegalizer::primitive_memory_sibling_feasible(const AtomBlockId blk_id, const t_pb_type* cur_pb_type, const AtomBlockId sibling_blk_id) { - const AtomContext& atom_ctx = g_vpr_ctx.atom(); - - VTR_ASSERT(cur_pb_type->class_type == MEMORY_CLASS); - - //First, identify the 'data' ports by looking at the cur_pb_type - std::unordered_set data_ports; - for (int iport = 0; iport < cur_pb_type->num_ports; ++iport) { - const char* port_class = cur_pb_type->ports[iport].port_class; - if (port_class && strstr(port_class, "data") == port_class) { - //The port_class starts with "data", so it is a data port - - //Record the port - data_ports.insert(cur_pb_type->ports[iport].model_port); - } - } - - //Now verify that all nets (except those connected to data ports) are equivalent - //between blk_id and sibling_blk_id - - //Since the atom netlist stores only in-use ports, we iterate over the model to ensure - //all ports are compared - const t_model* model = cur_pb_type->model; - for (t_model_ports* port : {model->inputs, model->outputs}) { - for (; port; port = port->next) { - if (data_ports.count(port)) { - //Don't check data ports - continue; - } - - //Note: VPR doesn't support multi-driven nets, so all outputs - //should be data ports, otherwise the siblings will both be - //driving the output net - - //Get the ports from each primitive - auto blk_port_id = atom_ctx.netlist().find_atom_port(blk_id, port); - auto sib_port_id = atom_ctx.netlist().find_atom_port(sibling_blk_id, port); - - //Check that all nets (including unconnected nets) match - for (int ipin = 0; ipin < port->size; ++ipin) { - //The nets are initialized as invalid (i.e. disconnected) - AtomNetId blk_net_id; - AtomNetId sib_net_id; - - //We can get the actual net provided the port exists - // - //Note that if the port did not exist, the net is left - //as invalid/disconneced - if (blk_port_id) { - blk_net_id = atom_ctx.netlist().port_net(blk_port_id, ipin); - } - if (sib_port_id) { - sib_net_id = atom_ctx.netlist().port_net(sib_port_id, ipin); - } - - //The sibling and block must have the same (possibly disconnected) - //net on this pin - if (blk_net_id != sib_net_id) { - //Nets do not match, not feasible - return false; - } - } - } - } - - return true; -} - -bool ClusterLegalizer::primitive_feasible(const AtomBlockId blk_id, t_pb* cur_pb) { - const AtomContext& atom_ctx = g_vpr_ctx.atom(); - - const t_pb_type* cur_pb_type = cur_pb->pb_graph_node->pb_type; - - VTR_ASSERT(cur_pb_type->num_modes == 0); /* primitive */ - - AtomBlockId cur_pb_blk_id = atom_pb_lookup().pb_atom(cur_pb); - if (cur_pb_blk_id && cur_pb_blk_id != blk_id) { - /* This pb already has a different logical block */ - return false; - } - - if (cur_pb_type->class_type == MEMORY_CLASS) { - /* Memory class has additional feasibility requirements: - * - all siblings must share all nets, including open nets, with the exception of data nets */ - - /* find sibling if one exists */ - const t_pb *sibling_memory_pb = find_memory_sibling(cur_pb); - AtomBlockId sibling_memory_blk_id = atom_pb_lookup().pb_atom(sibling_memory_pb); - - if (sibling_memory_blk_id) { - //There is a sibling, see if the current block is feasible with it - bool sibling_feasible = primitive_memory_sibling_feasible(blk_id, cur_pb_type, sibling_memory_blk_id); - if (!sibling_feasible) { - return false; - } - } - } - - //Generic feasibility check - return primitive_type_feasible(blk_id, cur_pb_type); -} - -enum e_block_pack_status -ClusterLegalizer::try_place_atom_block_rec(const t_pb_graph_node* pb_graph_node, - const AtomBlockId blk_id, - t_pb* cb, - t_pb** parent, - const LegalizationClusterId cluster_id, - vtr::vector_map& atom_cluster, - const PackMoleculeId molecule_id, - t_lb_router_data* router_data, - int verbosity, - const Prepacker& prepacker, - const vtr::vector_map& clustering_chain_info) { - const AtomContext& atom_ctx = g_vpr_ctx.atom(); - AtomContext& mutable_atom_ctx = g_vpr_ctx.mutable_atom(); - - VTR_ASSERT_SAFE(cb != nullptr); - e_block_pack_status block_pack_status = e_block_pack_status::BLK_PASSED; - - /* Discover parent */ - t_pb* parent_pb = nullptr; - if (pb_graph_node->parent_pb_graph_node != cb->pb_graph_node) { - t_pb* my_parent = nullptr; - block_pack_status = try_place_atom_block_rec(pb_graph_node->parent_pb_graph_node, blk_id, cb, - &my_parent, cluster_id, - atom_cluster, - molecule_id, router_data, - verbosity, - prepacker, clustering_chain_info); - parent_pb = my_parent; - } else { - parent_pb = cb; - } - - /* Create siblings if siblings are not allocated */ - VTR_ASSERT(parent_pb != nullptr); - if (parent_pb->child_pbs == nullptr) { - VTR_ASSERT(parent_pb->name == nullptr); - parent_pb->name = vtr::strdup(atom_ctx.netlist().block_name(blk_id).c_str()); - parent_pb->mode = pb_graph_node->pb_type->parent_mode->index; - set_reset_pb_modes(router_data, parent_pb, true); - const t_mode* mode = &parent_pb->pb_graph_node->pb_type->modes[parent_pb->mode]; - parent_pb->child_pbs = new t_pb*[mode->num_pb_type_children]; - - for (int i = 0; i < mode->num_pb_type_children; i++) { - parent_pb->child_pbs[i] = new t_pb[mode->pb_type_children[i].num_pb]; - - for (int j = 0; j < mode->pb_type_children[i].num_pb; j++) { - parent_pb->child_pbs[i][j].parent_pb = parent_pb; - parent_pb->child_pbs[i][j].pb_graph_node = &(parent_pb->pb_graph_node->child_pb_graph_nodes[parent_pb->mode][i][j]); - } - } - } else { - /* if this is not the first child of this parent, must match existing parent mode */ - if (parent_pb->mode != pb_graph_node->pb_type->parent_mode->index) { - return e_block_pack_status::BLK_FAILED_FEASIBLE; - } - } - - const t_mode* mode = &parent_pb->pb_graph_node->pb_type->modes[parent_pb->mode]; - int i; - for (i = 0; i < mode->num_pb_type_children; i++) { - if (pb_graph_node->pb_type == &mode->pb_type_children[i]) { - break; - } - } - VTR_ASSERT(i < mode->num_pb_type_children); - t_pb* pb = &parent_pb->child_pbs[i][pb_graph_node->placement_index]; - VTR_ASSERT_SAFE(pb != nullptr); - *parent = pb; /* this pb is parent of it's child that called this function */ - VTR_ASSERT(pb->pb_graph_node == pb_graph_node); - if (pb->pb_stats == nullptr) { - alloc_and_load_pb_stats(pb); - } - const t_pb_type* pb_type = pb_graph_node->pb_type; - - /* Any pb_type under an mode, which is disabled for packing, should not be considerd for mapping - * Early exit to flag failure - */ - if (true == pb_type->parent_mode->disable_packing) { - return e_block_pack_status::BLK_FAILED_FEASIBLE; - } - - bool is_primitive = (pb_type->num_modes == 0); - - if (is_primitive) { - VTR_ASSERT(!atom_pb_lookup().pb_atom(pb) - && atom_pb_lookup().atom_pb(blk_id) == nullptr - && atom_cluster[blk_id] == LegalizationClusterId::INVALID()); - /* try pack to location */ - VTR_ASSERT(pb->name == nullptr); - pb->name = vtr::strdup(atom_ctx.netlist().block_name(blk_id).c_str()); - - //Update the atom netlist mappings - atom_cluster[blk_id] = cluster_id; - // NOTE: This pb is different from the pb of the cluster. It is the pb - // of the actual primitive. - // TODO: It would be a good idea to remove the use of this global - // variables to prevent external users from modifying this by - // mistake. - mutable_atom_pb_lookup().set_atom_pb(blk_id, pb); - - add_atom_as_target(router_data, blk_id, atom_pb_lookup()); - if (!primitive_feasible(blk_id, pb)) { - /* failed location feasibility check, revert pack */ - block_pack_status = e_block_pack_status::BLK_FAILED_FEASIBLE; - } - - // if this block passed and is part of a chained molecule - const t_pack_molecule& molecule = prepacker.get_molecule(molecule_id); - if (block_pack_status == e_block_pack_status::BLK_PASSED && molecule.is_chain()) { - auto molecule_root_block = molecule.atom_block_ids[molecule.root]; - // if this is the root block of the chain molecule check its placmeent feasibility - if (blk_id == molecule_root_block) { - VTR_ASSERT(molecule.chain_id.is_valid()); - const t_chain_info& prepack_chain_info = prepacker.get_molecule_chain_info(molecule.chain_id); - block_pack_status = check_chain_root_placement_feasibility(pb_graph_node, - prepack_chain_info, - clustering_chain_info[molecule.chain_id], - molecule.pack_pattern, - blk_id); - } - } - - VTR_LOGV(verbosity > 4 && block_pack_status == e_block_pack_status::BLK_PASSED, - "\t\t\tPlaced atom '%s' (%s) at %s\n", - atom_ctx.netlist().block_name(blk_id).c_str(), - atom_ctx.netlist().block_model(blk_id)->name, - pb->hierarchical_type_name().c_str()); - } - - if (block_pack_status != e_block_pack_status::BLK_PASSED) { - free(pb->name); - pb->name = nullptr; - } - return block_pack_status; -} - -void ClusterLegalizer::reset_lookahead_pins_used(t_pb* cur_pb) { - const t_pb_type* pb_type = cur_pb->pb_graph_node->pb_type; - if (cur_pb->pb_stats == nullptr) { - return; /* No pins used, no need to continue */ - } - - if (pb_type->num_modes > 0 && cur_pb->name != nullptr) { - for (int i = 0; i < cur_pb->pb_graph_node->num_input_pin_class; i++) { - cur_pb->pb_stats->lookahead_input_pins_used[i].clear(); - } - - for (int i = 0; i < cur_pb->pb_graph_node->num_output_pin_class; i++) { - cur_pb->pb_stats->lookahead_output_pins_used[i].clear(); - } - - if (cur_pb->child_pbs != nullptr) { - for (int i = 0; i < pb_type->modes[cur_pb->mode].num_pb_type_children; i++) { - if (cur_pb->child_pbs[i] != nullptr) { - for (int j = 0; j < pb_type->modes[cur_pb->mode].pb_type_children[i].num_pb; j++) { - reset_lookahead_pins_used(&cur_pb->child_pbs[i][j]); - } - } - } - } - } -} - -int ClusterLegalizer::net_sinks_reachable_in_cluster(const t_pb_graph_pin* driver_pb_gpin, const int depth, const AtomNetId net_id) { - const AtomContext& atom_ctx = g_vpr_ctx.atom(); - - //Record the sink pb graph pins we are looking for - std::unordered_set sink_pb_gpins; - for (const AtomPinId pin_id : atom_ctx.netlist().net_sinks(net_id)) { - const t_pb_graph_pin* sink_pb_gpin = find_pb_graph_pin(atom_ctx.netlist(), atom_pb_lookup(), pin_id); - VTR_ASSERT(sink_pb_gpin); - - sink_pb_gpins.insert(sink_pb_gpin); - } - - //Count how many sink pins are reachable - size_t num_reachable_sinks = 0; - for (int i_prim_pin = 0; i_prim_pin < driver_pb_gpin->num_connectable_primitive_input_pins[depth]; ++i_prim_pin) { - const t_pb_graph_pin* reachable_pb_gpin = driver_pb_gpin->list_of_connectable_input_pin_ptrs[depth][i_prim_pin]; - - if (sink_pb_gpins.count(reachable_pb_gpin)) { - ++num_reachable_sinks; - if (num_reachable_sinks == atom_ctx.netlist().net_sinks(net_id).size()) { - return true; - } - } - } - - return false; -} - -t_pb_graph_pin* ClusterLegalizer::get_driver_pb_graph_pin(const t_pb* driver_pb, const AtomPinId driver_pin_id) { - const AtomContext& atom_ctx = g_vpr_ctx.atom(); - - const auto driver_pb_type = driver_pb->pb_graph_node->pb_type; - int output_port = 0; - // find the port of the pin driving the net as well as the port model - auto driver_port_id = atom_ctx.netlist().pin_port(driver_pin_id); - auto driver_model_port = atom_ctx.netlist().port_model(driver_port_id); - // find the port id of the port containing the driving pin in the driver_pb_type - for (int i = 0; i < driver_pb_type->num_ports; i++) { - auto& prim_port = driver_pb_type->ports[i]; - if (prim_port.type == OUT_PORT) { - if (prim_port.model_port == driver_model_port) { - // get the output pb_graph_pin driving this input net - return &(driver_pb->pb_graph_node->output_pins[output_port][atom_ctx.netlist().pin_port_bit(driver_pin_id)]); - } - output_port++; - } - } - // the pin should be found - VTR_ASSERT(false); - return nullptr; -} - -void ClusterLegalizer::compute_and_mark_lookahead_pins_used_for_pin(const t_pb_graph_pin* pb_graph_pin, - const t_pb* primitive_pb, - const AtomNetId net_id, - const vtr::vector_map& atom_cluster) { - const AtomContext& atom_ctx = g_vpr_ctx.atom(); - - // starting from the parent pb of the input primitive go up in the hierarchy till the root block - for (auto cur_pb = primitive_pb->parent_pb; cur_pb; cur_pb = cur_pb->parent_pb) { - const auto depth = cur_pb->pb_graph_node->pb_type->depth; - const auto pin_class = pb_graph_pin->parent_pin_class[depth]; - VTR_ASSERT(pin_class != OPEN); - - const auto driver_blk_id = atom_ctx.netlist().net_driver_block(net_id); - - // if this primitive pin is an input pin - if (pb_graph_pin->port->type == IN_PORT) { - /* find location of net driver if exist in clb, NULL otherwise */ - // find the driver of the input net connected to the pin being studied - const auto driver_pin_id = atom_ctx.netlist().net_driver(net_id); - // find the id of the atom occupying the input primitive_pb - const auto prim_blk_id = atom_pb_lookup().pb_atom(primitive_pb); - // find the pb block occupied by the driving atom - const auto driver_pb = atom_pb_lookup().atom_pb(driver_blk_id); - // pb_graph_pin driving net_id in the driver pb block - t_pb_graph_pin* output_pb_graph_pin = nullptr; - // if the driver block is in the same clb as the input primitive block - LegalizationClusterId driver_cluster_id = atom_cluster[driver_blk_id]; - LegalizationClusterId prim_cluster_id = atom_cluster[prim_blk_id]; - if (driver_cluster_id == prim_cluster_id) { - // get pb_graph_pin driving the given net - output_pb_graph_pin = get_driver_pb_graph_pin(driver_pb, driver_pin_id); - } - - bool is_reachable = false; - - // if the driver pin is within the cluster - if (output_pb_graph_pin) { - // find if the driver pin can reach the input pin of the primitive or not - const t_pb* check_pb = driver_pb; - while (check_pb && check_pb != cur_pb) { - check_pb = check_pb->parent_pb; - } - if (check_pb) { - for (int i = 0; i < output_pb_graph_pin->num_connectable_primitive_input_pins[depth]; i++) { - if (pb_graph_pin == output_pb_graph_pin->list_of_connectable_input_pin_ptrs[depth][i]) { - is_reachable = true; - break; - } - } - } - } - - // Must use an input pin to connect the driver to the input pin of the given primitive, either the - // driver atom is not contained in the cluster or is contained but cannot reach the primitive pin - if (!is_reachable) { - // add net to lookahead_input_pins_used if not already added - auto it = std::find(cur_pb->pb_stats->lookahead_input_pins_used[pin_class].begin(), - cur_pb->pb_stats->lookahead_input_pins_used[pin_class].end(), net_id); - if (it == cur_pb->pb_stats->lookahead_input_pins_used[pin_class].end()) { - cur_pb->pb_stats->lookahead_input_pins_used[pin_class].push_back(net_id); - } - } - } else { - VTR_ASSERT(pb_graph_pin->port->type == OUT_PORT); - /* - * Determine if this net (which is driven from within this cluster) leaves this cluster - * (and hence uses an output pin). - */ - - bool net_exits_cluster = true; - int num_net_sinks = static_cast(atom_ctx.netlist().net_sinks(net_id).size()); - - if (pb_graph_pin->num_connectable_primitive_input_pins[depth] >= num_net_sinks) { - //It is possible the net is completely absorbed in the cluster, - //since this pin could (potentially) drive all the net's sinks - - /* Important: This runtime penalty looks a lot scarier than it really is. - * For high fan-out nets, I at most look at the number of pins within the - * cluster which limits runtime. - * - * DO NOT REMOVE THIS INITIAL FILTER WITHOUT CAREFUL ANALYSIS ON RUNTIME!!! - * - * Key Observation: - * For LUT-based designs it is impossible for the average fanout to exceed - * the number of LUT inputs so it's usually around 4-5 (pigeon-hole argument, - * if the average fanout is greater than the number of LUT inputs, where do - * the extra connections go? Therefore, average fanout must be capped to a - * small constant where the constant is equal to the number of LUT inputs). - * The real danger to runtime is when the number of sinks of a net gets doubled - */ - - //Check if all the net sinks are, in fact, inside this cluster - bool all_sinks_in_cur_cluster = true; - LegalizationClusterId driver_cluster = atom_cluster[driver_blk_id]; - for (auto pin_id : atom_ctx.netlist().net_sinks(net_id)) { - auto sink_blk_id = atom_ctx.netlist().pin_block(pin_id); - if (atom_cluster[sink_blk_id] != driver_cluster) { - all_sinks_in_cur_cluster = false; - break; - } - } - - if (all_sinks_in_cur_cluster) { - //All the sinks are part of this cluster, so the net may be fully absorbed. - // - //Verify this, by counting the number of net sinks reachable from the driver pin. - //If the count equals the number of net sinks then the net is fully absorbed and - //the net does not exit the cluster - /* TODO: I should cache the absorbed outputs, once net is absorbed, - * net is forever absorbed, no point in rechecking every time */ - if (net_sinks_reachable_in_cluster(pb_graph_pin, depth, net_id)) { - //All the sinks are reachable inside the cluster - net_exits_cluster = false; - } - } - } - - if (net_exits_cluster) { - /* This output must exit this cluster */ - cur_pb->pb_stats->lookahead_output_pins_used[pin_class].push_back(net_id); - } - } - } -} - -void ClusterLegalizer::compute_and_mark_lookahead_pins_used(const AtomBlockId blk_id, - const vtr::vector_map& atom_cluster) { - const AtomContext& atom_ctx = g_vpr_ctx.atom(); - - const t_pb* cur_pb = atom_pb_lookup().atom_pb(blk_id); - VTR_ASSERT(cur_pb != nullptr); - - /* Walk through inputs, outputs, and clocks marking pins off of the same class */ - for (auto pin_id : atom_ctx.netlist().block_pins(blk_id)) { - auto net_id = atom_ctx.netlist().pin_net(pin_id); - - const t_pb_graph_pin* pb_graph_pin = find_pb_graph_pin(atom_ctx.netlist(), atom_pb_lookup(), pin_id); - compute_and_mark_lookahead_pins_used_for_pin(pb_graph_pin, cur_pb, net_id, atom_cluster); - } -} - -void ClusterLegalizer::try_update_lookahead_pins_used(t_pb* cur_pb, - const vtr::vector_map& atom_cluster) { - const AtomContext& atom_ctx = g_vpr_ctx.atom(); - - // run recursively till a leaf (primitive) pb block is reached - const t_pb_type* pb_type = cur_pb->pb_graph_node->pb_type; - if (pb_type->num_modes > 0 && cur_pb->name != nullptr) { - if (cur_pb->child_pbs != nullptr) { - for (int i = 0; i < pb_type->modes[cur_pb->mode].num_pb_type_children; i++) { - if (cur_pb->child_pbs[i] != nullptr) { - for (int j = 0; j < pb_type->modes[cur_pb->mode].pb_type_children[i].num_pb; j++) { - try_update_lookahead_pins_used(&cur_pb->child_pbs[i][j], atom_cluster); - } - } - } - } - } else { - // find if this child (primitive) pb block has an atom mapped to it, - // if yes compute and mark lookahead pins used for that pb block - AtomBlockId blk_id = atom_pb_lookup().pb_atom(cur_pb); - if (pb_type->blif_model != nullptr && blk_id) { - compute_and_mark_lookahead_pins_used(blk_id, atom_cluster); - } - } -} - -bool ClusterLegalizer::check_lookahead_pins_used(t_pb* cur_pb, t_ext_pin_util max_external_pin_util) { - const t_pb_type* pb_type = cur_pb->pb_graph_node->pb_type; - - if (pb_type->num_modes > 0 && cur_pb->name) { - for (int i = 0; i < cur_pb->pb_graph_node->num_input_pin_class; i++) { - size_t class_size = cur_pb->pb_graph_node->input_pin_class_size[i]; - - if (cur_pb->is_root()) { - // Scale the class size by the maximum external pin utilization factor - // Use ceil to avoid classes of size 1 from being scaled to zero - class_size = std::ceil(max_external_pin_util.input_pin_util * class_size); - // if the number of pins already used is larger than class size, then the number of - // cluster inputs already used should be our constraint. Why is this needed? This is - // needed since when packing the seed block the maximum external pin utilization is - // used as 1.0 allowing molecules that are using up to all the cluster inputs to be - // packed legally. Therefore, if the seed block is already using more inputs than - // the allowed maximum utilization, this should become the new maximum pin utilization. - class_size = std::max(class_size, cur_pb->pb_stats->input_pins_used[i].size()); - } - - if (cur_pb->pb_stats->lookahead_input_pins_used[i].size() > class_size) { - return false; - } - } - - for (int i = 0; i < cur_pb->pb_graph_node->num_output_pin_class; i++) { - size_t class_size = cur_pb->pb_graph_node->output_pin_class_size[i]; - if (cur_pb->is_root()) { - // Scale the class size by the maximum external pin utilization factor - // Use ceil to avoid classes of size 1 from being scaled to zero - class_size = std::ceil(max_external_pin_util.output_pin_util * class_size); - // if the number of pins already used is larger than class size, then the number of - // cluster outputs already used should be our constraint. Why is this needed? This is - // needed since when packing the seed block the maximum external pin utilization is - // used as 1.0 allowing molecules that are using up to all the cluster inputs to be - // packed legally. Therefore, if the seed block is already using more inputs than - // the allowed maximum utilization, this should become the new maximum pin utilization. - class_size = std::max(class_size, cur_pb->pb_stats->output_pins_used[i].size()); - } - - if (cur_pb->pb_stats->lookahead_output_pins_used[i].size() > class_size) { - return false; - } - } - - if (cur_pb->child_pbs) { - for (int i = 0; i < pb_type->modes[cur_pb->mode].num_pb_type_children; i++) { - if (cur_pb->child_pbs[i]) { - for (int j = 0; j < pb_type->modes[cur_pb->mode].pb_type_children[i].num_pb; j++) { - if (!check_lookahead_pins_used(&cur_pb->child_pbs[i][j], max_external_pin_util)) - return false; - } - } - } - } - } - - return true; -} - -void ClusterLegalizer::update_clustering_chain_info(PackMoleculeId chain_molecule_id, - const t_pb_graph_node* root_primitive) { - // Get the molecule - VTR_ASSERT(chain_molecule_id.is_valid()); - const t_pack_molecule& chain_molecule = prepacker_.get_molecule(chain_molecule_id); - - // Get the ID of the chain it is a part of - MoleculeChainId chain_id = chain_molecule.chain_id; - VTR_ASSERT(chain_id.is_valid()); - - // Get the prepacking and clustering information on this chain. - const t_chain_info& prepack_chain_info = prepacker_.get_molecule_chain_info(chain_id); - t_clustering_chain_info& clustering_chain_info = clustering_chain_info_[chain_id]; - VTR_ASSERT(clustering_chain_info.chain_id == -1 && prepack_chain_info.is_long_chain); - - // Update the clustering chain information. - // long chains should only be placed at the beginning of the chain - // Since for long chains the molecule size is already equal to the - // total number of adders in the cluster. Therefore, it should - // always be placed at the very first adder in this cluster. - auto chain_root_pins = chain_molecule.pack_pattern->chain_root_pins; - for (size_t chainId = 0; chainId < chain_root_pins.size(); chainId++) { - if (chain_root_pins[chainId][0]->parent_node == root_primitive) { - clustering_chain_info.chain_id = chainId; - clustering_chain_info.first_packed_molecule = chain_molecule_id; - return; - } - } - - VTR_ASSERT(false); -} - -void ClusterLegalizer::reset_molecule_info(PackMoleculeId mol_id) { - VTR_ASSERT(mol_id.is_valid()); - - // when invalidating a molecule check if it's a chain molecule - // that is part of a long chain. If so, check if this molecule - // has modified the chain_id value based on the stale packing - // then reset the chain id and the first packed molecule pointer - // this is packing is being reset - const t_pack_molecule& mol = prepacker_.get_molecule(mol_id); - if (!mol.is_chain()) - return; - - VTR_ASSERT(mol.chain_id.is_valid()); - const t_chain_info& prepack_chain_info = prepacker_.get_molecule_chain_info(mol.chain_id); - if (!prepack_chain_info.is_long_chain) - return; - - t_clustering_chain_info& clustering_chain_info = clustering_chain_info_[mol.chain_id]; - if (clustering_chain_info.first_packed_molecule == mol_id) { - clustering_chain_info.first_packed_molecule = PackMoleculeId::INVALID(); - clustering_chain_info.chain_id = -1; - } -} - -void ClusterLegalizer::revert_place_atom_block(const AtomBlockId blk_id, - t_lb_router_data* router_data, - vtr::vector_map& atom_cluster) { - const AtomContext& atom_ctx = g_vpr_ctx.atom(); - AtomContext& mutable_atom_ctx = g_vpr_ctx.mutable_atom(); - - //We cast away const here since we may free the pb, and it is - //being removed from the active mapping. - // - //In general most code works fine accessing cosnt t_pb*, - //which is why we store them as such in atom_ctx.lookup() - t_pb* pb = const_cast(atom_pb_lookup().atom_pb(blk_id)); - - if (pb != nullptr) { - /* When freeing molecules, the current block might already have been freed by a prior revert - * When this happens, no need to do anything beyond basic book keeping at the atom block - */ - - t_pb* next = pb->parent_pb; - free_pb(pb); - pb = next; - - while (pb != nullptr) { - /* If this is pb is created only for the purposes of holding new molecule, remove it - * Must check if cluster is already freed (which can be the case) - */ - next = pb->parent_pb; - - if (pb->child_pbs != nullptr && pb->pb_stats != nullptr - && pb->pb_stats->num_child_blocks_in_pb == 0) { - set_reset_pb_modes(router_data, pb, false); - if (next != nullptr) { - /* If the code gets here, then that means that placing the initial seed molecule - * failed, don't free the actual complex block itself as the seed needs to find - * another placement */ - free_pb(pb); - } - } - pb = next; - } - } - - //Update the atom netlist mapping - atom_cluster[blk_id] = LegalizationClusterId::INVALID(); - mutable_atom_pb_lookup().set_atom_pb(blk_id, nullptr); -} - -void ClusterLegalizer::commit_lookahead_pins_used(t_pb* cur_pb) { - const t_pb_type* pb_type = cur_pb->pb_graph_node->pb_type; - - if (pb_type->num_modes > 0 && cur_pb->name) { - for (int i = 0; i < cur_pb->pb_graph_node->num_input_pin_class; i++) { - VTR_ASSERT(cur_pb->pb_stats->lookahead_input_pins_used[i].size() <= (unsigned int)cur_pb->pb_graph_node->input_pin_class_size[i]); - for (size_t j = 0; j < cur_pb->pb_stats->lookahead_input_pins_used[i].size(); j++) { - VTR_ASSERT(cur_pb->pb_stats->lookahead_input_pins_used[i][j]); - cur_pb->pb_stats->input_pins_used[i].insert({j, cur_pb->pb_stats->lookahead_input_pins_used[i][j]}); - } - } - - for (int i = 0; i < cur_pb->pb_graph_node->num_output_pin_class; i++) { - VTR_ASSERT(cur_pb->pb_stats->lookahead_output_pins_used[i].size() <= (unsigned int)cur_pb->pb_graph_node->output_pin_class_size[i]); - for (size_t j = 0; j < cur_pb->pb_stats->lookahead_output_pins_used[i].size(); j++) { - VTR_ASSERT(cur_pb->pb_stats->lookahead_output_pins_used[i][j]); - cur_pb->pb_stats->output_pins_used[i].insert({j, cur_pb->pb_stats->lookahead_output_pins_used[i][j]}); - } - } - - if (cur_pb->child_pbs) { - for (int i = 0; i < pb_type->modes[cur_pb->mode].num_pb_type_children; i++) { - if (cur_pb->child_pbs[i]) { - for (int j = 0; j < pb_type->modes[cur_pb->mode].pb_type_children[i].num_pb; j++) { - commit_lookahead_pins_used(&cur_pb->child_pbs[i][j]); - } - } - } - } - } -} - -bool ClusterLegalizer::cleanup_pb(t_pb* pb) { - bool can_free = true; - - /* Recursively check if there are any children with already assigned atoms */ - if (pb->child_pbs != nullptr) { - const t_mode* mode = &pb->pb_graph_node->pb_type->modes[pb->mode]; - VTR_ASSERT(mode != nullptr); - - /* Check each mode */ - for (int i = 0; i < mode->num_pb_type_children; ++i) { - /* Check each child */ - if (pb->child_pbs[i] != nullptr) { - for (int j = 0; j < mode->pb_type_children[i].num_pb; ++j) { - t_pb* pb_child = &pb->child_pbs[i][j]; - t_pb_type* pb_type = pb_child->pb_graph_node->pb_type; - - /* Primitive, check occupancy */ - if (pb_type->num_modes == 0) { - if (pb_child->name != nullptr) { - can_free = false; - } - } - - /* Non-primitive, recurse */ - else { - if (!cleanup_pb(pb_child)) { - can_free = false; - } - } - } - } - } - - /* Free if can */ - if (can_free) { - for (int i = 0; i < mode->num_pb_type_children; ++i) { - if (pb->child_pbs[i] != nullptr) { - delete[] pb->child_pbs[i]; - } - } - - delete[] pb->child_pbs; - pb->child_pbs = nullptr; - pb->mode = 0; - - if (pb->name) { - free(pb->name); - pb->name = nullptr; - } - } - } - - return can_free; -} - -e_block_pack_status ClusterLegalizer::try_pack_molecule(PackMoleculeId molecule_id, - LegalizationCluster& cluster, - LegalizationClusterId cluster_id, - const t_ext_pin_util& max_external_pin_util) { - // Try to pack the molecule into a cluster with this pb type. - - // Safety debugs. - VTR_ASSERT_DEBUG(molecule_id.is_valid()); - VTR_ASSERT_DEBUG(cluster.pb != nullptr); - VTR_ASSERT_DEBUG(cluster.type != nullptr); - - // TODO: Remove these global accesses to the contexts. - // AtomContext used for: - // - printing verbose statements - // - Looking up the primitive pb - const AtomContext& atom_ctx = g_vpr_ctx.atom(); - // FloorplanningContext used for: - // - Checking if the atom can be placed in the cluster for floorplanning - // constraints. - const FloorplanningContext& floorplanning_ctx = g_vpr_ctx.floorplanning(); - - // Get the molecule object. - const t_pack_molecule& molecule = prepacker_.get_molecule(molecule_id); - - if (log_verbosity_ > 3) { - AtomBlockId root_atom = molecule.atom_block_ids[molecule.root]; - VTR_LOG("\t\tTry pack molecule: '%s' (%s)", - atom_ctx.netlist().block_name(root_atom).c_str(), - atom_ctx.netlist().block_model(root_atom)->name); - VTR_LOGV(molecule.pack_pattern, - " molecule_type %s molecule_size %zu", - molecule.pack_pattern->name, - molecule.atom_block_ids.size()); - VTR_LOG("\n"); - } - - // if this cluster has a molecule placed in it that is part of a long chain - // (a chain that consists of more than one molecule), don't allow more long chain - // molecules to be placed in this cluster. To avoid possibly creating cluster level - // blocks that have incompatible placement constraints or form very long placement - // macros that limit placement flexibility. - if (cluster.placement_stats->has_long_chain && molecule.is_chain() && prepacker_.get_molecule_chain_info(molecule.chain_id).is_long_chain) { - VTR_LOGV(log_verbosity_ > 4, "\t\t\tFAILED Placement Feasibility Filter: Only one long chain per cluster is allowed\n"); - return e_block_pack_status::BLK_FAILED_FEASIBLE; - } - - // Check if every atom in the molecule is legal in the cluster from a - // floorplanning perspective - bool cluster_pr_update_check = false; - PartitionRegion new_cluster_pr = cluster.pr; - // TODO: This can be made more efficient by pre-computing the intersection - // of all the atoms' PRs in the molecule. - for (AtomBlockId atom_blk_id : molecule.atom_block_ids) { - if (!atom_blk_id.is_valid()) - continue; - - // Try to intersect with atom PartitionRegion if atom exists - bool cluster_pr_needs_update = false; - bool block_pack_floorplan_status = check_cluster_floorplanning(atom_blk_id, - new_cluster_pr, - floorplanning_ctx.constraints, - log_verbosity_, - cluster_pr_needs_update); - if (!block_pack_floorplan_status) { - return e_block_pack_status::BLK_FAILED_FLOORPLANNING; - } - - if (cluster_pr_needs_update) { - cluster_pr_update_check = true; - } - } - - // Check if all atoms in the molecule can be added to the cluster without - // NoC group conflicts - NocGroupId new_cluster_noc_grp_id = cluster.noc_grp_id; - for (AtomBlockId atom_blk_id : molecule.atom_block_ids) { - if (!atom_blk_id.is_valid()) - continue; - - bool block_pack_noc_grp_status = check_cluster_noc_group(atom_blk_id, - new_cluster_noc_grp_id, - atom_noc_grp_id_, - log_verbosity_); - if (!block_pack_noc_grp_status) { - return e_block_pack_status::BLK_FAILED_NOC_GROUP; - } - } - - std::vector primitives_list(max_molecule_size_, nullptr); - e_block_pack_status block_pack_status = e_block_pack_status::BLK_STATUS_UNDEFINED; - while (block_pack_status != e_block_pack_status::BLK_PASSED) { - if (!get_next_primitive_list(cluster.placement_stats, - molecule_id, - primitives_list.data(), - prepacker_)) { - VTR_LOGV(log_verbosity_ > 3, "\t\tFAILED No candidate primitives available\n"); - block_pack_status = e_block_pack_status::BLK_FAILED_FEASIBLE; - break; /* no more candidate primitives available, this molecule will not pack, return fail */ - } - - block_pack_status = e_block_pack_status::BLK_PASSED; - size_t failed_location = 0; - for (size_t i_mol = 0; i_mol < molecule.atom_block_ids.size() && block_pack_status == e_block_pack_status::BLK_PASSED; i_mol++) { - VTR_ASSERT((primitives_list[i_mol] == nullptr) == (!molecule.atom_block_ids[i_mol])); - failed_location = i_mol + 1; - AtomBlockId atom_blk_id = molecule.atom_block_ids[i_mol]; - if (!atom_blk_id.is_valid()) - continue; - // NOTE: This parent variable is only used in the recursion of this - // function. - t_pb* parent = nullptr; - block_pack_status = try_place_atom_block_rec(primitives_list[i_mol], - atom_blk_id, - cluster.pb, - &parent, + cluster_pr = atom_pr; + cluster_pr_needs_update = true; + return true; + } + + // The Cluster's new PartitionRegion is the intersection of the Cluster's + // original PartitionRegion and the atom's PartitionRegion. + update_cluster_part_reg(cluster_pr, atom_pr); + + // If the intersection is empty, then the atom cannot be placed in this + // Cluster due to floorplanning constraints. + if (cluster_pr.empty()) { + VTR_LOGV(log_verbosity > 3, + "\t\t\t Intersect: Atom block %d failed floorplanning check for cluster\n", + atom_blk_id); + cluster_pr_needs_update = false; + return false; + } + + // If the Cluster's new PartitionRegion is non-empty, then this atom passes + // the floorplanning constraints and the cluster's PartitionRegion should be + // updated. + cluster_pr_needs_update = true; + VTR_LOGV(log_verbosity > 3, + "\t\t\t Intersect: Atom block %d passed cluster, cluster PR was updated with intersection result \n", + atom_blk_id); + return true; + } + + bool ClusterLegalizer::check_cluster_noc_group(AtomBlockId atom_blk_id, + NocGroupId& cluster_noc_grp_id, + const vtr::vector& atom_noc_grp_ids, + int log_verbosity) { + const NocGroupId atom_noc_grp_id = atom_noc_grp_ids.empty() ? NocGroupId::INVALID() : atom_noc_grp_ids[atom_blk_id]; + + if (!cluster_noc_grp_id.is_valid()) { + // If the cluster does not have a NoC group, assign the atom's NoC group + // to the cluster. + VTR_LOGV(log_verbosity > 3, + "\t\t\t NoC Group: Atom block %d passed cluster, cluster's NoC group was updated with the atom's group %d\n", + atom_blk_id, (size_t)atom_noc_grp_id); + cluster_noc_grp_id = atom_noc_grp_id; + return true; + } + + if (cluster_noc_grp_id == atom_noc_grp_id) { + // If the cluster has the same NoC group ID as the atom, they are + // compatible. + VTR_LOGV(log_verbosity > 3, + "\t\t\t NoC Group: Atom block %d passed cluster, cluster's NoC group was compatible with the atom's group %d\n", + atom_blk_id, (size_t)atom_noc_grp_id); + return true; + } + + // If the cluster belongs to a different NoC group than the atom's group, + // they are incompatible. + VTR_LOGV(log_verbosity > 3, + "\t\t\t NoC Group: Atom block %d failed NoC group check for cluster. Cluster's NoC group: %d, atom's NoC group: %d\n", + atom_blk_id, (size_t)cluster_noc_grp_id, (size_t)atom_noc_grp_id); + return false; + } + + enum e_block_pack_status ClusterLegalizer::check_chain_root_placement_feasibility( + const t_pb_graph_node* pb_graph_node, + const t_chain_info& prepack_chain_info, + const t_clustering_chain_info& clustering_chain_info, + t_pack_patterns* mol_pack_patterns, + const AtomBlockId blk_id) { + const AtomContext& atom_ctx = g_vpr_ctx.atom(); + + enum e_block_pack_status block_pack_status = e_block_pack_status::BLK_PASSED; + + bool is_long_chain = prepack_chain_info.is_long_chain; + + const auto& chain_root_pins = mol_pack_patterns->chain_root_pins; + + t_model_ports* root_port = chain_root_pins[0][0]->port->model_port; + AtomNetId chain_net_id; + auto port_id = atom_ctx.netlist().find_atom_port(blk_id, root_port); + + if (port_id) { + chain_net_id = atom_ctx.netlist().port_net(port_id, chain_root_pins[0][0]->pin_number); + } + + // if this block is part of a long chain or it is driven by a cluster + // input pin we need to check the placement legality of this block + // Depending on the logic synthesis even small chains that can fit within one + // cluster might need to start at the top of the cluster as their input can be + // driven by a global gnd or vdd. Therefore even if this is not a long chain + // but its input pin is driven by a net, the placement legality is checked. + if (is_long_chain || chain_net_id) { + auto chain_id = clustering_chain_info.chain_id; + // if this chain has a chain id assigned to it (implies is_long_chain too) + if (chain_id != -1) { + // the chosen primitive should be a valid starting point for the chain + // long chains should only be placed at the top of the chain tieOff = 0 + if (pb_graph_node != chain_root_pins[chain_id][0]->parent_node) { + block_pack_status = e_block_pack_status::BLK_FAILED_FEASIBLE; + } + // the chain doesn't have an assigned chain_id yet + } else { + block_pack_status = e_block_pack_status::BLK_FAILED_FEASIBLE; + for (const auto& chain : chain_root_pins) { + for (auto tieOff : chain) { + // check if this chosen primitive is one of the possible + // starting points for this chain. + if (pb_graph_node == tieOff->parent_node) { + // this location matches with the one of the dedicated chain + // input from outside logic block, therefore it is feasible + block_pack_status = e_block_pack_status::BLK_PASSED; + break; + } + // long chains should only be placed at the top of the chain tieOff = 0 + if (is_long_chain) break; + } + } + } + } + + return block_pack_status; + } + + bool ClusterLegalizer::primitive_memory_sibling_feasible(const AtomBlockId blk_id, const t_pb_type* cur_pb_type, const AtomBlockId sibling_blk_id) { + const AtomContext& atom_ctx = g_vpr_ctx.atom(); + + VTR_ASSERT(cur_pb_type->class_type == MEMORY_CLASS); + + //First, identify the 'data' ports by looking at the cur_pb_type + std::unordered_set data_ports; + for (int iport = 0; iport < cur_pb_type->num_ports; ++iport) { + const char* port_class = cur_pb_type->ports[iport].port_class; + if (port_class && strstr(port_class, "data") == port_class) { + //The port_class starts with "data", so it is a data port + + //Record the port + data_ports.insert(cur_pb_type->ports[iport].model_port); + } + } + + //Now verify that all nets (except those connected to data ports) are equivalent + //between blk_id and sibling_blk_id + + //Since the atom netlist stores only in-use ports, we iterate over the model to ensure + //all ports are compared + const t_model* model = cur_pb_type->model; + for (t_model_ports* port : {model->inputs, model->outputs}) { + for (; port; port = port->next) { + if (data_ports.count(port)) { + //Don't check data ports + continue; + } + + //Note: VPR doesn't support multi-driven nets, so all outputs + //should be data ports, otherwise the siblings will both be + //driving the output net + + //Get the ports from each primitive + auto blk_port_id = atom_ctx.netlist().find_atom_port(blk_id, port); + auto sib_port_id = atom_ctx.netlist().find_atom_port(sibling_blk_id, port); + + //Check that all nets (including unconnected nets) match + for (int ipin = 0; ipin < port->size; ++ipin) { + //The nets are initialized as invalid (i.e. disconnected) + AtomNetId blk_net_id; + AtomNetId sib_net_id; + + //We can get the actual net provided the port exists + // + //Note that if the port did not exist, the net is left + //as invalid/disconneced + if (blk_port_id) { + blk_net_id = atom_ctx.netlist().port_net(blk_port_id, ipin); + } + if (sib_port_id) { + sib_net_id = atom_ctx.netlist().port_net(sib_port_id, ipin); + } + + //The sibling and block must have the same (possibly disconnected) + //net on this pin + if (blk_net_id != sib_net_id) { + //Nets do not match, not feasible + return false; + } + } + } + } + + return true; + } + + bool ClusterLegalizer::primitive_feasible(const AtomBlockId blk_id, t_pb* cur_pb) { + const t_pb_type* cur_pb_type = cur_pb->pb_graph_node->pb_type; + + VTR_ASSERT(cur_pb_type->num_modes == 0); /* primitive */ + + AtomBlockId cur_pb_blk_id = atom_pb_lookup().pb_atom(cur_pb); + if (cur_pb_blk_id && cur_pb_blk_id != blk_id) { + /* This pb already has a different logical block */ + return false; + } + + if (cur_pb_type->class_type == MEMORY_CLASS) { + /* Memory class has additional feasibility requirements: + * - all siblings must share all nets, including open nets, with the exception of data nets */ + + /* find sibling if one exists */ + const t_pb *sibling_memory_pb = find_memory_sibling(cur_pb); + AtomBlockId sibling_memory_blk_id = atom_pb_lookup().pb_atom(sibling_memory_pb); + + if (sibling_memory_blk_id) { + //There is a sibling, see if the current block is feasible with it + bool sibling_feasible = primitive_memory_sibling_feasible(blk_id, cur_pb_type, sibling_memory_blk_id); + if (!sibling_feasible) { + return false; + } + } + } + + //Generic feasibility check + return primitive_type_feasible(blk_id, cur_pb_type); + } + + enum e_block_pack_status + ClusterLegalizer::try_place_atom_block_rec(const t_pb_graph_node* pb_graph_node, + const AtomBlockId blk_id, + t_pb* cb, + t_pb** parent, + const LegalizationClusterId cluster_id, + vtr::vector_map& atom_cluster, + const PackMoleculeId molecule_id, + t_lb_router_data* router_data, + int verbosity, + const Prepacker& prepacker, + const vtr::vector_map& clustering_chain_info) { + const AtomContext& atom_ctx = g_vpr_ctx.atom(); + + VTR_ASSERT_SAFE(cb != nullptr); + e_block_pack_status block_pack_status = e_block_pack_status::BLK_PASSED; + + /* Discover parent */ + t_pb* parent_pb = nullptr; + if (pb_graph_node->parent_pb_graph_node != cb->pb_graph_node) { + t_pb* my_parent = nullptr; + block_pack_status = try_place_atom_block_rec(pb_graph_node->parent_pb_graph_node, blk_id, cb, + &my_parent, cluster_id, + atom_cluster, + molecule_id, router_data, + verbosity, + prepacker, clustering_chain_info); + parent_pb = my_parent; + } else { + parent_pb = cb; + } + + /* Create siblings if siblings are not allocated */ + VTR_ASSERT(parent_pb != nullptr); + if (parent_pb->child_pbs == nullptr) { + VTR_ASSERT(parent_pb->name == nullptr); + parent_pb->name = vtr::strdup(atom_ctx.netlist().block_name(blk_id).c_str()); + parent_pb->mode = pb_graph_node->pb_type->parent_mode->index; + set_reset_pb_modes(router_data, parent_pb, true); + const t_mode* mode = &parent_pb->pb_graph_node->pb_type->modes[parent_pb->mode]; + parent_pb->child_pbs = new t_pb*[mode->num_pb_type_children]; + + for (int i = 0; i < mode->num_pb_type_children; i++) { + parent_pb->child_pbs[i] = new t_pb[mode->pb_type_children[i].num_pb]; + + for (int j = 0; j < mode->pb_type_children[i].num_pb; j++) { + parent_pb->child_pbs[i][j].parent_pb = parent_pb; + parent_pb->child_pbs[i][j].pb_graph_node = &(parent_pb->pb_graph_node->child_pb_graph_nodes[parent_pb->mode][i][j]); + } + } + } else { + /* if this is not the first child of this parent, must match existing parent mode */ + if (parent_pb->mode != pb_graph_node->pb_type->parent_mode->index) { + return e_block_pack_status::BLK_FAILED_FEASIBLE; + } + } + + const t_mode* mode = &parent_pb->pb_graph_node->pb_type->modes[parent_pb->mode]; + int i; + for (i = 0; i < mode->num_pb_type_children; i++) { + if (pb_graph_node->pb_type == &mode->pb_type_children[i]) { + break; + } + } + VTR_ASSERT(i < mode->num_pb_type_children); + t_pb* pb = &parent_pb->child_pbs[i][pb_graph_node->placement_index]; + VTR_ASSERT_SAFE(pb != nullptr); + *parent = pb; /* this pb is parent of it's child that called this function */ + VTR_ASSERT(pb->pb_graph_node == pb_graph_node); + if (pb->pb_stats == nullptr) { + alloc_and_load_pb_stats(pb); + } + const t_pb_type* pb_type = pb_graph_node->pb_type; + + /* Any pb_type under an mode, which is disabled for packing, should not be considerd for mapping + * Early exit to flag failure + */ + if (true == pb_type->parent_mode->disable_packing) { + return e_block_pack_status::BLK_FAILED_FEASIBLE; + } + + bool is_primitive = (pb_type->num_modes == 0); + + if (is_primitive) { + VTR_ASSERT(!atom_pb_lookup().pb_atom(pb) + && atom_pb_lookup().atom_pb(blk_id) == nullptr + && atom_cluster[blk_id] == LegalizationClusterId::INVALID()); + /* try pack to location */ + VTR_ASSERT(pb->name == nullptr); + pb->name = vtr::strdup(atom_ctx.netlist().block_name(blk_id).c_str()); + + //Update the atom netlist mappings + atom_cluster[blk_id] = cluster_id; + // NOTE: This pb is different from the pb of the cluster. It is the pb + // of the actual primitive. + // TODO: It would be a good idea to remove the use of this global + // variables to prevent external users from modifying this by + // mistake. + mutable_atom_pb_lookup().set_atom_pb(blk_id, pb); + + add_atom_as_target(router_data, blk_id, atom_pb_lookup()); + if (!primitive_feasible(blk_id, pb)) { + /* failed location feasibility check, revert pack */ + block_pack_status = e_block_pack_status::BLK_FAILED_FEASIBLE; + } + + // if this block passed and is part of a chained molecule + const t_pack_molecule& molecule = prepacker.get_molecule(molecule_id); + if (block_pack_status == e_block_pack_status::BLK_PASSED && molecule.is_chain()) { + auto molecule_root_block = molecule.atom_block_ids[molecule.root]; + // if this is the root block of the chain molecule check its placmeent feasibility + if (blk_id == molecule_root_block) { + VTR_ASSERT(molecule.chain_id.is_valid()); + const t_chain_info& prepack_chain_info = prepacker.get_molecule_chain_info(molecule.chain_id); + block_pack_status = check_chain_root_placement_feasibility(pb_graph_node, + prepack_chain_info, + clustering_chain_info[molecule.chain_id], + molecule.pack_pattern, + blk_id); + } + } + + VTR_LOGV(verbosity > 4 && block_pack_status == e_block_pack_status::BLK_PASSED, + "\t\t\tPlaced atom '%s' (%s) at %s\n", + atom_ctx.netlist().block_name(blk_id).c_str(), + atom_ctx.netlist().block_model(blk_id)->name, + pb->hierarchical_type_name().c_str()); + } + + if (block_pack_status != e_block_pack_status::BLK_PASSED) { + free(pb->name); + pb->name = nullptr; + } + return block_pack_status; + } + + void ClusterLegalizer::reset_lookahead_pins_used(t_pb* cur_pb) { + const t_pb_type* pb_type = cur_pb->pb_graph_node->pb_type; + if (cur_pb->pb_stats == nullptr) { + return; /* No pins used, no need to continue */ + } + + if (pb_type->num_modes > 0 && cur_pb->name != nullptr) { + for (int i = 0; i < cur_pb->pb_graph_node->num_input_pin_class; i++) { + cur_pb->pb_stats->lookahead_input_pins_used[i].clear(); + } + + for (int i = 0; i < cur_pb->pb_graph_node->num_output_pin_class; i++) { + cur_pb->pb_stats->lookahead_output_pins_used[i].clear(); + } + + if (cur_pb->child_pbs != nullptr) { + for (int i = 0; i < pb_type->modes[cur_pb->mode].num_pb_type_children; i++) { + if (cur_pb->child_pbs[i] != nullptr) { + for (int j = 0; j < pb_type->modes[cur_pb->mode].pb_type_children[i].num_pb; j++) { + reset_lookahead_pins_used(&cur_pb->child_pbs[i][j]); + } + } + } + } + } + } + + int ClusterLegalizer::net_sinks_reachable_in_cluster(const t_pb_graph_pin* driver_pb_gpin, const int depth, const AtomNetId net_id) { + const AtomContext& atom_ctx = g_vpr_ctx.atom(); + + //Record the sink pb graph pins we are looking for + std::unordered_set sink_pb_gpins; + for (const AtomPinId pin_id : atom_ctx.netlist().net_sinks(net_id)) { + const t_pb_graph_pin* sink_pb_gpin = find_pb_graph_pin(atom_ctx.netlist(), atom_pb_lookup(), pin_id); + VTR_ASSERT(sink_pb_gpin); + + sink_pb_gpins.insert(sink_pb_gpin); + } + + //Count how many sink pins are reachable + size_t num_reachable_sinks = 0; + for (int i_prim_pin = 0; i_prim_pin < driver_pb_gpin->num_connectable_primitive_input_pins[depth]; ++i_prim_pin) { + const t_pb_graph_pin* reachable_pb_gpin = driver_pb_gpin->list_of_connectable_input_pin_ptrs[depth][i_prim_pin]; + + if (sink_pb_gpins.count(reachable_pb_gpin)) { + ++num_reachable_sinks; + if (num_reachable_sinks == atom_ctx.netlist().net_sinks(net_id).size()) { + return true; + } + } + } + + return false; + } + + t_pb_graph_pin* ClusterLegalizer::get_driver_pb_graph_pin(const t_pb* driver_pb, const AtomPinId driver_pin_id) { + const AtomContext& atom_ctx = g_vpr_ctx.atom(); + + const auto driver_pb_type = driver_pb->pb_graph_node->pb_type; + int output_port = 0; + // find the port of the pin driving the net as well as the port model + auto driver_port_id = atom_ctx.netlist().pin_port(driver_pin_id); + auto driver_model_port = atom_ctx.netlist().port_model(driver_port_id); + // find the port id of the port containing the driving pin in the driver_pb_type + for (int i = 0; i < driver_pb_type->num_ports; i++) { + auto& prim_port = driver_pb_type->ports[i]; + if (prim_port.type == OUT_PORT) { + if (prim_port.model_port == driver_model_port) { + // get the output pb_graph_pin driving this input net + return &(driver_pb->pb_graph_node->output_pins[output_port][atom_ctx.netlist().pin_port_bit(driver_pin_id)]); + } + output_port++; + } + } + // the pin should be found + VTR_ASSERT(false); + return nullptr; + } + + void ClusterLegalizer::compute_and_mark_lookahead_pins_used_for_pin(const t_pb_graph_pin* pb_graph_pin, + const t_pb* primitive_pb, + const AtomNetId net_id, + const vtr::vector_map& atom_cluster) { + const AtomContext& atom_ctx = g_vpr_ctx.atom(); + + // starting from the parent pb of the input primitive go up in the hierarchy till the root block + for (auto cur_pb = primitive_pb->parent_pb; cur_pb; cur_pb = cur_pb->parent_pb) { + const auto depth = cur_pb->pb_graph_node->pb_type->depth; + const auto pin_class = pb_graph_pin->parent_pin_class[depth]; + VTR_ASSERT(pin_class != OPEN); + + const auto driver_blk_id = atom_ctx.netlist().net_driver_block(net_id); + + // if this primitive pin is an input pin + if (pb_graph_pin->port->type == IN_PORT) { + /* find location of net driver if exist in clb, NULL otherwise */ + // find the driver of the input net connected to the pin being studied + const auto driver_pin_id = atom_ctx.netlist().net_driver(net_id); + // find the id of the atom occupying the input primitive_pb + const auto prim_blk_id = atom_pb_lookup().pb_atom(primitive_pb); + // find the pb block occupied by the driving atom + const auto driver_pb = atom_pb_lookup().atom_pb(driver_blk_id); + // pb_graph_pin driving net_id in the driver pb block + t_pb_graph_pin* output_pb_graph_pin = nullptr; + // if the driver block is in the same clb as the input primitive block + LegalizationClusterId driver_cluster_id = atom_cluster[driver_blk_id]; + LegalizationClusterId prim_cluster_id = atom_cluster[prim_blk_id]; + if (driver_cluster_id == prim_cluster_id) { + // get pb_graph_pin driving the given net + output_pb_graph_pin = get_driver_pb_graph_pin(driver_pb, driver_pin_id); + } + + bool is_reachable = false; + + // if the driver pin is within the cluster + if (output_pb_graph_pin) { + // find if the driver pin can reach the input pin of the primitive or not + const t_pb* check_pb = driver_pb; + while (check_pb && check_pb != cur_pb) { + check_pb = check_pb->parent_pb; + } + if (check_pb) { + for (int i = 0; i < output_pb_graph_pin->num_connectable_primitive_input_pins[depth]; i++) { + if (pb_graph_pin == output_pb_graph_pin->list_of_connectable_input_pin_ptrs[depth][i]) { + is_reachable = true; + break; + } + } + } + } + + // Must use an input pin to connect the driver to the input pin of the given primitive, either the + // driver atom is not contained in the cluster or is contained but cannot reach the primitive pin + if (!is_reachable) { + // add net to lookahead_input_pins_used if not already added + auto it = std::find(cur_pb->pb_stats->lookahead_input_pins_used[pin_class].begin(), + cur_pb->pb_stats->lookahead_input_pins_used[pin_class].end(), net_id); + if (it == cur_pb->pb_stats->lookahead_input_pins_used[pin_class].end()) { + cur_pb->pb_stats->lookahead_input_pins_used[pin_class].push_back(net_id); + } + } + } else { + VTR_ASSERT(pb_graph_pin->port->type == OUT_PORT); + /* + * Determine if this net (which is driven from within this cluster) leaves this cluster + * (and hence uses an output pin). + */ + + bool net_exits_cluster = true; + int num_net_sinks = static_cast(atom_ctx.netlist().net_sinks(net_id).size()); + + if (pb_graph_pin->num_connectable_primitive_input_pins[depth] >= num_net_sinks) { + //It is possible the net is completely absorbed in the cluster, + //since this pin could (potentially) drive all the net's sinks + + /* Important: This runtime penalty looks a lot scarier than it really is. + * For high fan-out nets, I at most look at the number of pins within the + * cluster which limits runtime. + * + * DO NOT REMOVE THIS INITIAL FILTER WITHOUT CAREFUL ANALYSIS ON RUNTIME!!! + * + * Key Observation: + * For LUT-based designs it is impossible for the average fanout to exceed + * the number of LUT inputs so it's usually around 4-5 (pigeon-hole argument, + * if the average fanout is greater than the number of LUT inputs, where do + * the extra connections go? Therefore, average fanout must be capped to a + * small constant where the constant is equal to the number of LUT inputs). + * The real danger to runtime is when the number of sinks of a net gets doubled + */ + + //Check if all the net sinks are, in fact, inside this cluster + bool all_sinks_in_cur_cluster = true; + LegalizationClusterId driver_cluster = atom_cluster[driver_blk_id]; + for (auto pin_id : atom_ctx.netlist().net_sinks(net_id)) { + auto sink_blk_id = atom_ctx.netlist().pin_block(pin_id); + if (atom_cluster[sink_blk_id] != driver_cluster) { + all_sinks_in_cur_cluster = false; + break; + } + } + + if (all_sinks_in_cur_cluster) { + //All the sinks are part of this cluster, so the net may be fully absorbed. + // + //Verify this, by counting the number of net sinks reachable from the driver pin. + //If the count equals the number of net sinks then the net is fully absorbed and + //the net does not exit the cluster + /* TODO: I should cache the absorbed outputs, once net is absorbed, + * net is forever absorbed, no point in rechecking every time */ + if (net_sinks_reachable_in_cluster(pb_graph_pin, depth, net_id)) { + //All the sinks are reachable inside the cluster + net_exits_cluster = false; + } + } + } + + if (net_exits_cluster) { + /* This output must exit this cluster */ + cur_pb->pb_stats->lookahead_output_pins_used[pin_class].push_back(net_id); + } + } + } + } + + void ClusterLegalizer::compute_and_mark_lookahead_pins_used(const AtomBlockId blk_id, + const vtr::vector_map& atom_cluster) { + const AtomContext& atom_ctx = g_vpr_ctx.atom(); + + const t_pb* cur_pb = atom_pb_lookup().atom_pb(blk_id); + VTR_ASSERT(cur_pb != nullptr); + + /* Walk through inputs, outputs, and clocks marking pins off of the same class */ + for (auto pin_id : atom_ctx.netlist().block_pins(blk_id)) { + auto net_id = atom_ctx.netlist().pin_net(pin_id); + + const t_pb_graph_pin* pb_graph_pin = find_pb_graph_pin(atom_ctx.netlist(), atom_pb_lookup(), pin_id); + compute_and_mark_lookahead_pins_used_for_pin(pb_graph_pin, cur_pb, net_id, atom_cluster); + } + } + + void ClusterLegalizer::try_update_lookahead_pins_used(t_pb* cur_pb, + const vtr::vector_map& atom_cluster) { + // run recursively till a leaf (primitive) pb block is reached + const t_pb_type* pb_type = cur_pb->pb_graph_node->pb_type; + if (pb_type->num_modes > 0 && cur_pb->name != nullptr) { + if (cur_pb->child_pbs != nullptr) { + for (int i = 0; i < pb_type->modes[cur_pb->mode].num_pb_type_children; i++) { + if (cur_pb->child_pbs[i] != nullptr) { + for (int j = 0; j < pb_type->modes[cur_pb->mode].pb_type_children[i].num_pb; j++) { + try_update_lookahead_pins_used(&cur_pb->child_pbs[i][j], atom_cluster); + } + } + } + } + } else { + // find if this child (primitive) pb block has an atom mapped to it, + // if yes compute and mark lookahead pins used for that pb block + AtomBlockId blk_id = atom_pb_lookup().pb_atom(cur_pb); + if (pb_type->blif_model != nullptr && blk_id) { + compute_and_mark_lookahead_pins_used(blk_id, atom_cluster); + } + } + } + + bool ClusterLegalizer::check_lookahead_pins_used(t_pb* cur_pb, t_ext_pin_util max_external_pin_util) { + const t_pb_type* pb_type = cur_pb->pb_graph_node->pb_type; + + if (pb_type->num_modes > 0 && cur_pb->name) { + for (int i = 0; i < cur_pb->pb_graph_node->num_input_pin_class; i++) { + size_t class_size = cur_pb->pb_graph_node->input_pin_class_size[i]; + + if (cur_pb->is_root()) { + // Scale the class size by the maximum external pin utilization factor + // Use ceil to avoid classes of size 1 from being scaled to zero + class_size = std::ceil(max_external_pin_util.input_pin_util * class_size); + // if the number of pins already used is larger than class size, then the number of + // cluster inputs already used should be our constraint. Why is this needed? This is + // needed since when packing the seed block the maximum external pin utilization is + // used as 1.0 allowing molecules that are using up to all the cluster inputs to be + // packed legally. Therefore, if the seed block is already using more inputs than + // the allowed maximum utilization, this should become the new maximum pin utilization. + class_size = std::max(class_size, cur_pb->pb_stats->input_pins_used[i].size()); + } + + if (cur_pb->pb_stats->lookahead_input_pins_used[i].size() > class_size) { + return false; + } + } + + for (int i = 0; i < cur_pb->pb_graph_node->num_output_pin_class; i++) { + size_t class_size = cur_pb->pb_graph_node->output_pin_class_size[i]; + if (cur_pb->is_root()) { + // Scale the class size by the maximum external pin utilization factor + // Use ceil to avoid classes of size 1 from being scaled to zero + class_size = std::ceil(max_external_pin_util.output_pin_util * class_size); + // if the number of pins already used is larger than class size, then the number of + // cluster outputs already used should be our constraint. Why is this needed? This is + // needed since when packing the seed block the maximum external pin utilization is + // used as 1.0 allowing molecules that are using up to all the cluster inputs to be + // packed legally. Therefore, if the seed block is already using more inputs than + // the allowed maximum utilization, this should become the new maximum pin utilization. + class_size = std::max(class_size, cur_pb->pb_stats->output_pins_used[i].size()); + } + + if (cur_pb->pb_stats->lookahead_output_pins_used[i].size() > class_size) { + return false; + } + } + + if (cur_pb->child_pbs) { + for (int i = 0; i < pb_type->modes[cur_pb->mode].num_pb_type_children; i++) { + if (cur_pb->child_pbs[i]) { + for (int j = 0; j < pb_type->modes[cur_pb->mode].pb_type_children[i].num_pb; j++) { + if (!check_lookahead_pins_used(&cur_pb->child_pbs[i][j], max_external_pin_util)) + return false; + } + } + } + } + } + + return true; + } + + void ClusterLegalizer::update_clustering_chain_info(PackMoleculeId chain_molecule_id, + const t_pb_graph_node* root_primitive) { + // Get the molecule + VTR_ASSERT(chain_molecule_id.is_valid()); + const t_pack_molecule& chain_molecule = prepacker_.get_molecule(chain_molecule_id); + + // Get the ID of the chain it is a part of + MoleculeChainId chain_id = chain_molecule.chain_id; + VTR_ASSERT(chain_id.is_valid()); + + // Get the prepacking and clustering information on this chain. + const t_chain_info& prepack_chain_info = prepacker_.get_molecule_chain_info(chain_id); + t_clustering_chain_info& clustering_chain_info = clustering_chain_info_[chain_id]; + VTR_ASSERT(clustering_chain_info.chain_id == -1 && prepack_chain_info.is_long_chain); + + // Update the clustering chain information. + // long chains should only be placed at the beginning of the chain + // Since for long chains the molecule size is already equal to the + // total number of adders in the cluster. Therefore, it should + // always be placed at the very first adder in this cluster. + auto chain_root_pins = chain_molecule.pack_pattern->chain_root_pins; + for (size_t chainId = 0; chainId < chain_root_pins.size(); chainId++) { + if (chain_root_pins[chainId][0]->parent_node == root_primitive) { + clustering_chain_info.chain_id = chainId; + clustering_chain_info.first_packed_molecule = chain_molecule_id; + return; + } + } + + VTR_ASSERT(false); + } + + void ClusterLegalizer::reset_molecule_info(PackMoleculeId mol_id) { + VTR_ASSERT(mol_id.is_valid()); + + // when invalidating a molecule check if it's a chain molecule + // that is part of a long chain. If so, check if this molecule + // has modified the chain_id value based on the stale packing + // then reset the chain id and the first packed molecule pointer + // this is packing is being reset + const t_pack_molecule& mol = prepacker_.get_molecule(mol_id); + if (!mol.is_chain()) + return; + + VTR_ASSERT(mol.chain_id.is_valid()); + const t_chain_info& prepack_chain_info = prepacker_.get_molecule_chain_info(mol.chain_id); + if (!prepack_chain_info.is_long_chain) + return; + + t_clustering_chain_info& clustering_chain_info = clustering_chain_info_[mol.chain_id]; + if (clustering_chain_info.first_packed_molecule == mol_id) { + clustering_chain_info.first_packed_molecule = PackMoleculeId::INVALID(); + clustering_chain_info.chain_id = -1; + } + } + + void ClusterLegalizer::revert_place_atom_block(const AtomBlockId blk_id, + t_lb_router_data* router_data, + vtr::vector_map& atom_cluster) { + //We cast away const here since we may free the pb, and it is + //being removed from the active mapping. + // + //In general most code works fine accessing cosnt t_pb*, + //which is why we store them as such in atom_ctx.lookup() + t_pb* pb = const_cast(atom_pb_lookup().atom_pb(blk_id)); + + if (pb != nullptr) { + /* When freeing molecules, the current block might already have been freed by a prior revert + * When this happens, no need to do anything beyond basic book keeping at the atom block + */ + + t_pb* next = pb->parent_pb; + free_pb(pb); + pb = next; + + while (pb != nullptr) { + /* If this is pb is created only for the purposes of holding new molecule, remove it + * Must check if cluster is already freed (which can be the case) + */ + next = pb->parent_pb; + + if (pb->child_pbs != nullptr && pb->pb_stats != nullptr + && pb->pb_stats->num_child_blocks_in_pb == 0) { + set_reset_pb_modes(router_data, pb, false); + if (next != nullptr) { + /* If the code gets here, then that means that placing the initial seed molecule + * failed, don't free the actual complex block itself as the seed needs to find + * another placement */ + free_pb(pb); + } + } + pb = next; + } + } + + //Update the atom netlist mapping + atom_cluster[blk_id] = LegalizationClusterId::INVALID(); + mutable_atom_pb_lookup().set_atom_pb(blk_id, nullptr); + } + + void ClusterLegalizer::commit_lookahead_pins_used(t_pb* cur_pb) { + const t_pb_type* pb_type = cur_pb->pb_graph_node->pb_type; + + if (pb_type->num_modes > 0 && cur_pb->name) { + for (int i = 0; i < cur_pb->pb_graph_node->num_input_pin_class; i++) { + VTR_ASSERT(cur_pb->pb_stats->lookahead_input_pins_used[i].size() <= (unsigned int)cur_pb->pb_graph_node->input_pin_class_size[i]); + for (size_t j = 0; j < cur_pb->pb_stats->lookahead_input_pins_used[i].size(); j++) { + VTR_ASSERT(cur_pb->pb_stats->lookahead_input_pins_used[i][j]); + cur_pb->pb_stats->input_pins_used[i].insert({j, cur_pb->pb_stats->lookahead_input_pins_used[i][j]}); + } + } + + for (int i = 0; i < cur_pb->pb_graph_node->num_output_pin_class; i++) { + VTR_ASSERT(cur_pb->pb_stats->lookahead_output_pins_used[i].size() <= (unsigned int)cur_pb->pb_graph_node->output_pin_class_size[i]); + for (size_t j = 0; j < cur_pb->pb_stats->lookahead_output_pins_used[i].size(); j++) { + VTR_ASSERT(cur_pb->pb_stats->lookahead_output_pins_used[i][j]); + cur_pb->pb_stats->output_pins_used[i].insert({j, cur_pb->pb_stats->lookahead_output_pins_used[i][j]}); + } + } + + if (cur_pb->child_pbs) { + for (int i = 0; i < pb_type->modes[cur_pb->mode].num_pb_type_children; i++) { + if (cur_pb->child_pbs[i]) { + for (int j = 0; j < pb_type->modes[cur_pb->mode].pb_type_children[i].num_pb; j++) { + commit_lookahead_pins_used(&cur_pb->child_pbs[i][j]); + } + } + } + } + } + } + + bool ClusterLegalizer::cleanup_pb(t_pb* pb) { + bool can_free = true; + + /* Recursively check if there are any children with already assigned atoms */ + if (pb->child_pbs != nullptr) { + const t_mode* mode = &pb->pb_graph_node->pb_type->modes[pb->mode]; + VTR_ASSERT(mode != nullptr); + + /* Check each mode */ + for (int i = 0; i < mode->num_pb_type_children; ++i) { + /* Check each child */ + if (pb->child_pbs[i] != nullptr) { + for (int j = 0; j < mode->pb_type_children[i].num_pb; ++j) { + t_pb* pb_child = &pb->child_pbs[i][j]; + t_pb_type* pb_type = pb_child->pb_graph_node->pb_type; + + /* Primitive, check occupancy */ + if (pb_type->num_modes == 0) { + if (pb_child->name != nullptr) { + can_free = false; + } + } + + /* Non-primitive, recurse */ + else { + if (!cleanup_pb(pb_child)) { + can_free = false; + } + } + } + } + } + + /* Free if can */ + if (can_free) { + for (int i = 0; i < mode->num_pb_type_children; ++i) { + if (pb->child_pbs[i] != nullptr) { + delete[] pb->child_pbs[i]; + } + } + + delete[] pb->child_pbs; + pb->child_pbs = nullptr; + pb->mode = 0; + + if (pb->name) { + free(pb->name); + pb->name = nullptr; + } + } + } + + return can_free; + } + + e_block_pack_status ClusterLegalizer::try_pack_molecule(PackMoleculeId molecule_id, + LegalizationCluster& cluster, + LegalizationClusterId cluster_id, + const t_ext_pin_util& max_external_pin_util) { + // Try to pack the molecule into a cluster with this pb type. + + // Safety debugs. + VTR_ASSERT_DEBUG(molecule_id.is_valid()); + VTR_ASSERT_DEBUG(cluster.pb != nullptr); + VTR_ASSERT_DEBUG(cluster.type != nullptr); + + // TODO: Remove these global accesses to the contexts. + // AtomContext used for: + // - printing verbose statements + // - Looking up the primitive pb + const AtomContext& atom_ctx = g_vpr_ctx.atom(); + // FloorplanningContext used for: + // - Checking if the atom can be placed in the cluster for floorplanning + // constraints. + const FloorplanningContext& floorplanning_ctx = g_vpr_ctx.floorplanning(); + + // Get the molecule object. + const t_pack_molecule& molecule = prepacker_.get_molecule(molecule_id); + + if (log_verbosity_ > 3) { + AtomBlockId root_atom = molecule.atom_block_ids[molecule.root]; + VTR_LOG("\t\tTry pack molecule: '%s' (%s)", + atom_ctx.netlist().block_name(root_atom).c_str(), + atom_ctx.netlist().block_model(root_atom)->name); + VTR_LOGV(molecule.pack_pattern, + " molecule_type %s molecule_size %zu", + molecule.pack_pattern->name, + molecule.atom_block_ids.size()); + VTR_LOG("\n"); + } + + // if this cluster has a molecule placed in it that is part of a long chain + // (a chain that consists of more than one molecule), don't allow more long chain + // molecules to be placed in this cluster. To avoid possibly creating cluster level + // blocks that have incompatible placement constraints or form very long placement + // macros that limit placement flexibility. + if (cluster.placement_stats->has_long_chain && molecule.is_chain() && prepacker_.get_molecule_chain_info(molecule.chain_id).is_long_chain) { + VTR_LOGV(log_verbosity_ > 4, "\t\t\tFAILED Placement Feasibility Filter: Only one long chain per cluster is allowed\n"); + return e_block_pack_status::BLK_FAILED_FEASIBLE; + } + + // Check if every atom in the molecule is legal in the cluster from a + // floorplanning perspective + bool cluster_pr_update_check = false; + PartitionRegion new_cluster_pr = cluster.pr; + // TODO: This can be made more efficient by pre-computing the intersection + // of all the atoms' PRs in the molecule. + for (AtomBlockId atom_blk_id : molecule.atom_block_ids) { + if (!atom_blk_id.is_valid()) + continue; + + // Try to intersect with atom PartitionRegion if atom exists + bool cluster_pr_needs_update = false; + bool block_pack_floorplan_status = check_cluster_floorplanning(atom_blk_id, + new_cluster_pr, + floorplanning_ctx.constraints, + log_verbosity_, + cluster_pr_needs_update); + if (!block_pack_floorplan_status) { + return e_block_pack_status::BLK_FAILED_FLOORPLANNING; + } + + if (cluster_pr_needs_update) { + cluster_pr_update_check = true; + } + } + + // Check if all atoms in the molecule can be added to the cluster without + // NoC group conflicts + NocGroupId new_cluster_noc_grp_id = cluster.noc_grp_id; + for (AtomBlockId atom_blk_id : molecule.atom_block_ids) { + if (!atom_blk_id.is_valid()) + continue; + + bool block_pack_noc_grp_status = check_cluster_noc_group(atom_blk_id, + new_cluster_noc_grp_id, + atom_noc_grp_id_, + log_verbosity_); + if (!block_pack_noc_grp_status) { + return e_block_pack_status::BLK_FAILED_NOC_GROUP; + } + } + + std::vector primitives_list(max_molecule_size_, nullptr); + e_block_pack_status block_pack_status = e_block_pack_status::BLK_STATUS_UNDEFINED; + while (block_pack_status != e_block_pack_status::BLK_PASSED) { + if (!get_next_primitive_list(cluster.placement_stats, + molecule_id, + primitives_list.data(), + prepacker_)) { + VTR_LOGV(log_verbosity_ > 3, "\t\tFAILED No candidate primitives available\n"); + block_pack_status = e_block_pack_status::BLK_FAILED_FEASIBLE; + break; /* no more candidate primitives available, this molecule will not pack, return fail */ + } + + block_pack_status = e_block_pack_status::BLK_PASSED; + size_t failed_location = 0; + for (size_t i_mol = 0; i_mol < molecule.atom_block_ids.size() && block_pack_status == e_block_pack_status::BLK_PASSED; i_mol++) { + VTR_ASSERT((primitives_list[i_mol] == nullptr) == (!molecule.atom_block_ids[i_mol])); + failed_location = i_mol + 1; + AtomBlockId atom_blk_id = molecule.atom_block_ids[i_mol]; + if (!atom_blk_id.is_valid()) + continue; + // NOTE: This parent variable is only used in the recursion of this + // function. + t_pb* parent = nullptr; + block_pack_status = try_place_atom_block_rec(primitives_list[i_mol], + atom_blk_id, + cluster.pb, + &parent, + cluster_id, + atom_cluster_, + molecule_id, + cluster.router_data, + log_verbosity_, + prepacker_, + clustering_chain_info_); + } + + if (enable_pin_feasibility_filter_ && block_pack_status == e_block_pack_status::BLK_PASSED) { + // Check if pin usage is feasible for the current packing assignment + reset_lookahead_pins_used(cluster.pb); + try_update_lookahead_pins_used(cluster.pb, atom_cluster_); + if (!check_lookahead_pins_used(cluster.pb, max_external_pin_util)) { + VTR_LOGV(log_verbosity_ > 4, "\t\t\tFAILED Pin Feasibility Filter\n"); + block_pack_status = e_block_pack_status::BLK_FAILED_FEASIBLE; + } else { + VTR_LOGV(log_verbosity_ > 3, "\t\t\tPin Feasibility: Passed pin feasibility filter\n"); + } + } + + if (block_pack_status == e_block_pack_status::BLK_PASSED) { + /* + * during the clustering step of `do_clustering`, `detailed_routing_stage` is incremented at each iteration until it a cluster + * is correctly generated or `detailed_routing_stage` assumes an invalid value (E_DETAILED_ROUTE_INVALID). + * depending on its value we have different behaviors: + * - E_DETAILED_ROUTE_AT_END_ONLY: Skip routing if heuristic is to route at the end of packing complex block. + * - E_DETAILED_ROUTE_FOR_EACH_ATOM: Try to route if heuristic is to route for every atom. If the clusterer arrives at this stage, + * it means that more checks have to be performed as the previous stage failed to generate a new cluster. + * + * mode_status is a data structure containing the status of the mode selection. Its members are: + * - bool is_mode_conflict + * - bool try_expand_all_modes + * - bool expand_all_modes + * + * is_mode_conflict affects this stage. Its value determines whether the cluster failed to pack after a mode conflict issue. + * It holds a flag that is used to verify whether try_intra_lb_route ended in a mode conflict issue. + * + * Until is_mode_conflict is set to FALSE by try_intra_lb_route, the loop re-iterates. If all the available modes are exhausted + * an error will be thrown during mode conflicts checks (this to prevent infinite loops). + * + * If the value is TRUE the cluster has to be re-routed, and its internal pb_graph_nodes will have more restrict choices + * for what regards the mode that has to be selected. + * + * is_mode_conflict is initially set to TRUE, and, unless a mode conflict is found, it is set to false in `try_intra_lb_route`. + * + * try_expand_all_modes is set if the node expansion failed to find a valid routing path. The clusterer tries to find another route + * by using all the modes during node expansion. + * + * expand_all_modes is used to enable the expansion of all the nodes using all the possible modes. + */ + t_mode_selection_status mode_status; + bool is_routed = false; + bool do_detailed_routing_stage = (cluster_legalization_strategy_ == ClusterLegalizationStrategy::FULL); + if (do_detailed_routing_stage) { + do { + reset_intra_lb_route(cluster.router_data); + is_routed = try_intra_lb_route(cluster.router_data, log_verbosity_, &mode_status); + } while (do_detailed_routing_stage && mode_status.is_mode_issue()); + } + + if (do_detailed_routing_stage && !is_routed) { + /* Cannot pack */ + VTR_LOGV(log_verbosity_ > 4, "\t\t\tFAILED Detailed Routing Legality\n"); + block_pack_status = e_block_pack_status::BLK_FAILED_ROUTE; + } else { + /* Pack successful, commit + * TODO: SW Engineering note - may want to update cluster stats here too instead of doing it outside + */ + VTR_ASSERT(block_pack_status == e_block_pack_status::BLK_PASSED); + if (molecule.is_chain()) { + /* Chained molecules often take up lots of area and are important, + * if a chain is packed in, want to rename logic block to match chain name */ + AtomBlockId chain_root_blk_id = molecule.atom_block_ids[molecule.pack_pattern->root_block->block_id]; + t_pb* cur_pb = atom_pb_lookup().atom_pb(chain_root_blk_id)->parent_pb; + while (cur_pb != nullptr) { + free(cur_pb->name); + cur_pb->name = vtr::strdup(atom_ctx.netlist().block_name(chain_root_blk_id).c_str()); + cur_pb = cur_pb->parent_pb; + } + // if this molecule is part of a chain, mark the cluster as having a long chain + // molecule. Also check if it's the first molecule in the chain to be packed. + // If so, update the chain id for this chain of molecules to make sure all + // molecules will be packed to the same chain id and can reach each other using + // the chain direct links between clusters + VTR_ASSERT(molecule.chain_id.is_valid()); + const t_chain_info& prepack_chain_info = prepacker_.get_molecule_chain_info(molecule.chain_id); + if (prepack_chain_info.is_long_chain) { + cluster.placement_stats->has_long_chain = true; + const t_clustering_chain_info& clustering_chain_info = clustering_chain_info_[molecule.chain_id]; + if (clustering_chain_info.chain_id == -1) { + update_clustering_chain_info(molecule_id, primitives_list[molecule.root]); + } + } + } + + //update cluster PartitionRegion if atom with floorplanning constraints was added + if (cluster_pr_update_check) { + cluster.pr = new_cluster_pr; + VTR_LOGV(log_verbosity_ > 2, "\nUpdated PartitionRegion of cluster\n"); + } + + // Update the cluster's NoC group ID. This is cheap so it does + // not need the check like the what the PR did above. + cluster.noc_grp_id = new_cluster_noc_grp_id; + + // Insert the molecule into the cluster for bookkeeping. + cluster.molecules.push_back(molecule_id); + + for (size_t i = 0; i < molecule.atom_block_ids.size(); i++) { + AtomBlockId atom_blk_id = molecule.atom_block_ids[i]; + if (!atom_blk_id.is_valid()) + continue; + + commit_primitive(cluster.placement_stats, primitives_list[i]); + + atom_cluster_[atom_blk_id] = cluster_id; + + // Update the num child blocks in pb + const t_pb* atom_pb = atom_pb_lookup().atom_pb(atom_blk_id); + VTR_ASSERT_SAFE(atom_pb != nullptr); + t_pb* cur_pb = atom_pb->parent_pb; + while (cur_pb != nullptr) { + cur_pb->pb_stats->num_child_blocks_in_pb++; + cur_pb = cur_pb->parent_pb; + } + } + + // Update the lookahead pins used. + commit_lookahead_pins_used(cluster.pb); + } + } + + if (block_pack_status != e_block_pack_status::BLK_PASSED) { + /* Pack unsuccessful, undo inserting molecule into cluster */ + for (size_t i = 0; i < failed_location; i++) { + AtomBlockId atom_blk_id = molecule.atom_block_ids[i]; + if (atom_blk_id) { + remove_atom_from_target(cluster.router_data, atom_blk_id, atom_pb_lookup()); + } + } + for (size_t i = 0; i < failed_location; i++) { + AtomBlockId atom_blk_id = molecule.atom_block_ids[i]; + if (atom_blk_id) { + revert_place_atom_block(atom_blk_id, cluster.router_data, atom_cluster_); + } + } + reset_molecule_info(molecule_id); + + /* Packing failed, but a part of the pb tree is still allocated and pbs have their modes set. + * Before trying to pack next molecule the unused pbs need to be freed and, the most important, + * their modes reset. This task is performed by the cleanup_pb() function below. */ + cleanup_pb(cluster.pb); + } else { + VTR_LOGV(log_verbosity_ > 3, "\t\tPASSED pack molecule\n"); + } + } + + // Reset the cluster placement stats after packing a molecule. + // TODO: Not sure if this has to go here, but it makes sense to do it. + reset_tried_but_unused_cluster_placements(cluster.placement_stats); + + return block_pack_status; + } + + std::tuple + ClusterLegalizer::start_new_cluster(PackMoleculeId molecule_id, + t_logical_block_type_ptr cluster_type, + int cluster_mode) { + // Safety asserts to ensure the API is being called with valid arguments. + VTR_ASSERT_DEBUG(molecule_id.is_valid()); + VTR_ASSERT_DEBUG(cluster_type != nullptr); + VTR_ASSERT_DEBUG(cluster_mode < cluster_type->pb_graph_head->pb_type->num_modes); + // Ensure that the molecule has not already been placed. + VTR_ASSERT_SAFE(!molecule_cluster_[molecule_id].is_valid()); + // Safety asserts to ensure that the API was initialized properly. + VTR_ASSERT_DEBUG(lb_type_rr_graphs_ != nullptr); + + const AtomNetlist& atom_nlist = g_vpr_ctx.atom().netlist(); + + // Create the physical block for this cluster based on the type. + t_pb* cluster_pb = new t_pb; + cluster_pb->pb_graph_node = cluster_type->pb_graph_head; + alloc_and_load_pb_stats(cluster_pb); + cluster_pb->parent_pb = nullptr; + cluster_pb->mode = cluster_mode; + + // Allocate and load the LB router data + t_lb_router_data* router_data = alloc_and_load_router_data(&lb_type_rr_graphs_[cluster_type->index], + cluster_type); + + // Allocate and load the cluster's placement stats + t_intra_cluster_placement_stats* cluster_placement_stats = alloc_and_load_cluster_placement_stats(cluster_type, cluster_mode); + + // Create the new cluster + LegalizationCluster new_cluster; + new_cluster.pb = cluster_pb; + new_cluster.router_data = router_data; + new_cluster.pr = PartitionRegion(); + new_cluster.noc_grp_id = NocGroupId::INVALID(); + new_cluster.type = cluster_type; + new_cluster.placement_stats = cluster_placement_stats; + + // Try to pack the molecule into the new_cluster. + // When starting a new cluster, we set the external pin utilization to full + // (meaning all cluster pins are allowed to be used). + const t_ext_pin_util FULL_EXTERNAL_PIN_UTIL(1., 1.); + LegalizationClusterId new_cluster_id = LegalizationClusterId(legalization_cluster_ids_.size()); + e_block_pack_status pack_status = try_pack_molecule(molecule_id, + new_cluster, + new_cluster_id, + FULL_EXTERNAL_PIN_UTIL); + + if (pack_status == e_block_pack_status::BLK_PASSED) { + // Give the new cluster pb a name. The current convention is to name the + // cluster after the root atom of the first molecule packed into it. + const t_pack_molecule& molecule = prepacker_.get_molecule(molecule_id); + AtomBlockId root_atom = molecule.atom_block_ids[molecule.root]; + const std::string& root_atom_name = atom_nlist.block_name(root_atom); + if (new_cluster.pb->name != nullptr) + free(new_cluster.pb->name); + new_cluster.pb->name = vtr::strdup(root_atom_name.c_str()); + // Move the cluster into the vector of clusters and ids. + legalization_cluster_ids_.push_back(new_cluster_id); + legalization_clusters_.push_back(std::move(new_cluster)); + // Update the molecule to cluster map. + molecule_cluster_[molecule_id] = new_cluster_id; + } else { + // Delete the new_cluster. + free_pb(new_cluster.pb); + delete new_cluster.pb; + free_router_data(new_cluster.router_data); + free_cluster_placement_stats(new_cluster.placement_stats); + new_cluster_id = LegalizationClusterId::INVALID(); + } + + return {pack_status, new_cluster_id}; + } + + e_block_pack_status ClusterLegalizer::add_mol_to_cluster(PackMoleculeId molecule_id, + LegalizationClusterId cluster_id) { + // Safety asserts to make sure the inputs are valid. + VTR_ASSERT_SAFE(cluster_id.is_valid() && (size_t)cluster_id < legalization_clusters_.size()); + VTR_ASSERT(legalization_cluster_ids_[cluster_id].is_valid() && "Cannot add to a destroyed cluster"); + // Ensure that the molecule has not already been placed. + VTR_ASSERT(!molecule_cluster_[molecule_id].is_valid()); + // Safety asserts to ensure that the API was initialized properly. + VTR_ASSERT_DEBUG(lb_type_rr_graphs_ != nullptr); + + // Get the cluster. + LegalizationCluster& cluster = legalization_clusters_[cluster_id]; + VTR_ASSERT(cluster.router_data != nullptr && cluster.placement_stats != nullptr + && "Cannot add molecule to cleaned cluster!"); + // Set the target_external_pin_util. + t_ext_pin_util target_ext_pin_util = target_external_pin_util_.get_pin_util(cluster.type->name); + // Try to pack the molecule into the cluster. + e_block_pack_status pack_status = try_pack_molecule(molecule_id, + cluster, cluster_id, - atom_cluster_, - molecule_id, - cluster.router_data, - log_verbosity_, - prepacker_, - clustering_chain_info_); - } - - if (enable_pin_feasibility_filter_ && block_pack_status == e_block_pack_status::BLK_PASSED) { - // Check if pin usage is feasible for the current packing assignment - reset_lookahead_pins_used(cluster.pb); - try_update_lookahead_pins_used(cluster.pb, atom_cluster_); - if (!check_lookahead_pins_used(cluster.pb, max_external_pin_util)) { - VTR_LOGV(log_verbosity_ > 4, "\t\t\tFAILED Pin Feasibility Filter\n"); - block_pack_status = e_block_pack_status::BLK_FAILED_FEASIBLE; - } else { - VTR_LOGV(log_verbosity_ > 3, "\t\t\tPin Feasibility: Passed pin feasibility filter\n"); - } - } - - if (block_pack_status == e_block_pack_status::BLK_PASSED) { - /* - * during the clustering step of `do_clustering`, `detailed_routing_stage` is incremented at each iteration until it a cluster - * is correctly generated or `detailed_routing_stage` assumes an invalid value (E_DETAILED_ROUTE_INVALID). - * depending on its value we have different behaviors: - * - E_DETAILED_ROUTE_AT_END_ONLY: Skip routing if heuristic is to route at the end of packing complex block. - * - E_DETAILED_ROUTE_FOR_EACH_ATOM: Try to route if heuristic is to route for every atom. If the clusterer arrives at this stage, - * it means that more checks have to be performed as the previous stage failed to generate a new cluster. - * - * mode_status is a data structure containing the status of the mode selection. Its members are: - * - bool is_mode_conflict - * - bool try_expand_all_modes - * - bool expand_all_modes - * - * is_mode_conflict affects this stage. Its value determines whether the cluster failed to pack after a mode conflict issue. - * It holds a flag that is used to verify whether try_intra_lb_route ended in a mode conflict issue. - * - * Until is_mode_conflict is set to FALSE by try_intra_lb_route, the loop re-iterates. If all the available modes are exhausted - * an error will be thrown during mode conflicts checks (this to prevent infinite loops). - * - * If the value is TRUE the cluster has to be re-routed, and its internal pb_graph_nodes will have more restrict choices - * for what regards the mode that has to be selected. - * - * is_mode_conflict is initially set to TRUE, and, unless a mode conflict is found, it is set to false in `try_intra_lb_route`. - * - * try_expand_all_modes is set if the node expansion failed to find a valid routing path. The clusterer tries to find another route - * by using all the modes during node expansion. - * - * expand_all_modes is used to enable the expansion of all the nodes using all the possible modes. - */ - t_mode_selection_status mode_status; - bool is_routed = false; - bool do_detailed_routing_stage = (cluster_legalization_strategy_ == ClusterLegalizationStrategy::FULL); - if (do_detailed_routing_stage) { - do { - reset_intra_lb_route(cluster.router_data); - is_routed = try_intra_lb_route(cluster.router_data, log_verbosity_, &mode_status); - } while (do_detailed_routing_stage && mode_status.is_mode_issue()); - } - - if (do_detailed_routing_stage && !is_routed) { - /* Cannot pack */ - VTR_LOGV(log_verbosity_ > 4, "\t\t\tFAILED Detailed Routing Legality\n"); - block_pack_status = e_block_pack_status::BLK_FAILED_ROUTE; - } else { - /* Pack successful, commit - * TODO: SW Engineering note - may want to update cluster stats here too instead of doing it outside - */ - VTR_ASSERT(block_pack_status == e_block_pack_status::BLK_PASSED); - if (molecule.is_chain()) { - /* Chained molecules often take up lots of area and are important, - * if a chain is packed in, want to rename logic block to match chain name */ - AtomBlockId chain_root_blk_id = molecule.atom_block_ids[molecule.pack_pattern->root_block->block_id]; - t_pb* cur_pb = atom_pb_lookup().atom_pb(chain_root_blk_id)->parent_pb; - while (cur_pb != nullptr) { - free(cur_pb->name); - cur_pb->name = vtr::strdup(atom_ctx.netlist().block_name(chain_root_blk_id).c_str()); - cur_pb = cur_pb->parent_pb; - } - // if this molecule is part of a chain, mark the cluster as having a long chain - // molecule. Also check if it's the first molecule in the chain to be packed. - // If so, update the chain id for this chain of molecules to make sure all - // molecules will be packed to the same chain id and can reach each other using - // the chain direct links between clusters - VTR_ASSERT(molecule.chain_id.is_valid()); - const t_chain_info& prepack_chain_info = prepacker_.get_molecule_chain_info(molecule.chain_id); - if (prepack_chain_info.is_long_chain) { - cluster.placement_stats->has_long_chain = true; - const t_clustering_chain_info& clustering_chain_info = clustering_chain_info_[molecule.chain_id]; - if (clustering_chain_info.chain_id == -1) { - update_clustering_chain_info(molecule_id, primitives_list[molecule.root]); - } - } - } - - //update cluster PartitionRegion if atom with floorplanning constraints was added - if (cluster_pr_update_check) { - cluster.pr = new_cluster_pr; - VTR_LOGV(log_verbosity_ > 2, "\nUpdated PartitionRegion of cluster\n"); - } - - // Update the cluster's NoC group ID. This is cheap so it does - // not need the check like the what the PR did above. - cluster.noc_grp_id = new_cluster_noc_grp_id; - - // Insert the molecule into the cluster for bookkeeping. - cluster.molecules.push_back(molecule_id); - - for (size_t i = 0; i < molecule.atom_block_ids.size(); i++) { - AtomBlockId atom_blk_id = molecule.atom_block_ids[i]; - if (!atom_blk_id.is_valid()) - continue; - - commit_primitive(cluster.placement_stats, primitives_list[i]); - - atom_cluster_[atom_blk_id] = cluster_id; - - // Update the num child blocks in pb - const t_pb* atom_pb = atom_pb_lookup().atom_pb(atom_blk_id); - VTR_ASSERT_SAFE(atom_pb != nullptr); - t_pb* cur_pb = atom_pb->parent_pb; - while (cur_pb != nullptr) { - cur_pb->pb_stats->num_child_blocks_in_pb++; - cur_pb = cur_pb->parent_pb; - } - } - - // Update the lookahead pins used. - commit_lookahead_pins_used(cluster.pb); - } - } - - if (block_pack_status != e_block_pack_status::BLK_PASSED) { - /* Pack unsuccessful, undo inserting molecule into cluster */ - for (size_t i = 0; i < failed_location; i++) { - AtomBlockId atom_blk_id = molecule.atom_block_ids[i]; - if (atom_blk_id) { - remove_atom_from_target(cluster.router_data, atom_blk_id, atom_pb_lookup()); - } - } - for (size_t i = 0; i < failed_location; i++) { - AtomBlockId atom_blk_id = molecule.atom_block_ids[i]; - if (atom_blk_id) { - revert_place_atom_block(atom_blk_id, cluster.router_data, atom_cluster_); - } - } - reset_molecule_info(molecule_id); - - /* Packing failed, but a part of the pb tree is still allocated and pbs have their modes set. - * Before trying to pack next molecule the unused pbs need to be freed and, the most important, - * their modes reset. This task is performed by the cleanup_pb() function below. */ - cleanup_pb(cluster.pb); - } else { - VTR_LOGV(log_verbosity_ > 3, "\t\tPASSED pack molecule\n"); - } - } - - // Reset the cluster placement stats after packing a molecule. - // TODO: Not sure if this has to go here, but it makes sense to do it. - reset_tried_but_unused_cluster_placements(cluster.placement_stats); - - return block_pack_status; -} - -std::tuple -ClusterLegalizer::start_new_cluster(PackMoleculeId molecule_id, - t_logical_block_type_ptr cluster_type, - int cluster_mode) { - // Safety asserts to ensure the API is being called with valid arguments. - VTR_ASSERT_DEBUG(molecule_id.is_valid()); - VTR_ASSERT_DEBUG(cluster_type != nullptr); - VTR_ASSERT_DEBUG(cluster_mode < cluster_type->pb_graph_head->pb_type->num_modes); - // Ensure that the molecule has not already been placed. - VTR_ASSERT_SAFE(!molecule_cluster_[molecule_id].is_valid()); - // Safety asserts to ensure that the API was initialized properly. - VTR_ASSERT_DEBUG(lb_type_rr_graphs_ != nullptr); - - const AtomNetlist& atom_nlist = g_vpr_ctx.atom().netlist(); - - // Create the physical block for this cluster based on the type. - t_pb* cluster_pb = new t_pb; - cluster_pb->pb_graph_node = cluster_type->pb_graph_head; - alloc_and_load_pb_stats(cluster_pb); - cluster_pb->parent_pb = nullptr; - cluster_pb->mode = cluster_mode; - - // Allocate and load the LB router data - t_lb_router_data* router_data = alloc_and_load_router_data(&lb_type_rr_graphs_[cluster_type->index], - cluster_type); - - // Allocate and load the cluster's placement stats - t_intra_cluster_placement_stats* cluster_placement_stats = alloc_and_load_cluster_placement_stats(cluster_type, cluster_mode); - - // Create the new cluster - LegalizationCluster new_cluster; - new_cluster.pb = cluster_pb; - new_cluster.router_data = router_data; - new_cluster.pr = PartitionRegion(); - new_cluster.noc_grp_id = NocGroupId::INVALID(); - new_cluster.type = cluster_type; - new_cluster.placement_stats = cluster_placement_stats; - - // Try to pack the molecule into the new_cluster. - // When starting a new cluster, we set the external pin utilization to full - // (meaning all cluster pins are allowed to be used). - const t_ext_pin_util FULL_EXTERNAL_PIN_UTIL(1., 1.); - LegalizationClusterId new_cluster_id = LegalizationClusterId(legalization_cluster_ids_.size()); - e_block_pack_status pack_status = try_pack_molecule(molecule_id, - new_cluster, - new_cluster_id, - FULL_EXTERNAL_PIN_UTIL); - - if (pack_status == e_block_pack_status::BLK_PASSED) { - // Give the new cluster pb a name. The current convention is to name the - // cluster after the root atom of the first molecule packed into it. - const t_pack_molecule& molecule = prepacker_.get_molecule(molecule_id); - AtomBlockId root_atom = molecule.atom_block_ids[molecule.root]; - const std::string& root_atom_name = atom_nlist.block_name(root_atom); - if (new_cluster.pb->name != nullptr) - free(new_cluster.pb->name); - new_cluster.pb->name = vtr::strdup(root_atom_name.c_str()); - // Move the cluster into the vector of clusters and ids. - legalization_cluster_ids_.push_back(new_cluster_id); - legalization_clusters_.push_back(std::move(new_cluster)); - // Update the molecule to cluster map. - molecule_cluster_[molecule_id] = new_cluster_id; - } else { - // Delete the new_cluster. - free_pb(new_cluster.pb); - delete new_cluster.pb; - free_router_data(new_cluster.router_data); - free_cluster_placement_stats(new_cluster.placement_stats); - new_cluster_id = LegalizationClusterId::INVALID(); - } - - return {pack_status, new_cluster_id}; -} - -e_block_pack_status ClusterLegalizer::add_mol_to_cluster(PackMoleculeId molecule_id, - LegalizationClusterId cluster_id) { - // Safety asserts to make sure the inputs are valid. - VTR_ASSERT_SAFE(cluster_id.is_valid() && (size_t)cluster_id < legalization_clusters_.size()); - VTR_ASSERT(legalization_cluster_ids_[cluster_id].is_valid() && "Cannot add to a destroyed cluster"); - // Ensure that the molecule has not already been placed. - VTR_ASSERT(!molecule_cluster_[molecule_id].is_valid()); - // Safety asserts to ensure that the API was initialized properly. - VTR_ASSERT_DEBUG(lb_type_rr_graphs_ != nullptr); - - // Get the cluster. - LegalizationCluster& cluster = legalization_clusters_[cluster_id]; - VTR_ASSERT(cluster.router_data != nullptr && cluster.placement_stats != nullptr - && "Cannot add molecule to cleaned cluster!"); - // Set the target_external_pin_util. - t_ext_pin_util target_ext_pin_util = target_external_pin_util_.get_pin_util(cluster.type->name); - // Try to pack the molecule into the cluster. - e_block_pack_status pack_status = try_pack_molecule(molecule_id, - cluster, - cluster_id, - target_ext_pin_util); - - // If the packing was successful, set the molecules' cluster to this one. - if (pack_status == e_block_pack_status::BLK_PASSED) - molecule_cluster_[molecule_id] = cluster_id; - - return pack_status; -} - -void ClusterLegalizer::destroy_cluster(LegalizationClusterId cluster_id) { - // Safety asserts to make sure the inputs are valid. - VTR_ASSERT_SAFE(cluster_id.is_valid() && (size_t)cluster_id < legalization_clusters_.size()); - VTR_ASSERT(legalization_cluster_ids_[cluster_id].is_valid() && "Cannot destroy an already destroyed cluster"); - // Get the cluster. - LegalizationCluster& cluster = legalization_clusters_[cluster_id]; - // Remove all molecules from the cluster. - for (PackMoleculeId mol_id : cluster.molecules) { - VTR_ASSERT_SAFE(molecule_cluster_[mol_id] == cluster_id); - molecule_cluster_[mol_id] = LegalizationClusterId::INVALID(); - // Revert the placement of all blocks in the molecule. - const t_pack_molecule& mol = prepacker_.get_molecule(mol_id); - for (AtomBlockId atom_blk_id : mol.atom_block_ids) { - if (atom_blk_id) { - revert_place_atom_block(atom_blk_id, cluster.router_data, atom_cluster_); - } - } - reset_molecule_info(mol_id); - molecule_cluster_[mol_id] = LegalizationClusterId::INVALID(); - } - cluster.molecules.clear(); - // Free the rest of the cluster data. - // Casting things to nullptr for safety just in case someone is trying to use it. - free_pb(cluster.pb); - delete cluster.pb; - cluster.pb = nullptr; - free_router_data(cluster.router_data); - cluster.router_data = nullptr; - cluster.pr = PartitionRegion(); - free_cluster_placement_stats(cluster.placement_stats); - cluster.placement_stats = nullptr; - - // Mark the cluster as invalid. - legalization_cluster_ids_[cluster_id] = LegalizationClusterId::INVALID(); -} - -void ClusterLegalizer::compress() { - // Create a map from the old ids to the new (compressed) one. - vtr::vector_map cluster_id_map; - cluster_id_map = compress_ids(legalization_cluster_ids_); - // Update all cluster values. - legalization_cluster_ids_ = clean_and_reorder_ids(cluster_id_map); - legalization_clusters_ = clean_and_reorder_values(legalization_clusters_, cluster_id_map); - // Update the reverse lookups. - for (PackMoleculeId mol_id : prepacker_.molecules()) { - LegalizationClusterId old_cluster_id = molecule_cluster_[mol_id]; - if (!old_cluster_id.is_valid()) - continue; - molecule_cluster_[mol_id] = cluster_id_map[old_cluster_id]; - } - for (size_t i = 0; i < atom_cluster_.size(); i++) { - AtomBlockId atom_blk_id = AtomBlockId(i); - LegalizationClusterId old_cluster_id = atom_cluster_[atom_blk_id]; - if (!old_cluster_id.is_valid()) - continue; - atom_cluster_[atom_blk_id] = cluster_id_map[old_cluster_id]; - } - // Shrink everything to fit - legalization_cluster_ids_.shrink_to_fit(); - legalization_clusters_.shrink_to_fit(); - atom_cluster_.shrink_to_fit(); -} - -void ClusterLegalizer::clean_cluster(LegalizationClusterId cluster_id) { - // Safety asserts to make sure the inputs are valid. - VTR_ASSERT_SAFE(cluster_id.is_valid() && (size_t)cluster_id < legalization_clusters_.size()); - // Get the cluster. - LegalizationCluster& cluster = legalization_clusters_[cluster_id]; - VTR_ASSERT(cluster.router_data != nullptr && cluster.placement_stats != nullptr - && "Should not clean an already cleaned cluster!"); - // Free the pb stats. - free_pb_stats_recursive(cluster.pb); - // Load the pb_route so we can free the cluster router data. - // The pb_route is used when creating a netlist from the legalized clusters. - std::vector* saved_lb_nets = cluster.router_data->saved_lb_nets; - t_pb_graph_node* pb_graph_node = cluster.pb->pb_graph_node; - cluster.pb->pb_route = alloc_and_load_pb_route(saved_lb_nets, pb_graph_node); - // Free the router data. - free_router_data(cluster.router_data); - cluster.router_data = nullptr; - // Free the cluster placement stats. - free_cluster_placement_stats(cluster.placement_stats); - cluster.placement_stats = nullptr; -} - -// TODO: This is fine for the current implementation of the legalizer. But if -// more complex strategies are added, this will need to be updated to -// check more than just routing (such as PR and NoC groups). -bool ClusterLegalizer::check_cluster_legality(LegalizationClusterId cluster_id) { - // Safety asserts to make sure the inputs are valid. - VTR_ASSERT_SAFE(cluster_id.is_valid() && (size_t)cluster_id < legalization_clusters_.size()); - // To check if a cluster is fully legal, try to perform an intra logic block - // route on the cluster. If it succeeds, the cluster is fully legal. - t_mode_selection_status mode_status; - LegalizationCluster& cluster = legalization_clusters_[cluster_id]; - return try_intra_lb_route(cluster.router_data, log_verbosity_, &mode_status); -} - -ClusterLegalizer::ClusterLegalizer(const AtomNetlist& atom_netlist, - const Prepacker& prepacker, - std::vector* lb_type_rr_graphs, - const std::vector& target_external_pin_util_str, - const t_pack_high_fanout_thresholds& high_fanout_thresholds, - ClusterLegalizationStrategy cluster_legalization_strategy, - bool enable_pin_feasibility_filter, - int log_verbosity) : prepacker_(prepacker) - { - // Verify that the inputs are valid. - VTR_ASSERT_SAFE(lb_type_rr_graphs != nullptr); - - // Get the target external pin utilization - // NOTE: Be careful with this constructor, it may throw a VPR_FATAL_ERROR. - target_external_pin_util_ = t_ext_pin_util_targets(target_external_pin_util_str); - - // Resize the molecule_cluster lookup to make the accesses much cheaper. - molecule_cluster_.resize(prepacker_.molecules().size(), LegalizationClusterId::INVALID()); - // Resize the atom_cluster lookup to make the accesses much cheaper. - atom_cluster_.resize(atom_netlist.blocks().size(), LegalizationClusterId::INVALID()); - // Default the clustering chain info for each chain. - clustering_chain_info_.resize(prepacker_.get_num_molecule_chains()); - // Pre-compute the max size of any molecule. - max_molecule_size_ = prepacker.get_max_molecule_size(); - // Get a reference to the rr graphs. - lb_type_rr_graphs_ = lb_type_rr_graphs; - // Find all NoC router atoms. - std::vector noc_atoms = find_noc_router_atoms(atom_netlist); - update_noc_reachability_partitions(noc_atoms, - atom_netlist, - high_fanout_thresholds, - atom_noc_grp_id_); - // Copy the options passed by the user - cluster_legalization_strategy_ = cluster_legalization_strategy; - enable_pin_feasibility_filter_ = enable_pin_feasibility_filter; - log_verbosity_ = log_verbosity; - atom_pb_lookup_ = AtomPBLookUp(g_vpr_ctx.atom().lookup().atom_to_pb()); -} - -void ClusterLegalizer::reset() { - // Destroy all of the clusters and compress. - for (LegalizationClusterId cluster_id : legalization_cluster_ids_) { - if (!cluster_id.is_valid()) - continue; - destroy_cluster(cluster_id); - } - compress(); -} - -void ClusterLegalizer::verify() { - std::unordered_set atoms_checked; - auto& atom_ctx = g_vpr_ctx.atom(); - - if (clusters().size() == 0) { - VTR_LOG_WARN("Packing produced no clustered blocks"); - } - - /* - * Check that each atom block connects to one physical primitive and that the primitive links up to the parent clb - */ - for (auto blk_id : atom_ctx.netlist().blocks()) { - //Each atom should be part of a pb - const t_pb* atom_pb = atom_pb_lookup().atom_pb(blk_id); - if (!atom_pb) { - VPR_FATAL_ERROR(VPR_ERROR_PACK, - "Atom block %s is not mapped to a pb\n", - atom_ctx.netlist().block_name(blk_id).c_str()); - } - - //Check the reverse mapping is consistent - if (atom_pb_lookup().pb_atom(atom_pb) != blk_id) { - VPR_FATAL_ERROR(VPR_ERROR_PACK, - "pb %s does not contain atom block %s but atom block %s maps to pb.\n", - atom_pb->name, - atom_ctx.netlist().block_name(blk_id).c_str(), - atom_ctx.netlist().block_name(blk_id).c_str()); - } - - VTR_ASSERT(atom_ctx.netlist().block_name(blk_id) == atom_pb->name); - - const t_pb* cur_pb = atom_pb; - while (cur_pb->parent_pb) { - cur_pb = cur_pb->parent_pb; - VTR_ASSERT(cur_pb->name); - } - - LegalizationClusterId cluster_id = get_atom_cluster(blk_id); - if (cluster_id == LegalizationClusterId::INVALID()) { - VPR_FATAL_ERROR(VPR_ERROR_PACK, - "Atom %s is not mapped to a CLB\n", - atom_ctx.netlist().block_name(blk_id).c_str()); - } - - if (cur_pb != get_cluster_pb(cluster_id)) { - VPR_FATAL_ERROR(VPR_ERROR_PACK, - "CLB %s does not match CLB contained by pb %s.\n", - cur_pb->name, atom_pb->name); - } - } - - /* Check that I do not have spurious links in children pbs */ - for (LegalizationClusterId cluster_id : clusters()) { - if (!cluster_id.is_valid()) - continue; - check_cluster_atom_blocks(get_cluster_pb(cluster_id), - atoms_checked); - } - - for (auto blk_id : atom_ctx.netlist().blocks()) { - if (!atoms_checked.count(blk_id)) { - VPR_FATAL_ERROR(VPR_ERROR_PACK, - "Atom block %s not found in any cluster.\n", - atom_ctx.netlist().block_name(blk_id).c_str()); - } - } -} - -bool ClusterLegalizer::is_molecule_compatible(PackMoleculeId molecule_id, - LegalizationClusterId cluster_id) const { - VTR_ASSERT_SAFE(molecule_id.is_valid()); - VTR_ASSERT_SAFE(cluster_id.is_valid() && (size_t)cluster_id < legalization_clusters_.size()); - // Go through each atom in the molecule and check if there exists a free - // primitive for that atom block. - // TODO: This should probably also check if there are enough free primitives - // to support the given molecule. For example, a molecule of two FFs, - // but the cluster only has one free FF. This was something that Jason - // Luu was debating. Checking if placement exists for full molecule - // would be more robust, but checking individual atoms is faster. - const LegalizationCluster& cluster = legalization_clusters_[cluster_id]; - - const t_pack_molecule& molecule = prepacker_.get_molecule(molecule_id); - for (AtomBlockId atom_blk_id : molecule.atom_block_ids) { - // FIXME: Why is it possible that molecules contain invalid block IDs? - // This should be fixed! - if (!atom_blk_id.is_valid()) - continue; - // FIXME: This assert does not make sense. Can still check this even - // if the atom was clustered. - VTR_ASSERT(!is_atom_clustered(atom_blk_id)); - if (!exists_free_primitive_for_atom_block(cluster.placement_stats, - atom_blk_id)) { - return false; - } - } - // If every atom in the molecule has a free primitive it could theoretically - // be placed in, then it is compatible. - // TODO: Maybe add some more quick checks to save time, such as PR or NoC - // groups. - return true; -} - -size_t ClusterLegalizer::get_num_cluster_inputs_available( - LegalizationClusterId cluster_id) const { - VTR_ASSERT_SAFE(cluster_id.is_valid() && (size_t)cluster_id < legalization_clusters_.size()); - const LegalizationCluster& cluster = legalization_clusters_[cluster_id]; - - // Count the number of inputs available per pin class. - size_t inputs_avail = 0; - for (int i = 0; i < cluster.pb->pb_graph_node->num_input_pin_class; i++) { - inputs_avail += cluster.pb->pb_stats->input_pins_used[i].size(); - } - - return inputs_avail; -} - -void ClusterLegalizer::finalize() { - for (LegalizationClusterId cluster_id : legalization_cluster_ids_) { - if (!cluster_id.is_valid()) - continue; - // If the cluster has not already been cleaned, clean it. This will - // generate the pb_route necessary for generating a clustered netlist. - const LegalizationCluster& cluster = legalization_clusters_[cluster_id]; - if (cluster.router_data != nullptr) - clean_cluster(cluster_id); - } -} - -void ClusterLegalizer::free_pb(t_pb* pb) { - if (pb == nullptr) { - return; - } - - const t_pb_type* pb_type; - int i, j, mode; - - pb_type = pb->pb_graph_node->pb_type; - - if (pb->name) { - free(pb->name); - pb->name = nullptr; - } - - if (pb_type->blif_model == nullptr) { - mode = pb->mode; - for (i = 0; i < pb_type->modes[mode].num_pb_type_children && pb->child_pbs != nullptr; i++) { - for (j = 0; j < pb_type->modes[mode].pb_type_children[i].num_pb && pb->child_pbs[i] != nullptr; j++) { - if (pb->child_pbs[i][j].name != nullptr || pb->child_pbs[i][j].child_pbs != nullptr) { - free_pb(&pb->child_pbs[i][j]); - } - } - if (pb->child_pbs[i]) { - //Free children (num_pb) - delete[] pb->child_pbs[i]; - } - } - if (pb->child_pbs) { - //Free child pointers (modes) - delete[] pb->child_pbs; - } - - pb->child_pbs = nullptr; - - } else { - /* Primitive */ - auto& atom_ctx = g_vpr_ctx.mutable_atom(); - auto blk_id = atom_pb_lookup().pb_atom(pb); - if (blk_id) { - //Update atom netlist mapping - atom_ctx.mutable_lookup().set_atom_clb(blk_id, ClusterBlockId::INVALID()); - atom_pb_lookup_.set_atom_pb(blk_id, nullptr); - } - atom_pb_lookup_.set_atom_pb(AtomBlockId::INVALID(), pb); - } - - if (pb && pb->pb_stats != nullptr) { - delete pb->pb_stats; - pb->pb_stats = nullptr; - } -} - -ClusterLegalizer::~ClusterLegalizer() { - // Destroy all clusters (no need to compress). - for (LegalizationClusterId cluster_id : legalization_cluster_ids_) { - if (!cluster_id.is_valid()) - continue; - destroy_cluster(cluster_id); - } -} - -AtomPBLookUp::AtomPBLookUp(const vtr::bimap& atom_to_pb) { - atom_to_pb_ = atom_to_pb; -} - -const t_pb* AtomPBLookUp::atom_pb(const AtomBlockId blk_id) const { - auto iter = atom_to_pb_.find(blk_id); - if (iter == atom_to_pb_.end()) { - //Not found - return nullptr; - } - return iter->second; -} - -AtomBlockId AtomPBLookUp::pb_atom(const t_pb* pb) const { - auto iter = atom_to_pb_.find(pb); - if (iter == atom_to_pb_.inverse_end()) { - //Not found - return AtomBlockId::INVALID(); - } - return iter->second; -} - -const t_pb_graph_node* AtomPBLookUp::atom_pb_graph_node(const AtomBlockId blk_id) const { - const t_pb* pb = atom_pb(blk_id); - if (pb) { - //Found - return pb->pb_graph_node; - } - return nullptr; -} - -void AtomPBLookUp::set_atom_pb(const AtomBlockId blk_id, const t_pb* pb) { - //If either of blk_id or pb are not valid, - //remove any mapping - - if (!blk_id && pb) { - //Remove - atom_to_pb_.erase(pb); - } else if (blk_id && !pb) { - //Remove - atom_to_pb_.erase(blk_id); - } else if (blk_id && pb) { - //If both are valid store the mapping - atom_to_pb_.update(blk_id, pb); - } -} + target_ext_pin_util); + + // If the packing was successful, set the molecules' cluster to this one. + if (pack_status == e_block_pack_status::BLK_PASSED) + molecule_cluster_[molecule_id] = cluster_id; + + return pack_status; + } + + void ClusterLegalizer::destroy_cluster(LegalizationClusterId cluster_id) { + // Safety asserts to make sure the inputs are valid. + VTR_ASSERT_SAFE(cluster_id.is_valid() && (size_t)cluster_id < legalization_clusters_.size()); + VTR_ASSERT(legalization_cluster_ids_[cluster_id].is_valid() && "Cannot destroy an already destroyed cluster"); + // Get the cluster. + LegalizationCluster& cluster = legalization_clusters_[cluster_id]; + // Remove all molecules from the cluster. + for (PackMoleculeId mol_id : cluster.molecules) { + VTR_ASSERT_SAFE(molecule_cluster_[mol_id] == cluster_id); + molecule_cluster_[mol_id] = LegalizationClusterId::INVALID(); + // Revert the placement of all blocks in the molecule. + const t_pack_molecule& mol = prepacker_.get_molecule(mol_id); + for (AtomBlockId atom_blk_id : mol.atom_block_ids) { + if (atom_blk_id) { + revert_place_atom_block(atom_blk_id, cluster.router_data, atom_cluster_); + } + } + reset_molecule_info(mol_id); + molecule_cluster_[mol_id] = LegalizationClusterId::INVALID(); + } + cluster.molecules.clear(); + // Free the rest of the cluster data. + // Casting things to nullptr for safety just in case someone is trying to use it. + free_pb(cluster.pb); + delete cluster.pb; + cluster.pb = nullptr; + free_router_data(cluster.router_data); + cluster.router_data = nullptr; + cluster.pr = PartitionRegion(); + free_cluster_placement_stats(cluster.placement_stats); + cluster.placement_stats = nullptr; + + // Mark the cluster as invalid. + legalization_cluster_ids_[cluster_id] = LegalizationClusterId::INVALID(); + } + + void ClusterLegalizer::compress() { + // Create a map from the old ids to the new (compressed) one. + vtr::vector_map cluster_id_map; + cluster_id_map = compress_ids(legalization_cluster_ids_); + // Update all cluster values. + legalization_cluster_ids_ = clean_and_reorder_ids(cluster_id_map); + legalization_clusters_ = clean_and_reorder_values(legalization_clusters_, cluster_id_map); + // Update the reverse lookups. + for (PackMoleculeId mol_id : prepacker_.molecules()) { + LegalizationClusterId old_cluster_id = molecule_cluster_[mol_id]; + if (!old_cluster_id.is_valid()) + continue; + molecule_cluster_[mol_id] = cluster_id_map[old_cluster_id]; + } + for (size_t i = 0; i < atom_cluster_.size(); i++) { + AtomBlockId atom_blk_id = AtomBlockId(i); + LegalizationClusterId old_cluster_id = atom_cluster_[atom_blk_id]; + if (!old_cluster_id.is_valid()) + continue; + atom_cluster_[atom_blk_id] = cluster_id_map[old_cluster_id]; + } + // Shrink everything to fit + legalization_cluster_ids_.shrink_to_fit(); + legalization_clusters_.shrink_to_fit(); + atom_cluster_.shrink_to_fit(); + } + + void ClusterLegalizer::clean_cluster(LegalizationClusterId cluster_id) { + // Safety asserts to make sure the inputs are valid. + VTR_ASSERT_SAFE(cluster_id.is_valid() && (size_t)cluster_id < legalization_clusters_.size()); + // Get the cluster. + LegalizationCluster& cluster = legalization_clusters_[cluster_id]; + VTR_ASSERT(cluster.router_data != nullptr && cluster.placement_stats != nullptr + && "Should not clean an already cleaned cluster!"); + // Free the pb stats. + free_pb_stats_recursive(cluster.pb); + // Load the pb_route so we can free the cluster router data. + // The pb_route is used when creating a netlist from the legalized clusters. + std::vector* saved_lb_nets = cluster.router_data->saved_lb_nets; + t_pb_graph_node* pb_graph_node = cluster.pb->pb_graph_node; + cluster.pb->pb_route = alloc_and_load_pb_route(saved_lb_nets, pb_graph_node); + // Free the router data. + free_router_data(cluster.router_data); + cluster.router_data = nullptr; + // Free the cluster placement stats. + free_cluster_placement_stats(cluster.placement_stats); + cluster.placement_stats = nullptr; + } + + // TODO: This is fine for the current implementation of the legalizer. But if + // more complex strategies are added, this will need to be updated to + // check more than just routing (such as PR and NoC groups). + bool ClusterLegalizer::check_cluster_legality(LegalizationClusterId cluster_id) { + // Safety asserts to make sure the inputs are valid. + VTR_ASSERT_SAFE(cluster_id.is_valid() && (size_t)cluster_id < legalization_clusters_.size()); + // To check if a cluster is fully legal, try to perform an intra logic block + // route on the cluster. If it succeeds, the cluster is fully legal. + t_mode_selection_status mode_status; + LegalizationCluster& cluster = legalization_clusters_[cluster_id]; + return try_intra_lb_route(cluster.router_data, log_verbosity_, &mode_status); + } + + ClusterLegalizer::ClusterLegalizer(const AtomNetlist& atom_netlist, + const Prepacker& prepacker, + std::vector* lb_type_rr_graphs, + const std::vector& target_external_pin_util_str, + const t_pack_high_fanout_thresholds& high_fanout_thresholds, + ClusterLegalizationStrategy cluster_legalization_strategy, + bool enable_pin_feasibility_filter, + int log_verbosity) : prepacker_(prepacker) + { + // Verify that the inputs are valid. + VTR_ASSERT_SAFE(lb_type_rr_graphs != nullptr); + + // Get the target external pin utilization + // NOTE: Be careful with this constructor, it may throw a VPR_FATAL_ERROR. + target_external_pin_util_ = t_ext_pin_util_targets(target_external_pin_util_str); + + // Resize the molecule_cluster lookup to make the accesses much cheaper. + molecule_cluster_.resize(prepacker_.molecules().size(), LegalizationClusterId::INVALID()); + // Resize the atom_cluster lookup to make the accesses much cheaper. + atom_cluster_.resize(atom_netlist.blocks().size(), LegalizationClusterId::INVALID()); + // Default the clustering chain info for each chain. + clustering_chain_info_.resize(prepacker_.get_num_molecule_chains()); + // Pre-compute the max size of any molecule. + max_molecule_size_ = prepacker.get_max_molecule_size(); + // Get a reference to the rr graphs. + lb_type_rr_graphs_ = lb_type_rr_graphs; + // Find all NoC router atoms. + std::vector noc_atoms = find_noc_router_atoms(atom_netlist); + update_noc_reachability_partitions(noc_atoms, + atom_netlist, + high_fanout_thresholds, + atom_noc_grp_id_); + // Copy the options passed by the user + cluster_legalization_strategy_ = cluster_legalization_strategy; + enable_pin_feasibility_filter_ = enable_pin_feasibility_filter; + log_verbosity_ = log_verbosity; + atom_pb_lookup_ = AtomPBLookUp(g_vpr_ctx.atom().lookup().atom_to_pb()); + } + + void ClusterLegalizer::reset() { + // Destroy all of the clusters and compress. + for (LegalizationClusterId cluster_id : legalization_cluster_ids_) { + if (!cluster_id.is_valid()) + continue; + destroy_cluster(cluster_id); + } + compress(); + } + + void ClusterLegalizer::verify() { + std::unordered_set atoms_checked; + auto& atom_ctx = g_vpr_ctx.atom(); + + if (clusters().size() == 0) { + VTR_LOG_WARN("Packing produced no clustered blocks"); + } + + /* + * Check that each atom block connects to one physical primitive and that the primitive links up to the parent clb + */ + for (auto blk_id : atom_ctx.netlist().blocks()) { + //Each atom should be part of a pb + const t_pb* atom_pb = atom_pb_lookup().atom_pb(blk_id); + if (!atom_pb) { + VPR_FATAL_ERROR(VPR_ERROR_PACK, + "Atom block %s is not mapped to a pb\n", + atom_ctx.netlist().block_name(blk_id).c_str()); + } + + //Check the reverse mapping is consistent + if (atom_pb_lookup().pb_atom(atom_pb) != blk_id) { + VPR_FATAL_ERROR(VPR_ERROR_PACK, + "pb %s does not contain atom block %s but atom block %s maps to pb.\n", + atom_pb->name, + atom_ctx.netlist().block_name(blk_id).c_str(), + atom_ctx.netlist().block_name(blk_id).c_str()); + } + + VTR_ASSERT(atom_ctx.netlist().block_name(blk_id) == atom_pb->name); + + const t_pb* cur_pb = atom_pb; + while (cur_pb->parent_pb) { + cur_pb = cur_pb->parent_pb; + VTR_ASSERT(cur_pb->name); + } + + LegalizationClusterId cluster_id = get_atom_cluster(blk_id); + if (cluster_id == LegalizationClusterId::INVALID()) { + VPR_FATAL_ERROR(VPR_ERROR_PACK, + "Atom %s is not mapped to a CLB\n", + atom_ctx.netlist().block_name(blk_id).c_str()); + } + + if (cur_pb != get_cluster_pb(cluster_id)) { + VPR_FATAL_ERROR(VPR_ERROR_PACK, + "CLB %s does not match CLB contained by pb %s.\n", + cur_pb->name, atom_pb->name); + } + } + + /* Check that I do not have spurious links in children pbs */ + for (LegalizationClusterId cluster_id : clusters()) { + if (!cluster_id.is_valid()) + continue; + check_cluster_atom_blocks(get_cluster_pb(cluster_id), + atoms_checked); + } + + for (auto blk_id : atom_ctx.netlist().blocks()) { + if (!atoms_checked.count(blk_id)) { + VPR_FATAL_ERROR(VPR_ERROR_PACK, + "Atom block %s not found in any cluster.\n", + atom_ctx.netlist().block_name(blk_id).c_str()); + } + } + } + + bool ClusterLegalizer::is_molecule_compatible(PackMoleculeId molecule_id, + LegalizationClusterId cluster_id) const { + VTR_ASSERT_SAFE(molecule_id.is_valid()); + VTR_ASSERT_SAFE(cluster_id.is_valid() && (size_t)cluster_id < legalization_clusters_.size()); + // Go through each atom in the molecule and check if there exists a free + // primitive for that atom block. + // TODO: This should probably also check if there are enough free primitives + // to support the given molecule. For example, a molecule of two FFs, + // but the cluster only has one free FF. This was something that Jason + // Luu was debating. Checking if placement exists for full molecule + // would be more robust, but checking individual atoms is faster. + const LegalizationCluster& cluster = legalization_clusters_[cluster_id]; + + const t_pack_molecule& molecule = prepacker_.get_molecule(molecule_id); + for (AtomBlockId atom_blk_id : molecule.atom_block_ids) { + // FIXME: Why is it possible that molecules contain invalid block IDs? + // This should be fixed! + if (!atom_blk_id.is_valid()) + continue; + // FIXME: This assert does not make sense. Can still check this even + // if the atom was clustered. + VTR_ASSERT(!is_atom_clustered(atom_blk_id)); + if (!exists_free_primitive_for_atom_block(cluster.placement_stats, + atom_blk_id)) { + return false; + } + } + // If every atom in the molecule has a free primitive it could theoretically + // be placed in, then it is compatible. + // TODO: Maybe add some more quick checks to save time, such as PR or NoC + // groups. + return true; + } + + size_t ClusterLegalizer::get_num_cluster_inputs_available( + LegalizationClusterId cluster_id) const { + VTR_ASSERT_SAFE(cluster_id.is_valid() && (size_t)cluster_id < legalization_clusters_.size()); + const LegalizationCluster& cluster = legalization_clusters_[cluster_id]; + + // Count the number of inputs available per pin class. + size_t inputs_avail = 0; + for (int i = 0; i < cluster.pb->pb_graph_node->num_input_pin_class; i++) { + inputs_avail += cluster.pb->pb_stats->input_pins_used[i].size(); + } + + return inputs_avail; + } + + void ClusterLegalizer::finalize() { + for (LegalizationClusterId cluster_id : legalization_cluster_ids_) { + if (!cluster_id.is_valid()) + continue; + // If the cluster has not already been cleaned, clean it. This will + // generate the pb_route necessary for generating a clustered netlist. + const LegalizationCluster& cluster = legalization_clusters_[cluster_id]; + if (cluster.router_data != nullptr) + clean_cluster(cluster_id); + } + } + + void ClusterLegalizer::free_pb(t_pb* pb) { + if (pb == nullptr) { + return; + } + + const t_pb_type* pb_type; + int i, j, mode; + + pb_type = pb->pb_graph_node->pb_type; + + if (pb->name) { + free(pb->name); + pb->name = nullptr; + } + + if (pb_type->blif_model == nullptr) { + mode = pb->mode; + for (i = 0; i < pb_type->modes[mode].num_pb_type_children && pb->child_pbs != nullptr; i++) { + for (j = 0; j < pb_type->modes[mode].pb_type_children[i].num_pb && pb->child_pbs[i] != nullptr; j++) { + if (pb->child_pbs[i][j].name != nullptr || pb->child_pbs[i][j].child_pbs != nullptr) { + free_pb(&pb->child_pbs[i][j]); + } + } + if (pb->child_pbs[i]) { + //Free children (num_pb) + delete[] pb->child_pbs[i]; + } + } + if (pb->child_pbs) { + //Free child pointers (modes) + delete[] pb->child_pbs; + } + + pb->child_pbs = nullptr; + + } else { + /* Primitive */ + auto& atom_ctx = g_vpr_ctx.mutable_atom(); + auto blk_id = atom_pb_lookup().pb_atom(pb); + if (blk_id) { + //Update atom netlist mapping + atom_ctx.mutable_lookup().set_atom_clb(blk_id, ClusterBlockId::INVALID()); + atom_pb_lookup_.set_atom_pb(blk_id, nullptr); + } + atom_pb_lookup_.set_atom_pb(AtomBlockId::INVALID(), pb); + } + + if (pb && pb->pb_stats != nullptr) { + delete pb->pb_stats; + pb->pb_stats = nullptr; + } + } + + ClusterLegalizer::~ClusterLegalizer() { + // Destroy all clusters (no need to compress). + for (LegalizationClusterId cluster_id : legalization_cluster_ids_) { + if (!cluster_id.is_valid()) + continue; + destroy_cluster(cluster_id); + } + } + + AtomPBLookUp::AtomPBLookUp(const vtr::bimap& atom_to_pb) { + atom_to_pb_ = atom_to_pb; + } + + const t_pb* AtomPBLookUp::atom_pb(const AtomBlockId blk_id) const { + auto iter = atom_to_pb_.find(blk_id); + if (iter == atom_to_pb_.end()) { + //Not found + return nullptr; + } + return iter->second; + } + + AtomBlockId AtomPBLookUp::pb_atom(const t_pb* pb) const { + auto iter = atom_to_pb_.find(pb); + if (iter == atom_to_pb_.inverse_end()) { + //Not found + return AtomBlockId::INVALID(); + } + return iter->second; + } + + const t_pb_graph_node* AtomPBLookUp::atom_pb_graph_node(const AtomBlockId blk_id) const { + const t_pb* pb = atom_pb(blk_id); + if (pb) { + //Found + return pb->pb_graph_node; + } + return nullptr; + } + + void AtomPBLookUp::set_atom_pb(const AtomBlockId blk_id, const t_pb* pb) { + //If either of blk_id or pb are not valid, + //remove any mapping + + if (!blk_id && pb) { + //Remove + atom_to_pb_.erase(pb); + } else if (blk_id && !pb) { + //Remove + atom_to_pb_.erase(blk_id); + } else if (blk_id && pb) { + //If both are valid store the mapping + atom_to_pb_.update(blk_id, pb); + } + } + \ No newline at end of file From 14cff91a05e19edc659202bf79892fd36f2fa016 Mon Sep 17 00:00:00 2001 From: Amir Poolad Date: Thu, 13 Mar 2025 15:12:28 -0400 Subject: [PATCH 03/14] Remove code duplication between global context and AtomPBBimap --- utils/fasm/src/fasm.cpp | 8 +- vpr/src/base/atom_lookup.cpp | 49 - vpr/src/base/atom_lookup.h | 33 +- vpr/src/base/load_flat_place.cpp | 2 +- vpr/src/base/netlist_writer.cpp | 14 +- vpr/src/base/read_netlist.cpp | 16 +- vpr/src/draw/draw.cpp | 2 +- vpr/src/draw/draw_floorplanning.cpp | 6 +- vpr/src/draw/intra_logic_block.cpp | 16 +- vpr/src/pack/atom_pb_bimap.cpp | 48 + .../{atom_pb_lookup.h => atom_pb_bimap.h} | 8 +- vpr/src/pack/cluster_legalizer.cpp | 3503 +++++++++-------- vpr/src/pack/cluster_legalizer.h | 216 +- vpr/src/pack/cluster_router.cpp | 18 +- vpr/src/pack/cluster_router.h | 4 +- vpr/src/pack/pack.cpp | 2 +- vpr/src/pack/post_routing_pb_pin_fixup.cpp | 2 +- .../pack/sync_netlists_to_routing_flat.cpp | 2 +- vpr/src/pack/verify_clustering.cpp | 6 +- vpr/src/power/power.cpp | 2 +- vpr/src/route/overuse_report.cpp | 4 +- vpr/src/timing/VprTimingGraphResolver.cpp | 4 +- vpr/src/util/vpr_utils.cpp | 81 +- vpr/src/util/vpr_utils.h | 5 +- 24 files changed, 1968 insertions(+), 2083 deletions(-) create mode 100644 vpr/src/pack/atom_pb_bimap.cpp rename vpr/src/pack/{atom_pb_lookup.h => atom_pb_bimap.h} (85%) diff --git a/utils/fasm/src/fasm.cpp b/utils/fasm/src/fasm.cpp index 504e15dbfa8..2785c1149e8 100644 --- a/utils/fasm/src/fasm.cpp +++ b/utils/fasm/src/fasm.cpp @@ -343,9 +343,9 @@ static AtomNetId _find_atom_input_logical_net(const t_pb* atom, const t_pb_route static LogicVec lut_outputs(const t_pb* atom_pb, size_t num_inputs, const t_pb_routes &pb_route) { auto& atom_ctx = g_vpr_ctx.atom(); - AtomBlockId block_id = atom_ctx.lookup().pb_atom(atom_pb); + AtomBlockId block_id = atom_ctx.lookup().atom_pb_bimap().pb_atom(atom_pb); const auto& truth_table = atom_ctx.netlist().block_truth_table(block_id); - auto ports = atom_ctx.netlist().block_input_ports(atom_ctx.lookup().pb_atom(atom_pb)); + auto ports = atom_ctx.netlist().block_input_ports(atom_ctx.lookup().atom_pb_bimap().pb_atom(atom_pb)); const t_pb_graph_node* gnode = atom_pb->pb_graph_node; @@ -537,7 +537,7 @@ static const t_pb_routes &find_pb_route(const t_pb* pb) { void FasmWriterVisitor::check_for_param(const t_pb *atom) { auto& atom_ctx = g_vpr_ctx.atom(); - auto atom_blk_id = atom_ctx.lookup().pb_atom(atom); + auto atom_blk_id = atom_ctx.lookup().atom_pb_bimap().pb_atom(atom); if (atom_blk_id == AtomBlockId::INVALID()) { return; } @@ -592,7 +592,7 @@ void FasmWriterVisitor::check_for_param(const t_pb *atom) { void FasmWriterVisitor::check_for_lut(const t_pb* atom) { auto& atom_ctx = g_vpr_ctx.atom(); - auto atom_blk_id = atom_ctx.lookup().pb_atom(atom); + auto atom_blk_id = atom_ctx.lookup().atom_pb_bimap().pb_atom(atom); if (atom_blk_id == AtomBlockId::INVALID()) { return; } diff --git a/vpr/src/base/atom_lookup.cpp b/vpr/src/base/atom_lookup.cpp index 2d95e9d3e79..bfddc6ab5c1 100644 --- a/vpr/src/base/atom_lookup.cpp +++ b/vpr/src/base/atom_lookup.cpp @@ -4,55 +4,6 @@ #include "vtr_optional.h" #include "atom_lookup.h" -/* - * PB - */ -const t_pb* AtomLookup::atom_pb(const AtomBlockId blk_id) const { - VTR_ASSERT(!lock_atom_pb); - auto iter = atom_to_pb_.find(blk_id); - if (iter == atom_to_pb_.end()) { - //Not found - return nullptr; - } - return iter->second; -} - -AtomBlockId AtomLookup::pb_atom(const t_pb* pb) const { - VTR_ASSERT(!lock_atom_pb); - auto iter = atom_to_pb_.find(pb); - if (iter == atom_to_pb_.inverse_end()) { - //Not found - return AtomBlockId::INVALID(); - } - return iter->second; -} - -const t_pb_graph_node* AtomLookup::atom_pb_graph_node(const AtomBlockId blk_id) const { - VTR_ASSERT(!lock_atom_pb); - const t_pb* pb = atom_pb(blk_id); - if (pb) { - //Found - return pb->pb_graph_node; - } - return nullptr; -} - -void AtomLookup::set_atom_pb(const AtomBlockId blk_id, const t_pb* pb) { - VTR_ASSERT(!lock_atom_pb); - //If either of blk_id or pb are not valid, - //remove any mapping - - if (!blk_id && pb) { - //Remove - atom_to_pb_.erase(pb); - } else if (blk_id && !pb) { - //Remove - atom_to_pb_.erase(blk_id); - } else if (blk_id && pb) { - //If both are valid store the mapping - atom_to_pb_.update(blk_id, pb); - } -} /* * PB Pins diff --git a/vpr/src/base/atom_lookup.h b/vpr/src/base/atom_lookup.h index ba33be7e383..5e2da54c367 100644 --- a/vpr/src/base/atom_lookup.h +++ b/vpr/src/base/atom_lookup.h @@ -15,6 +15,8 @@ #include "vtr_optional.h" +#include "atom_pb_bimap.h" + /** * @brief The AtomLookup class describes the mapping between components in the AtomNetlist * and other netlists/entities (i.e. atom block <-> t_pb, atom block <-> clb) @@ -32,24 +34,8 @@ class AtomLookup { * PBs */ - /** - * @brief Returns the leaf pb associated with the atom blk_id - * @note this is the lowest level pb which corresponds directly to the atom block - */ - const t_pb* atom_pb(const AtomBlockId blk_id) const; - - ///@brief Returns the atom block id associated with pb - AtomBlockId pb_atom(const t_pb* pb) const; - - ///@brief Conveneince wrapper around atom_pb to access the associated graph node - const t_pb_graph_node* atom_pb_graph_node(const AtomBlockId blk_id) const; - - /** - * @brief Sets the bidirectional mapping between an atom and pb - * - * If either blk_id or pb are not valid any, existing mapping is removed - */ - void set_atom_pb(const AtomBlockId blk_id, const t_pb* pb); + inline AtomPBBimap &mutable_atom_pb_bimap() {return atom_to_pb_bimap_;} + inline const AtomPBBimap &atom_pb_bimap() const {return atom_to_pb_bimap_;} /* * PB Pins @@ -111,20 +97,15 @@ class AtomLookup { ///@brief Sets the bi-directional mapping between an atom netlist pin and timing graph node void set_atom_pin_tnode(const AtomPinId pin, const tatum::NodeId node, BlockTnode block_tnode_type); - - // Getter function for atom_to_pb_ - inline const vtr::bimap &atom_to_pb() const{ - return atom_to_pb_; - } // Setter function for atom_to_pb_ - void set_atom_to_pb(const vtr::bimap &atom_to_pb){ - atom_to_pb_ = atom_to_pb; + void set_atom_to_pb_bimap(const AtomPBBimap& atom_to_pb){ + atom_to_pb_bimap_ = atom_to_pb; } private: //Types private: - vtr::bimap atom_to_pb_; + AtomPBBimap atom_to_pb_bimap_; vtr::vector_map atom_pin_to_pb_graph_pin_; diff --git a/vpr/src/base/load_flat_place.cpp b/vpr/src/base/load_flat_place.cpp index 05841e6e014..4b2ceb18014 100644 --- a/vpr/src/base/load_flat_place.cpp +++ b/vpr/src/base/load_flat_place.cpp @@ -71,7 +71,7 @@ static void print_flat_cluster(FILE* fp, // Print a line for each atom. for (AtomBlockId atom : atoms_lookup[blk_id]) { // Get the atom pb graph node. - t_pb_graph_node* atom_pbgn = atom_ctx.lookup().atom_pb(atom)->pb_graph_node; + t_pb_graph_node* atom_pbgn = atom_ctx.lookup().atom_pb_bimap().atom_pb(atom)->pb_graph_node; // Print the flat placement information for this atom. fprintf(fp, "%s %d %d %d %d %d #%zu %s\n", diff --git a/vpr/src/base/netlist_writer.cpp b/vpr/src/base/netlist_writer.cpp index 32206cbf4c8..2cc43788007 100644 --- a/vpr/src/base/netlist_writer.cpp +++ b/vpr/src/base/netlist_writer.cpp @@ -859,7 +859,7 @@ class NetlistWriterVisitor : public NetlistVisitor { void visit_atom_impl(const t_pb* atom) override { auto& atom_ctx = g_vpr_ctx.atom(); - auto atom_pb = atom_ctx.lookup().pb_atom(atom); + auto atom_pb = atom_ctx.lookup().atom_pb_bimap().pb_atom(atom); if (atom_pb == AtomBlockId::INVALID()) { return; } @@ -1787,7 +1787,7 @@ class NetlistWriterVisitor : public NetlistVisitor { } auto& atom_ctx = g_vpr_ctx.atom(); - AtomBlockId blk_id = atom_ctx.lookup().pb_atom(atom); + AtomBlockId blk_id = atom_ctx.lookup().atom_pb_bimap().pb_atom(atom); for (auto param : atom_ctx.netlist().block_params(blk_id)) { params[param.first] = param.second; } @@ -1809,7 +1809,7 @@ class NetlistWriterVisitor : public NetlistVisitor { tatum::NodeId find_tnode(const t_pb* atom, int cluster_pin_idx) { auto& atom_ctx = g_vpr_ctx.atom(); - AtomBlockId blk_id = atom_ctx.lookup().pb_atom(atom); + AtomBlockId blk_id = atom_ctx.lookup().atom_pb_bimap().pb_atom(atom); ClusterBlockId clb_index = atom_ctx.lookup().atom_clb(blk_id); auto key = std::make_pair(clb_index, cluster_pin_idx); @@ -1840,7 +1840,7 @@ class NetlistWriterVisitor : public NetlistVisitor { const t_pb* atom) { //LUT primitive auto& atom_ctx = g_vpr_ctx.atom(); - const t_model* model = atom_ctx.netlist().block_model(atom_ctx.lookup().pb_atom(atom)); + const t_model* model = atom_ctx.netlist().block_model(atom_ctx.lookup().atom_pb_bimap().pb_atom(atom)); VTR_ASSERT(model->name == std::string(MODEL_NAMES)); #ifdef DEBUG_LUT_MASK @@ -1851,7 +1851,7 @@ class NetlistWriterVisitor : public NetlistVisitor { std::vector permute = determine_lut_permutation(num_inputs, atom); //Retrieve the truth table - const auto& truth_table = atom_ctx.netlist().block_truth_table(atom_ctx.lookup().pb_atom(atom)); + const auto& truth_table = atom_ctx.netlist().block_truth_table(atom_ctx.lookup().atom_pb_bimap().pb_atom(atom)); //Apply the permutation auto permuted_truth_table = permute_truth_table(truth_table, num_inputs, permute); @@ -1896,7 +1896,7 @@ class NetlistWriterVisitor : public NetlistVisitor { // //We walk through the logical inputs to this atom (i.e. in the original truth table/netlist) //and find the corresponding input in the implementation atom (i.e. in the current netlist) - auto ports = atom_ctx.netlist().block_input_ports(atom_ctx.lookup().pb_atom(atom_pb)); + auto ports = atom_ctx.netlist().block_input_ports(atom_ctx.lookup().atom_pb_bimap().pb_atom(atom_pb)); if (ports.size() == 1) { const t_pb_graph_node* gnode = atom_pb->pb_graph_node; VTR_ASSERT(gnode->num_input_ports == 1); @@ -2144,7 +2144,7 @@ class MergedNetlistWriterVisitor : public NetlistWriterVisitor { void visit_atom_impl(const t_pb* atom) override { auto& atom_ctx = g_vpr_ctx.atom(); - auto atom_pb = atom_ctx.lookup().pb_atom(atom); + auto atom_pb = atom_ctx.lookup().atom_pb_bimap().pb_atom(atom); if (atom_pb == AtomBlockId::INVALID()) { return; } diff --git a/vpr/src/base/read_netlist.cpp b/vpr/src/base/read_netlist.cpp index ff7a4b3c88d..ef0ec24fa41 100644 --- a/vpr/src/base/read_netlist.cpp +++ b/vpr/src/base/read_netlist.cpp @@ -177,7 +177,7 @@ ClusteredNetlist read_netlist(const char* net_file, //Reset atom/pb mapping (it is reloaded from the packed netlist file) for (auto blk_id : atom_ctx.netlist().blocks()) - atom_ctx.mutable_lookup().set_atom_pb(blk_id, nullptr); + atom_ctx.mutable_lookup().mutable_atom_pb_bimap().set_atom_pb(blk_id, nullptr); //Count the number of blocks for allocation bcount = pugiutil::count_children(top, "block", loc_data, pugiutil::ReqOpt::OPTIONAL); @@ -197,7 +197,7 @@ ClusteredNetlist read_netlist(const char* net_file, /* Error check */ for (auto blk_id : atom_ctx.netlist().blocks()) { - if (atom_ctx.lookup().atom_pb(blk_id) == nullptr) { + if (atom_ctx.lookup().atom_pb_bimap().atom_pb(blk_id) == nullptr) { VPR_FATAL_ERROR(VPR_ERROR_NET_F, ".blif file and .net file do not match, .net file missing atom %s.\n", atom_ctx.netlist().block_name(blk_id).c_str()); @@ -319,7 +319,7 @@ static void processComplexBlock(pugi::xml_node clb_block, } //Parse all pbs and CB internal nets - atom_ctx.mutable_lookup().set_atom_pb(AtomBlockId::INVALID(), clb_nlist->block_pb(index)); + atom_ctx.mutable_lookup().mutable_atom_pb_bimap().set_atom_pb(AtomBlockId::INVALID(), clb_nlist->block_pb(index)); clb_nlist->block_pb(index)->pb_graph_node = clb_nlist->block_type(index)->pb_graph_head; clb_nlist->block_pb(index)->pb_route = alloc_pb_route(clb_nlist->block_pb(index)->pb_graph_node); @@ -474,7 +474,7 @@ static void processPb(pugi::xml_node Parent, const ClusterBlockId index, t_pb* p //Update atom netlist mapping VTR_ASSERT(blk_id); - atom_ctx.mutable_lookup().set_atom_pb(blk_id, pb); + atom_ctx.mutable_lookup().mutable_atom_pb_bimap().set_atom_pb(blk_id, pb); atom_ctx.mutable_lookup().set_atom_clb(blk_id, index); auto atom_attrs = atom_ctx.netlist().block_attrs(blk_id); @@ -542,7 +542,7 @@ static void processPb(pugi::xml_node Parent, const ClusterBlockId index, t_pb* p pb->child_pbs[i][pb_index].name = vtr::strdup(name.value()); /* Parse all pbs and CB internal nets*/ - atom_ctx.mutable_lookup().set_atom_pb(AtomBlockId::INVALID(), &pb->child_pbs[i][pb_index]); + atom_ctx.mutable_lookup().mutable_atom_pb_bimap().set_atom_pb(AtomBlockId::INVALID(), &pb->child_pbs[i][pb_index]); auto mode = child.attribute("mode"); pb->child_pbs[i][pb_index].mode = 0; @@ -564,7 +564,7 @@ static void processPb(pugi::xml_node Parent, const ClusterBlockId index, t_pb* p } else { /* physical block has no used primitives but it may have used routing */ pb->child_pbs[i][pb_index].name = nullptr; - atom_ctx.mutable_lookup().set_atom_pb(AtomBlockId::INVALID(), &pb->child_pbs[i][pb_index]); + atom_ctx.mutable_lookup().mutable_atom_pb_bimap().set_atom_pb(AtomBlockId::INVALID(), &pb->child_pbs[i][pb_index]); auto lookahead1 = pugiutil::get_first_child(child, "outputs", loc_data, pugiutil::OPTIONAL); if (lookahead1) { @@ -1180,7 +1180,7 @@ static void load_atom_pin_mapping(const ClusteredNetlist& clb_nlist) { auto& atom_ctx = g_vpr_ctx.atom(); for (const AtomBlockId blk : atom_ctx.netlist().blocks()) { - const t_pb* pb = atom_ctx.lookup().atom_pb(blk); + const t_pb* pb = atom_ctx.lookup().atom_pb_bimap().atom_pb(blk); VTR_ASSERT_MSG(pb, "Atom block must have a matching PB"); const t_pb_graph_node* gnode = pb->pb_graph_node; @@ -1250,7 +1250,7 @@ void set_atom_pin_mapping(const ClusteredNetlist& clb_nlist, const AtomBlockId a return; } - const t_pb* atom_pb = atom_ctx.lookup().atom_pb(atom_blk); + const t_pb* atom_pb = atom_ctx.lookup().atom_pb_bimap().atom_pb(atom_blk); //This finds the index within the atom port to which the current gpin //is mapped. Note that this accounts for any applied pin rotations diff --git a/vpr/src/draw/draw.cpp b/vpr/src/draw/draw.cpp index 8da5eabbd05..2075f6291cc 100644 --- a/vpr/src/draw/draw.cpp +++ b/vpr/src/draw/draw.cpp @@ -797,7 +797,7 @@ ezgl::point2d atom_pin_draw_coord(AtomPinId pin) { AtomBlockId blk = atom_ctx.netlist().pin_block(pin); ClusterBlockId clb_index = atom_ctx.lookup().atom_clb(blk); - const t_pb_graph_node* pg_gnode = atom_ctx.lookup().atom_pb_graph_node(blk); + const t_pb_graph_node* pg_gnode = atom_ctx.lookup().atom_pb_bimap().atom_pb_graph_node(blk); t_draw_coords* draw_coords = get_draw_coords_vars(); ezgl::rectangle pb_bbox = draw_coords->get_absolute_pb_bbox(clb_index, diff --git a/vpr/src/draw/draw_floorplanning.cpp b/vpr/src/draw/draw_floorplanning.cpp index 5c07317dbaf..9a76f9013b1 100644 --- a/vpr/src/draw/draw_floorplanning.cpp +++ b/vpr/src/draw/draw_floorplanning.cpp @@ -149,8 +149,8 @@ void draw_constrained_atoms(ezgl::renderer* g) { auto atoms = constraints.get_part_atoms((PartitionId)partitionID); for (const AtomBlockId atom_id : atoms) { - if (atom_ctx.lookup().atom_pb(atom_id) != nullptr) { - const t_pb* pb = atom_ctx.lookup().atom_pb(atom_id); + if (atom_ctx.lookup().atom_pb_bimap().atom_pb(atom_id) != nullptr) { + const t_pb* pb = atom_ctx.lookup().atom_pb_bimap().atom_pb(atom_id); auto color = kelly_max_contrast_colors_no_black[partitionID % (kelly_max_contrast_colors_no_black.size())]; ClusterBlockId clb_index = atom_ctx.lookup().atom_clb(atom_id); auto type = cluster_ctx.clb_nlist.block_type(clb_index); @@ -306,7 +306,7 @@ static GtkTreeModel* create_and_fill_model() { -1); for (AtomBlockId const_atom : atoms) { - std::string atom_name = (atom_ctx.lookup().atom_pb(const_atom))->name; + std::string atom_name = (atom_ctx.lookup().atom_pb_bimap().atom_pb(const_atom))->name; gtk_tree_store_append(store, &child_iter, &iter); gtk_tree_store_set(store, &child_iter, COL_NAME, atom_name.c_str(), diff --git a/vpr/src/draw/intra_logic_block.cpp b/vpr/src/draw/intra_logic_block.cpp index 6bb6a1f6ab3..32ed07c538a 100644 --- a/vpr/src/draw/intra_logic_block.cpp +++ b/vpr/src/draw/intra_logic_block.cpp @@ -530,13 +530,13 @@ void collect_pb_atoms_recurr(const t_pb* pb, std::vector& atoms) { if (pb->is_primitive()) { //Base case - AtomBlockId blk = atom_ctx.lookup().pb_atom(pb); + AtomBlockId blk = atom_ctx.lookup().atom_pb_bimap().pb_atom(pb); if (blk) { atoms.push_back(blk); } } else { //Recurse - VTR_ASSERT_DEBUG(atom_ctx.lookup().pb_atom(pb) == AtomBlockId::INVALID()); + VTR_ASSERT_DEBUG(atom_ctx.lookup().atom_pb_bimap().pb_atom(pb) == AtomBlockId::INVALID()); for (int itype = 0; itype < pb->get_num_child_types(); ++itype) { for (int ichild = 0; ichild < pb->get_num_children_of_type(itype); ++ichild) { @@ -575,14 +575,14 @@ void draw_logical_connections(ezgl::renderer* g) { continue; /* Don't Draw */ } - const t_pb_graph_node* src_pb_gnode = atom_ctx.lookup().atom_pb_graph_node(src_blk_id); + const t_pb_graph_node* src_pb_gnode = atom_ctx.lookup().atom_pb_bimap().atom_pb_graph_node(src_blk_id); bool src_is_selected = sel_subblk_info.is_in_selected_subtree(src_pb_gnode, src_clb); bool src_is_src_of_selected = sel_subblk_info.is_source_of_selected(src_pb_gnode, src_clb); // iterate over the sinks for (auto sink_pin_id : atom_ctx.netlist().net_sinks(net_id)) { AtomBlockId sink_blk_id = atom_ctx.netlist().pin_block(sink_pin_id); - const t_pb_graph_node* sink_pb_gnode = atom_ctx.lookup().atom_pb_graph_node(sink_blk_id); + const t_pb_graph_node* sink_pb_gnode = atom_ctx.lookup().atom_pb_bimap().atom_pb_graph_node(sink_blk_id); ClusterBlockId sink_clb = atom_ctx.lookup().atom_clb(sink_blk_id); int sink_layer_num = block_locs[sink_clb].loc.layer; @@ -807,7 +807,7 @@ void t_selected_sub_block_info::set(t_pb* new_selected_sub_block, const ClusterB for (auto blk_id : atom_ctx.netlist().blocks()) { const ClusterBlockId clb = atom_ctx.lookup().atom_clb(blk_id); - const t_pb_graph_node* pb_graph_node = atom_ctx.lookup().atom_pb_graph_node(blk_id); + const t_pb_graph_node* pb_graph_node = atom_ctx.lookup().atom_pb_bimap().atom_pb_graph_node(blk_id); // find the atom block that corrisponds to this pb. if (is_in_selected_subtree(pb_graph_node, clb)) { //Collect the sources of all nets driving this node @@ -818,7 +818,7 @@ void t_selected_sub_block_info::set(t_pb* new_selected_sub_block, const ClusterB AtomBlockId src_blk = atom_ctx.netlist().pin_block(driver_pin_id); const ClusterBlockId src_clb = atom_ctx.lookup().atom_clb(src_blk); - const t_pb_graph_node* src_pb_graph_node = atom_ctx.lookup().atom_pb_graph_node(src_blk); + const t_pb_graph_node* src_pb_graph_node = atom_ctx.lookup().atom_pb_bimap().atom_pb_graph_node(src_blk); sources.insert(gnode_clb_pair(src_pb_graph_node, src_clb)); } @@ -830,7 +830,7 @@ void t_selected_sub_block_info::set(t_pb* new_selected_sub_block, const ClusterB AtomBlockId sink_blk = atom_ctx.netlist().pin_block(sink_pin_id); const ClusterBlockId sink_clb = atom_ctx.lookup().atom_clb(sink_blk); - const t_pb_graph_node* sink_pb_graph_node = atom_ctx.lookup().atom_pb_graph_node(sink_blk); + const t_pb_graph_node* sink_pb_graph_node = atom_ctx.lookup().atom_pb_bimap().atom_pb_graph_node(sink_blk); sinks.insert(gnode_clb_pair(sink_pb_graph_node, sink_clb)); } @@ -882,7 +882,7 @@ t_selected_sub_block_info::clb_pin_tuple::clb_pin_tuple(ClusterBlockId clb_index t_selected_sub_block_info::clb_pin_tuple::clb_pin_tuple(const AtomPinId atom_pin) { auto& atom_ctx = g_vpr_ctx.atom(); clb_index = atom_ctx.lookup().atom_clb(atom_ctx.netlist().pin_block(atom_pin)); - pb_gnode = atom_ctx.lookup().atom_pb_graph_node(atom_ctx.netlist().pin_block(atom_pin)); + pb_gnode = atom_ctx.lookup().atom_pb_bimap().atom_pb_graph_node(atom_ctx.netlist().pin_block(atom_pin)); } bool t_selected_sub_block_info::clb_pin_tuple::operator==(const clb_pin_tuple& rhs) const { diff --git a/vpr/src/pack/atom_pb_bimap.cpp b/vpr/src/pack/atom_pb_bimap.cpp new file mode 100644 index 00000000000..6c61aba7d74 --- /dev/null +++ b/vpr/src/pack/atom_pb_bimap.cpp @@ -0,0 +1,48 @@ +#include "atom_pb_bimap.h" + +AtomPBBimap::AtomPBBimap(const vtr::bimap& atom_to_pb) { + atom_to_pb_ = atom_to_pb; +} + +const t_pb* AtomPBBimap::atom_pb(const AtomBlockId blk_id) const { + auto iter = atom_to_pb_.find(blk_id); + if (iter == atom_to_pb_.end()) { + //Not found + return nullptr; + } + return iter->second; +} + +AtomBlockId AtomPBBimap::pb_atom(const t_pb* pb) const { + auto iter = atom_to_pb_.find(pb); + if (iter == atom_to_pb_.inverse_end()) { + //Not found + return AtomBlockId::INVALID(); + } + return iter->second; +} + +const t_pb_graph_node* AtomPBBimap::atom_pb_graph_node(const AtomBlockId blk_id) const { + const t_pb* pb = atom_pb(blk_id); + if (pb) { + //Found + return pb->pb_graph_node; + } + return nullptr; +} + +void AtomPBBimap::set_atom_pb(const AtomBlockId blk_id, const t_pb* pb) { + //If either of blk_id or pb are not valid, + //remove any mapping + + if (!blk_id && pb) { + //Remove + atom_to_pb_.erase(pb); + } else if (blk_id && !pb) { + //Remove + atom_to_pb_.erase(blk_id); + } else if (blk_id && pb) { + //If both are valid store the mapping + atom_to_pb_.update(blk_id, pb); + } +} diff --git a/vpr/src/pack/atom_pb_lookup.h b/vpr/src/pack/atom_pb_bimap.h similarity index 85% rename from vpr/src/pack/atom_pb_lookup.h rename to vpr/src/pack/atom_pb_bimap.h index df643cc1a14..22d0de4fdd7 100644 --- a/vpr/src/pack/atom_pb_lookup.h +++ b/vpr/src/pack/atom_pb_bimap.h @@ -5,13 +5,13 @@ // Forward declaration class t_pb_graph_node; -class AtomPBLookUp { +class AtomPBBimap { public: - AtomPBLookUp() = default; - AtomPBLookUp(const vtr::bimap &atom_to_pb); + AtomPBBimap() = default; + AtomPBBimap(const vtr::bimap &atom_to_pb); - /** + /** * @brief Returns the leaf pb associated with the atom blk_id * @note this is the lowest level pb which corresponds directly to the atom block */ diff --git a/vpr/src/pack/cluster_legalizer.cpp b/vpr/src/pack/cluster_legalizer.cpp index c338ff1bf97..079f81a4f48 100644 --- a/vpr/src/pack/cluster_legalizer.cpp +++ b/vpr/src/pack/cluster_legalizer.cpp @@ -10,35 +10,40 @@ * was moved into the ClusterLegalizer class. */ - #include "cluster_legalizer.h" - #include - #include - #include - #include - #include "atom_lookup.h" - #include "atom_netlist.h" - #include "cad_types.h" - #include "cluster_placement.h" - #include "cluster_router.h" - #include "globals.h" - #include "logic_types.h" - #include "netlist_utils.h" - #include "noc_aware_cluster_util.h" - #include "noc_data_types.h" - #include "pack_types.h" - #include "partition.h" - #include "partition_region.h" - #include "physical_types.h" - #include "prepack.h" - #include "user_place_constraints.h" - #include "vpr_context.h" - #include "vpr_types.h" - #include "vpr_utils.h" - #include "vtr_assert.h" - #include "vtr_vector.h" - #include "vtr_vector_map.h" - - void ClusterLegalizer::alloc_and_load_pb_stats(t_pb* pb) { +#include "cluster_legalizer.h" +#include +#include +#include +#include +#include "atom_lookup.h" +#include "atom_netlist.h" +#include "cad_types.h" +#include "cluster_placement.h" +#include "cluster_router.h" +#include "globals.h" +#include "logic_types.h" +#include "netlist_utils.h" +#include "noc_aware_cluster_util.h" +#include "noc_data_types.h" +#include "pack_types.h" +#include "partition.h" +#include "partition_region.h" +#include "physical_types.h" +#include "prepack.h" +#include "user_place_constraints.h" +#include "vpr_context.h" +#include "vpr_types.h" +#include "vpr_utils.h" +#include "vtr_assert.h" +#include "vtr_vector.h" +#include "vtr_vector_map.h" + +/* +* @brief Allocates the stats stored within the pb of a cluster. +* +* Used to store information used during clustering. +*/ +static void alloc_and_load_pb_stats(t_pb* pb) { /* Call this routine when starting to fill up a new cluster. It resets * * the gain vector, etc. */ @@ -50,1703 +55,1743 @@ pb->pb_stats->lookahead_output_pins_used = std::vector>(pb->pb_graph_node->num_output_pin_class); pb->pb_stats->num_child_blocks_in_pb = 0; - } - - void ClusterLegalizer::check_cluster_atom_blocks(t_pb* pb, std::unordered_set& blocks_checked) { - const AtomContext& atom_ctx = g_vpr_ctx.atom(); - - const t_pb_type* pb_type = pb->pb_graph_node->pb_type; - if (pb_type->num_modes == 0) { - /* primitive */ - AtomBlockId blk_id = atom_pb_lookup().pb_atom(pb); - if (blk_id) { - if (blocks_checked.count(blk_id)) { - VPR_FATAL_ERROR(VPR_ERROR_PACK, - "pb %s contains atom block %s but atom block is already contained in another pb.\n", - pb->name, atom_ctx.netlist().block_name(blk_id).c_str()); - } - blocks_checked.insert(blk_id); - if (pb != atom_pb_lookup().atom_pb(blk_id)) { - VPR_FATAL_ERROR(VPR_ERROR_PACK, - "pb %s contains atom block %s but atom block does not link to pb.\n", - pb->name, atom_ctx.netlist().block_name(blk_id).c_str()); - } - } - } else { - /* this is a container pb, all container pbs must contain children */ - bool has_child = false; - for (int i = 0; i < pb_type->modes[pb->mode].num_pb_type_children; i++) { - for (int j = 0; j < pb_type->modes[pb->mode].pb_type_children[i].num_pb; j++) { - if (pb->child_pbs[i] != nullptr) { - if (pb->child_pbs[i][j].name != nullptr) { - has_child = true; - check_cluster_atom_blocks(&pb->child_pbs[i][j], blocks_checked); - } - } - } - } - VTR_ASSERT(has_child); - } - } - - void ClusterLegalizer::free_pb_stats_recursive(t_pb* pb) { - /* Releases all the memory used by clustering data structures. */ - if (pb) { - if (pb->pb_graph_node != nullptr) { - if (!pb->pb_graph_node->is_primitive()) { - for (int i = 0; i < pb->pb_graph_node->pb_type->modes[pb->mode].num_pb_type_children; i++) { - for (int j = 0; j < pb->pb_graph_node->pb_type->modes[pb->mode].pb_type_children[i].num_pb; j++) { - if (pb->child_pbs && pb->child_pbs[i]) { - free_pb_stats_recursive(&pb->child_pbs[i][j]); - } - } - } - } - } - free_pb_stats(pb); - } - } - - bool ClusterLegalizer::check_cluster_floorplanning(AtomBlockId atom_blk_id, - PartitionRegion& cluster_pr, - const UserPlaceConstraints& constraints, - int log_verbosity, - bool& cluster_pr_needs_update) { - // Get the partition ID of the atom. - PartitionId part_id = constraints.get_atom_partition(atom_blk_id); - // If the partition ID is invalid, then it can be put in the cluster - // regardless of what the cluster's PartitionRegion is since it is not - // constrained. - if (!part_id.is_valid()) { - VTR_LOGV(log_verbosity > 3, - "\t\t\t Intersect: Atom block %d has no floorplanning constraints\n", - atom_blk_id); - cluster_pr_needs_update = false; - return true; - } - - // Get the Atom and Cluster Partition Regions - const PartitionRegion& atom_pr = constraints.get_partition_pr(part_id); - - // If the Cluster's PartitionRegion is empty, then this atom's PR becomes - // the Cluster's new PartitionRegion. - if (cluster_pr.empty()) { - VTR_LOGV(log_verbosity > 3, - "\t\t\t Intersect: Atom block %d has floorplanning constraints\n", - atom_blk_id); - cluster_pr = atom_pr; - cluster_pr_needs_update = true; - return true; - } - - // The Cluster's new PartitionRegion is the intersection of the Cluster's - // original PartitionRegion and the atom's PartitionRegion. - update_cluster_part_reg(cluster_pr, atom_pr); - - // If the intersection is empty, then the atom cannot be placed in this - // Cluster due to floorplanning constraints. - if (cluster_pr.empty()) { - VTR_LOGV(log_verbosity > 3, - "\t\t\t Intersect: Atom block %d failed floorplanning check for cluster\n", - atom_blk_id); - cluster_pr_needs_update = false; - return false; - } - - // If the Cluster's new PartitionRegion is non-empty, then this atom passes - // the floorplanning constraints and the cluster's PartitionRegion should be - // updated. - cluster_pr_needs_update = true; - VTR_LOGV(log_verbosity > 3, - "\t\t\t Intersect: Atom block %d passed cluster, cluster PR was updated with intersection result \n", - atom_blk_id); - return true; - } - - bool ClusterLegalizer::check_cluster_noc_group(AtomBlockId atom_blk_id, - NocGroupId& cluster_noc_grp_id, - const vtr::vector& atom_noc_grp_ids, - int log_verbosity) { - const NocGroupId atom_noc_grp_id = atom_noc_grp_ids.empty() ? NocGroupId::INVALID() : atom_noc_grp_ids[atom_blk_id]; - - if (!cluster_noc_grp_id.is_valid()) { - // If the cluster does not have a NoC group, assign the atom's NoC group - // to the cluster. - VTR_LOGV(log_verbosity > 3, - "\t\t\t NoC Group: Atom block %d passed cluster, cluster's NoC group was updated with the atom's group %d\n", - atom_blk_id, (size_t)atom_noc_grp_id); - cluster_noc_grp_id = atom_noc_grp_id; - return true; - } - - if (cluster_noc_grp_id == atom_noc_grp_id) { - // If the cluster has the same NoC group ID as the atom, they are - // compatible. - VTR_LOGV(log_verbosity > 3, - "\t\t\t NoC Group: Atom block %d passed cluster, cluster's NoC group was compatible with the atom's group %d\n", - atom_blk_id, (size_t)atom_noc_grp_id); - return true; - } - - // If the cluster belongs to a different NoC group than the atom's group, - // they are incompatible. - VTR_LOGV(log_verbosity > 3, - "\t\t\t NoC Group: Atom block %d failed NoC group check for cluster. Cluster's NoC group: %d, atom's NoC group: %d\n", - atom_blk_id, (size_t)cluster_noc_grp_id, (size_t)atom_noc_grp_id); - return false; - } - - enum e_block_pack_status ClusterLegalizer::check_chain_root_placement_feasibility( - const t_pb_graph_node* pb_graph_node, - const t_chain_info& prepack_chain_info, - const t_clustering_chain_info& clustering_chain_info, - t_pack_patterns* mol_pack_patterns, - const AtomBlockId blk_id) { - const AtomContext& atom_ctx = g_vpr_ctx.atom(); - - enum e_block_pack_status block_pack_status = e_block_pack_status::BLK_PASSED; - - bool is_long_chain = prepack_chain_info.is_long_chain; - - const auto& chain_root_pins = mol_pack_patterns->chain_root_pins; - - t_model_ports* root_port = chain_root_pins[0][0]->port->model_port; - AtomNetId chain_net_id; - auto port_id = atom_ctx.netlist().find_atom_port(blk_id, root_port); - - if (port_id) { - chain_net_id = atom_ctx.netlist().port_net(port_id, chain_root_pins[0][0]->pin_number); - } - - // if this block is part of a long chain or it is driven by a cluster - // input pin we need to check the placement legality of this block - // Depending on the logic synthesis even small chains that can fit within one - // cluster might need to start at the top of the cluster as their input can be - // driven by a global gnd or vdd. Therefore even if this is not a long chain - // but its input pin is driven by a net, the placement legality is checked. - if (is_long_chain || chain_net_id) { - auto chain_id = clustering_chain_info.chain_id; - // if this chain has a chain id assigned to it (implies is_long_chain too) - if (chain_id != -1) { - // the chosen primitive should be a valid starting point for the chain - // long chains should only be placed at the top of the chain tieOff = 0 - if (pb_graph_node != chain_root_pins[chain_id][0]->parent_node) { - block_pack_status = e_block_pack_status::BLK_FAILED_FEASIBLE; - } - // the chain doesn't have an assigned chain_id yet - } else { - block_pack_status = e_block_pack_status::BLK_FAILED_FEASIBLE; - for (const auto& chain : chain_root_pins) { - for (auto tieOff : chain) { - // check if this chosen primitive is one of the possible - // starting points for this chain. - if (pb_graph_node == tieOff->parent_node) { - // this location matches with the one of the dedicated chain - // input from outside logic block, therefore it is feasible - block_pack_status = e_block_pack_status::BLK_PASSED; - break; - } - // long chains should only be placed at the top of the chain tieOff = 0 - if (is_long_chain) break; - } - } - } - } - - return block_pack_status; - } - - bool ClusterLegalizer::primitive_memory_sibling_feasible(const AtomBlockId blk_id, const t_pb_type* cur_pb_type, const AtomBlockId sibling_blk_id) { - const AtomContext& atom_ctx = g_vpr_ctx.atom(); - - VTR_ASSERT(cur_pb_type->class_type == MEMORY_CLASS); - - //First, identify the 'data' ports by looking at the cur_pb_type - std::unordered_set data_ports; - for (int iport = 0; iport < cur_pb_type->num_ports; ++iport) { - const char* port_class = cur_pb_type->ports[iport].port_class; - if (port_class && strstr(port_class, "data") == port_class) { - //The port_class starts with "data", so it is a data port - - //Record the port - data_ports.insert(cur_pb_type->ports[iport].model_port); - } - } - - //Now verify that all nets (except those connected to data ports) are equivalent - //between blk_id and sibling_blk_id - - //Since the atom netlist stores only in-use ports, we iterate over the model to ensure - //all ports are compared - const t_model* model = cur_pb_type->model; - for (t_model_ports* port : {model->inputs, model->outputs}) { - for (; port; port = port->next) { - if (data_ports.count(port)) { - //Don't check data ports - continue; - } - - //Note: VPR doesn't support multi-driven nets, so all outputs - //should be data ports, otherwise the siblings will both be - //driving the output net - - //Get the ports from each primitive - auto blk_port_id = atom_ctx.netlist().find_atom_port(blk_id, port); - auto sib_port_id = atom_ctx.netlist().find_atom_port(sibling_blk_id, port); - - //Check that all nets (including unconnected nets) match - for (int ipin = 0; ipin < port->size; ++ipin) { - //The nets are initialized as invalid (i.e. disconnected) - AtomNetId blk_net_id; - AtomNetId sib_net_id; - - //We can get the actual net provided the port exists - // - //Note that if the port did not exist, the net is left - //as invalid/disconneced - if (blk_port_id) { - blk_net_id = atom_ctx.netlist().port_net(blk_port_id, ipin); - } - if (sib_port_id) { - sib_net_id = atom_ctx.netlist().port_net(sib_port_id, ipin); - } - - //The sibling and block must have the same (possibly disconnected) - //net on this pin - if (blk_net_id != sib_net_id) { - //Nets do not match, not feasible - return false; - } - } - } - } - - return true; - } - - bool ClusterLegalizer::primitive_feasible(const AtomBlockId blk_id, t_pb* cur_pb) { - const t_pb_type* cur_pb_type = cur_pb->pb_graph_node->pb_type; - - VTR_ASSERT(cur_pb_type->num_modes == 0); /* primitive */ - - AtomBlockId cur_pb_blk_id = atom_pb_lookup().pb_atom(cur_pb); - if (cur_pb_blk_id && cur_pb_blk_id != blk_id) { - /* This pb already has a different logical block */ - return false; - } - - if (cur_pb_type->class_type == MEMORY_CLASS) { - /* Memory class has additional feasibility requirements: - * - all siblings must share all nets, including open nets, with the exception of data nets */ - - /* find sibling if one exists */ - const t_pb *sibling_memory_pb = find_memory_sibling(cur_pb); - AtomBlockId sibling_memory_blk_id = atom_pb_lookup().pb_atom(sibling_memory_pb); - - if (sibling_memory_blk_id) { - //There is a sibling, see if the current block is feasible with it - bool sibling_feasible = primitive_memory_sibling_feasible(blk_id, cur_pb_type, sibling_memory_blk_id); - if (!sibling_feasible) { - return false; - } - } - } - - //Generic feasibility check - return primitive_type_feasible(blk_id, cur_pb_type); - } - - enum e_block_pack_status - ClusterLegalizer::try_place_atom_block_rec(const t_pb_graph_node* pb_graph_node, - const AtomBlockId blk_id, - t_pb* cb, - t_pb** parent, - const LegalizationClusterId cluster_id, - vtr::vector_map& atom_cluster, - const PackMoleculeId molecule_id, - t_lb_router_data* router_data, - int verbosity, - const Prepacker& prepacker, - const vtr::vector_map& clustering_chain_info) { - const AtomContext& atom_ctx = g_vpr_ctx.atom(); - - VTR_ASSERT_SAFE(cb != nullptr); - e_block_pack_status block_pack_status = e_block_pack_status::BLK_PASSED; - - /* Discover parent */ - t_pb* parent_pb = nullptr; - if (pb_graph_node->parent_pb_graph_node != cb->pb_graph_node) { - t_pb* my_parent = nullptr; - block_pack_status = try_place_atom_block_rec(pb_graph_node->parent_pb_graph_node, blk_id, cb, - &my_parent, cluster_id, - atom_cluster, - molecule_id, router_data, - verbosity, - prepacker, clustering_chain_info); - parent_pb = my_parent; - } else { - parent_pb = cb; - } - - /* Create siblings if siblings are not allocated */ - VTR_ASSERT(parent_pb != nullptr); - if (parent_pb->child_pbs == nullptr) { - VTR_ASSERT(parent_pb->name == nullptr); - parent_pb->name = vtr::strdup(atom_ctx.netlist().block_name(blk_id).c_str()); - parent_pb->mode = pb_graph_node->pb_type->parent_mode->index; - set_reset_pb_modes(router_data, parent_pb, true); - const t_mode* mode = &parent_pb->pb_graph_node->pb_type->modes[parent_pb->mode]; - parent_pb->child_pbs = new t_pb*[mode->num_pb_type_children]; - - for (int i = 0; i < mode->num_pb_type_children; i++) { - parent_pb->child_pbs[i] = new t_pb[mode->pb_type_children[i].num_pb]; - - for (int j = 0; j < mode->pb_type_children[i].num_pb; j++) { - parent_pb->child_pbs[i][j].parent_pb = parent_pb; - parent_pb->child_pbs[i][j].pb_graph_node = &(parent_pb->pb_graph_node->child_pb_graph_nodes[parent_pb->mode][i][j]); - } - } - } else { - /* if this is not the first child of this parent, must match existing parent mode */ - if (parent_pb->mode != pb_graph_node->pb_type->parent_mode->index) { - return e_block_pack_status::BLK_FAILED_FEASIBLE; - } - } - - const t_mode* mode = &parent_pb->pb_graph_node->pb_type->modes[parent_pb->mode]; - int i; - for (i = 0; i < mode->num_pb_type_children; i++) { - if (pb_graph_node->pb_type == &mode->pb_type_children[i]) { - break; - } - } - VTR_ASSERT(i < mode->num_pb_type_children); - t_pb* pb = &parent_pb->child_pbs[i][pb_graph_node->placement_index]; - VTR_ASSERT_SAFE(pb != nullptr); - *parent = pb; /* this pb is parent of it's child that called this function */ - VTR_ASSERT(pb->pb_graph_node == pb_graph_node); - if (pb->pb_stats == nullptr) { - alloc_and_load_pb_stats(pb); - } - const t_pb_type* pb_type = pb_graph_node->pb_type; - - /* Any pb_type under an mode, which is disabled for packing, should not be considerd for mapping - * Early exit to flag failure - */ - if (true == pb_type->parent_mode->disable_packing) { - return e_block_pack_status::BLK_FAILED_FEASIBLE; - } - - bool is_primitive = (pb_type->num_modes == 0); - - if (is_primitive) { - VTR_ASSERT(!atom_pb_lookup().pb_atom(pb) - && atom_pb_lookup().atom_pb(blk_id) == nullptr - && atom_cluster[blk_id] == LegalizationClusterId::INVALID()); - /* try pack to location */ - VTR_ASSERT(pb->name == nullptr); - pb->name = vtr::strdup(atom_ctx.netlist().block_name(blk_id).c_str()); - - //Update the atom netlist mappings - atom_cluster[blk_id] = cluster_id; - // NOTE: This pb is different from the pb of the cluster. It is the pb - // of the actual primitive. - // TODO: It would be a good idea to remove the use of this global - // variables to prevent external users from modifying this by - // mistake. - mutable_atom_pb_lookup().set_atom_pb(blk_id, pb); - - add_atom_as_target(router_data, blk_id, atom_pb_lookup()); - if (!primitive_feasible(blk_id, pb)) { - /* failed location feasibility check, revert pack */ - block_pack_status = e_block_pack_status::BLK_FAILED_FEASIBLE; - } - - // if this block passed and is part of a chained molecule - const t_pack_molecule& molecule = prepacker.get_molecule(molecule_id); - if (block_pack_status == e_block_pack_status::BLK_PASSED && molecule.is_chain()) { - auto molecule_root_block = molecule.atom_block_ids[molecule.root]; - // if this is the root block of the chain molecule check its placmeent feasibility - if (blk_id == molecule_root_block) { - VTR_ASSERT(molecule.chain_id.is_valid()); - const t_chain_info& prepack_chain_info = prepacker.get_molecule_chain_info(molecule.chain_id); - block_pack_status = check_chain_root_placement_feasibility(pb_graph_node, - prepack_chain_info, - clustering_chain_info[molecule.chain_id], - molecule.pack_pattern, - blk_id); - } - } - - VTR_LOGV(verbosity > 4 && block_pack_status == e_block_pack_status::BLK_PASSED, - "\t\t\tPlaced atom '%s' (%s) at %s\n", - atom_ctx.netlist().block_name(blk_id).c_str(), - atom_ctx.netlist().block_model(blk_id)->name, - pb->hierarchical_type_name().c_str()); - } - - if (block_pack_status != e_block_pack_status::BLK_PASSED) { - free(pb->name); - pb->name = nullptr; - } - return block_pack_status; - } - - void ClusterLegalizer::reset_lookahead_pins_used(t_pb* cur_pb) { - const t_pb_type* pb_type = cur_pb->pb_graph_node->pb_type; - if (cur_pb->pb_stats == nullptr) { - return; /* No pins used, no need to continue */ - } - - if (pb_type->num_modes > 0 && cur_pb->name != nullptr) { - for (int i = 0; i < cur_pb->pb_graph_node->num_input_pin_class; i++) { - cur_pb->pb_stats->lookahead_input_pins_used[i].clear(); - } - - for (int i = 0; i < cur_pb->pb_graph_node->num_output_pin_class; i++) { - cur_pb->pb_stats->lookahead_output_pins_used[i].clear(); - } - - if (cur_pb->child_pbs != nullptr) { - for (int i = 0; i < pb_type->modes[cur_pb->mode].num_pb_type_children; i++) { - if (cur_pb->child_pbs[i] != nullptr) { - for (int j = 0; j < pb_type->modes[cur_pb->mode].pb_type_children[i].num_pb; j++) { - reset_lookahead_pins_used(&cur_pb->child_pbs[i][j]); - } - } - } - } - } - } - - int ClusterLegalizer::net_sinks_reachable_in_cluster(const t_pb_graph_pin* driver_pb_gpin, const int depth, const AtomNetId net_id) { - const AtomContext& atom_ctx = g_vpr_ctx.atom(); - - //Record the sink pb graph pins we are looking for - std::unordered_set sink_pb_gpins; - for (const AtomPinId pin_id : atom_ctx.netlist().net_sinks(net_id)) { - const t_pb_graph_pin* sink_pb_gpin = find_pb_graph_pin(atom_ctx.netlist(), atom_pb_lookup(), pin_id); - VTR_ASSERT(sink_pb_gpin); - - sink_pb_gpins.insert(sink_pb_gpin); - } - - //Count how many sink pins are reachable - size_t num_reachable_sinks = 0; - for (int i_prim_pin = 0; i_prim_pin < driver_pb_gpin->num_connectable_primitive_input_pins[depth]; ++i_prim_pin) { - const t_pb_graph_pin* reachable_pb_gpin = driver_pb_gpin->list_of_connectable_input_pin_ptrs[depth][i_prim_pin]; - - if (sink_pb_gpins.count(reachable_pb_gpin)) { - ++num_reachable_sinks; - if (num_reachable_sinks == atom_ctx.netlist().net_sinks(net_id).size()) { - return true; - } - } - } - - return false; - } - - t_pb_graph_pin* ClusterLegalizer::get_driver_pb_graph_pin(const t_pb* driver_pb, const AtomPinId driver_pin_id) { - const AtomContext& atom_ctx = g_vpr_ctx.atom(); - - const auto driver_pb_type = driver_pb->pb_graph_node->pb_type; - int output_port = 0; - // find the port of the pin driving the net as well as the port model - auto driver_port_id = atom_ctx.netlist().pin_port(driver_pin_id); - auto driver_model_port = atom_ctx.netlist().port_model(driver_port_id); - // find the port id of the port containing the driving pin in the driver_pb_type - for (int i = 0; i < driver_pb_type->num_ports; i++) { - auto& prim_port = driver_pb_type->ports[i]; - if (prim_port.type == OUT_PORT) { - if (prim_port.model_port == driver_model_port) { - // get the output pb_graph_pin driving this input net - return &(driver_pb->pb_graph_node->output_pins[output_port][atom_ctx.netlist().pin_port_bit(driver_pin_id)]); - } - output_port++; - } - } - // the pin should be found - VTR_ASSERT(false); - return nullptr; - } - - void ClusterLegalizer::compute_and_mark_lookahead_pins_used_for_pin(const t_pb_graph_pin* pb_graph_pin, - const t_pb* primitive_pb, - const AtomNetId net_id, - const vtr::vector_map& atom_cluster) { - const AtomContext& atom_ctx = g_vpr_ctx.atom(); - - // starting from the parent pb of the input primitive go up in the hierarchy till the root block - for (auto cur_pb = primitive_pb->parent_pb; cur_pb; cur_pb = cur_pb->parent_pb) { - const auto depth = cur_pb->pb_graph_node->pb_type->depth; - const auto pin_class = pb_graph_pin->parent_pin_class[depth]; - VTR_ASSERT(pin_class != OPEN); - - const auto driver_blk_id = atom_ctx.netlist().net_driver_block(net_id); - - // if this primitive pin is an input pin - if (pb_graph_pin->port->type == IN_PORT) { - /* find location of net driver if exist in clb, NULL otherwise */ - // find the driver of the input net connected to the pin being studied - const auto driver_pin_id = atom_ctx.netlist().net_driver(net_id); - // find the id of the atom occupying the input primitive_pb - const auto prim_blk_id = atom_pb_lookup().pb_atom(primitive_pb); - // find the pb block occupied by the driving atom - const auto driver_pb = atom_pb_lookup().atom_pb(driver_blk_id); - // pb_graph_pin driving net_id in the driver pb block - t_pb_graph_pin* output_pb_graph_pin = nullptr; - // if the driver block is in the same clb as the input primitive block - LegalizationClusterId driver_cluster_id = atom_cluster[driver_blk_id]; - LegalizationClusterId prim_cluster_id = atom_cluster[prim_blk_id]; - if (driver_cluster_id == prim_cluster_id) { - // get pb_graph_pin driving the given net - output_pb_graph_pin = get_driver_pb_graph_pin(driver_pb, driver_pin_id); - } - - bool is_reachable = false; - - // if the driver pin is within the cluster - if (output_pb_graph_pin) { - // find if the driver pin can reach the input pin of the primitive or not - const t_pb* check_pb = driver_pb; - while (check_pb && check_pb != cur_pb) { - check_pb = check_pb->parent_pb; - } - if (check_pb) { - for (int i = 0; i < output_pb_graph_pin->num_connectable_primitive_input_pins[depth]; i++) { - if (pb_graph_pin == output_pb_graph_pin->list_of_connectable_input_pin_ptrs[depth][i]) { - is_reachable = true; - break; - } - } - } - } - - // Must use an input pin to connect the driver to the input pin of the given primitive, either the - // driver atom is not contained in the cluster or is contained but cannot reach the primitive pin - if (!is_reachable) { - // add net to lookahead_input_pins_used if not already added - auto it = std::find(cur_pb->pb_stats->lookahead_input_pins_used[pin_class].begin(), - cur_pb->pb_stats->lookahead_input_pins_used[pin_class].end(), net_id); - if (it == cur_pb->pb_stats->lookahead_input_pins_used[pin_class].end()) { - cur_pb->pb_stats->lookahead_input_pins_used[pin_class].push_back(net_id); - } - } - } else { - VTR_ASSERT(pb_graph_pin->port->type == OUT_PORT); - /* - * Determine if this net (which is driven from within this cluster) leaves this cluster - * (and hence uses an output pin). - */ - - bool net_exits_cluster = true; - int num_net_sinks = static_cast(atom_ctx.netlist().net_sinks(net_id).size()); - - if (pb_graph_pin->num_connectable_primitive_input_pins[depth] >= num_net_sinks) { - //It is possible the net is completely absorbed in the cluster, - //since this pin could (potentially) drive all the net's sinks - - /* Important: This runtime penalty looks a lot scarier than it really is. - * For high fan-out nets, I at most look at the number of pins within the - * cluster which limits runtime. - * - * DO NOT REMOVE THIS INITIAL FILTER WITHOUT CAREFUL ANALYSIS ON RUNTIME!!! - * - * Key Observation: - * For LUT-based designs it is impossible for the average fanout to exceed - * the number of LUT inputs so it's usually around 4-5 (pigeon-hole argument, - * if the average fanout is greater than the number of LUT inputs, where do - * the extra connections go? Therefore, average fanout must be capped to a - * small constant where the constant is equal to the number of LUT inputs). - * The real danger to runtime is when the number of sinks of a net gets doubled - */ - - //Check if all the net sinks are, in fact, inside this cluster - bool all_sinks_in_cur_cluster = true; - LegalizationClusterId driver_cluster = atom_cluster[driver_blk_id]; - for (auto pin_id : atom_ctx.netlist().net_sinks(net_id)) { - auto sink_blk_id = atom_ctx.netlist().pin_block(pin_id); - if (atom_cluster[sink_blk_id] != driver_cluster) { - all_sinks_in_cur_cluster = false; - break; - } - } - - if (all_sinks_in_cur_cluster) { - //All the sinks are part of this cluster, so the net may be fully absorbed. - // - //Verify this, by counting the number of net sinks reachable from the driver pin. - //If the count equals the number of net sinks then the net is fully absorbed and - //the net does not exit the cluster - /* TODO: I should cache the absorbed outputs, once net is absorbed, - * net is forever absorbed, no point in rechecking every time */ - if (net_sinks_reachable_in_cluster(pb_graph_pin, depth, net_id)) { - //All the sinks are reachable inside the cluster - net_exits_cluster = false; - } - } - } - - if (net_exits_cluster) { - /* This output must exit this cluster */ - cur_pb->pb_stats->lookahead_output_pins_used[pin_class].push_back(net_id); - } - } - } - } - - void ClusterLegalizer::compute_and_mark_lookahead_pins_used(const AtomBlockId blk_id, - const vtr::vector_map& atom_cluster) { - const AtomContext& atom_ctx = g_vpr_ctx.atom(); - - const t_pb* cur_pb = atom_pb_lookup().atom_pb(blk_id); - VTR_ASSERT(cur_pb != nullptr); - - /* Walk through inputs, outputs, and clocks marking pins off of the same class */ - for (auto pin_id : atom_ctx.netlist().block_pins(blk_id)) { - auto net_id = atom_ctx.netlist().pin_net(pin_id); - - const t_pb_graph_pin* pb_graph_pin = find_pb_graph_pin(atom_ctx.netlist(), atom_pb_lookup(), pin_id); - compute_and_mark_lookahead_pins_used_for_pin(pb_graph_pin, cur_pb, net_id, atom_cluster); - } - } - - void ClusterLegalizer::try_update_lookahead_pins_used(t_pb* cur_pb, - const vtr::vector_map& atom_cluster) { - // run recursively till a leaf (primitive) pb block is reached - const t_pb_type* pb_type = cur_pb->pb_graph_node->pb_type; - if (pb_type->num_modes > 0 && cur_pb->name != nullptr) { - if (cur_pb->child_pbs != nullptr) { - for (int i = 0; i < pb_type->modes[cur_pb->mode].num_pb_type_children; i++) { - if (cur_pb->child_pbs[i] != nullptr) { - for (int j = 0; j < pb_type->modes[cur_pb->mode].pb_type_children[i].num_pb; j++) { - try_update_lookahead_pins_used(&cur_pb->child_pbs[i][j], atom_cluster); - } - } - } - } - } else { - // find if this child (primitive) pb block has an atom mapped to it, - // if yes compute and mark lookahead pins used for that pb block - AtomBlockId blk_id = atom_pb_lookup().pb_atom(cur_pb); - if (pb_type->blif_model != nullptr && blk_id) { - compute_and_mark_lookahead_pins_used(blk_id, atom_cluster); - } - } - } - - bool ClusterLegalizer::check_lookahead_pins_used(t_pb* cur_pb, t_ext_pin_util max_external_pin_util) { - const t_pb_type* pb_type = cur_pb->pb_graph_node->pb_type; - - if (pb_type->num_modes > 0 && cur_pb->name) { - for (int i = 0; i < cur_pb->pb_graph_node->num_input_pin_class; i++) { - size_t class_size = cur_pb->pb_graph_node->input_pin_class_size[i]; - - if (cur_pb->is_root()) { - // Scale the class size by the maximum external pin utilization factor - // Use ceil to avoid classes of size 1 from being scaled to zero - class_size = std::ceil(max_external_pin_util.input_pin_util * class_size); - // if the number of pins already used is larger than class size, then the number of - // cluster inputs already used should be our constraint. Why is this needed? This is - // needed since when packing the seed block the maximum external pin utilization is - // used as 1.0 allowing molecules that are using up to all the cluster inputs to be - // packed legally. Therefore, if the seed block is already using more inputs than - // the allowed maximum utilization, this should become the new maximum pin utilization. - class_size = std::max(class_size, cur_pb->pb_stats->input_pins_used[i].size()); - } - - if (cur_pb->pb_stats->lookahead_input_pins_used[i].size() > class_size) { - return false; - } - } - - for (int i = 0; i < cur_pb->pb_graph_node->num_output_pin_class; i++) { - size_t class_size = cur_pb->pb_graph_node->output_pin_class_size[i]; - if (cur_pb->is_root()) { - // Scale the class size by the maximum external pin utilization factor - // Use ceil to avoid classes of size 1 from being scaled to zero - class_size = std::ceil(max_external_pin_util.output_pin_util * class_size); - // if the number of pins already used is larger than class size, then the number of - // cluster outputs already used should be our constraint. Why is this needed? This is - // needed since when packing the seed block the maximum external pin utilization is - // used as 1.0 allowing molecules that are using up to all the cluster inputs to be - // packed legally. Therefore, if the seed block is already using more inputs than - // the allowed maximum utilization, this should become the new maximum pin utilization. - class_size = std::max(class_size, cur_pb->pb_stats->output_pins_used[i].size()); - } - - if (cur_pb->pb_stats->lookahead_output_pins_used[i].size() > class_size) { - return false; - } - } - - if (cur_pb->child_pbs) { - for (int i = 0; i < pb_type->modes[cur_pb->mode].num_pb_type_children; i++) { - if (cur_pb->child_pbs[i]) { - for (int j = 0; j < pb_type->modes[cur_pb->mode].pb_type_children[i].num_pb; j++) { - if (!check_lookahead_pins_used(&cur_pb->child_pbs[i][j], max_external_pin_util)) - return false; - } - } - } - } - } - - return true; - } - - void ClusterLegalizer::update_clustering_chain_info(PackMoleculeId chain_molecule_id, - const t_pb_graph_node* root_primitive) { - // Get the molecule - VTR_ASSERT(chain_molecule_id.is_valid()); - const t_pack_molecule& chain_molecule = prepacker_.get_molecule(chain_molecule_id); - - // Get the ID of the chain it is a part of - MoleculeChainId chain_id = chain_molecule.chain_id; - VTR_ASSERT(chain_id.is_valid()); - - // Get the prepacking and clustering information on this chain. - const t_chain_info& prepack_chain_info = prepacker_.get_molecule_chain_info(chain_id); - t_clustering_chain_info& clustering_chain_info = clustering_chain_info_[chain_id]; - VTR_ASSERT(clustering_chain_info.chain_id == -1 && prepack_chain_info.is_long_chain); - - // Update the clustering chain information. - // long chains should only be placed at the beginning of the chain - // Since for long chains the molecule size is already equal to the - // total number of adders in the cluster. Therefore, it should - // always be placed at the very first adder in this cluster. - auto chain_root_pins = chain_molecule.pack_pattern->chain_root_pins; - for (size_t chainId = 0; chainId < chain_root_pins.size(); chainId++) { - if (chain_root_pins[chainId][0]->parent_node == root_primitive) { - clustering_chain_info.chain_id = chainId; - clustering_chain_info.first_packed_molecule = chain_molecule_id; - return; - } - } - - VTR_ASSERT(false); - } - - void ClusterLegalizer::reset_molecule_info(PackMoleculeId mol_id) { - VTR_ASSERT(mol_id.is_valid()); - - // when invalidating a molecule check if it's a chain molecule - // that is part of a long chain. If so, check if this molecule - // has modified the chain_id value based on the stale packing - // then reset the chain id and the first packed molecule pointer - // this is packing is being reset - const t_pack_molecule& mol = prepacker_.get_molecule(mol_id); - if (!mol.is_chain()) - return; - - VTR_ASSERT(mol.chain_id.is_valid()); - const t_chain_info& prepack_chain_info = prepacker_.get_molecule_chain_info(mol.chain_id); - if (!prepack_chain_info.is_long_chain) - return; - - t_clustering_chain_info& clustering_chain_info = clustering_chain_info_[mol.chain_id]; - if (clustering_chain_info.first_packed_molecule == mol_id) { - clustering_chain_info.first_packed_molecule = PackMoleculeId::INVALID(); - clustering_chain_info.chain_id = -1; - } - } - - void ClusterLegalizer::revert_place_atom_block(const AtomBlockId blk_id, - t_lb_router_data* router_data, - vtr::vector_map& atom_cluster) { - //We cast away const here since we may free the pb, and it is - //being removed from the active mapping. - // - //In general most code works fine accessing cosnt t_pb*, - //which is why we store them as such in atom_ctx.lookup() - t_pb* pb = const_cast(atom_pb_lookup().atom_pb(blk_id)); - - if (pb != nullptr) { - /* When freeing molecules, the current block might already have been freed by a prior revert - * When this happens, no need to do anything beyond basic book keeping at the atom block - */ - - t_pb* next = pb->parent_pb; - free_pb(pb); - pb = next; - - while (pb != nullptr) { - /* If this is pb is created only for the purposes of holding new molecule, remove it - * Must check if cluster is already freed (which can be the case) - */ - next = pb->parent_pb; - - if (pb->child_pbs != nullptr && pb->pb_stats != nullptr - && pb->pb_stats->num_child_blocks_in_pb == 0) { - set_reset_pb_modes(router_data, pb, false); - if (next != nullptr) { - /* If the code gets here, then that means that placing the initial seed molecule - * failed, don't free the actual complex block itself as the seed needs to find - * another placement */ - free_pb(pb); - } - } - pb = next; - } - } - - //Update the atom netlist mapping - atom_cluster[blk_id] = LegalizationClusterId::INVALID(); - mutable_atom_pb_lookup().set_atom_pb(blk_id, nullptr); - } - - void ClusterLegalizer::commit_lookahead_pins_used(t_pb* cur_pb) { - const t_pb_type* pb_type = cur_pb->pb_graph_node->pb_type; - - if (pb_type->num_modes > 0 && cur_pb->name) { - for (int i = 0; i < cur_pb->pb_graph_node->num_input_pin_class; i++) { - VTR_ASSERT(cur_pb->pb_stats->lookahead_input_pins_used[i].size() <= (unsigned int)cur_pb->pb_graph_node->input_pin_class_size[i]); - for (size_t j = 0; j < cur_pb->pb_stats->lookahead_input_pins_used[i].size(); j++) { - VTR_ASSERT(cur_pb->pb_stats->lookahead_input_pins_used[i][j]); - cur_pb->pb_stats->input_pins_used[i].insert({j, cur_pb->pb_stats->lookahead_input_pins_used[i][j]}); - } - } - - for (int i = 0; i < cur_pb->pb_graph_node->num_output_pin_class; i++) { - VTR_ASSERT(cur_pb->pb_stats->lookahead_output_pins_used[i].size() <= (unsigned int)cur_pb->pb_graph_node->output_pin_class_size[i]); - for (size_t j = 0; j < cur_pb->pb_stats->lookahead_output_pins_used[i].size(); j++) { - VTR_ASSERT(cur_pb->pb_stats->lookahead_output_pins_used[i][j]); - cur_pb->pb_stats->output_pins_used[i].insert({j, cur_pb->pb_stats->lookahead_output_pins_used[i][j]}); - } - } - - if (cur_pb->child_pbs) { - for (int i = 0; i < pb_type->modes[cur_pb->mode].num_pb_type_children; i++) { - if (cur_pb->child_pbs[i]) { - for (int j = 0; j < pb_type->modes[cur_pb->mode].pb_type_children[i].num_pb; j++) { - commit_lookahead_pins_used(&cur_pb->child_pbs[i][j]); - } - } - } - } - } - } - - bool ClusterLegalizer::cleanup_pb(t_pb* pb) { - bool can_free = true; - - /* Recursively check if there are any children with already assigned atoms */ - if (pb->child_pbs != nullptr) { - const t_mode* mode = &pb->pb_graph_node->pb_type->modes[pb->mode]; - VTR_ASSERT(mode != nullptr); - - /* Check each mode */ - for (int i = 0; i < mode->num_pb_type_children; ++i) { - /* Check each child */ - if (pb->child_pbs[i] != nullptr) { - for (int j = 0; j < mode->pb_type_children[i].num_pb; ++j) { - t_pb* pb_child = &pb->child_pbs[i][j]; - t_pb_type* pb_type = pb_child->pb_graph_node->pb_type; - - /* Primitive, check occupancy */ - if (pb_type->num_modes == 0) { - if (pb_child->name != nullptr) { - can_free = false; - } - } - - /* Non-primitive, recurse */ - else { - if (!cleanup_pb(pb_child)) { - can_free = false; - } - } - } - } - } - - /* Free if can */ - if (can_free) { - for (int i = 0; i < mode->num_pb_type_children; ++i) { - if (pb->child_pbs[i] != nullptr) { - delete[] pb->child_pbs[i]; - } - } - - delete[] pb->child_pbs; - pb->child_pbs = nullptr; - pb->mode = 0; - - if (pb->name) { - free(pb->name); - pb->name = nullptr; - } - } - } - - return can_free; - } - - e_block_pack_status ClusterLegalizer::try_pack_molecule(PackMoleculeId molecule_id, - LegalizationCluster& cluster, - LegalizationClusterId cluster_id, - const t_ext_pin_util& max_external_pin_util) { - // Try to pack the molecule into a cluster with this pb type. - - // Safety debugs. - VTR_ASSERT_DEBUG(molecule_id.is_valid()); - VTR_ASSERT_DEBUG(cluster.pb != nullptr); - VTR_ASSERT_DEBUG(cluster.type != nullptr); - - // TODO: Remove these global accesses to the contexts. - // AtomContext used for: - // - printing verbose statements - // - Looking up the primitive pb - const AtomContext& atom_ctx = g_vpr_ctx.atom(); - // FloorplanningContext used for: - // - Checking if the atom can be placed in the cluster for floorplanning - // constraints. - const FloorplanningContext& floorplanning_ctx = g_vpr_ctx.floorplanning(); - - // Get the molecule object. - const t_pack_molecule& molecule = prepacker_.get_molecule(molecule_id); - - if (log_verbosity_ > 3) { - AtomBlockId root_atom = molecule.atom_block_ids[molecule.root]; - VTR_LOG("\t\tTry pack molecule: '%s' (%s)", - atom_ctx.netlist().block_name(root_atom).c_str(), - atom_ctx.netlist().block_model(root_atom)->name); - VTR_LOGV(molecule.pack_pattern, - " molecule_type %s molecule_size %zu", - molecule.pack_pattern->name, - molecule.atom_block_ids.size()); - VTR_LOG("\n"); - } - - // if this cluster has a molecule placed in it that is part of a long chain - // (a chain that consists of more than one molecule), don't allow more long chain - // molecules to be placed in this cluster. To avoid possibly creating cluster level - // blocks that have incompatible placement constraints or form very long placement - // macros that limit placement flexibility. - if (cluster.placement_stats->has_long_chain && molecule.is_chain() && prepacker_.get_molecule_chain_info(molecule.chain_id).is_long_chain) { - VTR_LOGV(log_verbosity_ > 4, "\t\t\tFAILED Placement Feasibility Filter: Only one long chain per cluster is allowed\n"); - return e_block_pack_status::BLK_FAILED_FEASIBLE; - } - - // Check if every atom in the molecule is legal in the cluster from a - // floorplanning perspective - bool cluster_pr_update_check = false; - PartitionRegion new_cluster_pr = cluster.pr; - // TODO: This can be made more efficient by pre-computing the intersection - // of all the atoms' PRs in the molecule. - for (AtomBlockId atom_blk_id : molecule.atom_block_ids) { - if (!atom_blk_id.is_valid()) - continue; - - // Try to intersect with atom PartitionRegion if atom exists - bool cluster_pr_needs_update = false; - bool block_pack_floorplan_status = check_cluster_floorplanning(atom_blk_id, - new_cluster_pr, - floorplanning_ctx.constraints, - log_verbosity_, - cluster_pr_needs_update); - if (!block_pack_floorplan_status) { - return e_block_pack_status::BLK_FAILED_FLOORPLANNING; - } - - if (cluster_pr_needs_update) { - cluster_pr_update_check = true; - } - } - - // Check if all atoms in the molecule can be added to the cluster without - // NoC group conflicts - NocGroupId new_cluster_noc_grp_id = cluster.noc_grp_id; - for (AtomBlockId atom_blk_id : molecule.atom_block_ids) { - if (!atom_blk_id.is_valid()) - continue; - - bool block_pack_noc_grp_status = check_cluster_noc_group(atom_blk_id, - new_cluster_noc_grp_id, - atom_noc_grp_id_, - log_verbosity_); - if (!block_pack_noc_grp_status) { - return e_block_pack_status::BLK_FAILED_NOC_GROUP; - } - } - - std::vector primitives_list(max_molecule_size_, nullptr); - e_block_pack_status block_pack_status = e_block_pack_status::BLK_STATUS_UNDEFINED; - while (block_pack_status != e_block_pack_status::BLK_PASSED) { - if (!get_next_primitive_list(cluster.placement_stats, - molecule_id, - primitives_list.data(), - prepacker_)) { - VTR_LOGV(log_verbosity_ > 3, "\t\tFAILED No candidate primitives available\n"); - block_pack_status = e_block_pack_status::BLK_FAILED_FEASIBLE; - break; /* no more candidate primitives available, this molecule will not pack, return fail */ - } - - block_pack_status = e_block_pack_status::BLK_PASSED; - size_t failed_location = 0; - for (size_t i_mol = 0; i_mol < molecule.atom_block_ids.size() && block_pack_status == e_block_pack_status::BLK_PASSED; i_mol++) { - VTR_ASSERT((primitives_list[i_mol] == nullptr) == (!molecule.atom_block_ids[i_mol])); - failed_location = i_mol + 1; - AtomBlockId atom_blk_id = molecule.atom_block_ids[i_mol]; - if (!atom_blk_id.is_valid()) - continue; - // NOTE: This parent variable is only used in the recursion of this - // function. - t_pb* parent = nullptr; - block_pack_status = try_place_atom_block_rec(primitives_list[i_mol], - atom_blk_id, - cluster.pb, - &parent, - cluster_id, - atom_cluster_, - molecule_id, - cluster.router_data, - log_verbosity_, - prepacker_, - clustering_chain_info_); - } - - if (enable_pin_feasibility_filter_ && block_pack_status == e_block_pack_status::BLK_PASSED) { - // Check if pin usage is feasible for the current packing assignment - reset_lookahead_pins_used(cluster.pb); - try_update_lookahead_pins_used(cluster.pb, atom_cluster_); - if (!check_lookahead_pins_used(cluster.pb, max_external_pin_util)) { - VTR_LOGV(log_verbosity_ > 4, "\t\t\tFAILED Pin Feasibility Filter\n"); - block_pack_status = e_block_pack_status::BLK_FAILED_FEASIBLE; - } else { - VTR_LOGV(log_verbosity_ > 3, "\t\t\tPin Feasibility: Passed pin feasibility filter\n"); - } - } - - if (block_pack_status == e_block_pack_status::BLK_PASSED) { - /* - * during the clustering step of `do_clustering`, `detailed_routing_stage` is incremented at each iteration until it a cluster - * is correctly generated or `detailed_routing_stage` assumes an invalid value (E_DETAILED_ROUTE_INVALID). - * depending on its value we have different behaviors: - * - E_DETAILED_ROUTE_AT_END_ONLY: Skip routing if heuristic is to route at the end of packing complex block. - * - E_DETAILED_ROUTE_FOR_EACH_ATOM: Try to route if heuristic is to route for every atom. If the clusterer arrives at this stage, - * it means that more checks have to be performed as the previous stage failed to generate a new cluster. - * - * mode_status is a data structure containing the status of the mode selection. Its members are: - * - bool is_mode_conflict - * - bool try_expand_all_modes - * - bool expand_all_modes - * - * is_mode_conflict affects this stage. Its value determines whether the cluster failed to pack after a mode conflict issue. - * It holds a flag that is used to verify whether try_intra_lb_route ended in a mode conflict issue. - * - * Until is_mode_conflict is set to FALSE by try_intra_lb_route, the loop re-iterates. If all the available modes are exhausted - * an error will be thrown during mode conflicts checks (this to prevent infinite loops). - * - * If the value is TRUE the cluster has to be re-routed, and its internal pb_graph_nodes will have more restrict choices - * for what regards the mode that has to be selected. - * - * is_mode_conflict is initially set to TRUE, and, unless a mode conflict is found, it is set to false in `try_intra_lb_route`. - * - * try_expand_all_modes is set if the node expansion failed to find a valid routing path. The clusterer tries to find another route - * by using all the modes during node expansion. - * - * expand_all_modes is used to enable the expansion of all the nodes using all the possible modes. - */ - t_mode_selection_status mode_status; - bool is_routed = false; - bool do_detailed_routing_stage = (cluster_legalization_strategy_ == ClusterLegalizationStrategy::FULL); - if (do_detailed_routing_stage) { - do { - reset_intra_lb_route(cluster.router_data); - is_routed = try_intra_lb_route(cluster.router_data, log_verbosity_, &mode_status); - } while (do_detailed_routing_stage && mode_status.is_mode_issue()); - } - - if (do_detailed_routing_stage && !is_routed) { - /* Cannot pack */ - VTR_LOGV(log_verbosity_ > 4, "\t\t\tFAILED Detailed Routing Legality\n"); - block_pack_status = e_block_pack_status::BLK_FAILED_ROUTE; - } else { - /* Pack successful, commit - * TODO: SW Engineering note - may want to update cluster stats here too instead of doing it outside - */ - VTR_ASSERT(block_pack_status == e_block_pack_status::BLK_PASSED); - if (molecule.is_chain()) { - /* Chained molecules often take up lots of area and are important, - * if a chain is packed in, want to rename logic block to match chain name */ - AtomBlockId chain_root_blk_id = molecule.atom_block_ids[molecule.pack_pattern->root_block->block_id]; - t_pb* cur_pb = atom_pb_lookup().atom_pb(chain_root_blk_id)->parent_pb; - while (cur_pb != nullptr) { - free(cur_pb->name); - cur_pb->name = vtr::strdup(atom_ctx.netlist().block_name(chain_root_blk_id).c_str()); - cur_pb = cur_pb->parent_pb; - } - // if this molecule is part of a chain, mark the cluster as having a long chain - // molecule. Also check if it's the first molecule in the chain to be packed. - // If so, update the chain id for this chain of molecules to make sure all - // molecules will be packed to the same chain id and can reach each other using - // the chain direct links between clusters - VTR_ASSERT(molecule.chain_id.is_valid()); - const t_chain_info& prepack_chain_info = prepacker_.get_molecule_chain_info(molecule.chain_id); - if (prepack_chain_info.is_long_chain) { - cluster.placement_stats->has_long_chain = true; - const t_clustering_chain_info& clustering_chain_info = clustering_chain_info_[molecule.chain_id]; - if (clustering_chain_info.chain_id == -1) { - update_clustering_chain_info(molecule_id, primitives_list[molecule.root]); - } - } - } - - //update cluster PartitionRegion if atom with floorplanning constraints was added - if (cluster_pr_update_check) { - cluster.pr = new_cluster_pr; - VTR_LOGV(log_verbosity_ > 2, "\nUpdated PartitionRegion of cluster\n"); - } - - // Update the cluster's NoC group ID. This is cheap so it does - // not need the check like the what the PR did above. - cluster.noc_grp_id = new_cluster_noc_grp_id; - - // Insert the molecule into the cluster for bookkeeping. - cluster.molecules.push_back(molecule_id); - - for (size_t i = 0; i < molecule.atom_block_ids.size(); i++) { - AtomBlockId atom_blk_id = molecule.atom_block_ids[i]; - if (!atom_blk_id.is_valid()) - continue; - - commit_primitive(cluster.placement_stats, primitives_list[i]); - - atom_cluster_[atom_blk_id] = cluster_id; - - // Update the num child blocks in pb - const t_pb* atom_pb = atom_pb_lookup().atom_pb(atom_blk_id); - VTR_ASSERT_SAFE(atom_pb != nullptr); - t_pb* cur_pb = atom_pb->parent_pb; - while (cur_pb != nullptr) { - cur_pb->pb_stats->num_child_blocks_in_pb++; - cur_pb = cur_pb->parent_pb; - } - } - - // Update the lookahead pins used. - commit_lookahead_pins_used(cluster.pb); - } - } - - if (block_pack_status != e_block_pack_status::BLK_PASSED) { - /* Pack unsuccessful, undo inserting molecule into cluster */ - for (size_t i = 0; i < failed_location; i++) { - AtomBlockId atom_blk_id = molecule.atom_block_ids[i]; - if (atom_blk_id) { - remove_atom_from_target(cluster.router_data, atom_blk_id, atom_pb_lookup()); - } - } - for (size_t i = 0; i < failed_location; i++) { - AtomBlockId atom_blk_id = molecule.atom_block_ids[i]; - if (atom_blk_id) { - revert_place_atom_block(atom_blk_id, cluster.router_data, atom_cluster_); - } - } - reset_molecule_info(molecule_id); - - /* Packing failed, but a part of the pb tree is still allocated and pbs have their modes set. - * Before trying to pack next molecule the unused pbs need to be freed and, the most important, - * their modes reset. This task is performed by the cleanup_pb() function below. */ - cleanup_pb(cluster.pb); - } else { - VTR_LOGV(log_verbosity_ > 3, "\t\tPASSED pack molecule\n"); - } - } - - // Reset the cluster placement stats after packing a molecule. - // TODO: Not sure if this has to go here, but it makes sense to do it. - reset_tried_but_unused_cluster_placements(cluster.placement_stats); - - return block_pack_status; - } - - std::tuple - ClusterLegalizer::start_new_cluster(PackMoleculeId molecule_id, - t_logical_block_type_ptr cluster_type, - int cluster_mode) { - // Safety asserts to ensure the API is being called with valid arguments. - VTR_ASSERT_DEBUG(molecule_id.is_valid()); - VTR_ASSERT_DEBUG(cluster_type != nullptr); - VTR_ASSERT_DEBUG(cluster_mode < cluster_type->pb_graph_head->pb_type->num_modes); - // Ensure that the molecule has not already been placed. - VTR_ASSERT_SAFE(!molecule_cluster_[molecule_id].is_valid()); - // Safety asserts to ensure that the API was initialized properly. - VTR_ASSERT_DEBUG(lb_type_rr_graphs_ != nullptr); - - const AtomNetlist& atom_nlist = g_vpr_ctx.atom().netlist(); - - // Create the physical block for this cluster based on the type. - t_pb* cluster_pb = new t_pb; - cluster_pb->pb_graph_node = cluster_type->pb_graph_head; - alloc_and_load_pb_stats(cluster_pb); - cluster_pb->parent_pb = nullptr; - cluster_pb->mode = cluster_mode; - - // Allocate and load the LB router data - t_lb_router_data* router_data = alloc_and_load_router_data(&lb_type_rr_graphs_[cluster_type->index], - cluster_type); - - // Allocate and load the cluster's placement stats - t_intra_cluster_placement_stats* cluster_placement_stats = alloc_and_load_cluster_placement_stats(cluster_type, cluster_mode); - - // Create the new cluster - LegalizationCluster new_cluster; - new_cluster.pb = cluster_pb; - new_cluster.router_data = router_data; - new_cluster.pr = PartitionRegion(); - new_cluster.noc_grp_id = NocGroupId::INVALID(); - new_cluster.type = cluster_type; - new_cluster.placement_stats = cluster_placement_stats; - - // Try to pack the molecule into the new_cluster. - // When starting a new cluster, we set the external pin utilization to full - // (meaning all cluster pins are allowed to be used). - const t_ext_pin_util FULL_EXTERNAL_PIN_UTIL(1., 1.); - LegalizationClusterId new_cluster_id = LegalizationClusterId(legalization_cluster_ids_.size()); - e_block_pack_status pack_status = try_pack_molecule(molecule_id, - new_cluster, - new_cluster_id, - FULL_EXTERNAL_PIN_UTIL); - - if (pack_status == e_block_pack_status::BLK_PASSED) { - // Give the new cluster pb a name. The current convention is to name the - // cluster after the root atom of the first molecule packed into it. - const t_pack_molecule& molecule = prepacker_.get_molecule(molecule_id); - AtomBlockId root_atom = molecule.atom_block_ids[molecule.root]; - const std::string& root_atom_name = atom_nlist.block_name(root_atom); - if (new_cluster.pb->name != nullptr) - free(new_cluster.pb->name); - new_cluster.pb->name = vtr::strdup(root_atom_name.c_str()); - // Move the cluster into the vector of clusters and ids. - legalization_cluster_ids_.push_back(new_cluster_id); - legalization_clusters_.push_back(std::move(new_cluster)); - // Update the molecule to cluster map. - molecule_cluster_[molecule_id] = new_cluster_id; - } else { - // Delete the new_cluster. - free_pb(new_cluster.pb); - delete new_cluster.pb; - free_router_data(new_cluster.router_data); - free_cluster_placement_stats(new_cluster.placement_stats); - new_cluster_id = LegalizationClusterId::INVALID(); - } - - return {pack_status, new_cluster_id}; - } - - e_block_pack_status ClusterLegalizer::add_mol_to_cluster(PackMoleculeId molecule_id, - LegalizationClusterId cluster_id) { - // Safety asserts to make sure the inputs are valid. - VTR_ASSERT_SAFE(cluster_id.is_valid() && (size_t)cluster_id < legalization_clusters_.size()); - VTR_ASSERT(legalization_cluster_ids_[cluster_id].is_valid() && "Cannot add to a destroyed cluster"); - // Ensure that the molecule has not already been placed. - VTR_ASSERT(!molecule_cluster_[molecule_id].is_valid()); - // Safety asserts to ensure that the API was initialized properly. - VTR_ASSERT_DEBUG(lb_type_rr_graphs_ != nullptr); - - // Get the cluster. - LegalizationCluster& cluster = legalization_clusters_[cluster_id]; - VTR_ASSERT(cluster.router_data != nullptr && cluster.placement_stats != nullptr - && "Cannot add molecule to cleaned cluster!"); - // Set the target_external_pin_util. - t_ext_pin_util target_ext_pin_util = target_external_pin_util_.get_pin_util(cluster.type->name); - // Try to pack the molecule into the cluster. - e_block_pack_status pack_status = try_pack_molecule(molecule_id, - cluster, - cluster_id, - target_ext_pin_util); - - // If the packing was successful, set the molecules' cluster to this one. - if (pack_status == e_block_pack_status::BLK_PASSED) - molecule_cluster_[molecule_id] = cluster_id; - - return pack_status; - } - - void ClusterLegalizer::destroy_cluster(LegalizationClusterId cluster_id) { - // Safety asserts to make sure the inputs are valid. - VTR_ASSERT_SAFE(cluster_id.is_valid() && (size_t)cluster_id < legalization_clusters_.size()); - VTR_ASSERT(legalization_cluster_ids_[cluster_id].is_valid() && "Cannot destroy an already destroyed cluster"); - // Get the cluster. - LegalizationCluster& cluster = legalization_clusters_[cluster_id]; - // Remove all molecules from the cluster. - for (PackMoleculeId mol_id : cluster.molecules) { - VTR_ASSERT_SAFE(molecule_cluster_[mol_id] == cluster_id); - molecule_cluster_[mol_id] = LegalizationClusterId::INVALID(); - // Revert the placement of all blocks in the molecule. - const t_pack_molecule& mol = prepacker_.get_molecule(mol_id); - for (AtomBlockId atom_blk_id : mol.atom_block_ids) { - if (atom_blk_id) { - revert_place_atom_block(atom_blk_id, cluster.router_data, atom_cluster_); - } - } - reset_molecule_info(mol_id); - molecule_cluster_[mol_id] = LegalizationClusterId::INVALID(); - } - cluster.molecules.clear(); - // Free the rest of the cluster data. - // Casting things to nullptr for safety just in case someone is trying to use it. - free_pb(cluster.pb); - delete cluster.pb; - cluster.pb = nullptr; - free_router_data(cluster.router_data); - cluster.router_data = nullptr; - cluster.pr = PartitionRegion(); - free_cluster_placement_stats(cluster.placement_stats); - cluster.placement_stats = nullptr; - - // Mark the cluster as invalid. - legalization_cluster_ids_[cluster_id] = LegalizationClusterId::INVALID(); - } - - void ClusterLegalizer::compress() { - // Create a map from the old ids to the new (compressed) one. - vtr::vector_map cluster_id_map; - cluster_id_map = compress_ids(legalization_cluster_ids_); - // Update all cluster values. - legalization_cluster_ids_ = clean_and_reorder_ids(cluster_id_map); - legalization_clusters_ = clean_and_reorder_values(legalization_clusters_, cluster_id_map); - // Update the reverse lookups. - for (PackMoleculeId mol_id : prepacker_.molecules()) { - LegalizationClusterId old_cluster_id = molecule_cluster_[mol_id]; - if (!old_cluster_id.is_valid()) - continue; - molecule_cluster_[mol_id] = cluster_id_map[old_cluster_id]; - } - for (size_t i = 0; i < atom_cluster_.size(); i++) { - AtomBlockId atom_blk_id = AtomBlockId(i); - LegalizationClusterId old_cluster_id = atom_cluster_[atom_blk_id]; - if (!old_cluster_id.is_valid()) - continue; - atom_cluster_[atom_blk_id] = cluster_id_map[old_cluster_id]; - } - // Shrink everything to fit - legalization_cluster_ids_.shrink_to_fit(); - legalization_clusters_.shrink_to_fit(); - atom_cluster_.shrink_to_fit(); - } - - void ClusterLegalizer::clean_cluster(LegalizationClusterId cluster_id) { - // Safety asserts to make sure the inputs are valid. - VTR_ASSERT_SAFE(cluster_id.is_valid() && (size_t)cluster_id < legalization_clusters_.size()); - // Get the cluster. - LegalizationCluster& cluster = legalization_clusters_[cluster_id]; - VTR_ASSERT(cluster.router_data != nullptr && cluster.placement_stats != nullptr - && "Should not clean an already cleaned cluster!"); - // Free the pb stats. - free_pb_stats_recursive(cluster.pb); - // Load the pb_route so we can free the cluster router data. - // The pb_route is used when creating a netlist from the legalized clusters. - std::vector* saved_lb_nets = cluster.router_data->saved_lb_nets; - t_pb_graph_node* pb_graph_node = cluster.pb->pb_graph_node; - cluster.pb->pb_route = alloc_and_load_pb_route(saved_lb_nets, pb_graph_node); - // Free the router data. - free_router_data(cluster.router_data); - cluster.router_data = nullptr; - // Free the cluster placement stats. - free_cluster_placement_stats(cluster.placement_stats); - cluster.placement_stats = nullptr; - } - - // TODO: This is fine for the current implementation of the legalizer. But if - // more complex strategies are added, this will need to be updated to - // check more than just routing (such as PR and NoC groups). - bool ClusterLegalizer::check_cluster_legality(LegalizationClusterId cluster_id) { - // Safety asserts to make sure the inputs are valid. - VTR_ASSERT_SAFE(cluster_id.is_valid() && (size_t)cluster_id < legalization_clusters_.size()); - // To check if a cluster is fully legal, try to perform an intra logic block - // route on the cluster. If it succeeds, the cluster is fully legal. - t_mode_selection_status mode_status; - LegalizationCluster& cluster = legalization_clusters_[cluster_id]; - return try_intra_lb_route(cluster.router_data, log_verbosity_, &mode_status); - } - - ClusterLegalizer::ClusterLegalizer(const AtomNetlist& atom_netlist, - const Prepacker& prepacker, - std::vector* lb_type_rr_graphs, - const std::vector& target_external_pin_util_str, - const t_pack_high_fanout_thresholds& high_fanout_thresholds, - ClusterLegalizationStrategy cluster_legalization_strategy, - bool enable_pin_feasibility_filter, - int log_verbosity) : prepacker_(prepacker) - { - // Verify that the inputs are valid. - VTR_ASSERT_SAFE(lb_type_rr_graphs != nullptr); - - // Get the target external pin utilization - // NOTE: Be careful with this constructor, it may throw a VPR_FATAL_ERROR. - target_external_pin_util_ = t_ext_pin_util_targets(target_external_pin_util_str); - - // Resize the molecule_cluster lookup to make the accesses much cheaper. - molecule_cluster_.resize(prepacker_.molecules().size(), LegalizationClusterId::INVALID()); - // Resize the atom_cluster lookup to make the accesses much cheaper. - atom_cluster_.resize(atom_netlist.blocks().size(), LegalizationClusterId::INVALID()); - // Default the clustering chain info for each chain. - clustering_chain_info_.resize(prepacker_.get_num_molecule_chains()); - // Pre-compute the max size of any molecule. - max_molecule_size_ = prepacker.get_max_molecule_size(); - // Get a reference to the rr graphs. - lb_type_rr_graphs_ = lb_type_rr_graphs; - // Find all NoC router atoms. - std::vector noc_atoms = find_noc_router_atoms(atom_netlist); - update_noc_reachability_partitions(noc_atoms, - atom_netlist, - high_fanout_thresholds, - atom_noc_grp_id_); - // Copy the options passed by the user - cluster_legalization_strategy_ = cluster_legalization_strategy; - enable_pin_feasibility_filter_ = enable_pin_feasibility_filter; - log_verbosity_ = log_verbosity; - atom_pb_lookup_ = AtomPBLookUp(g_vpr_ctx.atom().lookup().atom_to_pb()); - } - - void ClusterLegalizer::reset() { - // Destroy all of the clusters and compress. - for (LegalizationClusterId cluster_id : legalization_cluster_ids_) { - if (!cluster_id.is_valid()) - continue; - destroy_cluster(cluster_id); - } - compress(); - } - - void ClusterLegalizer::verify() { - std::unordered_set atoms_checked; - auto& atom_ctx = g_vpr_ctx.atom(); - - if (clusters().size() == 0) { - VTR_LOG_WARN("Packing produced no clustered blocks"); - } - - /* - * Check that each atom block connects to one physical primitive and that the primitive links up to the parent clb - */ - for (auto blk_id : atom_ctx.netlist().blocks()) { - //Each atom should be part of a pb - const t_pb* atom_pb = atom_pb_lookup().atom_pb(blk_id); - if (!atom_pb) { - VPR_FATAL_ERROR(VPR_ERROR_PACK, - "Atom block %s is not mapped to a pb\n", - atom_ctx.netlist().block_name(blk_id).c_str()); - } - - //Check the reverse mapping is consistent - if (atom_pb_lookup().pb_atom(atom_pb) != blk_id) { - VPR_FATAL_ERROR(VPR_ERROR_PACK, - "pb %s does not contain atom block %s but atom block %s maps to pb.\n", - atom_pb->name, - atom_ctx.netlist().block_name(blk_id).c_str(), - atom_ctx.netlist().block_name(blk_id).c_str()); - } - - VTR_ASSERT(atom_ctx.netlist().block_name(blk_id) == atom_pb->name); - - const t_pb* cur_pb = atom_pb; - while (cur_pb->parent_pb) { - cur_pb = cur_pb->parent_pb; - VTR_ASSERT(cur_pb->name); - } - - LegalizationClusterId cluster_id = get_atom_cluster(blk_id); - if (cluster_id == LegalizationClusterId::INVALID()) { - VPR_FATAL_ERROR(VPR_ERROR_PACK, - "Atom %s is not mapped to a CLB\n", - atom_ctx.netlist().block_name(blk_id).c_str()); - } - - if (cur_pb != get_cluster_pb(cluster_id)) { - VPR_FATAL_ERROR(VPR_ERROR_PACK, - "CLB %s does not match CLB contained by pb %s.\n", - cur_pb->name, atom_pb->name); - } - } - - /* Check that I do not have spurious links in children pbs */ - for (LegalizationClusterId cluster_id : clusters()) { - if (!cluster_id.is_valid()) - continue; - check_cluster_atom_blocks(get_cluster_pb(cluster_id), - atoms_checked); - } - - for (auto blk_id : atom_ctx.netlist().blocks()) { - if (!atoms_checked.count(blk_id)) { - VPR_FATAL_ERROR(VPR_ERROR_PACK, - "Atom block %s not found in any cluster.\n", - atom_ctx.netlist().block_name(blk_id).c_str()); - } - } - } - - bool ClusterLegalizer::is_molecule_compatible(PackMoleculeId molecule_id, - LegalizationClusterId cluster_id) const { - VTR_ASSERT_SAFE(molecule_id.is_valid()); - VTR_ASSERT_SAFE(cluster_id.is_valid() && (size_t)cluster_id < legalization_clusters_.size()); - // Go through each atom in the molecule and check if there exists a free - // primitive for that atom block. - // TODO: This should probably also check if there are enough free primitives - // to support the given molecule. For example, a molecule of two FFs, - // but the cluster only has one free FF. This was something that Jason - // Luu was debating. Checking if placement exists for full molecule - // would be more robust, but checking individual atoms is faster. - const LegalizationCluster& cluster = legalization_clusters_[cluster_id]; - - const t_pack_molecule& molecule = prepacker_.get_molecule(molecule_id); - for (AtomBlockId atom_blk_id : molecule.atom_block_ids) { - // FIXME: Why is it possible that molecules contain invalid block IDs? - // This should be fixed! - if (!atom_blk_id.is_valid()) - continue; - // FIXME: This assert does not make sense. Can still check this even - // if the atom was clustered. - VTR_ASSERT(!is_atom_clustered(atom_blk_id)); - if (!exists_free_primitive_for_atom_block(cluster.placement_stats, - atom_blk_id)) { - return false; - } - } - // If every atom in the molecule has a free primitive it could theoretically - // be placed in, then it is compatible. - // TODO: Maybe add some more quick checks to save time, such as PR or NoC - // groups. - return true; - } - - size_t ClusterLegalizer::get_num_cluster_inputs_available( - LegalizationClusterId cluster_id) const { - VTR_ASSERT_SAFE(cluster_id.is_valid() && (size_t)cluster_id < legalization_clusters_.size()); - const LegalizationCluster& cluster = legalization_clusters_[cluster_id]; - - // Count the number of inputs available per pin class. - size_t inputs_avail = 0; - for (int i = 0; i < cluster.pb->pb_graph_node->num_input_pin_class; i++) { - inputs_avail += cluster.pb->pb_stats->input_pins_used[i].size(); - } - - return inputs_avail; - } - - void ClusterLegalizer::finalize() { - for (LegalizationClusterId cluster_id : legalization_cluster_ids_) { - if (!cluster_id.is_valid()) - continue; - // If the cluster has not already been cleaned, clean it. This will - // generate the pb_route necessary for generating a clustered netlist. - const LegalizationCluster& cluster = legalization_clusters_[cluster_id]; - if (cluster.router_data != nullptr) - clean_cluster(cluster_id); - } - } - - void ClusterLegalizer::free_pb(t_pb* pb) { - if (pb == nullptr) { - return; - } - - const t_pb_type* pb_type; - int i, j, mode; - - pb_type = pb->pb_graph_node->pb_type; - - if (pb->name) { - free(pb->name); - pb->name = nullptr; - } - - if (pb_type->blif_model == nullptr) { - mode = pb->mode; - for (i = 0; i < pb_type->modes[mode].num_pb_type_children && pb->child_pbs != nullptr; i++) { - for (j = 0; j < pb_type->modes[mode].pb_type_children[i].num_pb && pb->child_pbs[i] != nullptr; j++) { - if (pb->child_pbs[i][j].name != nullptr || pb->child_pbs[i][j].child_pbs != nullptr) { - free_pb(&pb->child_pbs[i][j]); - } - } - if (pb->child_pbs[i]) { - //Free children (num_pb) - delete[] pb->child_pbs[i]; - } - } - if (pb->child_pbs) { - //Free child pointers (modes) - delete[] pb->child_pbs; - } - - pb->child_pbs = nullptr; - - } else { - /* Primitive */ - auto& atom_ctx = g_vpr_ctx.mutable_atom(); - auto blk_id = atom_pb_lookup().pb_atom(pb); - if (blk_id) { - //Update atom netlist mapping - atom_ctx.mutable_lookup().set_atom_clb(blk_id, ClusterBlockId::INVALID()); - atom_pb_lookup_.set_atom_pb(blk_id, nullptr); - } - atom_pb_lookup_.set_atom_pb(AtomBlockId::INVALID(), pb); - } - - if (pb && pb->pb_stats != nullptr) { - delete pb->pb_stats; - pb->pb_stats = nullptr; - } - } - - ClusterLegalizer::~ClusterLegalizer() { - // Destroy all clusters (no need to compress). - for (LegalizationClusterId cluster_id : legalization_cluster_ids_) { - if (!cluster_id.is_valid()) - continue; - destroy_cluster(cluster_id); - } - } - - AtomPBLookUp::AtomPBLookUp(const vtr::bimap& atom_to_pb) { - atom_to_pb_ = atom_to_pb; - } - - const t_pb* AtomPBLookUp::atom_pb(const AtomBlockId blk_id) const { - auto iter = atom_to_pb_.find(blk_id); - if (iter == atom_to_pb_.end()) { - //Not found - return nullptr; - } - return iter->second; - } - - AtomBlockId AtomPBLookUp::pb_atom(const t_pb* pb) const { - auto iter = atom_to_pb_.find(pb); - if (iter == atom_to_pb_.inverse_end()) { - //Not found - return AtomBlockId::INVALID(); - } - return iter->second; - } - - const t_pb_graph_node* AtomPBLookUp::atom_pb_graph_node(const AtomBlockId blk_id) const { - const t_pb* pb = atom_pb(blk_id); - if (pb) { - //Found - return pb->pb_graph_node; - } - return nullptr; - } - - void AtomPBLookUp::set_atom_pb(const AtomBlockId blk_id, const t_pb* pb) { - //If either of blk_id or pb are not valid, - //remove any mapping - - if (!blk_id && pb) { - //Remove - atom_to_pb_.erase(pb); - } else if (blk_id && !pb) { - //Remove - atom_to_pb_.erase(blk_id); - } else if (blk_id && pb) { - //If both are valid store the mapping - atom_to_pb_.update(blk_id, pb); - } - } - \ No newline at end of file +} + +/* +* @brief Check the atom blocks of a cluster pb. Used in the verify method. +*/ +/* TODO: May want to check that all atom blocks are actually reached */ +static void check_cluster_atom_blocks(t_pb* pb, std::unordered_set& blocks_checked, const AtomPBBimap &atom_pb_lookup) { + const AtomContext& atom_ctx = g_vpr_ctx.atom(); + + const t_pb_type* pb_type = pb->pb_graph_node->pb_type; + if (pb_type->num_modes == 0) { + /* primitive */ + AtomBlockId blk_id = atom_pb_lookup.pb_atom(pb); + if (blk_id) { + if (blocks_checked.count(blk_id)) { + VPR_FATAL_ERROR(VPR_ERROR_PACK, + "pb %s contains atom block %s but atom block is already contained in another pb.\n", + pb->name, atom_ctx.netlist().block_name(blk_id).c_str()); + } + blocks_checked.insert(blk_id); + if (pb != atom_pb_lookup.atom_pb(blk_id)) { + VPR_FATAL_ERROR(VPR_ERROR_PACK, + "pb %s contains atom block %s but atom block does not link to pb.\n", + pb->name, atom_ctx.netlist().block_name(blk_id).c_str()); + } + } + } else { + /* this is a container pb, all container pbs must contain children */ + bool has_child = false; + for (int i = 0; i < pb_type->modes[pb->mode].num_pb_type_children; i++) { + for (int j = 0; j < pb_type->modes[pb->mode].pb_type_children[i].num_pb; j++) { + if (pb->child_pbs[i] != nullptr) { + if (pb->child_pbs[i][j].name != nullptr) { + has_child = true; + check_cluster_atom_blocks(&pb->child_pbs[i][j], blocks_checked, atom_pb_lookup); + } + } + } + } + VTR_ASSERT(has_child); + } +} + +/// @brief Recursively frees the pb stats of the given pb, without freeing the +/// pb itself. +static void free_pb_stats_recursive(t_pb* pb) { + /* Releases all the memory used by clustering data structures. */ + if (pb) { + if (pb->pb_graph_node != nullptr) { + if (!pb->pb_graph_node->is_primitive()) { + for (int i = 0; i < pb->pb_graph_node->pb_type->modes[pb->mode].num_pb_type_children; i++) { + for (int j = 0; j < pb->pb_graph_node->pb_type->modes[pb->mode].pb_type_children[i].num_pb; j++) { + if (pb->child_pbs && pb->child_pbs[i]) { + free_pb_stats_recursive(&pb->child_pbs[i][j]); + } + } + } + } + } + free_pb_stats(pb); + } +} + + +/** + * @brief Checks whether an atom block can be added to a clustered block + * without violating floorplanning constraints. It also updates the + * clustered block's floorplanning region by taking the intersection of + * its current region and the floorplanning region of the given atom block. + * + * @param atom_blk_id A unique ID for the candidate atom block to + * be added to the growing cluster. + * @param cluster_pr The floorplanning regions of the clustered + * block. This function may update the given + * region. + * @param constraints The set of user-given place constraints. + * @param log_verbosity Controls the detail level of log information + * printed by this function. + * @param cluster_pr_needs_update Indicates whether the floorplanning region + * of the clustered block have updated. + * + * @return True if adding the given atom block to the clustered block does not + * violated any floorplanning constraints. + */ +static bool check_cluster_floorplanning(AtomBlockId atom_blk_id, + PartitionRegion& cluster_pr, + const UserPlaceConstraints& constraints, + int log_verbosity, + bool& cluster_pr_needs_update) { + // Get the partition ID of the atom. + PartitionId part_id = constraints.get_atom_partition(atom_blk_id); + // If the partition ID is invalid, then it can be put in the cluster + // regardless of what the cluster's PartitionRegion is since it is not + // constrained. + if (!part_id.is_valid()) { + VTR_LOGV(log_verbosity > 3, + "\t\t\t Intersect: Atom block %d has no floorplanning constraints\n", + atom_blk_id); + cluster_pr_needs_update = false; + return true; + } + + // Get the Atom and Cluster Partition Regions + const PartitionRegion& atom_pr = constraints.get_partition_pr(part_id); + + // If the Cluster's PartitionRegion is empty, then this atom's PR becomes + // the Cluster's new PartitionRegion. + if (cluster_pr.empty()) { + VTR_LOGV(log_verbosity > 3, + "\t\t\t Intersect: Atom block %d has floorplanning constraints\n", + atom_blk_id); + cluster_pr = atom_pr; + cluster_pr_needs_update = true; + return true; + } + + // The Cluster's new PartitionRegion is the intersection of the Cluster's + // original PartitionRegion and the atom's PartitionRegion. + update_cluster_part_reg(cluster_pr, atom_pr); + + // If the intersection is empty, then the atom cannot be placed in this + // Cluster due to floorplanning constraints. + if (cluster_pr.empty()) { + VTR_LOGV(log_verbosity > 3, + "\t\t\t Intersect: Atom block %d failed floorplanning check for cluster\n", + atom_blk_id); + cluster_pr_needs_update = false; + return false; + } + + // If the Cluster's new PartitionRegion is non-empty, then this atom passes + // the floorplanning constraints and the cluster's PartitionRegion should be + // updated. + cluster_pr_needs_update = true; + VTR_LOGV(log_verbosity > 3, + "\t\t\t Intersect: Atom block %d passed cluster, cluster PR was updated with intersection result \n", + atom_blk_id); + return true; +} + + +/** + * @brief Checks if an atom block can be added to a clustered block without + * violating NoC group constraints. For passing this check, either both + * clustered and atom blocks must belong to the same NoC group, or at + * least one of them should not belong to any NoC group. If the atom block + * is associated with a NoC group while the clustered block does not + * belong to any NoC groups, the NoC group ID of the atom block is assigned + * to the clustered block when the atom is added to it. + * + * @param atom_blk_id A unique ID for the candidate atom block to be + * added to the growing cluster. + * @param cluster_noc_grp_id The NoC group ID of the clustered block. This + * function may update this ID. + * @param atom_noc_grp_ids A mapping from atoms to NoC group IDs. + * @param log_verbosity Controls the detail level of log information + * printed by this function. + * + * @return True if adding the atom block the cluster does not violate NoC group + * constraints. + */ +static bool check_cluster_noc_group(AtomBlockId atom_blk_id, + NocGroupId& cluster_noc_grp_id, + const vtr::vector& atom_noc_grp_ids, + int log_verbosity) { + const NocGroupId atom_noc_grp_id = atom_noc_grp_ids.empty() ? NocGroupId::INVALID() : atom_noc_grp_ids[atom_blk_id]; + + if (!cluster_noc_grp_id.is_valid()) { + // If the cluster does not have a NoC group, assign the atom's NoC group + // to the cluster. + VTR_LOGV(log_verbosity > 3, + "\t\t\t NoC Group: Atom block %d passed cluster, cluster's NoC group was updated with the atom's group %d\n", + atom_blk_id, (size_t)atom_noc_grp_id); + cluster_noc_grp_id = atom_noc_grp_id; + return true; + } + + if (cluster_noc_grp_id == atom_noc_grp_id) { + // If the cluster has the same NoC group ID as the atom, they are + // compatible. + VTR_LOGV(log_verbosity > 3, + "\t\t\t NoC Group: Atom block %d passed cluster, cluster's NoC group was compatible with the atom's group %d\n", + atom_blk_id, (size_t)atom_noc_grp_id); + return true; + } + + // If the cluster belongs to a different NoC group than the atom's group, + // they are incompatible. + VTR_LOGV(log_verbosity > 3, + "\t\t\t NoC Group: Atom block %d failed NoC group check for cluster. Cluster's NoC group: %d, atom's NoC group: %d\n", + atom_blk_id, (size_t)cluster_noc_grp_id, (size_t)atom_noc_grp_id); + return false; +} + +/** + * @brief This function takes the root block of a chain molecule and a proposed + * placement primitive for this block. The function then checks if this + * chain root block has a placement constraint (such as being driven from + * outside the cluster) and returns the status of the placement accordingly. + */ +static enum e_block_pack_status check_chain_root_placement_feasibility( + const t_pb_graph_node* pb_graph_node, + const t_chain_info& prepack_chain_info, + const t_clustering_chain_info& clustering_chain_info, + t_pack_patterns* mol_pack_patterns, + const AtomBlockId blk_id) { + const AtomNetlist& netlist_context = g_vpr_ctx.atom().netlist(); + + enum e_block_pack_status block_pack_status = e_block_pack_status::BLK_PASSED; + + bool is_long_chain = prepack_chain_info.is_long_chain; + + const auto& chain_root_pins = mol_pack_patterns->chain_root_pins; + + t_model_ports* root_port = chain_root_pins[0][0]->port->model_port; + AtomNetId chain_net_id; + auto port_id = netlist_context.find_atom_port(blk_id, root_port); + + if (port_id) { + chain_net_id = netlist_context.port_net(port_id, chain_root_pins[0][0]->pin_number); + } + + // if this block is part of a long chain or it is driven by a cluster + // input pin we need to check the placement legality of this block + // Depending on the logic synthesis even small chains that can fit within one + // cluster might need to start at the top of the cluster as their input can be + // driven by a global gnd or vdd. Therefore even if this is not a long chain + // but its input pin is driven by a net, the placement legality is checked. + if (is_long_chain || chain_net_id) { + auto chain_id = clustering_chain_info.chain_id; + // if this chain has a chain id assigned to it (implies is_long_chain too) + if (chain_id != -1) { + // the chosen primitive should be a valid starting point for the chain + // long chains should only be placed at the top of the chain tieOff = 0 + if (pb_graph_node != chain_root_pins[chain_id][0]->parent_node) { + block_pack_status = e_block_pack_status::BLK_FAILED_FEASIBLE; + } + // the chain doesn't have an assigned chain_id yet + } else { + block_pack_status = e_block_pack_status::BLK_FAILED_FEASIBLE; + for (const auto& chain : chain_root_pins) { + for (auto tieOff : chain) { + // check if this chosen primitive is one of the possible + // starting points for this chain. + if (pb_graph_node == tieOff->parent_node) { + // this location matches with the one of the dedicated chain + // input from outside logic block, therefore it is feasible + block_pack_status = e_block_pack_status::BLK_PASSED; + break; + } + // long chains should only be placed at the top of the chain tieOff = 0 + if (is_long_chain) break; + } + } + } + } + + return block_pack_status; +} + +/* +* @brief Check that the two atom blocks blk_id and sibling_blk_id (which should +* both be memory slices) are feasible, in the sense that they have +* precicely the same net connections (with the exception of nets in data +* port classes). +* +* Note that this routine does not check pin feasibility against the cur_pb_type; so +* primitive_type_feasible() should also be called on blk_id before concluding it is feasible. +*/ +static bool primitive_memory_sibling_feasible(const AtomBlockId blk_id, const t_pb_type* cur_pb_type, const AtomBlockId sibling_blk_id) { + const AtomContext& atom_ctx = g_vpr_ctx.atom(); + + VTR_ASSERT(cur_pb_type->class_type == MEMORY_CLASS); + + //First, identify the 'data' ports by looking at the cur_pb_type + std::unordered_set data_ports; + for (int iport = 0; iport < cur_pb_type->num_ports; ++iport) { + const char* port_class = cur_pb_type->ports[iport].port_class; + if (port_class && strstr(port_class, "data") == port_class) { + //The port_class starts with "data", so it is a data port + + //Record the port + data_ports.insert(cur_pb_type->ports[iport].model_port); + } + } + + //Now verify that all nets (except those connected to data ports) are equivalent + //between blk_id and sibling_blk_id + + //Since the atom netlist stores only in-use ports, we iterate over the model to ensure + //all ports are compared + const t_model* model = cur_pb_type->model; + for (t_model_ports* port : {model->inputs, model->outputs}) { + for (; port; port = port->next) { + if (data_ports.count(port)) { + //Don't check data ports + continue; + } + + //Note: VPR doesn't support multi-driven nets, so all outputs + //should be data ports, otherwise the siblings will both be + //driving the output net + + //Get the ports from each primitive + auto blk_port_id = atom_ctx.netlist().find_atom_port(blk_id, port); + auto sib_port_id = atom_ctx.netlist().find_atom_port(sibling_blk_id, port); + + //Check that all nets (including unconnected nets) match + for (int ipin = 0; ipin < port->size; ++ipin) { + //The nets are initialized as invalid (i.e. disconnected) + AtomNetId blk_net_id; + AtomNetId sib_net_id; + + //We can get the actual net provided the port exists + // + //Note that if the port did not exist, the net is left + //as invalid/disconneced + if (blk_port_id) { + blk_net_id = atom_ctx.netlist().port_net(blk_port_id, ipin); + } + if (sib_port_id) { + sib_net_id = atom_ctx.netlist().port_net(sib_port_id, ipin); + } + + //The sibling and block must have the same (possibly disconnected) + //net on this pin + if (blk_net_id != sib_net_id) { + //Nets do not match, not feasible + return false; + } + } + } + } + + return true; +} + +/* +* @brief Check if the given atom is feasible in the given pb. +*/ +static bool primitive_feasible(const AtomBlockId blk_id, t_pb* cur_pb, const AtomPBBimap& atom_to_pb) { + const t_pb_type* cur_pb_type = cur_pb->pb_graph_node->pb_type; + + VTR_ASSERT(cur_pb_type->num_modes == 0); /* primitive */ + + AtomBlockId cur_pb_blk_id = atom_to_pb.pb_atom(cur_pb); + if (cur_pb_blk_id && cur_pb_blk_id != blk_id) { + /* This pb already has a different logical block */ + return false; + } + + if (cur_pb_type->class_type == MEMORY_CLASS) { + /* Memory class has additional feasibility requirements: + * - all siblings must share all nets, including open nets, with the exception of data nets */ + + /* find sibling if one exists */ + const t_pb *sibling_memory_pb = find_memory_sibling(cur_pb); + AtomBlockId sibling_memory_blk_id = atom_to_pb.pb_atom(sibling_memory_pb); + + if (sibling_memory_blk_id) { + //There is a sibling, see if the current block is feasible with it + bool sibling_feasible = primitive_memory_sibling_feasible(blk_id, cur_pb_type, sibling_memory_blk_id); + if (!sibling_feasible) { + return false; + } + } + } + + //Generic feasibility check + return primitive_type_feasible(blk_id, cur_pb_type); +} + +/** + * @brief Try to place atom block into current primitive location + */ +static enum e_block_pack_status +try_place_atom_block_rec(const t_pb_graph_node* pb_graph_node, + const AtomBlockId blk_id, + t_pb* cb, + t_pb** parent, + const LegalizationClusterId cluster_id, + vtr::vector_map& atom_cluster, + const PackMoleculeId molecule_id, + t_lb_router_data* router_data, + int verbosity, + const Prepacker& prepacker, + const vtr::vector_map& clustering_chain_info, + AtomPBBimap& atom_to_pb) { + const AtomContext& atom_ctx = g_vpr_ctx.atom(); + + VTR_ASSERT_SAFE(cb != nullptr); + e_block_pack_status block_pack_status = e_block_pack_status::BLK_PASSED; + + /* Discover parent */ + t_pb* parent_pb = nullptr; + if (pb_graph_node->parent_pb_graph_node != cb->pb_graph_node) { + t_pb* my_parent = nullptr; + block_pack_status = try_place_atom_block_rec(pb_graph_node->parent_pb_graph_node, blk_id, cb, + &my_parent, cluster_id, + atom_cluster, + molecule_id, router_data, + verbosity, + prepacker, clustering_chain_info, atom_to_pb); + parent_pb = my_parent; + } else { + parent_pb = cb; + } + + /* Create siblings if siblings are not allocated */ + VTR_ASSERT(parent_pb != nullptr); + if (parent_pb->child_pbs == nullptr) { + VTR_ASSERT(parent_pb->name == nullptr); + parent_pb->name = vtr::strdup(atom_ctx.netlist().block_name(blk_id).c_str()); + parent_pb->mode = pb_graph_node->pb_type->parent_mode->index; + set_reset_pb_modes(router_data, parent_pb, true); + const t_mode* mode = &parent_pb->pb_graph_node->pb_type->modes[parent_pb->mode]; + parent_pb->child_pbs = new t_pb*[mode->num_pb_type_children]; + + for (int i = 0; i < mode->num_pb_type_children; i++) { + parent_pb->child_pbs[i] = new t_pb[mode->pb_type_children[i].num_pb]; + + for (int j = 0; j < mode->pb_type_children[i].num_pb; j++) { + parent_pb->child_pbs[i][j].parent_pb = parent_pb; + parent_pb->child_pbs[i][j].pb_graph_node = &(parent_pb->pb_graph_node->child_pb_graph_nodes[parent_pb->mode][i][j]); + } + } + } else { + /* if this is not the first child of this parent, must match existing parent mode */ + if (parent_pb->mode != pb_graph_node->pb_type->parent_mode->index) { + return e_block_pack_status::BLK_FAILED_FEASIBLE; + } + } + + const t_mode* mode = &parent_pb->pb_graph_node->pb_type->modes[parent_pb->mode]; + int i; + for (i = 0; i < mode->num_pb_type_children; i++) { + if (pb_graph_node->pb_type == &mode->pb_type_children[i]) { + break; + } + } + VTR_ASSERT(i < mode->num_pb_type_children); + t_pb* pb = &parent_pb->child_pbs[i][pb_graph_node->placement_index]; + VTR_ASSERT_SAFE(pb != nullptr); + *parent = pb; /* this pb is parent of it's child that called this function */ + VTR_ASSERT(pb->pb_graph_node == pb_graph_node); + if (pb->pb_stats == nullptr) { + alloc_and_load_pb_stats(pb); + } + const t_pb_type* pb_type = pb_graph_node->pb_type; + + /* Any pb_type under an mode, which is disabled for packing, should not be considerd for mapping + * Early exit to flag failure + */ + if (true == pb_type->parent_mode->disable_packing) { + return e_block_pack_status::BLK_FAILED_FEASIBLE; + } + + bool is_primitive = (pb_type->num_modes == 0); + + if (is_primitive) { + VTR_ASSERT(!atom_to_pb.pb_atom(pb) + && atom_to_pb.atom_pb(blk_id) == nullptr + && atom_cluster[blk_id] == LegalizationClusterId::INVALID()); + /* try pack to location */ + VTR_ASSERT(pb->name == nullptr); + pb->name = vtr::strdup(atom_ctx.netlist().block_name(blk_id).c_str()); + + //Update the atom netlist mappings + atom_cluster[blk_id] = cluster_id; + // NOTE: This pb is different from the pb of the cluster. It is the pb + // of the actual primitive. + // TODO: It would be a good idea to remove the use of this global + // variables to prevent external users from modifying this by + // mistake. + atom_to_pb.set_atom_pb(blk_id, pb); + + add_atom_as_target(router_data, blk_id, atom_to_pb); + if (!primitive_feasible(blk_id, pb, atom_to_pb)) { + /* failed location feasibility check, revert pack */ + block_pack_status = e_block_pack_status::BLK_FAILED_FEASIBLE; + } + + // if this block passed and is part of a chained molecule + const t_pack_molecule& molecule = prepacker.get_molecule(molecule_id); + if (block_pack_status == e_block_pack_status::BLK_PASSED && molecule.is_chain()) { + auto molecule_root_block = molecule.atom_block_ids[molecule.root]; + // if this is the root block of the chain molecule check its placmeent feasibility + if (blk_id == molecule_root_block) { + VTR_ASSERT(molecule.chain_id.is_valid()); + const t_chain_info& prepack_chain_info = prepacker.get_molecule_chain_info(molecule.chain_id); + block_pack_status = check_chain_root_placement_feasibility(pb_graph_node, + prepack_chain_info, + clustering_chain_info[molecule.chain_id], + molecule.pack_pattern, + blk_id); + } + } + + VTR_LOGV(verbosity > 4 && block_pack_status == e_block_pack_status::BLK_PASSED, + "\t\t\tPlaced atom '%s' (%s) at %s\n", + atom_ctx.netlist().block_name(blk_id).c_str(), + atom_ctx.netlist().block_model(blk_id)->name, + pb->hierarchical_type_name().c_str()); + } + + if (block_pack_status != e_block_pack_status::BLK_PASSED) { + free(pb->name); + pb->name = nullptr; + } + return block_pack_status; +} + +/* +* @brief Resets nets used at different pin classes for determining pin +* feasibility. +*/ +static void reset_lookahead_pins_used(t_pb* cur_pb) { + const t_pb_type* pb_type = cur_pb->pb_graph_node->pb_type; + if (cur_pb->pb_stats == nullptr) { + return; /* No pins used, no need to continue */ + } + + if (pb_type->num_modes > 0 && cur_pb->name != nullptr) { + for (int i = 0; i < cur_pb->pb_graph_node->num_input_pin_class; i++) { + cur_pb->pb_stats->lookahead_input_pins_used[i].clear(); + } + + for (int i = 0; i < cur_pb->pb_graph_node->num_output_pin_class; i++) { + cur_pb->pb_stats->lookahead_output_pins_used[i].clear(); + } + + if (cur_pb->child_pbs != nullptr) { + for (int i = 0; i < pb_type->modes[cur_pb->mode].num_pb_type_children; i++) { + if (cur_pb->child_pbs[i] != nullptr) { + for (int j = 0; j < pb_type->modes[cur_pb->mode].pb_type_children[i].num_pb; j++) { + reset_lookahead_pins_used(&cur_pb->child_pbs[i][j]); + } + } + } + } + } +} + + +/* +* @brief Checks if the sinks of the given net are reachable from the driver +* pb gpin. +*/ +static int net_sinks_reachable_in_cluster(const t_pb_graph_pin* driver_pb_gpin, const int depth, const AtomNetId net_id, const AtomPBBimap& atom_to_pb) { + const AtomContext& atom_ctx = g_vpr_ctx.atom(); + + //Record the sink pb graph pins we are looking for + std::unordered_set sink_pb_gpins; + for (const AtomPinId pin_id : atom_ctx.netlist().net_sinks(net_id)) { + const t_pb_graph_pin* sink_pb_gpin = find_pb_graph_pin(atom_ctx.netlist(), atom_to_pb, pin_id); + VTR_ASSERT(sink_pb_gpin); + + sink_pb_gpins.insert(sink_pb_gpin); + } + + //Count how many sink pins are reachable + size_t num_reachable_sinks = 0; + for (int i_prim_pin = 0; i_prim_pin < driver_pb_gpin->num_connectable_primitive_input_pins[depth]; ++i_prim_pin) { + const t_pb_graph_pin* reachable_pb_gpin = driver_pb_gpin->list_of_connectable_input_pin_ptrs[depth][i_prim_pin]; + + if (sink_pb_gpins.count(reachable_pb_gpin)) { + ++num_reachable_sinks; + if (num_reachable_sinks == atom_ctx.netlist().net_sinks(net_id).size()) { + return true; + } + } + } + + return false; +} + +/** + * @brief Returns the pb_graph_pin of the atom pin defined by the driver_pin_id in the driver_pb +*/ +static t_pb_graph_pin* get_driver_pb_graph_pin(const t_pb* driver_pb, const AtomPinId driver_pin_id) { + const AtomNetlist& atom_netlist = g_vpr_ctx.atom().netlist(); + + const auto driver_pb_type = driver_pb->pb_graph_node->pb_type; + int output_port = 0; + // find the port of the pin driving the net as well as the port model + auto driver_port_id = atom_netlist.pin_port(driver_pin_id); + auto driver_model_port = atom_netlist.port_model(driver_port_id); + // find the port id of the port containing the driving pin in the driver_pb_type + for (int i = 0; i < driver_pb_type->num_ports; i++) { + auto& prim_port = driver_pb_type->ports[i]; + if (prim_port.type == OUT_PORT) { + if (prim_port.model_port == driver_model_port) { + // get the output pb_graph_pin driving this input net + return &(driver_pb->pb_graph_node->output_pins[output_port][atom_netlist.pin_port_bit(driver_pin_id)]); + } + output_port++; + } + } + // the pin should be found + VTR_ASSERT(false); + return nullptr; +} + + +/** + * @brief Given a pin and its assigned net, mark all pin classes that are affected. + * Check if connecting this pin to it's driver pin or to all sink pins will + * require leaving a pb_block starting from the parent pb_block of the + * primitive till the root block (depth = 0). If leaving a pb_block is + * required add this net to the pin class (to increment the number of used + * pins from this class) that should be used to leave the pb_block. +*/ +static void compute_and_mark_lookahead_pins_used_for_pin(const t_pb_graph_pin* pb_graph_pin, + const t_pb* primitive_pb, + const AtomNetId net_id, + const vtr::vector_map& atom_cluster, + const AtomPBBimap& atom_to_pb) { + const AtomContext& atom_ctx = g_vpr_ctx.atom(); + + // starting from the parent pb of the input primitive go up in the hierarchy till the root block + for (auto cur_pb = primitive_pb->parent_pb; cur_pb; cur_pb = cur_pb->parent_pb) { + const auto depth = cur_pb->pb_graph_node->pb_type->depth; + const auto pin_class = pb_graph_pin->parent_pin_class[depth]; + VTR_ASSERT(pin_class != OPEN); + + const auto driver_blk_id = atom_ctx.netlist().net_driver_block(net_id); + + // if this primitive pin is an input pin + if (pb_graph_pin->port->type == IN_PORT) { + /* find location of net driver if exist in clb, NULL otherwise */ + // find the driver of the input net connected to the pin being studied + const auto driver_pin_id = atom_ctx.netlist().net_driver(net_id); + // find the id of the atom occupying the input primitive_pb + const auto prim_blk_id = atom_to_pb.pb_atom(primitive_pb); + // find the pb block occupied by the driving atom + const auto driver_pb = atom_to_pb.atom_pb(driver_blk_id); + // pb_graph_pin driving net_id in the driver pb block + t_pb_graph_pin* output_pb_graph_pin = nullptr; + // if the driver block is in the same clb as the input primitive block + LegalizationClusterId driver_cluster_id = atom_cluster[driver_blk_id]; + LegalizationClusterId prim_cluster_id = atom_cluster[prim_blk_id]; + if (driver_cluster_id == prim_cluster_id) { + // get pb_graph_pin driving the given net + output_pb_graph_pin = get_driver_pb_graph_pin(driver_pb, driver_pin_id); + } + + bool is_reachable = false; + + // if the driver pin is within the cluster + if (output_pb_graph_pin) { + // find if the driver pin can reach the input pin of the primitive or not + const t_pb* check_pb = driver_pb; + while (check_pb && check_pb != cur_pb) { + check_pb = check_pb->parent_pb; + } + if (check_pb) { + for (int i = 0; i < output_pb_graph_pin->num_connectable_primitive_input_pins[depth]; i++) { + if (pb_graph_pin == output_pb_graph_pin->list_of_connectable_input_pin_ptrs[depth][i]) { + is_reachable = true; + break; + } + } + } + } + + // Must use an input pin to connect the driver to the input pin of the given primitive, either the + // driver atom is not contained in the cluster or is contained but cannot reach the primitive pin + if (!is_reachable) { + // add net to lookahead_input_pins_used if not already added + auto it = std::find(cur_pb->pb_stats->lookahead_input_pins_used[pin_class].begin(), + cur_pb->pb_stats->lookahead_input_pins_used[pin_class].end(), net_id); + if (it == cur_pb->pb_stats->lookahead_input_pins_used[pin_class].end()) { + cur_pb->pb_stats->lookahead_input_pins_used[pin_class].push_back(net_id); + } + } + } else { + VTR_ASSERT(pb_graph_pin->port->type == OUT_PORT); + /* + * Determine if this net (which is driven from within this cluster) leaves this cluster + * (and hence uses an output pin). + */ + + bool net_exits_cluster = true; + int num_net_sinks = static_cast(atom_ctx.netlist().net_sinks(net_id).size()); + + if (pb_graph_pin->num_connectable_primitive_input_pins[depth] >= num_net_sinks) { + //It is possible the net is completely absorbed in the cluster, + //since this pin could (potentially) drive all the net's sinks + + /* Important: This runtime penalty looks a lot scarier than it really is. + * For high fan-out nets, I at most look at the number of pins within the + * cluster which limits runtime. + * + * DO NOT REMOVE THIS INITIAL FILTER WITHOUT CAREFUL ANALYSIS ON RUNTIME!!! + * + * Key Observation: + * For LUT-based designs it is impossible for the average fanout to exceed + * the number of LUT inputs so it's usually around 4-5 (pigeon-hole argument, + * if the average fanout is greater than the number of LUT inputs, where do + * the extra connections go? Therefore, average fanout must be capped to a + * small constant where the constant is equal to the number of LUT inputs). + * The real danger to runtime is when the number of sinks of a net gets doubled + */ + + //Check if all the net sinks are, in fact, inside this cluster + bool all_sinks_in_cur_cluster = true; + LegalizationClusterId driver_cluster = atom_cluster[driver_blk_id]; + for (auto pin_id : atom_ctx.netlist().net_sinks(net_id)) { + auto sink_blk_id = atom_ctx.netlist().pin_block(pin_id); + if (atom_cluster[sink_blk_id] != driver_cluster) { + all_sinks_in_cur_cluster = false; + break; + } + } + + if (all_sinks_in_cur_cluster) { + //All the sinks are part of this cluster, so the net may be fully absorbed. + // + //Verify this, by counting the number of net sinks reachable from the driver pin. + //If the count equals the number of net sinks then the net is fully absorbed and + //the net does not exit the cluster + /* TODO: I should cache the absorbed outputs, once net is absorbed, + * net is forever absorbed, no point in rechecking every time */ + if (net_sinks_reachable_in_cluster(pb_graph_pin, depth, net_id, atom_to_pb)) { + //All the sinks are reachable inside the cluster + net_exits_cluster = false; + } + } + } + + if (net_exits_cluster) { + /* This output must exit this cluster */ + cur_pb->pb_stats->lookahead_output_pins_used[pin_class].push_back(net_id); + } + } + } +} + +/* +* @brief Determine if pins of speculatively packed pb are legal +*/ +static void compute_and_mark_lookahead_pins_used(const AtomBlockId blk_id, + const vtr::vector_map& atom_cluster, + const AtomPBBimap& atom_to_pb) { + const AtomNetlist& atom_netlist = g_vpr_ctx.atom().netlist(); + + const t_pb* cur_pb = atom_to_pb.atom_pb(blk_id); + VTR_ASSERT(cur_pb != nullptr); + + /* Walk through inputs, outputs, and clocks marking pins off of the same class */ + for (auto pin_id : atom_netlist.block_pins(blk_id)) { + auto net_id = atom_netlist.pin_net(pin_id); + + const t_pb_graph_pin* pb_graph_pin = find_pb_graph_pin(atom_netlist, atom_to_pb, pin_id); + compute_and_mark_lookahead_pins_used_for_pin(pb_graph_pin, cur_pb, net_id, atom_cluster, atom_to_pb); + } +} + +/* +* @brief Determine if speculatively packed cur_pb is pin feasible +* +* Runtime is actually not that bad for this. It's worst case O(k^2) where k is the +* number of pb_graph pins. Can use hash tables or make incremental if becomes an issue. +*/ +static void try_update_lookahead_pins_used(t_pb* cur_pb, + const vtr::vector_map& atom_cluster, + const AtomPBBimap& atom_to_pb) { + // run recursively till a leaf (primitive) pb block is reached + const t_pb_type* pb_type = cur_pb->pb_graph_node->pb_type; + if (pb_type->num_modes > 0 && cur_pb->name != nullptr) { + if (cur_pb->child_pbs != nullptr) { + for (int i = 0; i < pb_type->modes[cur_pb->mode].num_pb_type_children; i++) { + if (cur_pb->child_pbs[i] != nullptr) { + for (int j = 0; j < pb_type->modes[cur_pb->mode].pb_type_children[i].num_pb; j++) { + try_update_lookahead_pins_used(&cur_pb->child_pbs[i][j], atom_cluster, atom_to_pb); + } + } + } + } + } else { + // find if this child (primitive) pb block has an atom mapped to it, + // if yes compute and mark lookahead pins used for that pb block + AtomBlockId blk_id = atom_to_pb.pb_atom(cur_pb); + if (pb_type->blif_model != nullptr && blk_id) { + compute_and_mark_lookahead_pins_used(blk_id, atom_cluster, atom_to_pb); + } + } +} + +/* +* @brief Check if the number of available inputs/outputs for a pin class is +* sufficient for speculatively packed blocks. +*/ +static bool check_lookahead_pins_used(t_pb* cur_pb, t_ext_pin_util max_external_pin_util) { + const t_pb_type* pb_type = cur_pb->pb_graph_node->pb_type; + + if (pb_type->num_modes > 0 && cur_pb->name) { + for (int i = 0; i < cur_pb->pb_graph_node->num_input_pin_class; i++) { + size_t class_size = cur_pb->pb_graph_node->input_pin_class_size[i]; + + if (cur_pb->is_root()) { + // Scale the class size by the maximum external pin utilization factor + // Use ceil to avoid classes of size 1 from being scaled to zero + class_size = std::ceil(max_external_pin_util.input_pin_util * class_size); + // if the number of pins already used is larger than class size, then the number of + // cluster inputs already used should be our constraint. Why is this needed? This is + // needed since when packing the seed block the maximum external pin utilization is + // used as 1.0 allowing molecules that are using up to all the cluster inputs to be + // packed legally. Therefore, if the seed block is already using more inputs than + // the allowed maximum utilization, this should become the new maximum pin utilization. + class_size = std::max(class_size, cur_pb->pb_stats->input_pins_used[i].size()); + } + + if (cur_pb->pb_stats->lookahead_input_pins_used[i].size() > class_size) { + return false; + } + } + + for (int i = 0; i < cur_pb->pb_graph_node->num_output_pin_class; i++) { + size_t class_size = cur_pb->pb_graph_node->output_pin_class_size[i]; + if (cur_pb->is_root()) { + // Scale the class size by the maximum external pin utilization factor + // Use ceil to avoid classes of size 1 from being scaled to zero + class_size = std::ceil(max_external_pin_util.output_pin_util * class_size); + // if the number of pins already used is larger than class size, then the number of + // cluster outputs already used should be our constraint. Why is this needed? This is + // needed since when packing the seed block the maximum external pin utilization is + // used as 1.0 allowing molecules that are using up to all the cluster inputs to be + // packed legally. Therefore, if the seed block is already using more inputs than + // the allowed maximum utilization, this should become the new maximum pin utilization. + class_size = std::max(class_size, cur_pb->pb_stats->output_pins_used[i].size()); + } + + if (cur_pb->pb_stats->lookahead_output_pins_used[i].size() > class_size) { + return false; + } + } + + if (cur_pb->child_pbs) { + for (int i = 0; i < pb_type->modes[cur_pb->mode].num_pb_type_children; i++) { + if (cur_pb->child_pbs[i]) { + for (int j = 0; j < pb_type->modes[cur_pb->mode].pb_type_children[i].num_pb; j++) { + if (!check_lookahead_pins_used(&cur_pb->child_pbs[i][j], max_external_pin_util)) + return false; + } + } + } + } + } + + return true; +} + +void ClusterLegalizer::update_clustering_chain_info(PackMoleculeId chain_molecule_id, + const t_pb_graph_node* root_primitive) { + // Get the molecule + VTR_ASSERT(chain_molecule_id.is_valid()); + const t_pack_molecule& chain_molecule = prepacker_.get_molecule(chain_molecule_id); + + // Get the ID of the chain it is a part of + MoleculeChainId chain_id = chain_molecule.chain_id; + VTR_ASSERT(chain_id.is_valid()); + + // Get the prepacking and clustering information on this chain. + const t_chain_info& prepack_chain_info = prepacker_.get_molecule_chain_info(chain_id); + t_clustering_chain_info& clustering_chain_info = clustering_chain_info_[chain_id]; + VTR_ASSERT(clustering_chain_info.chain_id == -1 && prepack_chain_info.is_long_chain); + + // Update the clustering chain information. + // long chains should only be placed at the beginning of the chain + // Since for long chains the molecule size is already equal to the + // total number of adders in the cluster. Therefore, it should + // always be placed at the very first adder in this cluster. + auto chain_root_pins = chain_molecule.pack_pattern->chain_root_pins; + for (size_t chainId = 0; chainId < chain_root_pins.size(); chainId++) { + if (chain_root_pins[chainId][0]->parent_node == root_primitive) { + clustering_chain_info.chain_id = chainId; + clustering_chain_info.first_packed_molecule = chain_molecule_id; + return; + } + } + + VTR_ASSERT(false); +} + +void ClusterLegalizer::reset_molecule_info(PackMoleculeId mol_id) { + VTR_ASSERT(mol_id.is_valid()); + + // when invalidating a molecule check if it's a chain molecule + // that is part of a long chain. If so, check if this molecule + // has modified the chain_id value based on the stale packing + // then reset the chain id and the first packed molecule pointer + // this is packing is being reset + const t_pack_molecule& mol = prepacker_.get_molecule(mol_id); + if (!mol.is_chain()) + return; + + VTR_ASSERT(mol.chain_id.is_valid()); + const t_chain_info& prepack_chain_info = prepacker_.get_molecule_chain_info(mol.chain_id); + if (!prepack_chain_info.is_long_chain) + return; + + t_clustering_chain_info& clustering_chain_info = clustering_chain_info_[mol.chain_id]; + if (clustering_chain_info.first_packed_molecule == mol_id) { + clustering_chain_info.first_packed_molecule = PackMoleculeId::INVALID(); + clustering_chain_info.chain_id = -1; + } +} + +/* +* @brief Revert trial atom block iblock and free up memory space accordingly. +*/ +static void revert_place_atom_block(const AtomBlockId blk_id, + t_lb_router_data* router_data, + vtr::vector_map& atom_cluster, + AtomPBBimap& atom_to_pb) { + //We cast away const here since we may free the pb, and it is + //being removed from the active mapping. + // + //In general most code works fine accessing cosnt t_pb*, + //which is why we store them as such in atom_ctx.lookup() + t_pb* pb = const_cast(atom_to_pb.atom_pb(blk_id)); + + if (pb != nullptr) { + /* When freeing molecules, the current block might already have been freed by a prior revert + * When this happens, no need to do anything beyond basic book keeping at the atom block + */ + + t_pb* next = pb->parent_pb; + free_pb(pb, atom_to_pb); + pb = next; + + while (pb != nullptr) { + /* If this is pb is created only for the purposes of holding new molecule, remove it + * Must check if cluster is already freed (which can be the case) + */ + next = pb->parent_pb; + + if (pb->child_pbs != nullptr && pb->pb_stats != nullptr + && pb->pb_stats->num_child_blocks_in_pb == 0) { + set_reset_pb_modes(router_data, pb, false); + if (next != nullptr) { + /* If the code gets here, then that means that placing the initial seed molecule + * failed, don't free the actual complex block itself as the seed needs to find + * another placement */ + free_pb(pb, atom_to_pb); + } + } + pb = next; + } + } + + //Update the atom netlist mapping + atom_cluster[blk_id] = LegalizationClusterId::INVALID(); + atom_to_pb.set_atom_pb(blk_id, nullptr); +} + +/* +* @brief Speculation successful, commit input/output pins used. +*/ +static void commit_lookahead_pins_used(t_pb* cur_pb) { + const t_pb_type* pb_type = cur_pb->pb_graph_node->pb_type; + + if (pb_type->num_modes > 0 && cur_pb->name) { + for (int i = 0; i < cur_pb->pb_graph_node->num_input_pin_class; i++) { + VTR_ASSERT(cur_pb->pb_stats->lookahead_input_pins_used[i].size() <= (unsigned int)cur_pb->pb_graph_node->input_pin_class_size[i]); + for (size_t j = 0; j < cur_pb->pb_stats->lookahead_input_pins_used[i].size(); j++) { + VTR_ASSERT(cur_pb->pb_stats->lookahead_input_pins_used[i][j]); + cur_pb->pb_stats->input_pins_used[i].insert({j, cur_pb->pb_stats->lookahead_input_pins_used[i][j]}); + } + } + + for (int i = 0; i < cur_pb->pb_graph_node->num_output_pin_class; i++) { + VTR_ASSERT(cur_pb->pb_stats->lookahead_output_pins_used[i].size() <= (unsigned int)cur_pb->pb_graph_node->output_pin_class_size[i]); + for (size_t j = 0; j < cur_pb->pb_stats->lookahead_output_pins_used[i].size(); j++) { + VTR_ASSERT(cur_pb->pb_stats->lookahead_output_pins_used[i][j]); + cur_pb->pb_stats->output_pins_used[i].insert({j, cur_pb->pb_stats->lookahead_output_pins_used[i][j]}); + } + } + + if (cur_pb->child_pbs) { + for (int i = 0; i < pb_type->modes[cur_pb->mode].num_pb_type_children; i++) { + if (cur_pb->child_pbs[i]) { + for (int j = 0; j < pb_type->modes[cur_pb->mode].pb_type_children[i].num_pb; j++) { + commit_lookahead_pins_used(&cur_pb->child_pbs[i][j]); + } + } + } + } + } +} + +/** +* @brief Cleans up a pb after unsuccessful molecule packing +* +* Recursively frees pbs from a t_pb tree. The given root pb itself is not +* deleted. +* +* If a pb object has its children allocated then before freeing them the +* function checks if there is no atom that corresponds to any of them. The +* check is performed only for leaf (primitive) pbs. The function recurses for +* non-primitive pbs. +* +* The cleaning itself includes deleting all child pbs, resetting mode of the +* pb and also freeing its name. This prepares the pb for another round of +* molecule packing tryout. +*/ +static bool cleanup_pb(t_pb* pb) { + bool can_free = true; + + /* Recursively check if there are any children with already assigned atoms */ + if (pb->child_pbs != nullptr) { + const t_mode* mode = &pb->pb_graph_node->pb_type->modes[pb->mode]; + VTR_ASSERT(mode != nullptr); + + /* Check each mode */ + for (int i = 0; i < mode->num_pb_type_children; ++i) { + /* Check each child */ + if (pb->child_pbs[i] != nullptr) { + for (int j = 0; j < mode->pb_type_children[i].num_pb; ++j) { + t_pb* pb_child = &pb->child_pbs[i][j]; + t_pb_type* pb_type = pb_child->pb_graph_node->pb_type; + + /* Primitive, check occupancy */ + if (pb_type->num_modes == 0) { + if (pb_child->name != nullptr) { + can_free = false; + } + } + + /* Non-primitive, recurse */ + else { + if (!cleanup_pb(pb_child)) { + can_free = false; + } + } + } + } + } + + /* Free if can */ + if (can_free) { + for (int i = 0; i < mode->num_pb_type_children; ++i) { + if (pb->child_pbs[i] != nullptr) { + delete[] pb->child_pbs[i]; + } + } + + delete[] pb->child_pbs; + pb->child_pbs = nullptr; + pb->mode = 0; + + if (pb->name) { + free(pb->name); + pb->name = nullptr; + } + } + } + + return can_free; +} + +e_block_pack_status ClusterLegalizer::try_pack_molecule(PackMoleculeId molecule_id, + LegalizationCluster& cluster, + LegalizationClusterId cluster_id, + const t_ext_pin_util& max_external_pin_util) { + // Try to pack the molecule into a cluster with this pb type. + + // Safety debugs. + VTR_ASSERT_DEBUG(molecule_id.is_valid()); + VTR_ASSERT_DEBUG(cluster.pb != nullptr); + VTR_ASSERT_DEBUG(cluster.type != nullptr); + + // TODO: Remove these global accesses to the contexts. + // AtomContext used for: + // - printing verbose statements + // - Looking up the primitive pb + const AtomContext& atom_ctx = g_vpr_ctx.atom(); + // FloorplanningContext used for: + // - Checking if the atom can be placed in the cluster for floorplanning + // constraints. + const FloorplanningContext& floorplanning_ctx = g_vpr_ctx.floorplanning(); + + // Get the molecule object. + const t_pack_molecule& molecule = prepacker_.get_molecule(molecule_id); + + if (log_verbosity_ > 3) { + AtomBlockId root_atom = molecule.atom_block_ids[molecule.root]; + VTR_LOG("\t\tTry pack molecule: '%s' (%s)", + atom_ctx.netlist().block_name(root_atom).c_str(), + atom_ctx.netlist().block_model(root_atom)->name); + VTR_LOGV(molecule.pack_pattern, + " molecule_type %s molecule_size %zu", + molecule.pack_pattern->name, + molecule.atom_block_ids.size()); + VTR_LOG("\n"); + } + + // if this cluster has a molecule placed in it that is part of a long chain + // (a chain that consists of more than one molecule), don't allow more long chain + // molecules to be placed in this cluster. To avoid possibly creating cluster level + // blocks that have incompatible placement constraints or form very long placement + // macros that limit placement flexibility. + if (cluster.placement_stats->has_long_chain && molecule.is_chain() && prepacker_.get_molecule_chain_info(molecule.chain_id).is_long_chain) { + VTR_LOGV(log_verbosity_ > 4, "\t\t\tFAILED Placement Feasibility Filter: Only one long chain per cluster is allowed\n"); + return e_block_pack_status::BLK_FAILED_FEASIBLE; + } + + // Check if every atom in the molecule is legal in the cluster from a + // floorplanning perspective + bool cluster_pr_update_check = false; + PartitionRegion new_cluster_pr = cluster.pr; + // TODO: This can be made more efficient by pre-computing the intersection + // of all the atoms' PRs in the molecule. + for (AtomBlockId atom_blk_id : molecule.atom_block_ids) { + if (!atom_blk_id.is_valid()) + continue; + + // Try to intersect with atom PartitionRegion if atom exists + bool cluster_pr_needs_update = false; + bool block_pack_floorplan_status = check_cluster_floorplanning(atom_blk_id, + new_cluster_pr, + floorplanning_ctx.constraints, + log_verbosity_, + cluster_pr_needs_update); + if (!block_pack_floorplan_status) { + return e_block_pack_status::BLK_FAILED_FLOORPLANNING; + } + + if (cluster_pr_needs_update) { + cluster_pr_update_check = true; + } + } + + // Check if all atoms in the molecule can be added to the cluster without + // NoC group conflicts + NocGroupId new_cluster_noc_grp_id = cluster.noc_grp_id; + for (AtomBlockId atom_blk_id : molecule.atom_block_ids) { + if (!atom_blk_id.is_valid()) + continue; + + bool block_pack_noc_grp_status = check_cluster_noc_group(atom_blk_id, + new_cluster_noc_grp_id, + atom_noc_grp_id_, + log_verbosity_); + if (!block_pack_noc_grp_status) { + return e_block_pack_status::BLK_FAILED_NOC_GROUP; + } + } + + std::vector primitives_list(max_molecule_size_, nullptr); + e_block_pack_status block_pack_status = e_block_pack_status::BLK_STATUS_UNDEFINED; + while (block_pack_status != e_block_pack_status::BLK_PASSED) { + if (!get_next_primitive_list(cluster.placement_stats, + molecule_id, + primitives_list.data(), + prepacker_)) { + VTR_LOGV(log_verbosity_ > 3, "\t\tFAILED No candidate primitives available\n"); + block_pack_status = e_block_pack_status::BLK_FAILED_FEASIBLE; + break; /* no more candidate primitives available, this molecule will not pack, return fail */ + } + + block_pack_status = e_block_pack_status::BLK_PASSED; + size_t failed_location = 0; + for (size_t i_mol = 0; i_mol < molecule.atom_block_ids.size() && block_pack_status == e_block_pack_status::BLK_PASSED; i_mol++) { + VTR_ASSERT((primitives_list[i_mol] == nullptr) == (!molecule.atom_block_ids[i_mol])); + failed_location = i_mol + 1; + AtomBlockId atom_blk_id = molecule.atom_block_ids[i_mol]; + if (!atom_blk_id.is_valid()) + continue; + // NOTE: This parent variable is only used in the recursion of this + // function. + t_pb* parent = nullptr; + block_pack_status = try_place_atom_block_rec(primitives_list[i_mol], + atom_blk_id, + cluster.pb, + &parent, + cluster_id, + atom_cluster_, + molecule_id, + cluster.router_data, + log_verbosity_, + prepacker_, + clustering_chain_info_, + mutable_atom_pb_lookup()); + } + + if (enable_pin_feasibility_filter_ && block_pack_status == e_block_pack_status::BLK_PASSED) { + // Check if pin usage is feasible for the current packing assignment + reset_lookahead_pins_used(cluster.pb); + try_update_lookahead_pins_used(cluster.pb, atom_cluster_, atom_pb_lookup()); + if (!check_lookahead_pins_used(cluster.pb, max_external_pin_util)) { + VTR_LOGV(log_verbosity_ > 4, "\t\t\tFAILED Pin Feasibility Filter\n"); + block_pack_status = e_block_pack_status::BLK_FAILED_FEASIBLE; + } else { + VTR_LOGV(log_verbosity_ > 3, "\t\t\tPin Feasibility: Passed pin feasibility filter\n"); + } + } + + if (block_pack_status == e_block_pack_status::BLK_PASSED) { + /* + * during the clustering step of `do_clustering`, `detailed_routing_stage` is incremented at each iteration until it a cluster + * is correctly generated or `detailed_routing_stage` assumes an invalid value (E_DETAILED_ROUTE_INVALID). + * depending on its value we have different behaviors: + * - E_DETAILED_ROUTE_AT_END_ONLY: Skip routing if heuristic is to route at the end of packing complex block. + * - E_DETAILED_ROUTE_FOR_EACH_ATOM: Try to route if heuristic is to route for every atom. If the clusterer arrives at this stage, + * it means that more checks have to be performed as the previous stage failed to generate a new cluster. + * + * mode_status is a data structure containing the status of the mode selection. Its members are: + * - bool is_mode_conflict + * - bool try_expand_all_modes + * - bool expand_all_modes + * + * is_mode_conflict affects this stage. Its value determines whether the cluster failed to pack after a mode conflict issue. + * It holds a flag that is used to verify whether try_intra_lb_route ended in a mode conflict issue. + * + * Until is_mode_conflict is set to FALSE by try_intra_lb_route, the loop re-iterates. If all the available modes are exhausted + * an error will be thrown during mode conflicts checks (this to prevent infinite loops). + * + * If the value is TRUE the cluster has to be re-routed, and its internal pb_graph_nodes will have more restrict choices + * for what regards the mode that has to be selected. + * + * is_mode_conflict is initially set to TRUE, and, unless a mode conflict is found, it is set to false in `try_intra_lb_route`. + * + * try_expand_all_modes is set if the node expansion failed to find a valid routing path. The clusterer tries to find another route + * by using all the modes during node expansion. + * + * expand_all_modes is used to enable the expansion of all the nodes using all the possible modes. + */ + t_mode_selection_status mode_status; + bool is_routed = false; + bool do_detailed_routing_stage = (cluster_legalization_strategy_ == ClusterLegalizationStrategy::FULL); + if (do_detailed_routing_stage) { + do { + reset_intra_lb_route(cluster.router_data); + is_routed = try_intra_lb_route(cluster.router_data, log_verbosity_, &mode_status); + } while (do_detailed_routing_stage && mode_status.is_mode_issue()); + } + + if (do_detailed_routing_stage && !is_routed) { + /* Cannot pack */ + VTR_LOGV(log_verbosity_ > 4, "\t\t\tFAILED Detailed Routing Legality\n"); + block_pack_status = e_block_pack_status::BLK_FAILED_ROUTE; + } else { + /* Pack successful, commit + * TODO: SW Engineering note - may want to update cluster stats here too instead of doing it outside + */ + VTR_ASSERT(block_pack_status == e_block_pack_status::BLK_PASSED); + if (molecule.is_chain()) { + /* Chained molecules often take up lots of area and are important, + * if a chain is packed in, want to rename logic block to match chain name */ + AtomBlockId chain_root_blk_id = molecule.atom_block_ids[molecule.pack_pattern->root_block->block_id]; + t_pb* cur_pb = atom_pb_lookup().atom_pb(chain_root_blk_id)->parent_pb; + while (cur_pb != nullptr) { + free(cur_pb->name); + cur_pb->name = vtr::strdup(atom_ctx.netlist().block_name(chain_root_blk_id).c_str()); + cur_pb = cur_pb->parent_pb; + } + // if this molecule is part of a chain, mark the cluster as having a long chain + // molecule. Also check if it's the first molecule in the chain to be packed. + // If so, update the chain id for this chain of molecules to make sure all + // molecules will be packed to the same chain id and can reach each other using + // the chain direct links between clusters + VTR_ASSERT(molecule.chain_id.is_valid()); + const t_chain_info& prepack_chain_info = prepacker_.get_molecule_chain_info(molecule.chain_id); + if (prepack_chain_info.is_long_chain) { + cluster.placement_stats->has_long_chain = true; + const t_clustering_chain_info& clustering_chain_info = clustering_chain_info_[molecule.chain_id]; + if (clustering_chain_info.chain_id == -1) { + update_clustering_chain_info(molecule_id, primitives_list[molecule.root]); + } + } + } + + //update cluster PartitionRegion if atom with floorplanning constraints was added + if (cluster_pr_update_check) { + cluster.pr = new_cluster_pr; + VTR_LOGV(log_verbosity_ > 2, "\nUpdated PartitionRegion of cluster\n"); + } + + // Update the cluster's NoC group ID. This is cheap so it does + // not need the check like the what the PR did above. + cluster.noc_grp_id = new_cluster_noc_grp_id; + + // Insert the molecule into the cluster for bookkeeping. + cluster.molecules.push_back(molecule_id); + + for (size_t i = 0; i < molecule.atom_block_ids.size(); i++) { + AtomBlockId atom_blk_id = molecule.atom_block_ids[i]; + if (!atom_blk_id.is_valid()) + continue; + + commit_primitive(cluster.placement_stats, primitives_list[i]); + + atom_cluster_[atom_blk_id] = cluster_id; + + // Update the num child blocks in pb + const t_pb* atom_pb = atom_pb_lookup().atom_pb(atom_blk_id); + VTR_ASSERT_SAFE(atom_pb != nullptr); + t_pb* cur_pb = atom_pb->parent_pb; + while (cur_pb != nullptr) { + cur_pb->pb_stats->num_child_blocks_in_pb++; + cur_pb = cur_pb->parent_pb; + } + } + + // Update the lookahead pins used. + commit_lookahead_pins_used(cluster.pb); + } + } + + if (block_pack_status != e_block_pack_status::BLK_PASSED) { + /* Pack unsuccessful, undo inserting molecule into cluster */ + for (size_t i = 0; i < failed_location; i++) { + AtomBlockId atom_blk_id = molecule.atom_block_ids[i]; + if (atom_blk_id) { + remove_atom_from_target(cluster.router_data, atom_blk_id, atom_pb_lookup()); + } + } + for (size_t i = 0; i < failed_location; i++) { + AtomBlockId atom_blk_id = molecule.atom_block_ids[i]; + if (atom_blk_id) { + revert_place_atom_block(atom_blk_id, cluster.router_data, atom_cluster_, mutable_atom_pb_lookup()); + } + } + reset_molecule_info(molecule_id); + + /* Packing failed, but a part of the pb tree is still allocated and pbs have their modes set. + * Before trying to pack next molecule the unused pbs need to be freed and, the most important, + * their modes reset. This task is performed by the cleanup_pb() function below. */ + cleanup_pb(cluster.pb); + } else { + VTR_LOGV(log_verbosity_ > 3, "\t\tPASSED pack molecule\n"); + } + } + + // Reset the cluster placement stats after packing a molecule. + // TODO: Not sure if this has to go here, but it makes sense to do it. + reset_tried_but_unused_cluster_placements(cluster.placement_stats); + + return block_pack_status; +} + +std::tuple +ClusterLegalizer::start_new_cluster(PackMoleculeId molecule_id, + t_logical_block_type_ptr cluster_type, + int cluster_mode) { + // Safety asserts to ensure the API is being called with valid arguments. + VTR_ASSERT_DEBUG(molecule_id.is_valid()); + VTR_ASSERT_DEBUG(cluster_type != nullptr); + VTR_ASSERT_DEBUG(cluster_mode < cluster_type->pb_graph_head->pb_type->num_modes); + // Ensure that the molecule has not already been placed. + VTR_ASSERT_SAFE(!molecule_cluster_[molecule_id].is_valid()); + // Safety asserts to ensure that the API was initialized properly. + VTR_ASSERT_DEBUG(lb_type_rr_graphs_ != nullptr); + + const AtomNetlist& atom_nlist = g_vpr_ctx.atom().netlist(); + + // Create the physical block for this cluster based on the type. + t_pb* cluster_pb = new t_pb; + cluster_pb->pb_graph_node = cluster_type->pb_graph_head; + alloc_and_load_pb_stats(cluster_pb); + cluster_pb->parent_pb = nullptr; + cluster_pb->mode = cluster_mode; + + // Allocate and load the LB router data + t_lb_router_data* router_data = alloc_and_load_router_data(&lb_type_rr_graphs_[cluster_type->index], + cluster_type); + + // Allocate and load the cluster's placement stats + t_intra_cluster_placement_stats* cluster_placement_stats = alloc_and_load_cluster_placement_stats(cluster_type, cluster_mode); + + // Create the new cluster + LegalizationCluster new_cluster; + new_cluster.pb = cluster_pb; + new_cluster.router_data = router_data; + new_cluster.pr = PartitionRegion(); + new_cluster.noc_grp_id = NocGroupId::INVALID(); + new_cluster.type = cluster_type; + new_cluster.placement_stats = cluster_placement_stats; + + // Try to pack the molecule into the new_cluster. + // When starting a new cluster, we set the external pin utilization to full + // (meaning all cluster pins are allowed to be used). + const t_ext_pin_util FULL_EXTERNAL_PIN_UTIL(1., 1.); + LegalizationClusterId new_cluster_id = LegalizationClusterId(legalization_cluster_ids_.size()); + e_block_pack_status pack_status = try_pack_molecule(molecule_id, + new_cluster, + new_cluster_id, + FULL_EXTERNAL_PIN_UTIL); + + if (pack_status == e_block_pack_status::BLK_PASSED) { + // Give the new cluster pb a name. The current convention is to name the + // cluster after the root atom of the first molecule packed into it. + const t_pack_molecule& molecule = prepacker_.get_molecule(molecule_id); + AtomBlockId root_atom = molecule.atom_block_ids[molecule.root]; + const std::string& root_atom_name = atom_nlist.block_name(root_atom); + if (new_cluster.pb->name != nullptr) + free(new_cluster.pb->name); + new_cluster.pb->name = vtr::strdup(root_atom_name.c_str()); + // Move the cluster into the vector of clusters and ids. + legalization_cluster_ids_.push_back(new_cluster_id); + legalization_clusters_.push_back(std::move(new_cluster)); + // Update the molecule to cluster map. + molecule_cluster_[molecule_id] = new_cluster_id; + } else { + // Delete the new_cluster. + free_pb(new_cluster.pb, mutable_atom_pb_lookup()); + delete new_cluster.pb; + free_router_data(new_cluster.router_data); + free_cluster_placement_stats(new_cluster.placement_stats); + new_cluster_id = LegalizationClusterId::INVALID(); + } + + return {pack_status, new_cluster_id}; +} + +e_block_pack_status ClusterLegalizer::add_mol_to_cluster(PackMoleculeId molecule_id, + LegalizationClusterId cluster_id) { + // Safety asserts to make sure the inputs are valid. + VTR_ASSERT_SAFE(cluster_id.is_valid() && (size_t)cluster_id < legalization_clusters_.size()); + VTR_ASSERT(legalization_cluster_ids_[cluster_id].is_valid() && "Cannot add to a destroyed cluster"); + // Ensure that the molecule has not already been placed. + VTR_ASSERT(!molecule_cluster_[molecule_id].is_valid()); + // Safety asserts to ensure that the API was initialized properly. + VTR_ASSERT_DEBUG(lb_type_rr_graphs_ != nullptr); + + // Get the cluster. + LegalizationCluster& cluster = legalization_clusters_[cluster_id]; + VTR_ASSERT(cluster.router_data != nullptr && cluster.placement_stats != nullptr + && "Cannot add molecule to cleaned cluster!"); + // Set the target_external_pin_util. + t_ext_pin_util target_ext_pin_util = target_external_pin_util_.get_pin_util(cluster.type->name); + // Try to pack the molecule into the cluster. + e_block_pack_status pack_status = try_pack_molecule(molecule_id, + cluster, + cluster_id, + target_ext_pin_util); + + // If the packing was successful, set the molecules' cluster to this one. + if (pack_status == e_block_pack_status::BLK_PASSED) + molecule_cluster_[molecule_id] = cluster_id; + + return pack_status; +} + +void ClusterLegalizer::destroy_cluster(LegalizationClusterId cluster_id) { + // Safety asserts to make sure the inputs are valid. + VTR_ASSERT_SAFE(cluster_id.is_valid() && (size_t)cluster_id < legalization_clusters_.size()); + VTR_ASSERT(legalization_cluster_ids_[cluster_id].is_valid() && "Cannot destroy an already destroyed cluster"); + // Get the cluster. + LegalizationCluster& cluster = legalization_clusters_[cluster_id]; + // Remove all molecules from the cluster. + for (PackMoleculeId mol_id : cluster.molecules) { + VTR_ASSERT_SAFE(molecule_cluster_[mol_id] == cluster_id); + molecule_cluster_[mol_id] = LegalizationClusterId::INVALID(); + // Revert the placement of all blocks in the molecule. + const t_pack_molecule& mol = prepacker_.get_molecule(mol_id); + for (AtomBlockId atom_blk_id : mol.atom_block_ids) { + if (atom_blk_id) { + revert_place_atom_block(atom_blk_id, cluster.router_data, atom_cluster_, mutable_atom_pb_lookup()); + } + } + reset_molecule_info(mol_id); + molecule_cluster_[mol_id] = LegalizationClusterId::INVALID(); + } + cluster.molecules.clear(); + // Free the rest of the cluster data. + // Casting things to nullptr for safety just in case someone is trying to use it. + free_pb(cluster.pb, mutable_atom_pb_lookup()); + delete cluster.pb; + cluster.pb = nullptr; + free_router_data(cluster.router_data); + cluster.router_data = nullptr; + cluster.pr = PartitionRegion(); + free_cluster_placement_stats(cluster.placement_stats); + cluster.placement_stats = nullptr; + + // Mark the cluster as invalid. + legalization_cluster_ids_[cluster_id] = LegalizationClusterId::INVALID(); +} + +void ClusterLegalizer::compress() { + // Create a map from the old ids to the new (compressed) one. + vtr::vector_map cluster_id_map; + cluster_id_map = compress_ids(legalization_cluster_ids_); + // Update all cluster values. + legalization_cluster_ids_ = clean_and_reorder_ids(cluster_id_map); + legalization_clusters_ = clean_and_reorder_values(legalization_clusters_, cluster_id_map); + // Update the reverse lookups. + for (PackMoleculeId mol_id : prepacker_.molecules()) { + LegalizationClusterId old_cluster_id = molecule_cluster_[mol_id]; + if (!old_cluster_id.is_valid()) + continue; + molecule_cluster_[mol_id] = cluster_id_map[old_cluster_id]; + } + for (size_t i = 0; i < atom_cluster_.size(); i++) { + AtomBlockId atom_blk_id = AtomBlockId(i); + LegalizationClusterId old_cluster_id = atom_cluster_[atom_blk_id]; + if (!old_cluster_id.is_valid()) + continue; + atom_cluster_[atom_blk_id] = cluster_id_map[old_cluster_id]; + } + // Shrink everything to fit + legalization_cluster_ids_.shrink_to_fit(); + legalization_clusters_.shrink_to_fit(); + atom_cluster_.shrink_to_fit(); +} + +void ClusterLegalizer::clean_cluster(LegalizationClusterId cluster_id) { + // Safety asserts to make sure the inputs are valid. + VTR_ASSERT_SAFE(cluster_id.is_valid() && (size_t)cluster_id < legalization_clusters_.size()); + // Get the cluster. + LegalizationCluster& cluster = legalization_clusters_[cluster_id]; + VTR_ASSERT(cluster.router_data != nullptr && cluster.placement_stats != nullptr + && "Should not clean an already cleaned cluster!"); + // Free the pb stats. + free_pb_stats_recursive(cluster.pb); + // Load the pb_route so we can free the cluster router data. + // The pb_route is used when creating a netlist from the legalized clusters. + std::vector* saved_lb_nets = cluster.router_data->saved_lb_nets; + t_pb_graph_node* pb_graph_node = cluster.pb->pb_graph_node; + cluster.pb->pb_route = alloc_and_load_pb_route(saved_lb_nets, pb_graph_node); + // Free the router data. + free_router_data(cluster.router_data); + cluster.router_data = nullptr; + // Free the cluster placement stats. + free_cluster_placement_stats(cluster.placement_stats); + cluster.placement_stats = nullptr; +} + +// TODO: This is fine for the current implementation of the legalizer. But if +// more complex strategies are added, this will need to be updated to +// check more than just routing (such as PR and NoC groups). +bool ClusterLegalizer::check_cluster_legality(LegalizationClusterId cluster_id) { + // Safety asserts to make sure the inputs are valid. + VTR_ASSERT_SAFE(cluster_id.is_valid() && (size_t)cluster_id < legalization_clusters_.size()); + // To check if a cluster is fully legal, try to perform an intra logic block + // route on the cluster. If it succeeds, the cluster is fully legal. + t_mode_selection_status mode_status; + LegalizationCluster& cluster = legalization_clusters_[cluster_id]; + return try_intra_lb_route(cluster.router_data, log_verbosity_, &mode_status); +} + +ClusterLegalizer::ClusterLegalizer(const AtomNetlist& atom_netlist, + const Prepacker& prepacker, + std::vector* lb_type_rr_graphs, + const std::vector& target_external_pin_util_str, + const t_pack_high_fanout_thresholds& high_fanout_thresholds, + ClusterLegalizationStrategy cluster_legalization_strategy, + bool enable_pin_feasibility_filter, + int log_verbosity) : prepacker_(prepacker) + { + // Verify that the inputs are valid. + VTR_ASSERT_SAFE(lb_type_rr_graphs != nullptr); + + // Get the target external pin utilization + // NOTE: Be careful with this constructor, it may throw a VPR_FATAL_ERROR. + target_external_pin_util_ = t_ext_pin_util_targets(target_external_pin_util_str); + + // Resize the molecule_cluster lookup to make the accesses much cheaper. + molecule_cluster_.resize(prepacker_.molecules().size(), LegalizationClusterId::INVALID()); + // Resize the atom_cluster lookup to make the accesses much cheaper. + atom_cluster_.resize(atom_netlist.blocks().size(), LegalizationClusterId::INVALID()); + // Default the clustering chain info for each chain. + clustering_chain_info_.resize(prepacker_.get_num_molecule_chains()); + // Pre-compute the max size of any molecule. + max_molecule_size_ = prepacker.get_max_molecule_size(); + // Get a reference to the rr graphs. + lb_type_rr_graphs_ = lb_type_rr_graphs; + // Find all NoC router atoms. + std::vector noc_atoms = find_noc_router_atoms(atom_netlist); + update_noc_reachability_partitions(noc_atoms, + atom_netlist, + high_fanout_thresholds, + atom_noc_grp_id_); + // Copy the options passed by the user + cluster_legalization_strategy_ = cluster_legalization_strategy; + enable_pin_feasibility_filter_ = enable_pin_feasibility_filter; + log_verbosity_ = log_verbosity; + atom_pb_lookup_ = AtomPBBimap(g_vpr_ctx.atom().lookup().atom_pb_bimap()); +} + +void ClusterLegalizer::reset() { + // Destroy all of the clusters and compress. + for (LegalizationClusterId cluster_id : legalization_cluster_ids_) { + if (!cluster_id.is_valid()) + continue; + destroy_cluster(cluster_id); + } + compress(); +} + +void ClusterLegalizer::verify() { + std::unordered_set atoms_checked; + auto& atom_ctx = g_vpr_ctx.atom(); + + if (clusters().size() == 0) { + VTR_LOG_WARN("Packing produced no clustered blocks"); + } + + /* + * Check that each atom block connects to one physical primitive and that the primitive links up to the parent clb + */ + for (auto blk_id : atom_ctx.netlist().blocks()) { + //Each atom should be part of a pb + const t_pb* atom_pb = atom_pb_lookup().atom_pb(blk_id); + if (!atom_pb) { + VPR_FATAL_ERROR(VPR_ERROR_PACK, + "Atom block %s is not mapped to a pb\n", + atom_ctx.netlist().block_name(blk_id).c_str()); + } + + //Check the reverse mapping is consistent + if (atom_pb_lookup().pb_atom(atom_pb) != blk_id) { + VPR_FATAL_ERROR(VPR_ERROR_PACK, + "pb %s does not contain atom block %s but atom block %s maps to pb.\n", + atom_pb->name, + atom_ctx.netlist().block_name(blk_id).c_str(), + atom_ctx.netlist().block_name(blk_id).c_str()); + } + + VTR_ASSERT(atom_ctx.netlist().block_name(blk_id) == atom_pb->name); + + const t_pb* cur_pb = atom_pb; + while (cur_pb->parent_pb) { + cur_pb = cur_pb->parent_pb; + VTR_ASSERT(cur_pb->name); + } + + LegalizationClusterId cluster_id = get_atom_cluster(blk_id); + if (cluster_id == LegalizationClusterId::INVALID()) { + VPR_FATAL_ERROR(VPR_ERROR_PACK, + "Atom %s is not mapped to a CLB\n", + atom_ctx.netlist().block_name(blk_id).c_str()); + } + + if (cur_pb != get_cluster_pb(cluster_id)) { + VPR_FATAL_ERROR(VPR_ERROR_PACK, + "CLB %s does not match CLB contained by pb %s.\n", + cur_pb->name, atom_pb->name); + } + } + + /* Check that I do not have spurious links in children pbs */ + for (LegalizationClusterId cluster_id : clusters()) { + if (!cluster_id.is_valid()) + continue; + check_cluster_atom_blocks(get_cluster_pb(cluster_id), atoms_checked, atom_pb_lookup()); + } + + for (auto blk_id : atom_ctx.netlist().blocks()) { + if (!atoms_checked.count(blk_id)) { + VPR_FATAL_ERROR(VPR_ERROR_PACK, + "Atom block %s not found in any cluster.\n", + atom_ctx.netlist().block_name(blk_id).c_str()); + } + } +} + +bool ClusterLegalizer::is_molecule_compatible(PackMoleculeId molecule_id, + LegalizationClusterId cluster_id) const { + VTR_ASSERT_SAFE(molecule_id.is_valid()); + VTR_ASSERT_SAFE(cluster_id.is_valid() && (size_t)cluster_id < legalization_clusters_.size()); + // Go through each atom in the molecule and check if there exists a free + // primitive for that atom block. + // TODO: This should probably also check if there are enough free primitives + // to support the given molecule. For example, a molecule of two FFs, + // but the cluster only has one free FF. This was something that Jason + // Luu was debating. Checking if placement exists for full molecule + // would be more robust, but checking individual atoms is faster. + const LegalizationCluster& cluster = legalization_clusters_[cluster_id]; + + const t_pack_molecule& molecule = prepacker_.get_molecule(molecule_id); + for (AtomBlockId atom_blk_id : molecule.atom_block_ids) { + // FIXME: Why is it possible that molecules contain invalid block IDs? + // This should be fixed! + if (!atom_blk_id.is_valid()) + continue; + // FIXME: This assert does not make sense. Can still check this even + // if the atom was clustered. + VTR_ASSERT(!is_atom_clustered(atom_blk_id)); + if (!exists_free_primitive_for_atom_block(cluster.placement_stats, + atom_blk_id)) { + return false; + } + } + // If every atom in the molecule has a free primitive it could theoretically + // be placed in, then it is compatible. + // TODO: Maybe add some more quick checks to save time, such as PR or NoC + // groups. + return true; +} + +size_t ClusterLegalizer::get_num_cluster_inputs_available( + LegalizationClusterId cluster_id) const { + VTR_ASSERT_SAFE(cluster_id.is_valid() && (size_t)cluster_id < legalization_clusters_.size()); + const LegalizationCluster& cluster = legalization_clusters_[cluster_id]; + + // Count the number of inputs available per pin class. + size_t inputs_avail = 0; + for (int i = 0; i < cluster.pb->pb_graph_node->num_input_pin_class; i++) { + inputs_avail += cluster.pb->pb_stats->input_pins_used[i].size(); + } + + return inputs_avail; +} + +void ClusterLegalizer::finalize() { + for (LegalizationClusterId cluster_id : legalization_cluster_ids_) { + if (!cluster_id.is_valid()) + continue; + // If the cluster has not already been cleaned, clean it. This will + // generate the pb_route necessary for generating a clustered netlist. + const LegalizationCluster& cluster = legalization_clusters_[cluster_id]; + if (cluster.router_data != nullptr) + clean_cluster(cluster_id); + } +} + +bool ClusterLegalizer::is_atom_blk_in_cluster_block(const AtomBlockId blk_id, const AtomBlockId clustered_blk_id) const { + const t_pb* cur_pb = atom_pb_lookup().atom_pb(blk_id); + const t_pb* pb = atom_pb_lookup().atom_pb(clustered_blk_id); + while (cur_pb) { + if (cur_pb == pb) { + return true; + } + cur_pb = cur_pb->parent_pb; + } + return false; +} + +ClusterLegalizer::~ClusterLegalizer() { + // Destroy all clusters (no need to compress). + for (LegalizationClusterId cluster_id : legalization_cluster_ids_) { + if (!cluster_id.is_valid()) + continue; + destroy_cluster(cluster_id); + } +} \ No newline at end of file diff --git a/vpr/src/pack/cluster_legalizer.h b/vpr/src/pack/cluster_legalizer.h index 4e3c628f7d6..bcf86e150ef 100644 --- a/vpr/src/pack/cluster_legalizer.h +++ b/vpr/src/pack/cluster_legalizer.h @@ -22,7 +22,7 @@ #include "vtr_strong_id.h" #include "vtr_vector.h" #include "vtr_vector_map.h" -#include "atom_pb_lookup.h" +#include "atom_pb_bimap.h" // Forward declarations class Prepacker; @@ -238,200 +238,6 @@ class ClusterLegalizer { */ void reset_molecule_info(PackMoleculeId mol_id); - /* - * @brief Allocates the stats stored within the pb of a cluster. - * - * Used to store information used during clustering. - */ - void alloc_and_load_pb_stats(t_pb* pb); - - /* - * @brief Check the atom blocks of a cluster pb. Used in the verify method. - */ - /* TODO: May want to check that all atom blocks are actually reached */ - void check_cluster_atom_blocks(t_pb* pb, std::unordered_set& blocks_checked); - - /// @brief Recursively frees the pb stats of the given pb, without freeing the - /// pb itself. - void free_pb_stats_recursive(t_pb* pb); - - /** - * @brief Checks whether an atom block can be added to a clustered block - * without violating floorplanning constraints. It also updates the - * clustered block's floorplanning region by taking the intersection of - * its current region and the floorplanning region of the given atom block. - * - * @param atom_blk_id A unique ID for the candidate atom block to - * be added to the growing cluster. - * @param cluster_pr The floorplanning regions of the clustered - * block. This function may update the given - * region. - * @param constraints The set of user-given place constraints. - * @param log_verbosity Controls the detail level of log information - * printed by this function. - * @param cluster_pr_needs_update Indicates whether the floorplanning region - * of the clustered block have updated. - * - * @return True if adding the given atom block to the clustered block does not - * violated any floorplanning constraints. - */ - bool check_cluster_floorplanning(AtomBlockId atom_blk_id, - PartitionRegion& cluster_pr, - const UserPlaceConstraints& constraints, - int log_verbosity, - bool& cluster_pr_needs_update); - - /** - * @brief Checks if an atom block can be added to a clustered block without - * violating NoC group constraints. For passing this check, either both - * clustered and atom blocks must belong to the same NoC group, or at - * least one of them should not belong to any NoC group. If the atom block - * is associated with a NoC group while the clustered block does not - * belong to any NoC groups, the NoC group ID of the atom block is assigned - * to the clustered block when the atom is added to it. - * - * @param atom_blk_id A unique ID for the candidate atom block to be - * added to the growing cluster. - * @param cluster_noc_grp_id The NoC group ID of the clustered block. This - * function may update this ID. - * @param atom_noc_grp_ids A mapping from atoms to NoC group IDs. - * @param log_verbosity Controls the detail level of log information - * printed by this function. - * - * @return True if adding the atom block the cluster does not violate NoC group - * constraints. - */ - bool check_cluster_noc_group(AtomBlockId atom_blk_id, - NocGroupId& cluster_noc_grp_id, - const vtr::vector& atom_noc_grp_ids, - int log_verbosity); - - /** - * @brief This function takes the root block of a chain molecule and a proposed - * placement primitive for this block. The function then checks if this - * chain root block has a placement constraint (such as being driven from - * outside the cluster) and returns the status of the placement accordingly. - */ - enum e_block_pack_status check_chain_root_placement_feasibility( - const t_pb_graph_node* pb_graph_node, - const t_chain_info& prepack_chain_info, - const t_clustering_chain_info& clustering_chain_info, - t_pack_patterns* mol_pack_patterns, - const AtomBlockId blk_id); - - /* - * @brief Check that the two atom blocks blk_id and sibling_blk_id (which should - * both be memory slices) are feasible, in the sense that they have - * precicely the same net connections (with the exception of nets in data - * port classes). - * - * Note that this routine does not check pin feasibility against the cur_pb_type; so - * primitive_type_feasible() should also be called on blk_id before concluding it is feasible. - */ - bool primitive_memory_sibling_feasible(const AtomBlockId blk_id, const t_pb_type* cur_pb_type, const AtomBlockId sibling_blk_id); - - /* - * @brief Check if the given atom is feasible in the given pb. - */ - bool primitive_feasible(const AtomBlockId blk_id, t_pb* cur_pb); - - /** - * @brief Try to place atom block into current primitive location - */ - enum e_block_pack_status - try_place_atom_block_rec(const t_pb_graph_node* pb_graph_node, - const AtomBlockId blk_id, - t_pb* cb, - t_pb** parent, - const LegalizationClusterId cluster_id, - vtr::vector_map& atom_cluster, - const PackMoleculeId molecule_id, - t_lb_router_data* router_data, - int verbosity, - const Prepacker& prepacker, - const vtr::vector_map& clustering_chain_info); - - /* - * @brief Resets nets used at different pin classes for determining pin - * feasibility. - */ - void reset_lookahead_pins_used(t_pb* cur_pb); - - /* - * @brief Checks if the sinks of the given net are reachable from the driver - * pb gpin. - */ - int net_sinks_reachable_in_cluster(const t_pb_graph_pin* driver_pb_gpin, const int depth, const AtomNetId net_id); - - /** - * @brief Returns the pb_graph_pin of the atom pin defined by the driver_pin_id in the driver_pb - */ - t_pb_graph_pin* get_driver_pb_graph_pin(const t_pb* driver_pb, const AtomPinId driver_pin_id); - - /** - * @brief Given a pin and its assigned net, mark all pin classes that are affected. - * Check if connecting this pin to it's driver pin or to all sink pins will - * require leaving a pb_block starting from the parent pb_block of the - * primitive till the root block (depth = 0). If leaving a pb_block is - * required add this net to the pin class (to increment the number of used - * pins from this class) that should be used to leave the pb_block. - */ - void compute_and_mark_lookahead_pins_used_for_pin(const t_pb_graph_pin* pb_graph_pin, - const t_pb* primitive_pb, - const AtomNetId net_id, - const vtr::vector_map& atom_cluster); - - /* - * @brief Determine if pins of speculatively packed pb are legal - */ - void compute_and_mark_lookahead_pins_used(const AtomBlockId blk_id, - const vtr::vector_map& atom_cluster); - - /* - * @brief Determine if speculatively packed cur_pb is pin feasible - * - * Runtime is actually not that bad for this. It's worst case O(k^2) where k is the - * number of pb_graph pins. Can use hash tables or make incremental if becomes an issue. - */ - void try_update_lookahead_pins_used(t_pb* cur_pb, - const vtr::vector_map& atom_cluster); - - /* - * @brief Check if the number of available inputs/outputs for a pin class is - * sufficient for speculatively packed blocks. - */ - bool check_lookahead_pins_used(t_pb* cur_pb, t_ext_pin_util max_external_pin_util); - - /* - * @brief Revert trial atom block iblock and free up memory space accordingly. - */ - void revert_place_atom_block(const AtomBlockId blk_id, - t_lb_router_data* router_data, - vtr::vector_map& atom_cluster); - - /* - * @brief Speculation successful, commit input/output pins used. - */ - void commit_lookahead_pins_used(t_pb* cur_pb); - - /** - * @brief Cleans up a pb after unsuccessful molecule packing - * - * Recursively frees pbs from a t_pb tree. The given root pb itself is not - * deleted. - * - * If a pb object has its children allocated then before freeing them the - * function checks if there is no atom that corresponds to any of them. The - * check is performed only for leaf (primitive) pbs. The function recurses for - * non-primitive pbs. - * - * The cleaning itself includes deleting all child pbs, resetting mode of the - * pb and also freeing its name. This prepares the pb for another round of - * molecule packing tryout. - */ - bool cleanup_pb(t_pb* pb); - - public: // Explicitly deleted default constructor. Need to use other constructor to @@ -716,22 +522,10 @@ class ClusterLegalizer { * @brief Determine if atom block is in cluster block. * */ - bool is_atom_blk_in_cluster_block(const AtomBlockId blk_id, const AtomBlockId clustered_blk_id) const { - const t_pb* cur_pb = atom_pb_lookup_.atom_pb(blk_id); - const t_pb* pb = atom_pb_lookup_.atom_pb(clustered_blk_id); - while (cur_pb) { - if (cur_pb == pb) { - return true; - } - cur_pb = cur_pb->parent_pb; - } - return false; - } - - void free_pb(t_pb* pb); + bool is_atom_blk_in_cluster_block(const AtomBlockId blk_id, const AtomBlockId clustered_blk_id) const; - inline const AtomPBLookUp &atom_pb_lookup() const {return atom_pb_lookup_;} - inline AtomPBLookUp &mutable_atom_pb_lookup() {return atom_pb_lookup_;} + inline const AtomPBBimap &atom_pb_lookup() const {return atom_pb_lookup_;} + inline AtomPBBimap &mutable_atom_pb_lookup() {return atom_pb_lookup_;} /// @brief Destructor of the class. Frees allocated data. @@ -801,6 +595,6 @@ class ClusterLegalizer { /// @brief The prepacker object that stores the molecules which will be /// legalized into clusters. const Prepacker& prepacker_; - AtomPBLookUp atom_pb_lookup_; + AtomPBBimap atom_pb_lookup_; }; \ No newline at end of file diff --git a/vpr/src/pack/cluster_router.cpp b/vpr/src/pack/cluster_router.cpp index 93739027149..d8c5c7b3ea9 100644 --- a/vpr/src/pack/cluster_router.cpp +++ b/vpr/src/pack/cluster_router.cpp @@ -34,7 +34,7 @@ #include "pb_type_graph.h" #include "lb_type_rr_graph.h" #include "cluster_router.h" -#include "atom_pb_lookup.h" +#include "atom_pb_bimap.h" /* #define PRINT_INTRA_LB_ROUTE */ @@ -75,10 +75,10 @@ class reservable_pq : public std::priority_queue { ******************************************************************************************/ static void free_lb_net_rt(t_lb_trace* lb_trace); static void free_lb_trace(t_lb_trace* lb_trace); -static void add_pin_to_rt_terminals(t_lb_router_data* router_data, const AtomPinId pin_id, const AtomPBLookUp &atom_to_pb); -static void remove_pin_from_rt_terminals(t_lb_router_data* router_data, const AtomPinId pin_id, const AtomPBLookUp &atom_to_pb); +static void add_pin_to_rt_terminals(t_lb_router_data* router_data, const AtomPinId pin_id, const AtomPBBimap &atom_to_pb); +static void remove_pin_from_rt_terminals(t_lb_router_data* router_data, const AtomPinId pin_id, const AtomPBBimap &atom_to_pb); -static void fix_duplicate_equivalent_pins(t_lb_router_data* router_data, const AtomPBLookUp &atom_to_pb); +static void fix_duplicate_equivalent_pins(t_lb_router_data* router_data, const AtomPBBimap &atom_to_pb); static void commit_remove_rt(t_lb_trace* rt, t_lb_router_data* router_data, e_commit_remove op, std::unordered_map* mode_map, t_mode_selection_status* mode_status); static bool is_skip_route_net(t_lb_trace* rt, t_lb_router_data* router_data); @@ -249,7 +249,7 @@ static bool check_edge_for_route_conflicts(std::unordered_map& atoms_added = *router_data->atoms_added; @@ -626,7 +626,7 @@ static void free_lb_trace(t_lb_trace* lb_trace) { /* Given a pin of a net, assign route tree terminals for it * Assumes that pin is not already assigned */ -static void add_pin_to_rt_terminals(t_lb_router_data* router_data, const AtomPinId pin_id, const AtomPBLookUp &atom_to_pb) { +static void add_pin_to_rt_terminals(t_lb_router_data* router_data, const AtomPinId pin_id, const AtomPBBimap &atom_to_pb) { std::vector& lb_nets = *router_data->intra_lb_nets; std::vector& lb_type_graph = *router_data->lb_type_graph; t_logical_block_type_ptr lb_type = router_data->lb_type; @@ -793,7 +793,7 @@ static void add_pin_to_rt_terminals(t_lb_router_data* router_data, const AtomPin /* Given a pin of a net, remove route tree terminals from it */ -static void remove_pin_from_rt_terminals(t_lb_router_data* router_data, const AtomPinId pin_id, const AtomPBLookUp &atom_to_pb) { +static void remove_pin_from_rt_terminals(t_lb_router_data* router_data, const AtomPinId pin_id, const AtomPBBimap &atom_to_pb) { std::vector& lb_nets = *router_data->intra_lb_nets; std::vector& lb_type_graph = *router_data->lb_type_graph; t_logical_block_type_ptr lb_type = router_data->lb_type; @@ -918,7 +918,7 @@ static void remove_pin_from_rt_terminals(t_lb_router_data* router_data, const At //To work around this, we fix all but one of these duplicate connections to route to specific pins, //(instead of the common sink). This ensures a legal routing is produced and that the duplicate pins //are not 'missing' in the clustered netlist. -static void fix_duplicate_equivalent_pins(t_lb_router_data* router_data, const AtomPBLookUp &atom_to_pb) { +static void fix_duplicate_equivalent_pins(t_lb_router_data* router_data, const AtomPBBimap &atom_to_pb) { auto& atom_ctx = g_vpr_ctx.atom(); std::vector& lb_type_graph = *router_data->lb_type_graph; diff --git a/vpr/src/pack/cluster_router.h b/vpr/src/pack/cluster_router.h index 8e1b0520596..95dd99fa3ae 100644 --- a/vpr/src/pack/cluster_router.h +++ b/vpr/src/pack/cluster_router.h @@ -16,8 +16,8 @@ void free_router_data(t_lb_router_data* router_data); void free_intra_lb_nets(std::vector* intra_lb_nets); /* Routing Functions */ -void add_atom_as_target(t_lb_router_data* router_data, const AtomBlockId blk_id, const AtomPBLookUp &atom_to_pb); -void remove_atom_from_target(t_lb_router_data* router_data, const AtomBlockId blk_id, const AtomPBLookUp &atom_to_pb); +void add_atom_as_target(t_lb_router_data* router_data, const AtomBlockId blk_id, const AtomPBBimap &atom_to_pb); +void remove_atom_from_target(t_lb_router_data* router_data, const AtomBlockId blk_id, const AtomPBBimap &atom_to_pb); void set_reset_pb_modes(t_lb_router_data* router_data, const t_pb* pb, const bool set); bool try_intra_lb_route(t_lb_router_data* router_data, int verbosity, t_mode_selection_status* mode_status); void reset_intra_lb_route(t_lb_router_data* router_data); diff --git a/vpr/src/pack/pack.cpp b/vpr/src/pack/pack.cpp index ab5ac3dd849..10b393a4bec 100644 --- a/vpr/src/pack/pack.cpp +++ b/vpr/src/pack/pack.cpp @@ -321,7 +321,7 @@ bool try_pack(t_packer_opts* packer_opts, */ /******************** End **************************/ g_vpr_ctx.mutable_atom().mutable_lookup().lock_atom_pb = false; - g_vpr_ctx.mutable_atom().mutable_lookup().set_atom_to_pb(cluster_legalizer.atom_pb_lookup().atom_to_pb()); + g_vpr_ctx.mutable_atom().mutable_lookup().set_atom_to_pb_bimap(cluster_legalizer.atom_pb_lookup()); //check clustering and output it check_and_output_clustering(cluster_legalizer, *packer_opts, is_clock, &arch); diff --git a/vpr/src/pack/post_routing_pb_pin_fixup.cpp b/vpr/src/pack/post_routing_pb_pin_fixup.cpp index 79e1962ca06..0deb23652d2 100644 --- a/vpr/src/pack/post_routing_pb_pin_fixup.cpp +++ b/vpr/src/pack/post_routing_pb_pin_fixup.cpp @@ -450,7 +450,7 @@ static AtomPinId find_mapped_atom_pin(const AtomContext& atom_ctx, const t_pb_graph_pin* sink_pb_pin = intra_lb_pb_pin_lookup.pb_gpin(logical_type->index, sink_pb_route_id); const t_pb* leaf_pb = pb->find_pb(sink_pb_pin->parent_node); - const AtomPortId& atom_port = atom_ctx.netlist().find_atom_port(atom_ctx.lookup().pb_atom(leaf_pb), sink_pb_pin->port->model_port); + const AtomPortId& atom_port = atom_ctx.netlist().find_atom_port(atom_ctx.lookup().atom_pb_bimap().pb_atom(leaf_pb), sink_pb_pin->port->model_port); BitIndex atom_pin_bit_index = leaf_pb->atom_pin_bit_index(sink_pb_pin); AtomPinId mapped_atom_pin = atom_ctx.netlist().port_pin(atom_port, atom_pin_bit_index); diff --git a/vpr/src/pack/sync_netlists_to_routing_flat.cpp b/vpr/src/pack/sync_netlists_to_routing_flat.cpp index a39c8944c44..27fd2230f3e 100644 --- a/vpr/src/pack/sync_netlists_to_routing_flat.cpp +++ b/vpr/src/pack/sync_netlists_to_routing_flat.cpp @@ -421,7 +421,7 @@ static void fixup_atom_pb_graph_pin_mapping(void){ const t_pb_graph_pin* atom_pbg_pin = pb_route.pb_graph_pin; t_pb* atom_pb = clb_pb->find_mutable_pb(atom_pbg_pin->parent_node); - AtomBlockId atb = atom_ctx.lookup().pb_atom(atom_pb); + AtomBlockId atb = atom_ctx.lookup().atom_pb_bimap().pb_atom(atom_pb); if(!atb) continue; diff --git a/vpr/src/pack/verify_clustering.cpp b/vpr/src/pack/verify_clustering.cpp index fca8f19d409..661ffa1c1e5 100644 --- a/vpr/src/pack/verify_clustering.cpp +++ b/vpr/src/pack/verify_clustering.cpp @@ -107,7 +107,7 @@ static bool is_atom_pb_in_cluster_pb(AtomBlockId atom_blk_id, const AtomLookup& atom_lookup, const ClusteredNetlist& clb_nlist) { // Get the pbs - const t_pb* atom_pb = atom_lookup.atom_pb(atom_blk_id); + const t_pb* atom_pb = atom_lookup.atom_pb_bimap().atom_pb(atom_blk_id); const t_pb* cluster_pb = clb_nlist.block_pb(clb_blk_id); // For the atom pb to be a part of the cluster pb, the atom pb must be a // descendent of the cluster pb (the cluster pb is the ancestor to all atom @@ -179,7 +179,7 @@ static unsigned check_clustering_pb_consistency(const ClusteredNetlist& clb_nlis ClusterBlockId atom_clb_blk_id = atom_lookup.atom_clb(atom_blk_id); if (!atom_clb_blk_id.is_valid()) continue; - const t_pb* atom_pb = atom_lookup.atom_pb(atom_blk_id); + const t_pb* atom_pb = atom_lookup.atom_pb_bimap().atom_pb(atom_blk_id); // Make sure the atom's pb exists if (atom_pb == nullptr) { VTR_LOG_ERROR( @@ -188,7 +188,7 @@ static unsigned check_clustering_pb_consistency(const ClusteredNetlist& clb_nlis num_errors++; } else { // Sanity check: atom_pb == pb_atom - if (atom_lookup.pb_atom(atom_pb) != atom_blk_id) { + if (atom_lookup.atom_pb_bimap().pb_atom(atom_pb) != atom_blk_id) { VTR_LOG_ERROR( "Atom block %zu in cluster block %zu has a pb which " "belongs to another atom.\n", diff --git a/vpr/src/power/power.cpp b/vpr/src/power/power.cpp index a162cf6380c..8e59103cef9 100644 --- a/vpr/src/power/power.cpp +++ b/vpr/src/power/power.cpp @@ -162,7 +162,7 @@ static void power_usage_primitive(t_power_usage* power_usage, t_pb* pb, t_pb_gra } if (pb) { - AtomBlockId blk_id = atom_ctx.lookup().pb_atom(pb); + AtomBlockId blk_id = atom_ctx.lookup().atom_pb_bimap().pb_atom(pb); SRAM_values = alloc_SRAM_values_from_truth_table(LUT_size, atom_ctx.netlist().block_truth_table(blk_id)); } else { diff --git a/vpr/src/route/overuse_report.cpp b/vpr/src/route/overuse_report.cpp index a51b22d35d1..14e705d10ab 100644 --- a/vpr/src/route/overuse_report.cpp +++ b/vpr/src/route/overuse_report.cpp @@ -335,7 +335,7 @@ static void report_congested_nets(const Netlist<>& net_list, os << "Net name = " << net_list.net_name(net_id) << ", "; if (is_flat) { AtomBlockId atom_blk_id = convert_to_atom_block_id(block_id); - os << "Driving block name = " << atom_lookup.atom_pb(atom_blk_id)->name << ", "; + os << "Driving block name = " << atom_lookup.atom_pb_bimap().atom_pb(atom_blk_id)->name << ", "; os << "Driving block type = " << g_vpr_ctx.clustering().clb_nlist.block_type(atom_lookup.atom_clb(atom_blk_id))->name << '\n'; } else { ClusterBlockId clb_blk_id = convert_to_cluster_block_id(block_id); @@ -363,7 +363,7 @@ static void report_congested_nets(const Netlist<>& net_list, << "\n"; if (is_flat) { auto pb_pin = atom_lookup.atom_pin_pb_graph_pin(convert_to_atom_pin_id(sink_id)); - auto pb_net_list = atom_lookup.atom_pb(convert_to_atom_block_id(net_list.pin_block(sink_id))); + auto pb_net_list = atom_lookup.atom_pb_bimap().atom_pb(convert_to_atom_block_id(net_list.pin_block(sink_id))); os << " " << "Pin Logical Num: " << pb_pin->pin_count_in_cluster << " PB Type: " << pb_pin->parent_node->pb_type->name << " Netlist PB: " << pb_net_list->name << " Parent PB Type: " << pb_net_list->parent_pb->pb_graph_node->pb_type->name << "Parent Netlist PB : " << pb_net_list->parent_pb->name << "\n"; os << " " diff --git a/vpr/src/timing/VprTimingGraphResolver.cpp b/vpr/src/timing/VprTimingGraphResolver.cpp index 8ace192035e..791615e5585 100644 --- a/vpr/src/timing/VprTimingGraphResolver.cpp +++ b/vpr/src/timing/VprTimingGraphResolver.cpp @@ -218,7 +218,7 @@ std::vector VprTimingGraphResolver::interconnect_delay_br //driver_component.inst_name = cluster_ctx.clb_nlist.block_name(src_blk); driver_component.type_name = "intra '"; if (is_flat_) { - const t_pb* atom_pb = atom_ctx.lookup().atom_pb((AtomBlockId&)src_blk); + const t_pb* atom_pb = atom_ctx.lookup().atom_pb_bimap().atom_pb((AtomBlockId&)src_blk); driver_component.type_name += (std::string(atom_pb->name) + "(" + atom_pb->hierarchical_type_name() + ")"); } else { driver_component.type_name += cluster_ctx.clb_nlist.block_type((ClusterBlockId&)src_blk)->name; @@ -263,7 +263,7 @@ std::vector VprTimingGraphResolver::interconnect_delay_br //sink_component.inst_name = cluster_ctx.clb_nlist.block_name(sink_blk); sink_component.type_name = "intra '"; if (is_flat_) { - sink_component.type_name += atom_ctx.lookup().atom_pb((AtomBlockId&)sink_blk)->name; + sink_component.type_name += atom_ctx.lookup().atom_pb_bimap().atom_pb((AtomBlockId&)sink_blk)->name; } else { sink_component.type_name += cluster_ctx.clb_nlist.block_type((ClusterBlockId&)sink_blk)->name; } diff --git a/vpr/src/util/vpr_utils.cpp b/vpr/src/util/vpr_utils.cpp index 70c0c8957fb..3fc6ce10036 100644 --- a/vpr/src/util/vpr_utils.cpp +++ b/vpr/src/util/vpr_utils.cpp @@ -301,7 +301,7 @@ static AtomPinId find_atom_pin_for_pb_route_id(ClusterBlockId clb, int pb_route_ //It is a leaf, and hence should map to an atom //Find the associated atom - AtomBlockId atom_block = atom_ctx.lookup().pb_atom(child_pb); + AtomBlockId atom_block = atom_ctx.lookup().atom_pb_bimap().pb_atom(child_pb); VTR_ASSERT(atom_block); //Now find the matching pin by seeing which pin maps to the gpin @@ -518,7 +518,7 @@ t_class_range get_class_range_for_block(const AtomBlockId atom_blk) { ClusterBlockId cluster_blk = atom_look_up.atom_clb(atom_blk); auto [physical_tile, sub_tile, sub_tile_cap, logical_block] = get_cluster_blk_physical_spec(cluster_blk); - const t_pb_graph_node* pb_graph_node = atom_look_up.atom_pb_graph_node(atom_blk); + const t_pb_graph_node* pb_graph_node = atom_look_up.atom_pb_bimap().atom_pb_graph_node(atom_blk); VTR_ASSERT(pb_graph_node != nullptr); return get_pb_graph_node_class_physical_range(physical_tile, sub_tile, @@ -986,7 +986,7 @@ const t_pb_graph_pin* find_pb_graph_pin(const AtomNetlist& netlist, const AtomLo //Get the graph node AtomBlockId blk_id = netlist.pin_block(pin_id); - const t_pb_graph_node* pb_gnode = netlist_lookup.atom_pb_graph_node(blk_id); + const t_pb_graph_node* pb_gnode = netlist_lookup.atom_pb_bimap().atom_pb_graph_node(blk_id); VTR_ASSERT(pb_gnode); //The graph node and pin/block should agree on the model they represent @@ -1007,7 +1007,7 @@ const t_pb_graph_pin* find_pb_graph_pin(const AtomNetlist& netlist, const AtomLo // TODO: Code duplication here. Could probably use a better pack-related abstraction // to avoid all this, as this function is only used in vpr/src/pack // Retrieves the pb_graph_pin associated with an AtomPinId -const t_pb_graph_pin* find_pb_graph_pin(const AtomNetlist& netlist, const AtomPBLookUp& atom_pb_lookup, const AtomPinId pin_id) { +const t_pb_graph_pin* find_pb_graph_pin(const AtomNetlist& netlist, const AtomPBBimap& atom_pb_lookup, const AtomPinId pin_id) { VTR_ASSERT(pin_id); //Get the graph node @@ -1281,7 +1281,7 @@ std::vector get_cluster_internal_class_pairs(const AtomLookup& atom_lookup, const auto& cluster_atoms = cluster_ctx.atoms_lookup[cluster_block_id]; for (AtomBlockId atom_blk_id : cluster_atoms) { - auto atom_pb_graph_node = atom_lookup.atom_pb_graph_node(atom_blk_id); + auto atom_pb_graph_node = atom_lookup.atom_pb_bimap().atom_pb_graph_node(atom_blk_id); auto class_range = get_pb_graph_node_class_physical_range(physical_tile, sub_tile, logical_block, @@ -1394,6 +1394,71 @@ int num_ext_inputs_atom_block(AtomBlockId blk_id) { return (ext_inps); } +/** + * @brief Free pb and remove its lookup data. + * CLB lookup data is removed from the global context + * and PB to Atom bimap data is removed from atom_pb_bimap + * + * @param pb + * Pointer to t_pb to be freed + * @param atom_pb_bimap + * Reference to the atom to pb bimap to free the data from + */ +void free_pb(t_pb* pb, AtomPBBimap& atom_pb_bimap) { + if (pb == nullptr) { + return; + } + + const t_pb_type* pb_type; + int i, j, mode; + + pb_type = pb->pb_graph_node->pb_type; + + if (pb->name) { + free(pb->name); + pb->name = nullptr; + } + + if (pb_type->blif_model == nullptr) { + mode = pb->mode; + for (i = 0; i < pb_type->modes[mode].num_pb_type_children && pb->child_pbs != nullptr; i++) { + for (j = 0; j < pb_type->modes[mode].pb_type_children[i].num_pb && pb->child_pbs[i] != nullptr; j++) { + if (pb->child_pbs[i][j].name != nullptr || pb->child_pbs[i][j].child_pbs != nullptr) { + free_pb(&pb->child_pbs[i][j], atom_pb_bimap); + } + } + if (pb->child_pbs[i]) { + //Free children (num_pb) + delete[] pb->child_pbs[i]; + } + } + if (pb->child_pbs) { + //Free child pointers (modes) + delete[] pb->child_pbs; + } + + pb->child_pbs = nullptr; + + } else { + /* Primitive */ + auto& atom_ctx = g_vpr_ctx.mutable_atom(); + auto blk_id = atom_pb_bimap.pb_atom(pb); + if (blk_id) { + //Update atom netlist mapping + atom_ctx.mutable_lookup().set_atom_clb(blk_id, ClusterBlockId::INVALID()); + atom_pb_bimap.set_atom_pb(blk_id, nullptr); + } + atom_pb_bimap.set_atom_pb(AtomBlockId::INVALID(), pb); + } + free_pb_stats(pb); +} + +/** + * @brief Free pb and remove its lookup data from global context. It is preffered to use the other overload if possible. + * + * @param pb + * Pointer to t_pb + */ void free_pb(t_pb* pb) { if (pb == nullptr) { return; @@ -1432,13 +1497,13 @@ void free_pb(t_pb* pb) { } else { /* Primitive */ auto& atom_ctx = g_vpr_ctx.mutable_atom(); - auto blk_id = atom_ctx.lookup().pb_atom(pb); + auto blk_id = atom_ctx.lookup().atom_pb_bimap().pb_atom(pb); if (blk_id) { //Update atom netlist mapping atom_ctx.mutable_lookup().set_atom_clb(blk_id, ClusterBlockId::INVALID()); - atom_ctx.mutable_lookup().set_atom_pb(blk_id, nullptr); + atom_ctx.mutable_lookup().mutable_atom_pb_bimap().set_atom_pb(blk_id, nullptr); } - atom_ctx.mutable_lookup().set_atom_pb(AtomBlockId::INVALID(), pb); + atom_ctx.mutable_lookup().mutable_atom_pb_bimap().set_atom_pb(AtomBlockId::INVALID(), pb); } free_pb_stats(pb); } diff --git a/vpr/src/util/vpr_utils.h b/vpr/src/util/vpr_utils.h index 5386f4cf661..c2e63e95abc 100644 --- a/vpr/src/util/vpr_utils.h +++ b/vpr/src/util/vpr_utils.h @@ -7,7 +7,7 @@ #include "rr_graph_utils.h" #include "vpr_types.h" #include "vtr_vector.h" -#include "atom_pb_lookup.h" +#include "atom_pb_bimap.h" #include #include @@ -167,7 +167,7 @@ const t_port* find_pb_graph_port(const t_pb_graph_node* pb_gnode, const std::str //Returns the graph pin matching name at pin index const t_pb_graph_pin* find_pb_graph_pin(const t_pb_graph_node* pb_gnode, const std::string& port_name, int index); -const t_pb_graph_pin* find_pb_graph_pin(const AtomNetlist& netlist, const AtomPBLookUp& atom_pb_lookup, const AtomPinId pin_id); +const t_pb_graph_pin* find_pb_graph_pin(const AtomNetlist& netlist, const AtomPBBimap& atom_pb_lookup, const AtomPinId pin_id); AtomPinId find_atom_pin(ClusterBlockId blk_id, const t_pb_graph_pin* pb_gpin); @@ -221,6 +221,7 @@ int num_ext_inputs_atom_block(AtomBlockId blk_id); std::tuple parse_direct_pin_name(std::string_view src_string, int line); void free_pb_stats(t_pb* pb); +void free_pb(t_pb* pb, AtomPBBimap& atom_pb_bimap); void free_pb(t_pb* pb); void print_switch_usage(); From f482530cdb8b84371b79ead3c04cab50810280bb Mon Sep 17 00:00:00 2001 From: Amir Poolad Date: Thu, 13 Mar 2025 15:21:34 -0400 Subject: [PATCH 04/14] Remove pb_free code duplication --- vpr/src/base/clustered_netlist.cpp | 4 +-- vpr/src/util/vpr_utils.cpp | 55 ------------------------------ vpr/src/util/vpr_utils.h | 1 - 3 files changed, 2 insertions(+), 58 deletions(-) diff --git a/vpr/src/base/clustered_netlist.cpp b/vpr/src/base/clustered_netlist.cpp index ce9e44dd422..2f2fce860a4 100644 --- a/vpr/src/base/clustered_netlist.cpp +++ b/vpr/src/base/clustered_netlist.cpp @@ -1,5 +1,5 @@ #include "clustered_netlist.h" - +#include "globals.h" #include "physical_types_util.h" #include "vtr_assert.h" @@ -171,7 +171,7 @@ ClusterNetId ClusteredNetlist::create_net(const std::string& name) { void ClusteredNetlist::remove_block_impl(const ClusterBlockId blk_id) { //Remove & invalidate pointers - free_pb(block_pbs_[blk_id]); + free_pb(block_pbs_[blk_id], g_vpr_ctx.mutable_atom().mutable_lookup().mutable_atom_pb_bimap()); delete block_pbs_[blk_id]; block_pbs_.insert(blk_id, NULL); block_types_.insert(blk_id, NULL); diff --git a/vpr/src/util/vpr_utils.cpp b/vpr/src/util/vpr_utils.cpp index 3fc6ce10036..53143e2b3ba 100644 --- a/vpr/src/util/vpr_utils.cpp +++ b/vpr/src/util/vpr_utils.cpp @@ -1453,61 +1453,6 @@ void free_pb(t_pb* pb, AtomPBBimap& atom_pb_bimap) { free_pb_stats(pb); } -/** - * @brief Free pb and remove its lookup data from global context. It is preffered to use the other overload if possible. - * - * @param pb - * Pointer to t_pb - */ -void free_pb(t_pb* pb) { - if (pb == nullptr) { - return; - } - - const t_pb_type* pb_type; - int i, j, mode; - - pb_type = pb->pb_graph_node->pb_type; - - if (pb->name) { - free(pb->name); - pb->name = nullptr; - } - - if (pb_type->blif_model == nullptr) { - mode = pb->mode; - for (i = 0; i < pb_type->modes[mode].num_pb_type_children && pb->child_pbs != nullptr; i++) { - for (j = 0; j < pb_type->modes[mode].pb_type_children[i].num_pb && pb->child_pbs[i] != nullptr; j++) { - if (pb->child_pbs[i][j].name != nullptr || pb->child_pbs[i][j].child_pbs != nullptr) { - free_pb(&pb->child_pbs[i][j]); - } - } - if (pb->child_pbs[i]) { - //Free children (num_pb) - delete[] pb->child_pbs[i]; - } - } - if (pb->child_pbs) { - //Free child pointers (modes) - delete[] pb->child_pbs; - } - - pb->child_pbs = nullptr; - - } else { - /* Primitive */ - auto& atom_ctx = g_vpr_ctx.mutable_atom(); - auto blk_id = atom_ctx.lookup().atom_pb_bimap().pb_atom(pb); - if (blk_id) { - //Update atom netlist mapping - atom_ctx.mutable_lookup().set_atom_clb(blk_id, ClusterBlockId::INVALID()); - atom_ctx.mutable_lookup().mutable_atom_pb_bimap().set_atom_pb(blk_id, nullptr); - } - atom_ctx.mutable_lookup().mutable_atom_pb_bimap().set_atom_pb(AtomBlockId::INVALID(), pb); - } - free_pb_stats(pb); -} - void free_pb_stats(t_pb* pb) { if (pb && pb->pb_stats != nullptr) { delete pb->pb_stats; diff --git a/vpr/src/util/vpr_utils.h b/vpr/src/util/vpr_utils.h index c2e63e95abc..5ca2cb57d7d 100644 --- a/vpr/src/util/vpr_utils.h +++ b/vpr/src/util/vpr_utils.h @@ -222,7 +222,6 @@ std::tuple parse_direct_pin_name(std::string void free_pb_stats(t_pb* pb); void free_pb(t_pb* pb, AtomPBBimap& atom_pb_bimap); -void free_pb(t_pb* pb); void print_switch_usage(); void print_usage_by_wire_length(); From 7f3b2744f25c5ed58a6ab553e1c3963b4e64ff60 Mon Sep 17 00:00:00 2001 From: Amir Poolad Date: Thu, 13 Mar 2025 15:56:12 -0400 Subject: [PATCH 05/14] Remove unused variable from update_connection_gain_values --- vpr/src/pack/greedy_candidate_selector.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/vpr/src/pack/greedy_candidate_selector.cpp b/vpr/src/pack/greedy_candidate_selector.cpp index fb42df475e1..495664ab965 100644 --- a/vpr/src/pack/greedy_candidate_selector.cpp +++ b/vpr/src/pack/greedy_candidate_selector.cpp @@ -384,11 +384,6 @@ void GreedyCandidateSelector::update_connection_gain_values( /*This function is called when the connection_gain values on the net net_id *require updating. */ - // Atom Context used to lookup the atom pb. - // TODO: Should investigate this. Using the atom pb in this class is very - // strange. - const AtomContext& atom_ctx = g_vpr_ctx.atom(); - int num_internal_connections, num_open_connections, num_stuck_connections; num_internal_connections = num_open_connections = num_stuck_connections = 0; @@ -397,6 +392,8 @@ void GreedyCandidateSelector::update_connection_gain_values( /* may wish to speed things up by ignoring clock nets since they are high fanout */ for (AtomPinId pin_id : atom_netlist_.net_pins(net_id)) { AtomBlockId blk_id = atom_netlist_.pin_block(pin_id); + // TODO: Should investigate this. Using the atom pb bimap through is_atom_blk_in_cluster_block + // in this class is very strange if (cluster_legalizer.get_atom_cluster(blk_id) == legalization_cluster_id && cluster_legalizer.is_atom_blk_in_cluster_block(blk_id, clustered_blk_id)) { num_internal_connections++; From c8fe4e09d52563c7b04800118e5c1b65ba1dca33 Mon Sep 17 00:00:00 2001 From: Amir Poolad Date: Thu, 13 Mar 2025 16:18:08 -0400 Subject: [PATCH 06/14] Add atom_pb_bimap locking This lock ensures that the atom_pb_bimap structure in global context is not accessed in packing. --- vpr/src/base/atom_lookup.h | 6 +++--- vpr/src/pack/pack.cpp | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/vpr/src/base/atom_lookup.h b/vpr/src/base/atom_lookup.h index 5e2da54c367..ca17b3e76b5 100644 --- a/vpr/src/base/atom_lookup.h +++ b/vpr/src/base/atom_lookup.h @@ -27,15 +27,15 @@ class AtomLookup { typedef vtr::Range pin_tnode_range; - bool lock_atom_pb = false; + bool lock_atom_pb_bimap = false; public: /* * PBs */ - inline AtomPBBimap &mutable_atom_pb_bimap() {return atom_to_pb_bimap_;} - inline const AtomPBBimap &atom_pb_bimap() const {return atom_to_pb_bimap_;} + inline AtomPBBimap &mutable_atom_pb_bimap() {VTR_ASSERT(!lock_atom_pb_bimap); return atom_to_pb_bimap_;} + inline const AtomPBBimap &atom_pb_bimap() const {VTR_ASSERT(!lock_atom_pb_bimap); return atom_to_pb_bimap_;} /* * PB Pins diff --git a/vpr/src/pack/pack.cpp b/vpr/src/pack/pack.cpp index 10b393a4bec..75b720fa275 100644 --- a/vpr/src/pack/pack.cpp +++ b/vpr/src/pack/pack.cpp @@ -176,7 +176,7 @@ bool try_pack(t_packer_opts* packer_opts, is_global, flat_placement_info); - g_vpr_ctx.mutable_atom().mutable_lookup().lock_atom_pb = true; + g_vpr_ctx.mutable_atom().mutable_lookup().lock_atom_pb_bimap = true; while (true) { //Cluster the netlist @@ -320,7 +320,7 @@ bool try_pack(t_packer_opts* packer_opts, * } */ /******************** End **************************/ - g_vpr_ctx.mutable_atom().mutable_lookup().lock_atom_pb = false; + g_vpr_ctx.mutable_atom().mutable_lookup().lock_atom_pb_bimap = false; g_vpr_ctx.mutable_atom().mutable_lookup().set_atom_to_pb_bimap(cluster_legalizer.atom_pb_lookup()); //check clustering and output it check_and_output_clustering(cluster_legalizer, *packer_opts, is_clock, &arch); From 6128622d78be868201ace4bf2611c08f307a4a67 Mon Sep 17 00:00:00 2001 From: Amir Poolad Date: Thu, 13 Mar 2025 16:52:30 -0400 Subject: [PATCH 07/14] Add documentation to atom_pb_bimap and general cleanup --- vpr/src/base/atom_lookup.h | 12 +++++++----- vpr/src/pack/atom_pb_bimap.h | 9 +++++++-- vpr/src/pack/pack.cpp | 1 - vpr/src/util/vpr_utils.cpp | 29 ----------------------------- vpr/src/util/vpr_utils.h | 1 - 5 files changed, 14 insertions(+), 38 deletions(-) diff --git a/vpr/src/base/atom_lookup.h b/vpr/src/base/atom_lookup.h index ca17b3e76b5..4df67f8b72f 100644 --- a/vpr/src/base/atom_lookup.h +++ b/vpr/src/base/atom_lookup.h @@ -37,6 +37,13 @@ class AtomLookup { inline AtomPBBimap &mutable_atom_pb_bimap() {VTR_ASSERT(!lock_atom_pb_bimap); return atom_to_pb_bimap_;} inline const AtomPBBimap &atom_pb_bimap() const {VTR_ASSERT(!lock_atom_pb_bimap); return atom_to_pb_bimap_;} + /** + * @brief Set atom to pb bimap + * + * @param atom_to_pb Reference to AtomPBBimab to be copied from + */ + void set_atom_to_pb_bimap(const AtomPBBimap& atom_to_pb){atom_to_pb_bimap_ = atom_to_pb;} + /* * PB Pins */ @@ -97,11 +104,6 @@ class AtomLookup { ///@brief Sets the bi-directional mapping between an atom netlist pin and timing graph node void set_atom_pin_tnode(const AtomPinId pin, const tatum::NodeId node, BlockTnode block_tnode_type); - - // Setter function for atom_to_pb_ - void set_atom_to_pb_bimap(const AtomPBBimap& atom_to_pb){ - atom_to_pb_bimap_ = atom_to_pb; - } private: //Types private: diff --git a/vpr/src/pack/atom_pb_bimap.h b/vpr/src/pack/atom_pb_bimap.h index 22d0de4fdd7..287ac1d1949 100644 --- a/vpr/src/pack/atom_pb_bimap.h +++ b/vpr/src/pack/atom_pb_bimap.h @@ -5,6 +5,13 @@ // Forward declaration class t_pb_graph_node; +/** + * @brief Class that holds a bimap between atoms and pb types. + * This means that you can get a pb from an atom and the + * other way around. + * + * Used in the global AtomLookup context and in ClusterLegalizer + */ class AtomPBBimap { public: AtomPBBimap() = default; @@ -30,8 +37,6 @@ class AtomPBBimap { */ void set_atom_pb(const AtomBlockId blk_id, const t_pb* pb); - const vtr::bimap &atom_to_pb() const {return atom_to_pb_;} - private: vtr::bimap atom_to_pb_; }; diff --git a/vpr/src/pack/pack.cpp b/vpr/src/pack/pack.cpp index 75b720fa275..64a9680973f 100644 --- a/vpr/src/pack/pack.cpp +++ b/vpr/src/pack/pack.cpp @@ -63,7 +63,6 @@ bool try_pack(t_packer_opts* packer_opts, // device if needed. DeviceContext& mutable_device_ctx = g_vpr_ctx.mutable_device(); - std::unordered_set is_clock, is_global; VTR_LOG("Begin packing '%s'.\n", packer_opts->circuit_file_name.c_str()); diff --git a/vpr/src/util/vpr_utils.cpp b/vpr/src/util/vpr_utils.cpp index 53143e2b3ba..51bfbf4939e 100644 --- a/vpr/src/util/vpr_utils.cpp +++ b/vpr/src/util/vpr_utils.cpp @@ -16,7 +16,6 @@ #include "globals.h" #include "vpr_utils.h" #include "cluster_placement.h" -#include "cluster_legalizer.h" #include "device_grid.h" #include "user_route_constraints.h" #include "grid_block.h" @@ -978,34 +977,6 @@ AtomPinId find_atom_pin(ClusterBlockId blk_id, const t_pb_graph_pin* pb_gpin) { return atom_pin; } -// Retrieves the pb_graph_pin associated with an AtomPinId -// Currently this function just wraps get_pb_graph_node_pin_from_model_port_pin() -// in a more convenient interface. -const t_pb_graph_pin* find_pb_graph_pin(const AtomNetlist& netlist, const AtomLookup& netlist_lookup, const AtomPinId pin_id) { - VTR_ASSERT(pin_id); - - //Get the graph node - AtomBlockId blk_id = netlist.pin_block(pin_id); - const t_pb_graph_node* pb_gnode = netlist_lookup.atom_pb_bimap().atom_pb_graph_node(blk_id); - VTR_ASSERT(pb_gnode); - - //The graph node and pin/block should agree on the model they represent - VTR_ASSERT(netlist.block_model(blk_id) == pb_gnode->pb_type->model); - - //Get the pin index - AtomPortId port_id = netlist.pin_port(pin_id); - int ipin = netlist.pin_port_bit(pin_id); - - //Get the model port - const t_model_ports* model_port = netlist.port_model(port_id); - VTR_ASSERT(model_port); - - return get_pb_graph_node_pin_from_model_port_pin(model_port, ipin, pb_gnode); -} - - -// TODO: Code duplication here. Could probably use a better pack-related abstraction -// to avoid all this, as this function is only used in vpr/src/pack // Retrieves the pb_graph_pin associated with an AtomPinId const t_pb_graph_pin* find_pb_graph_pin(const AtomNetlist& netlist, const AtomPBBimap& atom_pb_lookup, const AtomPinId pin_id) { VTR_ASSERT(pin_id); diff --git a/vpr/src/util/vpr_utils.h b/vpr/src/util/vpr_utils.h index 5ca2cb57d7d..f2b62cfac1c 100644 --- a/vpr/src/util/vpr_utils.h +++ b/vpr/src/util/vpr_utils.h @@ -189,7 +189,6 @@ int get_max_depth_of_pb_type(t_pb_type* pb_type); int get_max_nets_in_pb_type(const t_pb_type* pb_type); bool primitive_type_feasible(AtomBlockId blk_id, const t_pb_type* cur_pb_type); t_pb_graph_pin* get_pb_graph_node_pin_from_model_port_pin(const t_model_ports* model_port, const int model_pin, const t_pb_graph_node* pb_graph_node); -const t_pb_graph_pin* find_pb_graph_pin(const AtomNetlist& netlist, const AtomLookup& netlist_lookup, const AtomPinId pin_id); /// @brief Gets the pb_graph_node pin at the given pin index for the given /// pb_graph_node. t_pb_graph_pin* get_pb_graph_node_pin_from_pb_graph_node(t_pb_graph_node* pb_graph_node, int ipin); From 8d801d722278572485c9746bd1bafa6f20678328 Mon Sep 17 00:00:00 2001 From: Amir Poolad Date: Mon, 17 Mar 2025 12:31:31 -0400 Subject: [PATCH 08/14] Add documentation and clean up AtomPBBimap --- vpr/src/base/atom_lookup.h | 33 +++++++++++++++++++++++++++--- vpr/src/pack/atom_pb_bimap.cpp | 17 ++++++++++++++- vpr/src/pack/atom_pb_bimap.h | 14 +++++++++++++ vpr/src/pack/cluster_legalizer.cpp | 26 +++++++++++------------ vpr/src/pack/cluster_legalizer.h | 5 +++-- vpr/src/pack/pack.cpp | 12 ++++------- 6 files changed, 80 insertions(+), 27 deletions(-) diff --git a/vpr/src/base/atom_lookup.h b/vpr/src/base/atom_lookup.h index 4df67f8b72f..6dfff86f961 100644 --- a/vpr/src/base/atom_lookup.h +++ b/vpr/src/base/atom_lookup.h @@ -27,15 +27,36 @@ class AtomLookup { typedef vtr::Range pin_tnode_range; - bool lock_atom_pb_bimap = false; + public: /* * PBs */ - inline AtomPBBimap &mutable_atom_pb_bimap() {VTR_ASSERT(!lock_atom_pb_bimap); return atom_to_pb_bimap_;} - inline const AtomPBBimap &atom_pb_bimap() const {VTR_ASSERT(!lock_atom_pb_bimap); return atom_to_pb_bimap_;} + + /** + * @brief Sets the atom to pb bimap access lock to value. + * If set to true, access to the bimap is prohibited and will result in failing assertions. + * + * @param value Value to set to lock to + */ + inline void set_atom_pb_bimap_lock(bool value) { + VTR_ASSERT_SAFE_MSG(lock_atom_pb_bimap_ != value, "Double locking or unlocking the atom pb bimap lock"); + lock_atom_pb_bimap_ = value; + } + + /// @brief Gets the current atom to pb bimap lock value. + inline bool atom_pb_bimap_islocked() {return lock_atom_pb_bimap_;} + + + // All accesses, mutable or immutable, to the atom to pb bimap + // will result in failing assertions if the lock is set to true. + // This is done to make sure there is only a single source of + // data in places that are supposed to use a local data structure + // instead of the global context. + inline AtomPBBimap &mutable_atom_pb_bimap() {VTR_ASSERT(!lock_atom_pb_bimap_); return atom_to_pb_bimap_;} + inline const AtomPBBimap &atom_pb_bimap() const {VTR_ASSERT(!lock_atom_pb_bimap_); return atom_to_pb_bimap_;} /** * @brief Set atom to pb bimap @@ -107,6 +128,12 @@ class AtomLookup { private: //Types private: + + /** + * @brief Allows or disallows access to the AtomPBBimap data. + * Useful to make sure global context is not accessed in places you don't want it to. + */ + bool lock_atom_pb_bimap_ = false; AtomPBBimap atom_to_pb_bimap_; vtr::vector_map atom_pin_to_pb_graph_pin_; diff --git a/vpr/src/pack/atom_pb_bimap.cpp b/vpr/src/pack/atom_pb_bimap.cpp index 6c61aba7d74..f08a21f39fc 100644 --- a/vpr/src/pack/atom_pb_bimap.cpp +++ b/vpr/src/pack/atom_pb_bimap.cpp @@ -1,4 +1,14 @@ +/** + * @file + * @author Amir Poolad + * @date March 2025 + * @brief The code for the AtomPBBimap class. + * + * This file implements the various functions of the AtomPBBimap class. + */ + #include "atom_pb_bimap.h" +#include "atom_netlist.h" AtomPBBimap::AtomPBBimap(const vtr::bimap& atom_to_pb) { atom_to_pb_ = atom_to_pb; @@ -34,7 +44,6 @@ const t_pb_graph_node* AtomPBBimap::atom_pb_graph_node(const AtomBlockId blk_id) void AtomPBBimap::set_atom_pb(const AtomBlockId blk_id, const t_pb* pb) { //If either of blk_id or pb are not valid, //remove any mapping - if (!blk_id && pb) { //Remove atom_to_pb_.erase(pb); @@ -46,3 +55,9 @@ void AtomPBBimap::set_atom_pb(const AtomBlockId blk_id, const t_pb* pb) { atom_to_pb_.update(blk_id, pb); } } + +void AtomPBBimap::reset_bimap(const AtomNetlist &netlist) { + for (auto blk : netlist.blocks()) { + set_atom_pb(blk, nullptr); + } +} diff --git a/vpr/src/pack/atom_pb_bimap.h b/vpr/src/pack/atom_pb_bimap.h index 287ac1d1949..55df740bd87 100644 --- a/vpr/src/pack/atom_pb_bimap.h +++ b/vpr/src/pack/atom_pb_bimap.h @@ -1,3 +1,13 @@ +/** + * @file + * @author Amir Poolad + * @date March 2025 + * @brief The declaration of the AtomPBBimap class. + * + * This file declares a class called AtomPBBimap that + * contains a two way mapping between AtomBlockIds and pb types. + */ + #pragma once #include "vpr_types.h" @@ -37,6 +47,10 @@ class AtomPBBimap { */ void set_atom_pb(const AtomBlockId blk_id, const t_pb* pb); + /// @brief Sets the pb for all blocks in the netlist to nullptr. + void reset_bimap(const AtomNetlist &netlist); + private: + /// @brief Two way map between AtomBlockIds and t_pb vtr::bimap atom_to_pb_; }; diff --git a/vpr/src/pack/cluster_legalizer.cpp b/vpr/src/pack/cluster_legalizer.cpp index fdba3c8232e..6b32f5cd8e5 100644 --- a/vpr/src/pack/cluster_legalizer.cpp +++ b/vpr/src/pack/cluster_legalizer.cpp @@ -39,10 +39,10 @@ #include "vtr_vector_map.h" /* -* @brief Allocates the stats stored within the pb of a cluster. -* -* Used to store information used during clustering. -*/ + * @brief Allocates the stats stored within the pb of a cluster. + * + * Used to store information used during clustering. + */ static void alloc_and_load_pb_stats(t_pb* pb) { /* Call this routine when starting to fill up a new cluster. It resets * * the gain vector, etc. */ @@ -58,8 +58,8 @@ static void alloc_and_load_pb_stats(t_pb* pb) { } /* -* @brief Check the atom blocks of a cluster pb. Used in the verify method. -*/ + * @brief Check the atom blocks of a cluster pb. Used in the verify method. + */ /* TODO: May want to check that all atom blocks are actually reached */ static void check_cluster_atom_blocks(t_pb* pb, std::unordered_set& blocks_checked, const AtomPBBimap &atom_pb_lookup) { const AtomContext& atom_ctx = g_vpr_ctx.atom(); @@ -118,7 +118,6 @@ static void free_pb_stats_recursive(t_pb* pb) { } } - /** * @brief Checks whether an atom block can be added to a clustered block * without violating floorplanning constraints. It also updates the @@ -244,11 +243,12 @@ static bool check_cluster_noc_group(AtomBlockId atom_blk_id, // If the cluster belongs to a different NoC group than the atom's group, // they are incompatible. VTR_LOGV(log_verbosity > 3, - "\t\t\t NoC Group: Atom block %d failed NoC group check for cluster. Cluster's NoC group: %d, atom's NoC group: %d\n", - atom_blk_id, (size_t)cluster_noc_grp_id, (size_t)atom_noc_grp_id); - return false; + "\t\t\t NoC Group: Atom block %d failed NoC group check for cluster. Cluster's NoC group: %d, atom's NoC group: %d\n", + atom_blk_id, (size_t)cluster_noc_grp_id, (size_t)atom_noc_grp_id); +return false; } + /** * @brief This function takes the root block of a chain molecule and a proposed * placement primitive for this block. The function then checks if this @@ -260,7 +260,7 @@ static enum e_block_pack_status check_chain_root_placement_feasibility(const t_p const t_clustering_chain_info& clustering_chain_info, t_pack_patterns* mol_pack_patterns, const AtomBlockId blk_id) { - const AtomNetlist& netlist_context = g_vpr_ctx.atom().netlist(); + const AtomNetlist& atom_netlist = g_vpr_ctx.atom().netlist(); enum e_block_pack_status block_pack_status = e_block_pack_status::BLK_PASSED; @@ -270,10 +270,10 @@ static enum e_block_pack_status check_chain_root_placement_feasibility(const t_p t_model_ports* root_port = chain_root_pins[0][0]->port->model_port; AtomNetId chain_net_id; - auto port_id = netlist_context.find_atom_port(blk_id, root_port); + auto port_id = atom_netlist.find_atom_port(blk_id, root_port); if (port_id) { - chain_net_id = netlist_context.port_net(port_id, chain_root_pins[0][0]->pin_number); + chain_net_id = atom_netlist.port_net(port_id, chain_root_pins[0][0]->pin_number); } // if this block is part of a long chain or it is driven by a cluster diff --git a/vpr/src/pack/cluster_legalizer.h b/vpr/src/pack/cluster_legalizer.h index faa4032edf4..2c81fd041e0 100644 --- a/vpr/src/pack/cluster_legalizer.h +++ b/vpr/src/pack/cluster_legalizer.h @@ -29,7 +29,6 @@ class Prepacker; class t_intra_cluster_placement_stats; class t_pb_graph_node; struct t_lb_router_data; -class UserPlaceConstraints; // A special ID to identify the legalization clusters. This is separate from the // ClusterBlockId since this legalizer is not necessarily tied to the Clustered @@ -192,7 +191,6 @@ struct LegalizationCluster { * * // new_cluster_id now contains a fully legalized cluster. */ - class ClusterLegalizer { public: // Iterator for the legalization cluster IDs @@ -593,6 +591,9 @@ class ClusterLegalizer { /// @brief The prepacker object that stores the molecules which will be /// legalized into clusters. const Prepacker& prepacker_; + + /// @brief A two way map between AtomBlockIds and pb types. This is a copy + /// of the AtomPBBimap in the global context's AtomLookup AtomPBBimap atom_pb_lookup_; }; diff --git a/vpr/src/pack/pack.cpp b/vpr/src/pack/pack.cpp index 64a9680973f..2cfc0234579 100644 --- a/vpr/src/pack/pack.cpp +++ b/vpr/src/pack/pack.cpp @@ -175,7 +175,7 @@ bool try_pack(t_packer_opts* packer_opts, is_global, flat_placement_info); - g_vpr_ctx.mutable_atom().mutable_lookup().lock_atom_pb_bimap = true; + g_vpr_ctx.mutable_atom().mutable_lookup().set_atom_pb_bimap_lock(true); while (true) { //Cluster the netlist @@ -288,11 +288,8 @@ bool try_pack(t_packer_opts* packer_opts, } //Reset clustering for re-packing - // g_vpr_ctx.mutable_atom().mutable_lookup().lock_atom_pb = false; - for (auto blk : g_vpr_ctx.atom().netlist().blocks()) { - g_vpr_ctx.mutable_atom().mutable_lookup().set_atom_clb(blk, ClusterBlockId::INVALID()); - cluster_legalizer.mutable_atom_pb_lookup().set_atom_pb(blk, nullptr); - } + cluster_legalizer.mutable_atom_pb_lookup().reset_bimap(g_vpr_ctx.atom().netlist()); + for (auto net : g_vpr_ctx.atom().netlist().nets()) { g_vpr_ctx.mutable_atom().mutable_lookup().remove_atom_net(net); } @@ -301,7 +298,6 @@ bool try_pack(t_packer_opts* packer_opts, // Reset the cluster legalizer for re-clustering. cluster_legalizer.reset(); - // g_vpr_ctx.mutable_atom().mutable_lookup().lock_atom_pb = true; ++pack_iteration; } @@ -319,7 +315,7 @@ bool try_pack(t_packer_opts* packer_opts, * } */ /******************** End **************************/ - g_vpr_ctx.mutable_atom().mutable_lookup().lock_atom_pb_bimap = false; + g_vpr_ctx.mutable_atom().mutable_lookup().set_atom_pb_bimap_lock(false); g_vpr_ctx.mutable_atom().mutable_lookup().set_atom_to_pb_bimap(cluster_legalizer.atom_pb_lookup()); //check clustering and output it check_and_output_clustering(cluster_legalizer, *packer_opts, is_clock, &arch); From 4571d72a667ade965550b6e52bb3b4106c2ba7dd Mon Sep 17 00:00:00 2001 From: Amir Poolad Date: Mon, 17 Mar 2025 18:24:31 -0400 Subject: [PATCH 09/14] Add doxygen comments to atom_pb_bimap getter functions --- vpr/src/base/atom_lookup.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/vpr/src/base/atom_lookup.h b/vpr/src/base/atom_lookup.h index 6dfff86f961..9e2ccd1d278 100644 --- a/vpr/src/base/atom_lookup.h +++ b/vpr/src/base/atom_lookup.h @@ -47,7 +47,7 @@ class AtomLookup { } /// @brief Gets the current atom to pb bimap lock value. - inline bool atom_pb_bimap_islocked() {return lock_atom_pb_bimap_;} + inline bool atom_pb_bimap_islocked() const {return lock_atom_pb_bimap_;} // All accesses, mutable or immutable, to the atom to pb bimap @@ -55,7 +55,13 @@ class AtomLookup { // This is done to make sure there is only a single source of // data in places that are supposed to use a local data structure // instead of the global context. + + /// @brief Returns a mutable reference to the atom to pb bimap, provided that access to it is unlocked. It will result in a crash otherwise. + /// @return Mutable reference to the atom pb bimap. inline AtomPBBimap &mutable_atom_pb_bimap() {VTR_ASSERT(!lock_atom_pb_bimap_); return atom_to_pb_bimap_;} + + /// @brief Returns an immutable reference to the atom to pb bimap, provided that access to it is unlocked. It will result in a crash otherwise. + /// @return Immutable reference to the atom pb bimap. inline const AtomPBBimap &atom_pb_bimap() const {VTR_ASSERT(!lock_atom_pb_bimap_); return atom_to_pb_bimap_;} /** From e2ac8295633a32ee72526e31a5097e56fae93915 Mon Sep 17 00:00:00 2001 From: Amir Poolad Date: Tue, 18 Mar 2025 20:07:34 -0400 Subject: [PATCH 10/14] Fix styling regressions --- vpr/src/base/atom_lookup.h | 3 - vpr/src/pack/cluster_legalizer.cpp | 336 ++++++++++++++--------------- vpr/src/pack/cluster_legalizer.h | 1 - vpr/src/util/vpr_utils.cpp | 1 - 4 files changed, 166 insertions(+), 175 deletions(-) diff --git a/vpr/src/base/atom_lookup.h b/vpr/src/base/atom_lookup.h index 9e2ccd1d278..f3b59f9b4e3 100644 --- a/vpr/src/base/atom_lookup.h +++ b/vpr/src/base/atom_lookup.h @@ -14,7 +14,6 @@ #include "tatum/TimingGraphFwd.hpp" #include "vtr_optional.h" - #include "atom_pb_bimap.h" /** @@ -27,8 +26,6 @@ class AtomLookup { typedef vtr::Range pin_tnode_range; - - public: /* * PBs diff --git a/vpr/src/pack/cluster_legalizer.cpp b/vpr/src/pack/cluster_legalizer.cpp index 6b32f5cd8e5..9244f14eed8 100644 --- a/vpr/src/pack/cluster_legalizer.cpp +++ b/vpr/src/pack/cluster_legalizer.cpp @@ -44,17 +44,17 @@ * Used to store information used during clustering. */ static void alloc_and_load_pb_stats(t_pb* pb) { - /* Call this routine when starting to fill up a new cluster. It resets * - * the gain vector, etc. */ - - pb->pb_stats = new t_pb_stats; - - pb->pb_stats->input_pins_used = std::vector>(pb->pb_graph_node->num_input_pin_class); - pb->pb_stats->output_pins_used = std::vector>(pb->pb_graph_node->num_output_pin_class); - pb->pb_stats->lookahead_input_pins_used = std::vector>(pb->pb_graph_node->num_input_pin_class); - pb->pb_stats->lookahead_output_pins_used = std::vector>(pb->pb_graph_node->num_output_pin_class); - - pb->pb_stats->num_child_blocks_in_pb = 0; + /* Call this routine when starting to fill up a new cluster. It resets * + * the gain vector, etc. */ + + pb->pb_stats = new t_pb_stats; + + pb->pb_stats->input_pins_used = std::vector>(pb->pb_graph_node->num_input_pin_class); + pb->pb_stats->output_pins_used = std::vector>(pb->pb_graph_node->num_output_pin_class); + pb->pb_stats->lookahead_input_pins_used = std::vector>(pb->pb_graph_node->num_input_pin_class); + pb->pb_stats->lookahead_output_pins_used = std::vector>(pb->pb_graph_node->num_output_pin_class); + + pb->pb_stats->num_child_blocks_in_pb = 0; } /* @@ -150,8 +150,8 @@ static bool check_cluster_floorplanning(AtomBlockId atom_blk_id, // constrained. if (!part_id.is_valid()) { VTR_LOGV(log_verbosity > 3, - "\t\t\t Intersect: Atom block %d has no floorplanning constraints\n", - atom_blk_id); + "\t\t\t Intersect: Atom block %d has no floorplanning constraints\n", + atom_blk_id); cluster_pr_needs_update = false; return true; } @@ -189,12 +189,11 @@ static bool check_cluster_floorplanning(AtomBlockId atom_blk_id, // updated. cluster_pr_needs_update = true; VTR_LOGV(log_verbosity > 3, - "\t\t\t Intersect: Atom block %d passed cluster, cluster PR was updated with intersection result \n", - atom_blk_id); - return true; + "\t\t\t Intersect: Atom block %d passed cluster, cluster PR was updated with intersection result \n", + atom_blk_id); +return true; } - /** * @brief Checks if an atom block can be added to a clustered block without * violating NoC group constraints. For passing this check, either both @@ -225,8 +224,8 @@ static bool check_cluster_noc_group(AtomBlockId atom_blk_id, // If the cluster does not have a NoC group, assign the atom's NoC group // to the cluster. VTR_LOGV(log_verbosity > 3, - "\t\t\t NoC Group: Atom block %d passed cluster, cluster's NoC group was updated with the atom's group %d\n", - atom_blk_id, (size_t)atom_noc_grp_id); + "\t\t\t NoC Group: Atom block %d passed cluster, cluster's NoC group was updated with the atom's group %d\n", + atom_blk_id, (size_t)atom_noc_grp_id); cluster_noc_grp_id = atom_noc_grp_id; return true; } @@ -235,20 +234,19 @@ static bool check_cluster_noc_group(AtomBlockId atom_blk_id, // If the cluster has the same NoC group ID as the atom, they are // compatible. VTR_LOGV(log_verbosity > 3, - "\t\t\t NoC Group: Atom block %d passed cluster, cluster's NoC group was compatible with the atom's group %d\n", - atom_blk_id, (size_t)atom_noc_grp_id); + "\t\t\t NoC Group: Atom block %d passed cluster, cluster's NoC group was compatible with the atom's group %d\n", + atom_blk_id, (size_t)atom_noc_grp_id); return true; } // If the cluster belongs to a different NoC group than the atom's group, // they are incompatible. VTR_LOGV(log_verbosity > 3, - "\t\t\t NoC Group: Atom block %d failed NoC group check for cluster. Cluster's NoC group: %d, atom's NoC group: %d\n", - atom_blk_id, (size_t)cluster_noc_grp_id, (size_t)atom_noc_grp_id); -return false; + "\t\t\t NoC Group: Atom block %d failed NoC group check for cluster. Cluster's NoC group: %d, atom's NoC group: %d\n", + atom_blk_id, (size_t)cluster_noc_grp_id, (size_t)atom_noc_grp_id); + return false; } - /** * @brief This function takes the root block of a chain molecule and a proposed * placement primitive for this block. The function then checks if this @@ -315,14 +313,14 @@ static enum e_block_pack_status check_chain_root_placement_feasibility(const t_p } /* -* @brief Check that the two atom blocks blk_id and sibling_blk_id (which should -* both be memory slices) are feasible, in the sense that they have -* precicely the same net connections (with the exception of nets in data -* port classes). -* -* Note that this routine does not check pin feasibility against the cur_pb_type; so -* primitive_type_feasible() should also be called on blk_id before concluding it is feasible. -*/ + * @brief Check that the two atom blocks blk_id and sibling_blk_id (which should + * both be memory slices) are feasible, in the sense that they have + * precicely the same net connections (with the exception of nets in data + * port classes). + * + * Note that this routine does not check pin feasibility against the cur_pb_type; so + * primitive_type_feasible() should also be called on blk_id before concluding it is feasible. + */ static bool primitive_memory_sibling_feasible(const AtomBlockId blk_id, const t_pb_type* cur_pb_type, const AtomBlockId sibling_blk_id) { const AtomContext& atom_ctx = g_vpr_ctx.atom(); @@ -407,7 +405,7 @@ static bool primitive_feasible(const AtomBlockId blk_id, t_pb* cur_pb, const Ato if (cur_pb_type->class_type == MEMORY_CLASS) { /* Memory class has additional feasibility requirements: - * - all siblings must share all nets, including open nets, with the exception of data nets */ + * - all siblings must share all nets, including open nets, with the exception of data nets */ /* find sibling if one exists */ const t_pb *sibling_memory_pb = find_memory_sibling(cur_pb); @@ -505,8 +503,8 @@ try_place_atom_block_rec(const t_pb_graph_node* pb_graph_node, const t_pb_type* pb_type = pb_graph_node->pb_type; /* Any pb_type under an mode, which is disabled for packing, should not be considerd for mapping - * Early exit to flag failure - */ + * Early exit to flag failure + */ if (true == pb_type->parent_mode->disable_packing) { return e_block_pack_status::BLK_FAILED_FEASIBLE; } @@ -515,8 +513,8 @@ try_place_atom_block_rec(const t_pb_graph_node* pb_graph_node, if (is_primitive) { VTR_ASSERT(!atom_to_pb.pb_atom(pb) - && atom_to_pb.atom_pb(blk_id) == nullptr - && atom_cluster[blk_id] == LegalizationClusterId::INVALID()); + && atom_to_pb.atom_pb(blk_id) == nullptr + && atom_cluster[blk_id] == LegalizationClusterId::INVALID()); /* try pack to location */ VTR_ASSERT(pb->name == nullptr); pb->name = vtr::strdup(atom_ctx.netlist().block_name(blk_id).c_str()); @@ -553,10 +551,10 @@ try_place_atom_block_rec(const t_pb_graph_node* pb_graph_node, } VTR_LOGV(verbosity > 4 && block_pack_status == e_block_pack_status::BLK_PASSED, - "\t\t\tPlaced atom '%s' (%s) at %s\n", - atom_ctx.netlist().block_name(blk_id).c_str(), - atom_ctx.netlist().block_model(blk_id)->name, - pb->hierarchical_type_name().c_str()); + "\t\t\tPlaced atom '%s' (%s) at %s\n", + atom_ctx.netlist().block_name(blk_id).c_str(), + atom_ctx.netlist().block_model(blk_id)->name, + pb->hierarchical_type_name().c_str()); } if (block_pack_status != e_block_pack_status::BLK_PASSED) { @@ -567,9 +565,9 @@ try_place_atom_block_rec(const t_pb_graph_node* pb_graph_node, } /* -* @brief Resets nets used at different pin classes for determining pin -* feasibility. -*/ + * @brief Resets nets used at different pin classes for determining pin + * feasibility. + */ static void reset_lookahead_pins_used(t_pb* cur_pb) { const t_pb_type* pb_type = cur_pb->pb_graph_node->pb_type; if (cur_pb->pb_stats == nullptr) { @@ -597,11 +595,10 @@ static void reset_lookahead_pins_used(t_pb* cur_pb) { } } - /* -* @brief Checks if the sinks of the given net are reachable from the driver -* pb gpin. -*/ + * @brief Checks if the sinks of the given net are reachable from the driver + * pb gpin. + */ static int net_sinks_reachable_in_cluster(const t_pb_graph_pin* driver_pb_gpin, const int depth, const AtomNetId net_id, const AtomPBBimap& atom_to_pb) { const AtomContext& atom_ctx = g_vpr_ctx.atom(); @@ -657,7 +654,6 @@ static t_pb_graph_pin* get_driver_pb_graph_pin(const t_pb* driver_pb, const Atom return nullptr; } - /** * @brief Given a pin and its assigned net, mark all pin classes that are affected. * Check if connecting this pin to it's driver pin or to all sink pins will @@ -665,12 +661,12 @@ static t_pb_graph_pin* get_driver_pb_graph_pin(const t_pb* driver_pb, const Atom * primitive till the root block (depth = 0). If leaving a pb_block is * required add this net to the pin class (to increment the number of used * pins from this class) that should be used to leave the pb_block. -*/ + */ static void compute_and_mark_lookahead_pins_used_for_pin(const t_pb_graph_pin* pb_graph_pin, - const t_pb* primitive_pb, - const AtomNetId net_id, - const vtr::vector_map& atom_cluster, - const AtomPBBimap& atom_to_pb) { + const t_pb* primitive_pb, + const AtomNetId net_id, + const vtr::vector_map& atom_cluster, + const AtomPBBimap& atom_to_pb) { const AtomContext& atom_ctx = g_vpr_ctx.atom(); // starting from the parent pb of the input primitive go up in the hierarchy till the root block @@ -732,9 +728,9 @@ static void compute_and_mark_lookahead_pins_used_for_pin(const t_pb_graph_pin* p } else { VTR_ASSERT(pb_graph_pin->port->type == OUT_PORT); /* - * Determine if this net (which is driven from within this cluster) leaves this cluster - * (and hence uses an output pin). - */ + * Determine if this net (which is driven from within this cluster) leaves this cluster + * (and hence uses an output pin). + */ bool net_exits_cluster = true; int num_net_sinks = static_cast(atom_ctx.netlist().net_sinks(net_id).size()); @@ -744,19 +740,19 @@ static void compute_and_mark_lookahead_pins_used_for_pin(const t_pb_graph_pin* p //since this pin could (potentially) drive all the net's sinks /* Important: This runtime penalty looks a lot scarier than it really is. - * For high fan-out nets, I at most look at the number of pins within the - * cluster which limits runtime. - * - * DO NOT REMOVE THIS INITIAL FILTER WITHOUT CAREFUL ANALYSIS ON RUNTIME!!! - * - * Key Observation: - * For LUT-based designs it is impossible for the average fanout to exceed - * the number of LUT inputs so it's usually around 4-5 (pigeon-hole argument, - * if the average fanout is greater than the number of LUT inputs, where do - * the extra connections go? Therefore, average fanout must be capped to a - * small constant where the constant is equal to the number of LUT inputs). - * The real danger to runtime is when the number of sinks of a net gets doubled - */ + * For high fan-out nets, I at most look at the number of pins within the + * cluster which limits runtime. + * + * DO NOT REMOVE THIS INITIAL FILTER WITHOUT CAREFUL ANALYSIS ON RUNTIME!!! + * + * Key Observation: + * For LUT-based designs it is impossible for the average fanout to exceed + * the number of LUT inputs so it's usually around 4-5 (pigeon-hole argument, + * if the average fanout is greater than the number of LUT inputs, where do + * the extra connections go? Therefore, average fanout must be capped to a + * small constant where the constant is equal to the number of LUT inputs). + * The real danger to runtime is when the number of sinks of a net gets doubled + */ //Check if all the net sinks are, in fact, inside this cluster bool all_sinks_in_cur_cluster = true; @@ -776,7 +772,7 @@ static void compute_and_mark_lookahead_pins_used_for_pin(const t_pb_graph_pin* p //If the count equals the number of net sinks then the net is fully absorbed and //the net does not exit the cluster /* TODO: I should cache the absorbed outputs, once net is absorbed, - * net is forever absorbed, no point in rechecking every time */ + * net is forever absorbed, no point in rechecking every time */ if (net_sinks_reachable_in_cluster(pb_graph_pin, depth, net_id, atom_to_pb)) { //All the sinks are reachable inside the cluster net_exits_cluster = false; @@ -793,8 +789,8 @@ static void compute_and_mark_lookahead_pins_used_for_pin(const t_pb_graph_pin* p } /* -* @brief Determine if pins of speculatively packed pb are legal -*/ + * @brief Determine if pins of speculatively packed pb are legal + */ static void compute_and_mark_lookahead_pins_used(const AtomBlockId blk_id, const vtr::vector_map& atom_cluster, const AtomPBBimap& atom_to_pb) { @@ -813,11 +809,11 @@ static void compute_and_mark_lookahead_pins_used(const AtomBlockId blk_id, } /* -* @brief Determine if speculatively packed cur_pb is pin feasible -* -* Runtime is actually not that bad for this. It's worst case O(k^2) where k is the -* number of pb_graph pins. Can use hash tables or make incremental if becomes an issue. -*/ + * @brief Determine if speculatively packed cur_pb is pin feasible + * + * Runtime is actually not that bad for this. It's worst case O(k^2) where k is the + * number of pb_graph pins. Can use hash tables or make incremental if becomes an issue. + */ static void try_update_lookahead_pins_used(t_pb* cur_pb, const vtr::vector_map& atom_cluster, const AtomPBBimap& atom_to_pb) { @@ -844,9 +840,9 @@ static void try_update_lookahead_pins_used(t_pb* cur_pb, } /* -* @brief Check if the number of available inputs/outputs for a pin class is -* sufficient for speculatively packed blocks. -*/ + * @brief Check if the number of available inputs/outputs for a pin class is + * sufficient for speculatively packed blocks. + */ static bool check_lookahead_pins_used(t_pb* cur_pb, t_ext_pin_util max_external_pin_util) { const t_pb_type* pb_type = cur_pb->pb_graph_node->pb_type; @@ -964,8 +960,8 @@ void ClusterLegalizer::reset_molecule_info(PackMoleculeId mol_id) { } /* -* @brief Revert trial atom block iblock and free up memory space accordingly. -*/ + * @brief Revert trial atom block iblock and free up memory space accordingly. + */ static void revert_place_atom_block(const AtomBlockId blk_id, t_lb_router_data* router_data, vtr::vector_map& atom_cluster, @@ -979,8 +975,8 @@ static void revert_place_atom_block(const AtomBlockId blk_id, if (pb != nullptr) { /* When freeing molecules, the current block might already have been freed by a prior revert - * When this happens, no need to do anything beyond basic book keeping at the atom block - */ + * When this happens, no need to do anything beyond basic book keeping at the atom block + */ t_pb* next = pb->parent_pb; free_pb(pb, atom_to_pb); @@ -988,8 +984,8 @@ static void revert_place_atom_block(const AtomBlockId blk_id, while (pb != nullptr) { /* If this is pb is created only for the purposes of holding new molecule, remove it - * Must check if cluster is already freed (which can be the case) - */ + * Must check if cluster is already freed (which can be the case) + */ next = pb->parent_pb; if (pb->child_pbs != nullptr && pb->pb_stats != nullptr @@ -997,8 +993,8 @@ static void revert_place_atom_block(const AtomBlockId blk_id, set_reset_pb_modes(router_data, pb, false); if (next != nullptr) { /* If the code gets here, then that means that placing the initial seed molecule - * failed, don't free the actual complex block itself as the seed needs to find - * another placement */ + * failed, don't free the actual complex block itself as the seed needs to find + * another placement */ free_pb(pb, atom_to_pb); } } @@ -1012,8 +1008,8 @@ static void revert_place_atom_block(const AtomBlockId blk_id, } /* -* @brief Speculation successful, commit input/output pins used. -*/ + * @brief Speculation successful, commit input/output pins used. + */ static void commit_lookahead_pins_used(t_pb* cur_pb) { const t_pb_type* pb_type = cur_pb->pb_graph_node->pb_type; @@ -1047,20 +1043,20 @@ static void commit_lookahead_pins_used(t_pb* cur_pb) { } /** -* @brief Cleans up a pb after unsuccessful molecule packing -* -* Recursively frees pbs from a t_pb tree. The given root pb itself is not -* deleted. -* -* If a pb object has its children allocated then before freeing them the -* function checks if there is no atom that corresponds to any of them. The -* check is performed only for leaf (primitive) pbs. The function recurses for -* non-primitive pbs. -* -* The cleaning itself includes deleting all child pbs, resetting mode of the -* pb and also freeing its name. This prepares the pb for another round of -* molecule packing tryout. -*/ + * @brief Cleans up a pb after unsuccessful molecule packing + * + * Recursively frees pbs from a t_pb tree. The given root pb itself is not + * deleted. + * + * If a pb object has its children allocated then before freeing them the + * function checks if there is no atom that corresponds to any of them. The + * check is performed only for leaf (primitive) pbs. The function recurses for + * non-primitive pbs. + * + * The cleaning itself includes deleting all child pbs, resetting mode of the + * pb and also freeing its name. This prepares the pb for another round of + * molecule packing tryout. + */ static bool cleanup_pb(t_pb* pb) { bool can_free = true; @@ -1146,9 +1142,9 @@ e_block_pack_status ClusterLegalizer::try_pack_molecule(PackMoleculeId molecule_ atom_ctx.netlist().block_name(root_atom).c_str(), atom_ctx.netlist().block_model(root_atom)->name); VTR_LOGV(molecule.pack_pattern, - " molecule_type %s molecule_size %zu", - molecule.pack_pattern->name, - molecule.atom_block_ids.size()); + " molecule_type %s molecule_size %zu", + molecule.pack_pattern->name, + molecule.atom_block_ids.size()); VTR_LOG("\n"); } @@ -1175,10 +1171,10 @@ e_block_pack_status ClusterLegalizer::try_pack_molecule(PackMoleculeId molecule_ // Try to intersect with atom PartitionRegion if atom exists bool cluster_pr_needs_update = false; bool block_pack_floorplan_status = check_cluster_floorplanning(atom_blk_id, - new_cluster_pr, - floorplanning_ctx.constraints, - log_verbosity_, - cluster_pr_needs_update); + new_cluster_pr, + floorplanning_ctx.constraints, + log_verbosity_, + cluster_pr_needs_update); if (!block_pack_floorplan_status) { return e_block_pack_status::BLK_FAILED_FLOORPLANNING; } @@ -1196,9 +1192,9 @@ e_block_pack_status ClusterLegalizer::try_pack_molecule(PackMoleculeId molecule_ continue; bool block_pack_noc_grp_status = check_cluster_noc_group(atom_blk_id, - new_cluster_noc_grp_id, - atom_noc_grp_id_, - log_verbosity_); + new_cluster_noc_grp_id, + atom_noc_grp_id_, + log_verbosity_); if (!block_pack_noc_grp_status) { return e_block_pack_status::BLK_FAILED_NOC_GROUP; } @@ -1208,9 +1204,9 @@ e_block_pack_status ClusterLegalizer::try_pack_molecule(PackMoleculeId molecule_ e_block_pack_status block_pack_status = e_block_pack_status::BLK_STATUS_UNDEFINED; while (block_pack_status != e_block_pack_status::BLK_PASSED) { if (!get_next_primitive_list(cluster.placement_stats, - molecule_id, - primitives_list.data(), - prepacker_)) { + molecule_id, + primitives_list.data(), + prepacker_)) { VTR_LOGV(log_verbosity_ > 3, "\t\tFAILED No candidate primitives available\n"); block_pack_status = e_block_pack_status::BLK_FAILED_FEASIBLE; break; /* no more candidate primitives available, this molecule will not pack, return fail */ @@ -1228,17 +1224,17 @@ e_block_pack_status ClusterLegalizer::try_pack_molecule(PackMoleculeId molecule_ // function. t_pb* parent = nullptr; block_pack_status = try_place_atom_block_rec(primitives_list[i_mol], - atom_blk_id, - cluster.pb, - &parent, - cluster_id, - atom_cluster_, - molecule_id, - cluster.router_data, - log_verbosity_, - prepacker_, - clustering_chain_info_, - mutable_atom_pb_lookup()); + atom_blk_id, + cluster.pb, + &parent, + cluster_id, + atom_cluster_, + molecule_id, + cluster.router_data, + log_verbosity_, + prepacker_, + clustering_chain_info_, + mutable_atom_pb_lookup()); } if (enable_pin_feasibility_filter_ && block_pack_status == e_block_pack_status::BLK_PASSED) { @@ -1255,34 +1251,34 @@ e_block_pack_status ClusterLegalizer::try_pack_molecule(PackMoleculeId molecule_ if (block_pack_status == e_block_pack_status::BLK_PASSED) { /* - * during the clustering step of `do_clustering`, `detailed_routing_stage` is incremented at each iteration until it a cluster - * is correctly generated or `detailed_routing_stage` assumes an invalid value (E_DETAILED_ROUTE_INVALID). - * depending on its value we have different behaviors: - * - E_DETAILED_ROUTE_AT_END_ONLY: Skip routing if heuristic is to route at the end of packing complex block. - * - E_DETAILED_ROUTE_FOR_EACH_ATOM: Try to route if heuristic is to route for every atom. If the clusterer arrives at this stage, - * it means that more checks have to be performed as the previous stage failed to generate a new cluster. - * - * mode_status is a data structure containing the status of the mode selection. Its members are: - * - bool is_mode_conflict - * - bool try_expand_all_modes - * - bool expand_all_modes - * - * is_mode_conflict affects this stage. Its value determines whether the cluster failed to pack after a mode conflict issue. - * It holds a flag that is used to verify whether try_intra_lb_route ended in a mode conflict issue. - * - * Until is_mode_conflict is set to FALSE by try_intra_lb_route, the loop re-iterates. If all the available modes are exhausted - * an error will be thrown during mode conflicts checks (this to prevent infinite loops). - * - * If the value is TRUE the cluster has to be re-routed, and its internal pb_graph_nodes will have more restrict choices - * for what regards the mode that has to be selected. - * - * is_mode_conflict is initially set to TRUE, and, unless a mode conflict is found, it is set to false in `try_intra_lb_route`. - * - * try_expand_all_modes is set if the node expansion failed to find a valid routing path. The clusterer tries to find another route - * by using all the modes during node expansion. - * - * expand_all_modes is used to enable the expansion of all the nodes using all the possible modes. - */ + * during the clustering step of `do_clustering`, `detailed_routing_stage` is incremented at each iteration until it a cluster + * is correctly generated or `detailed_routing_stage` assumes an invalid value (E_DETAILED_ROUTE_INVALID). + * depending on its value we have different behaviors: + * - E_DETAILED_ROUTE_AT_END_ONLY: Skip routing if heuristic is to route at the end of packing complex block. + * - E_DETAILED_ROUTE_FOR_EACH_ATOM: Try to route if heuristic is to route for every atom. If the clusterer arrives at this stage, + * it means that more checks have to be performed as the previous stage failed to generate a new cluster. + * + * mode_status is a data structure containing the status of the mode selection. Its members are: + * - bool is_mode_conflict + * - bool try_expand_all_modes + * - bool expand_all_modes + * + * is_mode_conflict affects this stage. Its value determines whether the cluster failed to pack after a mode conflict issue. + * It holds a flag that is used to verify whether try_intra_lb_route ended in a mode conflict issue. + * + * Until is_mode_conflict is set to FALSE by try_intra_lb_route, the loop re-iterates. If all the available modes are exhausted + * an error will be thrown during mode conflicts checks (this to prevent infinite loops). + * + * If the value is TRUE the cluster has to be re-routed, and its internal pb_graph_nodes will have more restrict choices + * for what regards the mode that has to be selected. + * + * is_mode_conflict is initially set to TRUE, and, unless a mode conflict is found, it is set to false in `try_intra_lb_route`. + * + * try_expand_all_modes is set if the node expansion failed to find a valid routing path. The clusterer tries to find another route + * by using all the modes during node expansion. + * + * expand_all_modes is used to enable the expansion of all the nodes using all the possible modes. + */ t_mode_selection_status mode_status; bool is_routed = false; bool do_detailed_routing_stage = (cluster_legalization_strategy_ == ClusterLegalizationStrategy::FULL); @@ -1299,12 +1295,12 @@ e_block_pack_status ClusterLegalizer::try_pack_molecule(PackMoleculeId molecule_ block_pack_status = e_block_pack_status::BLK_FAILED_ROUTE; } else { /* Pack successful, commit - * TODO: SW Engineering note - may want to update cluster stats here too instead of doing it outside - */ + * TODO: SW Engineering note - may want to update cluster stats here too instead of doing it outside + */ VTR_ASSERT(block_pack_status == e_block_pack_status::BLK_PASSED); if (molecule.is_chain()) { /* Chained molecules often take up lots of area and are important, - * if a chain is packed in, want to rename logic block to match chain name */ + * if a chain is packed in, want to rename logic block to match chain name */ AtomBlockId chain_root_blk_id = molecule.atom_block_ids[molecule.pack_pattern->root_block->block_id]; t_pb* cur_pb = atom_pb_lookup().atom_pb(chain_root_blk_id)->parent_pb; while (cur_pb != nullptr) { @@ -1382,8 +1378,8 @@ e_block_pack_status ClusterLegalizer::try_pack_molecule(PackMoleculeId molecule_ reset_molecule_info(molecule_id); /* Packing failed, but a part of the pb tree is still allocated and pbs have their modes set. - * Before trying to pack next molecule the unused pbs need to be freed and, the most important, - * their modes reset. This task is performed by the cleanup_pb() function below. */ + * Before trying to pack next molecule the unused pbs need to be freed and, the most important, + * their modes reset. This task is performed by the cleanup_pb() function below. */ cleanup_pb(cluster.pb); } else { VTR_LOGV(log_verbosity_ > 3, "\t\tPASSED pack molecule\n"); @@ -1627,9 +1623,9 @@ ClusterLegalizer::ClusterLegalizer(const AtomNetlist& atom_netlist, // Find all NoC router atoms. std::vector noc_atoms = find_noc_router_atoms(atom_netlist); update_noc_reachability_partitions(noc_atoms, - atom_netlist, - high_fanout_thresholds, - atom_noc_grp_id_); + atom_netlist, + high_fanout_thresholds, + atom_noc_grp_id_); // Copy the options passed by the user cluster_legalization_strategy_ = cluster_legalization_strategy; enable_pin_feasibility_filter_ = enable_pin_feasibility_filter; @@ -1656,8 +1652,8 @@ void ClusterLegalizer::verify() { } /* - * Check that each atom block connects to one physical primitive and that the primitive links up to the parent clb - */ + * Check that each atom block connects to one physical primitive and that the primitive links up to the parent clb + */ for (auto blk_id : atom_ctx.netlist().blocks()) { //Each atom should be part of a pb const t_pb* atom_pb = atom_pb_lookup().atom_pb(blk_id); @@ -1715,7 +1711,7 @@ void ClusterLegalizer::verify() { } bool ClusterLegalizer::is_molecule_compatible(PackMoleculeId molecule_id, - LegalizationClusterId cluster_id) const { + LegalizationClusterId cluster_id) const { VTR_ASSERT_SAFE(molecule_id.is_valid()); VTR_ASSERT_SAFE(cluster_id.is_valid() && (size_t)cluster_id < legalization_clusters_.size()); // Go through each atom in the molecule and check if there exists a free @@ -1737,7 +1733,7 @@ bool ClusterLegalizer::is_molecule_compatible(PackMoleculeId molecule_id, // if the atom was clustered. VTR_ASSERT(!is_atom_clustered(atom_blk_id)); if (!exists_free_primitive_for_atom_block(cluster.placement_stats, - atom_blk_id)) { + atom_blk_id)) { return false; } } diff --git a/vpr/src/pack/cluster_legalizer.h b/vpr/src/pack/cluster_legalizer.h index 2c81fd041e0..a13a2caeaa8 100644 --- a/vpr/src/pack/cluster_legalizer.h +++ b/vpr/src/pack/cluster_legalizer.h @@ -523,7 +523,6 @@ class ClusterLegalizer { inline const AtomPBBimap &atom_pb_lookup() const {return atom_pb_lookup_;} inline AtomPBBimap &mutable_atom_pb_lookup() {return atom_pb_lookup_;} - /// @brief Destructor of the class. Frees allocated data. ~ClusterLegalizer(); diff --git a/vpr/src/util/vpr_utils.cpp b/vpr/src/util/vpr_utils.cpp index 51bfbf4939e..29b4b4c5774 100644 --- a/vpr/src/util/vpr_utils.cpp +++ b/vpr/src/util/vpr_utils.cpp @@ -1000,7 +1000,6 @@ const t_pb_graph_pin* find_pb_graph_pin(const AtomNetlist& netlist, const AtomPB return get_pb_graph_node_pin_from_model_port_pin(model_port, ipin, pb_gnode); } - t_pb_graph_pin* get_pb_graph_node_pin_from_pb_graph_node(t_pb_graph_node* pb_graph_node, int ipin) { int i, count; From 9e0d48a475836121bb728a16e68945bf3cdab79a Mon Sep 17 00:00:00 2001 From: Amir Poolad Date: Tue, 18 Mar 2025 20:12:09 -0400 Subject: [PATCH 11/14] Add reset_bimap helper method to AtomPBBimap --- vpr/src/pack/atom_pb_bimap.cpp | 10 ++++++---- vpr/src/pack/atom_pb_bimap.h | 5 ++++- vpr/src/pack/cluster_legalizer.cpp | 1 + vpr/src/pack/pack.cpp | 2 -- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/vpr/src/pack/atom_pb_bimap.cpp b/vpr/src/pack/atom_pb_bimap.cpp index f08a21f39fc..df837cfea6b 100644 --- a/vpr/src/pack/atom_pb_bimap.cpp +++ b/vpr/src/pack/atom_pb_bimap.cpp @@ -56,8 +56,10 @@ void AtomPBBimap::set_atom_pb(const AtomBlockId blk_id, const t_pb* pb) { } } -void AtomPBBimap::reset_bimap(const AtomNetlist &netlist) { - for (auto blk : netlist.blocks()) { - set_atom_pb(blk, nullptr); - } +void AtomPBBimap::reset_bimap() { + atom_to_pb_.clear(); +} + +bool AtomPBBimap::is_empty() const { + return atom_to_pb_.empty(); } diff --git a/vpr/src/pack/atom_pb_bimap.h b/vpr/src/pack/atom_pb_bimap.h index 55df740bd87..af4a19f08ec 100644 --- a/vpr/src/pack/atom_pb_bimap.h +++ b/vpr/src/pack/atom_pb_bimap.h @@ -48,7 +48,10 @@ class AtomPBBimap { void set_atom_pb(const AtomBlockId blk_id, const t_pb* pb); /// @brief Sets the pb for all blocks in the netlist to nullptr. - void reset_bimap(const AtomNetlist &netlist); + void reset_bimap(); + + /// @brief Returns if the bimap is empty + bool is_empty() const; private: /// @brief Two way map between AtomBlockIds and t_pb diff --git a/vpr/src/pack/cluster_legalizer.cpp b/vpr/src/pack/cluster_legalizer.cpp index 9244f14eed8..226ab04e460 100644 --- a/vpr/src/pack/cluster_legalizer.cpp +++ b/vpr/src/pack/cluster_legalizer.cpp @@ -1640,6 +1640,7 @@ void ClusterLegalizer::reset() { continue; destroy_cluster(cluster_id); } + mutable_atom_pb_lookup().reset_bimap(); compress(); } diff --git a/vpr/src/pack/pack.cpp b/vpr/src/pack/pack.cpp index 2cfc0234579..b3e0979a0a3 100644 --- a/vpr/src/pack/pack.cpp +++ b/vpr/src/pack/pack.cpp @@ -288,8 +288,6 @@ bool try_pack(t_packer_opts* packer_opts, } //Reset clustering for re-packing - cluster_legalizer.mutable_atom_pb_lookup().reset_bimap(g_vpr_ctx.atom().netlist()); - for (auto net : g_vpr_ctx.atom().netlist().nets()) { g_vpr_ctx.mutable_atom().mutable_lookup().remove_atom_net(net); } From 4c6186785e76de4d84a9c2d7eac62075f51a4125 Mon Sep 17 00:00:00 2001 From: Amir Poolad Date: Tue, 18 Mar 2025 20:13:43 -0400 Subject: [PATCH 12/14] Remove copying empty bimap from global context to cluster legalizer --- vpr/src/pack/cluster_legalizer.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/vpr/src/pack/cluster_legalizer.cpp b/vpr/src/pack/cluster_legalizer.cpp index 226ab04e460..e61fa42d4a4 100644 --- a/vpr/src/pack/cluster_legalizer.cpp +++ b/vpr/src/pack/cluster_legalizer.cpp @@ -1630,7 +1630,8 @@ ClusterLegalizer::ClusterLegalizer(const AtomNetlist& atom_netlist, cluster_legalization_strategy_ = cluster_legalization_strategy; enable_pin_feasibility_filter_ = enable_pin_feasibility_filter; log_verbosity_ = log_verbosity; - atom_pb_lookup_ = AtomPBBimap(g_vpr_ctx.atom().lookup().atom_pb_bimap()); + VTR_ASSERT(g_vpr_ctx.atom().lookup().atom_pb_bimap().is_empty()); + atom_pb_lookup_ = AtomPBBimap(); } void ClusterLegalizer::reset() { From 624f25142153dde5f7a691fa045e82883c91a604 Mon Sep 17 00:00:00 2001 From: Amir Poolad Date: Tue, 18 Mar 2025 20:14:43 -0400 Subject: [PATCH 13/14] Refactor is_atom_blk_in_pb function to get two t_pb* arguments --- vpr/src/pack/cluster_legalizer.cpp | 12 ------------ vpr/src/pack/cluster_legalizer.h | 6 ------ vpr/src/pack/greedy_candidate_selector.cpp | 20 ++++++++++++++++++-- 3 files changed, 18 insertions(+), 20 deletions(-) diff --git a/vpr/src/pack/cluster_legalizer.cpp b/vpr/src/pack/cluster_legalizer.cpp index e61fa42d4a4..4a5168a4885 100644 --- a/vpr/src/pack/cluster_legalizer.cpp +++ b/vpr/src/pack/cluster_legalizer.cpp @@ -1771,18 +1771,6 @@ void ClusterLegalizer::finalize() { } } -bool ClusterLegalizer::is_atom_blk_in_cluster_block(const AtomBlockId blk_id, const AtomBlockId clustered_blk_id) const { - const t_pb* cur_pb = atom_pb_lookup().atom_pb(blk_id); - const t_pb* pb = atom_pb_lookup().atom_pb(clustered_blk_id); - while (cur_pb) { - if (cur_pb == pb) { - return true; - } - cur_pb = cur_pb->parent_pb; - } - return false; -} - ClusterLegalizer::~ClusterLegalizer() { // Destroy all clusters (no need to compress). for (LegalizationClusterId cluster_id : legalization_cluster_ids_) { diff --git a/vpr/src/pack/cluster_legalizer.h b/vpr/src/pack/cluster_legalizer.h index a13a2caeaa8..03a76a4ab62 100644 --- a/vpr/src/pack/cluster_legalizer.h +++ b/vpr/src/pack/cluster_legalizer.h @@ -514,12 +514,6 @@ class ClusterLegalizer { log_verbosity_ = verbosity; } - /* - * @brief Determine if atom block is in cluster block. - * - */ - bool is_atom_blk_in_cluster_block(const AtomBlockId blk_id, const AtomBlockId clustered_blk_id) const; - inline const AtomPBBimap &atom_pb_lookup() const {return atom_pb_lookup_;} inline AtomPBBimap &mutable_atom_pb_lookup() {return atom_pb_lookup_;} diff --git a/vpr/src/pack/greedy_candidate_selector.cpp b/vpr/src/pack/greedy_candidate_selector.cpp index 495664ab965..397f0bb3905 100644 --- a/vpr/src/pack/greedy_candidate_selector.cpp +++ b/vpr/src/pack/greedy_candidate_selector.cpp @@ -374,6 +374,20 @@ void GreedyCandidateSelector::mark_and_update_partial_gain( cluster_gain_stats.num_pins_of_net_in_pb[net_id]++; } +/** + * @brief Determine if pb is a child of cluster_pb. + */ +static bool is_pb_in_cluster_pb(const t_pb* pb, const t_pb* cluster_pb) { + const t_pb* cur_pb = pb; + while (cur_pb) { + if (cur_pb == cluster_pb) { + return true; + } + cur_pb = cur_pb->parent_pb; + } + return false; +} + void GreedyCandidateSelector::update_connection_gain_values( ClusterGainStats& cluster_gain_stats, AtomNetId net_id, @@ -394,8 +408,10 @@ void GreedyCandidateSelector::update_connection_gain_values( AtomBlockId blk_id = atom_netlist_.pin_block(pin_id); // TODO: Should investigate this. Using the atom pb bimap through is_atom_blk_in_cluster_block // in this class is very strange - if (cluster_legalizer.get_atom_cluster(blk_id) == legalization_cluster_id - && cluster_legalizer.is_atom_blk_in_cluster_block(blk_id, clustered_blk_id)) { + const t_pb *pin_block_pb = cluster_legalizer.atom_pb_lookup().atom_pb(blk_id); + const t_pb *cluster_pb = cluster_legalizer.atom_pb_lookup().atom_pb(clustered_blk_id); + + if (cluster_legalizer.get_atom_cluster(blk_id) == legalization_cluster_id && is_pb_in_cluster_pb(pin_block_pb, cluster_pb)) { num_internal_connections++; } else if (!cluster_legalizer.is_atom_clustered(blk_id)) { num_open_connections++; From dfb6462b22b49a5770cede0d5f3e668e385fd8e6 Mon Sep 17 00:00:00 2001 From: Amir Poolad Date: Tue, 18 Mar 2025 20:38:07 -0400 Subject: [PATCH 14/14] Fix minor styling issues --- vpr/src/base/atom_lookup.h | 29 +++++----- vpr/src/pack/atom_pb_bimap.h | 13 +++-- vpr/src/pack/cluster_legalizer.cpp | 62 +++++++++++----------- vpr/src/pack/cluster_legalizer.h | 5 +- vpr/src/pack/cluster_router.cpp | 16 +++--- vpr/src/pack/cluster_router.h | 4 +- vpr/src/pack/greedy_candidate_selector.cpp | 4 +- 7 files changed, 67 insertions(+), 66 deletions(-) diff --git a/vpr/src/base/atom_lookup.h b/vpr/src/base/atom_lookup.h index f3b59f9b4e3..8a218fae207 100644 --- a/vpr/src/base/atom_lookup.h +++ b/vpr/src/base/atom_lookup.h @@ -31,7 +31,6 @@ class AtomLookup { * PBs */ - /** * @brief Sets the atom to pb bimap access lock to value. * If set to true, access to the bimap is prohibited and will result in failing assertions. @@ -41,11 +40,10 @@ class AtomLookup { inline void set_atom_pb_bimap_lock(bool value) { VTR_ASSERT_SAFE_MSG(lock_atom_pb_bimap_ != value, "Double locking or unlocking the atom pb bimap lock"); lock_atom_pb_bimap_ = value; - } - - /// @brief Gets the current atom to pb bimap lock value. - inline bool atom_pb_bimap_islocked() const {return lock_atom_pb_bimap_;} + } + /// @brief Gets the current atom to pb bimap lock value. + inline bool atom_pb_bimap_islocked() const { return lock_atom_pb_bimap_; } // All accesses, mutable or immutable, to the atom to pb bimap // will result in failing assertions if the lock is set to true. @@ -55,18 +53,24 @@ class AtomLookup { /// @brief Returns a mutable reference to the atom to pb bimap, provided that access to it is unlocked. It will result in a crash otherwise. /// @return Mutable reference to the atom pb bimap. - inline AtomPBBimap &mutable_atom_pb_bimap() {VTR_ASSERT(!lock_atom_pb_bimap_); return atom_to_pb_bimap_;} + inline AtomPBBimap& mutable_atom_pb_bimap() { + VTR_ASSERT(!lock_atom_pb_bimap_); + return atom_to_pb_bimap_; + } /// @brief Returns an immutable reference to the atom to pb bimap, provided that access to it is unlocked. It will result in a crash otherwise. /// @return Immutable reference to the atom pb bimap. - inline const AtomPBBimap &atom_pb_bimap() const {VTR_ASSERT(!lock_atom_pb_bimap_); return atom_to_pb_bimap_;} + inline const AtomPBBimap& atom_pb_bimap() const { + VTR_ASSERT(!lock_atom_pb_bimap_); + return atom_to_pb_bimap_; + } /** * @brief Set atom to pb bimap * * @param atom_to_pb Reference to AtomPBBimab to be copied from */ - void set_atom_to_pb_bimap(const AtomPBBimap& atom_to_pb){atom_to_pb_bimap_ = atom_to_pb;} + void set_atom_to_pb_bimap(const AtomPBBimap& atom_to_pb) { atom_to_pb_bimap_ = atom_to_pb; } /* * PB Pins @@ -131,11 +135,10 @@ class AtomLookup { private: //Types private: - - /** - * @brief Allows or disallows access to the AtomPBBimap data. - * Useful to make sure global context is not accessed in places you don't want it to. - */ + /** + * @brief Allows or disallows access to the AtomPBBimap data. + * Useful to make sure global context is not accessed in places you don't want it to. + */ bool lock_atom_pb_bimap_ = false; AtomPBBimap atom_to_pb_bimap_; diff --git a/vpr/src/pack/atom_pb_bimap.h b/vpr/src/pack/atom_pb_bimap.h index af4a19f08ec..d8623543910 100644 --- a/vpr/src/pack/atom_pb_bimap.h +++ b/vpr/src/pack/atom_pb_bimap.h @@ -23,10 +23,9 @@ class t_pb_graph_node; * Used in the global AtomLookup context and in ClusterLegalizer */ class AtomPBBimap { - public: + public: AtomPBBimap() = default; - AtomPBBimap(const vtr::bimap &atom_to_pb); - + AtomPBBimap(const vtr::bimap& atom_to_pb); /** * @brief Returns the leaf pb associated with the atom blk_id @@ -53,7 +52,7 @@ class AtomPBBimap { /// @brief Returns if the bimap is empty bool is_empty() const; - private: - /// @brief Two way map between AtomBlockIds and t_pb - vtr::bimap atom_to_pb_; - }; + private: + /// @brief Two way map between AtomBlockIds and t_pb + vtr::bimap atom_to_pb_; +}; diff --git a/vpr/src/pack/cluster_legalizer.cpp b/vpr/src/pack/cluster_legalizer.cpp index 4a5168a4885..d1a8a72076a 100644 --- a/vpr/src/pack/cluster_legalizer.cpp +++ b/vpr/src/pack/cluster_legalizer.cpp @@ -45,7 +45,7 @@ */ static void alloc_and_load_pb_stats(t_pb* pb) { /* Call this routine when starting to fill up a new cluster. It resets * - * the gain vector, etc. */ + * the gain vector, etc. */ pb->pb_stats = new t_pb_stats; @@ -61,7 +61,7 @@ static void alloc_and_load_pb_stats(t_pb* pb) { * @brief Check the atom blocks of a cluster pb. Used in the verify method. */ /* TODO: May want to check that all atom blocks are actually reached */ -static void check_cluster_atom_blocks(t_pb* pb, std::unordered_set& blocks_checked, const AtomPBBimap &atom_pb_lookup) { +static void check_cluster_atom_blocks(t_pb* pb, std::unordered_set& blocks_checked, const AtomPBBimap& atom_pb_lookup) { const AtomContext& atom_ctx = g_vpr_ctx.atom(); const t_pb_type* pb_type = pb->pb_graph_node->pb_type; @@ -191,7 +191,7 @@ static bool check_cluster_floorplanning(AtomBlockId atom_blk_id, VTR_LOGV(log_verbosity > 3, "\t\t\t Intersect: Atom block %d passed cluster, cluster PR was updated with intersection result \n", atom_blk_id); -return true; + return true; } /** @@ -390,8 +390,8 @@ static bool primitive_memory_sibling_feasible(const AtomBlockId blk_id, const t_ } /* -* @brief Check if the given atom is feasible in the given pb. -*/ + * @brief Check if the given atom is feasible in the given pb. + */ static bool primitive_feasible(const AtomBlockId blk_id, t_pb* cur_pb, const AtomPBBimap& atom_to_pb) { const t_pb_type* cur_pb_type = cur_pb->pb_graph_node->pb_type; @@ -408,7 +408,7 @@ static bool primitive_feasible(const AtomBlockId blk_id, t_pb* cur_pb, const Ato * - all siblings must share all nets, including open nets, with the exception of data nets */ /* find sibling if one exists */ - const t_pb *sibling_memory_pb = find_memory_sibling(cur_pb); + const t_pb* sibling_memory_pb = find_memory_sibling(cur_pb); AtomBlockId sibling_memory_blk_id = atom_to_pb.pb_atom(sibling_memory_pb); if (sibling_memory_blk_id) { @@ -429,17 +429,17 @@ static bool primitive_feasible(const AtomBlockId blk_id, t_pb* cur_pb, const Ato */ static enum e_block_pack_status try_place_atom_block_rec(const t_pb_graph_node* pb_graph_node, - const AtomBlockId blk_id, - t_pb* cb, - t_pb** parent, - const LegalizationClusterId cluster_id, - vtr::vector_map& atom_cluster, - const PackMoleculeId molecule_id, - t_lb_router_data* router_data, - int verbosity, - const Prepacker& prepacker, - const vtr::vector_map& clustering_chain_info, - AtomPBBimap& atom_to_pb) { + const AtomBlockId blk_id, + t_pb* cb, + t_pb** parent, + const LegalizationClusterId cluster_id, + vtr::vector_map& atom_cluster, + const PackMoleculeId molecule_id, + t_lb_router_data* router_data, + int verbosity, + const Prepacker& prepacker, + const vtr::vector_map& clustering_chain_info, + AtomPBBimap& atom_to_pb) { const AtomContext& atom_ctx = g_vpr_ctx.atom(); VTR_ASSERT_SAFE(cb != nullptr); @@ -450,11 +450,11 @@ try_place_atom_block_rec(const t_pb_graph_node* pb_graph_node, if (pb_graph_node->parent_pb_graph_node != cb->pb_graph_node) { t_pb* my_parent = nullptr; block_pack_status = try_place_atom_block_rec(pb_graph_node->parent_pb_graph_node, blk_id, cb, - &my_parent, cluster_id, - atom_cluster, - molecule_id, router_data, - verbosity, - prepacker, clustering_chain_info, atom_to_pb); + &my_parent, cluster_id, + atom_cluster, + molecule_id, router_data, + verbosity, + prepacker, clustering_chain_info, atom_to_pb); parent_pb = my_parent; } else { parent_pb = cb; @@ -543,10 +543,10 @@ try_place_atom_block_rec(const t_pb_graph_node* pb_graph_node, VTR_ASSERT(molecule.chain_id.is_valid()); const t_chain_info& prepack_chain_info = prepacker.get_molecule_chain_info(molecule.chain_id); block_pack_status = check_chain_root_placement_feasibility(pb_graph_node, - prepack_chain_info, - clustering_chain_info[molecule.chain_id], - molecule.pack_pattern, - blk_id); + prepack_chain_info, + clustering_chain_info[molecule.chain_id], + molecule.pack_pattern, + blk_id); } } @@ -629,7 +629,7 @@ static int net_sinks_reachable_in_cluster(const t_pb_graph_pin* driver_pb_gpin, /** * @brief Returns the pb_graph_pin of the atom pin defined by the driver_pin_id in the driver_pb -*/ + */ static t_pb_graph_pin* get_driver_pb_graph_pin(const t_pb* driver_pb, const AtomPinId driver_pin_id) { const AtomNetlist& atom_netlist = g_vpr_ctx.atom().netlist(); @@ -792,8 +792,8 @@ static void compute_and_mark_lookahead_pins_used_for_pin(const t_pb_graph_pin* p * @brief Determine if pins of speculatively packed pb are legal */ static void compute_and_mark_lookahead_pins_used(const AtomBlockId blk_id, - const vtr::vector_map& atom_cluster, - const AtomPBBimap& atom_to_pb) { + const vtr::vector_map& atom_cluster, + const AtomPBBimap& atom_to_pb) { const AtomNetlist& atom_netlist = g_vpr_ctx.atom().netlist(); const t_pb* cur_pb = atom_to_pb.atom_pb(blk_id); @@ -1417,7 +1417,7 @@ ClusterLegalizer::start_new_cluster(PackMoleculeId molecule_id, // Allocate and load the LB router data t_lb_router_data* router_data = alloc_and_load_router_data(&lb_type_rr_graphs_[cluster_type->index], - cluster_type); + cluster_type); // Allocate and load the cluster's placement stats t_intra_cluster_placement_stats* cluster_placement_stats = alloc_and_load_cluster_placement_stats(cluster_type, cluster_mode); @@ -1468,7 +1468,7 @@ ClusterLegalizer::start_new_cluster(PackMoleculeId molecule_id, } e_block_pack_status ClusterLegalizer::add_mol_to_cluster(PackMoleculeId molecule_id, - LegalizationClusterId cluster_id) { + LegalizationClusterId cluster_id) { // Safety asserts to make sure the inputs are valid. VTR_ASSERT_SAFE(cluster_id.is_valid() && (size_t)cluster_id < legalization_clusters_.size()); VTR_ASSERT(legalization_cluster_ids_[cluster_id].is_valid() && "Cannot add to a destroyed cluster"); diff --git a/vpr/src/pack/cluster_legalizer.h b/vpr/src/pack/cluster_legalizer.h index 03a76a4ab62..c51bde7ee02 100644 --- a/vpr/src/pack/cluster_legalizer.h +++ b/vpr/src/pack/cluster_legalizer.h @@ -514,8 +514,8 @@ class ClusterLegalizer { log_verbosity_ = verbosity; } - inline const AtomPBBimap &atom_pb_lookup() const {return atom_pb_lookup_;} - inline AtomPBBimap &mutable_atom_pb_lookup() {return atom_pb_lookup_;} + inline const AtomPBBimap& atom_pb_lookup() const { return atom_pb_lookup_; } + inline AtomPBBimap& mutable_atom_pb_lookup() { return atom_pb_lookup_; } /// @brief Destructor of the class. Frees allocated data. ~ClusterLegalizer(); @@ -588,5 +588,4 @@ class ClusterLegalizer { /// @brief A two way map between AtomBlockIds and pb types. This is a copy /// of the AtomPBBimap in the global context's AtomLookup AtomPBBimap atom_pb_lookup_; - }; diff --git a/vpr/src/pack/cluster_router.cpp b/vpr/src/pack/cluster_router.cpp index d8c5c7b3ea9..92d4b183dc5 100644 --- a/vpr/src/pack/cluster_router.cpp +++ b/vpr/src/pack/cluster_router.cpp @@ -75,10 +75,10 @@ class reservable_pq : public std::priority_queue { ******************************************************************************************/ static void free_lb_net_rt(t_lb_trace* lb_trace); static void free_lb_trace(t_lb_trace* lb_trace); -static void add_pin_to_rt_terminals(t_lb_router_data* router_data, const AtomPinId pin_id, const AtomPBBimap &atom_to_pb); -static void remove_pin_from_rt_terminals(t_lb_router_data* router_data, const AtomPinId pin_id, const AtomPBBimap &atom_to_pb); +static void add_pin_to_rt_terminals(t_lb_router_data* router_data, const AtomPinId pin_id, const AtomPBBimap& atom_to_pb); +static void remove_pin_from_rt_terminals(t_lb_router_data* router_data, const AtomPinId pin_id, const AtomPBBimap& atom_to_pb); -static void fix_duplicate_equivalent_pins(t_lb_router_data* router_data, const AtomPBBimap &atom_to_pb); +static void fix_duplicate_equivalent_pins(t_lb_router_data* router_data, const AtomPBBimap& atom_to_pb); static void commit_remove_rt(t_lb_trace* rt, t_lb_router_data* router_data, e_commit_remove op, std::unordered_map* mode_map, t_mode_selection_status* mode_status); static bool is_skip_route_net(t_lb_trace* rt, t_lb_router_data* router_data); @@ -249,7 +249,7 @@ static bool check_edge_for_route_conflicts(std::unordered_map& atoms_added = *router_data->atoms_added; @@ -626,7 +626,7 @@ static void free_lb_trace(t_lb_trace* lb_trace) { /* Given a pin of a net, assign route tree terminals for it * Assumes that pin is not already assigned */ -static void add_pin_to_rt_terminals(t_lb_router_data* router_data, const AtomPinId pin_id, const AtomPBBimap &atom_to_pb) { +static void add_pin_to_rt_terminals(t_lb_router_data* router_data, const AtomPinId pin_id, const AtomPBBimap& atom_to_pb) { std::vector& lb_nets = *router_data->intra_lb_nets; std::vector& lb_type_graph = *router_data->lb_type_graph; t_logical_block_type_ptr lb_type = router_data->lb_type; @@ -793,7 +793,7 @@ static void add_pin_to_rt_terminals(t_lb_router_data* router_data, const AtomPin /* Given a pin of a net, remove route tree terminals from it */ -static void remove_pin_from_rt_terminals(t_lb_router_data* router_data, const AtomPinId pin_id, const AtomPBBimap &atom_to_pb) { +static void remove_pin_from_rt_terminals(t_lb_router_data* router_data, const AtomPinId pin_id, const AtomPBBimap& atom_to_pb) { std::vector& lb_nets = *router_data->intra_lb_nets; std::vector& lb_type_graph = *router_data->lb_type_graph; t_logical_block_type_ptr lb_type = router_data->lb_type; @@ -918,7 +918,7 @@ static void remove_pin_from_rt_terminals(t_lb_router_data* router_data, const At //To work around this, we fix all but one of these duplicate connections to route to specific pins, //(instead of the common sink). This ensures a legal routing is produced and that the duplicate pins //are not 'missing' in the clustered netlist. -static void fix_duplicate_equivalent_pins(t_lb_router_data* router_data, const AtomPBBimap &atom_to_pb) { +static void fix_duplicate_equivalent_pins(t_lb_router_data* router_data, const AtomPBBimap& atom_to_pb) { auto& atom_ctx = g_vpr_ctx.atom(); std::vector& lb_type_graph = *router_data->lb_type_graph; diff --git a/vpr/src/pack/cluster_router.h b/vpr/src/pack/cluster_router.h index 95dd99fa3ae..0b40f84c627 100644 --- a/vpr/src/pack/cluster_router.h +++ b/vpr/src/pack/cluster_router.h @@ -16,8 +16,8 @@ void free_router_data(t_lb_router_data* router_data); void free_intra_lb_nets(std::vector* intra_lb_nets); /* Routing Functions */ -void add_atom_as_target(t_lb_router_data* router_data, const AtomBlockId blk_id, const AtomPBBimap &atom_to_pb); -void remove_atom_from_target(t_lb_router_data* router_data, const AtomBlockId blk_id, const AtomPBBimap &atom_to_pb); +void add_atom_as_target(t_lb_router_data* router_data, const AtomBlockId blk_id, const AtomPBBimap& atom_to_pb); +void remove_atom_from_target(t_lb_router_data* router_data, const AtomBlockId blk_id, const AtomPBBimap& atom_to_pb); void set_reset_pb_modes(t_lb_router_data* router_data, const t_pb* pb, const bool set); bool try_intra_lb_route(t_lb_router_data* router_data, int verbosity, t_mode_selection_status* mode_status); void reset_intra_lb_route(t_lb_router_data* router_data); diff --git a/vpr/src/pack/greedy_candidate_selector.cpp b/vpr/src/pack/greedy_candidate_selector.cpp index 9cc77f71780..f2853b0c64e 100644 --- a/vpr/src/pack/greedy_candidate_selector.cpp +++ b/vpr/src/pack/greedy_candidate_selector.cpp @@ -406,8 +406,8 @@ void GreedyCandidateSelector::update_connection_gain_values( AtomBlockId blk_id = atom_netlist_.pin_block(pin_id); // TODO: Should investigate this. Using the atom pb bimap through is_atom_blk_in_cluster_block // in this class is very strange - const t_pb *pin_block_pb = cluster_legalizer.atom_pb_lookup().atom_pb(blk_id); - const t_pb *cluster_pb = cluster_legalizer.atom_pb_lookup().atom_pb(clustered_blk_id); + const t_pb* pin_block_pb = cluster_legalizer.atom_pb_lookup().atom_pb(blk_id); + const t_pb* cluster_pb = cluster_legalizer.atom_pb_lookup().atom_pb(clustered_blk_id); if (cluster_legalizer.get_atom_cluster(blk_id) == legalization_cluster_id && is_pb_in_cluster_pb(pin_block_pb, cluster_pb)) { num_internal_connections++;