Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
998ba66
Add interposer crossing wire cutting
AmirhosseinPoolad Nov 6, 2025
2546789
Fix edge cutting
AmirhosseinPoolad Nov 10, 2025
21d5080
Fix integration with scatter-gather patterns
AmirhosseinPoolad Nov 11, 2025
72e8933
Add doxygen to interposer cut functions
AmirhosseinPoolad Nov 12, 2025
bc9a40a
Fix some code duplication issues
AmirhosseinPoolad Nov 12, 2025
b6f592a
Remove experimental interposer status from documentation
AmirhosseinPoolad Nov 12, 2025
349d672
Fix issue with interposers and 3D connection blocks
AmirhosseinPoolad Nov 12, 2025
e659e23
Address PR comments
AmirhosseinPoolad Nov 13, 2025
e9876b3
Change RR Graph warning enum to be clearer
AmirhosseinPoolad Nov 13, 2025
2d6de4f
Address PR comments on RR Graph changes
AmirhosseinPoolad Nov 17, 2025
16f26ce
Add doxygen to interposer_cut.h
AmirhosseinPoolad Nov 17, 2025
ea396cd
Fix formatting
AmirhosseinPoolad Nov 17, 2025
4d95419
Add .clangd to gitignore
AmirhosseinPoolad Nov 17, 2025
e2cee0c
Make remove_edges use ranges
AmirhosseinPoolad Nov 17, 2025
4573aa9
Pass RRNodeIds by value
AmirhosseinPoolad Nov 17, 2025
6b39edc
Add newline to 2D scatter-gather comment
AmirhosseinPoolad Nov 17, 2025
0fc6d07
Rename interposer_cut to rr_graph_interposer
AmirhosseinPoolad Nov 17, 2025
dda2e43
Add static function declarations to rr_graph_interposer.cpp
AmirhosseinPoolad Nov 17, 2025
61ebf99
Add std::pair include
AmirhosseinPoolad Nov 17, 2025
561b5fb
Fix formatting issues
AmirhosseinPoolad Nov 17, 2025
b57b08f
Fix bug with checking if device has interposer cuts
AmirhosseinPoolad Nov 17, 2025
0c0f149
Unset partitioned_ flag in RR Graph remove edges
AmirhosseinPoolad Nov 18, 2025
ef319d7
Improve RR Graph Interposer comments
AmirhosseinPoolad Nov 18, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -160,3 +160,4 @@ cmake-build-release
# Clangd
#
compile_commands.json
.clangd
3 changes: 1 addition & 2 deletions doc/src/arch/reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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 <interdie_wire> tags as children to specify the connection between the two sides.

Expand Down Expand Up @@ -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:

Expand Down
16 changes: 16 additions & 0 deletions libs/libarchfpga/src/device_grid.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,19 @@ void DeviceGrid::count_instances() {
}
}
}

bool DeviceGrid::has_interposer_cuts() const {
for (const std::vector<int>& layer_h_cuts : horizontal_interposer_cuts_) {
if (!layer_h_cuts.empty()) {
return true;
}
}

for (const std::vector<int>& layer_v_cuts : vertical_interposer_cuts_) {
if (!layer_v_cuts.empty()) {
return true;
}
}

return false;
}
5 changes: 5 additions & 0 deletions libs/libarchfpga/src/device_grid.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
12 changes: 12 additions & 0 deletions libs/librrgraph/src/base/rr_graph_builder.h
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,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<RREdgeId>& 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. */
Expand Down
72 changes: 61 additions & 11 deletions libs/librrgraph/src/base/rr_graph_storage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 <algorithm>
#include <cstddef>
#include <ranges>

void t_rr_graph_storage::reserve_edges(size_t num_edges) {
edge_src_node_.reserve(num_edges);
Expand Down Expand Up @@ -57,6 +60,50 @@ 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<RREdgeId>& 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<size_t>(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());

Expand All @@ -68,39 +115,42 @@ 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;
}
}
}

// 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());
Expand Down
18 changes: 18 additions & 0 deletions libs/librrgraph/src/base/rr_graph_storage.h
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,14 @@ class t_rr_graph_storage {
return vtr::StrongIdRange<RREdgeId>(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<RREdgeId> all_edges() const {
return vtr::StrongIdRange<RREdgeId>(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
Expand Down Expand Up @@ -776,6 +784,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<RREdgeId>& rr_edges_to_remove);

/* Edge finalization methods */

/** @brief Counts the number of rr switches needed based on fan in to support mux
Expand Down
2 changes: 0 additions & 2 deletions libs/librrgraph/src/base/rr_graph_view.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,5 +129,3 @@ bool RRGraphView::validate_in_edges() const {
}
return true;
}


19 changes: 16 additions & 3 deletions libs/librrgraph/src/base/rr_graph_view.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 -- */
Expand Down Expand Up @@ -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<RREdgeId> 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)));
}

Expand Down
23 changes: 23 additions & 0 deletions vpr/src/route/rr_graph_generation/rr_graph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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"
Expand Down Expand Up @@ -1667,6 +1669,16 @@ static std::function<void(t_chan_width*)> 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<RREdgeId> 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);
Expand Down Expand Up @@ -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);
}
Expand All @@ -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)
Expand Down
12 changes: 7 additions & 5 deletions vpr/src/route/rr_graph_generation/rr_graph.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you know what these are, comment what each value controls.
Is value sweep a channel width sweep?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did a quick check, this enum and the warning system is completely unused. The RR Graph generation code does set these warning bits, but the results are not used anywhere. I think I'll do a follow up PR to remove it.

* @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,
Expand Down
Loading