diff --git a/.gitignore b/.gitignore index 890ad1ac450..599c0f1854a 100644 --- a/.gitignore +++ b/.gitignore @@ -160,3 +160,4 @@ cmake-build-release # Clangd # compile_commands.json +.clangd \ No newline at end of file diff --git a/.gitmodules b/.gitmodules index b2b3b51feef..2c1f73cb34d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -16,3 +16,6 @@ [submodule "libs/EXTERNAL/yosys-slang"] path = libs/EXTERNAL/yosys-slang url = https://github.com/povik/yosys-slang.git +[submodule "libs/EXTERNAL/yaml-cpp"] + path = libs/EXTERNAL/yaml-cpp + url = https://github.com/jbeder/yaml-cpp.git diff --git a/doc/src/arch/reference.rst b/doc/src/arch/reference.rst index 5322fe473b9..9ef24a8df97 100644 --- a/doc/src/arch/reference.rst +++ b/doc/src/arch/reference.rst @@ -589,7 +589,6 @@ Grid Layout Example .. note:: Exactly one of the ``x`` or ``y`` attributes must be specified. - .. note:: Interposers are experimental and are currently not supported by VPR and using the related tags will not actually result in any changes to the flow. Defines an interposer cut for modelling 2.5D interposer-based architectures. An interposer cut will cut all connections at location 'loc' along the axis 'dim' Leaving the two sides completely unconnected. To reconnect the two sides, this tag can have multiple tags as children to specify the connection between the two sides. @@ -2751,7 +2750,7 @@ The number of any additional wires or muxes created by scatter-gather specificat Overview of how scatter-gather patterns work. First, connections from a switchblock location are selected according to the specification. These selected connection are then muxed and passed through the scatter-gather node, which is typically a wire segment. The scatter-gather node then fans out or scatters in another switchblock location. -.. note:: Scatter-Gather patterns are only supported for 3D architectures where the scatter-gather links are unidirectional. They are not currently supported in 2D architectures or with bidirectional sg_links. +.. note:: Scatter-Gather patterns are only supported for uni-directional 3D and uni-directional 2D architectures. Bidirectional sg_links are not currently supported. When instantiated, a scatter-gather pattern gathers connections from a switchblock and passes the connection through a multiplexer and the scatter-gather node which is typically a wire segment, then scatters or fans out somewhere else in the device. These patterns can be used to define 3D switchblocks. An example is shown below: diff --git a/libs/EXTERNAL/CMakeLists.txt b/libs/EXTERNAL/CMakeLists.txt index 816ea7f57c9..2061a6c328f 100644 --- a/libs/EXTERNAL/CMakeLists.txt +++ b/libs/EXTERNAL/CMakeLists.txt @@ -12,6 +12,7 @@ add_subdirectory(libsdcparse) add_subdirectory(libblifparse) add_subdirectory(libtatum) add_subdirectory(libcatch2) +add_subdirectory(yaml-cpp) #add_subdirectory(parmys) #Proc numbers diff --git a/libs/EXTERNAL/yaml-cpp b/libs/EXTERNAL/yaml-cpp new file mode 160000 index 00000000000..a83cd31548b --- /dev/null +++ b/libs/EXTERNAL/yaml-cpp @@ -0,0 +1 @@ +Subproject commit a83cd31548b19d50f3f983b069dceb4f4d50756d diff --git a/libs/libarchfpga/src/device_grid.cpp b/libs/libarchfpga/src/device_grid.cpp index f0644873a43..6873ffff6fb 100644 --- a/libs/libarchfpga/src/device_grid.cpp +++ b/libs/libarchfpga/src/device_grid.cpp @@ -89,3 +89,19 @@ void DeviceGrid::count_instances() { } } } + +bool DeviceGrid::has_interposer_cuts() const { + for (const std::vector& layer_h_cuts : horizontal_interposer_cuts_) { + if (!layer_h_cuts.empty()) { + return true; + } + } + + for (const std::vector& layer_v_cuts : vertical_interposer_cuts_) { + if (!layer_v_cuts.empty()) { + return true; + } + } + + return false; +} diff --git a/libs/libarchfpga/src/device_grid.h b/libs/libarchfpga/src/device_grid.h index 5a5f1c01054..74f3f18b88c 100644 --- a/libs/libarchfpga/src/device_grid.h +++ b/libs/libarchfpga/src/device_grid.h @@ -211,6 +211,11 @@ class DeviceGrid { return vertical_interposer_cuts_; } + /// Returns if the grid has any interposer cuts. You should use this function instead of + /// checking if get_horizontal/vertical_interposer_cuts is empty, since the return value + /// of those functions might look something like this: {{}} which is technically not empty. + bool has_interposer_cuts() const; + private: /// @brief Counts the number of each tile type on each layer and store it in instance_counts_. /// It is called in the constructor. diff --git a/libs/librrgraph/src/base/rr_graph_builder.cpp b/libs/librrgraph/src/base/rr_graph_builder.cpp index a09c4a727a4..a9fc82322cd 100644 --- a/libs/librrgraph/src/base/rr_graph_builder.cpp +++ b/libs/librrgraph/src/base/rr_graph_builder.cpp @@ -31,10 +31,6 @@ vtr::vector>& RRGraphBuilder::node_in_edge_stora return node_in_edges_; } -vtr::vector>& RRGraphBuilder::node_ptc_storage() { - return node_tilable_track_nums_; -} - void RRGraphBuilder::add_node_to_all_locs(RRNodeId node) { e_rr_type node_type = node_storage_.node_type(node); short node_ptc_num = node_storage_.node_ptc_num(node); @@ -76,7 +72,6 @@ RRNodeId RRGraphBuilder::create_node(int layer, int x, int y, e_rr_type type, in node_side = side; } node_storage_.emplace_back(); - node_tilable_track_nums_.emplace_back(); RRNodeId new_node = RRNodeId(node_storage_.size() - 1); node_storage_.set_node_layer(new_node, layer, layer); node_storage_.set_node_type(new_node, type); @@ -102,7 +97,6 @@ void RRGraphBuilder::clear() { node_lookup_.clear(); node_storage_.clear(); node_in_edges_.clear(); - node_tilable_track_nums_.clear(); rr_node_metadata_.clear(); rr_edge_metadata_.clear(); rr_segments_.clear(); @@ -232,58 +226,20 @@ std::vector RRGraphBuilder::node_in_edges(RRNodeId node) const { } void RRGraphBuilder::set_node_ptc_nums(RRNodeId node, const std::string& ptc_str) { - VTR_ASSERT(size_t(node) < node_storage_.size()); - std::vector ptc_tokens = vtr::StringToken(ptc_str).split(","); - VTR_ASSERT(ptc_tokens.size() >= 1); - set_node_ptc_num(node, std::stoi(ptc_tokens[0])); - if (ptc_tokens.size() > 1) { - VTR_ASSERT(size_t(node) < node_tilable_track_nums_.size()); - node_tilable_track_nums_[node].resize(ptc_tokens.size()); - for (size_t iptc = 0; iptc < ptc_tokens.size(); iptc++) { - node_tilable_track_nums_[node][iptc] = std::stoi(ptc_tokens[iptc]); - } - } + node_storage_.set_node_ptc_nums(node, ptc_str); } std::string RRGraphBuilder::node_ptc_nums_to_string(RRNodeId node) const { - if (node_tilable_track_nums_.empty()) { - return std::to_string(size_t(node_storage_.node_ptc_num(node))); - } - VTR_ASSERT(size_t(node) < node_tilable_track_nums_.size()); - if (node_tilable_track_nums_[node].empty()) { - return std::to_string(size_t(node_storage_.node_ptc_num(node))); - } - std::string ret; - for (size_t iptc = 0; iptc < node_tilable_track_nums_[node].size(); iptc++) { - ret += std::to_string(size_t(node_tilable_track_nums_[node][iptc])) + ","; - } - // Remove the last comma - ret.pop_back(); - return ret; + return node_storage_.node_ptc_nums_to_string(node); } bool RRGraphBuilder::node_contain_multiple_ptc(RRNodeId node) const { - if (node_tilable_track_nums_.empty()) { - return false; - } - return node_tilable_track_nums_[node].size() > 1; + return node_storage_.node_contain_multiple_ptc(node); } void RRGraphBuilder::add_node_track_num(RRNodeId node, vtr::Point node_offset, short track_id) { - VTR_ASSERT(size_t(node) < node_storage_.size()); - VTR_ASSERT(size_t(node) < node_tilable_track_nums_.size()); - VTR_ASSERT_MSG(node_storage_.node_type(node) == e_rr_type::CHANX || node_storage_.node_type(node) == e_rr_type::CHANY, "Track number valid only for CHANX/CHANY RR nodes"); - - size_t node_length = std::abs(node_storage_.node_xhigh(node) - node_storage_.node_xlow(node)) - + std::abs(node_storage_.node_yhigh(node) - node_storage_.node_ylow(node)); - if (node_length + 1 != node_tilable_track_nums_[node].size()) { - node_tilable_track_nums_[node].resize(node_length + 1); - } - - size_t offset = node_offset.x() - node_storage_.node_xlow(node) + node_offset.y() - node_storage_.node_ylow(node); - VTR_ASSERT(offset < node_tilable_track_nums_[node].size()); - - node_tilable_track_nums_[node][offset] = track_id; + size_t node_offset_value = node_offset.x() - node_storage_.node_xlow(node) + node_offset.y() - node_storage_.node_ylow(node); + node_storage_.add_node_tilable_track_num(node, node_offset_value, track_id); } void RRGraphBuilder::add_track_node_to_lookup(RRNodeId node) { @@ -309,8 +265,9 @@ void RRGraphBuilder::add_track_node_to_lookup(RRNodeId node) { // Routing channel nodes may have different ptc num // Find the track ids using the x/y offset if (e_rr_type::CHANX == node_type || e_rr_type::CHANY == node_type) { - ptc = (node_type == e_rr_type::CHANX) ? node_tilable_track_nums_[node][x - node_storage_.node_xlow(node)] : - node_tilable_track_nums_[node][y - node_storage_.node_ylow(node)]; + const std::vector& track_nums = node_storage_.node_tilable_track_nums(node); + ptc = (node_type == e_rr_type::CHANX) ? track_nums[x - node_storage_.node_xlow(node)] : + track_nums[y - node_storage_.node_ylow(node)]; node_lookup_.add_node(node, node_storage_.node_layer_low(node), x, y, node_type, ptc); } } diff --git a/libs/librrgraph/src/base/rr_graph_builder.h b/libs/librrgraph/src/base/rr_graph_builder.h index daf42742275..ad82c82799f 100644 --- a/libs/librrgraph/src/base/rr_graph_builder.h +++ b/libs/librrgraph/src/base/rr_graph_builder.h @@ -50,9 +50,6 @@ class RRGraphBuilder { /** @brief Return a writable object fo the incoming edge storage */ vtr::vector>& node_in_edge_storage(); - - /** @brief Return a writable object of the node ptc storage (for tileable routing resource graph) */ - vtr::vector>& node_ptc_storage(); /** @brief Return the size for rr_node_metadata */ inline size_t rr_node_metadata_size() const { @@ -351,6 +348,18 @@ class RRGraphBuilder { inline void alloc_and_load_edges(const t_rr_edge_info_set* rr_edges_to_create) { node_storage_.alloc_and_load_edges(rr_edges_to_create); } + + /** @brief Removes a given list of RREdgeIds from the RR Graph. + * This method does not preserve the order of edges. If you're + * calling it after partition_edges has been called, you will + * need to call partition_edges again. + * This operation is O(#RR Graph edges) and should not be called frequently. + * + * @param rr_edges_to_remove list of RREdgeIds to be removed + */ + inline void remove_edges(std::vector& rr_edges_to_remove) { + node_storage_.remove_edges(rr_edges_to_remove); + } /** @brief Overrides the associated switch for a given edge by * updating the edge to use the passed in switch. */ @@ -395,13 +404,6 @@ class RRGraphBuilder { return node_storage_.count_rr_switches(arch_switch_inf, arch_switch_fanins); } - /** @brief Unlock storage; required to modify an routing resource graph after edge is read */ - inline void unlock_storage() { - node_storage_.edges_read_ = false; - node_storage_.partitioned_ = false; - node_storage_.clear_node_first_edge(); - } - /** @brief Reserve the lists of nodes, edges, switches etc. to be memory efficient. * This function is mainly used to reserve memory space inside RRGraph, * when adding a large number of nodes/edge/switches/segments, @@ -420,11 +422,6 @@ class RRGraphBuilder { inline void resize_nodes(size_t size) { node_storage_.resize(size); } - /** @brief This function resize node ptc nums. Only used by RR graph I/O reader and writers. */ - inline void resize_node_ptc_nums(size_t size) { - node_tilable_track_nums_.resize(size); - } - /** @brief This function resize rr_switch to accomidate size RR Switch. */ inline void resize_switches(size_t size) { @@ -527,29 +524,6 @@ class RRGraphBuilder { */ vtr::vector> node_in_edges_; - /** - * @brief Extra ptc number for each routing resource node. - * @note This is required by tileable routing resource graphs. The first index is the node id, and - * the second index is is the relative distance from the starting point of the node. - * @details - * In a tileable routing architecture, routing tracks, e.g., CHANX and CHANY, follow a staggered organization. - * Hence, a routing track may appear in different routing channels, representing different ptc/track id. - * Here is an illustrative example of a X-direction routing track (CHANX) in INC direction, which is organized in staggered way. - * - * Coord(x,y) (1,0) (2,0) (3,0) (4,0) Another track (node) - * ptc=0 ------> ------> - * \ / - * ptc=1 ------> / - * \ / - * ptc=2 ------> / - * \ / - * ptc=3 -------> - * ^ ^ - * | | - * starting point ending point - */ - vtr::vector> node_tilable_track_nums_; - /** @warning The Metadata should stay as an independent data structure from the rest of the internal data, * e.g., node_lookup! */ /* Metadata is an extra data on rr-nodes and edges, respectively, that is not used by vpr diff --git a/libs/librrgraph/src/base/rr_graph_storage.cpp b/libs/librrgraph/src/base/rr_graph_storage.cpp index 1e15f127b8d..6ad7b54bc49 100644 --- a/libs/librrgraph/src/base/rr_graph_storage.cpp +++ b/libs/librrgraph/src/base/rr_graph_storage.cpp @@ -2,11 +2,14 @@ #include "rr_graph_storage.h" #include "physical_types.h" #include "rr_graph_fwd.h" +#include "vtr_assert.h" #include "vtr_error.h" #include "librrgraph_types.h" +#include "vtr_util.h" #include #include +#include void t_rr_graph_storage::reserve_edges(size_t num_edges) { edge_src_node_.reserve(num_edges); @@ -57,6 +60,54 @@ void t_rr_graph_storage::alloc_and_load_edges(const t_rr_edge_info_set* rr_edges } } +void t_rr_graph_storage::remove_edges(std::vector& rr_edges_to_remove) { + VTR_ASSERT(!edges_read_); + + if (rr_edges_to_remove.empty()) { + return; + } + + size_t starting_edge_count = edge_dest_node_.size(); + + // Sort and make sure all edge indices are unique + vtr::uniquify(rr_edges_to_remove); + VTR_ASSERT_SAFE(std::is_sorted(rr_edges_to_remove.begin(), rr_edges_to_remove.end())); + + // Make sure the edge indices are valid + VTR_ASSERT(static_cast(rr_edges_to_remove.back()) <= edge_dest_node_.size()); + + // Index of the last edge + size_t edge_list_end = edge_dest_node_.size() - 1; + + // Iterate backwards through the list of indices we want to remove. + + for (RREdgeId erase_idx : std::ranges::reverse_view(rr_edges_to_remove)) { + // Copy what's at the end of the list to the index we wanted to remove + edge_dest_node_[erase_idx] = edge_dest_node_[RREdgeId(edge_list_end)]; + edge_src_node_[erase_idx] = edge_src_node_[RREdgeId(edge_list_end)]; + edge_switch_[erase_idx] = edge_switch_[RREdgeId(edge_list_end)]; + edge_remapped_[erase_idx] = edge_remapped_[RREdgeId(edge_list_end)]; + + // At this point we have no copies of what was at erase_idx and two copies of + // what was at the end of the list. If we make the list one element shorter, + // we end up with a list that has removed the element at erase_idx. + edge_list_end--; + + } + + // We have a new index to the end of the list, call erase on the elements past that index + // to update the std::vector and shrink the actual data structures. + edge_dest_node_.erase(edge_dest_node_.begin() + edge_list_end + 1, edge_dest_node_.end()); + edge_src_node_.erase(edge_src_node_.begin() + edge_list_end + 1, edge_src_node_.end()); + edge_switch_.erase(edge_switch_.begin() + edge_list_end + 1, edge_switch_.end()); + edge_remapped_.erase(edge_remapped_.begin() + edge_list_end + 1, edge_remapped_.end()); + + VTR_ASSERT(edge_dest_node_.size() == (starting_edge_count - rr_edges_to_remove.size())); + + partitioned_ = false; +} + + void t_rr_graph_storage::assign_first_edges() { VTR_ASSERT(node_first_edge_.empty()); @@ -68,31 +119,34 @@ void t_rr_graph_storage::assign_first_edges() { edge_src_node_.end())); size_t node_id = 0; - size_t first_id = 0; - size_t second_id = 0; + size_t first_edge_id = 0; + size_t second_edge_id = 0; + size_t num_edges = edge_src_node_.size(); VTR_ASSERT(edge_dest_node_.size() == num_edges); VTR_ASSERT(edge_switch_.size() == num_edges); VTR_ASSERT(edge_remapped_.size() == num_edges); + while (true) { - VTR_ASSERT(first_id < num_edges); - VTR_ASSERT(second_id < num_edges); - size_t current_node_id = size_t(edge_src_node_[RREdgeId(second_id)]); + VTR_ASSERT(first_edge_id < num_edges); + VTR_ASSERT(second_edge_id < num_edges); + + size_t current_node_id = size_t(edge_src_node_[RREdgeId(second_edge_id)]); if (node_id < current_node_id) { // All edges belonging to node_id are assigned. while (node_id < current_node_id) { // Store any edges belongs to node_id. VTR_ASSERT(node_id < node_first_edge_.size()); - node_first_edge_[RRNodeId(node_id)] = RREdgeId(first_id); - first_id = second_id; + node_first_edge_[RRNodeId(node_id)] = RREdgeId(first_edge_id); + first_edge_id = second_edge_id; node_id += 1; } VTR_ASSERT(node_id == current_node_id); - node_first_edge_[RRNodeId(node_id)] = RREdgeId(second_id); + node_first_edge_[RRNodeId(node_id)] = RREdgeId(second_edge_id); } else { - second_id += 1; - if (second_id == num_edges) { + second_edge_id += 1; + if (second_edge_id == num_edges) { break; } } @@ -100,7 +154,7 @@ void t_rr_graph_storage::assign_first_edges() { // All remaining nodes have no edges, set as such. for (size_t inode = node_id + 1; inode < node_first_edge_.size(); ++inode) { - node_first_edge_[RRNodeId(inode)] = RREdgeId(second_id); + node_first_edge_[RRNodeId(inode)] = RREdgeId(second_edge_id); } VTR_ASSERT_SAFE(verify_first_edges()); @@ -128,7 +182,7 @@ bool t_rr_graph_storage::verify_first_edges() const { void t_rr_graph_storage::init_fan_in() { //Reset all fan-ins to zero - edges_read_ = true; + node_fan_in_.clear(); node_fan_in_.resize(node_storage_.size(), 0); node_fan_in_.shrink_to_fit(); //Walk the graph and increment fanin on all downstream nodes @@ -575,6 +629,54 @@ void t_rr_graph_storage::set_node_direction(RRNodeId id, Direction new_direction node_storage_[id].dir_side_.direction = new_direction; } +void t_rr_graph_storage::set_node_ptc_nums(RRNodeId node, const std::string& ptc_str) { + VTR_ASSERT(size_t(node) < node_storage_.size()); + std::vector ptc_tokens = vtr::StringToken(ptc_str).split(","); + VTR_ASSERT(ptc_tokens.size() >= 1); + set_node_ptc_num(node, std::stoi(ptc_tokens[0])); + if (ptc_tokens.size() > 1) { + VTR_ASSERT(size_t(node) < node_tilable_track_nums_.size()); + node_tilable_track_nums_[node].resize(ptc_tokens.size()); + for (size_t iptc = 0; iptc < ptc_tokens.size(); iptc++) { + node_tilable_track_nums_[node][iptc] = std::stoi(ptc_tokens[iptc]); + } + } +} + +void t_rr_graph_storage::add_node_tilable_track_num(RRNodeId node, size_t node_offset, short track_id) { + VTR_ASSERT(size_t(node) < node_storage_.size()); + VTR_ASSERT(size_t(node) < node_tilable_track_nums_.size()); + VTR_ASSERT_MSG(node_type(node) == e_rr_type::CHANX || node_type(node) == e_rr_type::CHANY, + "Track number valid only for CHANX/CHANY RR nodes"); + + size_t node_length = std::abs(node_xhigh(node) - node_xlow(node)) + + std::abs(node_yhigh(node) - node_ylow(node)); + if (node_length + 1 != node_tilable_track_nums_[node].size()) { + node_tilable_track_nums_[node].resize(node_length + 1); + } + + VTR_ASSERT(node_offset < node_tilable_track_nums_[node].size()); + + node_tilable_track_nums_[node][node_offset] = track_id; +} + +std::string t_rr_graph_storage::node_ptc_nums_to_string(RRNodeId node) const { + if (node_tilable_track_nums_.empty()) { + return std::to_string(size_t(node_ptc_num(node))); + } + VTR_ASSERT(size_t(node) < node_tilable_track_nums_.size()); + if (node_tilable_track_nums_[node].empty()) { + return std::to_string(size_t(node_ptc_num(node))); + } + std::string ret; + for (size_t iptc = 0; iptc < node_tilable_track_nums_[node].size(); iptc++) { + ret += std::to_string(size_t(node_tilable_track_nums_[node][iptc])) + ","; + } + // Remove the last comma + ret.pop_back(); + return ret; +} + void t_rr_graph_storage::add_node_side(RRNodeId id, e_side new_side) { if (node_type(id) != e_rr_type::IPIN && node_type(id) != e_rr_type::OPIN) { VTR_LOG_ERROR("Attempted to set RR node 'side' for non-pin type '%s'", node_type_string(id)); @@ -601,6 +703,80 @@ void t_rr_graph_storage::set_virtual_clock_network_root_idx(RRNodeId virtual_clo } } +void t_rr_graph_storage::remove_nodes(const std::vector& nodes) { + VTR_ASSERT(!edges_read_); + // To remove the nodes, we first sort them in ascending order. This makes it easy + // to calculate the offset by which other node IDs need to be adjusted. + // For example, after sorting the nodes to be removed, if a node ID falls between + // the first and second element, its ID should be reduced by 1. + // If a node ID is larger than the last element, its ID should be reduced by + // the total number of nodes being removed. + std::vector sorted_nodes = nodes; + std::sort(sorted_nodes.begin(), sorted_nodes.end()); + + // Iterate over the nodes to be removed and adjust the IDs of nodes + // that fall between them. + for (size_t i = 0; i < sorted_nodes.size(); ++i) { + size_t start_rr_node_index = size_t(sorted_nodes[i]) + 1; + size_t end_rr_node_index = (i == sorted_nodes.size() - 1) ? node_storage_.size() : size_t(sorted_nodes[i + 1]); + for (size_t j = start_rr_node_index; j < end_rr_node_index; ++j) { + RRNodeId old_node = RRNodeId(j); + // New node index is equal to the old nodex index minus the number of nodes being removed before it. + RRNodeId new_node = RRNodeId(j-(i+1)); + node_storage_[new_node] = node_storage_[old_node]; + node_ptc_[new_node] = node_ptc_[old_node]; + node_layer_[new_node] = node_layer_[old_node]; + node_name_[new_node] = node_name_[old_node]; + if (is_tileable_) { + node_bend_start_[new_node] = node_bend_start_[old_node]; + node_bend_end_[new_node] = node_bend_end_[old_node]; + node_tilable_track_nums_[new_node] = node_tilable_track_nums_[old_node]; + } + } + } + + // Now that the data structures are adjusted, we can shrink the size of them + size_t num_nodes_to_remove = sorted_nodes.size(); + VTR_ASSERT(num_nodes_to_remove <= node_storage_.size()); + node_storage_.erase(node_storage_.end()-num_nodes_to_remove, node_storage_.end()); + node_ptc_.erase(node_ptc_.end()-num_nodes_to_remove, node_ptc_.end()); + node_layer_.erase(node_layer_.end()-num_nodes_to_remove, node_layer_.end()); + for (size_t node_index = node_name_.size()-num_nodes_to_remove; node_index < node_name_.size(); ++node_index) { + RRNodeId node = RRNodeId(node_index); + node_name_.erase(node); + } + if (is_tileable_) { + node_bend_start_.erase(node_bend_start_.end()-num_nodes_to_remove, node_bend_start_.end()); + node_bend_end_.erase(node_bend_end_.end()-num_nodes_to_remove, node_bend_end_.end()); + node_tilable_track_nums_.erase(node_tilable_track_nums_.end()-num_nodes_to_remove, node_tilable_track_nums_.end()); + } + + std::vector removed_edges; + auto adjust_edges = [&](vtr::vector& edge_nodes) { + for (size_t edge_index = 0; edge_index < edge_nodes.size(); ++edge_index) { + RREdgeId edge_id = RREdgeId(edge_index); + RRNodeId node = edge_nodes[edge_id]; + + // Find insertion point in the sorted vector + auto node_it = std::lower_bound(sorted_nodes.begin(), sorted_nodes.end(), node); + + if (node_it != sorted_nodes.end() && *node_it == node) { + // Node exists in sorted_nodes, mark edge for removal + removed_edges.push_back(edge_id); + } else { + size_t node_offset = std::distance(sorted_nodes.begin(), node_it); + size_t new_node_index = size_t(node) - node_offset; + edge_nodes[edge_id] = RRNodeId(new_node_index); + } + } + }; + + adjust_edges(edge_src_node_); + adjust_edges(edge_dest_node_); + + remove_edges(removed_edges); +} + int t_rr_graph_view::node_ptc_num(RRNodeId id) const { return node_ptc_[id].ptc_.pin_num; } diff --git a/libs/librrgraph/src/base/rr_graph_storage.h b/libs/librrgraph/src/base/rr_graph_storage.h index ab1c844cd19..233b50f116d 100644 --- a/libs/librrgraph/src/base/rr_graph_storage.h +++ b/libs/librrgraph/src/base/rr_graph_storage.h @@ -400,6 +400,14 @@ class t_rr_graph_storage { return vtr::StrongIdRange(first_edge(id), last_edge(id)); } + /** @brief Returns a range of all edges in the RR Graph. + * This method does not depend on the edges being correctly + * sorted and can be used before partition_edges is called. + */ + inline vtr::StrongIdRange all_edges() const { + return vtr::StrongIdRange(RREdgeId(0), RREdgeId(edge_src_node_.size())); + } + /** @brief Retrieve the RREdgeId for iedge'th edge in RRNodeId. * * This method should generally not be used, and instead first_edge and @@ -556,6 +564,9 @@ class t_rr_graph_storage { node_bend_end_.reserve(node_storage_.capacity()); node_bend_end_.resize(node_storage_.size()); + + node_tilable_track_nums_.reserve(node_storage_.capacity()); + node_tilable_track_nums_.resize(node_storage_.size()); } } @@ -569,6 +580,7 @@ class t_rr_graph_storage { if (is_tileable_) { node_bend_start_.reserve(size); node_bend_end_.reserve(size); + node_tilable_track_nums_.reserve(size); } } @@ -582,6 +594,7 @@ class t_rr_graph_storage { if (is_tileable_) { node_bend_start_.resize(size); node_bend_end_.resize(size); + node_tilable_track_nums_.resize(size); } } @@ -606,6 +619,7 @@ class t_rr_graph_storage { node_layer_.clear(); node_bend_start_.clear(); node_bend_end_.clear(); + node_tilable_track_nums_.clear(); node_name_.clear(); virtual_clock_network_root_idx_.clear(); edge_src_node_.clear(); @@ -615,7 +629,6 @@ class t_rr_graph_storage { edges_read_ = false; partitioned_ = false; remapped_edges_ = false; - is_tileable_ = false; } /** @brief @@ -644,7 +657,7 @@ class t_rr_graph_storage { node_layer_.shrink_to_fit(); node_bend_start_.shrink_to_fit(); node_bend_end_.shrink_to_fit(); - + node_tilable_track_nums_.shrink_to_fit(); edge_src_node_.shrink_to_fit(); edge_dest_node_.shrink_to_fit(); edge_switch_.shrink_to_fit(); @@ -661,6 +674,7 @@ class t_rr_graph_storage { if (is_tileable_) { node_bend_start_.emplace_back(); node_bend_end_.emplace_back(); + node_tilable_track_nums_.emplace_back(); } } @@ -691,6 +705,25 @@ class t_rr_graph_storage { void set_node_capacity(RRNodeId, short new_capacity); void set_node_direction(RRNodeId, Direction new_direction); + void set_node_ptc_nums(RRNodeId node, const std::string& ptc_str); + void add_node_tilable_track_num(RRNodeId node, size_t node_offset, short track_id); + + void emplace_back_node_tilable_track_num(); + + bool node_contain_multiple_ptc(RRNodeId node) const { + if (node_tilable_track_nums_.empty()) { + return false; + } else { + return node_tilable_track_nums_[node].size() > 1; + } + } + + std::string node_ptc_nums_to_string(RRNodeId node) const; + + const std::vector& node_tilable_track_nums(RRNodeId node) const { + return node_tilable_track_nums_[node]; + } + /** @brief * Add a side to the node attributes * This is the function to use when you just add a new side WITHOUT resetting side attributes @@ -704,6 +737,17 @@ class t_rr_graph_storage { */ void set_virtual_clock_network_root_idx(RRNodeId virtual_clock_network_root_idx); + /** + * @brief Removes a given list of RRNodes from the RR Graph + * This method should be called before partition_edges has been called. + * If init_fan_in has been called, you need to call it again after removing the nodes. + * @note This a very expensive method, so should be called only when necessary. It is better + * to not add nodes in the first place, instead of relying on this method to remove nodes. + * + * @param nodes list of RRNodes to be removed + */ + void remove_nodes(const std::vector& nodes); + /**************** * Edge methods * ****************/ @@ -776,6 +820,16 @@ class t_rr_graph_storage { /** @brief Adds a batch of edges.*/ void alloc_and_load_edges(const t_rr_edge_info_set* rr_edges_to_create); + /** @brief Removes a given list of RREdgeIds from the RR Graph. + * This method does not preserve the order of edges. If you're + * calling it after partition_edges has been called, you will + * need to call partition_edges again. + * This operation is O(#RR Graph edges) and should not be called frequently. + * + * @param rr_edges_to_remove list of RREdgeIds to be removed + */ + void remove_edges(std::vector& rr_edges_to_remove); + /* Edge finalization methods */ /** @brief Counts the number of rr switches needed based on fan in to support mux @@ -944,6 +998,29 @@ class t_rr_graph_storage { */ vtr::vector node_ptc_; + /** + * @brief Extra ptc number for each routing resource node. + * @note This is required by tileable routing resource graphs. The first index is the node id, and + * the second index is is the relative distance from the starting point of the node. + * @details + * In a tileable routing architecture, routing tracks, e.g., CHANX and CHANY, follow a staggered organization. + * Hence, a routing track may appear in different routing channels, representing different ptc/track id. + * Here is an illustrative example of a X-direction routing track (CHANX) in INC direction, which is organized in staggered way. + * + * Coord(x,y) (1,0) (2,0) (3,0) (4,0) Another track (node) + * ptc=0 ------> ------> + * \ / + * ptc=1 ------> / + * \ / + * ptc=2 ------> / + * \ / + * ptc=3 -------> + * ^ ^ + * | | + * starting point ending point + */ + vtr::vector> node_tilable_track_nums_; + /** @brief * This array stores the first edge of each RRNodeId. Not that the length * of this vector is always storage_.size() + 1, where the last value is diff --git a/libs/librrgraph/src/base/rr_graph_view.cpp b/libs/librrgraph/src/base/rr_graph_view.cpp index f23762c3e7d..4f5cbb77739 100644 --- a/libs/librrgraph/src/base/rr_graph_view.cpp +++ b/libs/librrgraph/src/base/rr_graph_view.cpp @@ -10,8 +10,7 @@ RRGraphView::RRGraphView(const t_rr_graph_storage& node_storage, const std::vector& rr_rc_data, const vtr::vector& rr_segments, const vtr::vector& rr_switch_inf, - const vtr::vector>& node_in_edges, - const vtr::vector>& node_tileable_track_nums) + const vtr::vector>& node_in_edges) : node_storage_(node_storage) , node_lookup_(node_lookup) , rr_node_metadata_(rr_node_metadata) @@ -20,9 +19,7 @@ RRGraphView::RRGraphView(const t_rr_graph_storage& node_storage, , rr_rc_data_(rr_rc_data) , rr_segments_(rr_segments) , rr_switch_inf_(rr_switch_inf) - , node_in_edges_(node_in_edges) - , node_tileable_track_nums_(node_tileable_track_nums) { -} + , node_in_edges_(node_in_edges) {} std::vector RRGraphView::node_in_edges(RRNodeId node) const { VTR_ASSERT(size_t(node) < node_storage_.size()); @@ -129,5 +126,3 @@ bool RRGraphView::validate_in_edges() const { } return true; } - - diff --git a/libs/librrgraph/src/base/rr_graph_view.h b/libs/librrgraph/src/base/rr_graph_view.h index ca825d15642..8de56613c50 100644 --- a/libs/librrgraph/src/base/rr_graph_view.h +++ b/libs/librrgraph/src/base/rr_graph_view.h @@ -65,9 +65,11 @@ #include "metadata_storage.h" #include "rr_node.h" #include "physical_types.h" +#include "rr_node_types.h" #include "rr_spatial_lookup.h" #include "vtr_geometry.h" #include "rr_graph_utils.h" +#include "vtr_range.h" class RRGraphView { /* -- Constructors -- */ @@ -81,8 +83,7 @@ class RRGraphView { const std::vector& rr_rc_data, const vtr::vector& rr_segments, const vtr::vector& rr_switch_inf, - const vtr::vector>& node_in_edges, - const vtr::vector>& node_ptc_nums); + const vtr::vector>& node_in_edges); /* Disable copy constructors and copy assignment operator * This is to avoid accidental copy because it could be an expensive operation considering that the @@ -584,18 +585,29 @@ class RRGraphView { * @example * RRGraphView rr_graph; // A dummy rr_graph for a short example * RRNodeId node; // A dummy node for a short example - * for (RREdgeId edge : rr_graph.edges(node)) { + * for (t_edge_size edge : rr_graph.edges(node)) { * // Do something with the edge * } + * + * @note Iterating on the range returned by this function will not give you an RREdgeId, but instead gives you the index among a node's outgoing edges */ - inline edge_idx_range edges(const RRNodeId& id) const { + inline edge_idx_range edges(RRNodeId id) const { return vtr::make_range(edge_idx_iterator(0), edge_idx_iterator(num_edges(id))); } + /** @brief Returns a range of all edges in the RR Graph. + * This method does not depend on the edges begin correctly + * sorted and can be used before partition_edges is called. + */ + inline vtr::StrongIdRange all_edges() const { + return node_storage_.all_edges(); + } + + /** * @brief Return ID range for outgoing edges. */ - inline edge_idx_range node_out_edges(const RRNodeId& id) const { + inline edge_idx_range node_out_edges(RRNodeId id) const { return vtr::make_range(edge_idx_iterator(0), edge_idx_iterator(num_edges(id))); } @@ -812,8 +824,4 @@ class RRGraphView { /// A list of incoming edges for each routing resource node. This can be built optionally, as required by applications. /// By default, it is empty. Call build_in_edges() to construct it. const vtr::vector>& node_in_edges_; - - /// A list of extra ptc numbers for each routing resource node. This is only used for tileable architecture. - /// See details in RRGraphBuilder class - const vtr::vector>& node_tileable_track_nums_; }; diff --git a/libs/librrgraph/src/io/rr_graph_reader.cpp b/libs/librrgraph/src/io/rr_graph_reader.cpp index ff0f8b9e4ae..41d3d762118 100644 --- a/libs/librrgraph/src/io/rr_graph_reader.cpp +++ b/libs/librrgraph/src/io/rr_graph_reader.cpp @@ -83,6 +83,10 @@ void load_rr_file(RRGraphBuilder* rr_graph_builder, rr_graph_builder->add_rr_segment(segment_inf[(iseg)]); } + if (graph_type == e_graph_type::UNIDIR_TILEABLE) { + rr_graph_builder->set_tileable(true); + } + RrGraphSerializer reader( graph_type, base_cost_type, diff --git a/libs/librrgraph/src/io/rr_graph_uxsdcxx_serializer.h b/libs/librrgraph/src/io/rr_graph_uxsdcxx_serializer.h index 3f76fa47f25..0482f338bde 100644 --- a/libs/librrgraph/src/io/rr_graph_uxsdcxx_serializer.h +++ b/libs/librrgraph/src/io/rr_graph_uxsdcxx_serializer.h @@ -896,7 +896,6 @@ class RrGraphSerializer final : public uxsd::RrGraphBase { */ inline void preallocate_rr_nodes_node(void*& /*ctx*/, size_t size) final { rr_graph_builder_->reserve_nodes(size); - rr_graph_builder_->resize_node_ptc_nums(size); } inline int add_rr_nodes_node(void*& /*ctx*/, unsigned int capacity, unsigned int id, uxsd::enum_node_type type) final { // make_room_in_vector will not allocate if preallocate_rr_nodes_node @@ -1195,7 +1194,7 @@ class RrGraphSerializer final : public uxsd::RrGraphBase { most_frequent_switch.first = switch_id; most_frequent_switch.second = count_for_wire_to_ipin_switches[switch_id]; } - } else{ + } else { VTR_ASSERT(rr_graph.node_layer_low(RRNodeId(sink_node)) != rr_graph.node_layer_low(RRNodeId(source_node))); count_for_wire_to_ipin_switches_between_dice[switch_id]++; if(count_for_wire_to_ipin_switches_between_dice[switch_id] > most_frequent_switch_between_dice.second){ diff --git a/libs/librrgraph/src/utils/alloc_and_load_rr_indexed_data.cpp b/libs/librrgraph/src/utils/alloc_and_load_rr_indexed_data.cpp index 3e252131258..9438577902e 100644 --- a/libs/librrgraph/src/utils/alloc_and_load_rr_indexed_data.cpp +++ b/libs/librrgraph/src/utils/alloc_and_load_rr_indexed_data.cpp @@ -2,6 +2,7 @@ #include #include /* Needed only for sqrt call (remove if sqrt removed) */ +#include #include #include #include @@ -28,7 +29,9 @@ static void load_rr_indexed_data_base_costs(const RRGraphView& rr_graph, static float get_delay_normalization_fac(const vtr::vector& rr_indexed_data, const bool echo_enabled, const char* echo_file_name); -static void load_rr_indexed_data_T_values(const RRGraphView& rr_graph, vtr::vector& rr_indexed_data); +static void load_rr_indexed_data_T_values(const RRGraphView& rr_graph, + const RRSwitchId wire_to_ipin_switch, + vtr::vector& rr_indexed_data); /** * @brief Computes average R, Tdel, and Cinternal of fan-in switches for a given node. @@ -49,7 +52,7 @@ static void calculate_average_switch(const RRGraphView& rr_graph, int& num_switches, int& num_shorts, short& buffered, - vtr::vector>& fan_in_list); + const vtr::vector>& fan_in_list); static void fixup_rr_indexed_data_T_values(vtr::vector& rr_indexed_data, size_t num_segment); @@ -84,7 +87,7 @@ void alloc_and_load_rr_indexed_data(const RRGraphView& rr_graph, const std::vector& segment_inf_y, const std::vector& segment_inf_z, vtr::vector& rr_indexed_data, - RRSwitchId wire_to_ipin_switch, + const RRSwitchId wire_to_ipin_switch, e_base_cost_type base_cost_type, const bool echo_enabled, const char* echo_file_name) { @@ -108,9 +111,6 @@ void alloc_and_load_rr_indexed_data(const RRGraphView& rr_graph, rr_indexed_data[RRIndexedDataId(i)].C_load = 0.; } - //TODO: SM: IPIN t_linear assumes wire_to_ipin_switch which corresponds to within die switch connection - rr_indexed_data[RRIndexedDataId(IPIN_COST_INDEX)].T_linear = rr_graph.rr_switch_inf(wire_to_ipin_switch).Tdel; - std::vector ortho_costs = find_ortho_cost_index(rr_graph, segment_inf_x, segment_inf_y, e_parallel_axis::X_AXIS); /* AA: The code below should replace find_ortho_cost_index call once we deprecate the CLASSIC lookahead as it is the only lookahead @@ -158,7 +158,9 @@ void alloc_and_load_rr_indexed_data(const RRGraphView& rr_graph, rr_indexed_data[index].seg_index = seg_ptr->seg_index; } - load_rr_indexed_data_T_values(rr_graph, rr_indexed_data); + load_rr_indexed_data_T_values(rr_graph, + wire_to_ipin_switch, + rr_indexed_data); fixup_rr_indexed_data_T_values(rr_indexed_data, total_num_segment); @@ -513,6 +515,7 @@ static float get_delay_normalization_fac(const vtr::vector& rr_indexed_data) { vtr::vector> fan_in_list = get_fan_in_list(rr_graph); @@ -531,12 +534,24 @@ static void load_rr_indexed_data_T_values(const RRGraphView& rr_graph, vtr::vector> switch_Cinternal_total(rr_indexed_data.size()); vtr::vector switches_buffered(rr_indexed_data.size(), LIBRRGRAPH_UNDEFINED_VAL); + size_t ipin_switch_count = 0; + float ipin_switch_T_total = 0.; + // Walk through the RR graph and collect all R and C values of all the nodes, // as well as their fan-in switches R, T_del, and Cinternal values. // The median of R and C values for each cost index is assigned to the indexed data. for (const RRNodeId rr_id : rr_graph.nodes()) { e_rr_type rr_type = rr_graph.node_type(rr_id); + if (rr_type == e_rr_type::IPIN) { + for (const RREdgeId edge : fan_in_list[rr_id]) { + RRSwitchId rr_switch_id = RRSwitchId(rr_graph.edge_switch(edge)); + float switch_T_del = rr_graph.rr_switch_inf(rr_switch_id).Tdel; + ipin_switch_T_total += switch_T_del; + ipin_switch_count++; + } + } + if (!is_chanxy(rr_type) && !is_chanz(rr_type)) { continue; } @@ -550,7 +565,15 @@ static void load_rr_indexed_data_T_values(const RRGraphView& rr_graph, int num_switches = 0; int num_shorts = 0; short buffered = LIBRRGRAPH_UNDEFINED_VAL; - calculate_average_switch(rr_graph, rr_id, avg_switch_R, avg_switch_T, avg_switch_Cinternal, num_switches, num_shorts, buffered, fan_in_list); + calculate_average_switch(rr_graph, + rr_id, + avg_switch_R, + avg_switch_T, + avg_switch_Cinternal, + num_switches, + num_shorts, + buffered, + fan_in_list); if (num_switches == 0) { if (num_shorts == 0) { @@ -590,6 +613,18 @@ static void load_rr_indexed_data_T_values(const RRGraphView& rr_graph, } } + // Set the T_linear value for the IPIN cost index + { + if (ipin_switch_count == 0) { + VTR_LOG_WARN("No IPIN switches found. Setting T_linear to 0\n"); + float default_ipin_switch_T_del = rr_graph.rr_switch_inf(RRSwitchId(wire_to_ipin_switch)).Tdel; + rr_indexed_data[RRIndexedDataId(IPIN_COST_INDEX)].T_linear = default_ipin_switch_T_del; + } else { + float average_ipin_switch_T_del = ipin_switch_T_total / ipin_switch_count; + rr_indexed_data[RRIndexedDataId(IPIN_COST_INDEX)].T_linear = average_ipin_switch_T_del; + } + } + unsigned num_occurences_of_no_instances_with_cost_index = 0; for (size_t cost_index = CHANX_COST_INDEX_START; cost_index < rr_indexed_data.size(); cost_index++) { if (num_nodes_of_index[RRIndexedDataId(cost_index)] == 0) { // Segments don't exist. @@ -650,7 +685,7 @@ static void calculate_average_switch(const RRGraphView& rr_graph, int& num_switches, int& num_shorts, short& buffered, - vtr::vector>& fan_in_list) { + const vtr::vector>& fan_in_list) { avg_switch_R = 0; avg_switch_T = 0; diff --git a/libs/librrgraph/src/utils/alloc_and_load_rr_indexed_data.h b/libs/librrgraph/src/utils/alloc_and_load_rr_indexed_data.h index f98a4d45729..8d60ff93d19 100644 --- a/libs/librrgraph/src/utils/alloc_and_load_rr_indexed_data.h +++ b/libs/librrgraph/src/utils/alloc_and_load_rr_indexed_data.h @@ -12,7 +12,7 @@ void alloc_and_load_rr_indexed_data(const RRGraphView& rr_graph, const std::vector& segment_inf_y, const std::vector& segment_inf_z, vtr::vector& rr_indexed_data, - RRSwitchId wire_to_ipin_switch, + const RRSwitchId wire_to_ipin_switch, e_base_cost_type base_cost_type, const bool echo_enabled, const char* echo_file_name); diff --git a/utils/route_diag/src/main.cpp b/utils/route_diag/src/main.cpp index 3dd599504a0..af2f7833022 100644 --- a/utils/route_diag/src/main.cpp +++ b/utils/route_diag/src/main.cpp @@ -296,6 +296,7 @@ int main(int argc, const char **argv) { alloc_routing_structs(chan_width, vpr_setup.RouterOpts, + vpr_setup.CRROpts, vpr_setup.RoutingArch, vpr_setup.Segments, Arch.directs, diff --git a/vpr/CMakeLists.txt b/vpr/CMakeLists.txt index 2a30974832d..ca416c68703 100644 --- a/vpr/CMakeLists.txt +++ b/vpr/CMakeLists.txt @@ -119,6 +119,7 @@ target_link_libraries(libvpr libpugixml librrgraph ZLIB::ZLIB + yaml-cpp::yaml-cpp ) if(VPR_USE_SERVER) diff --git a/vpr/src/analytical_place/analytical_placement_flow.cpp b/vpr/src/analytical_place/analytical_placement_flow.cpp index 62b6f8889fa..b078c7e455a 100644 --- a/vpr/src/analytical_place/analytical_placement_flow.cpp +++ b/vpr/src/analytical_place/analytical_placement_flow.cpp @@ -238,6 +238,7 @@ void run_analytical_placement_flow(t_vpr_setup& vpr_setup) { if (pre_cluster_timing_manager.is_valid()) { place_delay_model = PlacementDelayModelCreator::create_delay_model(vpr_setup.PlacerOpts, vpr_setup.RouterOpts, + vpr_setup.CRROpts, (const Netlist<>&)atom_nlist, vpr_setup.RoutingArch, vpr_setup.Segments, diff --git a/vpr/src/analytical_place/detailed_placer.cpp b/vpr/src/analytical_place/detailed_placer.cpp index c764191524a..c2c375802a6 100644 --- a/vpr/src/analytical_place/detailed_placer.cpp +++ b/vpr/src/analytical_place/detailed_placer.cpp @@ -64,6 +64,7 @@ AnnealerDetailedPlacer::AnnealerDetailedPlacer(const BlkLocRegistry& curr_cluste if (vpr_setup.PlacerOpts.place_algorithm.is_timing_driven()) { place_delay_model = PlacementDelayModelCreator::create_delay_model(vpr_setup.PlacerOpts, vpr_setup.RouterOpts, + vpr_setup.CRROpts, (const Netlist<>&)clustered_netlist, vpr_setup.RoutingArch, vpr_setup.Segments, diff --git a/vpr/src/base/place_and_route.cpp b/vpr/src/base/place_and_route.cpp index 4462fbe1d8e..a50257360d3 100644 --- a/vpr/src/base/place_and_route.cpp +++ b/vpr/src/base/place_and_route.cpp @@ -42,6 +42,7 @@ int binary_search_place_and_route(const Netlist<>& placement_net_list, const Netlist<>& router_net_list, const t_placer_opts& placer_opts_ref, const t_router_opts& router_opts, + const t_crr_opts& crr_opts, const t_analysis_opts& analysis_opts, const t_noc_opts& noc_opts, const t_file_name_opts& filename_opts, @@ -172,6 +173,7 @@ int binary_search_place_and_route(const Netlist<>& placement_net_list, try_place(placement_net_list, placer_opts, router_opts, + crr_opts, analysis_opts, noc_opts, arch->Chans, @@ -184,6 +186,7 @@ int binary_search_place_and_route(const Netlist<>& placement_net_list, success = route(router_net_list, current, router_opts, + crr_opts, analysis_opts, det_routing_arch, segment_inf, net_delay, @@ -314,7 +317,7 @@ int binary_search_place_and_route(const Netlist<>& placement_net_list, break; if (placer_opts.place_freq == PLACE_ALWAYS) { placer_opts.place_chan_width = current; - try_place(placement_net_list, placer_opts, router_opts, analysis_opts, noc_opts, + try_place(placement_net_list, placer_opts, router_opts, crr_opts, analysis_opts, noc_opts, arch->Chans, det_routing_arch, segment_inf, arch->directs, FlatPlacementInfo(), // Pass empty flat placement info. @@ -324,6 +327,7 @@ int binary_search_place_and_route(const Netlist<>& placement_net_list, success = route(router_net_list, current, router_opts, + crr_opts, analysis_opts, det_routing_arch, segment_inf, @@ -374,6 +378,7 @@ int binary_search_place_and_route(const Netlist<>& placement_net_list, det_routing_arch, segment_inf, router_opts, + crr_opts, arch->directs, &warnings, is_flat); diff --git a/vpr/src/base/place_and_route.h b/vpr/src/base/place_and_route.h index a67712d694d..356eae042a5 100644 --- a/vpr/src/base/place_and_route.h +++ b/vpr/src/base/place_and_route.h @@ -21,6 +21,7 @@ int binary_search_place_and_route(const Netlist<>& placement_net_list, const Netlist<>& router_net_list, const t_placer_opts& placer_opts_ref, const t_router_opts& router_opts, + const t_crr_opts& crr_opts, const t_analysis_opts& analysis_opts, const t_noc_opts& noc_opts, const t_file_name_opts& filename_opts, diff --git a/vpr/src/base/read_options.cpp b/vpr/src/base/read_options.cpp index fca403f89e2..91b623cc411 100644 --- a/vpr/src/base/read_options.cpp +++ b/vpr/src/base/read_options.cpp @@ -3280,6 +3280,43 @@ argparse::ArgumentParser create_arg_parser(const std::string& prog_name, t_optio .default_value("off") .show_in(argparse::ShowIn::HELP_ONLY); + auto& crr_grp = parser.add_argument_group("CRR options"); + + crr_grp.add_argument(args.sb_maps, "--sb_maps") + .help("Switch block map file that specifed the switch block template used for each location") + .default_value("") + .show_in(argparse::ShowIn::HELP_ONLY); + + crr_grp.add_argument(args.sb_templates, "--sb_templates") + .help("Directory containing the switch block templates") + .default_value("") + .show_in(argparse::ShowIn::HELP_ONLY); + + crr_grp.add_argument(args.crr_num_threads, "--crr_num_threads") + .help("Number of threads to use for the CRR generation") + .default_value("1") + .show_in(argparse::ShowIn::HELP_ONLY); + + crr_grp.add_argument(args.preserve_input_pin_connections, "--preserve_input_pin_connections") + .help("If it set to on, the input pin connections will be generated by the default flow and not from the CRR template") + .default_value("off") + .show_in(argparse::ShowIn::HELP_ONLY); + + crr_grp.add_argument(args.preserve_output_pin_connections, "--preserve_output_pin_connections") + .help("If it set to on, the output pin connections will be generated by the default flow and not from the CRR template") + .default_value("off") + .show_in(argparse::ShowIn::HELP_ONLY); + + crr_grp.add_argument(args.annotated_rr_graph, "--annotated_rr_graph") + .help("Whether the generated CRR should be annotated with delay") + .default_value("off") + .show_in(argparse::ShowIn::HELP_ONLY); + + crr_grp.add_argument(args.remove_dangling_nodes, "--remove_dangling_nodes") + .help("Whether the generated CRR should remove CHANX and CHANY nodes that have no fan-in") + .default_value("off") + .show_in(argparse::ShowIn::HELP_ONLY); + auto& power_grp = parser.add_argument_group("power analysis options"); power_grp.add_argument(args.do_power, "--power") diff --git a/vpr/src/base/read_options.h b/vpr/src/base/read_options.h index 5485f058c49..c4156df2069 100644 --- a/vpr/src/base/read_options.h +++ b/vpr/src/base/read_options.h @@ -284,6 +284,15 @@ struct t_options { argparse::ArgValue write_timing_summary; argparse::ArgValue skip_sync_clustering_and_routing_results; argparse::ArgValue generate_net_timing_report; + + /* CRR options */ + argparse::ArgValue sb_maps; + argparse::ArgValue sb_templates; + argparse::ArgValue crr_num_threads; + argparse::ArgValue preserve_input_pin_connections; + argparse::ArgValue preserve_output_pin_connections; + argparse::ArgValue annotated_rr_graph; + argparse::ArgValue remove_dangling_nodes; }; argparse::ArgumentParser create_arg_parser(const std::string& prog_name, t_options& args); diff --git a/vpr/src/base/setup_vpr.cpp b/vpr/src/base/setup_vpr.cpp index 8bdbed39dcc..a15a2a51f84 100644 --- a/vpr/src/base/setup_vpr.cpp +++ b/vpr/src/base/setup_vpr.cpp @@ -48,6 +48,7 @@ static void setup_switches(const t_arch& Arch, t_det_routing_arch& RoutingArch, const std::vector& arch_switches); static void setup_analysis_opts(const t_options& Options, t_analysis_opts& analysis_opts); +static void setup_crr_opts(const t_options& Options, t_crr_opts& crr_opts); static void setup_power_opts(const t_options& Options, t_power_opts* power_opts, t_arch* Arch); /** @@ -99,6 +100,7 @@ void SetupVPR(const t_options* options, t_ap_opts* apOpts, t_router_opts* routerOpts, t_analysis_opts* analysisOpts, + t_crr_opts* crrOpts, t_noc_opts* nocOpts, t_server_opts* serverOpts, t_det_routing_arch& routingArch, @@ -151,6 +153,7 @@ void SetupVPR(const t_options* options, setup_anneal_sched(*options, &placerOpts->anneal_sched); setup_router_opts(*options, routerOpts); setup_analysis_opts(*options, *analysisOpts); + setup_crr_opts(*options, *crrOpts); setup_power_opts(*options, powerOpts, arch); setup_noc_opts(*options, nocOpts); setup_server_opts(*options, serverOpts); @@ -755,6 +758,16 @@ static void setup_analysis_opts(const t_options& Options, t_analysis_opts& analy analysis_opts.generate_net_timing_report = Options.generate_net_timing_report; } +static void setup_crr_opts(const t_options& Options, t_crr_opts& crr_opts) { + crr_opts.sb_maps = Options.sb_maps; + crr_opts.sb_templates = Options.sb_templates; + crr_opts.crr_num_threads = Options.crr_num_threads; + crr_opts.preserve_input_pin_connections = Options.preserve_input_pin_connections; + crr_opts.preserve_output_pin_connections = Options.preserve_output_pin_connections; + crr_opts.annotated_rr_graph = Options.annotated_rr_graph; + crr_opts.remove_dangling_nodes = Options.remove_dangling_nodes; +} + static void setup_power_opts(const t_options& Options, t_power_opts* power_opts, t_arch* Arch) { DeviceContext& device_ctx = g_vpr_ctx.mutable_device(); diff --git a/vpr/src/base/setup_vpr.h b/vpr/src/base/setup_vpr.h index f72bb231bd3..1e6eaa58120 100644 --- a/vpr/src/base/setup_vpr.h +++ b/vpr/src/base/setup_vpr.h @@ -16,6 +16,7 @@ void SetupVPR(const t_options* Options, t_ap_opts* APOpts, t_router_opts* RouterOpts, t_analysis_opts* AnalysisOpts, + t_crr_opts* CRROpts, t_noc_opts* NocOpts, t_server_opts* ServerOpts, t_det_routing_arch& RoutingArch, diff --git a/vpr/src/base/vpr_api.cpp b/vpr/src/base/vpr_api.cpp index 9dc4d353ae1..0ec0bfb03b7 100644 --- a/vpr/src/base/vpr_api.cpp +++ b/vpr/src/base/vpr_api.cpp @@ -297,6 +297,7 @@ void vpr_init_with_options(const t_options* options, t_vpr_setup* vpr_setup, t_a &vpr_setup->APOpts, &vpr_setup->RouterOpts, &vpr_setup->AnalysisOpts, + &vpr_setup->CRROpts, &vpr_setup->NocOpts, &vpr_setup->ServerOpts, vpr_setup->RoutingArch, @@ -879,6 +880,7 @@ void vpr_place(const Netlist<>& net_list, try_place(net_list, vpr_setup.PlacerOpts, vpr_setup.RouterOpts, + vpr_setup.CRROpts, vpr_setup.AnalysisOpts, vpr_setup.NocOpts, arch.Chans, @@ -1095,6 +1097,7 @@ RouteStatus vpr_route_fixed_W(const Netlist<>& net_list, status = route(net_list, fixed_channel_width, vpr_setup.RouterOpts, + vpr_setup.CRROpts, vpr_setup.AnalysisOpts, vpr_setup.RoutingArch, vpr_setup.Segments, @@ -1127,6 +1130,7 @@ RouteStatus vpr_route_min_W(const Netlist<>& net_list, net_list, vpr_setup.PlacerOpts, router_opts, + vpr_setup.CRROpts, vpr_setup.AnalysisOpts, vpr_setup.NocOpts, vpr_setup.FileNameOpts, @@ -1207,6 +1211,7 @@ void vpr_create_rr_graph(t_vpr_setup& vpr_setup, const t_arch& arch, int chan_wi det_routing_arch, vpr_setup.Segments, router_opts, + vpr_setup.CRROpts, arch.directs, &warnings, is_flat); @@ -1349,6 +1354,7 @@ void vpr_setup_vpr(t_options* Options, t_ap_opts* APOpts, t_router_opts* RouterOpts, t_analysis_opts* AnalysisOpts, + t_crr_opts* CRROpts, t_noc_opts* NocOpts, t_server_opts* ServerOpts, t_det_routing_arch& RoutingArch, @@ -1372,6 +1378,7 @@ void vpr_setup_vpr(t_options* Options, APOpts, RouterOpts, AnalysisOpts, + CRROpts, NocOpts, ServerOpts, RoutingArch, diff --git a/vpr/src/base/vpr_api.h b/vpr/src/base/vpr_api.h index 7147691ba73..90f156e2ca8 100644 --- a/vpr/src/base/vpr_api.h +++ b/vpr/src/base/vpr_api.h @@ -181,6 +181,7 @@ void vpr_setup_vpr(t_options* Options, t_ap_opts* APOpts, t_router_opts* RouterOpts, t_analysis_opts* AnalysisOpts, + t_crr_opts* CRROpts, t_noc_opts* NocOpts, t_server_opts* ServerOpts, t_det_routing_arch& RoutingArch, diff --git a/vpr/src/base/vpr_context.h b/vpr/src/base/vpr_context.h index 051e109b4b4..0ce96ac3aad 100644 --- a/vpr/src/base/vpr_context.h +++ b/vpr/src/base/vpr_context.h @@ -237,8 +237,7 @@ struct DeviceContext : public Context { rr_rc_data, rr_graph_builder.rr_segments(), rr_graph_builder.rr_switch(), - rr_graph_builder.node_in_edge_storage(), - rr_graph_builder.node_ptc_storage()}; + rr_graph_builder.node_in_edge_storage()}; ///@brief Track ids for each rr_node in the rr_graph. This is used by drawer for tileable routing resource graph std::map> rr_node_track_ids; diff --git a/vpr/src/base/vpr_types.h b/vpr/src/base/vpr_types.h index 4bd8b1557dd..8e889aa48fc 100644 --- a/vpr/src/base/vpr_types.h +++ b/vpr/src/base/vpr_types.h @@ -1410,6 +1410,17 @@ struct t_analysis_opts { bool skip_sync_clustering_and_routing_results; }; +/// Stores CRR specific options +struct t_crr_opts { + std::string sb_maps; + std::string sb_templates; + int crr_num_threads; + bool preserve_input_pin_connections; + bool preserve_output_pin_connections; + bool annotated_rr_graph; + bool remove_dangling_nodes; +}; + /// Stores NoC specific options, when supplied as an input by the user struct t_noc_opts { bool noc; ///& segment_ std::unique_ptr PlacementDelayModelCreator::create_delay_model(const t_placer_opts& placer_opts, const t_router_opts& router_opts, + const t_crr_opts& crr_opts, const Netlist<>& net_list, t_det_routing_arch& det_routing_arch, const std::vector& segment_inf, @@ -36,7 +37,13 @@ PlacementDelayModelCreator::create_delay_model(const t_placer_opts& placer_opts, t_chan_width chan_width = setup_chan_width(router_opts, chan_width_dist); - alloc_routing_structs(chan_width, router_opts, det_routing_arch, segment_inf, directs, is_flat); + alloc_routing_structs(chan_width, + router_opts, + crr_opts, + det_routing_arch, + segment_inf, + directs, + is_flat); const RouterLookahead* router_lookahead = get_cached_router_lookahead(det_routing_arch, router_opts.lookahead_type, diff --git a/vpr/src/place/delay_model/PlacementDelayModelCreator.h b/vpr/src/place/delay_model/PlacementDelayModelCreator.h index 6da4ac56f13..802d59ddcb2 100644 --- a/vpr/src/place/delay_model/PlacementDelayModelCreator.h +++ b/vpr/src/place/delay_model/PlacementDelayModelCreator.h @@ -8,6 +8,7 @@ class PlaceDelayModel; struct t_placer_opts; struct t_router_opts; +struct t_crr_opts; struct t_det_routing_arch; struct t_segment_inf; struct t_chan_width_dist; @@ -20,6 +21,7 @@ class PlacementDelayModelCreator { static std::unique_ptr create_delay_model(const t_placer_opts& placer_opts, const t_router_opts& router_opts, + const t_crr_opts& crr_opts, const Netlist<>& net_list, t_det_routing_arch& det_routing_arch, const std::vector& segment_inf, diff --git a/vpr/src/place/place.cpp b/vpr/src/place/place.cpp index 91777f45761..697f937799a 100644 --- a/vpr/src/place/place.cpp +++ b/vpr/src/place/place.cpp @@ -29,6 +29,7 @@ void print_clb_placement(const char* fname); void try_place(const Netlist<>& net_list, const t_placer_opts& placer_opts, const t_router_opts& router_opts, + const t_crr_opts& crr_opts, const t_analysis_opts& analysis_opts, const t_noc_opts& noc_opts, const t_chan_width_dist& chan_width_dist, @@ -83,6 +84,7 @@ void try_place(const Netlist<>& net_list, /*do this before the initial placement to avoid messing up the initial placement */ place_delay_model = PlacementDelayModelCreator::create_delay_model(placer_opts, router_opts, + crr_opts, net_list, det_routing_arch, segment_inf, diff --git a/vpr/src/place/place.h b/vpr/src/place/place.h index 37246502bca..109158d5b94 100644 --- a/vpr/src/place/place.h +++ b/vpr/src/place/place.h @@ -8,6 +8,7 @@ class FlatPlacementInfo; void try_place(const Netlist<>& net_list, const t_placer_opts& placer_opts, const t_router_opts& router_opts, + const t_crr_opts& crr_opts, const t_analysis_opts& analysis_opts, const t_noc_opts& noc_opts, const t_chan_width_dist& chan_width_dist, diff --git a/vpr/src/route/route.cpp b/vpr/src/route/route.cpp index 0696996cf85..8d4b5218836 100644 --- a/vpr/src/route/route.cpp +++ b/vpr/src/route/route.cpp @@ -17,6 +17,7 @@ bool route(const Netlist<>& net_list, int width_fac, const t_router_opts& router_opts, + const t_crr_opts& crr_opts, const t_analysis_opts& analysis_opts, t_det_routing_arch& det_routing_arch, std::vector& segment_inf, @@ -62,6 +63,7 @@ bool route(const Netlist<>& net_list, det_routing_arch, segment_inf, router_opts, + crr_opts, directs, &warning_count, is_flat); diff --git a/vpr/src/route/route.h b/vpr/src/route/route.h index 5d519228d0d..d3bf5f58f2e 100644 --- a/vpr/src/route/route.h +++ b/vpr/src/route/route.h @@ -20,6 +20,7 @@ bool route(const Netlist<>& net_list, int width_fac, const t_router_opts& router_opts, + const t_crr_opts& crr_opts, const t_analysis_opts& analysis_opts, t_det_routing_arch& det_routing_arch, std::vector& segment_inf, diff --git a/vpr/src/route/route_utils.cpp b/vpr/src/route/route_utils.cpp index 1d5118a39f8..a8e009a8434 100644 --- a/vpr/src/route/route_utils.cpp +++ b/vpr/src/route/route_utils.cpp @@ -493,6 +493,7 @@ vtr::vector>> set_net /** Wrapper for create_rr_graph() with extra checks */ void try_graph(int width_fac, const t_router_opts& router_opts, + const t_crr_opts& crr_opts, t_det_routing_arch& det_routing_arch, const std::vector& segment_inf, const t_chan_width_dist& chan_width_dist, @@ -525,6 +526,7 @@ void try_graph(int width_fac, det_routing_arch, segment_inf, router_opts, + crr_opts, directs, &warning_count, is_flat); diff --git a/vpr/src/route/route_utils.h b/vpr/src/route/route_utils.h index cc26d300042..ff0b4621dbd 100644 --- a/vpr/src/route/route_utils.h +++ b/vpr/src/route/route_utils.h @@ -149,6 +149,7 @@ vtr::vector>> set_net /** Wrapper for create_rr_graph() with extra checks */ void try_graph(int width_fac, const t_router_opts& router_opts, + const t_crr_opts& crr_opts, t_det_routing_arch& det_routing_arch, const std::vector& segment_inf, const t_chan_width_dist& chan_width_dist, diff --git a/vpr/src/route/router_delay_profiling.cpp b/vpr/src/route/router_delay_profiling.cpp index fcfa10335d7..208f6e7cf2a 100644 --- a/vpr/src/route/router_delay_profiling.cpp +++ b/vpr/src/route/router_delay_profiling.cpp @@ -238,6 +238,7 @@ vtr::vector calculate_all_path_delays_from_rr_node(RRNodeId src void alloc_routing_structs(const t_chan_width& chan_width, const t_router_opts& router_opts, + const t_crr_opts& crr_opts, t_det_routing_arch& det_routing_arch, const std::vector& segment_inf, const std::vector& directs, @@ -264,6 +265,7 @@ void alloc_routing_structs(const t_chan_width& chan_width, det_routing_arch, segment_inf, router_opts, + crr_opts, directs, &warnings, is_flat); diff --git a/vpr/src/route/router_delay_profiling.h b/vpr/src/route/router_delay_profiling.h index 2769a34c454..430382ab978 100644 --- a/vpr/src/route/router_delay_profiling.h +++ b/vpr/src/route/router_delay_profiling.h @@ -46,6 +46,7 @@ vtr::vector calculate_all_path_delays_from_rr_node(RRNodeId src void alloc_routing_structs(const t_chan_width& chan_width, const t_router_opts& router_opts, + const t_crr_opts& crr_opts, t_det_routing_arch& det_routing_arch, const std::vector& segment_inf, const std::vector& directs, diff --git a/vpr/src/route/router_lookahead/router_lookahead_map.cpp b/vpr/src/route/router_lookahead/router_lookahead_map.cpp index c7ea331f453..2cacbe14249 100644 --- a/vpr/src/route/router_lookahead/router_lookahead_map.cpp +++ b/vpr/src/route/router_lookahead/router_lookahead_map.cpp @@ -760,8 +760,7 @@ static void compute_tile_lookahead(std::unordered_map& segment_inf, const t_router_opts& router_opts, + const t_crr_opts& crr_opts, const std::vector& directs, int* Warnings, bool is_flat) { @@ -499,6 +502,7 @@ void create_rr_graph(e_graph_type graph_type, build_tileable_unidir_rr_graph(block_types, grid, nodes_per_chan, + crr_opts, det_routing_arch.switch_block_type, det_routing_arch.Fs, det_routing_arch.switch_block_subtype, @@ -1124,7 +1128,12 @@ static void build_rr_graph(e_graph_type graph_type, // Save the channel widths for the newly constructed graph device_ctx.chan_width = nodes_per_chan; - rr_graph_externals(segment_inf, segment_inf_x, segment_inf_y, segment_inf_z, wire_to_rr_ipin_switch, base_cost_type); + rr_graph_externals(segment_inf, + segment_inf_x, + segment_inf_y, + segment_inf_z, + wire_to_rr_ipin_switch, + base_cost_type); const VibDeviceGrid vib_grid; check_rr_graph(device_ctx.rr_graph, @@ -1667,6 +1676,16 @@ static std::function alloc_and_load_rr_graph(RRGraphBuilder } } + // If there are any interposer cuts, remove the edges and shorten the wires that cross interposer cut lines. + if (grid.has_interposer_cuts()) { + std::vector interposer_edges = get_interposer_cut_edges_for_removal(rr_graph, grid); + rr_graph_builder.remove_edges(interposer_edges); + + update_interposer_crossing_nodes_in_spatial_lookup_and_rr_graph_storage(rr_graph, grid, rr_graph_builder, sg_node_indices); + } + + // Add 2D scatter-gather link edges (the nodes have already been created at this point). + // These links are mostly used for interposer-crossing connections, but could also be used for other things. add_and_connect_non_3d_sg_links(rr_graph_builder, sg_links, sg_node_indices, chan_details_x, chan_details_y, num_seg_types_x, rr_edges_to_create); uniquify_edges(rr_edges_to_create); alloc_and_load_edges(rr_graph_builder, rr_edges_to_create); @@ -2118,6 +2137,11 @@ static void add_and_connect_non_3d_sg_links(RRGraphBuilder& rr_graph_builder, chan_loc.y, gather_chan_type, gather_wire.wire_switchpoint.wire); + // TODO: Some of the nodes that the scatter-gather patterns want to connect to have been cut because they were crossing the die + // For now we're ignoring those, but a proper fix should be investigated. + if (gather_node == RRNodeId::INVALID()) { + continue; + } // Record deferred edge creation (gather_node --> sg_node) non_3d_sg_rr_edges_to_create.emplace_back(gather_node, node_id, link.arch_wire_switch, false); } @@ -2135,6 +2159,12 @@ static void add_and_connect_non_3d_sg_links(RRGraphBuilder& rr_graph_builder, chan_loc.y, scatter_chan_type, scatter_wire.wire_switchpoint.wire); + + // TODO: Some of the nodes that the scatter-gather patterns want to connect to have been cut because they were crossing the die + // For now we're ignoring those, but a proper fix should be investigated. + if (scatter_node == RRNodeId::INVALID()) { + continue; + } // Determine which architecture switch this edge should use int switch_index = chan_details[chan_loc.x][chan_loc.y][scatter_wire.wire_switchpoint.wire].arch_wire_switch(); // Record deferred edge creation (sg_node --> scatter_node) diff --git a/vpr/src/route/rr_graph_generation/rr_graph.h b/vpr/src/route/rr_graph_generation/rr_graph.h index 4aca0d82b76..7729da867a8 100644 --- a/vpr/src/route/rr_graph_generation/rr_graph.h +++ b/vpr/src/route/rr_graph_generation/rr_graph.h @@ -11,13 +11,15 @@ #include "rr_graph_type.h" #include "clb2clb_directs.h" -/* Warnings about the routing graph that can be returned. - * This is to avoid output messages during a value sweep */ +/** @brief Warnings about the routing graph that can be returned. + * This is to avoid output messages during a value sweep. + * @note This enum is used as a bitmask and should be one-hot encoded. + */ enum { RR_GRAPH_NO_WARN = 0x00, - RR_GRAPH_WARN_FC_CLIPPED = 0x01, - RR_GRAPH_WARN_CHAN_X_WIDTH_CHANGED = 0x02, - RR_GRAPH_WARN_CHAN_Y_WIDTH_CHANGED = 0x03 + RR_GRAPH_WARN_FC_CLIPPED = 0x01 << 0, + RR_GRAPH_WARN_CHAN_X_WIDTH_CHANGED = 0x01 << 1, + RR_GRAPH_WARN_CHAN_Y_WIDTH_CHANGED = 0x01 << 2 }; void create_rr_graph(e_graph_type graph_type, @@ -27,6 +29,7 @@ void create_rr_graph(e_graph_type graph_type, t_det_routing_arch& det_routing_arch, const std::vector& segment_inf, const t_router_opts& router_opts, + const t_crr_opts& crr_opts, const std::vector& directs, int* Warnings, bool is_flat); diff --git a/vpr/src/route/rr_graph_generation/rr_graph_interposer.cpp b/vpr/src/route/rr_graph_generation/rr_graph_interposer.cpp new file mode 100644 index 00000000000..bdc40943acd --- /dev/null +++ b/vpr/src/route/rr_graph_generation/rr_graph_interposer.cpp @@ -0,0 +1,386 @@ +/** + * @file interposer_cut.h + * @brief This file implements functions can be used to generate RR Graphs for 2.5D architectures. See rr_graph_interposer.h for more details. + */ + +#include +#include +#include +#include +#include + +#include "device_grid.h" +#include "interposer_types.h" +#include "rr_graph_builder.h" +#include "rr_graph_fwd.h" +#include "rr_graph_view.h" +#include "rr_node_types.h" +#include "rr_spatial_lookup.h" +#include "vpr_error.h" +#include "vtr_assert.h" + +#include "rr_graph_interposer.h" + +// Static function declarations + +/** + * @brief Takes location of a source and a sink and determines whether it crosses cut_loc or not. + * For example, the interval (1, 4) is cut by 3, while it is not cut by 5 or 0. + */ +static bool should_cut(int src_loc, int sink_loc, int cut_loc); + +/** + * @brief Calculates the starting x point of node based on it's directionality. + */ +static short node_xstart(const RRGraphView& rr_graph, RRNodeId node); + +/** + * @brief Calculates the starting y point of node based on it's directionality. + */ +static short node_ystart(const RRGraphView& rr_graph, RRNodeId node); + +/** + * @brief Update a CHANY node's bounding box in RR Graph and Spatial Lookup entries. + * This function assumes that the channel node actually crosses the cut location and + * might not function correctly otherwise. + * + * This is a low level function, you should use cut_channel_node that wraps this up in a nicer API. + * + * @param node Node's ID + * @param x_low Node's lower x position + * @param y_low Node's lower y position + * @param x_high Node's higher x position + * @param y_high Node's higher y position + * @param layer Node's layer + * @param ptc_num Node's ptc_num + * @param cut_loc_y y location of horizontal interposer cut line + * @param node_direction Node's direction + * @param rr_graph_builder RR Graph builder for mutating the RR Graph + * @param spatial_lookup RR Graph spatial lookup + */ +static void cut_chan_y_node(RRNodeId node, + int x_low, + int y_low, + int x_high, + int y_high, + int layer, + int ptc_num, + int cut_loc_y, + Direction node_direction, + RRGraphBuilder& rr_graph_builder, + RRSpatialLookup& spatial_lookup); + +/** + * @brief Update a CHANX node's bounding box in RRGraph and SpatialLookup entries. + * This function assumes that the channel node actually crosses the cut location and + * might not function correctly otherwise. + * + * This is a low level function, you should use cut_channel_node that wraps this up in a nicer API. + * + * @param node Node's ID + * @param x_low Node's lower x position + * @param y_low Node's lower y position + * @param x_high Node's higher x position + * @param y_high Node's higher y position + * @param layer Node's layer + * @param ptc_num Node's ptc_num + * @param cut_loc_x x location of vertical interposer cut line + * @param node_direction Node's direction + * @param rr_graph_builder RR Graph builder for mutating the RR Graph + * @param spatial_lookup RR Graph spatial lookup + */ +static void cut_chan_x_node(RRNodeId node, + int x_low, + int y_low, + int x_high, + int y_high, + int layer, + int ptc_num, + int cut_loc_x, + Direction node_direction, + RRGraphBuilder& rr_graph_builder, + RRSpatialLookup& spatial_lookup); + +/** + * @brief Update a CHANX or CHANY node's bounding box in RRGraph and SpatialLookup entries if it crosses cut_loc + * + * @param node Channel segment RR graph node that might cross the interposer cut line + * @param cut_loc location of vertical interposer cut line + * @param interposer_cut_type Type of the interposer cut line (Horizontal or vertical) + * @param sg_node_indices Sorted list of scatter-gather node IDs. We do not want to cut these nodes as they're allowed to cross an interposer cut line. + */ +static void cut_channel_node(RRNodeId node, + int cut_loc, + e_interposer_cut_type interposer_cut_type, + const RRGraphView& rr_graph, + RRGraphBuilder& rr_graph_builder, + RRSpatialLookup& spatial_lookup, + const std::vector>& sg_node_indices); + +// Function definitions + +static bool should_cut(int src_loc, int sink_loc, int cut_loc) { + int src_delta = src_loc - cut_loc; + int sink_delta = sink_loc - cut_loc; + + // Same sign means that both sink and source are on the same side of this cut + if ((src_delta <= 0 && sink_delta <= 0) || (src_delta > 0 && sink_delta > 0)) { + return false; + } else { + return true; + } +} + +static short node_xstart(const RRGraphView& rr_graph, RRNodeId node) { + e_rr_type node_type = rr_graph.node_type(node); + Direction node_direction = rr_graph.node_direction(node); + short node_xlow = rr_graph.node_xlow(node); + short node_xhigh = rr_graph.node_xhigh(node); + + // Return early for OPIN and IPIN types (Some BIDIR pins would trigger the assertion below) + if (is_pin(node_type)) { + VTR_ASSERT(node_xlow == node_xhigh); + return node_xlow; + } + + switch (node_direction) { + case Direction::DEC: + return node_xhigh; + + case Direction::INC: + return node_xlow; + + case Direction::NONE: + VTR_ASSERT(node_xlow == node_xhigh); + return (node_xlow); + + case Direction::BIDIR: + VPR_FATAL_ERROR(VPR_ERROR_ROUTE, "Bidir node has no starting point."); + break; + + default: + VPR_FATAL_ERROR(VPR_ERROR_ROUTE, "Invalid RR node direction."); + break; + } +} + +static short node_ystart(const RRGraphView& rr_graph, RRNodeId node) { + e_rr_type node_type = rr_graph.node_type(node); + Direction node_direction = rr_graph.node_direction(node); + short node_ylow = rr_graph.node_ylow(node); + short node_yhigh = rr_graph.node_yhigh(node); + + // Return early for OPIN and IPIN types (Some BIDIR pins would trigger the assertion below) + if (is_pin(node_type)) { + return node_ylow; + } + + switch (node_direction) { + case Direction::DEC: + return node_yhigh; + + case Direction::INC: + return node_ylow; + + case Direction::NONE: + VTR_ASSERT(node_ylow == node_yhigh); + return (node_ylow); + + case Direction::BIDIR: + VPR_FATAL_ERROR(VPR_ERROR_ROUTE, "Bidir node has no starting point."); + break; + + default: + VPR_FATAL_ERROR(VPR_ERROR_ROUTE, "Invalid RR node direction."); + break; + } +} + +std::vector get_interposer_cut_edges_for_removal(const RRGraphView& rr_graph, const DeviceGrid& grid) { + std::vector edges_to_be_removed; + + // Loop over all RREdgeIds and add ones that cross a cutline to a list for removal + for (RREdgeId edge_id : rr_graph.all_edges()) { + RRNodeId src_node = rr_graph.edge_src_node(edge_id); + RRNodeId sink_node = rr_graph.edge_sink_node(edge_id); + + // TODO: ignoring ChanZ nodes for now + if (rr_graph.node_type(src_node) == e_rr_type::CHANZ || rr_graph.node_type(sink_node) == e_rr_type::CHANZ) { + continue; + } + + // Currently 3D connection block edges cross layers. We don't want to cut these edges. + if (rr_graph.node_layer_low(src_node) != rr_graph.node_layer_low(sink_node)) { + continue; + } + VTR_ASSERT(rr_graph.node_layer_low(src_node) == rr_graph.node_layer_high(src_node)); + VTR_ASSERT(rr_graph.node_layer_low(sink_node) == rr_graph.node_layer_high(sink_node)); + + int layer = rr_graph.node_layer_low(src_node); + + // If the start of the source node and the start of the sink node of this edge are on different sides of an interposer cut line + // the edge starts on a piece of wire that would be cut by the interposed and should be added to the removal list. + for (int cut_loc_y : grid.get_horizontal_interposer_cuts()[layer]) { + int src_start_loc_y = node_ystart(rr_graph, src_node); + int sink_start_loc_y = node_ystart(rr_graph, sink_node); + + if (should_cut(src_start_loc_y, sink_start_loc_y, cut_loc_y)) { + edges_to_be_removed.push_back(edge_id); + } + } + + for (int cut_loc_x : grid.get_vertical_interposer_cuts()[layer]) { + int src_start_loc_x = node_xstart(rr_graph, src_node); + int sink_start_loc_x = node_xstart(rr_graph, sink_node); + + if (should_cut(src_start_loc_x, sink_start_loc_x, cut_loc_x)) { + edges_to_be_removed.push_back(edge_id); + } + } + } + + return edges_to_be_removed; +} + +static void cut_chan_y_node(RRNodeId node, + int x_low, + int y_low, + int x_high, + int y_high, + int layer, + int ptc_num, + int cut_loc_y, + Direction node_direction, + RRGraphBuilder& rr_graph_builder, + RRSpatialLookup& spatial_lookup) { + + VTR_ASSERT_SAFE(should_cut(y_low, y_high, cut_loc_y)); + + if (node_direction == Direction::INC) { + // This node should not exist at locations above cut_loc_y + rr_graph_builder.set_node_coordinates(node, x_low, y_low, x_high, cut_loc_y); + + // Do a loop from cut_loc_y to y_high and remove node from spatial lookup + for (int y_loc = cut_loc_y + 1; y_loc <= y_high; y_loc++) { + spatial_lookup.remove_node(node, layer, x_low, y_loc, e_rr_type::CHANY, ptc_num); + } + } else if (node_direction == Direction::DEC) { + // This node should not exist at locations below cut_loc_y (inclusive) + rr_graph_builder.set_node_coordinates(node, x_low, cut_loc_y + 1, x_high, y_high); + + // Do a loop from y_low to cut_loc_y and remove node from spatial lookup + for (int y_loc = y_low; y_loc <= cut_loc_y; y_loc++) { + spatial_lookup.remove_node(node, layer, x_low, y_loc, e_rr_type::CHANY, ptc_num); + } + } else { + VTR_ASSERT_MSG(false, "Bidirectional routing is not supported for interposer architectures."); + } +} + +static void cut_chan_x_node(RRNodeId node, + int x_low, + int y_low, + int x_high, + int y_high, + int layer, + int ptc_num, + int cut_loc_x, + Direction node_direction, + RRGraphBuilder& rr_graph_builder, + RRSpatialLookup& spatial_lookup) { + VTR_ASSERT_SAFE(should_cut(x_low, x_high, cut_loc_x)); + + if (node_direction == Direction::INC) { + // This node should not exist at locations to the right of cut_loc_x + rr_graph_builder.set_node_coordinates(node, x_low, y_low, cut_loc_x, y_high); + + // Do a loop from cut_loc_x to x_high and remove node from spatial lookup + for (int x_loc = cut_loc_x + 1; x_loc <= x_high; x_loc++) { + spatial_lookup.remove_node(node, layer, x_loc, y_low, e_rr_type::CHANX, ptc_num); + } + } else if (node_direction == Direction::DEC) { + // This node should not exist at locations to the left of cut_loc_x (inclusive) + rr_graph_builder.set_node_coordinates(node, cut_loc_x + 1, y_low, x_high, y_high); + + // Do a loop from x_low to cut_loc_x - 1 and remove node from spatial lookup + for (int x_loc = x_low; x_loc <= cut_loc_x; x_loc++) { + spatial_lookup.remove_node(node, layer, x_loc, y_low, e_rr_type::CHANX, ptc_num); + } + } else { + VTR_ASSERT_MSG(false, "Bidirectional routing is not supported for interposer architectures."); + } +} + +static void cut_channel_node(RRNodeId node, + int cut_loc, + e_interposer_cut_type interposer_cut_type, + const RRGraphView& rr_graph, + RRGraphBuilder& rr_graph_builder, + RRSpatialLookup& spatial_lookup, + const std::vector>& sg_node_indices) { + constexpr auto node_indice_compare = [](RRNodeId l, RRNodeId r) noexcept { return size_t(l) < size_t(r); }; + bool is_sg_node = std::ranges::binary_search(std::views::keys(sg_node_indices), node, node_indice_compare); + if (is_sg_node) { + return; + } + + int x_high = rr_graph.node_xhigh(node); + int x_low = rr_graph.node_xlow(node); + + int y_high = rr_graph.node_yhigh(node); + int y_low = rr_graph.node_ylow(node); + + int layer = rr_graph.node_layer_low(node); + int ptc_num = rr_graph.node_ptc_num(node); + Direction node_direction = rr_graph.node_direction(node); + e_rr_type node_type = rr_graph.node_type(node); + + if (interposer_cut_type == e_interposer_cut_type::HORZ) { + VTR_ASSERT(node_type == e_rr_type::CHANY); + if (!should_cut(y_low, y_high, cut_loc)) { + return; + } + } else if (interposer_cut_type == e_interposer_cut_type::VERT) { + VTR_ASSERT(node_type == e_rr_type::CHANX); + if (!should_cut(x_low, x_high, cut_loc)) { + return; + } + } + + if (interposer_cut_type == e_interposer_cut_type::HORZ) { + cut_chan_y_node(node, x_low, y_low, x_high, y_high, layer, ptc_num, cut_loc, node_direction, rr_graph_builder, spatial_lookup); + } else if (interposer_cut_type == e_interposer_cut_type::VERT) { + cut_chan_x_node(node, x_low, y_low, x_high, y_high, layer, ptc_num, cut_loc, node_direction, rr_graph_builder, spatial_lookup); + } +} + +void update_interposer_crossing_nodes_in_spatial_lookup_and_rr_graph_storage(const RRGraphView& rr_graph, + const DeviceGrid& grid, + RRGraphBuilder& rr_graph_builder, + const std::vector>& sg_node_indices) { + + VTR_ASSERT(std::is_sorted(sg_node_indices.begin(), sg_node_indices.end())); + + RRSpatialLookup& spatial_lookup = rr_graph_builder.node_lookup(); + for (size_t layer = 0; layer < grid.get_num_layers(); layer++) { + for (int cut_loc_y : grid.get_horizontal_interposer_cuts()[layer]) { + for (size_t x_loc = 0; x_loc < grid.width(); x_loc++) { + std::vector channel_nodes = spatial_lookup.find_channel_nodes(layer, x_loc, cut_loc_y, e_rr_type::CHANY); + for (RRNodeId node : channel_nodes) { + cut_channel_node(node, cut_loc_y, e_interposer_cut_type::HORZ, + rr_graph, rr_graph_builder, spatial_lookup, sg_node_indices); + } + } + } + + for (int cut_loc_x : grid.get_vertical_interposer_cuts()[layer]) { + for (size_t y_loc = 0; y_loc < grid.height(); y_loc++) { + std::vector channel_nodes = spatial_lookup.find_channel_nodes(layer, cut_loc_x, y_loc, e_rr_type::CHANX); + for (RRNodeId node : channel_nodes) { + cut_channel_node(node, cut_loc_x, e_interposer_cut_type::VERT, + rr_graph, rr_graph_builder, spatial_lookup, sg_node_indices); + } + } + } + } +} diff --git a/vpr/src/route/rr_graph_generation/rr_graph_interposer.h b/vpr/src/route/rr_graph_generation/rr_graph_interposer.h new file mode 100644 index 00000000000..ff1ad0d98ec --- /dev/null +++ b/vpr/src/route/rr_graph_generation/rr_graph_interposer.h @@ -0,0 +1,86 @@ +#pragma once + +/** + * @file interposer_cut.h + * @brief This file implements functions that: + * + * (1) Get a list of all edges that cross an interposer cut for removal. Edges whose source and sink nodes start on opposite + * sides of an interposer cut line are considered to cross the cut line and will be removed. + * + * (2) Makes the channel nodes that cross an interposer cut shorter to have them not cross the interposer anymore + * + * Using these two functions and combined with 2D scatter-gather patterns, you can model and implement 2.5D FPGA RR Graphs. + * + * Below you can see a diagram of a device with a vertical interposer cut at x = 2 (Shown with X) and a horizontal interposer cut at y = 2 (shown with =) + * X(x,y) denotes the ChanX at location (x,y) and Y(x,y) denotes the ChanX at location (x,y). Consistent with VPR's coordinate system, each tile "owns" + * the channels above and to the right of it, therefore interposer cut lines will leave the channels on the side of the tile it belongs to. + * + * + * X(0,3) X(1,3) X X(2,3) X(3,3) X(4,3) + * _________ _________ X _________ _________ _________ + * | | | | X | | | | | | + * | | Y(0,3) | |Y(1,3)X | | Y(2,3) | | Y(3,3) | | Y(4,3) + * | | | | X | | | | | | + * --------- --------- X --------- --------- --------- + * X + * X(0,2) X(1,2) X X(2,2) X(3,2) X(4,2) + * X + * _________ _________ X _________ _________ _________ + * | | | | X | | | | | | + * | | Y(0,2) | |Y(1,2)X | | Y(2,2) | | Y(3,2) | | Y(4,2) + * | | | | X | | | | | | + * --------- --------- X --------- --------- --------- + * ===================================X================================================= + * X(0,1) X(1,1) X X(2,1) X(3,1) X(4,1) + * X + * _________ _________ X _________ _________ _________ + * | | | | X | | | | | | + * | | Y(0,1) | |Y(1,1)X | | Y(2,1) | | Y(3,1) | | Y(4,1) + * | | | | X | | | | | | + * --------- --------- X --------- --------- --------- + * X + * X(0,0) X(1,0) X X(2,0) X(3,0) X(4,0) + * X + * _________ _________ X _________ _________ _________ + * | | | | X | | | | | | + * | | Y(0,0) | |Y(1,0)X | | Y(2,0) | | Y(3,0) | | Y(4,0) + * | | | | X | | | | | | + * --------- --------- X --------- --------- --------- + * X + * X + * + * + */ + +#include +#include "rr_graph_fwd.h" +#include "rr_graph_view.h" +#include "device_grid.h" + +/** + * @brief Goes through all edges in the RR Graph and returns a list of the edges that cross an interposer cut line. + * Edges whose source and sink nodes start on opposite sides of an interposer cut line are considered to cross the + * cut line and will be removed. + * + * @return std::vector List of all edges that cross an interposer cut line. + */ +std::vector get_interposer_cut_edges_for_removal(const RRGraphView& rr_graph, const DeviceGrid& grid); + +/** + * @brief Shortens the channel nodes that cross an interposer cut line. + * + * @details When a wire crosses an interposer cut line, it is shortened to instead end at the interposed cut line + * unless it is in the list of sg_node_indices. The wire's drive point and the wire up to the interposer cut is + * retained and will not be modified. "Shortening a wire" in this context means modifying the information in the + * RR Graph itself (Changing the bounding box of the corresponding RR Graph node) and the spatial lookup + * (Making sure there is no reference to the shortened wire in locations it doesn't exist at anymore.) + * + * @param rr_graph RRGraphView, used to read the RR Graph. + * @param grid Device grid, used to access interposer cut locations. + * @param rr_graph_builder RRGraphBuilder, to modify the RRGraph. + * @param sg_node_indices list of scatter-gather node IDs. We do not want to cut these nodes as they're allowed to cross an interposer cut line. + */ +void update_interposer_crossing_nodes_in_spatial_lookup_and_rr_graph_storage(const RRGraphView& rr_graph, + const DeviceGrid& grid, + RRGraphBuilder& rr_graph_builder, + const std::vector>& sg_node_indices); diff --git a/vpr/src/route/rr_graph_generation/rr_node_indices.cpp b/vpr/src/route/rr_graph_generation/rr_node_indices.cpp index 3932fefd1e6..78970d42084 100644 --- a/vpr/src/route/rr_graph_generation/rr_node_indices.cpp +++ b/vpr/src/route/rr_graph_generation/rr_node_indices.cpp @@ -7,6 +7,8 @@ #include "physical_types.h" #include "physical_types_util.h" #include "rr_graph2.h" +#include "rr_graph_fwd.h" +#include "rr_node_types.h" #include "vpr_utils.h" /** @@ -84,7 +86,7 @@ static void load_block_rr_indices(RRGraphBuilder& rr_graph_builder, const DeviceGrid& grid, int* index) { // Walk through the grid assigning indices to SOURCE/SINK IPIN/OPIN - for (const t_physical_tile_loc grid_loc : grid.all_locations()) { + for (const t_physical_tile_loc& grid_loc : grid.all_locations()) { //Process each block from its root location if (grid.is_root_location(grid_loc)) { t_physical_tile_type_ptr physical_type = grid.get_physical_type(grid_loc); @@ -456,7 +458,7 @@ void alloc_and_load_intra_cluster_rr_node_indices(RRGraphBuilder& rr_graph_build const vtr::vector>& pin_chains_num, int* index) { - for (const t_physical_tile_loc grid_loc : grid.all_locations()) { + for (const t_physical_tile_loc& grid_loc : grid.all_locations()) { // Process each block from its root location if (grid.is_root_location(grid_loc)) { @@ -496,142 +498,133 @@ bool verify_rr_node_indices(const DeviceGrid& grid, const t_rr_graph_storage& rr_nodes, bool is_flat) { std::unordered_map rr_node_counts; + for (const t_physical_tile_loc& tile_loc : grid.all_locations()) { + for (e_rr_type rr_type : RR_TYPES) { + // Get the list of nodes at a specific location (x, y) + std::vector nodes_from_lookup; + if (rr_type == e_rr_type::CHANX || rr_type == e_rr_type::CHANY || rr_type == e_rr_type::CHANZ) { + nodes_from_lookup = rr_graph.node_lookup().find_channel_nodes(tile_loc.layer_num, tile_loc.x, tile_loc.y, rr_type); + } else { + nodes_from_lookup = rr_graph.node_lookup().find_grid_nodes_at_all_sides(tile_loc.layer_num, tile_loc.x, tile_loc.y, rr_type); + } + + for (RRNodeId inode : nodes_from_lookup) { + rr_node_counts[inode]++; + + if (rr_graph.node_type(inode) != rr_type) { + VPR_ERROR(VPR_ERROR_ROUTE, "RR node type does not match between rr_nodes and rr_node_indices (%s/%s): %s", + rr_node_typename[rr_graph.node_type(inode)], + rr_node_typename[rr_type], + describe_rr_node(rr_graph, grid, rr_indexed_data, inode, is_flat).c_str()); + } + + if (tile_loc.layer_num < rr_graph.node_layer_low(inode) && tile_loc.layer_num > rr_graph.node_layer_high(inode)) { + VPR_ERROR(VPR_ERROR_ROUTE, "RR node layer does not match between rr_nodes and rr_node_indices (%s/%s): %s", + rr_node_typename[rr_graph.node_type(inode)], + rr_node_typename[rr_type], + describe_rr_node(rr_graph, grid, rr_indexed_data, inode, is_flat).c_str()); + } + + if (rr_graph.node_type(inode) == e_rr_type::CHANX) { + VTR_ASSERT_MSG(rr_graph.node_ylow(inode) == rr_graph.node_yhigh(inode), "CHANX should be horizontal"); + if (tile_loc.y != rr_graph.node_ylow(inode)) { + VPR_ERROR(VPR_ERROR_ROUTE, "RR node y position does not agree between rr_nodes (%d) and rr_node_indices (%d): %s", + rr_graph.node_ylow(inode), + tile_loc.y, + describe_rr_node(rr_graph, grid, rr_indexed_data, inode, is_flat).c_str()); + } + + if (!rr_graph.x_in_node_range(tile_loc.x, inode)) { + VPR_ERROR(VPR_ERROR_ROUTE, "RR node x positions do not agree between rr_nodes (%d <-> %d) and rr_node_indices (%d): %s", + rr_graph.node_xlow(inode), + rr_graph.node_xlow(inode), + tile_loc.x, + describe_rr_node(rr_graph, grid, rr_indexed_data, inode, is_flat).c_str()); + } + } else if (rr_graph.node_type(inode) == e_rr_type::CHANY) { + VTR_ASSERT_MSG(rr_graph.node_xlow(inode) == rr_graph.node_xhigh(inode), "CHANY should be vertical"); + + if (tile_loc.x != rr_graph.node_xlow(inode)) { + VPR_ERROR(VPR_ERROR_ROUTE, "RR node x position does not agree between rr_nodes (%d) and rr_node_indices (%d): %s", + rr_graph.node_xlow(inode), + tile_loc.x, + describe_rr_node(rr_graph, grid, rr_indexed_data, inode, is_flat).c_str()); + } + + if (!rr_graph.y_in_node_range(tile_loc.y, inode)) { + VPR_ERROR(VPR_ERROR_ROUTE, "RR node y positions do not agree between rr_nodes (%d <-> %d) and rr_node_indices (%d): %s", + rr_graph.node_ylow(inode), + rr_graph.node_ylow(inode), + tile_loc.y, + describe_rr_node(rr_graph, grid, rr_indexed_data, inode, is_flat).c_str()); + } + } else if (rr_graph.node_type(inode) == e_rr_type::CHANZ) { + VTR_ASSERT_MSG(rr_graph.node_xlow(inode) == rr_graph.node_xhigh(inode), "CHANZ should move only along layers"); + VTR_ASSERT_MSG(rr_graph.node_ylow(inode) == rr_graph.node_yhigh(inode), "CHANZ should move only along layers"); + + if (tile_loc.x != rr_graph.node_xlow(inode)) { + VPR_ERROR(VPR_ERROR_ROUTE, "RR node x position does not agree between rr_nodes (%d) and rr_node_indices (%d): %s", + rr_graph.node_xlow(inode), + tile_loc.x, + describe_rr_node(rr_graph, grid, rr_indexed_data, inode, is_flat).c_str()); + } - int width = grid.width(); - int height = grid.height(); - int layer = grid.get_num_layers(); - - for (int l = 0; l < layer; ++l) { - for (int x = 0; x < width; ++x) { - for (int y = 0; y < height; ++y) { - for (e_rr_type rr_type : RR_TYPES) { - // Get the list of nodes at a specific location (x, y) - std::vector nodes_from_lookup; - if (rr_type == e_rr_type::CHANX || rr_type == e_rr_type::CHANY || rr_type == e_rr_type::CHANZ) { - nodes_from_lookup = rr_graph.node_lookup().find_channel_nodes(l, x, y, rr_type); - } else { - nodes_from_lookup = rr_graph.node_lookup().find_grid_nodes_at_all_sides(l, x, y, rr_type); + if (tile_loc.y != rr_graph.node_ylow(inode)) { + VPR_ERROR(VPR_ERROR_ROUTE, "RR node y position does not agree between rr_nodes (%d) and rr_node_indices (%d): %s", + rr_graph.node_xlow(inode), + tile_loc.y, + describe_rr_node(rr_graph, grid, rr_indexed_data, inode, is_flat).c_str()); } - for (RRNodeId inode : nodes_from_lookup) { - rr_node_counts[inode]++; - - if (rr_graph.node_type(inode) != rr_type) { - VPR_ERROR(VPR_ERROR_ROUTE, "RR node type does not match between rr_nodes and rr_node_indices (%s/%s): %s", - rr_node_typename[rr_graph.node_type(inode)], - rr_node_typename[rr_type], - describe_rr_node(rr_graph, grid, rr_indexed_data, inode, is_flat).c_str()); - } - - if (l < rr_graph.node_layer_low(inode) && l > rr_graph.node_layer_high(inode)) { - VPR_ERROR(VPR_ERROR_ROUTE, "RR node layer does not match between rr_nodes and rr_node_indices (%s/%s): %s", - rr_node_typename[rr_graph.node_type(inode)], - rr_node_typename[rr_type], - describe_rr_node(rr_graph, grid, rr_indexed_data, inode, is_flat).c_str()); - } - - if (rr_graph.node_type(inode) == e_rr_type::CHANX) { - VTR_ASSERT_MSG(rr_graph.node_ylow(inode) == rr_graph.node_yhigh(inode), "CHANX should be horizontal"); - if (y != rr_graph.node_ylow(inode)) { - VPR_ERROR(VPR_ERROR_ROUTE, "RR node y position does not agree between rr_nodes (%d) and rr_node_indices (%d): %s", - rr_graph.node_ylow(inode), - y, - describe_rr_node(rr_graph, grid, rr_indexed_data, inode, is_flat).c_str()); - } - - if (!rr_graph.x_in_node_range(x, inode)) { - VPR_ERROR(VPR_ERROR_ROUTE, "RR node x positions do not agree between rr_nodes (%d <-> %d) and rr_node_indices (%d): %s", - rr_graph.node_xlow(inode), - rr_graph.node_xlow(inode), - x, - describe_rr_node(rr_graph, grid, rr_indexed_data, inode, is_flat).c_str()); - } - } else if (rr_graph.node_type(inode) == e_rr_type::CHANY) { - VTR_ASSERT_MSG(rr_graph.node_xlow(inode) == rr_graph.node_xhigh(inode), "CHANY should be vertical"); - - if (x != rr_graph.node_xlow(inode)) { - VPR_ERROR(VPR_ERROR_ROUTE, "RR node x position does not agree between rr_nodes (%d) and rr_node_indices (%d): %s", - rr_graph.node_xlow(inode), - x, - describe_rr_node(rr_graph, grid, rr_indexed_data, inode, is_flat).c_str()); - } - - if (!rr_graph.y_in_node_range(y, inode)) { - VPR_ERROR(VPR_ERROR_ROUTE, "RR node y positions do not agree between rr_nodes (%d <-> %d) and rr_node_indices (%d): %s", - rr_graph.node_ylow(inode), - rr_graph.node_ylow(inode), - y, - describe_rr_node(rr_graph, grid, rr_indexed_data, inode, is_flat).c_str()); - } - } else if (rr_graph.node_type(inode) == e_rr_type::CHANZ) { - VTR_ASSERT_MSG(rr_graph.node_xlow(inode) == rr_graph.node_xhigh(inode), "CHANZ should move only along layers"); - VTR_ASSERT_MSG(rr_graph.node_ylow(inode) == rr_graph.node_yhigh(inode), "CHANZ should move only along layers"); - - if (x != rr_graph.node_xlow(inode)) { - VPR_ERROR(VPR_ERROR_ROUTE, "RR node x position does not agree between rr_nodes (%d) and rr_node_indices (%d): %s", - rr_graph.node_xlow(inode), - x, - describe_rr_node(rr_graph, grid, rr_indexed_data, inode, is_flat).c_str()); - } - - if (y != rr_graph.node_ylow(inode)) { - VPR_ERROR(VPR_ERROR_ROUTE, "RR node y position does not agree between rr_nodes (%d) and rr_node_indices (%d): %s", - rr_graph.node_xlow(inode), - y, - describe_rr_node(rr_graph, grid, rr_indexed_data, inode, is_flat).c_str()); - } - - } else if (rr_graph.node_type(inode) == e_rr_type::SOURCE || rr_graph.node_type(inode) == e_rr_type::SINK || rr_graph.node_type(inode) == e_rr_type::MUX) { - // Sources have co-ordinates covering the entire block they are in, but not sinks - if (!rr_graph.x_in_node_range(x, inode)) { - VPR_ERROR(VPR_ERROR_ROUTE, "RR node x positions do not agree between rr_nodes (%d <-> %d) and rr_node_indices (%d): %s", - rr_graph.node_xlow(inode), - rr_graph.node_xlow(inode), - x, - describe_rr_node(rr_graph, grid, rr_indexed_data, inode, is_flat).c_str()); - } - - if (!rr_graph.y_in_node_range(y, inode)) { - VPR_ERROR(VPR_ERROR_ROUTE, "RR node y positions do not agree between rr_nodes (%d <-> %d) and rr_node_indices (%d): %s", - rr_graph.node_ylow(inode), - rr_graph.node_ylow(inode), - y, - describe_rr_node(rr_graph, grid, rr_indexed_data, inode, is_flat).c_str()); - } - } else { - VTR_ASSERT(rr_graph.node_type(inode) == e_rr_type::IPIN || rr_graph.node_type(inode) == e_rr_type::OPIN); - /* As we allow a pin to be indexable on multiple sides, - * This check code should be invalid - * if (rr_node.xlow() != x) { - * VPR_ERROR(VPR_ERROR_ROUTE, "RR node xlow does not match between rr_nodes and rr_node_indices (%d/%d): %s", - * rr_node.xlow(), - * x, - * describe_rr_node(rr_graph, grid, rr_indexed_data, inode).c_str()); - * } - * - * if (rr_node.ylow() != y) { - * VPR_ERROR(VPR_ERROR_ROUTE, "RR node ylow does not match between rr_nodes and rr_node_indices (%d/%d): %s", - * rr_node.ylow(), - * y, - * describe_rr_node(rr_graph, grid, rr_indexed_data, inode).c_str()); - * } - */ - } - - if (rr_type == e_rr_type::IPIN || rr_type == e_rr_type::OPIN) { - /* As we allow a pin to be indexable on multiple sides, - * This check code should be invalid - * if (rr_node.side() != side) { - * VPR_ERROR(VPR_ERROR_ROUTE, "RR node xlow does not match between rr_nodes and rr_node_indices (%s/%s): %s", - * TOTAL_2D_SIDE_STRINGS[rr_node.side()], - * TOTAL_2D_SIDE_STRINGS[side], - * describe_rr_node(rr_graph, grid, rr_indexed_data, inode).c_str()); - * } else { - * VTR_ASSERT(rr_node.side() == side); - * } - */ - } + } else if (rr_graph.node_type(inode) == e_rr_type::SOURCE || rr_graph.node_type(inode) == e_rr_type::SINK || rr_graph.node_type(inode) == e_rr_type::MUX) { + // Sources have co-ordinates covering the entire block they are in, but not sinks + if (!rr_graph.x_in_node_range(tile_loc.x, inode)) { + VPR_ERROR(VPR_ERROR_ROUTE, "RR node x positions do not agree between rr_nodes (%d <-> %d) and rr_node_indices (%d): %s", + rr_graph.node_xlow(inode), + rr_graph.node_xlow(inode), + tile_loc.x, + describe_rr_node(rr_graph, grid, rr_indexed_data, inode, is_flat).c_str()); } + + if (!rr_graph.y_in_node_range(tile_loc.y, inode)) { + VPR_ERROR(VPR_ERROR_ROUTE, "RR node y positions do not agree between rr_nodes (%d <-> %d) and rr_node_indices (%d): %s", + rr_graph.node_ylow(inode), + rr_graph.node_ylow(inode), + tile_loc.y, + describe_rr_node(rr_graph, grid, rr_indexed_data, inode, is_flat).c_str()); + } + } else { + VTR_ASSERT(rr_graph.node_type(inode) == e_rr_type::IPIN || rr_graph.node_type(inode) == e_rr_type::OPIN); + /* As we allow a pin to be indexable on multiple sides, + * This check code should be invalid + * if (rr_node.xlow() != x) { + * VPR_ERROR(VPR_ERROR_ROUTE, "RR node xlow does not match between rr_nodes and rr_node_indices (%d/%d): %s", + * rr_node.xlow(), + * x, + * describe_rr_node(rr_graph, grid, rr_indexed_data, inode).c_str()); + * } + * + * if (rr_node.ylow() != y) { + * VPR_ERROR(VPR_ERROR_ROUTE, "RR node ylow does not match between rr_nodes and rr_node_indices (%d/%d): %s", + * rr_node.ylow(), + * y, + * describe_rr_node(rr_graph, grid, rr_indexed_data, inode).c_str()); + * } + */ + } + + if (rr_type == e_rr_type::IPIN || rr_type == e_rr_type::OPIN) { + /* As we allow a pin to be indexable on multiple sides, + * This check code should be invalid + * if (rr_node.side() != side) { + * VPR_ERROR(VPR_ERROR_ROUTE, "RR node xlow does not match between rr_nodes and rr_node_indices (%s/%s): %s", + * TOTAL_2D_SIDE_STRINGS[rr_node.side()], + * TOTAL_2D_SIDE_STRINGS[side], + * describe_rr_node(rr_graph, grid, rr_indexed_data, inode).c_str()); + * } else { + * VTR_ASSERT(rr_node.side() == side); + * } + */ } } } @@ -648,6 +641,7 @@ static void check_rr_node_counts(const std::unordered_map& rr_nod const DeviceGrid& grid, const vtr::vector& rr_indexed_data, bool is_flat) { + if (rr_node_counts.size() != rr_nodes.size()) { VPR_ERROR(VPR_ERROR_ROUTE, "Mismatch in number of unique RR nodes in rr_nodes (%zu) and rr_node_indices (%zu)", rr_nodes.size(), diff --git a/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/crr_common.h b/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/crr_common.h new file mode 100644 index 00000000000..572c72672b2 --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/crr_common.h @@ -0,0 +1,351 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // For getrusage on Unix/Linux + +#include "rr_graph_fwd.h" +#include "rr_node_types.h" +namespace crrgenerator { + +// Forward declarations +class SwitchBlockManager; +class NodeLookupManager; +class ConnectionBuilder; +class DataFrameProcessor; + +// Type aliases for convenience +using SwitchId = int32_t; +using SegmentId = int32_t; +using Coordinate = int32_t; +using DelayValue = float; + +// Constants +// Number of empty rows and columns in the Excel file +constexpr int NUM_EMPTY_ROWS = 5; +constexpr int NUM_EMPTY_COLS = 4; + +constexpr SwitchId DELAY_0_ID = 1; +constexpr int DEFAULT_SWITCH_DELAY_MIN = 10000; + +// Node types +enum class NodeType { SOURCE, + SINK, + IPIN, + OPIN, + CHANX, + CHANY, + INVALID }; + +// Direction types +enum class Direction { INC_DIR, + DEC_DIR }; + +// Side types +enum class Side { LEFT, + RIGHT, + TOP, + BOTTOM, + IPIN, + OPIN, + INVALID }; + +// Location structure +struct Location { + Coordinate x_low{-1}; + Coordinate x_high{-1}; + Coordinate y_low{-1}; + Coordinate y_high{-1}; + Coordinate layer{-1}; + std::string ptc; + std::string side; + + Location() = default; + Location(Coordinate xl, Coordinate xh, Coordinate yl, Coordinate yh, Coordinate l, const std::string& p, const std::string& s) + : x_low(xl) + , x_high(xh) + , y_low(yl) + , y_high(yh) + , layer(l) + , ptc(p) + , side(s) {} + + bool operator==(const Location& other) const { + return x_low == other.x_low && x_high == other.x_high && y_low == other.y_low && y_high == other.y_high && layer == other.layer && ptc == other.ptc && side == other.side; + } +}; + +// Node timing information +struct NodeTiming { + DelayValue r{0.0}; + DelayValue c{0.0}; + + NodeTiming() = default; + NodeTiming(DelayValue r_val, DelayValue c_val) + : r(r_val) + , c(c_val) {} +}; + +// Node segment information +struct NodeSegmentId { + int segment_id{-1}; + + NodeSegmentId() = default; + NodeSegmentId(int segment_id_val) + : segment_id(segment_id_val) {} + + bool empty() const { return segment_id == -1; } +}; + +// Timing information +struct Timing { + DelayValue Cin{0.0}; + DelayValue Tdel{0.0}; + + Timing() = default; + Timing(DelayValue cin_val, DelayValue tdel_val) + : Cin(cin_val) + , Tdel(tdel_val) {} +}; + +// Sizing information +struct Sizing { + DelayValue mux_trans_size{0.0}; + DelayValue buf_size{0.0}; + + Sizing() = default; + Sizing(DelayValue mux_size, DelayValue buffer_size) + : mux_trans_size(mux_size) + , buf_size(buffer_size) {} +}; + +/** + * @brief Connection class + * + * This class represents a connection between two nodes in the RR graph. + * It is used to store the connection information and to compare connections. + */ +class Connection { + public: + Connection(RRNodeId sink_node, RRNodeId src_node, int delay_ps) noexcept + : sink_node_(sink_node) + , src_node_(src_node) + , delay_ps_(delay_ps) {} + + RRNodeId sink_node() const { return sink_node_; } + RRNodeId src_node() const { return src_node_; } + int delay_ps() const { return delay_ps_; } + + bool operator<(const Connection& other) const { + return std::tie(sink_node_, src_node_, delay_ps_) < std::tie(other.sink_node_, other.src_node_, other.delay_ps_); + } + + bool operator==(const Connection& other) const { + return sink_node_ == other.sink_node_ && src_node_ == other.src_node_ && delay_ps_ == other.delay_ps_; + } + + private: + RRNodeId sink_node_; + RRNodeId src_node_; + int delay_ps_; +}; + +// Node hash type for lookups +using NodeHash = std::tuple; + +// Hash function for NodeHash +struct NodeHasher { + std::size_t operator()(const NodeHash& hash) const noexcept { + auto h1 = std::hash{}(static_cast(std::get<0>(hash))); + auto h2 = std::hash{}(std::get<1>(hash)); + auto h3 = std::hash{}(std::get<2>(hash)); + auto h4 = std::hash{}(std::get<3>(hash)); + auto h5 = std::hash{}(std::get<4>(hash)); + auto h6 = std::hash{}(std::get<5>(hash)); + + // Combine hashes using a better mixing function + std::size_t result = h1; + result ^= h2 + 0x9e3779b9 + (result << 6) + (result >> 2); + result ^= h3 + 0x9e3779b9 + (result << 6) + (result >> 2); + result ^= h4 + 0x9e3779b9 + (result << 6) + (result >> 2); + result ^= h5 + 0x9e3779b9 + (result << 6) + (result >> 2); + result ^= h6 + 0x9e3779b9 + (result << 6) + (result >> 2); + + return result; + } +}; + +// Utility functions +inline std::string to_string(NodeType type) { + switch (type) { + case NodeType::SOURCE: + return "SOURCE"; + case NodeType::SINK: + return "SINK"; + case NodeType::IPIN: + return "IPIN"; + case NodeType::OPIN: + return "OPIN"; + case NodeType::CHANX: + return "CHANX"; + case NodeType::CHANY: + return "CHANY"; + default: + return "UNKNOWN"; + } +} + +inline e_rr_type string_to_node_type(const std::string& str) { + if (str == "SOURCE" || str == "source") return e_rr_type::SOURCE; + if (str == "SINK" || str == "sink") return e_rr_type::SINK; + if (str == "IPIN" || str == "ipin") return e_rr_type::IPIN; + if (str == "OPIN" || str == "opin") return e_rr_type::OPIN; + if (str == "CHANX" || str == "chanx") return e_rr_type::CHANX; + if (str == "CHANY" || str == "chany") return e_rr_type::CHANY; + throw std::invalid_argument("Unknown node type: " + str); +} + +inline std::string to_string(Direction dir) { + return (dir == Direction::INC_DIR) ? "INC_DIR" : "DEC_DIR"; +} + +inline std::string to_string(Side side) { + switch (side) { + case Side::LEFT: + return "Left"; + case Side::RIGHT: + return "Right"; + case Side::TOP: + return "Top"; + case Side::BOTTOM: + return "Bottom"; + case Side::IPIN: + return "IPIN"; + case Side::OPIN: + return "OPIN"; + default: + return "UNKNOWN"; + } +} + +inline Side string_to_side(const std::string& str) { + if (str == "Left") return Side::LEFT; + if (str == "Right") return Side::RIGHT; + if (str == "Top") return Side::TOP; + if (str == "Bottom") return Side::BOTTOM; + if (str == "IPIN") return Side::IPIN; + if (str == "OPIN") return Side::OPIN; + throw std::invalid_argument("Unknown side: " + str); +} + +/** + * @brief Get the memory usage + * @return Memory usage in KB + */ +inline size_t get_memory_usage() { +#ifdef _WIN32 + PROCESS_MEMORY_COUNTERS pmc; + if (GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc))) { + return pmc.WorkingSetSize / 1024; // Convert bytes to KB + } + return 0; +#else + struct rusage usage; + getrusage(RUSAGE_SELF, &usage); + return static_cast(usage.ru_maxrss); // Already in KB on Linux, bytes on macOS +#ifdef __APPLE__ + return static_cast(usage.ru_maxrss / 1024); // Convert bytes to KB on macOS +#endif +#endif +} + +/** + * @brief Get the peak memory usage + * @return Peak memory usage in KB + */ +inline size_t get_peak_memory_usage() { +#ifdef _WIN32 + PROCESS_MEMORY_COUNTERS pmc; + if (GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc))) { + return pmc.PeakWorkingSetSize / 1024; // Convert bytes to KB + } + return 0; +#else + // On Unix/Linux, we need to read /proc/self/status for peak memory + std::ifstream status_file("/proc/self/status"); + std::string line; + while (std::getline(status_file, line)) { + if (line.substr(0, 6) == "VmHWM:") { + std::istringstream iss(line); + std::string label, value, unit; + iss >> label >> value >> unit; + return std::stoul(value); // Already in KB + } + } + + // Fallback to getrusage if /proc/self/status is not available + struct rusage usage; + getrusage(RUSAGE_SELF, &usage); +#ifdef __APPLE__ + return static_cast(usage.ru_maxrss / 1024); // Convert bytes to KB on macOS +#else + return static_cast(usage.ru_maxrss); // Already in KB on Linux +#endif +#endif +} + +// Exception classes +class RRGeneratorException : public std::runtime_error { + public: + explicit RRGeneratorException(const std::string& message) + : std::runtime_error(message) {} +}; + +class ConfigException : public RRGeneratorException { + public: + explicit ConfigException(const std::string& message) + : RRGeneratorException("Configuration error: " + message) {} +}; + +class FileException : public RRGeneratorException { + public: + explicit FileException(const std::string& message) + : RRGeneratorException("File error: " + message) {} +}; + +class ParseException : public RRGeneratorException { + public: + explicit ParseException(const std::string& message) + : RRGeneratorException("Parse error: " + message) {} +}; + +class SwitchException : public RRGeneratorException { + public: + explicit SwitchException(const std::string& message) + : RRGeneratorException("Switch error: " + message) {} +}; + +} // namespace crrgenerator diff --git a/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/crr_connection_builder.cpp b/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/crr_connection_builder.cpp new file mode 100644 index 00000000000..c7d68445e2b --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/crr_connection_builder.cpp @@ -0,0 +1,551 @@ +#include "crr_connection_builder.h" + +#include +#include +#include +// #include +#include +#include + +#include "vtr_log.h" +#include "vtr_assert.h" +#include "rr_node_types.h" + +namespace crrgenerator { + +static bool is_integer(const std::string& s) { + int value; + auto [ptr, ec] = std::from_chars(s.data(), s.data() + s.size(), value); + return ec == std::errc() && ptr == s.data() + s.size(); +} + +CRRConnectionBuilder::CRRConnectionBuilder(const RRGraphView& rr_graph, + const NodeLookupManager& node_lookup, + const SwitchBlockManager& sb_manager) + : rr_graph_(rr_graph) + , node_lookup_(node_lookup) + , sb_manager_(sb_manager) {} + +void CRRConnectionBuilder::initialize( + Coordinate fpga_grid_x, + Coordinate fpga_grid_y, + bool is_annotated_excel) { + + fpga_grid_x_ = fpga_grid_x; + fpga_grid_y_ = fpga_grid_y; + is_annotated_excel_ = is_annotated_excel; + + // for (const auto& original_switch : rr_graph_.rr_switches()) { + // std::string switch_name = original_switch.name; + // std::transform(switch_name.begin(), switch_name.end(), switch_name.begin(), + // ::tolower); + + // if (switch_name.find("delayless") != std::string::npos) { + // VTR_LOG("Adding delayless switch: %s\n", switch_name.c_str()); + // default_switch_id_["delayless"] = original_switch.get_id(); + // } else if (switch_name.find("ipin") != std::string::npos) { + // VTR_LOG("Adding ipin switch: %s\n", switch_name.c_str()); + // default_switch_id_["ipin"] = original_switch.get_id(); + // } else if (std::regex_match(switch_name, std::regex(R"(l1(_.*)?)"))) { + // VTR_LOG("Adding l1 switch: %s\n", switch_name.c_str()); + // default_switch_id_["l1"] = original_switch.get_id(); + // } else if (std::regex_match(switch_name, std::regex(R"(l2(_.*)?)"))) { + // VTR_LOG("Adding l2 switch: %s\n", switch_name.c_str()); + // default_switch_id_["l2"] = original_switch.get_id(); + // } else if (std::regex_match(switch_name, std::regex(R"(l4(_.*)?)"))) { + // VTR_LOG("Adding l4 switch: %s\n", switch_name.c_str()); + // default_switch_id_["l4"] = original_switch.get_id(); + // } else if (std::regex_match(switch_name, std::regex(R"(l8(_.*)?)"))) { + // VTR_LOG("Adding l8 switch: %s\n", switch_name.c_str()); + // default_switch_id_["l8"] = original_switch.get_id(); + // } else if (std::regex_match(switch_name, std::regex(R"(l12(_.*)?)"))) { + // VTR_LOG("Adding l12 switch: %s\n", switch_name.c_str()); + // default_switch_id_["l12"] = original_switch.get_id(); + // } else { + // VTR_LOG_ERROR("Unknown switch type: %s\n", switch_name.c_str()); + // } + // } + + // assert(default_switch_id_.size() == crr_graph_.get_switches().size()); + // sw_zero_id_ = static_cast(crr_graph_.get_switches().size()); + + // Total locations is the number of locations on the FPGA grid minus the 4 + // corner locations. + total_locations_ = static_cast(fpga_grid_x_ * fpga_grid_y_) - 4; + processed_locations_ = 0; + + all_connections_.resize(static_cast(fpga_grid_x_ + 1), + std::vector>(static_cast(fpga_grid_y_ + 1))); + + VTR_LOG("CRRConnectionBuilder initialized for %d x %d grid (%zu locations)\n", + fpga_grid_x_, fpga_grid_y_, total_locations_); +} + +void CRRConnectionBuilder::build_connections_for_location(size_t x, + size_t y, + std::vector& tile_connections) const { + + // Find matching switch block pattern + std::string sw_name = "SB_" + std::to_string(x) + "__" + std::to_string(y) + "_"; + std::string pattern = sb_manager_.find_matching_pattern(sw_name); + tile_connections.clear(); + + if (pattern.empty()) { + VTR_LOG_DEBUG("No pattern found for switch block at (%zu, %zu)\n", x, y); + return; + } + + const DataFrame* df = sb_manager_.get_switch_block_dataframe(pattern); + if (df == nullptr) { + VTR_LOG_WARN("No dataframe found for pattern '%s' at (%zu, %zu)\n", pattern.c_str(), x, y); + return; + } + + VTR_LOG("Processing switch block '%s' with pattern '%s' at (%zu, %zu)\n", + sw_name.c_str(), pattern.c_str(), x, y); + + // Get combined nodes for this location + auto combined_nodes = node_lookup_.get_combined_nodes(x, y); + + // Get vertical and horizontal nodes + auto source_nodes = get_vertical_nodes(x, y, *df, combined_nodes); + auto sink_nodes = get_horizontal_nodes(x, y, *df, combined_nodes); + + // Build connections based on dataframe + for (auto row_iter = df->begin(); row_iter != df->end(); ++row_iter) { + size_t row_idx = row_iter.get_row_index(); + if (row_idx < NUM_EMPTY_ROWS) { + continue; + } + + for (size_t col_idx = NUM_EMPTY_COLS; col_idx < df->cols(); ++col_idx) { + const Cell& cell = df->at(row_idx, col_idx); + + if (!cell.is_empty()) { + auto source_it = source_nodes.find(row_idx); + auto sink_it = sink_nodes.find(col_idx); + + if (source_it != source_nodes.end() && sink_it != sink_nodes.end()) { + RRNodeId source_node = source_it->second; + e_rr_type source_node_type = rr_graph_.node_type(source_node); + RRNodeId sink_node = sink_it->second; + e_rr_type sink_node_type = rr_graph_.node_type(sink_node); + // If the source node is an IPIN, then it should be considered as + // a sink of the connection. + if (source_node_type == e_rr_type::IPIN) { + int delay_ps = get_connection_delay_ps(cell.as_string(), + rr_node_typename[source_node_type], + sink_node, + source_node); + + tile_connections.emplace_back(source_node, sink_node, delay_ps); + } else { + int segment_length = -1; + if (sink_node_type == e_rr_type::CHANX || sink_node_type == e_rr_type::CHANY) { + segment_length = rr_graph_.node_length(sink_node); + } + int delay_ps = get_connection_delay_ps(cell.as_string(), + rr_node_typename[sink_node_type], + source_node, + sink_node, + segment_length); + + tile_connections.emplace_back(sink_node, source_node, delay_ps); + } + } + } + } + } + + std::sort(tile_connections.begin(), tile_connections.end()); + tile_connections.erase(std::unique(tile_connections.begin(), + tile_connections.end()), + tile_connections.end()); + tile_connections.shrink_to_fit(); + + VTR_LOG_DEBUG("Generated %zu connections for location (%zu, %zu)\n", + tile_connections.size(), x, y); +} + +std::vector CRRConnectionBuilder::get_tile_connections(size_t tile_x, size_t tile_y) const { + std::vector tile_connections; + build_connections_for_location(tile_x, tile_y, tile_connections); + + return tile_connections; +} + +std::map CRRConnectionBuilder::get_vertical_nodes(Coordinate x, Coordinate y, const DataFrame& df, const std::unordered_map& node_lookup) const { + std::map source_nodes; + std::string prev_seg_type = ""; + int prev_seg_index = -1; + Side prev_side = Side::INVALID; + int prev_ptc_number = 0; + + for (size_t row = NUM_EMPTY_ROWS; row < df.rows(); ++row) { + SegmentInfo info = parse_segment_info(df, row, true); + RRNodeId node_id; + + if (info.side == Side::IPIN || info.side == Side::OPIN) { + node_id = process_opin_ipin_node(info, x, y, node_lookup); + } else if (info.side == Side::LEFT || info.side == Side::RIGHT || info.side == Side::TOP || info.side == Side::BOTTOM) { + node_id = process_channel_node(info, x, y, node_lookup, prev_seg_index, + prev_side, prev_seg_type, prev_ptc_number, true); + } + + if (node_id != RRNodeId::INVALID()) { + source_nodes[row] = node_id; + } + + prev_seg_type = info.seg_type; + prev_side = info.side; + } + + return source_nodes; +} + +std::map CRRConnectionBuilder::get_horizontal_nodes(Coordinate x, Coordinate y, const DataFrame& df, const std::unordered_map& node_lookup) const { + std::map sink_nodes; + std::string prev_seg_type = ""; + int prev_seg_index = -1; + Side prev_side = Side::INVALID; + int prev_ptc_number = 0; + + for (size_t col = NUM_EMPTY_COLS; col < df.cols(); ++col) { + SegmentInfo info = parse_segment_info(df, col, false); + if (info.side == Side::INVALID) { + continue; + } + RRNodeId node_id; + + if (info.side == Side::IPIN) { + node_id = process_opin_ipin_node(info, x, y, node_lookup); + } else if (info.side == Side::LEFT || info.side == Side::RIGHT || info.side == Side::TOP || info.side == Side::BOTTOM) { + node_id = process_channel_node(info, x, y, node_lookup, prev_seg_index, + prev_side, prev_seg_type, prev_ptc_number, + false); + } + + if (node_id != RRNodeId::INVALID()) { + sink_nodes[col] = node_id; + } + + prev_seg_type = info.seg_type; + prev_side = info.side; + } + + return sink_nodes; +} + +CRRConnectionBuilder::SegmentInfo CRRConnectionBuilder::parse_segment_info(const DataFrame& df, + size_t row_or_col, + bool is_vertical) const { + SegmentInfo info; + + if (is_vertical) { + // Vertical processing (rows) + const Cell& side_cell = df.at(row_or_col, 0); + const Cell& type_cell = df.at(row_or_col, 1); + const Cell& index_cell = df.at(row_or_col, 2); + const Cell& tap_cell = df.at(row_or_col, 3); + + if (!side_cell.is_empty()) { + info.side = string_to_side(side_cell.as_string()); + } + if (!type_cell.is_empty()) { + info.seg_type = type_cell.as_string(); + } + if (!index_cell.is_empty()) { + info.seg_index = static_cast(index_cell.as_int()); + } + if (!tap_cell.is_empty() && tap_cell.is_number()) { + info.tap = static_cast(tap_cell.as_int()); + } + } else { + // Horizontal processing (columns) + const Cell& side_cell = df.at(0, row_or_col); + const Cell& type_cell = df.at(1, row_or_col); + const Cell& index_cell = + df.at(3, row_or_col); // Note: row 3 for horizontal + const Cell& tap_cell = df.at(4, row_or_col); // Note: row 4 for horizontal + + if (!side_cell.is_empty()) { + info.side = string_to_side(side_cell.as_string()); + } + if (!type_cell.is_empty()) { + info.seg_type = type_cell.as_string(); + } + if (!index_cell.is_empty()) { + info.seg_index = static_cast(index_cell.as_int()); + } + if (!tap_cell.is_empty() && tap_cell.is_number()) { + info.tap = static_cast(tap_cell.as_int()); + } else { + info.tap = 1; + } + } + + return info; +} + +RRNodeId CRRConnectionBuilder::process_opin_ipin_node(const SegmentInfo& info, Coordinate x, Coordinate y, const std::unordered_map& node_lookup) const { + VTR_ASSERT(info.side == Side::OPIN || info.side == Side::IPIN); + e_rr_type node_type = (info.side == Side::OPIN) ? e_rr_type::OPIN : e_rr_type::IPIN; + NodeHash hash = std::make_tuple(node_type, + std::to_string(info.seg_index), + x, x, y, y); + + auto it = node_lookup.find(hash); + if (it != node_lookup.end()) { + return it->second; + } + + return RRNodeId::INVALID(); +} + +RRNodeId CRRConnectionBuilder::process_channel_node(const SegmentInfo& info, Coordinate x, Coordinate y, const std::unordered_map& node_lookup, int& prev_seg_index, Side& prev_side, std::string& prev_seg_type, int& prev_ptc_number, bool is_vertical) const { + // Check grid boundaries + if ((info.side == Side::RIGHT && x == fpga_grid_x_) || (info.side == Side::TOP && y == fpga_grid_y_)) { + return RRNodeId::INVALID(); + } + + int seg_length = std::stoi( + info.seg_type.substr(1)); // Extract number from "L1", "L4", etc. + + // Update PTC number based on previous segment + if (prev_seg_type != info.seg_type) { + prev_ptc_number = prev_seg_index; + } + if (prev_side != info.side) { + prev_ptc_number = 0; + prev_seg_index = 0; + } + std::string seg_type_label = get_segment_type_label(info.side); + Direction direction = get_direction_for_side(info.side, is_vertical); + + // Calculate segment coordinates + Coordinate x_low, x_high, y_low, y_high; + int physical_length, truncated; + calculate_segment_coordinates(info, x, y, x_low, x_high, y_low, y_high, + physical_length, truncated, is_vertical); + + // Calculate starting PTC point + int seg_index = (info.seg_index - 1) * seg_length * 2; + seg_index += prev_ptc_number; + prev_seg_index = + std::max({prev_seg_index, seg_index + 2 * seg_length}); + + seg_index += (direction == Direction::INC_DIR) ? 0 : 1; + seg_index += (direction == Direction::DEC_DIR) ? 2 * (seg_length - 1) : 0; + + // Calculate PTC sequence + std::string seg_sequence = get_ptc_sequence( + seg_index, seg_length, physical_length, direction, truncated); + + // Create node hash and lookup + NodeHash hash = std::make_tuple(string_to_node_type(seg_type_label), + seg_sequence, + x_low, x_high, y_low, y_high); + auto it = node_lookup.find(hash); + + if (it != node_lookup.end()) { + return it->second; + } else { + VTR_LOG_DEBUG("Node not found: %s [%s] (%d,%d) -> (%d,%d)\n", seg_type_label.c_str(), + seg_sequence.c_str(), x_low, y_low, x_high, y_high); + return RRNodeId::INVALID(); + } +} + +void CRRConnectionBuilder::calculate_segment_coordinates(const SegmentInfo& info, + Coordinate x, + Coordinate y, + Coordinate& x_low, + Coordinate& x_high, + Coordinate& y_low, + Coordinate& y_high, + int& physical_length, + int& truncated, + bool is_vertical) const { + int seg_length = std::stoi(info.seg_type.substr(1)); + int tap = info.tap; + + // Calculate initial coordinates based on side + if (is_vertical) { + switch (info.side) { + case Side::LEFT: + x_high = x + (seg_length - tap); + x_low = x - (tap - 1); + y_high = y; + y_low = y; + break; + case Side::RIGHT: + x_high = x + tap; + x_low = x + tap + 1 - seg_length; + y_high = y; + y_low = y; + break; + case Side::TOP: + x_high = x; + x_low = x; + y_high = y + tap; + y_low = y + 1 - seg_length + tap; + break; + case Side::BOTTOM: + x_high = x; + x_low = x; + y_high = y + seg_length - tap; + y_low = y - tap + 1; + break; + default: + x_high = x_low = x; + y_high = y_low = y; + break; + } + } else { + switch (info.side) { + case Side::LEFT: + x_high = x + tap - 1; + x_low = x - seg_length + tap; + y_high = y; + y_low = y; + break; + case Side::RIGHT: + x_high = x + seg_length; + x_low = x + 1; + y_high = y; + y_low = y; + break; + case Side::TOP: + x_high = x; + x_low = x; + y_high = y + seg_length; + y_low = y + 1; + break; + case Side::BOTTOM: + x_high = x; + x_low = x; + y_high = y; + y_low = y - seg_length + tap; + break; + default: + x_high = x_low = x; + y_high = y_low = y; + break; + } + } + + // Calculate truncation + truncated = (std::max(x_low, static_cast(1)) - x_low) - (x_high - std::min(x_high, fpga_grid_x_)); + truncated += (std::max(y_low, static_cast(1)) - y_low) - (y_high - std::min(y_high, fpga_grid_y_)); + + // Apply grid boundaries + x_low = std::max(x_low, static_cast(1)); + y_low = std::max(y_low, static_cast(1)); + x_high = std::min(x_high, fpga_grid_x_); + y_high = std::min(y_high, fpga_grid_y_); + + // Calculate physical length + physical_length = (x_high - x_low) + (y_high - y_low) + 1; +} + +Direction CRRConnectionBuilder::get_direction_for_side(Side side, + bool is_vertical) const { + if (is_vertical) { + return (side == Side::RIGHT || side == Side::TOP) ? Direction::DEC_DIR + : Direction::INC_DIR; + } else { + return (side == Side::RIGHT || side == Side::TOP) ? Direction::INC_DIR + : Direction::DEC_DIR; + } +} + +std::string CRRConnectionBuilder::get_segment_type_label(Side side) const { + return (side == Side::LEFT || side == Side::RIGHT) ? "CHANX" : "CHANY"; +} + +std::string CRRConnectionBuilder::get_ptc_sequence(int seg_index, + int /*seg_length*/, + int physical_length, + Direction direction, + int truncated) const { + std::vector sequence; + + if (direction == Direction::DEC_DIR) { + seg_index -= (2 * truncated > 0) ? 2 * truncated : 0; + for (int i = 0; i < physical_length; ++i) { + sequence.push_back(std::to_string(seg_index - (i * 2))); + } + } else { + seg_index += (2 * truncated > 0) ? 2 * truncated : 0; + for (int i = 0; i < physical_length; ++i) { + sequence.push_back(std::to_string(seg_index + (i * 2))); + } + } + + std::string result; + for (size_t i = 0; i < sequence.size(); ++i) { + if (i > 0) result += ","; + result += sequence[i]; + } + return result; +} + +int CRRConnectionBuilder::get_connection_delay_ps(const std::string& cell_value, + const std::string& sink_node_type, + RRNodeId /*source_node*/, + RRNodeId /*sink_node*/, + int /*segment_length*/) const { + std::string lower_case_sink_node_type = sink_node_type; + std::transform(lower_case_sink_node_type.begin(), + lower_case_sink_node_type.end(), + lower_case_sink_node_type.begin(), ::tolower); + + if (is_integer(cell_value) && is_annotated_excel_) { + // TODO: This is a temporary solution. We need to have an API call to get + // the switch id from delay. + if (cell_value == "0") { + return 0; + } + int switch_delay_ps = std::stoi(cell_value); + return switch_delay_ps; + } else { + VTR_LOG_ERROR("Not implemented - get_connection_delay_ps\n"); + return static_cast(-1); + // std::string switch_id_key = ""; + // if (segment_length > 0) { + // switch_id_key = "l" + std::to_string(segment_length); + // } else { + // switch_id_key = lower_case_sink_node_type; + // } + + // std::string capitalized_switch_id_key = switch_id_key; + // std::transform(capitalized_switch_id_key.begin(), + // capitalized_switch_id_key.end(), + // capitalized_switch_id_key.begin(), ::toupper); + + // if (default_switch_id_.find(switch_id_key) != default_switch_id_.end()) { + // return default_switch_id_.at(switch_id_key); + // } else if (default_switch_id_.find(capitalized_switch_id_key) != default_switch_id_.end()) { + // return default_switch_id_.at(capitalized_switch_id_key); + // } else { + // throw std::runtime_error("Default switch id not found for Node Type: " + lower_case_sink_node_type + " and Switch ID Key: " + capitalized_switch_id_key); + // } + } +} + +bool CRRConnectionBuilder::is_valid_grid_location(Coordinate x, + Coordinate y) const { + return x >= 1 && x <= fpga_grid_x_ && y >= 1 && y <= fpga_grid_y_; +} + +void CRRConnectionBuilder::update_progress() { + size_t current = processed_locations_.fetch_add(1) + 1; + if (current % std::max(size_t(1), total_locations_ / 20) == 0 || current == total_locations_) { + double percentage = + (static_cast(current) / total_locations_) * 100.0; + VTR_LOG("Connection building progress: %zu/%zu (%.1f%%)\n", current, + total_locations_, percentage); + } +} + +} // namespace crrgenerator diff --git a/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/crr_connection_builder.h b/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/crr_connection_builder.h new file mode 100644 index 00000000000..c0eef5d487c --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/crr_connection_builder.h @@ -0,0 +1,162 @@ +#pragma once + +#include "rr_graph_view.h" + +#include "crr_common.h" +#include "data_frame_processor.h" +#include "node_lookup_manager.h" +#include "crr_switch_block_manager.h" + +namespace crrgenerator { + +/** + * @brief Builds connections between routing nodes based on switch block + * configurations + * + * This class processes switch block configurations and generates routing + * connections between nodes, supporting both parallel and sequential + * processing. + */ +class CRRConnectionBuilder { + public: + CRRConnectionBuilder(const RRGraphView& rr_graph, + const NodeLookupManager& node_lookup, + const SwitchBlockManager& sb_manager); + + /** + * @brief Initialize the connection builder + * @param node_lookup Node lookup manager + * @param sb_manager Switch block manager + * @param original_switches Original switches from the input graph + */ + void initialize(Coordinate fpga_grid_x, + Coordinate fpga_grid_y, + bool is_annotated_excel); + + /** + * @brief Get connections for a tile + * @param tile_x Tile x coordinate + * @param tile_y Tile y coordinate + * @return Vector of connections + */ + std::vector get_tile_connections(size_t tile_x, size_t tile_y) const; + + /** + * @brief Get all generated connections + * @return Vector of all connections + */ + const std::vector>>& get_all_connections() const { + return all_connections_; + } + + /** + * @brief Get connection count + * @return Number of connections generated + */ + size_t get_connection_count() const { + size_t count = 0; + for (const auto& x : all_connections_) { + for (const auto& y : x) { + count += y.size(); + } + } + return count; + } + + /** + * @brief Get the default switch id map + * @return Map of default switch id + */ + std::map get_default_swithes_map() const { + return default_switch_id_; + } + + /** + * @brief Clear all connections + */ + void clear() { all_connections_.clear(); } + void remove_tile_connections(Coordinate x, Coordinate y) { + all_connections_[static_cast(x)][static_cast(y)].clear(); + all_connections_[static_cast(x)][static_cast(y)].shrink_to_fit(); + } + + private: + // Info from config + Coordinate fpga_grid_x_; + Coordinate fpga_grid_y_; + bool is_annotated_excel_; + + // Dependencies + const RRGraphView& rr_graph_; + const NodeLookupManager& node_lookup_; + const SwitchBlockManager& sb_manager_; + SwitchId sw_zero_id_; + + // Generated connections + std::vector>> all_connections_; + + // Processing state + std::atomic processed_locations_{0}; + size_t total_locations_{0}; + + // Default switch id based on the node type + std::map default_switch_id_; + + // Connection building methods + void build_connections_for_location(size_t x, + size_t y, + std::vector& tile_connections) const; + + // Node processing methods + std::map get_vertical_nodes(Coordinate x, Coordinate y, const DataFrame& df, const std::unordered_map& node_lookup) const; + + std::map get_horizontal_nodes(Coordinate x, Coordinate y, const DataFrame& df, const std::unordered_map& node_lookup) const; + + // PTC sequence calculation + std::string get_ptc_sequence(int seg_index, int seg_length, int physical_length, Direction direction, int truncated) const; + + // Segment processing helpers + struct SegmentInfo { + Side side; + std::string seg_type; + int seg_index; + int tap; + + SegmentInfo() + : side(Side::INVALID) + , seg_index(-1) + , tap(-1) {} + SegmentInfo(Side s, const std::string& type, int index, int t = 1) + : side(s) + , seg_type(type) + , seg_index(index) + , tap(t) {} + }; + + SegmentInfo parse_segment_info(const DataFrame& df, size_t row_or_col, bool is_vertical) const; + + RRNodeId process_opin_ipin_node(const SegmentInfo& info, Coordinate x, Coordinate y, const std::unordered_map& node_lookup) const; + + RRNodeId process_channel_node(const SegmentInfo& info, Coordinate x, Coordinate y, const std::unordered_map& node_lookup, int& prev_seg_index, Side& prev_side, std::string& prev_seg_type, int& prev_ptc_number, bool is_vertical) const; + + // Coordinate and direction calculations + void calculate_segment_coordinates(const SegmentInfo& info, Coordinate x, Coordinate y, Coordinate& x_low, Coordinate& x_high, Coordinate& y_low, Coordinate& y_high, int& physical_length, int& truncated, bool is_vertical) const; + + Direction get_direction_for_side(Side side, bool is_vertical) const; + std::string get_segment_type_label(Side side) const; + + // Return the switch id of an edge between two nodes + int get_connection_delay_ps(const std::string& cell_value, + const std::string& sink_node_type, + RRNodeId source_node, + RRNodeId sink_node, + int segment_length = -1) const; + + // Validation and bounds checking + bool is_valid_grid_location(Coordinate x, Coordinate y) const; + + // Progress tracking + void update_progress(); +}; + +} // namespace crrgenerator diff --git a/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/crr_edge_builder.cpp b/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/crr_edge_builder.cpp new file mode 100644 index 00000000000..706679c0ca9 --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/crr_edge_builder.cpp @@ -0,0 +1,34 @@ +#include "crr_edge_builder.h" +#include "globals.h" + +#include "crr_connection_builder.h" + +static RRSwitchId find_crr_switch_id(const int delay_ps) { + const auto& arch_switches = g_vpr_ctx.device().arch_switch_inf; + std::string switch_name; + if (delay_ps == 0) { + switch_name = "sw_zero"; + } else { + switch_name = "sw_" + std::to_string(delay_ps); + } + for (int sw_id = 0; sw_id < (int)arch_switches.size(); sw_id++) { + if (arch_switches[sw_id].name == switch_name) { + return RRSwitchId(sw_id); + } + } + return RRSwitchId::INVALID(); +} + +void build_crr_gsb_track_to_track_edges(RRGraphBuilder& rr_graph_builder, + const RRGSB& rr_gsb, + const crrgenerator::CRRConnectionBuilder& connection_builder) { + size_t gsb_x = rr_gsb.get_sb_x(); + size_t gsb_y = rr_gsb.get_sb_y(); + + std::vector gsb_connections = connection_builder.get_tile_connections(gsb_x, gsb_y); + for (const auto& connection : gsb_connections) { + RRSwitchId rr_switch_id = find_crr_switch_id(connection.delay_ps()); + VTR_ASSERT(rr_switch_id != RRSwitchId::INVALID()); + rr_graph_builder.create_edge_in_cache(connection.src_node(), connection.sink_node(), rr_switch_id, false); + } +} diff --git a/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/crr_edge_builder.h b/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/crr_edge_builder.h new file mode 100644 index 00000000000..2730c1aab86 --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/crr_edge_builder.h @@ -0,0 +1,21 @@ +#pragma once + +#include + +#include "vtr_vector.h" +#include "vtr_geometry.h" + +#include "physical_types.h" +#include "device_grid.h" + +#include "rr_gsb.h" +#include "rr_graph_obj.h" +#include "rr_graph.h" +#include "rr_graph_view.h" +#include "rr_graph_builder.h" + +#include "crr_connection_builder.h" + +void build_crr_gsb_track_to_track_edges(RRGraphBuilder& rr_graph_builder, + const RRGSB& rr_gsb, + const crrgenerator::CRRConnectionBuilder& connection_builder); diff --git a/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/crr_generator.cpp b/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/crr_generator.cpp new file mode 100644 index 00000000000..0e6879a7358 --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/crr_generator.cpp @@ -0,0 +1,65 @@ +#include "crr_generator.h" + +#include + +#include "globals.h" +#include "vtr_log.h" +#include "vtr_assert.h" + +namespace crrgenerator { + +CRRGraphGenerator::CRRGraphGenerator(const t_crr_opts& crr_opts, + const RRGraphView& input_graph, + const NodeLookupManager& node_lookup, + const SwitchBlockManager& sb_manager) + : crr_opts_(crr_opts) + , input_graph_(input_graph) + , node_lookup_(node_lookup) + , sb_manager_(sb_manager) {} + +void CRRGraphGenerator::run() { + auto start_time = std::chrono::steady_clock::now(); + + VTR_LOG("Starting RR Graph parsing process\n"); + + try { + // Initialize all components + initialize_components(); + + // Build connections + build_connections(); + + auto end_time = std::chrono::steady_clock::now(); + auto duration = + std::chrono::duration_cast(end_time - start_time); + + VTR_LOG("RR Graph parsing completed successfully in %ds\n", duration.count()); + } catch (const std::exception& e) { + VTR_LOG_ERROR("RR Graph parsing failed: %s\n", e.what()); + } +} + +void CRRGraphGenerator::initialize_components() { + VTR_LOG("CRR Graph Generator: Initializing components\n"); + + // Initialize connection builder + connection_builder_ = std::make_unique(input_graph_, + node_lookup_, + sb_manager_); + + VTR_LOG("CRR Graph Generator: All components initialized successfully\n"); +} + +void CRRGraphGenerator::build_connections() { + VTR_LOG("CRR Graph Generator: Building connections\n"); + + // Initialize connection builder + const DeviceGrid& grid = g_vpr_ctx.device().grid; + connection_builder_->initialize(grid.width() - 2, + grid.height() - 2, + crr_opts_.annotated_rr_graph); + + VTR_LOG("CRR Graph Generator: Connection building completed\n"); +} + +} // namespace crrgenerator diff --git a/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/crr_generator.h b/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/crr_generator.h new file mode 100644 index 00000000000..f308f51fdac --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/crr_generator.h @@ -0,0 +1,51 @@ +#pragma once + +#include +#include + +#include "vpr_types.h" +#include "crr_common.h" +#include "crr_connection_builder.h" +#include "node_lookup_manager.h" +#include "crr_switch_block_manager.h" + +namespace crrgenerator { + +/** + * @brief Main orchestrator for RR graph parsing and generation + * + * This class coordinates all subsystems to read input graphs, + * process switch block configurations, and generate output graphs. + */ +class CRRGraphGenerator { + public: + CRRGraphGenerator(const t_crr_opts& crr_opts, + const RRGraphView& input_graph, + const NodeLookupManager& node_lookup, + const SwitchBlockManager& sb_manager); + ~CRRGraphGenerator() = default; + + /** + * @brief Run the complete parsing and generation process + */ + void run(); + + const CRRConnectionBuilder* get_connection_builder() const { + return connection_builder_.get(); + } + + private: + const t_crr_opts& crr_opts_; + const RRGraphView& input_graph_; + const NodeLookupManager& node_lookup_; + const SwitchBlockManager& sb_manager_; + + std::unique_ptr connection_builder_; + + // Processing methods + void initialize_components(); + void process_switch_blocks(); + void build_connections(); +}; + +} // namespace crrgenerator diff --git a/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/crr_pattern_matcher.h b/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/crr_pattern_matcher.h new file mode 100644 index 00000000000..4b6ed209ffc --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/crr_pattern_matcher.h @@ -0,0 +1,160 @@ +#include +#include +#include +#include +#include + +namespace crrgenerator { + +class PatternMatcher { + private: + // Helper function to parse range [start:end:step] or comma-separated values [7,20] + static bool matches_range(int value, const std::string& range_str) { + // Extract numbers from [start:end:step] or [7,20] format + size_t start_pos = range_str.find('['); + size_t end_pos = range_str.find(']'); + if (start_pos == std::string::npos || end_pos == std::string::npos) { + return false; + } + + std::string range_content = range_str.substr(start_pos + 1, end_pos - start_pos - 1); + + // Check if it's comma-separated values (e.g., [7,20]) + if (range_content.find(',') != std::string::npos) { + // Parse comma-separated values + std::vector values; + size_t pos = 0; + size_t comma_pos; + + while ((comma_pos = range_content.find(',', pos)) != std::string::npos) { + values.push_back(std::stoi(range_content.substr(pos, comma_pos - pos))); + pos = comma_pos + 1; + } + values.push_back(std::stoi(range_content.substr(pos))); + + // Check if value is in the list + return std::find(values.begin(), values.end(), value) != values.end(); + } + // Check if it's range notation (e.g., [2:32:3]) + else if (range_content.find(':') != std::string::npos) { + // Parse start:end:step + std::vector parts; + size_t pos = 0; + size_t colon_pos; + + while ((colon_pos = range_content.find(':', pos)) != std::string::npos) { + parts.push_back(std::stoi(range_content.substr(pos, colon_pos - pos))); + pos = colon_pos + 1; + } + parts.push_back(std::stoi(range_content.substr(pos))); + + if (parts.size() != 3) return false; + + int start = parts[0]; + int end = parts[1]; // end is NOT exclusive as per your specification + int step = parts[2]; + + if (value < start || value > end) return false; + if ((value - start) % step != 0) return false; + + return true; + } + // Single value (e.g., [5]) + else { + int single_value = std::stoi(range_content); + return value == single_value; + } + } + + // Convert pattern to regex, handling * and [start:end:step] ranges + static std::string pattern_to_regex(const std::string& pattern) { + std::string regex_pattern = "^"; + + for (size_t i = 0; i < pattern.length(); ++i) { + char c = pattern[i]; + + if (c == '*') { + regex_pattern += "([0-9]+)"; // Capture group for numbers + } else if (c == '[') { + // Find the matching closing bracket + size_t close_bracket = pattern.find(']', i); + if (close_bracket != std::string::npos) { + std::string range = pattern.substr(i, close_bracket - i + 1); + regex_pattern += "([0-9]+)"; // Capture the number, validate range later + i = close_bracket; // Skip to after the closing bracket + } else { + regex_pattern += "\\["; // Literal bracket if no closing bracket found + } + } else if (c == '\\' && i + 1 < pattern.length() && pattern[i + 1] == '*') { + // Handle escaped asterisk - treat as literal * + regex_pattern += "\\*"; + ++i; // Skip the next character + } else { + // Escape regex special characters + if (c == '.' || c == '^' || c == '$' || c == '+' || c == '?' || c == '(' || c == ')' || c == '|' || c == '{' || c == '}') { + regex_pattern += "\\"; + } + regex_pattern += c; + } + } + + regex_pattern += "$"; + return regex_pattern; + } + + public: + static bool matches_pattern(const std::string& name, const std::string& pattern) { + // Fast path for exact matches (no wildcards or ranges) + if (pattern.find('*') == std::string::npos && pattern.find('[') == std::string::npos && pattern.find('\\') == std::string::npos) { + return name == pattern; + } + + // Compile regex for this pattern + std::string regex_str = pattern_to_regex(pattern); + std::regex compiled_regex(regex_str); + + std::smatch matches; + if (!std::regex_match(name, matches, compiled_regex)) { + return false; + } + + // Now validate any range constraints + std::vector captured_numbers; + for (size_t i = 1; i < matches.size(); ++i) { + captured_numbers.push_back(matches[i].str()); + } + + // Parse pattern again to find ranges and validate them + size_t capture_index = 0; + for (size_t i = 0; i < pattern.length(); ++i) { + char c = pattern[i]; + + if (c == '*') { + // Simple wildcard, no validation needed + ++capture_index; + } else if (c == '[') { + // Find the matching closing bracket + size_t close_bracket = pattern.find(']', i); + if (close_bracket != std::string::npos) { + std::string range = pattern.substr(i, close_bracket - i + 1); + + if (capture_index < captured_numbers.size()) { + int captured_value = std::stoi(captured_numbers[capture_index]); + if (!matches_range(captured_value, range)) { + return false; + } + } + + ++capture_index; + i = close_bracket; // Skip to after the closing bracket + } + } else if (c == '\\' && i + 1 < pattern.length() && pattern[i + 1] == '*') { + ++i; // Skip escaped asterisk + } + } + + return true; + } +}; + +} // namespace crrgenerator diff --git a/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/crr_switch_block_manager.cpp b/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/crr_switch_block_manager.cpp new file mode 100644 index 00000000000..8ff7328bf23 --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/crr_switch_block_manager.cpp @@ -0,0 +1,183 @@ +#include + +#include +#include + +#include + +#include "crr_switch_block_manager.h" +#include "crr_pattern_matcher.h" + +#include "vtr_log.h" + +namespace crrgenerator { + +SwitchBlockManager::SwitchBlockManager() = default; + +void SwitchBlockManager::initialize(const std::string& sb_maps_file, + const std::string& sb_annotated_dir, + const bool is_annotated_excel) { + VTR_LOG("Initializing SwitchBlockManager with maps file: %s\n", sb_maps_file.c_str()); + + annotated_dir_ = sb_annotated_dir; + + // Load YAML configuration + try { + YAML::Node config = YAML::LoadFile(sb_maps_file); + validate_yaml_structure(config); + + if (!config["SB_MAPS"]) { + VTR_LOG_ERROR("SB_MAPS section not found in YAML file\n"); + } + + YAML::Node sb_maps = config["SB_MAPS"]; + VTR_LOG_DEBUG("Found SB_MAPS section with %zu entries\n", sb_maps.size()); + + // Process each switch block mapping + std::unordered_set unique_files; + for (const auto& item : sb_maps) { + std::string pattern = item.first.as(); + + std::string excel_file = item.second.as(); + if (item.second.IsNull()) { + excel_file = ""; + } + + std::string full_path = std::filesystem::path(annotated_dir_) / excel_file; + if (excel_file.empty()) { + full_path = ""; + } + + // Handle escaped asterisks (replace \* with *) + std::regex escaped_asterisk(R"(\\\*)"); + pattern = std::regex_replace(pattern, escaped_asterisk, "*"); + + ordered_switch_block_patterns_.push_back(pattern); + switch_block_to_file_[pattern] = full_path; + if (!full_path.empty()) { + unique_files.insert(full_path); + } + } + + for (const auto& full_path : unique_files) { + if (std::filesystem::exists(full_path)) { + try { + VTR_LOG_DEBUG("Attempting to read Excel file: %s\n", full_path.c_str()); + DataFrame df = processor_.read_csv(full_path); + df = processor_.process_dataframe(std::move(df), NUM_EMPTY_ROWS, + NUM_EMPTY_COLS); + file_cache_[full_path] = std::move(df); + VTR_LOG_DEBUG("Processed %zu connections in %s file\n", + file_cache_[full_path].connections, + std::filesystem::path(full_path).filename().string().c_str()); + } catch (const std::exception& e) { + VTR_LOG_ERROR("Failed to read required Excel file '%s': %s\n", full_path.c_str(), e.what()); + } + } else { + VTR_LOG_ERROR("Required Excel file not found: %s\n", full_path.c_str()); + } + } + + // Map patterns to loaded DataFrames + for (const auto& [pattern, full_path] : switch_block_to_file_) { + if (file_cache_.find(full_path) != file_cache_.end()) { + dataframes_[pattern] = &file_cache_[full_path]; + } + } + + update_and_set_global_switch_delays(is_annotated_excel); + + print_statistics(); + } catch (const YAML::Exception& e) { + VTR_LOG_ERROR("Failed to parse YAML file %s: %s\n", sb_maps_file.c_str(), e.what()); + } catch (const std::exception& e) { + VTR_LOG_ERROR("Failed to initialize SwitchBlockManager: %s\n", e.what()); + } +} + +const DataFrame* SwitchBlockManager::get_switch_block_dataframe(const std::string& pattern) const { + auto it = dataframes_.find(pattern); + return (it != dataframes_.end()) ? it->second : nullptr; +} + +bool SwitchBlockManager::has_pattern(const std::string& pattern) const { + return dataframes_.find(pattern) != dataframes_.end(); +} + +std::vector SwitchBlockManager::get_all_patterns() const { + std::vector patterns; + patterns.reserve(dataframes_.size()); + + for (const auto& [pattern, _] : dataframes_) { + patterns.push_back(pattern); + } + + return patterns; +} + +std::string SwitchBlockManager::find_matching_pattern(const std::string& sw_name) const { + for (const auto& pattern : ordered_switch_block_patterns_) { + if (PatternMatcher::matches_pattern(sw_name, pattern)) { + return pattern; + } + } + return ""; +} + +void SwitchBlockManager::update_and_set_global_switch_delays(const bool is_annotated_excel) { + if (is_annotated_excel) { + int switch_delay_max_ps = std::numeric_limits::min(); + int switch_delay_min_ps = std::numeric_limits::max(); + for (const auto& [pattern, df] : dataframes_) { + processor_.update_switch_delays(*df, switch_delay_max_ps, + switch_delay_min_ps); + } + + switch_delay_max_ps_ = switch_delay_max_ps; + switch_delay_min_ps_ = switch_delay_min_ps; + } else { + switch_delay_max_ps_ = 1000; + switch_delay_min_ps_ = 1; + } + + VTR_LOG("Global switch delay range updated: %d - %d\n", switch_delay_min_ps_, switch_delay_max_ps_); +} + +void SwitchBlockManager::print_statistics() const { + VTR_LOG("=== CRR Generator Switch Block Manager Statistics ===\n"); + VTR_LOG("Patterns loaded: %zu\n", dataframes_.size()); + VTR_LOG("Unique Excel files: %zu\n", file_cache_.size()); + VTR_LOG("Total connections: %zu\n", get_total_connections()); + VTR_LOG("Switch delay range: %d - %d\n", switch_delay_min_ps_, switch_delay_max_ps_); + + // Print file details + for (const auto& [file, df] : file_cache_) { + VTR_LOG(" %s: %zu connections (%zux%zu)\n", + std::filesystem::path(file).filename().string().c_str(), + df.connections, df.rows(), df.cols()); + } +} + +size_t SwitchBlockManager::get_total_connections() const { + size_t total = 0; + for (const auto& [file, df] : file_cache_) { + total += df.connections; + } + return total; +} + +void SwitchBlockManager::validate_yaml_structure(const YAML::Node& root) { + if (!root.IsMap()) { + VTR_LOG_ERROR("YAML root must be a map\n"); + } + + if (!root["SB_MAPS"]) { + VTR_LOG_ERROR("Required 'SB_MAPS' section not found in YAML\n"); + } + + if (!root["SB_MAPS"].IsMap()) { + VTR_LOG_ERROR("'SB_MAPS' must be a map\n"); + } +} + +} // namespace crrgenerator diff --git a/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/crr_switch_block_manager.h b/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/crr_switch_block_manager.h new file mode 100644 index 00000000000..c7de8013920 --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/crr_switch_block_manager.h @@ -0,0 +1,118 @@ +#pragma once + +#include +#include + +#include + +#include "data_frame_processor.h" + +namespace crrgenerator { + +/** + * @brief Manages switch block configurations and Excel file processing + * + * This class handles reading YAML configuration files, processing Excel files + * containing switch block data, and managing switch block patterns. + */ +class SwitchBlockManager { + public: + SwitchBlockManager(); + + /** + * @brief Initialize the manager with configuration file + * @param sb_maps_file Path to the YAML configuration file + * @param sb_annotated_dir Directory containing Excel files + * @param is_annotated_excel Whether the switches are annotated in Excel + */ + void initialize(const std::string& sb_maps_file, + const std::string& sb_annotated_dir, + const bool is_annotated_excel); + + /** + * @brief Get DataFrame for a specific switch block pattern + * @param pattern Switch block pattern name (e.g., "SB_1__2_") + * @return Pointer to DataFrame or nullptr if not found + */ + const DataFrame* get_switch_block_dataframe(const std::string& pattern) const; + + /** + * @brief Check if a pattern exists in the switch block mapping + * @param pattern Pattern to check + * @return true if pattern exists + */ + bool has_pattern(const std::string& pattern) const; + + /** + * @brief Get all available patterns + * @return Vector of all pattern names + */ + std::vector get_all_patterns() const; + + /** + * @brief Find the first matching pattern in switch mapping file + * for a switch block name + * @param sw_name Switch block name to match + * @return Matching pattern or empty string if no match + */ + std::string find_matching_pattern(const std::string& sw_name) const; + + /** + * @brief Print statistics about loaded switch blocks + */ + void print_statistics() const; + + /** + * @brief Get the total number of connections across all switch blocks + * @return Total connection count + */ + size_t get_total_connections() const; + + /** + * @brief Get the maximum switch delay in picoseconds + * @return Maximum switch delay in picoseconds + */ + int get_max_switch_delay_ps() const { return switch_delay_max_ps_; } + + /** + * @brief Get the minimum switch delay in picoseconds + * @return Minimum switch delay in picoseconds + */ + int get_min_switch_delay_ps() const { return switch_delay_min_ps_; } + + private: + /** + * @brief Ordered list of switch block patterns + * + * We need to store the switch blocks in the same order defined in the YAML + * file. Later, if a switch block match to multiple patterns defined in the + * YAML file, the pattern defined earliest in the list will be used. + */ + std::vector ordered_switch_block_patterns_; + std::unordered_map switch_block_to_file_; + std::unordered_map dataframes_; + std::unordered_map file_cache_; + + // Global switch delay range + int switch_delay_max_ps_; + int switch_delay_min_ps_; + + DataFrameProcessor processor_; + std::string annotated_dir_; + + // File processing + void load_excel_file(const std::string& pattern, + const std::string& excel_file); + + /** + * @brief Update global switch delay ranges based on all loaded DataFrames + * @param is_annotated_excel Whether the switches are annotated in Excel + */ + void update_and_set_global_switch_delays(const bool is_annotated_excel); + + // Validation + void validate_yaml_structure(const YAML::Node& root); + void validate_excel_files(); +}; + +} // namespace crrgenerator diff --git a/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/data_frame_processor.cpp b/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/data_frame_processor.cpp new file mode 100644 index 00000000000..e21fa6c24d5 --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/data_frame_processor.cpp @@ -0,0 +1,353 @@ +#include "data_frame_processor.h" + +#include "vtr_log.h" + +#include +#include + +namespace crrgenerator { + +// DataFrame implementation + +DataFrame::DataFrame(size_t rows, size_t cols) + : rows_(rows) + , cols_(cols) { + data_.resize(rows); + for (auto& row : data_) { + row.resize(cols); + } +} + +Cell& DataFrame::at(size_t row, size_t col) { + validate_bounds(row, col); + return data_[row][col]; +} + +const Cell& DataFrame::at(size_t row, size_t col) const { + validate_bounds(row, col); + return data_[row][col]; +} + +void DataFrame::resize(size_t rows, size_t cols) { + rows_ = rows; + cols_ = cols; + data_.resize(rows); + for (auto& row : data_) { + row.resize(cols); + } +} + +void DataFrame::clear() { + data_.clear(); + rows_ = 0; + cols_ = 0; + source_file.clear(); + connections = 0; +} + +std::vector DataFrame::get_row(size_t row) const { + if (row >= rows_) { + VTR_LOG_ERROR("Row index out of range: %zu - max %zu", row, rows_); + } + return data_[row]; +} + +std::vector DataFrame::get_column(size_t col) const { + if (col >= cols_) { + VTR_LOG_ERROR("Column index out of range: %zu - max %zu", col, cols_); + } + + std::vector column; + column.reserve(rows_); + for (size_t row = 0; row < rows_; ++row) { + column.push_back(data_[row][col]); + } + return column; +} + +void DataFrame::set_row(size_t row, const std::vector& values) { + if (row >= rows_) { + VTR_LOG_ERROR("Row index out of range: %zu - max %zu", row, rows_); + } + + for (size_t col = 0; col < std::min(values.size(), cols_); ++col) { + data_[row][col] = values[col]; + } +} + +void DataFrame::set_column(size_t col, const std::vector& values) { + if (col >= cols_) { + VTR_LOG_ERROR("Column index out of range: %zu - max %zu", col, cols_); + } + + for (size_t row = 0; row < std::min(values.size(), rows_); ++row) { + data_[row][col] = values[row]; + } +} + +size_t DataFrame::count_non_empty() const { + size_t count = 0; + for (const auto& row : data_) { + for (const auto& cell : row) { + if (!cell.is_empty()) { + ++count; + } + } + } + return count; +} + +size_t DataFrame::count_non_empty_in_range(size_t start_row, size_t start_col, size_t end_row, size_t end_col) const { + size_t count = 0; + for (size_t row = start_row; row < std::min(end_row, rows_); ++row) { + for (size_t col = start_col; col < std::min(end_col, cols_); ++col) { + if (!data_[row][col].is_empty()) { + ++count; + } + } + } + return count; +} + +void DataFrame::validate_bounds(size_t row, size_t col) const { + if (row >= rows_ || col >= cols_) { + VTR_LOG_ERROR("DataFrame index out of range: %zu,%zu - max %zu,%zu\n", row, col, rows_, cols_); + } +} + +// DataFrameProcessor implementation +DataFrame DataFrameProcessor::read_csv(const std::string& filename) { + + validate_csv_file(filename); + + VTR_LOG_DEBUG("Reading CSV file: %s\n", filename.c_str()); + + try { + std::ifstream file(filename); + if (!file.is_open()) { + VTR_LOG_ERROR("Failed to open CSV file: %s\n", filename.c_str()); + return DataFrame(); + } + + VTR_LOG_DEBUG("File %s opened successfully\n", filename.c_str()); + + // Read all lines first to determine dimensions + std::vector lines; + std::string line; + while (std::getline(file, line)) { + if (!line.empty()) { + lines.push_back(line); + } + } + file.close(); + + if (lines.empty()) { + VTR_LOG_ERROR("CSV file appears to be empty: %s\n", filename.c_str()); + return DataFrame(); + } + + // Parse first line to get column count + size_t num_cols = count_csv_columns(lines[0]); + size_t num_rows = lines.size(); + + VTR_LOG_DEBUG("CSV dimensions: %zux%zu\n", num_rows, num_cols); + + DataFrame df(num_rows, num_cols); + VTR_LOG_DEBUG("Created DataFrame with dimensions: %zux%zu\n", num_rows, num_cols); + + // Read all cells + VTR_LOG_DEBUG("Reading cell data...\n"); + size_t cells_read = 0; + + for (size_t row = 0; row < num_rows; ++row) { + std::vector tokens = parse_csv_line(lines[row]); + VTR_ASSERT_DEBUG(tokens.size() <= num_cols); + + for (size_t col = 0; col < tokens.size(); ++col) { + df.at(row, col) = parse_csv_cell(tokens[col]); + cells_read++; + } + + // Fill remaining columns with empty cells if row has fewer columns + for (size_t col = tokens.size(); col < num_cols; ++col) { + df.at(row, col) = Cell(); + } + + // Progress logging for large files + if ((row + 1) % 100 == 0) { + VTR_LOG_DEBUG("Read %zu rows (%zu cells)\n", row + 1, cells_read); + } + } + + df.source_file = std::filesystem::path(filename).stem().string(); + VTR_LOG_DEBUG("Successfully read CSV file with dimensions: %zux%zu, %zu cells\n", + df.rows(), df.cols(), cells_read); + + return df; + + } catch (const std::exception& e) { + VTR_LOG_ERROR("Error reading CSV file %s: %s\n", filename.c_str(), e.what()); + return DataFrame(); + } +} + + +size_t DataFrameProcessor::count_csv_columns(const std::string& line) { + size_t count = 1; + bool in_quotes = false; + + for (char c : line) { + if (c == '"') { + in_quotes = !in_quotes; + } else if (c == ',' && !in_quotes) { + count++; + } + } + + return count; +} + +std::vector DataFrameProcessor::parse_csv_line(const std::string& line) { + std::vector tokens; + std::string current; + bool in_quotes = false; + + for (size_t i = 0; i < line.size(); ++i) { + char c = line[i]; + + if (c == '"') { + // Handle escaped quotes ("") + if (in_quotes && i + 1 < line.size() && line[i + 1] == '"') { + current += '"'; + i++; // Skip next quote + } else { + in_quotes = !in_quotes; + } + } else if (c == ',' && !in_quotes) { + tokens.push_back(current); + current.clear(); + } else { + current += c; + } + } + + tokens.push_back(current); // Add last token + return tokens; +} + +DataFrame DataFrameProcessor::process_dataframe(DataFrame df, + int merge_rows_count, + int merge_cols_count) { + VTR_LOG_DEBUG("Processing dataframe with merge_rows=%d, merge_cols=%d\n", + merge_rows_count, merge_cols_count); + + // Perform row merging + std::vector merged_row_indices = {0, 1}; + merge_rows(df, merged_row_indices); + + // Perform column merging + std::vector merged_col_indices = {2}; + merge_columns(df, merged_col_indices); + + // Count connections in the data area + df.connections = df.count_non_empty_in_range(static_cast(merge_rows_count), + static_cast(merge_cols_count), + df.rows(), + df.cols()); + + VTR_LOG_DEBUG("Processed dataframe with %zu connections\n", df.connections); + + return df; +} + +void DataFrameProcessor::update_switch_delays(const DataFrame& df, + int& switch_delay_max_ps, + int& switch_delay_min_ps) { + for (size_t col = NUM_EMPTY_COLS; col < df.cols(); ++col) { + for (size_t row = NUM_EMPTY_ROWS; row < df.rows(); ++row) { + const Cell& cell = df.at(row, col); + + if (!cell.is_empty() && cell.is_number()) { + int switch_delay_ps = cell.as_int(); + + if (switch_delay_ps > switch_delay_max_ps) { + switch_delay_max_ps = switch_delay_ps; + } + if (switch_delay_ps < switch_delay_min_ps) { + switch_delay_min_ps = switch_delay_ps; + } + } + } + } + + VTR_LOG_DEBUG("Updated switch delays: min=%d, max=%d\n", + switch_delay_min_ps, + switch_delay_max_ps); +} + +// Parse csv cell to our Cell type +Cell DataFrameProcessor::parse_csv_cell(const std::string& value) { + // Trim whitespace + std::string trimmed = value; + trimmed.erase(0, trimmed.find_first_not_of(" \t\r\n")); + trimmed.erase(trimmed.find_last_not_of(" \t\r\n") + 1); + + if (trimmed.empty()) { + return Cell(); // Empty cell + } + + // Check if all characters are digits + bool is_integer = true; + for (char c : trimmed) { + if (!std::isdigit(static_cast(c))) { + is_integer = false; + break; + } + } + + if (is_integer) { + return Cell(std::stoi(trimmed)); + } + + return Cell(trimmed); +} + +void DataFrameProcessor::merge_rows(DataFrame& df, const std::vector& merge_row_indices) { + for (const auto& row : merge_row_indices) { + Cell value; + for (size_t col = 0; col < df.cols(); ++col) { + if (!df.at(row, col).is_empty()) { + value = df.at(row, col); + } + df.at(row, col) = value; + } + } +} + +void DataFrameProcessor::merge_columns(DataFrame& df, const std::vector& merge_col_indices) { + for (const auto& col : merge_col_indices) { + Cell value; + for (size_t row = 0; row < df.rows(); ++row) { + if (!df.at(row, col).is_empty()) { + value = df.at(row, col); + } + df.at(row, col) = value; + } + } +} + +void DataFrameProcessor::validate_csv_file(const std::string& filename) { + if (!std::filesystem::exists(filename)) { + VTR_LOG_ERROR("CSV file does not exist: %s\n", filename.c_str()); + } + + // Check file extension + std::string extension = std::filesystem::path(filename).extension().string(); + std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower); + + if (extension != ".csv") { + VTR_LOG_ERROR("Unsupported file format: %s. Expected .csv\n", extension.c_str()); + } +} + +} // namespace crrgenerator diff --git a/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/data_frame_processor.h b/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/data_frame_processor.h new file mode 100644 index 00000000000..a4c771757dd --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/data_frame_processor.h @@ -0,0 +1,201 @@ +#pragma once + +#include +#include + +#include "crr_common.h" +namespace crrgenerator { + +/** + * @brief Represents a cell in the dataframe + */ +struct Cell { + enum class Type { EMPTY, + STRING, + INTEGER }; + + Type type = Type::EMPTY; + std::string string_value; + int64_t int_value = 0; + + Cell() = default; + explicit Cell(const std::string& value) + : type(Type::STRING) + , string_value(value) {} + explicit Cell(int64_t value) + : type(Type::INTEGER) + , int_value(value) {} + + bool is_empty() const { return type == Type::EMPTY; } + bool is_string() const { return type == Type::STRING; } + bool is_integer() const { return type == Type::INTEGER; } + bool is_number() const { return is_integer(); } + + int64_t as_int() const { + if (is_integer()) return int_value; + if (is_string()) return std::stoll(string_value); + return 0; + } + + std::string as_string() const { + if (is_string()) return string_value; + if (is_integer()) return std::to_string(int_value); + return ""; + } +}; + +/** + * @brief Simple dataframe implementation for processing Excel data + * + * This class provides pandas-like functionality for processing Excel files + * containing switch block configuration data. + */ +class DataFrame { + public: + DataFrame() = default; + DataFrame(size_t rows, size_t cols); + + // Access methods + Cell& at(size_t row, size_t col); + const Cell& at(size_t row, size_t col) const; + Cell& operator()(size_t row, size_t col) { return at(row, col); } + const Cell& operator()(size_t row, size_t col) const { return at(row, col); } + + // Dimensions + size_t rows() const { return rows_; } + size_t cols() const { return cols_; } + std::pair shape() const { return {rows_, cols_}; } + + // Resize operations + void resize(size_t rows, size_t cols); + void clear(); + + // Row/column operations + std::vector get_row(size_t row) const; + std::vector get_column(size_t col) const; + void set_row(size_t row, const std::vector& values); + void set_column(size_t col, const std::vector& values); + + // Statistics and counting + size_t count_non_empty() const; + size_t count_non_empty_in_range(size_t start_row, size_t start_col, size_t end_row, size_t end_col) const; + + // Iteration support + class RowIterator { + public: + RowIterator(DataFrame* df, size_t row) + : df_(df) + , row_(row) {} + + Cell& operator[](size_t col) { return df_->at(row_, col); } + const Cell& operator[](size_t col) const { return df_->at(row_, col); } + + size_t size() const { return df_->cols(); } + size_t get_row_index() const { return row_; } + + RowIterator& operator++() { + ++row_; + return *this; + } + bool operator!=(const RowIterator& other) const { + return df_ != other.df_ || row_ != other.row_; + } + RowIterator& operator*() { return *this; } + + private: + DataFrame* df_; + size_t row_; + }; + + class ConstRowIterator { + public: + ConstRowIterator(const DataFrame* df, size_t row) + : df_(df) + , row_(row) {} + + const Cell& operator[](size_t col) const { return df_->at(row_, col); } + + size_t size() const { return df_->cols(); } + size_t get_row_index() const { return row_; } + + ConstRowIterator& operator++() { + ++row_; + return *this; + } + bool operator!=(const ConstRowIterator& other) const { + return row_ != other.row_; + } + const ConstRowIterator& operator*() const { return *this; } + + private: + const DataFrame* df_; + size_t row_; + }; + + RowIterator begin() { return RowIterator(this, 0); } + RowIterator end() { return RowIterator(this, rows_); } + ConstRowIterator begin() const { return ConstRowIterator(this, 0); } + ConstRowIterator end() const { return ConstRowIterator(this, rows_); } + + // Metadata + std::string source_file; + size_t connections = 0; + + private: + std::vector> data_; + size_t rows_ = 0; + size_t cols_ = 0; + + void validate_bounds(size_t row, size_t col) const; +}; + +/** + * @brief Processes Excel files and converts them to DataFrames + * + * This class handles reading Excel files and performing the cell merging + * operations similar to the Python pandas functionality. + */ +class DataFrameProcessor { + public: + DataFrameProcessor() = default; + + /** + * @brief Read Excel file and create DataFrame + * @param filename Path to Excel file + * @return DataFrame containing the Excel data + * @throws FileException if file cannot be read + */ + DataFrame read_csv(const std::string& filename); + + /** + * @brief Process DataFrame with merging operations + * @param df DataFrame to process + * @param merge_rows_count Number of rows to merge + * @param merge_cols_count Number of columns to merge + * @return Processed DataFrame + */ + DataFrame process_dataframe(DataFrame df, + int merge_rows_count = NUM_EMPTY_ROWS, + int merge_cols_count = NUM_EMPTY_COLS); + + /** + * @brief Update switch delay min/max based on DataFrame content + * @param df DataFrame to analyze + * @param switch_delay_max_ps Current maximum switch delay (will be updated) + * @param switch_delay_min_ps Current minimum switch delay (will be updated) + */ + void update_switch_delays(const DataFrame& df, int& switch_delay_max_ps, int& switch_delay_min_ps); + + private: + // Excel parsing helpers + Cell parse_csv_cell(const std::string& value); + size_t count_csv_columns(const std::string& line); + std::vector parse_csv_line(const std::string& line); + void merge_rows(DataFrame& df, const std::vector& merge_row_indices); + void merge_columns(DataFrame& df, const std::vector& merge_col_indices); + + // Validation + void validate_csv_file(const std::string& filename); +}; + +} // namespace crrgenerator diff --git a/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/node_lookup_manager.cpp b/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/node_lookup_manager.cpp new file mode 100644 index 00000000000..966ecd3a216 --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/node_lookup_manager.cpp @@ -0,0 +1,130 @@ +#include "node_lookup_manager.h" + +#include "vtr_log.h" +#include "vtr_assert.h" + +namespace crrgenerator { + +NodeLookupManager::NodeLookupManager(const RRGraphView& rr_graph, size_t fpga_grid_x, size_t fpga_grid_y) + : rr_graph_(rr_graph) + , fpga_grid_x_(fpga_grid_x) + , fpga_grid_y_(fpga_grid_y) {} + +void NodeLookupManager::initialize() { + VTR_LOG("Initializing NodeLookupManager for %d x %d grid with %zu nodes\n", + fpga_grid_x_, fpga_grid_y_, static_cast(rr_graph_.num_nodes())); + + // Clear existing data + clear(); + + // Initialize lookup structures + column_lookup_.resize(fpga_grid_x_ + 1); + row_lookup_.resize(fpga_grid_y_ + 1); + + // Index all nodes + for (RRNodeId node_id : rr_graph_.nodes()) { + index_node(node_id); + } + + VTR_LOG("Node lookup manager initialized successfully\n"); + print_statistics(); +} + +const std::unordered_map& NodeLookupManager::get_column_nodes(size_t x) const { + VTR_ASSERT(x <= fpga_grid_x_ && x < column_lookup_.size()); + return column_lookup_[x]; +} + +const std::unordered_map& NodeLookupManager::get_row_nodes(size_t y) const { + VTR_ASSERT(y <= fpga_grid_y_ && y < row_lookup_.size()); + return row_lookup_[y]; +} + +std::unordered_map NodeLookupManager::get_combined_nodes(size_t x, size_t y) const { + std::unordered_map combined; + + // Add column nodes + const auto& col_nodes = get_column_nodes(x); + combined.insert(col_nodes.begin(), col_nodes.end()); + + // Add row nodes + const auto& row_nodes = get_row_nodes(y); + combined.insert(row_nodes.begin(), row_nodes.end()); + + return combined; +} + +void NodeLookupManager::print_statistics() const { + VTR_LOG("=== Node Lookup Manager Statistics ===\n"); + VTR_LOG("Grid dimensions: %d x %d\n", fpga_grid_x_, fpga_grid_y_); + + // Count nodes per column + std::vector col_counts; + for (const auto& col_map : column_lookup_) { + col_counts.push_back(col_map.size()); + } + + if (!col_counts.empty()) { + auto [min_col, max_col] = + std::minmax_element(col_counts.begin(), col_counts.end()); + VTR_LOG("Nodes per column: min=%d, max=%d, avg=%f\n", *min_col, *max_col, + std::accumulate(col_counts.begin(), col_counts.end(), 0.0) / col_counts.size()); + } + + // Count nodes per row + std::vector row_counts; + for (const auto& row_map : row_lookup_) { + row_counts.push_back(row_map.size()); + } + + if (!row_counts.empty()) { + auto [min_row, max_row] = + std::minmax_element(row_counts.begin(), row_counts.end()); + VTR_LOG("Nodes per row: min=%d, max=%d, avg=%f\n", *min_row, *max_row, + *max_row, + std::accumulate(row_counts.begin(), row_counts.end(), 0.0) / row_counts.size()); + } +} + +void NodeLookupManager::clear() { + column_lookup_.clear(); + row_lookup_.clear(); +} + +NodeHash NodeLookupManager::build_node_hash(RRNodeId node_id) const { + const std::string& node_ptcs = rr_graph_.rr_nodes().node_ptc_nums_to_string(node_id); + return std::make_tuple(rr_graph_.node_type(node_id), node_ptcs, + rr_graph_.node_xlow(node_id), rr_graph_.node_xhigh(node_id), + rr_graph_.node_ylow(node_id), rr_graph_.node_yhigh(node_id)); +} + +void NodeLookupManager::index_node(RRNodeId node_id) { + NodeHash hash = build_node_hash(node_id); + + short x_low = rr_graph_.node_xlow(node_id); + short x_high = rr_graph_.node_xhigh(node_id); + short y_low = rr_graph_.node_ylow(node_id); + short y_high = rr_graph_.node_yhigh(node_id); + + VTR_ASSERT(static_cast(x_low) <= fpga_grid_x_); + VTR_ASSERT(static_cast(x_high) <= fpga_grid_x_); + VTR_ASSERT(static_cast(y_low) <= fpga_grid_y_); + VTR_ASSERT(static_cast(y_high) <= fpga_grid_y_); + + // Skip spatial indexing for source/sink nodes + if (rr_graph_.node_type(node_id) == e_rr_type::SOURCE || rr_graph_.node_type(node_id) == e_rr_type::SINK) { + return; + } + + // Add to column lookup (for single-column nodes) + if (x_low == x_high) { + column_lookup_[static_cast(x_low)][hash] = node_id; + } + + // Add to row lookup (for single-row nodes) + if (y_low == y_high) { + row_lookup_[static_cast(y_low)][hash] = node_id; + } +} + +} // namespace crrgenerator diff --git a/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/node_lookup_manager.h b/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/node_lookup_manager.h new file mode 100644 index 00000000000..b09833a95d6 --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/node_lookup_manager.h @@ -0,0 +1,78 @@ +#pragma once + +#include "rr_graph_fwd.h" +#include "rr_graph_view.h" + +#include "crr_common.h" + +namespace crrgenerator { + +/** + * @brief Manages efficient node lookup and indexing for RR graphs + * + * This class creates spatial indexes for nodes to enable fast lookup + * by coordinates, type, and other attributes. + */ +class NodeLookupManager { + public: + /** + * @brief Constructor + * @param rr_graph RR graph to index + * @param fpga_grid_x Maximum X coordinate + * @param fpga_grid_y Maximum Y coordinate + */ + NodeLookupManager(const RRGraphView& rr_graph, size_t fpga_grid_x, size_t fpga_grid_y); + + /** + * @brief Initialize lookup tables from RR graph + */ + void initialize(); + + /** + * @brief Get nodes in a specific column + * @param x Column coordinate + * @return Map of hash to node for nodes in column + */ + const std::unordered_map& get_column_nodes(size_t x) const; + + /** + * @brief Get nodes in a specific row + * @param y Row coordinate + * @return Map of hash to node for nodes in row + */ + const std::unordered_map& get_row_nodes(size_t y) const; + + /** + * @brief Get combined nodes for column and row (for switch block processing) + * @param x Column coordinate + * @param y Row coordinate + * @return Combined map of nodes + */ + std::unordered_map get_combined_nodes(size_t x, size_t y) const; + + /** + * @brief Print lookup statistics + */ + void print_statistics() const; + + /** + * @brief Clear all lookup tables + */ + void clear(); + + private: + const RRGraphView& rr_graph_; + // Spatial indexes - SINK and SOURCE nodes are not stored in these two maps + std::vector> column_lookup_; + std::vector> row_lookup_; + + // Grid dimensions + size_t fpga_grid_x_; + size_t fpga_grid_y_; + + // Helper methods + NodeHash build_node_hash(RRNodeId node_id) const; + void index_node(RRNodeId node_id); +}; + +} // namespace crrgenerator diff --git a/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_rr_graph_builder.cpp b/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_rr_graph_builder.cpp index 4c9369b9d88..31ff809925b 100644 --- a/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_rr_graph_builder.cpp +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_rr_graph_builder.cpp @@ -7,6 +7,8 @@ * producing large FPGA fabrics. ***********************************************************************/ /* Headers from vtrutil library */ +#include + #include "vtr_assert.h" #include "vtr_time.h" #include "vtr_log.h" @@ -28,6 +30,14 @@ #include "globals.h" +/** + * @brief Remove dangling chan nodes (chan nodes with no incoming edges) from the rr_graph + * @param grid The device grid + * @param rr_graph_builder The rr_graph builder + */ +static void remove_dangling_chan_nodes(const DeviceGrid& grid, + RRGraphBuilder& rr_graph_builder); + /************************************************************************ * Main function of this file * Builder for a detailed uni-directional tileable rr_graph @@ -71,6 +81,7 @@ void build_tileable_unidir_rr_graph(const std::vector& types, const DeviceGrid& grids, const t_chan_width& chan_width, + const t_crr_opts& crr_opts, const e_switch_block_type& sb_type, const int& Fs, const e_switch_block_type& sb_subtype, @@ -249,6 +260,7 @@ void build_tileable_unidir_rr_graph(const std::vector& typ build_rr_graph_edges(device_ctx.rr_graph, device_ctx.rr_graph_builder, rr_node_driver_switches, + crr_opts, grids, vib_grid, 0, device_chan_width, segment_inf, segment_inf_x, segment_inf_y, @@ -276,7 +288,17 @@ void build_tileable_unidir_rr_graph(const std::vector& typ // Allocate and load routing resource switches, which are derived from the switches from the architecture file, // based on their fanin in the rr graph. This routine also adjusts the rr nodes to point to these new rr switches device_ctx.rr_graph_builder.init_fan_in(); - alloc_and_load_rr_switch_inf(device_ctx.rr_graph_builder, device_ctx.switch_fanin_remap, device_ctx.all_sw_inf, R_minW_nmos, R_minW_pmos, wire_to_arch_ipin_switch, wire_to_rr_ipin_switch); + if (crr_opts.remove_dangling_nodes) { + remove_dangling_chan_nodes(device_ctx.grid, + device_ctx.rr_graph_builder); + } + alloc_and_load_rr_switch_inf(device_ctx.rr_graph_builder, + device_ctx.switch_fanin_remap, + device_ctx.all_sw_inf, + R_minW_nmos, + R_minW_pmos, + wire_to_arch_ipin_switch, + wire_to_rr_ipin_switch); // Save the channel widths for the newly constructed graph device_ctx.chan_width = chan_width; @@ -307,3 +329,45 @@ void build_tileable_unidir_rr_graph(const std::vector& typ check_rr_graph(device_ctx.rr_graph, types, device_ctx.rr_indexed_data, grids, vib_grid, device_ctx.chan_width, e_graph_type::UNIDIR_TILEABLE, false); } + +static void remove_dangling_chan_nodes(const DeviceGrid& grid, + RRGraphBuilder& rr_graph_builder) { + t_rr_graph_storage& rr_nodes = rr_graph_builder.rr_nodes(); + RRSpatialLookup& node_lookup = rr_graph_builder.node_lookup(); + std::vector dangling_nodes; + + for (size_t node_index = 0; node_index < rr_nodes.size(); ++node_index) { + RRNodeId node = RRNodeId(node_index); + if (rr_nodes.node_type(node) == e_rr_type::CHANX || rr_nodes.node_type(node) == e_rr_type::CHANY) { + if (rr_nodes.fan_in(node) == 0) { + dangling_nodes.push_back(node); + } + } + } + rr_nodes.remove_nodes(dangling_nodes); + + node_lookup.clear(); + // Alloc the lookup table + for (e_rr_type rr_type : RR_TYPES) { + node_lookup.resize_nodes(grid.get_num_layers(), + grid.width(), + grid.height(), + rr_type, + NUM_2D_SIDES); + } + + // Add the correct node into the vector + for (size_t node_index = 0; node_index < rr_nodes.size(); ++node_index) { + RRNodeId node = RRNodeId(node_index); + // Set track numbers as a node may have multiple ptc + if (rr_graph_builder.node_contain_multiple_ptc(node)) { + if (rr_nodes.node_type(node) == e_rr_type::CHANX || + rr_nodes.node_type(node) == e_rr_type::CHANY) { + rr_graph_builder.add_track_node_to_lookup(node); + } + } else { + rr_graph_builder.add_node_to_all_locs(node); + } + } + rr_graph_builder.init_fan_in(); +} diff --git a/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_rr_graph_builder.h b/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_rr_graph_builder.h index 5b3894fc5bb..19ad80b4bb4 100644 --- a/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_rr_graph_builder.h +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_rr_graph_builder.h @@ -16,6 +16,7 @@ void build_tileable_unidir_rr_graph(const std::vector& types, const DeviceGrid& grids, const t_chan_width& chan_width, + const t_crr_opts& crr_opts, const e_switch_block_type& sb_type, const int& Fs, const e_switch_block_type& sb_subtype, diff --git a/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_rr_graph_edge_builder.cpp b/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_rr_graph_edge_builder.cpp index 09b25caa5d0..e545f905e4f 100644 --- a/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_rr_graph_edge_builder.cpp +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_rr_graph_edge_builder.cpp @@ -15,6 +15,9 @@ #include "tileable_rr_graph_gsb.h" #include "tileable_rr_graph_edge_builder.h" +#include "crr_generator.h" +#include "crr_edge_builder.h" + /************************************************************************ * Build the edges for all the SOURCE and SINKs nodes: * 1. create edges between SOURCE and OPINs @@ -104,6 +107,7 @@ void build_rr_graph_edges_for_sink_nodes(const RRGraphView& rr_graph, void build_rr_graph_edges(const RRGraphView& rr_graph, RRGraphBuilder& rr_graph_builder, vtr::vector& rr_node_driver_switches, + const t_crr_opts& crr_opts, const DeviceGrid& grids, const VibDeviceGrid& vib_grid, const size_t& layer, @@ -140,6 +144,7 @@ void build_rr_graph_edges(const RRGraphView& rr_graph, build_rr_graph_regular_edges(rr_graph, rr_graph_builder, rr_node_driver_switches, + crr_opts, grids, layer, device_chan_width, @@ -294,7 +299,8 @@ void build_rr_graph_vib_edges(const RRGraphView& rr_graph, void build_rr_graph_regular_edges(const RRGraphView& rr_graph, RRGraphBuilder& rr_graph_builder, - vtr::vector& rr_node_driver_switches, + const vtr::vector& rr_node_driver_switches, + const t_crr_opts& crr_opts, const DeviceGrid& grids, const size_t& layer, const vtr::Point& device_chan_width, @@ -311,6 +317,7 @@ void build_rr_graph_regular_edges(const RRGraphView& rr_graph, const bool& opin2all_sides, const bool& concat_wire, const bool& wire_opposite_side) { + bool build_crr_edges = !crr_opts.sb_templates.empty(); size_t num_edges_to_create = 0; /* Create edges for SOURCE and SINK nodes for a tileable rr_graph */ build_rr_graph_edges_for_source_nodes(rr_graph, rr_graph_builder, rr_node_driver_switches, grids, layer, num_edges_to_create); @@ -318,6 +325,18 @@ void build_rr_graph_regular_edges(const RRGraphView& rr_graph, vtr::Point gsb_range(grids.width() - 1, grids.height() - 1); + // Building CRR Graph + const crrgenerator::CRRConnectionBuilder* crr_connection_builder = nullptr; + crrgenerator::SwitchBlockManager sb_manager; + crrgenerator::NodeLookupManager node_lookup(rr_graph, grids.width(), grids.height()); + crrgenerator::CRRGraphGenerator parser(crr_opts, rr_graph, node_lookup, sb_manager); + if (build_crr_edges) { + sb_manager.initialize(crr_opts.sb_maps, crr_opts.sb_templates, crr_opts.annotated_rr_graph); + node_lookup.initialize(); + parser.run(); + crr_connection_builder = parser.get_connection_builder(); + } + /* Go Switch Block by Switch Block */ for (size_t ix = 0; ix <= gsb_range.x(); ++ix) { for (size_t iy = 0; iy <= gsb_range.y(); ++iy) { @@ -329,25 +348,46 @@ void build_rr_graph_regular_edges(const RRGraphView& rr_graph, device_chan_width, segment_inf_x, segment_inf_y, layer, gsb_coord, perimeter_cb); - /* adapt the track_to_ipin_lookup for the GSB nodes */ t_track2pin_map track2ipin_map; /* [0..track_gsb_side][0..num_tracks][ipin_indices] */ - track2ipin_map = build_gsb_track_to_ipin_map(rr_graph, rr_gsb, grids, segment_inf, Fc_in); + t_pin2track_map opin2track_map; /* [0..gsb_side][0..num_opin_node][track_indices] */ + + /* adapt the track_to_ipin_lookup for the GSB nodes */ + if (!build_crr_edges || crr_opts.preserve_input_pin_connections) { + track2ipin_map = build_gsb_track_to_ipin_map(rr_graph, rr_gsb, grids, segment_inf, Fc_in); + } /* adapt the opin_to_track_map for the GSB nodes */ - t_pin2track_map opin2track_map; /* [0..gsb_side][0..num_opin_node][track_indices] */ - opin2track_map = build_gsb_opin_to_track_map(rr_graph, rr_gsb, grids, segment_inf, Fc_out, opin2all_sides); + if (!build_crr_edges || crr_opts.preserve_output_pin_connections) { + opin2track_map = build_gsb_opin_to_track_map(rr_graph, rr_gsb, grids, segment_inf, Fc_out, opin2all_sides); + } /* adapt the switch_block_conn for the GSB nodes */ t_track2track_map sb_conn; /* [0..from_gsb_side][0..chan_width-1][track_indices] */ - sb_conn = build_gsb_track_to_track_map(rr_graph, rr_gsb, - sb_type, Fs, sb_subtype, sub_fs, concat_wire, wire_opposite_side, - segment_inf); + if (build_crr_edges) { + if (ix != gsb_range.x() && iy != gsb_range.y()) { + build_crr_gsb_track_to_track_edges(rr_graph_builder, rr_gsb, *crr_connection_builder); + } + } else { + sb_conn = build_gsb_track_to_track_map(rr_graph, + rr_gsb, + sb_type, + Fs, + sb_subtype, + sub_fs, + concat_wire, + wire_opposite_side, + segment_inf); + } /* Build edges for a GSB */ - /* Build edges for a GSB */ - build_edges_for_one_tileable_rr_gsb(rr_graph_builder, rr_gsb, - track2ipin_map, opin2track_map, - sb_conn, rr_node_driver_switches, num_edges_to_create); + build_edges_for_one_tileable_rr_gsb(rr_graph_builder, + rr_gsb, + track2ipin_map, + opin2track_map, + sb_conn, + rr_node_driver_switches, + num_edges_to_create); + /* Finish this GSB, go to the next*/ rr_graph_builder.build_edges(true); } diff --git a/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_rr_graph_edge_builder.h b/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_rr_graph_edge_builder.h index 73f8ff65f88..95392850114 100644 --- a/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_rr_graph_edge_builder.h +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_rr_graph_edge_builder.h @@ -23,6 +23,7 @@ void build_rr_graph_edges(const RRGraphView& rr_graph, RRGraphBuilder& rr_graph_builder, vtr::vector& rr_node_driver_switches, + const t_crr_opts& crr_opts, const DeviceGrid& grids, const VibDeviceGrid& vib_grid, const size_t& layer, @@ -78,7 +79,8 @@ void build_rr_graph_vib_edges(const RRGraphView& rr_graph, void build_rr_graph_regular_edges(const RRGraphView& rr_graph, RRGraphBuilder& rr_graph_builder, - vtr::vector& rr_node_driver_switches, + const vtr::vector& rr_node_driver_switches, + const t_crr_opts& crr_opts, const DeviceGrid& grids, const size_t& layer, const vtr::Point& device_chan_width, diff --git a/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_rr_graph_gsb.cpp b/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_rr_graph_gsb.cpp index 0bc0bb9519a..b4f3e767a18 100644 --- a/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_rr_graph_gsb.cpp +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_rr_graph_gsb.cpp @@ -1190,15 +1190,17 @@ void build_edges_for_one_tileable_rr_gsb(RRGraphBuilder& rr_graph_builder, enum e_side gsb_side = side_manager.get_side(); /* Find OPINs */ - for (size_t inode = 0; inode < rr_gsb.get_num_opin_nodes(gsb_side); ++inode) { - const RRNodeId& opin_node = rr_gsb.get_opin_node(gsb_side, inode); - - for (size_t to_side = 0; to_side < opin2track_map[gsb_side][inode].size(); ++to_side) { - /* 1. create edges between OPINs and CHANX|CHANY, using opin2track_map */ - /* add edges to the opin_node */ - for (const RRNodeId& track_node : opin2track_map[gsb_side][inode][to_side]) { - rr_graph_builder.create_edge_in_cache(opin_node, track_node, rr_node_driver_switches[track_node], false); - edge_count++; + if (opin2track_map.size() > 0) { + for (size_t inode = 0; inode < rr_gsb.get_num_opin_nodes(gsb_side); ++inode) { + const RRNodeId& opin_node = rr_gsb.get_opin_node(gsb_side, inode); + + for (size_t to_side = 0; to_side < opin2track_map[gsb_side][inode].size(); ++to_side) { + /* 1. create edges between OPINs and CHANX|CHANY, using opin2track_map */ + /* add edges to the opin_node */ + for (const RRNodeId& track_node : opin2track_map[gsb_side][inode][to_side]) { + rr_graph_builder.create_edge_in_cache(opin_node, track_node, rr_node_driver_switches[track_node], false); + edge_count++; + } } } } @@ -1207,24 +1209,28 @@ void build_edges_for_one_tileable_rr_gsb(RRGraphBuilder& rr_graph_builder, /* For TRACKs to IPINs, we only care LEFT and TOP sides * Skip RIGHT and BOTTOM for the ipin2track_map since they should be handled in other GSBs */ - if ((side_manager.get_side() == rr_gsb.get_cb_chan_side(e_rr_type::CHANX)) - || (side_manager.get_side() == rr_gsb.get_cb_chan_side(e_rr_type::CHANY))) { - /* 2. create edges between CHANX|CHANY and IPINs, using ipin2track_map */ - for (size_t inode = 0; inode < rr_gsb.get_chan_width(gsb_side); ++inode) { - const RRNodeId& chan_node = rr_gsb.get_chan_node(gsb_side, inode); - for (const RRNodeId& ipin_node : track2ipin_map[gsb_side][inode]) { - rr_graph_builder.create_edge_in_cache(chan_node, ipin_node, rr_node_driver_switches[ipin_node], false); - edge_count++; + if (track2ipin_map.size() > 0) { + if ((side_manager.get_side() == rr_gsb.get_cb_chan_side(e_rr_type::CHANX)) + || (side_manager.get_side() == rr_gsb.get_cb_chan_side(e_rr_type::CHANY))) { + /* 2. create edges between CHANX|CHANY and IPINs, using ipin2track_map */ + for (size_t inode = 0; inode < rr_gsb.get_chan_width(gsb_side); ++inode) { + const RRNodeId& chan_node = rr_gsb.get_chan_node(gsb_side, inode); + for (const RRNodeId& ipin_node : track2ipin_map[gsb_side][inode]) { + rr_graph_builder.create_edge_in_cache(chan_node, ipin_node, rr_node_driver_switches[ipin_node], false); + edge_count++; + } } } } /* 3. create edges between CHANX|CHANY and CHANX|CHANY, using track2track_map */ - for (size_t inode = 0; inode < rr_gsb.get_chan_width(gsb_side); ++inode) { - const RRNodeId& chan_node = rr_gsb.get_chan_node(gsb_side, inode); - for (const RRNodeId& track_node : track2track_map[gsb_side][inode]) { - rr_graph_builder.create_edge_in_cache(chan_node, track_node, rr_node_driver_switches[track_node], false); - edge_count++; + if (track2track_map.size() > 0) { + for (size_t inode = 0; inode < rr_gsb.get_chan_width(gsb_side); ++inode) { + const RRNodeId& chan_node = rr_gsb.get_chan_node(gsb_side, inode); + for (const RRNodeId& track_node : track2track_map[gsb_side][inode]) { + rr_graph_builder.create_edge_in_cache(chan_node, track_node, rr_node_driver_switches[track_node], false); + edge_count++; + } } } }