#include <stdio.h>
#include <assert.h>
#include <sys/types.h>
#include "util.h"
#include "vpr_types.h"
#include "globals.h"
#include "mst.h"
#include "route_export.h"
#include "route_common.h"
#include "route_directed_search.h"
#include "draw.h"
Go to the source code of this file.
Defines | |
#define | ROUND_UP(x) (ceil (x - 0.001)) |
Functions | |
static boolean | directed_search_route_net (int inet, float pres_fac, float astar_fac, float bend_cost, t_mst_edge **mst) |
static void | directed_search_expand_trace_segment (struct s_trace *start_ptr, int target_node, float astar_fac, int remaining_connections_to_sink) |
static void | directed_search_expand_neighbours (struct s_heap *current, int inet, float bend_cost, int target_node, float astar_fac) |
static void | directed_search_add_source_to_heap (int inet, int target_node, float astar_fac) |
static float | get_directed_search_expected_cost (int inode, int target_node) |
static int | get_expected_segs_to_target (int inode, int target_node, int *num_segs_ortho_dir_ptr) |
boolean | try_directed_search_route (struct s_router_opts router_opts, t_ivec **fb_opins_used_locally, int width_fac, t_mst_edge **mst) |
#define ROUND_UP | ( | x | ) | (ceil (x - 0.001)) |
Definition at line 537 of file route_directed_search.c.
static void directed_search_add_source_to_heap | ( | int | inet, | |
int | target_node, | |||
float | astar_fac | |||
) | [static] |
Definition at line 456 of file route_directed_search.c.
00459 { 00460 00461 /* Adds the SOURCE of this net to the heap. Used to start a net's routing. */ 00462 00463 int inode; 00464 float back_cost, tot_cost; 00465 00466 inode = net_rr_terminals[inet][0]; /* SOURCE */ 00467 back_cost = 0.0 + get_rr_cong_cost(inode); 00468 00469 /* setting the total cost to 0 because it's the only element on the heap */ 00470 if(!is_empty_heap()) 00471 { 00472 printf 00473 ("Error: Wrong Assumption: in directed_search_add_source_to_heap " 00474 "the heap is not empty. Need to properly calculate source node's cost.\n"); 00475 exit(1); 00476 } 00477 00478 /* WMF: path cost is 0. could use tot_cost = 0 to save some computation time, but 00479 * for consistency, I chose to do the expected cost calculation. */ 00480 tot_cost = 00481 back_cost + astar_fac * get_directed_search_expected_cost(inode, 00482 target_node); 00483 node_to_heap(inode, tot_cost, NO_PREVIOUS, NO_PREVIOUS, back_cost, OPEN); 00484 00485 }
static void directed_search_expand_neighbours | ( | struct s_heap * | current, | |
int | inet, | |||
float | bend_cost, | |||
int | target_node, | |||
float | astar_fac | |||
) | [static] |
Definition at line 386 of file route_directed_search.c.
00391 { 00392 00393 /* Puts all the rr_nodes adjacent to current on the heap. rr_nodes outside * 00394 * the expanded bounding box specified in route_bb are not added to the * 00395 * heap. back_cost is the path_cost to get to inode. total cost i.e. 00396 * tot_cost is path_cost + (expected_cost to target sink) */ 00397 00398 int iconn, to_node, num_edges, inode, target_x, target_y; 00399 t_rr_type from_type, to_type; 00400 float new_tot_cost, old_back_pcost, new_back_pcost; 00401 00402 inode = current->index; 00403 old_back_pcost = current->backward_path_cost; 00404 num_edges = rr_node[inode].num_edges; 00405 00406 target_x = rr_node[target_node].xhigh; 00407 target_y = rr_node[target_node].yhigh; 00408 00409 for(iconn = 0; iconn < num_edges; iconn++) 00410 { 00411 to_node = rr_node[inode].edges[iconn]; 00412 00413 if(rr_node[to_node].xhigh < route_bb[inet].xmin || 00414 rr_node[to_node].xlow > route_bb[inet].xmax || 00415 rr_node[to_node].yhigh < route_bb[inet].ymin || 00416 rr_node[to_node].ylow > route_bb[inet].ymax) 00417 continue; /* Node is outside (expanded) bounding box. */ 00418 00419 /* Prune away IPINs that lead to blocks other than the target one. Avoids * 00420 * the issue of how to cost them properly so they don't get expanded before * 00421 * more promising routes, but makes route-throughs (via CLBs) impossible. * 00422 * Change this if you want to investigate route-throughs. */ 00423 00424 to_type = rr_node[to_node].type; 00425 if(to_type == IPIN && (rr_node[to_node].xhigh != target_x || 00426 rr_node[to_node].yhigh != target_y)) 00427 continue; 00428 00429 /* new_back_pcost stores the "known" part of the cost to this node -- the * 00430 * congestion cost of all the routing resources back to the existing route * 00431 * new_tot_cost 00432 * is this "known" backward cost + an expected cost to get to the target. */ 00433 00434 new_back_pcost = old_back_pcost + get_rr_cong_cost(to_node); 00435 00436 if(bend_cost != 0.) 00437 { 00438 from_type = rr_node[inode].type; 00439 to_type = rr_node[to_node].type; 00440 if((from_type == CHANX && to_type == CHANY) || 00441 (from_type == CHANY && to_type == CHANX)) 00442 new_back_pcost += bend_cost; 00443 } 00444 00445 /* Calculate the new total cost = path cost + astar_fac * remaining distance to target */ 00446 new_tot_cost = new_back_pcost + astar_fac * 00447 get_directed_search_expected_cost(to_node, target_node); 00448 00449 node_to_heap(to_node, new_tot_cost, inode, iconn, new_back_pcost, 00450 OPEN); 00451 } 00452 }
static void directed_search_expand_trace_segment | ( | struct s_trace * | start_ptr, | |
int | target_node, | |||
float | astar_fac, | |||
int | remaining_connections_to_sink | |||
) | [static] |
Definition at line 313 of file route_directed_search.c.
00317 { 00318 00319 /* Adds all the rr_nodes in the entire traceback from SOURCE to all SINKS * 00320 * routed so far (partial routing). 00321 * This allows expansion to begin from the existing wiring. The * 00322 * remaining_connections_to_sink value is 0 if the route segment ending * 00323 * at this location is the last one to connect to the SINK ending the route * 00324 * segment. This is the usual case. If it is not the last connection this * 00325 * net must make to this SINK, I have a hack to ensure the next connection * 00326 * to this SINK goes through a different IPIN. Without this hack, the * 00327 * router would always put all the connections from this net to this SINK * 00328 * through the same IPIN. With LUTs or cluster-based logic blocks, you * 00329 * should never have a net connecting to two logically-equivalent pins on * 00330 * the same logic block, so the hack will never execute. If your logic * 00331 * block is an and-gate, however, nets might connect to two and-inputs on * 00332 * the same logic block, and since the and-inputs are logically-equivalent, * 00333 * this means two connections to the same SINK. */ 00334 00335 struct s_trace *tptr; 00336 int inode, backward_path_cost, tot_cost; 00337 00338 tptr = start_ptr; 00339 00340 if(remaining_connections_to_sink == 0) 00341 { /* Usual case. */ 00342 while(tptr != NULL) 00343 { 00344 /* WMF: partial routing is added to the heap with path cost of 0, because 00345 * new extension to the next sink can start at any point on current partial 00346 * routing. However, for directed search the total cost must be made to favor 00347 * the points of current partial routing that are NEAR the next sink (target sink) */ 00348 00349 /* WMF: IPINs and SINKs should be excluded from the heap in this 00350 * since they NEVER connect TO any rr_node (no to_edges), but since they have 00351 * no to_edges, it's ok (ROUTE_THROUGHS are disabled). To clarify, see 00352 * rr_graph.c to find out rr_node[inode].num_edges = 0 for SINKs and 00353 * rr_node[inode].num_edges = 1 for INPINs */ 00354 00355 inode = tptr->index; 00356 00357 if(! 00358 (rr_node[inode].type == IPIN 00359 || rr_node[inode].type == SINK)) 00360 { 00361 00362 backward_path_cost = 0; 00363 tot_cost = 00364 backward_path_cost + 00365 astar_fac * 00366 get_directed_search_expected_cost(inode, 00367 target_node); 00368 node_to_heap(inode, tot_cost, NO_PREVIOUS, 00369 NO_PREVIOUS, backward_path_cost, 00370 OPEN); 00371 } 00372 00373 tptr = tptr->next; 00374 } 00375 } 00376 00377 else 00378 { /* This case never executes for most logic blocks. */ 00379 printf("Warning: Multiple connections from net to the same sink. " 00380 "This should not happen for LUT/Cluster based logic blocks. Aborting.\n"); 00381 exit(1); 00382 } 00383 }
static boolean directed_search_route_net | ( | int | inet, | |
float | pres_fac, | |||
float | astar_fac, | |||
float | bend_cost, | |||
t_mst_edge ** | mst | |||
) | [static] |
Definition at line 158 of file route_directed_search.c.
00163 { 00164 00165 /* Uses a maze routing (Dijkstra's) algorithm to route a net. The net * 00166 * begins at the net output, and expands outward until it hits a target * 00167 * pin. The algorithm is then restarted with the entire first wire segment * 00168 * included as part of the source this time. For an n-pin net, the maze * 00169 * router is invoked n-1 times to complete all the connections. Inet is * 00170 * the index of the net to be routed. Bends are penalized by bend_cost * 00171 * (which is typically zero for detailed routing and nonzero only for global * 00172 * routing), since global routes with lots of bends are tougher to detailed * 00173 * route (using a detailed router like SEGA). * 00174 * If this routine finds that a net *cannot* be connected (due to a complete * 00175 * lack of potential paths, rather than congestion), it returns FALSE, as * 00176 * routing is impossible on this architecture. Otherwise it returns TRUE. */ 00177 /* WMF: This is the directed search (A-star) version of maze router. */ 00178 00179 int inode, remaining_connections_to_sink; 00180 int itarget, target_pin, target_node; 00181 struct s_heap *current; 00182 struct s_trace *new_route_start_tptr; 00183 float old_tcost, new_tcost, old_back_cost, new_back_cost; 00184 00185 /* Rip-up any old routing. */ 00186 /* WMF: For the 1st router iteration trace_head[inet] is NULL, as it is 00187 * my_calloc'ed in alloc_route_structs() so the following does nothing. 00188 * However, for subsequent iterations, trace_head[inet] contains the previous 00189 * ieration's routing for this net. */ 00190 pathfinder_update_one_cost(trace_head[inet], -1, pres_fac); 00191 free_traceback(inet); /* kills trace, and set the trace head and tail to NULL */ 00192 00193 /* adding the SOURCE node to the heap with correct total cost */ 00194 target_pin = mst[inet][0].to_node; 00195 target_node = net_rr_terminals[inet][target_pin]; 00196 directed_search_add_source_to_heap(inet, target_node, astar_fac); 00197 00198 mark_ends(inet); 00199 00200 remaining_connections_to_sink = 0; 00201 00202 for(itarget = 0; itarget < net[inet].num_sinks; itarget++) 00203 { 00204 target_pin = mst[inet][itarget].to_node; 00205 target_node = net_rr_terminals[inet][target_pin]; 00206 00207 /* printf ("Target #%d, pin number %d, target_node: %d.\n", 00208 * itarget, target_pin, target_node); */ 00209 00210 /* WMF: since the heap has been emptied, need to restart the wavefront 00211 * from the current partial routing, starting at the trace_head (SOURCE) 00212 * Note: in the 1st iteration, there is no trace (no routing at all for this net) 00213 * i.e. trace_head[inet] == NULL (found in free_traceback() in route_common.c, 00214 * which is called before the routing of any net), 00215 * so this routine does nothing, but the heap does have the SOURCE node due 00216 * to directed_search_add_source_to_heap (inet) before the loop */ 00217 directed_search_expand_trace_segment(trace_head[inet], 00218 target_node, astar_fac, 00219 remaining_connections_to_sink); 00220 00221 current = get_heap_head(); 00222 00223 if(current == NULL) 00224 { /* Infeasible routing. No possible path for net. */ 00225 reset_path_costs(); /* Clean up before leaving. */ 00226 return (FALSE); 00227 } 00228 00229 inode = current->index; 00230 00231 while(inode != target_node) 00232 { 00233 old_tcost = rr_node_route_inf[inode].path_cost; 00234 new_tcost = current->cost; 00235 00236 /* WMF: not needed if Vaughn initialized rr_node_route_inf[inode].backward_path_cost 00237 * to HUGE_FLOAT along with rr_node_route_inf[inode].path_cost */ 00238 if(old_tcost > 0.99 * HUGE_FLOAT) /* First time touched. */ 00239 old_back_cost = HUGE_FLOAT; 00240 else 00241 old_back_cost = 00242 rr_node_route_inf[inode].backward_path_cost; 00243 00244 new_back_cost = current->backward_path_cost; 00245 00246 /* I only re-expand a node if both the "known" backward cost is lower * 00247 * in the new expansion (this is necessary to prevent loops from * 00248 * forming in the routing and causing havoc) *and* the expected total * 00249 * cost to the sink is lower than the old value. Different R_upstream * 00250 * values could make a path with lower back_path_cost less desirable * 00251 * than one with higher cost. Test whether or not I should disallow * 00252 * re-expansion based on a higher total cost. */ 00253 00254 /* updating the maze (Dijktra's min dist algorithm) if found "shorter" path */ 00255 if(old_tcost > new_tcost && old_back_cost > new_back_cost) 00256 { 00257 /* if (old_tcost > new_tcost) { */ 00258 rr_node_route_inf[inode].prev_node = 00259 current->u.prev_node; 00260 rr_node_route_inf[inode].prev_edge = 00261 current->prev_edge; 00262 rr_node_route_inf[inode].path_cost = new_tcost; 00263 rr_node_route_inf[inode].backward_path_cost = 00264 new_back_cost; 00265 00266 if(old_tcost > 0.99 * HUGE_FLOAT) /* First time touched. */ 00267 add_to_mod_list(&rr_node_route_inf[inode]. 00268 path_cost); 00269 00270 directed_search_expand_neighbours(current, inet, 00271 bend_cost, 00272 target_node, 00273 astar_fac); 00274 } 00275 00276 free_heap_data(current); 00277 current = get_heap_head(); 00278 00279 if(current == NULL) 00280 { /* Impossible routing. No path for net. */ 00281 reset_path_costs(); 00282 return (FALSE); 00283 } 00284 00285 inode = current->index; 00286 } 00287 00288 rr_node_route_inf[inode].target_flag--; /* Connected to this SINK. */ 00289 remaining_connections_to_sink = 00290 rr_node_route_inf[inode].target_flag; 00291 00292 /* keep info on the current routing of this net */ 00293 new_route_start_tptr = update_traceback(current, inet); 00294 00295 free_heap_data(current); 00296 /* update the congestion costs of rr_nodes due to the routing to this sink 00297 * so only those nodes used in the partial routing of this sink and not 00298 * of the entire net (remember we're in a loop for this net over its sinks) */ 00299 pathfinder_update_one_cost(new_route_start_tptr, 1, pres_fac); 00300 00301 /* WMF: MUST empty heap and recalculate all total costs, because 00302 * for the next sink, the target destination is changed, so the expected 00303 * cost calculation is changed also, meaning all the nodes on the heap have 00304 * "stale" total costs (costs based on last sink). */ 00305 empty_heap(); 00306 reset_path_costs(); 00307 } 00308 00309 return (TRUE); 00310 }
static float get_directed_search_expected_cost | ( | int | inode, | |
int | target_node | |||
) | [static] |
Definition at line 489 of file route_directed_search.c.
00491 { 00492 00493 /* Determines the expected cost (due to resouce cost i.e. distance) to reach * 00494 * the target node from inode. It doesn't include the cost of inode -- * 00495 * that's already in the "known" path_cost. */ 00496 00497 t_rr_type rr_type; 00498 int cost_index, ortho_cost_index, num_segs_same_dir, num_segs_ortho_dir; 00499 float cong_cost; 00500 00501 rr_type = rr_node[inode].type; 00502 00503 if(rr_type == CHANX || rr_type == CHANY) 00504 { 00505 num_segs_same_dir = 00506 get_expected_segs_to_target(inode, target_node, 00507 &num_segs_ortho_dir); 00508 cost_index = rr_node[inode].cost_index; 00509 ortho_cost_index = rr_indexed_data[cost_index].ortho_cost_index; 00510 00511 cong_cost = 00512 num_segs_same_dir * rr_indexed_data[cost_index].base_cost + 00513 num_segs_ortho_dir * 00514 rr_indexed_data[ortho_cost_index].base_cost; 00515 cong_cost += 00516 rr_indexed_data[IPIN_COST_INDEX].base_cost + 00517 rr_indexed_data[SINK_COST_INDEX].base_cost; 00518 00519 return (cong_cost); 00520 } 00521 00522 else if(rr_type == IPIN) 00523 { /* Change if you're allowing route-throughs */ 00524 return (rr_indexed_data[SINK_COST_INDEX].base_cost); 00525 } 00526 00527 else 00528 { /* Change this if you want to investigate route-throughs */ 00529 return (0.); 00530 } 00531 }
static int get_expected_segs_to_target | ( | int | inode, | |
int | target_node, | |||
int * | num_segs_ortho_dir_ptr | |||
) | [static] |
Definition at line 541 of file route_directed_search.c.
00544 { 00545 00546 /* Returns the number of segments the same type as inode that will be needed * 00547 * to reach target_node (not including inode) in each direction (the same * 00548 * direction (horizontal or vertical) as inode and the orthogonal direction).*/ 00549 00550 t_rr_type rr_type; 00551 int target_x, target_y, num_segs_same_dir, cost_index, ortho_cost_index; 00552 int no_need_to_pass_by_clb; 00553 float inv_length, ortho_inv_length, ylow, yhigh, xlow, xhigh; 00554 00555 target_x = rr_node[target_node].xlow; 00556 target_y = rr_node[target_node].ylow; 00557 cost_index = rr_node[inode].cost_index; 00558 inv_length = rr_indexed_data[cost_index].inv_length; 00559 ortho_cost_index = rr_indexed_data[cost_index].ortho_cost_index; 00560 ortho_inv_length = rr_indexed_data[ortho_cost_index].inv_length; 00561 rr_type = rr_node[inode].type; 00562 00563 if(rr_type == CHANX) 00564 { 00565 ylow = rr_node[inode].ylow; 00566 xhigh = rr_node[inode].xhigh; 00567 xlow = rr_node[inode].xlow; 00568 00569 /* Count vertical (orthogonal to inode) segs first. */ 00570 00571 if(ylow > target_y) 00572 { /* Coming from a row above target? */ 00573 *num_segs_ortho_dir_ptr = 00574 ROUND_UP((ylow - target_y + 1.) * ortho_inv_length); 00575 no_need_to_pass_by_clb = 1; 00576 } 00577 else if(ylow < target_y - 1) 00578 { /* Below the FB bottom? */ 00579 *num_segs_ortho_dir_ptr = ROUND_UP((target_y - ylow) * 00580 ortho_inv_length); 00581 no_need_to_pass_by_clb = 1; 00582 } 00583 else 00584 { /* In a row that passes by target FB */ 00585 *num_segs_ortho_dir_ptr = 0; 00586 no_need_to_pass_by_clb = 0; 00587 } 00588 00589 /* Now count horizontal (same dir. as inode) segs. */ 00590 00591 if(xlow > target_x + no_need_to_pass_by_clb) 00592 { 00593 num_segs_same_dir = 00594 ROUND_UP((xlow - no_need_to_pass_by_clb - 00595 target_x) * inv_length); 00596 } 00597 else if(xhigh < target_x - no_need_to_pass_by_clb) 00598 { 00599 num_segs_same_dir = 00600 ROUND_UP((target_x - no_need_to_pass_by_clb - 00601 xhigh) * inv_length); 00602 } 00603 else 00604 { 00605 num_segs_same_dir = 0; 00606 } 00607 } 00608 00609 else 00610 { /* inode is a CHANY */ 00611 ylow = rr_node[inode].ylow; 00612 yhigh = rr_node[inode].yhigh; 00613 xlow = rr_node[inode].xlow; 00614 00615 /* Count horizontal (orthogonal to inode) segs first. */ 00616 00617 if(xlow > target_x) 00618 { /* Coming from a column right of target? */ 00619 *num_segs_ortho_dir_ptr = 00620 ROUND_UP((xlow - target_x + 1.) * ortho_inv_length); 00621 no_need_to_pass_by_clb = 1; 00622 } 00623 else if(xlow < target_x - 1) 00624 { /* Left of and not adjacent to the FB? */ 00625 *num_segs_ortho_dir_ptr = ROUND_UP((target_x - xlow) * 00626 ortho_inv_length); 00627 no_need_to_pass_by_clb = 1; 00628 } 00629 else 00630 { /* In a column that passes by target FB */ 00631 *num_segs_ortho_dir_ptr = 0; 00632 no_need_to_pass_by_clb = 0; 00633 } 00634 00635 /* Now count vertical (same dir. as inode) segs. */ 00636 00637 if(ylow > target_y + no_need_to_pass_by_clb) 00638 { 00639 num_segs_same_dir = 00640 ROUND_UP((ylow - no_need_to_pass_by_clb - 00641 target_y) * inv_length); 00642 } 00643 else if(yhigh < target_y - no_need_to_pass_by_clb) 00644 { 00645 num_segs_same_dir = 00646 ROUND_UP((target_y - no_need_to_pass_by_clb - 00647 yhigh) * inv_length); 00648 } 00649 else 00650 { 00651 num_segs_same_dir = 0; 00652 } 00653 } 00654 00655 return (num_segs_same_dir); 00656 }
boolean try_directed_search_route | ( | struct s_router_opts | router_opts, | |
t_ivec ** | fb_opins_used_locally, | |||
int | width_fac, | |||
t_mst_edge ** | mst | |||
) |
Definition at line 48 of file route_directed_search.c.
00052 { 00053 00054 /* Iterated maze router ala Pathfinder Negotiated Congestion algorithm, * 00055 * (FPGA 95 p. 111). Returns TRUE if it can route this FPGA, FALSE if * 00056 * it can't. */ 00057 00058 float pres_fac; 00059 boolean success, is_routable, rip_up_local_opins; 00060 int itry, inet; 00061 00062 /* char msg[100]; */ 00063 00064 /* mst not built as good as it should, ideally, just have it after placement once only 00065 however, rr_node numbers changed when the channel width changes so forced to do it here */ 00066 if(mst) 00067 { 00068 for(inet = 0; inet < num_nets; inet++) 00069 { 00070 free(mst[inet]); 00071 mst[inet] = get_mst_of_net(inet); 00072 } 00073 } 00074 00075 /* Usually the first iteration uses a very small (or 0) pres_fac to find * 00076 * the shortest path and get a congestion map. For fast compiles, I set * 00077 * pres_fac high even for the first iteration. */ 00078 00079 pres_fac = router_opts.first_iter_pres_fac; 00080 00081 for(itry = 1; itry <= router_opts.max_router_iterations; itry++) 00082 { 00083 00084 for(inet = 0; inet < num_nets; inet++) 00085 { 00086 if(net[inet].is_global == FALSE) 00087 { /* Skip global nets. */ 00088 00089 is_routable = 00090 directed_search_route_net(inet, pres_fac, 00091 router_opts. 00092 astar_fac, 00093 router_opts. 00094 bend_cost, mst); 00095 00096 /* Impossible to route? (disconnected rr_graph) */ 00097 00098 if(!is_routable) 00099 { 00100 printf("Routing failed.\n"); 00101 return (FALSE); 00102 } 00103 00104 } 00105 } 00106 00107 /* Make sure any FB OPINs used up by subblocks being hooked directly * 00108 * to them are reserved for that purpose. */ 00109 00110 if(itry == 1) 00111 rip_up_local_opins = FALSE; 00112 else 00113 rip_up_local_opins = TRUE; 00114 00115 reserve_locally_used_opins(pres_fac, rip_up_local_opins, 00116 fb_opins_used_locally); 00117 00118 success = feasible_routing(); 00119 if(success) 00120 { 00121 printf 00122 ("Successfully routed after %d routing iterations by Directed Search.\n", 00123 itry); 00124 return (TRUE); 00125 } 00126 #if 0 00127 else 00128 { 00129 sprintf(msg, 00130 "After iteration %d routing failed (A*) with a channel width factor of %d and Fc_int of %d, Fs_int of %d.", 00131 itry, width_fac, Fc_int, Fs_int); 00132 init_draw_coords(pins_per_clb); 00133 update_screen(MAJOR, msg, ROUTING, FALSE); 00134 } 00135 #endif 00136 00137 00138 if(itry == 1) 00139 { 00140 pres_fac = router_opts.initial_pres_fac; 00141 pathfinder_update_cost(pres_fac, 0.); /* Acc_fac=0 for first iter. */ 00142 } 00143 else 00144 { 00145 pres_fac *= router_opts.pres_fac_mult; 00146 pathfinder_update_cost(pres_fac, router_opts.acc_fac); 00147 } 00148 00149 } 00150 00151 printf("Routing failed.\n"); 00152 00153 return (FALSE); 00154 }