From 4185235a690915bdbe59c4e5aae2dbc8319dc898 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Thu, 27 Jun 2024 15:02:20 -0700 Subject: [PATCH] [core] now clock routing is based on tree expansion. Unused part can be disconnected --- .../src/base/clock_network.cpp | 14 + .../src/base/clock_network.h | 4 + .../src/annotation/route_clock_rr_graph.cpp | 330 +++++++++++------- 3 files changed, 217 insertions(+), 131 deletions(-) diff --git a/libs/libclkarchopenfpga/src/base/clock_network.cpp b/libs/libclkarchopenfpga/src/base/clock_network.cpp index 70fd840f6..a34868be5 100644 --- a/libs/libclkarchopenfpga/src/base/clock_network.cpp +++ b/libs/libclkarchopenfpga/src/base/clock_network.cpp @@ -333,6 +333,20 @@ vtr::Point ClockNetwork::spine_switch_point( return spine_switch_coords_[spine_id][size_t(switch_point_id)]; } +std::vector ClockNetwork::find_spine_switch_points_with_coord( + const ClockSpineId& spine_id, + const vtr::Point& coord) const { + VTR_ASSERT(valid_spine_id(spine_id)); + std::vector ret; + for (size_t i = 0; i < spine_switch_points_[spine_id].size(); ++i) { + if (spine_switch_coords_[spine_id][i] == coord) { + ret.push_back(ClockSwitchPointId(i)); + } + } + + return ret; +} + std::vector ClockNetwork::spine_switch_point_internal_drivers( const ClockSpineId& spine_id, diff --git a/libs/libclkarchopenfpga/src/base/clock_network.h b/libs/libclkarchopenfpga/src/base/clock_network.h index 25042524a..0fc15e471 100644 --- a/libs/libclkarchopenfpga/src/base/clock_network.h +++ b/libs/libclkarchopenfpga/src/base/clock_network.h @@ -125,6 +125,10 @@ class ClockNetwork { vtr::Point spine_switch_point( const ClockSpineId& spine_id, const ClockSwitchPointId& switch_point_id) const; + + /* Find all the switching points at a given coordinate */ + std::vector find_spine_switch_points_with_coord(const ClockSpineId& spine_id, const vtr::Point& coord) const; + std::vector spine_switch_point_internal_drivers( const ClockSpineId& spine_id, const ClockSwitchPointId& switch_point_id) const; diff --git a/openfpga/src/annotation/route_clock_rr_graph.cpp b/openfpga/src/annotation/route_clock_rr_graph.cpp index 3c56b6d79..cabb763c6 100644 --- a/openfpga/src/annotation/route_clock_rr_graph.cpp +++ b/openfpga/src/annotation/route_clock_rr_graph.cpp @@ -78,126 +78,90 @@ static int build_clock_tree_net_map( return CMD_EXEC_SUCCESS; } -/******************************************************************** - * Route a selected clock spine in a staight line - * - route the spine from the starting point to the ending point - *******************************************************************/ -static int route_straight_spines( - VprRoutingAnnotation& vpr_routing_annotation, const RRGraphView& rr_graph, - const RRClockSpatialLookup& clk_rr_lookup, - const ClockNetwork& clk_ntwk, const ClockTreeId& clk_tree, - const ClockSpineId& ispine, const ClockTreePinId& ipin, - const bool& verbose) { - std::vector> spine_coords = - clk_ntwk.spine_coordinates(ispine); - VTR_LOGV(verbose, "Routing backbone of spine '%s'...\n", - clk_ntwk.spine_name(ispine).c_str()); - for (size_t icoord = 0; icoord < spine_coords.size() - 1; ++icoord) { - vtr::Point src_coord = spine_coords[icoord]; - vtr::Point des_coord = spine_coords[icoord + 1]; - Direction src_spine_direction = clk_ntwk.spine_direction(ispine); - Direction des_spine_direction = clk_ntwk.spine_direction(ispine); - ClockLevelId src_spine_level = clk_ntwk.spine_level(ispine); - ClockLevelId des_spine_level = clk_ntwk.spine_level(ispine); - RRNodeId src_node = - clk_rr_lookup.find_node(src_coord.x(), src_coord.y(), clk_tree, - src_spine_level, ipin, src_spine_direction); - RRNodeId des_node = - clk_rr_lookup.find_node(des_coord.x(), des_coord.y(), clk_tree, - des_spine_level, ipin, des_spine_direction); - VTR_ASSERT(rr_graph.valid_node(src_node)); - VTR_ASSERT(rr_graph.valid_node(des_node)); - vpr_routing_annotation.set_rr_node_prev_node(rr_graph, des_node, - src_node); - } - return CMD_EXEC_SUCCESS; -} - /******************************************************************** * Route a switching points between spines * - connect between two routing tracks (left or right turns) * - connect internal driver to routing track *******************************************************************/ -static int route_spine_switch_points( - VprRoutingAnnotation& vpr_routing_annotation, const RRGraphView& rr_graph, +static int route_clock_spine_switch_point( + VprRoutingAnnotation& vpr_routing_annotation, + const RRGraphView& rr_graph, const RRClockSpatialLookup& clk_rr_lookup, const vtr::vector& rr_node_gnets, const std::map& tree2clk_pin_map, const ClockNetwork& clk_ntwk, const ClockTreeId& clk_tree, const ClockSpineId& ispine, const ClockTreePinId& ipin, + const ClockSwitchPointId& switch_point_id, const bool& verbose) { VTR_LOGV(verbose, "Routing switch points of spine '%s'...\n", clk_ntwk.spine_name(ispine).c_str()); - for (ClockSwitchPointId switch_point_id : - clk_ntwk.spine_switch_points(ispine)) { - vtr::Point src_coord = - clk_ntwk.spine_switch_point(ispine, switch_point_id); - ClockSpineId des_spine = - clk_ntwk.spine_switch_point_tap(ispine, switch_point_id); - vtr::Point des_coord = clk_ntwk.spine_start_point(des_spine); - Direction src_spine_direction = clk_ntwk.spine_direction(ispine); - Direction des_spine_direction = clk_ntwk.spine_direction(des_spine); - ClockLevelId src_spine_level = clk_ntwk.spine_level(ispine); - ClockLevelId des_spine_level = clk_ntwk.spine_level(des_spine); - RRNodeId src_node = - clk_rr_lookup.find_node(src_coord.x(), src_coord.y(), clk_tree, - src_spine_level, ipin, src_spine_direction); - RRNodeId des_node = - clk_rr_lookup.find_node(des_coord.x(), des_coord.y(), clk_tree, - des_spine_level, ipin, des_spine_direction); - VTR_ASSERT(rr_graph.valid_node(src_node)); - VTR_ASSERT(rr_graph.valid_node(des_node)); - /* Internal drivers may appear at the switch point. Check if there are - * any defined and related rr_node found as incoming edges. If the - * global net is mapped to the internal driver, use it as the previous - * node */ - size_t use_int_driver = 0; - if (!clk_ntwk - .spine_switch_point_internal_drivers(ispine, switch_point_id) - .empty() && - tree2clk_pin_map.find(ipin) != tree2clk_pin_map.end()) { - for (RREdgeId cand_edge : rr_graph.node_in_edges(des_node)) { - RRNodeId opin_node = rr_graph.edge_src_node(cand_edge); - if (OPIN != rr_graph.node_type(opin_node)) { - continue; - } - if (rr_node_gnets[opin_node] != tree2clk_pin_map.at(ipin)) { - continue; - } - /* This is the opin node we need, use it as the internal driver */ - vpr_routing_annotation.set_rr_node_prev_node(rr_graph, des_node, - opin_node); - vpr_routing_annotation.set_rr_node_net(opin_node, - tree2clk_pin_map.at(ipin)); - vpr_routing_annotation.set_rr_node_net(des_node, - tree2clk_pin_map.at(ipin)); - use_int_driver++; - VTR_LOGV(verbose, "Routing switch points of spine '%s' at the switching point (%lu, %lu) using internal driver\n", - clk_ntwk.spine_name(ispine).c_str(), src_coord.x(), src_coord.y()); + vtr::Point src_coord = + clk_ntwk.spine_switch_point(ispine, switch_point_id); + ClockSpineId des_spine = + clk_ntwk.spine_switch_point_tap(ispine, switch_point_id); + vtr::Point des_coord = clk_ntwk.spine_start_point(des_spine); + Direction src_spine_direction = clk_ntwk.spine_direction(ispine); + Direction des_spine_direction = clk_ntwk.spine_direction(des_spine); + ClockLevelId src_spine_level = clk_ntwk.spine_level(ispine); + ClockLevelId des_spine_level = clk_ntwk.spine_level(des_spine); + RRNodeId src_node = + clk_rr_lookup.find_node(src_coord.x(), src_coord.y(), clk_tree, + src_spine_level, ipin, src_spine_direction); + RRNodeId des_node = + clk_rr_lookup.find_node(des_coord.x(), des_coord.y(), clk_tree, + des_spine_level, ipin, des_spine_direction); + VTR_ASSERT(rr_graph.valid_node(src_node)); + VTR_ASSERT(rr_graph.valid_node(des_node)); + /* Internal drivers may appear at the switch point. Check if there are + * any defined and related rr_node found as incoming edges. If the + * global net is mapped to the internal driver, use it as the previous + * node */ + size_t use_int_driver = 0; + if (!clk_ntwk + .spine_switch_point_internal_drivers(ispine, switch_point_id) + .empty() && + tree2clk_pin_map.find(ipin) != tree2clk_pin_map.end()) { + for (RREdgeId cand_edge : rr_graph.node_in_edges(des_node)) { + RRNodeId opin_node = rr_graph.edge_src_node(cand_edge); + if (OPIN != rr_graph.node_type(opin_node)) { + continue; } - } - if (use_int_driver > 1) { - VTR_LOG_ERROR( - "Found %lu internal drivers for the switching point (%lu, %lu) for " - "spine '%s'!\n Expect only 1!\n", - use_int_driver, src_coord.x(), src_coord.y(), - clk_ntwk.spine_name(ispine).c_str()); - return CMD_EXEC_FATAL_ERROR; - } - if (use_int_driver == 1) { - continue; /* Used internal driver, early pass */ - } - vpr_routing_annotation.set_rr_node_prev_node(rr_graph, des_node, - src_node); - /* It could happen that there is no net mapped some clock pin, skip the - * net mapping */ - if (tree2clk_pin_map.find(ipin) != tree2clk_pin_map.end()) { - vpr_routing_annotation.set_rr_node_net(src_node, + if (rr_node_gnets[opin_node] != tree2clk_pin_map.at(ipin)) { + continue; + } + /* This is the opin node we need, use it as the internal driver */ + vpr_routing_annotation.set_rr_node_prev_node(rr_graph, des_node, + opin_node); + vpr_routing_annotation.set_rr_node_net(opin_node, tree2clk_pin_map.at(ipin)); vpr_routing_annotation.set_rr_node_net(des_node, tree2clk_pin_map.at(ipin)); + use_int_driver++; + VTR_LOGV(verbose, "Routing switch points of spine '%s' at the switching point (%lu, %lu) using internal driver\n", + clk_ntwk.spine_name(ispine).c_str(), src_coord.x(), src_coord.y()); } } + if (use_int_driver > 1) { + VTR_LOG_ERROR( + "Found %lu internal drivers for the switching point (%lu, %lu) for " + "spine '%s'!\n Expect only 1!\n", + use_int_driver, src_coord.x(), src_coord.y(), + clk_ntwk.spine_name(ispine).c_str()); + return CMD_EXEC_FATAL_ERROR; + } + if (use_int_driver == 1) { + continue; /* Used internal driver, early pass */ + } + vpr_routing_annotation.set_rr_node_prev_node(rr_graph, des_node, + src_node); + /* It could happen that there is no net mapped some clock pin, skip the + * net mapping */ + if (tree2clk_pin_map.find(ipin) != tree2clk_pin_map.end()) { + vpr_routing_annotation.set_rr_node_net(src_node, + tree2clk_pin_map.at(ipin)); + vpr_routing_annotation.set_rr_node_net(des_node, + tree2clk_pin_map.at(ipin)); + } return CMD_EXEC_SUCCESS; } @@ -207,7 +171,9 @@ static int route_spine_switch_points( * - Only connect to tap points which are mapped by a global net *******************************************************************/ static int route_spine_taps( - VprRoutingAnnotation& vpr_routing_annotation, const RRGraphView& rr_graph, + VprRoutingAnnotation& vpr_routing_annotation, + bool& spine_usage, + const RRGraphView& rr_graph, const RRClockSpatialLookup& clk_rr_lookup, const vtr::vector& rr_node_gnets, const std::map& tree2clk_pin_map, @@ -216,6 +182,7 @@ static int route_spine_taps( const bool& verbose) { std::vector> spine_coords = clk_ntwk.spine_coordinates(ispine); + size_t spine_tap_cnt = 0; /* Route the spine-to-IPIN connections (only for the last level) */ if (clk_ntwk.is_last_level(ispine)) { VTR_LOGV(verbose, "Routing clock taps of spine '%s'...\n", @@ -259,16 +226,127 @@ static int route_spine_taps( VTR_ASSERT(rr_graph.valid_node(des_node)); vpr_routing_annotation.set_rr_node_prev_node(rr_graph, des_node, src_node); - if (tree2clk_pin_map.find(ipin) != tree2clk_pin_map.end()) { - vpr_routing_annotation.set_rr_node_net( - src_node, tree2clk_pin_map.at(ipin)); - vpr_routing_annotation.set_rr_node_net( - des_node, tree2clk_pin_map.at(ipin)); - } + vpr_routing_annotation.set_rr_node_net( + src_node, tree2clk_pin_map.at(ipin)); + vpr_routing_annotation.set_rr_node_net( + des_node, tree2clk_pin_map.at(ipin)); + /* Increment upon any required tap */ + spine_tap_cnt++; } } } } + if (spine_tap_cnt) { + spine_usage = true; + } + + return CMD_EXEC_SUCCESS; +} + +/******************************************************************** + * Recursively route a clock spine on an existing routing resource graph + * The strategy is to route spine one by one + * - route the spine from the ending point to starting point (straight line) + * - for each stops on the staight line, route the spine-to-spine switching points + * - for each switching point (des_spine_top|bottom), go recursively + * - If the downstream spine at any switching point is not used, disconnect + * - If any stop on the spine (straght line) is not used, disconnect + * - route the spine-to-IPIN connections (only for the last level) + * + * des_spine_top[0...N] + * ^ ^ ^ ^ + * | | | | + * spine_start ---->+---->+---->+---->+->spine_end + * | | | | + * v v v v + * des_spine_bottom[0...N] + * + * <-------------------------------------------- direction to walk through + * + *******************************************************************/ +static int rec_expand_and_route_clock_spine( + VprRoutingAnnotation& vpr_routing_annotation, + bool& spine_usage, + const RRGraphView& rr_graph, + const RRClockSpatialLookup& clk_rr_lookup, + const vtr::vector& rr_node_gnets, + const std::map& tree2clk_pin_map, + const ClockNetwork& clk_ntwk, const ClockTreeId& clk_tree, + const ClockSpineId& curr_spine, const ClockTreePinId& curr_pin, + const bool& disable_unused_spines, + const bool& verbose) { + int status = CMD_EXEC_SUCCESS; + bool curr_spine_usage = false; + std::vector> spine_coords = + clk_ntwk.spine_coordinates(curr_spine); + /* We expand from the the ending point to starting point on the straight line. As such, it is easy to turn off spines by any stop */ + std::reverse(spine_coords.begin(), spine_coords.end()); + /* The spine should go in a straight line, connect all the stops on the line */ + for (size_t icoord = 0; icoord < spine_coords.size() - 1; ++icoord) { + vtr::Point src_coord = spine_coords[icoord + 1]; + vtr::Point des_coord = spine_coords[icoord]; + bool curr_stop_usage = false; + /* Expand on the switching point here */ + for (ClockSwitchPointId switch_point_id : + clk_ntwk.find_spine_switch_points_with_coord(curr_spine, src_coord)) { + ClockSpineId des_spine = + clk_ntwk.spine_switch_point_tap(curr_spine, switch_point_id); + /* Go recursively for the destination spine */ + bool curr_branch_usage = false; + status = rec_expand_and_route_clock_spine(vpr_routing_annotation, curr_branch_usage, rr_graph, clk_rr_lookup, rr_node_gnets, tree2clk_pin_map, clk_ntwk, clk_tree, des_spine, curr_pin, verbose); + if (CMD_EXEC_SUCCESS != status) { + return CMD_EXEC_FATAL_ERROR; + } + /* Connect only when the destination spine is used */ + if (disable_unused_spines && !curr_branch_usage) { + VTR_LOGV(verbose, "Disconnect switching from spine '%s' to spine '%s' as downstream is not used\n", + clk_ntwk.spine_name(curr_spine).c_str(), clk_ntwk.spine_name(des_spine).c_str()); + continue; + } + curr_stop_usage = true; + /* Now connect to next spine, internal drivers may join */ + status = route_clock_spine_switch_point(vpr_routing_annotation, rr_graph, clk_rr_lookup, rr_node_gnets, tree2clk_pin_map, clk_ntwk, clk_tree, curr_spine, curr_pin, switch_point_id, verbose); + if (CMD_EXEC_SUCCESS != status) { + return CMD_EXEC_FATAL_ERROR; + } + } + if (disable_unused_spines && !curr_stop_usage) { + VTR_LOGV(verbose, "Disconnect backbone of spine '%s' from (x=%lu, y=%lu) to (x=%lu, y=%lu) as downstream is not used\n", + clk_ntwk.spine_name(curr_spine).c_str(), src_coord.x(), src_coord.y(), des_coord.x(), des_coord.y()); + continue; + } + /* Connect only when next stop is used */ + Direction src_spine_direction = clk_ntwk.spine_direction(ispine); + Direction des_spine_direction = clk_ntwk.spine_direction(ispine); + ClockLevelId src_spine_level = clk_ntwk.spine_level(ispine); + ClockLevelId des_spine_level = clk_ntwk.spine_level(ispine); + RRNodeId src_node = + clk_rr_lookup.find_node(src_coord.x(), src_coord.y(), clk_tree, + src_spine_level, ipin, src_spine_direction); + RRNodeId des_node = + clk_rr_lookup.find_node(des_coord.x(), des_coord.y(), clk_tree, + des_spine_level, ipin, des_spine_direction); + VTR_ASSERT(rr_graph.valid_node(src_node)); + VTR_ASSERT(rr_graph.valid_node(des_node)); + VTR_LOGV(verbose, "Routing backbone of spine '%s' from (x=%lu, y=%lu) to (x=%lu, y=%lu)...\n", + clk_ntwk.spine_name(curr_spine).c_str(), src_coord.x(), src_coord.y(), des_coord.x(), des_coord.y()); + vpr_routing_annotation.set_rr_node_prev_node(rr_graph, des_node, + src_node); + curr_spine_usage = true; + } + + bool curr_tap_usage = false; + /* For last level, we just connect tap points */ + status = route_spine_taps(vpr_routing_annotation, curr_tap_usage, rr_graph, clk_rr_lookup, rr_node_gnets, tree2clk_pin_map, clk_ntwk, clk_tree, curr_spine, curr_pin, verbose); + if (CMD_EXEC_SUCCESS != status) { + return CMD_EXEC_FATAL_ERROR; + } + if (curr_tap_usage) { + curr_spine_usage = true; + } + + /* Update status */ + spine_usage = curr_spine_usage; return CMD_EXEC_SUCCESS; } @@ -287,29 +365,19 @@ static int route_clock_tree_rr_graph( const std::map& tree2clk_pin_map, const ClockNetwork& clk_ntwk, const ClockTreeId& clk_tree, const bool& disable_unused_trees, + const bool& disable_unused_spines, const bool& verbose) { - for (auto ispine : clk_ntwk.spines(clk_tree)) { - VTR_LOGV(verbose, "Routing spine '%s'...\n", - clk_ntwk.spine_name(ispine).c_str()); - for (auto ipin : clk_ntwk.pins(clk_tree)) { - /* Do not route unused clock spines */ - if (disable_unused_trees && tree2clk_pin_map.find(ipin) == tree2clk_pin_map.end()) { - VTR_LOGV(verbose, "Skip routing backbone of unused spine '%s'...\n", - clk_ntwk.spine_name(ispine).c_str()); - continue; - } - /* Route the spine from starting point to ending point */ - std::vector> spine_coords = - clk_ntwk.spine_coordinates(ispine); - if (CMD_EXEC_SUCCESS != route_straight_spines(vpr_routing_annotation, rr_graph, clk_rr_lookup, clk_ntwk, clk_tree, ispine, ipin, verbose)) { - return CMD_EXEC_FATAL_ERROR; - } - /* Route the opin/spine-to-spine switching points */ - if (CMD_EXEC_SUCCESS != route_spine_switch_points(vpr_routing_annotation, rr_graph, clk_rr_lookup, rr_node_gnets, tree2clk_pin_map, clk_ntwk, clk_tree, ispine, ipin, verbose)) { - return CMD_EXEC_FATAL_ERROR; - } - /* Route the spine-to-IPIN connections (only for the last level) */ - if (CMD_EXEC_SUCCESS != route_spine_taps(vpr_routing_annotation, rr_graph, clk_rr_lookup, rr_node_gnets, tree2clk_pin_map, clk_ntwk, clk_tree, ispine, ipin, verbose)) { + for (auto ipin : clk_ntwk.pins(clk_tree)) { + /* Do not route unused clock spines */ + if (disable_unused_trees && tree2clk_pin_map.find(ipin) == tree2clk_pin_map.end()) { + VTR_LOGV(verbose, "Skip routing unused tree '%s' pin '%lu'...\n", + clk_ntwk.tree_name(clk_tree).c_str(), size_t(ipin)); + continue; + } + /* Start with the top-level spines. Recursively walk through coordinates and expand on switch points */ + for (auto top_spine : clk_ntwk.tree_top_spine(clk_tree)) { + int status = rec_expand_and_route_clock_spine(vpr_routing_annotation, spine_usage, rr_graph, clk_rr_lookup, rr_node_gnets, tree2clk_pin_map, clk_ntwk, clk_tree, top_spine, ipin, verbose); + if (CMD_EXEC_SUCCESS != status) { return CMD_EXEC_FATAL_ERROR; } } @@ -384,7 +452,7 @@ int route_clock_rr_graph( clk_ntwk.tree_name(itree).c_str()); status = route_clock_tree_rr_graph( vpr_routing_annotation, vpr_device_ctx.rr_graph, clk_rr_lookup, - rr_node_gnets, tree2clk_pin_map, clk_ntwk, itree, disable_unused_trees, verbose); + rr_node_gnets, tree2clk_pin_map, clk_ntwk, itree, disable_unused_trees, disable_unused_spines, verbose); if (status == CMD_EXEC_FATAL_ERROR) { return status; }