[core] now clock routing is based on tree expansion. Unused part can be disconnected

This commit is contained in:
tangxifan 2024-06-27 15:02:20 -07:00
parent e75fd57af2
commit 4185235a69
3 changed files with 217 additions and 131 deletions

View File

@ -333,6 +333,20 @@ vtr::Point<int> ClockNetwork::spine_switch_point(
return spine_switch_coords_[spine_id][size_t(switch_point_id)]; return spine_switch_coords_[spine_id][size_t(switch_point_id)];
} }
std::vector<ClockSwitchPointId> ClockNetwork::find_spine_switch_points_with_coord(
const ClockSpineId& spine_id,
const vtr::Point<int>& coord) const {
VTR_ASSERT(valid_spine_id(spine_id));
std::vector<ClockSwitchPointId> 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<ClockInternalDriverId> std::vector<ClockInternalDriverId>
ClockNetwork::spine_switch_point_internal_drivers( ClockNetwork::spine_switch_point_internal_drivers(
const ClockSpineId& spine_id, const ClockSpineId& spine_id,

View File

@ -125,6 +125,10 @@ class ClockNetwork {
vtr::Point<int> spine_switch_point( vtr::Point<int> spine_switch_point(
const ClockSpineId& spine_id, const ClockSpineId& spine_id,
const ClockSwitchPointId& switch_point_id) const; const ClockSwitchPointId& switch_point_id) const;
/* Find all the switching points at a given coordinate */
std::vector<ClockSwitchPointId> find_spine_switch_points_with_coord(const ClockSpineId& spine_id, const vtr::Point<int>& coord) const;
std::vector<ClockInternalDriverId> spine_switch_point_internal_drivers( std::vector<ClockInternalDriverId> spine_switch_point_internal_drivers(
const ClockSpineId& spine_id, const ClockSpineId& spine_id,
const ClockSwitchPointId& switch_point_id) const; const ClockSwitchPointId& switch_point_id) const;

View File

@ -78,126 +78,90 @@ static int build_clock_tree_net_map(
return CMD_EXEC_SUCCESS; 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<vtr::Point<int>> 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<int> src_coord = spine_coords[icoord];
vtr::Point<int> 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 * Route a switching points between spines
* - connect between two routing tracks (left or right turns) * - connect between two routing tracks (left or right turns)
* - connect internal driver to routing track * - connect internal driver to routing track
*******************************************************************/ *******************************************************************/
static int route_spine_switch_points( static int route_clock_spine_switch_point(
VprRoutingAnnotation& vpr_routing_annotation, const RRGraphView& rr_graph, VprRoutingAnnotation& vpr_routing_annotation,
const RRGraphView& rr_graph,
const RRClockSpatialLookup& clk_rr_lookup, const RRClockSpatialLookup& clk_rr_lookup,
const vtr::vector<RRNodeId, ClusterNetId>& rr_node_gnets, const vtr::vector<RRNodeId, ClusterNetId>& rr_node_gnets,
const std::map<ClockTreePinId, ClusterNetId>& tree2clk_pin_map, const std::map<ClockTreePinId, ClusterNetId>& tree2clk_pin_map,
const ClockNetwork& clk_ntwk, const ClockTreeId& clk_tree, const ClockNetwork& clk_ntwk, const ClockTreeId& clk_tree,
const ClockSpineId& ispine, const ClockTreePinId& ipin, const ClockSpineId& ispine, const ClockTreePinId& ipin,
const ClockSwitchPointId& switch_point_id,
const bool& verbose) { const bool& verbose) {
VTR_LOGV(verbose, "Routing switch points of spine '%s'...\n", VTR_LOGV(verbose, "Routing switch points of spine '%s'...\n",
clk_ntwk.spine_name(ispine).c_str()); clk_ntwk.spine_name(ispine).c_str());
for (ClockSwitchPointId switch_point_id : vtr::Point<int> src_coord =
clk_ntwk.spine_switch_points(ispine)) { clk_ntwk.spine_switch_point(ispine, switch_point_id);
vtr::Point<int> src_coord = ClockSpineId des_spine =
clk_ntwk.spine_switch_point(ispine, switch_point_id); clk_ntwk.spine_switch_point_tap(ispine, switch_point_id);
ClockSpineId des_spine = vtr::Point<int> des_coord = clk_ntwk.spine_start_point(des_spine);
clk_ntwk.spine_switch_point_tap(ispine, switch_point_id); Direction src_spine_direction = clk_ntwk.spine_direction(ispine);
vtr::Point<int> des_coord = clk_ntwk.spine_start_point(des_spine); Direction des_spine_direction = clk_ntwk.spine_direction(des_spine);
Direction src_spine_direction = clk_ntwk.spine_direction(ispine); ClockLevelId src_spine_level = clk_ntwk.spine_level(ispine);
Direction des_spine_direction = clk_ntwk.spine_direction(des_spine); ClockLevelId des_spine_level = clk_ntwk.spine_level(des_spine);
ClockLevelId src_spine_level = clk_ntwk.spine_level(ispine); RRNodeId src_node =
ClockLevelId des_spine_level = clk_ntwk.spine_level(des_spine); clk_rr_lookup.find_node(src_coord.x(), src_coord.y(), clk_tree,
RRNodeId src_node = src_spine_level, ipin, src_spine_direction);
clk_rr_lookup.find_node(src_coord.x(), src_coord.y(), clk_tree, RRNodeId des_node =
src_spine_level, ipin, src_spine_direction); clk_rr_lookup.find_node(des_coord.x(), des_coord.y(), clk_tree,
RRNodeId des_node = des_spine_level, ipin, des_spine_direction);
clk_rr_lookup.find_node(des_coord.x(), des_coord.y(), clk_tree, VTR_ASSERT(rr_graph.valid_node(src_node));
des_spine_level, ipin, des_spine_direction); VTR_ASSERT(rr_graph.valid_node(des_node));
VTR_ASSERT(rr_graph.valid_node(src_node)); /* Internal drivers may appear at the switch point. Check if there are
VTR_ASSERT(rr_graph.valid_node(des_node)); * any defined and related rr_node found as incoming edges. If the
/* Internal drivers may appear at the switch point. Check if there are * global net is mapped to the internal driver, use it as the previous
* any defined and related rr_node found as incoming edges. If the * node */
* global net is mapped to the internal driver, use it as the previous size_t use_int_driver = 0;
* node */ if (!clk_ntwk
size_t use_int_driver = 0; .spine_switch_point_internal_drivers(ispine, switch_point_id)
if (!clk_ntwk .empty() &&
.spine_switch_point_internal_drivers(ispine, switch_point_id) tree2clk_pin_map.find(ipin) != tree2clk_pin_map.end()) {
.empty() && for (RREdgeId cand_edge : rr_graph.node_in_edges(des_node)) {
tree2clk_pin_map.find(ipin) != tree2clk_pin_map.end()) { RRNodeId opin_node = rr_graph.edge_src_node(cand_edge);
for (RREdgeId cand_edge : rr_graph.node_in_edges(des_node)) { if (OPIN != rr_graph.node_type(opin_node)) {
RRNodeId opin_node = rr_graph.edge_src_node(cand_edge); continue;
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());
} }
} if (rr_node_gnets[opin_node] != tree2clk_pin_map.at(ipin)) {
if (use_int_driver > 1) { continue;
VTR_LOG_ERROR( }
"Found %lu internal drivers for the switching point (%lu, %lu) for " /* This is the opin node we need, use it as the internal driver */
"spine '%s'!\n Expect only 1!\n", vpr_routing_annotation.set_rr_node_prev_node(rr_graph, des_node,
use_int_driver, src_coord.x(), src_coord.y(), opin_node);
clk_ntwk.spine_name(ispine).c_str()); vpr_routing_annotation.set_rr_node_net(opin_node,
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)); tree2clk_pin_map.at(ipin));
vpr_routing_annotation.set_rr_node_net(des_node, vpr_routing_annotation.set_rr_node_net(des_node,
tree2clk_pin_map.at(ipin)); 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; 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 * - Only connect to tap points which are mapped by a global net
*******************************************************************/ *******************************************************************/
static int route_spine_taps( 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 RRClockSpatialLookup& clk_rr_lookup,
const vtr::vector<RRNodeId, ClusterNetId>& rr_node_gnets, const vtr::vector<RRNodeId, ClusterNetId>& rr_node_gnets,
const std::map<ClockTreePinId, ClusterNetId>& tree2clk_pin_map, const std::map<ClockTreePinId, ClusterNetId>& tree2clk_pin_map,
@ -216,6 +182,7 @@ static int route_spine_taps(
const bool& verbose) { const bool& verbose) {
std::vector<vtr::Point<int>> spine_coords = std::vector<vtr::Point<int>> spine_coords =
clk_ntwk.spine_coordinates(ispine); clk_ntwk.spine_coordinates(ispine);
size_t spine_tap_cnt = 0;
/* Route the spine-to-IPIN connections (only for the last level) */ /* Route the spine-to-IPIN connections (only for the last level) */
if (clk_ntwk.is_last_level(ispine)) { if (clk_ntwk.is_last_level(ispine)) {
VTR_LOGV(verbose, "Routing clock taps of spine '%s'...\n", 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)); VTR_ASSERT(rr_graph.valid_node(des_node));
vpr_routing_annotation.set_rr_node_prev_node(rr_graph, des_node, vpr_routing_annotation.set_rr_node_prev_node(rr_graph, des_node,
src_node); src_node);
if (tree2clk_pin_map.find(ipin) != tree2clk_pin_map.end()) { vpr_routing_annotation.set_rr_node_net(
vpr_routing_annotation.set_rr_node_net( src_node, tree2clk_pin_map.at(ipin));
src_node, tree2clk_pin_map.at(ipin)); vpr_routing_annotation.set_rr_node_net(
vpr_routing_annotation.set_rr_node_net( des_node, tree2clk_pin_map.at(ipin));
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<RRNodeId, ClusterNetId>& rr_node_gnets,
const std::map<ClockTreePinId, ClusterNetId>& 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<vtr::Point<int>> 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<int> src_coord = spine_coords[icoord + 1];
vtr::Point<int> 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; return CMD_EXEC_SUCCESS;
} }
@ -287,29 +365,19 @@ static int route_clock_tree_rr_graph(
const std::map<ClockTreePinId, ClusterNetId>& tree2clk_pin_map, const std::map<ClockTreePinId, ClusterNetId>& tree2clk_pin_map,
const ClockNetwork& clk_ntwk, const ClockTreeId& clk_tree, const ClockNetwork& clk_ntwk, const ClockTreeId& clk_tree,
const bool& disable_unused_trees, const bool& disable_unused_trees,
const bool& disable_unused_spines,
const bool& verbose) { const bool& verbose) {
for (auto ispine : clk_ntwk.spines(clk_tree)) { for (auto ipin : clk_ntwk.pins(clk_tree)) {
VTR_LOGV(verbose, "Routing spine '%s'...\n", /* Do not route unused clock spines */
clk_ntwk.spine_name(ispine).c_str()); if (disable_unused_trees && tree2clk_pin_map.find(ipin) == tree2clk_pin_map.end()) {
for (auto ipin : clk_ntwk.pins(clk_tree)) { VTR_LOGV(verbose, "Skip routing unused tree '%s' pin '%lu'...\n",
/* Do not route unused clock spines */ clk_ntwk.tree_name(clk_tree).c_str(), size_t(ipin));
if (disable_unused_trees && tree2clk_pin_map.find(ipin) == tree2clk_pin_map.end()) { continue;
VTR_LOGV(verbose, "Skip routing backbone of unused spine '%s'...\n", }
clk_ntwk.spine_name(ispine).c_str()); /* Start with the top-level spines. Recursively walk through coordinates and expand on switch points */
continue; 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);
/* Route the spine from starting point to ending point */ if (CMD_EXEC_SUCCESS != status) {
std::vector<vtr::Point<int>> 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)) {
return CMD_EXEC_FATAL_ERROR; return CMD_EXEC_FATAL_ERROR;
} }
} }
@ -384,7 +452,7 @@ int route_clock_rr_graph(
clk_ntwk.tree_name(itree).c_str()); clk_ntwk.tree_name(itree).c_str());
status = route_clock_tree_rr_graph( status = route_clock_tree_rr_graph(
vpr_routing_annotation, vpr_device_ctx.rr_graph, clk_rr_lookup, 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) { if (status == CMD_EXEC_FATAL_ERROR) {
return status; return status;
} }