VPR-6.0
|
00001 #include <stdio.h> 00002 #include <assert.h> 00003 #include <string.h> 00004 00005 #include "util.h" 00006 #include "physical_types.h" 00007 #include "vpr_types.h" 00008 #include "globals.h" 00009 #include "mst.h" 00010 #include "route_export.h" 00011 #include "route_common.h" 00012 #include "cluster_legality.h" 00013 #include "rr_graph.h" 00014 00015 static struct s_linked_vptr *rr_mem_chunk_list_head = NULL; 00016 static int chunk_bytes_avail = 0; 00017 static char *chunk_next_avail_mem = NULL; 00018 static struct s_trace **best_routing; 00019 00020 /** nets_in_cluster: array of all nets contained in the cluster */ 00021 static int *nets_in_cluster; /**< [0..num_nets_in_cluster-1] */ 00022 static int num_nets_in_cluster; 00023 static int curr_cluster_index; 00024 00025 static int ext_input_rr_node_index, ext_output_rr_node_index, ext_clock_rr_node_index, max_ext_index; 00026 static int **saved_net_rr_terminals; 00027 static float pres_fac; 00028 static float *rr_intrinsic_cost; 00029 static int num_rr_intrinsic_cost = 0; 00030 00031 00032 /********************* Subroutines local to this module *********************/ 00033 static boolean is_net_in_cluster(INP int inet); 00034 00035 static boolean add_net_rr_terminal_cluster(int iblk_net, t_pb * primitive, int ilogical_block, t_model_ports * model_port, int ipin); 00036 00037 static boolean reload_ext_net_rr_terminal_cluster(); 00038 00039 static boolean try_breadth_first_route_cluster(); 00040 00041 static boolean breadth_first_route_net_cluster(int inet); 00042 00043 static void breadth_first_expand_trace_segment_cluster(struct s_trace *start_ptr, 00044 int remaining_connections_to_sink); 00045 00046 static void breadth_first_expand_neighbours_cluster(int inode, 00047 float pcost, 00048 int inet, 00049 boolean first_time); 00050 00051 static void breadth_first_add_source_to_heap_cluster(int inet); 00052 00053 static void alloc_net_rr_terminals_cluster(); 00054 00055 static void save_and_reset_routing_cluster(); 00056 00057 static void restore_routing_cluster(); 00058 00059 static int get_num_violated_nets(); 00060 00061 static void mark_ends_cluster(int inet); 00062 00063 static void print_intra_cluster_route(char *route_file); 00064 00065 static float rr_node_intrinsic_cost(int inode); 00066 00067 00068 /************************ Subroutine definitions ****************************/ 00069 00070 /** load rr_node for source and sinks of net if exists, return FALSE otherwise. 00071 * Todo: Note this is an inefficient way to determine port, better to use a lookup, worry about this if runtime becomes an issue 00072 */ 00073 static boolean is_net_in_cluster(INP int inet) { 00074 int i; 00075 for(i = 0; i < num_nets_in_cluster; i++) { 00076 if(nets_in_cluster[i] == inet) { 00077 return TRUE; 00078 } 00079 } 00080 return FALSE; 00081 } 00082 00083 /** Ensure at most one external input/clock source and one external output sink for net */ 00084 static boolean add_net_rr_terminal_cluster(int iblk_net, t_pb * primitive, int ilogical_block, t_model_ports * model_port, int ipin) { 00085 int i, net_pin; 00086 t_port *prim_port; 00087 const t_pb_type *pb_type; 00088 boolean found; 00089 00090 int input_port; 00091 int output_port; 00092 int clock_port; 00093 00094 input_port = output_port = clock_port = 0; 00095 00096 pb_type = primitive->pb_graph_node->pb_type; 00097 prim_port = NULL; 00098 00099 assert(pb_type->num_modes == 0); 00100 00101 found = FALSE; 00102 /* TODO: This is inelegant design, I should change the primitive ports in pb_type to be input, output, or clock instead of this lookup */ 00103 for(i = 0; i < pb_type->num_ports && !found; i++) { 00104 prim_port = &pb_type->ports[i]; 00105 if(pb_type->ports[i].model_port == model_port) { 00106 found = TRUE; 00107 } else { 00108 if(prim_port->is_clock) { 00109 clock_port++; 00110 assert(prim_port->type == IN_PORT); 00111 } else if(prim_port->type == IN_PORT) { 00112 input_port++; 00113 } else if(prim_port->type == OUT_PORT) { 00114 output_port++; 00115 } else { 00116 assert(0); 00117 } 00118 } 00119 } 00120 if(!found) { 00121 return FALSE; 00122 } 00123 00124 if(ipin >= prim_port->num_pins) { 00125 return FALSE; 00126 } else { 00127 net_pin = OPEN; 00128 if(prim_port->is_clock) { 00129 for(i = 1; i <= vpack_net[iblk_net].num_sinks; i++) { 00130 if(vpack_net[iblk_net].node_block[i] == ilogical_block && 00131 vpack_net[iblk_net].node_block_port[i] == model_port->index && 00132 vpack_net[iblk_net].node_block_pin[i] == ipin) { 00133 net_pin = i; 00134 break; 00135 } 00136 } 00137 assert(net_pin != OPEN); 00138 net_rr_terminals[iblk_net][net_pin] = 00139 primitive->pb_graph_node->clock_pins[clock_port][ipin].pin_count_in_cluster; 00140 } else if(prim_port->type == IN_PORT) { 00141 for(i = 1; i <= vpack_net[iblk_net].num_sinks; i++) { 00142 if(vpack_net[iblk_net].node_block[i] == ilogical_block && 00143 vpack_net[iblk_net].node_block_port[i] == model_port->index && 00144 vpack_net[iblk_net].node_block_pin[i] == ipin) { 00145 net_pin = i; 00146 break; 00147 } 00148 } 00149 assert(net_pin != OPEN); 00150 net_rr_terminals[iblk_net][net_pin] = 00151 primitive->pb_graph_node->input_pins[input_port][ipin].pin_count_in_cluster; 00152 } else if(prim_port->type == OUT_PORT) { 00153 i = 0; 00154 if(vpack_net[iblk_net].node_block[i] == ilogical_block && 00155 vpack_net[iblk_net].node_block_port[i] == model_port->index && 00156 vpack_net[iblk_net].node_block_pin[i] == ipin) { 00157 net_pin = i; 00158 } 00159 assert(net_pin != OPEN); 00160 net_rr_terminals[iblk_net][net_pin] = 00161 primitive->pb_graph_node->output_pins[output_port][ipin].pin_count_in_cluster; 00162 } else { 00163 assert(0); 00164 } 00165 return TRUE; 00166 } 00167 } 00168 00169 static boolean reload_ext_net_rr_terminal_cluster() { 00170 int i, j, net_index; 00171 boolean has_ext_sink, has_ext_source; 00172 int curr_ext_output, curr_ext_input, curr_ext_clock; 00173 00174 curr_ext_input = ext_input_rr_node_index; 00175 curr_ext_output = ext_output_rr_node_index; 00176 curr_ext_clock = ext_clock_rr_node_index; 00177 00178 for(i = 0; i < num_nets_in_cluster; i++) 00179 { 00180 net_index = nets_in_cluster[i]; 00181 has_ext_sink = FALSE; 00182 has_ext_source = (logical_block[vpack_net[net_index].node_block[0]].clb_index != curr_cluster_index); 00183 if(has_ext_source) { 00184 /* Instantiate a source of this net */ 00185 if(vpack_net[net_index].is_global) { 00186 net_rr_terminals[net_index][0] = curr_ext_clock; 00187 curr_ext_clock++; 00188 } else { 00189 net_rr_terminals[net_index][0] = curr_ext_input; 00190 curr_ext_input++; 00191 } 00192 } 00193 for(j = 1; j <= vpack_net[net_index].num_sinks; j++) { 00194 if(logical_block[vpack_net[net_index].node_block[j]].clb_index != curr_cluster_index) { 00195 if(has_ext_sink || has_ext_source) { 00196 /* Only need one node driving external routing, either this cluster drives external routing or another cluster does it */ 00197 net_rr_terminals[net_index][j] = OPEN; 00198 } else { 00199 /* External sink, only need to route once, externally routing will take care of the rest */ 00200 net_rr_terminals[net_index][j] = curr_ext_output; 00201 curr_ext_output++; 00202 has_ext_sink = TRUE; 00203 } 00204 } 00205 } 00206 00207 if( curr_ext_input > ext_output_rr_node_index || 00208 curr_ext_output > ext_clock_rr_node_index || 00209 curr_ext_clock > max_ext_index) { 00210 /* failed, not enough pins of proper type, overran index */ 00211 return FALSE; 00212 } 00213 } 00214 00215 return TRUE; 00216 } 00217 00218 void alloc_and_load_cluster_legality_checker() { 00219 best_routing = (struct s_trace **)my_calloc(num_logical_nets, sizeof(struct s_trace *)); 00220 nets_in_cluster = my_malloc(num_logical_nets * sizeof(int)); 00221 num_nets_in_cluster = 0; 00222 num_nets = num_logical_nets; 00223 00224 /* inside a cluster, I do not consider rr_indexed_data cost, set to 1 since other costs are multiplied by it */ 00225 num_rr_indexed_data = 1; 00226 rr_indexed_data = my_calloc(1, sizeof(t_rr_indexed_data)); 00227 rr_indexed_data[0].base_cost = 1; 00228 00229 /* alloc routing structures */ 00230 alloc_route_static_structs(); 00231 alloc_net_rr_terminals_cluster(); 00232 } 00233 00234 00235 void free_cluster_legality_checker() { 00236 int inet; 00237 free(rr_indexed_data); 00238 free_rr_node_route_structs(); 00239 free_route_structs(NULL); 00240 free_trace_structs(); 00241 00242 free_chunk_memory(rr_mem_chunk_list_head); 00243 rr_mem_chunk_list_head = NULL; 00244 00245 for(inet = 0; inet < num_logical_nets; inet++) 00246 { 00247 free(saved_net_rr_terminals[inet]); 00248 } 00249 free(net_rr_terminals); 00250 free(saved_net_rr_terminals); 00251 } 00252 00253 00254 void alloc_and_load_rr_graph_for_pb_graph_node(INP t_pb_graph_node *pb_graph_node, INP const t_arch* arch, int mode) { 00255 00256 int i, j, k, index; 00257 boolean is_primitive; 00258 00259 is_primitive = (pb_graph_node->pb_type->num_modes == 0); 00260 00261 for(i = 0; i < pb_graph_node->num_input_ports; i++) { 00262 for(j = 0; j < pb_graph_node->num_input_pins[i]; j++) { 00263 index = pb_graph_node->input_pins[i][j].pin_count_in_cluster; 00264 rr_node[index].pb_graph_pin = &pb_graph_node->input_pins[i][j]; 00265 rr_node[index].fan_in = pb_graph_node->input_pins[i][j].num_input_edges; 00266 rr_node[index].num_edges = pb_graph_node->input_pins[i][j].num_output_edges; 00267 rr_node[index].pack_intrinsic_cost = 1 + (float)rr_node[index].num_edges / 5; /* need to normalize better than 5 */ 00268 rr_node[index].edges = my_malloc(rr_node[index].num_edges * sizeof(int)); 00269 rr_node[index].switches = my_calloc(rr_node[index].num_edges, sizeof(int)); 00270 rr_node[index].net_num = OPEN; 00271 rr_node[index].prev_node = OPEN; 00272 rr_node[index].prev_edge = OPEN; 00273 if(mode == 0) { /* default mode is the first mode */ 00274 rr_node[index].capacity = 1; 00275 } else { 00276 rr_node[index].capacity = 0; 00277 } 00278 for(k = 0; k < pb_graph_node->input_pins[i][j].num_output_edges; k++) { 00279 /* TODO: Intention was to do bus-based implementation here */ 00280 rr_node[index].edges[k] = pb_graph_node->input_pins[i][j].output_edges[k]->output_pins[0]->pin_count_in_cluster; 00281 rr_node[index].switches[k] = arch->num_switches - 1; /* last switch in arch switch properties is a delayless switch */ 00282 assert(pb_graph_node->input_pins[i][j].output_edges[k]->num_output_pins == 1); 00283 } 00284 rr_node[index].type = INTRA_CLUSTER_EDGE; 00285 if(is_primitive) { 00286 rr_node[index].type = SINK; 00287 } 00288 } 00289 } 00290 00291 for(i = 0; i < pb_graph_node->num_output_ports; i++) { 00292 for(j = 0; j < pb_graph_node->num_output_pins[i]; j++) { 00293 index = pb_graph_node->output_pins[i][j].pin_count_in_cluster; 00294 rr_node[index].pb_graph_pin = &pb_graph_node->output_pins[i][j]; 00295 rr_node[index].fan_in = pb_graph_node->output_pins[i][j].num_input_edges; 00296 rr_node[index].num_edges = pb_graph_node->output_pins[i][j].num_output_edges; 00297 rr_node[index].pack_intrinsic_cost = 1 + (float)rr_node[index].num_edges / 5; /* need to normalize better than 5 */ 00298 rr_node[index].edges = my_malloc(rr_node[index].num_edges * sizeof(int)); 00299 rr_node[index].switches = my_calloc(rr_node[index].num_edges, sizeof(int)); 00300 rr_node[index].net_num = OPEN; 00301 rr_node[index].prev_node = OPEN; 00302 rr_node[index].prev_edge = OPEN; 00303 if(mode == 0) { /* Default mode is the first mode */ 00304 rr_node[index].capacity = 1; 00305 } else { 00306 rr_node[index].capacity = 0; 00307 } 00308 for(k = 0; k < pb_graph_node->output_pins[i][j].num_output_edges; k++) { 00309 /* TODO: Intention was to do bus-based implementation here */ 00310 rr_node[index].edges[k] = pb_graph_node->output_pins[i][j].output_edges[k]->output_pins[0]->pin_count_in_cluster; 00311 rr_node[index].switches[k] = arch->num_switches - 1; 00312 assert(pb_graph_node->output_pins[i][j].output_edges[k]->num_output_pins == 1); 00313 } 00314 rr_node[index].type = INTRA_CLUSTER_EDGE; 00315 if(is_primitive) { 00316 rr_node[index].type = SOURCE; 00317 } 00318 } 00319 } 00320 00321 for(i = 0; i < pb_graph_node->num_clock_ports; i++) { 00322 for(j = 0; j < pb_graph_node->num_clock_pins[i]; j++) { 00323 index = pb_graph_node->clock_pins[i][j].pin_count_in_cluster; 00324 rr_node[index].pb_graph_pin = &pb_graph_node->clock_pins[i][j]; 00325 rr_node[index].fan_in = pb_graph_node->clock_pins[i][j].num_input_edges; 00326 rr_node[index].num_edges = pb_graph_node->clock_pins[i][j].num_output_edges; 00327 rr_node[index].pack_intrinsic_cost = 1 + (float)rr_node[index].num_edges / 5; /* need to normalize better than 5 */ 00328 rr_node[index].edges = my_malloc(rr_node[index].num_edges * sizeof(int)); 00329 rr_node[index].switches = my_calloc(rr_node[index].num_edges, sizeof(int)); 00330 rr_node[index].net_num = OPEN; 00331 rr_node[index].prev_node = OPEN; 00332 rr_node[index].prev_edge = OPEN; 00333 if(mode == 0) { /* default mode is the first mode (useful for routing */ 00334 rr_node[index].capacity = 1; 00335 } else { 00336 rr_node[index].capacity = 0; 00337 } 00338 for(k = 0; k < pb_graph_node->clock_pins[i][j].num_output_edges; k++) { 00339 /* TODO: Intention was to do bus-based implementation here */ 00340 rr_node[index].edges[k] = pb_graph_node->clock_pins[i][j].output_edges[k]->output_pins[0]->pin_count_in_cluster; 00341 rr_node[index].switches[k] = arch->num_switches - 1; 00342 assert(pb_graph_node->clock_pins[i][j].output_edges[k]->num_output_pins == 1); 00343 } 00344 rr_node[index].type = INTRA_CLUSTER_EDGE; 00345 if(is_primitive) { 00346 rr_node[index].type = SINK; 00347 } 00348 } 00349 } 00350 00351 for(i = 0; i < pb_graph_node->pb_type->num_modes; i++) { 00352 for(j = 0; j < pb_graph_node->pb_type->modes[i].num_pb_type_children; j++) { 00353 for(k = 0; k < pb_graph_node->pb_type->modes[i].pb_type_children[j].num_pb; k++) { 00354 alloc_and_load_rr_graph_for_pb_graph_node(&pb_graph_node->child_pb_graph_nodes[i][j][k], arch, i); 00355 } 00356 } 00357 } 00358 00359 } 00360 00361 00362 /** 00363 * Structure: Model external routing and internal routing 00364 * 00365 * 1. Model external routing 00366 * num input pins == num external sources for input pins, fully connect them to input pins (simulates external routing) 00367 * num output pins == num external sinks for output pins, fully connect them to output pins (simulates external routing) 00368 * num clock pins == num external sources for clock pins, fully connect them to clock pins (simulates external routing) 00369 * 2. Model internal routing 00370 * 00371 */ 00372 void alloc_and_load_legalizer_for_cluster(INP t_block* clb, INP int clb_index, INP const t_arch *arch) { 00373 /* make each rr_node one correspond with pin and correspond with pin's index pin_count_in_cluster */ 00374 int i, j, k, m, index, pb_graph_rr_index; 00375 int count_pins; 00376 t_pb_type * pb_type; 00377 t_pb_graph_node *pb_graph_node; 00378 int ipin; 00379 00380 /* Create rr_graph */ 00381 pb_type = clb->type->pb_type; 00382 pb_graph_node = clb->type->pb_graph_head; 00383 num_rr_nodes = pb_graph_node->total_pb_pins + pb_type->num_input_pins + 00384 pb_type->num_output_pins + pb_type->num_clock_pins; 00385 if(num_rr_nodes > num_rr_intrinsic_cost) { 00386 free(rr_intrinsic_cost); 00387 rr_intrinsic_cost = my_calloc(num_rr_nodes, sizeof(float)); 00388 num_rr_intrinsic_cost = num_rr_nodes; 00389 } 00390 rr_node = my_calloc(num_rr_nodes, sizeof(t_rr_node)); 00391 clb->pb->rr_graph = rr_node; 00392 00393 alloc_and_load_rr_graph_for_pb_graph_node(pb_graph_node, arch, 0); 00394 00395 curr_cluster_index = clb_index; 00396 00397 /* Alloc and load rr_graph external sources and sinks */ 00398 ext_input_rr_node_index = pb_graph_node->total_pb_pins; 00399 ext_output_rr_node_index = pb_type->num_input_pins + pb_graph_node->total_pb_pins; 00400 ext_clock_rr_node_index = pb_type->num_input_pins + pb_type->num_output_pins + pb_graph_node->total_pb_pins; 00401 max_ext_index = pb_type->num_input_pins + pb_type->num_output_pins + pb_type->num_clock_pins + pb_graph_node->total_pb_pins; 00402 00403 for(i = 0; i < pb_type->num_input_pins; i++) { 00404 index = i + pb_graph_node->total_pb_pins; 00405 rr_node[index].type = SOURCE; 00406 rr_node[index].fan_in = 0; 00407 rr_node[index].num_edges = pb_type->num_input_pins; 00408 rr_node[index].pack_intrinsic_cost = 1 + (float)rr_node[index].num_edges / 5; /* need to normalize better than 5 */ 00409 rr_node[index].edges = my_malloc(rr_node[index].num_edges * sizeof(int)); 00410 rr_node[index].switches = my_calloc(rr_node[index].num_edges, sizeof(int)); 00411 rr_node[index].capacity = 1; 00412 rr_intrinsic_cost[index] = 0; 00413 } 00414 00415 for(i = 0; i < pb_type->num_output_pins; i++) { 00416 index = i + pb_type->num_input_pins + pb_graph_node->total_pb_pins; 00417 rr_node[index].type = SINK; 00418 rr_node[index].fan_in = pb_type->num_output_pins; 00419 rr_node[index].num_edges = 0; 00420 rr_node[index].pack_intrinsic_cost = 1 + (float)rr_node[index].num_edges / 5; /* need to normalize better than 5 */ 00421 rr_node[index].capacity = 1; 00422 rr_intrinsic_cost[index] = 0; 00423 } 00424 00425 for(i = 0; i < pb_type->num_clock_pins; i++) { 00426 index = i + pb_type->num_input_pins + pb_type->num_output_pins + pb_graph_node->total_pb_pins; 00427 rr_node[index].type = SOURCE; 00428 rr_node[index].fan_in = 0; 00429 rr_node[index].num_edges = pb_type->num_clock_pins; 00430 rr_node[index].pack_intrinsic_cost = 1 + (float)rr_node[index].num_edges / 5; /* need to normalize better than 5 */ 00431 rr_node[index].edges = my_malloc(rr_node[index].num_edges * sizeof(int)); 00432 rr_node[index].switches = my_calloc(rr_node[index].num_edges, sizeof(int)); 00433 rr_node[index].capacity = 1; 00434 rr_intrinsic_cost[index] = 0; 00435 } 00436 00437 ipin = 0; 00438 for(i = 0; i < pb_graph_node->num_input_ports; i++) { 00439 for(j = 0; j < pb_graph_node->num_input_pins[i]; j++) { 00440 pb_graph_rr_index = pb_graph_node->input_pins[i][j].pin_count_in_cluster; 00441 for(k = 0; k < pb_type->num_input_pins; k++) { 00442 index = k + pb_graph_node->total_pb_pins; 00443 rr_node[index].edges[ipin] = pb_graph_rr_index; 00444 rr_node[index].switches[ipin] = arch->num_switches - 1; 00445 } 00446 rr_node[pb_graph_rr_index].pack_intrinsic_cost = MAX_SHORT; /* using an input pin should be made costly */ 00447 ipin++; 00448 } 00449 } 00450 00451 /* Must attach output pins to input pins because if a connection cannot fit using intra-cluster routing, it can also use external routing */ 00452 for(i = 0; i < pb_graph_node->num_output_ports; i++) { 00453 for(j = 0; j < pb_graph_node->num_output_pins[i]; j++) { 00454 count_pins = pb_graph_node->output_pins[i][j].num_output_edges + pb_type->num_output_pins + pb_type->num_input_pins; 00455 pb_graph_rr_index = pb_graph_node->output_pins[i][j].pin_count_in_cluster; 00456 rr_node[pb_graph_rr_index].edges = my_realloc(rr_node[pb_graph_rr_index].edges, 00457 (count_pins) * sizeof(int)); 00458 rr_node[pb_graph_rr_index].switches = my_realloc(rr_node[pb_graph_rr_index].switches, 00459 (count_pins) * sizeof(int)); 00460 00461 ipin = 0; 00462 for(k = 0; k < pb_graph_node->num_input_ports; k++) { 00463 for(m = 0; m < pb_graph_node->num_input_pins[k]; m++) { 00464 index = pb_graph_node->input_pins[k][m].pin_count_in_cluster; 00465 rr_node[pb_graph_rr_index].edges[ipin + pb_graph_node->output_pins[i][j].num_output_edges] = index; 00466 rr_node[pb_graph_rr_index].switches[ipin + pb_graph_node->output_pins[i][j].num_output_edges] = arch->num_switches - 1; 00467 ipin++; 00468 } 00469 } 00470 for(k = 0; k < pb_type->num_output_pins; k++) { 00471 index = k + pb_type->num_input_pins + pb_graph_node->total_pb_pins; 00472 rr_node[pb_graph_rr_index].edges[k + pb_type->num_input_pins + pb_graph_node->output_pins[i][j].num_output_edges] = index; 00473 rr_node[pb_graph_rr_index].switches[k + pb_type->num_input_pins + pb_graph_node->output_pins[i][j].num_output_edges] = arch->num_switches - 1; 00474 } 00475 rr_node[pb_graph_rr_index].num_edges += pb_type->num_output_pins + pb_type->num_input_pins; 00476 rr_node[pb_graph_rr_index].pack_intrinsic_cost = 1 + (float)rr_node[pb_graph_rr_index].num_edges / 5; /* need to normalize better than 5 */ 00477 } 00478 } 00479 00480 ipin = 0; 00481 for(i = 0; i < pb_graph_node->num_clock_ports; i++) { 00482 for(j = 0; j < pb_graph_node->num_clock_pins[i]; j++) { 00483 for(k = 0; k < pb_type->num_clock_pins; k++) { 00484 index = k + pb_type->num_input_pins + pb_type->num_output_pins + pb_graph_node->total_pb_pins; 00485 pb_graph_rr_index = pb_graph_node->clock_pins[i][j].pin_count_in_cluster; 00486 rr_node[index].edges[ipin] = pb_graph_rr_index; 00487 rr_node[index].switches[ipin] = arch->num_switches - 1; 00488 } 00489 ipin++; 00490 } 00491 } 00492 00493 alloc_and_load_rr_node_route_structs(); 00494 num_nets_in_cluster = 0; 00495 00496 } 00497 00498 00499 00500 void free_legalizer_for_cluster(INP t_block* clb) { 00501 int i; 00502 00503 free_rr_node_route_structs(); 00504 for(i = 0; i < num_rr_nodes; i++) { 00505 if(clb->pb->rr_graph[i].edges != NULL) { 00506 free(clb->pb->rr_graph[i].edges); 00507 } 00508 if(clb->pb->rr_graph[i].switches != NULL) { 00509 free(clb->pb->rr_graph[i].switches); 00510 } 00511 } 00512 free(clb->pb->rr_graph); 00513 } 00514 00515 void reset_legalizer_for_cluster(t_block *clb) { 00516 int i; 00517 for(i = 0; i < num_nets_in_cluster; i++) { 00518 free_traceback(nets_in_cluster[i]); 00519 trace_head[nets_in_cluster[i]] = best_routing[nets_in_cluster[i]]; 00520 free_traceback(nets_in_cluster[i]); 00521 best_routing[nets_in_cluster[i]] = NULL; 00522 } 00523 00524 free_rr_node_route_structs(); 00525 num_nets_in_cluster = 0; 00526 } 00527 00528 00529 /** 00530 * 00531 * internal_nets: index of nets to route [0..num_internal_nets - 1] 00532 */ 00533 /** Iterated maze router ala Pathfinder Negotiated Congestion algorithm, 00534 * (FPGA 95 p. 111). Returns TRUE if it can route this FPGA, FALSE if 00535 * it can't. 00536 */ 00537 static boolean try_breadth_first_route_cluster() 00538 { 00539 00540 /* For different modes, when a mode is turned on, I set the max occupancy of all rr_nodes in the mode to 1 and all others to 0 */ 00541 /* TODO: There is a bug for route-throughs where edges in route-throughs do not get turned off because the rr_edge is in a particular mode but the two rr_nodes are outside */ 00542 00543 boolean success, is_routable; 00544 int itry, inet, net_index; 00545 struct s_router_opts router_opts; 00546 00547 00548 /* Usually the first iteration uses a very small (or 0) pres_fac to find * 00549 * the shortest path and get a congestion map. For fast compiles, I set * 00550 * pres_fac high even for the first iteration. */ 00551 00552 /* sets up a fast breadth-first router */ 00553 router_opts.first_iter_pres_fac = 10; 00554 router_opts.max_router_iterations = 20; 00555 router_opts.initial_pres_fac = 10; 00556 router_opts.pres_fac_mult = 2; 00557 router_opts.acc_fac = 1; 00558 00559 pres_fac = router_opts.first_iter_pres_fac; 00560 00561 for(itry = 1; itry <= router_opts.max_router_iterations; itry++) 00562 { 00563 for(inet = 0; inet < num_nets_in_cluster; inet++) 00564 { 00565 net_index = nets_in_cluster[inet]; 00566 00567 pathfinder_update_one_cost(trace_head[net_index], -1, 00568 pres_fac); 00569 00570 is_routable = 00571 breadth_first_route_net_cluster(net_index); 00572 00573 /* Impossible to route? (disconnected rr_graph) */ 00574 00575 if(!is_routable) 00576 { 00577 /* TODO: Inelegant, can be more intelligent */ 00578 printf("Failed routing net %s\n", vpack_net[net_index].name); 00579 printf("Routing failed. Disconnected rr_graph\n"); 00580 return FALSE; 00581 } 00582 00583 pathfinder_update_one_cost(trace_head[net_index], 1, 00584 pres_fac); 00585 00586 } 00587 00588 00589 success = feasible_routing(); 00590 if(success) 00591 { 00592 return (TRUE); 00593 } 00594 00595 if(itry == 1) 00596 pres_fac = router_opts.initial_pres_fac; 00597 else 00598 pres_fac *= router_opts.pres_fac_mult; 00599 00600 pres_fac = min (pres_fac, HUGE_FLOAT / 1e5); 00601 00602 pathfinder_update_cost(pres_fac, router_opts.acc_fac); 00603 } 00604 00605 return (FALSE); 00606 } 00607 00608 00609 /** Uses a maze routing (Dijkstra's) algorithm to route a net. The net 00610 * begins at the net output, and expands outward until it hits a target 00611 * pin. The algorithm is then restarted with the entire first wire segment 00612 * included as part of the source this time. For an n-pin net, the maze 00613 * router is invoked n-1 times to complete all the connections. Inet is 00614 * the index of the net to be routed. 00615 * If this routine finds that a net *cannot* be connected (due to a complete 00616 * lack of potential paths, rather than congestion), it returns FALSE, as 00617 * routing is impossible on this architecture. Otherwise it returns TRUE. 00618 */ 00619 static boolean 00620 breadth_first_route_net_cluster(int inet) 00621 { 00622 int i, inode, prev_node, remaining_connections_to_sink; 00623 float pcost, new_pcost; 00624 struct s_heap *current; 00625 struct s_trace *tptr; 00626 boolean first_time; 00627 00628 free_traceback(inet); 00629 breadth_first_add_source_to_heap_cluster(inet); 00630 mark_ends_cluster(inet); 00631 00632 tptr = NULL; 00633 remaining_connections_to_sink = 0; 00634 00635 for(i = 1; i <= vpack_net[inet].num_sinks; i++) 00636 { /* Need n-1 wires to connect n pins */ 00637 00638 /* Do not connect open terminals */ 00639 if(net_rr_terminals[inet][i] == OPEN) 00640 continue; 00641 /* Expand and begin routing */ 00642 breadth_first_expand_trace_segment_cluster(tptr, 00643 remaining_connections_to_sink); 00644 current = get_heap_head(); 00645 00646 if(current == NULL) 00647 { /* Infeasible routing. No possible path for net. */ 00648 reset_path_costs(); /* Clean up before leaving. */ 00649 return (FALSE); 00650 } 00651 00652 inode = current->index; 00653 00654 while(rr_node_route_inf[inode].target_flag == 0) 00655 { 00656 pcost = rr_node_route_inf[inode].path_cost; 00657 new_pcost = current->cost; 00658 if(pcost > new_pcost) 00659 { /* New path is lowest cost. */ 00660 rr_node_route_inf[inode].path_cost = new_pcost; 00661 prev_node = current->u.prev_node; 00662 rr_node_route_inf[inode].prev_node = prev_node; 00663 rr_node_route_inf[inode].prev_edge = 00664 current->prev_edge; 00665 first_time = FALSE; 00666 00667 if(pcost > 0.99 * HUGE_FLOAT) /* First time touched. */ { 00668 add_to_mod_list(&rr_node_route_inf[inode]. 00669 path_cost); 00670 first_time = TRUE; 00671 } 00672 00673 breadth_first_expand_neighbours_cluster(inode, new_pcost, inet, first_time); 00674 } 00675 00676 free_heap_data(current); 00677 current = get_heap_head(); 00678 00679 if(current == NULL) 00680 { /* Impossible routing. No path for net. */ 00681 reset_path_costs(); 00682 return (FALSE); 00683 } 00684 00685 inode = current->index; 00686 } 00687 00688 rr_node_route_inf[inode].target_flag--; /* Connected to this SINK. */ 00689 remaining_connections_to_sink = 00690 rr_node_route_inf[inode].target_flag; 00691 tptr = update_traceback(current, inet); 00692 free_heap_data(current); 00693 } 00694 00695 empty_heap(); 00696 reset_path_costs(); 00697 return (TRUE); 00698 } 00699 00700 /** Adds all the rr_nodes in the traceback segment starting at tptr (and 00701 * continuing to the end of the traceback) to the heap with a cost of zero. 00702 * This allows expansion to begin from the existing wiring. The 00703 * remaining_connections_to_sink value is 0 if the route segment ending 00704 * at this location is the last one to connect to the SINK ending the route 00705 * segment. This is the usual case. If it is not the last connection this 00706 * net must make to this SINK, I have a hack to ensure the next connection 00707 * to this SINK goes through a different IPIN. Without this hack, the 00708 * router would always put all the connections from this net to this SINK 00709 * through the same IPIN. With LUTs or cluster-based logic blocks, you 00710 * should never have a net connecting to two logically-equivalent pins on 00711 * the same logic block, so the hack will never execute. If your logic 00712 * block is an and-gate, however, nets might connect to two and-inputs on 00713 * the same logic block, and since the and-inputs are logically-equivalent, 00714 * this means two connections to the same SINK. 00715 */ 00716 static void 00717 breadth_first_expand_trace_segment_cluster(struct s_trace *start_ptr, 00718 int remaining_connections_to_sink) 00719 { 00720 struct s_trace *tptr; 00721 00722 tptr = start_ptr; 00723 00724 /* For intra-cluster routing, logical equivalence does not occur, so it is impossible to get a value bigger than 0 */ 00725 assert(remaining_connections_to_sink == 0); 00726 00727 while(tptr != NULL) 00728 { 00729 node_to_heap(tptr->index, 0., NO_PREVIOUS, NO_PREVIOUS, 00730 OPEN, OPEN); 00731 tptr = tptr->next; 00732 } 00733 } 00734 00735 00736 /** Puts all the rr_nodes adjacent to inode on the heap. rr_nodes outside 00737 * the expanded bounding box specified in route_bb are not added to the 00738 * heap. pcost is the path_cost to get to inode. 00739 */ 00740 static void 00741 breadth_first_expand_neighbours_cluster(int inode, 00742 float pcost, 00743 int inet, 00744 boolean first_time) 00745 { 00746 int iconn, to_node, num_edges; 00747 float tot_cost; 00748 00749 num_edges = rr_node[inode].num_edges; 00750 for(iconn = 0; iconn < num_edges; iconn++) 00751 { 00752 to_node = rr_node[inode].edges[iconn]; 00753 /*if(first_time) { */ 00754 tot_cost = pcost + get_rr_cong_cost(to_node) * rr_node_intrinsic_cost(to_node); 00755 /* 00756 } else { 00757 tot_cost = pcost + get_rr_cong_cost(to_node); 00758 }*/ 00759 node_to_heap(to_node, tot_cost, inode, iconn, OPEN, OPEN); 00760 } 00761 } 00762 00763 00764 /** Adds the SOURCE of this net to the heap. Used to start a net's routing. */ 00765 static void 00766 breadth_first_add_source_to_heap_cluster(int inet) 00767 { 00768 int inode; 00769 float cost; 00770 00771 inode = net_rr_terminals[inet][0]; /* SOURCE */ 00772 cost = get_rr_cong_cost(inode); 00773 00774 node_to_heap(inode, cost, NO_PREVIOUS, NO_PREVIOUS, OPEN, OPEN); 00775 } 00776 00777 /** Mark all the SINKs of this net as targets by setting their target flags 00778 * to the number of times the net must connect to each SINK. Note that 00779 * this number can occassionally be greater than 1 -- think of connecting 00780 * the same net to two inputs of an and-gate (and-gate inputs are logically 00781 * equivalent, so both will connect to the same SINK). 00782 */ 00783 static void 00784 mark_ends_cluster(int inet) 00785 { 00786 int ipin, inode; 00787 00788 for(ipin = 1; ipin <= vpack_net[inet].num_sinks; ipin++) 00789 { 00790 inode = net_rr_terminals[inet][ipin]; 00791 if(inode == OPEN) 00792 continue; 00793 rr_node_route_inf[inode].target_flag++; 00794 assert(rr_node_route_inf[inode].target_flag == 1); 00795 } 00796 } 00797 00798 00799 static void 00800 alloc_net_rr_terminals_cluster() 00801 { 00802 int inet; 00803 00804 net_rr_terminals = (int **)my_malloc(num_logical_nets * sizeof(int *)); 00805 saved_net_rr_terminals = (int **)my_malloc(num_logical_nets * sizeof(int *)); 00806 00807 for(inet = 0; inet < num_logical_nets; inet++) 00808 { 00809 net_rr_terminals[inet] = 00810 (int *)my_chunk_malloc((vpack_net[inet].num_sinks + 1) * 00811 sizeof(int), &rr_mem_chunk_list_head, 00812 &chunk_bytes_avail, 00813 &chunk_next_avail_mem); 00814 00815 saved_net_rr_terminals[inet] = (int *)my_malloc((vpack_net[inet].num_sinks + 1) * sizeof(int)); 00816 } 00817 } 00818 00819 00820 /** Allocates and loads the net_rr_terminals data structure. For each net 00821 * it stores the rr_node index of the SOURCE of the net and all the SINKs 00822 * of the net. [0..num_logical_nets-1][0..num_pins-1]. 00823 */ 00824 enum e_block_pack_status 00825 try_add_block_to_current_cluster_and_primitive(INP int iblock, INP t_pb *primitive) 00826 { 00827 int ipin, iblk_net; 00828 int orig_num_nets_in_cluster; 00829 boolean success, found; 00830 t_model_ports *port; 00831 00832 success = FALSE; 00833 found = FALSE; 00834 00835 assert(primitive->pb_graph_node->pb_type->num_modes == 0); /* check if primitive */ 00836 assert(primitive->logical_block == OPEN && logical_block[iblock].clb_index == NO_CLUSTER); /* check if primitive and block is open */ 00837 00838 /* check if block type matches primitive type */ 00839 if(logical_block[iblock].model != primitive->pb_graph_node->pb_type->model) { 00840 /* End early, model is incompatible */ 00841 return BLK_FAILED_FEASIBLE; 00842 } 00843 00844 orig_num_nets_in_cluster = num_nets_in_cluster; 00845 save_and_reset_routing_cluster(); 00846 00847 /* try pack it in */ 00848 assert(primitive->logical_block == OPEN); 00849 assert(logical_block[iblock].clb_index == NO_CLUSTER); 00850 00851 primitive->logical_block = iblock; 00852 logical_block[iblock].pb = primitive; 00853 logical_block[iblock].clb_index = curr_cluster_index; 00854 00855 /* for each net of logical block, check if it is in cluster, if not add it */ 00856 /* also check if pins on primitive can fit logical block */ 00857 00858 port = logical_block[iblock].model->inputs; 00859 success = TRUE; 00860 00861 while(port && success) { 00862 for(ipin = 0; ipin < port->size && success; ipin++) { 00863 if(port->is_clock) { 00864 assert(port->size == 1); 00865 iblk_net = logical_block[iblock].clock_net; 00866 } else { 00867 iblk_net = logical_block[iblock].input_nets[port->index][ipin]; 00868 } 00869 if(iblk_net == OPEN) { 00870 continue; 00871 } 00872 if(!is_net_in_cluster(iblk_net)){ 00873 nets_in_cluster[num_nets_in_cluster] = iblk_net; 00874 num_nets_in_cluster++; 00875 } 00876 success = add_net_rr_terminal_cluster(iblk_net, primitive, iblock, port, ipin); 00877 } 00878 port = port->next; 00879 } 00880 00881 port = logical_block[iblock].model->outputs; 00882 while(port && success) { 00883 for(ipin = 0; ipin < port->size && success; ipin++) { 00884 iblk_net = logical_block[iblock].output_nets[port->index][ipin]; 00885 if(iblk_net == OPEN) { 00886 continue; 00887 } 00888 if(!is_net_in_cluster(iblk_net)){ 00889 nets_in_cluster[num_nets_in_cluster] = iblk_net; 00890 num_nets_in_cluster++; 00891 } 00892 success = add_net_rr_terminal_cluster(iblk_net, primitive, iblock, port, ipin); 00893 } 00894 port = port->next; 00895 } 00896 00897 if(success) { 00898 success = reload_ext_net_rr_terminal_cluster(); 00899 } 00900 00901 if(success) { 00902 /* route it */ 00903 reset_rr_node_route_structs(); /* Clear all prior rr_graph history */ 00904 success = try_breadth_first_route_cluster(); /* route from scratch */ 00905 } 00906 00907 if(success) { 00908 return BLK_PASSED; 00909 } else { 00910 /* Cannot pack, restore cluster */ 00911 primitive->logical_block = OPEN; 00912 logical_block[iblock].pb = NULL; 00913 logical_block[iblock].clb_index = NO_CLUSTER; 00914 restore_routing_cluster(); 00915 num_nets_in_cluster = orig_num_nets_in_cluster; 00916 return BLK_FAILED_ROUTE; 00917 } 00918 } 00919 00920 00921 /** This routing frees any routing currently held in best routing, 00922 * then copies over the current routing (held in trace_head), and 00923 * finally sets trace_head and trace_tail to all NULLs so that the 00924 * connection to the saved routing is broken. This is necessary so 00925 * that the next iteration of the router does not free the saved 00926 * routing elements. Also, the routing path costs and net_rr_terminals is stripped from the 00927 * existing rr_graph so that the saved routing does not affect the graph 00928 */ 00929 static void 00930 save_and_reset_routing_cluster() { 00931 00932 int inet, i, j; 00933 struct s_trace *tempptr; 00934 00935 for(i = 0; i < num_nets_in_cluster; i++) 00936 { 00937 inet = nets_in_cluster[i]; 00938 for(j = 0; j <= vpack_net[inet].num_sinks; j++) { 00939 saved_net_rr_terminals[inet][j] = net_rr_terminals[inet][j]; 00940 } 00941 00942 /* Free any previously saved routing. It is no longer best. */ 00943 /* Also Save a pointer to the current routing in best_routing. */ 00944 00945 pathfinder_update_one_cost(trace_head[inet], -1, pres_fac); 00946 tempptr = trace_head[inet]; 00947 trace_head[inet] = best_routing[inet]; 00948 free_traceback(inet); 00949 best_routing[inet] = tempptr; 00950 00951 /* Set the current (working) routing to NULL so the current trace * 00952 * elements won't be reused by the memory allocator. */ 00953 00954 trace_head[inet] = NULL; 00955 trace_tail[inet] = NULL; 00956 } 00957 } 00958 00959 /** Deallocates any current routing in trace_head, and replaces it with 00960 * the routing in best_routing. Best_routing is set to NULL to show that 00961 * it no longer points to a valid routing. NOTE: trace_tail is not 00962 * restored -- it is set to all NULLs since it is only used in 00963 * update_traceback. If you need trace_tail restored, modify this 00964 * routine. Also restores the locally used opin data. 00965 */ 00966 static void 00967 restore_routing_cluster() 00968 { 00969 int inet, i, j; 00970 00971 for(i = 0; i < num_nets_in_cluster; i++) 00972 { 00973 inet = nets_in_cluster[i]; 00974 00975 pathfinder_update_one_cost(trace_head[inet], -1, pres_fac); 00976 00977 /* Free any current routing. */ 00978 free_traceback(inet); 00979 00980 /* Set the current routing to the saved one. */ 00981 trace_head[inet] = best_routing[inet]; 00982 best_routing[inet] = NULL; /* No stored routing. */ 00983 00984 /* restore net terminals */ 00985 for(j = 0; j <= vpack_net[inet].num_sinks; j++) { 00986 net_rr_terminals[inet][j] = saved_net_rr_terminals[inet][j]; 00987 } 00988 00989 /* restore old routing */ 00990 pathfinder_update_one_cost(trace_head[inet], 1, pres_fac); 00991 } 00992 } 00993 00994 /** This routine updates the occupancy and pres_cost of the rr_nodes that are 00995 * affected by the portion of the routing of one net that starts at 00996 * route_segment_start. If route_segment_start is trace_head[inet], the 00997 * cost of all the nodes in the routing of net inet are updated. If 00998 * add_or_sub is -1 the net (or net portion) is ripped up, if it is 1 the 00999 * net is added to the routing. The size of pres_fac determines how severly 01000 * oversubscribed rr_nodes are penalized. 01001 */ 01002 void save_cluster_solution() 01003 { 01004 int i, j, net_index; 01005 struct s_trace *tptr, *prev; 01006 int inode; 01007 for(i = 0; i < max_ext_index; i++) { 01008 rr_node[i].net_num = OPEN; 01009 rr_node[i].prev_edge = OPEN; 01010 rr_node[i].prev_node = OPEN; 01011 } 01012 for(i = 0; i < num_nets_in_cluster; i++) { 01013 prev = NULL; 01014 net_index = nets_in_cluster[i]; 01015 tptr = trace_head[net_index]; 01016 if(tptr == NULL) /* No routing yet. */ 01017 return; 01018 01019 for(;;) 01020 { 01021 inode = tptr->index; 01022 rr_node[inode].net_num = net_index; 01023 if(prev != NULL) { 01024 rr_node[inode].prev_node = prev->index; 01025 for(j = 0; j < rr_node[prev->index].num_edges; j++) { 01026 if(rr_node[prev->index].edges[j] == inode) { 01027 rr_node[inode].prev_edge = j; 01028 break; 01029 } 01030 } 01031 assert(j != rr_node[prev->index].num_edges); 01032 } else { 01033 rr_node[inode].prev_node = OPEN; 01034 rr_node[inode].prev_edge = OPEN; 01035 } 01036 01037 if(rr_node[inode].type == SINK) 01038 { 01039 tptr = tptr->next; /* Skip next segment. */ 01040 if(tptr == NULL) 01041 break; 01042 } 01043 01044 prev = tptr; 01045 tptr = tptr->next; 01046 01047 } /* End while loop -- did an entire traceback. */ 01048 } 01049 } 01050 01051 boolean is_pin_open(int i) { 01052 return (rr_node[i].occ == 0); 01053 } 01054 01055 01056 /** Prints out the routing to file route_file. */ 01057 void 01058 print_intra_cluster_route(char *route_file) 01059 { 01060 int inet, inode; 01061 t_rr_type rr_type; 01062 struct s_trace *tptr; 01063 char *name_type[] = 01064 { "SOURCE", "SINK", "IPIN", "OPIN", "CHANX", "CHANY", "INTRA_CLUSTER_EDGE" }; 01065 FILE *fp; 01066 01067 fp = my_fopen(route_file, "w", 0); 01068 01069 fprintf(fp, "\nRouting:"); 01070 for(inet = 0; inet < num_nets; inet++) 01071 { 01072 01073 if(vpack_net[inet].num_sinks == FALSE) 01074 { 01075 fprintf(fp, "\n\nNet %d (%s)\n\n", inet, vpack_net[inet].name); 01076 fprintf(fp, "\n\n Used in local cluster only, reserved one CLB pin\n\n"); 01077 } else if(trace_head[inet]){ 01078 fprintf(fp, "\n\nNet %d (%s)\n\n", inet, vpack_net[inet].name); 01079 tptr = trace_head[inet]; 01080 01081 while(tptr != NULL) 01082 { 01083 inode = tptr->index; 01084 rr_type = rr_node[inode].type; 01085 01086 fprintf(fp, "%6s %d ", name_type[rr_type], inode); 01087 01088 01089 01090 /* Uncomment line below if you're debugging and want to see the switch types * 01091 * used in the routing. */ 01092 /* fprintf (fp, "Switch: %d", tptr->iswitch); */ 01093 01094 fprintf(fp, "\n"); 01095 01096 tptr = tptr->next; 01097 } 01098 } 01099 } 01100 01101 fclose(fp); 01102 } 01103 01104 /** This is a tie breaker to avoid using nodes with more edges whenever possible */ 01105 static float rr_node_intrinsic_cost(int inode) { 01106 float value; 01107 value = rr_node[inode].pack_intrinsic_cost; 01108 return value; 01109 } 01110 01111 /** turns on mode for a pb by setting capacity of its rr_nodes to 1 */ 01112 void set_pb_mode(t_pb *pb, int mode, int isOn) { 01113 int i, j, index; 01114 int i_pb_type, i_pb_inst; 01115 const t_pb_type *pb_type; 01116 t_pb_graph_node *pb_graph_node; 01117 01118 pb_type = pb->pb_graph_node->pb_type; 01119 for(i_pb_type = 0; i_pb_type < pb_type->modes[mode].num_pb_type_children; i_pb_type++) { 01120 for(i_pb_inst = 0; i_pb_inst < pb_type->modes[mode].pb_type_children[i_pb_type].num_pb; i_pb_inst++) { 01121 pb_graph_node = &pb->pb_graph_node->child_pb_graph_nodes[mode][i_pb_type][i_pb_inst]; 01122 for(i = 0; i < pb_graph_node->num_input_ports; i++) { 01123 for(j = 0; j < pb_graph_node->num_input_pins[i]; j++) { 01124 index = pb_graph_node->input_pins[i][j].pin_count_in_cluster; 01125 rr_node[index].capacity = isOn; 01126 } 01127 } 01128 01129 for(i = 0; i < pb_graph_node->num_output_ports; i++) { 01130 for(j = 0; j < pb_graph_node->num_output_pins[i]; j++) { 01131 index = pb_graph_node->output_pins[i][j].pin_count_in_cluster; 01132 rr_node[index].capacity = isOn; 01133 } 01134 } 01135 01136 for(i = 0; i < pb_graph_node->num_clock_ports; i++) { 01137 for(j = 0; j < pb_graph_node->num_clock_pins[i]; j++) { 01138 index = pb_graph_node->clock_pins[i][j].pin_count_in_cluster; 01139 rr_node[index].capacity = isOn; 01140 } 01141 } 01142 } 01143 } 01144 }