VPR-6.0
|
00001 #include <stdio.h> 00002 #include "util.h" 00003 #include "vpr_types.h" 00004 #include "globals.h" 00005 #include "net_delay.h" 00006 00007 00008 /***************** Types and defines local to this module ********************/ 00009 00010 /* Linked list listing the children of an rc_node. 00011 * - child: Pointer to an rc_node (child of the current node). 00012 * - iswitch: Index of the switch type used to connect to the child node. 00013 * - next: Pointer to the next linked_rc_edge in the linked list (allows 00014 * you to get the next child of the current rc_node). 00015 */ 00016 struct s_linked_rc_edge 00017 { 00018 struct s_rc_node *child; 00019 short iswitch; 00020 struct s_linked_rc_edge *next; 00021 }; 00022 00023 typedef struct s_linked_rc_edge t_linked_rc_edge; 00024 00025 00026 /** Structure describing one node in an RC tree (used to get net delays). 00027 * - u.child_list: Pointer to a linked list of linked_rc_edge. Each one of 00028 * the linked list entries gives a child of this node. 00029 * - u.next: Used only when this node is on the free list. Gives the next 00030 * node on the free list. 00031 * - inode: index (ID) of the rr_node that corresponds to this rc_node. 00032 * - C_downstream: Total downstream capacitance from this rc_node. That is, 00033 * the total C of the subtree rooted at the current node, 00034 * including the C of the current node. 00035 * - Tdel: Time delay for the signal to get from the net source to this node. 00036 * Includes the time to go through this node. 00037 */ 00038 struct s_rc_node 00039 { 00040 union 00041 { 00042 t_linked_rc_edge *child_list; 00043 struct s_rc_node *next; 00044 } 00045 u; 00046 int inode; 00047 float C_downstream; 00048 float Tdel; 00049 }; 00050 00051 typedef struct s_rc_node t_rc_node; 00052 00053 00054 /** Linked list of pointers to rc_nodes. 00055 * - rc_node: Pointer to an rc_node. 00056 * - next: Next list element. 00057 */ 00058 struct s_linked_rc_ptr 00059 { 00060 struct s_rc_node *rc_node; 00061 struct s_linked_rc_ptr *next; 00062 }; 00063 00064 typedef struct s_linked_rc_ptr t_linked_rc_ptr; 00065 00066 00067 00068 00069 /*********************** Subroutines local to this module ********************/ 00070 00071 static t_rc_node *alloc_and_load_rc_tree(int inet, 00072 t_rc_node ** rc_node_free_list_ptr, 00073 t_linked_rc_edge ** 00074 rc_edge_free_list_ptr, 00075 t_linked_rc_ptr * 00076 rr_node_to_rc_node); 00077 00078 static void add_to_rc_tree(t_rc_node * parent_rc, 00079 t_rc_node * child_rc, 00080 short iswitch, 00081 int inode, 00082 t_linked_rc_edge ** rc_edge_free_list_ptr); 00083 00084 static t_rc_node *alloc_rc_node(t_rc_node ** rc_node_free_list_ptr); 00085 00086 static void free_rc_node(t_rc_node * rc_node, 00087 t_rc_node ** rc_node_free_list_ptr); 00088 00089 static t_linked_rc_edge *alloc_linked_rc_edge(t_linked_rc_edge 00090 ** rc_edge_free_list_ptr); 00091 00092 static void free_linked_rc_edge(t_linked_rc_edge * rc_edge, 00093 t_linked_rc_edge ** rc_edge_free_list_ptr); 00094 00095 static float load_rc_tree_C(t_rc_node * rc_node); 00096 00097 static void load_rc_tree_T(t_rc_node * rc_node, 00098 float T_arrival); 00099 00100 static void load_one_net_delay(float **net_delay, 00101 int inet, 00102 struct s_net *nets, 00103 t_linked_rc_ptr * rr_node_to_rc_node); 00104 00105 static void load_one_constant_net_delay(float **net_delay, 00106 int inet, 00107 struct s_net *nets, 00108 float delay_value); 00109 00110 static void free_rc_tree(t_rc_node * rc_root, 00111 t_rc_node ** rc_node_free_list_ptr, 00112 t_linked_rc_edge ** rc_edge_free_list_ptr); 00113 00114 static void reset_rr_node_to_rc_node(t_linked_rc_ptr * rr_node_to_rc_node, 00115 int inet); 00116 00117 static void free_rc_node_free_list(t_rc_node * rc_node_free_list); 00118 00119 static void free_rc_edge_free_list(t_linked_rc_edge * rc_edge_free_list); 00120 00121 00122 00123 /*************************** Subroutine definitions **************************/ 00124 00125 /** Allocates space for the net_delay data structure 00126 * [0..num_nets-1][1..num_pins-1]. I chunk the data to save space on large 00127 * problems. 00128 */ 00129 float ** 00130 alloc_net_delay(struct s_linked_vptr **chunk_list_head_ptr, struct s_net *nets, int n_nets) 00131 { 00132 00133 float **net_delay; /* [0..num_nets-1][1..num_pins-1] */ 00134 float *tmp_ptr; 00135 int inet; 00136 int chunk_bytes_avail; 00137 char *chunk_next_avail_mem; 00138 00139 *chunk_list_head_ptr = NULL; 00140 chunk_bytes_avail = 0; 00141 chunk_next_avail_mem = NULL; 00142 00143 net_delay = (float **)my_malloc(n_nets * sizeof(float *)); 00144 00145 for(inet = 0; inet < n_nets; inet++) 00146 { 00147 tmp_ptr = 00148 (float *)my_chunk_malloc(((nets[inet].num_sinks + 1) - 1) * 00149 sizeof(float), chunk_list_head_ptr, 00150 &chunk_bytes_avail, 00151 &chunk_next_avail_mem); 00152 00153 net_delay[inet] = tmp_ptr - 1; /* [1..num_pins-1] */ 00154 } 00155 00156 return (net_delay); 00157 } 00158 00159 00160 /** Frees the net_delay structure. Assumes it was chunk allocated. */ 00161 void 00162 free_net_delay(float **net_delay, 00163 struct s_linked_vptr **chunk_list_head_ptr) 00164 { 00165 free(net_delay); 00166 free_chunk_memory(*chunk_list_head_ptr); 00167 *chunk_list_head_ptr = NULL; 00168 } 00169 00170 /** This routine loads net_delay[0..num_nets-1][1..num_pins-1]. Each entry 00171 * is the Elmore delay from the net source to the appropriate sink. Both 00172 * the rr_graph and the routing traceback must be completely constructed 00173 * before this routine is called, and the net_delay array must have been 00174 * allocated. 00175 */ 00176 void 00177 load_net_delay_from_routing(float **net_delay, struct s_net *nets, int n_nets) 00178 { 00179 00180 00181 t_rc_node *rc_node_free_list, *rc_root; 00182 t_linked_rc_edge *rc_edge_free_list; 00183 int inet; 00184 t_linked_rc_ptr *rr_node_to_rc_node; /* [0..num_rr_nodes-1] */ 00185 00186 rr_node_to_rc_node = (t_linked_rc_ptr *) my_calloc(num_rr_nodes, 00187 sizeof 00188 (t_linked_rc_ptr)); 00189 00190 rc_node_free_list = NULL; 00191 rc_edge_free_list = NULL; 00192 00193 for(inet = 0; inet < n_nets; inet++) 00194 { 00195 if(nets[inet].is_global) 00196 { 00197 load_one_constant_net_delay(net_delay, inet, nets, 0.); 00198 } 00199 else 00200 { 00201 rc_root = alloc_and_load_rc_tree(inet, &rc_node_free_list, 00202 &rc_edge_free_list, 00203 rr_node_to_rc_node); 00204 load_rc_tree_C(rc_root); 00205 load_rc_tree_T(rc_root, 0.); 00206 load_one_net_delay(net_delay, inet, nets, rr_node_to_rc_node); 00207 free_rc_tree(rc_root, &rc_node_free_list, 00208 &rc_edge_free_list); 00209 reset_rr_node_to_rc_node(rr_node_to_rc_node, inet); 00210 } 00211 } 00212 00213 free_rc_node_free_list(rc_node_free_list); 00214 free_rc_edge_free_list(rc_edge_free_list); 00215 free(rr_node_to_rc_node); 00216 } 00217 00218 /** Loads the net_delay array with delay_value for every source - sink 00219 * connection that is not on a global resource, and with 0. for every source 00220 * - sink connection on a global net. (This can be used to allow timing 00221 * analysis before routing is done with a constant net delay model). 00222 */ 00223 void 00224 load_constant_net_delay(float **net_delay, 00225 float delay_value, struct s_net *nets, int n_nets) 00226 { 00227 00228 int inet; 00229 00230 for(inet = 0; inet < n_nets; inet++) 00231 { 00232 if(nets[inet].is_global) 00233 { 00234 load_one_constant_net_delay(net_delay, inet, nets, 0.); 00235 } 00236 else 00237 { 00238 load_one_constant_net_delay(net_delay, inet, nets, delay_value); 00239 } 00240 } 00241 } 00242 00243 /** Builds a tree describing the routing of net inet. Allocates all the data 00244 * and inserts all the connections in the tree. 00245 */ 00246 static t_rc_node * 00247 alloc_and_load_rc_tree(int inet, 00248 t_rc_node ** rc_node_free_list_ptr, 00249 t_linked_rc_edge ** rc_edge_free_list_ptr, 00250 t_linked_rc_ptr * rr_node_to_rc_node) 00251 { 00252 00253 t_rc_node *curr_rc, *prev_rc, *root_rc; 00254 struct s_trace *tptr; 00255 int inode, prev_node; 00256 short iswitch; 00257 t_linked_rc_ptr *linked_rc_ptr; 00258 00259 root_rc = alloc_rc_node(rc_node_free_list_ptr); 00260 tptr = trace_head[inet]; 00261 00262 if(tptr == NULL) 00263 { 00264 printf 00265 ("Error in alloc_and_load_rc_tree: Traceback for net %d doesn't " 00266 "exist.\n", inet); 00267 exit(1); 00268 } 00269 00270 inode = tptr->index; 00271 iswitch = tptr->iswitch; 00272 root_rc->inode = inode; 00273 root_rc->u.child_list = NULL; 00274 rr_node_to_rc_node[inode].rc_node = root_rc; 00275 00276 prev_rc = root_rc; 00277 tptr = tptr->next; 00278 00279 while(tptr != NULL) 00280 { 00281 inode = tptr->index; 00282 00283 /* Is this node a "stitch-in" point to part of the existing routing or a * 00284 * new piece of routing along the current routing "arm?" */ 00285 00286 if(rr_node_to_rc_node[inode].rc_node == NULL) 00287 { /* Part of current "arm" */ 00288 curr_rc = alloc_rc_node(rc_node_free_list_ptr); 00289 add_to_rc_tree(prev_rc, curr_rc, iswitch, inode, 00290 rc_edge_free_list_ptr); 00291 rr_node_to_rc_node[inode].rc_node = curr_rc; 00292 prev_rc = curr_rc; 00293 } 00294 00295 else if(rr_node[inode].type != SINK) 00296 { /* Connection to old stuff. */ 00297 00298 #ifdef DEBUG 00299 prev_node = prev_rc->inode; 00300 if(rr_node[prev_node].type != SINK) 00301 { 00302 printf 00303 ("Error in alloc_and_load_rc_tree: Routing of net %d is " 00304 "not a tree.\n", inet); 00305 exit(1); 00306 } 00307 #endif 00308 00309 prev_rc = rr_node_to_rc_node[inode].rc_node; 00310 } 00311 00312 else 00313 { /* SINK that this net has connected to more than once. */ 00314 00315 /* I can connect to a SINK node more than once in some weird architectures. * 00316 * That means the routing isn't really a tree -- there is reconvergent * 00317 * fanout from two or more IPINs into one SINK. I convert this structure * 00318 * into a true RC tree on the fly by creating a new rc_node each time I hit * 00319 * the same sink. This means I need to keep a linked list of the rc_nodes * 00320 * associated with the rr_node (inode) associated with that SINK. */ 00321 00322 curr_rc = alloc_rc_node(rc_node_free_list_ptr); 00323 add_to_rc_tree(prev_rc, curr_rc, iswitch, inode, 00324 rc_edge_free_list_ptr); 00325 00326 linked_rc_ptr = (t_linked_rc_ptr *) 00327 my_malloc(sizeof(t_linked_rc_ptr)); 00328 linked_rc_ptr->next = rr_node_to_rc_node[inode].next; 00329 rr_node_to_rc_node[inode].next = linked_rc_ptr; 00330 linked_rc_ptr->rc_node = curr_rc; 00331 00332 prev_rc = curr_rc; 00333 } 00334 iswitch = tptr->iswitch; 00335 tptr = tptr->next; 00336 } 00337 00338 return (root_rc); 00339 } 00340 00341 /** Adds child_rc to the child list of parent_rc, and sets the switch between 00342 * them to iswitch. This routine also intitializes the child_rc properly 00343 * and sets its node value to inode. 00344 */ 00345 static void 00346 add_to_rc_tree(t_rc_node * parent_rc, 00347 t_rc_node * child_rc, 00348 short iswitch, 00349 int inode, 00350 t_linked_rc_edge ** rc_edge_free_list_ptr) 00351 { 00352 00353 t_linked_rc_edge *linked_rc_edge; 00354 00355 linked_rc_edge = alloc_linked_rc_edge(rc_edge_free_list_ptr); 00356 00357 linked_rc_edge->next = parent_rc->u.child_list; 00358 parent_rc->u.child_list = linked_rc_edge; 00359 00360 linked_rc_edge->child = child_rc; 00361 linked_rc_edge->iswitch = iswitch; 00362 00363 child_rc->u.child_list = NULL; 00364 child_rc->inode = inode; 00365 } 00366 00367 /** Allocates a new rc_node, from the free list if possible, from the free 00368 * store otherwise. 00369 */ 00370 static t_rc_node * 00371 alloc_rc_node(t_rc_node ** rc_node_free_list_ptr) 00372 { 00373 00374 t_rc_node *rc_node; 00375 00376 rc_node = *rc_node_free_list_ptr; 00377 00378 if(rc_node != NULL) 00379 { 00380 *rc_node_free_list_ptr = rc_node->u.next; 00381 } 00382 else 00383 { 00384 rc_node = (t_rc_node *) my_malloc(sizeof(t_rc_node)); 00385 } 00386 00387 return (rc_node); 00388 } 00389 00390 00391 /** Adds rc_node to the proper free list. */ 00392 static void 00393 free_rc_node(t_rc_node * rc_node, 00394 t_rc_node ** rc_node_free_list_ptr) 00395 { 00396 rc_node->u.next = *rc_node_free_list_ptr; 00397 *rc_node_free_list_ptr = rc_node; 00398 } 00399 00400 /** Allocates a new linked_rc_edge, from the free list if possible, from the 00401 * free store otherwise. 00402 */ 00403 static t_linked_rc_edge * 00404 alloc_linked_rc_edge(t_linked_rc_edge ** rc_edge_free_list_ptr) 00405 { 00406 00407 t_linked_rc_edge *linked_rc_edge; 00408 00409 linked_rc_edge = *rc_edge_free_list_ptr; 00410 00411 if(linked_rc_edge != NULL) 00412 { 00413 *rc_edge_free_list_ptr = linked_rc_edge->next; 00414 } 00415 else 00416 { 00417 linked_rc_edge = (t_linked_rc_edge *) my_malloc(sizeof 00418 (t_linked_rc_edge)); 00419 } 00420 00421 return (linked_rc_edge); 00422 } 00423 00424 00425 /** Adds the rc_edge to the rc_edge free list. */ 00426 static void 00427 free_linked_rc_edge(t_linked_rc_edge * rc_edge, 00428 t_linked_rc_edge ** rc_edge_free_list_ptr) 00429 { 00430 rc_edge->next = *rc_edge_free_list_ptr; 00431 *rc_edge_free_list_ptr = rc_edge; 00432 } 00433 00434 /** Does a post-order traversal of the rc tree to load each node's 00435 * C_downstream with the proper sum of all the downstream capacitances. 00436 * This routine calls itself recursively to perform the traversal. 00437 */ 00438 static float 00439 load_rc_tree_C(t_rc_node * rc_node) 00440 { 00441 00442 t_linked_rc_edge *linked_rc_edge; 00443 t_rc_node *child_node; 00444 int inode; 00445 short iswitch; 00446 float C, C_downstream; 00447 00448 linked_rc_edge = rc_node->u.child_list; 00449 inode = rc_node->inode; 00450 C = rr_node[inode].C; 00451 00452 while(linked_rc_edge != NULL) 00453 { /* For all children */ 00454 iswitch = linked_rc_edge->iswitch; 00455 child_node = linked_rc_edge->child; 00456 C_downstream = load_rc_tree_C(child_node); 00457 00458 if(switch_inf[iswitch].buffered == FALSE) 00459 C += C_downstream; 00460 00461 linked_rc_edge = linked_rc_edge->next; 00462 } 00463 00464 rc_node->C_downstream = C; 00465 return (C); 00466 } 00467 00468 /** This routine does a pre-order depth-first traversal of the rc tree to 00469 * compute the Tdel to each node in the rc tree. The T_arrival is the time 00470 * at which the signal hits the input to this node. This routine calls 00471 * itself recursively to perform the traversal. 00472 */ 00473 static void 00474 load_rc_tree_T(t_rc_node * rc_node, 00475 float T_arrival) 00476 { 00477 00478 float Tdel, Rmetal, Tchild; 00479 t_linked_rc_edge *linked_rc_edge; 00480 t_rc_node *child_node; 00481 short iswitch; 00482 int inode; 00483 00484 Tdel = T_arrival; 00485 inode = rc_node->inode; 00486 Rmetal = rr_node[inode].R; 00487 00488 /* NB: rr_node[inode].C gives the capacitance of this node, while * 00489 * rc_node->C_downstream gives the unbuffered downstream capacitance rooted * 00490 * at this node, including the C of the node itself. I want to multiply * 00491 * the C of this node by 0.5 Rmetal, since it's a distributed RC line. * 00492 * Hence 0.5 Rmetal * Cnode is a pessimistic estimate of delay (i.e. end to * 00493 * end). For the downstream capacitance rooted at this node (not including * 00494 * the capacitance of the node itself), I assume it is, on average, * 00495 * connected halfway along the line, so I also multiply by 0.5 Rmetal. To * 00496 * be totally pessimistic I would multiply the downstream part of the * 00497 * capacitance by Rmetal. Play with this equation if you like. */ 00498 00499 /* Rmetal is distributed so x0.5 */ 00500 Tdel += 0.5 * rc_node->C_downstream * Rmetal; 00501 rc_node->Tdel = Tdel; 00502 00503 /* Now expand the children of this node to load their Tdel values. */ 00504 00505 linked_rc_edge = rc_node->u.child_list; 00506 00507 while(linked_rc_edge != NULL) 00508 { /* For all children */ 00509 iswitch = linked_rc_edge->iswitch; 00510 child_node = linked_rc_edge->child; 00511 00512 Tchild = Tdel + switch_inf[iswitch].R * child_node->C_downstream; 00513 Tchild += switch_inf[iswitch].Tdel; /* Intrinsic switch delay. */ 00514 load_rc_tree_T(child_node, Tchild); 00515 00516 linked_rc_edge = linked_rc_edge->next; 00517 } 00518 } 00519 00520 /** Loads the net delay array for net inet. The rc tree for that net must 00521 * have already been completely built and loaded. 00522 */ 00523 static void 00524 load_one_net_delay(float **net_delay, 00525 int inet, 00526 struct s_net* nets, 00527 t_linked_rc_ptr * rr_node_to_rc_node) 00528 { 00529 int ipin, inode; 00530 float Tmax; 00531 t_rc_node *rc_node; 00532 t_linked_rc_ptr *linked_rc_ptr, *next_ptr; 00533 00534 for(ipin = 1; ipin < (nets[inet].num_sinks + 1); ipin++) 00535 { 00536 00537 inode = net_rr_terminals[inet][ipin]; 00538 00539 00540 linked_rc_ptr = rr_node_to_rc_node[inode].next; 00541 rc_node = rr_node_to_rc_node[inode].rc_node; 00542 Tmax = rc_node->Tdel; 00543 00544 /* If below only executes when one net connects several times to the * 00545 * same SINK. In this case, I can't tell which net pin each connection * 00546 * to this SINK corresponds to (I can just choose arbitrarily). To make * 00547 * sure the timing behaviour converges, I pessimistically set the delay * 00548 * for all of the connections to this SINK by this net to be the max. of * 00549 * the delays from this net to this SINK. NB: This code only occurs * 00550 * when a net connect more than once to the same pin class on the same * 00551 * logic block. Only a weird architecture would allow this. */ 00552 00553 if(linked_rc_ptr != NULL) 00554 { 00555 00556 /* The first time I hit a multiply-used SINK, I choose the largest delay * 00557 * from this net to this SINK and use it for every connection to this * 00558 * SINK by this net. */ 00559 00560 do 00561 { 00562 rc_node = linked_rc_ptr->rc_node; 00563 if(rc_node->Tdel > Tmax) 00564 { 00565 Tmax = rc_node->Tdel; 00566 rr_node_to_rc_node[inode].rc_node = 00567 rc_node; 00568 } 00569 next_ptr = linked_rc_ptr->next; 00570 free(linked_rc_ptr); 00571 linked_rc_ptr = next_ptr; 00572 } 00573 while(linked_rc_ptr != NULL); /* End do while */ 00574 00575 rr_node_to_rc_node[inode].next = NULL; 00576 } 00577 /* End of if multiply-used SINK */ 00578 net_delay[inet][ipin] = Tmax; 00579 } 00580 } 00581 00582 00583 /** Sets each entry of the net_delay array for net inet to delay_value. */ 00584 static void 00585 load_one_constant_net_delay(float **net_delay, 00586 int inet, 00587 struct s_net *nets, 00588 float delay_value) 00589 { 00590 int ipin; 00591 00592 for(ipin = 1; ipin < (nets[inet].num_sinks + 1); ipin++) 00593 net_delay[inet][ipin] = delay_value; 00594 } 00595 00596 /** Puts the rc tree pointed to by rc_root back on the free list. Depth- 00597 * first post-order traversal via recursion. 00598 */ 00599 static void 00600 free_rc_tree(t_rc_node * rc_root, 00601 t_rc_node ** rc_node_free_list_ptr, 00602 t_linked_rc_edge ** rc_edge_free_list_ptr) 00603 { 00604 00605 t_rc_node *rc_node, *child_node; 00606 t_linked_rc_edge *rc_edge, *next_edge; 00607 00608 rc_node = rc_root; 00609 rc_edge = rc_node->u.child_list; 00610 00611 while(rc_edge != NULL) 00612 { /* For all children */ 00613 child_node = rc_edge->child; 00614 free_rc_tree(child_node, rc_node_free_list_ptr, 00615 rc_edge_free_list_ptr); 00616 next_edge = rc_edge->next; 00617 free_linked_rc_edge(rc_edge, rc_edge_free_list_ptr); 00618 rc_edge = next_edge; 00619 } 00620 00621 free_rc_node(rc_node, rc_node_free_list_ptr); 00622 } 00623 00624 /** Resets the rr_node_to_rc_node mapping entries that were set during 00625 * construction of the RC tree for net inet. Any extra linked list entries 00626 * added to deal with a SINK being connected to multiple times have already 00627 * been freed by load_one_net_delay. 00628 */ 00629 static void 00630 reset_rr_node_to_rc_node(t_linked_rc_ptr * rr_node_to_rc_node, 00631 int inet) 00632 { 00633 00634 struct s_trace *tptr; 00635 int inode; 00636 00637 tptr = trace_head[inet]; 00638 00639 while(tptr != NULL) 00640 { 00641 inode = tptr->index; 00642 rr_node_to_rc_node[inode].rc_node = NULL; 00643 tptr = tptr->next; 00644 } 00645 } 00646 00647 00648 /** Really frees (i.e. calls free()) all the rc_nodes on the free list. */ 00649 static void 00650 free_rc_node_free_list(t_rc_node * rc_node_free_list) 00651 { 00652 t_rc_node *rc_node, *next_node; 00653 00654 rc_node = rc_node_free_list; 00655 00656 while(rc_node != NULL) 00657 { 00658 next_node = rc_node->u.next; 00659 free(rc_node); 00660 rc_node = next_node; 00661 } 00662 } 00663 00664 00665 00666 /** Really frees (i.e. calls free()) all the rc_edges on the free list. */ 00667 static void 00668 free_rc_edge_free_list(t_linked_rc_edge * rc_edge_free_list) 00669 { 00670 t_linked_rc_edge *rc_edge, *next_edge; 00671 00672 rc_edge = rc_edge_free_list; 00673 00674 while(rc_edge != NULL) 00675 { 00676 next_edge = rc_edge->next; 00677 free(rc_edge); 00678 rc_edge = next_edge; 00679 } 00680 } 00681 00682 00683 /** Dumps the net delays into file fname. */ 00684 void 00685 print_net_delay(float **net_delay, 00686 char *fname, struct s_net *nets, int n_nets) 00687 { 00688 FILE *fp; 00689 int inet, ipin; 00690 00691 fp = my_fopen(fname, "w", 0); 00692 00693 for(inet = 0; inet < n_nets; inet++) 00694 { 00695 fprintf(fp, "Net: %d.\n", inet); 00696 fprintf(fp, "Delays:"); 00697 00698 for(ipin = 1; ipin < (nets[inet].num_sinks + 1); ipin++) 00699 fprintf(fp, " %g", net_delay[inet][ipin]); 00700 00701 fprintf(fp, "\n\n"); 00702 } 00703 00704 fclose(fp); 00705 }