diff --git a/.gitignore b/.gitignore index 890ad1ac45..599c0f1854 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/doc/src/arch/reference.rst b/doc/src/arch/reference.rst index 5322fe473b..9ef24a8df9 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/libarchfpga/src/device_grid.cpp b/libs/libarchfpga/src/device_grid.cpp index f0644873a4..6873ffff6f 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 5a5f1c0105..74f3f18b88 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.h b/libs/librrgraph/src/base/rr_graph_builder.h index daf4274227..3ba04b3dc0 100644 --- a/libs/librrgraph/src/base/rr_graph_builder.h +++ b/libs/librrgraph/src/base/rr_graph_builder.h @@ -351,6 +351,17 @@ 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 for the RR Graph. + * This method does not preserve the order of edges. If you're + * calling it after partition_edges has been called, you need + * to call it again. + * + * @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. */ diff --git a/libs/librrgraph/src/base/rr_graph_storage.cpp b/libs/librrgraph/src/base/rr_graph_storage.cpp index 1e15f127b8..8494f8caf6 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,48 @@ 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_); + + 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())); +} + + void t_rr_graph_storage::assign_first_edges() { VTR_ASSERT(node_first_edge_.empty()); @@ -68,31 +113,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 +148,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()); diff --git a/libs/librrgraph/src/base/rr_graph_storage.h b/libs/librrgraph/src/base/rr_graph_storage.h index ab1c844cd1..7d06795f49 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 begin 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 @@ -776,6 +784,15 @@ 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 for the RR Graph. + * This method does not preserve the order of edges. If you're + * calling it after partition_edges has been called, you need + * to call it again. + * + * @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 diff --git a/libs/librrgraph/src/base/rr_graph_view.cpp b/libs/librrgraph/src/base/rr_graph_view.cpp index f23762c3e7..0b8b04b13e 100644 --- a/libs/librrgraph/src/base/rr_graph_view.cpp +++ b/libs/librrgraph/src/base/rr_graph_view.cpp @@ -129,5 +129,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 ca825d1564..ca87ef4ea1 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 -- */ @@ -584,18 +586,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))); } diff --git a/vpr/src/route/rr_graph_generation/rr_graph.cpp b/vpr/src/route/rr_graph_generation/rr_graph.cpp index 05b972646e..c4bbed47df 100644 --- a/vpr/src/route/rr_graph_generation/rr_graph.cpp +++ b/vpr/src/route/rr_graph_generation/rr_graph.cpp @@ -8,6 +8,7 @@ #include "get_parallel_segs.h" #include "physical_types.h" #include "physical_types_util.h" +#include "rr_graph_fwd.h" #include "rr_graph_view.h" #include "rr_rc_data.h" #include "switchblock_types.h" @@ -32,6 +33,7 @@ #include "rr_graph_intra_cluster.h" #include "rr_graph_tile_nodes.h" #include "rr_graph_3d.h" +#include "rr_graph_interposer.h" #include "rr_graph_timing_params.h" #include "check_rr_graph.h" #include "echo_files.h" @@ -1667,6 +1669,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 = mark_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 +2130,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 +2152,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 4aca0d82b7..fd4fefdc1e 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, 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 0000000000..5499b10549 --- /dev/null +++ b/vpr/src/route/rr_graph_generation/rr_graph_interposer.cpp @@ -0,0 +1,351 @@ +#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 wether 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 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. + */ +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. + */ +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 mark_interposer_cut_edges_for_removal(const RRGraphView& rr_graph, const DeviceGrid& grid) { + std::vector edges_to_be_removed; + + // Loop over all RREdgeIds and mark ones that cross a cutline to be removed + + 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); + + 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) { + if (node_direction == Direction::INC) { + // Anything above cut_loc_y shouldn't exist + 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) { + // Anything below cut_loc_y (inclusive) shouldn't exist + 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) { + if (node_direction == Direction::INC) { + // Anything to the right of cut_loc_x shouldn't exist + 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) { + // Anything to the left of cut_loc_x (inclusive) shouldn't exist + 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 0000000000..b4349afa38 --- /dev/null +++ b/vpr/src/route/rr_graph_generation/rr_graph_interposer.h @@ -0,0 +1,34 @@ +#pragma once + +/** + * @file interposer_cut.h + * @brief This file implements functions that: + * (1) Marks all edges that cross an interposer cut for removal + * (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. + */ + +#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. + * + * @return std::vector List of all edges that cross an interposer cut line. + */ +std::vector mark_interposer_cut_edges_for_removal(const RRGraphView& rr_graph, const DeviceGrid& grid); + +/** + * @brief Shortens the channel nodes that cross an interposer cut line + * + * @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 3932fefd1e..78970d4208 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(),