| #include <assert.h> |
| #include <stdio.h> |
| #include "util.h" |
| #include "vpr_types.h" |
| #include "globals.h" |
| #include "mst.h" |
| #include "route_export.h" |
| #include "check_route.h" |
| #include "rr_graph.h" |
| #include "check_rr_graph.h" |
| |
| |
| /******************** Subroutines local to this module **********************/ |
| static void check_node_and_range(int inode, |
| enum e_route_type route_type); |
| static void check_source(int inode, |
| int inet); |
| static void check_sink(int inode, |
| int inet, |
| boolean * pin_done); |
| static void check_switch(struct s_trace *tptr, |
| int num_switch); |
| static boolean check_adjacent(int from_node, |
| int to_node); |
| static int pin_and_chan_adjacent(int pin_node, |
| int chan_node); |
| static int chanx_chany_adjacent(int chanx_node, |
| int chany_node); |
| static void reset_flags(int inet, |
| boolean * connected_to_route); |
| static void recompute_occupancy_from_scratch(t_ivec ** fb_opins_used_locally); |
| static void check_locally_used_fb_opins(t_ivec ** fb_opins_used_locally, |
| enum e_route_type route_type); |
| |
| /************************ Subroutine definitions ****************************/ |
| |
| |
| void |
| check_route(enum e_route_type route_type, |
| int num_switch, |
| t_ivec ** fb_opins_used_locally) |
| { |
| |
| /* This routine checks that a routing: (1) Describes a properly * |
| * connected path for each net, (2) this path connects all the * |
| * pins spanned by that net, and (3) that no routing resources are * |
| * oversubscribed (the occupancy of everything is recomputed from * |
| * scratch). */ |
| |
| int inet, ipin, max_pins, inode, prev_node; |
| boolean valid, connects; |
| boolean *connected_to_route; /* [0 .. num_rr_nodes-1] */ |
| struct s_trace *tptr; |
| boolean *pin_done; |
| |
| printf("\nChecking to ensure routing is legal ...\n"); |
| |
| /* Recompute the occupancy from scratch and check for overuse of routing * |
| * resources. This was already checked in order to determine that this * |
| * is a successful routing, but I want to double check it here. */ |
| |
| recompute_occupancy_from_scratch(fb_opins_used_locally); |
| valid = feasible_routing(); |
| if(valid == FALSE) |
| { |
| printf |
| ("Error in check_route -- routing resources are overused.\n"); |
| exit(1); |
| } |
| |
| check_locally_used_fb_opins(fb_opins_used_locally, route_type); |
| |
| connected_to_route = (boolean *) my_calloc(num_rr_nodes, sizeof(boolean)); |
| |
| max_pins = 0; |
| for(inet = 0; inet < num_nets; inet++) |
| max_pins = max(max_pins, (net[inet].num_sinks + 1)); |
| |
| pin_done = (boolean *) my_malloc(max_pins * sizeof(boolean)); |
| |
| /* Now check that all nets are indeed connected. */ |
| |
| for(inet = 0; inet < num_nets; inet++) |
| { |
| |
| if(net[inet].is_global) /* Skip global nets. */ |
| continue; |
| |
| for(ipin = 0; ipin < (net[inet].num_sinks + 1); ipin++) |
| pin_done[ipin] = FALSE; |
| |
| /* Check the SOURCE of the net. */ |
| |
| tptr = trace_head[inet]; |
| if(tptr == NULL) |
| { |
| printf("Error in check_route: net %d has no routing.\n", |
| inet); |
| exit(1); |
| } |
| |
| inode = tptr->index; |
| check_node_and_range(inode, route_type); |
| check_switch(tptr, num_switch); |
| connected_to_route[inode] = TRUE; /* Mark as in path. */ |
| |
| check_source(inode, inet); |
| pin_done[0] = TRUE; |
| |
| prev_node = inode; |
| tptr = tptr->next; |
| |
| /* Check the rest of the net */ |
| |
| while(tptr != NULL) |
| { |
| inode = tptr->index; |
| check_node_and_range(inode, route_type); |
| check_switch(tptr, num_switch); |
| |
| if(rr_node[prev_node].type == SINK) |
| { |
| if(connected_to_route[inode] == FALSE) |
| { |
| printf |
| ("Error in check_route. Node %d does not link " |
| "into the existing routing for net %d.\n", |
| inode, inet); |
| exit(1); |
| } |
| } |
| |
| else |
| { |
| connects = check_adjacent(prev_node, inode); |
| if(!connects) |
| { |
| printf |
| ("Error in check_route while checking net %d.\n", |
| inet); |
| printf |
| ("Non-adjacent segments in traceback.\n"); |
| exit(1); |
| } |
| |
| if(connected_to_route[inode] |
| && rr_node[inode].type != SINK) |
| { |
| |
| /* Note: Can get multiple connections to the same logically-equivalent * |
| * SINK in some logic blocks. */ |
| |
| printf |
| ("Error in check_route: net %d routing is not a tree.\n", |
| inet); |
| exit(1); |
| } |
| |
| connected_to_route[inode] = TRUE; /* Mark as in path. */ |
| |
| if(rr_node[inode].type == SINK) |
| check_sink(inode, inet, pin_done); |
| |
| } /* End of prev_node type != SINK */ |
| prev_node = inode; |
| tptr = tptr->next; |
| } /* End while */ |
| |
| if(rr_node[prev_node].type != SINK) |
| { |
| printf("Error in check_route. Net %d does not end\n", |
| inet); |
| printf("with a SINK.\n"); |
| exit(1); |
| } |
| |
| for(ipin = 0; ipin < (net[inet].num_sinks + 1); ipin++) |
| { |
| if(pin_done[ipin] == FALSE) |
| { |
| printf |
| ("Error in check_route. Net %d does not \n", |
| inet); |
| printf("connect to pin %d.\n", ipin); |
| exit(1); |
| } |
| } |
| |
| reset_flags(inet, connected_to_route); |
| |
| } /* End for each net */ |
| |
| free(pin_done); |
| free(connected_to_route); |
| printf("Completed routing consistency check successfully.\n\n"); |
| } |
| |
| static void |
| check_sink(int inode, |
| int inet, |
| boolean * pin_done) |
| { |
| |
| /* Checks that this SINK node is one of the terminals of inet, and marks * |
| * the appropriate pin as being reached. */ |
| |
| int i, j, ipin, ifound, ptc_num, bnum, iclass, node_block_pin, iblk; |
| t_type_ptr type; |
| |
| assert(rr_node[inode].type == SINK); |
| i = rr_node[inode].xlow; |
| j = rr_node[inode].ylow; |
| type = grid[i][j].type; |
| ptc_num = rr_node[inode].ptc_num; /* For sinks, ptc_num is the class */ |
| ifound = 0; |
| |
| for(iblk = 0; iblk < type->capacity; iblk++) |
| { |
| bnum = grid[i][j].blocks[iblk]; /* Hardcoded to one block */ |
| for(ipin = 1; ipin < (net[inet].num_sinks + 1); ipin++) |
| { /* All net SINKs */ |
| if(net[inet].node_block[ipin] == bnum) |
| { |
| node_block_pin = net[inet].node_block_pin[ipin]; |
| iclass = type->pin_class[node_block_pin]; |
| if(iclass == ptc_num) |
| { |
| /* Could connect to same pin class on the same fb more than once. Only * |
| * update pin_done for a pin that hasn't been reached yet. */ |
| |
| if(pin_done[ipin] == FALSE) |
| { |
| ifound++; |
| pin_done[ipin] = TRUE; |
| } |
| } |
| } |
| } |
| } |
| |
| if(ifound > 1 && type == IO_TYPE) |
| { |
| printf("Error in check_sink: found %d terminals of net %d of pad" |
| "\n %d at location (%d, %d).\n", ifound, inet, ptc_num, i, |
| j); |
| exit(1); |
| } |
| |
| if(ifound < 1) |
| { |
| printf |
| ("Error in check_sink: node %d does not connect to any terminal " |
| "\n of net %d.\n", inode, inet); |
| exit(1); |
| } |
| } |
| |
| |
| static void |
| check_source(int inode, |
| int inet) |
| { |
| |
| /* Checks that the node passed in is a valid source for this net. */ |
| |
| t_rr_type rr_type; |
| t_type_ptr type; |
| int i, j, ptc_num, bnum, node_block_pin, iclass; |
| |
| rr_type = rr_node[inode].type; |
| if(rr_type != SOURCE) |
| { |
| printf |
| ("Error in check_source: net %d begins with a node of type %d.\n", |
| inet, rr_type); |
| exit(1); |
| } |
| |
| i = rr_node[inode].xlow; |
| j = rr_node[inode].ylow; |
| ptc_num = rr_node[inode].ptc_num; /* for sinks and sources, ptc_num is class */ |
| bnum = net[inet].node_block[0]; /* First node_block for net is the source */ |
| type = grid[i][j].type; |
| |
| if(block[bnum].x != i || block[bnum].y != j) |
| { |
| printf |
| ("Error in check_source: net SOURCE is in wrong location (%d,%d)." |
| "\n", i, j); |
| exit(1); |
| } |
| |
| node_block_pin = net[inet].node_block_pin[0]; |
| iclass = type->pin_class[node_block_pin]; |
| |
| if(ptc_num != iclass) |
| { |
| printf |
| ("Error in check_source: net SOURCE is of wrong class (%d).\n", |
| ptc_num); |
| exit(1); |
| } |
| } |
| |
| |
| static void |
| check_switch(struct s_trace *tptr, |
| int num_switch) |
| { |
| |
| /* Checks that the switch leading from this traceback element to the next * |
| * one is a legal switch type. */ |
| |
| int inode; |
| short switch_type; |
| |
| inode = tptr->index; |
| switch_type = tptr->iswitch; |
| |
| if(rr_node[inode].type != SINK) |
| { |
| if(switch_type < 0 || switch_type >= num_switch) |
| { |
| printf |
| ("Error in check_switch: rr_node %d left via switch type %d.\n", |
| inode, switch_type); |
| printf("Switch type is out of range.\n"); |
| exit(1); |
| } |
| } |
| |
| else |
| { /* Is a SINK */ |
| |
| /* Without feedthroughs, there should be no switch. If feedthroughs are * |
| * allowed, change to treat a SINK like any other node (as above). */ |
| |
| if(switch_type != OPEN) |
| { |
| printf |
| ("Error in check_switch: rr_node %d is a SINK, but attempts \n" |
| "to use a switch of type %d.\n", inode, switch_type); |
| exit(1); |
| } |
| } |
| } |
| |
| |
| static void |
| reset_flags(int inet, |
| boolean * connected_to_route) |
| { |
| |
| /* This routine resets the flags of all the channel segments contained * |
| * in the traceback of net inet to 0. This allows us to check the * |
| * next net for connectivity (and the default state of the flags * |
| * should always be zero after they have been used). */ |
| |
| struct s_trace *tptr; |
| int inode; |
| |
| tptr = trace_head[inet]; |
| |
| while(tptr != NULL) |
| { |
| inode = tptr->index; |
| connected_to_route[inode] = FALSE; /* Not in routed path now. */ |
| tptr = tptr->next; |
| } |
| } |
| |
| |
| static boolean |
| check_adjacent(int from_node, |
| int to_node) |
| { |
| |
| /* This routine checks if the rr_node to_node is reachable from from_node. * |
| * It returns TRUE if is reachable and FALSE if it is not. Check_node has * |
| * already been used to verify that both nodes are valid rr_nodes, so only * |
| * adjacency is checked here. */ |
| |
| int from_xlow, from_ylow, to_xlow, to_ylow, from_ptc, to_ptc, iclass; |
| int num_adj, to_xhigh, to_yhigh, from_xhigh, from_yhigh, iconn; |
| boolean reached; |
| t_rr_type from_type, to_type; |
| t_type_ptr from_grid_type, to_grid_type; |
| |
| reached = FALSE; |
| |
| for(iconn = 0; iconn < rr_node[from_node].num_edges; iconn++) |
| { |
| if(rr_node[from_node].edges[iconn] == to_node) |
| { |
| reached = TRUE; |
| break; |
| } |
| } |
| |
| if(!reached) |
| return (FALSE); |
| |
| /* Now we know the rr graph says these two nodes are adjacent. Double * |
| * check that this makes sense, to verify the rr graph. */ |
| |
| num_adj = 0; |
| |
| from_type = rr_node[from_node].type; |
| from_xlow = rr_node[from_node].xlow; |
| from_ylow = rr_node[from_node].ylow; |
| from_xhigh = rr_node[from_node].xhigh; |
| from_yhigh = rr_node[from_node].yhigh; |
| from_ptc = rr_node[from_node].ptc_num; |
| to_type = rr_node[to_node].type; |
| to_xlow = rr_node[to_node].xlow; |
| to_ylow = rr_node[to_node].ylow; |
| to_xhigh = rr_node[to_node].xhigh; |
| to_yhigh = rr_node[to_node].yhigh; |
| to_ptc = rr_node[to_node].ptc_num; |
| |
| switch (from_type) |
| { |
| |
| case SOURCE: |
| assert(to_type == OPIN); |
| if(from_xlow == to_xlow && from_ylow == to_ylow |
| && from_xhigh == to_xhigh && from_yhigh == to_yhigh) |
| { |
| |
| from_grid_type = grid[from_xlow][from_ylow].type; |
| to_grid_type = grid[to_xlow][to_ylow].type; |
| assert(from_grid_type == to_grid_type); |
| |
| iclass = to_grid_type->pin_class[to_ptc]; |
| if(iclass == from_ptc) |
| num_adj++; |
| } |
| break; |
| |
| case SINK: |
| /* SINKS are adjacent to not connected */ |
| break; |
| |
| case OPIN: |
| assert(to_type == CHANX || to_type == CHANY); |
| num_adj += pin_and_chan_adjacent(from_node, to_node); |
| |
| break; |
| |
| case IPIN: |
| assert(to_type == SINK); |
| if(from_xlow == to_xlow && from_ylow == to_ylow |
| && from_xhigh == to_xhigh && from_yhigh == to_yhigh) |
| { |
| |
| from_grid_type = grid[from_xlow][from_ylow].type; |
| to_grid_type = grid[to_xlow][to_ylow].type; |
| assert(from_grid_type == to_grid_type); |
| |
| iclass = from_grid_type->pin_class[from_ptc]; |
| if(iclass == to_ptc) |
| num_adj++; |
| } |
| break; |
| |
| case CHANX: |
| if(to_type == IPIN) |
| { |
| num_adj += pin_and_chan_adjacent(to_node, from_node); |
| } |
| else if(to_type == CHANX) |
| { |
| from_xhigh = rr_node[from_node].xhigh; |
| to_xhigh = rr_node[to_node].xhigh; |
| if(from_ylow == to_ylow) |
| { |
| /* UDSD Modification by WMF Begin */ |
| /*For Fs > 3, can connect to overlapping wire segment */ |
| if(to_xhigh == from_xlow - 1 |
| || from_xhigh == to_xlow - 1) |
| { |
| num_adj++; |
| } |
| /* Overlapping */ |
| else |
| { |
| int i; |
| |
| for(i = from_xlow; i <= from_xhigh; i++) |
| { |
| if(i >= to_xlow && i <= to_xhigh) |
| { |
| num_adj++; |
| break; |
| } |
| } |
| } |
| /* UDSD Modification by WMF End */ |
| } |
| } |
| else if(to_type == CHANY) |
| { |
| num_adj += chanx_chany_adjacent(from_node, to_node); |
| } |
| else |
| { |
| assert(0); |
| } |
| break; |
| |
| case CHANY: |
| if(to_type == IPIN) |
| { |
| num_adj += pin_and_chan_adjacent(to_node, from_node); |
| } |
| else if(to_type == CHANY) |
| { |
| from_yhigh = rr_node[from_node].yhigh; |
| to_yhigh = rr_node[to_node].yhigh; |
| if(from_xlow == to_xlow) |
| { |
| /* UDSD Modification by WMF Begin */ |
| if(to_yhigh == from_ylow - 1 |
| || from_yhigh == to_ylow - 1) |
| { |
| num_adj++; |
| } |
| /* Overlapping */ |
| else |
| { |
| int j; |
| |
| for(j = from_ylow; j <= from_yhigh; j++) |
| { |
| if(j >= to_ylow && j <= to_yhigh) |
| { |
| num_adj++; |
| break; |
| } |
| } |
| } |
| /* UDSD Modification by WMF End */ |
| } |
| } |
| else if(to_type == CHANX) |
| { |
| num_adj += chanx_chany_adjacent(to_node, from_node); |
| } |
| else |
| { |
| assert(0); |
| } |
| break; |
| |
| default: |
| break; |
| |
| } |
| |
| if(num_adj == 1) |
| return (TRUE); |
| else if(num_adj == 0) |
| return (FALSE); |
| |
| printf("Error in check_adjacent: num_adj = %d. Expected 0 or 1.\n", |
| num_adj); |
| exit(1); |
| } |
| |
| |
| static int |
| chanx_chany_adjacent(int chanx_node, |
| int chany_node) |
| { |
| |
| /* Returns 1 if the specified CHANX and CHANY nodes are adjacent, 0 * |
| * otherwise. */ |
| |
| int chanx_y, chanx_xlow, chanx_xhigh; |
| int chany_x, chany_ylow, chany_yhigh; |
| |
| chanx_y = rr_node[chanx_node].ylow; |
| chanx_xlow = rr_node[chanx_node].xlow; |
| chanx_xhigh = rr_node[chanx_node].xhigh; |
| |
| chany_x = rr_node[chany_node].xlow; |
| chany_ylow = rr_node[chany_node].ylow; |
| chany_yhigh = rr_node[chany_node].yhigh; |
| |
| if(chany_ylow > chanx_y + 1 || chany_yhigh < chanx_y) |
| return (0); |
| |
| if(chanx_xlow > chany_x + 1 || chanx_xhigh < chany_x) |
| return (0); |
| |
| return (1); |
| } |
| |
| |
| static int |
| pin_and_chan_adjacent(int pin_node, |
| int chan_node) |
| { |
| |
| /* Checks if pin_node is adjacent to chan_node. It returns 1 if the two * |
| * nodes are adjacent and 0 if they are not (any other value means there's * |
| * a bug in this routine). */ |
| |
| int num_adj, pin_xlow, pin_ylow, pin_xhigh, pin_yhigh, chan_xlow, |
| chan_ylow, chan_xhigh, chan_yhigh; |
| int pin_ptc, i; |
| t_rr_type chan_type; |
| t_type_ptr pin_grid_type; |
| |
| num_adj = 0; |
| pin_xlow = rr_node[pin_node].xlow; |
| pin_ylow = rr_node[pin_node].ylow; |
| pin_xhigh = rr_node[pin_node].xhigh; |
| pin_yhigh = rr_node[pin_node].yhigh; |
| pin_grid_type = grid[pin_xlow][pin_ylow].type; |
| pin_ptc = rr_node[pin_node].ptc_num; |
| chan_type = rr_node[chan_node].type; |
| chan_xlow = rr_node[chan_node].xlow; |
| chan_ylow = rr_node[chan_node].ylow; |
| chan_xhigh = rr_node[chan_node].xhigh; |
| chan_yhigh = rr_node[chan_node].yhigh; |
| |
| if(chan_type == CHANX) |
| { |
| if(chan_ylow == pin_yhigh) |
| { /* CHANX above FB */ |
| if(pin_grid_type-> |
| pinloc[pin_grid_type->height - 1][TOP][pin_ptc] == 1 |
| && pin_xlow <= chan_xhigh && pin_xhigh >= chan_xlow) |
| num_adj++; |
| } |
| else if(chan_ylow == pin_ylow - 1) |
| { /* CHANX below FB */ |
| if(pin_grid_type->pinloc[0][BOTTOM][pin_ptc] == 1 |
| && pin_xlow <= chan_xhigh && pin_xhigh >= chan_xlow) |
| num_adj++; |
| } |
| } |
| else if(chan_type == CHANY) |
| { |
| for(i = 0; i < pin_grid_type->height; i++) |
| { |
| if(chan_xlow == pin_xhigh) |
| { /* CHANY to right of FB */ |
| if(pin_grid_type->pinloc[i][RIGHT][pin_ptc] == 1 |
| && pin_ylow <= chan_yhigh |
| && pin_yhigh >= chan_ylow) |
| num_adj++; |
| } |
| else if(chan_xlow == pin_xlow - 1) |
| { /* CHANY to left of FB */ |
| if(pin_grid_type->pinloc[i][LEFT][pin_ptc] == 1 |
| && pin_ylow <= chan_yhigh |
| && pin_yhigh >= chan_ylow) |
| num_adj++; |
| } |
| } |
| } |
| return (num_adj); |
| } |
| |
| |
| static void |
| recompute_occupancy_from_scratch(t_ivec ** fb_opins_used_locally) |
| { |
| |
| /* This routine updates the occ field in the rr_node structure according to * |
| * the resource usage of the current routing. It does a brute force * |
| * recompute from scratch that is useful for sanity checking. */ |
| |
| int inode, inet, iblk, iclass, ipin, num_local_opins; |
| struct s_trace *tptr; |
| |
| /* First set the occupancy of everything to zero. */ |
| |
| for(inode = 0; inode < num_rr_nodes; inode++) |
| rr_node[inode].occ = 0; |
| |
| /* Now go through each net and count the tracks and pins used everywhere */ |
| |
| for(inet = 0; inet < num_nets; inet++) |
| { |
| |
| if(net[inet].is_global) /* Skip global nets. */ |
| continue; |
| |
| tptr = trace_head[inet]; |
| if(tptr == NULL) |
| continue; |
| |
| for(;;) |
| { |
| inode = tptr->index; |
| rr_node[inode].occ++; |
| |
| if(rr_node[inode].type == SINK) |
| { |
| tptr = tptr->next; /* Skip next segment. */ |
| if(tptr == NULL) |
| break; |
| } |
| |
| tptr = tptr->next; |
| } |
| } |
| |
| /* Now update the occupancy of each of the "locally used" OPINs on each FB * |
| * (FB outputs used up by being directly wired to subblocks used only * |
| * locally). */ |
| |
| for(iblk = 0; iblk < num_blocks; iblk++) |
| { |
| for(iclass = 0; iclass < block[iblk].type->num_class; iclass++) |
| { |
| num_local_opins = |
| fb_opins_used_locally[iblk][iclass].nelem; |
| /* Will always be 0 for pads or SINK classes. */ |
| for(ipin = 0; ipin < num_local_opins; ipin++) |
| { |
| inode = |
| fb_opins_used_locally[iblk][iclass]. |
| list[ipin]; |
| rr_node[inode].occ++; |
| } |
| } |
| } |
| } |
| |
| |
| static void |
| check_locally_used_fb_opins(t_ivec ** fb_opins_used_locally, |
| enum e_route_type route_type) |
| { |
| |
| /* Checks that enough OPINs on CLBs have been set aside (used up) to make a * |
| * legal routing if subblocks connect to OPINs directly. */ |
| |
| int iclass, iblk, num_local_opins, inode, ipin; |
| t_rr_type rr_type; |
| |
| for(iblk = 0; iblk < num_blocks; iblk++) |
| { |
| for(iclass = 0; iclass < block[iblk].type->num_class; iclass++) |
| { |
| num_local_opins = |
| fb_opins_used_locally[iblk][iclass].nelem; |
| /* Always 0 for pads and for SINK classes */ |
| |
| for(ipin = 0; ipin < num_local_opins; ipin++) |
| { |
| inode = |
| fb_opins_used_locally[iblk][iclass]. |
| list[ipin]; |
| check_node_and_range(inode, route_type); /* Node makes sense? */ |
| |
| /* Now check that node is an OPIN of the right type. */ |
| |
| rr_type = rr_node[inode].type; |
| if(rr_type != OPIN) |
| { |
| printf |
| ("Error in check_locally_used_opins: Block #%d (%s)\n" |
| "\tclass %d locally used OPIN is of the wrong rr_type --\n" |
| "\tit is rr_node #%d of type %d.\n", |
| iblk, block[iblk].name, iclass, |
| inode, rr_type); |
| exit(1); |
| } |
| |
| ipin = rr_node[inode].ptc_num; |
| if(block[iblk].type->pin_class[ipin] != iclass) |
| { |
| printf |
| ("Error in check_locally_used_opins: Block #%d (%s):\n" |
| "\tExpected class %d locally used OPIN, got class %d." |
| "\trr_node #: %d.\n", iblk, |
| block[iblk].name, iclass, |
| block[iblk].type->pin_class[ipin], |
| inode); |
| exit(1); |
| } |
| } |
| } |
| } |
| } |
| |
| |
| static void |
| check_node_and_range(int inode, |
| enum e_route_type route_type) |
| { |
| |
| /* Checks that inode is within the legal range, then calls check_node to * |
| * check that everything else about the node is OK. */ |
| |
| if(inode < 0 || inode >= num_rr_nodes) |
| { |
| printf |
| ("Error in check_node_and_range: rr_node #%d is out of legal " |
| "\trange (0 to %d).\n", inode, num_rr_nodes - 1); |
| exit(1); |
| } |
| check_node(inode, route_type); |
| } |