Track non-configurable edge usage to enable pruning.
Fixes #526
Signed-off-by: Keith Rothman <537074+litghost@users.noreply.github.com>
diff --git a/vpr/src/route/check_route.cpp b/vpr/src/route/check_route.cpp
index f0ec889..e602ee4 100644
--- a/vpr/src/route/check_route.cpp
+++ b/vpr/src/route/check_route.cpp
@@ -13,6 +13,8 @@
#include "rr_graph.h"
#include "check_rr_graph.h"
#include "read_xml_arch_file.h"
+#include "route_tree_type.h"
+#include "route_tree_timing.h"
/******************** Subroutines local to this module **********************/
static void check_node_and_range(int inode, enum e_route_type route_type);
@@ -26,6 +28,7 @@
enum e_route_type route_type);
static bool check_non_configurable_edges(ClusterNetId net, const t_non_configurable_rr_sets& non_configurable_rr_sets);
+static void check_net_for_stubs(ClusterNetId net);
/************************ Subroutine definitions ****************************/
@@ -155,6 +158,8 @@
check_non_configurable_edges(net_id, non_configurable_rr_sets);
+ check_net_for_stubs(net_id);
+
reset_flags(net_id, connected_to_route);
} /* End for each net */
@@ -776,3 +781,90 @@
return true;
}
+
+// StubFinder traverses a net definition and find ensures that all nodes that
+// are children of a configurable node have at least one sink.
+class StubFinder {
+ public:
+ // Checks specified net for stubs, return true if at least one stub is
+ // found.
+ bool CheckNet(ClusterNetId net);
+
+ // Returns set of stub nodes.
+ const std::set<int>& stub_nodes() {
+ return stub_nodes_;
+ }
+
+ private:
+ bool RecurseTree(t_rt_node* rt_root);
+
+ // Set of stub nodes
+ // Note this is an ordered set so that node output is sorted by node
+ // id.
+ std::set<int> stub_nodes_;
+};
+
+//Cheks for stubs in a net's routing.
+//
+//Stubs (routing branches which don't connect to SINKs) serve no purpose, and only chew up wiring unecessarily.
+//The only exception are stubs required by non-configurable switches (e.g. shorts).
+//
+//We treat any configurable stubs as an error.
+void check_net_for_stubs(ClusterNetId net) {
+ auto& route_ctx = g_vpr_ctx.mutable_routing();
+
+ StubFinder stub_finder;
+
+ bool any_stubs = stub_finder.CheckNet(net);
+ if (any_stubs) {
+ auto& cluster_ctx = g_vpr_ctx.clustering();
+ std::string msg = vtr::string_fmt("Route tree for net '%s' (#%zu) contains stub branches rooted at:\n",
+ cluster_ctx.clb_nlist.net_name(net).c_str(), size_t(net));
+ for (int inode : stub_finder.stub_nodes()) {
+ msg += vtr::string_fmt(" %s\n", describe_rr_node(inode).c_str());
+ }
+
+ VPR_THROW(VPR_ERROR_ROUTE, msg.c_str());
+ }
+}
+
+bool StubFinder::CheckNet(ClusterNetId net) {
+ stub_nodes_.clear();
+
+ t_rt_node* rt_root = traceback_to_route_tree(net);
+ RecurseTree(rt_root);
+ free_route_tree(rt_root);
+
+ return !stub_nodes_.empty();
+}
+
+// Returns true if this node is a stub.
+bool StubFinder::RecurseTree(t_rt_node* rt_root) {
+ auto& device_ctx = g_vpr_ctx.device();
+
+ if (rt_root->u.child_list == nullptr) {
+ //If a leaf of the route tree is not a SINK, then it is a stub
+ if (device_ctx.rr_nodes[rt_root->inode].type() != SINK) {
+ return true; //It is the current root of this stub
+ } else {
+ return false;
+ }
+ } else {
+ bool is_stub = true;
+ for (t_linked_rt_edge* edge = rt_root->u.child_list; edge != nullptr; edge = edge->next) {
+ bool driver_switch_configurable = device_ctx.rr_switch_inf[edge->iswitch].configurable();
+
+ bool child_is_stub = RecurseTree(edge->child);
+ if (!child_is_stub) {
+ // Because the child was not a stub, this node is not a stub.
+ is_stub = false;
+ } else if (driver_switch_configurable) {
+ // This child was stub, and we drove it from a configurable
+ // edge, this is an error.
+ stub_nodes_.insert(edge->child->inode);
+ }
+ }
+
+ return is_stub;
+ }
+}
diff --git a/vpr/src/route/route_timing.cpp b/vpr/src/route/route_timing.cpp
index 5e2f358..42aa2a9 100644
--- a/vpr/src/route/route_timing.cpp
+++ b/vpr/src/route/route_timing.cpp
@@ -1539,7 +1539,9 @@
profiling::net_rebuild_start();
// convert the previous iteration's traceback into a route tree
- rt_root = traceback_to_route_tree(net_id);
+ auto& device_ctx = g_vpr_ctx.device();
+ std::vector<int> non_config_node_set_usage(device_ctx.rr_non_config_node_sets.size(), 0);
+ rt_root = traceback_to_route_tree(net_id, &non_config_node_set_usage);
//Santiy check that route tree and traceback are equivalent before pruning
VTR_ASSERT_DEBUG(verify_traceback_route_tree_equivalent(route_ctx.trace[net_id].head, rt_root));
@@ -1549,7 +1551,7 @@
VTR_ASSERT_SAFE(should_route_net(net_id, connections_inf, true));
//Prune the branches of the tree that don't legally lead to sinks
- rt_root = prune_route_tree(rt_root, connections_inf);
+ rt_root = prune_route_tree(rt_root, connections_inf, &non_config_node_set_usage);
//Now that the tree has been pruned, we can free the old traceback
// NOTE: this must happen *after* pruning since it changes the
diff --git a/vpr/src/route/route_tree_timing.cpp b/vpr/src/route/route_tree_timing.cpp
index cff2126..b93aee3 100644
--- a/vpr/src/route/route_tree_timing.cpp
+++ b/vpr/src/route/route_tree_timing.cpp
@@ -53,9 +53,9 @@
bool verify_route_tree_recurr(t_rt_node* node, std::set<int>& seen_nodes);
-t_rt_node* prune_route_tree_recurr(t_rt_node* node, CBRR& connections_inf, bool congested);
+static t_rt_node* prune_route_tree_recurr(t_rt_node* node, CBRR& connections_inf, bool congested, std::vector<int>* non_config_node_set_usage);
-static t_trace* traceback_to_route_tree_branch(t_trace* trace, std::map<int, t_rt_node*>& rr_node_to_rt);
+static t_trace* traceback_to_route_tree_branch(t_trace* trace, std::map<int, t_rt_node*>& rr_node_to_rt, std::vector<int>* non_config_node_set_usage);
static std::pair<t_trace*, t_trace*> traceback_from_route_tree_recurr(t_trace* head, t_trace* tail, const t_rt_node* node);
@@ -702,12 +702,20 @@
}
/*************** Conversion between traceback and route tree *******************/
-t_rt_node* traceback_to_route_tree(ClusterNetId inet) {
+t_rt_node* traceback_to_route_tree(ClusterNetId inet, std::vector<int>* non_config_node_set_usage) {
auto& route_ctx = g_vpr_ctx.routing();
- return traceback_to_route_tree(route_ctx.trace[inet].head);
+ return traceback_to_route_tree(route_ctx.trace[inet].head, non_config_node_set_usage);
+}
+
+t_rt_node* traceback_to_route_tree(ClusterNetId inet) {
+ return traceback_to_route_tree(inet, nullptr);
}
t_rt_node* traceback_to_route_tree(t_trace* head) {
+ return traceback_to_route_tree(head, nullptr);
+}
+
+t_rt_node* traceback_to_route_tree(t_trace* head, std::vector<int>* non_config_node_set_usage) {
/* Builds a skeleton route tree from a traceback
* does not calculate R_upstream, C_downstream, or Tdel at all (left uninitialized)
* returns the root of the converted route tree
@@ -723,7 +731,7 @@
t_trace* trace = head;
while (trace) { //Each branch
- trace = traceback_to_route_tree_branch(trace, rr_node_to_rt);
+ trace = traceback_to_route_tree_branch(trace, rr_node_to_rt, non_config_node_set_usage);
}
// Due to the recursive nature of traceback_to_route_tree_branch,
// the source node is not properly configured.
@@ -740,7 +748,9 @@
//Note that R_upstream and C_downstream are initialized to NaN
//
//Returns the t_trace defining the start of the next branch
-static t_trace* traceback_to_route_tree_branch(t_trace* trace, std::map<int, t_rt_node*>& rr_node_to_rt) {
+static t_trace* traceback_to_route_tree_branch(t_trace* trace,
+ std::map<int, t_rt_node*>& rr_node_to_rt,
+ std::vector<int>* non_config_node_set_usage) {
t_trace* next = nullptr;
if (trace) {
@@ -779,8 +789,21 @@
next = trace->next;
if (iswitch != OPEN) {
+ // Keep track of non-configurable set usage by looking for
+ // configurable edges that extend out of a non-configurable set.
+ //
+ // Each configurable edges from the non-configurable set is a
+ // usage of the set.
+ auto& device_ctx = g_vpr_ctx.device();
+ auto set_itr = device_ctx.rr_node_to_non_config_node_set.find(inode);
+ if (non_config_node_set_usage != nullptr && set_itr != device_ctx.rr_node_to_non_config_node_set.end()) {
+ if (device_ctx.rr_switch_inf[iswitch].configurable()) {
+ (*non_config_node_set_usage)[set_itr->second] += 1;
+ }
+ }
+
//Recursively construct the remaining branch
- next = traceback_to_route_tree_branch(next, rr_node_to_rt);
+ next = traceback_to_route_tree_branch(next, rr_node_to_rt, non_config_node_set_usage);
//Get the created child
itr = rr_node_to_rt.find(trace->next->index);
@@ -894,7 +917,7 @@
//Prunes a route tree (recursively) based on congestion and the 'force_prune' argument
//
//Returns true if the current node was pruned
-t_rt_node* prune_route_tree_recurr(t_rt_node* node, CBRR& connections_inf, bool force_prune) {
+static t_rt_node* prune_route_tree_recurr(t_rt_node* node, CBRR& connections_inf, bool force_prune, std::vector<int>* non_config_node_set_usage) {
//Recursively traverse the route tree rooted at node and remove any congested
//sub-trees
@@ -904,6 +927,11 @@
auto& route_ctx = g_vpr_ctx.routing();
bool congested = (route_ctx.rr_node_route_inf[node->inode].occ() > device_ctx.rr_nodes[node->inode].capacity());
+ int node_set = -1;
+ auto itr = device_ctx.rr_node_to_non_config_node_set.find(node->inode);
+ if (itr != device_ctx.rr_node_to_non_config_node_set.end()) {
+ node_set = itr->second;
+ }
if (congested) {
//This connection is congested -- prune it
@@ -920,7 +948,8 @@
t_linked_rt_edge* prev_edge = nullptr;
t_linked_rt_edge* edge = node->u.child_list;
while (edge) {
- t_rt_node* child = prune_route_tree_recurr(edge->child, connections_inf, force_prune);
+ t_rt_node* child = prune_route_tree_recurr(edge->child,
+ connections_inf, force_prune, non_config_node_set_usage);
if (!child) { //Child was pruned
@@ -935,6 +964,13 @@
t_linked_rt_edge* old_edge = edge;
edge = edge->next;
+ // After removing an edge, check if non_config_node_set_usage
+ // needs an update.
+ if (node_set != -1 && device_ctx.rr_switch_inf[old_edge->iswitch].configurable()) {
+ (*non_config_node_set_usage)[node_set] -= 1;
+ VTR_ASSERT((*non_config_node_set_usage)[node_set] >= 0);
+ }
+
free_linked_rt_edge(old_edge);
//Note prev_edge is unchanged
@@ -982,18 +1018,8 @@
// We take the corresponding actions:
// 1) Prune the node.
//
- // 2) Prune the node only if force_prune is set
- //
- // Since the node was reached non-configurably it is illegal to
- // remove it independently of any other nodes which are non-
- // configurably connected to it.
- //
- // As a result we only remove a non-configurably connected node
- // if force_prune is set.
- //
- // Note that if a node upstream of the non-configurably connected
- // nodes becomes congested, force_prune will be set and the *entire*
- // set of non-configurably connected nodes will be removed.
+ // 2) Prune the node only if the node set is unused or if force_prune
+ // is set.
//
// 3) Prune the node.
//
@@ -1005,6 +1031,12 @@
bool reached_non_configurably = false;
if (node->parent_node) {
reached_non_configurably = !device_ctx.rr_switch_inf[node->parent_switch].configurable();
+
+ if (reached_non_configurably) {
+ // Check if this non-configurable node set is in use.
+ VTR_ASSERT(node_set != -1);
+ force_prune = force_prune || (*non_config_node_set_usage)[node_set] == 0;
+ }
}
if (reached_non_configurably && !force_prune) {
@@ -1022,7 +1054,7 @@
}
}
-t_rt_node* prune_route_tree(t_rt_node* rt_root, CBRR& connections_inf) {
+t_rt_node* prune_route_tree(t_rt_node* rt_root, CBRR& connections_inf, std::vector<int>* non_config_node_set_usage) {
/* Prune a skeleton route tree of illegal branches - when there is at least 1 congested node on the path to a sink
* This is the top level function to be called with the SOURCE node as root.
* Returns true if the entire tree has been pruned.
@@ -1040,7 +1072,7 @@
VTR_ASSERT_MSG(route_ctx.rr_node_route_inf[rt_root->inode].occ() <= device_ctx.rr_nodes[rt_root->inode].capacity(),
"Route tree root/SOURCE should never be congested");
- return prune_route_tree_recurr(rt_root, connections_inf, false);
+ return prune_route_tree_recurr(rt_root, connections_inf, false, non_config_node_set_usage);
}
void pathfinder_update_cost_from_route_tree(const t_rt_node* rt_root, int add_or_sub, float pres_fac) {
diff --git a/vpr/src/route/route_tree_timing.h b/vpr/src/route/route_tree_timing.h
index 6dfc122..e998efa 100644
--- a/vpr/src/route/route_tree_timing.h
+++ b/vpr/src/route/route_tree_timing.h
@@ -49,11 +49,13 @@
void print_route_tree_inf(const t_rt_node* rt_root);
void print_route_tree_congestion(const t_rt_node* rt_root);
-t_rt_node* traceback_to_route_tree(t_trace* head);
t_rt_node* traceback_to_route_tree(ClusterNetId inet);
+t_rt_node* traceback_to_route_tree(ClusterNetId inet, std::vector<int>* non_config_node_set_usage);
+t_rt_node* traceback_to_route_tree(t_trace* head);
+t_rt_node* traceback_to_route_tree(t_trace* head, std::vector<int>* non_config_node_set_usage);
t_trace* traceback_from_route_tree(ClusterNetId inet, const t_rt_node* root, int num_remaining_sinks);
-t_rt_node* prune_route_tree(t_rt_node* rt_root, CBRR& connections_inf);
+t_rt_node* prune_route_tree(t_rt_node* rt_root, CBRR& connections_inf, std::vector<int>* non_config_node_set_usage);
void pathfinder_update_cost_from_route_tree(const t_rt_node* rt_root, int add_or_sub, float pres_fac);