static void directed_search_add_source_to_heap(int inet, int target_node, float astar_fac) { /* Adds the SOURCE of this net to the heap. Used to start a net's routing. */ int inode; float back_cost, tot_cost; inode = net_rr_terminals[inet][0]; /* SOURCE */ back_cost = 0.0 + get_rr_cong_cost(inode); /* setting the total cost to 0 because it's the only element on the heap */ if(!is_empty_heap()) { printf ("Error: Wrong Assumption: in directed_search_add_source_to_heap " "the heap is not empty. Need to properly calculate source node's cost.\n"); exit(1); } /* WMF: path cost is 0. could use tot_cost = 0 to save some computation time, but * for consistency, I chose to do the expected cost calculation. */ tot_cost = back_cost + astar_fac * get_directed_search_expected_cost(inode, target_node); node_to_heap(inode, tot_cost, NO_PREVIOUS, NO_PREVIOUS, back_cost, OPEN); }
static void breadth_first_expand_neighbours (int inode, float pcost, int inet, float bend_cost) { /* Puts all the rr_nodes adjacent to inode on the heap. rr_nodes outside * * the expanded bounding box specified in route_bb are not added to the * * heap. pcost is the path_cost to get to inode. */ int iconn, to_node, num_edges; t_rr_type from_type, to_type; float tot_cost; num_edges = rr_node[inode].num_edges; for (iconn=0;iconn<num_edges;iconn++) { to_node = rr_node[inode].edges[iconn]; if ( rr_node[to_node].xhigh < route_bb[inet].xmin || rr_node[to_node].xlow > route_bb[inet].xmax || rr_node[to_node].yhigh < route_bb[inet].ymin || rr_node[to_node].ylow > route_bb[inet].ymax ) continue; /* Node is outside (expanded) bounding box. */ tot_cost = pcost + get_rr_cong_cost (to_node); if (bend_cost != 0.) { from_type = rr_node[inode].type; to_type = rr_node[to_node].type; if ((from_type == CHANX && to_type == CHANY) || (from_type == CHANY && to_type == CHANX)) tot_cost += bend_cost; } node_to_heap (to_node, tot_cost, inode, iconn, OPEN, OPEN); } }
void reserve_locally_used_opins (float pres_fac, boolean rip_up_local_opins, t_ivec **clb_opins_used_locally) { /* If some subblock outputs are hooked directly to CLB outputs, then * * some CLB outputs are occupied if their associated subblock is used * * locally, even though the inter-CLB netlist does not say those outputs * * have to connect to anything. This is important when you have * * logically equivalent outputs. Code below makes sure any CLB outputs * * that are used by being directly hooked to subblocks get properly * * reserved. */ int iblk, num_local_opin, inode, from_node, iconn, num_edges, to_node; int iclass, ipin; float cost; struct s_heap *heap_head_ptr; if (rip_up_local_opins) { for (iblk=0;iblk<num_blocks;iblk++) { for (iclass=0;iclass<num_class;iclass++) { num_local_opin = clb_opins_used_locally[iblk][iclass].nelem; /* Always 0 for pads and for RECEIVER (IPIN) classes */ for (ipin=0;ipin<num_local_opin;ipin++) { inode = clb_opins_used_locally[iblk][iclass].list[ipin]; adjust_one_rr_occ_and_pcost (inode, -1, pres_fac); } } } } for (iblk=0;iblk<num_blocks;iblk++) { for (iclass=0;iclass<num_class;iclass++) { num_local_opin = clb_opins_used_locally[iblk][iclass].nelem; /* Always 0 for pads and for RECEIVER (IPIN) classes */ if (num_local_opin != 0) { /* Have to reserve (use) some OPINs */ from_node = rr_clb_source[iblk][iclass]; num_edges = rr_node[from_node].num_edges; for (iconn=0;iconn<num_edges;iconn++) { to_node = rr_node[from_node].edges[iconn]; cost = get_rr_cong_cost (to_node); node_to_heap (to_node, cost, OPEN, OPEN, 0., 0.); } for (ipin=0;ipin<num_local_opin;ipin++) { heap_head_ptr = get_heap_head (); inode = heap_head_ptr->index; adjust_one_rr_occ_and_pcost (inode, 1, pres_fac); clb_opins_used_locally[iblk][iclass].list[ipin] = inode; free_heap_data (heap_head_ptr); } empty_heap (); } } } }
static void breadth_first_add_source_to_heap (int inet) { /* Adds the SOURCE of this net to the heap. Used to start a net's routing. */ int inode; float cost; inode = net_rr_terminals[inet][0]; /* SOURCE */ cost = get_rr_cong_cost (inode); node_to_heap (inode, cost, NO_PREVIOUS, NO_PREVIOUS, OPEN, OPEN); }
/** * Adapted from breadth_first_add_source_to_heap() */ static void inc_breadth_first_add_inode_to_heap(int inode) { float cost; cost = get_rr_cong_cost(inode); node_to_heap(inode, cost, NO_PREVIOUS, NO_PREVIOUS, OPEN, OPEN); }
static void breadth_first_expand_trace_segment (struct s_trace *start_ptr, int remaining_connections_to_sink) { /* Adds all the rr_nodes in the traceback segment starting at tptr (and * * continuing to the end of the traceback) to the heap with a cost of zero. * * This allows expansion to begin from the existing wiring. The * * remaining_connections_to_sink value is 0 if the route segment ending * * at this location is the last one to connect to the SINK ending the route * * segment. This is the usual case. If it is not the last connection this * * net must make to this SINK, I have a hack to ensure the next connection * * to this SINK goes through a different IPIN. Without this hack, the * * router would always put all the connections from this net to this SINK * * through the same IPIN. With LUTs or cluster-based logic blocks, you * * should never have a net connecting to two logically-equivalent pins on * * the same logic block, so the hack will never execute. If your logic * * block is an and-gate, however, nets might connect to two and-inputs on * * the same logic block, and since the and-inputs are logically-equivalent, * * this means two connections to the same SINK. */ struct s_trace *tptr, *next_ptr; int inode, sink_node, last_ipin_node; tptr = start_ptr; if (remaining_connections_to_sink == 0) { /* Usual case. */ while (tptr != NULL) { node_to_heap (tptr->index, 0., NO_PREVIOUS, NO_PREVIOUS, OPEN, OPEN); tptr = tptr->next; } } else { /* This case never executes for most logic blocks. */ /* Weird case. Lots of hacks. The cleanest way to do this would be to empty * * the heap, update the congestion due to the partially-completed route, put * * the whole route so far (excluding IPINs and SINKs) on the heap with cost * * 0., and expand till you hit the next SINK. That would be slow, so I * * do some hacks to enable incremental wavefront expansion instead. */ if (tptr == NULL) return; /* No route yet */ next_ptr = tptr->next; last_ipin_node = OPEN; /* Stops compiler from complaining. */ /* Can't put last SINK on heap with NO_PREVIOUS, etc, since that won't let * * us reach it again. Instead, leave the last traceback element (SINK) off * * the heap. */ while (next_ptr != NULL) { inode = tptr->index; node_to_heap (inode, 0., NO_PREVIOUS, NO_PREVIOUS, OPEN, OPEN); if (rr_node[inode].type == IPIN) last_ipin_node = inode; tptr = next_ptr; next_ptr = tptr->next; } /* This will stop the IPIN node used to get to this SINK from being * * reexpanded for the remainder of this net's routing. This will make us * * hook up more IPINs to this SINK (which is what we want). If IPIN * * doglegs are allowed in the graph, we won't be able to use this IPIN to * * do a dogleg, since it won't be re-expanded. Shouldn't be a big problem. */ rr_node_route_inf[last_ipin_node].path_cost = - HUGE_FLOAT; /* Also need to mark the SINK as having high cost, so another connection can * * be made to it. */ sink_node = tptr->index; rr_node_route_inf[sink_node].path_cost = HUGE_FLOAT; /* Finally, I need to remove any pending connections to this SINK via the * * IPIN I just used (since they would result in congestion). Scan through * * the heap to do this. */ invalidate_heap_entries (sink_node, last_ipin_node); } }
static void directed_search_expand_neighbours(struct s_heap *current, int inet, float bend_cost, int target_node, int highfanout_rlim, float astar_fac) { /* Puts all the rr_nodes adjacent to current on the heap. rr_nodes outside * * the expanded bounding box specified in route_bb are not added to the * * heap. back_cost is the path_cost to get to inode. total cost i.e. * tot_cost is path_cost + (expected_cost to target sink) */ int iconn, to_node, num_edges, inode, target_x, target_y; t_rr_type from_type, to_type; float new_tot_cost, old_back_pcost, new_back_pcost; inode = current->index; old_back_pcost = current->backward_path_cost; num_edges = rr_node[inode].num_edges; target_x = rr_node[target_node].xhigh; target_y = rr_node[target_node].yhigh; for(iconn = 0; iconn < num_edges; iconn++) { to_node = rr_node[inode].edges[iconn]; if(rr_node[to_node].xhigh < route_bb[inet].xmin || rr_node[to_node].xlow > route_bb[inet].xmax || rr_node[to_node].yhigh < route_bb[inet].ymin || rr_node[to_node].ylow > route_bb[inet].ymax) continue; /* Node is outside (expanded) bounding box. */ if(clb_net[inet].num_sinks >= HIGH_FANOUT_NET_LIM) { if(rr_node[to_node].xhigh < target_x - highfanout_rlim || rr_node[to_node].xlow > target_x + highfanout_rlim || rr_node[to_node].yhigh < target_y - highfanout_rlim || rr_node[to_node].ylow > target_y + highfanout_rlim) continue; /* Node is outside high fanout bin. */ } /* Prune away IPINs that lead to blocks other than the target one. Avoids * * the issue of how to cost them properly so they don't get expanded before * * more promising routes, but makes route-throughs (via CLBs) impossible. * * Change this if you want to investigate route-throughs. */ to_type = rr_node[to_node].type; if(to_type == IPIN && (rr_node[to_node].xhigh != target_x || rr_node[to_node].yhigh != target_y)) continue; /* new_back_pcost stores the "known" part of the cost to this node -- the * * congestion cost of all the routing resources back to the existing route * * new_tot_cost * is this "known" backward cost + an expected cost to get to the target. */ new_back_pcost = old_back_pcost + get_rr_cong_cost(to_node); if(bend_cost != 0.) { from_type = rr_node[inode].type; to_type = rr_node[to_node].type; if((from_type == CHANX && to_type == CHANY) || (from_type == CHANY && to_type == CHANX)) new_back_pcost += bend_cost; } /* Calculate the new total cost = path cost + astar_fac * remaining distance to target */ new_tot_cost = new_back_pcost + astar_fac * get_directed_search_expected_cost(to_node, target_node); node_to_heap(to_node, new_tot_cost, inode, iconn, new_back_pcost, OPEN); } }
static int directed_search_expand_trace_segment(struct s_trace *start_ptr, int target_node, float astar_fac, int inet, int remaining_connections_to_sink) { /* Adds all the rr_nodes in the entire traceback from SOURCE to all SINKS * * routed so far (partial routing). * This allows expansion to begin from the existing wiring. The * * remaining_connections_to_sink value is 0 if the route segment ending * * at this location is the last one to connect to the SINK ending the route * * segment. This is the usual case. If it is not the last connection this * * net must make to this SINK, I have a hack to ensure the next connection * * to this SINK goes through a different IPIN. Without this hack, the * * router would always put all the connections from this net to this SINK * * through the same IPIN. With LUTs or cluster-based logic blocks, you * * should never have a net connecting to two logically-equivalent pins on * * the same logic block, so the hack will never execute. If your logic * * block is an and-gate, however, nets might connect to two and-inputs on * * the same logic block, and since the and-inputs are logically-equivalent, * * this means two connections to the same SINK. * * * * For high-fanout nets, return the radius of the expansion bin, * * undefined otherwise */ struct s_trace *tptr; int inode, backward_path_cost, tot_cost; int target_x, target_y; int rlim, area; boolean success; target_x = rr_node[target_node].xhigh; target_y = rr_node[target_node].yhigh; area = (route_bb[inet].xmax - route_bb[inet].xmin) * (route_bb[inet].ymax - route_bb[inet].ymin); if(area <= 0) { area = 1; } if(clb_net[inet].num_sinks < HIGH_FANOUT_NET_LIM) { rlim = 1; } else { rlim = ceil(sqrt((float)area / (float)clb_net[inet].num_sinks)); if(start_ptr == NULL) { /* For first node, route normally since there is nothing in the current traceback path */ rlim = max(nx + 2, ny + 2); } } success = FALSE; /* determine quickly a feasible bin radius to route sink for high fanout nets this is necessary to prevent super long runtimes for high fanout nets; in best case, a reduction in complexity from O(N^2logN) to O(NlogN) (Swartz fast router) */ while(success == FALSE && start_ptr != NULL) { tptr = start_ptr; while(tptr != NULL && success == FALSE) { inode = tptr->index; if(!(rr_node[inode].type == IPIN || rr_node[inode].type == SINK)) { if( clb_net[inet].num_sinks < HIGH_FANOUT_NET_LIM || (rr_node[inode].xlow <= target_x + rlim && rr_node[inode].xhigh >= target_x - rlim && rr_node[inode].ylow <= target_y + rlim && rr_node[inode].yhigh >= target_y - rlim)) { success = TRUE; } } tptr = tptr->next; } if(success == FALSE) { if(rlim > max(nx + 2, ny + 2)) { printf(ERRTAG "VPR internal error, net %s has paths that are not found in traceback\n", clb_net[inet].name); exit(1); } /* if sink not in bin, increase bin size until fit */ rlim *= 2; } else { /* Sometimes might just catch a wire in the end segment, need to give it some channel space to explore */ rlim += 4; } } if(remaining_connections_to_sink == 0) { /* Usual case. */ tptr = start_ptr; while(tptr != NULL) { /* WMF: partial routing is added to the heap with path cost of 0, because * new extension to the next sink can start at any point on current partial * routing. However, for directed search the total cost must be made to favor * the points of current partial routing that are NEAR the next sink (target sink) */ /* WMF: IPINs and SINKs should be excluded from the heap in this * since they NEVER connect TO any rr_node (no to_edges), but since they have * no to_edges, it's ok (ROUTE_THROUGHS are disabled). To clarify, see * rr_graph.c to find out rr_node[inode].num_edges = 0 for SINKs and * rr_node[inode].num_edges = 1 for INPINs */ inode = tptr->index; if(! (rr_node[inode].type == IPIN || rr_node[inode].type == SINK)) { if( clb_net[inet].num_sinks < HIGH_FANOUT_NET_LIM || (rr_node[inode].xlow <= target_x + rlim && rr_node[inode].xhigh >= target_x - rlim && rr_node[inode].ylow <= target_y + rlim && rr_node[inode].yhigh >= target_y - rlim)) { backward_path_cost = 0; tot_cost = backward_path_cost + astar_fac * get_directed_search_expected_cost(inode, target_node); node_to_heap(inode, tot_cost, NO_PREVIOUS, NO_PREVIOUS, backward_path_cost, OPEN); } } tptr = tptr->next; } } else { /* This case never executes for most logic blocks. */ printf("Warning: Multiple connections from net to the same sink. " "This should not happen for LUT/Cluster based logic blocks. Aborting.\n"); exit(1); } return rlim; }
static void directed_search_expand_trace_segment(struct s_trace *start_ptr, int target_node, float astar_fac, int remaining_connections_to_sink) { /* Adds all the rr_nodes in the entire traceback from SOURCE to all SINKS * * routed so far (partial routing). * This allows expansion to begin from the existing wiring. The * * remaining_connections_to_sink value is 0 if the route segment ending * * at this location is the last one to connect to the SINK ending the route * * segment. This is the usual case. If it is not the last connection this * * net must make to this SINK, I have a hack to ensure the next connection * * to this SINK goes through a different IPIN. Without this hack, the * * router would always put all the connections from this net to this SINK * * through the same IPIN. With LUTs or cluster-based logic blocks, you * * should never have a net connecting to two logically-equivalent pins on * * the same logic block, so the hack will never execute. If your logic * * block is an and-gate, however, nets might connect to two and-inputs on * * the same logic block, and since the and-inputs are logically-equivalent, * * this means two connections to the same SINK. */ struct s_trace *tptr; int inode, backward_path_cost, tot_cost; tptr = start_ptr; if(remaining_connections_to_sink == 0) { /* Usual case. */ while(tptr != NULL) { /* WMF: partial routing is added to the heap with path cost of 0, because * new extension to the next sink can start at any point on current partial * routing. However, for directed search the total cost must be made to favor * the points of current partial routing that are NEAR the next sink (target sink) */ /* WMF: IPINs and SINKs should be excluded from the heap in this * since they NEVER connect TO any rr_node (no to_edges), but since they have * no to_edges, it's ok (ROUTE_THROUGHS are disabled). To clarify, see * rr_graph.c to find out rr_node[inode].num_edges = 0 for SINKs and * rr_node[inode].num_edges = 1 for INPINs */ inode = tptr->index; if(! (rr_node[inode].type == IPIN || rr_node[inode].type == SINK)) { backward_path_cost = 0; tot_cost = backward_path_cost + astar_fac * get_directed_search_expected_cost(inode, target_node); node_to_heap(inode, tot_cost, NO_PREVIOUS, NO_PREVIOUS, backward_path_cost, OPEN); } tptr = tptr->next; } } else { /* This case never executes for most logic blocks. */ printf("Warning: Multiple connections from net to the same sink. " "This should not happen for LUT/Cluster based logic blocks. Aborting.\n"); exit(1); } }