Skip to content

Commit 831b520

Browse files
[Clocks][RouterLookahead] Improved lookahead for clock networks
These changed improve how the router lookahead is computed when clock networks exist in the FPGA fabric. When clock networks exist, special segments are created which have the resource type GCLK. It is assumed that these clock network resources are dedicated, fast resources and should only be used by clocks. The problem is that the router lookahead was doing dijkstra floods which were using these segments. This means that the general fabric was assuming that it could use these resources, and the clock fabric was assuming that it could hop off to the general fabric. To fix this, I added a check during the Dijkstra flood (which is used to find the lookahead costs) which ignores edges that go from/to a GENERAL routing segment to/from a GCLK segment. This bakes the following assumption into the lookahead: Signals that start on general routing will take general routing to the target, and signals that start on clock routing will take clock routing to the target. I also found an issue with orthogonal segments with clock networks. Clock ribs and spines are only CHANX and CHANY and do not have orthogonal components. This causes an issue when the lookahead is created and these elements have zero delay, since VPR populates zero delay entries with the delays on the orthogonal segments. Fixed this by directly checking if the values are uninitialized and preventing using orthogonal data from different resources. I also changed the segment frequency of clock segments to zero by default, since this is used in VPR to imply that the segments are not part of the general routing fabric. I tested this on an architecture with zero delays on the clock network and verified that the lookahead did not have any delay contributions from the clock network.
1 parent debd87c commit 831b520

4 files changed

Lines changed: 58 additions & 17 deletions

File tree

libs/librrgraph/src/utils/alloc_and_load_rr_indexed_data.cpp

Lines changed: 35 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,10 @@ static void load_rr_indexed_data_base_costs(const RRGraphView& rr_graph,
3030

3131
static float get_delay_normalization_fac(const vtr::vector<RRIndexedDataId, t_rr_indexed_data>& rr_indexed_data, const bool echo_enabled, const char* echo_file_name, bool device_model_warnings);
3232

33-
static void load_rr_indexed_data_T_values(const RRGraphView& rr_graph,
34-
vtr::vector<RRIndexedDataId, t_rr_indexed_data>& rr_indexed_data,
35-
int route_verbosity,
36-
bool device_model_warnings);
33+
static vtr::vector<RRIndexedDataId, int> load_rr_indexed_data_T_values(const RRGraphView& rr_graph,
34+
vtr::vector<RRIndexedDataId, t_rr_indexed_data>& rr_indexed_data,
35+
int route_verbosity,
36+
bool device_model_warnings);
3737

3838
/**
3939
* @brief Computes average R, Tdel, and Cinternal of fan-in switches for a given node.
@@ -56,7 +56,10 @@ static void calculate_average_switch(const RRGraphView& rr_graph,
5656
short& buffered,
5757
const vtr::vector<RRNodeId, std::vector<RREdgeId>>& fan_in_list);
5858

59-
static void fixup_rr_indexed_data_T_values(vtr::vector<RRIndexedDataId, t_rr_indexed_data>& rr_indexed_data, size_t num_segment);
59+
static void fixup_rr_indexed_data_T_values(vtr::vector<RRIndexedDataId, t_rr_indexed_data>& rr_indexed_data,
60+
const vtr::vector<RRIndexedDataId, int>& num_nodes_of_index,
61+
const std::vector<t_segment_inf>& segment_inf,
62+
size_t num_segment);
6063

6164
static std::vector<size_t> count_rr_segment_types(const RRGraphView& rr_graph,
6265
const vtr::vector<RRIndexedDataId, t_rr_indexed_data>& rr_indexed_data);
@@ -161,9 +164,9 @@ void alloc_and_load_rr_indexed_data(const RRGraphView& rr_graph,
161164
rr_indexed_data[index].seg_index = seg_ptr->seg_index;
162165
}
163166

164-
load_rr_indexed_data_T_values(rr_graph, rr_indexed_data, route_verbosity, device_model_warnings);
167+
vtr::vector<RRIndexedDataId, int> num_nodes_of_index = load_rr_indexed_data_T_values(rr_graph, rr_indexed_data, route_verbosity, device_model_warnings);
165168

166-
fixup_rr_indexed_data_T_values(rr_indexed_data, total_num_segment);
169+
fixup_rr_indexed_data_T_values(rr_indexed_data, num_nodes_of_index, segment_inf, total_num_segment);
167170

168171
load_rr_indexed_data_base_costs(rr_graph, rr_indexed_data, base_cost_type, echo_enabled, echo_file_name, device_model_warnings);
169172

@@ -516,11 +519,14 @@ static float get_delay_normalization_fac(const vtr::vector<RRIndexedDataId, t_rr
516519
* - Base cost calculation for each cost_index
517520
* - Lookahead map computation
518521
* - Placement Delay Matrix computation
522+
*
523+
* Returns the number of RR nodes found for each cost index. A count of zero means the
524+
* cost index's T-values were left at their default of zero.
519525
*/
520-
static void load_rr_indexed_data_T_values(const RRGraphView& rr_graph,
521-
vtr::vector<RRIndexedDataId, t_rr_indexed_data>& rr_indexed_data,
522-
int route_verbosity,
523-
bool device_model_warnings) {
526+
static vtr::vector<RRIndexedDataId, int> load_rr_indexed_data_T_values(const RRGraphView& rr_graph,
527+
vtr::vector<RRIndexedDataId, t_rr_indexed_data>& rr_indexed_data,
528+
int route_verbosity,
529+
bool device_model_warnings) {
524530
vtr::vector<RRNodeId, std::vector<RREdgeId>> fan_in_list = get_fan_in_list(rr_graph);
525531

526532
vtr::vector<RRIndexedDataId, int> num_nodes_of_index(rr_indexed_data.size(), 0);
@@ -682,6 +688,8 @@ static void load_rr_indexed_data_T_values(const RRGraphView& rr_graph,
682688
VTR_LOGV_WARN(device_model_warnings && route_verbosity <= 1 && num_nodes_without_outgoing_switches > 0,
683689
"Found %zu nodes with no out-going switches. Run VTR with route_verbosity > 1 to see details.\n",
684690
num_nodes_without_outgoing_switches);
691+
692+
return num_nodes_of_index;
685693
}
686694

687695
static void calculate_average_switch(const RRGraphView& rr_graph,
@@ -750,6 +758,8 @@ static void calculate_average_switch(const RRGraphView& rr_graph,
750758
}
751759

752760
static void fixup_rr_indexed_data_T_values(vtr::vector<RRIndexedDataId, t_rr_indexed_data>& rr_indexed_data,
761+
const vtr::vector<RRIndexedDataId, int>& num_nodes_of_index,
762+
const std::vector<t_segment_inf>& segment_inf,
753763
size_t total_num_segments) {
754764
// Scan CHANX/CHANY indexed data and search for uninitialized costs.
755765
//
@@ -769,12 +779,21 @@ static void fixup_rr_indexed_data_T_values(vtr::vector<RRIndexedDataId, t_rr_ind
769779

770780
auto& indexed_data = rr_indexed_data[RRIndexedDataId(cost_index)];
771781
auto& ortho_indexed_data = rr_indexed_data[RRIndexedDataId(ortho_cost_index)];
772-
// Check if this data is uninitialized, but the orthogonal data is
773-
// initialized.
774-
// Uninitialized data is set to zero by default.
775-
bool needs_fixup = indexed_data.T_linear == 0 && indexed_data.T_quadratic == 0 && indexed_data.C_load == 0;
782+
783+
// Only fix up cost indices whose T-values were never computed because no RR
784+
// nodes of that cost index exist (a segment used as CHANX or CHANY but not
785+
// both).
786+
bool needs_fixup = num_nodes_of_index[RRIndexedDataId(cost_index)] == 0;
776787
bool ortho_data_valid = ortho_indexed_data.T_linear != 0 || ortho_indexed_data.T_quadratic != 0 || ortho_indexed_data.C_load != 0;
777-
if (needs_fixup && ortho_data_valid) {
788+
789+
// Never copy timing across a segment resource-type boundary (e.g. general
790+
// routing <-> clock network). We assume that their delays are not correlated.
791+
int seg_index = indexed_data.seg_index;
792+
int ortho_seg_index = ortho_indexed_data.seg_index;
793+
bool same_res_type = seg_index >= 0 && ortho_seg_index >= 0
794+
&& segment_inf[seg_index].res_type == segment_inf[ortho_seg_index].res_type;
795+
796+
if (needs_fixup && ortho_data_valid && same_res_type) {
778797
// Copy orthogonal data over.
779798
indexed_data.T_linear = ortho_indexed_data.T_linear;
780799
indexed_data.T_quadratic = ortho_indexed_data.T_quadratic;

vpr/src/route/router_lookahead/router_lookahead_map_utils.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,12 @@
1515
#include "globals.h"
1616
#include "physical_types.h"
1717
#include "physical_types_util.h"
18+
#include "rr_graph_fwd.h"
1819
#include "vpr_context.h"
1920
#include "vpr_error.h"
2021
#include "vpr_utils.h"
2122
#include "vpr_types.h"
23+
#include "vtr_assert.h"
2224
#include "vtr_math.h"
2325
#include "vtr_time.h"
2426
#include "route_common.h"
@@ -1419,6 +1421,7 @@ static void expand_dijkstra_neighbours(util::PQ_Entry parent_entry,
14191421
const auto& rr_graph = device_ctx.rr_graph;
14201422

14211423
RRNodeId parent = parent_entry.rr_node;
1424+
RRSegmentId parent_segment_id = rr_graph.node_segment(parent);
14221425

14231426
for (t_edge_size edge : rr_graph.edges(parent)) {
14241427
RRNodeId child_node = rr_graph.edge_sink_node(parent, edge);
@@ -1427,6 +1430,22 @@ static void expand_dijkstra_neighbours(util::PQ_Entry parent_entry,
14271430
if (!is_inter_cluster_node(rr_graph, child_node)) {
14281431
continue;
14291432
}
1433+
1434+
// If the edge connects between a general segment and clock segment, do not expand.
1435+
// This dijkstra expansion is used to populate cost maps, which assume that the routes
1436+
// stay within the general or clock networks.
1437+
// NOTE: IPINs/OPINs/SOURCEs/SINKs do not have valid segments. This check only cuts
1438+
// muxes that connect a GENERAL segment to a GCLK segment or vice versa.
1439+
RRSegmentId child_segment_id = rr_graph.node_segment(child_node);
1440+
if (parent_segment_id.is_valid() && child_segment_id.is_valid()) {
1441+
SegResType parent_res_type = rr_graph.rr_segments(parent_segment_id).res_type;
1442+
SegResType child_res_type = rr_graph.rr_segments(child_segment_id).res_type;
1443+
VTR_ASSERT_SAFE(parent_res_type == SegResType::GENERAL || parent_res_type == SegResType::GCLK);
1444+
VTR_ASSERT_SAFE(child_res_type == SegResType::GENERAL || child_res_type == SegResType::GCLK);
1445+
if ((parent_res_type == SegResType::GCLK) ^ (child_res_type == SegResType::GCLK))
1446+
continue;
1447+
}
1448+
14301449
int switch_ind = size_t(rr_graph.edge_switch(parent, edge));
14311450

14321451
if (rr_graph.node_type(child_node) == e_rr_type::SINK) return;

vpr/src/route/rr_graph_generation/clock_network_builders.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,9 @@ void populate_segment_values(int seg_index,
3131
e_directionality directionality) {
3232
segment_inf[seg_index].name = name;
3333
segment_inf[seg_index].length = length;
34-
segment_inf[seg_index].frequency = 1;
34+
// We set the frequency to zero to indicate that this segment
35+
// is not part of the general routing fabric.
36+
segment_inf[seg_index].frequency = 0;
3537
segment_inf[seg_index].Rmetal = layer.r_metal;
3638
segment_inf[seg_index].Cmetal = layer.c_metal;
3739
segment_inf[seg_index].directionality = directionality;

vtr_flow/arch/clock_networks/clock_sectors/generate_clock_sector_archs.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ def build_switch_grid(n: int, denom: int) -> Any:
9696
chan_w="4",
9797
switch_name="drive_buff",
9898
switch_block_type="subset",
99+
directionality="bidir",
99100
)
100101

101102
grid.append(comment("Put a drive in the middle of the device"))

0 commit comments

Comments
 (0)